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 <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>
46 #include <xlcontent.hxx>
47 #include <xltools.hxx>
48 #include <xeformula.hxx>
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. */
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
);
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
),
116 sal_uInt32
XclExpSstImpl::Insert( XclExpStringRef xString
)
118 OSL_ENSURE( xString
.get(), "XclExpSstImpl::Insert - empty pointer not allowed" );
120 xString
.reset( new XclExpString
);
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
) )
135 maStringVector
.push_back( xString
);
136 rVec
.insert( aIt
, aEntry
);
141 nSstIndex
= aIt
->mnSstIndex
;
147 void XclExpSstImpl::Save( XclExpStream
& rStrm
)
149 if( maStringVector
.empty() )
152 SvMemoryStream
aExtSst( 8192 );
154 sal_uInt32 nBucket
= mnSize
;
155 while( nBucket
> 0x0100 )
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
)
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
180 if( ++nBucketIndex
== nPerBucket
)
186 // *** write the EXTSST record ***
188 rStrm
.StartRecord( EXC_ID_EXTSST
, 0 );
191 rStrm
.SetSliceSize( 8 ); // size of one bucket info
192 aExtSst
.Seek( STREAM_SEEK_TO_BEGIN
);
193 rStrm
.CopyFromStream( aExtSst
);
198 void XclExpSstImpl::SaveXml( XclExpXmlStream
& rStrm
)
200 if( maStringVector
.empty() )
203 sax_fastparser::FSHelperPtr pSst
= rStrm
.CreateOutputStream(
204 "xl/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
);
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
) :
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
) )
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
);
296 nFirstRange
+= nRangeCount
;
297 nRemainingRanges
-= nRangeCount
;
302 void XclExpMergedcells::SaveXml( XclExpXmlStream
& rStrm
)
304 size_t nCount
= maMergedRanges
.size();
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
),
323 mxVarData( new SvMemoryStream
),
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.
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
;
346 if( eProtocol
== INetProtocol::File
|| eProtocol
== INetProtocol::Smb
)
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('/', '\\');
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
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())
384 msTarget
= XclXmlUtils::ToOUString( aLink
);
388 for( int i
= 0; i
< nLevel
; ++i
)
389 msTarget
= "../" + msTarget
;
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())
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
)
422 aTextMark
= aTextMark
.replaceAt( 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 ) );
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
));
469 rbRel
= rRoot
.IsRelUrl();
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
485 else if (aDosName
.startsWith("./"))
487 aDosName
= aDosName
.copy(2);
491 while (aDosName
.startsWith("../"))
493 aDosName
= aDosName
.copy(3);
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
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()
529 XML_location
, mxTextMark
.get() != nullptr
530 ? XclXmlUtils::ToOString( *mxTextMark
).getStr()
532 // OOXTODO: XML_tooltip, from record HLinkTooltip 800h wzTooltip
533 XML_display
, m_Repr
.toUtf8() );
536 // Label ranges ===============================================================
538 XclExpLabelranges::XclExpLabelranges( const XclExpRoot
& rRoot
) :
541 SCTAB nScTab
= GetCurrScTab();
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() );
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
;
581 // Conditional formatting ====================================================
583 /** Represents a CF record that contains one condition of a conditional format. */
584 class XclExpCFImpl
: protected XclExpRoot
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
);
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.
617 XclExpCFImpl::XclExpCFImpl( const XclExpRoot
& rRoot
, const ScCondFormatEntry
& rFormatEntry
, sal_Int32 nPriority
, ScAddress aOrigin
) :
619 mrFormatEntry( rFormatEntry
),
622 mnType( EXC_CF_TYPE_CELL
),
623 mnOperator( EXC_CF_CMP_NONE
),
624 mnPriority( nPriority
),
626 mbHeightUsed( false ),
627 mbWeightUsed( false ),
628 mbColorUsed( false ),
629 mbUnderlUsed( false ),
630 mbItalicUsed( false ),
631 mbStrikeUsed( false ),
632 mbBorderUsed( 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();
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
;
658 ScPatternAttr::GetFont( aFont
, rItemSet
, SC_AUTOCOL_RAW
);
659 maFontData
.FillFromVclFont( aFont
);
660 mnFontColorId
= GetPalette().InsertColor( maFontData
.maColor
, EXC_COLOR_CELLTEXT
);
664 mbBorderUsed
= ScfTools::CheckItem( rItemSet
, ATTR_BORDER
, true );
666 maBorder
.FillFromItemSet( rItemSet
, GetPalette(), GetBiff() );
669 mbPattUsed
= ScfTools::CheckItem( rItemSet
, ATTR_BACKGROUND
, true );
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
;
681 case ScConditionMode::Between
:
682 mnOperator
= EXC_CF_CMP_BETWEEN
;
685 case ScConditionMode::NotBetween
:
686 mnOperator
= EXC_CF_CMP_NOT_BETWEEN
;
689 case ScConditionMode::Equal
:
690 mnOperator
= EXC_CF_CMP_EQUAL
;
692 case ScConditionMode::NotEqual
:
693 mnOperator
= EXC_CF_CMP_NOT_EQUAL
;
695 case ScConditionMode::Greater
:
696 mnOperator
= EXC_CF_CMP_GREATER
;
698 case ScConditionMode::Less
:
699 mnOperator
= EXC_CF_CMP_LESS
;
701 case ScConditionMode::EqGreater
:
702 mnOperator
= EXC_CF_CMP_GREATER_EQUAL
;
704 case ScConditionMode::EqLess
:
705 mnOperator
= EXC_CF_CMP_LESS_EQUAL
;
707 case ScConditionMode::Direct
:
708 mnType
= EXC_CF_TYPE_FMLA
;
711 mnType
= EXC_CF_TYPE_NONE
;
712 OSL_FAIL( "XclExpCF::WriteBody - unknown condition type" );
716 void XclExpCFImpl::WriteBody( XclExpStream
& rStrm
)
721 XclExpFormulaCompiler
& rFmlaComp
= GetFormulaCompiler();
723 std::unique_ptr
< ScTokenArray
> xScTokArr( mrFormatEntry
.CreateFlatCopiedTokenArray( 0 ) );
724 mxTokArr1
= rFmlaComp
.CreateFormula( EXC_FMLATYPE_CONDFMT
, *xScTokArr
);
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 );
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 );
778 << maFontData
.mnWeight
780 << maFontData
.mnUnderline
;
781 rStrm
.WriteZeroBytesToRecord( 3 );
785 << EXC_CF_FONT_ESCAPEM
// escapement never used -> set the flag
787 rStrm
.WriteZeroBytesToRecord( 16 );
788 rStrm
<< sal_uInt16( 1 ); // must be 1
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 );
802 sal_uInt16 nPattern
= 0, nColor
= 0;
803 maArea
.SetFinalColors( GetPalette() );
804 maArea
.FillToCF8( nPattern
, nColor
);
805 rStrm
<< nPattern
<< nColor
;
810 // no data blocks at all
811 rStrm
<< sal_uInt32( 0 ) << sal_uInt16( 0 );
816 if( mxTokArr1
.get() )
817 mxTokArr1
->WriteArray( rStrm
);
818 if( mxTokArr2
.get() )
819 mxTokArr2
->WriteArray( rStrm
);
824 const char* GetOperatorString(ScConditionMode eMode
, bool& bFrmla2
)
826 const char *pRet
= nullptr;
829 case ScConditionMode::Equal
:
832 case ScConditionMode::Less
:
835 case ScConditionMode::Greater
:
836 pRet
= "greaterThan";
838 case ScConditionMode::EqLess
:
839 pRet
= "lessThanOrEqual";
841 case ScConditionMode::EqGreater
:
842 pRet
= "greaterThanOrEqual";
844 case ScConditionMode::NotEqual
:
847 case ScConditionMode::Between
:
851 case ScConditionMode::NotBetween
:
855 case ScConditionMode::Duplicate
:
858 case ScConditionMode::NotDuplicate
:
861 case ScConditionMode::BeginsWith
:
864 case ScConditionMode::EndsWith
:
867 case ScConditionMode::ContainsText
:
868 pRet
= "containsText";
870 case ScConditionMode::NotContainsText
:
871 pRet
= "notContains";
873 case ScConditionMode::Direct
:
875 case ScConditionMode::NONE
:
882 const char* GetTypeString(ScConditionMode eMode
)
886 case ScConditionMode::Direct
:
888 case ScConditionMode::Top10
:
889 case ScConditionMode::TopPercent
:
890 case ScConditionMode::Bottom10
:
891 case ScConditionMode::BottomPercent
:
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
:
908 case ScConditionMode::EndsWith
:
910 case ScConditionMode::ContainsText
:
911 return "containsText";
912 case ScConditionMode::NotContainsText
:
913 return "notContainsText";
919 bool IsTopBottomRule(ScConditionMode eMode
)
923 case ScConditionMode::Top10
:
924 case ScConditionMode::Bottom10
:
925 case ScConditionMode::TopPercent
:
926 case ScConditionMode::BottomPercent
:
935 bool IsTextRule(ScConditionMode eMode
)
939 case ScConditionMode::BeginsWith
:
940 case ScConditionMode::EndsWith
:
941 case ScConditionMode::ContainsText
:
942 case ScConditionMode::NotContainsText
:
951 bool RequiresFormula(ScConditionMode eMode
)
953 if (IsTopBottomRule(eMode
))
955 else if (IsTextRule(eMode
))
960 case ScConditionMode::NoError
:
961 case ScConditionMode::Error
:
962 case ScConditionMode::Duplicate
:
963 case ScConditionMode::NotDuplicate
:
972 bool RequiresFixedFormula(ScConditionMode 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
:
990 OString
GetFixedFormula(ScConditionMode eMode
, const ScAddress
& rAddress
, const OString
& rText
)
992 OStringBuffer aBuffer
;
993 XclXmlUtils::ToOString(aBuffer
, rAddress
);
994 OString aPos
= aBuffer
.makeStringAndClear();
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
+ "))");
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
;
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();
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
),
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
);
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()
1123 const char* getTimePeriodString( condformat::ScCondFormatDateType eType
)
1127 case condformat::TODAY
:
1129 case condformat::YESTERDAY
:
1131 case condformat::TOMORROW
:
1133 case condformat::THISWEEK
:
1135 case condformat::LASTWEEK
:
1137 case condformat::NEXTWEEK
:
1139 case condformat::THISMONTH
:
1141 case condformat::LASTMONTH
:
1143 case condformat::NEXTMONTH
:
1145 case condformat::LAST7DAYS
:
1155 void XclExpDateFormat::SaveXml( XclExpXmlStream
& rStrm
)
1157 // only write the supported entries into OOXML
1158 const char* sTimePeriod
= getTimePeriodString(mrFormatEntry
.GetDateType());
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
):
1173 XclExpRoot( rRoot
),
1182 OString
getColorScaleType( const ScColorScaleEntry
& rEntry
, bool bFirst
)
1184 switch(rEntry
.GetType())
1186 case COLORSCALE_MIN
:
1188 case COLORSCALE_MAX
:
1190 case COLORSCALE_PERCENT
:
1192 case COLORSCALE_FORMULA
:
1194 case COLORSCALE_AUTO
:
1199 case COLORSCALE_PERCENTILE
:
1200 return "percentile";
1210 void XclExpCfvo::SaveXml( XclExpXmlStream
& rStrm
)
1212 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1215 if(mrEntry
.GetType() == COLORSCALE_FORMULA
)
1217 OUString aFormula
= XclXmlUtils::ToOUString( GetCompileFormulaContext(), maSrcPos
,
1218 mrEntry
.GetFormula());
1219 aValue
= OUStringToOString(aFormula
, RTL_TEXTENCODING_UTF8
);
1223 aValue
= OString::number( mrEntry
.GetValue() );
1226 rWorksheet
->startElement( XML_cfvo
,
1227 XML_type
, getColorScaleType(mrEntry
, mbFirst
),
1230 rWorksheet
->endElement( XML_cfvo
);
1233 XclExpColScaleCol::XclExpColScaleCol( const XclExpRoot
& rRoot
, const Color
& rColor
):
1235 XclExpRoot( rRoot
),
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
);
1255 OString
createHexStringFromDigit(sal_uInt8 nDigit
)
1257 OString aString
= OString::number( nDigit
, 16 );
1258 if(aString
.getLength() == 1)
1259 aString
+= OString::number(0);
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
),
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
:
1344 bNeedsExt
|= rIconSet
.GetIconSetData()->mbCustom
;
1348 XclExpExtCondFormatData aExtEntry
;
1349 aExtEntry
.nPriority
= ++rIndex
;
1350 aExtEntry
.aGUID
= generateGUIDString();
1351 aExtEntry
.pEntry
= &rIconSet
;
1352 aExtEntries
.push_back(aExtEntry
);
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() )
1411 << maXclRanges
.GetEnclosingRange()
1415 void XclExpCondfmt::SaveXml( XclExpXmlStream
& rStrm
)
1417 if( !IsValidForXml() )
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
):
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
):
1469 XclExpRoot( rRoot
),
1470 mrFormat( rFormat
),
1471 mnPriority( nPriority
),
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
):
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
) :
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 =================================================================
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 );
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 )
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";
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";
1639 XclExpDV::XclExpDV( const XclExpRoot
& rRoot
, sal_uLong nScHandle
) :
1640 XclExpRecord( EXC_ID_DV
),
1641 XclExpRoot( rRoot
),
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
);
1653 maPromptTitle
.Assign( '\0' );
1654 if( !aText
.isEmpty() )
1655 maPromptText
.Assign( aText
);
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
);
1665 maErrorTitle
.Assign( '\0' );
1666 if( !aText
.isEmpty() )
1667 maErrorText
.Assign( aText
);
1669 maErrorText
.Assign( '\0' );
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
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
);
1716 XclExpFormulaCompiler
& rFmlaComp
= GetFormulaCompiler();
1719 std::unique_ptr
< ScTokenArray
> xScTokArr
= pValData
->CreateFlatCopiedTokenArray( 0 );
1722 if( pValData
->GetDataMode() == SC_VALID_LIST
)
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;
1738 const OUString
aToken( aString
.getToken( 0, '\n', nStringIx
) );
1739 mxString1
->Append( aToken
);
1740 sFormulaBuf
.append( aToken
);
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
1754 if( sFormulaBuf
[nLen
- 1] == ',' )
1756 sFormulaBuf
.truncate(nLen
);
1759 sFormulaBuf
.append( '"' );
1760 msFormula1
= sFormulaBuf
.makeStringAndClear();
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
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
);
1776 msFormula1
= XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData
->GetSrcPos(),
1782 // no list validation -> convert the formula
1783 if(GetOutput() == EXC_OUTPUT_BINARY
)
1784 mxTokArr1
= rFmlaComp
.CreateFormula( EXC_FMLATYPE_DATAVAL
, *xScTokArr
);
1786 msFormula1
= XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData
->GetSrcPos(),
1792 xScTokArr
= pValData
->CreateFlatCopiedTokenArray( 1 );
1795 if(GetOutput() == EXC_OUTPUT_BINARY
)
1796 mxTokArr2
= rFmlaComp
.CreateFormula( EXC_FMLATYPE_DATAVAL
, *xScTokArr
);
1798 msFormula2
= XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData
->GetSrcPos(),
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
);
1832 lclWriteDvFormula( rStrm
, mxTokArr1
.get() );
1833 lclWriteDvFormula( rStrm
, mxTokArr2
.get() );
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 ),
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();
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() )
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
;
1933 size_t nCurrPos
= 0;
1934 if( !maDVList
.IsEmpty() )
1936 size_t nFirstPos
= 0;
1937 size_t nLastPos
= maDVList
.GetSize() - 1;
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
)
1947 else if( nCurrScHandle
< nScHandle
)
1948 nFirstPos
= nCurrPos
+ 1;
1950 nLastPos
= nCurrPos
- 1;
1951 else // special case for nLastPos = -1
1954 if( nCurrScHandle
== nScHandle
)
1955 return *mxLastFoundDV
;
1956 else if( nCurrScHandle
< nScHandle
)
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
),
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
) );
2012 XclExpWebQuery::~XclExpWebQuery()
2016 void XclExpWebQuery::Save( XclExpStream
& rStrm
)
2018 OSL_ENSURE( !mbEntireDoc
|| !mxQryTables
.get(), "XclExpWebQuery::Save - illegal mode" );
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 )
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 );
2037 << sal_uInt16( 0x0000 )
2038 << sal_uInt16( 0x0001 );
2039 rStrm
.WriteZeroBytes( 6 );
2043 rStrm
.StartRecord( EXC_ID_WQSTRING
, maUrl
.GetSize() );
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 )
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
2066 rStrm
.WriteZeroBytes( 10 );
2067 rStrm
<< mnRefresh
// refresh delay in minutes
2068 << EXC_WQSETT_FORMATFULL
2069 << sal_uInt16( 0x0000 );
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
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
);
2105 if( aLinkProp
.GetProperty( aFilter
, SC_UNONAME_FILTER
) &&
2106 (aFilter
== EXC_WEBQRY_FILTER
) )
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();
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: */