xref: /trunk/main/sc/source/filter/excel/xetable.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 "xetable.hxx"
27 
28 #include <map>
29 #include <com/sun/star/i18n/ScriptType.hpp>
30 #include "scitems.hxx"
31 #include <svl/intitem.hxx>
32 #include "document.hxx"
33 #include "dociter.hxx"
34 #include "olinetab.hxx"
35 #include "cell.hxx"
36 #include "patattr.hxx"
37 #include "attrib.hxx"
38 #include "xehelper.hxx"
39 #include "xecontent.hxx"
40 #include "xeescher.hxx"
41 
42 using namespace ::oox;
43 
44 using ::rtl::OString;
45 using ::rtl::OUString;
46 using ::rtl::OUStringBuffer;
47 
48 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
49 
50 // ============================================================================
51 // Helper records for cell records
52 // ============================================================================
53 
XclExpStringRec(const XclExpRoot & rRoot,const String & rResult)54 XclExpStringRec::XclExpStringRec( const XclExpRoot& rRoot, const String& rResult ) :
55     XclExpRecord( EXC_ID3_STRING ),
56     mxResult( XclExpStringHelper::CreateString( rRoot, rResult ) )
57 {
58     DBG_ASSERT( (rRoot.GetBiff() <= EXC_BIFF5) || (mxResult->Len() > 0),
59         "XclExpStringRec::XclExpStringRec - empty result not allowed in BIFF8+" );
60     SetRecSize( mxResult->GetSize() );
61 }
62 
WriteBody(XclExpStream & rStrm)63 void XclExpStringRec::WriteBody( XclExpStream& rStrm )
64 {
65     rStrm << *mxResult;
66 }
67 
68 // Additional records for special formula ranges ==============================
69 
XclExpRangeFmlaBase(sal_uInt16 nRecId,sal_uInt32 nRecSize,const ScAddress & rScPos)70 XclExpRangeFmlaBase::XclExpRangeFmlaBase(
71         sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScAddress& rScPos ) :
72     XclExpRecord( nRecId, nRecSize ),
73     maXclRange( ScAddress::UNINITIALIZED ),
74     maBaseXclPos( ScAddress::UNINITIALIZED )
75 {
76     maBaseXclPos.Set( static_cast< sal_uInt16 >( rScPos.Col() ), static_cast< sal_uInt16 >( rScPos.Row() ) );
77     maXclRange.maFirst = maXclRange.maLast = maBaseXclPos;
78 }
79 
XclExpRangeFmlaBase(sal_uInt16 nRecId,sal_uInt32 nRecSize,const ScRange & rScRange)80 XclExpRangeFmlaBase::XclExpRangeFmlaBase(
81         sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScRange& rScRange ) :
82     XclExpRecord( nRecId, nRecSize ),
83     maXclRange( ScAddress::UNINITIALIZED ),
84     maBaseXclPos( ScAddress::UNINITIALIZED )
85 {
86     maXclRange.Set(
87         static_cast< sal_uInt16 >( rScRange.aStart.Col() ),
88         static_cast< sal_uInt16 >( rScRange.aStart.Row() ),
89         static_cast< sal_uInt16 >( rScRange.aEnd.Col() ),
90         static_cast< sal_uInt16 >( rScRange.aEnd.Row() ) );
91     maBaseXclPos = maXclRange.maFirst;
92 }
93 
IsBasePos(sal_uInt16 nXclCol,sal_uInt16 nXclRow) const94 bool XclExpRangeFmlaBase::IsBasePos( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const
95 {
96     return (maBaseXclPos.mnCol == nXclCol) && (maBaseXclPos.mnRow == nXclRow);
97 }
98 
Extend(const ScAddress & rScPos)99 void XclExpRangeFmlaBase::Extend( const ScAddress& rScPos )
100 {
101     sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
102     sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() );
103     maXclRange.maFirst.mnCol = ::std::min( maXclRange.maFirst.mnCol, nXclCol );
104     maXclRange.maFirst.mnRow = ::std::min( maXclRange.maFirst.mnRow, nXclRow );
105     maXclRange.maLast.mnCol  = ::std::max( maXclRange.maLast.mnCol,  nXclCol );
106     maXclRange.maLast.mnRow  = ::std::max( maXclRange.maLast.mnRow,  nXclRow );
107 }
108 
WriteRangeAddress(XclExpStream & rStrm) const109 void XclExpRangeFmlaBase::WriteRangeAddress( XclExpStream& rStrm ) const
110 {
111     maXclRange.Write( rStrm, false );
112 }
113 
114 // Array formulas =============================================================
115 
XclExpArray(XclTokenArrayRef xTokArr,const ScRange & rScRange)116 XclExpArray::XclExpArray( XclTokenArrayRef xTokArr, const ScRange& rScRange ) :
117     XclExpRangeFmlaBase( EXC_ID3_ARRAY, 14 + xTokArr->GetSize(), rScRange ),
118     mxTokArr( xTokArr )
119 {
120 }
121 
CreateCellTokenArray(const XclExpRoot & rRoot) const122 XclTokenArrayRef XclExpArray::CreateCellTokenArray( const XclExpRoot& rRoot ) const
123 {
124     return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
125 }
126 
IsVolatile() const127 bool XclExpArray::IsVolatile() const
128 {
129     return mxTokArr->IsVolatile();
130 }
131 
WriteBody(XclExpStream & rStrm)132 void XclExpArray::WriteBody( XclExpStream& rStrm )
133 {
134     WriteRangeAddress( rStrm );
135     sal_uInt16 nFlags = EXC_ARRAY_DEFAULTFLAGS;
136     ::set_flag( nFlags, EXC_ARRAY_RECALC_ALWAYS, IsVolatile() );
137     rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
138 }
139 
140 // ----------------------------------------------------------------------------
141 
XclExpArrayBuffer(const XclExpRoot & rRoot)142 XclExpArrayBuffer::XclExpArrayBuffer( const XclExpRoot& rRoot ) :
143     XclExpRoot( rRoot )
144 {
145 }
146 
CreateArray(const ScTokenArray & rScTokArr,const ScRange & rScRange)147 XclExpArrayRef XclExpArrayBuffer::CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange )
148 {
149     const ScAddress& rScPos = rScRange.aStart;
150     XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_MATRIX, rScTokArr, &rScPos );
151 
152     DBG_ASSERT( maRecMap.find( rScPos ) == maRecMap.end(), "XclExpArrayBuffer::CreateArray - array exists already" );
153     XclExpArrayRef& rxRec = maRecMap[ rScPos ];
154     rxRec.reset( new XclExpArray( xTokArr, rScRange ) );
155     return rxRec;
156 }
157 
FindArray(const ScTokenArray & rScTokArr) const158 XclExpArrayRef XclExpArrayBuffer::FindArray( const ScTokenArray& rScTokArr ) const
159 {
160     XclExpArrayRef xRec;
161     // try to extract a matrix reference token
162     if( rScTokArr.GetLen() == 1 )
163     {
164         const formula::FormulaToken* pToken = rScTokArr.GetArray()[ 0 ];
165         if( pToken && (pToken->GetOpCode() == ocMatRef) )
166         {
167             const ScSingleRefData& rRef = static_cast<const ScToken*>(pToken)->GetSingleRef();
168             ScAddress aBasePos( rRef.nCol, rRef.nRow, GetCurrScTab() );
169             XclExpArrayMap::const_iterator aIt = maRecMap.find( aBasePos );
170             if( aIt != maRecMap.end() )
171                 xRec = aIt->second;
172         }
173     }
174     return xRec;
175 }
176 
177 // Shared formulas ============================================================
178 
XclExpShrfmla(XclTokenArrayRef xTokArr,const ScAddress & rScPos)179 XclExpShrfmla::XclExpShrfmla( XclTokenArrayRef xTokArr, const ScAddress& rScPos ) :
180     XclExpRangeFmlaBase( EXC_ID_SHRFMLA, 10 + xTokArr->GetSize(), rScPos ),
181     mxTokArr( xTokArr ),
182     mnUsedCount( 1 )
183 {
184 }
185 
ExtendRange(const ScAddress & rScPos)186 void XclExpShrfmla::ExtendRange( const ScAddress& rScPos )
187 {
188     Extend( rScPos );
189     ++mnUsedCount;
190 }
191 
CreateCellTokenArray(const XclExpRoot & rRoot) const192 XclTokenArrayRef XclExpShrfmla::CreateCellTokenArray( const XclExpRoot& rRoot ) const
193 {
194     return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
195 }
196 
IsVolatile() const197 bool XclExpShrfmla::IsVolatile() const
198 {
199     return mxTokArr->IsVolatile();
200 }
201 
WriteBody(XclExpStream & rStrm)202 void XclExpShrfmla::WriteBody( XclExpStream& rStrm )
203 {
204     WriteRangeAddress( rStrm );
205     rStrm << sal_uInt8( 0 ) << mnUsedCount << *mxTokArr;
206 }
207 
208 // ----------------------------------------------------------------------------
209 
XclExpShrfmlaBuffer(const XclExpRoot & rRoot)210 XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) :
211     XclExpRoot( rRoot )
212 {
213 }
214 
CreateOrExtendShrfmla(const ScTokenArray & rScTokArr,const ScAddress & rScPos)215 XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
216         const ScTokenArray& rScTokArr, const ScAddress& rScPos )
217 {
218     XclExpShrfmlaRef xRec;
219     if( const ScTokenArray* pShrdScTokArr = XclTokenArrayHelper::GetSharedFormula( GetRoot(), rScTokArr ) )
220     {
221         XclExpShrfmlaMap::iterator aIt = maRecMap.find( pShrdScTokArr );
222         if( aIt == maRecMap.end() )
223         {
224             // create a new record
225             XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_SHARED, *pShrdScTokArr, &rScPos );
226             xRec.reset( new XclExpShrfmla( xTokArr, rScPos ) );
227             maRecMap[ pShrdScTokArr ] = xRec;
228         }
229         else
230         {
231             // extend existing record
232             DBG_ASSERT( aIt->second, "XclExpShrfmlaBuffer::CreateOrExtendShrfmla - missing record" );
233             xRec = aIt->second;
234             xRec->ExtendRange( rScPos );
235         }
236     }
237     return xRec;
238 }
239 
240 // Multiple operations ========================================================
241 
XclExpTableop(const ScAddress & rScPos,const XclMultipleOpRefs & rRefs,sal_uInt8 nScMode)242 XclExpTableop::XclExpTableop( const ScAddress& rScPos,
243         const XclMultipleOpRefs& rRefs, sal_uInt8 nScMode ) :
244     XclExpRangeFmlaBase( EXC_ID3_TABLEOP, 16, rScPos ),
245     mnLastAppXclCol( static_cast< sal_uInt16 >( rScPos.Col() ) ),
246     mnColInpXclCol( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Col() ) ),
247     mnColInpXclRow( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Row() ) ),
248     mnRowInpXclCol( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Col() ) ),
249     mnRowInpXclRow( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Row() ) ),
250     mnScMode( nScMode ),
251     mbValid( false )
252 {
253 }
254 
TryExtend(const ScAddress & rScPos,const XclMultipleOpRefs & rRefs)255 bool XclExpTableop::TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
256 {
257     sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
258     sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() );
259 
260     bool bOk = IsAppendable( nXclCol, nXclRow );
261     if( bOk )
262     {
263         SCCOL nFirstScCol  = static_cast< SCCOL >( maXclRange.maFirst.mnCol );
264         SCROW nFirstScRow  = static_cast< SCROW >( maXclRange.maFirst.mnRow );
265         SCCOL nColInpScCol = static_cast< SCCOL >( mnColInpXclCol );
266         SCROW nColInpScRow = static_cast< SCROW >( mnColInpXclRow );
267         SCCOL nRowInpScCol = static_cast< SCCOL >( mnRowInpXclCol );
268         SCROW nRowInpScRow = static_cast< SCROW >( mnRowInpXclRow );
269 
270         bOk =   ((mnScMode == 2) == rRefs.mbDblRefMode) &&
271                 (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
272                 (nColInpScCol == rRefs.maColFirstScPos.Col()) &&
273                 (nColInpScRow == rRefs.maColFirstScPos.Row()) &&
274                 (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
275                 (rScPos.Tab() == rRefs.maColRelScPos.Tab());
276 
277         if( bOk ) switch( mnScMode )
278         {
279             case 0:
280                 bOk =   (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
281                         (nFirstScRow  == rRefs.maFmlaScPos.Row() + 1) &&
282                         (nFirstScCol  == rRefs.maColRelScPos.Col() + 1) &&
283                         (rScPos.Row() == rRefs.maColRelScPos.Row());
284             break;
285             case 1:
286                 bOk =   (nFirstScCol  == rRefs.maFmlaScPos.Col() + 1) &&
287                         (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
288                         (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
289                         (nFirstScRow  == rRefs.maColRelScPos.Row() + 1);
290             break;
291             case 2:
292                 bOk =   (nFirstScCol  == rRefs.maFmlaScPos.Col() + 1) &&
293                         (nFirstScRow  == rRefs.maFmlaScPos.Row() + 1) &&
294                         (nFirstScCol  == rRefs.maColRelScPos.Col() + 1) &&
295                         (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
296                         (nRowInpScCol == rRefs.maRowFirstScPos.Col()) &&
297                         (nRowInpScRow == rRefs.maRowFirstScPos.Row()) &&
298                         (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
299                         (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
300                         (nFirstScRow  == rRefs.maRowRelScPos.Row() + 1) &&
301                         (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
302             break;
303             default:
304                 bOk = false;
305         }
306 
307         if( bOk )
308         {
309             // extend the cell range
310             DBG_ASSERT( IsAppendable( nXclCol, nXclRow ), "XclExpTableop::TryExtend - wrong cell address" );
311             Extend( rScPos );
312             mnLastAppXclCol = nXclCol;
313         }
314     }
315 
316     return bOk;
317 }
318 
Finalize()319 void XclExpTableop::Finalize()
320 {
321     // is the range complete? (last appended cell is in last column)
322     mbValid = maXclRange.maLast.mnCol == mnLastAppXclCol;
323     // if last row is incomplete, try to shorten the used range
324     if( !mbValid && (maXclRange.maFirst.mnRow < maXclRange.maLast.mnRow) )
325     {
326         --maXclRange.maLast.mnRow;
327         mbValid = true;
328     }
329 
330     // check if referred cells are outside of own range
331     if( mbValid ) switch( mnScMode )
332     {
333         case 0:
334             mbValid =   (mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
335                         (mnColInpXclRow     < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
336         break;
337         case 1:
338             mbValid =   (mnColInpXclCol     < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
339                         (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
340         break;
341         case 2:
342             mbValid =   ((mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
343                          (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow)) &&
344                         ((mnRowInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnRowInpXclCol > maXclRange.maLast.mnCol) ||
345                          (mnRowInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnRowInpXclRow > maXclRange.maLast.mnRow));
346         break;
347     }
348 }
349 
CreateCellTokenArray(const XclExpRoot & rRoot) const350 XclTokenArrayRef XclExpTableop::CreateCellTokenArray( const XclExpRoot& rRoot ) const
351 {
352     XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
353     return mbValid ?
354         rFmlaComp.CreateSpecialRefFormula( EXC_TOKID_TBL, maBaseXclPos ) :
355         rFmlaComp.CreateErrorFormula( EXC_ERR_NA );
356 }
357 
IsVolatile() const358 bool XclExpTableop::IsVolatile() const
359 {
360     return true;
361 }
362 
Save(XclExpStream & rStrm)363 void XclExpTableop::Save( XclExpStream& rStrm )
364 {
365     if( mbValid )
366         XclExpRangeFmlaBase::Save( rStrm );
367 }
368 
IsAppendable(sal_uInt16 nXclCol,sal_uInt16 nXclRow) const369 bool XclExpTableop::IsAppendable( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const
370 {
371     return  ((nXclCol == mnLastAppXclCol + 1) && (nXclRow == maXclRange.maFirst.mnRow)) ||
372             ((nXclCol == mnLastAppXclCol + 1) && (nXclCol <= maXclRange.maLast.mnCol) && (nXclRow == maXclRange.maLast.mnRow)) ||
373             ((mnLastAppXclCol == maXclRange.maLast.mnCol) && (nXclCol == maXclRange.maFirst.mnCol) && (nXclRow == maXclRange.maLast.mnRow + 1));
374 }
375 
WriteBody(XclExpStream & rStrm)376 void XclExpTableop::WriteBody( XclExpStream& rStrm )
377 {
378     sal_uInt16 nFlags = EXC_TABLEOP_DEFAULTFLAGS;
379     ::set_flag( nFlags, EXC_TABLEOP_RECALC_ALWAYS, IsVolatile() );
380     switch( mnScMode )
381     {
382         case 1: ::set_flag( nFlags, EXC_TABLEOP_ROW );  break;
383         case 2: ::set_flag( nFlags, EXC_TABLEOP_BOTH ); break;
384     }
385 
386     WriteRangeAddress( rStrm );
387     rStrm << nFlags;
388     if( mnScMode == 2 )
389         rStrm << mnRowInpXclRow << mnRowInpXclCol << mnColInpXclRow << mnColInpXclCol;
390     else
391         rStrm << mnColInpXclRow << mnColInpXclCol << sal_uInt32( 0 );
392 }
393 
394 // ----------------------------------------------------------------------------
395 
XclExpTableopBuffer(const XclExpRoot & rRoot)396 XclExpTableopBuffer::XclExpTableopBuffer( const XclExpRoot& rRoot ) :
397     XclExpRoot( rRoot )
398 {
399 }
400 
CreateOrExtendTableop(const ScTokenArray & rScTokArr,const ScAddress & rScPos)401 XclExpTableopRef XclExpTableopBuffer::CreateOrExtendTableop(
402         const ScTokenArray& rScTokArr, const ScAddress& rScPos )
403 {
404     XclExpTableopRef xRec;
405 
406     // try to extract cell references of a multiple operations formula
407     XclMultipleOpRefs aRefs;
408     if( XclTokenArrayHelper::GetMultipleOpRefs( aRefs, rScTokArr ) )
409     {
410         // try to find an existing TABLEOP record for this cell position
411         for( size_t nPos = 0, nSize = maTableopList.GetSize(); !xRec && (nPos < nSize); ++nPos )
412         {
413             XclExpTableopRef xTempRec = maTableopList.GetRecord( nPos );
414             if( xTempRec->TryExtend( rScPos, aRefs ) )
415                 xRec = xTempRec;
416         }
417 
418         // no record found, or found record not extensible
419         if( !xRec )
420             xRec = TryCreate( rScPos, aRefs );
421     }
422 
423     return xRec;
424 }
425 
Finalize()426 void XclExpTableopBuffer::Finalize()
427 {
428     for( size_t nPos = 0, nSize = maTableopList.GetSize(); nPos < nSize; ++nPos )
429         maTableopList.GetRecord( nPos )->Finalize();
430 }
431 
TryCreate(const ScAddress & rScPos,const XclMultipleOpRefs & rRefs)432 XclExpTableopRef XclExpTableopBuffer::TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
433 {
434     sal_uInt8 nScMode = 0;
435     bool bOk =  (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
436                 (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
437                 (rScPos.Tab() == rRefs.maColRelScPos.Tab());
438 
439     if( bOk )
440     {
441         if( rRefs.mbDblRefMode )
442         {
443             nScMode = 2;
444             bOk =   (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
445                     (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
446                     (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
447                     (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
448                     (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
449                     (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
450                     (rScPos.Row() == rRefs.maRowRelScPos.Row() + 1) &&
451                     (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
452         }
453         else if( (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
454                  (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
455                  (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
456                  (rScPos.Row() == rRefs.maColRelScPos.Row()) )
457         {
458             nScMode = 0;
459         }
460         else if( (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
461                  (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
462                  (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
463                  (rScPos.Row() == rRefs.maColRelScPos.Row() + 1) )
464         {
465             nScMode = 1;
466         }
467         else
468         {
469             bOk = false;
470         }
471     }
472 
473     XclExpTableopRef xRec;
474     if( bOk )
475     {
476         xRec.reset( new XclExpTableop( rScPos, rRefs, nScMode ) );
477         maTableopList.AppendRecord( xRec );
478     }
479 
480     return xRec;
481 }
482 
483 // ============================================================================
484 // Cell records
485 // ============================================================================
486 
XclExpCellBase(sal_uInt16 nRecId,sal_Size nContSize,const XclAddress & rXclPos)487 XclExpCellBase::XclExpCellBase(
488         sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos ) :
489     XclExpRecord( nRecId, nContSize + 4 ),
490     maXclPos( rXclPos )
491 {
492 }
493 
IsMultiLineText() const494 bool XclExpCellBase::IsMultiLineText() const
495 {
496     return false;
497 }
498 
TryMerge(const XclExpCellBase &)499 bool XclExpCellBase::TryMerge( const XclExpCellBase& /*rCell*/ )
500 {
501     return false;
502 }
503 
GetBlankXFIndexes(ScfUInt16Vec &) const504 void XclExpCellBase::GetBlankXFIndexes( ScfUInt16Vec& /*rXFIndexes*/ ) const
505 {
506     // default: do nothing
507 }
508 
RemoveUnusedBlankCells(const ScfUInt16Vec &)509 void XclExpCellBase::RemoveUnusedBlankCells( const ScfUInt16Vec& /*rXFIndexes*/ )
510 {
511     // default: do nothing
512 }
513 
514 // Single cell records ========================================================
515 
XclExpSingleCellBase(sal_uInt16 nRecId,sal_Size nContSize,const XclAddress & rXclPos,sal_uInt32 nXFId)516 XclExpSingleCellBase::XclExpSingleCellBase(
517         sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos, sal_uInt32 nXFId ) :
518     XclExpCellBase( nRecId, 2, rXclPos ),
519     maXFId( nXFId ),
520     mnContSize( nContSize )
521 {
522 }
523 
XclExpSingleCellBase(const XclExpRoot & rRoot,sal_uInt16 nRecId,sal_Size nContSize,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_Int16 nScript,sal_uInt32 nForcedXFId)524 XclExpSingleCellBase::XclExpSingleCellBase( const XclExpRoot& rRoot,
525         sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos,
526         const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForcedXFId ) :
527     XclExpCellBase( nRecId, 2, rXclPos ),
528     maXFId( nForcedXFId ),
529     mnContSize( nContSize )
530 {
531     if( GetXFId() == EXC_XFID_NOTFOUND )
532         SetXFId( rRoot.GetXFBuffer().Insert( pPattern, nScript ) );
533 }
534 
GetLastXclCol() const535 sal_uInt16 XclExpSingleCellBase::GetLastXclCol() const
536 {
537     return GetXclCol();
538 }
539 
GetFirstXFId() const540 sal_uInt32 XclExpSingleCellBase::GetFirstXFId() const
541 {
542     return GetXFId();
543 }
544 
IsEmpty() const545 bool XclExpSingleCellBase::IsEmpty() const
546 {
547     return false;
548 }
549 
ConvertXFIndexes(const XclExpRoot & rRoot)550 void XclExpSingleCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
551 {
552     maXFId.ConvertXFIndex( rRoot );
553 }
554 
Save(XclExpStream & rStrm)555 void XclExpSingleCellBase::Save( XclExpStream& rStrm )
556 {
557     DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
558     AddRecSize( mnContSize );
559     XclExpCellBase::Save( rStrm );
560 }
561 
WriteBody(XclExpStream & rStrm)562 void XclExpSingleCellBase::WriteBody( XclExpStream& rStrm )
563 {
564     rStrm << GetXclRow() << GetXclCol() << maXFId.mnXFIndex;
565     WriteContents( rStrm );
566 }
567 
568 // ----------------------------------------------------------------------------
569 
570 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpNumberCell, 256, 256 )
571 
XclExpNumberCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,double fValue)572 XclExpNumberCell::XclExpNumberCell(
573         const XclExpRoot& rRoot, const XclAddress& rXclPos,
574         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, double fValue ) :
575     // #i41210# always use latin script for number cells - may look wrong for special number formats...
576     XclExpSingleCellBase( rRoot, EXC_ID3_NUMBER, 8, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
577     mfValue( fValue )
578 {
579 }
580 
lcl_GetStyleId(XclExpXmlStream & rStrm,sal_uInt32 nXFIndex)581 static OString lcl_GetStyleId( XclExpXmlStream& rStrm, sal_uInt32 nXFIndex )
582 {
583     return OString::valueOf( rStrm.GetRoot().GetXFBuffer()
584             .GetXmlCellIndex( nXFIndex ) );
585 }
586 
lcl_GetStyleId(XclExpXmlStream & rStrm,const XclExpCellBase & rCell)587 static OString lcl_GetStyleId( XclExpXmlStream& rStrm, const XclExpCellBase& rCell )
588 {
589     sal_uInt32 nXFId    = rCell.GetFirstXFId();
590     sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( nXFId );
591     return lcl_GetStyleId( rStrm, nXFIndex );
592 }
593 
SaveXml(XclExpXmlStream & rStrm)594 void XclExpNumberCell::SaveXml( XclExpXmlStream& rStrm )
595 {
596     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
597     rWorksheet->startElement( XML_c,
598             XML_r,      XclXmlUtils::ToOString( GetXclPos() ).getStr(),
599             XML_s,      lcl_GetStyleId( rStrm, *this ).getStr(),
600             XML_t,      "n",
601             // OOXTODO: XML_cm, XML_vm, XML_ph
602             FSEND );
603     rWorksheet->startElement( XML_v, FSEND );
604     rWorksheet->write( mfValue );
605     rWorksheet->endElement( XML_v );
606     rWorksheet->endElement( XML_c );
607 }
608 
WriteContents(XclExpStream & rStrm)609 void XclExpNumberCell::WriteContents( XclExpStream& rStrm )
610 {
611     rStrm << mfValue;
612 }
613 
614 // ----------------------------------------------------------------------------
615 
616 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpBooleanCell, 256, 256 )
617 
XclExpBooleanCell(const XclExpRoot rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,bool bValue)618 XclExpBooleanCell::XclExpBooleanCell(
619         const XclExpRoot rRoot, const XclAddress& rXclPos,
620         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, bool bValue ) :
621     // #i41210# always use latin script for boolean cells
622     XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
623     mbValue( bValue )
624 {
625 }
626 
SaveXml(XclExpXmlStream & rStrm)627 void XclExpBooleanCell::SaveXml( XclExpXmlStream& rStrm )
628 {
629     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
630     rWorksheet->startElement( XML_c,
631             XML_r,      XclXmlUtils::ToOString( GetXclPos() ).getStr(),
632             XML_s,      lcl_GetStyleId( rStrm, *this ).getStr(),
633             XML_t,      "b",
634             // OOXTODO: XML_cm, XML_vm, XML_ph
635             FSEND );
636     rWorksheet->startElement( XML_v, FSEND );
637     rWorksheet->write( mbValue ? "1" : "0" );
638     rWorksheet->endElement( XML_v );
639     rWorksheet->endElement( XML_c );
640 }
641 
WriteContents(XclExpStream & rStrm)642 void XclExpBooleanCell::WriteContents( XclExpStream& rStrm )
643 {
644     rStrm << sal_uInt16( mbValue ? 1 : 0 ) << EXC_BOOLERR_BOOL;
645 }
646 
647 // ----------------------------------------------------------------------------
648 
649 //UNUSED2009-05 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpErrorCell, 256, 256 )
650 //UNUSED2009-05
651 //UNUSED2009-05 XclExpErrorCell::XclExpErrorCell(
652 //UNUSED2009-05         const XclExpRoot rRoot, const XclAddress& rXclPos,
653 //UNUSED2009-05         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_uInt8 nErrCode ) :
654 //UNUSED2009-05     // #i41210# always use latin script for error cells
655 //UNUSED2009-05     XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
656 //UNUSED2009-05     mnErrCode( nErrCode )
657 //UNUSED2009-05 {
658 //UNUSED2009-05 }
659 //UNUSED2009-05
660 //UNUSED2009-05 void XclExpErrorCell::SaveXml( XclExpXmlStream& rStrm )
661 //UNUSED2009-05 {
662 //UNUSED2009-05     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
663 //UNUSED2009-05     rWorksheet->startElement( XML_c,
664 //UNUSED2009-05             XML_r,      XclXmlUtils::ToOString( GetXclPos() ).getStr(),
665 //UNUSED2009-05             XML_s,      lcl_GetStyleId( rStrm, *this ).getStr(),
666 //UNUSED2009-05             XML_t,      "e",
667 //UNUSED2009-05             // OOXTODO: XML_cm, XML_vm, XML_ph
668 //UNUSED2009-05             FSEND );
669 //UNUSED2009-05     rWorksheet->startElement( XML_v, FSEND );
670 //UNUSED2009-05     rWorksheet->write( (sal_Int32) mnErrCode );
671 //UNUSED2009-05     rWorksheet->endElement( XML_v );
672 //UNUSED2009-05     rWorksheet->endElement( XML_c );
673 //UNUSED2009-05 }
674 //UNUSED2009-05
675 //UNUSED2009-05 void XclExpErrorCell::WriteContents( XclExpStream& rStrm )
676 //UNUSED2009-05 {
677 //UNUSED2009-05     rStrm << mnErrCode << EXC_BOOLERR_ERROR;
678 //UNUSED2009-05 }
679 
680 // ----------------------------------------------------------------------------
681 
682 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpLabelCell, 256, 256 )
683 
XclExpLabelCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,const ScStringCell & rCell)684 XclExpLabelCell::XclExpLabelCell(
685         const XclExpRoot& rRoot, const XclAddress& rXclPos,
686         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, const ScStringCell& rCell ) :
687     XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
688 {
689     sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
690     XclExpStringRef xText = XclExpStringHelper::CreateCellString( rRoot, rCell, pPattern, EXC_STR_DEFAULT, nMaxLen );
691     Init( rRoot, pPattern, xText );
692 }
693 
XclExpLabelCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,const ScEditCell & rCell,XclExpHyperlinkHelper & rLinkHelper)694 XclExpLabelCell::XclExpLabelCell(
695         const XclExpRoot& rRoot, const XclAddress& rXclPos,
696         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
697         const ScEditCell& rCell, XclExpHyperlinkHelper& rLinkHelper ) :
698     XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
699 {
700     sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
701     XclExpStringRef xText = XclExpStringHelper::CreateCellString( rRoot, rCell, pPattern, rLinkHelper, EXC_STR_DEFAULT, nMaxLen );
702     Init( rRoot, pPattern, xText );
703 }
704 
IsMultiLineText() const705 bool XclExpLabelCell::IsMultiLineText() const
706 {
707     return mbLineBreak || mxText->IsWrapped();
708 }
709 
Init(const XclExpRoot & rRoot,const ScPatternAttr * pPattern,XclExpStringRef xText)710 void XclExpLabelCell::Init( const XclExpRoot& rRoot,
711         const ScPatternAttr* pPattern, XclExpStringRef xText )
712 {
713     DBG_ASSERT( xText.is() && xText->Len(), "XclExpLabelCell::XclExpLabelCell - empty string passed" );
714     mxText = xText;
715     mnSstIndex = 0;
716 
717     // create the cell format
718     sal_uInt16 nXclFont = mxText->RemoveLeadingFont();
719     if( GetXFId() == EXC_XFID_NOTFOUND )
720     {
721         DBG_ASSERT( nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found" );
722         bool bForceLineBreak = mxText->IsWrapped();
723         SetXFId( rRoot.GetXFBuffer().InsertWithFont( pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak ) );
724     }
725 
726     // get auto-wrap attribute from cell format
727     const XclExpXF* pXF = rRoot.GetXFBuffer().GetXFById( GetXFId() );
728     mbLineBreak = pXF && pXF->GetAlignmentData().mbLineBreak;
729 
730     // initialize the record contents
731     switch( rRoot.GetBiff() )
732     {
733         case EXC_BIFF5:
734             // BIFF5-BIFF7: create a LABEL or RSTRING record
735             DBG_ASSERT( mxText->Len() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::XclExpLabelCell - string too long" );
736             SetContSize( mxText->GetSize() );
737             // formatted string is exported in an RSTRING record
738             if( mxText->IsRich() )
739             {
740                 DBG_ASSERT( mxText->GetFormatsCount() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::WriteContents - too many formats" );
741                 mxText->LimitFormatCount( EXC_LABEL_MAXLEN );
742                 SetRecId( EXC_ID_RSTRING );
743                 SetContSize( GetContSize() + 1 + 2 * mxText->GetFormatsCount() );
744             }
745         break;
746         case EXC_BIFF8:
747             // BIFF8+: create a LABELSST record
748             mnSstIndex = rRoot.GetSst().Insert( xText );
749             SetRecId( EXC_ID_LABELSST );
750             SetContSize( 4 );
751         break;
752         default:    DBG_ERROR_BIFF();
753     }
754 }
755 
SaveXml(XclExpXmlStream & rStrm)756 void XclExpLabelCell::SaveXml( XclExpXmlStream& rStrm )
757 {
758     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
759     rWorksheet->startElement( XML_c,
760             XML_r,      XclXmlUtils::ToOString( GetXclPos() ).getStr(),
761             XML_s,      lcl_GetStyleId( rStrm, *this ).getStr(),
762             XML_t,      "s",
763             // OOXTODO: XML_cm, XML_vm, XML_ph
764             FSEND );
765     rWorksheet->startElement( XML_v, FSEND );
766     rWorksheet->write( (sal_Int32) mnSstIndex );
767     rWorksheet->endElement( XML_v );
768     rWorksheet->endElement( XML_c );
769 }
770 
WriteContents(XclExpStream & rStrm)771 void XclExpLabelCell::WriteContents( XclExpStream& rStrm )
772 {
773     switch( rStrm.GetRoot().GetBiff() )
774     {
775         case EXC_BIFF5:
776             rStrm << *mxText;
777             if( mxText->IsRich() )
778             {
779                 rStrm << static_cast< sal_uInt8 >( mxText->GetFormatsCount() );
780                 mxText->WriteFormats( rStrm );
781             }
782         break;
783         case EXC_BIFF8:
784             rStrm << mnSstIndex;
785         break;
786         default:    DBG_ERROR_BIFF();
787     }
788 }
789 
790 // ----------------------------------------------------------------------------
791 
792 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpFormulaCell, 256, 256 )
793 
XclExpFormulaCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,const ScFormulaCell & rScFmlaCell,XclExpArrayBuffer & rArrayBfr,XclExpShrfmlaBuffer & rShrfmlaBfr,XclExpTableopBuffer & rTableopBfr)794 XclExpFormulaCell::XclExpFormulaCell(
795         const XclExpRoot& rRoot, const XclAddress& rXclPos,
796         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
797         const ScFormulaCell& rScFmlaCell,
798         XclExpArrayBuffer& rArrayBfr,
799         XclExpShrfmlaBuffer& rShrfmlaBfr,
800         XclExpTableopBuffer& rTableopBfr ) :
801     XclExpSingleCellBase( EXC_ID2_FORMULA, 0, rXclPos, nForcedXFId ),
802     mrScFmlaCell( const_cast< ScFormulaCell& >( rScFmlaCell ) )
803 {
804     // *** Find result number format overwriting cell number format *** -------
805 
806     if( GetXFId() == EXC_XFID_NOTFOUND )
807     {
808         SvNumberFormatter& rFormatter = rRoot.GetFormatter();
809         XclExpNumFmtBuffer& rNumFmtBfr = rRoot.GetNumFmtBuffer();
810 
811         // current cell number format
812         sal_uLong nScNumFmt = pPattern ?
813             GETITEMVALUE( pPattern->GetItemSet(), SfxUInt32Item, ATTR_VALUE_FORMAT, sal_uLong ) :
814             rNumFmtBfr.GetStandardFormat();
815 
816         // alternative number format passed to XF buffer
817         sal_uLong nAltScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
818         /*  #73420# Xcl doesn't know Boolean number formats, we write
819             "TRUE";"FALSE" (language dependent). Don't do it for automatic
820             formula formats, because Excel gets them right. */
821         /*  #i8640# Don't set text format, if we have string results. */
822         short nFormatType = mrScFmlaCell.GetFormatType();
823         if( ((nScNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0) &&
824                 (nFormatType != NUMBERFORMAT_LOGICAL) &&
825                 (nFormatType != NUMBERFORMAT_TEXT) )
826             nAltScNumFmt = mrScFmlaCell.GetStandardFormat( rFormatter, nScNumFmt );
827         /*  #73420# If cell number format is Boolean and automatic formula
828             format is Boolean don't write that ugly special format. */
829         else if( (nFormatType == NUMBERFORMAT_LOGICAL) &&
830                 (rFormatter.GetType( nScNumFmt ) == NUMBERFORMAT_LOGICAL) )
831             nAltScNumFmt = rNumFmtBfr.GetStandardFormat();
832 
833         // #i41420# find script type according to result type (always latin for numeric results)
834         sal_Int16 nScript = ApiScriptType::LATIN;
835         bool bForceLineBreak = false;
836         if( nFormatType == NUMBERFORMAT_TEXT )
837         {
838             String aResult;
839             mrScFmlaCell.GetString( aResult );
840             bForceLineBreak = mrScFmlaCell.IsMultilineResult();
841             nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult );
842         }
843         SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) );
844     }
845 
846     // *** Convert the formula token array *** --------------------------------
847 
848     ScAddress aScPos( static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), rRoot.GetCurrScTab() );
849     const ScTokenArray& rScTokArr = *mrScFmlaCell.GetCode();
850 
851     // first try to create multiple operations
852     mxAddRec = rTableopBfr.CreateOrExtendTableop( rScTokArr, aScPos );
853 
854     // no multiple operation found - try to create matrix formula
855     if( !mxAddRec ) switch( static_cast< ScMatrixMode >( mrScFmlaCell.GetMatrixFlag() ) )
856     {
857         case MM_FORMULA:
858         {
859             // origin of the matrix - find the used matrix range
860             SCCOL nMatWidth;
861             SCROW nMatHeight;
862             mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
863             DBG_ASSERT( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
864             ScRange aMatScRange( aScPos );
865             ScAddress& rMatEnd = aMatScRange.aEnd;
866             rMatEnd.IncCol( static_cast< SCsCOL >( nMatWidth - 1 ) );
867             rMatEnd.IncRow( static_cast< SCsROW >( nMatHeight - 1 ) );
868             // reduce to valid range (range keeps valid, because start position IS valid)
869             rRoot.GetAddressConverter().ValidateRange( aMatScRange, true );
870             // create the ARRAY record
871             mxAddRec = rArrayBfr.CreateArray( rScTokArr, aMatScRange );
872         }
873         break;
874         case MM_REFERENCE:
875         {
876             // other formula cell covered by a matrix - find the ARRAY record
877             mxAddRec = rArrayBfr.FindArray( rScTokArr );
878             // should always be found, if Calc document is not broken
879             DBG_ASSERT( mxAddRec.is(), "XclExpFormulaCell::XclExpFormulaCell - no matrix found" );
880         }
881         break;
882         default:;
883     }
884 
885     // no matrix found - try to create shared formula
886     if( !mxAddRec )
887         mxAddRec = rShrfmlaBfr.CreateOrExtendShrfmla( rScTokArr, aScPos );
888 
889     // no shared formula found - create a simple cell formula
890     if( !mxAddRec )
891         mxTokArr = rRoot.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL, rScTokArr, &aScPos );
892 }
893 
Save(XclExpStream & rStrm)894 void XclExpFormulaCell::Save( XclExpStream& rStrm )
895 {
896     // create token array for FORMULA cells with additional record
897     if( mxAddRec.is() )
898         mxTokArr = mxAddRec->CreateCellTokenArray( rStrm.GetRoot() );
899 
900     // FORMULA record itself
901     DBG_ASSERT( mxTokArr.is(), "XclExpFormulaCell::Save - missing token array" );
902     if( !mxTokArr )
903         mxTokArr = rStrm.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA );
904     SetContSize( 16 + mxTokArr->GetSize() );
905     XclExpSingleCellBase::Save( rStrm );
906 
907     // additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record
908     if( mxAddRec.is() && mxAddRec->IsBasePos( GetXclCol(), GetXclRow() ) )
909         mxAddRec->Save( rStrm );
910 
911     // STRING record for string result
912     if( mxStringRec.is() )
913         mxStringRec->Save( rStrm );
914 }
915 
lcl_GetErrorString(sal_uInt16 nScErrCode)916 static const char* lcl_GetErrorString( sal_uInt16 nScErrCode )
917 {
918     sal_uInt8 nXclErrCode = XclTools::GetXclErrorCode( nScErrCode );
919     switch( nXclErrCode )
920     {
921         case EXC_ERR_NULL:  return "#NULL!";
922         case EXC_ERR_DIV0:  return "#DIV/0!";
923         case EXC_ERR_VALUE: return "#VALUE!";
924         case EXC_ERR_REF:   return "#REF!";
925         case EXC_ERR_NAME:  return "#NAME?";
926         case EXC_ERR_NUM:   return "#NUM!";
927         case EXC_ERR_NA:
928         default:            return "#N/A";
929     }
930 }
931 
lcl_GetFormulaInfo(ScFormulaCell & rCell,const char ** pType,OUString & rValue)932 static void lcl_GetFormulaInfo( ScFormulaCell& rCell, const char** pType, OUString& rValue)
933 {
934     switch( rCell.GetFormatType() )
935     {
936         case NUMBERFORMAT_NUMBER:
937         {
938             // either value or error code
939             sal_uInt16 nScErrCode = rCell.GetErrCode();
940             if( nScErrCode )
941             {
942                 *pType = "e";
943                 rValue = XclXmlUtils::ToOUString( lcl_GetErrorString( nScErrCode ) );
944             }
945             else
946             {
947                 *pType = "n";
948                 rValue = OUString::valueOf( rCell.GetValue() );
949             }
950         }
951         break;
952 
953         case NUMBERFORMAT_TEXT:
954         {
955             *pType = "str";
956             String aResult;
957             rCell.GetString( aResult );
958             rValue = XclXmlUtils::ToOUString( aResult );
959         }
960         break;
961 
962         case NUMBERFORMAT_LOGICAL:
963         {
964             *pType = "b";
965             rValue = XclXmlUtils::ToOUString( rCell.GetValue() == 0.0 ? "0" : "1" );
966         }
967         break;
968 
969         default:
970         {
971             *pType = "inlineStr";
972             String aResult;
973             rCell.GetString( aResult );
974             rValue = XclXmlUtils::ToOUString( aResult );
975         }
976         break;
977     }
978 }
979 
SaveXml(XclExpXmlStream & rStrm)980 void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
981 {
982     const char* sType = NULL;
983     OUString    sValue;
984 
985     lcl_GetFormulaInfo( mrScFmlaCell, &sType, sValue );
986     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
987     rWorksheet->startElement( XML_c,
988             XML_r,      XclXmlUtils::ToOString( GetXclPos() ).getStr(),
989             XML_s,      lcl_GetStyleId( rStrm, *this ).getStr(),
990             XML_t,      sType,
991             // OOXTODO: XML_cm, XML_vm, XML_ph
992             FSEND );
993 
994     rWorksheet->startElement( XML_f,
995             // OOXTODO: XML_t,      ST_CellFormulaType
996             XML_aca,    XclXmlUtils::ToPsz( mxTokArr->IsVolatile() || (mxAddRec.is() && mxAddRec->IsVolatile()) ),
997             // OOXTODO: XML_ref,    ST_Ref
998             // OOXTODO: XML_dt2D,   bool
999             // OOXTODO: XML_dtr,    bool
1000             // OOXTODO: XML_del1,   bool
1001             // OOXTODO: XML_del2,   bool
1002             // OOXTODO: XML_r1,     ST_CellRef
1003             // OOXTODO: XML_r2,     ST_CellRef
1004             // OOXTODO: XML_ca,     bool
1005             // OOXTODO: XML_si,     uint
1006             // OOXTODO: XML_bx      bool
1007             FSEND );
1008     rWorksheet->writeEscaped( XclXmlUtils::ToOUString( *mrScFmlaCell.GetDocument(), mrScFmlaCell.aPos, mrScFmlaCell.GetCode() ) );
1009     rWorksheet->endElement( XML_f );
1010     if( strcmp( sType, "inlineStr" ) == 0 )
1011     {
1012         rWorksheet->startElement( XML_is, FSEND );
1013         rWorksheet->startElement( XML_t, FSEND );
1014         rWorksheet->writeEscaped( sValue );
1015         rWorksheet->endElement( XML_t );
1016         rWorksheet->endElement( XML_is );
1017     }
1018     else
1019     {
1020         rWorksheet->startElement( XML_v, FSEND );
1021         rWorksheet->writeEscaped( sValue );
1022         rWorksheet->endElement( XML_v );
1023     }
1024     rWorksheet->endElement( XML_c );
1025 }
1026 
WriteContents(XclExpStream & rStrm)1027 void XclExpFormulaCell::WriteContents( XclExpStream& rStrm )
1028 {
1029     // result of the formula
1030     switch( mrScFmlaCell.GetFormatType() )
1031     {
1032         case NUMBERFORMAT_NUMBER:
1033         {
1034             // either value or error code
1035             sal_uInt16 nScErrCode = mrScFmlaCell.GetErrCode();
1036             if( nScErrCode )
1037                 rStrm << EXC_FORMULA_RES_ERROR << sal_uInt8( 0 )
1038                       << XclTools::GetXclErrorCode( nScErrCode )
1039                       << sal_uInt8( 0 ) << sal_uInt16( 0 )
1040                       << sal_uInt16( 0xFFFF );
1041             else
1042                 rStrm << mrScFmlaCell.GetValue();
1043         }
1044         break;
1045 
1046         case NUMBERFORMAT_TEXT:
1047         {
1048             String aResult;
1049             mrScFmlaCell.GetString( aResult );
1050             if( aResult.Len() || (rStrm.GetRoot().GetBiff() <= EXC_BIFF5) )
1051             {
1052                 rStrm << EXC_FORMULA_RES_STRING;
1053                 mxStringRec.reset( new XclExpStringRec( rStrm.GetRoot(), aResult ) );
1054             }
1055             else
1056                 rStrm << EXC_FORMULA_RES_EMPTY;     // BIFF8 only
1057             rStrm << sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
1058         }
1059         break;
1060 
1061         case NUMBERFORMAT_LOGICAL:
1062         {
1063             sal_uInt8 nXclValue = (mrScFmlaCell.GetValue() == 0.0) ? 0 : 1;
1064             rStrm << EXC_FORMULA_RES_BOOL << sal_uInt8( 0 )
1065                   << nXclValue << sal_uInt8( 0 ) << sal_uInt16( 0 )
1066                   << sal_uInt16( 0xFFFF );
1067         }
1068         break;
1069 
1070         default:
1071             rStrm << mrScFmlaCell.GetValue();
1072     }
1073 
1074     // flags and formula token array
1075     sal_uInt16 nFlags = EXC_FORMULA_DEFAULTFLAGS;
1076     ::set_flag( nFlags, EXC_FORMULA_RECALC_ALWAYS, mxTokArr->IsVolatile() || (mxAddRec.is() && mxAddRec->IsVolatile()) );
1077     ::set_flag( nFlags, EXC_FORMULA_SHARED, mxAddRec.is() && (mxAddRec->GetRecId() == EXC_ID_SHRFMLA) );
1078     rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
1079 }
1080 
1081 // Multiple cell records ======================================================
1082 
XclExpMultiCellBase(sal_uInt16 nRecId,sal_uInt16 nMulRecId,sal_Size nContSize,const XclAddress & rXclPos)1083 XclExpMultiCellBase::XclExpMultiCellBase(
1084         sal_uInt16 nRecId, sal_uInt16 nMulRecId, sal_Size nContSize, const XclAddress& rXclPos ) :
1085     XclExpCellBase( nRecId, 0, rXclPos ),
1086     mnMulRecId( nMulRecId ),
1087     mnContSize( nContSize )
1088 {
1089 }
1090 
GetLastXclCol() const1091 sal_uInt16 XclExpMultiCellBase::GetLastXclCol() const
1092 {
1093     return GetXclCol() + GetCellCount() - 1;
1094 }
1095 
GetFirstXFId() const1096 sal_uInt32 XclExpMultiCellBase::GetFirstXFId() const
1097 {
1098     return maXFIds.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds.front().mnXFId;
1099 }
1100 
IsEmpty() const1101 bool XclExpMultiCellBase::IsEmpty() const
1102 {
1103     return maXFIds.empty();
1104 }
1105 
ConvertXFIndexes(const XclExpRoot & rRoot)1106 void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
1107 {
1108     for( XclExpMultiXFIdDeq::iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
1109         aIt->ConvertXFIndex( rRoot );
1110 }
1111 
Save(XclExpStream & rStrm)1112 void XclExpMultiCellBase::Save( XclExpStream& rStrm )
1113 {
1114     DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1115 
1116     XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1117     XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1118     XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1119     sal_uInt16 nBegXclCol = GetXclCol();
1120     sal_uInt16 nEndXclCol = nBegXclCol;
1121 
1122     while( aRangeEnd != aEnd )
1123     {
1124         // find begin of next used XF range
1125         aRangeBeg = aRangeEnd;
1126         nBegXclCol = nEndXclCol;
1127         while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1128         {
1129             nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1130             ++aRangeBeg;
1131         }
1132         // find end of next used XF range
1133         aRangeEnd = aRangeBeg;
1134         nEndXclCol = nBegXclCol;
1135         while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1136         {
1137             nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1138             ++aRangeEnd;
1139         }
1140 
1141         // export this range as a record
1142         if( aRangeBeg != aRangeEnd )
1143         {
1144             sal_uInt16 nCount = nEndXclCol - nBegXclCol;
1145             bool bIsMulti = nCount > 1;
1146             sal_Size nTotalSize = GetRecSize() + (2 + mnContSize) * nCount;
1147             if( bIsMulti ) nTotalSize += 2;
1148 
1149             rStrm.StartRecord( bIsMulti ? mnMulRecId : GetRecId(), nTotalSize );
1150             rStrm << GetXclRow() << nBegXclCol;
1151 
1152             sal_uInt16 nRelCol = nBegXclCol - GetXclCol();
1153             for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1154             {
1155                 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1156                 {
1157                     rStrm << aIt->mnXFIndex;
1158                     WriteContents( rStrm, nRelCol );
1159                     ++nRelCol;
1160                 }
1161             }
1162             if( bIsMulti )
1163                 rStrm << static_cast< sal_uInt16 >( nEndXclCol - 1 );
1164             rStrm.EndRecord();
1165         }
1166     }
1167 }
1168 
SaveXml(XclExpXmlStream & rStrm)1169 void XclExpMultiCellBase::SaveXml( XclExpXmlStream& rStrm )
1170 {
1171     XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1172     XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1173     XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1174     sal_uInt16 nBegXclCol = GetXclCol();
1175     sal_uInt16 nEndXclCol = nBegXclCol;
1176 
1177     while( aRangeEnd != aEnd )
1178     {
1179         // find begin of next used XF range
1180         aRangeBeg = aRangeEnd;
1181         nBegXclCol = nEndXclCol;
1182         while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1183         {
1184             nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1185             ++aRangeBeg;
1186         }
1187         // find end of next used XF range
1188         aRangeEnd = aRangeBeg;
1189         nEndXclCol = nBegXclCol;
1190         while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1191         {
1192             nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1193             ++aRangeEnd;
1194         }
1195 
1196         // export this range as a record
1197         if( aRangeBeg != aRangeEnd )
1198         {
1199             sal_uInt16 nRelColIdx = nBegXclCol - GetXclCol();
1200             sal_Int32  nRelCol    = 0;
1201             for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1202             {
1203                 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1204                 {
1205                     WriteXmlContents(
1206                             rStrm,
1207                             XclAddress( static_cast<sal_uInt16>(nBegXclCol + nRelCol), GetXclRow() ),
1208                             aIt->mnXFIndex,
1209                             nRelColIdx );
1210                     ++nRelCol;
1211                     ++nRelColIdx;
1212                 }
1213             }
1214         }
1215     }
1216 }
1217 
GetCellCount() const1218 sal_uInt16 XclExpMultiCellBase::GetCellCount() const
1219 {
1220     sal_uInt16 nCount = 0;
1221     for( XclExpMultiXFIdDeq::const_iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
1222         nCount = nCount + aIt->mnCount;
1223     return nCount;
1224 }
1225 
AppendXFId(const XclExpMultiXFId & rXFId)1226 void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId& rXFId )
1227 {
1228     if( maXFIds.empty() || (maXFIds.back().mnXFId != rXFId.mnXFId) )
1229         maXFIds.push_back( rXFId );
1230     else
1231         maXFIds.back().mnCount = maXFIds.back().mnCount + rXFId.mnCount;
1232 }
1233 
AppendXFId(const XclExpRoot & rRoot,const ScPatternAttr * pPattern,sal_uInt16 nScript,sal_uInt32 nForcedXFId,sal_uInt16 nCount)1234 void XclExpMultiCellBase::AppendXFId( const XclExpRoot& rRoot,
1235         const ScPatternAttr* pPattern, sal_uInt16 nScript, sal_uInt32 nForcedXFId, sal_uInt16 nCount )
1236 {
1237     sal_uInt32 nXFId = (nForcedXFId == EXC_XFID_NOTFOUND) ?
1238         rRoot.GetXFBuffer().Insert( pPattern, nScript ) : nForcedXFId;
1239     AppendXFId( XclExpMultiXFId( nXFId, nCount ) );
1240 }
1241 
TryMergeXFIds(const XclExpMultiCellBase & rCell)1242 bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase& rCell )
1243 {
1244     if( GetLastXclCol() + 1 == rCell.GetXclCol() )
1245     {
1246         maXFIds.insert( maXFIds.end(), rCell.maXFIds.begin(), rCell.maXFIds.end() );
1247         return true;
1248     }
1249     return false;
1250 }
1251 
GetXFIndexes(ScfUInt16Vec & rXFIndexes) const1252 void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1253 {
1254     DBG_ASSERT( GetLastXclCol() < rXFIndexes.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" );
1255     ScfUInt16Vec::iterator aDestIt = rXFIndexes.begin() + GetXclCol();
1256     for( XclExpMultiXFIdDeq::const_iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
1257     {
1258         ::std::fill( aDestIt, aDestIt + aIt->mnCount, aIt->mnXFIndex );
1259         aDestIt += aIt->mnCount;
1260     }
1261 }
1262 
RemoveUnusedXFIndexes(const ScfUInt16Vec & rXFIndexes)1263 void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes )
1264 {
1265     // save last column before calling maXFIds.clear()
1266     sal_uInt16 nLastXclCol = GetLastXclCol();
1267     DBG_ASSERT( nLastXclCol < rXFIndexes.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" );
1268 
1269     // build new XF index vector, containing passed XF indexes
1270     maXFIds.clear();
1271     XclExpMultiXFId aXFId( 0 );
1272     for( ScfUInt16Vec::const_iterator aIt = rXFIndexes.begin() + GetXclCol(), aEnd = rXFIndexes.begin() + nLastXclCol + 1; aIt != aEnd; ++aIt )
1273     {
1274         // AppendXFId() tests XclExpXFIndex::mnXFId, set it too
1275         aXFId.mnXFId = aXFId.mnXFIndex = *aIt;
1276         AppendXFId( aXFId );
1277     }
1278 
1279     // remove leading and trailing unused XF indexes
1280     if( !maXFIds.empty() && (maXFIds.front().mnXFIndex == EXC_XF_NOTFOUND) )
1281     {
1282         SetXclCol( GetXclCol() + maXFIds.front().mnCount );
1283         maXFIds.pop_front();
1284     }
1285     if( !maXFIds.empty() && (maXFIds.back().mnXFIndex == EXC_XF_NOTFOUND) )
1286         maXFIds.pop_back();
1287 
1288     // The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND.
1289 }
1290 
1291 // ----------------------------------------------------------------------------
1292 
1293 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpBlankCell, 256, 256 )
1294 
XclExpBlankCell(const XclAddress & rXclPos,const XclExpMultiXFId & rXFId)1295 XclExpBlankCell::XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId ) :
1296     XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1297 {
1298     DBG_ASSERT( rXFId.mnCount > 0, "XclExpBlankCell::XclExpBlankCell - invalid count" );
1299     AppendXFId( rXFId );
1300 }
1301 
XclExpBlankCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,sal_uInt16 nLastXclCol,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId)1302 XclExpBlankCell::XclExpBlankCell(
1303         const XclExpRoot& rRoot, const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
1304         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId ) :
1305     XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1306 {
1307     DBG_ASSERT( rXclPos.mnCol <= nLastXclCol, "XclExpBlankCell::XclExpBlankCell - invalid column range" );
1308     // #i46627# use default script type instead of ApiScriptType::WEAK
1309     AppendXFId( rRoot, pPattern, rRoot.GetDefApiScript(), nForcedXFId, nLastXclCol - rXclPos.mnCol + 1 );
1310 }
1311 
TryMerge(const XclExpCellBase & rCell)1312 bool XclExpBlankCell::TryMerge( const XclExpCellBase& rCell )
1313 {
1314     const XclExpBlankCell* pBlankCell = dynamic_cast< const XclExpBlankCell* >( &rCell );
1315     return pBlankCell && TryMergeXFIds( *pBlankCell );
1316 }
1317 
GetBlankXFIndexes(ScfUInt16Vec & rXFIndexes) const1318 void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1319 {
1320     GetXFIndexes( rXFIndexes );
1321 }
1322 
RemoveUnusedBlankCells(const ScfUInt16Vec & rXFIndexes)1323 void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes )
1324 {
1325     RemoveUnusedXFIndexes( rXFIndexes );
1326 }
1327 
WriteContents(XclExpStream &,sal_uInt16)1328 void XclExpBlankCell::WriteContents( XclExpStream& /*rStrm*/, sal_uInt16 /*nRelCol*/ )
1329 {
1330 }
1331 
WriteXmlContents(XclExpXmlStream & rStrm,const XclAddress & rAddress,sal_uInt32 nXFId,sal_uInt16)1332 void XclExpBlankCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 /* nRelCol */ )
1333 {
1334     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1335     rWorksheet->singleElement( XML_c,
1336             XML_r,      XclXmlUtils::ToOString( rAddress ).getStr(),
1337             XML_s,      lcl_GetStyleId( rStrm, nXFId ).getStr(),
1338             FSEND );
1339 }
1340 
1341 // ----------------------------------------------------------------------------
1342 
1343 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpRkCell, 256, 256 )
1344 
XclExpRkCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,sal_Int32 nRkValue)1345 XclExpRkCell::XclExpRkCell(
1346         const XclExpRoot& rRoot, const XclAddress& rXclPos,
1347         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_Int32 nRkValue ) :
1348     XclExpMultiCellBase( EXC_ID_RK, EXC_ID_MULRK, 4, rXclPos )
1349 {
1350     // #i41210# always use latin script for number cells - may look wrong for special number formats...
1351     AppendXFId( rRoot, pPattern, ApiScriptType::LATIN, nForcedXFId );
1352     maRkValues.push_back( nRkValue );
1353 }
1354 
TryMerge(const XclExpCellBase & rCell)1355 bool XclExpRkCell::TryMerge( const XclExpCellBase& rCell )
1356 {
1357     const XclExpRkCell* pRkCell = dynamic_cast< const XclExpRkCell* >( &rCell );
1358     if( pRkCell && TryMergeXFIds( *pRkCell ) )
1359     {
1360         maRkValues.insert( maRkValues.end(), pRkCell->maRkValues.begin(), pRkCell->maRkValues.end() );
1361         return true;
1362     }
1363     return false;
1364 }
1365 
WriteXmlContents(XclExpXmlStream & rStrm,const XclAddress & rAddress,sal_uInt32 nXFId,sal_uInt16 nRelCol)1366 void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol )
1367 {
1368     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1369     rWorksheet->startElement( XML_c,
1370             XML_r,      XclXmlUtils::ToOString( rAddress ).getStr(),
1371             XML_s,      lcl_GetStyleId( rStrm, nXFId ).getStr(),
1372             XML_t,      "n",
1373             // OOXTODO: XML_cm, XML_vm, XML_ph
1374             FSEND );
1375     rWorksheet->startElement( XML_v, FSEND );
1376     rWorksheet->write( XclTools::GetDoubleFromRK( maRkValues[ nRelCol ] ) );
1377     rWorksheet->endElement( XML_v );
1378     rWorksheet->endElement( XML_c );
1379 }
1380 
WriteContents(XclExpStream & rStrm,sal_uInt16 nRelCol)1381 void XclExpRkCell::WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol )
1382 {
1383     DBG_ASSERT( nRelCol < maRkValues.size(), "XclExpRkCell::WriteContents - overflow error" );
1384     rStrm << maRkValues[ nRelCol ];
1385 }
1386 
1387 // ============================================================================
1388 // Rows and Columns
1389 // ============================================================================
1390 
XclExpOutlineBuffer(const XclExpRoot & rRoot,bool bRows)1391 XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows ) :
1392         mpScOLArray( 0 ),
1393         maLevelInfos( SC_OL_MAXDEPTH ),
1394         mnCurrLevel( 0 ),
1395         mbCurrCollapse( false )
1396 {
1397     if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
1398         mpScOLArray = bRows ? pOutlineTable->GetRowArray() : pOutlineTable->GetColArray();
1399 
1400     if( mpScOLArray )
1401         for( sal_uInt16 nLevel = 0; nLevel < SC_OL_MAXDEPTH; ++nLevel )
1402             if( ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nLevel, 0 ) )
1403                 maLevelInfos[ nLevel ].mnScEndPos = pEntry->GetEnd();
1404 }
1405 
UpdateColRow(SCCOLROW nScPos)1406 void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos )
1407 {
1408     if( mpScOLArray )
1409     {
1410         // find open level index for passed position
1411         sal_uInt16 nNewOpenScLevel = 0; // new open level (0-based Calc index)
1412         sal_uInt8 nNewLevel = 0;    // new open level (1-based Excel index)
1413 
1414         if( mpScOLArray->FindTouchedLevel( nScPos, nScPos, nNewOpenScLevel ) )
1415             nNewLevel = static_cast< sal_uInt8 >( nNewOpenScLevel + 1 );
1416         // else nNewLevel keeps 0 to show that there are no groups
1417 
1418         mbCurrCollapse = false;
1419         if( nNewLevel >= mnCurrLevel )
1420         {
1421             // new level(s) opened, or no level closed - update all level infos
1422             for( sal_uInt16 nScLevel = 0; nScLevel <= nNewOpenScLevel; ++nScLevel )
1423             {
1424                 /*  In each level: check if a new group is started (there may be
1425                     neighbored groups without gap - therefore check ALL levels). */
1426                 if( maLevelInfos[ nScLevel ].mnScEndPos < nScPos )
1427                 {
1428                     if( ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nScLevel, nScPos ) )
1429                     {
1430                         maLevelInfos[ nScLevel ].mnScEndPos = pEntry->GetEnd();
1431                         maLevelInfos[ nScLevel ].mbHidden = pEntry->IsHidden();
1432                     }
1433                 }
1434             }
1435         }
1436         else
1437         {
1438             // level(s) closed - check if any of the closed levels are collapsed
1439             // Calc uses 0-based level indexes
1440             sal_uInt16 nOldOpenScLevel = mnCurrLevel - 1;
1441             for( sal_uInt16 nScLevel = nNewOpenScLevel + 1; !mbCurrCollapse && (nScLevel <= nOldOpenScLevel); ++nScLevel )
1442                 mbCurrCollapse = maLevelInfos[ nScLevel ].mbHidden;
1443         }
1444 
1445         // cache new opened level
1446         mnCurrLevel = nNewLevel;
1447     }
1448 }
1449 
1450 // ----------------------------------------------------------------------------
1451 
XclExpGuts(const XclExpRoot & rRoot)1452 XclExpGuts::XclExpGuts( const XclExpRoot& rRoot ) :
1453     XclExpRecord( EXC_ID_GUTS, 8 ),
1454     mnColLevels( 0 ),
1455     mnColWidth( 0 ),
1456     mnRowLevels( 0 ),
1457     mnRowWidth( 0 )
1458 {
1459     if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
1460     {
1461         // column outline groups
1462         if( const ScOutlineArray* pColArray = pOutlineTable->GetColArray() )
1463             mnColLevels = ulimit_cast< sal_uInt16 >( pColArray->GetDepth(), EXC_OUTLINE_MAX );
1464         if( mnColLevels )
1465         {
1466             ++mnColLevels;
1467             mnColWidth = 12 * mnColLevels + 5;
1468         }
1469 
1470         // row outline groups
1471         if( const ScOutlineArray* pRowArray = pOutlineTable->GetRowArray() )
1472             mnRowLevels = ulimit_cast< sal_uInt16 >( pRowArray->GetDepth(), EXC_OUTLINE_MAX );
1473         if( mnRowLevels )
1474         {
1475             ++mnRowLevels;
1476             mnRowWidth = 12 * mnRowLevels + 5;
1477         }
1478     }
1479 }
1480 
WriteBody(XclExpStream & rStrm)1481 void XclExpGuts::WriteBody( XclExpStream& rStrm )
1482 {
1483     rStrm << mnRowWidth << mnColWidth << mnRowLevels << mnColLevels;
1484 }
1485 
1486 // ----------------------------------------------------------------------------
1487 
XclExpDimensions(const XclExpRoot & rRoot)1488 XclExpDimensions::XclExpDimensions( const XclExpRoot& rRoot ) :
1489     mnFirstUsedXclRow( 0 ),
1490     mnFirstFreeXclRow( 0 ),
1491     mnFirstUsedXclCol( 0 ),
1492     mnFirstFreeXclCol( 0 )
1493 {
1494     switch( rRoot.GetBiff() )
1495     {
1496         case EXC_BIFF2: SetRecHeader( EXC_ID2_DIMENSIONS, 8 );  break;
1497         case EXC_BIFF3:
1498         case EXC_BIFF4:
1499         case EXC_BIFF5: SetRecHeader( EXC_ID3_DIMENSIONS, 10 ); break;
1500         case EXC_BIFF8: SetRecHeader( EXC_ID3_DIMENSIONS, 14 ); break;
1501         default:        DBG_ERROR_BIFF();
1502     }
1503 }
1504 
SetDimensions(sal_uInt16 nFirstUsedXclCol,sal_uInt32 nFirstUsedXclRow,sal_uInt16 nFirstFreeXclCol,sal_uInt32 nFirstFreeXclRow)1505 void XclExpDimensions::SetDimensions(
1506         sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
1507         sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow )
1508 {
1509     mnFirstUsedXclRow = nFirstUsedXclRow;
1510     mnFirstFreeXclRow = nFirstFreeXclRow;
1511     mnFirstUsedXclCol = nFirstUsedXclCol;
1512     mnFirstFreeXclCol = nFirstFreeXclCol;
1513 }
1514 
SaveXml(XclExpXmlStream & rStrm)1515 void XclExpDimensions::SaveXml( XclExpXmlStream& rStrm )
1516 {
1517     ScRange aRange;
1518     aRange.aStart.SetRow( (SCROW) mnFirstUsedXclRow );
1519     aRange.aStart.SetCol( (SCCOL) mnFirstUsedXclCol );
1520 
1521     if( mnFirstFreeXclRow != mnFirstUsedXclRow && mnFirstFreeXclCol != mnFirstUsedXclCol )
1522     {
1523         aRange.aEnd.SetRow( (SCROW) (mnFirstFreeXclRow-1) );
1524         aRange.aEnd.SetCol( (SCCOL) (mnFirstFreeXclCol-1) );
1525     }
1526 
1527     rStrm.GetCurrentStream()->singleElement( XML_dimension,
1528             XML_ref, XclXmlUtils::ToOString( aRange ).getStr(),
1529             FSEND );
1530 }
1531 
WriteBody(XclExpStream & rStrm)1532 void XclExpDimensions::WriteBody( XclExpStream& rStrm )
1533 {
1534     XclBiff eBiff = rStrm.GetRoot().GetBiff();
1535     if( eBiff == EXC_BIFF8 )
1536         rStrm << mnFirstUsedXclRow << mnFirstFreeXclRow;
1537     else
1538         rStrm << static_cast< sal_uInt16 >( mnFirstUsedXclRow ) << static_cast< sal_uInt16 >( mnFirstFreeXclRow );
1539     rStrm << mnFirstUsedXclCol << mnFirstFreeXclCol;
1540     if( eBiff >= EXC_BIFF3 )
1541         rStrm << sal_uInt16( 0 );
1542 }
1543 
1544 // ============================================================================
1545 
1546 namespace {
1547 
lclGetCorrectedColWidth(const XclExpRoot & rRoot,sal_uInt16 nXclColWidth)1548 double lclGetCorrectedColWidth( const XclExpRoot& rRoot, sal_uInt16 nXclColWidth )
1549 {
1550     long nFontHt = rRoot.GetFontBuffer().GetAppFontData().mnHeight;
1551     return nXclColWidth - XclTools::GetXclDefColWidthCorrection( nFontHt );
1552 }
1553 
1554 } // namespace
1555 
1556 // ----------------------------------------------------------------------------
1557 
XclExpDefcolwidth(const XclExpRoot & rRoot)1558 XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot& rRoot ) :
1559     XclExpUInt16Record( EXC_ID_DEFCOLWIDTH, EXC_DEFCOLWIDTH_DEF ),
1560     XclExpRoot( rRoot )
1561 {
1562 }
1563 
IsDefWidth(sal_uInt16 nXclColWidth) const1564 bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth ) const
1565 {
1566     double fNewColWidth = lclGetCorrectedColWidth( GetRoot(), nXclColWidth );
1567     // exactly matched, if difference is less than 1/16 of a character to the left or to the right
1568     return Abs( static_cast< long >( GetValue() * 256.0 - fNewColWidth + 0.5 ) ) < 16;
1569 }
1570 
SetDefWidth(sal_uInt16 nXclColWidth)1571 void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth )
1572 {
1573     double fNewColWidth = lclGetCorrectedColWidth( GetRoot(), nXclColWidth );
1574     SetValue( limit_cast< sal_uInt16 >( fNewColWidth / 256.0 + 0.5 ) );
1575 }
1576 
1577 // ----------------------------------------------------------------------------
1578 
XclExpColinfo(const XclExpRoot & rRoot,SCCOL nScCol,SCROW nLastScRow,XclExpColOutlineBuffer & rOutlineBfr)1579 XclExpColinfo::XclExpColinfo( const XclExpRoot& rRoot,
1580         SCCOL nScCol, SCROW nLastScRow, XclExpColOutlineBuffer& rOutlineBfr ) :
1581     XclExpRecord( EXC_ID_COLINFO, 12 ),
1582     XclExpRoot( rRoot ),
1583     mnWidth( 0 ),
1584     mnFlags( 0 ),
1585     mnFirstXclCol( static_cast< sal_uInt16 >( nScCol ) ),
1586     mnLastXclCol( static_cast< sal_uInt16 >( nScCol ) )
1587 {
1588     ScDocument& rDoc = GetDoc();
1589     SCTAB nScTab = GetCurrScTab();
1590 
1591     // column default format
1592     maXFId.mnXFId = GetXFBuffer().Insert(
1593         rDoc.GetMostUsedPattern( nScCol, 0, nLastScRow, nScTab ), GetDefApiScript() );
1594 
1595     // column width
1596     sal_uInt16 nScWidth = rDoc.GetColWidth( nScCol, nScTab );
1597     mnWidth = XclTools::GetXclColumnWidth( nScWidth, GetCharWidth() );
1598 
1599     // column flags
1600     ::set_flag( mnFlags, EXC_COLINFO_HIDDEN, rDoc.ColHidden(nScCol, nScTab) );
1601 
1602     // outline data
1603     rOutlineBfr.Update( nScCol );
1604     ::set_flag( mnFlags, EXC_COLINFO_COLLAPSED, rOutlineBfr.IsCollapsed() );
1605     ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 8, 3 );
1606 }
1607 
ConvertXFIndexes()1608 sal_uInt16 XclExpColinfo::ConvertXFIndexes()
1609 {
1610     maXFId.ConvertXFIndex( GetRoot() );
1611     return maXFId.mnXFIndex;
1612 }
1613 
IsDefault(const XclExpDefcolwidth & rDefColWidth) const1614 bool XclExpColinfo::IsDefault( const XclExpDefcolwidth& rDefColWidth ) const
1615 {
1616     return (maXFId.mnXFIndex == EXC_XF_DEFAULTCELL) && (mnFlags == 0) && rDefColWidth.IsDefWidth( mnWidth );
1617 }
1618 
TryMerge(const XclExpColinfo & rColInfo)1619 bool XclExpColinfo::TryMerge( const XclExpColinfo& rColInfo )
1620 {
1621     if( (maXFId.mnXFIndex == rColInfo.maXFId.mnXFIndex) &&
1622         (mnWidth == rColInfo.mnWidth) &&
1623         (mnFlags == rColInfo.mnFlags) &&
1624         (mnLastXclCol + 1 == rColInfo.mnFirstXclCol) )
1625     {
1626         mnLastXclCol = rColInfo.mnLastXclCol;
1627         return true;
1628     }
1629     return false;
1630 }
1631 
WriteBody(XclExpStream & rStrm)1632 void XclExpColinfo::WriteBody( XclExpStream& rStrm )
1633 {
1634     // if last column is equal to last possible column, Excel adds one more
1635     sal_uInt16 nLastXclCol = mnLastXclCol;
1636     if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
1637         ++nLastXclCol;
1638 
1639     rStrm   << mnFirstXclCol
1640             << nLastXclCol
1641             << mnWidth
1642             << maXFId.mnXFIndex
1643             << mnFlags
1644             << sal_uInt16( 0 );
1645 }
1646 
SaveXml(XclExpXmlStream & rStrm)1647 void XclExpColinfo::SaveXml( XclExpXmlStream& rStrm )
1648 {
1649     // if last column is equal to last possible column, Excel adds one more
1650     sal_uInt16 nLastXclCol = mnLastXclCol;
1651     if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
1652         ++nLastXclCol;
1653 
1654     rStrm.GetCurrentStream()->singleElement( XML_col,
1655             // OOXTODO: XML_bestFit,
1656             XML_collapsed,      XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_COLINFO_COLLAPSED ) ),
1657             // OOXTODO: XML_customWidth,
1658             XML_hidden,         XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_COLINFO_HIDDEN ) ),
1659             XML_max,            OString::valueOf( (sal_Int32) (nLastXclCol+1) ).getStr(),
1660             XML_min,            OString::valueOf( (sal_Int32) (mnFirstXclCol+1) ).getStr(),
1661             // OOXTODO: XML_outlineLevel,
1662             // OOXTODO: XML_phonetic,
1663             XML_style,          lcl_GetStyleId( rStrm, maXFId.mnXFIndex ).getStr(),
1664             XML_width,          OString::valueOf( (double) (mnWidth / 255.0) ).getStr(),
1665             FSEND );
1666 }
1667 
1668 // ----------------------------------------------------------------------------
1669 
XclExpColinfoBuffer(const XclExpRoot & rRoot)1670 XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot& rRoot ) :
1671     XclExpRoot( rRoot ),
1672     maDefcolwidth( rRoot ),
1673     maOutlineBfr( rRoot )
1674 {
1675 }
1676 
Initialize(SCROW nLastScRow)1677 void XclExpColinfoBuffer::Initialize( SCROW nLastScRow )
1678 {
1679 
1680     for( sal_uInt16 nScCol = 0, nLastScCol = GetMaxPos().Col(); nScCol <= nLastScCol; ++nScCol )
1681         maColInfos.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol, nLastScRow, maOutlineBfr ) );
1682 }
1683 
Finalize(ScfUInt16Vec & rXFIndexes)1684 void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes )
1685 {
1686     rXFIndexes.clear();
1687     rXFIndexes.reserve( maColInfos.GetSize() );
1688 
1689     size_t nPos, nSize;
1690 
1691     // do not cache the record list size, it may change in the loop
1692     for( nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
1693     {
1694         XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
1695         xRec->ConvertXFIndexes();
1696 
1697         // try to merge with previous record
1698         if( nPos > 0 )
1699         {
1700             XclExpColinfoRef xPrevRec = maColInfos.GetRecord( nPos - 1 );
1701             if( xPrevRec->TryMerge( *xRec ) )
1702                 // adjust nPos to get the next COLINFO record at the same position
1703                 maColInfos.RemoveRecord( nPos-- );
1704         }
1705     }
1706 
1707     // put XF indexes into passed vector, collect use count of all different widths
1708     typedef ::std::map< sal_uInt16, sal_uInt16 > XclExpWidthMap;
1709     XclExpWidthMap aWidthMap;
1710     sal_uInt16 nMaxColCount = 0;
1711     sal_uInt16 nMaxUsedWidth = 0;
1712     for( nPos = 0, nSize = maColInfos.GetSize(); nPos < nSize; ++nPos )
1713     {
1714         XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
1715         sal_uInt16 nColCount = xRec->GetColCount();
1716 
1717         // add XF index to passed vector
1718         rXFIndexes.resize( rXFIndexes.size() + nColCount, xRec->GetXFIndex() );
1719 
1720         // collect use count of column width
1721         sal_uInt16 nWidth = xRec->GetColWidth();
1722         sal_uInt16& rnMapCount = aWidthMap[ nWidth ];
1723         rnMapCount = rnMapCount + nColCount;
1724         if( rnMapCount > nMaxColCount )
1725         {
1726             nMaxColCount = rnMapCount;
1727             nMaxUsedWidth = nWidth;
1728         }
1729     }
1730     maDefcolwidth.SetDefWidth( nMaxUsedWidth );
1731 
1732     // remove all default COLINFO records
1733     nPos = 0;
1734     while( nPos < maColInfos.GetSize() )
1735     {
1736         XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
1737         if( xRec->IsDefault( maDefcolwidth ) )
1738             maColInfos.RemoveRecord( nPos );
1739         else
1740             ++nPos;
1741     }
1742 }
1743 
Save(XclExpStream & rStrm)1744 void XclExpColinfoBuffer::Save( XclExpStream& rStrm )
1745 {
1746     // DEFCOLWIDTH
1747     maDefcolwidth.Save( rStrm );
1748     // COLINFO records
1749     maColInfos.Save( rStrm );
1750 }
1751 
SaveXml(XclExpXmlStream & rStrm)1752 void XclExpColinfoBuffer::SaveXml( XclExpXmlStream& rStrm )
1753 {
1754     if( maColInfos.IsEmpty() )
1755         return;
1756 
1757     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1758     rWorksheet->startElement( XML_cols,
1759             FSEND );
1760     maColInfos.SaveXml( rStrm );
1761     rWorksheet->endElement( XML_cols );
1762 }
1763 
1764 // ============================================================================
1765 
XclExpDefaultRowData()1766 XclExpDefaultRowData::XclExpDefaultRowData() :
1767     mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1768     mnHeight( EXC_DEFROW_DEFAULTHEIGHT )
1769 {
1770 }
1771 
XclExpDefaultRowData(const XclExpRow & rRow)1772 XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow& rRow ) :
1773     mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1774     mnHeight( rRow.GetHeight() )
1775 {
1776     ::set_flag( mnFlags, EXC_DEFROW_HIDDEN, rRow.IsHidden() );
1777     ::set_flag( mnFlags, EXC_DEFROW_UNSYNCED, rRow.IsUnsynced() );
1778 }
1779 
operator <(const XclExpDefaultRowData & rLeft,const XclExpDefaultRowData & rRight)1780 bool operator<( const XclExpDefaultRowData& rLeft, const XclExpDefaultRowData& rRight )
1781 {
1782     return (rLeft.mnHeight < rRight.mnHeight) ||
1783         ((rLeft.mnHeight == rRight.mnHeight) && (rLeft.mnFlags < rRight.mnFlags));
1784 }
1785 
1786 // ----------------------------------------------------------------------------
1787 
XclExpDefrowheight()1788 XclExpDefrowheight::XclExpDefrowheight() :
1789     XclExpRecord( EXC_ID3_DEFROWHEIGHT, 4 )
1790 {
1791 }
1792 
SetDefaultData(const XclExpDefaultRowData & rDefData)1793 void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData& rDefData )
1794 {
1795     maDefData = rDefData;
1796 }
1797 
WriteBody(XclExpStream & rStrm)1798 void XclExpDefrowheight::WriteBody( XclExpStream& rStrm )
1799 {
1800     DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1801     rStrm << maDefData.mnFlags << maDefData.mnHeight;
1802 }
1803 
1804 // ----------------------------------------------------------------------------
1805 
XclExpRow(const XclExpRoot & rRoot,sal_uInt16 nXclRow,XclExpRowOutlineBuffer & rOutlineBfr,bool bAlwaysEmpty)1806 XclExpRow::XclExpRow( const XclExpRoot& rRoot, sal_uInt16 nXclRow,
1807         XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty ) :
1808     XclExpRecord( EXC_ID3_ROW, 16 ),
1809     XclExpRoot( rRoot ),
1810     mnXclRow( nXclRow ),
1811     mnHeight( 0 ),
1812     mnFlags( EXC_ROW_DEFAULTFLAGS ),
1813     mnXFIndex( EXC_XF_DEFAULTCELL ),
1814     mnOutlineLevel( 0 ),
1815     mbAlwaysEmpty( bAlwaysEmpty ),
1816     mbEnabled( true )
1817 {
1818     SCTAB nScTab = GetCurrScTab();
1819     SCROW nScRow = static_cast< SCROW >( mnXclRow );
1820 
1821     // *** Row flags *** ------------------------------------------------------
1822 
1823     sal_uInt8 nRowFlags = GetDoc().GetRowFlags( nScRow, nScTab );
1824     bool bUserHeight = ::get_flag< sal_uInt8 >( nRowFlags, CR_MANUALSIZE );
1825     bool bHidden = GetDoc().RowHidden(nScRow, nScTab);
1826     ::set_flag( mnFlags, EXC_ROW_UNSYNCED, bUserHeight );
1827     ::set_flag( mnFlags, EXC_ROW_HIDDEN, bHidden );
1828 
1829     // *** Row height *** -----------------------------------------------------
1830 
1831     if (bUserHeight)
1832         mnHeight = GetDoc().GetRowHeight(nScRow, nScTab, false);
1833     else
1834         mnHeight = EXC_ROW_DEFAULTHEIGHT;
1835 
1836     // #76250# not usable in Applix
1837 //    ::set_flag( mnHeight, EXC_ROW_FLAGDEFHEIGHT, !bUserHeight );
1838 
1839     // *** Outline data *** ---------------------------------------------------
1840 
1841     rOutlineBfr.Update( nScRow );
1842     ::set_flag( mnFlags, EXC_ROW_COLLAPSED, rOutlineBfr.IsCollapsed() );
1843     ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 0, 3 );
1844     mnOutlineLevel = rOutlineBfr.GetLevel();
1845 
1846     // *** Progress bar *** ---------------------------------------------------
1847 
1848     XclExpProgressBar& rProgress = GetProgressBar();
1849     rProgress.IncRowRecordCount();
1850     rProgress.Progress();
1851 }
1852 
AppendCell(XclExpCellRef xCell,bool bIsMergedBase)1853 void XclExpRow::AppendCell( XclExpCellRef xCell, bool bIsMergedBase )
1854 {
1855     DBG_ASSERT( !mbAlwaysEmpty, "XclExpRow::AppendCell - row is marked to be always empty" );
1856     // try to merge with last existing cell
1857     InsertCell( xCell, maCellList.GetSize(), bIsMergedBase );
1858 }
1859 
Finalize(const ScfUInt16Vec & rColXFIndexes)1860 void XclExpRow::Finalize( const ScfUInt16Vec& rColXFIndexes )
1861 {
1862     size_t nPos, nSize;
1863 
1864     // *** Convert XF identifiers *** -----------------------------------------
1865 
1866     // additionally collect the blank XF indexes
1867     size_t nColCount = GetMaxPos().Col() + 1;
1868     DBG_ASSERT( rColXFIndexes.size() == nColCount, "XclExpRow::Finalize - wrong column XF index count" );
1869 
1870     ScfUInt16Vec aXFIndexes( nColCount, EXC_XF_NOTFOUND );
1871     for( nPos = 0, nSize = maCellList.GetSize(); nPos < nSize; ++nPos )
1872     {
1873         XclExpCellRef xCell = maCellList.GetRecord( nPos );
1874         xCell->ConvertXFIndexes( GetRoot() );
1875         xCell->GetBlankXFIndexes( aXFIndexes );
1876     }
1877 
1878     // *** Fill gaps with BLANK/MULBLANK cell records *** ---------------------
1879 
1880     /*  This is needed because nonexistent cells in Calc are not formatted at all,
1881         but in Excel they would have the column default format. Blank cells that
1882         are equal to the respective column default are removed later in this function. */
1883     if( !mbAlwaysEmpty )
1884     {
1885         // XF identifier representing default cell XF
1886         XclExpMultiXFId aXFId( XclExpXFBuffer::GetDefCellXFId() );
1887         aXFId.ConvertXFIndex( GetRoot() );
1888 
1889         nPos = 0;
1890         while( nPos <= maCellList.GetSize() )  // don't cache list size, may change in the loop
1891         {
1892             // get column index that follows previous cell
1893             sal_uInt16 nFirstFreeXclCol = (nPos > 0) ? (maCellList.GetRecord( nPos - 1 )->GetLastXclCol() + 1) : 0;
1894             // get own column index
1895             sal_uInt16 nNextUsedXclCol = (nPos < maCellList.GetSize()) ? maCellList.GetRecord( nPos )->GetXclCol() : (GetMaxPos().Col() + 1);
1896 
1897             // is there a gap?
1898             if( nFirstFreeXclCol < nNextUsedXclCol )
1899             {
1900                 aXFId.mnCount = nNextUsedXclCol - nFirstFreeXclCol;
1901                 XclExpCellRef xNewCell( new XclExpBlankCell( XclAddress( nFirstFreeXclCol, mnXclRow ), aXFId ) );
1902                 // insert the cell, InsertCell() may merge it with existing BLANK records
1903                 InsertCell( xNewCell, nPos, false );
1904                 // insert default XF indexes into aXFIndexes
1905                 ::std::fill( aXFIndexes.begin() + nFirstFreeXclCol,
1906                     aXFIndexes.begin() + nNextUsedXclCol, aXFId.mnXFIndex );
1907                 // don't step forward with nPos, InsertCell() may remove records
1908             }
1909             else
1910                 ++nPos;
1911         }
1912     }
1913 
1914     // *** Find default row format *** ----------------------------------------
1915 
1916     ScfUInt16Vec::iterator aCellBeg = aXFIndexes.begin(), aCellEnd = aXFIndexes.end(), aCellIt;
1917     ScfUInt16Vec::const_iterator aColBeg = rColXFIndexes.begin(), aColIt;
1918 
1919     // find most used XF index in the row
1920     typedef ::std::map< sal_uInt16, size_t > XclExpXFIndexMap;
1921     XclExpXFIndexMap aIndexMap;
1922     sal_uInt16 nRowXFIndex = EXC_XF_DEFAULTCELL;
1923     size_t nMaxXFCount = 0;
1924     for( aCellIt = aCellBeg; aCellIt != aCellEnd; ++aCellIt )
1925     {
1926         if( *aCellIt != EXC_XF_NOTFOUND )
1927         {
1928             size_t& rnCount = aIndexMap[ *aCellIt ];
1929             ++rnCount;
1930             if( rnCount > nMaxXFCount )
1931             {
1932                 nRowXFIndex = *aCellIt;
1933                 nMaxXFCount = rnCount;
1934             }
1935         }
1936     }
1937 
1938     // decide whether to use the row default XF index or column default XF indexes
1939     bool bUseColDefXFs = nRowXFIndex == EXC_XF_DEFAULTCELL;
1940     if( !bUseColDefXFs )
1941     {
1942         // count needed XF indexes for blank cells with and without row default XF index
1943         size_t nXFCountWithRowDefXF = 0;
1944         size_t nXFCountWithoutRowDefXF = 0;
1945         for( aCellIt = aCellBeg, aColIt = aColBeg; aCellIt != aCellEnd; ++aCellIt, ++aColIt )
1946         {
1947             sal_uInt16 nXFIndex = *aCellIt;
1948             if( nXFIndex != EXC_XF_NOTFOUND )
1949             {
1950                 if( nXFIndex != nRowXFIndex )
1951                     ++nXFCountWithRowDefXF;     // with row default XF index
1952                 if( nXFIndex != *aColIt )
1953                     ++nXFCountWithoutRowDefXF;  // without row default XF index
1954             }
1955         }
1956 
1957         // use column XF indexes if this would cause less or equal number of BLANK records
1958         bUseColDefXFs = nXFCountWithoutRowDefXF <= nXFCountWithRowDefXF;
1959     }
1960 
1961     // *** Remove unused BLANK cell records *** -------------------------------
1962 
1963     if( bUseColDefXFs )
1964     {
1965         // use column default XF indexes
1966         // #i194#: remove cell XF indexes equal to column default XF indexes
1967         for( aCellIt = aCellBeg, aColIt = aColBeg; aCellIt != aCellEnd; ++aCellIt, ++aColIt )
1968             if( *aCellIt == *aColIt )
1969                 *aCellIt = EXC_XF_NOTFOUND;
1970     }
1971     else
1972     {
1973         // use row default XF index
1974         mnXFIndex = nRowXFIndex;
1975         ::set_flag( mnFlags, EXC_ROW_USEDEFXF );
1976         // #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index
1977         for( aCellIt = aCellBeg; aCellIt != aCellEnd; ++aCellIt )
1978             if( *aCellIt == nRowXFIndex )
1979                 *aCellIt = EXC_XF_NOTFOUND;
1980     }
1981 
1982     // remove unused parts of BLANK/MULBLANK cell records
1983     nPos = 0;
1984     while( nPos < maCellList.GetSize() )   // do not cache list size, may change in the loop
1985     {
1986         XclExpCellRef xCell = maCellList.GetRecord( nPos );
1987         xCell->RemoveUnusedBlankCells( aXFIndexes );
1988         if( xCell->IsEmpty() )
1989             maCellList.RemoveRecord( nPos );
1990         else
1991             ++nPos;
1992     }
1993 
1994     // progress bar includes disabled rows
1995     GetProgressBar().Progress();
1996 }
1997 
GetFirstUsedXclCol() const1998 sal_uInt16 XclExpRow::GetFirstUsedXclCol() const
1999 {
2000     return maCellList.IsEmpty() ? 0 : maCellList.GetFirstRecord()->GetXclCol();
2001 }
2002 
GetFirstFreeXclCol() const2003 sal_uInt16 XclExpRow::GetFirstFreeXclCol() const
2004 {
2005     return maCellList.IsEmpty() ? 0 : (maCellList.GetLastRecord()->GetLastXclCol() + 1);
2006 }
2007 
IsDefaultable() const2008 bool XclExpRow::IsDefaultable() const
2009 {
2010     const sal_uInt16 nAllowedFlags = EXC_ROW_DEFAULTFLAGS | EXC_ROW_HIDDEN | EXC_ROW_UNSYNCED;
2011     return !::get_flag( mnFlags, static_cast< sal_uInt16 >( ~nAllowedFlags ) ) && IsEmpty();
2012 }
2013 
DisableIfDefault(const XclExpDefaultRowData & rDefRowData)2014 void XclExpRow::DisableIfDefault( const XclExpDefaultRowData& rDefRowData )
2015 {
2016     mbEnabled = !IsDefaultable() ||
2017         (mnHeight != rDefRowData.mnHeight) ||
2018         (IsHidden() != rDefRowData.IsHidden()) ||
2019         (IsUnsynced() != rDefRowData.IsUnsynced());
2020 }
2021 
WriteCellList(XclExpStream & rStrm)2022 void XclExpRow::WriteCellList( XclExpStream& rStrm )
2023 {
2024     DBG_ASSERT( mbEnabled || maCellList.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" );
2025     maCellList.Save( rStrm );
2026 }
2027 
Save(XclExpStream & rStrm)2028 void XclExpRow::Save( XclExpStream& rStrm )
2029 {
2030     if( mbEnabled )
2031         XclExpRecord::Save( rStrm );
2032 }
2033 
InsertCell(XclExpCellRef xCell,size_t nPos,bool bIsMergedBase)2034 void XclExpRow::InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase )
2035 {
2036     DBG_ASSERT( xCell.is(), "XclExpRow::InsertCell - missing cell" );
2037 
2038     /*  #109751# If we have a multi-line text in a merged cell, and the resulting
2039         row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED
2040         flag to be true to ensure Excel works correctly. */
2041     if( bIsMergedBase && xCell->IsMultiLineText() )
2042         ::set_flag( mnFlags, EXC_ROW_UNSYNCED );
2043 
2044     // try to merge with previous cell, insert the new cell if not successful
2045     XclExpCellRef xPrevCell = maCellList.GetRecord( nPos - 1 );
2046     if( xPrevCell.is() && xPrevCell->TryMerge( *xCell ) )
2047         xCell = xPrevCell;
2048     else
2049         maCellList.InsertRecord( xCell, nPos++ );
2050     // nPos points now to following cell
2051 
2052     // try to merge with following cell, remove it if successful
2053     XclExpCellRef xNextCell = maCellList.GetRecord( nPos );
2054     if( xNextCell.is() && xCell->TryMerge( *xNextCell ) )
2055         maCellList.RemoveRecord( nPos );
2056 }
2057 
WriteBody(XclExpStream & rStrm)2058 void XclExpRow::WriteBody( XclExpStream& rStrm )
2059 {
2060     rStrm   << mnXclRow
2061             << GetFirstUsedXclCol()
2062             << GetFirstFreeXclCol()
2063             << mnHeight
2064             << sal_uInt32( 0 )
2065             << mnFlags
2066             << mnXFIndex;
2067 }
2068 
SaveXml(XclExpXmlStream & rStrm)2069 void XclExpRow::SaveXml( XclExpXmlStream& rStrm )
2070 {
2071     if( !mbEnabled )
2072         return;
2073     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2074     bool haveFormat = ::get_flag( mnFlags, EXC_ROW_USEDEFXF );
2075     rWorksheet->startElement( XML_row,
2076             XML_r,              OString::valueOf( (sal_Int32) (mnXclRow+1) ).getStr(),
2077             // OOXTODO: XML_spans,          optional
2078             XML_s,              haveFormat ? lcl_GetStyleId( rStrm, mnXFIndex ).getStr() : NULL,
2079             XML_customFormat,   XclXmlUtils::ToPsz( haveFormat ),
2080             XML_ht,             OString::valueOf( (double) mnHeight / 20.0 ).getStr(),
2081             XML_hidden,         XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_HIDDEN ) ),
2082             XML_customHeight,   XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_UNSYNCED ) ),
2083             XML_outlineLevel,   OString::valueOf( (sal_Int32) mnOutlineLevel ).getStr(),
2084             XML_collapsed,      XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_COLLAPSED ) ),
2085             // OOXTODO: XML_thickTop,       bool
2086             // OOXTODO: XML_thickBot,       bool
2087             // OOXTODO: XML_ph,             bool
2088             FSEND );
2089     // OOXTODO: XML_extLst
2090     maCellList.SaveXml( rStrm );
2091     rWorksheet->endElement( XML_row );
2092 }
2093 
2094 // ----------------------------------------------------------------------------
2095 
XclExpRowBuffer(const XclExpRoot & rRoot)2096 XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot& rRoot ) :
2097     XclExpRoot( rRoot ),
2098     maOutlineBfr( rRoot ),
2099     maDimensions( rRoot ),
2100     mpLastUsedRow( 0 ),
2101     mnLastUsedXclRow( 0 )
2102 {
2103 }
2104 
AppendCell(XclExpCellRef xCell,bool bIsMergedBase)2105 void XclExpRowBuffer::AppendCell( XclExpCellRef xCell, bool bIsMergedBase )
2106 {
2107     DBG_ASSERT( xCell.is(), "XclExpRowBuffer::AppendCell - missing cell" );
2108     GetOrCreateRow( xCell->GetXclRow(), false ).AppendCell( xCell, bIsMergedBase );
2109 }
2110 
CreateRows(SCROW nFirstFreeScRow)2111 void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow )
2112 {
2113     if( nFirstFreeScRow > 0 )
2114         GetOrCreateRow( static_cast< sal_uInt16 >( nFirstFreeScRow - 1 ), true );
2115 }
2116 
Finalize(XclExpDefaultRowData & rDefRowData,const ScfUInt16Vec & rColXFIndexes)2117 void XclExpRowBuffer::Finalize( XclExpDefaultRowData& rDefRowData, const ScfUInt16Vec& rColXFIndexes )
2118 {
2119     size_t nPos, nSize;
2120 
2121     // *** Finalize all rows *** ----------------------------------------------
2122 
2123     GetProgressBar().ActivateFinalRowsSegment();
2124 
2125     // unused blank cell records will be removed
2126     for( nPos = 0, nSize = maRowList.GetSize(); nPos < nSize; ++nPos )
2127         maRowList.GetRecord( nPos )->Finalize( rColXFIndexes );
2128 
2129     // *** Default row format *** ---------------------------------------------
2130 
2131     typedef ::std::map< XclExpDefaultRowData, size_t > XclExpDefRowDataMap;
2132     XclExpDefRowDataMap aDefRowMap;
2133 
2134     // find default row format for rows beyond used area
2135     sal_uInt32 nDefaultXclRow = maRowList.IsEmpty() ? 0 : (maRowList.GetLastRecord()->GetXclRow() + 1);
2136     XclExpDefaultRowData aMaxDefData;
2137     size_t nMaxDefCount = 0;
2138     /*  #i30411# Files saved with SO7/OOo1.x with nonstandard default column
2139         formatting cause big Excel files, because all rows from row 1 to row
2140         32000 are exported. Now, if the used area goes exactly to row 32000,
2141         ignore all rows >32000.
2142         #i59220# Tolerance of +-128 rows for inserted/removed rows. */
2143     if( (nDefaultXclRow < 31872) || (nDefaultXclRow > 32128) )
2144     {
2145         sal_uInt16 nLastXclRow = static_cast< sal_uInt16 >( GetMaxPos().Row() );
2146         if( nDefaultXclRow <= nLastXclRow )
2147         {
2148             // create a dummy ROW record and fill aMaxDefData
2149             XclExpRowOutlineBuffer aOutlineBfr( GetRoot() );
2150             XclExpRow aRow( GetRoot(), nLastXclRow, aOutlineBfr, true );
2151             aMaxDefData = XclExpDefaultRowData( aRow );
2152             aDefRowMap[ aMaxDefData ] = nMaxDefCount =
2153                 static_cast< size_t >( nLastXclRow - nDefaultXclRow + 1 );
2154         }
2155     }
2156 
2157     // only look for default format in existing rows, if there are more than unused
2158     nSize = maRowList.GetSize();
2159     if( nMaxDefCount < nSize )
2160     {
2161         for( nPos = 0; nPos < nSize; ++nPos )
2162         {
2163             XclExpRowRef xRow = maRowList.GetRecord( nPos );
2164             /*  Collect formats of unused rows (rows without cells), which are able
2165                 to be defaulted (i.e. no explicit format or outline level). */
2166             if( xRow->IsDefaultable() )
2167             {
2168                 XclExpDefaultRowData aDefData( *xRow );
2169                 size_t& rnDefCount = aDefRowMap[ aDefData ];
2170                 ++rnDefCount;
2171                 if( rnDefCount > nMaxDefCount )
2172                 {
2173                     nMaxDefCount = rnDefCount;
2174                     aMaxDefData = aDefData;
2175                 }
2176             }
2177         }
2178     }
2179 
2180     // return the default row format to caller
2181     rDefRowData = aMaxDefData;
2182 
2183     // *** Disable unused ROW records, find used area *** ---------------------
2184 
2185     sal_uInt16 nFirstUsedXclCol = SAL_MAX_UINT16;
2186     sal_uInt16 nFirstFreeXclCol = 0;
2187     sal_uInt32 nFirstUsedXclRow = SAL_MAX_UINT32;
2188     sal_uInt32 nFirstFreeXclRow = 0;
2189 
2190     for( nPos = 0, nSize = maRowList.GetSize(); nPos < nSize; ++nPos )
2191     {
2192         XclExpRowRef xRow = maRowList.GetRecord( nPos );
2193 
2194         // disable unused rows
2195         xRow->DisableIfDefault( aMaxDefData );
2196 
2197         // find used column range
2198         if( !xRow->IsEmpty() )      // empty rows return (0...0) as used range
2199         {
2200             nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, xRow->GetFirstUsedXclCol() );
2201             nFirstFreeXclCol = ::std::max( nFirstFreeXclCol, xRow->GetFirstFreeXclCol() );
2202         }
2203 
2204         // find used row range
2205         if( xRow->IsEnabled() )
2206         {
2207             sal_uInt16 nXclRow = xRow->GetXclRow();
2208             nFirstUsedXclRow = ::std::min< sal_uInt32 >( nFirstUsedXclRow, nXclRow );
2209             nFirstFreeXclRow = ::std::max< sal_uInt32 >( nFirstFreeXclRow, nXclRow + 1 );
2210         }
2211     }
2212 
2213     // adjust start position, if there are no or only empty/disabled ROW records
2214     nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, nFirstFreeXclCol );
2215     nFirstUsedXclRow = ::std::min( nFirstUsedXclRow, nFirstFreeXclRow );
2216 
2217     // initialize the DIMENSIONS record
2218     maDimensions.SetDimensions(
2219         nFirstUsedXclCol, nFirstUsedXclRow, nFirstFreeXclCol, nFirstFreeXclRow );
2220 }
2221 
Save(XclExpStream & rStrm)2222 void XclExpRowBuffer::Save( XclExpStream& rStrm )
2223 {
2224     // DIMENSIONS record
2225     maDimensions.Save( rStrm );
2226 
2227     // save in blocks of 32 rows, each block contains first all ROWs, then all cells
2228     size_t nSize = maRowList.GetSize();
2229     size_t nBlockStart = 0;
2230     sal_uInt16 nStartXclRow = (nSize == 0) ? 0 : maRowList.GetRecord( 0 )->GetXclRow();
2231 
2232     while( nBlockStart < nSize )
2233     {
2234         // find end of row block
2235         size_t nBlockEnd = nBlockStart + 1;
2236         while( (nBlockEnd < nSize) && (maRowList.GetRecord( nBlockEnd )->GetXclRow() - nStartXclRow < EXC_ROW_ROWBLOCKSIZE) )
2237             ++nBlockEnd;
2238 
2239         // write the ROW records
2240         size_t nPos;
2241         for( nPos = nBlockStart; nPos < nBlockEnd; ++nPos )
2242             maRowList.GetRecord( nPos )->Save( rStrm );
2243 
2244         // write the cell records
2245         for( nPos = nBlockStart; nPos < nBlockEnd; ++nPos )
2246             maRowList.GetRecord( nPos )->WriteCellList( rStrm );
2247 
2248         nBlockStart = nBlockEnd;
2249         nStartXclRow += EXC_ROW_ROWBLOCKSIZE;
2250     }
2251 }
2252 
SaveXml(XclExpXmlStream & rStrm)2253 void XclExpRowBuffer::SaveXml( XclExpXmlStream& rStrm )
2254 {
2255     sal_Int32 nNonEmpty = 0;
2256 
2257     size_t nRows = maRowList.GetSize();
2258     for( size_t i = 0; i < nRows; ++i)
2259         if( maRowList.GetRecord( i )->IsEnabled() )
2260             ++nNonEmpty;
2261 
2262     if( nNonEmpty == 0 )
2263     {
2264         rStrm.GetCurrentStream()->singleElement( XML_sheetData, FSEND );
2265     }
2266     else
2267     {
2268         sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2269         rWorksheet->startElement( XML_sheetData, FSEND );
2270         maRowList.SaveXml( rStrm );
2271         rWorksheet->endElement( XML_sheetData );
2272     }
2273 }
2274 
GetDimensions()2275 XclExpDimensions* XclExpRowBuffer::GetDimensions()
2276 {
2277     return &maDimensions;
2278 }
2279 
GetOrCreateRow(sal_uInt16 nXclRow,bool bRowAlwaysEmpty)2280 XclExpRow& XclExpRowBuffer::GetOrCreateRow( sal_uInt16 nXclRow, bool bRowAlwaysEmpty )
2281 {
2282     if( !mpLastUsedRow || (mnLastUsedXclRow != nXclRow) )
2283     {
2284         // fill up missing ROW records
2285         // do not use sal_uInt16 for nFirstFreeXclRow, would cause loop in full sheets
2286         for( size_t nFirstFreeXclRow = maRowList.GetSize(); nFirstFreeXclRow <= nXclRow; ++nFirstFreeXclRow )
2287             maRowList.AppendNewRecord( new XclExpRow(
2288                 GetRoot(), static_cast< sal_uInt16 >( nFirstFreeXclRow ), maOutlineBfr, bRowAlwaysEmpty ) );
2289 
2290         mpLastUsedRow = maRowList.GetRecord( nXclRow ).get();
2291         mnLastUsedXclRow = nXclRow;
2292     }
2293     return *mpLastUsedRow;
2294 }
2295 
2296 // ============================================================================
2297 // Cell Table
2298 // ============================================================================
2299 
XclExpCellTable(const XclExpRoot & rRoot)2300 XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) :
2301     XclExpRoot( rRoot ),
2302     maColInfoBfr( rRoot ),
2303     maRowBfr( rRoot ),
2304     maArrayBfr( rRoot ),
2305     maShrfmlaBfr( rRoot ),
2306     maTableopBfr( rRoot ),
2307     mxDefrowheight( new XclExpDefrowheight ),
2308     mxGuts( new XclExpGuts( rRoot ) ),
2309     mxNoteList( new XclExpNoteList ),
2310     mxMergedcells( new XclExpMergedcells( rRoot ) ),
2311     mxHyperlinkList( new XclExpHyperlinkList ),
2312     mxDval( new XclExpDval( rRoot ) )
2313 {
2314     ScDocument& rDoc = GetDoc();
2315     SCTAB nScTab = GetCurrScTab();
2316     SvNumberFormatter& rFormatter = GetFormatter();
2317 
2318     // maximum sheet limits
2319     SCCOL nMaxScCol = GetMaxPos().Col();
2320     SCROW nMaxScRow = GetMaxPos().Row();
2321 
2322     // find used area (non-empty cells)
2323     SCCOL nLastUsedScCol;
2324     SCROW nLastUsedScRow;
2325     rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
2326 
2327     ScRange aUsedRange( 0, 0, nScTab, nLastUsedScCol, nLastUsedScRow, nScTab );
2328     GetAddressConverter().ValidateRange( aUsedRange, true );
2329     nLastUsedScCol = aUsedRange.aEnd.Col();
2330     nLastUsedScRow = aUsedRange.aEnd.Row();
2331 
2332     // first row without any set attributes (height/hidden/...)
2333     SCROW nFirstUnflaggedScRow = rDoc.GetLastFlaggedRow( nScTab ) + 1;
2334 
2335     // find range of outlines
2336     SCROW nFirstUngroupedScRow = 0;
2337     if( const ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable( nScTab ) )
2338     {
2339         SCCOLROW nScStartPos, nScEndPos;
2340         if( const ScOutlineArray* pRowArray = pOutlineTable->GetRowArray() )
2341         {
2342             pRowArray->GetRange( nScStartPos, nScEndPos );
2343             // +1 because open/close button is in next row in Excel, +1 for "end->first unused"
2344             nFirstUngroupedScRow = static_cast< SCROW >( nScEndPos + 2 );
2345         }
2346     }
2347 
2348     // column settings
2349     /*  #i30411# Files saved with SO7/OOo1.x with nonstandard default column
2350         formatting cause big Excel files, because all rows from row 1 to row
2351         32000 are exported. Now, if the used area goes exactly to row 32000,
2352         use this row as default and ignore all rows >32000.
2353         #i59220# Tolerance of +-128 rows for inserted/removed rows. */
2354     if( (31871 <= nLastUsedScRow) && (nLastUsedScRow <= 32127) && (nFirstUnflaggedScRow < nLastUsedScRow) && (nFirstUngroupedScRow <= nLastUsedScRow) )
2355         nMaxScRow = nLastUsedScRow;
2356     maColInfoBfr.Initialize( nMaxScRow );
2357 
2358     // range for cell iterator
2359     SCCOL nLastIterScCol = nMaxScCol;
2360     SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow + 128, nMaxScRow );
2361 	// modified for 119707 by zhanglu
2362 
2363 	SCCOL rEndColAtt = 0;
2364 	SCROW rEndRowAtt = 0;
2365 	rDoc.GetLastAttrCell( nScTab, rEndColAtt,rEndRowAtt ); // To get the real last cell's row number, which has visual data or attribute.
2366 	if( rEndRowAtt > nLastIterScRow )
2367 		nLastIterScRow = rEndRowAtt;
2368 
2369 	if (nLastIterScRow > nMaxScRow)
2370 		nLastIterScRow = nMaxScRow;
2371 
2372 	// modified for 119707 end
2373     ScUsedAreaIterator aIt( &rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow );
2374 
2375     // activate the correct segment and sub segment at the progress bar
2376     GetProgressBar().ActivateCreateRowsSegment();
2377 
2378     for( bool bIt = aIt.GetNext(); bIt; bIt = aIt.GetNext() )
2379     {
2380         SCCOL nScCol = aIt.GetStartCol();
2381         SCROW nScRow = aIt.GetRow();
2382         SCCOL nLastScCol = aIt.GetEndCol();
2383         ScAddress aScPos( nScCol, nScRow, nScTab );
2384 
2385         XclAddress aXclPos( static_cast< sal_uInt16 >( nScCol ), static_cast< sal_uInt16 >( nScRow ) );
2386         sal_uInt16 nLastXclCol = static_cast< sal_uInt16 >( nLastScCol );
2387 
2388         const ScBaseCell* pScCell = aIt.GetCell();
2389         XclExpCellRef xCell;
2390 
2391         const ScPatternAttr* pPattern = aIt.GetPattern();
2392 
2393         // handle overlapped merged cells before creating the cell record
2394         sal_uInt32 nMergeBaseXFId = EXC_XFID_NOTFOUND;
2395         bool bIsMergedBase = false;
2396         if( pPattern )
2397         {
2398             const SfxItemSet& rItemSet = pPattern->GetItemSet();
2399             // base cell in a merged range
2400             const ScMergeAttr& rMergeItem = GETITEM( rItemSet, ScMergeAttr, ATTR_MERGE );
2401             bIsMergedBase = rMergeItem.IsMerged();
2402             /*  overlapped cell in a merged range; in Excel all merged cells
2403                 must contain same XF index, for correct border */
2404             const ScMergeFlagAttr& rMergeFlagItem = GETITEM( rItemSet, ScMergeFlagAttr, ATTR_MERGE_FLAG );
2405             if( rMergeFlagItem.IsOverlapped() )
2406                 nMergeBaseXFId = mxMergedcells->GetBaseXFId( aScPos );
2407         }
2408 
2409         String aAddNoteText;    // additional text to be appended to a note
2410 
2411         CellType eCellType = pScCell ? pScCell->GetCellType() : CELLTYPE_NONE;
2412         switch( eCellType )
2413         {
2414             case CELLTYPE_VALUE:
2415             {
2416                 double fValue = static_cast< const ScValueCell* >( pScCell )->GetValue();
2417 
2418                 // try to create a Boolean cell
2419                 if( pPattern && ((fValue == 0.0) || (fValue == 1.0)) )
2420                 {
2421                     sal_uLong nScNumFmt = GETITEMVALUE( pPattern->GetItemSet(), SfxUInt32Item, ATTR_VALUE_FORMAT, sal_uLong );
2422                     if( rFormatter.GetType( nScNumFmt ) == NUMBERFORMAT_LOGICAL )
2423                         xCell.reset( new XclExpBooleanCell(
2424                             GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue != 0.0 ) );
2425                 }
2426 
2427                 // try to create an RK value (compressed floating-point number)
2428                 sal_Int32 nRkValue;
2429                 if( !xCell && XclTools::GetRKFromDouble( nRkValue, fValue ) )
2430                     xCell.reset( new XclExpRkCell(
2431                         GetRoot(), aXclPos, pPattern, nMergeBaseXFId, nRkValue ) );
2432 
2433                 // else: simple floating-point number cell
2434                 if( !xCell )
2435                     xCell.reset( new XclExpNumberCell(
2436                         GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue ) );
2437             }
2438             break;
2439 
2440             case CELLTYPE_STRING:
2441             {
2442                 const ScStringCell& rScStrCell = *static_cast< const ScStringCell* >( pScCell );
2443                 xCell.reset( new XclExpLabelCell(
2444                     GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScStrCell ) );
2445             }
2446             break;
2447 
2448             case CELLTYPE_EDIT:
2449             {
2450                 const ScEditCell& rScEditCell = *static_cast< const ScEditCell* >( pScCell );
2451                 XclExpHyperlinkHelper aLinkHelper( GetRoot(), aScPos );
2452                 xCell.reset( new XclExpLabelCell(
2453                     GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScEditCell, aLinkHelper ) );
2454 
2455                 // add a single created HLINK record to the record list
2456                 if( aLinkHelper.HasLinkRecord() )
2457                     mxHyperlinkList->AppendRecord( aLinkHelper.GetLinkRecord() );
2458                 // add list of multiple URLs to the additional cell note text
2459                 if( aLinkHelper.HasMultipleUrls() )
2460                     ScGlobal::AddToken( aAddNoteText, aLinkHelper.GetUrlList(), '\n', 2 );
2461             }
2462             break;
2463 
2464             case CELLTYPE_FORMULA:
2465             {
2466                 const ScFormulaCell& rScFmlaCell = *static_cast< const ScFormulaCell* >( pScCell );
2467                 xCell.reset( new XclExpFormulaCell(
2468                     GetRoot(), aXclPos, pPattern, nMergeBaseXFId,
2469                     rScFmlaCell, maArrayBfr, maShrfmlaBfr, maTableopBfr ) );
2470             }
2471             break;
2472 
2473             default:
2474                 DBG_ERRORFILE( "XclExpCellTable::XclExpCellTable - unknown cell type" );
2475                 // run-through!
2476             case CELLTYPE_NONE:
2477             case CELLTYPE_NOTE:
2478             {
2479                 xCell.reset( new XclExpBlankCell(
2480                     GetRoot(), aXclPos, nLastXclCol, pPattern, nMergeBaseXFId ) );
2481             }
2482             break;
2483         }
2484 
2485         // insert the cell into the current row
2486         if( xCell.is() )
2487             maRowBfr.AppendCell( xCell, bIsMergedBase );
2488 
2489         // notes
2490         const ScPostIt* pScNote = pScCell ? pScCell->GetNote() : 0;
2491         if( pScNote || (aAddNoteText.Len() > 0) )
2492             mxNoteList->AppendNewRecord( new XclExpNote( GetRoot(), aScPos, pScNote, aAddNoteText ) );
2493 
2494         // other sheet contents
2495         if( pPattern )
2496         {
2497             const SfxItemSet& rItemSet = pPattern->GetItemSet();
2498 
2499             // base cell in a merged range
2500             if( bIsMergedBase )
2501             {
2502                 const ScMergeAttr& rMergeItem = GETITEM( rItemSet, ScMergeAttr, ATTR_MERGE );
2503                 ScRange aScRange( aScPos );
2504                 aScRange.aEnd.IncCol( rMergeItem.GetColMerge() - 1 );
2505                 aScRange.aEnd.IncRow( rMergeItem.GetRowMerge() - 1 );
2506                 sal_uInt32 nXFId = xCell.is() ? xCell->GetFirstXFId() : EXC_XFID_NOTFOUND;
2507                 // #120156# blank cells merged vertically may occur repeatedly
2508                 DBG_ASSERT( (aScRange.aStart.Col() == aScRange.aEnd.Col()) || (nScCol == nLastScCol),
2509                     "XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" );
2510                 for( SCCOL nIndex = nScCol; nIndex <= nLastScCol; ++nIndex )
2511                 {
2512                     mxMergedcells->AppendRange( aScRange, nXFId );
2513                     aScRange.aStart.IncCol();
2514                     aScRange.aEnd.IncCol();
2515                 }
2516             }
2517 
2518             // data validation
2519             if( ScfTools::CheckItem( rItemSet, ATTR_VALIDDATA, false ) )
2520             {
2521                 sal_uLong nScHandle = GETITEMVALUE( rItemSet, SfxUInt32Item, ATTR_VALIDDATA, sal_uLong );
2522                 ScRange aScRange( aScPos );
2523                 aScRange.aEnd.SetCol( nLastScCol );
2524                 mxDval->InsertCellRange( aScRange, nScHandle );
2525             }
2526         }
2527     }
2528 
2529     // create missing row settings for rows anyhow flagged or with outlines
2530     maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, nFirstUngroupedScRow ) );
2531 }
2532 
Finalize()2533 void XclExpCellTable::Finalize()
2534 {
2535     // Finalize multiple operations.
2536     maTableopBfr.Finalize();
2537 
2538     /*  Finalize column buffer. This calculates column default XF indexes from
2539         the XF identifiers and fills a vector with these XF indexes. */
2540     ScfUInt16Vec aColXFIndexes;
2541     maColInfoBfr.Finalize( aColXFIndexes );
2542 
2543     /*  Finalize row buffer. This calculates all cell XF indexes from the XF
2544         identifiers. Then the XF index vector aColXFIndexes (filled above) is
2545         used to calculate the row default formats. With this, all unneeded blank
2546         cell records (equal to row default or column default) will be removed.
2547         The function returns the (most used) default row format in aDefRowData. */
2548     XclExpDefaultRowData aDefRowData;
2549     maRowBfr.Finalize( aDefRowData, aColXFIndexes );
2550 
2551     // Initialize the DEFROWHEIGHT record.
2552     mxDefrowheight->SetDefaultData( aDefRowData );
2553 }
2554 
CreateRecord(sal_uInt16 nRecId) const2555 XclExpRecordRef XclExpCellTable::CreateRecord( sal_uInt16 nRecId ) const
2556 {
2557     XclExpRecordRef xRec;
2558     switch( nRecId )
2559     {
2560         case EXC_ID3_DIMENSIONS:    xRec.reset( new XclExpDelegatingRecord( const_cast<XclExpRowBuffer*>(&maRowBfr)->GetDimensions() ) );   break;
2561         case EXC_ID2_DEFROWHEIGHT:  xRec = mxDefrowheight;  break;
2562         case EXC_ID_GUTS:           xRec = mxGuts;          break;
2563         case EXC_ID_NOTE:           xRec = mxNoteList;      break;
2564         case EXC_ID_MERGEDCELLS:    xRec = mxMergedcells;   break;
2565         case EXC_ID_HLINK:          xRec = mxHyperlinkList; break;
2566         case EXC_ID_DVAL:           xRec = mxDval;          break;
2567         default:    DBG_ERRORFILE( "XclExpCellTable::CreateRecord - unknown record id" );
2568     }
2569     return xRec;
2570 }
2571 
Save(XclExpStream & rStrm)2572 void XclExpCellTable::Save( XclExpStream& rStrm )
2573 {
2574     // DEFCOLWIDTH and COLINFOs
2575     maColInfoBfr.Save( rStrm );
2576     // ROWs and cell records
2577     maRowBfr.Save( rStrm );
2578 }
2579 
SaveXml(XclExpXmlStream & rStrm)2580 void XclExpCellTable::SaveXml( XclExpXmlStream& rStrm )
2581 {
2582     maColInfoBfr.SaveXml( rStrm );
2583     maRowBfr.SaveXml( rStrm );
2584 }
2585 
2586 // ============================================================================
2587 
2588