Update to m13
[ooovba.git] / sc / source / filter / xlsx / xlsx-xetable.cxx
blobbf7af82f42deb31f9a77e2ff39838cd3d70ef173
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 "xestream.hxx"
34 #include "xlsx/xetable.hxx"
36 #include <map>
37 #include <com/sun/star/i18n/ScriptType.hpp>
38 #include "scitems.hxx"
39 #include <svtools/intitem.hxx>
40 #include "document.hxx"
41 #include "dociter.hxx"
42 #include "olinetab.hxx"
43 #include "cell.hxx"
44 #include "patattr.hxx"
45 #include "attrib.hxx"
46 #include "xehelper.hxx"
47 #include "xecontent.hxx"
48 #include "xeescher.hxx"
50 #include <oox/core/tokens.hxx>
52 using ::rtl::OString;
53 using ::rtl::OUString;
54 using ::rtl::OUStringBuffer;
56 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
58 // ============================================================================
59 // Helper records for cell records
60 // ============================================================================
62 XclExpStringRec::XclExpStringRec( const XclExpRoot& rRoot, const String& rResult ) :
63 XclExpRecord( EXC_ID3_STRING ),
64 mxResult( XclExpStringHelper::CreateString( rRoot, rResult ) )
66 DBG_ASSERT( (rRoot.GetBiff() <= EXC_BIFF5) || (mxResult->Len() > 0),
67 "XclExpStringRec::XclExpStringRec - empty result not allowed in BIFF8+" );
68 SetRecSize( mxResult->GetSize() );
71 void XclExpStringRec::WriteBody( XclExpStream& rStrm )
73 rStrm << *mxResult;
76 // Additional records for special formula ranges ==============================
78 XclExpRangeFmlaBase::XclExpRangeFmlaBase(
79 sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScAddress& rScPos ) :
80 XclExpRecord( nRecId, nRecSize ),
81 maXclRange( ScAddress::UNINITIALIZED ),
82 maBaseXclPos( ScAddress::UNINITIALIZED )
84 maBaseXclPos.Set( static_cast< sal_uInt16 >( rScPos.Col() ), static_cast< sal_uInt32 >( rScPos.Row() ) );
85 maXclRange.maFirst = maXclRange.maLast = maBaseXclPos;
88 XclExpRangeFmlaBase::XclExpRangeFmlaBase(
89 sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScRange& rScRange ) :
90 XclExpRecord( nRecId, nRecSize ),
91 maXclRange( ScAddress::UNINITIALIZED ),
92 maBaseXclPos( ScAddress::UNINITIALIZED )
94 maXclRange.Set(
95 static_cast< sal_uInt16 >( rScRange.aStart.Col() ),
96 static_cast< sal_uInt32 >( rScRange.aStart.Row() ),
97 static_cast< sal_uInt16 >( rScRange.aEnd.Col() ),
98 static_cast< sal_uInt32 >( rScRange.aEnd.Row() ) );
99 maBaseXclPos = maXclRange.maFirst;
102 bool XclExpRangeFmlaBase::IsBasePos( sal_uInt16 nXclCol, sal_uInt32 nXclRow ) const
104 return (maBaseXclPos.mnCol == nXclCol) && (maBaseXclPos.mnRow == nXclRow);
107 void XclExpRangeFmlaBase::Extend( const ScAddress& rScPos )
109 sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
110 sal_uInt32 nXclRow = static_cast< sal_uInt32 >( rScPos.Row() );
111 maXclRange.maFirst.mnCol = ::std::min( maXclRange.maFirst.mnCol, nXclCol );
112 maXclRange.maFirst.mnRow = ::std::min( maXclRange.maFirst.mnRow, nXclRow );
113 maXclRange.maLast.mnCol = ::std::max( maXclRange.maLast.mnCol, nXclCol );
114 maXclRange.maLast.mnRow = ::std::max( maXclRange.maLast.mnRow, nXclRow );
117 void XclExpRangeFmlaBase::WriteRangeAddress( XclExpStream& rStrm ) const
119 maXclRange.Write( rStrm, false );
122 // Array formulas =============================================================
124 XclExpArray::XclExpArray( XclTokenArrayRef xTokArr, const ScRange& rScRange ) :
125 XclExpRangeFmlaBase( EXC_ID3_ARRAY, 14 + xTokArr->GetSize(), rScRange ),
126 mxTokArr( xTokArr )
130 XclTokenArrayRef XclExpArray::CreateCellTokenArray( const XclExpRoot& rRoot ) const
132 return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
135 bool XclExpArray::IsVolatile() const
137 return mxTokArr->IsVolatile();
140 void XclExpArray::WriteBody( XclExpStream& rStrm )
142 WriteRangeAddress( rStrm );
143 sal_uInt16 nFlags = EXC_ARRAY_DEFAULTFLAGS;
144 ::set_flag( nFlags, EXC_ARRAY_RECALC_ALWAYS, IsVolatile() );
145 rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
148 // ----------------------------------------------------------------------------
150 XclExpArrayBuffer::XclExpArrayBuffer( const XclExpRoot& rRoot ) :
151 XclExpRoot( rRoot )
155 XclExpArrayRef XclExpArrayBuffer::CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange )
157 const ScAddress& rScPos = rScRange.aStart;
158 XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_MATRIX, rScTokArr, &rScPos );
160 DBG_ASSERT( maRecMap.find( rScPos ) == maRecMap.end(), "XclExpArrayBuffer::CreateArray - array exists already" );
161 XclExpArrayRef& rxRec = maRecMap[ rScPos ];
162 rxRec.reset( new XclExpArray( xTokArr, rScRange ) );
163 return rxRec;
166 XclExpArrayRef XclExpArrayBuffer::FindArray( const ScTokenArray& rScTokArr ) const
168 XclExpArrayRef xRec;
169 // try to extract a matrix reference token
170 if( rScTokArr.GetLen() == 1 )
172 const formula::FormulaToken* pToken = rScTokArr.GetArray()[ 0 ];
173 if( pToken && (pToken->GetOpCode() == ocMatRef) )
175 const ScSingleRefData& rRef = static_cast<const ScToken*>(pToken)->GetSingleRef();
176 ScAddress aBasePos( rRef.nCol, rRef.nRow, GetCurrScTab() );
177 XclExpArrayMap::const_iterator aIt = maRecMap.find( aBasePos );
178 if( aIt != maRecMap.end() )
179 xRec = aIt->second;
182 return xRec;
185 // Shared formulas ============================================================
187 XclExpShrfmla::XclExpShrfmla( XclTokenArrayRef xTokArr, const ScAddress& rScPos ) :
188 XclExpRangeFmlaBase( EXC_ID_SHRFMLA, 10 + xTokArr->GetSize(), rScPos ),
189 mxTokArr( xTokArr ),
190 mnUsedCount( 1 )
194 void XclExpShrfmla::ExtendRange( const ScAddress& rScPos )
196 Extend( rScPos );
197 ++mnUsedCount;
200 XclTokenArrayRef XclExpShrfmla::CreateCellTokenArray( const XclExpRoot& rRoot ) const
202 return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
205 bool XclExpShrfmla::IsVolatile() const
207 return mxTokArr->IsVolatile();
210 void XclExpShrfmla::WriteBody( XclExpStream& rStrm )
212 WriteRangeAddress( rStrm );
213 rStrm << sal_uInt8( 0 ) << mnUsedCount << *mxTokArr;
216 // ----------------------------------------------------------------------------
218 XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) :
219 XclExpRoot( rRoot )
223 XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
224 const ScTokenArray& rScTokArr, const ScAddress& rScPos )
226 XclExpShrfmlaRef xRec;
227 if( const ScTokenArray* pShrdScTokArr = XclTokenArrayHelper::GetSharedFormula( GetRoot(), rScTokArr ) )
229 XclExpShrfmlaMap::iterator aIt = maRecMap.find( pShrdScTokArr );
230 if( aIt == maRecMap.end() )
232 // create a new record
233 XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_SHARED, *pShrdScTokArr, &rScPos );
234 xRec.reset( new XclExpShrfmla( xTokArr, rScPos ) );
235 maRecMap[ pShrdScTokArr ] = xRec;
237 else
239 // extend existing record
240 DBG_ASSERT( aIt->second, "XclExpShrfmlaBuffer::CreateOrExtendShrfmla - missing record" );
241 xRec = aIt->second;
242 xRec->ExtendRange( rScPos );
245 return xRec;
248 // Multiple operations ========================================================
250 XclExpTableop::XclExpTableop( const ScAddress& rScPos,
251 const XclMultipleOpRefs& rRefs, sal_uInt8 nScMode ) :
252 XclExpRangeFmlaBase( EXC_ID3_TABLEOP, 16, rScPos ),
253 mnLastAppXclCol( static_cast< sal_uInt16 >( rScPos.Col() ) ),
254 mnColInpXclCol( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Col() ) ),
255 mnColInpXclRow( static_cast< sal_uInt32 >( rRefs.maColFirstScPos.Row() ) ),
256 mnRowInpXclCol( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Col() ) ),
257 mnRowInpXclRow( static_cast< sal_uInt32 >( rRefs.maRowFirstScPos.Row() ) ),
258 mnScMode( nScMode ),
259 mbValid( false )
263 bool XclExpTableop::TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
265 sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
266 sal_uInt32 nXclRow = static_cast< sal_uInt32 >( rScPos.Row() );
268 bool bOk = IsAppendable( nXclCol, nXclRow );
269 if( bOk )
271 SCCOL nFirstScCol = static_cast< SCCOL >( maXclRange.maFirst.mnCol );
272 SCROW nFirstScRow = static_cast< SCROW >( maXclRange.maFirst.mnRow );
273 SCCOL nColInpScCol = static_cast< SCCOL >( mnColInpXclCol );
274 SCROW nColInpScRow = static_cast< SCROW >( mnColInpXclRow );
275 SCCOL nRowInpScCol = static_cast< SCCOL >( mnRowInpXclCol );
276 SCROW nRowInpScRow = static_cast< SCROW >( mnRowInpXclRow );
278 bOk = ((mnScMode == 2) == rRefs.mbDblRefMode) &&
279 (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
280 (nColInpScCol == rRefs.maColFirstScPos.Col()) &&
281 (nColInpScRow == rRefs.maColFirstScPos.Row()) &&
282 (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
283 (rScPos.Tab() == rRefs.maColRelScPos.Tab());
285 if( bOk ) switch( mnScMode )
287 case 0:
288 bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
289 (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
290 (nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
291 (rScPos.Row() == rRefs.maColRelScPos.Row());
292 break;
293 case 1:
294 bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
295 (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
296 (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
297 (nFirstScRow == rRefs.maColRelScPos.Row() + 1);
298 break;
299 case 2:
300 bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
301 (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
302 (nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
303 (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
304 (nRowInpScCol == rRefs.maRowFirstScPos.Col()) &&
305 (nRowInpScRow == rRefs.maRowFirstScPos.Row()) &&
306 (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
307 (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
308 (nFirstScRow == rRefs.maRowRelScPos.Row() + 1) &&
309 (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
310 break;
311 default:
312 bOk = false;
315 if( bOk )
317 // extend the cell range
318 DBG_ASSERT( IsAppendable( nXclCol, nXclRow ), "XclExpTableop::TryExtend - wrong cell address" );
319 Extend( rScPos );
320 mnLastAppXclCol = nXclCol;
324 return bOk;
327 void XclExpTableop::Finalize()
329 // is the range complete? (last appended cell is in last column)
330 mbValid = maXclRange.maLast.mnCol == mnLastAppXclCol;
331 // if last row is incomplete, try to shorten the used range
332 if( !mbValid && (maXclRange.maFirst.mnRow < maXclRange.maLast.mnRow) )
334 --maXclRange.maLast.mnRow;
335 mbValid = true;
338 // check if referred cells are outside of own range
339 if( mbValid ) switch( mnScMode )
341 case 0:
342 mbValid = (mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
343 (mnColInpXclRow < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
344 break;
345 case 1:
346 mbValid = (mnColInpXclCol < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
347 (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
348 break;
349 case 2:
350 mbValid = ((mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
351 (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow)) &&
352 ((mnRowInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnRowInpXclCol > maXclRange.maLast.mnCol) ||
353 (mnRowInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnRowInpXclRow > maXclRange.maLast.mnRow));
354 break;
358 XclTokenArrayRef XclExpTableop::CreateCellTokenArray( const XclExpRoot& rRoot ) const
360 XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
361 return mbValid ?
362 rFmlaComp.CreateSpecialRefFormula( EXC_TOKID_TBL, maBaseXclPos ) :
363 rFmlaComp.CreateErrorFormula( EXC_ERR_NA );
366 bool XclExpTableop::IsVolatile() const
368 return true;
371 void XclExpTableop::Save( XclExpStream& rStrm )
373 if( mbValid )
374 XclExpRangeFmlaBase::Save( rStrm );
377 bool XclExpTableop::IsAppendable( sal_uInt16 nXclCol, sal_uInt32 nXclRow ) const
379 return ((nXclCol == mnLastAppXclCol + 1) && (nXclRow == maXclRange.maFirst.mnRow)) ||
380 ((nXclCol == mnLastAppXclCol + 1) && (nXclCol <= maXclRange.maLast.mnCol) && (nXclRow == maXclRange.maLast.mnRow)) ||
381 ((mnLastAppXclCol == maXclRange.maLast.mnCol) && (nXclCol == maXclRange.maFirst.mnCol) && (nXclRow == maXclRange.maLast.mnRow + 1));
384 void XclExpTableop::WriteBody( XclExpStream& rStrm )
386 sal_uInt16 nFlags = EXC_TABLEOP_DEFAULTFLAGS;
387 ::set_flag( nFlags, EXC_TABLEOP_RECALC_ALWAYS, IsVolatile() );
388 switch( mnScMode )
390 case 1: ::set_flag( nFlags, EXC_TABLEOP_ROW ); break;
391 case 2: ::set_flag( nFlags, EXC_TABLEOP_BOTH ); break;
394 WriteRangeAddress( rStrm );
395 rStrm << nFlags;
396 if( mnScMode == 2 )
397 rStrm << mnRowInpXclRow << mnRowInpXclCol << mnColInpXclRow << mnColInpXclCol;
398 else
399 rStrm << mnColInpXclRow << mnColInpXclCol << sal_uInt32( 0 );
402 // ----------------------------------------------------------------------------
404 XclExpTableopBuffer::XclExpTableopBuffer( const XclExpRoot& rRoot ) :
405 XclExpRoot( rRoot )
409 XclExpTableopRef XclExpTableopBuffer::CreateOrExtendTableop(
410 const ScTokenArray& rScTokArr, const ScAddress& rScPos )
412 XclExpTableopRef xRec;
414 // try to extract cell references of a multiple operations formula
415 XclMultipleOpRefs aRefs;
416 if( XclTokenArrayHelper::GetMultipleOpRefs( aRefs, rScTokArr ) )
418 // try to find an existing TABLEOP record for this cell position
419 for( size_t nPos = 0, nSize = maTableopList.GetSize(); !xRec && (nPos < nSize); ++nPos )
421 XclExpTableopRef xTempRec = maTableopList.GetRecord( nPos );
422 if( xTempRec->TryExtend( rScPos, aRefs ) )
423 xRec = xTempRec;
426 // no record found, or found record not extensible
427 if( !xRec )
428 xRec = TryCreate( rScPos, aRefs );
431 return xRec;
434 void XclExpTableopBuffer::Finalize()
436 for( size_t nPos = 0, nSize = maTableopList.GetSize(); nPos < nSize; ++nPos )
437 maTableopList.GetRecord( nPos )->Finalize();
440 XclExpTableopRef XclExpTableopBuffer::TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
442 sal_uInt8 nScMode = 0;
443 bool bOk = (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
444 (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
445 (rScPos.Tab() == rRefs.maColRelScPos.Tab());
447 if( bOk )
449 if( rRefs.mbDblRefMode )
451 nScMode = 2;
452 bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
453 (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
454 (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
455 (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
456 (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
457 (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
458 (rScPos.Row() == rRefs.maRowRelScPos.Row() + 1) &&
459 (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
461 else if( (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
462 (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
463 (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
464 (rScPos.Row() == rRefs.maColRelScPos.Row()) )
466 nScMode = 0;
468 else if( (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
469 (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
470 (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
471 (rScPos.Row() == rRefs.maColRelScPos.Row() + 1) )
473 nScMode = 1;
475 else
477 bOk = false;
481 XclExpTableopRef xRec;
482 if( bOk )
484 xRec.reset( new XclExpTableop( rScPos, rRefs, nScMode ) );
485 maTableopList.AppendRecord( xRec );
488 return xRec;
491 // ============================================================================
492 // Cell records
493 // ============================================================================
495 XclExpCellBase::XclExpCellBase(
496 sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos ) :
497 XclExpRecord( nRecId, nContSize + 4 ),
498 maXclPos( rXclPos )
502 bool XclExpCellBase::IsMultiLineText() const
504 return false;
507 bool XclExpCellBase::TryMerge( const XclExpCellBase& /*rCell*/ )
509 return false;
512 void XclExpCellBase::GetBlankXFIndexes( ScfUInt16Vec& /*rXFIndexes*/ ) const
514 // default: do nothing
517 void XclExpCellBase::RemoveUnusedBlankCells( const ScfUInt16Vec& /*rXFIndexes*/ )
519 // default: do nothing
522 // Single cell records ========================================================
524 XclExpSingleCellBase::XclExpSingleCellBase(
525 sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos, sal_uInt32 nXFId ) :
526 XclExpCellBase( nRecId, 2, rXclPos ),
527 maXFId( nXFId ),
528 mnContSize( nContSize )
532 XclExpSingleCellBase::XclExpSingleCellBase( const XclExpRoot& rRoot,
533 sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos,
534 const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForcedXFId ) :
535 XclExpCellBase( nRecId, 2, rXclPos ),
536 maXFId( nForcedXFId ),
537 mnContSize( nContSize )
539 if( GetXFId() == EXC_XFID_NOTFOUND )
540 SetXFId( rRoot.GetXFBuffer().Insert( pPattern, nScript ) );
543 sal_uInt16 XclExpSingleCellBase::GetLastXclCol() const
545 return GetXclCol();
548 sal_uInt32 XclExpSingleCellBase::GetFirstXFId() const
550 return GetXFId();
553 bool XclExpSingleCellBase::IsEmpty() const
555 return false;
558 void XclExpSingleCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
560 maXFId.ConvertXFIndex( rRoot );
563 void XclExpSingleCellBase::Save( XclExpStream& rStrm )
565 DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
566 AddRecSize( mnContSize );
567 XclExpCellBase::Save( rStrm );
570 void XclExpSingleCellBase::WriteBody( XclExpStream& rStrm )
572 rStrm << GetXclRow() << GetXclCol() << maXFId.mnXFIndex;
573 WriteContents( rStrm );
576 // ----------------------------------------------------------------------------
578 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpNumberCell, 256, 256 )
580 XclExpNumberCell::XclExpNumberCell(
581 const XclExpRoot& rRoot, const XclAddress& rXclPos,
582 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, double fValue ) :
583 // #i41210# always use latin script for number cells - may look wrong for special number formats...
584 XclExpSingleCellBase( rRoot, EXC_ID3_NUMBER, 8, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
585 mfValue( fValue )
589 static OString lcl_GetStyleId( XclExpXmlStream& rStrm, sal_uInt32 nXFIndex )
591 return OString::valueOf( rStrm.GetRoot().GetXFBuffer()
592 .GetXmlCellIndex( nXFIndex ) );
595 static OString lcl_GetStyleId( XclExpXmlStream& rStrm, const XclExpCellBase& rCell )
597 sal_uInt32 nXFId = rCell.GetFirstXFId();
598 sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( nXFId );
599 return lcl_GetStyleId( rStrm, nXFIndex );
602 void XclExpNumberCell::SaveXml( XclExpXmlStream& rStrm )
604 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
605 rWorksheet->startElement( XML_c,
606 XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
607 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
608 XML_t, "n",
609 // OOXTODO: XML_cm, XML_vm, XML_ph
610 FSEND );
611 rWorksheet->startElement( XML_v, FSEND );
612 rWorksheet->write( mfValue );
613 rWorksheet->endElement( XML_v );
614 rWorksheet->endElement( XML_c );
617 void XclExpNumberCell::WriteContents( XclExpStream& rStrm )
619 rStrm << mfValue;
622 // ----------------------------------------------------------------------------
624 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpBooleanCell, 256, 256 )
626 XclExpBooleanCell::XclExpBooleanCell(
627 const XclExpRoot rRoot, const XclAddress& rXclPos,
628 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, bool bValue ) :
629 // #i41210# always use latin script for boolean cells
630 XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
631 mbValue( bValue )
635 void XclExpBooleanCell::SaveXml( XclExpXmlStream& rStrm )
637 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
638 rWorksheet->startElement( XML_c,
639 XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
640 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
641 XML_t, "b",
642 // OOXTODO: XML_cm, XML_vm, XML_ph
643 FSEND );
644 rWorksheet->startElement( XML_v, FSEND );
645 rWorksheet->write( mbValue ? "1" : "0" );
646 rWorksheet->endElement( XML_v );
647 rWorksheet->endElement( XML_c );
650 void XclExpBooleanCell::WriteContents( XclExpStream& rStrm )
652 rStrm << sal_uInt16( mbValue ? 1 : 0 ) << EXC_BOOLERR_BOOL;
655 // ----------------------------------------------------------------------------
657 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpErrorCell, 256, 256 )
659 XclExpErrorCell::XclExpErrorCell(
660 const XclExpRoot rRoot, const XclAddress& rXclPos,
661 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_uInt8 nErrCode ) :
662 // #i41210# always use latin script for error cells
663 XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
664 mnErrCode( nErrCode )
668 void XclExpErrorCell::SaveXml( XclExpXmlStream& rStrm )
670 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
671 rWorksheet->startElement( XML_c,
672 XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
673 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
674 XML_t, "e",
675 // OOXTODO: XML_cm, XML_vm, XML_ph
676 FSEND );
677 rWorksheet->startElement( XML_v, FSEND );
678 rWorksheet->write( (sal_Int32) mnErrCode );
679 rWorksheet->endElement( XML_v );
680 rWorksheet->endElement( XML_c );
683 void XclExpErrorCell::WriteContents( XclExpStream& rStrm )
685 rStrm << mnErrCode << EXC_BOOLERR_ERROR;
688 // ----------------------------------------------------------------------------
690 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpLabelCell, 256, 256 )
692 XclExpLabelCell::XclExpLabelCell(
693 const XclExpRoot& rRoot, const XclAddress& rXclPos,
694 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, const ScStringCell& rCell ) :
695 XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
697 sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
698 XclExpStringRef xText = XclExpStringHelper::CreateCellString( rRoot, rCell, pPattern, EXC_STR_DEFAULT, nMaxLen );
699 Init( rRoot, pPattern, xText );
702 XclExpLabelCell::XclExpLabelCell(
703 const XclExpRoot& rRoot, const XclAddress& rXclPos,
704 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
705 const ScEditCell& rCell, XclExpHyperlinkHelper& rLinkHelper ) :
706 XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
708 sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
709 XclExpStringRef xText = XclExpStringHelper::CreateCellString( rRoot, rCell, pPattern, rLinkHelper, EXC_STR_DEFAULT, nMaxLen );
710 Init( rRoot, pPattern, xText );
713 bool XclExpLabelCell::IsMultiLineText() const
715 return mbLineBreak || mxText->IsWrapped();
718 void XclExpLabelCell::Init( const XclExpRoot& rRoot,
719 const ScPatternAttr* pPattern, XclExpStringRef xText )
721 DBG_ASSERT( xText.is() && xText->Len(), "XclExpLabelCell::XclExpLabelCell - empty string passed" );
722 mxText = xText;
723 mnSstIndex = 0;
725 // create the cell format
726 sal_uInt16 nXclFont = mxText->RemoveLeadingFont();
727 if( GetXFId() == EXC_XFID_NOTFOUND )
729 DBG_ASSERT( nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found" );
730 bool bForceLineBreak = mxText->IsWrapped();
731 SetXFId( rRoot.GetXFBuffer().InsertWithFont( pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak ) );
734 // get auto-wrap attribute from cell format
735 const XclExpXF* pXF = rRoot.GetXFBuffer().GetXFById( GetXFId() );
736 mbLineBreak = pXF && pXF->GetAlignmentData().mbLineBreak;
738 // initialize the record contents
739 switch( rRoot.GetBiff() )
741 case EXC_BIFF5:
742 // BIFF5-BIFF7: create a LABEL or RSTRING record
743 DBG_ASSERT( mxText->Len() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::XclExpLabelCell - string too long" );
744 SetContSize( mxText->GetSize() );
745 // formatted string is exported in an RSTRING record
746 if( mxText->IsRich() )
748 DBG_ASSERT( mxText->GetFormatsCount() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::WriteContents - too many formats" );
749 mxText->LimitFormatCount( EXC_LABEL_MAXLEN );
750 SetRecId( EXC_ID_RSTRING );
751 SetContSize( GetContSize() + 1 + 2 * mxText->GetFormatsCount() );
753 break;
754 case EXC_BIFF8:
755 // BIFF8+: create a LABELSST record
756 mnSstIndex = rRoot.GetSst().Insert( xText );
757 SetRecId( EXC_ID_LABELSST );
758 SetContSize( 4 );
759 break;
760 default: DBG_ERROR_BIFF();
764 void XclExpLabelCell::SaveXml( XclExpXmlStream& rStrm )
766 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
767 rWorksheet->startElement( XML_c,
768 XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
769 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
770 XML_t, "s",
771 // OOXTODO: XML_cm, XML_vm, XML_ph
772 FSEND );
773 rWorksheet->startElement( XML_v, FSEND );
774 rWorksheet->write( (sal_Int32) mnSstIndex );
775 rWorksheet->endElement( XML_v );
776 rWorksheet->endElement( XML_c );
779 void XclExpLabelCell::WriteContents( XclExpStream& rStrm )
781 switch( rStrm.GetRoot().GetBiff() )
783 case EXC_BIFF5:
784 rStrm << *mxText;
785 if( mxText->IsRich() )
787 rStrm << static_cast< sal_uInt8 >( mxText->GetFormatsCount() );
788 mxText->WriteFormats( rStrm );
790 break;
791 case EXC_BIFF8:
792 rStrm << mnSstIndex;
793 break;
794 default: DBG_ERROR_BIFF();
798 // ----------------------------------------------------------------------------
800 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpFormulaCell, 256, 256 )
802 XclExpFormulaCell::XclExpFormulaCell(
803 const XclExpRoot& rRoot, const XclAddress& rXclPos,
804 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
805 const ScFormulaCell& rScFmlaCell,
806 XclExpArrayBuffer& rArrayBfr,
807 XclExpShrfmlaBuffer& rShrfmlaBfr,
808 XclExpTableopBuffer& rTableopBfr ) :
809 XclExpSingleCellBase( EXC_ID2_FORMULA, 0, rXclPos, nForcedXFId ),
810 mrScFmlaCell( const_cast< ScFormulaCell& >( rScFmlaCell ) )
812 // *** Find result number format overwriting cell number format *** -------
814 if( GetXFId() == EXC_XFID_NOTFOUND )
816 SvNumberFormatter& rFormatter = rRoot.GetFormatter();
817 XclExpNumFmtBuffer& rNumFmtBfr = rRoot.GetNumFmtBuffer();
819 // current cell number format
820 ULONG nScNumFmt = pPattern ?
821 GETITEMVALUE( pPattern->GetItemSet(), SfxUInt32Item, ATTR_VALUE_FORMAT, ULONG ) :
822 rNumFmtBfr.GetStandardFormat();
824 // alternative number format passed to XF buffer
825 ULONG nAltScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
826 /* #73420# Xcl doesn't know Boolean number formats, we write
827 "TRUE";"FALSE" (language dependent). Don't do it for automatic
828 formula formats, because Excel gets them right. */
829 /* #i8640# Don't set text format, if we have string results. */
830 short nFormatType = mrScFmlaCell.GetFormatType();
831 if( ((nScNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0) &&
832 (nFormatType != NUMBERFORMAT_LOGICAL) &&
833 (nFormatType != NUMBERFORMAT_TEXT) )
834 nAltScNumFmt = mrScFmlaCell.GetStandardFormat( rFormatter, nScNumFmt );
835 /* #73420# If cell number format is Boolean and automatic formula
836 format is Boolean don't write that ugly special format. */
837 else if( (nFormatType == NUMBERFORMAT_LOGICAL) &&
838 (rFormatter.GetType( nScNumFmt ) == NUMBERFORMAT_LOGICAL) )
839 nAltScNumFmt = rNumFmtBfr.GetStandardFormat();
841 // #i41420# find script type according to result type (always latin for numeric results)
842 sal_Int16 nScript = ApiScriptType::LATIN;
843 bool bForceLineBreak = false;
844 if( nFormatType == NUMBERFORMAT_TEXT )
846 String aResult;
847 mrScFmlaCell.GetString( aResult );
848 bForceLineBreak = mrScFmlaCell.IsMultilineResult();
849 nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult );
851 SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) );
854 // *** Convert the formula token array *** --------------------------------
856 ScAddress aScPos( static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), rRoot.GetCurrScTab() );
857 const ScTokenArray& rScTokArr = *mrScFmlaCell.GetCode();
859 // first try to create multiple operations
860 mxAddRec = rTableopBfr.CreateOrExtendTableop( rScTokArr, aScPos );
862 // no multiple operation found - try to create matrix formula
863 if( !mxAddRec ) switch( static_cast< ScMatrixMode >( mrScFmlaCell.GetMatrixFlag() ) )
865 case MM_FORMULA:
867 // origin of the matrix - find the used matrix range
868 SCCOL nMatWidth;
869 SCROW nMatHeight;
870 mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
871 DBG_ASSERT( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
872 ScRange aMatScRange( aScPos );
873 ScAddress& rMatEnd = aMatScRange.aEnd;
874 rMatEnd.IncCol( static_cast< SCsCOL >( nMatWidth - 1 ) );
875 rMatEnd.IncRow( static_cast< SCsROW >( nMatHeight - 1 ) );
876 // reduce to valid range (range keeps valid, because start position IS valid)
877 rRoot.GetAddressConverter().ValidateRange( aMatScRange, true );
878 // create the ARRAY record
879 mxAddRec = rArrayBfr.CreateArray( rScTokArr, aMatScRange );
881 break;
882 case MM_REFERENCE:
884 // other formula cell covered by a matrix - find the ARRAY record
885 mxAddRec = rArrayBfr.FindArray( rScTokArr );
886 // should always be found, if Calc document is not broken
887 DBG_ASSERT( mxAddRec.is(), "XclExpFormulaCell::XclExpFormulaCell - no matrix found" );
889 break;
890 default:;
893 // no matrix found - try to create shared formula
894 if( !mxAddRec )
895 mxAddRec = rShrfmlaBfr.CreateOrExtendShrfmla( rScTokArr, aScPos );
897 // no shared formula found - create a simple cell formula
898 if( !mxAddRec || ! mxTokArr )
899 mxTokArr = rRoot.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL, rScTokArr, &aScPos );
902 void XclExpFormulaCell::Save( XclExpStream& rStrm )
904 // create token array for FORMULA cells with additional record
905 if( mxAddRec.is() )
906 mxTokArr = mxAddRec->CreateCellTokenArray( rStrm.GetRoot() );
908 // FORMULA record itself
909 DBG_ASSERT( mxTokArr.is(), "XclExpFormulaCell::Save - missing token array" );
910 if( !mxTokArr )
911 mxTokArr = rStrm.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA );
912 SetContSize( 16 + mxTokArr->GetSize() );
913 XclExpSingleCellBase::Save( rStrm );
915 // additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record
916 if( mxAddRec.is() && mxAddRec->IsBasePos( GetXclCol(), GetXclRow() ) )
917 mxAddRec->Save( rStrm );
919 // STRING record for string result
920 if( mxStringRec.is() )
921 mxStringRec->Save( rStrm );
924 void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
926 const char* sType = NULL;
927 OUString sValue;
929 XclXmlUtils::GetFormulaTypeAndValue( mrScFmlaCell, sType, sValue );
930 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
931 rWorksheet->startElement( XML_c,
932 XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
933 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
934 XML_t, sType,
935 // OOXTODO: XML_cm, XML_vm, XML_ph
936 FSEND );
938 rWorksheet->startElement( XML_f,
939 // OOXTODO: XML_t, ST_CellFormulaType
940 XML_aca, XclXmlUtils::ToPsz( mxTokArr->IsVolatile() || (mxAddRec.is() && mxAddRec->IsVolatile()) ),
941 // OOXTODO: XML_ref, ST_Ref
942 // OOXTODO: XML_dt2D, bool
943 // OOXTODO: XML_dtr, bool
944 // OOXTODO: XML_del1, bool
945 // OOXTODO: XML_del2, bool
946 // OOXTODO: XML_r1, ST_CellRef
947 // OOXTODO: XML_r2, ST_CellRef
948 // OOXTODO: XML_ca, bool
949 // OOXTODO: XML_si, uint
950 // OOXTODO: XML_bx bool
951 FSEND );
952 rWorksheet->writeEscaped( XclXmlUtils::ToOUString( *mrScFmlaCell.GetDocument(), mrScFmlaCell.aPos, mrScFmlaCell.GetCode() ) );
953 rWorksheet->endElement( XML_f );
954 if( strcmp( sType, "inlineStr" ) == 0 )
956 rWorksheet->startElement( XML_is, FSEND );
957 rWorksheet->startElement( XML_t, FSEND );
958 rWorksheet->writeEscaped( sValue );
959 rWorksheet->endElement( XML_t );
960 rWorksheet->endElement( XML_is );
962 else
964 rWorksheet->startElement( XML_v, FSEND );
965 rWorksheet->writeEscaped( sValue );
966 rWorksheet->endElement( XML_v );
968 rWorksheet->endElement( XML_c );
971 void XclExpFormulaCell::WriteContents( XclExpStream& rStrm )
973 // result of the formula
974 switch( mrScFmlaCell.GetFormatType() )
976 case NUMBERFORMAT_NUMBER:
978 // either value or error code
979 USHORT nScErrCode = mrScFmlaCell.GetErrCode();
980 if( nScErrCode )
981 rStrm << EXC_FORMULA_RES_ERROR << sal_uInt8( 0 )
982 << XclTools::GetXclErrorCode( nScErrCode )
983 << sal_uInt8( 0 ) << sal_uInt16( 0 )
984 << sal_uInt16( 0xFFFF );
985 else
986 rStrm << mrScFmlaCell.GetValue();
988 break;
990 case NUMBERFORMAT_TEXT:
992 String aResult;
993 mrScFmlaCell.GetString( aResult );
994 if( aResult.Len() || (rStrm.GetRoot().GetBiff() <= EXC_BIFF5) )
996 rStrm << EXC_FORMULA_RES_STRING;
997 mxStringRec.reset( new XclExpStringRec( rStrm.GetRoot(), aResult ) );
999 else
1000 rStrm << EXC_FORMULA_RES_EMPTY; // BIFF8 only
1001 rStrm << sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
1003 break;
1005 case NUMBERFORMAT_LOGICAL:
1007 sal_uInt8 nXclValue = (mrScFmlaCell.GetValue() == 0.0) ? 0 : 1;
1008 rStrm << EXC_FORMULA_RES_BOOL << sal_uInt8( 0 )
1009 << nXclValue << sal_uInt8( 0 ) << sal_uInt16( 0 )
1010 << sal_uInt16( 0xFFFF );
1012 break;
1014 default:
1015 rStrm << mrScFmlaCell.GetValue();
1018 // flags and formula token array
1019 sal_uInt16 nFlags = EXC_FORMULA_DEFAULTFLAGS;
1020 ::set_flag( nFlags, EXC_FORMULA_RECALC_ALWAYS, mxTokArr->IsVolatile() || (mxAddRec.is() && mxAddRec->IsVolatile()) );
1021 ::set_flag( nFlags, EXC_FORMULA_SHARED, mxAddRec.is() && (mxAddRec->GetRecId() == EXC_ID_SHRFMLA) );
1022 rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
1025 // Multiple cell records ======================================================
1027 XclExpMultiCellBase::XclExpMultiCellBase(
1028 sal_uInt16 nRecId, sal_uInt16 nMulRecId, sal_Size nContSize, const XclAddress& rXclPos ) :
1029 XclExpCellBase( nRecId, 0, rXclPos ),
1030 mnMulRecId( nMulRecId ),
1031 mnContSize( nContSize )
1035 sal_uInt16 XclExpMultiCellBase::GetLastXclCol() const
1037 return GetXclCol() + GetCellCount() - 1;
1040 sal_uInt32 XclExpMultiCellBase::GetFirstXFId() const
1042 return maXFIds.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds.front().mnXFId;
1045 bool XclExpMultiCellBase::IsEmpty() const
1047 return maXFIds.empty();
1050 void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
1052 for( XclExpMultiXFIdDeq::iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
1053 aIt->ConvertXFIndex( rRoot );
1056 void XclExpMultiCellBase::Save( XclExpStream& rStrm )
1058 DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1060 XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1061 XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1062 XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1063 sal_uInt16 nBegXclCol = GetXclCol();
1064 sal_uInt16 nEndXclCol = nBegXclCol;
1066 while( aRangeEnd != aEnd )
1068 // find begin of next used XF range
1069 aRangeBeg = aRangeEnd;
1070 nBegXclCol = nEndXclCol;
1071 while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1073 nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1074 ++aRangeBeg;
1076 // find end of next used XF range
1077 aRangeEnd = aRangeBeg;
1078 nEndXclCol = nBegXclCol;
1079 while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1081 nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1082 ++aRangeEnd;
1085 // export this range as a record
1086 if( aRangeBeg != aRangeEnd )
1088 sal_uInt16 nCount = nEndXclCol - nBegXclCol;
1089 bool bIsMulti = nCount > 1;
1090 sal_Size nTotalSize = GetRecSize() + (2 + mnContSize) * nCount;
1091 if( bIsMulti ) nTotalSize += 2;
1093 rStrm.StartRecord( bIsMulti ? mnMulRecId : GetRecId(), nTotalSize );
1094 rStrm << GetXclRow() << nBegXclCol;
1096 sal_uInt16 nRelCol = nBegXclCol - GetXclCol();
1097 for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1099 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1101 rStrm << aIt->mnXFIndex;
1102 WriteContents( rStrm, nRelCol );
1103 ++nRelCol;
1106 if( bIsMulti )
1107 rStrm << static_cast< sal_uInt16 >( nEndXclCol - 1 );
1108 rStrm.EndRecord();
1113 void XclExpMultiCellBase::SaveXml( XclExpXmlStream& rStrm )
1115 XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1116 XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1117 XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1118 sal_uInt16 nBegXclCol = GetXclCol();
1119 sal_uInt16 nEndXclCol = nBegXclCol;
1121 while( aRangeEnd != aEnd )
1123 // find begin of next used XF range
1124 aRangeBeg = aRangeEnd;
1125 nBegXclCol = nEndXclCol;
1126 while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1128 nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1129 ++aRangeBeg;
1131 // find end of next used XF range
1132 aRangeEnd = aRangeBeg;
1133 nEndXclCol = nBegXclCol;
1134 while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1136 nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1137 ++aRangeEnd;
1140 // export this range as a record
1141 if( aRangeBeg != aRangeEnd )
1143 sal_uInt16 nRelColIdx = nBegXclCol - GetXclCol();
1144 sal_Int32 nRelCol = 0;
1145 for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1147 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1149 WriteXmlContents(
1150 rStrm,
1151 XclAddress( static_cast<sal_uInt16>(nBegXclCol + nRelCol), GetXclRow() ),
1152 aIt->mnXFIndex,
1153 nRelColIdx );
1154 ++nRelCol;
1155 ++nRelColIdx;
1162 sal_uInt16 XclExpMultiCellBase::GetCellCount() const
1164 sal_uInt16 nCount = 0;
1165 for( XclExpMultiXFIdDeq::const_iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
1166 nCount = nCount + aIt->mnCount;
1167 return nCount;
1170 void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId& rXFId )
1172 if( maXFIds.empty() || (maXFIds.back().mnXFId != rXFId.mnXFId) )
1173 maXFIds.push_back( rXFId );
1174 else
1175 maXFIds.back().mnCount = maXFIds.back().mnCount + rXFId.mnCount;
1178 void XclExpMultiCellBase::AppendXFId( const XclExpRoot& rRoot,
1179 const ScPatternAttr* pPattern, sal_uInt16 nScript, sal_uInt32 nForcedXFId, sal_uInt16 nCount )
1181 sal_uInt32 nXFId = (nForcedXFId == EXC_XFID_NOTFOUND) ?
1182 rRoot.GetXFBuffer().Insert( pPattern, nScript ) : nForcedXFId;
1183 AppendXFId( XclExpMultiXFId( nXFId, nCount ) );
1186 bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase& rCell )
1188 if( GetLastXclCol() + 1 == rCell.GetXclCol() )
1190 maXFIds.insert( maXFIds.end(), rCell.maXFIds.begin(), rCell.maXFIds.end() );
1191 return true;
1193 return false;
1196 void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1198 DBG_ASSERT( GetLastXclCol() < rXFIndexes.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" );
1199 ScfUInt16Vec::iterator aDestIt = rXFIndexes.begin() + GetXclCol();
1200 for( XclExpMultiXFIdDeq::const_iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
1202 ::std::fill( aDestIt, aDestIt + aIt->mnCount, aIt->mnXFIndex );
1203 aDestIt += aIt->mnCount;
1207 void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes )
1209 // save last column before calling maXFIds.clear()
1210 sal_uInt16 nLastXclCol = GetLastXclCol();
1211 DBG_ASSERT( nLastXclCol < rXFIndexes.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" );
1213 // build new XF index vector, containing passed XF indexes
1214 maXFIds.clear();
1215 XclExpMultiXFId aXFId( 0 );
1216 for( ScfUInt16Vec::const_iterator aIt = rXFIndexes.begin() + GetXclCol(), aEnd = rXFIndexes.begin() + nLastXclCol + 1; aIt != aEnd; ++aIt )
1218 // AppendXFId() tests XclExpXFIndex::mnXFId, set it too
1219 aXFId.mnXFId = aXFId.mnXFIndex = *aIt;
1220 AppendXFId( aXFId );
1223 // remove leading and trailing unused XF indexes
1224 if( !maXFIds.empty() && (maXFIds.front().mnXFIndex == EXC_XF_NOTFOUND) )
1226 SetXclCol( GetXclCol() + maXFIds.front().mnCount );
1227 maXFIds.pop_front();
1229 if( !maXFIds.empty() && (maXFIds.back().mnXFIndex == EXC_XF_NOTFOUND) )
1230 maXFIds.pop_back();
1232 // The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND.
1235 // ----------------------------------------------------------------------------
1237 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpBlankCell, 256, 256 )
1239 XclExpBlankCell::XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId ) :
1240 XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1242 DBG_ASSERT( rXFId.mnCount > 0, "XclExpBlankCell::XclExpBlankCell - invalid count" );
1243 AppendXFId( rXFId );
1246 XclExpBlankCell::XclExpBlankCell(
1247 const XclExpRoot& rRoot, const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
1248 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId ) :
1249 XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1251 DBG_ASSERT( rXclPos.mnCol <= nLastXclCol, "XclExpBlankCell::XclExpBlankCell - invalid column range" );
1252 // #i46627# use default script type instead of ApiScriptType::WEAK
1253 AppendXFId( rRoot, pPattern, rRoot.GetDefApiScript(), nForcedXFId, nLastXclCol - rXclPos.mnCol + 1 );
1256 bool XclExpBlankCell::TryMerge( const XclExpCellBase& rCell )
1258 const XclExpBlankCell* pBlankCell = dynamic_cast< const XclExpBlankCell* >( &rCell );
1259 return pBlankCell && TryMergeXFIds( *pBlankCell );
1262 void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1264 GetXFIndexes( rXFIndexes );
1267 void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes )
1269 RemoveUnusedXFIndexes( rXFIndexes );
1272 void XclExpBlankCell::WriteContents( XclExpStream& /*rStrm*/, sal_uInt16 /*nRelCol*/ )
1276 void XclExpBlankCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 /* nRelCol */ )
1278 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1279 rWorksheet->singleElement( XML_c,
1280 XML_r, XclXmlUtils::ToOString( rAddress ).getStr(),
1281 XML_s, lcl_GetStyleId( rStrm, nXFId ).getStr(),
1282 FSEND );
1285 // ----------------------------------------------------------------------------
1287 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpRkCell, 256, 256 )
1289 XclExpRkCell::XclExpRkCell(
1290 const XclExpRoot& rRoot, const XclAddress& rXclPos,
1291 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_Int32 nRkValue ) :
1292 XclExpMultiCellBase( EXC_ID_RK, EXC_ID_MULRK, 4, rXclPos )
1294 // #i41210# always use latin script for number cells - may look wrong for special number formats...
1295 AppendXFId( rRoot, pPattern, ApiScriptType::LATIN, nForcedXFId );
1296 maRkValues.push_back( nRkValue );
1299 bool XclExpRkCell::TryMerge( const XclExpCellBase& rCell )
1301 const XclExpRkCell* pRkCell = dynamic_cast< const XclExpRkCell* >( &rCell );
1302 if( pRkCell && TryMergeXFIds( *pRkCell ) )
1304 maRkValues.insert( maRkValues.end(), pRkCell->maRkValues.begin(), pRkCell->maRkValues.end() );
1305 return true;
1307 return false;
1310 void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol )
1312 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1313 rWorksheet->startElement( XML_c,
1314 XML_r, XclXmlUtils::ToOString( rAddress ).getStr(),
1315 XML_s, lcl_GetStyleId( rStrm, nXFId ).getStr(),
1316 XML_t, "n",
1317 // OOXTODO: XML_cm, XML_vm, XML_ph
1318 FSEND );
1319 rWorksheet->startElement( XML_v, FSEND );
1320 rWorksheet->write( XclTools::GetDoubleFromRK( maRkValues[ nRelCol ] ) );
1321 rWorksheet->endElement( XML_v );
1322 rWorksheet->endElement( XML_c );
1325 void XclExpRkCell::WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol )
1327 DBG_ASSERT( nRelCol < maRkValues.size(), "XclExpRkCell::WriteContents - overflow error" );
1328 rStrm << maRkValues[ nRelCol ];
1331 // ============================================================================
1332 // Rows and Columns
1333 // ============================================================================
1335 XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows ) :
1336 mpScOLArray( 0 ),
1337 maLevelInfos( SC_OL_MAXDEPTH ),
1338 mnCurrLevel( 0 ),
1339 mbCurrCollapse( false )
1341 if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
1342 mpScOLArray = bRows ? pOutlineTable->GetRowArray() : pOutlineTable->GetColArray();
1344 if( mpScOLArray )
1345 for( USHORT nLevel = 0; nLevel < SC_OL_MAXDEPTH; ++nLevel )
1346 if( ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nLevel, 0 ) )
1347 maLevelInfos[ nLevel ].mnScEndPos = pEntry->GetEnd();
1350 void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos )
1352 if( mpScOLArray )
1354 // find open level index for passed position
1355 USHORT nNewOpenScLevel = 0; // new open level (0-based Calc index)
1356 sal_uInt8 nNewLevel = 0; // new open level (1-based Excel index)
1358 if( mpScOLArray->FindTouchedLevel( nScPos, nScPos, nNewOpenScLevel ) )
1359 nNewLevel = static_cast< sal_uInt8 >( nNewOpenScLevel + 1 );
1360 // else nNewLevel keeps 0 to show that there are no groups
1362 mbCurrCollapse = false;
1363 if( nNewLevel >= mnCurrLevel )
1365 // new level(s) opened, or no level closed - update all level infos
1366 for( USHORT nScLevel = 0; nScLevel <= nNewOpenScLevel; ++nScLevel )
1368 /* In each level: check if a new group is started (there may be
1369 neighbored groups without gap - therefore check ALL levels). */
1370 if( maLevelInfos[ nScLevel ].mnScEndPos < nScPos )
1372 if( ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nScLevel, nScPos ) )
1374 maLevelInfos[ nScLevel ].mnScEndPos = pEntry->GetEnd();
1375 maLevelInfos[ nScLevel ].mbHidden = pEntry->IsHidden();
1380 else
1382 // level(s) closed - check if any of the closed levels are collapsed
1383 // Calc uses 0-based level indexes
1384 USHORT nOldOpenScLevel = mnCurrLevel - 1;
1385 for( USHORT nScLevel = nNewOpenScLevel + 1; !mbCurrCollapse && (nScLevel <= nOldOpenScLevel); ++nScLevel )
1386 mbCurrCollapse = maLevelInfos[ nScLevel ].mbHidden;
1389 // cache new opened level
1390 mnCurrLevel = nNewLevel;
1394 // ----------------------------------------------------------------------------
1396 XclExpGuts::XclExpGuts( const XclExpRoot& rRoot ) :
1397 XclExpRecord( EXC_ID_GUTS, 8 ),
1398 mnColLevels( 0 ),
1399 mnColWidth( 0 ),
1400 mnRowLevels( 0 ),
1401 mnRowWidth( 0 )
1403 if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
1405 // column outline groups
1406 if( const ScOutlineArray* pColArray = pOutlineTable->GetColArray() )
1407 mnColLevels = ulimit_cast< sal_uInt16 >( pColArray->GetDepth(), EXC_OUTLINE_MAX );
1408 if( mnColLevels )
1410 ++mnColLevels;
1411 mnColWidth = 12 * mnColLevels + 5;
1414 // row outline groups
1415 if( const ScOutlineArray* pRowArray = pOutlineTable->GetRowArray() )
1416 mnRowLevels = ulimit_cast< sal_uInt16 >( pRowArray->GetDepth(), EXC_OUTLINE_MAX );
1417 if( mnRowLevels )
1419 ++mnRowLevels;
1420 mnRowWidth = 12 * mnRowLevels + 5;
1425 void XclExpGuts::WriteBody( XclExpStream& rStrm )
1427 rStrm << mnRowWidth << mnColWidth << mnRowLevels << mnColLevels;
1430 // ----------------------------------------------------------------------------
1432 XclExpDimensions::XclExpDimensions( const XclExpRoot& rRoot ) :
1433 mnFirstUsedXclRow( 0 ),
1434 mnFirstFreeXclRow( 0 ),
1435 mnFirstUsedXclCol( 0 ),
1436 mnFirstFreeXclCol( 0 )
1438 switch( rRoot.GetBiff() )
1440 case EXC_BIFF2: SetRecHeader( EXC_ID2_DIMENSIONS, 8 ); break;
1441 case EXC_BIFF3:
1442 case EXC_BIFF4:
1443 case EXC_BIFF5: SetRecHeader( EXC_ID3_DIMENSIONS, 10 ); break;
1444 case EXC_BIFF8: SetRecHeader( EXC_ID3_DIMENSIONS, 14 ); break;
1445 default: DBG_ERROR_BIFF();
1449 void XclExpDimensions::SetDimensions(
1450 sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
1451 sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow )
1453 mnFirstUsedXclRow = nFirstUsedXclRow;
1454 mnFirstFreeXclRow = nFirstFreeXclRow;
1455 mnFirstUsedXclCol = nFirstUsedXclCol;
1456 mnFirstFreeXclCol = nFirstFreeXclCol;
1459 void XclExpDimensions::SaveXml( XclExpXmlStream& rStrm )
1461 ScRange aRange;
1462 aRange.aStart.SetRow( (SCROW) mnFirstUsedXclRow );
1463 aRange.aStart.SetCol( (SCCOL) mnFirstUsedXclCol );
1465 if( mnFirstFreeXclRow != mnFirstUsedXclRow && mnFirstFreeXclCol != mnFirstUsedXclCol )
1467 aRange.aEnd.SetRow( (SCROW) (mnFirstFreeXclRow-1) );
1468 aRange.aEnd.SetCol( (SCCOL) (mnFirstFreeXclCol-1) );
1471 rStrm.GetCurrentStream()->singleElement( XML_dimension,
1472 XML_ref, XclXmlUtils::ToOString( aRange ).getStr(),
1473 FSEND );
1476 void XclExpDimensions::WriteBody( XclExpStream& rStrm )
1478 XclBiff eBiff = rStrm.GetRoot().GetBiff();
1479 if( eBiff == EXC_BIFF8 )
1480 rStrm << mnFirstUsedXclRow << mnFirstFreeXclRow;
1481 else
1482 rStrm << static_cast< sal_uInt16 >( mnFirstUsedXclRow ) << static_cast< sal_uInt16 >( mnFirstFreeXclRow );
1483 rStrm << mnFirstUsedXclCol << mnFirstFreeXclCol;
1484 if( eBiff >= EXC_BIFF3 )
1485 rStrm << sal_uInt16( 0 );
1488 // ============================================================================
1490 namespace {
1492 double lclGetCorrectedColWidth( const XclExpRoot& rRoot, sal_uInt16 nXclColWidth )
1494 long nFontHt = rRoot.GetFontBuffer().GetAppFontData().mnHeight;
1495 return nXclColWidth - XclTools::GetXclDefColWidthCorrection( nFontHt );
1498 } // namespace
1500 // ----------------------------------------------------------------------------
1502 XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot& rRoot ) :
1503 XclExpUInt16Record( EXC_ID_DEFCOLWIDTH, EXC_DEFCOLWIDTH_DEF ),
1504 XclExpRoot( rRoot )
1508 bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth ) const
1510 double fNewColWidth = lclGetCorrectedColWidth( GetRoot(), nXclColWidth );
1511 // exactly matched, if difference is less than 1/16 of a character to the left or to the right
1512 return Abs( static_cast< long >( GetValue() * 256.0 - fNewColWidth + 0.5 ) ) < 16;
1515 void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth )
1517 double fNewColWidth = lclGetCorrectedColWidth( GetRoot(), nXclColWidth );
1518 SetValue( limit_cast< sal_uInt16 >( fNewColWidth / 256.0 + 0.5 ) );
1521 // ----------------------------------------------------------------------------
1523 XclExpColinfo::XclExpColinfo( const XclExpRoot& rRoot,
1524 SCCOL nScCol, SCROW nLastScRow, XclExpColOutlineBuffer& rOutlineBfr ) :
1525 XclExpRecord( EXC_ID_COLINFO, 12 ),
1526 XclExpRoot( rRoot ),
1527 mnWidth( 0 ),
1528 mnFlags( 0 ),
1529 mnFirstXclCol( static_cast< sal_uInt16 >( nScCol ) ),
1530 mnLastXclCol( static_cast< sal_uInt16 >( nScCol ) )
1532 ScDocument& rDoc = GetDoc();
1533 SCTAB nScTab = GetCurrScTab();
1535 // column default format
1536 maXFId.mnXFId = GetXFBuffer().Insert(
1537 rDoc.GetMostUsedPattern( nScCol, 0, nLastScRow, nScTab ), GetDefApiScript() );
1539 // column width
1540 USHORT nScWidth = rDoc.GetColWidth( nScCol, nScTab );
1541 mnWidth = XclTools::GetXclColumnWidth( nScWidth, GetCharWidth() );
1543 // column flags
1544 ::set_flag( mnFlags, EXC_COLINFO_HIDDEN, rDoc.ColHidden(nScCol, nScTab) );
1546 // outline data
1547 rOutlineBfr.Update( nScCol );
1548 ::set_flag( mnFlags, EXC_COLINFO_COLLAPSED, rOutlineBfr.IsCollapsed() );
1549 ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 8, 3 );
1552 sal_uInt16 XclExpColinfo::ConvertXFIndexes()
1554 maXFId.ConvertXFIndex( GetRoot() );
1555 return maXFId.mnXFIndex;
1558 bool XclExpColinfo::IsDefault( const XclExpDefcolwidth& rDefColWidth ) const
1560 return (maXFId.mnXFIndex == EXC_XF_DEFAULTCELL) && (mnFlags == 0) && rDefColWidth.IsDefWidth( mnWidth );
1563 bool XclExpColinfo::TryMerge( const XclExpColinfo& rColInfo )
1565 if( (maXFId.mnXFIndex == rColInfo.maXFId.mnXFIndex) &&
1566 (mnWidth == rColInfo.mnWidth) &&
1567 (mnFlags == rColInfo.mnFlags) &&
1568 (mnLastXclCol + 1 == rColInfo.mnFirstXclCol) )
1570 mnLastXclCol = rColInfo.mnLastXclCol;
1571 return true;
1573 return false;
1576 void XclExpColinfo::WriteBody( XclExpStream& rStrm )
1578 // if last column is equal to last possible column, Excel adds one more
1579 sal_uInt16 nLastXclCol = mnLastXclCol;
1580 if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
1581 ++nLastXclCol;
1583 rStrm << mnFirstXclCol
1584 << nLastXclCol
1585 << mnWidth
1586 << maXFId.mnXFIndex
1587 << mnFlags
1588 << sal_uInt16( 0 );
1591 void XclExpColinfo::SaveXml( XclExpXmlStream& rStrm )
1593 // if last column is equal to last possible column, Excel adds one more
1594 sal_uInt16 nLastXclCol = mnLastXclCol;
1595 if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
1596 ++nLastXclCol;
1598 rStrm.GetCurrentStream()->singleElement( XML_col,
1599 // OOXTODO: XML_bestFit,
1600 XML_collapsed, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_COLINFO_COLLAPSED ) ),
1601 // OOXTODO: XML_customWidth,
1602 XML_hidden, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_COLINFO_HIDDEN ) ),
1603 XML_max, OString::valueOf( (sal_Int32) (nLastXclCol+1) ).getStr(),
1604 XML_min, OString::valueOf( (sal_Int32) (mnFirstXclCol+1) ).getStr(),
1605 // OOXTODO: XML_outlineLevel,
1606 // OOXTODO: XML_phonetic,
1607 XML_style, lcl_GetStyleId( rStrm, maXFId.mnXFIndex ).getStr(),
1608 XML_width, OString::valueOf( (double) (mnWidth / 255.0) ).getStr(),
1609 FSEND );
1612 // ----------------------------------------------------------------------------
1614 XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot& rRoot ) :
1615 XclExpRoot( rRoot ),
1616 maDefcolwidth( rRoot ),
1617 maOutlineBfr( rRoot )
1621 void XclExpColinfoBuffer::Initialize( SCROW nLastScRow )
1624 for( sal_uInt16 nScCol = 0, nLastScCol = GetMaxPos().Col(); nScCol <= nLastScCol; ++nScCol )
1625 maColInfos.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol, nLastScRow, maOutlineBfr ) );
1628 void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes )
1630 rXFIndexes.clear();
1631 rXFIndexes.reserve( maColInfos.GetSize() );
1633 size_t nPos, nSize;
1635 // do not cache the record list size, it may change in the loop
1636 for( nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
1638 XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
1639 xRec->ConvertXFIndexes();
1641 // try to merge with previous record
1642 if( nPos > 0 )
1644 XclExpColinfoRef xPrevRec = maColInfos.GetRecord( nPos - 1 );
1645 if( xPrevRec->TryMerge( *xRec ) )
1646 // adjust nPos to get the next COLINFO record at the same position
1647 maColInfos.RemoveRecord( nPos-- );
1651 // put XF indexes into passed vector, collect use count of all different widths
1652 typedef ::std::map< sal_uInt16, sal_uInt16 > XclExpWidthMap;
1653 XclExpWidthMap aWidthMap;
1654 sal_uInt16 nMaxColCount = 0;
1655 sal_uInt16 nMaxUsedWidth = 0;
1656 for( nPos = 0, nSize = maColInfos.GetSize(); nPos < nSize; ++nPos )
1658 XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
1659 sal_uInt16 nColCount = xRec->GetColCount();
1661 // add XF index to passed vector
1662 rXFIndexes.resize( rXFIndexes.size() + nColCount, xRec->GetXFIndex() );
1664 // collect use count of column width
1665 sal_uInt16 nWidth = xRec->GetColWidth();
1666 sal_uInt16& rnMapCount = aWidthMap[ nWidth ];
1667 rnMapCount = rnMapCount + nColCount;
1668 if( rnMapCount > nMaxColCount )
1670 nMaxColCount = rnMapCount;
1671 nMaxUsedWidth = nWidth;
1674 maDefcolwidth.SetDefWidth( nMaxUsedWidth );
1676 // remove all default COLINFO records
1677 nPos = 0;
1678 while( nPos < maColInfos.GetSize() )
1680 XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
1681 if( xRec->IsDefault( maDefcolwidth ) )
1682 maColInfos.RemoveRecord( nPos );
1683 else
1684 ++nPos;
1688 void XclExpColinfoBuffer::Save( XclExpStream& rStrm )
1690 // DEFCOLWIDTH
1691 maDefcolwidth.Save( rStrm );
1692 // COLINFO records
1693 maColInfos.Save( rStrm );
1696 void XclExpColinfoBuffer::SaveXml( XclExpXmlStream& rStrm )
1698 if( maColInfos.IsEmpty() )
1699 return;
1701 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1702 rWorksheet->startElement( XML_cols,
1703 FSEND );
1704 maColInfos.SaveXml( rStrm );
1705 rWorksheet->endElement( XML_cols );
1708 // ============================================================================
1710 XclExpDefaultRowData::XclExpDefaultRowData() :
1711 mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1712 mnHeight( EXC_DEFROW_DEFAULTHEIGHT )
1716 XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow& rRow ) :
1717 mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1718 mnHeight( rRow.GetHeight() )
1720 ::set_flag( mnFlags, EXC_DEFROW_HIDDEN, rRow.IsHidden() );
1721 ::set_flag( mnFlags, EXC_DEFROW_UNSYNCED, rRow.IsUnsynced() );
1724 bool operator<( const XclExpDefaultRowData& rLeft, const XclExpDefaultRowData& rRight )
1726 return (rLeft.mnHeight < rRight.mnHeight) ||
1727 ((rLeft.mnHeight == rRight.mnHeight) && (rLeft.mnFlags < rRight.mnFlags));
1730 // ----------------------------------------------------------------------------
1732 XclExpDefrowheight::XclExpDefrowheight() :
1733 XclExpRecord( EXC_ID3_DEFROWHEIGHT, 4 )
1737 void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData& rDefData )
1739 maDefData = rDefData;
1742 void XclExpDefrowheight::WriteBody( XclExpStream& rStrm )
1744 DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1745 rStrm << maDefData.mnFlags << maDefData.mnHeight;
1748 // ----------------------------------------------------------------------------
1750 XclExpRow::XclExpRow( const XclExpRoot& rRoot, sal_uInt32 nXclRow,
1751 XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty ) :
1752 XclExpRecord( EXC_ID3_ROW, 16 ),
1753 XclExpRoot( rRoot ),
1754 mnXclRow( nXclRow ),
1755 mnHeight( 0 ),
1756 mnFlags( EXC_ROW_DEFAULTFLAGS ),
1757 mnXFIndex( EXC_XF_DEFAULTCELL ),
1758 mnOutlineLevel( 0 ),
1759 mbAlwaysEmpty( bAlwaysEmpty ),
1760 mbEnabled( true )
1762 SCTAB nScTab = GetCurrScTab();
1763 SCROW nScRow = static_cast< SCROW >( mnXclRow );
1765 // *** Row flags *** ------------------------------------------------------
1767 BYTE nRowFlags = GetDoc().GetRowFlags( nScRow, nScTab );
1768 bool bUserHeight = ::get_flag< BYTE >( nRowFlags, CR_MANUALSIZE );
1769 bool bHidden = GetDoc().RowHidden(nScRow, nScTab);
1770 ::set_flag( mnFlags, EXC_ROW_UNSYNCED, bUserHeight );
1771 ::set_flag( mnFlags, EXC_ROW_HIDDEN, bHidden );
1773 // *** Row height *** -----------------------------------------------------
1775 USHORT nScHeight = GetDoc().GetRowHeight( nScRow, nScTab );
1776 if( nScHeight == 0 )
1778 ::set_flag( mnFlags, EXC_ROW_HIDDEN );
1779 mnHeight = EXC_ROW_DEFAULTHEIGHT;
1781 else
1783 // Calc and Excel use twips
1784 mnHeight = static_cast< sal_uInt16 >( nScHeight );
1786 // #76250# not usable in Applix
1787 // ::set_flag( mnHeight, EXC_ROW_FLAGDEFHEIGHT, !bUserHeight );
1789 // *** Outline data *** ---------------------------------------------------
1791 rOutlineBfr.Update( nScRow );
1792 ::set_flag( mnFlags, EXC_ROW_COLLAPSED, rOutlineBfr.IsCollapsed() );
1793 ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 0, 3 );
1794 mnOutlineLevel = rOutlineBfr.GetLevel();
1796 // *** Progress bar *** ---------------------------------------------------
1798 XclExpProgressBar& rProgress = GetProgressBar();
1799 rProgress.IncRowRecordCount();
1800 rProgress.Progress();
1803 void XclExpRow::AppendCell( XclExpCellRef xCell, bool bIsMergedBase )
1805 DBG_ASSERT( !mbAlwaysEmpty, "XclExpRow::AppendCell - row is marked to be always empty" );
1806 // try to merge with last existing cell
1807 InsertCell( xCell, maCellList.GetSize(), bIsMergedBase );
1810 void XclExpRow::Finalize( const ScfUInt16Vec& rColXFIndexes )
1812 size_t nPos, nSize;
1814 // *** Convert XF identifiers *** -----------------------------------------
1816 // additionally collect the blank XF indexes
1817 size_t nColCount = GetMaxPos().Col() + 1;
1818 DBG_ASSERT( rColXFIndexes.size() == nColCount, "XclExpRow::Finalize - wrong column XF index count" );
1820 ScfUInt16Vec aXFIndexes( nColCount, EXC_XF_NOTFOUND );
1821 for( nPos = 0, nSize = maCellList.GetSize(); nPos < nSize; ++nPos )
1823 XclExpCellRef xCell = maCellList.GetRecord( nPos );
1824 xCell->ConvertXFIndexes( GetRoot() );
1825 xCell->GetBlankXFIndexes( aXFIndexes );
1828 // *** Fill gaps with BLANK/MULBLANK cell records *** ---------------------
1830 /* This is needed because nonexistant cells in Calc are not formatted at all,
1831 but in Excel they would have the column default format. Blank cells that
1832 are equal to the respective column default are removed later in this function. */
1833 if( !mbAlwaysEmpty )
1835 // XF identifier representing default cell XF
1836 XclExpMultiXFId aXFId( XclExpXFBuffer::GetDefCellXFId() );
1837 aXFId.ConvertXFIndex( GetRoot() );
1839 nPos = 0;
1840 while( nPos <= maCellList.GetSize() ) // don't cache list size, may change in the loop
1842 // get column index that follows previous cell
1843 sal_uInt16 nFirstFreeXclCol = (nPos > 0) ? (maCellList.GetRecord( nPos - 1 )->GetLastXclCol() + 1) : 0;
1844 // get own column index
1845 sal_uInt16 nNextUsedXclCol = (nPos < maCellList.GetSize()) ? maCellList.GetRecord( nPos )->GetXclCol() : (GetMaxPos().Col() + 1);
1847 // is there a gap?
1848 if( nFirstFreeXclCol < nNextUsedXclCol )
1850 aXFId.mnCount = nNextUsedXclCol - nFirstFreeXclCol;
1851 XclExpCellRef xNewCell( new XclExpBlankCell( XclAddress( nFirstFreeXclCol, mnXclRow ), aXFId ) );
1852 // insert the cell, InsertCell() may merge it with existing BLANK records
1853 InsertCell( xNewCell, nPos, false );
1854 // insert default XF indexes into aXFIndexes
1855 ::std::fill( aXFIndexes.begin() + nFirstFreeXclCol,
1856 aXFIndexes.begin() + nNextUsedXclCol, aXFId.mnXFIndex );
1857 // don't step forward with nPos, InsertCell() may remove records
1859 else
1860 ++nPos;
1864 // *** Find default row format *** ----------------------------------------
1866 ScfUInt16Vec::iterator aCellBeg = aXFIndexes.begin(), aCellEnd = aXFIndexes.end(), aCellIt;
1867 ScfUInt16Vec::const_iterator aColBeg = rColXFIndexes.begin(), aColIt;
1869 // find most used XF index in the row
1870 typedef ::std::map< sal_uInt16, size_t > XclExpXFIndexMap;
1871 XclExpXFIndexMap aIndexMap;
1872 sal_uInt16 nRowXFIndex = EXC_XF_DEFAULTCELL;
1873 size_t nMaxXFCount = 0;
1874 for( aCellIt = aCellBeg; aCellIt != aCellEnd; ++aCellIt )
1876 if( *aCellIt != EXC_XF_NOTFOUND )
1878 size_t& rnCount = aIndexMap[ *aCellIt ];
1879 ++rnCount;
1880 if( rnCount > nMaxXFCount )
1882 nRowXFIndex = *aCellIt;
1883 nMaxXFCount = rnCount;
1888 // decide whether to use the row default XF index or column default XF indexes
1889 bool bUseColDefXFs = nRowXFIndex == EXC_XF_DEFAULTCELL;
1890 if( !bUseColDefXFs )
1892 // count needed XF indexes for blank cells with and without row default XF index
1893 size_t nXFCountWithRowDefXF = 0;
1894 size_t nXFCountWithoutRowDefXF = 0;
1895 for( aCellIt = aCellBeg, aColIt = aColBeg; aCellIt != aCellEnd; ++aCellIt, ++aColIt )
1897 sal_uInt16 nXFIndex = *aCellIt;
1898 if( nXFIndex != EXC_XF_NOTFOUND )
1900 if( nXFIndex != nRowXFIndex )
1901 ++nXFCountWithRowDefXF; // with row default XF index
1902 if( nXFIndex != *aColIt )
1903 ++nXFCountWithoutRowDefXF; // without row default XF index
1907 // use column XF indexes if this would cause less or equal number of BLANK records
1908 bUseColDefXFs = nXFCountWithoutRowDefXF <= nXFCountWithRowDefXF;
1911 // *** Remove unused BLANK cell records *** -------------------------------
1913 if( bUseColDefXFs )
1915 // use column default XF indexes
1916 // #i194#: remove cell XF indexes equal to column default XF indexes
1917 for( aCellIt = aCellBeg, aColIt = aColBeg; aCellIt != aCellEnd; ++aCellIt, ++aColIt )
1918 if( *aCellIt == *aColIt )
1919 *aCellIt = EXC_XF_NOTFOUND;
1921 else
1923 // use row default XF index
1924 mnXFIndex = nRowXFIndex;
1925 ::set_flag( mnFlags, EXC_ROW_USEDEFXF );
1926 // #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index
1927 for( aCellIt = aCellBeg; aCellIt != aCellEnd; ++aCellIt )
1928 if( *aCellIt == nRowXFIndex )
1929 *aCellIt = EXC_XF_NOTFOUND;
1932 // remove unused parts of BLANK/MULBLANK cell records
1933 nPos = 0;
1934 while( nPos < maCellList.GetSize() ) // do not cache list size, may change in the loop
1936 XclExpCellRef xCell = maCellList.GetRecord( nPos );
1937 xCell->RemoveUnusedBlankCells( aXFIndexes );
1938 if( xCell->IsEmpty() )
1939 maCellList.RemoveRecord( nPos );
1940 else
1941 ++nPos;
1944 // progress bar includes disabled rows
1945 GetProgressBar().Progress();
1948 sal_uInt16 XclExpRow::GetFirstUsedXclCol() const
1950 return maCellList.IsEmpty() ? 0 : maCellList.GetFirstRecord()->GetXclCol();
1953 sal_uInt16 XclExpRow::GetFirstFreeXclCol() const
1955 return maCellList.IsEmpty() ? 0 : (maCellList.GetLastRecord()->GetLastXclCol() + 1);
1958 bool XclExpRow::IsDefaultable() const
1960 const sal_uInt16 nAllowedFlags = EXC_ROW_DEFAULTFLAGS | EXC_ROW_HIDDEN | EXC_ROW_UNSYNCED;
1961 return !::get_flag( mnFlags, static_cast< sal_uInt16 >( ~nAllowedFlags ) ) && IsEmpty();
1964 void XclExpRow::DisableIfDefault( const XclExpDefaultRowData& rDefRowData )
1966 mbEnabled = !IsDefaultable() ||
1967 (mnHeight != rDefRowData.mnHeight) ||
1968 (IsHidden() != rDefRowData.IsHidden()) ||
1969 (IsUnsynced() != rDefRowData.IsUnsynced());
1972 void XclExpRow::WriteCellList( XclExpStream& rStrm )
1974 DBG_ASSERT( mbEnabled || maCellList.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" );
1975 maCellList.Save( rStrm );
1978 void XclExpRow::Save( XclExpStream& rStrm )
1980 if( mbEnabled )
1981 XclExpRecord::Save( rStrm );
1984 void XclExpRow::InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase )
1986 DBG_ASSERT( xCell.is(), "XclExpRow::InsertCell - missing cell" );
1988 /* #109751# If we have a multi-line text in a merged cell, and the resulting
1989 row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED
1990 flag to be true to ensure Excel works correctly. */
1991 if( bIsMergedBase && xCell->IsMultiLineText() )
1992 ::set_flag( mnFlags, EXC_ROW_UNSYNCED );
1994 // try to merge with previous cell, insert the new cell if not successful
1995 XclExpCellRef xPrevCell = maCellList.GetRecord( nPos - 1 );
1996 if( xPrevCell.is() && xPrevCell->TryMerge( *xCell ) )
1997 xCell = xPrevCell;
1998 else
1999 maCellList.InsertRecord( xCell, nPos++ );
2000 // nPos points now to following cell
2002 // try to merge with following cell, remove it if successful
2003 XclExpCellRef xNextCell = maCellList.GetRecord( nPos );
2004 if( xNextCell.is() && xCell->TryMerge( *xNextCell ) )
2005 maCellList.RemoveRecord( nPos );
2008 void XclExpRow::WriteBody( XclExpStream& rStrm )
2010 rStrm << mnXclRow
2011 << GetFirstUsedXclCol()
2012 << GetFirstFreeXclCol()
2013 << mnHeight
2014 << sal_uInt32( 0 )
2015 << mnFlags
2016 << mnXFIndex;
2019 void XclExpRow::SaveXml( XclExpXmlStream& rStrm )
2021 if( !mbEnabled )
2022 return;
2023 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2024 bool haveFormat = ::get_flag( mnFlags, EXC_ROW_USEDEFXF );
2025 rWorksheet->startElement( XML_row,
2026 XML_r, OString::valueOf( (sal_Int32) (mnXclRow+1) ).getStr(),
2027 // OOXTODO: XML_spans, optional
2028 XML_s, haveFormat ? lcl_GetStyleId( rStrm, mnXFIndex ).getStr() : NULL,
2029 XML_customFormat, XclXmlUtils::ToPsz( haveFormat ),
2030 XML_ht, OString::valueOf( (double) mnHeight / 20.0 ).getStr(),
2031 XML_hidden, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_HIDDEN ) ),
2032 XML_customHeight, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_UNSYNCED ) ),
2033 XML_outlineLevel, OString::valueOf( (sal_Int32) mnOutlineLevel ).getStr(),
2034 XML_collapsed, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_COLLAPSED ) ),
2035 // OOXTODO: XML_thickTop, bool
2036 // OOXTODO: XML_thickBot, bool
2037 // OOXTODO: XML_ph, bool
2038 FSEND );
2039 // OOXTODO: XML_extLst
2040 maCellList.SaveXml( rStrm );
2041 rWorksheet->endElement( XML_row );
2044 // ----------------------------------------------------------------------------
2046 XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot& rRoot ) :
2047 XclExpRoot( rRoot ),
2048 maOutlineBfr( rRoot ),
2049 maDimensions( rRoot ),
2050 mpLastUsedRow( 0 ),
2051 mnLastUsedXclRow( 0 )
2055 void XclExpRowBuffer::AppendCell( XclExpCellRef xCell, bool bIsMergedBase )
2057 DBG_ASSERT( xCell.is(), "XclExpRowBuffer::AppendCell - missing cell" );
2058 GetOrCreateRow( xCell->GetXclRow(), false ).AppendCell( xCell, bIsMergedBase );
2061 void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow )
2063 if( nFirstFreeScRow > 0 )
2064 GetOrCreateRow( static_cast< sal_uInt32 >( nFirstFreeScRow - 1 ), true );
2067 void XclExpRowBuffer::Finalize( XclExpDefaultRowData& rDefRowData, const ScfUInt16Vec& rColXFIndexes )
2069 size_t nPos, nSize;
2071 // *** Finalize all rows *** ----------------------------------------------
2073 GetProgressBar().ActivateFinalRowsSegment();
2075 // unused blank cell records will be removed
2076 for( nPos = 0, nSize = maRowList.GetSize(); nPos < nSize; ++nPos )
2077 maRowList.GetRecord( nPos )->Finalize( rColXFIndexes );
2079 // *** Default row format *** ---------------------------------------------
2081 typedef ::std::map< XclExpDefaultRowData, size_t > XclExpDefRowDataMap;
2082 XclExpDefRowDataMap aDefRowMap;
2084 // find default row format for rows beyond used area
2085 sal_uInt32 nDefaultXclRow = maRowList.IsEmpty() ? 0 : (maRowList.GetLastRecord()->GetXclRow() + 1);
2086 XclExpDefaultRowData aMaxDefData;
2087 size_t nMaxDefCount = 0;
2088 /* #i30411# Files saved with SO7/OOo1.x with nonstandard default column
2089 formatting cause big Excel files, because all rows from row 1 to row
2090 32000 are exported. Now, if the used area goes exactly to row 32000,
2091 ignore all rows >32000.
2092 #i59220# Tolerance of +-128 rows for inserted/removed rows. */
2093 if( (nDefaultXclRow < 31872) || (nDefaultXclRow > 32128) )
2095 sal_uInt32 nLastXclRow = static_cast< sal_uInt32 >( GetMaxPos().Row() );
2096 if( nDefaultXclRow <= nLastXclRow )
2098 // create a dummy ROW record and fill aMaxDefData
2099 XclExpRowOutlineBuffer aOutlineBfr( GetRoot() );
2100 XclExpRow aRow( GetRoot(), nLastXclRow, aOutlineBfr, true );
2101 aMaxDefData = XclExpDefaultRowData( aRow );
2102 aDefRowMap[ aMaxDefData ] = nMaxDefCount =
2103 static_cast< size_t >( nLastXclRow - nDefaultXclRow + 1 );
2107 // only look for default format in existing rows, if there are more than unused
2108 nSize = maRowList.GetSize();
2109 if( nMaxDefCount < nSize )
2111 for( nPos = 0; nPos < nSize; ++nPos )
2113 XclExpRowRef xRow = maRowList.GetRecord( nPos );
2114 /* Collect formats of unused rows (rows without cells), which are able
2115 to be defaulted (i.e. no explicit format or outline level). */
2116 if( xRow->IsDefaultable() )
2118 XclExpDefaultRowData aDefData( *xRow );
2119 size_t& rnDefCount = aDefRowMap[ aDefData ];
2120 ++rnDefCount;
2121 if( rnDefCount > nMaxDefCount )
2123 nMaxDefCount = rnDefCount;
2124 aMaxDefData = aDefData;
2130 // return the default row format to caller
2131 rDefRowData = aMaxDefData;
2133 // *** Disable unused ROW records, find used area *** ---------------------
2135 sal_uInt16 nFirstUsedXclCol = SAL_MAX_UINT16;
2136 sal_uInt16 nFirstFreeXclCol = 0;
2137 sal_uInt32 nFirstUsedXclRow = SAL_MAX_UINT32;
2138 sal_uInt32 nFirstFreeXclRow = 0;
2140 for( nPos = 0, nSize = maRowList.GetSize(); nPos < nSize; ++nPos )
2142 XclExpRowRef xRow = maRowList.GetRecord( nPos );
2144 // disable unused rows
2145 xRow->DisableIfDefault( aMaxDefData );
2147 // find used column range
2148 if( !xRow->IsEmpty() ) // empty rows return (0...0) as used range
2150 nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, xRow->GetFirstUsedXclCol() );
2151 nFirstFreeXclCol = ::std::max( nFirstFreeXclCol, xRow->GetFirstFreeXclCol() );
2154 // find used row range
2155 if( xRow->IsEnabled() )
2157 sal_uInt32 nXclRow = xRow->GetXclRow();
2158 nFirstUsedXclRow = ::std::min< sal_uInt32 >( nFirstUsedXclRow, nXclRow );
2159 nFirstFreeXclRow = ::std::max< sal_uInt32 >( nFirstFreeXclRow, nXclRow + 1 );
2163 // adjust start position, if there are no or only empty/disabled ROW records
2164 nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, nFirstFreeXclCol );
2165 nFirstUsedXclRow = ::std::min( nFirstUsedXclRow, nFirstFreeXclRow );
2167 // initialize the DIMENSIONS record
2168 maDimensions.SetDimensions(
2169 nFirstUsedXclCol, nFirstUsedXclRow, nFirstFreeXclCol, nFirstFreeXclRow );
2172 void XclExpRowBuffer::Save( XclExpStream& rStrm )
2174 // DIMENSIONS record
2175 maDimensions.Save( rStrm );
2177 // save in blocks of 32 rows, each block contains first all ROWs, then all cells
2178 size_t nSize = maRowList.GetSize();
2179 size_t nBlockStart = 0;
2180 sal_uInt32 nStartXclRow = (nSize == 0) ? 0 : maRowList.GetRecord( 0 )->GetXclRow();
2182 while( nBlockStart < nSize )
2184 // find end of row block
2185 size_t nBlockEnd = nBlockStart + 1;
2186 while( (nBlockEnd < nSize) && (maRowList.GetRecord( nBlockEnd )->GetXclRow() - nStartXclRow < EXC_ROW_ROWBLOCKSIZE) )
2187 ++nBlockEnd;
2189 // write the ROW records
2190 size_t nPos;
2191 for( nPos = nBlockStart; nPos < nBlockEnd; ++nPos )
2192 maRowList.GetRecord( nPos )->Save( rStrm );
2194 // write the cell records
2195 for( nPos = nBlockStart; nPos < nBlockEnd; ++nPos )
2196 maRowList.GetRecord( nPos )->WriteCellList( rStrm );
2198 nBlockStart = nBlockEnd;
2199 nStartXclRow += EXC_ROW_ROWBLOCKSIZE;
2203 void XclExpRowBuffer::SaveXml( XclExpXmlStream& rStrm )
2205 sal_Int32 nNonEmpty = 0;
2207 size_t nRows = maRowList.GetSize();
2208 for( size_t i = 0; i < nRows; ++i)
2209 if( maRowList.GetRecord( i )->IsEnabled() )
2210 ++nNonEmpty;
2212 if( nNonEmpty == 0 )
2214 rStrm.GetCurrentStream()->singleElement( XML_sheetData, FSEND );
2216 else
2218 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2219 rWorksheet->startElement( XML_sheetData, FSEND );
2220 maRowList.SaveXml( rStrm );
2221 rWorksheet->endElement( XML_sheetData );
2225 XclExpDimensions* XclExpRowBuffer::GetDimensions()
2227 return &maDimensions;
2230 XclExpRow& XclExpRowBuffer::GetOrCreateRow( sal_uInt32 nXclRow, bool bRowAlwaysEmpty )
2232 if( !mpLastUsedRow || (mnLastUsedXclRow != nXclRow) )
2234 // fill up missing ROW records
2235 // do not use sal_uInt16 for nFirstFreeXclRow, would cause loop in full sheets
2236 for( size_t nFirstFreeXclRow = maRowList.GetSize(); nFirstFreeXclRow <= nXclRow; ++nFirstFreeXclRow )
2237 maRowList.AppendNewRecord( new XclExpRow(
2238 GetRoot(), static_cast< sal_uInt32 >( nFirstFreeXclRow ), maOutlineBfr, bRowAlwaysEmpty ) );
2240 mpLastUsedRow = maRowList.GetRecord( nXclRow ).get();
2241 mnLastUsedXclRow = nXclRow;
2243 return *mpLastUsedRow;
2246 // ============================================================================
2247 // Cell Table
2248 // ============================================================================
2250 XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) :
2251 XclExpRoot( rRoot ),
2252 maColInfoBfr( rRoot ),
2253 maRowBfr( rRoot ),
2254 maArrayBfr( rRoot ),
2255 maShrfmlaBfr( rRoot ),
2256 maTableopBfr( rRoot ),
2257 mxDefrowheight( new XclExpDefrowheight ),
2258 mxGuts( new XclExpGuts( rRoot ) ),
2259 mxNoteList( new XclExpNoteList ),
2260 mxMergedcells( new XclExpMergedcells( rRoot ) ),
2261 mxHyperlinkList( new XclExpHyperlinkList ),
2262 mxDval( new XclExpDval( rRoot ) )
2264 ScDocument& rDoc = GetDoc();
2265 SCTAB nScTab = GetCurrScTab();
2266 SvNumberFormatter& rFormatter = GetFormatter();
2268 // maximum sheet limits
2269 SCCOL nMaxScCol = GetMaxPos().Col();
2270 SCROW nMaxScRow = GetMaxPos().Row();
2272 // find used area (non-empty cells)
2273 SCCOL nLastUsedScCol;
2274 SCROW nLastUsedScRow;
2275 rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
2277 ScRange aUsedRange( 0, 0, nScTab, nLastUsedScCol, nLastUsedScRow, nScTab );
2278 GetAddressConverter().ValidateRange( aUsedRange, true );
2279 nLastUsedScCol = aUsedRange.aEnd.Col();
2280 nLastUsedScRow = aUsedRange.aEnd.Row();
2282 // first row without any set attributes (height/hidden/...)
2283 SCROW nFirstUnflaggedScRow = rDoc.GetLastFlaggedRow( nScTab ) + 1;
2285 // find range of outlines
2286 SCROW nFirstUngroupedScRow = 0;
2287 if( const ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable( nScTab ) )
2289 SCCOLROW nScStartPos, nScEndPos;
2290 if( const ScOutlineArray* pRowArray = pOutlineTable->GetRowArray() )
2292 pRowArray->GetRange( nScStartPos, nScEndPos );
2293 // +1 because open/close button is in next row in Excel, +1 for "end->first unused"
2294 nFirstUngroupedScRow = static_cast< SCROW >( nScEndPos + 2 );
2298 // column settings
2299 /* #i30411# Files saved with SO7/OOo1.x with nonstandard default column
2300 formatting cause big Excel files, because all rows from row 1 to row
2301 32000 are exported. Now, if the used area goes exactly to row 32000,
2302 use this row as default and ignore all rows >32000.
2303 #i59220# Tolerance of +-128 rows for inserted/removed rows. */
2304 if( (31871 <= nLastUsedScRow) && (nLastUsedScRow <= 32127) && (nFirstUnflaggedScRow < nLastUsedScRow) && (nFirstUngroupedScRow <= nLastUsedScRow) )
2305 nMaxScRow = nLastUsedScRow;
2306 maColInfoBfr.Initialize( nMaxScRow );
2308 // range for cell iterator
2309 SCCOL nLastIterScCol = nMaxScCol;
2310 SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow + 128, nMaxScRow );
2311 ScUsedAreaIterator aIt( &rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow );
2313 // activate the correct segment and sub segment at the progress bar
2314 GetProgressBar().ActivateCreateRowsSegment();
2316 for( bool bIt = aIt.GetNext(); bIt; bIt = aIt.GetNext() )
2318 SCCOL nScCol = aIt.GetStartCol();
2319 SCROW nScRow = aIt.GetRow();
2320 SCCOL nLastScCol = aIt.GetEndCol();
2321 ScAddress aScPos( nScCol, nScRow, nScTab );
2323 XclAddress aXclPos( static_cast< sal_uInt16 >( nScCol ), static_cast< sal_uInt32 >( nScRow ) );
2324 sal_uInt16 nLastXclCol = static_cast< sal_uInt16 >( nLastScCol );
2326 const ScBaseCell* pScCell = aIt.GetCell();
2327 XclExpCellRef xCell;
2329 const ScPatternAttr* pPattern = aIt.GetPattern();
2331 // handle overlapped merged cells before creating the cell record
2332 sal_uInt32 nMergeBaseXFId = EXC_XFID_NOTFOUND;
2333 bool bIsMergedBase = false;
2334 if( pPattern )
2336 const SfxItemSet& rItemSet = pPattern->GetItemSet();
2337 // base cell in a merged range
2338 const ScMergeAttr& rMergeItem = GETITEM( rItemSet, ScMergeAttr, ATTR_MERGE );
2339 bIsMergedBase = rMergeItem.IsMerged();
2340 /* overlapped cell in a merged range; in Excel all merged cells
2341 must contain same XF index, for correct border */
2342 const ScMergeFlagAttr& rMergeFlagItem = GETITEM( rItemSet, ScMergeFlagAttr, ATTR_MERGE_FLAG );
2343 if( rMergeFlagItem.IsOverlapped() )
2344 nMergeBaseXFId = mxMergedcells->GetBaseXFId( aScPos );
2347 String aAddNoteText; // additional text to be appended to a note
2349 CellType eCellType = pScCell ? pScCell->GetCellType() : CELLTYPE_NONE;
2350 switch( eCellType )
2352 case CELLTYPE_VALUE:
2354 double fValue = static_cast< const ScValueCell* >( pScCell )->GetValue();
2356 // try to create a Boolean cell
2357 if( pPattern && ((fValue == 0.0) || (fValue == 1.0)) )
2359 ULONG nScNumFmt = GETITEMVALUE( pPattern->GetItemSet(), SfxUInt32Item, ATTR_VALUE_FORMAT, ULONG );
2360 if( rFormatter.GetType( nScNumFmt ) == NUMBERFORMAT_LOGICAL )
2361 xCell.reset( new XclExpBooleanCell(
2362 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue != 0.0 ) );
2365 // try to create an RK value (compressed floating-point number)
2366 sal_Int32 nRkValue;
2367 if( !xCell && XclTools::GetRKFromDouble( nRkValue, fValue ) )
2368 xCell.reset( new XclExpRkCell(
2369 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, nRkValue ) );
2371 // else: simple floating-point number cell
2372 if( !xCell )
2373 xCell.reset( new XclExpNumberCell(
2374 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue ) );
2376 break;
2378 case CELLTYPE_STRING:
2380 const ScStringCell& rScStrCell = *static_cast< const ScStringCell* >( pScCell );
2381 xCell.reset( new XclExpLabelCell(
2382 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScStrCell ) );
2384 break;
2386 case CELLTYPE_EDIT:
2388 const ScEditCell& rScEditCell = *static_cast< const ScEditCell* >( pScCell );
2389 XclExpHyperlinkHelper aLinkHelper( GetRoot(), aScPos );
2390 xCell.reset( new XclExpLabelCell(
2391 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScEditCell, aLinkHelper ) );
2393 // add a single created HLINK record to the record list
2394 if( aLinkHelper.HasLinkRecord() )
2395 mxHyperlinkList->AppendRecord( aLinkHelper.GetLinkRecord() );
2396 // add list of multiple URLs to the additional cell note text
2397 if( aLinkHelper.HasMultipleUrls() )
2398 ScGlobal::AddToken( aAddNoteText, aLinkHelper.GetUrlList(), '\n', 2 );
2400 break;
2402 case CELLTYPE_FORMULA:
2404 const ScFormulaCell& rScFmlaCell = *static_cast< const ScFormulaCell* >( pScCell );
2405 xCell.reset( new XclExpFormulaCell(
2406 GetRoot(), aXclPos, pPattern, nMergeBaseXFId,
2407 rScFmlaCell, maArrayBfr, maShrfmlaBfr, maTableopBfr ) );
2409 break;
2411 default:
2412 DBG_ERRORFILE( "XclExpCellTable::XclExpCellTable - unknown cell type" );
2413 // run-through!
2414 case CELLTYPE_NONE:
2415 case CELLTYPE_NOTE:
2417 xCell.reset( new XclExpBlankCell(
2418 GetRoot(), aXclPos, nLastXclCol, pPattern, nMergeBaseXFId ) );
2420 break;
2423 // insert the cell into the current row
2424 if( xCell.is() )
2425 maRowBfr.AppendCell( xCell, bIsMergedBase );
2427 // notes
2428 const ScPostIt* pScNote = pScCell ? pScCell->GetNote() : 0;
2429 if( pScNote || (aAddNoteText.Len() > 0) )
2430 mxNoteList->AppendNewRecord( new XclExpNote( GetRoot(), aScPos, pScNote, aAddNoteText ) );
2432 // other sheet contents
2433 if( pPattern )
2435 const SfxItemSet& rItemSet = pPattern->GetItemSet();
2437 // base cell in a merged range
2438 if( bIsMergedBase )
2440 const ScMergeAttr& rMergeItem = GETITEM( rItemSet, ScMergeAttr, ATTR_MERGE );
2441 ScRange aScRange( aScPos );
2442 aScRange.aEnd.IncCol( rMergeItem.GetColMerge() - 1 );
2443 aScRange.aEnd.IncRow( rMergeItem.GetRowMerge() - 1 );
2444 sal_uInt32 nXFId = xCell.is() ? xCell->GetFirstXFId() : EXC_XFID_NOTFOUND;
2445 // #120156# blank cells merged vertically may occur repeatedly
2446 DBG_ASSERT( (aScRange.aStart.Col() == aScRange.aEnd.Col()) || (nScCol == nLastScCol),
2447 "XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" );
2448 for( SCCOL nIndex = nScCol; nIndex <= nLastScCol; ++nIndex )
2450 mxMergedcells->AppendRange( aScRange, nXFId );
2451 aScRange.aStart.IncCol();
2452 aScRange.aEnd.IncCol();
2456 // data validation
2457 if( ScfTools::CheckItem( rItemSet, ATTR_VALIDDATA, false ) )
2459 ULONG nScHandle = GETITEMVALUE( rItemSet, SfxUInt32Item, ATTR_VALIDDATA, ULONG );
2460 ScRange aScRange( aScPos );
2461 aScRange.aEnd.SetCol( nLastScCol );
2462 mxDval->InsertCellRange( aScRange, nScHandle );
2467 // create missing row settings for rows anyhow flagged or with outlines
2468 maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, nFirstUngroupedScRow ) );
2471 void XclExpCellTable::Finalize()
2473 // Finalize multiple operations.
2474 maTableopBfr.Finalize();
2476 /* Finalize column buffer. This calculates column default XF indexes from
2477 the XF identifiers and fills a vector with these XF indexes. */
2478 ScfUInt16Vec aColXFIndexes;
2479 maColInfoBfr.Finalize( aColXFIndexes );
2481 /* Finalize row buffer. This calculates all cell XF indexes from the XF
2482 identifiers. Then the XF index vector aColXFIndexes (filled above) is
2483 used to calculate the row default formats. With this, all unneeded blank
2484 cell records (equal to row default or column default) will be removed.
2485 The function returns the (most used) default row format in aDefRowData. */
2486 XclExpDefaultRowData aDefRowData;
2487 maRowBfr.Finalize( aDefRowData, aColXFIndexes );
2489 // Initialize the DEFROWHEIGHT record.
2490 mxDefrowheight->SetDefaultData( aDefRowData );
2493 XclExpRecordRef XclExpCellTable::CreateRecord( sal_uInt16 nRecId ) const
2495 XclExpRecordRef xRec;
2496 switch( nRecId )
2498 case EXC_ID3_DIMENSIONS: xRec.reset( new XclExpDelegatingRecord( const_cast<XclExpRowBuffer*>(&maRowBfr)->GetDimensions() ) ); break;
2499 case EXC_ID2_DEFROWHEIGHT: xRec = mxDefrowheight; break;
2500 case EXC_ID_GUTS: xRec = mxGuts; break;
2501 case EXC_ID_NOTE: xRec = mxNoteList; break;
2502 case EXC_ID_MERGEDCELLS: xRec = mxMergedcells; break;
2503 case EXC_ID_HLINK: xRec = mxHyperlinkList; break;
2504 case EXC_ID_DVAL: xRec = mxDval; break;
2505 default: DBG_ERRORFILE( "XclExpCellTable::CreateRecord - unknown record id" );
2507 return xRec;
2510 void XclExpCellTable::Save( XclExpStream& rStrm )
2512 // DEFCOLWIDTH and COLINFOs
2513 maColInfoBfr.Save( rStrm );
2514 // ROWs and cell records
2515 maRowBfr.Save( rStrm );
2518 void XclExpCellTable::SaveXml( XclExpXmlStream& rStrm )
2520 maColInfoBfr.SaveXml( rStrm );
2521 maRowBfr.SaveXml( rStrm );
2524 // ============================================================================