Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / excel / xecontent.cxx
blobd1fa70c52dd5f77ed74cf33be7c18f95d50356e2
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 <com/sun/star/sheet/XAreaLinks.hpp>
26 #include <com/sun/star/sheet/XAreaLink.hpp>
27 #include <com/sun/star/sheet/TableValidationVisibility.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <sfx2/objsh.hxx>
30 #include <tools/urlobj.hxx>
31 #include <formula/grammar.hxx>
32 #include <scitems.hxx>
33 #include <editeng/flditem.hxx>
34 #include <document.hxx>
35 #include <validat.hxx>
36 #include <unonames.hxx>
37 #include <convuno.hxx>
38 #include <rangenam.hxx>
39 #include <tokenarray.hxx>
40 #include <stlpool.hxx>
41 #include <patattr.hxx>
42 #include <fapihelper.hxx>
43 #include <xehelper.hxx>
44 #include <xestyle.hxx>
45 #include <xename.hxx>
46 #include <xlcontent.hxx>
47 #include <xltools.hxx>
48 #include <xeformula.hxx>
49 #include <rtl/uuid.h>
50 #include <sal/log.hxx>
51 #include <oox/export/utils.hxx>
52 #include <oox/token/namespaces.hxx>
53 #include <oox/token/relationship.hxx>
55 using namespace ::oox;
57 using ::com::sun::star::uno::Reference;
58 using ::com::sun::star::uno::Any;
59 using ::com::sun::star::uno::UNO_QUERY;
60 using ::com::sun::star::table::CellRangeAddress;
61 using ::com::sun::star::sheet::XAreaLinks;
62 using ::com::sun::star::sheet::XAreaLink;
64 // Shared string table ========================================================
66 /** A single string entry in the hash table. */
67 struct XclExpHashEntry
69 const XclExpString* mpString; /// Pointer to the string (no ownership).
70 sal_uInt32 mnSstIndex; /// The SST index of this string.
71 explicit XclExpHashEntry( const XclExpString* pString, sal_uInt32 nSstIndex ) :
72 mpString( pString ), mnSstIndex( nSstIndex ) {}
75 /** Function object for strict weak ordering. */
76 struct XclExpHashEntrySWO
78 bool operator()( const XclExpHashEntry& rLeft, const XclExpHashEntry& rRight ) const
79 { return *rLeft.mpString < *rRight.mpString; }
82 /** Implementation of the SST export.
83 @descr Stores all passed strings in a hash table and prevents repeated
84 insertion of equal strings. */
85 class XclExpSstImpl
87 public:
88 explicit XclExpSstImpl();
90 /** Inserts the passed string, if not already inserted, and returns the unique SST index. */
91 sal_uInt32 Insert( XclExpStringRef xString );
93 /** Writes the complete SST and EXTSST records. */
94 void Save( XclExpStream& rStrm );
95 void SaveXml( XclExpXmlStream& rStrm );
97 private:
98 typedef ::std::vector< XclExpHashEntry > XclExpHashVec;
99 typedef ::std::vector< XclExpHashVec > XclExpHashTab;
101 std::vector< XclExpStringRef > maStringVector; /// List of unique strings (in SST ID order).
102 XclExpHashTab maHashTab; /// Hashed table that manages string pointers.
103 sal_uInt32 mnTotal; /// Total count of strings (including doubles).
104 sal_uInt32 mnSize; /// Size of the SST (count of unique strings).
107 const sal_uInt32 EXC_SST_HASHTABLE_SIZE = 2048;
109 XclExpSstImpl::XclExpSstImpl() :
110 maHashTab( EXC_SST_HASHTABLE_SIZE ),
111 mnTotal( 0 ),
112 mnSize( 0 )
116 sal_uInt32 XclExpSstImpl::Insert( XclExpStringRef xString )
118 OSL_ENSURE( xString.get(), "XclExpSstImpl::Insert - empty pointer not allowed" );
119 if( !xString.get() )
120 xString.reset( new XclExpString );
122 ++mnTotal;
123 sal_uInt32 nSstIndex = 0;
125 // calculate hash value in range [0,EXC_SST_HASHTABLE_SIZE)
126 sal_uInt16 nHash = xString->GetHash();
127 nHash = (nHash ^ (nHash / EXC_SST_HASHTABLE_SIZE)) % EXC_SST_HASHTABLE_SIZE;
129 XclExpHashVec& rVec = maHashTab[ nHash ];
130 XclExpHashEntry aEntry( xString.get(), mnSize );
131 XclExpHashVec::iterator aIt = ::std::lower_bound( rVec.begin(), rVec.end(), aEntry, XclExpHashEntrySWO() );
132 if( (aIt == rVec.end()) || (*aIt->mpString != *xString) )
134 nSstIndex = mnSize;
135 maStringVector.push_back( xString );
136 rVec.insert( aIt, aEntry );
137 ++mnSize;
139 else
141 nSstIndex = aIt->mnSstIndex;
144 return nSstIndex;
147 void XclExpSstImpl::Save( XclExpStream& rStrm )
149 if( maStringVector.empty() )
150 return;
152 SvMemoryStream aExtSst( 8192 );
154 sal_uInt32 nBucket = mnSize;
155 while( nBucket > 0x0100 )
156 nBucket /= 2;
158 sal_uInt16 nPerBucket = llimit_cast< sal_uInt16 >( nBucket, 8 );
159 sal_uInt16 nBucketIndex = 0;
161 // *** write the SST record ***
163 rStrm.StartRecord( EXC_ID_SST, 8 );
165 rStrm << mnTotal << mnSize;
166 for (auto const& elem : maStringVector)
168 if( !nBucketIndex )
170 // write bucket info before string to get correct record position
171 sal_uInt32 nStrmPos = static_cast< sal_uInt32 >( rStrm.GetSvStreamPos() );
172 sal_uInt16 nRecPos = rStrm.GetRawRecPos() + 4;
173 aExtSst.WriteUInt32( nStrmPos ) // stream position
174 .WriteUInt16( nRecPos ) // position from start of SST or CONTINUE
175 .WriteUInt16( 0 ); // reserved
178 rStrm << *elem;
180 if( ++nBucketIndex == nPerBucket )
181 nBucketIndex = 0;
184 rStrm.EndRecord();
186 // *** write the EXTSST record ***
188 rStrm.StartRecord( EXC_ID_EXTSST, 0 );
190 rStrm << nPerBucket;
191 rStrm.SetSliceSize( 8 ); // size of one bucket info
192 aExtSst.Seek( STREAM_SEEK_TO_BEGIN );
193 rStrm.CopyFromStream( aExtSst );
195 rStrm.EndRecord();
198 void XclExpSstImpl::SaveXml( XclExpXmlStream& rStrm )
200 if( maStringVector.empty() )
201 return;
203 sax_fastparser::FSHelperPtr pSst = rStrm.CreateOutputStream(
204 "xl/sharedStrings.xml",
205 "sharedStrings.xml",
206 rStrm.GetCurrentStream()->getOutputStream(),
207 "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
208 OUStringToOString(oox::getRelationship(Relationship::SHAREDSTRINGS), RTL_TEXTENCODING_UTF8).getStr());
209 rStrm.PushStream( pSst );
211 pSst->startElement( XML_sst,
212 XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
213 XML_count, OString::number(mnTotal),
214 XML_uniqueCount, OString::number(mnSize) );
216 for (auto const& elem : maStringVector)
218 pSst->startElement(XML_si);
219 elem->WriteXml( rStrm );
220 pSst->endElement( XML_si );
223 pSst->endElement( XML_sst );
225 rStrm.PopStream();
228 XclExpSst::XclExpSst() :
229 mxImpl( new XclExpSstImpl )
233 XclExpSst::~XclExpSst()
237 sal_uInt32 XclExpSst::Insert( const XclExpStringRef& xString )
239 return mxImpl->Insert( xString );
242 void XclExpSst::Save( XclExpStream& rStrm )
244 mxImpl->Save( rStrm );
247 void XclExpSst::SaveXml( XclExpXmlStream& rStrm )
249 mxImpl->SaveXml( rStrm );
252 // Merged cells ===============================================================
254 XclExpMergedcells::XclExpMergedcells( const XclExpRoot& rRoot ) :
255 XclExpRoot( rRoot )
259 void XclExpMergedcells::AppendRange( const ScRange& rRange, sal_uInt32 nBaseXFId )
261 if( GetBiff() == EXC_BIFF8 )
263 maMergedRanges.push_back( rRange );
264 maBaseXFIds.push_back( nBaseXFId );
268 sal_uInt32 XclExpMergedcells::GetBaseXFId( const ScAddress& rPos ) const
270 OSL_ENSURE( maBaseXFIds.size() == maMergedRanges.size(), "XclExpMergedcells::GetBaseXFId - invalid lists" );
271 ScfUInt32Vec::const_iterator aIt = maBaseXFIds.begin();
272 ScRangeList& rNCRanges = const_cast< ScRangeList& >( maMergedRanges );
273 for ( size_t i = 0, nRanges = rNCRanges.size(); i < nRanges; ++i, ++aIt )
275 const ScRange & rScRange = rNCRanges[ i ];
276 if( rScRange.In( rPos ) )
277 return *aIt;
279 return EXC_XFID_NOTFOUND;
282 void XclExpMergedcells::Save( XclExpStream& rStrm )
284 if( GetBiff() == EXC_BIFF8 )
286 XclRangeList aXclRanges;
287 GetAddressConverter().ConvertRangeList( aXclRanges, maMergedRanges, true );
288 size_t nFirstRange = 0;
289 size_t nRemainingRanges = aXclRanges.size();
290 while( nRemainingRanges > 0 )
292 size_t nRangeCount = ::std::min< size_t >( nRemainingRanges, EXC_MERGEDCELLS_MAXCOUNT );
293 rStrm.StartRecord( EXC_ID_MERGEDCELLS, 2 + 8 * nRangeCount );
294 aXclRanges.WriteSubList( rStrm, nFirstRange, nRangeCount );
295 rStrm.EndRecord();
296 nFirstRange += nRangeCount;
297 nRemainingRanges -= nRangeCount;
302 void XclExpMergedcells::SaveXml( XclExpXmlStream& rStrm )
304 size_t nCount = maMergedRanges.size();
305 if( !nCount )
306 return;
307 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
308 rWorksheet->startElement(XML_mergeCells, XML_count, OString::number(nCount));
309 for( size_t i = 0; i < nCount; ++i )
311 const ScRange & rRange = maMergedRanges[ i ];
312 rWorksheet->singleElement(XML_mergeCell, XML_ref,
313 XclXmlUtils::ToOString(&rStrm.GetRoot().GetDoc(), rRange));
315 rWorksheet->endElement( XML_mergeCells );
318 // Hyperlinks =================================================================
320 XclExpHyperlink::XclExpHyperlink( const XclExpRoot& rRoot, const SvxURLField& rUrlField, const ScAddress& rScPos ) :
321 XclExpRecord( EXC_ID_HLINK ),
322 maScPos( rScPos ),
323 mxVarData( new SvMemoryStream ),
324 mnFlags( 0 )
326 const OUString& rUrl = rUrlField.GetURL();
327 const OUString& rRepr = rUrlField.GetRepresentation();
328 INetURLObject aUrlObj( rUrl );
329 const INetProtocol eProtocol = aUrlObj.GetProtocol();
330 bool bWithRepr = !rRepr.isEmpty();
331 XclExpStream aXclStrm( *mxVarData, rRoot ); // using in raw write mode.
333 // description
334 if( bWithRepr )
336 XclExpString aDescr( rRepr, XclStrFlags::ForceUnicode, 255 );
337 aXclStrm << sal_uInt32( aDescr.Len() + 1 ); // string length + 1 trailing zero word
338 aDescr.WriteBuffer( aXclStrm ); // NO flags
339 aXclStrm << sal_uInt16( 0 );
341 mnFlags |= EXC_HLINK_DESCR;
342 m_Repr = rRepr;
345 // file link or URL
346 if( eProtocol == INetProtocol::File || eProtocol == INetProtocol::Smb )
348 sal_uInt16 nLevel;
349 bool bRel;
350 /* TODO: should we differentiate between BIFF and OOXML and write IURI
351 * encoded for OOXML? */
352 OUString aFileName( BuildFileName( nLevel, bRel, rUrl, rRoot, false ) );
354 if( eProtocol == INetProtocol::Smb )
356 // #n382718# (and #n261623#) Convert smb notation to '\\'
357 aFileName = aUrlObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
358 aFileName = aFileName.copy(4); // skip the 'smb:' part
359 aFileName = aFileName.replace('/', '\\');
362 if( !bRel )
363 mnFlags |= EXC_HLINK_ABS;
364 mnFlags |= EXC_HLINK_BODY;
366 OString aAsciiLink(OUStringToOString(aFileName,
367 rRoot.GetTextEncoding()));
368 XclExpString aLink( aFileName, XclStrFlags::ForceUnicode, 255 );
369 aXclStrm << XclTools::maGuidFileMoniker
370 << nLevel
371 << sal_uInt32( aAsciiLink.getLength() + 1 ); // string length + 1 trailing zero byte
372 aXclStrm.Write( aAsciiLink.getStr(), aAsciiLink.getLength() );
373 aXclStrm << sal_uInt8( 0 )
374 << sal_uInt32( 0xDEADFFFF );
375 aXclStrm.WriteZeroBytes( 20 );
376 aXclStrm << sal_uInt32( aLink.GetBufferSize() + 6 )
377 << sal_uInt32( aLink.GetBufferSize() ) // byte count, not string length
378 << sal_uInt16( 0x0003 );
379 aLink.WriteBuffer( aXclStrm ); // NO flags
381 if (m_Repr.isEmpty())
382 m_Repr = aFileName;
384 msTarget = XclXmlUtils::ToOUString( aLink );
386 if( bRel )
388 for( int i = 0; i < nLevel; ++i )
389 msTarget = "../" + msTarget;
391 else
393 // ooxml expects the file:/// part appended ( or at least
394 // ms2007 does, ms2010 is more tolerant )
395 msTarget = "file:///" + msTarget;
398 else if( eProtocol != INetProtocol::NotValid )
400 XclExpString aUrl( aUrlObj.GetURLNoMark(), XclStrFlags::ForceUnicode, 255 );
401 aXclStrm << XclTools::maGuidUrlMoniker
402 << sal_uInt32( aUrl.GetBufferSize() + 2 ); // byte count + 1 trailing zero word
403 aUrl.WriteBuffer( aXclStrm ); // NO flags
404 aXclStrm << sal_uInt16( 0 );
406 mnFlags |= EXC_HLINK_BODY | EXC_HLINK_ABS;
407 if (m_Repr.isEmpty())
408 m_Repr = rUrl;
410 msTarget = XclXmlUtils::ToOUString( aUrl );
412 else if( !rUrl.isEmpty() && rUrl[0] == '#' ) // hack for #89066#
414 OUString aTextMark( rUrl.copy( 1 ) );
416 sal_Int32 nSepPos = aTextMark.lastIndexOf( '!' );
417 sal_Int32 nPointPos = aTextMark.lastIndexOf( '.' );
418 // last dot is the separator, if there is no ! after it
419 if(nSepPos < nPointPos)
421 nSepPos = nPointPos;
422 aTextMark = aTextMark.replaceAt( nSepPos, 1, "!" );
425 if(nSepPos != -1)
427 OUString aSheetName( aTextMark.copy(0, nSepPos));
429 if ( aSheetName.indexOf(' ') != -1 && aSheetName[0] != '\'')
431 aTextMark = "'" + aTextMark.replaceAt(nSepPos, 0, "'");
435 mxTextMark.reset( new XclExpString( aTextMark, XclStrFlags::ForceUnicode, 255 ) );
438 // text mark
439 if( !mxTextMark.get() && aUrlObj.HasMark() )
440 mxTextMark.reset( new XclExpString( aUrlObj.GetMark(), XclStrFlags::ForceUnicode, 255 ) );
442 if( mxTextMark.get() )
444 aXclStrm << sal_uInt32( mxTextMark->Len() + 1 ); // string length + 1 trailing zero word
445 mxTextMark->WriteBuffer( aXclStrm ); // NO flags
446 aXclStrm << sal_uInt16( 0 );
448 mnFlags |= EXC_HLINK_MARK;
450 OUString location = XclXmlUtils::ToOUString(*mxTextMark);
451 if (!location.isEmpty() && msTarget.endsWith("#" + location))
452 msTarget = msTarget.copy(0, msTarget.getLength() - location.getLength() - 1);
455 SetRecSize( 32 + mxVarData->Tell() );
458 XclExpHyperlink::~XclExpHyperlink()
462 OUString XclExpHyperlink::BuildFileName(
463 sal_uInt16& rnLevel, bool& rbRel, const OUString& rUrl, const XclExpRoot& rRoot, bool bEncoded )
465 INetURLObject aURLObject( rUrl );
466 OUString aDosName(bEncoded ? aURLObject.GetMainURL(INetURLObject::DecodeMechanism::ToIUri)
467 : aURLObject.getFSysPath(FSysStyle::Dos));
468 rnLevel = 0;
469 rbRel = rRoot.IsRelUrl();
471 if( rbRel )
473 // try to convert to relative file name
474 OUString aTmpName( aDosName );
475 aDosName = INetURLObject::GetRelURL( rRoot.GetBasePath(), rUrl,
476 INetURLObject::EncodeMechanism::WasEncoded,
477 (bEncoded ? INetURLObject::DecodeMechanism::ToIUri : INetURLObject::DecodeMechanism::WithCharset));
479 if (aDosName.startsWith(INET_FILE_SCHEME))
481 // not converted to rel -> back to old, return absolute flag
482 aDosName = aTmpName;
483 rbRel = false;
485 else if (aDosName.startsWith("./"))
487 aDosName = aDosName.copy(2);
489 else
491 while (aDosName.startsWith("../"))
493 aDosName = aDosName.copy(3);
494 ++rnLevel;
498 return aDosName;
501 void XclExpHyperlink::WriteBody( XclExpStream& rStrm )
503 sal_uInt16 nXclCol = static_cast< sal_uInt16 >( maScPos.Col() );
504 sal_uInt16 nXclRow = static_cast< sal_uInt16 >( maScPos.Row() );
505 rStrm << nXclRow << nXclRow << nXclCol << nXclCol;
506 WriteEmbeddedData( rStrm );
509 void XclExpHyperlink::WriteEmbeddedData( XclExpStream& rStrm )
511 rStrm << XclTools::maGuidStdLink
512 << sal_uInt32( 2 )
513 << mnFlags;
515 mxVarData->Seek( STREAM_SEEK_TO_BEGIN );
516 rStrm.CopyFromStream( *mxVarData );
519 void XclExpHyperlink::SaveXml( XclExpXmlStream& rStrm )
521 OUString sId = !msTarget.isEmpty() ? rStrm.addRelation( rStrm.GetCurrentStream()->getOutputStream(),
522 oox::getRelationship(Relationship::HYPERLINK),
523 msTarget, true ) : OUString();
524 rStrm.GetCurrentStream()->singleElement( XML_hyperlink,
525 XML_ref, XclXmlUtils::ToOString(&rStrm.GetRoot().GetDoc(), maScPos),
526 FSNS( XML_r, XML_id ), !sId.isEmpty()
527 ? sId.toUtf8().getStr()
528 : nullptr,
529 XML_location, mxTextMark.get() != nullptr
530 ? XclXmlUtils::ToOString( *mxTextMark ).getStr()
531 : nullptr,
532 // OOXTODO: XML_tooltip, from record HLinkTooltip 800h wzTooltip
533 XML_display, m_Repr.toUtf8() );
536 // Label ranges ===============================================================
538 XclExpLabelranges::XclExpLabelranges( const XclExpRoot& rRoot ) :
539 XclExpRoot( rRoot )
541 SCTAB nScTab = GetCurrScTab();
542 // row label ranges
543 FillRangeList( maRowRanges, rRoot.GetDoc().GetRowNameRangesRef(), nScTab );
544 // row labels only over 1 column (restriction of Excel97/2000/XP)
545 for ( size_t i = 0, nRanges = maRowRanges.size(); i < nRanges; ++i )
547 ScRange & rScRange = maRowRanges[ i ];
548 if( rScRange.aStart.Col() != rScRange.aEnd.Col() )
549 rScRange.aEnd.SetCol( rScRange.aStart.Col() );
551 // col label ranges
552 FillRangeList( maColRanges, rRoot.GetDoc().GetColNameRangesRef(), nScTab );
555 void XclExpLabelranges::FillRangeList( ScRangeList& rScRanges,
556 const ScRangePairListRef& xLabelRangesRef, SCTAB nScTab )
558 for ( size_t i = 0, nPairs = xLabelRangesRef->size(); i < nPairs; ++i )
560 const ScRangePair & rRangePair = (*xLabelRangesRef)[i];
561 const ScRange& rScRange = rRangePair.GetRange( 0 );
562 if( rScRange.aStart.Tab() == nScTab )
563 rScRanges.push_back( rScRange );
567 void XclExpLabelranges::Save( XclExpStream& rStrm )
569 XclExpAddressConverter& rAddrConv = GetAddressConverter();
570 XclRangeList aRowXclRanges, aColXclRanges;
571 rAddrConv.ConvertRangeList( aRowXclRanges, maRowRanges, false );
572 rAddrConv.ConvertRangeList( aColXclRanges, maColRanges, false );
573 if( !aRowXclRanges.empty() || !aColXclRanges.empty() )
575 rStrm.StartRecord( EXC_ID_LABELRANGES, 4 + 8 * (aRowXclRanges.size() + aColXclRanges.size()) );
576 rStrm << aRowXclRanges << aColXclRanges;
577 rStrm.EndRecord();
581 // Conditional formatting ====================================================
583 /** Represents a CF record that contains one condition of a conditional format. */
584 class XclExpCFImpl : protected XclExpRoot
586 public:
587 explicit XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin );
589 /** Writes the body of the CF record. */
590 void WriteBody( XclExpStream& rStrm );
591 void SaveXml( XclExpXmlStream& rStrm );
593 private:
594 const ScCondFormatEntry& mrFormatEntry; /// Calc conditional format entry.
595 ScAddress maOrigin; /// Top left cell of the combined range
596 XclFontData maFontData; /// Font formatting attributes.
597 XclExpCellBorder maBorder; /// Border formatting attributes.
598 XclExpCellArea maArea; /// Pattern formatting attributes.
599 XclTokenArrayRef mxTokArr1; /// Formula for first condition.
600 XclTokenArrayRef mxTokArr2; /// Formula for second condition.
601 sal_uInt32 mnFontColorId; /// Font color ID.
602 sal_uInt8 mnType; /// Type of the condition (cell/formula).
603 sal_uInt8 mnOperator; /// Comparison operator for cell type.
604 sal_Int32 mnPriority; /// Priority of this entry; needed for oox export
605 bool mbFontUsed; /// true = Any font attribute used.
606 bool mbHeightUsed; /// true = Font height used.
607 bool mbWeightUsed; /// true = Font weight used.
608 bool mbColorUsed; /// true = Font color used.
609 bool mbUnderlUsed; /// true = Font underline type used.
610 bool mbItalicUsed; /// true = Font posture used.
611 bool mbStrikeUsed; /// true = Font strikeout used.
612 bool mbBorderUsed; /// true = Border attribute used.
613 bool mbPattUsed; /// true = Pattern attribute used.
614 bool mbFormula2;
617 XclExpCFImpl::XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin ) :
618 XclExpRoot( rRoot ),
619 mrFormatEntry( rFormatEntry ),
620 maOrigin( aOrigin ),
621 mnFontColorId( 0 ),
622 mnType( EXC_CF_TYPE_CELL ),
623 mnOperator( EXC_CF_CMP_NONE ),
624 mnPriority( nPriority ),
625 mbFontUsed( false ),
626 mbHeightUsed( false ),
627 mbWeightUsed( false ),
628 mbColorUsed( false ),
629 mbUnderlUsed( false ),
630 mbItalicUsed( false ),
631 mbStrikeUsed( false ),
632 mbBorderUsed( false ),
633 mbPattUsed( false ),
634 mbFormula2(false)
636 // Set correct tab for maOrigin from GetValidSrcPos() of the format-entry.
637 ScAddress aValidSrcPos = mrFormatEntry.GetValidSrcPos();
638 maOrigin.SetTab(aValidSrcPos.Tab());
640 /* Get formatting attributes here, and not in WriteBody(). This is needed to
641 correctly insert all colors into the palette. */
643 if( SfxStyleSheetBase* pStyleSheet = GetDoc().GetStyleSheetPool()->Find( mrFormatEntry.GetStyle(), SfxStyleFamily::Para ) )
645 const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
647 // font
648 mbHeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_HEIGHT, true );
649 mbWeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_WEIGHT, true );
650 mbColorUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_COLOR, true );
651 mbUnderlUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_UNDERLINE, true );
652 mbItalicUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_POSTURE, true );
653 mbStrikeUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_CROSSEDOUT, true );
654 mbFontUsed = mbHeightUsed || mbWeightUsed || mbColorUsed || mbUnderlUsed || mbItalicUsed || mbStrikeUsed;
655 if( mbFontUsed )
657 vcl::Font aFont;
658 ScPatternAttr::GetFont( aFont, rItemSet, SC_AUTOCOL_RAW );
659 maFontData.FillFromVclFont( aFont );
660 mnFontColorId = GetPalette().InsertColor( maFontData.maColor, EXC_COLOR_CELLTEXT );
663 // border
664 mbBorderUsed = ScfTools::CheckItem( rItemSet, ATTR_BORDER, true );
665 if( mbBorderUsed )
666 maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff() );
668 // pattern
669 mbPattUsed = ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true );
670 if( mbPattUsed )
671 maArea.FillFromItemSet( rItemSet, GetPalette(), true );
674 // *** mode and comparison operator ***
676 switch( rFormatEntry.GetOperation() )
678 case ScConditionMode::NONE:
679 mnType = EXC_CF_TYPE_NONE;
680 break;
681 case ScConditionMode::Between:
682 mnOperator = EXC_CF_CMP_BETWEEN;
683 mbFormula2 = true;
684 break;
685 case ScConditionMode::NotBetween:
686 mnOperator = EXC_CF_CMP_NOT_BETWEEN;
687 mbFormula2 = true;
688 break;
689 case ScConditionMode::Equal:
690 mnOperator = EXC_CF_CMP_EQUAL;
691 break;
692 case ScConditionMode::NotEqual:
693 mnOperator = EXC_CF_CMP_NOT_EQUAL;
694 break;
695 case ScConditionMode::Greater:
696 mnOperator = EXC_CF_CMP_GREATER;
697 break;
698 case ScConditionMode::Less:
699 mnOperator = EXC_CF_CMP_LESS;
700 break;
701 case ScConditionMode::EqGreater:
702 mnOperator = EXC_CF_CMP_GREATER_EQUAL;
703 break;
704 case ScConditionMode::EqLess:
705 mnOperator = EXC_CF_CMP_LESS_EQUAL;
706 break;
707 case ScConditionMode::Direct:
708 mnType = EXC_CF_TYPE_FMLA;
709 break;
710 default:
711 mnType = EXC_CF_TYPE_NONE;
712 OSL_FAIL( "XclExpCF::WriteBody - unknown condition type" );
716 void XclExpCFImpl::WriteBody( XclExpStream& rStrm )
719 // *** formulas ***
721 XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
723 std::unique_ptr< ScTokenArray > xScTokArr( mrFormatEntry.CreateFlatCopiedTokenArray( 0 ) );
724 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
726 if (mbFormula2)
728 xScTokArr = mrFormatEntry.CreateFlatCopiedTokenArray( 1 );
729 mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
732 // *** mode and comparison operator ***
734 rStrm << mnType << mnOperator;
736 // *** formula sizes ***
738 sal_uInt16 nFmlaSize1 = mxTokArr1.get() ? mxTokArr1->GetSize() : 0;
739 sal_uInt16 nFmlaSize2 = mxTokArr2.get() ? mxTokArr2->GetSize() : 0;
740 rStrm << nFmlaSize1 << nFmlaSize2;
742 // *** formatting blocks ***
744 if( mbFontUsed || mbBorderUsed || mbPattUsed )
746 sal_uInt32 nFlags = EXC_CF_ALLDEFAULT;
748 ::set_flag( nFlags, EXC_CF_BLOCK_FONT, mbFontUsed );
749 ::set_flag( nFlags, EXC_CF_BLOCK_BORDER, mbBorderUsed );
750 ::set_flag( nFlags, EXC_CF_BLOCK_AREA, mbPattUsed );
752 // attributes used -> set flags to 0.
753 ::set_flag( nFlags, EXC_CF_BORDER_ALL, !mbBorderUsed );
754 ::set_flag( nFlags, EXC_CF_AREA_ALL, !mbPattUsed );
756 rStrm << nFlags << sal_uInt16( 0 );
758 if( mbFontUsed )
760 // font height, 0xFFFFFFFF indicates unused
761 sal_uInt32 nHeight = mbHeightUsed ? maFontData.mnHeight : 0xFFFFFFFF;
762 // font style: italic and strikeout
763 sal_uInt32 nStyle = 0;
764 ::set_flag( nStyle, EXC_CF_FONT_STYLE, maFontData.mbItalic );
765 ::set_flag( nStyle, EXC_CF_FONT_STRIKEOUT, maFontData.mbStrikeout );
766 // font color, 0xFFFFFFFF indicates unused
767 sal_uInt32 nColor = mbColorUsed ? GetPalette().GetColorIndex( mnFontColorId ) : 0xFFFFFFFF;
768 // font used flags for italic, weight, and strikeout -> 0 = used, 1 = default
769 sal_uInt32 nFontFlags1 = EXC_CF_FONT_ALLDEFAULT;
770 ::set_flag( nFontFlags1, EXC_CF_FONT_STYLE, !(mbItalicUsed || mbWeightUsed) );
771 ::set_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT, !mbStrikeUsed );
772 // font used flag for underline -> 0 = used, 1 = default
773 sal_uInt32 nFontFlags3 = mbUnderlUsed ? 0 : EXC_CF_FONT_UNDERL;
775 rStrm.WriteZeroBytesToRecord( 64 );
776 rStrm << nHeight
777 << nStyle
778 << maFontData.mnWeight
779 << EXC_FONTESC_NONE
780 << maFontData.mnUnderline;
781 rStrm.WriteZeroBytesToRecord( 3 );
782 rStrm << nColor
783 << sal_uInt32( 0 )
784 << nFontFlags1
785 << EXC_CF_FONT_ESCAPEM // escapement never used -> set the flag
786 << nFontFlags3;
787 rStrm.WriteZeroBytesToRecord( 16 );
788 rStrm << sal_uInt16( 1 ); // must be 1
791 if( mbBorderUsed )
793 sal_uInt16 nLineStyle = 0;
794 sal_uInt32 nLineColor = 0;
795 maBorder.SetFinalColors( GetPalette() );
796 maBorder.FillToCF8( nLineStyle, nLineColor );
797 rStrm << nLineStyle << nLineColor << sal_uInt16( 0 );
800 if( mbPattUsed )
802 sal_uInt16 nPattern = 0, nColor = 0;
803 maArea.SetFinalColors( GetPalette() );
804 maArea.FillToCF8( nPattern, nColor );
805 rStrm << nPattern << nColor;
808 else
810 // no data blocks at all
811 rStrm << sal_uInt32( 0 ) << sal_uInt16( 0 );
814 // *** formulas ***
816 if( mxTokArr1.get() )
817 mxTokArr1->WriteArray( rStrm );
818 if( mxTokArr2.get() )
819 mxTokArr2->WriteArray( rStrm );
822 namespace {
824 const char* GetOperatorString(ScConditionMode eMode, bool& bFrmla2)
826 const char *pRet = nullptr;
827 switch(eMode)
829 case ScConditionMode::Equal:
830 pRet = "equal";
831 break;
832 case ScConditionMode::Less:
833 pRet = "lessThan";
834 break;
835 case ScConditionMode::Greater:
836 pRet = "greaterThan";
837 break;
838 case ScConditionMode::EqLess:
839 pRet = "lessThanOrEqual";
840 break;
841 case ScConditionMode::EqGreater:
842 pRet = "greaterThanOrEqual";
843 break;
844 case ScConditionMode::NotEqual:
845 pRet = "notEqual";
846 break;
847 case ScConditionMode::Between:
848 bFrmla2 = true;
849 pRet = "between";
850 break;
851 case ScConditionMode::NotBetween:
852 bFrmla2 = true;
853 pRet = "notBetween";
854 break;
855 case ScConditionMode::Duplicate:
856 pRet = nullptr;
857 break;
858 case ScConditionMode::NotDuplicate:
859 pRet = nullptr;
860 break;
861 case ScConditionMode::BeginsWith:
862 pRet = "beginsWith";
863 break;
864 case ScConditionMode::EndsWith:
865 pRet = "endsWith";
866 break;
867 case ScConditionMode::ContainsText:
868 pRet = "containsText";
869 break;
870 case ScConditionMode::NotContainsText:
871 pRet = "notContains";
872 break;
873 case ScConditionMode::Direct:
874 break;
875 case ScConditionMode::NONE:
876 default:
877 break;
879 return pRet;
882 const char* GetTypeString(ScConditionMode eMode)
884 switch(eMode)
886 case ScConditionMode::Direct:
887 return "expression";
888 case ScConditionMode::Top10:
889 case ScConditionMode::TopPercent:
890 case ScConditionMode::Bottom10:
891 case ScConditionMode::BottomPercent:
892 return "top10";
893 case ScConditionMode::AboveAverage:
894 case ScConditionMode::BelowAverage:
895 case ScConditionMode::AboveEqualAverage:
896 case ScConditionMode::BelowEqualAverage:
897 return "aboveAverage";
898 case ScConditionMode::NotDuplicate:
899 return "uniqueValues";
900 case ScConditionMode::Duplicate:
901 return "duplicateValues";
902 case ScConditionMode::Error:
903 return "containsErrors";
904 case ScConditionMode::NoError:
905 return "notContainsErrors";
906 case ScConditionMode::BeginsWith:
907 return "beginsWith";
908 case ScConditionMode::EndsWith:
909 return "endsWith";
910 case ScConditionMode::ContainsText:
911 return "containsText";
912 case ScConditionMode::NotContainsText:
913 return "notContainsText";
914 default:
915 return "cellIs";
919 bool IsTopBottomRule(ScConditionMode eMode)
921 switch(eMode)
923 case ScConditionMode::Top10:
924 case ScConditionMode::Bottom10:
925 case ScConditionMode::TopPercent:
926 case ScConditionMode::BottomPercent:
927 return true;
928 default:
929 break;
932 return false;
935 bool IsTextRule(ScConditionMode eMode)
937 switch(eMode)
939 case ScConditionMode::BeginsWith:
940 case ScConditionMode::EndsWith:
941 case ScConditionMode::ContainsText:
942 case ScConditionMode::NotContainsText:
943 return true;
944 default:
945 break;
948 return false;
951 bool RequiresFormula(ScConditionMode eMode)
953 if (IsTopBottomRule(eMode))
954 return false;
955 else if (IsTextRule(eMode))
956 return false;
958 switch (eMode)
960 case ScConditionMode::NoError:
961 case ScConditionMode::Error:
962 case ScConditionMode::Duplicate:
963 case ScConditionMode::NotDuplicate:
964 return false;
965 default:
966 break;
969 return true;
972 bool RequiresFixedFormula(ScConditionMode eMode)
974 switch(eMode)
976 case ScConditionMode::NoError:
977 case ScConditionMode::Error:
978 case ScConditionMode::BeginsWith:
979 case ScConditionMode::EndsWith:
980 case ScConditionMode::ContainsText:
981 case ScConditionMode::NotContainsText:
982 return true;
983 default:
984 break;
987 return false;
990 OString GetFixedFormula(ScConditionMode eMode, const ScAddress& rAddress, const OString& rText)
992 OStringBuffer aBuffer;
993 XclXmlUtils::ToOString(aBuffer, rAddress);
994 OString aPos = aBuffer.makeStringAndClear();
995 switch (eMode)
997 case ScConditionMode::Error:
998 return OString("ISERROR(" + aPos + ")") ;
999 case ScConditionMode::NoError:
1000 return OString("NOT(ISERROR(" + aPos + "))") ;
1001 case ScConditionMode::BeginsWith:
1002 return OString("LEFT(" + aPos + ",LEN(\"" + rText + "\"))=\"" + rText + "\"");
1003 case ScConditionMode::EndsWith:
1004 return OString("RIGHT(" + aPos +",LEN(\"" + rText + "\"))=\"" + rText + "\"");
1005 case ScConditionMode::ContainsText:
1006 return OString("NOT(ISERROR(SEARCH(\"" + rText + "\"," + aPos + ")))");
1007 case ScConditionMode::NotContainsText:
1008 return OString("ISERROR(SEARCH(\"" + rText + "\"," + aPos + "))");
1009 default:
1010 break;
1013 return "";
1018 void XclExpCFImpl::SaveXml( XclExpXmlStream& rStrm )
1020 bool bFmla2 = false;
1021 ScConditionMode eOperation = mrFormatEntry.GetOperation();
1022 bool bAboveAverage = eOperation == ScConditionMode::AboveAverage ||
1023 eOperation == ScConditionMode::AboveEqualAverage;
1024 bool bEqualAverage = eOperation == ScConditionMode::AboveEqualAverage ||
1025 eOperation == ScConditionMode::BelowEqualAverage;
1026 bool bBottom = eOperation == ScConditionMode::Bottom10
1027 || eOperation == ScConditionMode::BottomPercent;
1028 bool bPercent = eOperation == ScConditionMode::TopPercent ||
1029 eOperation == ScConditionMode::BottomPercent;
1030 OString aRank("0");
1031 if(IsTopBottomRule(eOperation))
1033 // position and formula grammar are not important
1034 // we only store a number there
1035 aRank = mrFormatEntry.GetExpression(ScAddress(0,0,0), 0).toUtf8();
1037 OString aText;
1038 if(IsTextRule(eOperation))
1040 // we need to write the text without quotes
1041 // we have to actually get the string from
1042 // the token array for that
1043 std::unique_ptr<ScTokenArray> pTokenArray(mrFormatEntry.CreateFlatCopiedTokenArray(0));
1044 if(pTokenArray->GetLen())
1045 aText = pTokenArray->FirstToken()->GetString().getString().toUtf8();
1048 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1049 rWorksheet->startElement( XML_cfRule,
1050 XML_type, GetTypeString( mrFormatEntry.GetOperation() ),
1051 XML_priority, OString::number(mnPriority + 1),
1052 XML_operator, GetOperatorString( mrFormatEntry.GetOperation(), bFmla2 ),
1053 XML_aboveAverage, ToPsz10(bAboveAverage),
1054 XML_equalAverage, ToPsz10(bEqualAverage),
1055 XML_bottom, ToPsz10(bBottom),
1056 XML_percent, ToPsz10(bPercent),
1057 XML_rank, aRank,
1058 XML_text, aText,
1059 XML_dxfId, OString::number(GetDxfs().GetDxfId(mrFormatEntry.GetStyle())) );
1061 if (RequiresFixedFormula(eOperation))
1063 rWorksheet->startElement(XML_formula);
1064 OString aFormula = GetFixedFormula(eOperation, maOrigin, aText);
1065 rWorksheet->writeEscaped(aFormula.getStr());
1066 rWorksheet->endElement( XML_formula );
1068 else if(RequiresFormula(eOperation))
1070 rWorksheet->startElement(XML_formula);
1071 std::unique_ptr<ScTokenArray> pTokenArray(mrFormatEntry.CreateFlatCopiedTokenArray(0));
1072 rWorksheet->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry.GetValidSrcPos(),
1073 pTokenArray.get()));
1074 rWorksheet->endElement( XML_formula );
1075 if (bFmla2)
1077 rWorksheet->startElement(XML_formula);
1078 std::unique_ptr<ScTokenArray> pTokenArray2(mrFormatEntry.CreateFlatCopiedTokenArray(1));
1079 rWorksheet->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry.GetValidSrcPos(),
1080 pTokenArray2.get()));
1081 rWorksheet->endElement( XML_formula );
1084 // OOXTODO: XML_extLst
1085 rWorksheet->endElement( XML_cfRule );
1088 XclExpCF::XclExpCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin ) :
1089 XclExpRecord( EXC_ID_CF ),
1090 XclExpRoot( rRoot ),
1091 mxImpl( new XclExpCFImpl( rRoot, rFormatEntry, nPriority, aOrigin ) )
1095 XclExpCF::~XclExpCF()
1099 void XclExpCF::WriteBody( XclExpStream& rStrm )
1101 mxImpl->WriteBody( rStrm );
1104 void XclExpCF::SaveXml( XclExpXmlStream& rStrm )
1106 mxImpl->SaveXml( rStrm );
1109 XclExpDateFormat::XclExpDateFormat( const XclExpRoot& rRoot, const ScCondDateFormatEntry& rFormatEntry, sal_Int32 nPriority ):
1110 XclExpRecord( EXC_ID_CF ),
1111 XclExpRoot( rRoot ),
1112 mrFormatEntry(rFormatEntry),
1113 mnPriority(nPriority)
1117 XclExpDateFormat::~XclExpDateFormat()
1121 namespace {
1123 const char* getTimePeriodString( condformat::ScCondFormatDateType eType )
1125 switch(eType)
1127 case condformat::TODAY:
1128 return "today";
1129 case condformat::YESTERDAY:
1130 return "yesterday";
1131 case condformat::TOMORROW:
1132 return "yesterday";
1133 case condformat::THISWEEK:
1134 return "thisWeek";
1135 case condformat::LASTWEEK:
1136 return "lastWeek";
1137 case condformat::NEXTWEEK:
1138 return "nextWeek";
1139 case condformat::THISMONTH:
1140 return "thisMonth";
1141 case condformat::LASTMONTH:
1142 return "lastMonth";
1143 case condformat::NEXTMONTH:
1144 return "nextMonth";
1145 case condformat::LAST7DAYS:
1146 return "last7Days";
1147 default:
1148 break;
1150 return nullptr;
1155 void XclExpDateFormat::SaveXml( XclExpXmlStream& rStrm )
1157 // only write the supported entries into OOXML
1158 const char* sTimePeriod = getTimePeriodString(mrFormatEntry.GetDateType());
1159 if(!sTimePeriod)
1160 return;
1162 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1163 rWorksheet->startElement( XML_cfRule,
1164 XML_type, "timePeriod",
1165 XML_priority, OString::number(mnPriority + 1),
1166 XML_timePeriod, sTimePeriod,
1167 XML_dxfId, OString::number(GetDxfs().GetDxfId(mrFormatEntry.GetStyleName())) );
1168 rWorksheet->endElement( XML_cfRule);
1171 XclExpCfvo::XclExpCfvo(const XclExpRoot& rRoot, const ScColorScaleEntry& rEntry, const ScAddress& rAddr, bool bFirst):
1172 XclExpRecord(),
1173 XclExpRoot( rRoot ),
1174 mrEntry(rEntry),
1175 maSrcPos(rAddr),
1176 mbFirst(bFirst)
1180 namespace {
1182 OString getColorScaleType( const ScColorScaleEntry& rEntry, bool bFirst )
1184 switch(rEntry.GetType())
1186 case COLORSCALE_MIN:
1187 return "min";
1188 case COLORSCALE_MAX:
1189 return "max";
1190 case COLORSCALE_PERCENT:
1191 return "percent";
1192 case COLORSCALE_FORMULA:
1193 return "formula";
1194 case COLORSCALE_AUTO:
1195 if(bFirst)
1196 return "min";
1197 else
1198 return "max";
1199 case COLORSCALE_PERCENTILE:
1200 return "percentile";
1201 default:
1202 break;
1205 return "num";
1210 void XclExpCfvo::SaveXml( XclExpXmlStream& rStrm )
1212 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1214 OString aValue;
1215 if(mrEntry.GetType() == COLORSCALE_FORMULA)
1217 OUString aFormula = XclXmlUtils::ToOUString( GetCompileFormulaContext(), maSrcPos,
1218 mrEntry.GetFormula());
1219 aValue = OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8 );
1221 else
1223 aValue = OString::number( mrEntry.GetValue() );
1226 rWorksheet->startElement( XML_cfvo,
1227 XML_type, getColorScaleType(mrEntry, mbFirst),
1228 XML_val, aValue );
1230 rWorksheet->endElement( XML_cfvo );
1233 XclExpColScaleCol::XclExpColScaleCol( const XclExpRoot& rRoot, const Color& rColor ):
1234 XclExpRecord(),
1235 XclExpRoot( rRoot ),
1236 mrColor( rColor )
1240 XclExpColScaleCol::~XclExpColScaleCol()
1244 void XclExpColScaleCol::SaveXml( XclExpXmlStream& rStrm )
1246 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1248 rWorksheet->startElement(XML_color, XML_rgb, XclXmlUtils::ToOString(mrColor));
1250 rWorksheet->endElement( XML_color );
1253 namespace {
1255 OString createHexStringFromDigit(sal_uInt8 nDigit)
1257 OString aString = OString::number( nDigit, 16 );
1258 if(aString.getLength() == 1)
1259 aString += OString::number(0);
1260 return aString;
1263 OString createGuidStringFromInt(sal_uInt8 nGuid[16])
1265 OStringBuffer aBuffer;
1266 aBuffer.append('{');
1267 for(size_t i = 0; i < 16; ++i)
1269 aBuffer.append(createHexStringFromDigit(nGuid[i]));
1270 if(i == 3|| i == 5 || i == 7 || i == 9 )
1271 aBuffer.append('-');
1273 aBuffer.append('}');
1274 OString aString = aBuffer.makeStringAndClear();
1275 return aString.toAsciiUpperCase();
1278 OString generateGUIDString()
1280 sal_uInt8 nGuid[16];
1281 rtl_createUuid(nGuid, nullptr, true);
1282 return createGuidStringFromInt(nGuid);
1287 XclExpCondfmt::XclExpCondfmt( const XclExpRoot& rRoot, const ScConditionalFormat& rCondFormat, const XclExtLstRef& xExtLst, sal_Int32& rIndex ) :
1288 XclExpRecord( EXC_ID_CONDFMT ),
1289 XclExpRoot( rRoot )
1291 const ScRangeList& aScRanges = rCondFormat.GetRange();
1292 GetAddressConverter().ConvertRangeList( maXclRanges, aScRanges, true );
1293 if( !maXclRanges.empty() )
1295 std::vector<XclExpExtCondFormatData> aExtEntries;
1296 ScAddress aOrigin = aScRanges.Combine().aStart;
1297 for( size_t nIndex = 0, nCount = rCondFormat.size(); nIndex < nCount; ++nIndex )
1298 if( const ScFormatEntry* pFormatEntry = rCondFormat.GetEntry( nIndex ) )
1300 if(pFormatEntry->GetType() == ScFormatEntry::Type::Condition)
1301 maCFList.AppendNewRecord( new XclExpCF( GetRoot(), static_cast<const ScCondFormatEntry&>(*pFormatEntry), ++rIndex, aOrigin ) );
1302 else if(pFormatEntry->GetType() == ScFormatEntry::Type::ExtCondition)
1304 const ScCondFormatEntry& rFormat = static_cast<const ScCondFormatEntry&>(*pFormatEntry);
1305 XclExpExtCondFormatData aExtEntry;
1306 aExtEntry.nPriority = ++rIndex;
1307 aExtEntry.aGUID = generateGUIDString();
1308 aExtEntry.pEntry = &rFormat;
1309 aExtEntries.push_back(aExtEntry);
1311 else if(pFormatEntry->GetType() == ScFormatEntry::Type::Colorscale)
1312 maCFList.AppendNewRecord( new XclExpColorScale( GetRoot(), static_cast<const ScColorScaleFormat&>(*pFormatEntry), ++rIndex ) );
1313 else if(pFormatEntry->GetType() == ScFormatEntry::Type::Databar)
1315 const ScDataBarFormat& rFormat = static_cast<const ScDataBarFormat&>(*pFormatEntry);
1316 XclExpExtCondFormatData aExtEntry;
1317 aExtEntry.nPriority = -1;
1318 aExtEntry.aGUID = generateGUIDString();
1319 aExtEntry.pEntry = &rFormat;
1320 aExtEntries.push_back(aExtEntry);
1322 maCFList.AppendNewRecord( new XclExpDataBar( GetRoot(), rFormat, ++rIndex, aExtEntry.aGUID));
1324 else if(pFormatEntry->GetType() == ScFormatEntry::Type::Iconset)
1326 // don't export iconSet entries that are not in OOXML
1327 const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(*pFormatEntry);
1328 bool bNeedsExt = false;
1329 switch (rIconSet.GetIconSetData()->eIconSetType)
1331 case IconSet_3Smilies:
1332 case IconSet_3ColorSmilies:
1333 case IconSet_3Stars:
1334 case IconSet_3Triangles:
1335 case IconSet_5Boxes:
1337 bNeedsExt = true;
1339 break;
1340 default:
1341 break;
1344 bNeedsExt |= rIconSet.GetIconSetData()->mbCustom;
1346 if (bNeedsExt)
1348 XclExpExtCondFormatData aExtEntry;
1349 aExtEntry.nPriority = ++rIndex;
1350 aExtEntry.aGUID = generateGUIDString();
1351 aExtEntry.pEntry = &rIconSet;
1352 aExtEntries.push_back(aExtEntry);
1354 else
1355 maCFList.AppendNewRecord( new XclExpIconSet( GetRoot(), rIconSet, ++rIndex ) );
1357 else if(pFormatEntry->GetType() == ScFormatEntry::Type::Date)
1358 maCFList.AppendNewRecord( new XclExpDateFormat( GetRoot(), static_cast<const ScCondDateFormatEntry&>(*pFormatEntry), ++rIndex ) );
1360 aScRanges.Format( msSeqRef, ScRefFlags::VALID, &GetDoc(), formula::FormulaGrammar::CONV_XL_OOX, ' ', true );
1362 if(!aExtEntries.empty() && xExtLst.get())
1364 XclExpExtRef pParent = xExtLst->GetItem( XclExpExtDataBarType );
1365 if( !pParent.get() )
1367 xExtLst->AddRecord( XclExpExtRef(new XclExpExtCondFormat( *xExtLst )) );
1368 pParent = xExtLst->GetItem( XclExpExtDataBarType );
1370 static_cast<XclExpExtCondFormat*>(xExtLst->GetItem( XclExpExtDataBarType ).get())->AddRecord(
1371 std::make_shared<XclExpExtConditionalFormatting>( *pParent, aExtEntries, aScRanges));
1376 XclExpCondfmt::~XclExpCondfmt()
1380 bool XclExpCondfmt::IsValidForBinary() const
1382 // ccf (2 bytes): An unsigned integer that specifies the count of CF records that follow this
1383 // record. MUST be greater than or equal to 0x0001, and less than or equal to 0x0003.
1385 SAL_WARN_IF( maCFList.GetSize() > 3, "sc.filter", "More than 3 conditional filters for cell(s), won't export");
1387 return !maCFList.IsEmpty() && maCFList.GetSize() <= 3 && !maXclRanges.empty();
1390 bool XclExpCondfmt::IsValidForXml() const
1392 return !maCFList.IsEmpty() && !maXclRanges.empty();
1395 void XclExpCondfmt::Save( XclExpStream& rStrm )
1397 if( IsValidForBinary() )
1399 XclExpRecord::Save( rStrm );
1400 maCFList.Save( rStrm );
1404 void XclExpCondfmt::WriteBody( XclExpStream& rStrm )
1406 OSL_ENSURE( !maCFList.IsEmpty(), "XclExpCondfmt::WriteBody - no CF records to write" );
1407 OSL_ENSURE( !maXclRanges.empty(), "XclExpCondfmt::WriteBody - no cell ranges found" );
1409 rStrm << static_cast< sal_uInt16 >( maCFList.GetSize() )
1410 << sal_uInt16( 1 )
1411 << maXclRanges.GetEnclosingRange()
1412 << maXclRanges;
1415 void XclExpCondfmt::SaveXml( XclExpXmlStream& rStrm )
1417 if( !IsValidForXml() )
1418 return;
1420 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1421 rWorksheet->startElement( XML_conditionalFormatting,
1422 XML_sqref, msSeqRef.toUtf8()
1423 // OOXTODO: XML_pivot
1426 maCFList.SaveXml( rStrm );
1428 rWorksheet->endElement( XML_conditionalFormatting );
1431 XclExpColorScale::XclExpColorScale( const XclExpRoot& rRoot, const ScColorScaleFormat& rFormat, sal_Int32 nPriority ):
1432 XclExpRecord(),
1433 XclExpRoot( rRoot ),
1434 mnPriority( nPriority )
1436 const ScRange & rRange = rFormat.GetRange().front();
1437 ScAddress aAddr = rRange.aStart;
1438 for(const auto& rxColorScaleEntry : rFormat)
1440 // exact position is not important, we allow only absolute refs
1442 XclExpCfvoList::RecordRefType xCfvo( new XclExpCfvo( GetRoot(), *rxColorScaleEntry, aAddr ) );
1443 maCfvoList.AppendRecord( xCfvo );
1444 XclExpColScaleColList::RecordRefType xClo( new XclExpColScaleCol( GetRoot(), rxColorScaleEntry->GetColor() ) );
1445 maColList.AppendRecord( xClo );
1449 void XclExpColorScale::SaveXml( XclExpXmlStream& rStrm )
1451 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1453 rWorksheet->startElement( XML_cfRule,
1454 XML_type, "colorScale",
1455 XML_priority, OString::number(mnPriority + 1) );
1457 rWorksheet->startElement(XML_colorScale);
1459 maCfvoList.SaveXml(rStrm);
1460 maColList.SaveXml(rStrm);
1462 rWorksheet->endElement( XML_colorScale );
1464 rWorksheet->endElement( XML_cfRule );
1467 XclExpDataBar::XclExpDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, sal_Int32 nPriority, const OString& rGUID):
1468 XclExpRecord(),
1469 XclExpRoot( rRoot ),
1470 mrFormat( rFormat ),
1471 mnPriority( nPriority ),
1472 maGUID(rGUID)
1474 const ScRange & rRange = rFormat.GetRange().front();
1475 ScAddress aAddr = rRange.aStart;
1476 // exact position is not important, we allow only absolute refs
1477 mpCfvoLowerLimit.reset(
1478 new XclExpCfvo(GetRoot(), *mrFormat.GetDataBarData()->mpLowerLimit, aAddr, true));
1479 mpCfvoUpperLimit.reset(
1480 new XclExpCfvo(GetRoot(), *mrFormat.GetDataBarData()->mpUpperLimit, aAddr, false));
1482 mpCol.reset( new XclExpColScaleCol( GetRoot(), mrFormat.GetDataBarData()->maPositiveColor ) );
1485 void XclExpDataBar::SaveXml( XclExpXmlStream& rStrm )
1487 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1489 rWorksheet->startElement( XML_cfRule,
1490 XML_type, "dataBar",
1491 XML_priority, OString::number(mnPriority + 1) );
1493 rWorksheet->startElement( XML_dataBar,
1494 XML_showValue, ToPsz10(!mrFormat.GetDataBarData()->mbOnlyBar),
1495 XML_minLength, OString::number(sal_uInt32(mrFormat.GetDataBarData()->mnMinLength)),
1496 XML_maxLength, OString::number(sal_uInt32(mrFormat.GetDataBarData()->mnMaxLength)) );
1498 mpCfvoLowerLimit->SaveXml(rStrm);
1499 mpCfvoUpperLimit->SaveXml(rStrm);
1500 mpCol->SaveXml(rStrm);
1502 rWorksheet->endElement( XML_dataBar );
1504 // extLst entries for Excel 2010 and 2013
1505 rWorksheet->startElement(XML_extLst);
1506 rWorksheet->startElement(XML_ext,
1507 FSNS(XML_xmlns, XML_x14), rStrm.getNamespaceURL(OOX_NS(xls14Lst)).toUtf8(),
1508 XML_uri, "{B025F937-C7B1-47D3-B67F-A62EFF666E3E}");
1510 rWorksheet->startElementNS( XML_x14, XML_id );
1511 rWorksheet->write(maGUID);
1512 rWorksheet->endElementNS( XML_x14, XML_id );
1514 rWorksheet->endElement( XML_ext );
1515 rWorksheet->endElement( XML_extLst );
1517 rWorksheet->endElement( XML_cfRule );
1520 XclExpIconSet::XclExpIconSet( const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, sal_Int32 nPriority ):
1521 XclExpRecord(),
1522 XclExpRoot( rRoot ),
1523 mrFormat( rFormat ),
1524 mnPriority( nPriority )
1526 const ScRange & rRange = rFormat.GetRange().front();
1527 ScAddress aAddr = rRange.aStart;
1528 for (auto const& itr : rFormat)
1530 // exact position is not important, we allow only absolute refs
1532 XclExpCfvoList::RecordRefType xCfvo( new XclExpCfvo( GetRoot(), *itr, aAddr ) );
1533 maCfvoList.AppendRecord( xCfvo );
1537 void XclExpIconSet::SaveXml( XclExpXmlStream& rStrm )
1539 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1541 rWorksheet->startElement( XML_cfRule,
1542 XML_type, "iconSet",
1543 XML_priority, OString::number(mnPriority + 1) );
1545 const char* pIconSetName = ScIconSetFormat::getIconSetName(mrFormat.GetIconSetData()->eIconSetType);
1546 rWorksheet->startElement( XML_iconSet,
1547 XML_iconSet, pIconSetName,
1548 XML_showValue, mrFormat.GetIconSetData()->mbShowValue ? nullptr : "0",
1549 XML_reverse, mrFormat.GetIconSetData()->mbReverse ? "1" : nullptr );
1551 maCfvoList.SaveXml( rStrm );
1553 rWorksheet->endElement( XML_iconSet );
1554 rWorksheet->endElement( XML_cfRule );
1557 XclExpCondFormatBuffer::XclExpCondFormatBuffer( const XclExpRoot& rRoot, const XclExtLstRef& xExtLst ) :
1558 XclExpRoot( rRoot )
1560 if( const ScConditionalFormatList* pCondFmtList = GetDoc().GetCondFormList(GetCurrScTab()) )
1562 sal_Int32 nIndex = 0;
1563 for( const auto& rxCondFmt : *pCondFmtList)
1565 XclExpCondfmtList::RecordRefType xCondfmtRec( new XclExpCondfmt( GetRoot(), *rxCondFmt, xExtLst, nIndex ));
1566 if( xCondfmtRec->IsValidForXml() )
1567 maCondfmtList.AppendRecord( xCondfmtRec );
1572 void XclExpCondFormatBuffer::Save( XclExpStream& rStrm )
1574 maCondfmtList.Save( rStrm );
1577 void XclExpCondFormatBuffer::SaveXml( XclExpXmlStream& rStrm )
1579 maCondfmtList.SaveXml( rStrm );
1582 // Validation =================================================================
1584 namespace {
1586 /** Writes a formula for the DV record. */
1587 void lclWriteDvFormula( XclExpStream& rStrm, const XclTokenArray* pXclTokArr )
1589 sal_uInt16 nFmlaSize = pXclTokArr ? pXclTokArr->GetSize() : 0;
1590 rStrm << nFmlaSize << sal_uInt16( 0 );
1591 if( pXclTokArr )
1592 pXclTokArr->WriteArray( rStrm );
1595 /** Writes a formula for the DV record, based on a single string. */
1596 void lclWriteDvFormula( XclExpStream& rStrm, const XclExpString& rString )
1598 // fake a formula with a single tStr token
1599 rStrm << static_cast< sal_uInt16 >( rString.GetSize() + 1 )
1600 << sal_uInt16( 0 )
1601 << EXC_TOKID_STR
1602 << rString;
1605 const char* lcl_GetValidationType( sal_uInt32 nFlags )
1607 switch( nFlags & EXC_DV_MODE_MASK )
1609 case EXC_DV_MODE_ANY: return "none";
1610 case EXC_DV_MODE_WHOLE: return "whole";
1611 case EXC_DV_MODE_DECIMAL: return "decimal";
1612 case EXC_DV_MODE_LIST: return "list";
1613 case EXC_DV_MODE_DATE: return "date";
1614 case EXC_DV_MODE_TIME: return "time";
1615 case EXC_DV_MODE_TEXTLEN: return "textLength";
1616 case EXC_DV_MODE_CUSTOM: return "custom";
1618 return nullptr;
1621 const char* lcl_GetOperatorType( sal_uInt32 nFlags )
1623 switch( nFlags & EXC_DV_COND_MASK )
1625 case EXC_DV_COND_BETWEEN: return "between";
1626 case EXC_DV_COND_NOTBETWEEN: return "notBetween";
1627 case EXC_DV_COND_EQUAL: return "equal";
1628 case EXC_DV_COND_NOTEQUAL: return "notEqual";
1629 case EXC_DV_COND_GREATER: return "greaterThan";
1630 case EXC_DV_COND_LESS: return "lessThan";
1631 case EXC_DV_COND_EQGREATER: return "greaterThanOrEqual";
1632 case EXC_DV_COND_EQLESS: return "lessThanOrEqual";
1634 return nullptr;
1637 } // namespace
1639 XclExpDV::XclExpDV( const XclExpRoot& rRoot, sal_uLong nScHandle ) :
1640 XclExpRecord( EXC_ID_DV ),
1641 XclExpRoot( rRoot ),
1642 mnFlags( 0 ),
1643 mnScHandle( nScHandle )
1645 if( const ScValidationData* pValData = GetDoc().GetValidationEntry( mnScHandle ) )
1647 // prompt box - empty string represented by single NUL character
1648 OUString aTitle, aText;
1649 bool bShowPrompt = pValData->GetInput( aTitle, aText );
1650 if( !aTitle.isEmpty() )
1651 maPromptTitle.Assign( aTitle );
1652 else
1653 maPromptTitle.Assign( '\0' );
1654 if( !aText.isEmpty() )
1655 maPromptText.Assign( aText );
1656 else
1657 maPromptText.Assign( '\0' );
1659 // error box - empty string represented by single NUL character
1660 ScValidErrorStyle eScErrorStyle;
1661 bool bShowError = pValData->GetErrMsg( aTitle, aText, eScErrorStyle );
1662 if( !aTitle.isEmpty() )
1663 maErrorTitle.Assign( aTitle );
1664 else
1665 maErrorTitle.Assign( '\0' );
1666 if( !aText.isEmpty() )
1667 maErrorText.Assign( aText );
1668 else
1669 maErrorText.Assign( '\0' );
1671 // flags
1672 switch( pValData->GetDataMode() )
1674 case SC_VALID_ANY: mnFlags |= EXC_DV_MODE_ANY; break;
1675 case SC_VALID_WHOLE: mnFlags |= EXC_DV_MODE_WHOLE; break;
1676 case SC_VALID_DECIMAL: mnFlags |= EXC_DV_MODE_DECIMAL; break;
1677 case SC_VALID_LIST: mnFlags |= EXC_DV_MODE_LIST; break;
1678 case SC_VALID_DATE: mnFlags |= EXC_DV_MODE_DATE; break;
1679 case SC_VALID_TIME: mnFlags |= EXC_DV_MODE_TIME; break;
1680 case SC_VALID_TEXTLEN: mnFlags |= EXC_DV_MODE_TEXTLEN; break;
1681 case SC_VALID_CUSTOM: mnFlags |= EXC_DV_MODE_CUSTOM; break;
1682 default: OSL_FAIL( "XclExpDV::XclExpDV - unknown mode" );
1685 switch( pValData->GetOperation() )
1687 case ScConditionMode::NONE:
1688 case ScConditionMode::Equal: mnFlags |= EXC_DV_COND_EQUAL; break;
1689 case ScConditionMode::Less: mnFlags |= EXC_DV_COND_LESS; break;
1690 case ScConditionMode::Greater: mnFlags |= EXC_DV_COND_GREATER; break;
1691 case ScConditionMode::EqLess: mnFlags |= EXC_DV_COND_EQLESS; break;
1692 case ScConditionMode::EqGreater: mnFlags |= EXC_DV_COND_EQGREATER; break;
1693 case ScConditionMode::NotEqual: mnFlags |= EXC_DV_COND_NOTEQUAL; break;
1694 case ScConditionMode::Between: mnFlags |= EXC_DV_COND_BETWEEN; break;
1695 case ScConditionMode::NotBetween: mnFlags |= EXC_DV_COND_NOTBETWEEN; break;
1696 default: OSL_FAIL( "XclExpDV::XclExpDV - unknown condition" );
1698 switch( eScErrorStyle )
1700 case SC_VALERR_STOP: mnFlags |= EXC_DV_ERROR_STOP; break;
1701 case SC_VALERR_WARNING: mnFlags |= EXC_DV_ERROR_WARNING; break;
1702 case SC_VALERR_INFO: mnFlags |= EXC_DV_ERROR_INFO; break;
1703 case SC_VALERR_MACRO:
1704 // set INFO for validity with macro call, delete title
1705 mnFlags |= EXC_DV_ERROR_INFO;
1706 maErrorTitle.Assign( '\0' ); // contains macro name
1707 break;
1708 default: OSL_FAIL( "XclExpDV::XclExpDV - unknown error style" );
1710 ::set_flag( mnFlags, EXC_DV_IGNOREBLANK, pValData->IsIgnoreBlank() );
1711 ::set_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN, pValData->GetListType() == css::sheet::TableValidationVisibility::INVISIBLE );
1712 ::set_flag( mnFlags, EXC_DV_SHOWPROMPT, bShowPrompt );
1713 ::set_flag( mnFlags, EXC_DV_SHOWERROR, bShowError );
1715 // formulas
1716 XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
1718 // first formula
1719 std::unique_ptr< ScTokenArray > xScTokArr = pValData->CreateFlatCopiedTokenArray( 0 );
1720 if (xScTokArr)
1722 if( pValData->GetDataMode() == SC_VALID_LIST )
1724 OUString aString;
1725 if( XclTokenArrayHelper::GetStringList( aString, *xScTokArr, '\n' ) )
1727 OUStringBuffer sFormulaBuf;
1728 sFormulaBuf.append( '"' );
1729 /* Formula is a list of string tokens -> build the Excel string.
1730 Data validity is BIFF8 only (important for the XclExpString object).
1731 Excel uses the NUL character as string list separator. */
1732 mxString1.reset( new XclExpString( XclStrFlags::EightBitLength ) );
1733 if (!aString.isEmpty())
1735 sal_Int32 nStringIx = 0;
1736 for(;;)
1738 const OUString aToken( aString.getToken( 0, '\n', nStringIx ) );
1739 mxString1->Append( aToken );
1740 sFormulaBuf.append( aToken );
1741 if (nStringIx<0)
1742 break;
1743 mxString1->Append(OUString(u'\0'));
1744 sFormulaBuf.append( ',' );
1747 ::set_flag( mnFlags, EXC_DV_STRINGLIST );
1749 // maximum length allowed in Excel is 255 characters, and don't end with an empty token
1750 sal_uInt32 nLen = sFormulaBuf.getLength();
1751 if( nLen > 256 ) // 255 + beginning quote mark
1753 nLen = 256;
1754 if( sFormulaBuf[nLen - 1] == ',' )
1755 --nLen;
1756 sFormulaBuf.truncate(nLen);
1759 sFormulaBuf.append( '"' );
1760 msFormula1 = sFormulaBuf.makeStringAndClear();
1762 else
1764 /* All other formulas in validation are stored like conditional
1765 formatting formulas (with tRefN/tAreaN tokens as value or
1766 array class). But NOT the cell references and defined names
1767 in list validation - they are stored as reference class
1768 tokens... Example:
1769 1) Cell must be equal to A1 -> formula is =A1 -> writes tRefNV token
1770 2) List is taken from A1 -> formula is =A1 -> writes tRefNR token
1771 Formula compiler supports this by offering two different functions
1772 CreateDataValFormula() and CreateListValFormula(). */
1773 if(GetOutput() == EXC_OUTPUT_BINARY)
1774 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_LISTVAL, *xScTokArr );
1775 else
1776 msFormula1 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
1777 xScTokArr.get());
1780 else
1782 // no list validation -> convert the formula
1783 if(GetOutput() == EXC_OUTPUT_BINARY)
1784 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
1785 else
1786 msFormula1 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
1787 xScTokArr.get());
1791 // second formula
1792 xScTokArr = pValData->CreateFlatCopiedTokenArray( 1 );
1793 if (xScTokArr)
1795 if(GetOutput() == EXC_OUTPUT_BINARY)
1796 mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
1797 else
1798 msFormula2 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
1799 xScTokArr.get());
1802 else
1804 OSL_FAIL( "XclExpDV::XclExpDV - missing core data" );
1805 mnScHandle = ULONG_MAX;
1809 XclExpDV::~XclExpDV()
1813 void XclExpDV::InsertCellRange( const ScRange& rRange )
1815 maScRanges.Join( rRange );
1818 bool XclExpDV::Finalize()
1820 GetAddressConverter().ConvertRangeList( maXclRanges, maScRanges, true );
1821 return (mnScHandle != ULONG_MAX) && !maXclRanges.empty();
1824 void XclExpDV::WriteBody( XclExpStream& rStrm )
1826 // flags and strings
1827 rStrm << mnFlags << maPromptTitle << maErrorTitle << maPromptText << maErrorText;
1828 // condition formulas
1829 if( mxString1.get() )
1830 lclWriteDvFormula( rStrm, *mxString1 );
1831 else
1832 lclWriteDvFormula( rStrm, mxTokArr1.get() );
1833 lclWriteDvFormula( rStrm, mxTokArr2.get() );
1834 // cell ranges
1835 rStrm << maXclRanges;
1838 void XclExpDV::SaveXml( XclExpXmlStream& rStrm )
1840 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1841 rWorksheet->startElement( XML_dataValidation,
1842 XML_allowBlank, ToPsz( ::get_flag( mnFlags, EXC_DV_IGNOREBLANK ) ),
1843 XML_error, XESTRING_TO_PSZ( maErrorText ),
1844 // OOXTODO: XML_errorStyle,
1845 XML_errorTitle, XESTRING_TO_PSZ( maErrorTitle ),
1846 // OOXTODO: XML_imeMode,
1847 XML_operator, lcl_GetOperatorType( mnFlags ),
1848 XML_prompt, XESTRING_TO_PSZ( maPromptText ),
1849 XML_promptTitle, XESTRING_TO_PSZ( maPromptTitle ),
1850 // showDropDown should have been showNoDropDown - check oox/xlsx import for details
1851 XML_showDropDown, ToPsz( ::get_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN ) ),
1852 XML_showErrorMessage, ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWERROR ) ),
1853 XML_showInputMessage, ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWPROMPT ) ),
1854 XML_sqref, XclXmlUtils::ToOString(&rStrm.GetRoot().GetDoc(), maScRanges),
1855 XML_type, lcl_GetValidationType(mnFlags) );
1856 if( !msFormula1.isEmpty() )
1858 rWorksheet->startElement(XML_formula1);
1859 rWorksheet->writeEscaped( msFormula1 );
1860 rWorksheet->endElement( XML_formula1 );
1862 if( !msFormula2.isEmpty() )
1864 rWorksheet->startElement(XML_formula2);
1865 rWorksheet->writeEscaped( msFormula2 );
1866 rWorksheet->endElement( XML_formula2 );
1868 rWorksheet->endElement( XML_dataValidation );
1871 XclExpDval::XclExpDval( const XclExpRoot& rRoot ) :
1872 XclExpRecord( EXC_ID_DVAL, 18 ),
1873 XclExpRoot( rRoot )
1877 XclExpDval::~XclExpDval()
1881 void XclExpDval::InsertCellRange( const ScRange& rRange, sal_uLong nScHandle )
1883 if( GetBiff() == EXC_BIFF8 )
1885 XclExpDV& rDVRec = SearchOrCreateDv( nScHandle );
1886 rDVRec.InsertCellRange( rRange );
1890 void XclExpDval::Save( XclExpStream& rStrm )
1892 // check all records
1893 size_t nPos = maDVList.GetSize();
1894 while( nPos )
1896 --nPos; // backwards to keep nPos valid
1897 XclExpDVRef xDVRec = maDVList.GetRecord( nPos );
1898 if( !xDVRec->Finalize() )
1899 maDVList.RemoveRecord( nPos );
1902 // write the DVAL and the DV's
1903 if( !maDVList.IsEmpty() )
1905 XclExpRecord::Save( rStrm );
1906 maDVList.Save( rStrm );
1910 void XclExpDval::SaveXml( XclExpXmlStream& rStrm )
1912 if( maDVList.IsEmpty() )
1913 return;
1915 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1916 rWorksheet->startElement( XML_dataValidations,
1917 XML_count, OString::number(maDVList.GetSize())
1918 // OOXTODO: XML_disablePrompts,
1919 // OOXTODO: XML_xWindow,
1920 // OOXTODO: XML_yWindow
1922 maDVList.SaveXml( rStrm );
1923 rWorksheet->endElement( XML_dataValidations );
1926 XclExpDV& XclExpDval::SearchOrCreateDv( sal_uLong nScHandle )
1928 // test last found record
1929 if( mxLastFoundDV.get() && (mxLastFoundDV->GetScHandle() == nScHandle) )
1930 return *mxLastFoundDV;
1932 // binary search
1933 size_t nCurrPos = 0;
1934 if( !maDVList.IsEmpty() )
1936 size_t nFirstPos = 0;
1937 size_t nLastPos = maDVList.GetSize() - 1;
1938 bool bLoop = true;
1939 sal_uLong nCurrScHandle = ::std::numeric_limits< sal_uLong >::max();
1940 while( (nFirstPos <= nLastPos) && bLoop )
1942 nCurrPos = (nFirstPos + nLastPos) / 2;
1943 mxLastFoundDV = maDVList.GetRecord( nCurrPos );
1944 nCurrScHandle = mxLastFoundDV->GetScHandle();
1945 if( nCurrScHandle == nScHandle )
1946 bLoop = false;
1947 else if( nCurrScHandle < nScHandle )
1948 nFirstPos = nCurrPos + 1;
1949 else if( nCurrPos )
1950 nLastPos = nCurrPos - 1;
1951 else // special case for nLastPos = -1
1952 bLoop = false;
1954 if( nCurrScHandle == nScHandle )
1955 return *mxLastFoundDV;
1956 else if( nCurrScHandle < nScHandle )
1957 ++nCurrPos;
1960 // create new DV record
1961 mxLastFoundDV.reset( new XclExpDV( *this, nScHandle ) );
1962 maDVList.InsertRecord( mxLastFoundDV, nCurrPos );
1963 return *mxLastFoundDV;
1966 void XclExpDval::WriteBody( XclExpStream& rStrm )
1968 rStrm.WriteZeroBytes( 10 );
1969 rStrm << EXC_DVAL_NOOBJ << static_cast< sal_uInt32 >( maDVList.GetSize() );
1972 // Web Queries ================================================================
1974 XclExpWebQuery::XclExpWebQuery(
1975 const OUString& rRangeName,
1976 const OUString& rUrl,
1977 const OUString& rSource,
1978 sal_Int32 nRefrSecs ) :
1979 maDestRange( rRangeName ),
1980 maUrl( rUrl ),
1981 // refresh delay time: seconds -> minutes
1982 mnRefresh( ulimit_cast< sal_Int16 >( (nRefrSecs + 59) / 60 ) ),
1983 mbEntireDoc( false )
1985 // comma separated list of HTML table names or indexes
1986 OUString aNewTables;
1987 OUString aAppendTable;
1988 bool bExitLoop = false;
1989 if (!rSource.isEmpty())
1991 sal_Int32 nStringIx = 0;
1994 OUString aToken( rSource.getToken( 0, ';', nStringIx ) );
1995 mbEntireDoc = ScfTools::IsHTMLDocName( aToken );
1996 bExitLoop = mbEntireDoc || ScfTools::IsHTMLTablesName( aToken );
1997 if( !bExitLoop && ScfTools::GetHTMLNameFromName( aToken, aAppendTable ) )
1998 aNewTables = ScGlobal::addToken( aNewTables, aAppendTable, ',' );
2000 while (nStringIx>0 && !bExitLoop);
2003 if( !bExitLoop ) // neither HTML_all nor HTML_tables found
2005 if( !aNewTables.isEmpty() )
2006 mxQryTables.reset( new XclExpString( aNewTables ) );
2007 else
2008 mbEntireDoc = true;
2012 XclExpWebQuery::~XclExpWebQuery()
2016 void XclExpWebQuery::Save( XclExpStream& rStrm )
2018 OSL_ENSURE( !mbEntireDoc || !mxQryTables.get(), "XclExpWebQuery::Save - illegal mode" );
2019 sal_uInt16 nFlags;
2021 // QSI record
2022 rStrm.StartRecord( EXC_ID_QSI, 10 + maDestRange.GetSize() );
2023 rStrm << EXC_QSI_DEFAULTFLAGS
2024 << sal_uInt16( 0x0010 )
2025 << sal_uInt16( 0x0012 )
2026 << sal_uInt32( 0x00000000 )
2027 << maDestRange;
2028 rStrm.EndRecord();
2030 // PARAMQRY record
2031 nFlags = 0;
2032 ::insert_value( nFlags, EXC_PQRYTYPE_WEBQUERY, 0, 3 );
2033 ::set_flag( nFlags, EXC_PQRY_WEBQUERY );
2034 ::set_flag( nFlags, EXC_PQRY_TABLES, !mbEntireDoc );
2035 rStrm.StartRecord( EXC_ID_PQRY, 12 );
2036 rStrm << nFlags
2037 << sal_uInt16( 0x0000 )
2038 << sal_uInt16( 0x0001 );
2039 rStrm.WriteZeroBytes( 6 );
2040 rStrm.EndRecord();
2042 // WQSTRING record
2043 rStrm.StartRecord( EXC_ID_WQSTRING, maUrl.GetSize() );
2044 rStrm << maUrl;
2045 rStrm.EndRecord();
2047 // unknown record 0x0802
2048 rStrm.StartRecord( EXC_ID_0802, 16 + maDestRange.GetSize() );
2049 rStrm << EXC_ID_0802; // repeated record id ?!?
2050 rStrm.WriteZeroBytes( 6 );
2051 rStrm << sal_uInt16( 0x0003 )
2052 << sal_uInt32( 0x00000000 )
2053 << sal_uInt16( 0x0010 )
2054 << maDestRange;
2055 rStrm.EndRecord();
2057 // WEBQRYSETTINGS record
2058 nFlags = mxQryTables.get() ? EXC_WQSETT_SPECTABLES : EXC_WQSETT_ALL;
2059 rStrm.StartRecord( EXC_ID_WQSETT, 28 );
2060 rStrm << EXC_ID_WQSETT // repeated record id ?!?
2061 << sal_uInt16( 0x0000 )
2062 << sal_uInt16( 0x0004 )
2063 << sal_uInt16( 0x0000 )
2064 << EXC_WQSETT_DEFAULTFLAGS
2065 << nFlags;
2066 rStrm.WriteZeroBytes( 10 );
2067 rStrm << mnRefresh // refresh delay in minutes
2068 << EXC_WQSETT_FORMATFULL
2069 << sal_uInt16( 0x0000 );
2070 rStrm.EndRecord();
2072 // WEBQRYTABLES record
2073 if( mxQryTables.get() )
2075 rStrm.StartRecord( EXC_ID_WQTABLES, 4 + mxQryTables->GetSize() );
2076 rStrm << EXC_ID_WQTABLES // repeated record id ?!?
2077 << sal_uInt16( 0x0000 )
2078 << *mxQryTables; // comma separated list of source tables
2079 rStrm.EndRecord();
2083 XclExpWebQueryBuffer::XclExpWebQueryBuffer( const XclExpRoot& rRoot )
2085 SCTAB nScTab = rRoot.GetCurrScTab();
2086 SfxObjectShell* pShell = rRoot.GetDocShell();
2087 if( !pShell ) return;
2088 ScfPropertySet aModelProp( pShell->GetModel() );
2089 if( !aModelProp.Is() ) return;
2091 Reference< XAreaLinks > xAreaLinks;
2092 aModelProp.GetProperty( xAreaLinks, SC_UNO_AREALINKS );
2093 if( !xAreaLinks.is() ) return;
2095 for( sal_Int32 nIndex = 0, nCount = xAreaLinks->getCount(); nIndex < nCount; ++nIndex )
2097 Reference< XAreaLink > xAreaLink( xAreaLinks->getByIndex( nIndex ), UNO_QUERY );
2098 if( xAreaLink.is() )
2100 CellRangeAddress aDestRange( xAreaLink->getDestArea() );
2101 if( static_cast< SCTAB >( aDestRange.Sheet ) == nScTab )
2103 ScfPropertySet aLinkProp( xAreaLink );
2104 OUString aFilter;
2105 if( aLinkProp.GetProperty( aFilter, SC_UNONAME_FILTER ) &&
2106 (aFilter == EXC_WEBQRY_FILTER) )
2108 // get properties
2109 OUString /*aFilterOpt,*/ aUrl;
2110 sal_Int32 nRefresh = 0;
2112 // aLinkProp.GetProperty( aFilterOpt, SC_UNONAME_FILTOPT );
2113 aLinkProp.GetProperty( aUrl, SC_UNONAME_LINKURL );
2114 aLinkProp.GetProperty( nRefresh, SC_UNONAME_REFDELAY );
2116 OUString aAbsDoc( ScGlobal::GetAbsDocName( aUrl, pShell ) );
2117 INetURLObject aUrlObj( aAbsDoc );
2118 OUString aWebQueryUrl( aUrlObj.getFSysPath( FSysStyle::Dos ) );
2119 if( aWebQueryUrl.isEmpty() )
2120 aWebQueryUrl = aAbsDoc;
2122 // find range or create a new range
2123 OUString aRangeName;
2124 ScRange aScDestRange;
2125 ScUnoConversion::FillScRange( aScDestRange, aDestRange );
2126 if( const ScRangeData* pRangeData = rRoot.GetNamedRanges().findByRange( aScDestRange ) )
2128 aRangeName = pRangeData->GetName();
2130 else
2132 XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
2133 XclExpNameManager& rNameMgr = rRoot.GetNameManager();
2135 // create a new unique defined name containing the range
2136 XclTokenArrayRef xTokArr = rFmlaComp.CreateFormula( EXC_FMLATYPE_WQUERY, aScDestRange );
2137 sal_uInt16 nNameIdx = rNameMgr.InsertUniqueName( aUrlObj.getBase(), xTokArr, nScTab );
2138 aRangeName = rNameMgr.GetOrigName( nNameIdx );
2141 // create and store the web query record
2142 if( !aRangeName.isEmpty() )
2143 AppendNewRecord( new XclExpWebQuery(
2144 aRangeName, aWebQueryUrl, xAreaLink->getSourceArea(), nRefresh ) );
2151 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */