1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_scfilt.hxx"
26 #include "xecontent.hxx"
27
28 #include <list>
29 #include <algorithm>
30 #include <com/sun/star/container/XIndexAccess.hpp>
31 #include <com/sun/star/frame/XModel.hpp>
32 #include <com/sun/star/sheet/XAreaLinks.hpp>
33 #include <com/sun/star/sheet/XAreaLink.hpp>
34 #include <sfx2/objsh.hxx>
35 #include <tools/urlobj.hxx>
36 #include <svl/itemset.hxx>
37 #include <formula/grammar.hxx>
38 #include "scitems.hxx"
39 #include <editeng/eeitem.hxx>
40 #include <editeng/flditem.hxx>
41 #include "document.hxx"
42 #include "validat.hxx"
43 #include "unonames.hxx"
44 #include "convuno.hxx"
45 #include "rangenam.hxx"
46 #include "tokenarray.hxx"
47 #include "stlpool.hxx"
48 #include "patattr.hxx"
49 #include "fapihelper.hxx"
50 #include "xehelper.hxx"
51 #include "xestyle.hxx"
52 #include "xename.hxx"
53
54 using namespace ::oox;
55
56 using ::com::sun::star::uno::Reference;
57 using ::com::sun::star::uno::Any;
58 using ::com::sun::star::uno::UNO_QUERY;
59 using ::com::sun::star::beans::XPropertySet;
60 using ::com::sun::star::container::XIndexAccess;
61 using ::com::sun::star::frame::XModel;
62 using ::com::sun::star::table::CellRangeAddress;
63 using ::com::sun::star::sheet::XAreaLinks;
64 using ::com::sun::star::sheet::XAreaLink;
65 using ::rtl::OString;
66 using ::rtl::OUString;
67 using ::rtl::OUStringBuffer;
68
69 // Shared string table ========================================================
70
71 // 1 = SST hash table statistics prompt
72 #define EXC_INCL_SST_STATISTICS 0
73
74 // ----------------------------------------------------------------------------
75
76 /** A single string entry in the hash table. */
77 struct XclExpHashEntry
78 {
79 const XclExpString* mpString; /// Pointer to the string (no ownership).
80 sal_uInt32 mnSstIndex; /// The SST index of this string.
XclExpHashEntryXclExpHashEntry81 inline explicit XclExpHashEntry( const XclExpString* pString = 0, sal_uInt32 nSstIndex = 0 ) :
82 mpString( pString ), mnSstIndex( nSstIndex ) {}
83 };
84
85 /** Function object for strict weak ordering. */
86 struct XclExpHashEntrySWO
87 {
operator ()XclExpHashEntrySWO88 inline bool operator()( const XclExpHashEntry& rLeft, const XclExpHashEntry& rRight ) const
89 { return *rLeft.mpString < *rRight.mpString; }
90 };
91
92 // ----------------------------------------------------------------------------
93
94 /** Implementation of the SST export.
95 @descr Stores all passed strings in a hash table and prevents repeated
96 insertion of equal strings. */
97 class XclExpSstImpl
98 {
99 public:
100 explicit XclExpSstImpl();
101
102 /** Inserts the passed string, if not already inserted, and returns the unique SST index. */
103 sal_uInt32 Insert( XclExpStringRef xString );
104
105 /** Writes the complete SST and EXTSST records. */
106 void Save( XclExpStream& rStrm );
107 void SaveXml( XclExpXmlStream& rStrm );
108
109 private:
110 typedef ::std::list< XclExpStringRef > XclExpStringList;
111 typedef ::std::vector< XclExpHashEntry > XclExpHashVec;
112 typedef ::std::vector< XclExpHashVec > XclExpHashTab;
113
114 XclExpStringList maStringList; /// List of unique strings (in SST ID order).
115 XclExpHashTab maHashTab; /// Hashed table that manages string pointers.
116 sal_uInt32 mnTotal; /// Total count of strings (including doubles).
117 sal_uInt32 mnSize; /// Size of the SST (count of unique strings).
118 };
119
120 // ----------------------------------------------------------------------------
121
122 const sal_uInt32 EXC_SST_HASHTABLE_SIZE = 2048;
123
XclExpSstImpl()124 XclExpSstImpl::XclExpSstImpl() :
125 maHashTab( EXC_SST_HASHTABLE_SIZE ),
126 mnTotal( 0 ),
127 mnSize( 0 )
128 {
129 }
130
Insert(XclExpStringRef xString)131 sal_uInt32 XclExpSstImpl::Insert( XclExpStringRef xString )
132 {
133 DBG_ASSERT( xString.get(), "XclExpSstImpl::Insert - empty pointer not allowed" );
134 if( !xString.get() )
135 xString.reset( new XclExpString );
136
137 ++mnTotal;
138 sal_uInt32 nSstIndex = 0;
139
140 // calculate hash value in range [0,EXC_SST_HASHTABLE_SIZE)
141 sal_uInt16 nHash = xString->GetHash();
142 (nHash ^= (nHash / EXC_SST_HASHTABLE_SIZE)) %= EXC_SST_HASHTABLE_SIZE;
143
144 XclExpHashVec& rVec = maHashTab[ nHash ];
145 XclExpHashEntry aEntry( xString.get(), mnSize );
146 XclExpHashVec::iterator aIt = ::std::lower_bound( rVec.begin(), rVec.end(), aEntry, XclExpHashEntrySWO() );
147 if( (aIt == rVec.end()) || (*aIt->mpString != *xString) )
148 {
149 nSstIndex = mnSize;
150 maStringList.push_back( xString );
151 rVec.insert( aIt, aEntry );
152 ++mnSize;
153 }
154 else
155 {
156 nSstIndex = aIt->mnSstIndex;
157 }
158
159 return nSstIndex;
160 }
161
Save(XclExpStream & rStrm)162 void XclExpSstImpl::Save( XclExpStream& rStrm )
163 {
164 if( maStringList.empty() )
165 return;
166
167 #if (OSL_DEBUG_LEVEL > 1) && EXC_INCL_SST_STATISTICS
168 { // own scope for the statistics
169 #define APPENDINT( value ) Append( ByteString::CreateFromInt32( value ) )
170 ScfUInt32Vec aVec;
171 size_t nPerBucket = mnSize / EXC_SST_HASHTABLE_SIZE + 1, nEff = 0;
172 for( XclExpHashTab::const_iterator aTIt = maHashTab.begin(), aTEnd = maHashTab.end(); aTIt != aTEnd; ++aTIt )
173 {
174 size_t nSize = aTIt->size();
175 if( nSize >= aVec.size() ) aVec.resize( nSize + 1, 0 );
176 ++aVec[ nSize ];
177 if( nSize > nPerBucket ) nEff += nSize - nPerBucket;
178 }
179 ByteString aStr( "SST HASHING STATISTICS\n\n" );
180 aStr.Append( "Total count:\t" ).APPENDINT( mnTotal ).Append( " strings\n" );
181 aStr.Append( "Reduced to:\t" ).APPENDINT( mnSize ).Append( " strings (" );
182 aStr.APPENDINT( 100 * mnSize / mnTotal ).Append( "%)\n" );
183 aStr.Append( "Effectivity:\t\t" ).APPENDINT( 100 - 100 * nEff / mnSize );
184 aStr.Append( "% (best: " ).APPENDINT( nPerBucket ).Append( " strings per bucket)\n" );
185 aStr.Append( "\t\tCount of buckets\nBucket size\ttotal\tmax\tTotal strings\n" );
186 for( size_t nIx = 0, nSize = aVec.size(), nInc = 1; nIx < nSize; nIx += nInc )
187 {
188 if( (nIx == 10) || (nIx == 100) || (nIx == 1000) ) nInc = nIx;
189 size_t nMaxIx = ::std::min( nIx + nInc, nSize ), nCount = 0, nMaxCount = 0, nStrings = 0;
190 for( size_t nSubIx = nIx; nSubIx < nMaxIx; ++nSubIx )
191 {
192 nCount += aVec[ nSubIx ];
193 if( aVec[ nSubIx ] > nMaxCount ) nMaxCount = aVec[ nSubIx ];
194 nStrings += nSubIx * aVec[ nSubIx ];
195 }
196 if( nMaxCount )
197 {
198 aStr.APPENDINT( nIx );
199 if( nMaxIx - nIx > 1 ) aStr.Append( '-' ).APPENDINT( nMaxIx - 1 );
200 aStr.Append( "\t\t" ).APPENDINT( nCount ).Append( '\t' ).APPENDINT( nMaxCount );
201 aStr.Append( '\t' ).APPENDINT( nStrings ).Append( '\n' );
202 }
203 }
204 DBG_ERRORFILE( aStr.GetBuffer() );
205 #undef APPENDINT
206 }
207 #endif
208
209 SvMemoryStream aExtSst( 8192 );
210
211 sal_uInt32 nBucket = mnSize;
212 while( nBucket > 0x0100 )
213 nBucket /= 2;
214
215 sal_uInt16 nPerBucket = llimit_cast< sal_uInt16 >( nBucket, 8 );
216 sal_uInt16 nBucketIndex = 0;
217
218 // *** write the SST record ***
219
220 rStrm.StartRecord( EXC_ID_SST, 8 );
221
222 rStrm << mnTotal << mnSize;
223 for( XclExpStringList::const_iterator aIt = maStringList.begin(), aEnd = maStringList.end(); aIt != aEnd; ++aIt )
224 {
225 if( !nBucketIndex )
226 {
227 // write bucket info before string to get correct record position
228 sal_uInt32 nStrmPos = static_cast< sal_uInt32 >( rStrm.GetSvStreamPos() );
229 sal_uInt16 nRecPos = rStrm.GetRawRecPos() + 4;
230 aExtSst << nStrmPos // stream position
231 << nRecPos // position from start of SST or CONTINUE
232 << sal_uInt16( 0 ); // reserved
233 }
234
235 rStrm << **aIt;
236
237 if( ++nBucketIndex == nPerBucket )
238 nBucketIndex = 0;
239 }
240
241 rStrm.EndRecord();
242
243 // *** write the EXTSST record ***
244
245 rStrm.StartRecord( EXC_ID_EXTSST, 0 );
246
247 rStrm << nPerBucket;
248 rStrm.SetSliceSize( 8 ); // size of one bucket info
249 aExtSst.Seek( STREAM_SEEK_TO_BEGIN );
250 rStrm.CopyFromStream( aExtSst );
251
252 rStrm.EndRecord();
253 }
254
SaveXml(XclExpXmlStream & rStrm)255 void XclExpSstImpl::SaveXml( XclExpXmlStream& rStrm )
256 {
257 if( maStringList.empty() )
258 return;
259
260 sax_fastparser::FSHelperPtr pSst = rStrm.CreateOutputStream(
261 OUString::createFromAscii( "xl/sharedStrings.xml" ),
262 OUString::createFromAscii( "sharedStrings.xml" ),
263 rStrm.GetCurrentStream()->getOutputStream(),
264 "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
265 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" );
266 rStrm.PushStream( pSst );
267
268 pSst->startElement( XML_sst,
269 XML_xmlns, "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
270 XML_count, OString::valueOf( (sal_Int32) mnTotal ).getStr(),
271 XML_uniqueCount, OString::valueOf( (sal_Int32) mnSize ).getStr(),
272 FSEND );
273
274 for( XclExpStringList::const_iterator aIt = maStringList.begin(), aEnd = maStringList.end(); aIt != aEnd; ++aIt )
275 {
276 pSst->startElement( XML_si, FSEND );
277 (*aIt)->WriteXml( rStrm );
278 pSst->endElement( XML_si );
279 }
280
281 pSst->endElement( XML_sst );
282
283 rStrm.PopStream();
284 }
285
286 // ----------------------------------------------------------------------------
287
XclExpSst()288 XclExpSst::XclExpSst() :
289 mxImpl( new XclExpSstImpl )
290 {
291 }
292
~XclExpSst()293 XclExpSst::~XclExpSst()
294 {
295 }
296
Insert(XclExpStringRef xString)297 sal_uInt32 XclExpSst::Insert( XclExpStringRef xString )
298 {
299 return mxImpl->Insert( xString );
300 }
301
Save(XclExpStream & rStrm)302 void XclExpSst::Save( XclExpStream& rStrm )
303 {
304 mxImpl->Save( rStrm );
305 }
306
SaveXml(XclExpXmlStream & rStrm)307 void XclExpSst::SaveXml( XclExpXmlStream& rStrm )
308 {
309 mxImpl->SaveXml( rStrm );
310 }
311
312 // Merged cells ===============================================================
313
XclExpMergedcells(const XclExpRoot & rRoot)314 XclExpMergedcells::XclExpMergedcells( const XclExpRoot& rRoot ) :
315 XclExpRoot( rRoot )
316 {
317 }
318
AppendRange(const ScRange & rRange,sal_uInt32 nBaseXFId)319 void XclExpMergedcells::AppendRange( const ScRange& rRange, sal_uInt32 nBaseXFId )
320 {
321 if( GetBiff() == EXC_BIFF8 )
322 {
323 maMergedRanges.Append( rRange );
324 maBaseXFIds.push_back( nBaseXFId );
325 }
326 }
327
GetBaseXFId(const ScAddress & rPos) const328 sal_uInt32 XclExpMergedcells::GetBaseXFId( const ScAddress& rPos ) const
329 {
330 DBG_ASSERT( maBaseXFIds.size() == maMergedRanges.Count(), "XclExpMergedcells::GetBaseXFId - invalid lists" );
331 ScfUInt32Vec::const_iterator aIt = maBaseXFIds.begin();
332 ScRangeList& rNCRanges = const_cast< ScRangeList& >( maMergedRanges );
333 for( const ScRange* pScRange = rNCRanges.First(); pScRange; pScRange = rNCRanges.Next(), ++aIt )
334 if( pScRange->In( rPos ) )
335 return *aIt;
336 return EXC_XFID_NOTFOUND;
337 }
338
Save(XclExpStream & rStrm)339 void XclExpMergedcells::Save( XclExpStream& rStrm )
340 {
341 if( GetBiff() == EXC_BIFF8 )
342 {
343 XclRangeList aXclRanges;
344 GetAddressConverter().ConvertRangeList( aXclRanges, maMergedRanges, true );
345 size_t nFirstRange = 0;
346 size_t nRemainingRanges = aXclRanges.size();
347 while( nRemainingRanges > 0 )
348 {
349 size_t nRangeCount = ::std::min< size_t >( nRemainingRanges, EXC_MERGEDCELLS_MAXCOUNT );
350 rStrm.StartRecord( EXC_ID_MERGEDCELLS, 2 + 8 * nRangeCount );
351 aXclRanges.WriteSubList( rStrm, nFirstRange, nRangeCount );
352 rStrm.EndRecord();
353 nFirstRange += nRangeCount;
354 nRemainingRanges -= nRangeCount;
355 }
356 }
357 }
358
SaveXml(XclExpXmlStream & rStrm)359 void XclExpMergedcells::SaveXml( XclExpXmlStream& rStrm )
360 {
361 sal_uLong nCount = maMergedRanges.Count();
362 if( !nCount )
363 return;
364 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
365 rWorksheet->startElement( XML_mergeCells,
366 XML_count, OString::valueOf( (sal_Int32) nCount ).getStr(),
367 FSEND );
368 for( sal_uLong i = 0; i < nCount; ++i )
369 {
370 if( const ScRange* pRange = maMergedRanges.GetObject( i ) )
371 {
372 rWorksheet->singleElement( XML_mergeCell,
373 XML_ref, XclXmlUtils::ToOString( *pRange ).getStr(),
374 FSEND );
375 }
376 }
377 rWorksheet->endElement( XML_mergeCells );
378 }
379
380 // Hyperlinks =================================================================
381
XclExpHyperlink(const XclExpRoot & rRoot,const SvxURLField & rUrlField,const ScAddress & rScPos)382 XclExpHyperlink::XclExpHyperlink( const XclExpRoot& rRoot, const SvxURLField& rUrlField, const ScAddress& rScPos ) :
383 XclExpRecord( EXC_ID_HLINK ),
384 maScPos( rScPos ),
385 mxVarData( new SvMemoryStream ),
386 mnFlags( 0 )
387 {
388 const String& rUrl = rUrlField.GetURL();
389 const String& rRepr = rUrlField.GetRepresentation();
390 INetURLObject aUrlObj( rUrl );
391 const INetProtocol eProtocol = aUrlObj.GetProtocol();
392 bool bWithRepr = rRepr.Len() > 0;
393 XclExpStream aXclStrm( *mxVarData, rRoot ); // using in raw write mode.
394
395 // description
396 if( bWithRepr )
397 {
398 XclExpString aDescr( rRepr, EXC_STR_FORCEUNICODE, 255 );
399 aXclStrm << sal_uInt32( aDescr.Len() + 1 ); // string length + 1 trailing zero word
400 aDescr.WriteBuffer( aXclStrm ); // NO flags
401 aXclStrm << sal_uInt16( 0 );
402
403 mnFlags |= EXC_HLINK_DESCR;
404 mxRepr.reset( new String( rRepr ) );
405 }
406
407 // file link or URL
408 if( eProtocol == INET_PROT_FILE )
409 {
410 sal_uInt16 nLevel;
411 bool bRel;
412 String aFileName( BuildFileName( nLevel, bRel, rUrl, rRoot ) );
413
414 if( !bRel )
415 mnFlags |= EXC_HLINK_ABS;
416 mnFlags |= EXC_HLINK_BODY;
417
418 ByteString aAsciiLink( aFileName, rRoot.GetTextEncoding() );
419 XclExpString aLink( aFileName, EXC_STR_FORCEUNICODE, 255 );
420 aXclStrm << XclTools::maGuidFileMoniker
421 << nLevel
422 << sal_uInt32( aAsciiLink.Len() + 1 ); // string length + 1 trailing zero byte
423 aXclStrm.Write( aAsciiLink.GetBuffer(), aAsciiLink.Len() );
424 aXclStrm << sal_uInt8( 0 )
425 << sal_uInt32( 0xDEADFFFF );
426 aXclStrm.WriteZeroBytes( 20 );
427 aXclStrm << sal_uInt32( aLink.GetBufferSize() + 6 )
428 << sal_uInt32( aLink.GetBufferSize() ) // byte count, not string length
429 << sal_uInt16( 0x0003 );
430 aLink.WriteBuffer( aXclStrm ); // NO flags
431
432 if( !mxRepr.get() )
433 mxRepr.reset( new String( aFileName ) );
434
435 msTarget = XclXmlUtils::ToOUString( aLink );
436 }
437 else if( eProtocol != INET_PROT_NOT_VALID )
438 {
439 XclExpString aUrl( aUrlObj.GetURLNoMark(), EXC_STR_FORCEUNICODE, 255 );
440 aXclStrm << XclTools::maGuidUrlMoniker
441 << sal_uInt32( aUrl.GetBufferSize() + 2 ); // byte count + 1 trailing zero word
442 aUrl.WriteBuffer( aXclStrm ); // NO flags
443 aXclStrm << sal_uInt16( 0 );
444
445 mnFlags |= EXC_HLINK_BODY | EXC_HLINK_ABS;
446 if( !mxRepr.get() )
447 mxRepr.reset( new String( rUrl ) );
448
449 msTarget = XclXmlUtils::ToOUString( aUrl );
450 }
451 else if( rUrl.GetChar( 0 ) == '#' ) // hack for #89066#
452 {
453 String aTextMark( rUrl.Copy( 1 ) );
454 aTextMark.SearchAndReplace( '.', '!' );
455 mxTextMark.reset( new XclExpString( aTextMark, EXC_STR_FORCEUNICODE, 255 ) );
456 }
457
458 // text mark
459 if( !mxTextMark.get() && aUrlObj.HasMark() )
460 mxTextMark.reset( new XclExpString( aUrlObj.GetMark(), EXC_STR_FORCEUNICODE, 255 ) );
461
462 if( mxTextMark.get() )
463 {
464 aXclStrm << sal_uInt32( mxTextMark->Len() + 1 ); // string length + 1 trailing zero word
465 mxTextMark->WriteBuffer( aXclStrm ); // NO flags
466 aXclStrm << sal_uInt16( 0 );
467
468 mnFlags |= EXC_HLINK_MARK;
469 }
470
471 SetRecSize( 32 + mxVarData->Tell() );
472 }
473
~XclExpHyperlink()474 XclExpHyperlink::~XclExpHyperlink()
475 {
476 }
477
BuildFileName(sal_uInt16 & rnLevel,bool & rbRel,const String & rUrl,const XclExpRoot & rRoot) const478 String XclExpHyperlink::BuildFileName(
479 sal_uInt16& rnLevel, bool& rbRel, const String& rUrl, const XclExpRoot& rRoot ) const
480 {
481 String aDosName( INetURLObject( rUrl ).getFSysPath( INetURLObject::FSYS_DOS ) );
482 rnLevel = 0;
483 rbRel = rRoot.IsRelUrl();
484
485 if( rbRel )
486 {
487 // try to convert to relative file name
488 String aTmpName( aDosName );
489 aDosName = INetURLObject::GetRelURL( rRoot.GetBasePath(), rUrl,
490 INetURLObject::WAS_ENCODED, INetURLObject::DECODE_WITH_CHARSET );
491
492 if( aDosName.SearchAscii( INET_FILE_SCHEME ) == 0 )
493 {
494 // not converted to rel -> back to old, return absolute flag
495 aDosName = aTmpName;
496 rbRel = false;
497 }
498 else if( aDosName.SearchAscii( "./" ) == 0 )
499 {
500 aDosName.Erase( 0, 2 );
501 }
502 else
503 {
504 while( aDosName.SearchAndReplaceAscii( "../", EMPTY_STRING ) == 0 )
505 ++rnLevel;
506 }
507 }
508 return aDosName;
509 }
510
WriteBody(XclExpStream & rStrm)511 void XclExpHyperlink::WriteBody( XclExpStream& rStrm )
512 {
513 sal_uInt16 nXclCol = static_cast< sal_uInt16 >( maScPos.Col() );
514 sal_uInt16 nXclRow = static_cast< sal_uInt16 >( maScPos.Row() );
515 mxVarData->Seek( STREAM_SEEK_TO_BEGIN );
516
517 rStrm << nXclRow << nXclRow << nXclCol << nXclCol
518 << XclTools::maGuidStdLink
519 << sal_uInt32( 2 )
520 << mnFlags;
521 rStrm.CopyFromStream( *mxVarData );
522 }
523
SaveXml(XclExpXmlStream & rStrm)524 void XclExpHyperlink::SaveXml( XclExpXmlStream& rStrm )
525 {
526 OUString sId = rStrm.addRelation( rStrm.GetCurrentStream()->getOutputStream(),
527 XclXmlUtils::ToOUString( "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" ),
528 msTarget,
529 true );
530 rStrm.GetCurrentStream()->singleElement( XML_hyperlink,
531 XML_ref, XclXmlUtils::ToOString( maScPos ).getStr(),
532 FSNS( XML_r, XML_id ), XclXmlUtils::ToOString( sId ).getStr(),
533 XML_location, mxTextMark.get() != NULL
534 ? XclXmlUtils::ToOString( *mxTextMark ).getStr()
535 : NULL,
536 // OOXTODO: XML_tooltip, from record HLinkTooltip 800h wzTooltip
537 XML_display, XclXmlUtils::ToOString( *mxRepr ).getStr(),
538 FSEND );
539 }
540
541 // Label ranges ===============================================================
542
XclExpLabelranges(const XclExpRoot & rRoot)543 XclExpLabelranges::XclExpLabelranges( const XclExpRoot& rRoot ) :
544 XclExpRoot( rRoot )
545 {
546 SCTAB nScTab = GetCurrScTab();
547 // row label ranges
548 FillRangeList( maRowRanges, rRoot.GetDoc().GetRowNameRangesRef(), nScTab );
549 // row labels only over 1 column (restriction of Excel97/2000/XP)
550 for( ScRange* pScRange = maRowRanges.First(); pScRange; pScRange = maRowRanges.Next() )
551 if( pScRange->aStart.Col() != pScRange->aEnd.Col() )
552 pScRange->aEnd.SetCol( pScRange->aStart.Col() );
553 // col label ranges
554 FillRangeList( maColRanges, rRoot.GetDoc().GetColNameRangesRef(), nScTab );
555 }
556
FillRangeList(ScRangeList & rScRanges,ScRangePairListRef xLabelRangesRef,SCTAB nScTab)557 void XclExpLabelranges::FillRangeList( ScRangeList& rScRanges,
558 ScRangePairListRef xLabelRangesRef, SCTAB nScTab )
559 {
560 for( const ScRangePair* pRangePair = xLabelRangesRef->First(); pRangePair; pRangePair = xLabelRangesRef->Next() )
561 {
562 const ScRange& rScRange = pRangePair->GetRange( 0 );
563 if( rScRange.aStart.Tab() == nScTab )
564 rScRanges.Append( rScRange );
565 }
566 }
567
Save(XclExpStream & rStrm)568 void XclExpLabelranges::Save( XclExpStream& rStrm )
569 {
570 XclExpAddressConverter& rAddrConv = GetAddressConverter();
571 XclRangeList aRowXclRanges, aColXclRanges;
572 rAddrConv.ConvertRangeList( aRowXclRanges, maRowRanges, false );
573 rAddrConv.ConvertRangeList( aColXclRanges, maColRanges, false );
574 if( !aRowXclRanges.empty() || !aColXclRanges.empty() )
575 {
576 rStrm.StartRecord( EXC_ID_LABELRANGES, 4 + 8 * (aRowXclRanges.size() + aColXclRanges.size()) );
577 rStrm << aRowXclRanges << aColXclRanges;
578 rStrm.EndRecord();
579 }
580 }
581
582 // Conditional formatting ====================================================
583
584 /** Represents a CF record that contains one condition of a conditional format. */
585 class XclExpCFImpl : protected XclExpRoot
586 {
587 public:
588 explicit XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry );
589
590 /** Writes the body of the CF record. */
591 void WriteBody( XclExpStream& rStrm );
592
593 private:
594 const ScCondFormatEntry& mrFormatEntry; /// Calc conditional format entry.
595 XclFontData maFontData; /// Font formatting attributes.
596 XclExpCellBorder maBorder; /// Border formatting attributes.
597 XclExpCellArea maArea; /// Pattern formatting attributes.
598 XclTokenArrayRef mxTokArr1; /// Formula for first condition.
599 XclTokenArrayRef mxTokArr2; /// Formula for second condition.
600 sal_uInt32 mnFontColorId; /// Font color ID.
601 sal_uInt8 mnType; /// Type of the condition (cell/formula).
602 sal_uInt8 mnOperator; /// Comparison operator for cell type.
603 bool mbFontUsed; /// true = Any font attribute used.
604 bool mbHeightUsed; /// true = Font height used.
605 bool mbWeightUsed; /// true = Font weight used.
606 bool mbColorUsed; /// true = Font color used.
607 bool mbUnderlUsed; /// true = Font underline type used.
608 bool mbItalicUsed; /// true = Font posture used.
609 bool mbStrikeUsed; /// true = Font strikeout used.
610 bool mbBorderUsed; /// true = Border attribute used.
611 bool mbPattUsed; /// true = Pattern attribute used.
612 };
613
614 // ----------------------------------------------------------------------------
615
XclExpCFImpl(const XclExpRoot & rRoot,const ScCondFormatEntry & rFormatEntry)616 XclExpCFImpl::XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry ) :
617 XclExpRoot( rRoot ),
618 mrFormatEntry( rFormatEntry ),
619 mnFontColorId( 0 ),
620 mnType( EXC_CF_TYPE_CELL ),
621 mnOperator( EXC_CF_CMP_NONE ),
622 mbFontUsed( false ),
623 mbHeightUsed( false ),
624 mbWeightUsed( false ),
625 mbColorUsed( false ),
626 mbUnderlUsed( false ),
627 mbItalicUsed( false ),
628 mbStrikeUsed( false ),
629 mbBorderUsed( false ),
630 mbPattUsed( false )
631 {
632 /* Get formatting attributes here, and not in WriteBody(). This is needed to
633 correctly insert all colors into the palette. */
634
635 if( SfxStyleSheetBase* pStyleSheet = GetDoc().GetStyleSheetPool()->Find( mrFormatEntry.GetStyle(), SFX_STYLE_FAMILY_PARA ) )
636 {
637 const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
638
639 // font
640 mbHeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_HEIGHT, true );
641 mbWeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_WEIGHT, true );
642 mbColorUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_COLOR, true );
643 mbUnderlUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_UNDERLINE, true );
644 mbItalicUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_POSTURE, true );
645 mbStrikeUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_CROSSEDOUT, true );
646 mbFontUsed = mbHeightUsed || mbWeightUsed || mbColorUsed || mbUnderlUsed || mbItalicUsed || mbStrikeUsed;
647 if( mbFontUsed )
648 {
649 Font aFont;
650 ScPatternAttr::GetFont( aFont, rItemSet, SC_AUTOCOL_RAW );
651 maFontData.FillFromVclFont( aFont );
652 mnFontColorId = GetPalette().InsertColor( maFontData.maColor, EXC_COLOR_CELLTEXT );
653 }
654
655 // border
656 mbBorderUsed = ScfTools::CheckItem( rItemSet, ATTR_BORDER, true );
657 if( mbBorderUsed )
658 maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff() );
659
660 // pattern
661 mbPattUsed = ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true );
662 if( mbPattUsed )
663 maArea.FillFromItemSet( rItemSet, GetPalette(), GetBiff() );
664 }
665
666 // *** mode and comparison operator ***
667
668 bool bFmla2 = false;
669 switch( rFormatEntry.GetOperation() )
670 {
671 case SC_COND_NONE: mnType = EXC_CF_TYPE_NONE; break;
672 case SC_COND_BETWEEN: mnOperator = EXC_CF_CMP_BETWEEN; bFmla2 = true; break;
673 case SC_COND_NOTBETWEEN: mnOperator = EXC_CF_CMP_NOT_BETWEEN; bFmla2 = true; break;
674 case SC_COND_EQUAL: mnOperator = EXC_CF_CMP_EQUAL; break;
675 case SC_COND_NOTEQUAL: mnOperator = EXC_CF_CMP_NOT_EQUAL; break;
676 case SC_COND_GREATER: mnOperator = EXC_CF_CMP_GREATER; break;
677 case SC_COND_LESS: mnOperator = EXC_CF_CMP_LESS; break;
678 case SC_COND_EQGREATER: mnOperator = EXC_CF_CMP_GREATER_EQUAL; break;
679 case SC_COND_EQLESS: mnOperator = EXC_CF_CMP_LESS_EQUAL; break;
680 case SC_COND_DIRECT: mnType = EXC_CF_TYPE_FMLA; break;
681 default: mnType = EXC_CF_TYPE_NONE;
682 DBG_ERRORFILE( "XclExpCF::WriteBody - unknown condition type" );
683 }
684
685 // *** formulas ***
686
687 XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
688
689 ::std::auto_ptr< ScTokenArray > xScTokArr( mrFormatEntry.CreateTokenArry( 0 ) );
690 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
691
692 if( bFmla2 )
693 {
694 xScTokArr.reset( mrFormatEntry.CreateTokenArry( 1 ) );
695 mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
696 }
697 }
698
WriteBody(XclExpStream & rStrm)699 void XclExpCFImpl::WriteBody( XclExpStream& rStrm )
700 {
701 // *** mode and comparison operator ***
702
703 rStrm << mnType << mnOperator;
704
705 // *** formula sizes ***
706
707 sal_uInt16 nFmlaSize1 = mxTokArr1.get() ? mxTokArr1->GetSize() : 0;
708 sal_uInt16 nFmlaSize2 = mxTokArr2.get() ? mxTokArr2->GetSize() : 0;
709 rStrm << nFmlaSize1 << nFmlaSize2;
710
711 // *** formatting blocks ***
712
713 if( mbFontUsed || mbBorderUsed || mbPattUsed )
714 {
715 sal_uInt32 nFlags = EXC_CF_ALLDEFAULT;
716
717 ::set_flag( nFlags, EXC_CF_BLOCK_FONT, mbFontUsed );
718 ::set_flag( nFlags, EXC_CF_BLOCK_BORDER, mbBorderUsed );
719 ::set_flag( nFlags, EXC_CF_BLOCK_AREA, mbPattUsed );
720
721 // attributes used -> set flags to 0.
722 ::set_flag( nFlags, EXC_CF_BORDER_ALL, !mbBorderUsed );
723 ::set_flag( nFlags, EXC_CF_AREA_ALL, !mbPattUsed );
724
725 rStrm << nFlags << sal_uInt16( 0 );
726
727 if( mbFontUsed )
728 {
729 // font height, 0xFFFFFFFF indicates unused
730 sal_uInt32 nHeight = mbHeightUsed ? maFontData.mnHeight : 0xFFFFFFFF;
731 // font style: italic and strikeout
732 sal_uInt32 nStyle = 0;
733 ::set_flag( nStyle, EXC_CF_FONT_STYLE, maFontData.mbItalic );
734 ::set_flag( nStyle, EXC_CF_FONT_STRIKEOUT, maFontData.mbStrikeout );
735 // font color, 0xFFFFFFFF indicates unused
736 sal_uInt32 nColor = mbColorUsed ? GetPalette().GetColorIndex( mnFontColorId ) : 0xFFFFFFFF;
737 // font used flags for italic, weight, and strikeout -> 0 = used, 1 = default
738 sal_uInt32 nFontFlags1 = EXC_CF_FONT_ALLDEFAULT;
739 ::set_flag( nFontFlags1, EXC_CF_FONT_STYLE, !(mbItalicUsed || mbWeightUsed) );
740 ::set_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT, !mbStrikeUsed );
741 // font used flag for underline -> 0 = used, 1 = default
742 sal_uInt32 nFontFlags3 = mbUnderlUsed ? 0 : EXC_CF_FONT_UNDERL;
743
744 rStrm.WriteZeroBytesToRecord( 64 );
745 rStrm << nHeight
746 << nStyle
747 << maFontData.mnWeight
748 << EXC_FONTESC_NONE
749 << maFontData.mnUnderline;
750 rStrm.WriteZeroBytesToRecord( 3 );
751 rStrm << nColor
752 << sal_uInt32( 0 )
753 << nFontFlags1
754 << EXC_CF_FONT_ESCAPEM // escapement never used -> set the flag
755 << nFontFlags3;
756 rStrm.WriteZeroBytesToRecord( 16 );
757 rStrm << sal_uInt16( 1 ); // must be 1
758 }
759
760 if( mbBorderUsed )
761 {
762 sal_uInt16 nLineStyle = 0;
763 sal_uInt32 nLineColor = 0;
764 maBorder.SetFinalColors( GetPalette() );
765 maBorder.FillToCF8( nLineStyle, nLineColor );
766 rStrm << nLineStyle << nLineColor << sal_uInt16( 0 );
767 }
768
769 if( mbPattUsed )
770 {
771 sal_uInt16 nPattern = 0, nColor = 0;
772 maArea.SetFinalColors( GetPalette() );
773 maArea.FillToCF8( nPattern, nColor );
774 rStrm << nPattern << nColor;
775 }
776 }
777 else
778 {
779 // no data blocks at all
780 rStrm << sal_uInt32( 0 ) << sal_uInt16( 0 );
781 }
782
783 // *** formulas ***
784
785 if( mxTokArr1.get() )
786 mxTokArr1->WriteArray( rStrm );
787 if( mxTokArr2.get() )
788 mxTokArr2->WriteArray( rStrm );
789 }
790
791 // ----------------------------------------------------------------------------
792
XclExpCF(const XclExpRoot & rRoot,const ScCondFormatEntry & rFormatEntry)793 XclExpCF::XclExpCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry ) :
794 XclExpRecord( EXC_ID_CF ),
795 XclExpRoot( rRoot ),
796 mxImpl( new XclExpCFImpl( rRoot, rFormatEntry ) )
797 {
798 }
799
~XclExpCF()800 XclExpCF::~XclExpCF()
801 {
802 }
803
WriteBody(XclExpStream & rStrm)804 void XclExpCF::WriteBody( XclExpStream& rStrm )
805 {
806 mxImpl->WriteBody( rStrm );
807 }
808
809 // ----------------------------------------------------------------------------
810
XclExpCondfmt(const XclExpRoot & rRoot,const ScConditionalFormat & rCondFormat)811 XclExpCondfmt::XclExpCondfmt( const XclExpRoot& rRoot, const ScConditionalFormat& rCondFormat ) :
812 XclExpRecord( EXC_ID_CONDFMT ),
813 XclExpRoot( rRoot )
814 {
815 ScRangeList aScRanges;
816 GetDoc().FindConditionalFormat( rCondFormat.GetKey(), aScRanges, GetCurrScTab() );
817 GetAddressConverter().ConvertRangeList( maXclRanges, aScRanges, true );
818 if( !maXclRanges.empty() )
819 {
820 for( sal_uInt16 nIndex = 0, nCount = rCondFormat.Count(); nIndex < nCount; ++nIndex )
821 if( const ScCondFormatEntry* pEntry = rCondFormat.GetEntry( nIndex ) )
822 maCFList.AppendNewRecord( new XclExpCF( GetRoot(), *pEntry ) );
823 aScRanges.Format( msSeqRef, SCA_VALID, NULL, formula::FormulaGrammar::CONV_XL_A1 );
824 }
825 }
826
~XclExpCondfmt()827 XclExpCondfmt::~XclExpCondfmt()
828 {
829 }
830
IsValid() const831 bool XclExpCondfmt::IsValid() const
832 {
833 return !maCFList.IsEmpty() && !maXclRanges.empty();
834 }
835
Save(XclExpStream & rStrm)836 void XclExpCondfmt::Save( XclExpStream& rStrm )
837 {
838 if( IsValid() )
839 {
840 XclExpRecord::Save( rStrm );
841 maCFList.Save( rStrm );
842 }
843 }
844
WriteBody(XclExpStream & rStrm)845 void XclExpCondfmt::WriteBody( XclExpStream& rStrm )
846 {
847 DBG_ASSERT( !maCFList.IsEmpty(), "XclExpCondfmt::WriteBody - no CF records to write" );
848 DBG_ASSERT( !maXclRanges.empty(), "XclExpCondfmt::WriteBody - no cell ranges found" );
849
850 rStrm << static_cast< sal_uInt16 >( maCFList.GetSize() )
851 << sal_uInt16( 1 )
852 << maXclRanges.GetEnclosingRange()
853 << maXclRanges;
854 }
855
SaveXml(XclExpXmlStream & rStrm)856 void XclExpCondfmt::SaveXml( XclExpXmlStream& rStrm )
857 {
858 if( !IsValid() )
859 return;
860
861 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
862 rWorksheet->startElement( XML_conditionalFormatting,
863 XML_sqref, XclXmlUtils::ToOString( msSeqRef ).getStr(),
864 // OOXTODO: XML_pivot,
865 FSEND );
866 maCFList.SaveXml( rStrm );
867 // OOXTODO: XML_extLst
868 rWorksheet->endElement( XML_conditionalFormatting );
869 }
870
871 // ----------------------------------------------------------------------------
872
XclExpCondFormatBuffer(const XclExpRoot & rRoot)873 XclExpCondFormatBuffer::XclExpCondFormatBuffer( const XclExpRoot& rRoot ) :
874 XclExpRoot( rRoot )
875 {
876 if( const ScConditionalFormatList* pCondFmtList = GetDoc().GetCondFormList() )
877 {
878 if( const ScConditionalFormatPtr* ppCondFmt = pCondFmtList->GetData() )
879 {
880 const ScConditionalFormatPtr* ppCondEnd = ppCondFmt + pCondFmtList->Count();
881 for( ; ppCondFmt < ppCondEnd; ++ppCondFmt )
882 {
883 if( *ppCondFmt )
884 {
885 XclExpCondfmtList::RecordRefType xCondfmtRec( new XclExpCondfmt( GetRoot(), **ppCondFmt ) );
886 if( xCondfmtRec->IsValid() )
887 maCondfmtList.AppendRecord( xCondfmtRec );
888 }
889 }
890 }
891 }
892 }
893
Save(XclExpStream & rStrm)894 void XclExpCondFormatBuffer::Save( XclExpStream& rStrm )
895 {
896 maCondfmtList.Save( rStrm );
897 }
898
SaveXml(XclExpXmlStream & rStrm)899 void XclExpCondFormatBuffer::SaveXml( XclExpXmlStream& rStrm )
900 {
901 maCondfmtList.SaveXml( rStrm );
902 }
903
904 // Validation =================================================================
905
906 namespace {
907
908 /** Writes a formula for the DV record. */
lclWriteDvFormula(XclExpStream & rStrm,const XclTokenArray * pXclTokArr)909 void lclWriteDvFormula( XclExpStream& rStrm, const XclTokenArray* pXclTokArr )
910 {
911 sal_uInt16 nFmlaSize = pXclTokArr ? pXclTokArr->GetSize() : 0;
912 rStrm << nFmlaSize << sal_uInt16( 0 );
913 if( pXclTokArr )
914 pXclTokArr->WriteArray( rStrm );
915 }
916
917 /** Writes a formula for the DV record, based on a single string. */
lclWriteDvFormula(XclExpStream & rStrm,const XclExpString & rString)918 void lclWriteDvFormula( XclExpStream& rStrm, const XclExpString& rString )
919 {
920 // fake a formula with a single tStr token
921 rStrm << static_cast< sal_uInt16 >( rString.GetSize() + 1 )
922 << sal_uInt16( 0 )
923 << EXC_TOKID_STR
924 << rString;
925 }
926
lcl_GetValidationType(sal_uInt32 nFlags)927 const char* lcl_GetValidationType( sal_uInt32 nFlags )
928 {
929 switch( nFlags & EXC_DV_MODE_MASK )
930 {
931 case EXC_DV_MODE_ANY: return "none";
932 case EXC_DV_MODE_WHOLE: return "whole";
933 case EXC_DV_MODE_DECIMAL: return "decimal";
934 case EXC_DV_MODE_LIST: return "list";
935 case EXC_DV_MODE_DATE: return "date";
936 case EXC_DV_MODE_TIME: return "time";
937 case EXC_DV_MODE_TEXTLEN: return "textLength";
938 case EXC_DV_MODE_CUSTOM: return "custom";
939 }
940 return NULL;
941 }
942
lcl_GetOperatorType(sal_uInt32 nFlags)943 const char* lcl_GetOperatorType( sal_uInt32 nFlags )
944 {
945 switch( nFlags & EXC_DV_COND_MASK )
946 {
947 case EXC_DV_COND_BETWEEN: return "between";
948 case EXC_DV_COND_NOTBETWEEN: return "notBetween";
949 case EXC_DV_COND_EQUAL: return "equal";
950 case EXC_DV_COND_NOTEQUAL: return "notEqual";
951 case EXC_DV_COND_GREATER: return "greaterThan";
952 case EXC_DV_COND_LESS: return "lessThan";
953 case EXC_DV_COND_EQGREATER: return "greaterThanOrEqual";
954 case EXC_DV_COND_EQLESS: return "lessThanOrEqual";
955 }
956 return NULL;
957 }
958
959 } // namespace
960
961 // ----------------------------------------------------------------------------
962
XclExpDV(const XclExpRoot & rRoot,sal_uLong nScHandle)963 XclExpDV::XclExpDV( const XclExpRoot& rRoot, sal_uLong nScHandle ) :
964 XclExpRecord( EXC_ID_DV ),
965 XclExpRoot( rRoot ),
966 mnFlags( 0 ),
967 mnScHandle( nScHandle )
968 {
969 if( const ScValidationData* pValData = GetDoc().GetValidationEntry( mnScHandle ) )
970 {
971 // prompt box - empty string represented by single NUL character
972 String aTitle, aText;
973 bool bShowPrompt = (pValData->GetInput( aTitle, aText ) == sal_True);
974 if( aTitle.Len() )
975 maPromptTitle.Assign( aTitle );
976 else
977 maPromptTitle.Assign( '\0' );
978 if( aText.Len() )
979 maPromptText.Assign( aText );
980 else
981 maPromptText.Assign( '\0' );
982
983 // error box - empty string represented by single NUL character
984 ScValidErrorStyle eScErrorStyle;
985 bool bShowError = (pValData->GetErrMsg( aTitle, aText, eScErrorStyle ) == sal_True);
986 if( aTitle.Len() )
987 maErrorTitle.Assign( aTitle );
988 else
989 maErrorTitle.Assign( '\0' );
990 if( aText.Len() )
991 maErrorText.Assign( aText );
992 else
993 maErrorText.Assign( '\0' );
994
995 // flags
996 switch( pValData->GetDataMode() )
997 {
998 case SC_VALID_ANY: mnFlags |= EXC_DV_MODE_ANY; break;
999 case SC_VALID_WHOLE: mnFlags |= EXC_DV_MODE_WHOLE; break;
1000 case SC_VALID_DECIMAL: mnFlags |= EXC_DV_MODE_DECIMAL; break;
1001 case SC_VALID_LIST: mnFlags |= EXC_DV_MODE_LIST; break;
1002 case SC_VALID_DATE: mnFlags |= EXC_DV_MODE_DATE; break;
1003 case SC_VALID_TIME: mnFlags |= EXC_DV_MODE_TIME; break;
1004 case SC_VALID_TEXTLEN: mnFlags |= EXC_DV_MODE_TEXTLEN; break;
1005 case SC_VALID_CUSTOM: mnFlags |= EXC_DV_MODE_CUSTOM; break;
1006 default: DBG_ERRORFILE( "XclExpDV::XclExpDV - unknown mode" );
1007 }
1008
1009 switch( pValData->GetOperation() )
1010 {
1011 case SC_COND_NONE:
1012 case SC_COND_EQUAL: mnFlags |= EXC_DV_COND_EQUAL; break;
1013 case SC_COND_LESS: mnFlags |= EXC_DV_COND_LESS; break;
1014 case SC_COND_GREATER: mnFlags |= EXC_DV_COND_GREATER; break;
1015 case SC_COND_EQLESS: mnFlags |= EXC_DV_COND_EQLESS; break;
1016 case SC_COND_EQGREATER: mnFlags |= EXC_DV_COND_EQGREATER; break;
1017 case SC_COND_NOTEQUAL: mnFlags |= EXC_DV_COND_NOTEQUAL; break;
1018 case SC_COND_BETWEEN: mnFlags |= EXC_DV_COND_BETWEEN; break;
1019 case SC_COND_NOTBETWEEN: mnFlags |= EXC_DV_COND_NOTBETWEEN; break;
1020 default: DBG_ERRORFILE( "XclExpDV::XclExpDV - unknown condition" );
1021 }
1022 switch( eScErrorStyle )
1023 {
1024 case SC_VALERR_STOP: mnFlags |= EXC_DV_ERROR_STOP; break;
1025 case SC_VALERR_WARNING: mnFlags |= EXC_DV_ERROR_WARNING; break;
1026 case SC_VALERR_INFO: mnFlags |= EXC_DV_ERROR_INFO; break;
1027 case SC_VALERR_MACRO:
1028 // #111781# set INFO for validity with macro call, delete title
1029 mnFlags |= EXC_DV_ERROR_INFO;
1030 maErrorTitle.Assign( '\0' ); // contains macro name
1031 break;
1032 default: DBG_ERRORFILE( "XclExpDV::XclExpDV - unknown error style" );
1033 }
1034 ::set_flag( mnFlags, EXC_DV_IGNOREBLANK, pValData->IsIgnoreBlank() );
1035 ::set_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN, pValData->GetListType() == ValidListType::INVISIBLE );
1036 ::set_flag( mnFlags, EXC_DV_SHOWPROMPT, bShowPrompt );
1037 ::set_flag( mnFlags, EXC_DV_SHOWERROR, bShowError );
1038
1039 // formulas
1040 XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
1041 ::std::auto_ptr< ScTokenArray > xScTokArr;
1042
1043 // first formula
1044 xScTokArr.reset( pValData->CreateTokenArry( 0 ) );
1045 if( xScTokArr.get() )
1046 {
1047 if( pValData->GetDataMode() == SC_VALID_LIST )
1048 {
1049 String aString;
1050 if( XclTokenArrayHelper::GetStringList( aString, *xScTokArr, '\n' ) )
1051 {
1052 OUStringBuffer sFormulaBuf;
1053 sFormulaBuf.append( (sal_Unicode) '"' );
1054 /* Formula is a list of string tokens -> build the Excel string.
1055 Data validity is BIFF8 only (important for the XclExpString object).
1056 Excel uses the NUL character as string list separator. */
1057 mxString1.reset( new XclExpString( EXC_STR_8BITLENGTH ) );
1058 xub_StrLen nTokenCnt = aString.GetTokenCount( '\n' );
1059 xub_StrLen nStringIx = 0;
1060 for( xub_StrLen nToken = 0; nToken < nTokenCnt; ++nToken )
1061 {
1062 String aToken( aString.GetToken( 0, '\n', nStringIx ) );
1063 if( nToken > 0 )
1064 {
1065 mxString1->Append( '\0' );
1066 sFormulaBuf.append( (sal_Unicode) ',' );
1067 }
1068 mxString1->Append( aToken );
1069 sFormulaBuf.append( XclXmlUtils::ToOUString( aToken ) );
1070 }
1071 ::set_flag( mnFlags, EXC_DV_STRINGLIST );
1072
1073 sFormulaBuf.append( (sal_Unicode) '"' );
1074 msFormula1 = sFormulaBuf.makeStringAndClear();
1075 }
1076 else
1077 {
1078 /* All other formulas in validation are stored like conditional
1079 formatting formulas (with tRefN/tAreaN tokens as value or
1080 array class). But NOT the cell references and defined names
1081 in list validation - they are stored as reference class
1082 tokens... Example:
1083 1) Cell must be equal to A1 -> formula is =A1 -> writes tRefNV token
1084 2) List is taken from A1 -> formula is =A1 -> writes tRefNR token
1085 Formula compiler supports this by offering two different functions
1086 CreateDataValFormula() and CreateListValFormula(). */
1087 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_LISTVAL, *xScTokArr );
1088 msFormula1 = XclXmlUtils::ToOUString( GetDoc(), pValData->GetSrcPos(), xScTokArr.get() );
1089 }
1090 }
1091 else
1092 {
1093 // no list validation -> convert the formula
1094 mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
1095 msFormula1 = XclXmlUtils::ToOUString( GetDoc(), pValData->GetSrcPos(), xScTokArr.get() );
1096 }
1097 }
1098
1099 // second formula
1100 xScTokArr.reset( pValData->CreateTokenArry( 1 ) );
1101 if( xScTokArr.get() )
1102 {
1103 mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
1104 msFormula2 = XclXmlUtils::ToOUString( GetDoc(), pValData->GetSrcPos(), xScTokArr.get() );
1105 }
1106 }
1107 else
1108 {
1109 DBG_ERRORFILE( "XclExpDV::XclExpDV - missing core data" );
1110 mnScHandle = ULONG_MAX;
1111 }
1112 }
1113
~XclExpDV()1114 XclExpDV::~XclExpDV()
1115 {
1116 }
1117
InsertCellRange(const ScRange & rRange)1118 void XclExpDV::InsertCellRange( const ScRange& rRange )
1119 {
1120 maScRanges.Join( rRange );
1121 }
1122
Finalize()1123 bool XclExpDV::Finalize()
1124 {
1125 GetAddressConverter().ConvertRangeList( maXclRanges, maScRanges, true );
1126 return (mnScHandle != ULONG_MAX) && !maXclRanges.empty();
1127 }
1128
WriteBody(XclExpStream & rStrm)1129 void XclExpDV::WriteBody( XclExpStream& rStrm )
1130 {
1131 // flags and strings
1132 rStrm << mnFlags << maPromptTitle << maErrorTitle << maPromptText << maErrorText;
1133 // condition formulas
1134 if( mxString1.get() )
1135 lclWriteDvFormula( rStrm, *mxString1 );
1136 else
1137 lclWriteDvFormula( rStrm, mxTokArr1.get() );
1138 lclWriteDvFormula( rStrm, mxTokArr2.get() );
1139 // cell ranges
1140 rStrm << maXclRanges;
1141 }
1142
SaveXml(XclExpXmlStream & rStrm)1143 void XclExpDV::SaveXml( XclExpXmlStream& rStrm )
1144 {
1145 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1146 rWorksheet->startElement( XML_dataValidation,
1147 XML_allowBlank, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_DV_IGNOREBLANK ) ),
1148 XML_error, XESTRING_TO_PSZ( maErrorText ),
1149 // OOXTODO: XML_errorStyle,
1150 XML_errorTitle, XESTRING_TO_PSZ( maErrorTitle ),
1151 // OOXTODO: XML_imeMode,
1152 XML_operator, lcl_GetOperatorType( mnFlags ),
1153 XML_prompt, XESTRING_TO_PSZ( maPromptText ),
1154 XML_promptTitle, XESTRING_TO_PSZ( maPromptTitle ),
1155 XML_showDropDown, XclXmlUtils::ToPsz( ! ::get_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN ) ),
1156 XML_showErrorMessage, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWERROR ) ),
1157 XML_showInputMessage, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWPROMPT ) ),
1158 XML_sqref, XclXmlUtils::ToOString( maScRanges ).getStr(),
1159 XML_type, lcl_GetValidationType( mnFlags ),
1160 FSEND );
1161 if( msFormula1.getLength() )
1162 {
1163 rWorksheet->startElement( XML_formula1, FSEND );
1164 rWorksheet->writeEscaped( msFormula1 );
1165 rWorksheet->endElement( XML_formula1 );
1166 }
1167 if( msFormula2.getLength() )
1168 {
1169 rWorksheet->startElement( XML_formula2, FSEND );
1170 rWorksheet->writeEscaped( msFormula2 );
1171 rWorksheet->endElement( XML_formula2 );
1172 }
1173 rWorksheet->endElement( XML_dataValidation );
1174 }
1175
1176 // ----------------------------------------------------------------------------
1177
XclExpDval(const XclExpRoot & rRoot)1178 XclExpDval::XclExpDval( const XclExpRoot& rRoot ) :
1179 XclExpRecord( EXC_ID_DVAL, 18 ),
1180 XclExpRoot( rRoot )
1181 {
1182 }
1183
~XclExpDval()1184 XclExpDval::~XclExpDval()
1185 {
1186 }
1187
InsertCellRange(const ScRange & rRange,sal_uLong nScHandle)1188 void XclExpDval::InsertCellRange( const ScRange& rRange, sal_uLong nScHandle )
1189 {
1190 if( GetBiff() == EXC_BIFF8 )
1191 {
1192 XclExpDV& rDVRec = SearchOrCreateDv( nScHandle );
1193 rDVRec.InsertCellRange( rRange );
1194 }
1195 }
1196
Save(XclExpStream & rStrm)1197 void XclExpDval::Save( XclExpStream& rStrm )
1198 {
1199 // check all records
1200 size_t nPos = maDVList.GetSize();
1201 while( nPos )
1202 {
1203 --nPos; // backwards to keep nPos valid
1204 XclExpDVRef xDVRec = maDVList.GetRecord( nPos );
1205 if( !xDVRec->Finalize() )
1206 maDVList.RemoveRecord( nPos );
1207 }
1208
1209 // write the DVAL and the DV's
1210 if( !maDVList.IsEmpty() )
1211 {
1212 XclExpRecord::Save( rStrm );
1213 maDVList.Save( rStrm );
1214 }
1215 }
1216
SaveXml(XclExpXmlStream & rStrm)1217 void XclExpDval::SaveXml( XclExpXmlStream& rStrm )
1218 {
1219 if( maDVList.IsEmpty() )
1220 return;
1221
1222 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1223 rWorksheet->startElement( XML_dataValidations,
1224 XML_count, OString::valueOf( (sal_Int32) maDVList.GetSize() ).getStr(),
1225 // OOXTODO: XML_disablePrompts,
1226 // OOXTODO: XML_xWindow,
1227 // OOXTODO: XML_yWindow,
1228 FSEND );
1229 maDVList.SaveXml( rStrm );
1230 rWorksheet->endElement( XML_dataValidations );
1231 }
1232
SearchOrCreateDv(sal_uLong nScHandle)1233 XclExpDV& XclExpDval::SearchOrCreateDv( sal_uLong nScHandle )
1234 {
1235 // test last found record
1236 if( mxLastFoundDV.get() && (mxLastFoundDV->GetScHandle() == nScHandle) )
1237 return *mxLastFoundDV;
1238
1239 // binary search
1240 size_t nCurrPos = 0;
1241 if( !maDVList.IsEmpty() )
1242 {
1243 size_t nFirstPos = 0;
1244 size_t nLastPos = maDVList.GetSize() - 1;
1245 bool bLoop = true;
1246 sal_uLong nCurrScHandle = ::std::numeric_limits< sal_uLong >::max();
1247 while( (nFirstPos <= nLastPos) && bLoop )
1248 {
1249 nCurrPos = (nFirstPos + nLastPos) / 2;
1250 mxLastFoundDV = maDVList.GetRecord( nCurrPos );
1251 nCurrScHandle = mxLastFoundDV->GetScHandle();
1252 if( nCurrScHandle == nScHandle )
1253 bLoop = false;
1254 else if( nCurrScHandle < nScHandle )
1255 nFirstPos = nCurrPos + 1;
1256 else if( nCurrPos )
1257 nLastPos = nCurrPos - 1;
1258 else // special case for nLastPos = -1
1259 bLoop = false;
1260 }
1261 if( nCurrScHandle == nScHandle )
1262 return *mxLastFoundDV;
1263 else if( nCurrScHandle < nScHandle )
1264 ++nCurrPos;
1265 }
1266
1267 // create new DV record
1268 mxLastFoundDV.reset( new XclExpDV( *this, nScHandle ) );
1269 maDVList.InsertRecord( mxLastFoundDV, nCurrPos );
1270 return *mxLastFoundDV;
1271 }
1272
WriteBody(XclExpStream & rStrm)1273 void XclExpDval::WriteBody( XclExpStream& rStrm )
1274 {
1275 rStrm.WriteZeroBytes( 10 );
1276 rStrm << EXC_DVAL_NOOBJ << static_cast< sal_uInt32 >( maDVList.GetSize() );
1277 }
1278
1279 // Web Queries ================================================================
1280
XclExpWebQuery(const String & rRangeName,const String & rUrl,const String & rSource,sal_Int32 nRefrSecs)1281 XclExpWebQuery::XclExpWebQuery(
1282 const String& rRangeName,
1283 const String& rUrl,
1284 const String& rSource,
1285 sal_Int32 nRefrSecs ) :
1286 maDestRange( rRangeName ),
1287 maUrl( rUrl ),
1288 // refresh delay time: seconds -> minutes
1289 mnRefresh( ulimit_cast< sal_Int16 >( (nRefrSecs + 59L) / 60L ) ),
1290 mbEntireDoc( false )
1291 {
1292 // comma separated list of HTML table names or indexes
1293 xub_StrLen nTokenCnt = rSource.GetTokenCount( ';' );
1294 String aNewTables, aAppendTable;
1295 xub_StrLen nStringIx = 0;
1296 bool bExitLoop = false;
1297 for( xub_StrLen nToken = 0; (nToken < nTokenCnt) && !bExitLoop; ++nToken )
1298 {
1299 String aToken( rSource.GetToken( 0, ';', nStringIx ) );
1300 mbEntireDoc = ScfTools::IsHTMLDocName( aToken );
1301 bExitLoop = mbEntireDoc || ScfTools::IsHTMLTablesName( aToken );
1302 if( !bExitLoop && ScfTools::GetHTMLNameFromName( aToken, aAppendTable ) )
1303 ScGlobal::AddToken( aNewTables, aAppendTable, ',' );
1304 }
1305
1306 if( !bExitLoop ) // neither HTML_all nor HTML_tables found
1307 {
1308 if( aNewTables.Len() )
1309 mxQryTables.reset( new XclExpString( aNewTables ) );
1310 else
1311 mbEntireDoc = true;
1312 }
1313 }
1314
~XclExpWebQuery()1315 XclExpWebQuery::~XclExpWebQuery()
1316 {
1317 }
1318
Save(XclExpStream & rStrm)1319 void XclExpWebQuery::Save( XclExpStream& rStrm )
1320 {
1321 DBG_ASSERT( !mbEntireDoc || !mxQryTables.get(), "XclExpWebQuery::Save - illegal mode" );
1322 sal_uInt16 nFlags;
1323
1324 // QSI record
1325 rStrm.StartRecord( EXC_ID_QSI, 10 + maDestRange.GetSize() );
1326 rStrm << EXC_QSI_DEFAULTFLAGS
1327 << sal_uInt16( 0x0010 )
1328 << sal_uInt16( 0x0012 )
1329 << sal_uInt32( 0x00000000 )
1330 << maDestRange;
1331 rStrm.EndRecord();
1332
1333 // PARAMQRY record
1334 nFlags = 0;
1335 ::insert_value( nFlags, EXC_PQRYTYPE_WEBQUERY, 0, 3 );
1336 ::set_flag( nFlags, EXC_PQRY_WEBQUERY );
1337 ::set_flag( nFlags, EXC_PQRY_TABLES, !mbEntireDoc );
1338 rStrm.StartRecord( EXC_ID_PQRY, 12 );
1339 rStrm << nFlags
1340 << sal_uInt16( 0x0000 )
1341 << sal_uInt16( 0x0001 );
1342 rStrm.WriteZeroBytes( 6 );
1343 rStrm.EndRecord();
1344
1345 // WQSTRING record
1346 rStrm.StartRecord( EXC_ID_WQSTRING, maUrl.GetSize() );
1347 rStrm << maUrl;
1348 rStrm.EndRecord();
1349
1350 // unknown record 0x0802
1351 rStrm.StartRecord( EXC_ID_0802, 16 + maDestRange.GetSize() );
1352 rStrm << EXC_ID_0802; // repeated record id ?!?
1353 rStrm.WriteZeroBytes( 6 );
1354 rStrm << sal_uInt16( 0x0003 )
1355 << sal_uInt32( 0x00000000 )
1356 << sal_uInt16( 0x0010 )
1357 << maDestRange;
1358 rStrm.EndRecord();
1359
1360 // WEBQRYSETTINGS record
1361 nFlags = mxQryTables.get() ? EXC_WQSETT_SPECTABLES : EXC_WQSETT_ALL;
1362 rStrm.StartRecord( EXC_ID_WQSETT, 28 );
1363 rStrm << EXC_ID_WQSETT // repeated record id ?!?
1364 << sal_uInt16( 0x0000 )
1365 << sal_uInt16( 0x0004 )
1366 << sal_uInt16( 0x0000 )
1367 << EXC_WQSETT_DEFAULTFLAGS
1368 << nFlags;
1369 rStrm.WriteZeroBytes( 10 );
1370 rStrm << mnRefresh // refresh delay in minutes
1371 << EXC_WQSETT_FORMATFULL
1372 << sal_uInt16( 0x0000 );
1373 rStrm.EndRecord();
1374
1375 // WEBQRYTABLES record
1376 if( mxQryTables.get() )
1377 {
1378 rStrm.StartRecord( EXC_ID_WQTABLES, 4 + mxQryTables->GetSize() );
1379 rStrm << EXC_ID_WQTABLES // repeated record id ?!?
1380 << sal_uInt16( 0x0000 )
1381 << *mxQryTables; // comma separated list of source tables
1382 rStrm.EndRecord();
1383 }
1384 }
1385
1386 // ----------------------------------------------------------------------------
1387
XclExpWebQueryBuffer(const XclExpRoot & rRoot)1388 XclExpWebQueryBuffer::XclExpWebQueryBuffer( const XclExpRoot& rRoot )
1389 {
1390 SCTAB nScTab = rRoot.GetCurrScTab();
1391 SfxObjectShell* pShell = rRoot.GetDocShell();
1392 if( !pShell ) return;
1393 ScfPropertySet aModelProp( pShell->GetModel() );
1394 if( !aModelProp.Is() ) return;
1395
1396 Reference< XAreaLinks > xAreaLinks;
1397 aModelProp.GetProperty( xAreaLinks, CREATE_OUSTRING( SC_UNO_AREALINKS ) );
1398 Reference< XIndexAccess > xLinksIA( xAreaLinks, UNO_QUERY );
1399 if( !xLinksIA.is() ) return;
1400
1401 for( sal_Int32 nIndex = 0, nCount = xLinksIA->getCount(); nIndex < nCount; ++nIndex )
1402 {
1403 Reference< XAreaLink > xAreaLink( xLinksIA->getByIndex( nIndex ), UNO_QUERY );
1404 if( xAreaLink.is() )
1405 {
1406 CellRangeAddress aDestRange( xAreaLink->getDestArea() );
1407 if( static_cast< SCTAB >( aDestRange.Sheet ) == nScTab )
1408 {
1409 ScfPropertySet aLinkProp( xAreaLink );
1410 OUString aFilter;
1411 if( aLinkProp.GetProperty( aFilter, CREATE_OUSTRING( SC_UNONAME_FILTER ) ) &&
1412 (aFilter == CREATE_OUSTRING( EXC_WEBQRY_FILTER )) )
1413 {
1414 // get properties
1415 OUString /*aFilterOpt,*/ aUrl;
1416 sal_Int32 nRefresh = 0;
1417
1418 // aLinkProp.GetProperty( aFilterOpt, CREATE_OUSTRING( SC_UNONAME_FILTOPT ) );
1419 aLinkProp.GetProperty( aUrl, CREATE_OUSTRING( SC_UNONAME_LINKURL ) );
1420 aLinkProp.GetProperty( nRefresh, CREATE_OUSTRING( SC_UNONAME_REFDELAY ) );
1421
1422 String aAbsDoc( ScGlobal::GetAbsDocName( aUrl, pShell ) );
1423 INetURLObject aUrlObj( aAbsDoc );
1424 String aWebQueryUrl( aUrlObj.getFSysPath( INetURLObject::FSYS_DOS ) );
1425 if( !aWebQueryUrl.Len() )
1426 aWebQueryUrl = aAbsDoc;
1427
1428 // find range or create a new range
1429 String aRangeName;
1430 ScRange aScDestRange;
1431 ScUnoConversion::FillScRange( aScDestRange, aDestRange );
1432 if( const ScRangeData* pRangeData = rRoot.GetNamedRanges().GetRangeAtBlock( aScDestRange ) )
1433 {
1434 aRangeName = pRangeData->GetName();
1435 }
1436 else
1437 {
1438 XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
1439 XclExpNameManager& rNameMgr = rRoot.GetNameManager();
1440
1441 // create a new unique defined name containing the range
1442 XclTokenArrayRef xTokArr = rFmlaComp.CreateFormula( EXC_FMLATYPE_WQUERY, aScDestRange );
1443 sal_uInt16 nNameIdx = rNameMgr.InsertUniqueName( aUrlObj.getBase(), xTokArr, nScTab );
1444 aRangeName = rNameMgr.GetOrigName( nNameIdx );
1445 }
1446
1447 // create and store the web query record
1448 if( aRangeName.Len() )
1449 AppendNewRecord( new XclExpWebQuery(
1450 aRangeName, aWebQueryUrl, xAreaLink->getSourceArea(), nRefresh ) );
1451 }
1452 }
1453 }
1454 }
1455 }
1456
1457 // ============================================================================
1458
1459