merge the formfield patch from ooo-build
[ooovba.git] / sc / source / filter / excel / xecontent.cxx
blob4575fca49082e63588a5207939b923a3428733ce
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: xecontent.cxx,v $
10 * $Revision: 1.23 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
33 #include "xecontent.hxx"
35 #include <list>
36 #include <algorithm>
37 #include <com/sun/star/container/XIndexAccess.hpp>
38 #include <com/sun/star/frame/XModel.hpp>
39 #include <com/sun/star/sheet/XAreaLinks.hpp>
40 #include <com/sun/star/sheet/XAreaLink.hpp>
41 #include <sfx2/objsh.hxx>
42 #include <tools/urlobj.hxx>
43 #include <svtools/itemset.hxx>
44 #include <formula/grammar.hxx>
45 #include "scitems.hxx"
46 #include <svx/eeitem.hxx>
47 #include <svx/flditem.hxx>
48 #include "document.hxx"
49 #include "validat.hxx"
50 #include "unonames.hxx"
51 #include "convuno.hxx"
52 #include "rangenam.hxx"
53 #include "tokenarray.hxx"
54 #include "stlpool.hxx"
55 #include "patattr.hxx"
56 #include "fapihelper.hxx"
57 #include "xehelper.hxx"
58 #include "xestyle.hxx"
59 #include "xename.hxx"
61 #include <oox/core/tokens.hxx>
63 using ::com::sun::star::uno::Reference;
64 using ::com::sun::star::uno::Any;
65 using ::com::sun::star::uno::UNO_QUERY;
66 using ::com::sun::star::beans::XPropertySet;
67 using ::com::sun::star::container::XIndexAccess;
68 using ::com::sun::star::frame::XModel;
69 using ::com::sun::star::table::CellRangeAddress;
70 using ::com::sun::star::sheet::XAreaLinks;
71 using ::com::sun::star::sheet::XAreaLink;
72 using ::rtl::OString;
73 using ::rtl::OUString;
74 using ::rtl::OUStringBuffer;
76 // Shared string table ========================================================
78 // 1 = SST hash table statistics prompt
79 #define EXC_INCL_SST_STATISTICS 0
81 // ----------------------------------------------------------------------------
83 /** A single string entry in the hash table. */
84 struct XclExpHashEntry
86 const XclExpString* mpString; /// Pointer to the string (no ownership).
87 sal_uInt32 mnSstIndex; /// The SST index of this string.
88 inline explicit XclExpHashEntry( const XclExpString* pString = 0, sal_uInt32 nSstIndex = 0 ) :
89 mpString( pString ), mnSstIndex( nSstIndex ) {}
92 /** Function object for strict weak ordering. */
93 struct XclExpHashEntrySWO
95 inline bool operator()( const XclExpHashEntry& rLeft, const XclExpHashEntry& rRight ) const
96 { return *rLeft.mpString < *rRight.mpString; }
99 // ----------------------------------------------------------------------------
101 /** Implementation of the SST export.
102 @descr Stores all passed strings in a hash table and prevents repeated
103 insertion of equal strings. */
104 class XclExpSstImpl
106 public:
107 explicit XclExpSstImpl();
109 /** Inserts the passed string, if not already inserted, and returns the unique SST index. */
110 sal_uInt32 Insert( XclExpStringRef xString );
112 /** Writes the complete SST and EXTSST records. */
113 void Save( XclExpStream& rStrm );
114 void SaveXml( XclExpXmlStream& rStrm );
116 private:
117 typedef ::std::list< XclExpStringRef > XclExpStringList;
118 typedef ::std::vector< XclExpHashEntry > XclExpHashVec;
119 typedef ::std::vector< XclExpHashVec > XclExpHashTab;
121 XclExpStringList maStringList; /// List of unique strings (in SST ID order).
122 XclExpHashTab maHashTab; /// Hashed table that manages string pointers.
123 sal_uInt32 mnTotal; /// Total count of strings (including doubles).
124 sal_uInt32 mnSize; /// Size of the SST (count of unique strings).
127 // ----------------------------------------------------------------------------
129 const sal_uInt32 EXC_SST_HASHTABLE_SIZE = 2048;
131 XclExpSstImpl::XclExpSstImpl() :
132 maHashTab( EXC_SST_HASHTABLE_SIZE ),
133 mnTotal( 0 ),
134 mnSize( 0 )
138 sal_uInt32 XclExpSstImpl::Insert( XclExpStringRef xString )
140 DBG_ASSERT( xString.get(), "XclExpSstImpl::Insert - empty pointer not allowed" );
141 if( !xString.get() )
142 xString.reset( new XclExpString );
144 ++mnTotal;
145 sal_uInt32 nSstIndex = 0;
147 // calculate hash value in range [0,EXC_SST_HASHTABLE_SIZE)
148 sal_uInt16 nHash = xString->GetHash();
149 (nHash ^= (nHash / EXC_SST_HASHTABLE_SIZE)) %= EXC_SST_HASHTABLE_SIZE;
151 XclExpHashVec& rVec = maHashTab[ nHash ];
152 XclExpHashEntry aEntry( xString.get(), mnSize );
153 XclExpHashVec::iterator aIt = ::std::lower_bound( rVec.begin(), rVec.end(), aEntry, XclExpHashEntrySWO() );
154 if( (aIt == rVec.end()) || (*aIt->mpString != *xString) )
156 nSstIndex = mnSize;
157 maStringList.push_back( xString );
158 rVec.insert( aIt, aEntry );
159 ++mnSize;
161 else
163 nSstIndex = aIt->mnSstIndex;
166 return nSstIndex;
169 void XclExpSstImpl::Save( XclExpStream& rStrm )
171 if( maStringList.empty() )
172 return;
174 #if (OSL_DEBUG_LEVEL > 1) && EXC_INCL_SST_STATISTICS
175 { // own scope for the statistics
176 #define APPENDINT( value ) Append( ByteString::CreateFromInt32( value ) )
177 ScfUInt32Vec aVec;
178 size_t nPerBucket = mnSize / EXC_SST_HASHTABLE_SIZE + 1, nEff = 0;
179 for( XclExpHashTab::const_iterator aTIt = maHashTab.begin(), aTEnd = maHashTab.end(); aTIt != aTEnd; ++aTIt )
181 size_t nSize = aTIt->size();
182 if( nSize >= aVec.size() ) aVec.resize( nSize + 1, 0 );
183 ++aVec[ nSize ];
184 if( nSize > nPerBucket ) nEff += nSize - nPerBucket;
186 ByteString aStr( "SST HASHING STATISTICS\n\n" );
187 aStr.Append( "Total count:\t" ).APPENDINT( mnTotal ).Append( " strings\n" );
188 aStr.Append( "Reduced to:\t" ).APPENDINT( mnSize ).Append( " strings (" );
189 aStr.APPENDINT( 100 * mnSize / mnTotal ).Append( "%)\n" );
190 aStr.Append( "Effectivity:\t\t" ).APPENDINT( 100 - 100 * nEff / mnSize );
191 aStr.Append( "% (best: " ).APPENDINT( nPerBucket ).Append( " strings per bucket)\n" );
192 aStr.Append( "\t\tCount of buckets\nBucket size\ttotal\tmax\tTotal strings\n" );
193 for( size_t nIx = 0, nSize = aVec.size(), nInc = 1; nIx < nSize; nIx += nInc )
195 if( (nIx == 10) || (nIx == 100) || (nIx == 1000) ) nInc = nIx;
196 size_t nMaxIx = ::std::min( nIx + nInc, nSize ), nCount = 0, nMaxCount = 0, nStrings = 0;
197 for( size_t nSubIx = nIx; nSubIx < nMaxIx; ++nSubIx )
199 nCount += aVec[ nSubIx ];
200 if( aVec[ nSubIx ] > nMaxCount ) nMaxCount = aVec[ nSubIx ];
201 nStrings += nSubIx * aVec[ nSubIx ];
203 if( nMaxCount )
205 aStr.APPENDINT( nIx );
206 if( nMaxIx - nIx > 1 ) aStr.Append( '-' ).APPENDINT( nMaxIx - 1 );
207 aStr.Append( "\t\t" ).APPENDINT( nCount ).Append( '\t' ).APPENDINT( nMaxCount );
208 aStr.Append( '\t' ).APPENDINT( nStrings ).Append( '\n' );
211 DBG_ERRORFILE( aStr.GetBuffer() );
212 #undef APPENDINT
214 #endif
216 SvMemoryStream aExtSst( 8192 );
218 sal_uInt32 nBucket = mnSize;
219 while( nBucket > 0x0100 )
220 nBucket /= 2;
222 sal_uInt16 nPerBucket = llimit_cast< sal_uInt16 >( nBucket, 8 );
223 sal_uInt16 nBucketIndex = 0;
225 // *** write the SST record ***
227 rStrm.StartRecord( EXC_ID_SST, 8 );
229 rStrm << mnTotal << mnSize;
230 for( XclExpStringList::const_iterator aIt = maStringList.begin(), aEnd = maStringList.end(); aIt != aEnd; ++aIt )
232 if( !nBucketIndex )
234 // write bucket info before string to get correct record position
235 sal_uInt32 nStrmPos = static_cast< sal_uInt32 >( rStrm.GetSvStreamPos() );
236 sal_uInt16 nRecPos = rStrm.GetRawRecPos() + 4;
237 aExtSst << nStrmPos // stream position
238 << nRecPos // position from start of SST or CONTINUE
239 << sal_uInt16( 0 ); // reserved
242 rStrm << **aIt;
244 if( ++nBucketIndex == nPerBucket )
245 nBucketIndex = 0;
248 rStrm.EndRecord();
250 // *** write the EXTSST record ***
252 rStrm.StartRecord( EXC_ID_EXTSST, 0 );
254 rStrm << nPerBucket;
255 rStrm.SetSliceSize( 8 ); // size of one bucket info
256 aExtSst.Seek( STREAM_SEEK_TO_BEGIN );
257 rStrm.CopyFromStream( aExtSst );
259 rStrm.EndRecord();
262 void XclExpSstImpl::SaveXml( XclExpXmlStream& rStrm )
264 if( maStringList.empty() )
265 return;
267 sax_fastparser::FSHelperPtr pSst = rStrm.CreateOutputStream(
268 OUString::createFromAscii( "xl/sharedStrings.xml" ),
269 OUString::createFromAscii( "sharedStrings.xml" ),
270 rStrm.GetCurrentStream()->getOutputStream(),
271 "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
272 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" );
273 rStrm.PushStream( pSst );
275 pSst->startElement( XML_sst,
276 XML_xmlns, "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
277 XML_count, OString::valueOf( (sal_Int32) mnTotal ).getStr(),
278 XML_uniqueCount, OString::valueOf( (sal_Int32) mnSize ).getStr(),
279 FSEND );
281 for( XclExpStringList::const_iterator aIt = maStringList.begin(), aEnd = maStringList.end(); aIt != aEnd; ++aIt )
283 pSst->startElement( XML_si, FSEND );
284 (*aIt)->WriteXml( rStrm );
285 pSst->endElement( XML_si );
288 pSst->endElement( XML_sst );
290 rStrm.PopStream();
293 // ----------------------------------------------------------------------------
295 XclExpSst::XclExpSst() :
296 mxImpl( new XclExpSstImpl )
300 XclExpSst::~XclExpSst()
304 sal_uInt32 XclExpSst::Insert( XclExpStringRef xString )
306 return mxImpl->Insert( xString );
309 void XclExpSst::Save( XclExpStream& rStrm )
311 mxImpl->Save( rStrm );
314 void XclExpSst::SaveXml( XclExpXmlStream& rStrm )
316 mxImpl->SaveXml( rStrm );
319 // Merged cells ===============================================================
321 XclExpMergedcells::XclExpMergedcells( const XclExpRoot& rRoot ) :
322 XclExpRoot( rRoot )
326 void XclExpMergedcells::AppendRange( const ScRange& rRange, sal_uInt32 nBaseXFId )
328 if( GetBiff() == EXC_BIFF8 )
330 maMergedRanges.Append( rRange );
331 maBaseXFIds.push_back( nBaseXFId );
335 sal_uInt32 XclExpMergedcells::GetBaseXFId( const ScAddress& rPos ) const
337 DBG_ASSERT( maBaseXFIds.size() == maMergedRanges.Count(), "XclExpMergedcells::GetBaseXFId - invalid lists" );
338 ScfUInt32Vec::const_iterator aIt = maBaseXFIds.begin();
339 ScRangeList& rNCRanges = const_cast< ScRangeList& >( maMergedRanges );
340 for( const ScRange* pScRange = rNCRanges.First(); pScRange; pScRange = rNCRanges.Next(), ++aIt )
341 if( pScRange->In( rPos ) )
342 return *aIt;
343 return EXC_XFID_NOTFOUND;
346 void XclExpMergedcells::Save( XclExpStream& rStrm )
348 if( GetBiff() == EXC_BIFF8 )
350 XclRangeList aXclRanges;
351 GetAddressConverter().ConvertRangeList( aXclRanges, maMergedRanges, true );
352 size_t nFirstRange = 0;
353 size_t nRemainingRanges = aXclRanges.size();
354 while( nRemainingRanges > 0 )
356 size_t nRangeCount = ::std::min< size_t >( nRemainingRanges, EXC_MERGEDCELLS_MAXCOUNT );
357 rStrm.StartRecord( EXC_ID_MERGEDCELLS, 2 + 8 * nRangeCount );
358 aXclRanges.WriteSubList( rStrm, nFirstRange, nRangeCount );
359 rStrm.EndRecord();
360 nFirstRange += nRangeCount;
361 nRemainingRanges -= nRangeCount;
366 void XclExpMergedcells::SaveXml( XclExpXmlStream& rStrm )
368 ULONG nCount = maMergedRanges.Count();
369 if( !nCount )
370 return;
371 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
372 rWorksheet->startElement( XML_mergeCells,
373 XML_count, OString::valueOf( (sal_Int32) nCount ).getStr(),
374 FSEND );
375 for( ULONG i = 0; i < nCount; ++i )
377 if( const ScRange* pRange = maMergedRanges.GetObject( i ) )
379 rWorksheet->singleElement( XML_mergeCell,
380 XML_ref, XclXmlUtils::ToOString( *pRange ).getStr(),
381 FSEND );
384 rWorksheet->endElement( XML_mergeCells );
387 // Hyperlinks =================================================================
389 XclExpHyperlink::XclExpHyperlink( const XclExpRoot& rRoot, const SvxURLField& rUrlField, const ScAddress& rScPos ) :
390 XclExpRecord( EXC_ID_HLINK ),
391 maScPos( rScPos ),
392 mxVarData( new SvMemoryStream ),
393 mnFlags( 0 )
395 const String& rUrl = rUrlField.GetURL();
396 const String& rRepr = rUrlField.GetRepresentation();
397 INetURLObject aUrlObj( rUrl );
398 const INetProtocol eProtocol = aUrlObj.GetProtocol();
399 bool bWithRepr = rRepr.Len() > 0;
400 XclExpStream aXclStrm( *mxVarData, rRoot ); // using in raw write mode.
402 // description
403 if( bWithRepr )
405 XclExpString aDescr( rRepr, EXC_STR_FORCEUNICODE, 255 );
406 aXclStrm << sal_uInt32( aDescr.Len() + 1 ); // string length + 1 trailing zero word
407 aDescr.WriteBuffer( aXclStrm ); // NO flags
408 aXclStrm << sal_uInt16( 0 );
410 mnFlags |= EXC_HLINK_DESCR;
411 mxRepr.reset( new String( rRepr ) );
414 // file link or URL
415 if( eProtocol == INET_PROT_FILE || eProtocol == INET_PROT_SMB )
417 sal_uInt16 nLevel;
418 bool bRel;
419 String aFileName( BuildFileName( nLevel, bRel, rUrl, rRoot ) );
421 if( eProtocol == INET_PROT_SMB )
423 // #n382718# (and #n261623#) Convert smb notation to '\\'
424 aFileName = aUrlObj.GetMainURL( INetURLObject::NO_DECODE );
425 aFileName = String( aFileName.GetBuffer() + 4 ); // skip the 'smb:' part
426 aFileName.SearchAndReplaceAll( '/', '\\' );
429 if( !bRel )
430 mnFlags |= EXC_HLINK_ABS;
431 mnFlags |= EXC_HLINK_BODY;
433 ByteString aAsciiLink( aFileName, rRoot.GetTextEncoding() );
434 XclExpString aLink( aFileName, EXC_STR_FORCEUNICODE, 255 );
435 aXclStrm << XclTools::maGuidFileMoniker
436 << nLevel
437 << sal_uInt32( aAsciiLink.Len() + 1 ); // string length + 1 trailing zero byte
438 aXclStrm.Write( aAsciiLink.GetBuffer(), aAsciiLink.Len() );
439 aXclStrm << sal_uInt8( 0 )
440 << sal_uInt32( 0xDEADFFFF );
441 aXclStrm.WriteZeroBytes( 20 );
442 aXclStrm << sal_uInt32( aLink.GetBufferSize() + 6 )
443 << sal_uInt32( aLink.GetBufferSize() ) // byte count, not string length
444 << sal_uInt16( 0x0003 );
445 aLink.WriteBuffer( aXclStrm ); // NO flags
447 if( !mxRepr.get() )
448 mxRepr.reset( new String( aFileName ) );
450 msTarget = XclXmlUtils::ToOUString( aLink );
452 else if( eProtocol != INET_PROT_NOT_VALID )
454 XclExpString aUrl( aUrlObj.GetURLNoMark(), EXC_STR_FORCEUNICODE, 255 );
455 aXclStrm << XclTools::maGuidUrlMoniker
456 << sal_uInt32( aUrl.GetBufferSize() + 2 ); // byte count + 1 trailing zero word
457 aUrl.WriteBuffer( aXclStrm ); // NO flags
458 aXclStrm << sal_uInt16( 0 );
460 mnFlags |= EXC_HLINK_BODY | EXC_HLINK_ABS;
461 if( !mxRepr.get() )
462 mxRepr.reset( new String( rUrl ) );
464 msTarget = XclXmlUtils::ToOUString( aUrl );
466 else if( rUrl.GetChar( 0 ) == '#' ) // hack for #89066#
468 String aTextMark( rUrl.Copy( 1 ) );
469 aTextMark.SearchAndReplace( '.', '!' );
470 mxTextMark.reset( new XclExpString( aTextMark, EXC_STR_FORCEUNICODE, 255 ) );
473 // text mark
474 if( !mxTextMark.get() && aUrlObj.HasMark() )
475 mxTextMark.reset( new XclExpString( aUrlObj.GetMark(), EXC_STR_FORCEUNICODE, 255 ) );
477 if( mxTextMark.get() )
479 aXclStrm << sal_uInt32( mxTextMark->Len() + 1 ); // string length + 1 trailing zero word
480 mxTextMark->WriteBuffer( aXclStrm ); // NO flags
481 aXclStrm << sal_uInt16( 0 );
483 mnFlags |= EXC_HLINK_MARK;
486 SetRecSize( 32 + mxVarData->Tell() );
489 XclExpHyperlink::~XclExpHyperlink()
493 String XclExpHyperlink::BuildFileName(
494 sal_uInt16& rnLevel, bool& rbRel, const String& rUrl, const XclExpRoot& rRoot ) const
496 String aDosName( INetURLObject( rUrl ).getFSysPath( INetURLObject::FSYS_DOS ) );
497 rnLevel = 0;
498 rbRel = rRoot.IsRelUrl();
500 if( rbRel )
502 // try to convert to relative file name
503 String aTmpName( aDosName );
504 aDosName = INetURLObject::GetRelURL( rRoot.GetBasePath(), rUrl,
505 INetURLObject::WAS_ENCODED, INetURLObject::DECODE_WITH_CHARSET );
507 if( aDosName.SearchAscii( INET_FILE_SCHEME ) == 0 )
509 // not converted to rel -> back to old, return absolute flag
510 aDosName = aTmpName;
511 rbRel = false;
513 else if( aDosName.SearchAscii( "./" ) == 0 )
515 aDosName.Erase( 0, 2 );
517 else
519 while( aDosName.SearchAndReplaceAscii( "../", EMPTY_STRING ) == 0 )
520 ++rnLevel;
523 return aDosName;
526 void XclExpHyperlink::WriteBody( XclExpStream& rStrm )
528 sal_uInt16 nXclCol = static_cast< sal_uInt16 >( maScPos.Col() );
529 sal_uInt16 nXclRow = static_cast< sal_uInt16 >( maScPos.Row() );
530 rStrm << nXclRow << nXclRow << nXclCol << nXclCol;
531 WriteEmbeddedData( rStrm );
534 void XclExpHyperlink::WriteEmbeddedData( XclExpStream& rStrm )
536 rStrm << XclTools::maGuidStdLink
537 << sal_uInt32( 2 )
538 << mnFlags;
540 mxVarData->Seek( STREAM_SEEK_TO_BEGIN );
541 rStrm.CopyFromStream( *mxVarData );
544 void XclExpHyperlink::SaveXml( XclExpXmlStream& rStrm )
546 OUString sId = rStrm.addRelation( rStrm.GetCurrentStream()->getOutputStream(),
547 XclXmlUtils::ToOUString( "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" ),
548 msTarget,
549 XclXmlUtils::ToOUString( "External" ) );
550 rStrm.GetCurrentStream()->singleElement( XML_hyperlink,
551 XML_ref, XclXmlUtils::ToOString( maScPos ).getStr(),
552 FSNS( XML_r, XML_id ), XclXmlUtils::ToOString( sId ).getStr(),
553 XML_location, mxTextMark.get() != NULL
554 ? XclXmlUtils::ToOString( *mxTextMark ).getStr()
555 : NULL,
556 // OOXTODO: XML_tooltip, from record HLinkTooltip 800h wzTooltip
557 XML_display, XclXmlUtils::ToOString( *mxRepr ).getStr(),
558 FSEND );
561 // Label ranges ===============================================================
563 XclExpLabelranges::XclExpLabelranges( const XclExpRoot& rRoot ) :
564 XclExpRoot( rRoot )
566 SCTAB nScTab = GetCurrScTab();
567 // row label ranges
568 FillRangeList( maRowRanges, rRoot.GetDoc().GetRowNameRangesRef(), nScTab );
569 // row labels only over 1 column (restriction of Excel97/2000/XP)
570 for( ScRange* pScRange = maRowRanges.First(); pScRange; pScRange = maRowRanges.Next() )
571 if( pScRange->aStart.Col() != pScRange->aEnd.Col() )
572 pScRange->aEnd.SetCol( pScRange->aStart.Col() );
573 // col label ranges
574 FillRangeList( maColRanges, rRoot.GetDoc().GetColNameRangesRef(), nScTab );
577 void XclExpLabelranges::FillRangeList( ScRangeList& rScRanges,
578 ScRangePairListRef xLabelRangesRef, SCTAB nScTab )
580 for( const ScRangePair* pRangePair = xLabelRangesRef->First(); pRangePair; pRangePair = xLabelRangesRef->Next() )
582 const ScRange& rScRange = pRangePair->GetRange( 0 );
583 if( rScRange.aStart.Tab() == nScTab )
584 rScRanges.Append( rScRange );
588 void XclExpLabelranges::Save( XclExpStream& rStrm )
590 XclExpAddressConverter& rAddrConv = GetAddressConverter();
591 XclRangeList aRowXclRanges, aColXclRanges;
592 rAddrConv.ConvertRangeList( aRowXclRanges, maRowRanges, false );
593 rAddrConv.ConvertRangeList( aColXclRanges, maColRanges, false );
594 if( !aRowXclRanges.empty() || !aColXclRanges.empty() )
596 rStrm.StartRecord( EXC_ID_LABELRANGES, 4 + 8 * (aRowXclRanges.size() + aColXclRanges.size()) );
597 rStrm << aRowXclRanges << aColXclRanges;
598 rStrm.EndRecord();
602 // Conditional formatting ====================================================
604 /** Represents a CF record that contains one condition of a conditional format. */
605 class XclExpCFImpl : protected XclExpRoot
607 public:
608 explicit XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry );
610 /** Writes the body of the CF record. */
611 void WriteBody( XclExpStream& rStrm );
613 private:
614 const ScCondFormatEntry& mrFormatEntry; /// Calc conditional format entry.
615 XclFontData maFontData; /// Font formatting attributes.
616 XclExpCellBorder maBorder; /// Border formatting attributes.
617 XclExpCellArea maArea; /// Pattern formatting attributes.
618 XclTokenArrayRef mxTokArr1; /// Formula for first condition.
619 XclTokenArrayRef mxTokArr2; /// Formula for second condition.
620 sal_uInt32 mnFontColorId; /// Font color ID.
621 sal_uInt8 mnType; /// Type of the condition (cell/formula).
622 sal_uInt8 mnOperator; /// Comparison operator for cell type.
623 bool mbFontUsed; /// true = Any font attribute used.
624 bool mbHeightUsed; /// true = Font height used.
625 bool mbWeightUsed; /// true = Font weight used.
626 bool mbColorUsed; /// true = Font color used.
627 bool mbUnderlUsed; /// true = Font underline type used.
628 bool mbItalicUsed; /// true = Font posture used.
629 bool mbStrikeUsed; /// true = Font strikeout used.
630 bool mbBorderUsed; /// true = Border attribute used.
631 bool mbPattUsed; /// true = Pattern attribute used.
634 // ----------------------------------------------------------------------------
636 XclExpCFImpl::XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry ) :
637 XclExpRoot( rRoot ),
638 mrFormatEntry( rFormatEntry ),
639 mnFontColorId( 0 ),
640 mnType( EXC_CF_TYPE_CELL ),
641 mnOperator( EXC_CF_CMP_NONE ),
642 mbFontUsed( false ),
643 mbHeightUsed( false ),
644 mbWeightUsed( false ),
645 mbColorUsed( false ),
646 mbUnderlUsed( false ),
647 mbItalicUsed( false ),
648 mbStrikeUsed( false ),
649 mbBorderUsed( false ),
650 mbPattUsed( false )
652 /* Get formatting attributes here, and not in WriteBody(). This is needed to
653 correctly insert all colors into the palette. */
655 if( SfxStyleSheetBase* pStyleSheet = GetDoc().GetStyleSheetPool()->Find( mrFormatEntry.GetStyle(), SFX_STYLE_FAMILY_PARA ) )
657 const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
659 // font
660 mbHeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_HEIGHT, true );
661 mbWeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_WEIGHT, true );
662 mbColorUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_COLOR, true );
663 mbUnderlUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_UNDERLINE, true );
664 mbItalicUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_POSTURE, true );
665 mbStrikeUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_CROSSEDOUT, true );
666 mbFontUsed = mbHeightUsed || mbWeightUsed || mbColorUsed || mbUnderlUsed || mbItalicUsed || mbStrikeUsed;
667 if( mbFontUsed )
669 Font aFont;
670 ScPatternAttr::GetFont( aFont, rItemSet, SC_AUTOCOL_RAW );
671 maFontData.FillFromVclFont( aFont );
672 mnFontColorId = GetPalette().InsertColor( maFontData.maColor, EXC_COLOR_CELLTEXT );
675 // border
676 mbBorderUsed = ScfTools::CheckItem( rItemSet, ATTR_BORDER, true );
677 if( mbBorderUsed )
678 maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff() );
680 // pattern
681 mbPattUsed = ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true );
682 if( mbPattUsed )
683 maArea.FillFromItemSet( rItemSet, GetPalette(), GetBiff() );
686 // *** mode and comparison operator ***
688 bool bFmla2 = false;
689 switch( rFormatEntry.GetOperation() )
691 case SC_COND_NONE: mnType = EXC_CF_TYPE_NONE; break;
692 case SC_COND_BETWEEN: mnOperator = EXC_CF_CMP_BETWEEN; bFmla2 = true; break;
693 case SC_COND_NOTBETWEEN: mnOperator = EXC_CF_CMP_NOT_BETWEEN; bFmla2 = true; break;
694 case SC_COND_EQUAL: mnOperator = EXC_CF_CMP_EQUAL; break;
695 case SC_COND_NOTEQUAL: mnOperator = EXC_CF_CMP_NOT_EQUAL; break;
696 case SC_COND_GREATER: mnOperator = EXC_CF_CMP_GREATER; break;
697 case SC_COND_LESS: mnOperator = EXC_CF_CMP_LESS; break;
698 case SC_COND_EQGREATER: mnOperator = EXC_CF_CMP_GREATER_EQUAL; break;
699 case SC_COND_EQLESS: mnOperator = EXC_CF_CMP_LESS_EQUAL; break;
700 case SC_COND_DIRECT: mnType = EXC_CF_TYPE_FMLA; break;
701 default: mnType = EXC_CF_TYPE_NONE;
702 DBG_ERRORFILE( "XclExpCF::WriteBody - unknown condition type" );
705 // *** formulas ***
707 XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
709 ::std::auto_ptr< ScTokenArray > xScTokArr( mrFormatEntry.CreateTokenArry( 0 ) );
710 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
712 if( bFmla2 )
714 xScTokArr.reset( mrFormatEntry.CreateTokenArry( 1 ) );
715 mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
719 void XclExpCFImpl::WriteBody( XclExpStream& rStrm )
721 // *** mode and comparison operator ***
723 rStrm << mnType << mnOperator;
725 // *** formula sizes ***
727 sal_uInt16 nFmlaSize1 = mxTokArr1.get() ? mxTokArr1->GetSize() : 0;
728 sal_uInt16 nFmlaSize2 = mxTokArr2.get() ? mxTokArr2->GetSize() : 0;
729 rStrm << nFmlaSize1 << nFmlaSize2;
731 // *** formatting blocks ***
733 if( mbFontUsed || mbBorderUsed || mbPattUsed )
735 sal_uInt32 nFlags = EXC_CF_ALLDEFAULT;
737 ::set_flag( nFlags, EXC_CF_BLOCK_FONT, mbFontUsed );
738 ::set_flag( nFlags, EXC_CF_BLOCK_BORDER, mbBorderUsed );
739 ::set_flag( nFlags, EXC_CF_BLOCK_AREA, mbPattUsed );
741 // attributes used -> set flags to 0.
742 ::set_flag( nFlags, EXC_CF_BORDER_ALL, !mbBorderUsed );
743 ::set_flag( nFlags, EXC_CF_AREA_ALL, !mbPattUsed );
745 rStrm << nFlags << sal_uInt16( 0 );
747 if( mbFontUsed )
749 // font height, 0xFFFFFFFF indicates unused
750 sal_uInt32 nHeight = mbHeightUsed ? maFontData.mnHeight : 0xFFFFFFFF;
751 // font style: italic and strikeout
752 sal_uInt32 nStyle = 0;
753 ::set_flag( nStyle, EXC_CF_FONT_STYLE, maFontData.mbItalic );
754 ::set_flag( nStyle, EXC_CF_FONT_STRIKEOUT, maFontData.mbStrikeout );
755 // font color, 0xFFFFFFFF indicates unused
756 sal_uInt32 nColor = mbColorUsed ? GetPalette().GetColorIndex( mnFontColorId ) : 0xFFFFFFFF;
757 // font used flags for italic, weight, and strikeout -> 0 = used, 1 = default
758 sal_uInt32 nFontFlags1 = EXC_CF_FONT_ALLDEFAULT;
759 ::set_flag( nFontFlags1, EXC_CF_FONT_STYLE, !(mbItalicUsed || mbWeightUsed) );
760 ::set_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT, !mbStrikeUsed );
761 // font used flag for underline -> 0 = used, 1 = default
762 sal_uInt32 nFontFlags3 = mbUnderlUsed ? 0 : EXC_CF_FONT_UNDERL;
764 rStrm.WriteZeroBytesToRecord( 64 );
765 rStrm << nHeight
766 << nStyle
767 << maFontData.mnWeight
768 << EXC_FONTESC_NONE
769 << maFontData.mnUnderline;
770 rStrm.WriteZeroBytesToRecord( 3 );
771 rStrm << nColor
772 << sal_uInt32( 0 )
773 << nFontFlags1
774 << EXC_CF_FONT_ESCAPEM // escapement never used -> set the flag
775 << nFontFlags3;
776 rStrm.WriteZeroBytesToRecord( 16 );
777 rStrm << sal_uInt16( 1 ); // must be 1
780 if( mbBorderUsed )
782 sal_uInt16 nLineStyle = 0;
783 sal_uInt32 nLineColor = 0;
784 maBorder.SetFinalColors( GetPalette() );
785 maBorder.FillToCF8( nLineStyle, nLineColor );
786 rStrm << nLineStyle << nLineColor << sal_uInt16( 0 );
789 if( mbPattUsed )
791 sal_uInt16 nPattern = 0, nColor = 0;
792 maArea.SetFinalColors( GetPalette() );
793 maArea.FillToCF8( nPattern, nColor );
794 rStrm << nPattern << nColor;
797 else
799 // no data blocks at all
800 rStrm << sal_uInt32( 0 ) << sal_uInt16( 0 );
803 // *** formulas ***
805 if( mxTokArr1.get() )
806 mxTokArr1->WriteArray( rStrm );
807 if( mxTokArr2.get() )
808 mxTokArr2->WriteArray( rStrm );
811 // ----------------------------------------------------------------------------
813 XclExpCF::XclExpCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry ) :
814 XclExpRecord( EXC_ID_CF ),
815 XclExpRoot( rRoot ),
816 mxImpl( new XclExpCFImpl( rRoot, rFormatEntry ) )
820 XclExpCF::~XclExpCF()
824 void XclExpCF::WriteBody( XclExpStream& rStrm )
826 mxImpl->WriteBody( rStrm );
829 // ----------------------------------------------------------------------------
831 XclExpCondfmt::XclExpCondfmt( const XclExpRoot& rRoot, const ScConditionalFormat& rCondFormat ) :
832 XclExpRecord( EXC_ID_CONDFMT ),
833 XclExpRoot( rRoot )
835 ScRangeList aScRanges;
836 GetDoc().FindConditionalFormat( rCondFormat.GetKey(), aScRanges, GetCurrScTab() );
837 GetAddressConverter().ConvertRangeList( maXclRanges, aScRanges, true );
838 if( !maXclRanges.empty() )
840 for( USHORT nIndex = 0, nCount = rCondFormat.Count(); nIndex < nCount; ++nIndex )
841 if( const ScCondFormatEntry* pEntry = rCondFormat.GetEntry( nIndex ) )
842 maCFList.AppendNewRecord( new XclExpCF( GetRoot(), *pEntry ) );
843 aScRanges.Format( msSeqRef, SCA_VALID, NULL, formula::FormulaGrammar::CONV_XL_A1 );
847 XclExpCondfmt::~XclExpCondfmt()
851 bool XclExpCondfmt::IsValid() const
853 return !maCFList.IsEmpty() && !maXclRanges.empty();
856 void XclExpCondfmt::Save( XclExpStream& rStrm )
858 if( IsValid() )
860 XclExpRecord::Save( rStrm );
861 maCFList.Save( rStrm );
865 void XclExpCondfmt::WriteBody( XclExpStream& rStrm )
867 DBG_ASSERT( !maCFList.IsEmpty(), "XclExpCondfmt::WriteBody - no CF records to write" );
868 DBG_ASSERT( !maXclRanges.empty(), "XclExpCondfmt::WriteBody - no cell ranges found" );
870 rStrm << static_cast< sal_uInt16 >( maCFList.GetSize() )
871 << sal_uInt16( 1 )
872 << maXclRanges.GetEnclosingRange()
873 << maXclRanges;
876 void XclExpCondfmt::SaveXml( XclExpXmlStream& rStrm )
878 if( !IsValid() )
879 return;
881 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
882 rWorksheet->startElement( XML_conditionalFormatting,
883 XML_sqref, XclXmlUtils::ToOString( msSeqRef ).getStr(),
884 // OOXTODO: XML_pivot,
885 FSEND );
886 maCFList.SaveXml( rStrm );
887 // OOXTODO: XML_extLst
888 rWorksheet->endElement( XML_conditionalFormatting );
891 // ----------------------------------------------------------------------------
893 XclExpCondFormatBuffer::XclExpCondFormatBuffer( const XclExpRoot& rRoot ) :
894 XclExpRoot( rRoot )
896 if( const ScConditionalFormatList* pCondFmtList = GetDoc().GetCondFormList() )
898 if( const ScConditionalFormatPtr* ppCondFmt = pCondFmtList->GetData() )
900 const ScConditionalFormatPtr* ppCondEnd = ppCondFmt + pCondFmtList->Count();
901 for( ; ppCondFmt < ppCondEnd; ++ppCondFmt )
903 if( *ppCondFmt )
905 XclExpCondfmtList::RecordRefType xCondfmtRec( new XclExpCondfmt( GetRoot(), **ppCondFmt ) );
906 if( xCondfmtRec->IsValid() )
907 maCondfmtList.AppendRecord( xCondfmtRec );
914 void XclExpCondFormatBuffer::Save( XclExpStream& rStrm )
916 maCondfmtList.Save( rStrm );
919 void XclExpCondFormatBuffer::SaveXml( XclExpXmlStream& rStrm )
921 maCondfmtList.SaveXml( rStrm );
924 // Validation =================================================================
926 namespace {
928 /** Writes a formula for the DV record. */
929 void lclWriteDvFormula( XclExpStream& rStrm, const XclTokenArray* pXclTokArr )
931 sal_uInt16 nFmlaSize = pXclTokArr ? pXclTokArr->GetSize() : 0;
932 rStrm << nFmlaSize << sal_uInt16( 0 );
933 if( pXclTokArr )
934 pXclTokArr->WriteArray( rStrm );
937 /** Writes a formula for the DV record, based on a single string. */
938 void lclWriteDvFormula( XclExpStream& rStrm, const XclExpString& rString )
940 // fake a formula with a single tStr token
941 rStrm << static_cast< sal_uInt16 >( rString.GetSize() + 1 )
942 << sal_uInt16( 0 )
943 << EXC_TOKID_STR
944 << rString;
947 const char* lcl_GetValidationType( sal_uInt32 nFlags )
949 switch( nFlags & EXC_DV_MODE_MASK )
951 case EXC_DV_MODE_ANY: return "none";
952 case EXC_DV_MODE_WHOLE: return "whole";
953 case EXC_DV_MODE_DECIMAL: return "decimal";
954 case EXC_DV_MODE_LIST: return "list";
955 case EXC_DV_MODE_DATE: return "date";
956 case EXC_DV_MODE_TIME: return "time";
957 case EXC_DV_MODE_TEXTLEN: return "textLength";
958 case EXC_DV_MODE_CUSTOM: return "custom";
960 return NULL;
963 const char* lcl_GetOperatorType( sal_uInt32 nFlags )
965 switch( nFlags & EXC_DV_COND_MASK )
967 case EXC_DV_COND_BETWEEN: return "between";
968 case EXC_DV_COND_NOTBETWEEN: return "notBetween";
969 case EXC_DV_COND_EQUAL: return "equal";
970 case EXC_DV_COND_NOTEQUAL: return "notEqual";
971 case EXC_DV_COND_GREATER: return "greaterThan";
972 case EXC_DV_COND_LESS: return "lessThan";
973 case EXC_DV_COND_EQGREATER: return "greaterThanOrEqual";
974 case EXC_DV_COND_EQLESS: return "lessThanOrEqual";
976 return NULL;
979 } // namespace
981 // ----------------------------------------------------------------------------
983 XclExpDV::XclExpDV( const XclExpRoot& rRoot, ULONG nScHandle ) :
984 XclExpRecord( EXC_ID_DV ),
985 XclExpRoot( rRoot ),
986 mnFlags( 0 ),
987 mnScHandle( nScHandle )
989 if( const ScValidationData* pValData = GetDoc().GetValidationEntry( mnScHandle ) )
991 // prompt box - empty string represented by single NUL character
992 String aTitle, aText;
993 bool bShowPrompt = (pValData->GetInput( aTitle, aText ) == TRUE);
994 if( aTitle.Len() )
995 maPromptTitle.Assign( aTitle );
996 else
997 maPromptTitle.Assign( '\0' );
998 if( aText.Len() )
999 maPromptText.Assign( aText );
1000 else
1001 maPromptText.Assign( '\0' );
1003 // error box - empty string represented by single NUL character
1004 ScValidErrorStyle eScErrorStyle;
1005 bool bShowError = (pValData->GetErrMsg( aTitle, aText, eScErrorStyle ) == TRUE);
1006 if( aTitle.Len() )
1007 maErrorTitle.Assign( aTitle );
1008 else
1009 maErrorTitle.Assign( '\0' );
1010 if( aText.Len() )
1011 maErrorText.Assign( aText );
1012 else
1013 maErrorText.Assign( '\0' );
1015 // flags
1016 switch( pValData->GetDataMode() )
1018 case SC_VALID_ANY: mnFlags |= EXC_DV_MODE_ANY; break;
1019 case SC_VALID_WHOLE: mnFlags |= EXC_DV_MODE_WHOLE; break;
1020 case SC_VALID_DECIMAL: mnFlags |= EXC_DV_MODE_DECIMAL; break;
1021 case SC_VALID_LIST: mnFlags |= EXC_DV_MODE_LIST; break;
1022 case SC_VALID_DATE: mnFlags |= EXC_DV_MODE_DATE; break;
1023 case SC_VALID_TIME: mnFlags |= EXC_DV_MODE_TIME; break;
1024 case SC_VALID_TEXTLEN: mnFlags |= EXC_DV_MODE_TEXTLEN; break;
1025 case SC_VALID_CUSTOM: mnFlags |= EXC_DV_MODE_CUSTOM; break;
1026 default: DBG_ERRORFILE( "XclExpDV::XclExpDV - unknown mode" );
1029 switch( pValData->GetOperation() )
1031 case SC_COND_NONE:
1032 case SC_COND_EQUAL: mnFlags |= EXC_DV_COND_EQUAL; break;
1033 case SC_COND_LESS: mnFlags |= EXC_DV_COND_LESS; break;
1034 case SC_COND_GREATER: mnFlags |= EXC_DV_COND_GREATER; break;
1035 case SC_COND_EQLESS: mnFlags |= EXC_DV_COND_EQLESS; break;
1036 case SC_COND_EQGREATER: mnFlags |= EXC_DV_COND_EQGREATER; break;
1037 case SC_COND_NOTEQUAL: mnFlags |= EXC_DV_COND_NOTEQUAL; break;
1038 case SC_COND_BETWEEN: mnFlags |= EXC_DV_COND_BETWEEN; break;
1039 case SC_COND_NOTBETWEEN: mnFlags |= EXC_DV_COND_NOTBETWEEN; break;
1040 default: DBG_ERRORFILE( "XclExpDV::XclExpDV - unknown condition" );
1042 switch( eScErrorStyle )
1044 case SC_VALERR_STOP: mnFlags |= EXC_DV_ERROR_STOP; break;
1045 case SC_VALERR_WARNING: mnFlags |= EXC_DV_ERROR_WARNING; break;
1046 case SC_VALERR_INFO: mnFlags |= EXC_DV_ERROR_INFO; break;
1047 case SC_VALERR_MACRO:
1048 // #111781# set INFO for validity with macro call, delete title
1049 mnFlags |= EXC_DV_ERROR_INFO;
1050 maErrorTitle.Assign( '\0' ); // contains macro name
1051 break;
1052 default: DBG_ERRORFILE( "XclExpDV::XclExpDV - unknown error style" );
1054 ::set_flag( mnFlags, EXC_DV_IGNOREBLANK, pValData->IsIgnoreBlank() );
1055 ::set_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN, pValData->GetListType() == ValidListType::INVISIBLE );
1056 ::set_flag( mnFlags, EXC_DV_SHOWPROMPT, bShowPrompt );
1057 ::set_flag( mnFlags, EXC_DV_SHOWERROR, bShowError );
1059 // formulas
1060 XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
1061 ::std::auto_ptr< ScTokenArray > xScTokArr;
1063 // first formula
1064 xScTokArr.reset( pValData->CreateTokenArry( 0 ) );
1065 if( xScTokArr.get() )
1067 if( pValData->GetDataMode() == SC_VALID_LIST )
1069 String aString;
1070 if( XclTokenArrayHelper::GetStringList( aString, *xScTokArr, '\n' ) )
1072 OUStringBuffer sFormulaBuf;
1073 sFormulaBuf.append( (sal_Unicode) '"' );
1074 /* Formula is a list of string tokens -> build the Excel string.
1075 Data validity is BIFF8 only (important for the XclExpString object).
1076 Excel uses the NUL character as string list separator. */
1077 mxString1.reset( new XclExpString( EXC_STR_8BITLENGTH ) );
1078 xub_StrLen nTokenCnt = aString.GetTokenCount( '\n' );
1079 xub_StrLen nStringIx = 0;
1080 for( xub_StrLen nToken = 0; nToken < nTokenCnt; ++nToken )
1082 String aToken( aString.GetToken( 0, '\n', nStringIx ) );
1083 if( nToken > 0 )
1085 mxString1->Append( '\0' );
1086 sFormulaBuf.append( (sal_Unicode) ',' );
1088 mxString1->Append( aToken );
1089 sFormulaBuf.append( XclXmlUtils::ToOUString( aToken ) );
1091 ::set_flag( mnFlags, EXC_DV_STRINGLIST );
1093 sFormulaBuf.append( (sal_Unicode) '"' );
1094 msFormula1 = sFormulaBuf.makeStringAndClear();
1096 else
1098 /* All other formulas in validation are stored like conditional
1099 formatting formulas (with tRefN/tAreaN tokens as value or
1100 array class). But NOT the cell references and defined names
1101 in list validation - they are stored as reference class
1102 tokens... Example:
1103 1) Cell must be equal to A1 -> formula is =A1 -> writes tRefNV token
1104 2) List is taken from A1 -> formula is =A1 -> writes tRefNR token
1105 Formula compiler supports this by offering two different functions
1106 CreateDataValFormula() and CreateListValFormula(). */
1107 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_LISTVAL, *xScTokArr );
1108 msFormula1 = XclXmlUtils::ToOUString( GetDoc(), pValData->GetSrcPos(), xScTokArr.get() );
1111 else
1113 // no list validation -> convert the formula
1114 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
1115 msFormula1 = XclXmlUtils::ToOUString( GetDoc(), pValData->GetSrcPos(), xScTokArr.get() );
1119 // second formula
1120 xScTokArr.reset( pValData->CreateTokenArry( 1 ) );
1121 if( xScTokArr.get() )
1123 mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
1124 msFormula2 = XclXmlUtils::ToOUString( GetDoc(), pValData->GetSrcPos(), xScTokArr.get() );
1127 else
1129 DBG_ERRORFILE( "XclExpDV::XclExpDV - missing core data" );
1130 mnScHandle = ULONG_MAX;
1134 XclExpDV::~XclExpDV()
1138 void XclExpDV::InsertCellRange( const ScRange& rRange )
1140 maScRanges.Join( rRange );
1143 bool XclExpDV::Finalize()
1145 GetAddressConverter().ConvertRangeList( maXclRanges, maScRanges, true );
1146 return (mnScHandle != ULONG_MAX) && !maXclRanges.empty();
1149 void XclExpDV::WriteBody( XclExpStream& rStrm )
1151 // flags and strings
1152 rStrm << mnFlags << maPromptTitle << maErrorTitle << maPromptText << maErrorText;
1153 // condition formulas
1154 if( mxString1.get() )
1155 lclWriteDvFormula( rStrm, *mxString1 );
1156 else
1157 lclWriteDvFormula( rStrm, mxTokArr1.get() );
1158 lclWriteDvFormula( rStrm, mxTokArr2.get() );
1159 // cell ranges
1160 rStrm << maXclRanges;
1163 void XclExpDV::SaveXml( XclExpXmlStream& rStrm )
1165 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1166 rWorksheet->startElement( XML_dataValidation,
1167 XML_allowBlank, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_DV_IGNOREBLANK ) ),
1168 XML_error, XESTRING_TO_PSZ( maErrorText ),
1169 // OOXTODO: XML_errorStyle,
1170 XML_errorTitle, XESTRING_TO_PSZ( maErrorTitle ),
1171 // OOXTODO: XML_imeMode,
1172 XML_operator, lcl_GetOperatorType( mnFlags ),
1173 XML_prompt, XESTRING_TO_PSZ( maPromptText ),
1174 XML_promptTitle, XESTRING_TO_PSZ( maPromptTitle ),
1175 XML_showDropDown, XclXmlUtils::ToPsz( ! ::get_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN ) ),
1176 XML_showErrorMessage, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWERROR ) ),
1177 XML_showInputMessage, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWPROMPT ) ),
1178 XML_sqref, XclXmlUtils::ToOString( maScRanges ).getStr(),
1179 XML_type, lcl_GetValidationType( mnFlags ),
1180 FSEND );
1181 if( msFormula1.getLength() )
1183 rWorksheet->startElement( XML_formula1, FSEND );
1184 rWorksheet->writeEscaped( msFormula1 );
1185 rWorksheet->endElement( XML_formula1 );
1187 if( msFormula2.getLength() )
1189 rWorksheet->startElement( XML_formula2, FSEND );
1190 rWorksheet->writeEscaped( msFormula2 );
1191 rWorksheet->endElement( XML_formula2 );
1193 rWorksheet->endElement( XML_dataValidation );
1196 // ----------------------------------------------------------------------------
1198 XclExpDval::XclExpDval( const XclExpRoot& rRoot ) :
1199 XclExpRecord( EXC_ID_DVAL, 18 ),
1200 XclExpRoot( rRoot )
1204 XclExpDval::~XclExpDval()
1208 void XclExpDval::InsertCellRange( const ScRange& rRange, ULONG nScHandle )
1210 if( GetBiff() == EXC_BIFF8 )
1212 XclExpDV& rDVRec = SearchOrCreateDv( nScHandle );
1213 rDVRec.InsertCellRange( rRange );
1217 void XclExpDval::Save( XclExpStream& rStrm )
1219 // check all records
1220 size_t nPos = maDVList.GetSize();
1221 while( nPos )
1223 --nPos; // backwards to keep nPos valid
1224 XclExpDVRef xDVRec = maDVList.GetRecord( nPos );
1225 if( !xDVRec->Finalize() )
1226 maDVList.RemoveRecord( nPos );
1229 // write the DVAL and the DV's
1230 if( !maDVList.IsEmpty() )
1232 XclExpRecord::Save( rStrm );
1233 maDVList.Save( rStrm );
1237 void XclExpDval::SaveXml( XclExpXmlStream& rStrm )
1239 if( maDVList.IsEmpty() )
1240 return;
1242 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1243 rWorksheet->startElement( XML_dataValidations,
1244 XML_count, OString::valueOf( (sal_Int32) maDVList.GetSize() ).getStr(),
1245 // OOXTODO: XML_disablePrompts,
1246 // OOXTODO: XML_xWindow,
1247 // OOXTODO: XML_yWindow,
1248 FSEND );
1249 maDVList.SaveXml( rStrm );
1250 rWorksheet->endElement( XML_dataValidations );
1253 XclExpDV& XclExpDval::SearchOrCreateDv( ULONG nScHandle )
1255 // test last found record
1256 if( mxLastFoundDV.get() && (mxLastFoundDV->GetScHandle() == nScHandle) )
1257 return *mxLastFoundDV;
1259 // binary search
1260 size_t nCurrPos = 0;
1261 if( !maDVList.IsEmpty() )
1263 size_t nFirstPos = 0;
1264 size_t nLastPos = maDVList.GetSize() - 1;
1265 bool bLoop = true;
1266 ULONG nCurrScHandle = ::std::numeric_limits< ULONG >::max();
1267 while( (nFirstPos <= nLastPos) && bLoop )
1269 nCurrPos = (nFirstPos + nLastPos) / 2;
1270 mxLastFoundDV = maDVList.GetRecord( nCurrPos );
1271 nCurrScHandle = mxLastFoundDV->GetScHandle();
1272 if( nCurrScHandle == nScHandle )
1273 bLoop = false;
1274 else if( nCurrScHandle < nScHandle )
1275 nFirstPos = nCurrPos + 1;
1276 else if( nCurrPos )
1277 nLastPos = nCurrPos - 1;
1278 else // special case for nLastPos = -1
1279 bLoop = false;
1281 if( nCurrScHandle == nScHandle )
1282 return *mxLastFoundDV;
1283 else if( nCurrScHandle < nScHandle )
1284 ++nCurrPos;
1287 // create new DV record
1288 mxLastFoundDV.reset( new XclExpDV( *this, nScHandle ) );
1289 maDVList.InsertRecord( mxLastFoundDV, nCurrPos );
1290 return *mxLastFoundDV;
1293 void XclExpDval::WriteBody( XclExpStream& rStrm )
1295 rStrm.WriteZeroBytes( 10 );
1296 rStrm << EXC_DVAL_NOOBJ << static_cast< sal_uInt32 >( maDVList.GetSize() );
1299 // Web Queries ================================================================
1301 XclExpWebQuery::XclExpWebQuery(
1302 const String& rRangeName,
1303 const String& rUrl,
1304 const String& rSource,
1305 sal_Int32 nRefrSecs ) :
1306 maDestRange( rRangeName ),
1307 maUrl( rUrl ),
1308 // refresh delay time: seconds -> minutes
1309 mnRefresh( ulimit_cast< sal_Int16 >( (nRefrSecs + 59L) / 60L ) ),
1310 mbEntireDoc( false )
1312 // comma separated list of HTML table names or indexes
1313 xub_StrLen nTokenCnt = rSource.GetTokenCount( ';' );
1314 String aNewTables, aAppendTable;
1315 xub_StrLen nStringIx = 0;
1316 bool bExitLoop = false;
1317 for( xub_StrLen nToken = 0; (nToken < nTokenCnt) && !bExitLoop; ++nToken )
1319 String aToken( rSource.GetToken( 0, ';', nStringIx ) );
1320 mbEntireDoc = ScfTools::IsHTMLDocName( aToken );
1321 bExitLoop = mbEntireDoc || ScfTools::IsHTMLTablesName( aToken );
1322 if( !bExitLoop && ScfTools::GetHTMLNameFromName( aToken, aAppendTable ) )
1323 ScGlobal::AddToken( aNewTables, aAppendTable, ',' );
1326 if( !bExitLoop ) // neither HTML_all nor HTML_tables found
1328 if( aNewTables.Len() )
1329 mxQryTables.reset( new XclExpString( aNewTables ) );
1330 else
1331 mbEntireDoc = true;
1335 XclExpWebQuery::~XclExpWebQuery()
1339 void XclExpWebQuery::Save( XclExpStream& rStrm )
1341 DBG_ASSERT( !mbEntireDoc || !mxQryTables.get(), "XclExpWebQuery::Save - illegal mode" );
1342 sal_uInt16 nFlags;
1344 // QSI record
1345 rStrm.StartRecord( EXC_ID_QSI, 10 + maDestRange.GetSize() );
1346 rStrm << EXC_QSI_DEFAULTFLAGS
1347 << sal_uInt16( 0x0010 )
1348 << sal_uInt16( 0x0012 )
1349 << sal_uInt32( 0x00000000 )
1350 << maDestRange;
1351 rStrm.EndRecord();
1353 // PARAMQRY record
1354 nFlags = 0;
1355 ::insert_value( nFlags, EXC_PQRYTYPE_WEBQUERY, 0, 3 );
1356 ::set_flag( nFlags, EXC_PQRY_WEBQUERY );
1357 ::set_flag( nFlags, EXC_PQRY_TABLES, !mbEntireDoc );
1358 rStrm.StartRecord( EXC_ID_PQRY, 12 );
1359 rStrm << nFlags
1360 << sal_uInt16( 0x0000 )
1361 << sal_uInt16( 0x0001 );
1362 rStrm.WriteZeroBytes( 6 );
1363 rStrm.EndRecord();
1365 // WQSTRING record
1366 rStrm.StartRecord( EXC_ID_WQSTRING, maUrl.GetSize() );
1367 rStrm << maUrl;
1368 rStrm.EndRecord();
1370 // unknown record 0x0802
1371 rStrm.StartRecord( EXC_ID_0802, 16 + maDestRange.GetSize() );
1372 rStrm << EXC_ID_0802; // repeated record id ?!?
1373 rStrm.WriteZeroBytes( 6 );
1374 rStrm << sal_uInt16( 0x0003 )
1375 << sal_uInt32( 0x00000000 )
1376 << sal_uInt16( 0x0010 )
1377 << maDestRange;
1378 rStrm.EndRecord();
1380 // WEBQRYSETTINGS record
1381 nFlags = mxQryTables.get() ? EXC_WQSETT_SPECTABLES : EXC_WQSETT_ALL;
1382 rStrm.StartRecord( EXC_ID_WQSETT, 28 );
1383 rStrm << EXC_ID_WQSETT // repeated record id ?!?
1384 << sal_uInt16( 0x0000 )
1385 << sal_uInt16( 0x0004 )
1386 << sal_uInt16( 0x0000 )
1387 << EXC_WQSETT_DEFAULTFLAGS
1388 << nFlags;
1389 rStrm.WriteZeroBytes( 10 );
1390 rStrm << mnRefresh // refresh delay in minutes
1391 << EXC_WQSETT_FORMATFULL
1392 << sal_uInt16( 0x0000 );
1393 rStrm.EndRecord();
1395 // WEBQRYTABLES record
1396 if( mxQryTables.get() )
1398 rStrm.StartRecord( EXC_ID_WQTABLES, 4 + mxQryTables->GetSize() );
1399 rStrm << EXC_ID_WQTABLES // repeated record id ?!?
1400 << sal_uInt16( 0x0000 )
1401 << *mxQryTables; // comma separated list of source tables
1402 rStrm.EndRecord();
1406 // ----------------------------------------------------------------------------
1408 XclExpWebQueryBuffer::XclExpWebQueryBuffer( const XclExpRoot& rRoot )
1410 SCTAB nScTab = rRoot.GetCurrScTab();
1411 SfxObjectShell* pShell = rRoot.GetDocShell();
1412 if( !pShell ) return;
1413 ScfPropertySet aModelProp( pShell->GetModel() );
1414 if( !aModelProp.Is() ) return;
1416 Reference< XAreaLinks > xAreaLinks;
1417 aModelProp.GetProperty( xAreaLinks, CREATE_OUSTRING( SC_UNO_AREALINKS ) );
1418 Reference< XIndexAccess > xLinksIA( xAreaLinks, UNO_QUERY );
1419 if( !xLinksIA.is() ) return;
1421 for( sal_Int32 nIndex = 0, nCount = xLinksIA->getCount(); nIndex < nCount; ++nIndex )
1423 Reference< XAreaLink > xAreaLink( xLinksIA->getByIndex( nIndex ), UNO_QUERY );
1424 if( xAreaLink.is() )
1426 CellRangeAddress aDestRange( xAreaLink->getDestArea() );
1427 if( static_cast< SCTAB >( aDestRange.Sheet ) == nScTab )
1429 ScfPropertySet aLinkProp( xAreaLink );
1430 OUString aFilter;
1431 if( aLinkProp.GetProperty( aFilter, CREATE_OUSTRING( SC_UNONAME_FILTER ) ) &&
1432 (aFilter == CREATE_OUSTRING( EXC_WEBQRY_FILTER )) )
1434 // get properties
1435 OUString /*aFilterOpt,*/ aUrl;
1436 sal_Int32 nRefresh = 0;
1438 // aLinkProp.GetProperty( aFilterOpt, CREATE_OUSTRING( SC_UNONAME_FILTOPT ) );
1439 aLinkProp.GetProperty( aUrl, CREATE_OUSTRING( SC_UNONAME_LINKURL ) );
1440 aLinkProp.GetProperty( nRefresh, CREATE_OUSTRING( SC_UNONAME_REFDELAY ) );
1442 String aAbsDoc( ScGlobal::GetAbsDocName( aUrl, pShell ) );
1443 INetURLObject aUrlObj( aAbsDoc );
1444 String aWebQueryUrl( aUrlObj.getFSysPath( INetURLObject::FSYS_DOS ) );
1445 if( !aWebQueryUrl.Len() )
1446 aWebQueryUrl = aAbsDoc;
1448 // find range or create a new range
1449 String aRangeName;
1450 ScRange aScDestRange;
1451 ScUnoConversion::FillScRange( aScDestRange, aDestRange );
1452 if( const ScRangeData* pRangeData = rRoot.GetNamedRanges().GetRangeAtBlock( aScDestRange ) )
1454 aRangeName = pRangeData->GetName();
1456 else
1458 XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
1459 XclExpNameManager& rNameMgr = rRoot.GetNameManager();
1461 // create a new unique defined name containing the range
1462 XclTokenArrayRef xTokArr = rFmlaComp.CreateFormula( EXC_FMLATYPE_WQUERY, aScDestRange );
1463 sal_uInt16 nNameIdx = rNameMgr.InsertUniqueName( aUrlObj.getBase(), xTokArr, nScTab );
1464 aRangeName = rNameMgr.GetOrigName( nNameIdx );
1467 // create and store the web query record
1468 if( aRangeName.Len() )
1469 AppendNewRecord( new XclExpWebQuery(
1470 aRangeName, aWebQueryUrl, xAreaLink->getSourceArea(), nRefresh ) );
1477 // ============================================================================