LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sc / source / filter / excel / xecontent.cxx
blob17a614b8fdf3c7635fb743566e3fc5813994ef68
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 <xecontent.hxx>
23 #include <vector>
24 #include <algorithm>
25 #include <string_view>
27 #include <com/sun/star/frame/XModel.hpp>
28 #include <com/sun/star/sheet/XAreaLinks.hpp>
29 #include <com/sun/star/sheet/XAreaLink.hpp>
30 #include <com/sun/star/sheet/TableValidationVisibility.hpp>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <sfx2/objsh.hxx>
33 #include <tools/urlobj.hxx>
34 #include <formula/grammar.hxx>
35 #include <scitems.hxx>
36 #include <editeng/flditem.hxx>
37 #include <document.hxx>
38 #include <validat.hxx>
39 #include <unonames.hxx>
40 #include <convuno.hxx>
41 #include <rangenam.hxx>
42 #include <tokenarray.hxx>
43 #include <stlpool.hxx>
44 #include <patattr.hxx>
45 #include <fapihelper.hxx>
46 #include <xehelper.hxx>
47 #include <xestyle.hxx>
48 #include <xename.hxx>
49 #include <xlcontent.hxx>
50 #include <xltools.hxx>
51 #include <xeformula.hxx>
52 #include <rtl/uuid.h>
53 #include <sal/log.hxx>
54 #include <oox/export/utils.hxx>
55 #include <oox/token/namespaces.hxx>
56 #include <oox/token/relationship.hxx>
57 #include <comphelper/string.hxx>
59 using namespace ::oox;
61 using ::com::sun::star::uno::Reference;
62 using ::com::sun::star::uno::UNO_QUERY;
63 using ::com::sun::star::table::CellRangeAddress;
64 using ::com::sun::star::sheet::XAreaLinks;
65 using ::com::sun::star::sheet::XAreaLink;
67 // Shared string table ========================================================
69 namespace {
71 /** A single string entry in the hash table. */
72 struct XclExpHashEntry
74 const XclExpString* mpString; /// Pointer to the string (no ownership).
75 sal_uInt32 mnSstIndex; /// The SST index of this string.
76 explicit XclExpHashEntry( const XclExpString* pString, sal_uInt32 nSstIndex ) :
77 mpString( pString ), mnSstIndex( nSstIndex ) {}
80 /** Function object for strict weak ordering. */
81 struct XclExpHashEntrySWO
83 bool operator()( const XclExpHashEntry& rLeft, const XclExpHashEntry& rRight ) const
84 { return *rLeft.mpString < *rRight.mpString; }
89 /** Implementation of the SST export.
90 @descr Stores all passed strings in a hash table and prevents repeated
91 insertion of equal strings. */
92 class XclExpSstImpl
94 public:
95 explicit XclExpSstImpl();
97 /** Inserts the passed string, if not already inserted, and returns the unique SST index. */
98 sal_uInt32 Insert( XclExpStringRef xString );
100 /** Writes the complete SST and EXTSST records. */
101 void Save( XclExpStream& rStrm );
102 void SaveXml( XclExpXmlStream& rStrm );
104 private:
105 typedef ::std::vector< XclExpHashEntry > XclExpHashVec;
107 std::vector< XclExpStringRef > maStringVector; /// List of unique strings (in SST ID order).
108 std::vector< XclExpHashVec >
109 maHashTab; /// Hashed table that manages string pointers.
110 sal_uInt32 mnTotal; /// Total count of strings (including doubles).
111 sal_uInt32 mnSize; /// Size of the SST (count of unique strings).
114 const sal_uInt32 EXC_SST_HASHTABLE_SIZE = 2048;
116 XclExpSstImpl::XclExpSstImpl() :
117 maHashTab( EXC_SST_HASHTABLE_SIZE ),
118 mnTotal( 0 ),
119 mnSize( 0 )
123 sal_uInt32 XclExpSstImpl::Insert( XclExpStringRef xString )
125 OSL_ENSURE( xString, "XclExpSstImpl::Insert - empty pointer not allowed" );
126 if( !xString )
127 xString.reset( new XclExpString );
129 ++mnTotal;
130 sal_uInt32 nSstIndex = 0;
132 // calculate hash value in range [0,EXC_SST_HASHTABLE_SIZE)
133 sal_uInt16 nHash = xString->GetHash();
134 nHash = (nHash ^ (nHash / EXC_SST_HASHTABLE_SIZE)) % EXC_SST_HASHTABLE_SIZE;
136 XclExpHashVec& rVec = maHashTab[ nHash ];
137 XclExpHashEntry aEntry( xString.get(), mnSize );
138 XclExpHashVec::iterator aIt = ::std::lower_bound( rVec.begin(), rVec.end(), aEntry, XclExpHashEntrySWO() );
139 if( (aIt == rVec.end()) || (*aIt->mpString != *xString) )
141 nSstIndex = mnSize;
142 maStringVector.push_back( xString );
143 rVec.insert( aIt, aEntry );
144 ++mnSize;
146 else
148 nSstIndex = aIt->mnSstIndex;
151 return nSstIndex;
154 void XclExpSstImpl::Save( XclExpStream& rStrm )
156 if( maStringVector.empty() )
157 return;
159 SvMemoryStream aExtSst( 8192 );
161 sal_uInt32 nBucket = mnSize;
162 while( nBucket > 0x0100 )
163 nBucket /= 2;
165 sal_uInt16 nPerBucket = llimit_cast< sal_uInt16 >( nBucket, 8 );
166 sal_uInt16 nBucketIndex = 0;
168 // *** write the SST record ***
170 rStrm.StartRecord( EXC_ID_SST, 8 );
172 rStrm << mnTotal << mnSize;
173 for (auto const& elem : maStringVector)
175 if( !nBucketIndex )
177 // write bucket info before string to get correct record position
178 sal_uInt32 nStrmPos = static_cast< sal_uInt32 >( rStrm.GetSvStreamPos() );
179 sal_uInt16 nRecPos = rStrm.GetRawRecPos() + 4;
180 aExtSst.WriteUInt32( nStrmPos ) // stream position
181 .WriteUInt16( nRecPos ) // position from start of SST or CONTINUE
182 .WriteUInt16( 0 ); // reserved
185 rStrm << *elem;
187 if( ++nBucketIndex == nPerBucket )
188 nBucketIndex = 0;
191 rStrm.EndRecord();
193 // *** write the EXTSST record ***
195 rStrm.StartRecord( EXC_ID_EXTSST, 0 );
197 rStrm << nPerBucket;
198 rStrm.SetSliceSize( 8 ); // size of one bucket info
199 aExtSst.Seek( STREAM_SEEK_TO_BEGIN );
200 rStrm.CopyFromStream( aExtSst );
202 rStrm.EndRecord();
205 void XclExpSstImpl::SaveXml( XclExpXmlStream& rStrm )
207 if( maStringVector.empty() )
208 return;
210 sax_fastparser::FSHelperPtr pSst = rStrm.CreateOutputStream(
211 "xl/sharedStrings.xml",
212 u"sharedStrings.xml",
213 rStrm.GetCurrentStream()->getOutputStream(),
214 "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
215 oox::getRelationship(Relationship::SHAREDSTRINGS));
216 rStrm.PushStream( pSst );
218 pSst->startElement( XML_sst,
219 XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
220 XML_count, OString::number(mnTotal),
221 XML_uniqueCount, OString::number(mnSize) );
223 for (auto const& elem : maStringVector)
225 pSst->startElement(XML_si);
226 elem->WriteXml( rStrm );
227 pSst->endElement( XML_si );
230 pSst->endElement( XML_sst );
232 rStrm.PopStream();
235 XclExpSst::XclExpSst() :
236 mxImpl( new XclExpSstImpl )
240 XclExpSst::~XclExpSst()
244 sal_uInt32 XclExpSst::Insert( const XclExpStringRef& xString )
246 return mxImpl->Insert( xString );
249 void XclExpSst::Save( XclExpStream& rStrm )
251 mxImpl->Save( rStrm );
254 void XclExpSst::SaveXml( XclExpXmlStream& rStrm )
256 mxImpl->SaveXml( rStrm );
259 // Merged cells ===============================================================
261 XclExpMergedcells::XclExpMergedcells( const XclExpRoot& rRoot ) :
262 XclExpRoot( rRoot )
266 void XclExpMergedcells::AppendRange( const ScRange& rRange, sal_uInt32 nBaseXFId )
268 if( GetBiff() == EXC_BIFF8 )
270 maMergedRanges.push_back( rRange );
271 maBaseXFIds.push_back( nBaseXFId );
275 sal_uInt32 XclExpMergedcells::GetBaseXFId( const ScAddress& rPos ) const
277 OSL_ENSURE( maBaseXFIds.size() == maMergedRanges.size(), "XclExpMergedcells::GetBaseXFId - invalid lists" );
278 ScfUInt32Vec::const_iterator aIt = maBaseXFIds.begin();
279 ScRangeList& rNCRanges = const_cast< ScRangeList& >( maMergedRanges );
280 for ( size_t i = 0, nRanges = rNCRanges.size(); i < nRanges; ++i, ++aIt )
282 const ScRange & rScRange = rNCRanges[ i ];
283 if( rScRange.In( rPos ) )
284 return *aIt;
286 return EXC_XFID_NOTFOUND;
289 void XclExpMergedcells::Save( XclExpStream& rStrm )
291 if( GetBiff() != EXC_BIFF8 )
292 return;
294 XclRangeList aXclRanges;
295 GetAddressConverter().ConvertRangeList( aXclRanges, maMergedRanges, true );
296 size_t nFirstRange = 0;
297 size_t nRemainingRanges = aXclRanges.size();
298 while( nRemainingRanges > 0 )
300 size_t nRangeCount = ::std::min< size_t >( nRemainingRanges, EXC_MERGEDCELLS_MAXCOUNT );
301 rStrm.StartRecord( EXC_ID_MERGEDCELLS, 2 + 8 * nRangeCount );
302 aXclRanges.WriteSubList( rStrm, nFirstRange, nRangeCount );
303 rStrm.EndRecord();
304 nFirstRange += nRangeCount;
305 nRemainingRanges -= nRangeCount;
309 void XclExpMergedcells::SaveXml( XclExpXmlStream& rStrm )
311 size_t nCount = maMergedRanges.size();
312 if( !nCount )
313 return;
314 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
315 rWorksheet->startElement(XML_mergeCells, XML_count, OString::number(nCount));
316 for( size_t i = 0; i < nCount; ++i )
318 const ScRange & rRange = maMergedRanges[ i ];
319 rWorksheet->singleElement(XML_mergeCell, XML_ref,
320 XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rRange));
322 rWorksheet->endElement( XML_mergeCells );
325 // Hyperlinks =================================================================
327 XclExpHyperlink::XclExpHyperlink( const XclExpRoot& rRoot, const SvxURLField& rUrlField, const ScAddress& rScPos ) :
328 XclExpRecord( EXC_ID_HLINK ),
329 maScPos( rScPos ),
330 mxVarData( new SvMemoryStream ),
331 mnFlags( 0 )
333 const OUString& rUrl = rUrlField.GetURL();
334 const OUString& rRepr = rUrlField.GetRepresentation();
335 INetURLObject aUrlObj( rUrl );
336 const INetProtocol eProtocol = aUrlObj.GetProtocol();
337 bool bWithRepr = !rRepr.isEmpty();
338 XclExpStream aXclStrm( *mxVarData, rRoot ); // using in raw write mode.
340 // description
341 if( bWithRepr )
343 XclExpString aDescr( rRepr, XclStrFlags::ForceUnicode, 255 );
344 aXclStrm << sal_uInt32( aDescr.Len() + 1 ); // string length + 1 trailing zero word
345 aDescr.WriteBuffer( aXclStrm ); // NO flags
346 aXclStrm << sal_uInt16( 0 );
348 mnFlags |= EXC_HLINK_DESCR;
349 m_Repr = rRepr;
352 // file link or URL
353 if( eProtocol == INetProtocol::File || eProtocol == INetProtocol::Smb )
355 sal_uInt16 nLevel;
356 bool bRel;
357 OUString aFileName(
358 BuildFileName(nLevel, bRel, rUrl, rRoot, rRoot.GetOutput() == EXC_OUTPUT_XML_2007));
360 if( eProtocol == INetProtocol::Smb )
362 // #n382718# (and #n261623#) Convert smb notation to '\\'
363 aFileName = aUrlObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
364 aFileName = aFileName.copy(4); // skip the 'smb:' part
365 aFileName = aFileName.replace('/', '\\');
368 if( !bRel )
369 mnFlags |= EXC_HLINK_ABS;
370 mnFlags |= EXC_HLINK_BODY;
372 OString aAsciiLink(OUStringToOString(aFileName,
373 rRoot.GetTextEncoding()));
374 XclExpString aLink( aFileName, XclStrFlags::ForceUnicode, 255 );
375 aXclStrm << XclTools::maGuidFileMoniker
376 << nLevel
377 << sal_uInt32( aAsciiLink.getLength() + 1 ); // string length + 1 trailing zero byte
378 aXclStrm.Write( aAsciiLink.getStr(), aAsciiLink.getLength() );
379 aXclStrm << sal_uInt8( 0 )
380 << sal_uInt32( 0xDEADFFFF );
381 aXclStrm.WriteZeroBytes( 20 );
382 aXclStrm << sal_uInt32( aLink.GetBufferSize() + 6 )
383 << sal_uInt32( aLink.GetBufferSize() ) // byte count, not string length
384 << sal_uInt16( 0x0003 );
385 aLink.WriteBuffer( aXclStrm ); // NO flags
387 if (m_Repr.isEmpty())
388 m_Repr = aFileName;
390 msTarget = XclXmlUtils::ToOUString( aLink );
392 if( bRel )
394 for( int i = 0; i < nLevel; ++i )
395 msTarget = "../" + msTarget;
397 else if (rRoot.GetOutput() != EXC_OUTPUT_XML_2007)
399 // xls expects the file:/// part appended ( or at least
400 // ms2007 does, ms2010 is more tolerant )
401 msTarget = "file:///" + msTarget;
404 else if( eProtocol != INetProtocol::NotValid )
406 XclExpString aUrl( aUrlObj.GetURLNoMark(), XclStrFlags::ForceUnicode, 255 );
407 aXclStrm << XclTools::maGuidUrlMoniker
408 << sal_uInt32( aUrl.GetBufferSize() + 2 ); // byte count + 1 trailing zero word
409 aUrl.WriteBuffer( aXclStrm ); // NO flags
410 aXclStrm << sal_uInt16( 0 );
412 mnFlags |= EXC_HLINK_BODY | EXC_HLINK_ABS;
413 if (m_Repr.isEmpty())
414 m_Repr = rUrl;
416 msTarget = XclXmlUtils::ToOUString( aUrl );
418 else if( !rUrl.isEmpty() && rUrl[0] == '#' ) // hack for #89066#
420 OUString aTextMark( rUrl.copy( 1 ) );
422 sal_Int32 nSepPos = aTextMark.lastIndexOf( '!' );
423 sal_Int32 nPointPos = aTextMark.lastIndexOf( '.' );
424 // last dot is the separator, if there is no ! after it
425 if(nSepPos < nPointPos)
427 nSepPos = nPointPos;
428 aTextMark = aTextMark.replaceAt( nSepPos, 1, u"!" );
431 if (nSepPos != -1)
433 OUString aSheetName(aTextMark.copy(0, nSepPos));
435 if (aSheetName.indexOf(' ') != -1 && aSheetName[0] != '\'')
437 aTextMark = "'" + aTextMark.replaceAt(nSepPos, 0, u"'");
440 else
442 SCTAB nTab;
443 if (rRoot.GetDoc().GetTable(aTextMark, nTab))
444 aTextMark += "!A1"; // tdf#143220 link to sheet not valid without cell reference
447 mxTextMark.reset( new XclExpString( aTextMark, XclStrFlags::ForceUnicode, 255 ) );
450 // text mark
451 if( !mxTextMark && aUrlObj.HasMark() )
452 mxTextMark.reset( new XclExpString( aUrlObj.GetMark(), XclStrFlags::ForceUnicode, 255 ) );
454 if( mxTextMark )
456 aXclStrm << sal_uInt32( mxTextMark->Len() + 1 ); // string length + 1 trailing zero word
457 mxTextMark->WriteBuffer( aXclStrm ); // NO flags
458 aXclStrm << sal_uInt16( 0 );
460 mnFlags |= EXC_HLINK_MARK;
462 OUString location = XclXmlUtils::ToOUString(*mxTextMark);
463 if (!location.isEmpty() && msTarget.endsWith(OUStringConcatenation("#" + location)))
464 msTarget = msTarget.copy(0, msTarget.getLength() - location.getLength() - 1);
467 SetRecSize( 32 + mxVarData->Tell() );
470 XclExpHyperlink::~XclExpHyperlink()
474 OUString XclExpHyperlink::BuildFileName(
475 sal_uInt16& rnLevel, bool& rbRel, const OUString& rUrl, const XclExpRoot& rRoot, bool bEncoded )
477 INetURLObject aURLObject( rUrl );
478 OUString aDosName(bEncoded ? aURLObject.GetMainURL(INetURLObject::DecodeMechanism::ToIUri)
479 : aURLObject.getFSysPath(FSysStyle::Dos));
480 rnLevel = 0;
481 rbRel = rRoot.IsRelUrl();
483 if( rbRel )
485 // try to convert to relative file name
486 OUString aTmpName( aDosName );
487 aDosName = INetURLObject::GetRelURL( rRoot.GetBasePath(), rUrl,
488 INetURLObject::EncodeMechanism::WasEncoded,
489 (bEncoded ? INetURLObject::DecodeMechanism::ToIUri : INetURLObject::DecodeMechanism::WithCharset));
491 if (aDosName.startsWith(INET_FILE_SCHEME))
493 // not converted to rel -> back to old, return absolute flag
494 aDosName = aTmpName;
495 rbRel = false;
497 else if (aDosName.startsWith("./"))
499 aDosName = aDosName.copy(2);
501 else
503 while (aDosName.startsWith("../"))
505 aDosName = aDosName.copy(3);
506 ++rnLevel;
510 return aDosName;
513 void XclExpHyperlink::WriteBody( XclExpStream& rStrm )
515 sal_uInt16 nXclCol = static_cast< sal_uInt16 >( maScPos.Col() );
516 sal_uInt16 nXclRow = static_cast< sal_uInt16 >( maScPos.Row() );
517 rStrm << nXclRow << nXclRow << nXclCol << nXclCol;
518 WriteEmbeddedData( rStrm );
521 void XclExpHyperlink::WriteEmbeddedData( XclExpStream& rStrm )
523 rStrm << XclTools::maGuidStdLink
524 << sal_uInt32( 2 )
525 << mnFlags;
527 mxVarData->Seek( STREAM_SEEK_TO_BEGIN );
528 rStrm.CopyFromStream( *mxVarData );
531 void XclExpHyperlink::SaveXml( XclExpXmlStream& rStrm )
533 OUString sId = !msTarget.isEmpty() ? rStrm.addRelation( rStrm.GetCurrentStream()->getOutputStream(),
534 oox::getRelationship(Relationship::HYPERLINK),
535 msTarget, true ) : OUString();
536 std::optional<OString> sTextMark;
537 if (mxTextMark)
538 sTextMark = XclXmlUtils::ToOString(*mxTextMark);
539 rStrm.GetCurrentStream()->singleElement( XML_hyperlink,
540 XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maScPos),
541 FSNS( XML_r, XML_id ), sax_fastparser::UseIf(sId, !sId.isEmpty()),
542 XML_location, sTextMark,
543 // OOXTODO: XML_tooltip, from record HLinkTooltip 800h wzTooltip
544 XML_display, m_Repr );
547 // Label ranges ===============================================================
549 XclExpLabelranges::XclExpLabelranges( const XclExpRoot& rRoot ) :
550 XclExpRoot( rRoot )
552 SCTAB nScTab = GetCurrScTab();
553 // row label ranges
554 FillRangeList( maRowRanges, rRoot.GetDoc().GetRowNameRangesRef(), nScTab );
555 // row labels only over 1 column (restriction of Excel97/2000/XP)
556 for ( size_t i = 0, nRanges = maRowRanges.size(); i < nRanges; ++i )
558 ScRange & rScRange = maRowRanges[ i ];
559 if( rScRange.aStart.Col() != rScRange.aEnd.Col() )
560 rScRange.aEnd.SetCol( rScRange.aStart.Col() );
562 // col label ranges
563 FillRangeList( maColRanges, rRoot.GetDoc().GetColNameRangesRef(), nScTab );
566 void XclExpLabelranges::FillRangeList( ScRangeList& rScRanges,
567 const ScRangePairListRef& xLabelRangesRef, SCTAB nScTab )
569 for ( size_t i = 0, nPairs = xLabelRangesRef->size(); i < nPairs; ++i )
571 const ScRangePair & rRangePair = (*xLabelRangesRef)[i];
572 const ScRange& rScRange = rRangePair.GetRange( 0 );
573 if( rScRange.aStart.Tab() == nScTab )
574 rScRanges.push_back( rScRange );
578 void XclExpLabelranges::Save( XclExpStream& rStrm )
580 XclExpAddressConverter& rAddrConv = GetAddressConverter();
581 XclRangeList aRowXclRanges, aColXclRanges;
582 rAddrConv.ConvertRangeList( aRowXclRanges, maRowRanges, false );
583 rAddrConv.ConvertRangeList( aColXclRanges, maColRanges, false );
584 if( !aRowXclRanges.empty() || !aColXclRanges.empty() )
586 rStrm.StartRecord( EXC_ID_LABELRANGES, 4 + 8 * (aRowXclRanges.size() + aColXclRanges.size()) );
587 rStrm << aRowXclRanges << aColXclRanges;
588 rStrm.EndRecord();
592 // Conditional formatting ====================================================
594 /** Represents a CF record that contains one condition of a conditional format. */
595 class XclExpCFImpl : protected XclExpRoot
597 public:
598 explicit XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin );
600 /** Writes the body of the CF record. */
601 void WriteBody( XclExpStream& rStrm );
602 void SaveXml( XclExpXmlStream& rStrm );
604 private:
605 const ScCondFormatEntry& mrFormatEntry; /// Calc conditional format entry.
606 ScAddress maOrigin; /// Top left cell of the combined range
607 XclFontData maFontData; /// Font formatting attributes.
608 XclExpCellBorder maBorder; /// Border formatting attributes.
609 XclExpCellArea maArea; /// Pattern formatting attributes.
610 XclTokenArrayRef mxTokArr1; /// Formula for first condition.
611 XclTokenArrayRef mxTokArr2; /// Formula for second condition.
612 sal_uInt32 mnFontColorId; /// Font color ID.
613 sal_uInt8 mnType; /// Type of the condition (cell/formula).
614 sal_uInt8 mnOperator; /// Comparison operator for cell type.
615 sal_Int32 mnPriority; /// Priority of this entry; needed for oox export
616 bool mbFontUsed; /// true = Any font attribute used.
617 bool mbHeightUsed; /// true = Font height used.
618 bool mbWeightUsed; /// true = Font weight used.
619 bool mbColorUsed; /// true = Font color used.
620 bool mbUnderlUsed; /// true = Font underline type used.
621 bool mbItalicUsed; /// true = Font posture used.
622 bool mbStrikeUsed; /// true = Font strikeout used.
623 bool mbBorderUsed; /// true = Border attribute used.
624 bool mbPattUsed; /// true = Pattern attribute used.
625 bool mbFormula2;
628 XclExpCFImpl::XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin ) :
629 XclExpRoot( rRoot ),
630 mrFormatEntry( rFormatEntry ),
631 maOrigin( aOrigin ),
632 mnFontColorId( 0 ),
633 mnType( EXC_CF_TYPE_CELL ),
634 mnOperator( EXC_CF_CMP_NONE ),
635 mnPriority( nPriority ),
636 mbFontUsed( false ),
637 mbHeightUsed( false ),
638 mbWeightUsed( false ),
639 mbColorUsed( false ),
640 mbUnderlUsed( false ),
641 mbItalicUsed( false ),
642 mbStrikeUsed( false ),
643 mbBorderUsed( false ),
644 mbPattUsed( false ),
645 mbFormula2(false)
647 // Set correct tab for maOrigin from GetValidSrcPos() of the format-entry.
648 ScAddress aValidSrcPos = mrFormatEntry.GetValidSrcPos();
649 maOrigin.SetTab(aValidSrcPos.Tab());
651 /* Get formatting attributes here, and not in WriteBody(). This is needed to
652 correctly insert all colors into the palette. */
654 if( SfxStyleSheetBase* pStyleSheet = GetDoc().GetStyleSheetPool()->Find( mrFormatEntry.GetStyle(), SfxStyleFamily::Para ) )
656 const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
658 // font
659 mbHeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_HEIGHT, true );
660 mbWeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_WEIGHT, true );
661 mbColorUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_COLOR, true );
662 mbUnderlUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_UNDERLINE, true );
663 mbItalicUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_POSTURE, true );
664 mbStrikeUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_CROSSEDOUT, true );
665 mbFontUsed = mbHeightUsed || mbWeightUsed || mbColorUsed || mbUnderlUsed || mbItalicUsed || mbStrikeUsed;
666 if( mbFontUsed )
668 vcl::Font aFont;
669 ScPatternAttr::GetFont( aFont, rItemSet, SC_AUTOCOL_RAW );
670 maFontData.FillFromVclFont( aFont );
671 mnFontColorId = GetPalette().InsertColor( maFontData.maColor, EXC_COLOR_CELLTEXT );
674 // border
675 mbBorderUsed = ScfTools::CheckItem( rItemSet, ATTR_BORDER, true );
676 if( mbBorderUsed )
677 maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff() );
679 // pattern
680 mbPattUsed = ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true );
681 if( mbPattUsed )
682 maArea.FillFromItemSet( rItemSet, GetPalette(), true );
685 // *** mode and comparison operator ***
687 switch( rFormatEntry.GetOperation() )
689 case ScConditionMode::NONE:
690 mnType = EXC_CF_TYPE_NONE;
691 break;
692 case ScConditionMode::Between:
693 mnOperator = EXC_CF_CMP_BETWEEN;
694 mbFormula2 = true;
695 break;
696 case ScConditionMode::NotBetween:
697 mnOperator = EXC_CF_CMP_NOT_BETWEEN;
698 mbFormula2 = true;
699 break;
700 case ScConditionMode::Equal:
701 mnOperator = EXC_CF_CMP_EQUAL;
702 break;
703 case ScConditionMode::NotEqual:
704 mnOperator = EXC_CF_CMP_NOT_EQUAL;
705 break;
706 case ScConditionMode::Greater:
707 mnOperator = EXC_CF_CMP_GREATER;
708 break;
709 case ScConditionMode::Less:
710 mnOperator = EXC_CF_CMP_LESS;
711 break;
712 case ScConditionMode::EqGreater:
713 mnOperator = EXC_CF_CMP_GREATER_EQUAL;
714 break;
715 case ScConditionMode::EqLess:
716 mnOperator = EXC_CF_CMP_LESS_EQUAL;
717 break;
718 case ScConditionMode::Direct:
719 mnType = EXC_CF_TYPE_FMLA;
720 break;
721 default:
722 mnType = EXC_CF_TYPE_NONE;
723 OSL_FAIL( "XclExpCF::WriteBody - unknown condition type" );
727 void XclExpCFImpl::WriteBody( XclExpStream& rStrm )
730 // *** formulas ***
732 XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
734 std::unique_ptr< ScTokenArray > xScTokArr( mrFormatEntry.CreateFlatCopiedTokenArray( 0 ) );
735 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
737 if (mbFormula2)
739 xScTokArr = mrFormatEntry.CreateFlatCopiedTokenArray( 1 );
740 mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
743 // *** mode and comparison operator ***
745 rStrm << mnType << mnOperator;
747 // *** formula sizes ***
749 sal_uInt16 nFmlaSize1 = mxTokArr1 ? mxTokArr1->GetSize() : 0;
750 sal_uInt16 nFmlaSize2 = mxTokArr2 ? mxTokArr2->GetSize() : 0;
751 rStrm << nFmlaSize1 << nFmlaSize2;
753 // *** formatting blocks ***
755 if( mbFontUsed || mbBorderUsed || mbPattUsed )
757 sal_uInt32 nFlags = EXC_CF_ALLDEFAULT;
759 ::set_flag( nFlags, EXC_CF_BLOCK_FONT, mbFontUsed );
760 ::set_flag( nFlags, EXC_CF_BLOCK_BORDER, mbBorderUsed );
761 ::set_flag( nFlags, EXC_CF_BLOCK_AREA, mbPattUsed );
763 // attributes used -> set flags to 0.
764 ::set_flag( nFlags, EXC_CF_BORDER_ALL, !mbBorderUsed );
765 ::set_flag( nFlags, EXC_CF_AREA_ALL, !mbPattUsed );
767 rStrm << nFlags << sal_uInt16( 0 );
769 if( mbFontUsed )
771 // font height, 0xFFFFFFFF indicates unused
772 sal_uInt32 nHeight = mbHeightUsed ? maFontData.mnHeight : 0xFFFFFFFF;
773 // font style: italic and strikeout
774 sal_uInt32 nStyle = 0;
775 ::set_flag( nStyle, EXC_CF_FONT_STYLE, maFontData.mbItalic );
776 ::set_flag( nStyle, EXC_CF_FONT_STRIKEOUT, maFontData.mbStrikeout );
777 // font color, 0xFFFFFFFF indicates unused
778 sal_uInt32 nColor = mbColorUsed ? GetPalette().GetColorIndex( mnFontColorId ) : 0xFFFFFFFF;
779 // font used flags for italic, weight, and strikeout -> 0 = used, 1 = default
780 sal_uInt32 nFontFlags1 = EXC_CF_FONT_ALLDEFAULT;
781 ::set_flag( nFontFlags1, EXC_CF_FONT_STYLE, !(mbItalicUsed || mbWeightUsed) );
782 ::set_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT, !mbStrikeUsed );
783 // font used flag for underline -> 0 = used, 1 = default
784 sal_uInt32 nFontFlags3 = mbUnderlUsed ? 0 : EXC_CF_FONT_UNDERL;
786 rStrm.WriteZeroBytesToRecord( 64 );
787 rStrm << nHeight
788 << nStyle
789 << maFontData.mnWeight
790 << EXC_FONTESC_NONE
791 << maFontData.mnUnderline;
792 rStrm.WriteZeroBytesToRecord( 3 );
793 rStrm << nColor
794 << sal_uInt32( 0 )
795 << nFontFlags1
796 << EXC_CF_FONT_ESCAPEM // escapement never used -> set the flag
797 << nFontFlags3;
798 rStrm.WriteZeroBytesToRecord( 16 );
799 rStrm << sal_uInt16( 1 ); // must be 1
802 if( mbBorderUsed )
804 sal_uInt16 nLineStyle = 0;
805 sal_uInt32 nLineColor = 0;
806 maBorder.SetFinalColors( GetPalette() );
807 maBorder.FillToCF8( nLineStyle, nLineColor );
808 rStrm << nLineStyle << nLineColor << sal_uInt16( 0 );
811 if( mbPattUsed )
813 sal_uInt16 nPattern = 0, nColor = 0;
814 maArea.SetFinalColors( GetPalette() );
815 maArea.FillToCF8( nPattern, nColor );
816 rStrm << nPattern << nColor;
819 else
821 // no data blocks at all
822 rStrm << sal_uInt32( 0 ) << sal_uInt16( 0 );
825 // *** formulas ***
827 if( mxTokArr1 )
828 mxTokArr1->WriteArray( rStrm );
829 if( mxTokArr2 )
830 mxTokArr2->WriteArray( rStrm );
833 namespace {
835 const char* GetOperatorString(ScConditionMode eMode, bool& bFrmla2)
837 const char *pRet = nullptr;
838 switch(eMode)
840 case ScConditionMode::Equal:
841 pRet = "equal";
842 break;
843 case ScConditionMode::Less:
844 pRet = "lessThan";
845 break;
846 case ScConditionMode::Greater:
847 pRet = "greaterThan";
848 break;
849 case ScConditionMode::EqLess:
850 pRet = "lessThanOrEqual";
851 break;
852 case ScConditionMode::EqGreater:
853 pRet = "greaterThanOrEqual";
854 break;
855 case ScConditionMode::NotEqual:
856 pRet = "notEqual";
857 break;
858 case ScConditionMode::Between:
859 bFrmla2 = true;
860 pRet = "between";
861 break;
862 case ScConditionMode::NotBetween:
863 bFrmla2 = true;
864 pRet = "notBetween";
865 break;
866 case ScConditionMode::Duplicate:
867 pRet = nullptr;
868 break;
869 case ScConditionMode::NotDuplicate:
870 pRet = nullptr;
871 break;
872 case ScConditionMode::BeginsWith:
873 pRet = "beginsWith";
874 break;
875 case ScConditionMode::EndsWith:
876 pRet = "endsWith";
877 break;
878 case ScConditionMode::ContainsText:
879 pRet = "containsText";
880 break;
881 case ScConditionMode::NotContainsText:
882 pRet = "notContains";
883 break;
884 case ScConditionMode::Direct:
885 break;
886 case ScConditionMode::NONE:
887 default:
888 break;
890 return pRet;
893 const char* GetTypeString(ScConditionMode eMode)
895 switch(eMode)
897 case ScConditionMode::Direct:
898 return "expression";
899 case ScConditionMode::Top10:
900 case ScConditionMode::TopPercent:
901 case ScConditionMode::Bottom10:
902 case ScConditionMode::BottomPercent:
903 return "top10";
904 case ScConditionMode::AboveAverage:
905 case ScConditionMode::BelowAverage:
906 case ScConditionMode::AboveEqualAverage:
907 case ScConditionMode::BelowEqualAverage:
908 return "aboveAverage";
909 case ScConditionMode::NotDuplicate:
910 return "uniqueValues";
911 case ScConditionMode::Duplicate:
912 return "duplicateValues";
913 case ScConditionMode::Error:
914 return "containsErrors";
915 case ScConditionMode::NoError:
916 return "notContainsErrors";
917 case ScConditionMode::BeginsWith:
918 return "beginsWith";
919 case ScConditionMode::EndsWith:
920 return "endsWith";
921 case ScConditionMode::ContainsText:
922 return "containsText";
923 case ScConditionMode::NotContainsText:
924 return "notContainsText";
925 default:
926 return "cellIs";
930 bool IsTopBottomRule(ScConditionMode eMode)
932 switch(eMode)
934 case ScConditionMode::Top10:
935 case ScConditionMode::Bottom10:
936 case ScConditionMode::TopPercent:
937 case ScConditionMode::BottomPercent:
938 return true;
939 default:
940 break;
943 return false;
946 bool IsTextRule(ScConditionMode eMode)
948 switch(eMode)
950 case ScConditionMode::BeginsWith:
951 case ScConditionMode::EndsWith:
952 case ScConditionMode::ContainsText:
953 case ScConditionMode::NotContainsText:
954 return true;
955 default:
956 break;
959 return false;
962 bool RequiresFormula(ScConditionMode eMode)
964 if (IsTopBottomRule(eMode))
965 return false;
966 else if (IsTextRule(eMode))
967 return false;
969 switch (eMode)
971 case ScConditionMode::NoError:
972 case ScConditionMode::Error:
973 case ScConditionMode::Duplicate:
974 case ScConditionMode::NotDuplicate:
975 return false;
976 default:
977 break;
980 return true;
983 bool RequiresFixedFormula(ScConditionMode eMode)
985 switch(eMode)
987 case ScConditionMode::NoError:
988 case ScConditionMode::Error:
989 case ScConditionMode::BeginsWith:
990 case ScConditionMode::EndsWith:
991 case ScConditionMode::ContainsText:
992 case ScConditionMode::NotContainsText:
993 return true;
994 default:
995 break;
998 return false;
1001 OString GetFixedFormula(ScConditionMode eMode, const ScAddress& rAddress, std::string_view rText)
1003 OStringBuffer aBuffer;
1004 XclXmlUtils::ToOString(aBuffer, rAddress);
1005 OString aPos = aBuffer.makeStringAndClear();
1006 switch (eMode)
1008 case ScConditionMode::Error:
1009 return OString("ISERROR(" + aPos + ")") ;
1010 case ScConditionMode::NoError:
1011 return OString("NOT(ISERROR(" + aPos + "))") ;
1012 case ScConditionMode::BeginsWith:
1013 return OString("LEFT(" + aPos + ",LEN(\"" + rText + "\"))=\"" + rText + "\"");
1014 case ScConditionMode::EndsWith:
1015 return OString("RIGHT(" + aPos +",LEN(\"" + rText + "\"))=\"" + rText + "\"");
1016 case ScConditionMode::ContainsText:
1017 return OString(OString::Concat("NOT(ISERROR(SEARCH(\"") + rText + "\"," + aPos + ")))");
1018 case ScConditionMode::NotContainsText:
1019 return OString(OString::Concat("ISERROR(SEARCH(\"") + rText + "\"," + aPos + "))");
1020 default:
1021 break;
1024 return "";
1029 void XclExpCFImpl::SaveXml( XclExpXmlStream& rStrm )
1031 bool bFmla2 = false;
1032 ScConditionMode eOperation = mrFormatEntry.GetOperation();
1033 bool bAboveAverage = eOperation == ScConditionMode::AboveAverage ||
1034 eOperation == ScConditionMode::AboveEqualAverage;
1035 bool bEqualAverage = eOperation == ScConditionMode::AboveEqualAverage ||
1036 eOperation == ScConditionMode::BelowEqualAverage;
1037 bool bBottom = eOperation == ScConditionMode::Bottom10
1038 || eOperation == ScConditionMode::BottomPercent;
1039 bool bPercent = eOperation == ScConditionMode::TopPercent ||
1040 eOperation == ScConditionMode::BottomPercent;
1041 OUString aRank("0");
1042 if(IsTopBottomRule(eOperation))
1044 // position and formula grammar are not important
1045 // we only store a number there
1046 aRank = mrFormatEntry.GetExpression(ScAddress(0,0,0), 0);
1048 OString aText;
1049 if(IsTextRule(eOperation))
1051 // we need to write the text without quotes
1052 // we have to actually get the string from
1053 // the token array for that
1054 std::unique_ptr<ScTokenArray> pTokenArray(mrFormatEntry.CreateFlatCopiedTokenArray(0));
1055 if(pTokenArray->GetLen())
1056 aText = pTokenArray->FirstToken()->GetString().getString().toUtf8();
1059 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1060 rWorksheet->startElement( XML_cfRule,
1061 XML_type, GetTypeString( mrFormatEntry.GetOperation() ),
1062 XML_priority, OString::number(mnPriority + 1),
1063 XML_operator, GetOperatorString( mrFormatEntry.GetOperation(), bFmla2 ),
1064 XML_aboveAverage, ToPsz10(bAboveAverage),
1065 XML_equalAverage, ToPsz10(bEqualAverage),
1066 XML_bottom, ToPsz10(bBottom),
1067 XML_percent, ToPsz10(bPercent),
1068 XML_rank, aRank,
1069 XML_text, aText,
1070 XML_dxfId, OString::number(GetDxfs().GetDxfId(mrFormatEntry.GetStyle())) );
1072 if (RequiresFixedFormula(eOperation))
1074 rWorksheet->startElement(XML_formula);
1075 OString aFormula = GetFixedFormula(eOperation, maOrigin, aText);
1076 rWorksheet->writeEscaped(aFormula.getStr());
1077 rWorksheet->endElement( XML_formula );
1079 else if(RequiresFormula(eOperation))
1081 rWorksheet->startElement(XML_formula);
1082 std::unique_ptr<ScTokenArray> pTokenArray(mrFormatEntry.CreateFlatCopiedTokenArray(0));
1083 rWorksheet->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry.GetValidSrcPos(),
1084 pTokenArray.get()));
1085 rWorksheet->endElement( XML_formula );
1086 if (bFmla2)
1088 rWorksheet->startElement(XML_formula);
1089 std::unique_ptr<ScTokenArray> pTokenArray2(mrFormatEntry.CreateFlatCopiedTokenArray(1));
1090 rWorksheet->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry.GetValidSrcPos(),
1091 pTokenArray2.get()));
1092 rWorksheet->endElement( XML_formula );
1095 // OOXTODO: XML_extLst
1096 rWorksheet->endElement( XML_cfRule );
1099 XclExpCF::XclExpCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin ) :
1100 XclExpRecord( EXC_ID_CF ),
1101 XclExpRoot( rRoot ),
1102 mxImpl( new XclExpCFImpl( rRoot, rFormatEntry, nPriority, aOrigin ) )
1106 XclExpCF::~XclExpCF()
1110 void XclExpCF::WriteBody( XclExpStream& rStrm )
1112 mxImpl->WriteBody( rStrm );
1115 void XclExpCF::SaveXml( XclExpXmlStream& rStrm )
1117 mxImpl->SaveXml( rStrm );
1120 XclExpDateFormat::XclExpDateFormat( const XclExpRoot& rRoot, const ScCondDateFormatEntry& rFormatEntry, sal_Int32 nPriority ):
1121 XclExpRecord( EXC_ID_CF ),
1122 XclExpRoot( rRoot ),
1123 mrFormatEntry(rFormatEntry),
1124 mnPriority(nPriority)
1128 XclExpDateFormat::~XclExpDateFormat()
1132 namespace {
1134 const char* getTimePeriodString( condformat::ScCondFormatDateType eType )
1136 switch(eType)
1138 case condformat::TODAY:
1139 return "today";
1140 case condformat::YESTERDAY:
1141 return "yesterday";
1142 case condformat::TOMORROW:
1143 return "yesterday";
1144 case condformat::THISWEEK:
1145 return "thisWeek";
1146 case condformat::LASTWEEK:
1147 return "lastWeek";
1148 case condformat::NEXTWEEK:
1149 return "nextWeek";
1150 case condformat::THISMONTH:
1151 return "thisMonth";
1152 case condformat::LASTMONTH:
1153 return "lastMonth";
1154 case condformat::NEXTMONTH:
1155 return "nextMonth";
1156 case condformat::LAST7DAYS:
1157 return "last7Days";
1158 default:
1159 break;
1161 return nullptr;
1166 void XclExpDateFormat::SaveXml( XclExpXmlStream& rStrm )
1168 // only write the supported entries into OOXML
1169 const char* sTimePeriod = getTimePeriodString(mrFormatEntry.GetDateType());
1170 if(!sTimePeriod)
1171 return;
1173 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1174 rWorksheet->startElement( XML_cfRule,
1175 XML_type, "timePeriod",
1176 XML_priority, OString::number(mnPriority + 1),
1177 XML_timePeriod, sTimePeriod,
1178 XML_dxfId, OString::number(GetDxfs().GetDxfId(mrFormatEntry.GetStyleName())) );
1179 rWorksheet->endElement( XML_cfRule);
1182 XclExpCfvo::XclExpCfvo(const XclExpRoot& rRoot, const ScColorScaleEntry& rEntry, const ScAddress& rAddr, bool bFirst):
1183 XclExpRoot( rRoot ),
1184 mrEntry(rEntry),
1185 maSrcPos(rAddr),
1186 mbFirst(bFirst)
1190 namespace {
1192 OString getColorScaleType( const ScColorScaleEntry& rEntry, bool bFirst )
1194 switch(rEntry.GetType())
1196 case COLORSCALE_MIN:
1197 return "min";
1198 case COLORSCALE_MAX:
1199 return "max";
1200 case COLORSCALE_PERCENT:
1201 return "percent";
1202 case COLORSCALE_FORMULA:
1203 return "formula";
1204 case COLORSCALE_AUTO:
1205 if(bFirst)
1206 return "min";
1207 else
1208 return "max";
1209 case COLORSCALE_PERCENTILE:
1210 return "percentile";
1211 default:
1212 break;
1215 return "num";
1220 void XclExpCfvo::SaveXml( XclExpXmlStream& rStrm )
1222 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1224 OString aValue;
1225 if(mrEntry.GetType() == COLORSCALE_FORMULA)
1227 OUString aFormula = XclXmlUtils::ToOUString( GetCompileFormulaContext(), maSrcPos,
1228 mrEntry.GetFormula());
1229 aValue = OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8 );
1231 else
1233 aValue = OString::number( mrEntry.GetValue() );
1236 rWorksheet->startElement( XML_cfvo,
1237 XML_type, getColorScaleType(mrEntry, mbFirst),
1238 XML_val, aValue );
1240 rWorksheet->endElement( XML_cfvo );
1243 XclExpColScaleCol::XclExpColScaleCol( const XclExpRoot& rRoot, const Color& rColor ):
1244 XclExpRoot( rRoot ),
1245 mrColor( rColor )
1249 XclExpColScaleCol::~XclExpColScaleCol()
1253 void XclExpColScaleCol::SaveXml( XclExpXmlStream& rStrm )
1255 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1257 rWorksheet->startElement(XML_color, XML_rgb, XclXmlUtils::ToOString(mrColor));
1259 rWorksheet->endElement( XML_color );
1262 namespace {
1264 OString createHexStringFromDigit(sal_uInt8 nDigit)
1266 OString aString = OString::number( nDigit, 16 );
1267 if(aString.getLength() == 1)
1268 aString += OString::number(0);
1269 return aString;
1272 OString createGuidStringFromInt(sal_uInt8 nGuid[16])
1274 OStringBuffer aBuffer;
1275 aBuffer.append('{');
1276 for(size_t i = 0; i < 16; ++i)
1278 aBuffer.append(createHexStringFromDigit(nGuid[i]));
1279 if(i == 3|| i == 5 || i == 7 || i == 9 )
1280 aBuffer.append('-');
1282 aBuffer.append('}');
1283 OString aString = aBuffer.makeStringAndClear();
1284 return aString.toAsciiUpperCase();
1287 OString generateGUIDString()
1289 sal_uInt8 nGuid[16];
1290 rtl_createUuid(nGuid, nullptr, true);
1291 return createGuidStringFromInt(nGuid);
1296 XclExpCondfmt::XclExpCondfmt( const XclExpRoot& rRoot, const ScConditionalFormat& rCondFormat, const XclExtLstRef& xExtLst, sal_Int32& rIndex ) :
1297 XclExpRecord( EXC_ID_CONDFMT ),
1298 XclExpRoot( rRoot )
1300 const ScRangeList& aScRanges = rCondFormat.GetRange();
1301 GetAddressConverter().ConvertRangeList( maXclRanges, aScRanges, true );
1302 if( maXclRanges.empty() )
1303 return;
1305 std::vector<XclExpExtCondFormatData> aExtEntries;
1306 ScAddress aOrigin = aScRanges.Combine().aStart;
1307 for( size_t nIndex = 0, nCount = rCondFormat.size(); nIndex < nCount; ++nIndex )
1308 if( const ScFormatEntry* pFormatEntry = rCondFormat.GetEntry( nIndex ) )
1310 if(pFormatEntry->GetType() == ScFormatEntry::Type::Condition)
1311 maCFList.AppendNewRecord( new XclExpCF( GetRoot(), static_cast<const ScCondFormatEntry&>(*pFormatEntry), ++rIndex, aOrigin ) );
1312 else if(pFormatEntry->GetType() == ScFormatEntry::Type::ExtCondition)
1314 const ScCondFormatEntry& rFormat = static_cast<const ScCondFormatEntry&>(*pFormatEntry);
1315 XclExpExtCondFormatData aExtEntry;
1316 aExtEntry.nPriority = ++rIndex;
1317 aExtEntry.aGUID = generateGUIDString();
1318 aExtEntry.pEntry = &rFormat;
1319 aExtEntries.push_back(aExtEntry);
1321 else if(pFormatEntry->GetType() == ScFormatEntry::Type::Colorscale)
1322 maCFList.AppendNewRecord( new XclExpColorScale( GetRoot(), static_cast<const ScColorScaleFormat&>(*pFormatEntry), ++rIndex ) );
1323 else if(pFormatEntry->GetType() == ScFormatEntry::Type::Databar)
1325 const ScDataBarFormat& rFormat = static_cast<const ScDataBarFormat&>(*pFormatEntry);
1326 XclExpExtCondFormatData aExtEntry;
1327 aExtEntry.nPriority = -1;
1328 aExtEntry.aGUID = generateGUIDString();
1329 aExtEntry.pEntry = &rFormat;
1330 aExtEntries.push_back(aExtEntry);
1332 maCFList.AppendNewRecord( new XclExpDataBar( GetRoot(), rFormat, ++rIndex, aExtEntry.aGUID));
1334 else if(pFormatEntry->GetType() == ScFormatEntry::Type::Iconset)
1336 // don't export iconSet entries that are not in OOXML
1337 const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(*pFormatEntry);
1338 bool bNeedsExt = false;
1339 switch (rIconSet.GetIconSetData()->eIconSetType)
1341 case IconSet_3Smilies:
1342 case IconSet_3ColorSmilies:
1343 case IconSet_3Stars:
1344 case IconSet_3Triangles:
1345 case IconSet_5Boxes:
1347 bNeedsExt = true;
1349 break;
1350 default:
1351 break;
1354 bNeedsExt |= rIconSet.GetIconSetData()->mbCustom;
1356 if (bNeedsExt)
1358 XclExpExtCondFormatData aExtEntry;
1359 aExtEntry.nPriority = ++rIndex;
1360 aExtEntry.aGUID = generateGUIDString();
1361 aExtEntry.pEntry = &rIconSet;
1362 aExtEntries.push_back(aExtEntry);
1364 else
1365 maCFList.AppendNewRecord( new XclExpIconSet( GetRoot(), rIconSet, ++rIndex ) );
1367 else if(pFormatEntry->GetType() == ScFormatEntry::Type::Date)
1368 maCFList.AppendNewRecord( new XclExpDateFormat( GetRoot(), static_cast<const ScCondDateFormatEntry&>(*pFormatEntry), ++rIndex ) );
1370 aScRanges.Format( msSeqRef, ScRefFlags::VALID, GetDoc(), formula::FormulaGrammar::CONV_XL_OOX, ' ', true );
1372 if(!aExtEntries.empty() && xExtLst)
1374 XclExpExt* pParent = xExtLst->GetItem( XclExpExtDataBarType );
1375 if( !pParent )
1377 xExtLst->AddRecord( new XclExpExtCondFormat( *xExtLst ) );
1378 pParent = xExtLst->GetItem( XclExpExtDataBarType );
1380 static_cast<XclExpExtCondFormat*>(xExtLst->GetItem( XclExpExtDataBarType ))->AddRecord(
1381 new XclExpExtConditionalFormatting( *pParent, aExtEntries, aScRanges));
1385 XclExpCondfmt::~XclExpCondfmt()
1389 bool XclExpCondfmt::IsValidForBinary() const
1391 // ccf (2 bytes): An unsigned integer that specifies the count of CF records that follow this
1392 // record. MUST be greater than or equal to 0x0001, and less than or equal to 0x0003.
1394 SAL_WARN_IF( maCFList.GetSize() > 3, "sc.filter", "More than 3 conditional filters for cell(s), won't export");
1396 return !maCFList.IsEmpty() && maCFList.GetSize() <= 3 && !maXclRanges.empty();
1399 bool XclExpCondfmt::IsValidForXml() const
1401 return !maCFList.IsEmpty() && !maXclRanges.empty();
1404 void XclExpCondfmt::Save( XclExpStream& rStrm )
1406 if( IsValidForBinary() )
1408 XclExpRecord::Save( rStrm );
1409 maCFList.Save( rStrm );
1413 void XclExpCondfmt::WriteBody( XclExpStream& rStrm )
1415 OSL_ENSURE( !maCFList.IsEmpty(), "XclExpCondfmt::WriteBody - no CF records to write" );
1416 OSL_ENSURE( !maXclRanges.empty(), "XclExpCondfmt::WriteBody - no cell ranges found" );
1418 rStrm << static_cast< sal_uInt16 >( maCFList.GetSize() )
1419 << sal_uInt16( 1 )
1420 << maXclRanges.GetEnclosingRange()
1421 << maXclRanges;
1424 void XclExpCondfmt::SaveXml( XclExpXmlStream& rStrm )
1426 if( !IsValidForXml() )
1427 return;
1429 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1430 rWorksheet->startElement( XML_conditionalFormatting,
1431 XML_sqref, msSeqRef
1432 // OOXTODO: XML_pivot
1435 maCFList.SaveXml( rStrm );
1437 rWorksheet->endElement( XML_conditionalFormatting );
1440 XclExpColorScale::XclExpColorScale( const XclExpRoot& rRoot, const ScColorScaleFormat& rFormat, sal_Int32 nPriority ):
1441 XclExpRoot( rRoot ),
1442 mnPriority( nPriority )
1444 const ScRange & rRange = rFormat.GetRange().front();
1445 ScAddress aAddr = rRange.aStart;
1446 for(const auto& rxColorScaleEntry : rFormat)
1448 // exact position is not important, we allow only absolute refs
1450 XclExpCfvoList::RecordRefType xCfvo( new XclExpCfvo( GetRoot(), *rxColorScaleEntry, aAddr ) );
1451 maCfvoList.AppendRecord( xCfvo );
1452 XclExpColScaleColList::RecordRefType xClo( new XclExpColScaleCol( GetRoot(), rxColorScaleEntry->GetColor() ) );
1453 maColList.AppendRecord( xClo );
1457 void XclExpColorScale::SaveXml( XclExpXmlStream& rStrm )
1459 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1461 rWorksheet->startElement( XML_cfRule,
1462 XML_type, "colorScale",
1463 XML_priority, OString::number(mnPriority + 1) );
1465 rWorksheet->startElement(XML_colorScale);
1467 maCfvoList.SaveXml(rStrm);
1468 maColList.SaveXml(rStrm);
1470 rWorksheet->endElement( XML_colorScale );
1472 rWorksheet->endElement( XML_cfRule );
1475 XclExpDataBar::XclExpDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, sal_Int32 nPriority, const OString& rGUID):
1476 XclExpRoot( rRoot ),
1477 mrFormat( rFormat ),
1478 mnPriority( nPriority ),
1479 maGUID(rGUID)
1481 const ScRange & rRange = rFormat.GetRange().front();
1482 ScAddress aAddr = rRange.aStart;
1483 // exact position is not important, we allow only absolute refs
1484 mpCfvoLowerLimit.reset(
1485 new XclExpCfvo(GetRoot(), *mrFormat.GetDataBarData()->mpLowerLimit, aAddr, true));
1486 mpCfvoUpperLimit.reset(
1487 new XclExpCfvo(GetRoot(), *mrFormat.GetDataBarData()->mpUpperLimit, aAddr, false));
1489 mpCol.reset( new XclExpColScaleCol( GetRoot(), mrFormat.GetDataBarData()->maPositiveColor ) );
1492 void XclExpDataBar::SaveXml( XclExpXmlStream& rStrm )
1494 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1496 rWorksheet->startElement( XML_cfRule,
1497 XML_type, "dataBar",
1498 XML_priority, OString::number(mnPriority + 1) );
1500 rWorksheet->startElement( XML_dataBar,
1501 XML_showValue, ToPsz10(!mrFormat.GetDataBarData()->mbOnlyBar),
1502 XML_minLength, OString::number(sal_uInt32(mrFormat.GetDataBarData()->mnMinLength)),
1503 XML_maxLength, OString::number(sal_uInt32(mrFormat.GetDataBarData()->mnMaxLength)) );
1505 mpCfvoLowerLimit->SaveXml(rStrm);
1506 mpCfvoUpperLimit->SaveXml(rStrm);
1507 mpCol->SaveXml(rStrm);
1509 rWorksheet->endElement( XML_dataBar );
1511 // extLst entries for Excel 2010 and 2013
1512 rWorksheet->startElement(XML_extLst);
1513 rWorksheet->startElement(XML_ext,
1514 FSNS(XML_xmlns, XML_x14), rStrm.getNamespaceURL(OOX_NS(xls14Lst)),
1515 XML_uri, "{B025F937-C7B1-47D3-B67F-A62EFF666E3E}");
1517 rWorksheet->startElementNS( XML_x14, XML_id );
1518 rWorksheet->write(maGUID);
1519 rWorksheet->endElementNS( XML_x14, XML_id );
1521 rWorksheet->endElement( XML_ext );
1522 rWorksheet->endElement( XML_extLst );
1524 rWorksheet->endElement( XML_cfRule );
1527 XclExpIconSet::XclExpIconSet( const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, sal_Int32 nPriority ):
1528 XclExpRoot( rRoot ),
1529 mrFormat( rFormat ),
1530 mnPriority( nPriority )
1532 const ScRange & rRange = rFormat.GetRange().front();
1533 ScAddress aAddr = rRange.aStart;
1534 for (auto const& itr : rFormat)
1536 // exact position is not important, we allow only absolute refs
1538 XclExpCfvoList::RecordRefType xCfvo( new XclExpCfvo( GetRoot(), *itr, aAddr ) );
1539 maCfvoList.AppendRecord( xCfvo );
1543 void XclExpIconSet::SaveXml( XclExpXmlStream& rStrm )
1545 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1547 rWorksheet->startElement( XML_cfRule,
1548 XML_type, "iconSet",
1549 XML_priority, OString::number(mnPriority + 1) );
1551 const char* pIconSetName = ScIconSetFormat::getIconSetName(mrFormat.GetIconSetData()->eIconSetType);
1552 rWorksheet->startElement( XML_iconSet,
1553 XML_iconSet, pIconSetName,
1554 XML_showValue, sax_fastparser::UseIf("0", !mrFormat.GetIconSetData()->mbShowValue),
1555 XML_reverse, sax_fastparser::UseIf("1", mrFormat.GetIconSetData()->mbReverse));
1557 maCfvoList.SaveXml( rStrm );
1559 rWorksheet->endElement( XML_iconSet );
1560 rWorksheet->endElement( XML_cfRule );
1563 XclExpCondFormatBuffer::XclExpCondFormatBuffer( const XclExpRoot& rRoot, const XclExtLstRef& xExtLst ) :
1564 XclExpRoot( rRoot )
1566 if( const ScConditionalFormatList* pCondFmtList = GetDoc().GetCondFormList(GetCurrScTab()) )
1568 sal_Int32 nIndex = 0;
1569 for( const auto& rxCondFmt : *pCondFmtList)
1571 XclExpCondfmtList::RecordRefType xCondfmtRec( new XclExpCondfmt( GetRoot(), *rxCondFmt, xExtLst, nIndex ));
1572 if( xCondfmtRec->IsValidForXml() )
1573 maCondfmtList.AppendRecord( xCondfmtRec );
1578 void XclExpCondFormatBuffer::Save( XclExpStream& rStrm )
1580 maCondfmtList.Save( rStrm );
1583 void XclExpCondFormatBuffer::SaveXml( XclExpXmlStream& rStrm )
1585 maCondfmtList.SaveXml( rStrm );
1588 // Validation =================================================================
1590 namespace {
1592 /** Writes a formula for the DV record. */
1593 void lclWriteDvFormula( XclExpStream& rStrm, const XclTokenArray* pXclTokArr )
1595 sal_uInt16 nFmlaSize = pXclTokArr ? pXclTokArr->GetSize() : 0;
1596 rStrm << nFmlaSize << sal_uInt16( 0 );
1597 if( pXclTokArr )
1598 pXclTokArr->WriteArray( rStrm );
1601 /** Writes a formula for the DV record, based on a single string. */
1602 void lclWriteDvFormula( XclExpStream& rStrm, const XclExpString& rString )
1604 // fake a formula with a single tStr token
1605 rStrm << static_cast< sal_uInt16 >( rString.GetSize() + 1 )
1606 << sal_uInt16( 0 )
1607 << EXC_TOKID_STR
1608 << rString;
1611 const char* lcl_GetValidationType( sal_uInt32 nFlags )
1613 switch( nFlags & EXC_DV_MODE_MASK )
1615 case EXC_DV_MODE_ANY: return "none";
1616 case EXC_DV_MODE_WHOLE: return "whole";
1617 case EXC_DV_MODE_DECIMAL: return "decimal";
1618 case EXC_DV_MODE_LIST: return "list";
1619 case EXC_DV_MODE_DATE: return "date";
1620 case EXC_DV_MODE_TIME: return "time";
1621 case EXC_DV_MODE_TEXTLEN: return "textLength";
1622 case EXC_DV_MODE_CUSTOM: return "custom";
1624 return nullptr;
1627 const char* lcl_GetOperatorType( sal_uInt32 nFlags )
1629 switch( nFlags & EXC_DV_COND_MASK )
1631 case EXC_DV_COND_BETWEEN: return "between";
1632 case EXC_DV_COND_NOTBETWEEN: return "notBetween";
1633 case EXC_DV_COND_EQUAL: return "equal";
1634 case EXC_DV_COND_NOTEQUAL: return "notEqual";
1635 case EXC_DV_COND_GREATER: return "greaterThan";
1636 case EXC_DV_COND_LESS: return "lessThan";
1637 case EXC_DV_COND_EQGREATER: return "greaterThanOrEqual";
1638 case EXC_DV_COND_EQLESS: return "lessThanOrEqual";
1640 return nullptr;
1643 const char* lcl_GetErrorType( sal_uInt32 nFlags )
1645 switch (nFlags & EXC_DV_ERROR_MASK)
1647 case EXC_DV_ERROR_STOP: return "stop";
1648 case EXC_DV_ERROR_WARNING: return "warning";
1649 case EXC_DV_ERROR_INFO: return "information";
1651 return nullptr;
1654 void lcl_SetValidationText(const OUString& rText, XclExpString& rValidationText)
1656 if ( !rText.isEmpty() )
1658 // maximum length allowed in Excel is 255 characters
1659 if ( rText.getLength() > 255 )
1661 OUStringBuffer aBuf( rText );
1662 rValidationText.Assign(
1663 comphelper::string::truncateToLength(aBuf, 255).makeStringAndClear() );
1665 else
1666 rValidationText.Assign( rText );
1668 else
1669 rValidationText.Assign( '\0' );
1672 } // namespace
1674 XclExpDV::XclExpDV( const XclExpRoot& rRoot, sal_uLong nScHandle ) :
1675 XclExpRecord( EXC_ID_DV ),
1676 XclExpRoot( rRoot ),
1677 mnFlags( 0 ),
1678 mnScHandle( nScHandle )
1680 if( const ScValidationData* pValData = GetDoc().GetValidationEntry( mnScHandle ) )
1682 // prompt box - empty string represented by single NUL character
1683 OUString aTitle, aText;
1684 bool bShowPrompt = pValData->GetInput( aTitle, aText );
1685 lcl_SetValidationText(aTitle, maPromptTitle);
1686 lcl_SetValidationText(aText, maPromptText);
1688 // error box - empty string represented by single NUL character
1689 ScValidErrorStyle eScErrorStyle;
1690 bool bShowError = pValData->GetErrMsg( aTitle, aText, eScErrorStyle );
1691 lcl_SetValidationText(aTitle, maErrorTitle);
1692 lcl_SetValidationText(aText, maErrorText);
1694 // flags
1695 switch( pValData->GetDataMode() )
1697 case SC_VALID_ANY: mnFlags |= EXC_DV_MODE_ANY; break;
1698 case SC_VALID_WHOLE: mnFlags |= EXC_DV_MODE_WHOLE; break;
1699 case SC_VALID_DECIMAL: mnFlags |= EXC_DV_MODE_DECIMAL; break;
1700 case SC_VALID_LIST: mnFlags |= EXC_DV_MODE_LIST; break;
1701 case SC_VALID_DATE: mnFlags |= EXC_DV_MODE_DATE; break;
1702 case SC_VALID_TIME: mnFlags |= EXC_DV_MODE_TIME; break;
1703 case SC_VALID_TEXTLEN: mnFlags |= EXC_DV_MODE_TEXTLEN; break;
1704 case SC_VALID_CUSTOM: mnFlags |= EXC_DV_MODE_CUSTOM; break;
1705 default: OSL_FAIL( "XclExpDV::XclExpDV - unknown mode" );
1708 switch( pValData->GetOperation() )
1710 case ScConditionMode::NONE:
1711 case ScConditionMode::Equal: mnFlags |= EXC_DV_COND_EQUAL; break;
1712 case ScConditionMode::Less: mnFlags |= EXC_DV_COND_LESS; break;
1713 case ScConditionMode::Greater: mnFlags |= EXC_DV_COND_GREATER; break;
1714 case ScConditionMode::EqLess: mnFlags |= EXC_DV_COND_EQLESS; break;
1715 case ScConditionMode::EqGreater: mnFlags |= EXC_DV_COND_EQGREATER; break;
1716 case ScConditionMode::NotEqual: mnFlags |= EXC_DV_COND_NOTEQUAL; break;
1717 case ScConditionMode::Between: mnFlags |= EXC_DV_COND_BETWEEN; break;
1718 case ScConditionMode::NotBetween: mnFlags |= EXC_DV_COND_NOTBETWEEN; break;
1719 default: OSL_FAIL( "XclExpDV::XclExpDV - unknown condition" );
1721 switch( eScErrorStyle )
1723 case SC_VALERR_STOP: mnFlags |= EXC_DV_ERROR_STOP; break;
1724 case SC_VALERR_WARNING: mnFlags |= EXC_DV_ERROR_WARNING; break;
1725 case SC_VALERR_INFO: mnFlags |= EXC_DV_ERROR_INFO; break;
1726 case SC_VALERR_MACRO:
1727 // set INFO for validity with macro call, delete title
1728 mnFlags |= EXC_DV_ERROR_INFO;
1729 maErrorTitle.Assign( '\0' ); // contains macro name
1730 break;
1731 default: OSL_FAIL( "XclExpDV::XclExpDV - unknown error style" );
1733 ::set_flag( mnFlags, EXC_DV_IGNOREBLANK, pValData->IsIgnoreBlank() );
1734 ::set_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN, pValData->GetListType() == css::sheet::TableValidationVisibility::INVISIBLE );
1735 ::set_flag( mnFlags, EXC_DV_SHOWPROMPT, bShowPrompt );
1736 ::set_flag( mnFlags, EXC_DV_SHOWERROR, bShowError );
1738 // formulas
1739 XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
1741 // first formula
1742 std::unique_ptr< ScTokenArray > xScTokArr = pValData->CreateFlatCopiedTokenArray( 0 );
1743 if (xScTokArr)
1745 if( pValData->GetDataMode() == SC_VALID_LIST )
1747 OUString aString;
1748 if( XclTokenArrayHelper::GetStringList( aString, *xScTokArr, '\n' ) )
1750 bool bList = false;
1751 OUStringBuffer sListBuf;
1752 OUStringBuffer sFormulaBuf;
1753 sFormulaBuf.append( '"' );
1754 /* Formula is a list of string tokens -> build the Excel string.
1755 Data validity is BIFF8 only (important for the XclExpString object).
1756 Excel uses the NUL character as string list separator. */
1757 mxString1.reset( new XclExpString( XclStrFlags::EightBitLength ) );
1758 if (!aString.isEmpty())
1760 sal_Int32 nStringIx = 0;
1761 for(;;)
1763 const OUString aToken( aString.getToken( 0, '\n', nStringIx ) );
1764 if (aToken.indexOf(",") != -1)
1766 sListBuf.append('"');
1767 sListBuf.append(aToken);
1768 sListBuf.append('"');
1769 bList = true;
1771 else
1772 sListBuf.append(aToken);
1773 mxString1->Append( aToken );
1774 sFormulaBuf.append( aToken );
1775 if (nStringIx<0)
1776 break;
1777 mxString1->Append(OUString(u'\0'));
1778 sFormulaBuf.append( ',' );
1779 sListBuf.append( ',' );
1782 ::set_flag( mnFlags, EXC_DV_STRINGLIST );
1784 // maximum length allowed in Excel is 255 characters, and don't end with an empty token
1785 // It should be 8192 but Excel doesn't accept it for unknown reason
1786 // See also https://bugs.documentfoundation.org/show_bug.cgi?id=137167#c2 for more details
1787 sal_uInt32 nLen = sFormulaBuf.getLength();
1788 if( nLen > 256 ) // 255 + beginning quote mark
1790 nLen = 256;
1791 if( sFormulaBuf[nLen - 1] == ',' )
1792 --nLen;
1793 sFormulaBuf.truncate(nLen);
1796 sFormulaBuf.append( '"' );
1797 msFormula1 = sFormulaBuf.makeStringAndClear();
1798 if (bList)
1799 msList = sListBuf.makeStringAndClear();
1800 else
1801 sListBuf.remove(0, sListBuf.getLength());
1803 else
1805 /* All other formulas in validation are stored like conditional
1806 formatting formulas (with tRefN/tAreaN tokens as value or
1807 array class). But NOT the cell references and defined names
1808 in list validation - they are stored as reference class
1809 tokens... Example:
1810 1) Cell must be equal to A1 -> formula is =A1 -> writes tRefNV token
1811 2) List is taken from A1 -> formula is =A1 -> writes tRefNR token
1812 Formula compiler supports this by offering two different functions
1813 CreateDataValFormula() and CreateListValFormula(). */
1814 if(GetOutput() == EXC_OUTPUT_BINARY)
1815 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_LISTVAL, *xScTokArr );
1816 else
1817 msFormula1 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
1818 xScTokArr.get());
1821 else
1823 // no list validation -> convert the formula
1824 if(GetOutput() == EXC_OUTPUT_BINARY)
1825 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
1826 else
1827 msFormula1 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
1828 xScTokArr.get());
1832 // second formula
1833 xScTokArr = pValData->CreateFlatCopiedTokenArray( 1 );
1834 if (xScTokArr)
1836 if(GetOutput() == EXC_OUTPUT_BINARY)
1837 mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
1838 else
1839 msFormula2 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
1840 xScTokArr.get());
1843 else
1845 OSL_FAIL( "XclExpDV::XclExpDV - missing core data" );
1846 mnScHandle = ULONG_MAX;
1850 XclExpDV::~XclExpDV()
1854 void XclExpDV::InsertCellRange( const ScRange& rRange )
1856 maScRanges.Join( rRange );
1859 bool XclExpDV::Finalize()
1861 GetAddressConverter().ConvertRangeList( maXclRanges, maScRanges, true );
1862 return (mnScHandle != ULONG_MAX) && !maXclRanges.empty();
1865 void XclExpDV::WriteBody( XclExpStream& rStrm )
1867 // flags and strings
1868 rStrm << mnFlags << maPromptTitle << maErrorTitle << maPromptText << maErrorText;
1869 // condition formulas
1870 if( mxString1 )
1871 lclWriteDvFormula( rStrm, *mxString1 );
1872 else
1873 lclWriteDvFormula( rStrm, mxTokArr1.get() );
1874 lclWriteDvFormula( rStrm, mxTokArr2.get() );
1875 // cell ranges
1876 rStrm << maXclRanges;
1879 void XclExpDV::SaveXml( XclExpXmlStream& rStrm )
1881 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1882 rWorksheet->startElement( XML_dataValidation,
1883 XML_allowBlank, ToPsz( ::get_flag( mnFlags, EXC_DV_IGNOREBLANK ) ),
1884 XML_error, XESTRING_TO_PSZ( maErrorText ),
1885 XML_errorStyle, lcl_GetErrorType(mnFlags),
1886 XML_errorTitle, XESTRING_TO_PSZ( maErrorTitle ),
1887 // OOXTODO: XML_imeMode,
1888 XML_operator, lcl_GetOperatorType( mnFlags ),
1889 XML_prompt, XESTRING_TO_PSZ( maPromptText ),
1890 XML_promptTitle, XESTRING_TO_PSZ( maPromptTitle ),
1891 // showDropDown should have been showNoDropDown - check oox/xlsx import for details
1892 XML_showDropDown, ToPsz( ::get_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN ) ),
1893 XML_showErrorMessage, ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWERROR ) ),
1894 XML_showInputMessage, ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWPROMPT ) ),
1895 XML_sqref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maScRanges),
1896 XML_type, lcl_GetValidationType(mnFlags) );
1897 if (!msList.isEmpty())
1899 rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent),
1900 FSNS(XML_xmlns, XML_x12ac),rStrm.getNamespaceURL(OOX_NS(x12ac)),
1901 FSNS(XML_xmlns, XML_mc),rStrm.getNamespaceURL(OOX_NS(mce)));
1902 rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x12ac");
1903 rWorksheet->startElement(FSNS(XML_x12ac, XML_list));
1904 rWorksheet->writeEscaped(msList);
1905 rWorksheet->endElement(FSNS(XML_x12ac, XML_list));
1906 rWorksheet->endElement(FSNS(XML_mc, XML_Choice));
1907 rWorksheet->startElement(FSNS(XML_mc, XML_Fallback));
1908 rWorksheet->startElement(XML_formula1);
1909 rWorksheet->writeEscaped(msFormula1);
1910 rWorksheet->endElement(XML_formula1);
1911 rWorksheet->endElement(FSNS(XML_mc, XML_Fallback));
1912 rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent));
1914 if (msList.isEmpty() && !msFormula1.isEmpty())
1916 rWorksheet->startElement(XML_formula1);
1917 rWorksheet->writeEscaped( msFormula1 );
1918 rWorksheet->endElement( XML_formula1 );
1920 if( !msFormula2.isEmpty() )
1922 rWorksheet->startElement(XML_formula2);
1923 rWorksheet->writeEscaped( msFormula2 );
1924 rWorksheet->endElement( XML_formula2 );
1926 rWorksheet->endElement( XML_dataValidation );
1929 XclExpDval::XclExpDval( const XclExpRoot& rRoot ) :
1930 XclExpRecord( EXC_ID_DVAL, 18 ),
1931 XclExpRoot( rRoot )
1935 XclExpDval::~XclExpDval()
1939 void XclExpDval::InsertCellRange( const ScRange& rRange, sal_uLong nScHandle )
1941 if( GetBiff() == EXC_BIFF8 )
1943 XclExpDV& rDVRec = SearchOrCreateDv( nScHandle );
1944 rDVRec.InsertCellRange( rRange );
1948 void XclExpDval::Save( XclExpStream& rStrm )
1950 // check all records
1951 size_t nPos = maDVList.GetSize();
1952 while( nPos )
1954 --nPos; // backwards to keep nPos valid
1955 XclExpDVRef xDVRec = maDVList.GetRecord( nPos );
1956 if( !xDVRec->Finalize() )
1957 maDVList.RemoveRecord( nPos );
1960 // write the DVAL and the DV's
1961 if( !maDVList.IsEmpty() )
1963 XclExpRecord::Save( rStrm );
1964 maDVList.Save( rStrm );
1968 void XclExpDval::SaveXml( XclExpXmlStream& rStrm )
1970 if( maDVList.IsEmpty() )
1971 return;
1973 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1974 rWorksheet->startElement( XML_dataValidations,
1975 XML_count, OString::number(maDVList.GetSize())
1976 // OOXTODO: XML_disablePrompts,
1977 // OOXTODO: XML_xWindow,
1978 // OOXTODO: XML_yWindow
1980 maDVList.SaveXml( rStrm );
1981 rWorksheet->endElement( XML_dataValidations );
1984 XclExpDV& XclExpDval::SearchOrCreateDv( sal_uLong nScHandle )
1986 // test last found record
1987 if( mxLastFoundDV && (mxLastFoundDV->GetScHandle() == nScHandle) )
1988 return *mxLastFoundDV;
1990 // binary search
1991 size_t nCurrPos = 0;
1992 if( !maDVList.IsEmpty() )
1994 size_t nFirstPos = 0;
1995 size_t nLastPos = maDVList.GetSize() - 1;
1996 bool bLoop = true;
1997 sal_uLong nCurrScHandle = ::std::numeric_limits< sal_uLong >::max();
1998 while( (nFirstPos <= nLastPos) && bLoop )
2000 nCurrPos = (nFirstPos + nLastPos) / 2;
2001 mxLastFoundDV = maDVList.GetRecord( nCurrPos );
2002 nCurrScHandle = mxLastFoundDV->GetScHandle();
2003 if( nCurrScHandle == nScHandle )
2004 bLoop = false;
2005 else if( nCurrScHandle < nScHandle )
2006 nFirstPos = nCurrPos + 1;
2007 else if( nCurrPos )
2008 nLastPos = nCurrPos - 1;
2009 else // special case for nLastPos = -1
2010 bLoop = false;
2012 if( nCurrScHandle == nScHandle )
2013 return *mxLastFoundDV;
2014 else if( nCurrScHandle < nScHandle )
2015 ++nCurrPos;
2018 // create new DV record
2019 mxLastFoundDV = new XclExpDV( *this, nScHandle );
2020 maDVList.InsertRecord( mxLastFoundDV, nCurrPos );
2021 return *mxLastFoundDV;
2024 void XclExpDval::WriteBody( XclExpStream& rStrm )
2026 rStrm.WriteZeroBytes( 10 );
2027 rStrm << EXC_DVAL_NOOBJ << static_cast< sal_uInt32 >( maDVList.GetSize() );
2030 // Web Queries ================================================================
2032 XclExpWebQuery::XclExpWebQuery(
2033 const OUString& rRangeName,
2034 const OUString& rUrl,
2035 const OUString& rSource,
2036 sal_Int32 nRefrSecs ) :
2037 maDestRange( rRangeName ),
2038 maUrl( rUrl ),
2039 // refresh delay time: seconds -> minutes
2040 mnRefresh( ulimit_cast< sal_Int16 >( (nRefrSecs + 59) / 60 ) ),
2041 mbEntireDoc( false )
2043 // comma separated list of HTML table names or indexes
2044 OUString aNewTables;
2045 OUString aAppendTable;
2046 bool bExitLoop = false;
2047 if (!rSource.isEmpty())
2049 sal_Int32 nStringIx = 0;
2052 OUString aToken( rSource.getToken( 0, ';', nStringIx ) );
2053 mbEntireDoc = ScfTools::IsHTMLDocName( aToken );
2054 bExitLoop = mbEntireDoc || ScfTools::IsHTMLTablesName( aToken );
2055 if( !bExitLoop && ScfTools::GetHTMLNameFromName( aToken, aAppendTable ) )
2056 aNewTables = ScGlobal::addToken( aNewTables, aAppendTable, ',' );
2058 while (nStringIx>0 && !bExitLoop);
2061 if( !bExitLoop ) // neither HTML_all nor HTML_tables found
2063 if( !aNewTables.isEmpty() )
2064 mxQryTables.reset( new XclExpString( aNewTables ) );
2065 else
2066 mbEntireDoc = true;
2070 XclExpWebQuery::~XclExpWebQuery()
2074 void XclExpWebQuery::Save( XclExpStream& rStrm )
2076 OSL_ENSURE( !mbEntireDoc || !mxQryTables, "XclExpWebQuery::Save - illegal mode" );
2077 sal_uInt16 nFlags;
2079 // QSI record
2080 rStrm.StartRecord( EXC_ID_QSI, 10 + maDestRange.GetSize() );
2081 rStrm << EXC_QSI_DEFAULTFLAGS
2082 << sal_uInt16( 0x0010 )
2083 << sal_uInt16( 0x0012 )
2084 << sal_uInt32( 0x00000000 )
2085 << maDestRange;
2086 rStrm.EndRecord();
2088 // PARAMQRY record
2089 nFlags = 0;
2090 ::insert_value( nFlags, EXC_PQRYTYPE_WEBQUERY, 0, 3 );
2091 ::set_flag( nFlags, EXC_PQRY_WEBQUERY );
2092 ::set_flag( nFlags, EXC_PQRY_TABLES, !mbEntireDoc );
2093 rStrm.StartRecord( EXC_ID_PQRY, 12 );
2094 rStrm << nFlags
2095 << sal_uInt16( 0x0000 )
2096 << sal_uInt16( 0x0001 );
2097 rStrm.WriteZeroBytes( 6 );
2098 rStrm.EndRecord();
2100 // WQSTRING record
2101 rStrm.StartRecord( EXC_ID_WQSTRING, maUrl.GetSize() );
2102 rStrm << maUrl;
2103 rStrm.EndRecord();
2105 // unknown record 0x0802
2106 rStrm.StartRecord( EXC_ID_0802, 16 + maDestRange.GetSize() );
2107 rStrm << EXC_ID_0802; // repeated record id ?!?
2108 rStrm.WriteZeroBytes( 6 );
2109 rStrm << sal_uInt16( 0x0003 )
2110 << sal_uInt32( 0x00000000 )
2111 << sal_uInt16( 0x0010 )
2112 << maDestRange;
2113 rStrm.EndRecord();
2115 // WEBQRYSETTINGS record
2116 nFlags = mxQryTables ? EXC_WQSETT_SPECTABLES : EXC_WQSETT_ALL;
2117 rStrm.StartRecord( EXC_ID_WQSETT, 28 );
2118 rStrm << EXC_ID_WQSETT // repeated record id ?!?
2119 << sal_uInt16( 0x0000 )
2120 << sal_uInt16( 0x0004 )
2121 << sal_uInt16( 0x0000 )
2122 << EXC_WQSETT_DEFAULTFLAGS
2123 << nFlags;
2124 rStrm.WriteZeroBytes( 10 );
2125 rStrm << mnRefresh // refresh delay in minutes
2126 << EXC_WQSETT_FORMATFULL
2127 << sal_uInt16( 0x0000 );
2128 rStrm.EndRecord();
2130 // WEBQRYTABLES record
2131 if( mxQryTables )
2133 rStrm.StartRecord( EXC_ID_WQTABLES, 4 + mxQryTables->GetSize() );
2134 rStrm << EXC_ID_WQTABLES // repeated record id ?!?
2135 << sal_uInt16( 0x0000 )
2136 << *mxQryTables; // comma separated list of source tables
2137 rStrm.EndRecord();
2141 XclExpWebQueryBuffer::XclExpWebQueryBuffer( const XclExpRoot& rRoot )
2143 SCTAB nScTab = rRoot.GetCurrScTab();
2144 SfxObjectShell* pShell = rRoot.GetDocShell();
2145 if( !pShell ) return;
2146 ScfPropertySet aModelProp( pShell->GetModel() );
2147 if( !aModelProp.Is() ) return;
2149 Reference< XAreaLinks > xAreaLinks;
2150 aModelProp.GetProperty( xAreaLinks, SC_UNO_AREALINKS );
2151 if( !xAreaLinks.is() ) return;
2153 for( sal_Int32 nIndex = 0, nCount = xAreaLinks->getCount(); nIndex < nCount; ++nIndex )
2155 Reference< XAreaLink > xAreaLink( xAreaLinks->getByIndex( nIndex ), UNO_QUERY );
2156 if( xAreaLink.is() )
2158 CellRangeAddress aDestRange( xAreaLink->getDestArea() );
2159 if( static_cast< SCTAB >( aDestRange.Sheet ) == nScTab )
2161 ScfPropertySet aLinkProp( xAreaLink );
2162 OUString aFilter;
2163 if( aLinkProp.GetProperty( aFilter, SC_UNONAME_FILTER ) &&
2164 (aFilter == EXC_WEBQRY_FILTER) )
2166 // get properties
2167 OUString /*aFilterOpt,*/ aUrl;
2168 sal_Int32 nRefresh = 0;
2170 // aLinkProp.GetProperty( aFilterOpt, SC_UNONAME_FILTOPT );
2171 aLinkProp.GetProperty( aUrl, SC_UNONAME_LINKURL );
2172 aLinkProp.GetProperty( nRefresh, SC_UNONAME_REFDELAY );
2174 OUString aAbsDoc( ScGlobal::GetAbsDocName( aUrl, pShell ) );
2175 INetURLObject aUrlObj( aAbsDoc );
2176 OUString aWebQueryUrl( aUrlObj.getFSysPath( FSysStyle::Dos ) );
2177 if( aWebQueryUrl.isEmpty() )
2178 aWebQueryUrl = aAbsDoc;
2180 // find range or create a new range
2181 OUString aRangeName;
2182 ScRange aScDestRange;
2183 ScUnoConversion::FillScRange( aScDestRange, aDestRange );
2184 if( const ScRangeData* pRangeData = rRoot.GetNamedRanges().findByRange( aScDestRange ) )
2186 aRangeName = pRangeData->GetName();
2188 else
2190 XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
2191 XclExpNameManager& rNameMgr = rRoot.GetNameManager();
2193 // create a new unique defined name containing the range
2194 XclTokenArrayRef xTokArr = rFmlaComp.CreateFormula( EXC_FMLATYPE_WQUERY, aScDestRange );
2195 sal_uInt16 nNameIdx = rNameMgr.InsertUniqueName( aUrlObj.getBase(), xTokArr, nScTab );
2196 aRangeName = rNameMgr.GetOrigName( nNameIdx );
2199 // create and store the web query record
2200 if( !aRangeName.isEmpty() )
2201 AppendNewRecord( new XclExpWebQuery(
2202 aRangeName, aWebQueryUrl, xAreaLink->getSourceArea(), nRefresh ) );
2209 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */