1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
21 #include <xecontent.hxx>
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>
49 #include <xlcontent.hxx>
50 #include <xltools.hxx>
51 #include <xeformula.hxx>
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 ========================================================
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. */
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
);
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
),
123 sal_uInt32
XclExpSstImpl::Insert( XclExpStringRef xString
)
125 OSL_ENSURE( xString
, "XclExpSstImpl::Insert - empty pointer not allowed" );
127 xString
.reset( new XclExpString
);
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
) )
142 maStringVector
.push_back( xString
);
143 rVec
.insert( aIt
, aEntry
);
148 nSstIndex
= aIt
->mnSstIndex
;
154 void XclExpSstImpl::Save( XclExpStream
& rStrm
)
156 if( maStringVector
.empty() )
159 SvMemoryStream
aExtSst( 8192 );
161 sal_uInt32 nBucket
= mnSize
;
162 while( nBucket
> 0x0100 )
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
)
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
187 if( ++nBucketIndex
== nPerBucket
)
193 // *** write the EXTSST record ***
195 rStrm
.StartRecord( EXC_ID_EXTSST
, 0 );
198 rStrm
.SetSliceSize( 8 ); // size of one bucket info
199 aExtSst
.Seek( STREAM_SEEK_TO_BEGIN
);
200 rStrm
.CopyFromStream( aExtSst
);
205 void XclExpSstImpl::SaveXml( XclExpXmlStream
& rStrm
)
207 if( maStringVector
.empty() )
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
);
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
) :
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
) )
286 return EXC_XFID_NOTFOUND
;
289 void XclExpMergedcells::Save( XclExpStream
& rStrm
)
291 if( GetBiff() != EXC_BIFF8
)
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
);
304 nFirstRange
+= nRangeCount
;
305 nRemainingRanges
-= nRangeCount
;
309 void XclExpMergedcells::SaveXml( XclExpXmlStream
& rStrm
)
311 size_t nCount
= maMergedRanges
.size();
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
),
330 mxVarData( new SvMemoryStream
),
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.
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
;
353 if( eProtocol
== INetProtocol::File
|| eProtocol
== INetProtocol::Smb
)
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('/', '\\');
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
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())
390 msTarget
= XclXmlUtils::ToOUString( aLink
);
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())
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
)
428 aTextMark
= aTextMark
.replaceAt( nSepPos
, 1, u
"!" );
433 OUString
aSheetName(aTextMark
.copy(0, nSepPos
));
435 if (aSheetName
.indexOf(' ') != -1 && aSheetName
[0] != '\'')
437 aTextMark
= "'" + aTextMark
.replaceAt(nSepPos
, 0, u
"'");
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 ) );
451 if( !mxTextMark
&& aUrlObj
.HasMark() )
452 mxTextMark
.reset( new XclExpString( aUrlObj
.GetMark(), XclStrFlags::ForceUnicode
, 255 ) );
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
));
481 rbRel
= rRoot
.IsRelUrl();
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
497 else if (aDosName
.startsWith("./"))
499 aDosName
= aDosName
.copy(2);
503 while (aDosName
.startsWith("../"))
505 aDosName
= aDosName
.copy(3);
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
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
;
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
) :
552 SCTAB nScTab
= GetCurrScTab();
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() );
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
;
592 // Conditional formatting ====================================================
594 /** Represents a CF record that contains one condition of a conditional format. */
595 class XclExpCFImpl
: protected XclExpRoot
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
);
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.
628 XclExpCFImpl::XclExpCFImpl( const XclExpRoot
& rRoot
, const ScCondFormatEntry
& rFormatEntry
, sal_Int32 nPriority
, ScAddress aOrigin
) :
630 mrFormatEntry( rFormatEntry
),
633 mnType( EXC_CF_TYPE_CELL
),
634 mnOperator( EXC_CF_CMP_NONE
),
635 mnPriority( nPriority
),
637 mbHeightUsed( false ),
638 mbWeightUsed( false ),
639 mbColorUsed( false ),
640 mbUnderlUsed( false ),
641 mbItalicUsed( false ),
642 mbStrikeUsed( false ),
643 mbBorderUsed( 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();
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
;
669 ScPatternAttr::GetFont( aFont
, rItemSet
, SC_AUTOCOL_RAW
);
670 maFontData
.FillFromVclFont( aFont
);
671 mnFontColorId
= GetPalette().InsertColor( maFontData
.maColor
, EXC_COLOR_CELLTEXT
);
675 mbBorderUsed
= ScfTools::CheckItem( rItemSet
, ATTR_BORDER
, true );
677 maBorder
.FillFromItemSet( rItemSet
, GetPalette(), GetBiff() );
680 mbPattUsed
= ScfTools::CheckItem( rItemSet
, ATTR_BACKGROUND
, true );
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
;
692 case ScConditionMode::Between
:
693 mnOperator
= EXC_CF_CMP_BETWEEN
;
696 case ScConditionMode::NotBetween
:
697 mnOperator
= EXC_CF_CMP_NOT_BETWEEN
;
700 case ScConditionMode::Equal
:
701 mnOperator
= EXC_CF_CMP_EQUAL
;
703 case ScConditionMode::NotEqual
:
704 mnOperator
= EXC_CF_CMP_NOT_EQUAL
;
706 case ScConditionMode::Greater
:
707 mnOperator
= EXC_CF_CMP_GREATER
;
709 case ScConditionMode::Less
:
710 mnOperator
= EXC_CF_CMP_LESS
;
712 case ScConditionMode::EqGreater
:
713 mnOperator
= EXC_CF_CMP_GREATER_EQUAL
;
715 case ScConditionMode::EqLess
:
716 mnOperator
= EXC_CF_CMP_LESS_EQUAL
;
718 case ScConditionMode::Direct
:
719 mnType
= EXC_CF_TYPE_FMLA
;
722 mnType
= EXC_CF_TYPE_NONE
;
723 OSL_FAIL( "XclExpCF::WriteBody - unknown condition type" );
727 void XclExpCFImpl::WriteBody( XclExpStream
& rStrm
)
732 XclExpFormulaCompiler
& rFmlaComp
= GetFormulaCompiler();
734 std::unique_ptr
< ScTokenArray
> xScTokArr( mrFormatEntry
.CreateFlatCopiedTokenArray( 0 ) );
735 mxTokArr1
= rFmlaComp
.CreateFormula( EXC_FMLATYPE_CONDFMT
, *xScTokArr
);
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 );
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 );
789 << maFontData
.mnWeight
791 << maFontData
.mnUnderline
;
792 rStrm
.WriteZeroBytesToRecord( 3 );
796 << EXC_CF_FONT_ESCAPEM
// escapement never used -> set the flag
798 rStrm
.WriteZeroBytesToRecord( 16 );
799 rStrm
<< sal_uInt16( 1 ); // must be 1
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 );
813 sal_uInt16 nPattern
= 0, nColor
= 0;
814 maArea
.SetFinalColors( GetPalette() );
815 maArea
.FillToCF8( nPattern
, nColor
);
816 rStrm
<< nPattern
<< nColor
;
821 // no data blocks at all
822 rStrm
<< sal_uInt32( 0 ) << sal_uInt16( 0 );
828 mxTokArr1
->WriteArray( rStrm
);
830 mxTokArr2
->WriteArray( rStrm
);
835 const char* GetOperatorString(ScConditionMode eMode
, bool& bFrmla2
)
837 const char *pRet
= nullptr;
840 case ScConditionMode::Equal
:
843 case ScConditionMode::Less
:
846 case ScConditionMode::Greater
:
847 pRet
= "greaterThan";
849 case ScConditionMode::EqLess
:
850 pRet
= "lessThanOrEqual";
852 case ScConditionMode::EqGreater
:
853 pRet
= "greaterThanOrEqual";
855 case ScConditionMode::NotEqual
:
858 case ScConditionMode::Between
:
862 case ScConditionMode::NotBetween
:
866 case ScConditionMode::Duplicate
:
869 case ScConditionMode::NotDuplicate
:
872 case ScConditionMode::BeginsWith
:
875 case ScConditionMode::EndsWith
:
878 case ScConditionMode::ContainsText
:
879 pRet
= "containsText";
881 case ScConditionMode::NotContainsText
:
882 pRet
= "notContains";
884 case ScConditionMode::Direct
:
886 case ScConditionMode::NONE
:
893 const char* GetTypeString(ScConditionMode eMode
)
897 case ScConditionMode::Direct
:
899 case ScConditionMode::Top10
:
900 case ScConditionMode::TopPercent
:
901 case ScConditionMode::Bottom10
:
902 case ScConditionMode::BottomPercent
:
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
:
919 case ScConditionMode::EndsWith
:
921 case ScConditionMode::ContainsText
:
922 return "containsText";
923 case ScConditionMode::NotContainsText
:
924 return "notContainsText";
930 bool IsTopBottomRule(ScConditionMode eMode
)
934 case ScConditionMode::Top10
:
935 case ScConditionMode::Bottom10
:
936 case ScConditionMode::TopPercent
:
937 case ScConditionMode::BottomPercent
:
946 bool IsTextRule(ScConditionMode eMode
)
950 case ScConditionMode::BeginsWith
:
951 case ScConditionMode::EndsWith
:
952 case ScConditionMode::ContainsText
:
953 case ScConditionMode::NotContainsText
:
962 bool RequiresFormula(ScConditionMode eMode
)
964 if (IsTopBottomRule(eMode
))
966 else if (IsTextRule(eMode
))
971 case ScConditionMode::NoError
:
972 case ScConditionMode::Error
:
973 case ScConditionMode::Duplicate
:
974 case ScConditionMode::NotDuplicate
:
983 bool RequiresFixedFormula(ScConditionMode 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
:
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();
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
+ "))");
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);
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
),
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
);
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()
1134 const char* getTimePeriodString( condformat::ScCondFormatDateType eType
)
1138 case condformat::TODAY
:
1140 case condformat::YESTERDAY
:
1142 case condformat::TOMORROW
:
1144 case condformat::THISWEEK
:
1146 case condformat::LASTWEEK
:
1148 case condformat::NEXTWEEK
:
1150 case condformat::THISMONTH
:
1152 case condformat::LASTMONTH
:
1154 case condformat::NEXTMONTH
:
1156 case condformat::LAST7DAYS
:
1166 void XclExpDateFormat::SaveXml( XclExpXmlStream
& rStrm
)
1168 // only write the supported entries into OOXML
1169 const char* sTimePeriod
= getTimePeriodString(mrFormatEntry
.GetDateType());
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
),
1192 OString
getColorScaleType( const ScColorScaleEntry
& rEntry
, bool bFirst
)
1194 switch(rEntry
.GetType())
1196 case COLORSCALE_MIN
:
1198 case COLORSCALE_MAX
:
1200 case COLORSCALE_PERCENT
:
1202 case COLORSCALE_FORMULA
:
1204 case COLORSCALE_AUTO
:
1209 case COLORSCALE_PERCENTILE
:
1210 return "percentile";
1220 void XclExpCfvo::SaveXml( XclExpXmlStream
& rStrm
)
1222 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1225 if(mrEntry
.GetType() == COLORSCALE_FORMULA
)
1227 OUString aFormula
= XclXmlUtils::ToOUString( GetCompileFormulaContext(), maSrcPos
,
1228 mrEntry
.GetFormula());
1229 aValue
= OUStringToOString(aFormula
, RTL_TEXTENCODING_UTF8
);
1233 aValue
= OString::number( mrEntry
.GetValue() );
1236 rWorksheet
->startElement( XML_cfvo
,
1237 XML_type
, getColorScaleType(mrEntry
, mbFirst
),
1240 rWorksheet
->endElement( XML_cfvo
);
1243 XclExpColScaleCol::XclExpColScaleCol( const XclExpRoot
& rRoot
, const Color
& rColor
):
1244 XclExpRoot( rRoot
),
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
);
1264 OString
createHexStringFromDigit(sal_uInt8 nDigit
)
1266 OString aString
= OString::number( nDigit
, 16 );
1267 if(aString
.getLength() == 1)
1268 aString
+= OString::number(0);
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
),
1300 const ScRangeList
& aScRanges
= rCondFormat
.GetRange();
1301 GetAddressConverter().ConvertRangeList( maXclRanges
, aScRanges
, true );
1302 if( maXclRanges
.empty() )
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
:
1354 bNeedsExt
|= rIconSet
.GetIconSetData()->mbCustom
;
1358 XclExpExtCondFormatData aExtEntry
;
1359 aExtEntry
.nPriority
= ++rIndex
;
1360 aExtEntry
.aGUID
= generateGUIDString();
1361 aExtEntry
.pEntry
= &rIconSet
;
1362 aExtEntries
.push_back(aExtEntry
);
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
);
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() )
1420 << maXclRanges
.GetEnclosingRange()
1424 void XclExpCondfmt::SaveXml( XclExpXmlStream
& rStrm
)
1426 if( !IsValidForXml() )
1429 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1430 rWorksheet
->startElement( XML_conditionalFormatting
,
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
),
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
) :
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 =================================================================
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 );
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 )
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";
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";
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";
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() );
1666 rValidationText
.Assign( rText
);
1669 rValidationText
.Assign( '\0' );
1674 XclExpDV::XclExpDV( const XclExpRoot
& rRoot
, sal_uLong nScHandle
) :
1675 XclExpRecord( EXC_ID_DV
),
1676 XclExpRoot( rRoot
),
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
);
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
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
);
1739 XclExpFormulaCompiler
& rFmlaComp
= GetFormulaCompiler();
1742 std::unique_ptr
< ScTokenArray
> xScTokArr
= pValData
->CreateFlatCopiedTokenArray( 0 );
1745 if( pValData
->GetDataMode() == SC_VALID_LIST
)
1748 if( XclTokenArrayHelper::GetStringList( aString
, *xScTokArr
, '\n' ) )
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;
1763 const OUString
aToken( aString
.getToken( 0, '\n', nStringIx
) );
1764 if (aToken
.indexOf(",") != -1)
1766 sListBuf
.append('"');
1767 sListBuf
.append(aToken
);
1768 sListBuf
.append('"');
1772 sListBuf
.append(aToken
);
1773 mxString1
->Append( aToken
);
1774 sFormulaBuf
.append( aToken
);
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
1791 if( sFormulaBuf
[nLen
- 1] == ',' )
1793 sFormulaBuf
.truncate(nLen
);
1796 sFormulaBuf
.append( '"' );
1797 msFormula1
= sFormulaBuf
.makeStringAndClear();
1799 msList
= sListBuf
.makeStringAndClear();
1801 sListBuf
.remove(0, sListBuf
.getLength());
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
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
);
1817 msFormula1
= XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData
->GetSrcPos(),
1823 // no list validation -> convert the formula
1824 if(GetOutput() == EXC_OUTPUT_BINARY
)
1825 mxTokArr1
= rFmlaComp
.CreateFormula( EXC_FMLATYPE_DATAVAL
, *xScTokArr
);
1827 msFormula1
= XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData
->GetSrcPos(),
1833 xScTokArr
= pValData
->CreateFlatCopiedTokenArray( 1 );
1836 if(GetOutput() == EXC_OUTPUT_BINARY
)
1837 mxTokArr2
= rFmlaComp
.CreateFormula( EXC_FMLATYPE_DATAVAL
, *xScTokArr
);
1839 msFormula2
= XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData
->GetSrcPos(),
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
1871 lclWriteDvFormula( rStrm
, *mxString1
);
1873 lclWriteDvFormula( rStrm
, mxTokArr1
.get() );
1874 lclWriteDvFormula( rStrm
, mxTokArr2
.get() );
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 ),
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();
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() )
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
;
1991 size_t nCurrPos
= 0;
1992 if( !maDVList
.IsEmpty() )
1994 size_t nFirstPos
= 0;
1995 size_t nLastPos
= maDVList
.GetSize() - 1;
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
)
2005 else if( nCurrScHandle
< nScHandle
)
2006 nFirstPos
= nCurrPos
+ 1;
2008 nLastPos
= nCurrPos
- 1;
2009 else // special case for nLastPos = -1
2012 if( nCurrScHandle
== nScHandle
)
2013 return *mxLastFoundDV
;
2014 else if( nCurrScHandle
< nScHandle
)
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
),
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
) );
2070 XclExpWebQuery::~XclExpWebQuery()
2074 void XclExpWebQuery::Save( XclExpStream
& rStrm
)
2076 OSL_ENSURE( !mbEntireDoc
|| !mxQryTables
, "XclExpWebQuery::Save - illegal mode" );
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 )
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 );
2095 << sal_uInt16( 0x0000 )
2096 << sal_uInt16( 0x0001 );
2097 rStrm
.WriteZeroBytes( 6 );
2101 rStrm
.StartRecord( EXC_ID_WQSTRING
, maUrl
.GetSize() );
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 )
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
2124 rStrm
.WriteZeroBytes( 10 );
2125 rStrm
<< mnRefresh
// refresh delay in minutes
2126 << EXC_WQSETT_FORMATFULL
2127 << sal_uInt16( 0x0000 );
2130 // WEBQRYTABLES record
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
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
);
2163 if( aLinkProp
.GetProperty( aFilter
, SC_UNONAME_FILTER
) &&
2164 (aFilter
== EXC_WEBQRY_FILTER
) )
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();
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: */