xref: /trunk/main/sc/source/filter/excel/xecontent.cxx (revision b77af630)
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