Update ooo320-m1
[ooovba.git] / sc / source / filter / excel / xetable.cxx
blob2816dafbe263d1693fca4be5c92c2a97274a6299
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: xetable.cxx,v $
10 * $Revision: 1.18.126.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
33 #include "xetable.hxx"
35 #include <map>
36 #include <com/sun/star/i18n/ScriptType.hpp>
37 #include "scitems.hxx"
38 #include <svtools/intitem.hxx>
39 #include "document.hxx"
40 #include "dociter.hxx"
41 #include "olinetab.hxx"
42 #include "cell.hxx"
43 #include "patattr.hxx"
44 #include "attrib.hxx"
45 #include "xehelper.hxx"
46 #include "xecontent.hxx"
47 #include "xeescher.hxx"
49 #include <oox/core/tokens.hxx>
51 using ::rtl::OString;
52 using ::rtl::OUString;
53 using ::rtl::OUStringBuffer;
55 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
57 // ============================================================================
58 // Helper records for cell records
59 // ============================================================================
61 XclExpStringRec::XclExpStringRec( const XclExpRoot& rRoot, const String& rResult ) :
62 XclExpRecord( EXC_ID3_STRING ),
63 mxResult( XclExpStringHelper::CreateString( rRoot, rResult ) )
65 DBG_ASSERT( (rRoot.GetBiff() <= EXC_BIFF5) || (mxResult->Len() > 0),
66 "XclExpStringRec::XclExpStringRec - empty result not allowed in BIFF8+" );
67 SetRecSize( mxResult->GetSize() );
70 void XclExpStringRec::WriteBody( XclExpStream& rStrm )
72 rStrm << *mxResult;
75 // Additional records for special formula ranges ==============================
77 XclExpRangeFmlaBase::XclExpRangeFmlaBase(
78 sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScAddress& rScPos ) :
79 XclExpRecord( nRecId, nRecSize ),
80 maXclRange( ScAddress::UNINITIALIZED ),
81 maBaseXclPos( ScAddress::UNINITIALIZED )
83 maBaseXclPos.Set( static_cast< sal_uInt16 >( rScPos.Col() ), static_cast< sal_uInt16 >( rScPos.Row() ) );
84 maXclRange.maFirst = maXclRange.maLast = maBaseXclPos;
87 XclExpRangeFmlaBase::XclExpRangeFmlaBase(
88 sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScRange& rScRange ) :
89 XclExpRecord( nRecId, nRecSize ),
90 maXclRange( ScAddress::UNINITIALIZED ),
91 maBaseXclPos( ScAddress::UNINITIALIZED )
93 maXclRange.Set(
94 static_cast< sal_uInt16 >( rScRange.aStart.Col() ),
95 static_cast< sal_uInt16 >( rScRange.aStart.Row() ),
96 static_cast< sal_uInt16 >( rScRange.aEnd.Col() ),
97 static_cast< sal_uInt16 >( rScRange.aEnd.Row() ) );
98 maBaseXclPos = maXclRange.maFirst;
101 bool XclExpRangeFmlaBase::IsBasePos( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const
103 return (maBaseXclPos.mnCol == nXclCol) && (maBaseXclPos.mnRow == nXclRow);
106 void XclExpRangeFmlaBase::Extend( const ScAddress& rScPos )
108 sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
109 sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() );
110 maXclRange.maFirst.mnCol = ::std::min( maXclRange.maFirst.mnCol, nXclCol );
111 maXclRange.maFirst.mnRow = ::std::min( maXclRange.maFirst.mnRow, nXclRow );
112 maXclRange.maLast.mnCol = ::std::max( maXclRange.maLast.mnCol, nXclCol );
113 maXclRange.maLast.mnRow = ::std::max( maXclRange.maLast.mnRow, nXclRow );
116 void XclExpRangeFmlaBase::WriteRangeAddress( XclExpStream& rStrm ) const
118 maXclRange.Write( rStrm, false );
121 // Array formulas =============================================================
123 XclExpArray::XclExpArray( XclTokenArrayRef xTokArr, const ScRange& rScRange ) :
124 XclExpRangeFmlaBase( EXC_ID3_ARRAY, 14 + xTokArr->GetSize(), rScRange ),
125 mxTokArr( xTokArr )
129 XclTokenArrayRef XclExpArray::CreateCellTokenArray( const XclExpRoot& rRoot ) const
131 return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
134 bool XclExpArray::IsVolatile() const
136 return mxTokArr->IsVolatile();
139 void XclExpArray::WriteBody( XclExpStream& rStrm )
141 WriteRangeAddress( rStrm );
142 sal_uInt16 nFlags = EXC_ARRAY_DEFAULTFLAGS;
143 ::set_flag( nFlags, EXC_ARRAY_RECALC_ALWAYS, IsVolatile() );
144 rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
147 // ----------------------------------------------------------------------------
149 XclExpArrayBuffer::XclExpArrayBuffer( const XclExpRoot& rRoot ) :
150 XclExpRoot( rRoot )
154 XclExpArrayRef XclExpArrayBuffer::CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange )
156 const ScAddress& rScPos = rScRange.aStart;
157 XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_MATRIX, rScTokArr, &rScPos );
159 DBG_ASSERT( maRecMap.find( rScPos ) == maRecMap.end(), "XclExpArrayBuffer::CreateArray - array exists already" );
160 XclExpArrayRef& rxRec = maRecMap[ rScPos ];
161 rxRec.reset( new XclExpArray( xTokArr, rScRange ) );
162 return rxRec;
165 XclExpArrayRef XclExpArrayBuffer::FindArray( const ScTokenArray& rScTokArr ) const
167 XclExpArrayRef xRec;
168 // try to extract a matrix reference token
169 if( rScTokArr.GetLen() == 1 )
171 const formula::FormulaToken* pToken = rScTokArr.GetArray()[ 0 ];
172 if( pToken && (pToken->GetOpCode() == ocMatRef) )
174 const ScSingleRefData& rRef = static_cast<const ScToken*>(pToken)->GetSingleRef();
175 ScAddress aBasePos( rRef.nCol, rRef.nRow, GetCurrScTab() );
176 XclExpArrayMap::const_iterator aIt = maRecMap.find( aBasePos );
177 if( aIt != maRecMap.end() )
178 xRec = aIt->second;
181 return xRec;
184 // Shared formulas ============================================================
186 XclExpShrfmla::XclExpShrfmla( XclTokenArrayRef xTokArr, const ScAddress& rScPos ) :
187 XclExpRangeFmlaBase( EXC_ID_SHRFMLA, 10 + xTokArr->GetSize(), rScPos ),
188 mxTokArr( xTokArr ),
189 mnUsedCount( 1 )
193 void XclExpShrfmla::ExtendRange( const ScAddress& rScPos )
195 Extend( rScPos );
196 ++mnUsedCount;
199 XclTokenArrayRef XclExpShrfmla::CreateCellTokenArray( const XclExpRoot& rRoot ) const
201 return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
204 bool XclExpShrfmla::IsVolatile() const
206 return mxTokArr->IsVolatile();
209 void XclExpShrfmla::WriteBody( XclExpStream& rStrm )
211 WriteRangeAddress( rStrm );
212 rStrm << sal_uInt8( 0 ) << mnUsedCount << *mxTokArr;
215 // ----------------------------------------------------------------------------
217 XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) :
218 XclExpRoot( rRoot )
222 XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
223 const ScTokenArray& rScTokArr, const ScAddress& rScPos )
225 XclExpShrfmlaRef xRec;
226 if( const ScTokenArray* pShrdScTokArr = XclTokenArrayHelper::GetSharedFormula( GetRoot(), rScTokArr ) )
228 XclExpShrfmlaMap::iterator aIt = maRecMap.find( pShrdScTokArr );
229 if( aIt == maRecMap.end() )
231 // create a new record
232 XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_SHARED, *pShrdScTokArr, &rScPos );
233 xRec.reset( new XclExpShrfmla( xTokArr, rScPos ) );
234 maRecMap[ pShrdScTokArr ] = xRec;
236 else
238 // extend existing record
239 DBG_ASSERT( aIt->second, "XclExpShrfmlaBuffer::CreateOrExtendShrfmla - missing record" );
240 xRec = aIt->second;
241 xRec->ExtendRange( rScPos );
244 return xRec;
247 // Multiple operations ========================================================
249 XclExpTableop::XclExpTableop( const ScAddress& rScPos,
250 const XclMultipleOpRefs& rRefs, sal_uInt8 nScMode ) :
251 XclExpRangeFmlaBase( EXC_ID3_TABLEOP, 16, rScPos ),
252 mnLastAppXclCol( static_cast< sal_uInt16 >( rScPos.Col() ) ),
253 mnColInpXclCol( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Col() ) ),
254 mnColInpXclRow( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Row() ) ),
255 mnRowInpXclCol( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Col() ) ),
256 mnRowInpXclRow( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Row() ) ),
257 mnScMode( nScMode ),
258 mbValid( false )
262 bool XclExpTableop::TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
264 sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
265 sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() );
267 bool bOk = IsAppendable( nXclCol, nXclRow );
268 if( bOk )
270 SCCOL nFirstScCol = static_cast< SCCOL >( maXclRange.maFirst.mnCol );
271 SCROW nFirstScRow = static_cast< SCROW >( maXclRange.maFirst.mnRow );
272 SCCOL nColInpScCol = static_cast< SCCOL >( mnColInpXclCol );
273 SCROW nColInpScRow = static_cast< SCROW >( mnColInpXclRow );
274 SCCOL nRowInpScCol = static_cast< SCCOL >( mnRowInpXclCol );
275 SCROW nRowInpScRow = static_cast< SCROW >( mnRowInpXclRow );
277 bOk = ((mnScMode == 2) == rRefs.mbDblRefMode) &&
278 (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
279 (nColInpScCol == rRefs.maColFirstScPos.Col()) &&
280 (nColInpScRow == rRefs.maColFirstScPos.Row()) &&
281 (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
282 (rScPos.Tab() == rRefs.maColRelScPos.Tab());
284 if( bOk ) switch( mnScMode )
286 case 0:
287 bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
288 (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
289 (nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
290 (rScPos.Row() == rRefs.maColRelScPos.Row());
291 break;
292 case 1:
293 bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
294 (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
295 (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
296 (nFirstScRow == rRefs.maColRelScPos.Row() + 1);
297 break;
298 case 2:
299 bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
300 (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
301 (nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
302 (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
303 (nRowInpScCol == rRefs.maRowFirstScPos.Col()) &&
304 (nRowInpScRow == rRefs.maRowFirstScPos.Row()) &&
305 (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
306 (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
307 (nFirstScRow == rRefs.maRowRelScPos.Row() + 1) &&
308 (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
309 break;
310 default:
311 bOk = false;
314 if( bOk )
316 // extend the cell range
317 DBG_ASSERT( IsAppendable( nXclCol, nXclRow ), "XclExpTableop::TryExtend - wrong cell address" );
318 Extend( rScPos );
319 mnLastAppXclCol = nXclCol;
323 return bOk;
326 void XclExpTableop::Finalize()
328 // is the range complete? (last appended cell is in last column)
329 mbValid = maXclRange.maLast.mnCol == mnLastAppXclCol;
330 // if last row is incomplete, try to shorten the used range
331 if( !mbValid && (maXclRange.maFirst.mnRow < maXclRange.maLast.mnRow) )
333 --maXclRange.maLast.mnRow;
334 mbValid = true;
337 // check if referred cells are outside of own range
338 if( mbValid ) switch( mnScMode )
340 case 0:
341 mbValid = (mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
342 (mnColInpXclRow < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
343 break;
344 case 1:
345 mbValid = (mnColInpXclCol < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
346 (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
347 break;
348 case 2:
349 mbValid = ((mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
350 (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow)) &&
351 ((mnRowInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnRowInpXclCol > maXclRange.maLast.mnCol) ||
352 (mnRowInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnRowInpXclRow > maXclRange.maLast.mnRow));
353 break;
357 XclTokenArrayRef XclExpTableop::CreateCellTokenArray( const XclExpRoot& rRoot ) const
359 XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
360 return mbValid ?
361 rFmlaComp.CreateSpecialRefFormula( EXC_TOKID_TBL, maBaseXclPos ) :
362 rFmlaComp.CreateErrorFormula( EXC_ERR_NA );
365 bool XclExpTableop::IsVolatile() const
367 return true;
370 void XclExpTableop::Save( XclExpStream& rStrm )
372 if( mbValid )
373 XclExpRangeFmlaBase::Save( rStrm );
376 bool XclExpTableop::IsAppendable( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const
378 return ((nXclCol == mnLastAppXclCol + 1) && (nXclRow == maXclRange.maFirst.mnRow)) ||
379 ((nXclCol == mnLastAppXclCol + 1) && (nXclCol <= maXclRange.maLast.mnCol) && (nXclRow == maXclRange.maLast.mnRow)) ||
380 ((mnLastAppXclCol == maXclRange.maLast.mnCol) && (nXclCol == maXclRange.maFirst.mnCol) && (nXclRow == maXclRange.maLast.mnRow + 1));
383 void XclExpTableop::WriteBody( XclExpStream& rStrm )
385 sal_uInt16 nFlags = EXC_TABLEOP_DEFAULTFLAGS;
386 ::set_flag( nFlags, EXC_TABLEOP_RECALC_ALWAYS, IsVolatile() );
387 switch( mnScMode )
389 case 1: ::set_flag( nFlags, EXC_TABLEOP_ROW ); break;
390 case 2: ::set_flag( nFlags, EXC_TABLEOP_BOTH ); break;
393 WriteRangeAddress( rStrm );
394 rStrm << nFlags;
395 if( mnScMode == 2 )
396 rStrm << mnRowInpXclRow << mnRowInpXclCol << mnColInpXclRow << mnColInpXclCol;
397 else
398 rStrm << mnColInpXclRow << mnColInpXclCol << sal_uInt32( 0 );
401 // ----------------------------------------------------------------------------
403 XclExpTableopBuffer::XclExpTableopBuffer( const XclExpRoot& rRoot ) :
404 XclExpRoot( rRoot )
408 XclExpTableopRef XclExpTableopBuffer::CreateOrExtendTableop(
409 const ScTokenArray& rScTokArr, const ScAddress& rScPos )
411 XclExpTableopRef xRec;
413 // try to extract cell references of a multiple operations formula
414 XclMultipleOpRefs aRefs;
415 if( XclTokenArrayHelper::GetMultipleOpRefs( aRefs, rScTokArr ) )
417 // try to find an existing TABLEOP record for this cell position
418 for( size_t nPos = 0, nSize = maTableopList.GetSize(); !xRec && (nPos < nSize); ++nPos )
420 XclExpTableopRef xTempRec = maTableopList.GetRecord( nPos );
421 if( xTempRec->TryExtend( rScPos, aRefs ) )
422 xRec = xTempRec;
425 // no record found, or found record not extensible
426 if( !xRec )
427 xRec = TryCreate( rScPos, aRefs );
430 return xRec;
433 void XclExpTableopBuffer::Finalize()
435 for( size_t nPos = 0, nSize = maTableopList.GetSize(); nPos < nSize; ++nPos )
436 maTableopList.GetRecord( nPos )->Finalize();
439 XclExpTableopRef XclExpTableopBuffer::TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
441 sal_uInt8 nScMode = 0;
442 bool bOk = (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
443 (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
444 (rScPos.Tab() == rRefs.maColRelScPos.Tab());
446 if( bOk )
448 if( rRefs.mbDblRefMode )
450 nScMode = 2;
451 bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
452 (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
453 (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
454 (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
455 (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
456 (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
457 (rScPos.Row() == rRefs.maRowRelScPos.Row() + 1) &&
458 (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
460 else if( (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
461 (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
462 (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
463 (rScPos.Row() == rRefs.maColRelScPos.Row()) )
465 nScMode = 0;
467 else if( (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
468 (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
469 (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
470 (rScPos.Row() == rRefs.maColRelScPos.Row() + 1) )
472 nScMode = 1;
474 else
476 bOk = false;
480 XclExpTableopRef xRec;
481 if( bOk )
483 xRec.reset( new XclExpTableop( rScPos, rRefs, nScMode ) );
484 maTableopList.AppendRecord( xRec );
487 return xRec;
490 // ============================================================================
491 // Cell records
492 // ============================================================================
494 XclExpCellBase::XclExpCellBase(
495 sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos ) :
496 XclExpRecord( nRecId, nContSize + 4 ),
497 maXclPos( rXclPos )
501 bool XclExpCellBase::IsMultiLineText() const
503 return false;
506 bool XclExpCellBase::TryMerge( const XclExpCellBase& /*rCell*/ )
508 return false;
511 void XclExpCellBase::GetBlankXFIndexes( ScfUInt16Vec& /*rXFIndexes*/ ) const
513 // default: do nothing
516 void XclExpCellBase::RemoveUnusedBlankCells( const ScfUInt16Vec& /*rXFIndexes*/ )
518 // default: do nothing
521 // Single cell records ========================================================
523 XclExpSingleCellBase::XclExpSingleCellBase(
524 sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos, sal_uInt32 nXFId ) :
525 XclExpCellBase( nRecId, 2, rXclPos ),
526 maXFId( nXFId ),
527 mnContSize( nContSize )
531 XclExpSingleCellBase::XclExpSingleCellBase( const XclExpRoot& rRoot,
532 sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos,
533 const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForcedXFId ) :
534 XclExpCellBase( nRecId, 2, rXclPos ),
535 maXFId( nForcedXFId ),
536 mnContSize( nContSize )
538 if( GetXFId() == EXC_XFID_NOTFOUND )
539 SetXFId( rRoot.GetXFBuffer().Insert( pPattern, nScript ) );
542 sal_uInt16 XclExpSingleCellBase::GetLastXclCol() const
544 return GetXclCol();
547 sal_uInt32 XclExpSingleCellBase::GetFirstXFId() const
549 return GetXFId();
552 bool XclExpSingleCellBase::IsEmpty() const
554 return false;
557 void XclExpSingleCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
559 maXFId.ConvertXFIndex( rRoot );
562 void XclExpSingleCellBase::Save( XclExpStream& rStrm )
564 DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
565 AddRecSize( mnContSize );
566 XclExpCellBase::Save( rStrm );
569 void XclExpSingleCellBase::WriteBody( XclExpStream& rStrm )
571 rStrm << GetXclRow() << GetXclCol() << maXFId.mnXFIndex;
572 WriteContents( rStrm );
575 // ----------------------------------------------------------------------------
577 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpNumberCell, 256, 256 )
579 XclExpNumberCell::XclExpNumberCell(
580 const XclExpRoot& rRoot, const XclAddress& rXclPos,
581 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, double fValue ) :
582 // #i41210# always use latin script for number cells - may look wrong for special number formats...
583 XclExpSingleCellBase( rRoot, EXC_ID3_NUMBER, 8, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
584 mfValue( fValue )
588 static OString lcl_GetStyleId( XclExpXmlStream& rStrm, sal_uInt32 nXFIndex )
590 return OString::valueOf( rStrm.GetRoot().GetXFBuffer()
591 .GetXmlCellIndex( nXFIndex ) );
594 static OString lcl_GetStyleId( XclExpXmlStream& rStrm, const XclExpCellBase& rCell )
596 sal_uInt32 nXFId = rCell.GetFirstXFId();
597 sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( nXFId );
598 return lcl_GetStyleId( rStrm, nXFIndex );
601 void XclExpNumberCell::SaveXml( XclExpXmlStream& rStrm )
603 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
604 rWorksheet->startElement( XML_c,
605 XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
606 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
607 XML_t, "n",
608 // OOXTODO: XML_cm, XML_vm, XML_ph
609 FSEND );
610 rWorksheet->startElement( XML_v, FSEND );
611 rWorksheet->write( mfValue );
612 rWorksheet->endElement( XML_v );
613 rWorksheet->endElement( XML_c );
616 void XclExpNumberCell::WriteContents( XclExpStream& rStrm )
618 rStrm << mfValue;
621 // ----------------------------------------------------------------------------
623 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpBooleanCell, 256, 256 )
625 XclExpBooleanCell::XclExpBooleanCell(
626 const XclExpRoot rRoot, const XclAddress& rXclPos,
627 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, bool bValue ) :
628 // #i41210# always use latin script for boolean cells
629 XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
630 mbValue( bValue )
634 void XclExpBooleanCell::SaveXml( XclExpXmlStream& rStrm )
636 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
637 rWorksheet->startElement( XML_c,
638 XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
639 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
640 XML_t, "b",
641 // OOXTODO: XML_cm, XML_vm, XML_ph
642 FSEND );
643 rWorksheet->startElement( XML_v, FSEND );
644 rWorksheet->write( mbValue ? "1" : "0" );
645 rWorksheet->endElement( XML_v );
646 rWorksheet->endElement( XML_c );
649 void XclExpBooleanCell::WriteContents( XclExpStream& rStrm )
651 rStrm << sal_uInt16( mbValue ? 1 : 0 ) << EXC_BOOLERR_BOOL;
654 // ----------------------------------------------------------------------------
656 //UNUSED2009-05 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpErrorCell, 256, 256 )
657 //UNUSED2009-05
658 //UNUSED2009-05 XclExpErrorCell::XclExpErrorCell(
659 //UNUSED2009-05 const XclExpRoot rRoot, const XclAddress& rXclPos,
660 //UNUSED2009-05 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_uInt8 nErrCode ) :
661 //UNUSED2009-05 // #i41210# always use latin script for error cells
662 //UNUSED2009-05 XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
663 //UNUSED2009-05 mnErrCode( nErrCode )
664 //UNUSED2009-05 {
665 //UNUSED2009-05 }
666 //UNUSED2009-05
667 //UNUSED2009-05 void XclExpErrorCell::SaveXml( XclExpXmlStream& rStrm )
668 //UNUSED2009-05 {
669 //UNUSED2009-05 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
670 //UNUSED2009-05 rWorksheet->startElement( XML_c,
671 //UNUSED2009-05 XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
672 //UNUSED2009-05 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
673 //UNUSED2009-05 XML_t, "e",
674 //UNUSED2009-05 // OOXTODO: XML_cm, XML_vm, XML_ph
675 //UNUSED2009-05 FSEND );
676 //UNUSED2009-05 rWorksheet->startElement( XML_v, FSEND );
677 //UNUSED2009-05 rWorksheet->write( (sal_Int32) mnErrCode );
678 //UNUSED2009-05 rWorksheet->endElement( XML_v );
679 //UNUSED2009-05 rWorksheet->endElement( XML_c );
680 //UNUSED2009-05 }
681 //UNUSED2009-05
682 //UNUSED2009-05 void XclExpErrorCell::WriteContents( XclExpStream& rStrm )
683 //UNUSED2009-05 {
684 //UNUSED2009-05 rStrm << mnErrCode << EXC_BOOLERR_ERROR;
685 //UNUSED2009-05 }
687 // ----------------------------------------------------------------------------
689 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpLabelCell, 256, 256 )
691 XclExpLabelCell::XclExpLabelCell(
692 const XclExpRoot& rRoot, const XclAddress& rXclPos,
693 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, const ScStringCell& rCell ) :
694 XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
696 sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
697 XclExpStringRef xText = XclExpStringHelper::CreateCellString( rRoot, rCell, pPattern, EXC_STR_DEFAULT, nMaxLen );
698 Init( rRoot, pPattern, xText );
701 XclExpLabelCell::XclExpLabelCell(
702 const XclExpRoot& rRoot, const XclAddress& rXclPos,
703 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
704 const ScEditCell& rCell, XclExpHyperlinkHelper& rLinkHelper ) :
705 XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
707 sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
708 XclExpStringRef xText = XclExpStringHelper::CreateCellString( rRoot, rCell, pPattern, rLinkHelper, EXC_STR_DEFAULT, nMaxLen );
709 Init( rRoot, pPattern, xText );
712 bool XclExpLabelCell::IsMultiLineText() const
714 return mbLineBreak || mxText->IsWrapped();
717 void XclExpLabelCell::Init( const XclExpRoot& rRoot,
718 const ScPatternAttr* pPattern, XclExpStringRef xText )
720 DBG_ASSERT( xText.is() && xText->Len(), "XclExpLabelCell::XclExpLabelCell - empty string passed" );
721 mxText = xText;
722 mnSstIndex = 0;
724 // create the cell format
725 sal_uInt16 nXclFont = mxText->RemoveLeadingFont();
726 if( GetXFId() == EXC_XFID_NOTFOUND )
728 DBG_ASSERT( nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found" );
729 bool bForceLineBreak = mxText->IsWrapped();
730 SetXFId( rRoot.GetXFBuffer().InsertWithFont( pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak ) );
733 // get auto-wrap attribute from cell format
734 const XclExpXF* pXF = rRoot.GetXFBuffer().GetXFById( GetXFId() );
735 mbLineBreak = pXF && pXF->GetAlignmentData().mbLineBreak;
737 // initialize the record contents
738 switch( rRoot.GetBiff() )
740 case EXC_BIFF5:
741 // BIFF5-BIFF7: create a LABEL or RSTRING record
742 DBG_ASSERT( mxText->Len() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::XclExpLabelCell - string too long" );
743 SetContSize( mxText->GetSize() );
744 // formatted string is exported in an RSTRING record
745 if( mxText->IsRich() )
747 DBG_ASSERT( mxText->GetFormatsCount() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::WriteContents - too many formats" );
748 mxText->LimitFormatCount( EXC_LABEL_MAXLEN );
749 SetRecId( EXC_ID_RSTRING );
750 SetContSize( GetContSize() + 1 + 2 * mxText->GetFormatsCount() );
752 break;
753 case EXC_BIFF8:
754 // BIFF8+: create a LABELSST record
755 mnSstIndex = rRoot.GetSst().Insert( xText );
756 SetRecId( EXC_ID_LABELSST );
757 SetContSize( 4 );
758 break;
759 default: DBG_ERROR_BIFF();
763 void XclExpLabelCell::SaveXml( XclExpXmlStream& rStrm )
765 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
766 rWorksheet->startElement( XML_c,
767 XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
768 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
769 XML_t, "s",
770 // OOXTODO: XML_cm, XML_vm, XML_ph
771 FSEND );
772 rWorksheet->startElement( XML_v, FSEND );
773 rWorksheet->write( (sal_Int32) mnSstIndex );
774 rWorksheet->endElement( XML_v );
775 rWorksheet->endElement( XML_c );
778 void XclExpLabelCell::WriteContents( XclExpStream& rStrm )
780 switch( rStrm.GetRoot().GetBiff() )
782 case EXC_BIFF5:
783 rStrm << *mxText;
784 if( mxText->IsRich() )
786 rStrm << static_cast< sal_uInt8 >( mxText->GetFormatsCount() );
787 mxText->WriteFormats( rStrm );
789 break;
790 case EXC_BIFF8:
791 rStrm << mnSstIndex;
792 break;
793 default: DBG_ERROR_BIFF();
797 // ----------------------------------------------------------------------------
799 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpFormulaCell, 256, 256 )
801 XclExpFormulaCell::XclExpFormulaCell(
802 const XclExpRoot& rRoot, const XclAddress& rXclPos,
803 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
804 const ScFormulaCell& rScFmlaCell,
805 XclExpArrayBuffer& rArrayBfr,
806 XclExpShrfmlaBuffer& rShrfmlaBfr,
807 XclExpTableopBuffer& rTableopBfr ) :
808 XclExpSingleCellBase( EXC_ID2_FORMULA, 0, rXclPos, nForcedXFId ),
809 mrScFmlaCell( const_cast< ScFormulaCell& >( rScFmlaCell ) )
811 // *** Find result number format overwriting cell number format *** -------
813 if( GetXFId() == EXC_XFID_NOTFOUND )
815 SvNumberFormatter& rFormatter = rRoot.GetFormatter();
816 XclExpNumFmtBuffer& rNumFmtBfr = rRoot.GetNumFmtBuffer();
818 // current cell number format
819 ULONG nScNumFmt = pPattern ?
820 GETITEMVALUE( pPattern->GetItemSet(), SfxUInt32Item, ATTR_VALUE_FORMAT, ULONG ) :
821 rNumFmtBfr.GetStandardFormat();
823 // alternative number format passed to XF buffer
824 ULONG nAltScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
825 /* #73420# Xcl doesn't know Boolean number formats, we write
826 "TRUE";"FALSE" (language dependent). Don't do it for automatic
827 formula formats, because Excel gets them right. */
828 /* #i8640# Don't set text format, if we have string results. */
829 short nFormatType = mrScFmlaCell.GetFormatType();
830 if( ((nScNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0) &&
831 (nFormatType != NUMBERFORMAT_LOGICAL) &&
832 (nFormatType != NUMBERFORMAT_TEXT) )
833 nAltScNumFmt = mrScFmlaCell.GetStandardFormat( rFormatter, nScNumFmt );
834 /* #73420# If cell number format is Boolean and automatic formula
835 format is Boolean don't write that ugly special format. */
836 else if( (nFormatType == NUMBERFORMAT_LOGICAL) &&
837 (rFormatter.GetType( nScNumFmt ) == NUMBERFORMAT_LOGICAL) )
838 nAltScNumFmt = rNumFmtBfr.GetStandardFormat();
840 // #i41420# find script type according to result type (always latin for numeric results)
841 sal_Int16 nScript = ApiScriptType::LATIN;
842 bool bForceLineBreak = false;
843 if( nFormatType == NUMBERFORMAT_TEXT )
845 String aResult;
846 mrScFmlaCell.GetString( aResult );
847 bForceLineBreak = mrScFmlaCell.IsMultilineResult();
848 nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult );
850 SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) );
853 // *** Convert the formula token array *** --------------------------------
855 ScAddress aScPos( static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), rRoot.GetCurrScTab() );
856 const ScTokenArray& rScTokArr = *mrScFmlaCell.GetCode();
858 // first try to create multiple operations
859 mxAddRec = rTableopBfr.CreateOrExtendTableop( rScTokArr, aScPos );
861 // no multiple operation found - try to create matrix formula
862 if( !mxAddRec ) switch( static_cast< ScMatrixMode >( mrScFmlaCell.GetMatrixFlag() ) )
864 case MM_FORMULA:
866 // origin of the matrix - find the used matrix range
867 SCCOL nMatWidth;
868 SCROW nMatHeight;
869 mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
870 DBG_ASSERT( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
871 ScRange aMatScRange( aScPos );
872 ScAddress& rMatEnd = aMatScRange.aEnd;
873 rMatEnd.IncCol( static_cast< SCsCOL >( nMatWidth - 1 ) );
874 rMatEnd.IncRow( static_cast< SCsROW >( nMatHeight - 1 ) );
875 // reduce to valid range (range keeps valid, because start position IS valid)
876 rRoot.GetAddressConverter().ValidateRange( aMatScRange, true );
877 // create the ARRAY record
878 mxAddRec = rArrayBfr.CreateArray( rScTokArr, aMatScRange );
880 break;
881 case MM_REFERENCE:
883 // other formula cell covered by a matrix - find the ARRAY record
884 mxAddRec = rArrayBfr.FindArray( rScTokArr );
885 // should always be found, if Calc document is not broken
886 DBG_ASSERT( mxAddRec.is(), "XclExpFormulaCell::XclExpFormulaCell - no matrix found" );
888 break;
889 default:;
892 // no matrix found - try to create shared formula
893 if( !mxAddRec )
894 mxAddRec = rShrfmlaBfr.CreateOrExtendShrfmla( rScTokArr, aScPos );
896 // no shared formula found - create a simple cell formula
897 if( !mxAddRec )
898 mxTokArr = rRoot.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL, rScTokArr, &aScPos );
901 void XclExpFormulaCell::Save( XclExpStream& rStrm )
903 // create token array for FORMULA cells with additional record
904 if( mxAddRec.is() )
905 mxTokArr = mxAddRec->CreateCellTokenArray( rStrm.GetRoot() );
907 // FORMULA record itself
908 DBG_ASSERT( mxTokArr.is(), "XclExpFormulaCell::Save - missing token array" );
909 if( !mxTokArr )
910 mxTokArr = rStrm.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA );
911 SetContSize( 16 + mxTokArr->GetSize() );
912 XclExpSingleCellBase::Save( rStrm );
914 // additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record
915 if( mxAddRec.is() && mxAddRec->IsBasePos( GetXclCol(), GetXclRow() ) )
916 mxAddRec->Save( rStrm );
918 // STRING record for string result
919 if( mxStringRec.is() )
920 mxStringRec->Save( rStrm );
923 static const char* lcl_GetErrorString( USHORT nScErrCode )
925 sal_uInt8 nXclErrCode = XclTools::GetXclErrorCode( nScErrCode );
926 switch( nXclErrCode )
928 case EXC_ERR_NULL: return "#NULL!";
929 case EXC_ERR_DIV0: return "#DIV/0!";
930 case EXC_ERR_VALUE: return "#VALUE!";
931 case EXC_ERR_REF: return "#REF!";
932 case EXC_ERR_NAME: return "#NAME?";
933 case EXC_ERR_NUM: return "#NUM!";
934 case EXC_ERR_NA:
935 default: return "#N/A";
939 static void lcl_GetFormulaInfo( ScFormulaCell& rCell, const char** pType, OUString& rValue)
941 switch( rCell.GetFormatType() )
943 case NUMBERFORMAT_NUMBER:
945 // either value or error code
946 USHORT nScErrCode = rCell.GetErrCode();
947 if( nScErrCode )
949 *pType = "e";
950 rValue = XclXmlUtils::ToOUString( lcl_GetErrorString( nScErrCode ) );
952 else
954 *pType = "n";
955 rValue = OUString::valueOf( rCell.GetValue() );
958 break;
960 case NUMBERFORMAT_TEXT:
962 *pType = "str";
963 String aResult;
964 rCell.GetString( aResult );
965 rValue = XclXmlUtils::ToOUString( aResult );
967 break;
969 case NUMBERFORMAT_LOGICAL:
971 *pType = "b";
972 rValue = XclXmlUtils::ToOUString( rCell.GetValue() == 0.0 ? "0" : "1" );
974 break;
976 default:
978 *pType = "inlineStr";
979 String aResult;
980 rCell.GetString( aResult );
981 rValue = XclXmlUtils::ToOUString( aResult );
983 break;
987 void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
989 const char* sType = NULL;
990 OUString sValue;
992 lcl_GetFormulaInfo( mrScFmlaCell, &sType, sValue );
993 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
994 rWorksheet->startElement( XML_c,
995 XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
996 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
997 XML_t, sType,
998 // OOXTODO: XML_cm, XML_vm, XML_ph
999 FSEND );
1001 rWorksheet->startElement( XML_f,
1002 // OOXTODO: XML_t, ST_CellFormulaType
1003 XML_aca, XclXmlUtils::ToPsz( mxTokArr->IsVolatile() || (mxAddRec.is() && mxAddRec->IsVolatile()) ),
1004 // OOXTODO: XML_ref, ST_Ref
1005 // OOXTODO: XML_dt2D, bool
1006 // OOXTODO: XML_dtr, bool
1007 // OOXTODO: XML_del1, bool
1008 // OOXTODO: XML_del2, bool
1009 // OOXTODO: XML_r1, ST_CellRef
1010 // OOXTODO: XML_r2, ST_CellRef
1011 // OOXTODO: XML_ca, bool
1012 // OOXTODO: XML_si, uint
1013 // OOXTODO: XML_bx bool
1014 FSEND );
1015 rWorksheet->writeEscaped( XclXmlUtils::ToOUString( *mrScFmlaCell.GetDocument(), mrScFmlaCell.aPos, mrScFmlaCell.GetCode() ) );
1016 rWorksheet->endElement( XML_f );
1017 if( strcmp( sType, "inlineStr" ) == 0 )
1019 rWorksheet->startElement( XML_is, FSEND );
1020 rWorksheet->startElement( XML_t, FSEND );
1021 rWorksheet->writeEscaped( sValue );
1022 rWorksheet->endElement( XML_t );
1023 rWorksheet->endElement( XML_is );
1025 else
1027 rWorksheet->startElement( XML_v, FSEND );
1028 rWorksheet->writeEscaped( sValue );
1029 rWorksheet->endElement( XML_v );
1031 rWorksheet->endElement( XML_c );
1034 void XclExpFormulaCell::WriteContents( XclExpStream& rStrm )
1036 // result of the formula
1037 switch( mrScFmlaCell.GetFormatType() )
1039 case NUMBERFORMAT_NUMBER:
1041 // either value or error code
1042 USHORT nScErrCode = mrScFmlaCell.GetErrCode();
1043 if( nScErrCode )
1044 rStrm << EXC_FORMULA_RES_ERROR << sal_uInt8( 0 )
1045 << XclTools::GetXclErrorCode( nScErrCode )
1046 << sal_uInt8( 0 ) << sal_uInt16( 0 )
1047 << sal_uInt16( 0xFFFF );
1048 else
1049 rStrm << mrScFmlaCell.GetValue();
1051 break;
1053 case NUMBERFORMAT_TEXT:
1055 String aResult;
1056 mrScFmlaCell.GetString( aResult );
1057 if( aResult.Len() || (rStrm.GetRoot().GetBiff() <= EXC_BIFF5) )
1059 rStrm << EXC_FORMULA_RES_STRING;
1060 mxStringRec.reset( new XclExpStringRec( rStrm.GetRoot(), aResult ) );
1062 else
1063 rStrm << EXC_FORMULA_RES_EMPTY; // BIFF8 only
1064 rStrm << sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
1066 break;
1068 case NUMBERFORMAT_LOGICAL:
1070 sal_uInt8 nXclValue = (mrScFmlaCell.GetValue() == 0.0) ? 0 : 1;
1071 rStrm << EXC_FORMULA_RES_BOOL << sal_uInt8( 0 )
1072 << nXclValue << sal_uInt8( 0 ) << sal_uInt16( 0 )
1073 << sal_uInt16( 0xFFFF );
1075 break;
1077 default:
1078 rStrm << mrScFmlaCell.GetValue();
1081 // flags and formula token array
1082 sal_uInt16 nFlags = EXC_FORMULA_DEFAULTFLAGS;
1083 ::set_flag( nFlags, EXC_FORMULA_RECALC_ALWAYS, mxTokArr->IsVolatile() || (mxAddRec.is() && mxAddRec->IsVolatile()) );
1084 ::set_flag( nFlags, EXC_FORMULA_SHARED, mxAddRec.is() && (mxAddRec->GetRecId() == EXC_ID_SHRFMLA) );
1085 rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
1088 // Multiple cell records ======================================================
1090 XclExpMultiCellBase::XclExpMultiCellBase(
1091 sal_uInt16 nRecId, sal_uInt16 nMulRecId, sal_Size nContSize, const XclAddress& rXclPos ) :
1092 XclExpCellBase( nRecId, 0, rXclPos ),
1093 mnMulRecId( nMulRecId ),
1094 mnContSize( nContSize )
1098 sal_uInt16 XclExpMultiCellBase::GetLastXclCol() const
1100 return GetXclCol() + GetCellCount() - 1;
1103 sal_uInt32 XclExpMultiCellBase::GetFirstXFId() const
1105 return maXFIds.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds.front().mnXFId;
1108 bool XclExpMultiCellBase::IsEmpty() const
1110 return maXFIds.empty();
1113 void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
1115 for( XclExpMultiXFIdDeq::iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
1116 aIt->ConvertXFIndex( rRoot );
1119 void XclExpMultiCellBase::Save( XclExpStream& rStrm )
1121 DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1123 XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1124 XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1125 XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1126 sal_uInt16 nBegXclCol = GetXclCol();
1127 sal_uInt16 nEndXclCol = nBegXclCol;
1129 while( aRangeEnd != aEnd )
1131 // find begin of next used XF range
1132 aRangeBeg = aRangeEnd;
1133 nBegXclCol = nEndXclCol;
1134 while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1136 nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1137 ++aRangeBeg;
1139 // find end of next used XF range
1140 aRangeEnd = aRangeBeg;
1141 nEndXclCol = nBegXclCol;
1142 while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1144 nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1145 ++aRangeEnd;
1148 // export this range as a record
1149 if( aRangeBeg != aRangeEnd )
1151 sal_uInt16 nCount = nEndXclCol - nBegXclCol;
1152 bool bIsMulti = nCount > 1;
1153 sal_Size nTotalSize = GetRecSize() + (2 + mnContSize) * nCount;
1154 if( bIsMulti ) nTotalSize += 2;
1156 rStrm.StartRecord( bIsMulti ? mnMulRecId : GetRecId(), nTotalSize );
1157 rStrm << GetXclRow() << nBegXclCol;
1159 sal_uInt16 nRelCol = nBegXclCol - GetXclCol();
1160 for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1162 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1164 rStrm << aIt->mnXFIndex;
1165 WriteContents( rStrm, nRelCol );
1166 ++nRelCol;
1169 if( bIsMulti )
1170 rStrm << static_cast< sal_uInt16 >( nEndXclCol - 1 );
1171 rStrm.EndRecord();
1176 void XclExpMultiCellBase::SaveXml( XclExpXmlStream& rStrm )
1178 XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1179 XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1180 XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1181 sal_uInt16 nBegXclCol = GetXclCol();
1182 sal_uInt16 nEndXclCol = nBegXclCol;
1184 while( aRangeEnd != aEnd )
1186 // find begin of next used XF range
1187 aRangeBeg = aRangeEnd;
1188 nBegXclCol = nEndXclCol;
1189 while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1191 nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1192 ++aRangeBeg;
1194 // find end of next used XF range
1195 aRangeEnd = aRangeBeg;
1196 nEndXclCol = nBegXclCol;
1197 while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1199 nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1200 ++aRangeEnd;
1203 // export this range as a record
1204 if( aRangeBeg != aRangeEnd )
1206 sal_uInt16 nRelColIdx = nBegXclCol - GetXclCol();
1207 sal_Int32 nRelCol = 0;
1208 for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1210 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1212 WriteXmlContents(
1213 rStrm,
1214 XclAddress( static_cast<sal_uInt16>(nBegXclCol + nRelCol), GetXclRow() ),
1215 aIt->mnXFIndex,
1216 nRelColIdx );
1217 ++nRelCol;
1218 ++nRelColIdx;
1225 sal_uInt16 XclExpMultiCellBase::GetCellCount() const
1227 sal_uInt16 nCount = 0;
1228 for( XclExpMultiXFIdDeq::const_iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
1229 nCount = nCount + aIt->mnCount;
1230 return nCount;
1233 void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId& rXFId )
1235 if( maXFIds.empty() || (maXFIds.back().mnXFId != rXFId.mnXFId) )
1236 maXFIds.push_back( rXFId );
1237 else
1238 maXFIds.back().mnCount = maXFIds.back().mnCount + rXFId.mnCount;
1241 void XclExpMultiCellBase::AppendXFId( const XclExpRoot& rRoot,
1242 const ScPatternAttr* pPattern, sal_uInt16 nScript, sal_uInt32 nForcedXFId, sal_uInt16 nCount )
1244 sal_uInt32 nXFId = (nForcedXFId == EXC_XFID_NOTFOUND) ?
1245 rRoot.GetXFBuffer().Insert( pPattern, nScript ) : nForcedXFId;
1246 AppendXFId( XclExpMultiXFId( nXFId, nCount ) );
1249 bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase& rCell )
1251 if( GetLastXclCol() + 1 == rCell.GetXclCol() )
1253 maXFIds.insert( maXFIds.end(), rCell.maXFIds.begin(), rCell.maXFIds.end() );
1254 return true;
1256 return false;
1259 void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1261 DBG_ASSERT( GetLastXclCol() < rXFIndexes.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" );
1262 ScfUInt16Vec::iterator aDestIt = rXFIndexes.begin() + GetXclCol();
1263 for( XclExpMultiXFIdDeq::const_iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
1265 ::std::fill( aDestIt, aDestIt + aIt->mnCount, aIt->mnXFIndex );
1266 aDestIt += aIt->mnCount;
1270 void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes )
1272 // save last column before calling maXFIds.clear()
1273 sal_uInt16 nLastXclCol = GetLastXclCol();
1274 DBG_ASSERT( nLastXclCol < rXFIndexes.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" );
1276 // build new XF index vector, containing passed XF indexes
1277 maXFIds.clear();
1278 XclExpMultiXFId aXFId( 0 );
1279 for( ScfUInt16Vec::const_iterator aIt = rXFIndexes.begin() + GetXclCol(), aEnd = rXFIndexes.begin() + nLastXclCol + 1; aIt != aEnd; ++aIt )
1281 // AppendXFId() tests XclExpXFIndex::mnXFId, set it too
1282 aXFId.mnXFId = aXFId.mnXFIndex = *aIt;
1283 AppendXFId( aXFId );
1286 // remove leading and trailing unused XF indexes
1287 if( !maXFIds.empty() && (maXFIds.front().mnXFIndex == EXC_XF_NOTFOUND) )
1289 SetXclCol( GetXclCol() + maXFIds.front().mnCount );
1290 maXFIds.pop_front();
1292 if( !maXFIds.empty() && (maXFIds.back().mnXFIndex == EXC_XF_NOTFOUND) )
1293 maXFIds.pop_back();
1295 // The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND.
1298 // ----------------------------------------------------------------------------
1300 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpBlankCell, 256, 256 )
1302 XclExpBlankCell::XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId ) :
1303 XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1305 DBG_ASSERT( rXFId.mnCount > 0, "XclExpBlankCell::XclExpBlankCell - invalid count" );
1306 AppendXFId( rXFId );
1309 XclExpBlankCell::XclExpBlankCell(
1310 const XclExpRoot& rRoot, const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
1311 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId ) :
1312 XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1314 DBG_ASSERT( rXclPos.mnCol <= nLastXclCol, "XclExpBlankCell::XclExpBlankCell - invalid column range" );
1315 // #i46627# use default script type instead of ApiScriptType::WEAK
1316 AppendXFId( rRoot, pPattern, rRoot.GetDefApiScript(), nForcedXFId, nLastXclCol - rXclPos.mnCol + 1 );
1319 bool XclExpBlankCell::TryMerge( const XclExpCellBase& rCell )
1321 const XclExpBlankCell* pBlankCell = dynamic_cast< const XclExpBlankCell* >( &rCell );
1322 return pBlankCell && TryMergeXFIds( *pBlankCell );
1325 void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1327 GetXFIndexes( rXFIndexes );
1330 void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes )
1332 RemoveUnusedXFIndexes( rXFIndexes );
1335 void XclExpBlankCell::WriteContents( XclExpStream& /*rStrm*/, sal_uInt16 /*nRelCol*/ )
1339 void XclExpBlankCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 /* nRelCol */ )
1341 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1342 rWorksheet->singleElement( XML_c,
1343 XML_r, XclXmlUtils::ToOString( rAddress ).getStr(),
1344 XML_s, lcl_GetStyleId( rStrm, nXFId ).getStr(),
1345 FSEND );
1348 // ----------------------------------------------------------------------------
1350 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpRkCell, 256, 256 )
1352 XclExpRkCell::XclExpRkCell(
1353 const XclExpRoot& rRoot, const XclAddress& rXclPos,
1354 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_Int32 nRkValue ) :
1355 XclExpMultiCellBase( EXC_ID_RK, EXC_ID_MULRK, 4, rXclPos )
1357 // #i41210# always use latin script for number cells - may look wrong for special number formats...
1358 AppendXFId( rRoot, pPattern, ApiScriptType::LATIN, nForcedXFId );
1359 maRkValues.push_back( nRkValue );
1362 bool XclExpRkCell::TryMerge( const XclExpCellBase& rCell )
1364 const XclExpRkCell* pRkCell = dynamic_cast< const XclExpRkCell* >( &rCell );
1365 if( pRkCell && TryMergeXFIds( *pRkCell ) )
1367 maRkValues.insert( maRkValues.end(), pRkCell->maRkValues.begin(), pRkCell->maRkValues.end() );
1368 return true;
1370 return false;
1373 void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol )
1375 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1376 rWorksheet->startElement( XML_c,
1377 XML_r, XclXmlUtils::ToOString( rAddress ).getStr(),
1378 XML_s, lcl_GetStyleId( rStrm, nXFId ).getStr(),
1379 XML_t, "n",
1380 // OOXTODO: XML_cm, XML_vm, XML_ph
1381 FSEND );
1382 rWorksheet->startElement( XML_v, FSEND );
1383 rWorksheet->write( XclTools::GetDoubleFromRK( maRkValues[ nRelCol ] ) );
1384 rWorksheet->endElement( XML_v );
1385 rWorksheet->endElement( XML_c );
1388 void XclExpRkCell::WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol )
1390 DBG_ASSERT( nRelCol < maRkValues.size(), "XclExpRkCell::WriteContents - overflow error" );
1391 rStrm << maRkValues[ nRelCol ];
1394 // ============================================================================
1395 // Rows and Columns
1396 // ============================================================================
1398 XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows ) :
1399 mpScOLArray( 0 ),
1400 maLevelInfos( SC_OL_MAXDEPTH ),
1401 mnCurrLevel( 0 ),
1402 mbCurrCollapse( false )
1404 if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
1405 mpScOLArray = bRows ? pOutlineTable->GetRowArray() : pOutlineTable->GetColArray();
1407 if( mpScOLArray )
1408 for( USHORT nLevel = 0; nLevel < SC_OL_MAXDEPTH; ++nLevel )
1409 if( ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nLevel, 0 ) )
1410 maLevelInfos[ nLevel ].mnScEndPos = pEntry->GetEnd();
1413 void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos )
1415 if( mpScOLArray )
1417 // find open level index for passed position
1418 USHORT nNewOpenScLevel = 0; // new open level (0-based Calc index)
1419 sal_uInt8 nNewLevel = 0; // new open level (1-based Excel index)
1421 if( mpScOLArray->FindTouchedLevel( nScPos, nScPos, nNewOpenScLevel ) )
1422 nNewLevel = static_cast< sal_uInt8 >( nNewOpenScLevel + 1 );
1423 // else nNewLevel keeps 0 to show that there are no groups
1425 mbCurrCollapse = false;
1426 if( nNewLevel >= mnCurrLevel )
1428 // new level(s) opened, or no level closed - update all level infos
1429 for( USHORT nScLevel = 0; nScLevel <= nNewOpenScLevel; ++nScLevel )
1431 /* In each level: check if a new group is started (there may be
1432 neighbored groups without gap - therefore check ALL levels). */
1433 if( maLevelInfos[ nScLevel ].mnScEndPos < nScPos )
1435 if( ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nScLevel, nScPos ) )
1437 maLevelInfos[ nScLevel ].mnScEndPos = pEntry->GetEnd();
1438 maLevelInfos[ nScLevel ].mbHidden = pEntry->IsHidden();
1443 else
1445 // level(s) closed - check if any of the closed levels are collapsed
1446 // Calc uses 0-based level indexes
1447 USHORT nOldOpenScLevel = mnCurrLevel - 1;
1448 for( USHORT nScLevel = nNewOpenScLevel + 1; !mbCurrCollapse && (nScLevel <= nOldOpenScLevel); ++nScLevel )
1449 mbCurrCollapse = maLevelInfos[ nScLevel ].mbHidden;
1452 // cache new opened level
1453 mnCurrLevel = nNewLevel;
1457 // ----------------------------------------------------------------------------
1459 XclExpGuts::XclExpGuts( const XclExpRoot& rRoot ) :
1460 XclExpRecord( EXC_ID_GUTS, 8 ),
1461 mnColLevels( 0 ),
1462 mnColWidth( 0 ),
1463 mnRowLevels( 0 ),
1464 mnRowWidth( 0 )
1466 if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
1468 // column outline groups
1469 if( const ScOutlineArray* pColArray = pOutlineTable->GetColArray() )
1470 mnColLevels = ulimit_cast< sal_uInt16 >( pColArray->GetDepth(), EXC_OUTLINE_MAX );
1471 if( mnColLevels )
1473 ++mnColLevels;
1474 mnColWidth = 12 * mnColLevels + 5;
1477 // row outline groups
1478 if( const ScOutlineArray* pRowArray = pOutlineTable->GetRowArray() )
1479 mnRowLevels = ulimit_cast< sal_uInt16 >( pRowArray->GetDepth(), EXC_OUTLINE_MAX );
1480 if( mnRowLevels )
1482 ++mnRowLevels;
1483 mnRowWidth = 12 * mnRowLevels + 5;
1488 void XclExpGuts::WriteBody( XclExpStream& rStrm )
1490 rStrm << mnRowWidth << mnColWidth << mnRowLevels << mnColLevels;
1493 // ----------------------------------------------------------------------------
1495 XclExpDimensions::XclExpDimensions( const XclExpRoot& rRoot ) :
1496 mnFirstUsedXclRow( 0 ),
1497 mnFirstFreeXclRow( 0 ),
1498 mnFirstUsedXclCol( 0 ),
1499 mnFirstFreeXclCol( 0 )
1501 switch( rRoot.GetBiff() )
1503 case EXC_BIFF2: SetRecHeader( EXC_ID2_DIMENSIONS, 8 ); break;
1504 case EXC_BIFF3:
1505 case EXC_BIFF4:
1506 case EXC_BIFF5: SetRecHeader( EXC_ID3_DIMENSIONS, 10 ); break;
1507 case EXC_BIFF8: SetRecHeader( EXC_ID3_DIMENSIONS, 14 ); break;
1508 default: DBG_ERROR_BIFF();
1512 void XclExpDimensions::SetDimensions(
1513 sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
1514 sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow )
1516 mnFirstUsedXclRow = nFirstUsedXclRow;
1517 mnFirstFreeXclRow = nFirstFreeXclRow;
1518 mnFirstUsedXclCol = nFirstUsedXclCol;
1519 mnFirstFreeXclCol = nFirstFreeXclCol;
1522 void XclExpDimensions::SaveXml( XclExpXmlStream& rStrm )
1524 ScRange aRange;
1525 aRange.aStart.SetRow( (SCROW) mnFirstUsedXclRow );
1526 aRange.aStart.SetCol( (SCCOL) mnFirstUsedXclCol );
1528 if( mnFirstFreeXclRow != mnFirstUsedXclRow && mnFirstFreeXclCol != mnFirstUsedXclCol )
1530 aRange.aEnd.SetRow( (SCROW) (mnFirstFreeXclRow-1) );
1531 aRange.aEnd.SetCol( (SCCOL) (mnFirstFreeXclCol-1) );
1534 rStrm.GetCurrentStream()->singleElement( XML_dimension,
1535 XML_ref, XclXmlUtils::ToOString( aRange ).getStr(),
1536 FSEND );
1539 void XclExpDimensions::WriteBody( XclExpStream& rStrm )
1541 XclBiff eBiff = rStrm.GetRoot().GetBiff();
1542 if( eBiff == EXC_BIFF8 )
1543 rStrm << mnFirstUsedXclRow << mnFirstFreeXclRow;
1544 else
1545 rStrm << static_cast< sal_uInt16 >( mnFirstUsedXclRow ) << static_cast< sal_uInt16 >( mnFirstFreeXclRow );
1546 rStrm << mnFirstUsedXclCol << mnFirstFreeXclCol;
1547 if( eBiff >= EXC_BIFF3 )
1548 rStrm << sal_uInt16( 0 );
1551 // ============================================================================
1553 namespace {
1555 double lclGetCorrectedColWidth( const XclExpRoot& rRoot, sal_uInt16 nXclColWidth )
1557 long nFontHt = rRoot.GetFontBuffer().GetAppFontData().mnHeight;
1558 return nXclColWidth - XclTools::GetXclDefColWidthCorrection( nFontHt );
1561 } // namespace
1563 // ----------------------------------------------------------------------------
1565 XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot& rRoot ) :
1566 XclExpUInt16Record( EXC_ID_DEFCOLWIDTH, EXC_DEFCOLWIDTH_DEF ),
1567 XclExpRoot( rRoot )
1571 bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth ) const
1573 double fNewColWidth = lclGetCorrectedColWidth( GetRoot(), nXclColWidth );
1574 // exactly matched, if difference is less than 1/16 of a character to the left or to the right
1575 return Abs( static_cast< long >( GetValue() * 256.0 - fNewColWidth + 0.5 ) ) < 16;
1578 void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth )
1580 double fNewColWidth = lclGetCorrectedColWidth( GetRoot(), nXclColWidth );
1581 SetValue( limit_cast< sal_uInt16 >( fNewColWidth / 256.0 + 0.5 ) );
1584 // ----------------------------------------------------------------------------
1586 XclExpColinfo::XclExpColinfo( const XclExpRoot& rRoot,
1587 SCCOL nScCol, SCROW nLastScRow, XclExpColOutlineBuffer& rOutlineBfr ) :
1588 XclExpRecord( EXC_ID_COLINFO, 12 ),
1589 XclExpRoot( rRoot ),
1590 mnWidth( 0 ),
1591 mnFlags( 0 ),
1592 mnFirstXclCol( static_cast< sal_uInt16 >( nScCol ) ),
1593 mnLastXclCol( static_cast< sal_uInt16 >( nScCol ) )
1595 ScDocument& rDoc = GetDoc();
1596 SCTAB nScTab = GetCurrScTab();
1598 // column default format
1599 maXFId.mnXFId = GetXFBuffer().Insert(
1600 rDoc.GetMostUsedPattern( nScCol, 0, nLastScRow, nScTab ), GetDefApiScript() );
1602 // column width
1603 USHORT nScWidth = rDoc.GetColWidth( nScCol, nScTab );
1604 mnWidth = XclTools::GetXclColumnWidth( nScWidth, GetCharWidth() );
1606 // column flags
1607 ::set_flag( mnFlags, EXC_COLINFO_HIDDEN, rDoc.ColHidden(nScCol, nScTab) );
1609 // outline data
1610 rOutlineBfr.Update( nScCol );
1611 ::set_flag( mnFlags, EXC_COLINFO_COLLAPSED, rOutlineBfr.IsCollapsed() );
1612 ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 8, 3 );
1615 sal_uInt16 XclExpColinfo::ConvertXFIndexes()
1617 maXFId.ConvertXFIndex( GetRoot() );
1618 return maXFId.mnXFIndex;
1621 bool XclExpColinfo::IsDefault( const XclExpDefcolwidth& rDefColWidth ) const
1623 return (maXFId.mnXFIndex == EXC_XF_DEFAULTCELL) && (mnFlags == 0) && rDefColWidth.IsDefWidth( mnWidth );
1626 bool XclExpColinfo::TryMerge( const XclExpColinfo& rColInfo )
1628 if( (maXFId.mnXFIndex == rColInfo.maXFId.mnXFIndex) &&
1629 (mnWidth == rColInfo.mnWidth) &&
1630 (mnFlags == rColInfo.mnFlags) &&
1631 (mnLastXclCol + 1 == rColInfo.mnFirstXclCol) )
1633 mnLastXclCol = rColInfo.mnLastXclCol;
1634 return true;
1636 return false;
1639 void XclExpColinfo::WriteBody( XclExpStream& rStrm )
1641 // if last column is equal to last possible column, Excel adds one more
1642 sal_uInt16 nLastXclCol = mnLastXclCol;
1643 if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
1644 ++nLastXclCol;
1646 rStrm << mnFirstXclCol
1647 << nLastXclCol
1648 << mnWidth
1649 << maXFId.mnXFIndex
1650 << mnFlags
1651 << sal_uInt16( 0 );
1654 void XclExpColinfo::SaveXml( XclExpXmlStream& rStrm )
1656 // if last column is equal to last possible column, Excel adds one more
1657 sal_uInt16 nLastXclCol = mnLastXclCol;
1658 if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
1659 ++nLastXclCol;
1661 rStrm.GetCurrentStream()->singleElement( XML_col,
1662 // OOXTODO: XML_bestFit,
1663 XML_collapsed, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_COLINFO_COLLAPSED ) ),
1664 // OOXTODO: XML_customWidth,
1665 XML_hidden, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_COLINFO_HIDDEN ) ),
1666 XML_max, OString::valueOf( (sal_Int32) (nLastXclCol+1) ).getStr(),
1667 XML_min, OString::valueOf( (sal_Int32) (mnFirstXclCol+1) ).getStr(),
1668 // OOXTODO: XML_outlineLevel,
1669 // OOXTODO: XML_phonetic,
1670 XML_style, lcl_GetStyleId( rStrm, maXFId.mnXFIndex ).getStr(),
1671 XML_width, OString::valueOf( (double) (mnWidth / 255.0) ).getStr(),
1672 FSEND );
1675 // ----------------------------------------------------------------------------
1677 XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot& rRoot ) :
1678 XclExpRoot( rRoot ),
1679 maDefcolwidth( rRoot ),
1680 maOutlineBfr( rRoot )
1684 void XclExpColinfoBuffer::Initialize( SCROW nLastScRow )
1687 for( sal_uInt16 nScCol = 0, nLastScCol = GetMaxPos().Col(); nScCol <= nLastScCol; ++nScCol )
1688 maColInfos.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol, nLastScRow, maOutlineBfr ) );
1691 void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes )
1693 rXFIndexes.clear();
1694 rXFIndexes.reserve( maColInfos.GetSize() );
1696 size_t nPos, nSize;
1698 // do not cache the record list size, it may change in the loop
1699 for( nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
1701 XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
1702 xRec->ConvertXFIndexes();
1704 // try to merge with previous record
1705 if( nPos > 0 )
1707 XclExpColinfoRef xPrevRec = maColInfos.GetRecord( nPos - 1 );
1708 if( xPrevRec->TryMerge( *xRec ) )
1709 // adjust nPos to get the next COLINFO record at the same position
1710 maColInfos.RemoveRecord( nPos-- );
1714 // put XF indexes into passed vector, collect use count of all different widths
1715 typedef ::std::map< sal_uInt16, sal_uInt16 > XclExpWidthMap;
1716 XclExpWidthMap aWidthMap;
1717 sal_uInt16 nMaxColCount = 0;
1718 sal_uInt16 nMaxUsedWidth = 0;
1719 for( nPos = 0, nSize = maColInfos.GetSize(); nPos < nSize; ++nPos )
1721 XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
1722 sal_uInt16 nColCount = xRec->GetColCount();
1724 // add XF index to passed vector
1725 rXFIndexes.resize( rXFIndexes.size() + nColCount, xRec->GetXFIndex() );
1727 // collect use count of column width
1728 sal_uInt16 nWidth = xRec->GetColWidth();
1729 sal_uInt16& rnMapCount = aWidthMap[ nWidth ];
1730 rnMapCount = rnMapCount + nColCount;
1731 if( rnMapCount > nMaxColCount )
1733 nMaxColCount = rnMapCount;
1734 nMaxUsedWidth = nWidth;
1737 maDefcolwidth.SetDefWidth( nMaxUsedWidth );
1739 // remove all default COLINFO records
1740 nPos = 0;
1741 while( nPos < maColInfos.GetSize() )
1743 XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
1744 if( xRec->IsDefault( maDefcolwidth ) )
1745 maColInfos.RemoveRecord( nPos );
1746 else
1747 ++nPos;
1751 void XclExpColinfoBuffer::Save( XclExpStream& rStrm )
1753 // DEFCOLWIDTH
1754 maDefcolwidth.Save( rStrm );
1755 // COLINFO records
1756 maColInfos.Save( rStrm );
1759 void XclExpColinfoBuffer::SaveXml( XclExpXmlStream& rStrm )
1761 if( maColInfos.IsEmpty() )
1762 return;
1764 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1765 rWorksheet->startElement( XML_cols,
1766 FSEND );
1767 maColInfos.SaveXml( rStrm );
1768 rWorksheet->endElement( XML_cols );
1771 // ============================================================================
1773 XclExpDefaultRowData::XclExpDefaultRowData() :
1774 mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1775 mnHeight( EXC_DEFROW_DEFAULTHEIGHT )
1779 XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow& rRow ) :
1780 mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1781 mnHeight( rRow.GetHeight() )
1783 ::set_flag( mnFlags, EXC_DEFROW_HIDDEN, rRow.IsHidden() );
1784 ::set_flag( mnFlags, EXC_DEFROW_UNSYNCED, rRow.IsUnsynced() );
1787 bool operator<( const XclExpDefaultRowData& rLeft, const XclExpDefaultRowData& rRight )
1789 return (rLeft.mnHeight < rRight.mnHeight) ||
1790 ((rLeft.mnHeight == rRight.mnHeight) && (rLeft.mnFlags < rRight.mnFlags));
1793 // ----------------------------------------------------------------------------
1795 XclExpDefrowheight::XclExpDefrowheight() :
1796 XclExpRecord( EXC_ID3_DEFROWHEIGHT, 4 )
1800 void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData& rDefData )
1802 maDefData = rDefData;
1805 void XclExpDefrowheight::WriteBody( XclExpStream& rStrm )
1807 DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1808 rStrm << maDefData.mnFlags << maDefData.mnHeight;
1811 // ----------------------------------------------------------------------------
1813 XclExpRow::XclExpRow( const XclExpRoot& rRoot, sal_uInt16 nXclRow,
1814 XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty ) :
1815 XclExpRecord( EXC_ID3_ROW, 16 ),
1816 XclExpRoot( rRoot ),
1817 mnXclRow( nXclRow ),
1818 mnHeight( 0 ),
1819 mnFlags( EXC_ROW_DEFAULTFLAGS ),
1820 mnXFIndex( EXC_XF_DEFAULTCELL ),
1821 mnOutlineLevel( 0 ),
1822 mbAlwaysEmpty( bAlwaysEmpty ),
1823 mbEnabled( true )
1825 SCTAB nScTab = GetCurrScTab();
1826 SCROW nScRow = static_cast< SCROW >( mnXclRow );
1828 // *** Row flags *** ------------------------------------------------------
1830 BYTE nRowFlags = GetDoc().GetRowFlags( nScRow, nScTab );
1831 bool bUserHeight = ::get_flag< BYTE >( nRowFlags, CR_MANUALSIZE );
1832 bool bHidden = GetDoc().RowHidden(nScRow, nScTab);
1833 ::set_flag( mnFlags, EXC_ROW_UNSYNCED, bUserHeight );
1834 ::set_flag( mnFlags, EXC_ROW_HIDDEN, bHidden );
1836 // *** Row height *** -----------------------------------------------------
1838 USHORT nScHeight = GetDoc().GetRowHeight( nScRow, nScTab );
1839 if( nScHeight == 0 )
1841 ::set_flag( mnFlags, EXC_ROW_HIDDEN );
1842 mnHeight = EXC_ROW_DEFAULTHEIGHT;
1844 else
1846 // Calc and Excel use twips
1847 mnHeight = static_cast< sal_uInt16 >( nScHeight );
1849 // #76250# not usable in Applix
1850 // ::set_flag( mnHeight, EXC_ROW_FLAGDEFHEIGHT, !bUserHeight );
1852 // *** Outline data *** ---------------------------------------------------
1854 rOutlineBfr.Update( nScRow );
1855 ::set_flag( mnFlags, EXC_ROW_COLLAPSED, rOutlineBfr.IsCollapsed() );
1856 ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 0, 3 );
1857 mnOutlineLevel = rOutlineBfr.GetLevel();
1859 // *** Progress bar *** ---------------------------------------------------
1861 XclExpProgressBar& rProgress = GetProgressBar();
1862 rProgress.IncRowRecordCount();
1863 rProgress.Progress();
1866 void XclExpRow::AppendCell( XclExpCellRef xCell, bool bIsMergedBase )
1868 DBG_ASSERT( !mbAlwaysEmpty, "XclExpRow::AppendCell - row is marked to be always empty" );
1869 // try to merge with last existing cell
1870 InsertCell( xCell, maCellList.GetSize(), bIsMergedBase );
1873 void XclExpRow::Finalize( const ScfUInt16Vec& rColXFIndexes )
1875 size_t nPos, nSize;
1877 // *** Convert XF identifiers *** -----------------------------------------
1879 // additionally collect the blank XF indexes
1880 size_t nColCount = GetMaxPos().Col() + 1;
1881 DBG_ASSERT( rColXFIndexes.size() == nColCount, "XclExpRow::Finalize - wrong column XF index count" );
1883 ScfUInt16Vec aXFIndexes( nColCount, EXC_XF_NOTFOUND );
1884 for( nPos = 0, nSize = maCellList.GetSize(); nPos < nSize; ++nPos )
1886 XclExpCellRef xCell = maCellList.GetRecord( nPos );
1887 xCell->ConvertXFIndexes( GetRoot() );
1888 xCell->GetBlankXFIndexes( aXFIndexes );
1891 // *** Fill gaps with BLANK/MULBLANK cell records *** ---------------------
1893 /* This is needed because nonexistant cells in Calc are not formatted at all,
1894 but in Excel they would have the column default format. Blank cells that
1895 are equal to the respective column default are removed later in this function. */
1896 if( !mbAlwaysEmpty )
1898 // XF identifier representing default cell XF
1899 XclExpMultiXFId aXFId( XclExpXFBuffer::GetDefCellXFId() );
1900 aXFId.ConvertXFIndex( GetRoot() );
1902 nPos = 0;
1903 while( nPos <= maCellList.GetSize() ) // don't cache list size, may change in the loop
1905 // get column index that follows previous cell
1906 sal_uInt16 nFirstFreeXclCol = (nPos > 0) ? (maCellList.GetRecord( nPos - 1 )->GetLastXclCol() + 1) : 0;
1907 // get own column index
1908 sal_uInt16 nNextUsedXclCol = (nPos < maCellList.GetSize()) ? maCellList.GetRecord( nPos )->GetXclCol() : (GetMaxPos().Col() + 1);
1910 // is there a gap?
1911 if( nFirstFreeXclCol < nNextUsedXclCol )
1913 aXFId.mnCount = nNextUsedXclCol - nFirstFreeXclCol;
1914 XclExpCellRef xNewCell( new XclExpBlankCell( XclAddress( nFirstFreeXclCol, mnXclRow ), aXFId ) );
1915 // insert the cell, InsertCell() may merge it with existing BLANK records
1916 InsertCell( xNewCell, nPos, false );
1917 // insert default XF indexes into aXFIndexes
1918 ::std::fill( aXFIndexes.begin() + nFirstFreeXclCol,
1919 aXFIndexes.begin() + nNextUsedXclCol, aXFId.mnXFIndex );
1920 // don't step forward with nPos, InsertCell() may remove records
1922 else
1923 ++nPos;
1927 // *** Find default row format *** ----------------------------------------
1929 ScfUInt16Vec::iterator aCellBeg = aXFIndexes.begin(), aCellEnd = aXFIndexes.end(), aCellIt;
1930 ScfUInt16Vec::const_iterator aColBeg = rColXFIndexes.begin(), aColIt;
1932 // find most used XF index in the row
1933 typedef ::std::map< sal_uInt16, size_t > XclExpXFIndexMap;
1934 XclExpXFIndexMap aIndexMap;
1935 sal_uInt16 nRowXFIndex = EXC_XF_DEFAULTCELL;
1936 size_t nMaxXFCount = 0;
1937 for( aCellIt = aCellBeg; aCellIt != aCellEnd; ++aCellIt )
1939 if( *aCellIt != EXC_XF_NOTFOUND )
1941 size_t& rnCount = aIndexMap[ *aCellIt ];
1942 ++rnCount;
1943 if( rnCount > nMaxXFCount )
1945 nRowXFIndex = *aCellIt;
1946 nMaxXFCount = rnCount;
1951 // decide whether to use the row default XF index or column default XF indexes
1952 bool bUseColDefXFs = nRowXFIndex == EXC_XF_DEFAULTCELL;
1953 if( !bUseColDefXFs )
1955 // count needed XF indexes for blank cells with and without row default XF index
1956 size_t nXFCountWithRowDefXF = 0;
1957 size_t nXFCountWithoutRowDefXF = 0;
1958 for( aCellIt = aCellBeg, aColIt = aColBeg; aCellIt != aCellEnd; ++aCellIt, ++aColIt )
1960 sal_uInt16 nXFIndex = *aCellIt;
1961 if( nXFIndex != EXC_XF_NOTFOUND )
1963 if( nXFIndex != nRowXFIndex )
1964 ++nXFCountWithRowDefXF; // with row default XF index
1965 if( nXFIndex != *aColIt )
1966 ++nXFCountWithoutRowDefXF; // without row default XF index
1970 // use column XF indexes if this would cause less or equal number of BLANK records
1971 bUseColDefXFs = nXFCountWithoutRowDefXF <= nXFCountWithRowDefXF;
1974 // *** Remove unused BLANK cell records *** -------------------------------
1976 if( bUseColDefXFs )
1978 // use column default XF indexes
1979 // #i194#: remove cell XF indexes equal to column default XF indexes
1980 for( aCellIt = aCellBeg, aColIt = aColBeg; aCellIt != aCellEnd; ++aCellIt, ++aColIt )
1981 if( *aCellIt == *aColIt )
1982 *aCellIt = EXC_XF_NOTFOUND;
1984 else
1986 // use row default XF index
1987 mnXFIndex = nRowXFIndex;
1988 ::set_flag( mnFlags, EXC_ROW_USEDEFXF );
1989 // #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index
1990 for( aCellIt = aCellBeg; aCellIt != aCellEnd; ++aCellIt )
1991 if( *aCellIt == nRowXFIndex )
1992 *aCellIt = EXC_XF_NOTFOUND;
1995 // remove unused parts of BLANK/MULBLANK cell records
1996 nPos = 0;
1997 while( nPos < maCellList.GetSize() ) // do not cache list size, may change in the loop
1999 XclExpCellRef xCell = maCellList.GetRecord( nPos );
2000 xCell->RemoveUnusedBlankCells( aXFIndexes );
2001 if( xCell->IsEmpty() )
2002 maCellList.RemoveRecord( nPos );
2003 else
2004 ++nPos;
2007 // progress bar includes disabled rows
2008 GetProgressBar().Progress();
2011 sal_uInt16 XclExpRow::GetFirstUsedXclCol() const
2013 return maCellList.IsEmpty() ? 0 : maCellList.GetFirstRecord()->GetXclCol();
2016 sal_uInt16 XclExpRow::GetFirstFreeXclCol() const
2018 return maCellList.IsEmpty() ? 0 : (maCellList.GetLastRecord()->GetLastXclCol() + 1);
2021 bool XclExpRow::IsDefaultable() const
2023 const sal_uInt16 nAllowedFlags = EXC_ROW_DEFAULTFLAGS | EXC_ROW_HIDDEN | EXC_ROW_UNSYNCED;
2024 return !::get_flag( mnFlags, static_cast< sal_uInt16 >( ~nAllowedFlags ) ) && IsEmpty();
2027 void XclExpRow::DisableIfDefault( const XclExpDefaultRowData& rDefRowData )
2029 mbEnabled = !IsDefaultable() ||
2030 (mnHeight != rDefRowData.mnHeight) ||
2031 (IsHidden() != rDefRowData.IsHidden()) ||
2032 (IsUnsynced() != rDefRowData.IsUnsynced());
2035 void XclExpRow::WriteCellList( XclExpStream& rStrm )
2037 DBG_ASSERT( mbEnabled || maCellList.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" );
2038 maCellList.Save( rStrm );
2041 void XclExpRow::Save( XclExpStream& rStrm )
2043 if( mbEnabled )
2044 XclExpRecord::Save( rStrm );
2047 void XclExpRow::InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase )
2049 DBG_ASSERT( xCell.is(), "XclExpRow::InsertCell - missing cell" );
2051 /* #109751# If we have a multi-line text in a merged cell, and the resulting
2052 row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED
2053 flag to be true to ensure Excel works correctly. */
2054 if( bIsMergedBase && xCell->IsMultiLineText() )
2055 ::set_flag( mnFlags, EXC_ROW_UNSYNCED );
2057 // try to merge with previous cell, insert the new cell if not successful
2058 XclExpCellRef xPrevCell = maCellList.GetRecord( nPos - 1 );
2059 if( xPrevCell.is() && xPrevCell->TryMerge( *xCell ) )
2060 xCell = xPrevCell;
2061 else
2062 maCellList.InsertRecord( xCell, nPos++ );
2063 // nPos points now to following cell
2065 // try to merge with following cell, remove it if successful
2066 XclExpCellRef xNextCell = maCellList.GetRecord( nPos );
2067 if( xNextCell.is() && xCell->TryMerge( *xNextCell ) )
2068 maCellList.RemoveRecord( nPos );
2071 void XclExpRow::WriteBody( XclExpStream& rStrm )
2073 rStrm << mnXclRow
2074 << GetFirstUsedXclCol()
2075 << GetFirstFreeXclCol()
2076 << mnHeight
2077 << sal_uInt32( 0 )
2078 << mnFlags
2079 << mnXFIndex;
2082 void XclExpRow::SaveXml( XclExpXmlStream& rStrm )
2084 if( !mbEnabled )
2085 return;
2086 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2087 bool haveFormat = ::get_flag( mnFlags, EXC_ROW_USEDEFXF );
2088 rWorksheet->startElement( XML_row,
2089 XML_r, OString::valueOf( (sal_Int32) (mnXclRow+1) ).getStr(),
2090 // OOXTODO: XML_spans, optional
2091 XML_s, haveFormat ? lcl_GetStyleId( rStrm, mnXFIndex ).getStr() : NULL,
2092 XML_customFormat, XclXmlUtils::ToPsz( haveFormat ),
2093 XML_ht, OString::valueOf( (double) mnHeight / 20.0 ).getStr(),
2094 XML_hidden, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_HIDDEN ) ),
2095 XML_customHeight, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_UNSYNCED ) ),
2096 XML_outlineLevel, OString::valueOf( (sal_Int32) mnOutlineLevel ).getStr(),
2097 XML_collapsed, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_COLLAPSED ) ),
2098 // OOXTODO: XML_thickTop, bool
2099 // OOXTODO: XML_thickBot, bool
2100 // OOXTODO: XML_ph, bool
2101 FSEND );
2102 // OOXTODO: XML_extLst
2103 maCellList.SaveXml( rStrm );
2104 rWorksheet->endElement( XML_row );
2107 // ----------------------------------------------------------------------------
2109 XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot& rRoot ) :
2110 XclExpRoot( rRoot ),
2111 maOutlineBfr( rRoot ),
2112 maDimensions( rRoot ),
2113 mpLastUsedRow( 0 ),
2114 mnLastUsedXclRow( 0 )
2118 void XclExpRowBuffer::AppendCell( XclExpCellRef xCell, bool bIsMergedBase )
2120 DBG_ASSERT( xCell.is(), "XclExpRowBuffer::AppendCell - missing cell" );
2121 GetOrCreateRow( xCell->GetXclRow(), false ).AppendCell( xCell, bIsMergedBase );
2124 void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow )
2126 if( nFirstFreeScRow > 0 )
2127 GetOrCreateRow( static_cast< sal_uInt16 >( nFirstFreeScRow - 1 ), true );
2130 void XclExpRowBuffer::Finalize( XclExpDefaultRowData& rDefRowData, const ScfUInt16Vec& rColXFIndexes )
2132 size_t nPos, nSize;
2134 // *** Finalize all rows *** ----------------------------------------------
2136 GetProgressBar().ActivateFinalRowsSegment();
2138 // unused blank cell records will be removed
2139 for( nPos = 0, nSize = maRowList.GetSize(); nPos < nSize; ++nPos )
2140 maRowList.GetRecord( nPos )->Finalize( rColXFIndexes );
2142 // *** Default row format *** ---------------------------------------------
2144 typedef ::std::map< XclExpDefaultRowData, size_t > XclExpDefRowDataMap;
2145 XclExpDefRowDataMap aDefRowMap;
2147 // find default row format for rows beyond used area
2148 sal_uInt32 nDefaultXclRow = maRowList.IsEmpty() ? 0 : (maRowList.GetLastRecord()->GetXclRow() + 1);
2149 XclExpDefaultRowData aMaxDefData;
2150 size_t nMaxDefCount = 0;
2151 /* #i30411# Files saved with SO7/OOo1.x with nonstandard default column
2152 formatting cause big Excel files, because all rows from row 1 to row
2153 32000 are exported. Now, if the used area goes exactly to row 32000,
2154 ignore all rows >32000.
2155 #i59220# Tolerance of +-128 rows for inserted/removed rows. */
2156 if( (nDefaultXclRow < 31872) || (nDefaultXclRow > 32128) )
2158 sal_uInt16 nLastXclRow = static_cast< sal_uInt16 >( GetMaxPos().Row() );
2159 if( nDefaultXclRow <= nLastXclRow )
2161 // create a dummy ROW record and fill aMaxDefData
2162 XclExpRowOutlineBuffer aOutlineBfr( GetRoot() );
2163 XclExpRow aRow( GetRoot(), nLastXclRow, aOutlineBfr, true );
2164 aMaxDefData = XclExpDefaultRowData( aRow );
2165 aDefRowMap[ aMaxDefData ] = nMaxDefCount =
2166 static_cast< size_t >( nLastXclRow - nDefaultXclRow + 1 );
2170 // only look for default format in existing rows, if there are more than unused
2171 nSize = maRowList.GetSize();
2172 if( nMaxDefCount < nSize )
2174 for( nPos = 0; nPos < nSize; ++nPos )
2176 XclExpRowRef xRow = maRowList.GetRecord( nPos );
2177 /* Collect formats of unused rows (rows without cells), which are able
2178 to be defaulted (i.e. no explicit format or outline level). */
2179 if( xRow->IsDefaultable() )
2181 XclExpDefaultRowData aDefData( *xRow );
2182 size_t& rnDefCount = aDefRowMap[ aDefData ];
2183 ++rnDefCount;
2184 if( rnDefCount > nMaxDefCount )
2186 nMaxDefCount = rnDefCount;
2187 aMaxDefData = aDefData;
2193 // return the default row format to caller
2194 rDefRowData = aMaxDefData;
2196 // *** Disable unused ROW records, find used area *** ---------------------
2198 sal_uInt16 nFirstUsedXclCol = SAL_MAX_UINT16;
2199 sal_uInt16 nFirstFreeXclCol = 0;
2200 sal_uInt32 nFirstUsedXclRow = SAL_MAX_UINT32;
2201 sal_uInt32 nFirstFreeXclRow = 0;
2203 for( nPos = 0, nSize = maRowList.GetSize(); nPos < nSize; ++nPos )
2205 XclExpRowRef xRow = maRowList.GetRecord( nPos );
2207 // disable unused rows
2208 xRow->DisableIfDefault( aMaxDefData );
2210 // find used column range
2211 if( !xRow->IsEmpty() ) // empty rows return (0...0) as used range
2213 nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, xRow->GetFirstUsedXclCol() );
2214 nFirstFreeXclCol = ::std::max( nFirstFreeXclCol, xRow->GetFirstFreeXclCol() );
2217 // find used row range
2218 if( xRow->IsEnabled() )
2220 sal_uInt16 nXclRow = xRow->GetXclRow();
2221 nFirstUsedXclRow = ::std::min< sal_uInt32 >( nFirstUsedXclRow, nXclRow );
2222 nFirstFreeXclRow = ::std::max< sal_uInt32 >( nFirstFreeXclRow, nXclRow + 1 );
2226 // adjust start position, if there are no or only empty/disabled ROW records
2227 nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, nFirstFreeXclCol );
2228 nFirstUsedXclRow = ::std::min( nFirstUsedXclRow, nFirstFreeXclRow );
2230 // initialize the DIMENSIONS record
2231 maDimensions.SetDimensions(
2232 nFirstUsedXclCol, nFirstUsedXclRow, nFirstFreeXclCol, nFirstFreeXclRow );
2235 void XclExpRowBuffer::Save( XclExpStream& rStrm )
2237 // DIMENSIONS record
2238 maDimensions.Save( rStrm );
2240 // save in blocks of 32 rows, each block contains first all ROWs, then all cells
2241 size_t nSize = maRowList.GetSize();
2242 size_t nBlockStart = 0;
2243 sal_uInt16 nStartXclRow = (nSize == 0) ? 0 : maRowList.GetRecord( 0 )->GetXclRow();
2245 while( nBlockStart < nSize )
2247 // find end of row block
2248 size_t nBlockEnd = nBlockStart + 1;
2249 while( (nBlockEnd < nSize) && (maRowList.GetRecord( nBlockEnd )->GetXclRow() - nStartXclRow < EXC_ROW_ROWBLOCKSIZE) )
2250 ++nBlockEnd;
2252 // write the ROW records
2253 size_t nPos;
2254 for( nPos = nBlockStart; nPos < nBlockEnd; ++nPos )
2255 maRowList.GetRecord( nPos )->Save( rStrm );
2257 // write the cell records
2258 for( nPos = nBlockStart; nPos < nBlockEnd; ++nPos )
2259 maRowList.GetRecord( nPos )->WriteCellList( rStrm );
2261 nBlockStart = nBlockEnd;
2262 nStartXclRow += EXC_ROW_ROWBLOCKSIZE;
2266 void XclExpRowBuffer::SaveXml( XclExpXmlStream& rStrm )
2268 sal_Int32 nNonEmpty = 0;
2270 size_t nRows = maRowList.GetSize();
2271 for( size_t i = 0; i < nRows; ++i)
2272 if( maRowList.GetRecord( i )->IsEnabled() )
2273 ++nNonEmpty;
2275 if( nNonEmpty == 0 )
2277 rStrm.GetCurrentStream()->singleElement( XML_sheetData, FSEND );
2279 else
2281 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2282 rWorksheet->startElement( XML_sheetData, FSEND );
2283 maRowList.SaveXml( rStrm );
2284 rWorksheet->endElement( XML_sheetData );
2288 XclExpDimensions* XclExpRowBuffer::GetDimensions()
2290 return &maDimensions;
2293 XclExpRow& XclExpRowBuffer::GetOrCreateRow( sal_uInt16 nXclRow, bool bRowAlwaysEmpty )
2295 if( !mpLastUsedRow || (mnLastUsedXclRow != nXclRow) )
2297 // fill up missing ROW records
2298 // do not use sal_uInt16 for nFirstFreeXclRow, would cause loop in full sheets
2299 for( size_t nFirstFreeXclRow = maRowList.GetSize(); nFirstFreeXclRow <= nXclRow; ++nFirstFreeXclRow )
2300 maRowList.AppendNewRecord( new XclExpRow(
2301 GetRoot(), static_cast< sal_uInt16 >( nFirstFreeXclRow ), maOutlineBfr, bRowAlwaysEmpty ) );
2303 mpLastUsedRow = maRowList.GetRecord( nXclRow ).get();
2304 mnLastUsedXclRow = nXclRow;
2306 return *mpLastUsedRow;
2309 // ============================================================================
2310 // Cell Table
2311 // ============================================================================
2313 XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) :
2314 XclExpRoot( rRoot ),
2315 maColInfoBfr( rRoot ),
2316 maRowBfr( rRoot ),
2317 maArrayBfr( rRoot ),
2318 maShrfmlaBfr( rRoot ),
2319 maTableopBfr( rRoot ),
2320 mxDefrowheight( new XclExpDefrowheight ),
2321 mxGuts( new XclExpGuts( rRoot ) ),
2322 mxNoteList( new XclExpNoteList ),
2323 mxMergedcells( new XclExpMergedcells( rRoot ) ),
2324 mxHyperlinkList( new XclExpHyperlinkList ),
2325 mxDval( new XclExpDval( rRoot ) )
2327 ScDocument& rDoc = GetDoc();
2328 SCTAB nScTab = GetCurrScTab();
2329 SvNumberFormatter& rFormatter = GetFormatter();
2331 // maximum sheet limits
2332 SCCOL nMaxScCol = GetMaxPos().Col();
2333 SCROW nMaxScRow = GetMaxPos().Row();
2335 // find used area (non-empty cells)
2336 SCCOL nLastUsedScCol;
2337 SCROW nLastUsedScRow;
2338 rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
2340 ScRange aUsedRange( 0, 0, nScTab, nLastUsedScCol, nLastUsedScRow, nScTab );
2341 GetAddressConverter().ValidateRange( aUsedRange, true );
2342 nLastUsedScCol = aUsedRange.aEnd.Col();
2343 nLastUsedScRow = aUsedRange.aEnd.Row();
2345 // first row without any set attributes (height/hidden/...)
2346 SCROW nFirstUnflaggedScRow = rDoc.GetLastFlaggedRow( nScTab ) + 1;
2348 // find range of outlines
2349 SCROW nFirstUngroupedScRow = 0;
2350 if( const ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable( nScTab ) )
2352 SCCOLROW nScStartPos, nScEndPos;
2353 if( const ScOutlineArray* pRowArray = pOutlineTable->GetRowArray() )
2355 pRowArray->GetRange( nScStartPos, nScEndPos );
2356 // +1 because open/close button is in next row in Excel, +1 for "end->first unused"
2357 nFirstUngroupedScRow = static_cast< SCROW >( nScEndPos + 2 );
2361 // column settings
2362 /* #i30411# Files saved with SO7/OOo1.x with nonstandard default column
2363 formatting cause big Excel files, because all rows from row 1 to row
2364 32000 are exported. Now, if the used area goes exactly to row 32000,
2365 use this row as default and ignore all rows >32000.
2366 #i59220# Tolerance of +-128 rows for inserted/removed rows. */
2367 if( (31871 <= nLastUsedScRow) && (nLastUsedScRow <= 32127) && (nFirstUnflaggedScRow < nLastUsedScRow) && (nFirstUngroupedScRow <= nLastUsedScRow) )
2368 nMaxScRow = nLastUsedScRow;
2369 maColInfoBfr.Initialize( nMaxScRow );
2371 // range for cell iterator
2372 SCCOL nLastIterScCol = nMaxScCol;
2373 SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow + 128, nMaxScRow );
2374 ScUsedAreaIterator aIt( &rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow );
2376 // activate the correct segment and sub segment at the progress bar
2377 GetProgressBar().ActivateCreateRowsSegment();
2379 for( bool bIt = aIt.GetNext(); bIt; bIt = aIt.GetNext() )
2381 SCCOL nScCol = aIt.GetStartCol();
2382 SCROW nScRow = aIt.GetRow();
2383 SCCOL nLastScCol = aIt.GetEndCol();
2384 ScAddress aScPos( nScCol, nScRow, nScTab );
2386 XclAddress aXclPos( static_cast< sal_uInt16 >( nScCol ), static_cast< sal_uInt16 >( nScRow ) );
2387 sal_uInt16 nLastXclCol = static_cast< sal_uInt16 >( nLastScCol );
2389 const ScBaseCell* pScCell = aIt.GetCell();
2390 XclExpCellRef xCell;
2392 const ScPatternAttr* pPattern = aIt.GetPattern();
2394 // handle overlapped merged cells before creating the cell record
2395 sal_uInt32 nMergeBaseXFId = EXC_XFID_NOTFOUND;
2396 bool bIsMergedBase = false;
2397 if( pPattern )
2399 const SfxItemSet& rItemSet = pPattern->GetItemSet();
2400 // base cell in a merged range
2401 const ScMergeAttr& rMergeItem = GETITEM( rItemSet, ScMergeAttr, ATTR_MERGE );
2402 bIsMergedBase = rMergeItem.IsMerged();
2403 /* overlapped cell in a merged range; in Excel all merged cells
2404 must contain same XF index, for correct border */
2405 const ScMergeFlagAttr& rMergeFlagItem = GETITEM( rItemSet, ScMergeFlagAttr, ATTR_MERGE_FLAG );
2406 if( rMergeFlagItem.IsOverlapped() )
2407 nMergeBaseXFId = mxMergedcells->GetBaseXFId( aScPos );
2410 String aAddNoteText; // additional text to be appended to a note
2412 CellType eCellType = pScCell ? pScCell->GetCellType() : CELLTYPE_NONE;
2413 switch( eCellType )
2415 case CELLTYPE_VALUE:
2417 double fValue = static_cast< const ScValueCell* >( pScCell )->GetValue();
2419 // try to create a Boolean cell
2420 if( pPattern && ((fValue == 0.0) || (fValue == 1.0)) )
2422 ULONG nScNumFmt = GETITEMVALUE( pPattern->GetItemSet(), SfxUInt32Item, ATTR_VALUE_FORMAT, ULONG );
2423 if( rFormatter.GetType( nScNumFmt ) == NUMBERFORMAT_LOGICAL )
2424 xCell.reset( new XclExpBooleanCell(
2425 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue != 0.0 ) );
2428 // try to create an RK value (compressed floating-point number)
2429 sal_Int32 nRkValue;
2430 if( !xCell && XclTools::GetRKFromDouble( nRkValue, fValue ) )
2431 xCell.reset( new XclExpRkCell(
2432 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, nRkValue ) );
2434 // else: simple floating-point number cell
2435 if( !xCell )
2436 xCell.reset( new XclExpNumberCell(
2437 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue ) );
2439 break;
2441 case CELLTYPE_STRING:
2443 const ScStringCell& rScStrCell = *static_cast< const ScStringCell* >( pScCell );
2444 xCell.reset( new XclExpLabelCell(
2445 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScStrCell ) );
2447 break;
2449 case CELLTYPE_EDIT:
2451 const ScEditCell& rScEditCell = *static_cast< const ScEditCell* >( pScCell );
2452 XclExpHyperlinkHelper aLinkHelper( GetRoot(), aScPos );
2453 xCell.reset( new XclExpLabelCell(
2454 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScEditCell, aLinkHelper ) );
2456 // add a single created HLINK record to the record list
2457 if( aLinkHelper.HasLinkRecord() )
2458 mxHyperlinkList->AppendRecord( aLinkHelper.GetLinkRecord() );
2459 // add list of multiple URLs to the additional cell note text
2460 if( aLinkHelper.HasMultipleUrls() )
2461 ScGlobal::AddToken( aAddNoteText, aLinkHelper.GetUrlList(), '\n', 2 );
2463 break;
2465 case CELLTYPE_FORMULA:
2467 const ScFormulaCell& rScFmlaCell = *static_cast< const ScFormulaCell* >( pScCell );
2468 xCell.reset( new XclExpFormulaCell(
2469 GetRoot(), aXclPos, pPattern, nMergeBaseXFId,
2470 rScFmlaCell, maArrayBfr, maShrfmlaBfr, maTableopBfr ) );
2472 break;
2474 default:
2475 DBG_ERRORFILE( "XclExpCellTable::XclExpCellTable - unknown cell type" );
2476 // run-through!
2477 case CELLTYPE_NONE:
2478 case CELLTYPE_NOTE:
2480 xCell.reset( new XclExpBlankCell(
2481 GetRoot(), aXclPos, nLastXclCol, pPattern, nMergeBaseXFId ) );
2483 break;
2486 // insert the cell into the current row
2487 if( xCell.is() )
2488 maRowBfr.AppendCell( xCell, bIsMergedBase );
2490 // notes
2491 const ScPostIt* pScNote = pScCell ? pScCell->GetNote() : 0;
2492 if( pScNote || (aAddNoteText.Len() > 0) )
2493 mxNoteList->AppendNewRecord( new XclExpNote( GetRoot(), aScPos, pScNote, aAddNoteText ) );
2495 // other sheet contents
2496 if( pPattern )
2498 const SfxItemSet& rItemSet = pPattern->GetItemSet();
2500 // base cell in a merged range
2501 if( bIsMergedBase )
2503 const ScMergeAttr& rMergeItem = GETITEM( rItemSet, ScMergeAttr, ATTR_MERGE );
2504 ScRange aScRange( aScPos );
2505 aScRange.aEnd.IncCol( rMergeItem.GetColMerge() - 1 );
2506 aScRange.aEnd.IncRow( rMergeItem.GetRowMerge() - 1 );
2507 sal_uInt32 nXFId = xCell.is() ? xCell->GetFirstXFId() : EXC_XFID_NOTFOUND;
2508 // #120156# blank cells merged vertically may occur repeatedly
2509 DBG_ASSERT( (aScRange.aStart.Col() == aScRange.aEnd.Col()) || (nScCol == nLastScCol),
2510 "XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" );
2511 for( SCCOL nIndex = nScCol; nIndex <= nLastScCol; ++nIndex )
2513 mxMergedcells->AppendRange( aScRange, nXFId );
2514 aScRange.aStart.IncCol();
2515 aScRange.aEnd.IncCol();
2519 // data validation
2520 if( ScfTools::CheckItem( rItemSet, ATTR_VALIDDATA, false ) )
2522 ULONG nScHandle = GETITEMVALUE( rItemSet, SfxUInt32Item, ATTR_VALIDDATA, ULONG );
2523 ScRange aScRange( aScPos );
2524 aScRange.aEnd.SetCol( nLastScCol );
2525 mxDval->InsertCellRange( aScRange, nScHandle );
2530 // create missing row settings for rows anyhow flagged or with outlines
2531 maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, nFirstUngroupedScRow ) );
2534 void XclExpCellTable::Finalize()
2536 // Finalize multiple operations.
2537 maTableopBfr.Finalize();
2539 /* Finalize column buffer. This calculates column default XF indexes from
2540 the XF identifiers and fills a vector with these XF indexes. */
2541 ScfUInt16Vec aColXFIndexes;
2542 maColInfoBfr.Finalize( aColXFIndexes );
2544 /* Finalize row buffer. This calculates all cell XF indexes from the XF
2545 identifiers. Then the XF index vector aColXFIndexes (filled above) is
2546 used to calculate the row default formats. With this, all unneeded blank
2547 cell records (equal to row default or column default) will be removed.
2548 The function returns the (most used) default row format in aDefRowData. */
2549 XclExpDefaultRowData aDefRowData;
2550 maRowBfr.Finalize( aDefRowData, aColXFIndexes );
2552 // Initialize the DEFROWHEIGHT record.
2553 mxDefrowheight->SetDefaultData( aDefRowData );
2556 XclExpRecordRef XclExpCellTable::CreateRecord( sal_uInt16 nRecId ) const
2558 XclExpRecordRef xRec;
2559 switch( nRecId )
2561 case EXC_ID3_DIMENSIONS: xRec.reset( new XclExpDelegatingRecord( const_cast<XclExpRowBuffer*>(&maRowBfr)->GetDimensions() ) ); break;
2562 case EXC_ID2_DEFROWHEIGHT: xRec = mxDefrowheight; break;
2563 case EXC_ID_GUTS: xRec = mxGuts; break;
2564 case EXC_ID_NOTE: xRec = mxNoteList; break;
2565 case EXC_ID_MERGEDCELLS: xRec = mxMergedcells; break;
2566 case EXC_ID_HLINK: xRec = mxHyperlinkList; break;
2567 case EXC_ID_DVAL: xRec = mxDval; break;
2568 default: DBG_ERRORFILE( "XclExpCellTable::CreateRecord - unknown record id" );
2570 return xRec;
2573 void XclExpCellTable::Save( XclExpStream& rStrm )
2575 // DEFCOLWIDTH and COLINFOs
2576 maColInfoBfr.Save( rStrm );
2577 // ROWs and cell records
2578 maRowBfr.Save( rStrm );
2581 void XclExpCellTable::SaveXml( XclExpXmlStream& rStrm )
2583 maColInfoBfr.SaveXml( rStrm );
2584 maRowBfr.SaveXml( rStrm );
2587 // ============================================================================