fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / filter / excel / xetable.cxx
blob8422ef63da9d722073af7f282c2dcd032a11dbba
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "xetable.hxx"
22 #include <map>
23 #include <com/sun/star/i18n/ScriptType.hpp>
24 #include "scitems.hxx"
25 #include <svl/intitem.hxx>
26 #include "document.hxx"
27 #include "dociter.hxx"
28 #include "olinetab.hxx"
29 #include "formulacell.hxx"
30 #include "patattr.hxx"
31 #include "attrib.hxx"
32 #include "xehelper.hxx"
33 #include "xecontent.hxx"
34 #include "xeescher.hxx"
35 #include "xeextlst.hxx"
36 #include "tokenarray.hxx"
37 #include <thread>
38 #include <comphelper/threadpool.hxx>
40 using namespace ::oox;
42 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
44 // Helper records for cell records
46 XclExpStringRec::XclExpStringRec( const XclExpRoot& rRoot, const OUString& rResult ) :
47 XclExpRecord( EXC_ID3_STRING ),
48 mxResult( XclExpStringHelper::CreateString( rRoot, rResult ) )
50 OSL_ENSURE( (rRoot.GetBiff() <= EXC_BIFF5) || (mxResult->Len() > 0),
51 "XclExpStringRec::XclExpStringRec - empty result not allowed in BIFF8+" );
52 SetRecSize( mxResult->GetSize() );
55 void XclExpStringRec::WriteBody( XclExpStream& rStrm )
57 rStrm << *mxResult;
60 // Additional records for special formula ranges ==============================
62 XclExpRangeFmlaBase::XclExpRangeFmlaBase(
63 sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScAddress& rScPos ) :
64 XclExpRecord( nRecId, nRecSize ),
65 maXclRange( ScAddress::UNINITIALIZED ),
66 maBaseXclPos( ScAddress::UNINITIALIZED )
68 maBaseXclPos.Set( static_cast< sal_uInt16 >( rScPos.Col() ), static_cast< sal_uInt16 >( rScPos.Row() ) );
69 maXclRange.maFirst = maXclRange.maLast = maBaseXclPos;
72 XclExpRangeFmlaBase::XclExpRangeFmlaBase(
73 sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScRange& rScRange ) :
74 XclExpRecord( nRecId, nRecSize ),
75 maXclRange( ScAddress::UNINITIALIZED ),
76 maBaseXclPos( ScAddress::UNINITIALIZED )
78 maXclRange.Set(
79 static_cast< sal_uInt16 >( rScRange.aStart.Col() ),
80 static_cast< sal_uInt16 >( rScRange.aStart.Row() ),
81 static_cast< sal_uInt16 >( rScRange.aEnd.Col() ),
82 static_cast< sal_uInt16 >( rScRange.aEnd.Row() ) );
83 maBaseXclPos = maXclRange.maFirst;
86 bool XclExpRangeFmlaBase::IsBasePos( sal_uInt16 nXclCol, sal_uInt32 nXclRow ) const
88 return (maBaseXclPos.mnCol == nXclCol) && (maBaseXclPos.mnRow == nXclRow);
91 void XclExpRangeFmlaBase::Extend( const ScAddress& rScPos )
93 sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
94 sal_uInt32 nXclRow = static_cast< sal_uInt32 >( rScPos.Row() );
95 maXclRange.maFirst.mnCol = ::std::min( maXclRange.maFirst.mnCol, nXclCol );
96 maXclRange.maFirst.mnRow = ::std::min( maXclRange.maFirst.mnRow, nXclRow );
97 maXclRange.maLast.mnCol = ::std::max( maXclRange.maLast.mnCol, nXclCol );
98 maXclRange.maLast.mnRow = ::std::max( maXclRange.maLast.mnRow, nXclRow );
101 void XclExpRangeFmlaBase::WriteRangeAddress( XclExpStream& rStrm ) const
103 maXclRange.Write( rStrm, false );
106 // Array formulas =============================================================
108 XclExpArray::XclExpArray( XclTokenArrayRef xTokArr, const ScRange& rScRange ) :
109 XclExpRangeFmlaBase( EXC_ID3_ARRAY, 14 + xTokArr->GetSize(), rScRange ),
110 mxTokArr( xTokArr )
114 XclTokenArrayRef XclExpArray::CreateCellTokenArray( const XclExpRoot& rRoot ) const
116 return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
119 bool XclExpArray::IsVolatile() const
121 return mxTokArr->IsVolatile();
124 void XclExpArray::WriteBody( XclExpStream& rStrm )
126 WriteRangeAddress( rStrm );
127 sal_uInt16 nFlags = EXC_ARRAY_DEFAULTFLAGS;
128 ::set_flag( nFlags, EXC_ARRAY_RECALC_ALWAYS, IsVolatile() );
129 rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
132 XclExpArrayBuffer::XclExpArrayBuffer( const XclExpRoot& rRoot ) :
133 XclExpRoot( rRoot )
137 XclExpArrayRef XclExpArrayBuffer::CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange )
139 const ScAddress& rScPos = rScRange.aStart;
140 XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_MATRIX, rScTokArr, &rScPos );
142 OSL_ENSURE( maRecMap.find( rScPos ) == maRecMap.end(), "XclExpArrayBuffer::CreateArray - array exists already" );
143 XclExpArrayRef& rxRec = maRecMap[ rScPos ];
144 rxRec.reset( new XclExpArray( xTokArr, rScRange ) );
145 return rxRec;
148 XclExpArrayRef XclExpArrayBuffer::FindArray( const ScTokenArray& rScTokArr, const ScAddress& rBasePos ) const
150 XclExpArrayRef xRec;
151 // try to extract a matrix reference token
152 if (rScTokArr.GetLen() != 1)
153 // Must consist of a single reference token.
154 return xRec;
156 const formula::FormulaToken* pToken = rScTokArr.GetArray()[0];
157 if (!pToken || pToken->GetOpCode() != ocMatRef)
158 // not a matrix reference token.
159 return xRec;
161 const ScSingleRefData& rRef = *pToken->GetSingleRef();
162 ScAddress aAbsPos = rRef.toAbs(rBasePos);
163 XclExpArrayMap::const_iterator it = maRecMap.find(aAbsPos);
165 return (it == maRecMap.end()) ? xRec : xRec = it->second;
168 // Shared formulas ============================================================
170 XclExpShrfmla::XclExpShrfmla( XclTokenArrayRef xTokArr, const ScAddress& rScPos ) :
171 XclExpRangeFmlaBase( EXC_ID_SHRFMLA, 10 + xTokArr->GetSize(), rScPos ),
172 mxTokArr( xTokArr ),
173 mnUsedCount( 1 )
177 void XclExpShrfmla::ExtendRange( const ScAddress& rScPos )
179 Extend( rScPos );
180 ++mnUsedCount;
183 XclTokenArrayRef XclExpShrfmla::CreateCellTokenArray( const XclExpRoot& rRoot ) const
185 return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
188 bool XclExpShrfmla::IsVolatile() const
190 return mxTokArr->IsVolatile();
193 void XclExpShrfmla::WriteBody( XclExpStream& rStrm )
195 WriteRangeAddress( rStrm );
196 rStrm << sal_uInt8( 0 ) << mnUsedCount << *mxTokArr;
199 XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) :
200 XclExpRoot( rRoot )
204 bool XclExpShrfmlaBuffer::IsValidTokenArray( const ScTokenArray& rArray ) const
206 using namespace formula;
208 FormulaToken** pTokens = rArray.GetArray();
209 sal_uInt16 nLen = rArray.GetLen();
210 for (sal_uInt16 i = 0; i < nLen; ++i)
212 const FormulaToken* p = pTokens[i];
213 switch (p->GetType())
215 case svSingleRef:
217 const ScSingleRefData& rRefData = *p->GetSingleRef();
218 if (!GetFormulaCompiler().IsRef2D(rRefData))
219 // Excel's shared formula cannot include 3D reference.
220 return false;
222 break;
223 case svDoubleRef:
225 const ScComplexRefData& rRefData = *p->GetDoubleRef();
226 if (!GetFormulaCompiler().IsRef2D(rRefData))
227 // Excel's shared formula cannot include 3D reference.
228 return false;
230 break;
231 case svExternalSingleRef:
232 case svExternalDoubleRef:
233 case svExternalName:
234 // External references aren't allowed.
235 return false;
236 default:
240 return true;
243 XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
244 const ScFormulaCell& rScCell, const ScAddress& rScPos )
246 XclExpShrfmlaRef xRec;
247 const ScTokenArray* pShrdScTokArr = rScCell.GetSharedCode();
248 if (!pShrdScTokArr)
249 // This formula cell is not shared formula cell.
250 return xRec;
252 // Check to see if this shared formula contains any tokens that Excel's shared formula cannot handle.
253 if (maBadTokens.count(pShrdScTokArr) > 0)
254 // Already on the black list. Skip it.
255 return xRec;
257 if (!IsValidTokenArray(*pShrdScTokArr))
259 // We can't export this as shared formula.
260 maBadTokens.insert(pShrdScTokArr);
261 return xRec;
264 TokensType::iterator aIt = maRecMap.find(pShrdScTokArr);
265 if( aIt == maRecMap.end() )
267 // create a new record
268 XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_SHARED, *pShrdScTokArr, &rScPos );
269 xRec.reset( new XclExpShrfmla( xTokArr, rScPos ) );
270 maRecMap[ pShrdScTokArr ] = xRec;
272 else
274 // extend existing record
275 OSL_ENSURE( aIt->second, "XclExpShrfmlaBuffer::CreateOrExtendShrfmla - missing record" );
276 xRec = aIt->second;
277 xRec->ExtendRange( rScPos );
280 return xRec;
283 // Multiple operations ========================================================
285 XclExpTableop::XclExpTableop( const ScAddress& rScPos,
286 const XclMultipleOpRefs& rRefs, sal_uInt8 nScMode ) :
287 XclExpRangeFmlaBase( EXC_ID3_TABLEOP, 16, rScPos ),
288 mnLastAppXclCol( static_cast< sal_uInt16 >( rScPos.Col() ) ),
289 mnColInpXclCol( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Col() ) ),
290 mnColInpXclRow( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Row() ) ),
291 mnRowInpXclCol( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Col() ) ),
292 mnRowInpXclRow( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Row() ) ),
293 mnScMode( nScMode ),
294 mbValid( false )
298 bool XclExpTableop::TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
300 sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
301 sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() );
303 bool bOk = IsAppendable( nXclCol, nXclRow );
304 if( bOk )
306 SCCOL nFirstScCol = static_cast< SCCOL >( maXclRange.maFirst.mnCol );
307 SCROW nFirstScRow = static_cast< SCROW >( maXclRange.maFirst.mnRow );
308 SCCOL nColInpScCol = static_cast< SCCOL >( mnColInpXclCol );
309 SCROW nColInpScRow = static_cast< SCROW >( mnColInpXclRow );
310 SCCOL nRowInpScCol = static_cast< SCCOL >( mnRowInpXclCol );
311 SCROW nRowInpScRow = static_cast< SCROW >( mnRowInpXclRow );
313 bOk = ((mnScMode == 2) == rRefs.mbDblRefMode) &&
314 (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
315 (nColInpScCol == rRefs.maColFirstScPos.Col()) &&
316 (nColInpScRow == rRefs.maColFirstScPos.Row()) &&
317 (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
318 (rScPos.Tab() == rRefs.maColRelScPos.Tab());
320 if( bOk ) switch( mnScMode )
322 case 0:
323 bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
324 (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
325 (nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
326 (rScPos.Row() == rRefs.maColRelScPos.Row());
327 break;
328 case 1:
329 bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
330 (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
331 (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
332 (nFirstScRow == rRefs.maColRelScPos.Row() + 1);
333 break;
334 case 2:
335 bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
336 (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
337 (nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
338 (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
339 (nRowInpScCol == rRefs.maRowFirstScPos.Col()) &&
340 (nRowInpScRow == rRefs.maRowFirstScPos.Row()) &&
341 (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
342 (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
343 (nFirstScRow == rRefs.maRowRelScPos.Row() + 1) &&
344 (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
345 break;
346 default:
347 bOk = false;
350 if( bOk )
352 // extend the cell range
353 OSL_ENSURE( IsAppendable( nXclCol, nXclRow ), "XclExpTableop::TryExtend - wrong cell address" );
354 Extend( rScPos );
355 mnLastAppXclCol = nXclCol;
359 return bOk;
362 void XclExpTableop::Finalize()
364 // is the range complete? (last appended cell is in last column)
365 mbValid = maXclRange.maLast.mnCol == mnLastAppXclCol;
366 // if last row is incomplete, try to shorten the used range
367 if( !mbValid && (maXclRange.maFirst.mnRow < maXclRange.maLast.mnRow) )
369 --maXclRange.maLast.mnRow;
370 mbValid = true;
373 // check if referred cells are outside of own range
374 if( mbValid ) switch( mnScMode )
376 case 0:
377 mbValid = (mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
378 (mnColInpXclRow < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
379 break;
380 case 1:
381 mbValid = (mnColInpXclCol < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
382 (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
383 break;
384 case 2:
385 mbValid = ((mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
386 (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow)) &&
387 ((mnRowInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnRowInpXclCol > maXclRange.maLast.mnCol) ||
388 (mnRowInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnRowInpXclRow > maXclRange.maLast.mnRow));
389 break;
393 XclTokenArrayRef XclExpTableop::CreateCellTokenArray( const XclExpRoot& rRoot ) const
395 XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
396 return mbValid ?
397 rFmlaComp.CreateSpecialRefFormula( EXC_TOKID_TBL, maBaseXclPos ) :
398 rFmlaComp.CreateErrorFormula( EXC_ERR_NA );
401 bool XclExpTableop::IsVolatile() const
403 return true;
406 void XclExpTableop::Save( XclExpStream& rStrm )
408 if( mbValid )
409 XclExpRangeFmlaBase::Save( rStrm );
412 bool XclExpTableop::IsAppendable( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const
414 return ((nXclCol == mnLastAppXclCol + 1) && (nXclRow == maXclRange.maFirst.mnRow)) ||
415 ((nXclCol == mnLastAppXclCol + 1) && (nXclCol <= maXclRange.maLast.mnCol) && (nXclRow == maXclRange.maLast.mnRow)) ||
416 ((mnLastAppXclCol == maXclRange.maLast.mnCol) && (nXclCol == maXclRange.maFirst.mnCol) && (nXclRow == maXclRange.maLast.mnRow + 1));
419 void XclExpTableop::WriteBody( XclExpStream& rStrm )
421 sal_uInt16 nFlags = EXC_TABLEOP_DEFAULTFLAGS;
422 ::set_flag( nFlags, EXC_TABLEOP_RECALC_ALWAYS, IsVolatile() );
423 switch( mnScMode )
425 case 1: ::set_flag( nFlags, EXC_TABLEOP_ROW ); break;
426 case 2: ::set_flag( nFlags, EXC_TABLEOP_BOTH ); break;
429 WriteRangeAddress( rStrm );
430 rStrm << nFlags;
431 if( mnScMode == 2 )
432 rStrm << mnRowInpXclRow << mnRowInpXclCol << mnColInpXclRow << mnColInpXclCol;
433 else
434 rStrm << mnColInpXclRow << mnColInpXclCol << sal_uInt32( 0 );
437 XclExpTableopBuffer::XclExpTableopBuffer( const XclExpRoot& rRoot ) :
438 XclExpRoot( rRoot )
442 XclExpTableopRef XclExpTableopBuffer::CreateOrExtendTableop(
443 const ScTokenArray& rScTokArr, const ScAddress& rScPos )
445 XclExpTableopRef xRec;
447 // try to extract cell references of a multiple operations formula
448 XclMultipleOpRefs aRefs;
449 if (XclTokenArrayHelper::GetMultipleOpRefs(aRefs, rScTokArr, rScPos))
451 // try to find an existing TABLEOP record for this cell position
452 for( size_t nPos = 0, nSize = maTableopList.GetSize(); !xRec && (nPos < nSize); ++nPos )
454 XclExpTableopRef xTempRec = maTableopList.GetRecord( nPos );
455 if( xTempRec->TryExtend( rScPos, aRefs ) )
456 xRec = xTempRec;
459 // no record found, or found record not extensible
460 if( !xRec )
461 xRec = TryCreate( rScPos, aRefs );
464 return xRec;
467 void XclExpTableopBuffer::Finalize()
469 for( size_t nPos = 0, nSize = maTableopList.GetSize(); nPos < nSize; ++nPos )
470 maTableopList.GetRecord( nPos )->Finalize();
473 XclExpTableopRef XclExpTableopBuffer::TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
475 sal_uInt8 nScMode = 0;
476 bool bOk = (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
477 (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
478 (rScPos.Tab() == rRefs.maColRelScPos.Tab());
480 if( bOk )
482 if( rRefs.mbDblRefMode )
484 nScMode = 2;
485 bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
486 (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
487 (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
488 (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
489 (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
490 (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
491 (rScPos.Row() == rRefs.maRowRelScPos.Row() + 1) &&
492 (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
494 else if( (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
495 (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
496 (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
497 (rScPos.Row() == rRefs.maColRelScPos.Row()) )
499 nScMode = 0;
501 else if( (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
502 (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
503 (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
504 (rScPos.Row() == rRefs.maColRelScPos.Row() + 1) )
506 nScMode = 1;
508 else
510 bOk = false;
514 XclExpTableopRef xRec;
515 if( bOk )
517 xRec.reset( new XclExpTableop( rScPos, rRefs, nScMode ) );
518 maTableopList.AppendRecord( xRec );
521 return xRec;
524 // Cell records
526 XclExpCellBase::XclExpCellBase(
527 sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos ) :
528 XclExpRecord( nRecId, nContSize + 4 ),
529 maXclPos( rXclPos )
533 bool XclExpCellBase::IsMultiLineText() const
535 return false;
538 bool XclExpCellBase::TryMerge( const XclExpCellBase& /*rCell*/ )
540 return false;
543 void XclExpCellBase::GetBlankXFIndexes( ScfUInt16Vec& /*rXFIndexes*/ ) const
545 // default: do nothing
548 void XclExpCellBase::RemoveUnusedBlankCells( const ScfUInt16Vec& /*rXFIndexes*/ )
550 // default: do nothing
553 // Single cell records ========================================================
555 XclExpSingleCellBase::XclExpSingleCellBase(
556 sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos, sal_uInt32 nXFId ) :
557 XclExpCellBase( nRecId, 2, rXclPos ),
558 maXFId( nXFId ),
559 mnContSize( nContSize )
563 XclExpSingleCellBase::XclExpSingleCellBase( const XclExpRoot& rRoot,
564 sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos,
565 const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForcedXFId ) :
566 XclExpCellBase( nRecId, 2, rXclPos ),
567 maXFId( nForcedXFId ),
568 mnContSize( nContSize )
570 if( GetXFId() == EXC_XFID_NOTFOUND )
571 SetXFId( rRoot.GetXFBuffer().Insert( pPattern, nScript ) );
574 sal_uInt16 XclExpSingleCellBase::GetLastXclCol() const
576 return GetXclCol();
579 sal_uInt32 XclExpSingleCellBase::GetFirstXFId() const
581 return GetXFId();
584 bool XclExpSingleCellBase::IsEmpty() const
586 return false;
589 void XclExpSingleCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
591 maXFId.ConvertXFIndex( rRoot );
594 void XclExpSingleCellBase::Save( XclExpStream& rStrm )
596 OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
597 AddRecSize( mnContSize );
598 XclExpCellBase::Save( rStrm );
601 void XclExpSingleCellBase::WriteBody( XclExpStream& rStrm )
603 rStrm << static_cast<sal_uInt16> (GetXclRow()) << GetXclCol() << maXFId.mnXFIndex;
604 WriteContents( rStrm );
607 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpNumberCell )
609 XclExpNumberCell::XclExpNumberCell(
610 const XclExpRoot& rRoot, const XclAddress& rXclPos,
611 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, double fValue ) :
612 // #i41210# always use latin script for number cells - may look wrong for special number formats...
613 XclExpSingleCellBase( rRoot, EXC_ID3_NUMBER, 8, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
614 mfValue( fValue )
618 static OString lcl_GetStyleId( XclExpXmlStream& rStrm, sal_uInt32 nXFIndex )
620 return OString::number( rStrm.GetRoot().GetXFBuffer()
621 .GetXmlCellIndex( nXFIndex ) );
624 static OString lcl_GetStyleId( XclExpXmlStream& rStrm, const XclExpCellBase& rCell )
626 sal_uInt32 nXFId = rCell.GetFirstXFId();
627 sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( nXFId );
628 return lcl_GetStyleId( rStrm, nXFIndex );
631 void XclExpNumberCell::SaveXml( XclExpXmlStream& rStrm )
633 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
634 rWorksheet->startElement( XML_c,
635 XML_r, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), GetXclPos() ).getStr(),
636 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
637 XML_t, "n",
638 // OOXTODO: XML_cm, XML_vm, XML_ph
639 FSEND );
640 rWorksheet->startElement( XML_v, FSEND );
641 rWorksheet->write( mfValue );
642 rWorksheet->endElement( XML_v );
643 rWorksheet->endElement( XML_c );
646 void XclExpNumberCell::WriteContents( XclExpStream& rStrm )
648 rStrm << mfValue;
651 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpBooleanCell )
653 XclExpBooleanCell::XclExpBooleanCell(
654 const XclExpRoot& rRoot, const XclAddress& rXclPos,
655 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, bool bValue ) :
656 // #i41210# always use latin script for boolean cells
657 XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
658 mbValue( bValue )
662 void XclExpBooleanCell::SaveXml( XclExpXmlStream& rStrm )
664 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
665 rWorksheet->startElement( XML_c,
666 XML_r, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), GetXclPos() ).getStr(),
667 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
668 XML_t, "b",
669 // OOXTODO: XML_cm, XML_vm, XML_ph
670 FSEND );
671 rWorksheet->startElement( XML_v, FSEND );
672 rWorksheet->write( mbValue ? "1" : "0" );
673 rWorksheet->endElement( XML_v );
674 rWorksheet->endElement( XML_c );
677 void XclExpBooleanCell::WriteContents( XclExpStream& rStrm )
679 rStrm << sal_uInt16( mbValue ? 1 : 0 ) << EXC_BOOLERR_BOOL;
682 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpLabelCell )
684 XclExpLabelCell::XclExpLabelCell(
685 const XclExpRoot& rRoot, const XclAddress& rXclPos,
686 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, const OUString& rStr ) :
687 XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
689 sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
690 XclExpStringRef xText = XclExpStringHelper::CreateCellString(
691 rRoot, rStr, pPattern, EXC_STR_DEFAULT, nMaxLen);
692 Init( rRoot, pPattern, xText );
695 XclExpLabelCell::XclExpLabelCell(
696 const XclExpRoot& rRoot, const XclAddress& rXclPos,
697 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
698 const EditTextObject* pEditText, XclExpHyperlinkHelper& rLinkHelper ) :
699 XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
701 sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
703 XclExpStringRef xText;
704 if (pEditText)
705 xText = XclExpStringHelper::CreateCellString(
706 rRoot, *pEditText, pPattern, rLinkHelper, EXC_STR_DEFAULT, nMaxLen);
707 else
708 xText = XclExpStringHelper::CreateCellString(
709 rRoot, EMPTY_OUSTRING, pPattern, EXC_STR_DEFAULT, nMaxLen);
711 Init( rRoot, pPattern, xText );
714 bool XclExpLabelCell::IsMultiLineText() const
716 return mbLineBreak || mxText->IsWrapped();
719 void XclExpLabelCell::Init( const XclExpRoot& rRoot,
720 const ScPatternAttr* pPattern, XclExpStringRef xText )
722 OSL_ENSURE( xText && xText->Len(), "XclExpLabelCell::XclExpLabelCell - empty string passed" );
723 mxText = xText;
724 mnSstIndex = 0;
726 // create the cell format
727 sal_uInt16 nXclFont = mxText->RemoveLeadingFont();
728 if( GetXFId() == EXC_XFID_NOTFOUND )
730 OSL_ENSURE( nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found" );
731 bool bForceLineBreak = mxText->IsWrapped();
732 SetXFId( rRoot.GetXFBuffer().InsertWithFont( pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak ) );
735 // get auto-wrap attribute from cell format
736 const XclExpXF* pXF = rRoot.GetXFBuffer().GetXFById( GetXFId() );
737 mbLineBreak = pXF && pXF->GetAlignmentData().mbLineBreak;
739 // initialize the record contents
740 switch( rRoot.GetBiff() )
742 case EXC_BIFF5:
743 // BIFF5-BIFF7: create a LABEL or RSTRING record
744 OSL_ENSURE( mxText->Len() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::XclExpLabelCell - string too long" );
745 SetContSize( mxText->GetSize() );
746 // formatted string is exported in an RSTRING record
747 if( mxText->IsRich() )
749 OSL_ENSURE( mxText->GetFormatsCount() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::WriteContents - too many formats" );
750 mxText->LimitFormatCount( EXC_LABEL_MAXLEN );
751 SetRecId( EXC_ID_RSTRING );
752 SetContSize( GetContSize() + 1 + 2 * mxText->GetFormatsCount() );
754 break;
755 case EXC_BIFF8:
756 // BIFF8+: create a LABELSST record
757 mnSstIndex = rRoot.GetSst().Insert( xText );
758 SetRecId( EXC_ID_LABELSST );
759 SetContSize( 4 );
760 break;
761 default: DBG_ERROR_BIFF();
765 void XclExpLabelCell::SaveXml( XclExpXmlStream& rStrm )
767 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
768 rWorksheet->startElement( XML_c,
769 XML_r, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), GetXclPos() ).getStr(),
770 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
771 XML_t, "s",
772 // OOXTODO: XML_cm, XML_vm, XML_ph
773 FSEND );
774 rWorksheet->startElement( XML_v, FSEND );
775 rWorksheet->write( (sal_Int32) mnSstIndex );
776 rWorksheet->endElement( XML_v );
777 rWorksheet->endElement( XML_c );
780 void XclExpLabelCell::WriteContents( XclExpStream& rStrm )
782 switch( rStrm.GetRoot().GetBiff() )
784 case EXC_BIFF5:
785 rStrm << *mxText;
786 if( mxText->IsRich() )
788 rStrm << static_cast< sal_uInt8 >( mxText->GetFormatsCount() );
789 mxText->WriteFormats( rStrm );
791 break;
792 case EXC_BIFF8:
793 rStrm << mnSstIndex;
794 break;
795 default: DBG_ERROR_BIFF();
799 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpFormulaCell )
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 sal_uLong nScNumFmt = pPattern ?
820 GETITEMVALUE( pPattern->GetItemSet(), SfxUInt32Item, ATTR_VALUE_FORMAT, sal_uLong ) :
821 rNumFmtBfr.GetStandardFormat();
823 // alternative number format passed to XF buffer
824 sal_uLong nAltScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
825 /* 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 != css::util::NumberFormat::LOGICAL) &&
832 (nFormatType != css::util::NumberFormat::TEXT) )
833 nAltScNumFmt = nScNumFmt;
834 /* If cell number format is Boolean and automatic formula
835 format is Boolean don't write that ugly special format. */
836 else if( (nFormatType == css::util::NumberFormat::LOGICAL) &&
837 (rFormatter.GetType( nScNumFmt ) == css::util::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 == css::util::NumberFormat::TEXT )
845 OUString aResult = mrScFmlaCell.GetString().getString();
846 bForceLineBreak = mrScFmlaCell.IsMultilineResult();
847 nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult );
849 SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) );
852 // *** Convert the formula token array *** --------------------------------
854 ScAddress aScPos( static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), rRoot.GetCurrScTab() );
855 const ScTokenArray& rScTokArr = *mrScFmlaCell.GetCode();
857 // first try to create multiple operations
858 mxAddRec = rTableopBfr.CreateOrExtendTableop( rScTokArr, aScPos );
860 // no multiple operation found - try to create matrix formula
861 if( !mxAddRec ) switch( static_cast< ScMatrixMode >( mrScFmlaCell.GetMatrixFlag() ) )
863 case MM_FORMULA:
865 // origin of the matrix - find the used matrix range
866 SCCOL nMatWidth;
867 SCROW nMatHeight;
868 mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
869 OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
870 ScRange aMatScRange( aScPos );
871 ScAddress& rMatEnd = aMatScRange.aEnd;
872 rMatEnd.IncCol( static_cast< SCsCOL >( nMatWidth - 1 ) );
873 rMatEnd.IncRow( static_cast< SCsROW >( nMatHeight - 1 ) );
874 // reduce to valid range (range keeps valid, because start position IS valid)
875 rRoot.GetAddressConverter().ValidateRange( aMatScRange, true );
876 // create the ARRAY record
877 mxAddRec = rArrayBfr.CreateArray( rScTokArr, aMatScRange );
879 break;
880 case MM_REFERENCE:
882 // other formula cell covered by a matrix - find the ARRAY record
883 mxAddRec = rArrayBfr.FindArray(rScTokArr, aScPos);
884 // should always be found, if Calc document is not broken
885 OSL_ENSURE( mxAddRec, "XclExpFormulaCell::XclExpFormulaCell - no matrix found" );
887 break;
888 default:;
891 // no matrix found - try to create shared formula
892 if( !mxAddRec )
893 mxAddRec = rShrfmlaBfr.CreateOrExtendShrfmla(mrScFmlaCell, aScPos);
895 // no shared formula found - create a simple cell formula
896 if( !mxAddRec )
897 mxTokArr = rRoot.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL, rScTokArr, &aScPos );
900 void XclExpFormulaCell::Save( XclExpStream& rStrm )
902 // create token array for FORMULA cells with additional record
903 if( mxAddRec )
904 mxTokArr = mxAddRec->CreateCellTokenArray( rStrm.GetRoot() );
906 // FORMULA record itself
907 OSL_ENSURE( mxTokArr, "XclExpFormulaCell::Save - missing token array" );
908 if( !mxTokArr )
909 mxTokArr = rStrm.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA );
910 SetContSize( 16 + mxTokArr->GetSize() );
911 XclExpSingleCellBase::Save( rStrm );
913 // additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record
914 if( mxAddRec && mxAddRec->IsBasePos( GetXclCol(), GetXclRow() ) )
915 mxAddRec->Save( rStrm );
917 // STRING record for string result
918 if( mxStringRec )
919 mxStringRec->Save( rStrm );
922 void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
924 const char* sType = NULL;
925 OUString sValue;
926 XclXmlUtils::GetFormulaTypeAndValue( mrScFmlaCell, sType, sValue );
927 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
928 rWorksheet->startElement( XML_c,
929 XML_r, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), GetXclPos() ).getStr(),
930 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
931 XML_t, sType,
932 // OOXTODO: XML_cm, XML_vm, XML_ph
933 FSEND );
935 bool bWriteFormula = true;
936 bool bTagStarted = false;
937 ScAddress aScPos( static_cast< SCCOL >( GetXclPos().mnCol ),
938 static_cast< SCROW >( GetXclPos().mnRow ), rStrm.GetRoot().GetCurrScTab() );
940 switch (mrScFmlaCell.GetMatrixFlag())
942 case MM_NONE:
943 break;
944 case MM_REFERENCE:
945 bWriteFormula = false;
946 break;
947 case MM_FORMULA:
948 case MM_FAKE:
950 // origin of the matrix - find the used matrix range
951 SCCOL nMatWidth;
952 SCROW nMatHeight;
953 mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
954 OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
955 ScRange aMatScRange( aScPos );
956 ScAddress& rMatEnd = aMatScRange.aEnd;
957 rMatEnd.IncCol( static_cast< SCsCOL >( nMatWidth - 1 ) );
958 rMatEnd.IncRow( static_cast< SCsROW >( nMatHeight - 1 ) );
959 // reduce to valid range (range keeps valid, because start position IS valid
960 rStrm.GetRoot().GetAddressConverter().ValidateRange( aMatScRange, true );
962 OStringBuffer sFmlaCellRange;
963 if (ValidRange(aMatScRange))
965 // calculate the cell range.
966 sFmlaCellRange.append( XclXmlUtils::ToOString(
967 rStrm.GetRoot().GetStringBuf(), aMatScRange.aStart ).getStr());
968 sFmlaCellRange.append(":");
969 sFmlaCellRange.append( XclXmlUtils::ToOString(
970 rStrm.GetRoot().GetStringBuf(), aMatScRange.aEnd ).getStr());
973 if ( aMatScRange.aStart.Col() == GetXclPos().mnCol &&
974 aMatScRange.aStart.Row() == static_cast<SCROW>(GetXclPos().mnRow))
976 rWorksheet->startElement( XML_f,
977 XML_aca, XclXmlUtils::ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
978 (mxAddRec && mxAddRec->IsVolatile())),
979 XML_t, mxAddRec ? "array" : NULL,
980 XML_ref, !sFmlaCellRange.isEmpty()? sFmlaCellRange.getStr() : NULL,
981 // OOXTODO: XML_dt2D, bool
982 // OOXTODO: XML_dtr, bool
983 // OOXTODO: XML_del1, bool
984 // OOXTODO: XML_del2, bool
985 // OOXTODO: XML_r1, ST_CellRef
986 // OOXTODO: XML_r2, ST_CellRef
987 // OOXTODO: XML_ca, bool
988 // OOXTODO: XML_si, uint
989 // OOXTODO: XML_bx bool
990 FSEND );
991 bTagStarted = true;
994 break;
997 if (bWriteFormula)
999 if (!bTagStarted)
1001 rWorksheet->startElement( XML_f,
1002 XML_aca, XclXmlUtils::ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
1003 (mxAddRec && mxAddRec->IsVolatile()) ),
1004 FSEND );
1006 rWorksheet->writeEscaped( XclXmlUtils::ToOUString(
1007 rStrm.GetRoot().GetCompileFormulaContext(), mrScFmlaCell.aPos, mrScFmlaCell.GetCode()));
1008 rWorksheet->endElement( XML_f );
1011 if( strcmp( sType, "inlineStr" ) == 0 )
1013 rWorksheet->startElement( XML_is, FSEND );
1014 rWorksheet->startElement( XML_t, FSEND );
1015 rWorksheet->writeEscaped( sValue );
1016 rWorksheet->endElement( XML_t );
1017 rWorksheet->endElement( XML_is );
1019 else
1021 rWorksheet->startElement( XML_v, FSEND );
1022 rWorksheet->writeEscaped( sValue );
1023 rWorksheet->endElement( XML_v );
1025 rWorksheet->endElement( XML_c );
1028 void XclExpFormulaCell::WriteContents( XclExpStream& rStrm )
1030 sal_uInt16 nScErrCode = mrScFmlaCell.GetErrCode();
1031 if( nScErrCode )
1033 rStrm << EXC_FORMULA_RES_ERROR << sal_uInt8( 0 )
1034 << XclTools::GetXclErrorCode( nScErrCode )
1035 << sal_uInt8( 0 ) << sal_uInt16( 0 )
1036 << sal_uInt16( 0xFFFF );
1038 else
1040 // result of the formula
1041 switch( mrScFmlaCell.GetFormatType() )
1043 case css::util::NumberFormat::NUMBER:
1045 // either value or error code
1046 rStrm << mrScFmlaCell.GetValue();
1048 break;
1050 case css::util::NumberFormat::TEXT:
1052 OUString aResult = mrScFmlaCell.GetString().getString();
1053 if( !aResult.isEmpty() || (rStrm.GetRoot().GetBiff() <= EXC_BIFF5) )
1055 rStrm << EXC_FORMULA_RES_STRING;
1056 mxStringRec.reset( new XclExpStringRec( rStrm.GetRoot(), aResult ) );
1058 else
1059 rStrm << EXC_FORMULA_RES_EMPTY; // BIFF8 only
1060 rStrm << sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
1062 break;
1064 case css::util::NumberFormat::LOGICAL:
1066 sal_uInt8 nXclValue = (mrScFmlaCell.GetValue() == 0.0) ? 0 : 1;
1067 rStrm << EXC_FORMULA_RES_BOOL << sal_uInt8( 0 )
1068 << nXclValue << sal_uInt8( 0 ) << sal_uInt16( 0 )
1069 << sal_uInt16( 0xFFFF );
1071 break;
1073 default:
1074 rStrm << mrScFmlaCell.GetValue();
1078 // flags and formula token array
1079 sal_uInt16 nFlags = EXC_FORMULA_DEFAULTFLAGS;
1080 ::set_flag( nFlags, EXC_FORMULA_RECALC_ALWAYS, mxTokArr->IsVolatile() || (mxAddRec && mxAddRec->IsVolatile()) );
1081 ::set_flag( nFlags, EXC_FORMULA_SHARED, mxAddRec && (mxAddRec->GetRecId() == EXC_ID_SHRFMLA) );
1082 rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
1085 // Multiple cell records ======================================================
1087 XclExpMultiCellBase::XclExpMultiCellBase(
1088 sal_uInt16 nRecId, sal_uInt16 nMulRecId, sal_Size nContSize, const XclAddress& rXclPos ) :
1089 XclExpCellBase( nRecId, 0, rXclPos ),
1090 mnMulRecId( nMulRecId ),
1091 mnContSize( nContSize )
1095 sal_uInt16 XclExpMultiCellBase::GetLastXclCol() const
1097 return GetXclCol() + GetCellCount() - 1;
1100 sal_uInt32 XclExpMultiCellBase::GetFirstXFId() const
1102 return maXFIds.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds.front().mnXFId;
1105 bool XclExpMultiCellBase::IsEmpty() const
1107 return maXFIds.empty();
1110 void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
1112 for( XclExpMultiXFIdDeq::iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
1113 aIt->ConvertXFIndex( rRoot );
1116 void XclExpMultiCellBase::Save( XclExpStream& rStrm )
1118 OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1120 XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1121 XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1122 XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1123 sal_uInt16 nBegXclCol = GetXclCol();
1124 sal_uInt16 nEndXclCol = nBegXclCol;
1126 while( aRangeEnd != aEnd )
1128 // find begin of next used XF range
1129 aRangeBeg = aRangeEnd;
1130 nBegXclCol = nEndXclCol;
1131 while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1133 nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1134 ++aRangeBeg;
1136 // find end of next used XF range
1137 aRangeEnd = aRangeBeg;
1138 nEndXclCol = nBegXclCol;
1139 while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1141 nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1142 ++aRangeEnd;
1145 // export this range as a record
1146 if( aRangeBeg != aRangeEnd )
1148 sal_uInt16 nCount = nEndXclCol - nBegXclCol;
1149 bool bIsMulti = nCount > 1;
1150 sal_Size nTotalSize = GetRecSize() + (2 + mnContSize) * nCount;
1151 if( bIsMulti ) nTotalSize += 2;
1153 rStrm.StartRecord( bIsMulti ? mnMulRecId : GetRecId(), nTotalSize );
1154 rStrm << static_cast<sal_uInt16> (GetXclRow()) << nBegXclCol;
1156 sal_uInt16 nRelCol = nBegXclCol - GetXclCol();
1157 for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1159 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1161 rStrm << aIt->mnXFIndex;
1162 WriteContents( rStrm, nRelCol );
1163 ++nRelCol;
1166 if( bIsMulti )
1167 rStrm << static_cast< sal_uInt16 >( nEndXclCol - 1 );
1168 rStrm.EndRecord();
1173 void XclExpMultiCellBase::SaveXml( XclExpXmlStream& rStrm )
1175 XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1176 XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1177 XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1178 sal_uInt16 nBegXclCol = GetXclCol();
1179 sal_uInt16 nEndXclCol = nBegXclCol;
1181 while( aRangeEnd != aEnd )
1183 // find begin of next used XF range
1184 aRangeBeg = aRangeEnd;
1185 nBegXclCol = nEndXclCol;
1186 while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1188 nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1189 ++aRangeBeg;
1191 // find end of next used XF range
1192 aRangeEnd = aRangeBeg;
1193 nEndXclCol = nBegXclCol;
1194 while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1196 nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1197 ++aRangeEnd;
1200 // export this range as a record
1201 if( aRangeBeg != aRangeEnd )
1203 sal_uInt16 nRelColIdx = nBegXclCol - GetXclCol();
1204 sal_Int32 nRelCol = 0;
1205 for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1207 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1209 WriteXmlContents(
1210 rStrm,
1211 XclAddress( static_cast<sal_uInt16>(nBegXclCol + nRelCol), GetXclRow() ),
1212 aIt->mnXFIndex,
1213 nRelColIdx );
1214 ++nRelCol;
1215 ++nRelColIdx;
1222 sal_uInt16 XclExpMultiCellBase::GetCellCount() const
1224 sal_uInt16 nCount = 0;
1225 for( XclExpMultiXFIdDeq::const_iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
1226 nCount = nCount + aIt->mnCount;
1227 return nCount;
1230 void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId& rXFId )
1232 if( maXFIds.empty() || (maXFIds.back().mnXFId != rXFId.mnXFId) )
1233 maXFIds.push_back( rXFId );
1234 else
1235 maXFIds.back().mnCount = maXFIds.back().mnCount + rXFId.mnCount;
1238 void XclExpMultiCellBase::AppendXFId( const XclExpRoot& rRoot,
1239 const ScPatternAttr* pPattern, sal_uInt16 nScript, sal_uInt32 nForcedXFId, sal_uInt16 nCount )
1241 sal_uInt32 nXFId = (nForcedXFId == EXC_XFID_NOTFOUND) ?
1242 rRoot.GetXFBuffer().Insert( pPattern, nScript ) : nForcedXFId;
1243 AppendXFId( XclExpMultiXFId( nXFId, nCount ) );
1246 bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase& rCell )
1248 if( GetLastXclCol() + 1 == rCell.GetXclCol() )
1250 maXFIds.insert( maXFIds.end(), rCell.maXFIds.begin(), rCell.maXFIds.end() );
1251 return true;
1253 return false;
1256 void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1258 OSL_ENSURE( GetLastXclCol() < rXFIndexes.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" );
1259 ScfUInt16Vec::iterator aDestIt = rXFIndexes.begin() + GetXclCol();
1260 for( XclExpMultiXFIdDeq::const_iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
1262 ::std::fill( aDestIt, aDestIt + aIt->mnCount, aIt->mnXFIndex );
1263 aDestIt += aIt->mnCount;
1267 void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes )
1269 // save last column before calling maXFIds.clear()
1270 sal_uInt16 nLastXclCol = GetLastXclCol();
1271 OSL_ENSURE( nLastXclCol < rXFIndexes.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" );
1273 // build new XF index vector, containing passed XF indexes
1274 maXFIds.clear();
1275 XclExpMultiXFId aXFId( 0 );
1276 for( ScfUInt16Vec::const_iterator aIt = rXFIndexes.begin() + GetXclCol(), aEnd = rXFIndexes.begin() + nLastXclCol + 1; aIt != aEnd; ++aIt )
1278 // AppendXFId() tests XclExpXFIndex::mnXFId, set it too
1279 aXFId.mnXFId = aXFId.mnXFIndex = *aIt;
1280 AppendXFId( aXFId );
1283 // remove leading and trailing unused XF indexes
1284 if( !maXFIds.empty() && (maXFIds.front().mnXFIndex == EXC_XF_NOTFOUND) )
1286 SetXclCol( GetXclCol() + maXFIds.front().mnCount );
1287 maXFIds.erase(maXFIds.begin(), maXFIds.begin() + 1);
1289 if( !maXFIds.empty() && (maXFIds.back().mnXFIndex == EXC_XF_NOTFOUND) )
1290 maXFIds.pop_back();
1292 // The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND.
1295 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpBlankCell )
1297 XclExpBlankCell::XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId ) :
1298 XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1300 OSL_ENSURE( rXFId.mnCount > 0, "XclExpBlankCell::XclExpBlankCell - invalid count" );
1301 AppendXFId( rXFId );
1304 XclExpBlankCell::XclExpBlankCell(
1305 const XclExpRoot& rRoot, const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
1306 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId ) :
1307 XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1309 OSL_ENSURE( rXclPos.mnCol <= nLastXclCol, "XclExpBlankCell::XclExpBlankCell - invalid column range" );
1310 // #i46627# use default script type instead of ApiScriptType::WEAK
1311 AppendXFId( rRoot, pPattern, rRoot.GetDefApiScript(), nForcedXFId, nLastXclCol - rXclPos.mnCol + 1 );
1314 bool XclExpBlankCell::TryMerge( const XclExpCellBase& rCell )
1316 const XclExpBlankCell* pBlankCell = dynamic_cast< const XclExpBlankCell* >( &rCell );
1317 return pBlankCell && TryMergeXFIds( *pBlankCell );
1320 void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1322 GetXFIndexes( rXFIndexes );
1325 void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes )
1327 RemoveUnusedXFIndexes( rXFIndexes );
1330 void XclExpBlankCell::WriteContents( XclExpStream& /*rStrm*/, sal_uInt16 /*nRelCol*/ )
1334 void XclExpBlankCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 /* nRelCol */ )
1336 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1337 rWorksheet->singleElement( XML_c,
1338 XML_r, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), rAddress ).getStr(),
1339 XML_s, lcl_GetStyleId( rStrm, nXFId ).getStr(),
1340 FSEND );
1343 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpRkCell )
1345 XclExpRkCell::XclExpRkCell(
1346 const XclExpRoot& rRoot, const XclAddress& rXclPos,
1347 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_Int32 nRkValue ) :
1348 XclExpMultiCellBase( EXC_ID_RK, EXC_ID_MULRK, 4, rXclPos )
1350 // #i41210# always use latin script for number cells - may look wrong for special number formats...
1351 AppendXFId( rRoot, pPattern, ApiScriptType::LATIN, nForcedXFId );
1352 maRkValues.push_back( nRkValue );
1355 bool XclExpRkCell::TryMerge( const XclExpCellBase& rCell )
1357 const XclExpRkCell* pRkCell = dynamic_cast< const XclExpRkCell* >( &rCell );
1358 if( pRkCell && TryMergeXFIds( *pRkCell ) )
1360 maRkValues.insert( maRkValues.end(), pRkCell->maRkValues.begin(), pRkCell->maRkValues.end() );
1361 return true;
1363 return false;
1366 void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol )
1368 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1369 rWorksheet->startElement( XML_c,
1370 XML_r, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), rAddress ).getStr(),
1371 XML_s, lcl_GetStyleId( rStrm, nXFId ).getStr(),
1372 XML_t, "n",
1373 // OOXTODO: XML_cm, XML_vm, XML_ph
1374 FSEND );
1375 rWorksheet->startElement( XML_v, FSEND );
1376 rWorksheet->write( XclTools::GetDoubleFromRK( maRkValues[ nRelCol ] ) );
1377 rWorksheet->endElement( XML_v );
1378 rWorksheet->endElement( XML_c );
1381 void XclExpRkCell::WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol )
1383 OSL_ENSURE( nRelCol < maRkValues.size(), "XclExpRkCell::WriteContents - overflow error" );
1384 rStrm << maRkValues[ nRelCol ];
1387 // Rows and Columns
1389 XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows ) :
1390 mpScOLArray( 0 ),
1391 maLevelInfos( SC_OL_MAXDEPTH ),
1392 mnCurrLevel( 0 ),
1393 mbCurrCollapse( false )
1395 if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
1396 mpScOLArray = &(bRows ? pOutlineTable->GetRowArray() : pOutlineTable->GetColArray());
1398 if( mpScOLArray )
1399 for( size_t nLevel = 0; nLevel < SC_OL_MAXDEPTH; ++nLevel )
1400 if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nLevel, 0 ) )
1401 maLevelInfos[ nLevel ].mnScEndPos = pEntry->GetEnd();
1404 void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos )
1406 if( mpScOLArray )
1408 // find open level index for passed position
1409 size_t nNewOpenScLevel = 0; // new open level (0-based Calc index)
1410 sal_uInt8 nNewLevel = 0; // new open level (1-based Excel index)
1412 if( mpScOLArray->FindTouchedLevel( nScPos, nScPos, nNewOpenScLevel ) )
1413 nNewLevel = static_cast< sal_uInt8 >( nNewOpenScLevel + 1 );
1414 // else nNewLevel keeps 0 to show that there are no groups
1416 mbCurrCollapse = false;
1417 if( nNewLevel >= mnCurrLevel )
1419 // new level(s) opened, or no level closed - update all level infos
1420 for( sal_uInt16 nScLevel = 0; nScLevel <= nNewOpenScLevel; ++nScLevel )
1422 /* In each level: check if a new group is started (there may be
1423 neighbored groups without gap - therefore check ALL levels). */
1424 if( maLevelInfos[ nScLevel ].mnScEndPos < nScPos )
1426 if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nScLevel, nScPos ) )
1428 maLevelInfos[ nScLevel ].mnScEndPos = pEntry->GetEnd();
1429 maLevelInfos[ nScLevel ].mbHidden = pEntry->IsHidden();
1434 else
1436 // level(s) closed - check if any of the closed levels are collapsed
1437 // Calc uses 0-based level indexes
1438 sal_uInt16 nOldOpenScLevel = mnCurrLevel - 1;
1439 for( sal_uInt16 nScLevel = nNewOpenScLevel + 1; !mbCurrCollapse && (nScLevel <= nOldOpenScLevel); ++nScLevel )
1440 mbCurrCollapse = maLevelInfos[ nScLevel ].mbHidden;
1443 // cache new opened level
1444 mnCurrLevel = nNewLevel;
1448 XclExpGuts::XclExpGuts( const XclExpRoot& rRoot ) :
1449 XclExpRecord( EXC_ID_GUTS, 8 ),
1450 mnColLevels( 0 ),
1451 mnColWidth( 0 ),
1452 mnRowLevels( 0 ),
1453 mnRowWidth( 0 )
1455 if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
1457 // column outline groups
1458 const ScOutlineArray& rColArray = pOutlineTable->GetColArray();
1459 mnColLevels = ulimit_cast< sal_uInt16 >( rColArray.GetDepth(), EXC_OUTLINE_MAX );
1460 if( mnColLevels )
1462 ++mnColLevels;
1463 mnColWidth = 12 * mnColLevels + 5;
1466 // row outline groups
1467 const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
1468 mnRowLevels = ulimit_cast< sal_uInt16 >( rRowArray.GetDepth(), EXC_OUTLINE_MAX );
1469 if( mnRowLevels )
1471 ++mnRowLevels;
1472 mnRowWidth = 12 * mnRowLevels + 5;
1477 void XclExpGuts::WriteBody( XclExpStream& rStrm )
1479 rStrm << mnRowWidth << mnColWidth << mnRowLevels << mnColLevels;
1482 XclExpDimensions::XclExpDimensions( const XclExpRoot& rRoot ) :
1483 mnFirstUsedXclRow( 0 ),
1484 mnFirstFreeXclRow( 0 ),
1485 mnFirstUsedXclCol( 0 ),
1486 mnFirstFreeXclCol( 0 )
1488 switch( rRoot.GetBiff() )
1490 case EXC_BIFF2: SetRecHeader( EXC_ID2_DIMENSIONS, 8 ); break;
1491 case EXC_BIFF3:
1492 case EXC_BIFF4:
1493 case EXC_BIFF5: SetRecHeader( EXC_ID3_DIMENSIONS, 10 ); break;
1494 case EXC_BIFF8: SetRecHeader( EXC_ID3_DIMENSIONS, 14 ); break;
1495 default: DBG_ERROR_BIFF();
1499 void XclExpDimensions::SetDimensions(
1500 sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
1501 sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow )
1503 mnFirstUsedXclRow = nFirstUsedXclRow;
1504 mnFirstFreeXclRow = nFirstFreeXclRow;
1505 mnFirstUsedXclCol = nFirstUsedXclCol;
1506 mnFirstFreeXclCol = nFirstFreeXclCol;
1509 void XclExpDimensions::SaveXml( XclExpXmlStream& rStrm )
1511 ScRange aRange;
1512 aRange.aStart.SetRow( (SCROW) mnFirstUsedXclRow );
1513 aRange.aStart.SetCol( (SCCOL) mnFirstUsedXclCol );
1515 if( mnFirstFreeXclRow != mnFirstUsedXclRow && mnFirstFreeXclCol != mnFirstUsedXclCol )
1517 aRange.aEnd.SetRow( (SCROW) (mnFirstFreeXclRow-1) );
1518 aRange.aEnd.SetCol( (SCCOL) (mnFirstFreeXclCol-1) );
1521 rStrm.GetCurrentStream()->singleElement( XML_dimension,
1522 XML_ref, XclXmlUtils::ToOString( aRange ).getStr(),
1523 FSEND );
1526 void XclExpDimensions::WriteBody( XclExpStream& rStrm )
1528 XclBiff eBiff = rStrm.GetRoot().GetBiff();
1529 if( eBiff == EXC_BIFF8 )
1530 rStrm << mnFirstUsedXclRow << mnFirstFreeXclRow;
1531 else
1532 rStrm << static_cast< sal_uInt16 >( mnFirstUsedXclRow ) << static_cast< sal_uInt16 >( mnFirstFreeXclRow );
1533 rStrm << mnFirstUsedXclCol << mnFirstFreeXclCol;
1534 if( eBiff >= EXC_BIFF3 )
1535 rStrm << sal_uInt16( 0 );
1538 namespace {
1540 double lclGetCorrectedColWidth( const XclExpRoot& rRoot, sal_uInt16 nXclColWidth )
1542 long nFontHt = rRoot.GetFontBuffer().GetAppFontData().mnHeight;
1543 return nXclColWidth - XclTools::GetXclDefColWidthCorrection( nFontHt );
1546 } // namespace
1548 XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot& rRoot ) :
1549 XclExpUInt16Record( EXC_ID_DEFCOLWIDTH, EXC_DEFCOLWIDTH_DEF ),
1550 XclExpRoot( rRoot )
1554 bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth ) const
1556 double fNewColWidth = lclGetCorrectedColWidth( GetRoot(), nXclColWidth );
1557 // exactly matched, if difference is less than 1/16 of a character to the left or to the right
1558 return std::abs( static_cast< long >( GetValue() * 256.0 - fNewColWidth + 0.5 ) ) < 16;
1561 void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth )
1563 double fNewColWidth = lclGetCorrectedColWidth( GetRoot(), nXclColWidth );
1564 SetValue( limit_cast< sal_uInt16 >( fNewColWidth / 256.0 + 0.5 ) );
1567 XclExpColinfo::XclExpColinfo( const XclExpRoot& rRoot,
1568 SCCOL nScCol, SCROW nLastScRow, XclExpColOutlineBuffer& rOutlineBfr ) :
1569 XclExpRecord( EXC_ID_COLINFO, 12 ),
1570 XclExpRoot( rRoot ),
1571 mnWidth( 0 ),
1572 mnScWidth( 0 ),
1573 mnFlags( 0 ),
1574 mnFirstXclCol( static_cast< sal_uInt16 >( nScCol ) ),
1575 mnLastXclCol( static_cast< sal_uInt16 >( nScCol ) )
1577 ScDocument& rDoc = GetDoc();
1578 SCTAB nScTab = GetCurrScTab();
1580 // column default format
1581 maXFId.mnXFId = GetXFBuffer().Insert(
1582 rDoc.GetMostUsedPattern( nScCol, 0, nLastScRow, nScTab ), GetDefApiScript() );
1584 // column width
1585 sal_uInt16 nScWidth = rDoc.GetColWidth( nScCol, nScTab );
1586 mnWidth = XclTools::GetXclColumnWidth( nScWidth, GetCharWidth() );
1587 mnScWidth = sc::TwipsToHMM( nScWidth );
1588 // column flags
1589 ::set_flag( mnFlags, EXC_COLINFO_HIDDEN, rDoc.ColHidden(nScCol, nScTab) );
1591 // outline data
1592 rOutlineBfr.Update( nScCol );
1593 ::set_flag( mnFlags, EXC_COLINFO_COLLAPSED, rOutlineBfr.IsCollapsed() );
1594 ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 8, 3 );
1597 sal_uInt16 XclExpColinfo::ConvertXFIndexes()
1599 maXFId.ConvertXFIndex( GetRoot() );
1600 return maXFId.mnXFIndex;
1603 bool XclExpColinfo::IsDefault( const XclExpDefcolwidth& rDefColWidth ) const
1605 return (maXFId.mnXFIndex == EXC_XF_DEFAULTCELL) && (mnFlags == 0) && rDefColWidth.IsDefWidth( mnWidth );
1608 bool XclExpColinfo::TryMerge( const XclExpColinfo& rColInfo )
1610 if( (maXFId.mnXFIndex == rColInfo.maXFId.mnXFIndex) &&
1611 (mnWidth == rColInfo.mnWidth) &&
1612 (mnFlags == rColInfo.mnFlags) &&
1613 (mnLastXclCol + 1 == rColInfo.mnFirstXclCol) )
1615 mnLastXclCol = rColInfo.mnLastXclCol;
1616 return true;
1618 return false;
1621 void XclExpColinfo::WriteBody( XclExpStream& rStrm )
1623 // if last column is equal to last possible column, Excel adds one more
1624 sal_uInt16 nLastXclCol = mnLastXclCol;
1625 if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
1626 ++nLastXclCol;
1628 rStrm << mnFirstXclCol
1629 << nLastXclCol
1630 << mnWidth
1631 << maXFId.mnXFIndex
1632 << mnFlags
1633 << sal_uInt16( 0 );
1636 void XclExpColinfo::SaveXml( XclExpXmlStream& rStrm )
1638 // if last column is equal to last possible column, Excel adds one more
1639 sal_uInt16 nLastXclCol = mnLastXclCol;
1640 if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
1641 ++nLastXclCol;
1643 rStrm.GetCurrentStream()->singleElement( XML_col,
1644 // OOXTODO: XML_bestFit,
1645 XML_collapsed, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_COLINFO_COLLAPSED ) ),
1646 // OOXTODO: XML_customWidth,
1647 XML_hidden, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_COLINFO_HIDDEN ) ),
1648 XML_max, OString::number( (nLastXclCol+1) ).getStr(),
1649 XML_min, OString::number( (mnFirstXclCol+1) ).getStr(),
1650 // OOXTODO: XML_outlineLevel,
1651 // OOXTODO: XML_phonetic,
1652 XML_style, lcl_GetStyleId( rStrm, maXFId.mnXFIndex ).getStr(),
1653 XML_width, OString::number( (double) (mnScWidth / (double)sc::TwipsToHMM( GetCharWidth() )) ).getStr(),
1654 FSEND );
1657 XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot& rRoot ) :
1658 XclExpRoot( rRoot ),
1659 maDefcolwidth( rRoot ),
1660 maOutlineBfr( rRoot )
1664 void XclExpColinfoBuffer::Initialize( SCROW nLastScRow )
1667 for( sal_uInt16 nScCol = 0, nLastScCol = GetMaxPos().Col(); nScCol <= nLastScCol; ++nScCol )
1668 maColInfos.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol, nLastScRow, maOutlineBfr ) );
1671 void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes )
1673 rXFIndexes.clear();
1674 rXFIndexes.reserve( maColInfos.GetSize() );
1676 size_t nPos, nSize;
1678 // do not cache the record list size, it may change in the loop
1679 for( nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
1681 XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
1682 xRec->ConvertXFIndexes();
1684 // try to merge with previous record
1685 if( nPos > 0 )
1687 XclExpColinfoRef xPrevRec = maColInfos.GetRecord( nPos - 1 );
1688 if( xPrevRec->TryMerge( *xRec ) )
1689 // adjust nPos to get the next COLINFO record at the same position
1690 maColInfos.RemoveRecord( nPos-- );
1694 // put XF indexes into passed vector, collect use count of all different widths
1695 typedef ::std::map< sal_uInt16, sal_uInt16 > XclExpWidthMap;
1696 XclExpWidthMap aWidthMap;
1697 sal_uInt16 nMaxColCount = 0;
1698 sal_uInt16 nMaxUsedWidth = 0;
1699 for( nPos = 0, nSize = maColInfos.GetSize(); nPos < nSize; ++nPos )
1701 XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
1702 sal_uInt16 nColCount = xRec->GetColCount();
1704 // add XF index to passed vector
1705 rXFIndexes.resize( rXFIndexes.size() + nColCount, xRec->GetXFIndex() );
1707 // collect use count of column width
1708 sal_uInt16 nWidth = xRec->GetColWidth();
1709 sal_uInt16& rnMapCount = aWidthMap[ nWidth ];
1710 rnMapCount = rnMapCount + nColCount;
1711 if( rnMapCount > nMaxColCount )
1713 nMaxColCount = rnMapCount;
1714 nMaxUsedWidth = nWidth;
1717 maDefcolwidth.SetDefWidth( nMaxUsedWidth );
1719 // remove all default COLINFO records
1720 nPos = 0;
1721 while( nPos < maColInfos.GetSize() )
1723 XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
1724 if( xRec->IsDefault( maDefcolwidth ) )
1725 maColInfos.RemoveRecord( nPos );
1726 else
1727 ++nPos;
1731 void XclExpColinfoBuffer::Save( XclExpStream& rStrm )
1733 // DEFCOLWIDTH
1734 maDefcolwidth.Save( rStrm );
1735 // COLINFO records
1736 maColInfos.Save( rStrm );
1739 void XclExpColinfoBuffer::SaveXml( XclExpXmlStream& rStrm )
1741 if( maColInfos.IsEmpty() )
1742 return;
1744 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1745 rWorksheet->startElement( XML_cols,
1746 FSEND );
1747 maColInfos.SaveXml( rStrm );
1748 rWorksheet->endElement( XML_cols );
1751 XclExpDefaultRowData::XclExpDefaultRowData() :
1752 mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1753 mnHeight( EXC_DEFROW_DEFAULTHEIGHT )
1757 XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow& rRow ) :
1758 mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1759 mnHeight( rRow.GetHeight() )
1761 ::set_flag( mnFlags, EXC_DEFROW_HIDDEN, rRow.IsHidden() );
1762 ::set_flag( mnFlags, EXC_DEFROW_UNSYNCED, rRow.IsUnsynced() );
1765 bool operator<( const XclExpDefaultRowData& rLeft, const XclExpDefaultRowData& rRight )
1767 return (rLeft.mnHeight < rRight.mnHeight) ||
1768 ((rLeft.mnHeight == rRight.mnHeight) && (rLeft.mnFlags < rRight.mnFlags));
1771 XclExpDefrowheight::XclExpDefrowheight() :
1772 XclExpRecord( EXC_ID3_DEFROWHEIGHT, 4 )
1776 void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData& rDefData )
1778 maDefData = rDefData;
1781 void XclExpDefrowheight::WriteBody( XclExpStream& rStrm )
1783 OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1784 rStrm << maDefData.mnFlags << maDefData.mnHeight;
1787 XclExpRow::XclExpRow( const XclExpRoot& rRoot, sal_uInt32 nXclRow,
1788 XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty ) :
1789 XclExpRecord( EXC_ID3_ROW, 16 ),
1790 XclExpRoot( rRoot ),
1791 mnXclRow( nXclRow ),
1792 mnHeight( 0 ),
1793 mnFlags( EXC_ROW_DEFAULTFLAGS ),
1794 mnXFIndex( EXC_XF_DEFAULTCELL ),
1795 mnOutlineLevel( 0 ),
1796 mnXclRowRpt( 1 ),
1797 mnCurrentRow( nXclRow ),
1798 mbAlwaysEmpty( bAlwaysEmpty ),
1799 mbEnabled( true )
1801 SCTAB nScTab = GetCurrScTab();
1802 SCROW nScRow = static_cast< SCROW >( mnXclRow );
1804 // *** Row flags *** ------------------------------------------------------
1806 sal_uInt8 nRowFlags = GetDoc().GetRowFlags( nScRow, nScTab );
1807 bool bUserHeight = ::get_flag< sal_uInt8 >( nRowFlags, CR_MANUALSIZE );
1808 bool bHidden = GetDoc().RowHidden(nScRow, nScTab);
1809 ::set_flag( mnFlags, EXC_ROW_UNSYNCED, bUserHeight );
1810 ::set_flag( mnFlags, EXC_ROW_HIDDEN, bHidden );
1812 // *** Row height *** -----------------------------------------------------
1814 // Always get the actual row height even if the manual size flag is not set,
1815 // to correctly export the heights of rows with wrapped texts.
1817 mnHeight = GetDoc().GetRowHeight(nScRow, nScTab, false);
1819 // *** Outline data *** ---------------------------------------------------
1821 rOutlineBfr.Update( nScRow );
1822 ::set_flag( mnFlags, EXC_ROW_COLLAPSED, rOutlineBfr.IsCollapsed() );
1823 ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 0, 3 );
1824 mnOutlineLevel = rOutlineBfr.GetLevel();
1826 // *** Progress bar *** ---------------------------------------------------
1828 XclExpProgressBar& rProgress = GetProgressBar();
1829 rProgress.IncRowRecordCount();
1830 rProgress.Progress();
1833 void XclExpRow::AppendCell( XclExpCellRef xCell, bool bIsMergedBase )
1835 OSL_ENSURE( !mbAlwaysEmpty, "XclExpRow::AppendCell - row is marked to be always empty" );
1836 // try to merge with last existing cell
1837 InsertCell( xCell, maCellList.GetSize(), bIsMergedBase );
1840 void XclExpRow::Finalize( const ScfUInt16Vec& rColXFIndexes, bool bProgress )
1842 size_t nPos, nSize;
1844 // *** Convert XF identifiers *** -----------------------------------------
1846 // additionally collect the blank XF indexes
1847 size_t nColCount = GetMaxPos().Col() + 1;
1848 OSL_ENSURE( rColXFIndexes.size() == nColCount, "XclExpRow::Finalize - wrong column XF index count" );
1850 ScfUInt16Vec aXFIndexes( nColCount, EXC_XF_NOTFOUND );
1851 for( nPos = 0, nSize = maCellList.GetSize(); nPos < nSize; ++nPos )
1853 XclExpCellRef xCell = maCellList.GetRecord( nPos );
1854 xCell->ConvertXFIndexes( GetRoot() );
1855 xCell->GetBlankXFIndexes( aXFIndexes );
1858 // *** Fill gaps with BLANK/MULBLANK cell records *** ---------------------
1860 /* This is needed because nonexistant cells in Calc are not formatted at all,
1861 but in Excel they would have the column default format. Blank cells that
1862 are equal to the respective column default are removed later in this function. */
1863 if( !mbAlwaysEmpty )
1865 // XF identifier representing default cell XF
1866 XclExpMultiXFId aXFId( XclExpXFBuffer::GetDefCellXFId() );
1867 aXFId.ConvertXFIndex( GetRoot() );
1869 nPos = 0;
1870 while( nPos <= maCellList.GetSize() ) // don't cache list size, may change in the loop
1872 // get column index that follows previous cell
1873 sal_uInt16 nFirstFreeXclCol = (nPos > 0) ? (maCellList.GetRecord( nPos - 1 )->GetLastXclCol() + 1) : 0;
1874 // get own column index
1875 sal_uInt16 nNextUsedXclCol = (nPos < maCellList.GetSize()) ? maCellList.GetRecord( nPos )->GetXclCol() : (GetMaxPos().Col() + 1);
1877 // is there a gap?
1878 if( nFirstFreeXclCol < nNextUsedXclCol )
1880 aXFId.mnCount = nNextUsedXclCol - nFirstFreeXclCol;
1881 XclExpCellRef xNewCell( new XclExpBlankCell( XclAddress( nFirstFreeXclCol, mnXclRow ), aXFId ) );
1882 // insert the cell, InsertCell() may merge it with existing BLANK records
1883 InsertCell( xNewCell, nPos, false );
1884 // insert default XF indexes into aXFIndexes
1885 ::std::fill( aXFIndexes.begin() + nFirstFreeXclCol,
1886 aXFIndexes.begin() + nNextUsedXclCol, aXFId.mnXFIndex );
1887 // don't step forward with nPos, InsertCell() may remove records
1889 else
1890 ++nPos;
1894 // *** Find default row format *** ----------------------------------------
1896 ScfUInt16Vec::iterator aCellBeg = aXFIndexes.begin(), aCellEnd = aXFIndexes.end(), aCellIt;
1897 ScfUInt16Vec::const_iterator aColBeg = rColXFIndexes.begin(), aColIt;
1899 // find most used XF index in the row
1900 typedef ::std::map< sal_uInt16, size_t > XclExpXFIndexMap;
1901 XclExpXFIndexMap aIndexMap;
1902 sal_uInt16 nRowXFIndex = EXC_XF_DEFAULTCELL;
1903 size_t nMaxXFCount = 0;
1904 for( aCellIt = aCellBeg; aCellIt != aCellEnd; ++aCellIt )
1906 if( *aCellIt != EXC_XF_NOTFOUND )
1908 size_t& rnCount = aIndexMap[ *aCellIt ];
1909 ++rnCount;
1910 if( rnCount > nMaxXFCount )
1912 nRowXFIndex = *aCellIt;
1913 nMaxXFCount = rnCount;
1918 // decide whether to use the row default XF index or column default XF indexes
1919 bool bUseColDefXFs = nRowXFIndex == EXC_XF_DEFAULTCELL;
1920 if( !bUseColDefXFs )
1922 // count needed XF indexes for blank cells with and without row default XF index
1923 size_t nXFCountWithRowDefXF = 0;
1924 size_t nXFCountWithoutRowDefXF = 0;
1925 for( aCellIt = aCellBeg, aColIt = aColBeg; aCellIt != aCellEnd; ++aCellIt, ++aColIt )
1927 sal_uInt16 nXFIndex = *aCellIt;
1928 if( nXFIndex != EXC_XF_NOTFOUND )
1930 if( nXFIndex != nRowXFIndex )
1931 ++nXFCountWithRowDefXF; // with row default XF index
1932 if( nXFIndex != *aColIt )
1933 ++nXFCountWithoutRowDefXF; // without row default XF index
1937 // use column XF indexes if this would cause less or equal number of BLANK records
1938 bUseColDefXFs = nXFCountWithoutRowDefXF <= nXFCountWithRowDefXF;
1941 // *** Remove unused BLANK cell records *** -------------------------------
1943 if( bUseColDefXFs )
1945 // use column default XF indexes
1946 // #i194#: remove cell XF indexes equal to column default XF indexes
1947 for( aCellIt = aCellBeg, aColIt = aColBeg; aCellIt != aCellEnd; ++aCellIt, ++aColIt )
1948 if( *aCellIt == *aColIt )
1949 *aCellIt = EXC_XF_NOTFOUND;
1951 else
1953 // use row default XF index
1954 mnXFIndex = nRowXFIndex;
1955 ::set_flag( mnFlags, EXC_ROW_USEDEFXF );
1956 // #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index
1957 for( aCellIt = aCellBeg; aCellIt != aCellEnd; ++aCellIt )
1958 if( *aCellIt == nRowXFIndex )
1959 *aCellIt = EXC_XF_NOTFOUND;
1962 // remove unused parts of BLANK/MULBLANK cell records
1963 nPos = 0;
1964 while( nPos < maCellList.GetSize() ) // do not cache list size, may change in the loop
1966 XclExpCellRef xCell = maCellList.GetRecord( nPos );
1967 xCell->RemoveUnusedBlankCells( aXFIndexes );
1968 if( xCell->IsEmpty() )
1969 maCellList.RemoveRecord( nPos );
1970 else
1971 ++nPos;
1974 // progress bar includes disabled rows; only update it in the lead thread.
1975 if (bProgress)
1976 GetProgressBar().Progress();
1978 sal_uInt16 XclExpRow::GetFirstUsedXclCol() const
1980 return maCellList.IsEmpty() ? 0 : maCellList.GetFirstRecord()->GetXclCol();
1983 sal_uInt16 XclExpRow::GetFirstFreeXclCol() const
1985 return maCellList.IsEmpty() ? 0 : (maCellList.GetLastRecord()->GetLastXclCol() + 1);
1988 bool XclExpRow::IsDefaultable() const
1990 const sal_uInt16 nAllowedFlags = EXC_ROW_DEFAULTFLAGS | EXC_ROW_HIDDEN | EXC_ROW_UNSYNCED;
1991 return !::get_flag( mnFlags, static_cast< sal_uInt16 >( ~nAllowedFlags ) ) && IsEmpty();
1994 void XclExpRow::DisableIfDefault( const XclExpDefaultRowData& rDefRowData )
1996 mbEnabled = !IsDefaultable() ||
1997 (mnHeight != rDefRowData.mnHeight) ||
1998 (IsHidden() != rDefRowData.IsHidden()) ||
1999 (IsUnsynced() != rDefRowData.IsUnsynced());
2002 void XclExpRow::WriteCellList( XclExpStream& rStrm )
2004 OSL_ENSURE( mbEnabled || maCellList.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" );
2005 maCellList.Save( rStrm );
2008 void XclExpRow::Save( XclExpStream& rStrm )
2010 if( mbEnabled )
2012 mnCurrentRow = mnXclRow;
2013 for ( sal_uInt32 i = 0; i < mnXclRowRpt; ++i, ++mnCurrentRow )
2014 XclExpRecord::Save( rStrm );
2018 void XclExpRow::InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase )
2020 OSL_ENSURE( xCell, "XclExpRow::InsertCell - missing cell" );
2022 /* If we have a multi-line text in a merged cell, and the resulting
2023 row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED
2024 flag to be true to ensure Excel works correctly. */
2025 if( bIsMergedBase && xCell->IsMultiLineText() )
2026 ::set_flag( mnFlags, EXC_ROW_UNSYNCED );
2028 // try to merge with previous cell, insert the new cell if not successful
2029 XclExpCellRef xPrevCell = maCellList.GetRecord( nPos - 1 );
2030 if( xPrevCell && xPrevCell->TryMerge( *xCell ) )
2031 xCell = xPrevCell;
2032 else
2033 maCellList.InsertRecord( xCell, nPos++ );
2034 // nPos points now to following cell
2036 // try to merge with following cell, remove it if successful
2037 XclExpCellRef xNextCell = maCellList.GetRecord( nPos );
2038 if( xNextCell && xCell->TryMerge( *xNextCell ) )
2039 maCellList.RemoveRecord( nPos );
2042 void XclExpRow::WriteBody( XclExpStream& rStrm )
2044 rStrm << static_cast< sal_uInt16 >(mnCurrentRow)
2045 << GetFirstUsedXclCol()
2046 << GetFirstFreeXclCol()
2047 << mnHeight
2048 << sal_uInt32( 0 )
2049 << mnFlags
2050 << mnXFIndex;
2053 void XclExpRow::SaveXml( XclExpXmlStream& rStrm )
2055 if( !mbEnabled )
2056 return;
2057 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2058 bool haveFormat = ::get_flag( mnFlags, EXC_ROW_USEDEFXF );
2059 mnCurrentRow = mnXclRow + 1;
2060 for ( sal_uInt32 i=0; i<mnXclRowRpt; ++i )
2062 rWorksheet->startElement( XML_row,
2063 XML_r, OString::number( (mnCurrentRow++) ).getStr(),
2064 // OOXTODO: XML_spans, optional
2065 XML_s, haveFormat ? lcl_GetStyleId( rStrm, mnXFIndex ).getStr() : NULL,
2066 XML_customFormat, XclXmlUtils::ToPsz( haveFormat ),
2067 XML_ht, OString::number( (double) mnHeight / 20.0 ).getStr(),
2068 XML_hidden, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_HIDDEN ) ),
2069 XML_customHeight, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_UNSYNCED ) ),
2070 XML_outlineLevel, OString::number( mnOutlineLevel ).getStr(),
2071 XML_collapsed, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_COLLAPSED ) ),
2072 // OOXTODO: XML_thickTop, bool
2073 // OOXTODO: XML_thickBot, bool
2074 // OOXTODO: XML_ph, bool
2075 FSEND );
2076 // OOXTODO: XML_extLst
2077 maCellList.SaveXml( rStrm );
2078 rWorksheet->endElement( XML_row );
2082 XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot& rRoot ) :
2083 XclExpRoot( rRoot ),
2084 maOutlineBfr( rRoot ),
2085 maDimensions( rRoot )
2089 void XclExpRowBuffer::AppendCell( XclExpCellRef xCell, bool bIsMergedBase )
2091 OSL_ENSURE( xCell, "XclExpRowBuffer::AppendCell - missing cell" );
2092 GetOrCreateRow( xCell->GetXclRow(), false ).AppendCell( xCell, bIsMergedBase );
2095 void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow )
2097 if( nFirstFreeScRow > 0 )
2098 GetOrCreateRow( ::std::max ( nFirstFreeScRow - 1, GetMaxPos().Row() ), true );
2101 class RowFinalizeTask : public comphelper::ThreadTask
2103 bool mbProgress;
2104 const ScfUInt16Vec& mrColXFIndexes;
2105 std::vector< XclExpRow * > maRows;
2106 public:
2107 RowFinalizeTask( const ScfUInt16Vec& rColXFIndexes,
2108 bool bProgress ) :
2109 mbProgress( bProgress ),
2110 mrColXFIndexes( rColXFIndexes ) {}
2111 virtual ~RowFinalizeTask() {}
2112 void push_back( XclExpRow *pRow ) { maRows.push_back( pRow ); }
2113 virtual void doWork() SAL_OVERRIDE
2115 for (size_t i = 0; i < maRows.size(); i++ )
2116 maRows[ i ]->Finalize( mrColXFIndexes, mbProgress );
2120 void XclExpRowBuffer::Finalize( XclExpDefaultRowData& rDefRowData, const ScfUInt16Vec& rColXFIndexes )
2122 // *** Finalize all rows *** ----------------------------------------------
2124 GetProgressBar().ActivateFinalRowsSegment();
2126 #if 1
2127 // This is staggeringly slow, and each element operates only
2128 // on its own data.
2129 const size_t nRows = maRowMap.size();
2130 const size_t nThreads = nRows < 128 ? 1 : std::max(std::thread::hardware_concurrency(), 1U);
2131 #else
2132 const size_t nThreads = 1; // globally disable multi-threading for now.
2133 #endif
2134 if (nThreads == 1)
2136 RowMap::iterator itr, itrBeg = maRowMap.begin(), itrEnd = maRowMap.end();
2137 for (itr = itrBeg; itr != itrEnd; ++itr)
2138 itr->second->Finalize( rColXFIndexes, true );
2140 else
2142 comphelper::ThreadPool &rPool = comphelper::ThreadPool::getSharedOptimalPool();
2143 std::vector<RowFinalizeTask*> pTasks(nThreads, NULL);
2144 for ( size_t i = 0; i < nThreads; i++ )
2145 pTasks[ i ] = new RowFinalizeTask( rColXFIndexes, i == 0 );
2147 RowMap::iterator itr, itrBeg = maRowMap.begin(), itrEnd = maRowMap.end();
2148 size_t nIdx = 0;
2149 for ( itr = itrBeg; itr != itrEnd; ++itr, ++nIdx )
2150 pTasks[ nIdx % nThreads ]->push_back( itr->second.get() );
2152 for ( size_t i = 1; i < nThreads; i++ )
2153 rPool.pushTask( pTasks[ i ] );
2155 // Progress bar updates must be synchronous to avoid deadlock
2156 pTasks[0]->doWork();
2158 rPool.waitUntilEmpty();
2161 // *** Default row format *** ---------------------------------------------
2163 typedef ::std::map< XclExpDefaultRowData, size_t > XclExpDefRowDataMap;
2164 XclExpDefRowDataMap aDefRowMap;
2166 XclExpDefaultRowData aMaxDefData;
2167 size_t nMaxDefCount = 0;
2168 // only look for default format in existing rows, if there are more than unused
2169 XclExpRow* pPrev = NULL;
2170 typedef std::vector< XclExpRow* > XclRepeatedRows;
2171 XclRepeatedRows aRepeated;
2172 RowMap::iterator itr, itrBeg = maRowMap.begin(), itrEnd = maRowMap.end();
2173 for (itr = itrBeg; itr != itrEnd; ++itr)
2175 const RowRef& rRow = itr->second;
2176 if (rRow->IsDefaultable())
2178 XclExpDefaultRowData aDefData( *rRow );
2179 size_t& rnDefCount = aDefRowMap[ aDefData ];
2180 ++rnDefCount;
2181 if( rnDefCount > nMaxDefCount )
2183 nMaxDefCount = rnDefCount;
2184 aMaxDefData = aDefData;
2187 if ( pPrev )
2189 if ( pPrev->IsDefaultable())
2191 // if the previous row we processed is not
2192 // defaultable then afaict the rows inbetween are
2193 // not used ( and not repeatable )
2194 sal_uInt32 nRpt = rRow->GetXclRow() - pPrev->GetXclRow();
2195 if ( nRpt > 1 )
2196 aRepeated.push_back( pPrev );
2197 pPrev->SetXclRowRpt( nRpt );
2198 XclExpDefaultRowData aDefData( *pPrev );
2199 size_t& rnDefCount = aDefRowMap[ aDefData ];
2200 rnDefCount += ( pPrev->GetXclRowRpt() - 1 );
2201 if( rnDefCount > nMaxDefCount )
2203 nMaxDefCount = rnDefCount;
2204 aMaxDefData = aDefData;
2208 pPrev = rRow.get();
2210 // return the default row format to caller
2211 rDefRowData = aMaxDefData;
2213 // now disable repeating extra (empty) rows that are equal to
2214 // default row height
2215 for ( XclRepeatedRows::iterator it = aRepeated.begin(), it_end = aRepeated.end(); it != it_end; ++it)
2217 if ( (*it)->GetXclRowRpt() > 1 && (*it)->GetHeight() == rDefRowData.mnHeight )
2218 (*it)->SetXclRowRpt( 1 );
2221 // *** Disable unused ROW records, find used area *** ---------------------
2223 sal_uInt16 nFirstUsedXclCol = SAL_MAX_UINT16;
2224 sal_uInt16 nFirstFreeXclCol = 0;
2225 sal_uInt32 nFirstUsedXclRow = SAL_MAX_UINT32;
2226 sal_uInt32 nFirstFreeXclRow = 0;
2228 for (itr = itrBeg; itr != itrEnd; ++itr)
2230 const RowRef& rRow = itr->second;
2231 // disable unused rows
2232 rRow->DisableIfDefault( aMaxDefData );
2234 // find used column range
2235 if( !rRow->IsEmpty() ) // empty rows return (0...0) as used range
2237 nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, rRow->GetFirstUsedXclCol() );
2238 nFirstFreeXclCol = ::std::max( nFirstFreeXclCol, rRow->GetFirstFreeXclCol() );
2241 // find used row range
2242 if( rRow->IsEnabled() )
2244 sal_uInt16 nXclRow = rRow->GetXclRow();
2245 nFirstUsedXclRow = ::std::min< sal_uInt32 >( nFirstUsedXclRow, nXclRow );
2246 nFirstFreeXclRow = ::std::max< sal_uInt32 >( nFirstFreeXclRow, nXclRow + 1 );
2250 // adjust start position, if there are no or only empty/disabled ROW records
2251 nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, nFirstFreeXclCol );
2252 nFirstUsedXclRow = ::std::min( nFirstUsedXclRow, nFirstFreeXclRow );
2254 // initialize the DIMENSIONS record
2255 maDimensions.SetDimensions(
2256 nFirstUsedXclCol, nFirstUsedXclRow, nFirstFreeXclCol, nFirstFreeXclRow );
2259 void XclExpRowBuffer::Save( XclExpStream& rStrm )
2261 // DIMENSIONS record
2262 maDimensions.Save( rStrm );
2264 // save in blocks of 32 rows, each block contains first all ROWs, then all cells
2265 size_t nSize = maRowMap.size();
2266 RowMap::iterator itr, itrBeg = maRowMap.begin(), itrEnd = maRowMap.end();
2267 RowMap::iterator itrBlkStart = maRowMap.begin(), itrBlkEnd = maRowMap.begin();
2268 sal_uInt16 nStartXclRow = (nSize == 0) ? 0 : itrBeg->second->GetXclRow();
2270 for (itr = itrBeg; itr != itrEnd; ++itr)
2272 // find end of row block
2273 while( (itrBlkEnd != itrEnd) && (itrBlkEnd->second->GetXclRow() - nStartXclRow < EXC_ROW_ROWBLOCKSIZE) )
2274 ++itrBlkEnd;
2276 // write the ROW records
2277 RowMap::iterator itRow;
2278 for( itRow = itrBlkStart; itRow != itrBlkEnd; ++itRow )
2279 itRow->second->Save( rStrm );
2281 // write the cell records
2282 for( itRow = itrBlkStart; itRow != itrBlkEnd; ++itRow )
2283 itRow->second->WriteCellList( rStrm );
2285 itrBlkStart = (itrBlkEnd == itrEnd) ? itrBlkEnd : itrBlkEnd++;
2286 nStartXclRow += EXC_ROW_ROWBLOCKSIZE;
2290 void XclExpRowBuffer::SaveXml( XclExpXmlStream& rStrm )
2292 sal_Int32 nNonEmpty = 0;
2293 RowMap::iterator itr = maRowMap.begin(), itrEnd = maRowMap.end();
2294 for (; itr != itrEnd; ++itr)
2295 if (itr->second->IsEnabled())
2296 ++nNonEmpty;
2298 if (nNonEmpty == 0)
2300 rStrm.GetCurrentStream()->singleElement( XML_sheetData, FSEND );
2301 return;
2304 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2305 rWorksheet->startElement( XML_sheetData, FSEND );
2306 for (itr = maRowMap.begin(); itr != itrEnd; ++itr)
2307 itr->second->SaveXml(rStrm);
2308 rWorksheet->endElement( XML_sheetData );
2311 XclExpRow& XclExpRowBuffer::GetOrCreateRow( sal_uInt32 nXclRow, bool bRowAlwaysEmpty )
2313 RowMap::iterator itr = maRowMap.begin();
2314 ScDocument& rDoc = GetRoot().GetDoc();
2315 SCTAB nScTab = GetRoot().GetCurrScTab();
2316 for ( size_t nFrom = maRowMap.size(); nFrom <= nXclRow; ++nFrom )
2318 itr = maRowMap.find(nFrom);
2319 if ( itr == maRowMap.end() )
2321 // only create RowMap entries for rows that differ from previous,
2322 // or if it is the desired row
2323 if ( !nFrom || ( nFrom == nXclRow ) || ( nFrom && ( rDoc.GetRowHeight(nFrom, nScTab, false) != rDoc.GetRowHeight(nFrom-1, nScTab, false) ) ) )
2325 RowRef p(new XclExpRow(GetRoot(), nFrom, maOutlineBfr, bRowAlwaysEmpty));
2326 maRowMap.insert(RowMap::value_type(nFrom, p));
2330 itr = maRowMap.find(nXclRow);
2331 return *itr->second;
2335 // Cell Table
2337 XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) :
2338 XclExpRoot( rRoot ),
2339 maColInfoBfr( rRoot ),
2340 maRowBfr( rRoot ),
2341 maArrayBfr( rRoot ),
2342 maShrfmlaBfr( rRoot ),
2343 maTableopBfr( rRoot ),
2344 mxDefrowheight( new XclExpDefrowheight ),
2345 mxGuts( new XclExpGuts( rRoot ) ),
2346 mxNoteList( new XclExpNoteList ),
2347 mxMergedcells( new XclExpMergedcells( rRoot ) ),
2348 mxHyperlinkList( new XclExpHyperlinkList ),
2349 mxDval( new XclExpDval( rRoot ) ),
2350 mxExtLst( new XclExtLst( rRoot ) )
2352 ScDocument& rDoc = GetDoc();
2353 SCTAB nScTab = GetCurrScTab();
2354 SvNumberFormatter& rFormatter = GetFormatter();
2356 // maximum sheet limits
2357 SCCOL nMaxScCol = GetMaxPos().Col();
2358 SCROW nMaxScRow = GetMaxPos().Row();
2360 // find used area (non-empty cells)
2361 SCCOL nLastUsedScCol;
2362 SCROW nLastUsedScRow;
2363 rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
2365 if(nLastUsedScCol > nMaxScCol)
2366 nLastUsedScCol = nMaxScCol;
2368 if(nLastUsedScRow > nMaxScRow)
2369 nLastUsedScRow = nMaxScRow;
2371 ScRange aUsedRange( 0, 0, nScTab, nLastUsedScCol, nLastUsedScRow, nScTab );
2372 GetAddressConverter().ValidateRange( aUsedRange, true );
2373 nLastUsedScCol = aUsedRange.aEnd.Col();
2374 nLastUsedScRow = aUsedRange.aEnd.Row();
2376 // first row without any set attributes (height/hidden/...)
2377 SCROW nFirstUnflaggedScRow = rDoc.GetLastFlaggedRow( nScTab ) + 1;
2379 // find range of outlines
2380 SCROW nFirstUngroupedScRow = 0;
2381 if( const ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable( nScTab ) )
2383 SCCOLROW nScStartPos, nScEndPos;
2384 const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
2385 rRowArray.GetRange( nScStartPos, nScEndPos );
2386 // +1 because open/close button is in next row in Excel, +1 for "end->first unused"
2387 nFirstUngroupedScRow = static_cast< SCROW >( nScEndPos + 2 );
2390 // column settings
2391 /* #i30411# Files saved with SO7/OOo1.x with nonstandard default column
2392 formatting cause big Excel files, because all rows from row 1 to row
2393 32000 are exported. Now, if the used area goes exactly to row 32000,
2394 use this row as default and ignore all rows >32000.
2395 #i59220# Tolerance of +-128 rows for inserted/removed rows. */
2396 if( (31871 <= nLastUsedScRow) && (nLastUsedScRow <= 32127) && (nFirstUnflaggedScRow < nLastUsedScRow) && (nFirstUngroupedScRow <= nLastUsedScRow) )
2397 nMaxScRow = nLastUsedScRow;
2398 maColInfoBfr.Initialize( nMaxScRow );
2400 // range for cell iterator
2401 SCCOL nLastIterScCol = nMaxScCol;
2402 SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow, nMaxScRow );
2403 ScUsedAreaIterator aIt( &rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow );
2405 // activate the correct segment and sub segment at the progress bar
2406 GetProgressBar().ActivateCreateRowsSegment();
2408 for( bool bIt = aIt.GetNext(); bIt; bIt = aIt.GetNext() )
2410 SCCOL nScCol = aIt.GetStartCol();
2411 SCROW nScRow = aIt.GetRow();
2412 SCCOL nLastScCol = aIt.GetEndCol();
2413 ScAddress aScPos( nScCol, nScRow, nScTab );
2415 XclAddress aXclPos( static_cast< sal_uInt16 >( nScCol ), static_cast< sal_uInt32 >( nScRow ) );
2416 sal_uInt16 nLastXclCol = static_cast< sal_uInt16 >( nLastScCol );
2418 const ScRefCellValue& rScCell = aIt.GetCell();
2419 XclExpCellRef xCell;
2421 const ScPatternAttr* pPattern = aIt.GetPattern();
2423 // handle overlapped merged cells before creating the cell record
2424 sal_uInt32 nMergeBaseXFId = EXC_XFID_NOTFOUND;
2425 bool bIsMergedBase = false;
2426 if( pPattern )
2428 const SfxItemSet& rItemSet = pPattern->GetItemSet();
2429 // base cell in a merged range
2430 const ScMergeAttr& rMergeItem = GETITEM( rItemSet, ScMergeAttr, ATTR_MERGE );
2431 bIsMergedBase = rMergeItem.IsMerged();
2432 /* overlapped cell in a merged range; in Excel all merged cells
2433 must contain same XF index, for correct border */
2434 const ScMergeFlagAttr& rMergeFlagItem = GETITEM( rItemSet, ScMergeFlagAttr, ATTR_MERGE_FLAG );
2435 if( rMergeFlagItem.IsOverlapped() )
2436 nMergeBaseXFId = mxMergedcells->GetBaseXFId( aScPos );
2439 OUString aAddNoteText; // additional text to be appended to a note
2441 switch (rScCell.meType)
2443 case CELLTYPE_VALUE:
2445 double fValue = rScCell.mfValue;
2447 // try to create a Boolean cell
2448 if( pPattern && ((fValue == 0.0) || (fValue == 1.0)) )
2450 sal_uLong nScNumFmt = GETITEMVALUE( pPattern->GetItemSet(), SfxUInt32Item, ATTR_VALUE_FORMAT, sal_uLong );
2451 if( rFormatter.GetType( nScNumFmt ) == css::util::NumberFormat::LOGICAL )
2452 xCell.reset( new XclExpBooleanCell(
2453 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue != 0.0 ) );
2456 // try to create an RK value (compressed floating-point number)
2457 sal_Int32 nRkValue;
2458 if( !xCell && XclTools::GetRKFromDouble( nRkValue, fValue ) )
2459 xCell.reset( new XclExpRkCell(
2460 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, nRkValue ) );
2462 // else: simple floating-point number cell
2463 if( !xCell )
2464 xCell.reset( new XclExpNumberCell(
2465 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue ) );
2467 break;
2469 case CELLTYPE_STRING:
2471 xCell.reset(new XclExpLabelCell(
2472 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.mpString->getString()));
2474 break;
2476 case CELLTYPE_EDIT:
2478 XclExpHyperlinkHelper aLinkHelper( GetRoot(), aScPos );
2479 xCell.reset(new XclExpLabelCell(
2480 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.mpEditText, aLinkHelper));
2482 // add a single created HLINK record to the record list
2483 if( aLinkHelper.HasLinkRecord() )
2484 mxHyperlinkList->AppendRecord( aLinkHelper.GetLinkRecord() );
2485 // add list of multiple URLs to the additional cell note text
2486 if( aLinkHelper.HasMultipleUrls() )
2487 aAddNoteText = ScGlobal::addToken( aAddNoteText, aLinkHelper.GetUrlList(), '\n', 2 );
2489 break;
2491 case CELLTYPE_FORMULA:
2493 xCell.reset(new XclExpFormulaCell(
2494 GetRoot(), aXclPos, pPattern, nMergeBaseXFId,
2495 *rScCell.mpFormula, maArrayBfr, maShrfmlaBfr, maTableopBfr));
2497 break;
2499 default:
2500 OSL_FAIL( "XclExpCellTable::XclExpCellTable - unknown cell type" );
2501 // run-through!
2502 case CELLTYPE_NONE:
2504 xCell.reset( new XclExpBlankCell(
2505 GetRoot(), aXclPos, nLastXclCol, pPattern, nMergeBaseXFId ) );
2507 break;
2510 // insert the cell into the current row
2511 if( xCell )
2512 maRowBfr.AppendCell( xCell, bIsMergedBase );
2514 if ( !aAddNoteText.isEmpty() )
2515 mxNoteList->AppendNewRecord( new XclExpNote( GetRoot(), aScPos, NULL, aAddNoteText ) );
2517 // other sheet contents
2518 if( pPattern )
2520 const SfxItemSet& rItemSet = pPattern->GetItemSet();
2522 // base cell in a merged range
2523 if( bIsMergedBase )
2525 const ScMergeAttr& rMergeItem = GETITEM( rItemSet, ScMergeAttr, ATTR_MERGE );
2526 ScRange aScRange( aScPos );
2527 aScRange.aEnd.IncCol( rMergeItem.GetColMerge() - 1 );
2528 aScRange.aEnd.IncRow( rMergeItem.GetRowMerge() - 1 );
2529 sal_uInt32 nXFId = xCell ? xCell->GetFirstXFId() : EXC_XFID_NOTFOUND;
2530 // blank cells merged vertically may occur repeatedly
2531 OSL_ENSURE( (aScRange.aStart.Col() == aScRange.aEnd.Col()) || (nScCol == nLastScCol),
2532 "XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" );
2533 for( SCCOL nIndex = nScCol; nIndex <= nLastScCol; ++nIndex )
2535 mxMergedcells->AppendRange( aScRange, nXFId );
2536 aScRange.aStart.IncCol();
2537 aScRange.aEnd.IncCol();
2541 // data validation
2542 if( ScfTools::CheckItem( rItemSet, ATTR_VALIDDATA, false ) )
2544 sal_uLong nScHandle = GETITEMVALUE( rItemSet, SfxUInt32Item, ATTR_VALIDDATA, sal_uLong );
2545 ScRange aScRange( aScPos );
2546 aScRange.aEnd.SetCol( nLastScCol );
2547 mxDval->InsertCellRange( aScRange, nScHandle );
2552 // create missing row settings for rows anyhow flagged or with outlines
2553 maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, nFirstUngroupedScRow ) );
2556 void XclExpCellTable::Finalize()
2558 // Finalize multiple operations.
2559 maTableopBfr.Finalize();
2561 /* Finalize column buffer. This calculates column default XF indexes from
2562 the XF identifiers and fills a vector with these XF indexes. */
2563 ScfUInt16Vec aColXFIndexes;
2564 maColInfoBfr.Finalize( aColXFIndexes );
2566 /* Finalize row buffer. This calculates all cell XF indexes from the XF
2567 identifiers. Then the XF index vector aColXFIndexes (filled above) is
2568 used to calculate the row default formats. With this, all unneeded blank
2569 cell records (equal to row default or column default) will be removed.
2570 The function returns the (most used) default row format in aDefRowData. */
2571 XclExpDefaultRowData aDefRowData;
2572 maRowBfr.Finalize( aDefRowData, aColXFIndexes );
2574 // Initialize the DEFROWHEIGHT record.
2575 mxDefrowheight->SetDefaultData( aDefRowData );
2578 XclExpRecordRef XclExpCellTable::CreateRecord( sal_uInt16 nRecId ) const
2580 XclExpRecordRef xRec;
2581 switch( nRecId )
2583 case EXC_ID3_DIMENSIONS: xRec.reset( new XclExpDelegatingRecord( &const_cast<XclExpRowBuffer*>(&maRowBfr)->GetDimensions() ) ); break;
2584 case EXC_ID2_DEFROWHEIGHT: xRec = mxDefrowheight; break;
2585 case EXC_ID_GUTS: xRec = mxGuts; break;
2586 case EXC_ID_NOTE: xRec = mxNoteList; break;
2587 case EXC_ID_MERGEDCELLS: xRec = mxMergedcells; break;
2588 case EXC_ID_HLINK: xRec = mxHyperlinkList; break;
2589 case EXC_ID_DVAL: xRec = mxDval; break;
2590 case EXC_ID_EXTLST: xRec = mxExtLst; break;
2591 default: OSL_FAIL( "XclExpCellTable::CreateRecord - unknown record id" );
2593 return xRec;
2596 void XclExpCellTable::Save( XclExpStream& rStrm )
2598 // DEFCOLWIDTH and COLINFOs
2599 maColInfoBfr.Save( rStrm );
2600 // ROWs and cell records
2601 maRowBfr.Save( rStrm );
2604 void XclExpCellTable::SaveXml( XclExpXmlStream& rStrm )
2606 // DEFAULT row height
2607 XclExpDefaultRowData& rDefData = mxDefrowheight->GetDefaultData();
2608 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2609 rWorksheet->startElement( XML_sheetFormatPr,
2610 XML_defaultRowHeight, OString::number( (double) rDefData.mnHeight / 20.0 ).getStr(), FSEND );
2611 rWorksheet->endElement( XML_sheetFormatPr );
2613 maColInfoBfr.SaveXml( rStrm );
2614 maRowBfr.SaveXml( rStrm );
2615 mxExtLst->SaveXml( rStrm );
2618 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */