blob: 0a74e84f4a031749743735040c5ea6d8990fe6b1 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"
#include "xetable.hxx"
#include <map>
#include <com/sun/star/i18n/ScriptType.hpp>
#include "scitems.hxx"
#include <svl/intitem.hxx>
#include "document.hxx"
#include "dociter.hxx"
#include "olinetab.hxx"
#include "cell.hxx"
#include "patattr.hxx"
#include "attrib.hxx"
#include "xehelper.hxx"
#include "xecontent.hxx"
#include "xeescher.hxx"
using namespace ::oox;
using ::rtl::OString;
using ::rtl::OUString;
using ::rtl::OUStringBuffer;
namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
// ============================================================================
// Helper records for cell records
// ============================================================================
XclExpStringRec::XclExpStringRec( const XclExpRoot& rRoot, const String& rResult ) :
XclExpRecord( EXC_ID3_STRING ),
mxResult( XclExpStringHelper::CreateString( rRoot, rResult ) )
{
DBG_ASSERT( (rRoot.GetBiff() <= EXC_BIFF5) || (mxResult->Len() > 0),
"XclExpStringRec::XclExpStringRec - empty result not allowed in BIFF8+" );
SetRecSize( mxResult->GetSize() );
}
void XclExpStringRec::WriteBody( XclExpStream& rStrm )
{
rStrm << *mxResult;
}
// Additional records for special formula ranges ==============================
XclExpRangeFmlaBase::XclExpRangeFmlaBase(
sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScAddress& rScPos ) :
XclExpRecord( nRecId, nRecSize ),
maXclRange( ScAddress::UNINITIALIZED ),
maBaseXclPos( ScAddress::UNINITIALIZED )
{
maBaseXclPos.Set( static_cast< sal_uInt16 >( rScPos.Col() ), static_cast< sal_uInt16 >( rScPos.Row() ) );
maXclRange.maFirst = maXclRange.maLast = maBaseXclPos;
}
XclExpRangeFmlaBase::XclExpRangeFmlaBase(
sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScRange& rScRange ) :
XclExpRecord( nRecId, nRecSize ),
maXclRange( ScAddress::UNINITIALIZED ),
maBaseXclPos( ScAddress::UNINITIALIZED )
{
maXclRange.Set(
static_cast< sal_uInt16 >( rScRange.aStart.Col() ),
static_cast< sal_uInt16 >( rScRange.aStart.Row() ),
static_cast< sal_uInt16 >( rScRange.aEnd.Col() ),
static_cast< sal_uInt16 >( rScRange.aEnd.Row() ) );
maBaseXclPos = maXclRange.maFirst;
}
bool XclExpRangeFmlaBase::IsBasePos( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const
{
return (maBaseXclPos.mnCol == nXclCol) && (maBaseXclPos.mnRow == nXclRow);
}
void XclExpRangeFmlaBase::Extend( const ScAddress& rScPos )
{
sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() );
maXclRange.maFirst.mnCol = ::std::min( maXclRange.maFirst.mnCol, nXclCol );
maXclRange.maFirst.mnRow = ::std::min( maXclRange.maFirst.mnRow, nXclRow );
maXclRange.maLast.mnCol = ::std::max( maXclRange.maLast.mnCol, nXclCol );
maXclRange.maLast.mnRow = ::std::max( maXclRange.maLast.mnRow, nXclRow );
}
void XclExpRangeFmlaBase::WriteRangeAddress( XclExpStream& rStrm ) const
{
maXclRange.Write( rStrm, false );
}
// Array formulas =============================================================
XclExpArray::XclExpArray( XclTokenArrayRef xTokArr, const ScRange& rScRange ) :
XclExpRangeFmlaBase( EXC_ID3_ARRAY, 14 + xTokArr->GetSize(), rScRange ),
mxTokArr( xTokArr )
{
}
XclTokenArrayRef XclExpArray::CreateCellTokenArray( const XclExpRoot& rRoot ) const
{
return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
}
bool XclExpArray::IsVolatile() const
{
return mxTokArr->IsVolatile();
}
void XclExpArray::WriteBody( XclExpStream& rStrm )
{
WriteRangeAddress( rStrm );
sal_uInt16 nFlags = EXC_ARRAY_DEFAULTFLAGS;
::set_flag( nFlags, EXC_ARRAY_RECALC_ALWAYS, IsVolatile() );
rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
}
// ----------------------------------------------------------------------------
XclExpArrayBuffer::XclExpArrayBuffer( const XclExpRoot& rRoot ) :
XclExpRoot( rRoot )
{
}
XclExpArrayRef XclExpArrayBuffer::CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange )
{
const ScAddress& rScPos = rScRange.aStart;
XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_MATRIX, rScTokArr, &rScPos );
DBG_ASSERT( maRecMap.find( rScPos ) == maRecMap.end(), "XclExpArrayBuffer::CreateArray - array exists already" );
XclExpArrayRef& rxRec = maRecMap[ rScPos ];
rxRec.reset( new XclExpArray( xTokArr, rScRange ) );
return rxRec;
}
XclExpArrayRef XclExpArrayBuffer::FindArray( const ScTokenArray& rScTokArr ) const
{
XclExpArrayRef xRec;
// try to extract a matrix reference token
if( rScTokArr.GetLen() == 1 )
{
const formula::FormulaToken* pToken = rScTokArr.GetArray()[ 0 ];
if( pToken && (pToken->GetOpCode() == ocMatRef) )
{
const ScSingleRefData& rRef = static_cast<const ScToken*>(pToken)->GetSingleRef();
ScAddress aBasePos( rRef.nCol, rRef.nRow, GetCurrScTab() );
XclExpArrayMap::const_iterator aIt = maRecMap.find( aBasePos );
if( aIt != maRecMap.end() )
xRec = aIt->second;
}
}
return xRec;
}
// Shared formulas ============================================================
XclExpShrfmla::XclExpShrfmla( XclTokenArrayRef xTokArr, const ScAddress& rScPos ) :
XclExpRangeFmlaBase( EXC_ID_SHRFMLA, 10 + xTokArr->GetSize(), rScPos ),
mxTokArr( xTokArr ),
mnUsedCount( 1 )
{
}
void XclExpShrfmla::ExtendRange( const ScAddress& rScPos )
{
Extend( rScPos );
++mnUsedCount;
}
XclTokenArrayRef XclExpShrfmla::CreateCellTokenArray( const XclExpRoot& rRoot ) const
{
return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
}
bool XclExpShrfmla::IsVolatile() const
{
return mxTokArr->IsVolatile();
}
void XclExpShrfmla::WriteBody( XclExpStream& rStrm )
{
WriteRangeAddress( rStrm );
rStrm << sal_uInt8( 0 ) << mnUsedCount << *mxTokArr;
}
// ----------------------------------------------------------------------------
XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) :
XclExpRoot( rRoot )
{
}
XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
const ScTokenArray& rScTokArr, const ScAddress& rScPos )
{
XclExpShrfmlaRef xRec;
if( const ScTokenArray* pShrdScTokArr = XclTokenArrayHelper::GetSharedFormula( GetRoot(), rScTokArr ) )
{
XclExpShrfmlaMap::iterator aIt = maRecMap.find( pShrdScTokArr );
if( aIt == maRecMap.end() )
{
// create a new record
XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_SHARED, *pShrdScTokArr, &rScPos );
xRec.reset( new XclExpShrfmla( xTokArr, rScPos ) );
maRecMap[ pShrdScTokArr ] = xRec;
}
else
{
// extend existing record
DBG_ASSERT( aIt->second, "XclExpShrfmlaBuffer::CreateOrExtendShrfmla - missing record" );
xRec = aIt->second;
xRec->ExtendRange( rScPos );
}
}
return xRec;
}
// Multiple operations ========================================================
XclExpTableop::XclExpTableop( const ScAddress& rScPos,
const XclMultipleOpRefs& rRefs, sal_uInt8 nScMode ) :
XclExpRangeFmlaBase( EXC_ID3_TABLEOP, 16, rScPos ),
mnLastAppXclCol( static_cast< sal_uInt16 >( rScPos.Col() ) ),
mnColInpXclCol( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Col() ) ),
mnColInpXclRow( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Row() ) ),
mnRowInpXclCol( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Col() ) ),
mnRowInpXclRow( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Row() ) ),
mnScMode( nScMode ),
mbValid( false )
{
}
bool XclExpTableop::TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
{
sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() );
bool bOk = IsAppendable( nXclCol, nXclRow );
if( bOk )
{
SCCOL nFirstScCol = static_cast< SCCOL >( maXclRange.maFirst.mnCol );
SCROW nFirstScRow = static_cast< SCROW >( maXclRange.maFirst.mnRow );
SCCOL nColInpScCol = static_cast< SCCOL >( mnColInpXclCol );
SCROW nColInpScRow = static_cast< SCROW >( mnColInpXclRow );
SCCOL nRowInpScCol = static_cast< SCCOL >( mnRowInpXclCol );
SCROW nRowInpScRow = static_cast< SCROW >( mnRowInpXclRow );
bOk = ((mnScMode == 2) == rRefs.mbDblRefMode) &&
(rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
(nColInpScCol == rRefs.maColFirstScPos.Col()) &&
(nColInpScRow == rRefs.maColFirstScPos.Row()) &&
(rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
(rScPos.Tab() == rRefs.maColRelScPos.Tab());
if( bOk ) switch( mnScMode )
{
case 0:
bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
(nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
(nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
(rScPos.Row() == rRefs.maColRelScPos.Row());
break;
case 1:
bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
(rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
(rScPos.Col() == rRefs.maColRelScPos.Col()) &&
(nFirstScRow == rRefs.maColRelScPos.Row() + 1);
break;
case 2:
bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
(nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
(nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
(rScPos.Row() == rRefs.maColRelScPos.Row()) &&
(nRowInpScCol == rRefs.maRowFirstScPos.Col()) &&
(nRowInpScRow == rRefs.maRowFirstScPos.Row()) &&
(rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
(rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
(nFirstScRow == rRefs.maRowRelScPos.Row() + 1) &&
(rScPos.Tab() == rRefs.maRowRelScPos.Tab());
break;
default:
bOk = false;
}
if( bOk )
{
// extend the cell range
DBG_ASSERT( IsAppendable( nXclCol, nXclRow ), "XclExpTableop::TryExtend - wrong cell address" );
Extend( rScPos );
mnLastAppXclCol = nXclCol;
}
}
return bOk;
}
void XclExpTableop::Finalize()
{
// is the range complete? (last appended cell is in last column)
mbValid = maXclRange.maLast.mnCol == mnLastAppXclCol;
// if last row is incomplete, try to shorten the used range
if( !mbValid && (maXclRange.maFirst.mnRow < maXclRange.maLast.mnRow) )
{
--maXclRange.maLast.mnRow;
mbValid = true;
}
// check if referred cells are outside of own range
if( mbValid ) switch( mnScMode )
{
case 0:
mbValid = (mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
(mnColInpXclRow < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
break;
case 1:
mbValid = (mnColInpXclCol < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
(mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
break;
case 2:
mbValid = ((mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
(mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow)) &&
((mnRowInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnRowInpXclCol > maXclRange.maLast.mnCol) ||
(mnRowInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnRowInpXclRow > maXclRange.maLast.mnRow));
break;
}
}
XclTokenArrayRef XclExpTableop::CreateCellTokenArray( const XclExpRoot& rRoot ) const
{
XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
return mbValid ?
rFmlaComp.CreateSpecialRefFormula( EXC_TOKID_TBL, maBaseXclPos ) :
rFmlaComp.CreateErrorFormula( EXC_ERR_NA );
}
bool XclExpTableop::IsVolatile() const
{
return true;
}
void XclExpTableop::Save( XclExpStream& rStrm )
{
if( mbValid )
XclExpRangeFmlaBase::Save( rStrm );
}
bool XclExpTableop::IsAppendable( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const
{
return ((nXclCol == mnLastAppXclCol + 1) && (nXclRow == maXclRange.maFirst.mnRow)) ||
((nXclCol == mnLastAppXclCol + 1) && (nXclCol <= maXclRange.maLast.mnCol) && (nXclRow == maXclRange.maLast.mnRow)) ||
((mnLastAppXclCol == maXclRange.maLast.mnCol) && (nXclCol == maXclRange.maFirst.mnCol) && (nXclRow == maXclRange.maLast.mnRow + 1));
}
void XclExpTableop::WriteBody( XclExpStream& rStrm )
{
sal_uInt16 nFlags = EXC_TABLEOP_DEFAULTFLAGS;
::set_flag( nFlags, EXC_TABLEOP_RECALC_ALWAYS, IsVolatile() );
switch( mnScMode )
{
case 1: ::set_flag( nFlags, EXC_TABLEOP_ROW ); break;
case 2: ::set_flag( nFlags, EXC_TABLEOP_BOTH ); break;
}
WriteRangeAddress( rStrm );
rStrm << nFlags;
if( mnScMode == 2 )
rStrm << mnRowInpXclRow << mnRowInpXclCol << mnColInpXclRow << mnColInpXclCol;
else
rStrm << mnColInpXclRow << mnColInpXclCol << sal_uInt32( 0 );
}
// ----------------------------------------------------------------------------
XclExpTableopBuffer::XclExpTableopBuffer( const XclExpRoot& rRoot ) :
XclExpRoot( rRoot )
{
}
XclExpTableopRef XclExpTableopBuffer::CreateOrExtendTableop(
const ScTokenArray& rScTokArr, const ScAddress& rScPos )
{
XclExpTableopRef xRec;
// try to extract cell references of a multiple operations formula
XclMultipleOpRefs aRefs;
if( XclTokenArrayHelper::GetMultipleOpRefs( aRefs, rScTokArr ) )
{
// try to find an existing TABLEOP record for this cell position
for( size_t nPos = 0, nSize = maTableopList.GetSize(); !xRec && (nPos < nSize); ++nPos )
{
XclExpTableopRef xTempRec = maTableopList.GetRecord( nPos );
if( xTempRec->TryExtend( rScPos, aRefs ) )
xRec = xTempRec;
}
// no record found, or found record not extensible
if( !xRec )
xRec = TryCreate( rScPos, aRefs );
}
return xRec;
}
void XclExpTableopBuffer::Finalize()
{
for( size_t nPos = 0, nSize = maTableopList.GetSize(); nPos < nSize; ++nPos )
maTableopList.GetRecord( nPos )->Finalize();
}
XclExpTableopRef XclExpTableopBuffer::TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
{
sal_uInt8 nScMode = 0;
bool bOk = (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
(rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
(rScPos.Tab() == rRefs.maColRelScPos.Tab());
if( bOk )
{
if( rRefs.mbDblRefMode )
{
nScMode = 2;
bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
(rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
(rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
(rScPos.Row() == rRefs.maColRelScPos.Row()) &&
(rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
(rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
(rScPos.Row() == rRefs.maRowRelScPos.Row() + 1) &&
(rScPos.Tab() == rRefs.maRowRelScPos.Tab());
}
else if( (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
(rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
(rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
(rScPos.Row() == rRefs.maColRelScPos.Row()) )
{
nScMode = 0;
}
else if( (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
(rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
(rScPos.Col() == rRefs.maColRelScPos.Col()) &&
(rScPos.Row() == rRefs.maColRelScPos.Row() + 1) )
{
nScMode = 1;
}
else
{
bOk = false;
}
}
XclExpTableopRef xRec;
if( bOk )
{
xRec.reset( new XclExpTableop( rScPos, rRefs, nScMode ) );
maTableopList.AppendRecord( xRec );
}
return xRec;
}
// ============================================================================
// Cell records
// ============================================================================
XclExpCellBase::XclExpCellBase(
sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos ) :
XclExpRecord( nRecId, nContSize + 4 ),
maXclPos( rXclPos )
{
}
bool XclExpCellBase::IsMultiLineText() const
{
return false;
}
bool XclExpCellBase::TryMerge( const XclExpCellBase& /*rCell*/ )
{
return false;
}
void XclExpCellBase::GetBlankXFIndexes( ScfUInt16Vec& /*rXFIndexes*/ ) const
{
// default: do nothing
}
void XclExpCellBase::RemoveUnusedBlankCells( const ScfUInt16Vec& /*rXFIndexes*/ )
{
// default: do nothing
}
// Single cell records ========================================================
XclExpSingleCellBase::XclExpSingleCellBase(
sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos, sal_uInt32 nXFId ) :
XclExpCellBase( nRecId, 2, rXclPos ),
maXFId( nXFId ),
mnContSize( nContSize )
{
}
XclExpSingleCellBase::XclExpSingleCellBase( const XclExpRoot& rRoot,
sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos,
const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForcedXFId ) :
XclExpCellBase( nRecId, 2, rXclPos ),
maXFId( nForcedXFId ),
mnContSize( nContSize )
{
if( GetXFId() == EXC_XFID_NOTFOUND )
SetXFId( rRoot.GetXFBuffer().Insert( pPattern, nScript ) );
}
sal_uInt16 XclExpSingleCellBase::GetLastXclCol() const
{
return GetXclCol();
}
sal_uInt32 XclExpSingleCellBase::GetFirstXFId() const
{
return GetXFId();
}
bool XclExpSingleCellBase::IsEmpty() const
{
return false;
}
void XclExpSingleCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
{
maXFId.ConvertXFIndex( rRoot );
}
void XclExpSingleCellBase::Save( XclExpStream& rStrm )
{
DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
AddRecSize( mnContSize );
XclExpCellBase::Save( rStrm );
}
void XclExpSingleCellBase::WriteBody( XclExpStream& rStrm )
{
rStrm << GetXclRow() << GetXclCol() << maXFId.mnXFIndex;
WriteContents( rStrm );
}
// ----------------------------------------------------------------------------
IMPL_FIXEDMEMPOOL_NEWDEL( XclExpNumberCell, 256, 256 )
XclExpNumberCell::XclExpNumberCell(
const XclExpRoot& rRoot, const XclAddress& rXclPos,
const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, double fValue ) :
// #i41210# always use latin script for number cells - may look wrong for special number formats...
XclExpSingleCellBase( rRoot, EXC_ID3_NUMBER, 8, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
mfValue( fValue )
{
}
static OString lcl_GetStyleId( XclExpXmlStream& rStrm, sal_uInt32 nXFIndex )
{
return OString::valueOf( rStrm.GetRoot().GetXFBuffer()
.GetXmlCellIndex( nXFIndex ) );
}
static OString lcl_GetStyleId( XclExpXmlStream& rStrm, const XclExpCellBase& rCell )
{
sal_uInt32 nXFId = rCell.GetFirstXFId();
sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( nXFId );
return lcl_GetStyleId( rStrm, nXFIndex );
}
void XclExpNumberCell::SaveXml( XclExpXmlStream& rStrm )
{
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
rWorksheet->startElement( XML_c,
XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
XML_t, "n",
// OOXTODO: XML_cm, XML_vm, XML_ph
FSEND );
rWorksheet->startElement( XML_v, FSEND );
rWorksheet->write( mfValue );
rWorksheet->endElement( XML_v );
rWorksheet->endElement( XML_c );
}
void XclExpNumberCell::WriteContents( XclExpStream& rStrm )
{
rStrm << mfValue;
}
// ----------------------------------------------------------------------------
IMPL_FIXEDMEMPOOL_NEWDEL( XclExpBooleanCell, 256, 256 )
XclExpBooleanCell::XclExpBooleanCell(
const XclExpRoot rRoot, const XclAddress& rXclPos,
const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, bool bValue ) :
// #i41210# always use latin script for boolean cells
XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
mbValue( bValue )
{
}
void XclExpBooleanCell::SaveXml( XclExpXmlStream& rStrm )
{
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
rWorksheet->startElement( XML_c,
XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
XML_t, "b",
// OOXTODO: XML_cm, XML_vm, XML_ph
FSEND );
rWorksheet->startElement( XML_v, FSEND );
rWorksheet->write( mbValue ? "1" : "0" );
rWorksheet->endElement( XML_v );
rWorksheet->endElement( XML_c );
}
void XclExpBooleanCell::WriteContents( XclExpStream& rStrm )
{
rStrm << sal_uInt16( mbValue ? 1 : 0 ) << EXC_BOOLERR_BOOL;
}
// ----------------------------------------------------------------------------
//UNUSED2009-05 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpErrorCell, 256, 256 )
//UNUSED2009-05
//UNUSED2009-05 XclExpErrorCell::XclExpErrorCell(
//UNUSED2009-05 const XclExpRoot rRoot, const XclAddress& rXclPos,
//UNUSED2009-05 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_uInt8 nErrCode ) :
//UNUSED2009-05 // #i41210# always use latin script for error cells
//UNUSED2009-05 XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
//UNUSED2009-05 mnErrCode( nErrCode )
//UNUSED2009-05 {
//UNUSED2009-05 }
//UNUSED2009-05
//UNUSED2009-05 void XclExpErrorCell::SaveXml( XclExpXmlStream& rStrm )
//UNUSED2009-05 {
//UNUSED2009-05 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
//UNUSED2009-05 rWorksheet->startElement( XML_c,
//UNUSED2009-05 XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
//UNUSED2009-05 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
//UNUSED2009-05 XML_t, "e",
//UNUSED2009-05 // OOXTODO: XML_cm, XML_vm, XML_ph
//UNUSED2009-05 FSEND );
//UNUSED2009-05 rWorksheet->startElement( XML_v, FSEND );
//UNUSED2009-05 rWorksheet->write( (sal_Int32) mnErrCode );
//UNUSED2009-05 rWorksheet->endElement( XML_v );
//UNUSED2009-05 rWorksheet->endElement( XML_c );
//UNUSED2009-05 }
//UNUSED2009-05
//UNUSED2009-05 void XclExpErrorCell::WriteContents( XclExpStream& rStrm )
//UNUSED2009-05 {
//UNUSED2009-05 rStrm << mnErrCode << EXC_BOOLERR_ERROR;
//UNUSED2009-05 }
// ----------------------------------------------------------------------------
IMPL_FIXEDMEMPOOL_NEWDEL( XclExpLabelCell, 256, 256 )
XclExpLabelCell::XclExpLabelCell(
const XclExpRoot& rRoot, const XclAddress& rXclPos,
const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, const ScStringCell& rCell ) :
XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
{
sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
XclExpStringRef xText = XclExpStringHelper::CreateCellString( rRoot, rCell, pPattern, EXC_STR_DEFAULT, nMaxLen );
Init( rRoot, pPattern, xText );
}
XclExpLabelCell::XclExpLabelCell(
const XclExpRoot& rRoot, const XclAddress& rXclPos,
const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
const ScEditCell& rCell, XclExpHyperlinkHelper& rLinkHelper ) :
XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
{
sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
XclExpStringRef xText = XclExpStringHelper::CreateCellString( rRoot, rCell, pPattern, rLinkHelper, EXC_STR_DEFAULT, nMaxLen );
Init( rRoot, pPattern, xText );
}
bool XclExpLabelCell::IsMultiLineText() const
{
return mbLineBreak || mxText->IsWrapped();
}
void XclExpLabelCell::Init( const XclExpRoot& rRoot,
const ScPatternAttr* pPattern, XclExpStringRef xText )
{
DBG_ASSERT( xText.is() && xText->Len(), "XclExpLabelCell::XclExpLabelCell - empty string passed" );
mxText = xText;
mnSstIndex = 0;
// create the cell format
sal_uInt16 nXclFont = mxText->RemoveLeadingFont();
if( GetXFId() == EXC_XFID_NOTFOUND )
{
DBG_ASSERT( nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found" );
bool bForceLineBreak = mxText->IsWrapped();
SetXFId( rRoot.GetXFBuffer().InsertWithFont( pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak ) );
}
// get auto-wrap attribute from cell format
const XclExpXF* pXF = rRoot.GetXFBuffer().GetXFById( GetXFId() );
mbLineBreak = pXF && pXF->GetAlignmentData().mbLineBreak;
// initialize the record contents
switch( rRoot.GetBiff() )
{
case EXC_BIFF5:
// BIFF5-BIFF7: create a LABEL or RSTRING record
DBG_ASSERT( mxText->Len() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::XclExpLabelCell - string too long" );
SetContSize( mxText->GetSize() );
// formatted string is exported in an RSTRING record
if( mxText->IsRich() )
{
DBG_ASSERT( mxText->GetFormatsCount() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::WriteContents - too many formats" );
mxText->LimitFormatCount( EXC_LABEL_MAXLEN );
SetRecId( EXC_ID_RSTRING );
SetContSize( GetContSize() + 1 + 2 * mxText->GetFormatsCount() );
}
break;
case EXC_BIFF8:
// BIFF8+: create a LABELSST record
mnSstIndex = rRoot.GetSst().Insert( xText );
SetRecId( EXC_ID_LABELSST );
SetContSize( 4 );
break;
default: DBG_ERROR_BIFF();
}
}
void XclExpLabelCell::SaveXml( XclExpXmlStream& rStrm )
{
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
rWorksheet->startElement( XML_c,
XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
XML_t, "s",
// OOXTODO: XML_cm, XML_vm, XML_ph
FSEND );
rWorksheet->startElement( XML_v, FSEND );
rWorksheet->write( (sal_Int32) mnSstIndex );
rWorksheet->endElement( XML_v );
rWorksheet->endElement( XML_c );
}
void XclExpLabelCell::WriteContents( XclExpStream& rStrm )
{
switch( rStrm.GetRoot().GetBiff() )
{
case EXC_BIFF5:
rStrm << *mxText;
if( mxText->IsRich() )
{
rStrm << static_cast< sal_uInt8 >( mxText->GetFormatsCount() );
mxText->WriteFormats( rStrm );
}
break;
case EXC_BIFF8:
rStrm << mnSstIndex;
break;
default: DBG_ERROR_BIFF();
}
}
// ----------------------------------------------------------------------------
IMPL_FIXEDMEMPOOL_NEWDEL( XclExpFormulaCell, 256, 256 )
XclExpFormulaCell::XclExpFormulaCell(
const XclExpRoot& rRoot, const XclAddress& rXclPos,
const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
const ScFormulaCell& rScFmlaCell,
XclExpArrayBuffer& rArrayBfr,
XclExpShrfmlaBuffer& rShrfmlaBfr,
XclExpTableopBuffer& rTableopBfr ) :
XclExpSingleCellBase( EXC_ID2_FORMULA, 0, rXclPos, nForcedXFId ),
mrScFmlaCell( const_cast< ScFormulaCell& >( rScFmlaCell ) )
{
// *** Find result number format overwriting cell number format *** -------
if( GetXFId() == EXC_XFID_NOTFOUND )
{
SvNumberFormatter& rFormatter = rRoot.GetFormatter();
XclExpNumFmtBuffer& rNumFmtBfr = rRoot.GetNumFmtBuffer();
// current cell number format
sal_uLong nScNumFmt = pPattern ?
GETITEMVALUE( pPattern->GetItemSet(), SfxUInt32Item, ATTR_VALUE_FORMAT, sal_uLong ) :
rNumFmtBfr.GetStandardFormat();
// alternative number format passed to XF buffer
sal_uLong nAltScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
/* #73420# Xcl doesn't know Boolean number formats, we write
"TRUE";"FALSE" (language dependent). Don't do it for automatic
formula formats, because Excel gets them right. */
/* #i8640# Don't set text format, if we have string results. */
short nFormatType = mrScFmlaCell.GetFormatType();
if( ((nScNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0) &&
(nFormatType != NUMBERFORMAT_LOGICAL) &&
(nFormatType != NUMBERFORMAT_TEXT) )
nAltScNumFmt = mrScFmlaCell.GetStandardFormat( rFormatter, nScNumFmt );
/* #73420# If cell number format is Boolean and automatic formula
format is Boolean don't write that ugly special format. */
else if( (nFormatType == NUMBERFORMAT_LOGICAL) &&
(rFormatter.GetType( nScNumFmt ) == NUMBERFORMAT_LOGICAL) )
nAltScNumFmt = rNumFmtBfr.GetStandardFormat();
// #i41420# find script type according to result type (always latin for numeric results)
sal_Int16 nScript = ApiScriptType::LATIN;
bool bForceLineBreak = false;
if( nFormatType == NUMBERFORMAT_TEXT )
{
String aResult;
mrScFmlaCell.GetString( aResult );
bForceLineBreak = mrScFmlaCell.IsMultilineResult();
nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult );
}
SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) );
}
// *** Convert the formula token array *** --------------------------------
ScAddress aScPos( static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), rRoot.GetCurrScTab() );
const ScTokenArray& rScTokArr = *mrScFmlaCell.GetCode();
// first try to create multiple operations
mxAddRec = rTableopBfr.CreateOrExtendTableop( rScTokArr, aScPos );
// no multiple operation found - try to create matrix formula
if( !mxAddRec ) switch( static_cast< ScMatrixMode >( mrScFmlaCell.GetMatrixFlag() ) )
{
case MM_FORMULA:
{
// origin of the matrix - find the used matrix range
SCCOL nMatWidth;
SCROW nMatHeight;
mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
DBG_ASSERT( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
ScRange aMatScRange( aScPos );
ScAddress& rMatEnd = aMatScRange.aEnd;
rMatEnd.IncCol( static_cast< SCsCOL >( nMatWidth - 1 ) );
rMatEnd.IncRow( static_cast< SCsROW >( nMatHeight - 1 ) );
// reduce to valid range (range keeps valid, because start position IS valid)
rRoot.GetAddressConverter().ValidateRange( aMatScRange, true );
// create the ARRAY record
mxAddRec = rArrayBfr.CreateArray( rScTokArr, aMatScRange );
}
break;
case MM_REFERENCE:
{
// other formula cell covered by a matrix - find the ARRAY record
mxAddRec = rArrayBfr.FindArray( rScTokArr );
// should always be found, if Calc document is not broken
DBG_ASSERT( mxAddRec.is(), "XclExpFormulaCell::XclExpFormulaCell - no matrix found" );
}
break;
default:;
}
// no matrix found - try to create shared formula
if( !mxAddRec )
mxAddRec = rShrfmlaBfr.CreateOrExtendShrfmla( rScTokArr, aScPos );
// no shared formula found - create a simple cell formula
if( !mxAddRec )
mxTokArr = rRoot.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL, rScTokArr, &aScPos );
}
void XclExpFormulaCell::Save( XclExpStream& rStrm )
{
// create token array for FORMULA cells with additional record
if( mxAddRec.is() )
mxTokArr = mxAddRec->CreateCellTokenArray( rStrm.GetRoot() );
// FORMULA record itself
DBG_ASSERT( mxTokArr.is(), "XclExpFormulaCell::Save - missing token array" );
if( !mxTokArr )
mxTokArr = rStrm.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA );
SetContSize( 16 + mxTokArr->GetSize() );
XclExpSingleCellBase::Save( rStrm );
// additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record
if( mxAddRec.is() && mxAddRec->IsBasePos( GetXclCol(), GetXclRow() ) )
mxAddRec->Save( rStrm );
// STRING record for string result
if( mxStringRec.is() )
mxStringRec->Save( rStrm );
}
static const char* lcl_GetErrorString( sal_uInt16 nScErrCode )
{
sal_uInt8 nXclErrCode = XclTools::GetXclErrorCode( nScErrCode );
switch( nXclErrCode )
{
case EXC_ERR_NULL: return "#NULL!";
case EXC_ERR_DIV0: return "#DIV/0!";
case EXC_ERR_VALUE: return "#VALUE!";
case EXC_ERR_REF: return "#REF!";
case EXC_ERR_NAME: return "#NAME?";
case EXC_ERR_NUM: return "#NUM!";
case EXC_ERR_NA:
default: return "#N/A";
}
}
static void lcl_GetFormulaInfo( ScFormulaCell& rCell, const char** pType, OUString& rValue)
{
switch( rCell.GetFormatType() )
{
case NUMBERFORMAT_NUMBER:
{
// either value or error code
sal_uInt16 nScErrCode = rCell.GetErrCode();
if( nScErrCode )
{
*pType = "e";
rValue = XclXmlUtils::ToOUString( lcl_GetErrorString( nScErrCode ) );
}
else
{
*pType = "n";
rValue = OUString::valueOf( rCell.GetValue() );
}
}
break;
case NUMBERFORMAT_TEXT:
{
*pType = "str";
String aResult;
rCell.GetString( aResult );
rValue = XclXmlUtils::ToOUString( aResult );
}
break;
case NUMBERFORMAT_LOGICAL:
{
*pType = "b";
rValue = XclXmlUtils::ToOUString( rCell.GetValue() == 0.0 ? "0" : "1" );
}
break;
default:
{
*pType = "inlineStr";
String aResult;
rCell.GetString( aResult );
rValue = XclXmlUtils::ToOUString( aResult );
}
break;
}
}
void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
{
const char* sType = NULL;
OUString sValue;
lcl_GetFormulaInfo( mrScFmlaCell, &sType, sValue );
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
rWorksheet->startElement( XML_c,
XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(),
XML_s, lcl_GetStyleId( rStrm, *this ).getStr(),
XML_t, sType,
// OOXTODO: XML_cm, XML_vm, XML_ph
FSEND );
rWorksheet->startElement( XML_f,
// OOXTODO: XML_t, ST_CellFormulaType
XML_aca, XclXmlUtils::ToPsz( mxTokArr->IsVolatile() || (mxAddRec.is() && mxAddRec->IsVolatile()) ),
// OOXTODO: XML_ref, ST_Ref
// OOXTODO: XML_dt2D, bool
// OOXTODO: XML_dtr, bool
// OOXTODO: XML_del1, bool
// OOXTODO: XML_del2, bool
// OOXTODO: XML_r1, ST_CellRef
// OOXTODO: XML_r2, ST_CellRef
// OOXTODO: XML_ca, bool
// OOXTODO: XML_si, uint
// OOXTODO: XML_bx bool
FSEND );
rWorksheet->writeEscaped( XclXmlUtils::ToOUString( *mrScFmlaCell.GetDocument(), mrScFmlaCell.aPos, mrScFmlaCell.GetCode() ) );
rWorksheet->endElement( XML_f );
if( strcmp( sType, "inlineStr" ) == 0 )
{
rWorksheet->startElement( XML_is, FSEND );
rWorksheet->startElement( XML_t, FSEND );
rWorksheet->writeEscaped( sValue );
rWorksheet->endElement( XML_t );
rWorksheet->endElement( XML_is );
}
else
{
rWorksheet->startElement( XML_v, FSEND );
rWorksheet->writeEscaped( sValue );
rWorksheet->endElement( XML_v );
}
rWorksheet->endElement( XML_c );
}
void XclExpFormulaCell::WriteContents( XclExpStream& rStrm )
{
// result of the formula
switch( mrScFmlaCell.GetFormatType() )
{
case NUMBERFORMAT_NUMBER:
{
// either value or error code
sal_uInt16 nScErrCode = mrScFmlaCell.GetErrCode();
if( nScErrCode )
rStrm << EXC_FORMULA_RES_ERROR << sal_uInt8( 0 )
<< XclTools::GetXclErrorCode( nScErrCode )
<< sal_uInt8( 0 ) << sal_uInt16( 0 )
<< sal_uInt16( 0xFFFF );
else
rStrm << mrScFmlaCell.GetValue();
}
break;
case NUMBERFORMAT_TEXT:
{
String aResult;
mrScFmlaCell.GetString( aResult );
if( aResult.Len() || (rStrm.GetRoot().GetBiff() <= EXC_BIFF5) )
{
rStrm << EXC_FORMULA_RES_STRING;
mxStringRec.reset( new XclExpStringRec( rStrm.GetRoot(), aResult ) );
}
else
rStrm << EXC_FORMULA_RES_EMPTY; // BIFF8 only
rStrm << sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
}
break;
case NUMBERFORMAT_LOGICAL:
{
sal_uInt8 nXclValue = (mrScFmlaCell.GetValue() == 0.0) ? 0 : 1;
rStrm << EXC_FORMULA_RES_BOOL << sal_uInt8( 0 )
<< nXclValue << sal_uInt8( 0 ) << sal_uInt16( 0 )
<< sal_uInt16( 0xFFFF );
}
break;
default:
rStrm << mrScFmlaCell.GetValue();
}
// flags and formula token array
sal_uInt16 nFlags = EXC_FORMULA_DEFAULTFLAGS;
::set_flag( nFlags, EXC_FORMULA_RECALC_ALWAYS, mxTokArr->IsVolatile() || (mxAddRec.is() && mxAddRec->IsVolatile()) );
::set_flag( nFlags, EXC_FORMULA_SHARED, mxAddRec.is() && (mxAddRec->GetRecId() == EXC_ID_SHRFMLA) );
rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
}
// Multiple cell records ======================================================
XclExpMultiCellBase::XclExpMultiCellBase(
sal_uInt16 nRecId, sal_uInt16 nMulRecId, sal_Size nContSize, const XclAddress& rXclPos ) :
XclExpCellBase( nRecId, 0, rXclPos ),
mnMulRecId( nMulRecId ),
mnContSize( nContSize )
{
}
sal_uInt16 XclExpMultiCellBase::GetLastXclCol() const
{
return GetXclCol() + GetCellCount() - 1;
}
sal_uInt32 XclExpMultiCellBase::GetFirstXFId() const
{
return maXFIds.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds.front().mnXFId;
}
bool XclExpMultiCellBase::IsEmpty() const
{
return maXFIds.empty();
}
void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
{
for( XclExpMultiXFIdDeq::iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
aIt->ConvertXFIndex( rRoot );
}
void XclExpMultiCellBase::Save( XclExpStream& rStrm )
{
DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
sal_uInt16 nBegXclCol = GetXclCol();
sal_uInt16 nEndXclCol = nBegXclCol;
while( aRangeEnd != aEnd )
{
// find begin of next used XF range
aRangeBeg = aRangeEnd;
nBegXclCol = nEndXclCol;
while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
{
nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
++aRangeBeg;
}
// find end of next used XF range
aRangeEnd = aRangeBeg;
nEndXclCol = nBegXclCol;
while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
{
nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
++aRangeEnd;
}
// export this range as a record
if( aRangeBeg != aRangeEnd )
{
sal_uInt16 nCount = nEndXclCol - nBegXclCol;
bool bIsMulti = nCount > 1;
sal_Size nTotalSize = GetRecSize() + (2 + mnContSize) * nCount;
if( bIsMulti ) nTotalSize += 2;
rStrm.StartRecord( bIsMulti ? mnMulRecId : GetRecId(), nTotalSize );
rStrm << GetXclRow() << nBegXclCol;
sal_uInt16 nRelCol = nBegXclCol - GetXclCol();
for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
{
for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
{
rStrm << aIt->mnXFIndex;
WriteContents( rStrm, nRelCol );
++nRelCol;
}
}
if( bIsMulti )
rStrm << static_cast< sal_uInt16 >( nEndXclCol - 1 );
rStrm.EndRecord();
}
}
}
void XclExpMultiCellBase::SaveXml( XclExpXmlStream& rStrm )
{
XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
sal_uInt16 nBegXclCol = GetXclCol();
sal_uInt16 nEndXclCol = nBegXclCol;
while( aRangeEnd != aEnd )
{
// find begin of next used XF range
aRangeBeg = aRangeEnd;
nBegXclCol = nEndXclCol;
while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
{
nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
++aRangeBeg;
}
// find end of next used XF range
aRangeEnd = aRangeBeg;
nEndXclCol = nBegXclCol;
while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
{
nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
++aRangeEnd;
}
// export this range as a record
if( aRangeBeg != aRangeEnd )
{
sal_uInt16 nRelColIdx = nBegXclCol - GetXclCol();
sal_Int32 nRelCol = 0;
for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
{
for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
{
WriteXmlContents(
rStrm,
XclAddress( static_cast<sal_uInt16>(nBegXclCol + nRelCol), GetXclRow() ),
aIt->mnXFIndex,
nRelColIdx );
++nRelCol;
++nRelColIdx;
}
}
}
}
}
sal_uInt16 XclExpMultiCellBase::GetCellCount() const
{
sal_uInt16 nCount = 0;
for( XclExpMultiXFIdDeq::const_iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
nCount = nCount + aIt->mnCount;
return nCount;
}
void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId& rXFId )
{
if( maXFIds.empty() || (maXFIds.back().mnXFId != rXFId.mnXFId) )
maXFIds.push_back( rXFId );
else
maXFIds.back().mnCount = maXFIds.back().mnCount + rXFId.mnCount;
}
void XclExpMultiCellBase::AppendXFId( const XclExpRoot& rRoot,
const ScPatternAttr* pPattern, sal_uInt16 nScript, sal_uInt32 nForcedXFId, sal_uInt16 nCount )
{
sal_uInt32 nXFId = (nForcedXFId == EXC_XFID_NOTFOUND) ?
rRoot.GetXFBuffer().Insert( pPattern, nScript ) : nForcedXFId;
AppendXFId( XclExpMultiXFId( nXFId, nCount ) );
}
bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase& rCell )
{
if( GetLastXclCol() + 1 == rCell.GetXclCol() )
{
maXFIds.insert( maXFIds.end(), rCell.maXFIds.begin(), rCell.maXFIds.end() );
return true;
}
return false;
}
void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const
{
DBG_ASSERT( GetLastXclCol() < rXFIndexes.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" );
ScfUInt16Vec::iterator aDestIt = rXFIndexes.begin() + GetXclCol();
for( XclExpMultiXFIdDeq::const_iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt )
{
::std::fill( aDestIt, aDestIt + aIt->mnCount, aIt->mnXFIndex );
aDestIt += aIt->mnCount;
}
}
void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes )
{
// save last column before calling maXFIds.clear()
sal_uInt16 nLastXclCol = GetLastXclCol();
DBG_ASSERT( nLastXclCol < rXFIndexes.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" );
// build new XF index vector, containing passed XF indexes
maXFIds.clear();
XclExpMultiXFId aXFId( 0 );
for( ScfUInt16Vec::const_iterator aIt = rXFIndexes.begin() + GetXclCol(), aEnd = rXFIndexes.begin() + nLastXclCol + 1; aIt != aEnd; ++aIt )
{
// AppendXFId() tests XclExpXFIndex::mnXFId, set it too
aXFId.mnXFId = aXFId.mnXFIndex = *aIt;
AppendXFId( aXFId );
}
// remove leading and trailing unused XF indexes
if( !maXFIds.empty() && (maXFIds.front().mnXFIndex == EXC_XF_NOTFOUND) )
{
SetXclCol( GetXclCol() + maXFIds.front().mnCount );
maXFIds.pop_front();
}
if( !maXFIds.empty() && (maXFIds.back().mnXFIndex == EXC_XF_NOTFOUND) )
maXFIds.pop_back();
// The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND.
}
// ----------------------------------------------------------------------------
IMPL_FIXEDMEMPOOL_NEWDEL( XclExpBlankCell, 256, 256 )
XclExpBlankCell::XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId ) :
XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
{
DBG_ASSERT( rXFId.mnCount > 0, "XclExpBlankCell::XclExpBlankCell - invalid count" );
AppendXFId( rXFId );
}
XclExpBlankCell::XclExpBlankCell(
const XclExpRoot& rRoot, const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId ) :
XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
{
DBG_ASSERT( rXclPos.mnCol <= nLastXclCol, "XclExpBlankCell::XclExpBlankCell - invalid column range" );
// #i46627# use default script type instead of ApiScriptType::WEAK
AppendXFId( rRoot, pPattern, rRoot.GetDefApiScript(), nForcedXFId, nLastXclCol - rXclPos.mnCol + 1 );
}
bool XclExpBlankCell::TryMerge( const XclExpCellBase& rCell )
{
const XclExpBlankCell* pBlankCell = dynamic_cast< const XclExpBlankCell* >( &rCell );
return pBlankCell && TryMergeXFIds( *pBlankCell );
}
void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const
{
GetXFIndexes( rXFIndexes );
}
void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes )
{
RemoveUnusedXFIndexes( rXFIndexes );
}
void XclExpBlankCell::WriteContents( XclExpStream& /*rStrm*/, sal_uInt16 /*nRelCol*/ )
{
}
void XclExpBlankCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 /* nRelCol */ )
{
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
rWorksheet->singleElement( XML_c,
XML_r, XclXmlUtils::ToOString( rAddress ).getStr(),
XML_s, lcl_GetStyleId( rStrm, nXFId ).getStr(),
FSEND );
}
// ----------------------------------------------------------------------------
IMPL_FIXEDMEMPOOL_NEWDEL( XclExpRkCell, 256, 256 )
XclExpRkCell::XclExpRkCell(
const XclExpRoot& rRoot, const XclAddress& rXclPos,
const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_Int32 nRkValue ) :
XclExpMultiCellBase( EXC_ID_RK, EXC_ID_MULRK, 4, rXclPos )
{
// #i41210# always use latin script for number cells - may look wrong for special number formats...
AppendXFId( rRoot, pPattern, ApiScriptType::LATIN, nForcedXFId );
maRkValues.push_back( nRkValue );
}
bool XclExpRkCell::TryMerge( const XclExpCellBase& rCell )
{
const XclExpRkCell* pRkCell = dynamic_cast< const XclExpRkCell* >( &rCell );
if( pRkCell && TryMergeXFIds( *pRkCell ) )
{
maRkValues.insert( maRkValues.end(), pRkCell->maRkValues.begin(), pRkCell->maRkValues.end() );
return true;
}
return false;
}
void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol )
{
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
rWorksheet->startElement( XML_c,
XML_r, XclXmlUtils::ToOString( rAddress ).getStr(),
XML_s, lcl_GetStyleId( rStrm, nXFId ).getStr(),
XML_t, "n",
// OOXTODO: XML_cm, XML_vm, XML_ph
FSEND );
rWorksheet->startElement( XML_v, FSEND );
rWorksheet->write( XclTools::GetDoubleFromRK( maRkValues[ nRelCol ] ) );
rWorksheet->endElement( XML_v );
rWorksheet->endElement( XML_c );
}
void XclExpRkCell::WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol )
{
DBG_ASSERT( nRelCol < maRkValues.size(), "XclExpRkCell::WriteContents - overflow error" );
rStrm << maRkValues[ nRelCol ];
}
// ============================================================================
// Rows and Columns
// ============================================================================
XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows ) :
mpScOLArray( 0 ),
maLevelInfos( SC_OL_MAXDEPTH ),
mnCurrLevel( 0 ),
mbCurrCollapse( false )
{
if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
mpScOLArray = bRows ? pOutlineTable->GetRowArray() : pOutlineTable->GetColArray();
if( mpScOLArray )
for( sal_uInt16 nLevel = 0; nLevel < SC_OL_MAXDEPTH; ++nLevel )
if( ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nLevel, 0 ) )
maLevelInfos[ nLevel ].mnScEndPos = pEntry->GetEnd();
}
void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos )
{
if( mpScOLArray )
{
// find open level index for passed position
sal_uInt16 nNewOpenScLevel = 0; // new open level (0-based Calc index)
sal_uInt8 nNewLevel = 0; // new open level (1-based Excel index)
if( mpScOLArray->FindTouchedLevel( nScPos, nScPos, nNewOpenScLevel ) )
nNewLevel = static_cast< sal_uInt8 >( nNewOpenScLevel + 1 );
// else nNewLevel keeps 0 to show that there are no groups
mbCurrCollapse = false;
if( nNewLevel >= mnCurrLevel )
{
// new level(s) opened, or no level closed - update all level infos
for( sal_uInt16 nScLevel = 0; nScLevel <= nNewOpenScLevel; ++nScLevel )
{
/* In each level: check if a new group is started (there may be
neighbored groups without gap - therefore check ALL levels). */
if( maLevelInfos[ nScLevel ].mnScEndPos < nScPos )
{
if( ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nScLevel, nScPos ) )
{
maLevelInfos[ nScLevel ].mnScEndPos = pEntry->GetEnd();
maLevelInfos[ nScLevel ].mbHidden = pEntry->IsHidden();
}
}
}
}
else
{
// level(s) closed - check if any of the closed levels are collapsed
// Calc uses 0-based level indexes
sal_uInt16 nOldOpenScLevel = mnCurrLevel - 1;
for( sal_uInt16 nScLevel = nNewOpenScLevel + 1; !mbCurrCollapse && (nScLevel <= nOldOpenScLevel); ++nScLevel )
mbCurrCollapse = maLevelInfos[ nScLevel ].mbHidden;
}
// cache new opened level
mnCurrLevel = nNewLevel;
}
}
// ----------------------------------------------------------------------------
XclExpGuts::XclExpGuts( const XclExpRoot& rRoot ) :
XclExpRecord( EXC_ID_GUTS, 8 ),
mnColLevels( 0 ),
mnColWidth( 0 ),
mnRowLevels( 0 ),
mnRowWidth( 0 )
{
if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
{
// column outline groups
if( const ScOutlineArray* pColArray = pOutlineTable->GetColArray() )
mnColLevels = ulimit_cast< sal_uInt16 >( pColArray->GetDepth(), EXC_OUTLINE_MAX );
if( mnColLevels )
{
++mnColLevels;
mnColWidth = 12 * mnColLevels + 5;
}
// row outline groups
if( const ScOutlineArray* pRowArray = pOutlineTable->GetRowArray() )
mnRowLevels = ulimit_cast< sal_uInt16 >( pRowArray->GetDepth(), EXC_OUTLINE_MAX );
if( mnRowLevels )
{
++mnRowLevels;
mnRowWidth = 12 * mnRowLevels + 5;
}
}
}
void XclExpGuts::WriteBody( XclExpStream& rStrm )
{
rStrm << mnRowWidth << mnColWidth << mnRowLevels << mnColLevels;
}
// ----------------------------------------------------------------------------
XclExpDimensions::XclExpDimensions( const XclExpRoot& rRoot ) :
mnFirstUsedXclRow( 0 ),
mnFirstFreeXclRow( 0 ),
mnFirstUsedXclCol( 0 ),
mnFirstFreeXclCol( 0 )
{
switch( rRoot.GetBiff() )
{
case EXC_BIFF2: SetRecHeader( EXC_ID2_DIMENSIONS, 8 ); break;
case EXC_BIFF3:
case EXC_BIFF4:
case EXC_BIFF5: SetRecHeader( EXC_ID3_DIMENSIONS, 10 ); break;
case EXC_BIFF8: SetRecHeader( EXC_ID3_DIMENSIONS, 14 ); break;
default: DBG_ERROR_BIFF();
}
}
void XclExpDimensions::SetDimensions(
sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow )
{
mnFirstUsedXclRow = nFirstUsedXclRow;
mnFirstFreeXclRow = nFirstFreeXclRow;
mnFirstUsedXclCol = nFirstUsedXclCol;
mnFirstFreeXclCol = nFirstFreeXclCol;
}
void XclExpDimensions::SaveXml( XclExpXmlStream& rStrm )
{
ScRange aRange;
aRange.aStart.SetRow( (SCROW) mnFirstUsedXclRow );
aRange.aStart.SetCol( (SCCOL) mnFirstUsedXclCol );
if( mnFirstFreeXclRow != mnFirstUsedXclRow && mnFirstFreeXclCol != mnFirstUsedXclCol )
{
aRange.aEnd.SetRow( (SCROW) (mnFirstFreeXclRow-1) );
aRange.aEnd.SetCol( (SCCOL) (mnFirstFreeXclCol-1) );
}
rStrm.GetCurrentStream()->singleElement( XML_dimension,
XML_ref, XclXmlUtils::ToOString( aRange ).getStr(),
FSEND );
}
void XclExpDimensions::WriteBody( XclExpStream& rStrm )
{
XclBiff eBiff = rStrm.GetRoot().GetBiff();
if( eBiff == EXC_BIFF8 )
rStrm << mnFirstUsedXclRow << mnFirstFreeXclRow;
else
rStrm << static_cast< sal_uInt16 >( mnFirstUsedXclRow ) << static_cast< sal_uInt16 >( mnFirstFreeXclRow );
rStrm << mnFirstUsedXclCol << mnFirstFreeXclCol;
if( eBiff >= EXC_BIFF3 )
rStrm << sal_uInt16( 0 );
}
// ============================================================================
namespace {
double lclGetCorrectedColWidth( const XclExpRoot& rRoot, sal_uInt16 nXclColWidth )
{
long nFontHt = rRoot.GetFontBuffer().GetAppFontData().mnHeight;
return nXclColWidth - XclTools::GetXclDefColWidthCorrection( nFontHt );
}
} // namespace
// ----------------------------------------------------------------------------
XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot& rRoot ) :
XclExpUInt16Record( EXC_ID_DEFCOLWIDTH, EXC_DEFCOLWIDTH_DEF ),
XclExpRoot( rRoot )
{
}
bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth ) const
{
double fNewColWidth = lclGetCorrectedColWidth( GetRoot(), nXclColWidth );
// exactly matched, if difference is less than 1/16 of a character to the left or to the right
return Abs( static_cast< long >( GetValue() * 256.0 - fNewColWidth + 0.5 ) ) < 16;
}
void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth )
{
double fNewColWidth = lclGetCorrectedColWidth( GetRoot(), nXclColWidth );
SetValue( limit_cast< sal_uInt16 >( fNewColWidth / 256.0 + 0.5 ) );
}
// ----------------------------------------------------------------------------
XclExpColinfo::XclExpColinfo( const XclExpRoot& rRoot,
SCCOL nScCol, SCROW nLastScRow, XclExpColOutlineBuffer& rOutlineBfr ) :
XclExpRecord( EXC_ID_COLINFO, 12 ),
XclExpRoot( rRoot ),
mnWidth( 0 ),
mnFlags( 0 ),
mnFirstXclCol( static_cast< sal_uInt16 >( nScCol ) ),
mnLastXclCol( static_cast< sal_uInt16 >( nScCol ) )
{
ScDocument& rDoc = GetDoc();
SCTAB nScTab = GetCurrScTab();
// column default format
maXFId.mnXFId = GetXFBuffer().Insert(
rDoc.GetMostUsedPattern( nScCol, 0, nLastScRow, nScTab ), GetDefApiScript() );
// column width
sal_uInt16 nScWidth = rDoc.GetColWidth( nScCol, nScTab );
mnWidth = XclTools::GetXclColumnWidth( nScWidth, GetCharWidth() );
// column flags
::set_flag( mnFlags, EXC_COLINFO_HIDDEN, rDoc.ColHidden(nScCol, nScTab) );
// outline data
rOutlineBfr.Update( nScCol );
::set_flag( mnFlags, EXC_COLINFO_COLLAPSED, rOutlineBfr.IsCollapsed() );
::insert_value( mnFlags, rOutlineBfr.GetLevel(), 8, 3 );
}
sal_uInt16 XclExpColinfo::ConvertXFIndexes()
{
maXFId.ConvertXFIndex( GetRoot() );
return maXFId.mnXFIndex;
}
bool XclExpColinfo::IsDefault( const XclExpDefcolwidth& rDefColWidth ) const
{
return (maXFId.mnXFIndex == EXC_XF_DEFAULTCELL) && (mnFlags == 0) && rDefColWidth.IsDefWidth( mnWidth );
}
bool XclExpColinfo::TryMerge( const XclExpColinfo& rColInfo )
{
if( (maXFId.mnXFIndex == rColInfo.maXFId.mnXFIndex) &&
(mnWidth == rColInfo.mnWidth) &&
(mnFlags == rColInfo.mnFlags) &&
(mnLastXclCol + 1 == rColInfo.mnFirstXclCol) )
{
mnLastXclCol = rColInfo.mnLastXclCol;
return true;
}
return false;
}
void XclExpColinfo::WriteBody( XclExpStream& rStrm )
{
// if last column is equal to last possible column, Excel adds one more
sal_uInt16 nLastXclCol = mnLastXclCol;
if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
++nLastXclCol;
rStrm << mnFirstXclCol
<< nLastXclCol
<< mnWidth
<< maXFId.mnXFIndex
<< mnFlags
<< sal_uInt16( 0 );
}
void XclExpColinfo::SaveXml( XclExpXmlStream& rStrm )
{
// if last column is equal to last possible column, Excel adds one more
sal_uInt16 nLastXclCol = mnLastXclCol;
if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
++nLastXclCol;
rStrm.GetCurrentStream()->singleElement( XML_col,
// OOXTODO: XML_bestFit,
XML_collapsed, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_COLINFO_COLLAPSED ) ),
// OOXTODO: XML_customWidth,
XML_hidden, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_COLINFO_HIDDEN ) ),
XML_max, OString::valueOf( (sal_Int32) (nLastXclCol+1) ).getStr(),
XML_min, OString::valueOf( (sal_Int32) (mnFirstXclCol+1) ).getStr(),
// OOXTODO: XML_outlineLevel,
// OOXTODO: XML_phonetic,
XML_style, lcl_GetStyleId( rStrm, maXFId.mnXFIndex ).getStr(),
XML_width, OString::valueOf( (double) (mnWidth / 255.0) ).getStr(),
FSEND );
}
// ----------------------------------------------------------------------------
XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot& rRoot ) :
XclExpRoot( rRoot ),
maDefcolwidth( rRoot ),
maOutlineBfr( rRoot )
{
}
void XclExpColinfoBuffer::Initialize( SCROW nLastScRow )
{
for( sal_uInt16 nScCol = 0, nLastScCol = GetMaxPos().Col(); nScCol <= nLastScCol; ++nScCol )
maColInfos.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol, nLastScRow, maOutlineBfr ) );
}
void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes )
{
rXFIndexes.clear();
rXFIndexes.reserve( maColInfos.GetSize() );
size_t nPos, nSize;
// do not cache the record list size, it may change in the loop
for( nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
{
XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
xRec->ConvertXFIndexes();
// try to merge with previous record
if( nPos > 0 )
{
XclExpColinfoRef xPrevRec = maColInfos.GetRecord( nPos - 1 );
if( xPrevRec->TryMerge( *xRec ) )
// adjust nPos to get the next COLINFO record at the same position
maColInfos.RemoveRecord( nPos-- );
}
}
// put XF indexes into passed vector, collect use count of all different widths
typedef ::std::map< sal_uInt16, sal_uInt16 > XclExpWidthMap;
XclExpWidthMap aWidthMap;
sal_uInt16 nMaxColCount = 0;
sal_uInt16 nMaxUsedWidth = 0;
for( nPos = 0, nSize = maColInfos.GetSize(); nPos < nSize; ++nPos )
{
XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
sal_uInt16 nColCount = xRec->GetColCount();
// add XF index to passed vector
rXFIndexes.resize( rXFIndexes.size() + nColCount, xRec->GetXFIndex() );
// collect use count of column width
sal_uInt16 nWidth = xRec->GetColWidth();
sal_uInt16& rnMapCount = aWidthMap[ nWidth ];
rnMapCount = rnMapCount + nColCount;
if( rnMapCount > nMaxColCount )
{
nMaxColCount = rnMapCount;
nMaxUsedWidth = nWidth;
}
}
maDefcolwidth.SetDefWidth( nMaxUsedWidth );
// remove all default COLINFO records
nPos = 0;
while( nPos < maColInfos.GetSize() )
{
XclExpColinfoRef xRec = maColInfos.GetRecord( nPos );
if( xRec->IsDefault( maDefcolwidth ) )
maColInfos.RemoveRecord( nPos );
else
++nPos;
}
}
void XclExpColinfoBuffer::Save( XclExpStream& rStrm )
{
// DEFCOLWIDTH
maDefcolwidth.Save( rStrm );
// COLINFO records
maColInfos.Save( rStrm );
}
void XclExpColinfoBuffer::SaveXml( XclExpXmlStream& rStrm )
{
if( maColInfos.IsEmpty() )
return;
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
rWorksheet->startElement( XML_cols,
FSEND );
maColInfos.SaveXml( rStrm );
rWorksheet->endElement( XML_cols );
}
// ============================================================================
XclExpDefaultRowData::XclExpDefaultRowData() :
mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
mnHeight( EXC_DEFROW_DEFAULTHEIGHT )
{
}
XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow& rRow ) :
mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
mnHeight( rRow.GetHeight() )
{
::set_flag( mnFlags, EXC_DEFROW_HIDDEN, rRow.IsHidden() );
::set_flag( mnFlags, EXC_DEFROW_UNSYNCED, rRow.IsUnsynced() );
}
bool operator<( const XclExpDefaultRowData& rLeft, const XclExpDefaultRowData& rRight )
{
return (rLeft.mnHeight < rRight.mnHeight) ||
((rLeft.mnHeight == rRight.mnHeight) && (rLeft.mnFlags < rRight.mnFlags));
}
// ----------------------------------------------------------------------------
XclExpDefrowheight::XclExpDefrowheight() :
XclExpRecord( EXC_ID3_DEFROWHEIGHT, 4 )
{
}
void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData& rDefData )
{
maDefData = rDefData;
}
void XclExpDefrowheight::WriteBody( XclExpStream& rStrm )
{
DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
rStrm << maDefData.mnFlags << maDefData.mnHeight;
}
// ----------------------------------------------------------------------------
XclExpRow::XclExpRow( const XclExpRoot& rRoot, sal_uInt16 nXclRow,
XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty ) :
XclExpRecord( EXC_ID3_ROW, 16 ),
XclExpRoot( rRoot ),
mnXclRow( nXclRow ),
mnHeight( 0 ),
mnFlags( EXC_ROW_DEFAULTFLAGS ),
mnXFIndex( EXC_XF_DEFAULTCELL ),
mnOutlineLevel( 0 ),
mbAlwaysEmpty( bAlwaysEmpty ),
mbEnabled( true )
{
SCTAB nScTab = GetCurrScTab();
SCROW nScRow = static_cast< SCROW >( mnXclRow );
// *** Row flags *** ------------------------------------------------------
sal_uInt8 nRowFlags = GetDoc().GetRowFlags( nScRow, nScTab );
bool bUserHeight = ::get_flag< sal_uInt8 >( nRowFlags, CR_MANUALSIZE );
bool bHidden = GetDoc().RowHidden(nScRow, nScTab);
::set_flag( mnFlags, EXC_ROW_UNSYNCED, bUserHeight );
::set_flag( mnFlags, EXC_ROW_HIDDEN, bHidden );
// *** Row height *** -----------------------------------------------------
if (bUserHeight)
mnHeight = GetDoc().GetRowHeight(nScRow, nScTab, false);
else
mnHeight = EXC_ROW_DEFAULTHEIGHT;
// #76250# not usable in Applix
// ::set_flag( mnHeight, EXC_ROW_FLAGDEFHEIGHT, !bUserHeight );
// *** Outline data *** ---------------------------------------------------
rOutlineBfr.Update( nScRow );
::set_flag( mnFlags, EXC_ROW_COLLAPSED, rOutlineBfr.IsCollapsed() );
::insert_value( mnFlags, rOutlineBfr.GetLevel(), 0, 3 );
mnOutlineLevel = rOutlineBfr.GetLevel();
// *** Progress bar *** ---------------------------------------------------
XclExpProgressBar& rProgress = GetProgressBar();
rProgress.IncRowRecordCount();
rProgress.Progress();
}
void XclExpRow::AppendCell( XclExpCellRef xCell, bool bIsMergedBase )
{
DBG_ASSERT( !mbAlwaysEmpty, "XclExpRow::AppendCell - row is marked to be always empty" );
// try to merge with last existing cell
InsertCell( xCell, maCellList.GetSize(), bIsMergedBase );
}
void XclExpRow::Finalize( const ScfUInt16Vec& rColXFIndexes )
{
size_t nPos, nSize;
// *** Convert XF identifiers *** -----------------------------------------
// additionally collect the blank XF indexes
size_t nColCount = GetMaxPos().Col() + 1;
DBG_ASSERT( rColXFIndexes.size() == nColCount, "XclExpRow::Finalize - wrong column XF index count" );
ScfUInt16Vec aXFIndexes( nColCount, EXC_XF_NOTFOUND );
for( nPos = 0, nSize = maCellList.GetSize(); nPos < nSize; ++nPos )
{
XclExpCellRef xCell = maCellList.GetRecord( nPos );
xCell->ConvertXFIndexes( GetRoot() );
xCell->GetBlankXFIndexes( aXFIndexes );
}
// *** Fill gaps with BLANK/MULBLANK cell records *** ---------------------
/* This is needed because nonexistant cells in Calc are not formatted at all,
but in Excel they would have the column default format. Blank cells that
are equal to the respective column default are removed later in this function. */
if( !mbAlwaysEmpty )
{
// XF identifier representing default cell XF
XclExpMultiXFId aXFId( XclExpXFBuffer::GetDefCellXFId() );
aXFId.ConvertXFIndex( GetRoot() );
nPos = 0;
while( nPos <= maCellList.GetSize() ) // don't cache list size, may change in the loop
{
// get column index that follows previous cell
sal_uInt16 nFirstFreeXclCol = (nPos > 0) ? (maCellList.GetRecord( nPos - 1 )->GetLastXclCol() + 1) : 0;
// get own column index
sal_uInt16 nNextUsedXclCol = (nPos < maCellList.GetSize()) ? maCellList.GetRecord( nPos )->GetXclCol() : (GetMaxPos().Col() + 1);
// is there a gap?
if( nFirstFreeXclCol < nNextUsedXclCol )
{
aXFId.mnCount = nNextUsedXclCol - nFirstFreeXclCol;
XclExpCellRef xNewCell( new XclExpBlankCell( XclAddress( nFirstFreeXclCol, mnXclRow ), aXFId ) );
// insert the cell, InsertCell() may merge it with existing BLANK records
InsertCell( xNewCell, nPos, false );
// insert default XF indexes into aXFIndexes
::std::fill( aXFIndexes.begin() + nFirstFreeXclCol,
aXFIndexes.begin() + nNextUsedXclCol, aXFId.mnXFIndex );
// don't step forward with nPos, InsertCell() may remove records
}
else
++nPos;
}
}
// *** Find default row format *** ----------------------------------------
ScfUInt16Vec::iterator aCellBeg = aXFIndexes.begin(), aCellEnd = aXFIndexes.end(), aCellIt;
ScfUInt16Vec::const_iterator aColBeg = rColXFIndexes.begin(), aColIt;
// find most used XF index in the row
typedef ::std::map< sal_uInt16, size_t > XclExpXFIndexMap;
XclExpXFIndexMap aIndexMap;
sal_uInt16 nRowXFIndex = EXC_XF_DEFAULTCELL;
size_t nMaxXFCount = 0;
for( aCellIt = aCellBeg; aCellIt != aCellEnd; ++aCellIt )
{
if( *aCellIt != EXC_XF_NOTFOUND )
{
size_t& rnCount = aIndexMap[ *aCellIt ];
++rnCount;
if( rnCount > nMaxXFCount )
{
nRowXFIndex = *aCellIt;
nMaxXFCount = rnCount;
}
}
}
// decide whether to use the row default XF index or column default XF indexes
bool bUseColDefXFs = nRowXFIndex == EXC_XF_DEFAULTCELL;
if( !bUseColDefXFs )
{
// count needed XF indexes for blank cells with and without row default XF index
size_t nXFCountWithRowDefXF = 0;
size_t nXFCountWithoutRowDefXF = 0;
for( aCellIt = aCellBeg, aColIt = aColBeg; aCellIt != aCellEnd; ++aCellIt, ++aColIt )
{
sal_uInt16 nXFIndex = *aCellIt;
if( nXFIndex != EXC_XF_NOTFOUND )
{
if( nXFIndex != nRowXFIndex )
++nXFCountWithRowDefXF; // with row default XF index
if( nXFIndex != *aColIt )
++nXFCountWithoutRowDefXF; // without row default XF index
}
}
// use column XF indexes if this would cause less or equal number of BLANK records
bUseColDefXFs = nXFCountWithoutRowDefXF <= nXFCountWithRowDefXF;
}
// *** Remove unused BLANK cell records *** -------------------------------
if( bUseColDefXFs )
{
// use column default XF indexes
// #i194#: remove cell XF indexes equal to column default XF indexes
for( aCellIt = aCellBeg, aColIt = aColBeg; aCellIt != aCellEnd; ++aCellIt, ++aColIt )
if( *aCellIt == *aColIt )
*aCellIt = EXC_XF_NOTFOUND;
}
else
{
// use row default XF index
mnXFIndex = nRowXFIndex;
::set_flag( mnFlags, EXC_ROW_USEDEFXF );
// #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index
for( aCellIt = aCellBeg; aCellIt != aCellEnd; ++aCellIt )
if( *aCellIt == nRowXFIndex )
*aCellIt = EXC_XF_NOTFOUND;
}
// remove unused parts of BLANK/MULBLANK cell records
nPos = 0;
while( nPos < maCellList.GetSize() ) // do not cache list size, may change in the loop
{
XclExpCellRef xCell = maCellList.GetRecord( nPos );
xCell->RemoveUnusedBlankCells( aXFIndexes );
if( xCell->IsEmpty() )
maCellList.RemoveRecord( nPos );
else
++nPos;
}
// progress bar includes disabled rows
GetProgressBar().Progress();
}
sal_uInt16 XclExpRow::GetFirstUsedXclCol() const
{
return maCellList.IsEmpty() ? 0 : maCellList.GetFirstRecord()->GetXclCol();
}
sal_uInt16 XclExpRow::GetFirstFreeXclCol() const
{
return maCellList.IsEmpty() ? 0 : (maCellList.GetLastRecord()->GetLastXclCol() + 1);
}
bool XclExpRow::IsDefaultable() const
{
const sal_uInt16 nAllowedFlags = EXC_ROW_DEFAULTFLAGS | EXC_ROW_HIDDEN | EXC_ROW_UNSYNCED;
return !::get_flag( mnFlags, static_cast< sal_uInt16 >( ~nAllowedFlags ) ) && IsEmpty();
}
void XclExpRow::DisableIfDefault( const XclExpDefaultRowData& rDefRowData )
{
mbEnabled = !IsDefaultable() ||
(mnHeight != rDefRowData.mnHeight) ||
(IsHidden() != rDefRowData.IsHidden()) ||
(IsUnsynced() != rDefRowData.IsUnsynced());
}
void XclExpRow::WriteCellList( XclExpStream& rStrm )
{
DBG_ASSERT( mbEnabled || maCellList.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" );
maCellList.Save( rStrm );
}
void XclExpRow::Save( XclExpStream& rStrm )
{
if( mbEnabled )
XclExpRecord::Save( rStrm );
}
void XclExpRow::InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase )
{
DBG_ASSERT( xCell.is(), "XclExpRow::InsertCell - missing cell" );
/* #109751# If we have a multi-line text in a merged cell, and the resulting
row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED
flag to be true to ensure Excel works correctly. */
if( bIsMergedBase && xCell->IsMultiLineText() )
::set_flag( mnFlags, EXC_ROW_UNSYNCED );
// try to merge with previous cell, insert the new cell if not successful
XclExpCellRef xPrevCell = maCellList.GetRecord( nPos - 1 );
if( xPrevCell.is() && xPrevCell->TryMerge( *xCell ) )
xCell = xPrevCell;
else
maCellList.InsertRecord( xCell, nPos++ );
// nPos points now to following cell
// try to merge with following cell, remove it if successful
XclExpCellRef xNextCell = maCellList.GetRecord( nPos );
if( xNextCell.is() && xCell->TryMerge( *xNextCell ) )
maCellList.RemoveRecord( nPos );
}
void XclExpRow::WriteBody( XclExpStream& rStrm )
{
rStrm << mnXclRow
<< GetFirstUsedXclCol()
<< GetFirstFreeXclCol()
<< mnHeight
<< sal_uInt32( 0 )
<< mnFlags
<< mnXFIndex;
}
void XclExpRow::SaveXml( XclExpXmlStream& rStrm )
{
if( !mbEnabled )
return;
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
bool haveFormat = ::get_flag( mnFlags, EXC_ROW_USEDEFXF );
rWorksheet->startElement( XML_row,
XML_r, OString::valueOf( (sal_Int32) (mnXclRow+1) ).getStr(),
// OOXTODO: XML_spans, optional
XML_s, haveFormat ? lcl_GetStyleId( rStrm, mnXFIndex ).getStr() : NULL,
XML_customFormat, XclXmlUtils::ToPsz( haveFormat ),
XML_ht, OString::valueOf( (double) mnHeight / 20.0 ).getStr(),
XML_hidden, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_HIDDEN ) ),
XML_customHeight, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_UNSYNCED ) ),
XML_outlineLevel, OString::valueOf( (sal_Int32) mnOutlineLevel ).getStr(),
XML_collapsed, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_COLLAPSED ) ),
// OOXTODO: XML_thickTop, bool
// OOXTODO: XML_thickBot, bool
// OOXTODO: XML_ph, bool
FSEND );
// OOXTODO: XML_extLst
maCellList.SaveXml( rStrm );
rWorksheet->endElement( XML_row );
}
// ----------------------------------------------------------------------------
XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot& rRoot ) :
XclExpRoot( rRoot ),
maOutlineBfr( rRoot ),
maDimensions( rRoot ),
mpLastUsedRow( 0 ),
mnLastUsedXclRow( 0 )
{
}
void XclExpRowBuffer::AppendCell( XclExpCellRef xCell, bool bIsMergedBase )
{
DBG_ASSERT( xCell.is(), "XclExpRowBuffer::AppendCell - missing cell" );
GetOrCreateRow( xCell->GetXclRow(), false ).AppendCell( xCell, bIsMergedBase );
}
void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow )
{
if( nFirstFreeScRow > 0 )
GetOrCreateRow( static_cast< sal_uInt16 >( nFirstFreeScRow - 1 ), true );
}
void XclExpRowBuffer::Finalize( XclExpDefaultRowData& rDefRowData, const ScfUInt16Vec& rColXFIndexes )
{
size_t nPos, nSize;
// *** Finalize all rows *** ----------------------------------------------
GetProgressBar().ActivateFinalRowsSegment();
// unused blank cell records will be removed
for( nPos = 0, nSize = maRowList.GetSize(); nPos < nSize; ++nPos )
maRowList.GetRecord( nPos )->Finalize( rColXFIndexes );
// *** Default row format *** ---------------------------------------------
typedef ::std::map< XclExpDefaultRowData, size_t > XclExpDefRowDataMap;
XclExpDefRowDataMap aDefRowMap;
// find default row format for rows beyond used area
sal_uInt32 nDefaultXclRow = maRowList.IsEmpty() ? 0 : (maRowList.GetLastRecord()->GetXclRow() + 1);
XclExpDefaultRowData aMaxDefData;
size_t nMaxDefCount = 0;
/* #i30411# Files saved with SO7/OOo1.x with nonstandard default column
formatting cause big Excel files, because all rows from row 1 to row
32000 are exported. Now, if the used area goes exactly to row 32000,
ignore all rows >32000.
#i59220# Tolerance of +-128 rows for inserted/removed rows. */
if( (nDefaultXclRow < 31872) || (nDefaultXclRow > 32128) )
{
sal_uInt16 nLastXclRow = static_cast< sal_uInt16 >( GetMaxPos().Row() );
if( nDefaultXclRow <= nLastXclRow )
{
// create a dummy ROW record and fill aMaxDefData
XclExpRowOutlineBuffer aOutlineBfr( GetRoot() );
XclExpRow aRow( GetRoot(), nLastXclRow, aOutlineBfr, true );
aMaxDefData = XclExpDefaultRowData( aRow );
aDefRowMap[ aMaxDefData ] = nMaxDefCount =
static_cast< size_t >( nLastXclRow - nDefaultXclRow + 1 );
}
}
// only look for default format in existing rows, if there are more than unused
nSize = maRowList.GetSize();
if( nMaxDefCount < nSize )
{
for( nPos = 0; nPos < nSize; ++nPos )
{
XclExpRowRef xRow = maRowList.GetRecord( nPos );
/* Collect formats of unused rows (rows without cells), which are able
to be defaulted (i.e. no explicit format or outline level). */
if( xRow->IsDefaultable() )
{
XclExpDefaultRowData aDefData( *xRow );
size_t& rnDefCount = aDefRowMap[ aDefData ];
++rnDefCount;
if( rnDefCount > nMaxDefCount )
{
nMaxDefCount = rnDefCount;
aMaxDefData = aDefData;
}
}
}
}
// return the default row format to caller
rDefRowData = aMaxDefData;
// *** Disable unused ROW records, find used area *** ---------------------
sal_uInt16 nFirstUsedXclCol = SAL_MAX_UINT16;
sal_uInt16 nFirstFreeXclCol = 0;
sal_uInt32 nFirstUsedXclRow = SAL_MAX_UINT32;
sal_uInt32 nFirstFreeXclRow = 0;
for( nPos = 0, nSize = maRowList.GetSize(); nPos < nSize; ++nPos )
{
XclExpRowRef xRow = maRowList.GetRecord( nPos );
// disable unused rows
xRow->DisableIfDefault( aMaxDefData );
// find used column range
if( !xRow->IsEmpty() ) // empty rows return (0...0) as used range
{
nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, xRow->GetFirstUsedXclCol() );
nFirstFreeXclCol = ::std::max( nFirstFreeXclCol, xRow->GetFirstFreeXclCol() );
}
// find used row range
if( xRow->IsEnabled() )
{
sal_uInt16 nXclRow = xRow->GetXclRow();
nFirstUsedXclRow = ::std::min< sal_uInt32 >( nFirstUsedXclRow, nXclRow );
nFirstFreeXclRow = ::std::max< sal_uInt32 >( nFirstFreeXclRow, nXclRow + 1 );
}
}
// adjust start position, if there are no or only empty/disabled ROW records
nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, nFirstFreeXclCol );
nFirstUsedXclRow = ::std::min( nFirstUsedXclRow, nFirstFreeXclRow );
// initialize the DIMENSIONS record
maDimensions.SetDimensions(
nFirstUsedXclCol, nFirstUsedXclRow, nFirstFreeXclCol, nFirstFreeXclRow );
}
void XclExpRowBuffer::Save( XclExpStream& rStrm )
{
// DIMENSIONS record
maDimensions.Save( rStrm );
// save in blocks of 32 rows, each block contains first all ROWs, then all cells
size_t nSize = maRowList.GetSize();
size_t nBlockStart = 0;
sal_uInt16 nStartXclRow = (nSize == 0) ? 0 : maRowList.GetRecord( 0 )->GetXclRow();
while( nBlockStart < nSize )
{
// find end of row block
size_t nBlockEnd = nBlockStart + 1;
while( (nBlockEnd < nSize) && (maRowList.GetRecord( nBlockEnd )->GetXclRow() - nStartXclRow < EXC_ROW_ROWBLOCKSIZE) )
++nBlockEnd;
// write the ROW records
size_t nPos;
for( nPos = nBlockStart; nPos < nBlockEnd; ++nPos )
maRowList.GetRecord( nPos )->Save( rStrm );
// write the cell records
for( nPos = nBlockStart; nPos < nBlockEnd; ++nPos )
maRowList.GetRecord( nPos )->WriteCellList( rStrm );
nBlockStart = nBlockEnd;
nStartXclRow += EXC_ROW_ROWBLOCKSIZE;
}
}
void XclExpRowBuffer::SaveXml( XclExpXmlStream& rStrm )
{
sal_Int32 nNonEmpty = 0;
size_t nRows = maRowList.GetSize();
for( size_t i = 0; i < nRows; ++i)
if( maRowList.GetRecord( i )->IsEnabled() )
++nNonEmpty;
if( nNonEmpty == 0 )
{
rStrm.GetCurrentStream()->singleElement( XML_sheetData, FSEND );
}
else
{
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
rWorksheet->startElement( XML_sheetData, FSEND );
maRowList.SaveXml( rStrm );
rWorksheet->endElement( XML_sheetData );
}
}
XclExpDimensions* XclExpRowBuffer::GetDimensions()
{
return &maDimensions;
}
XclExpRow& XclExpRowBuffer::GetOrCreateRow( sal_uInt16 nXclRow, bool bRowAlwaysEmpty )
{
if( !mpLastUsedRow || (mnLastUsedXclRow != nXclRow) )
{
// fill up missing ROW records
// do not use sal_uInt16 for nFirstFreeXclRow, would cause loop in full sheets
for( size_t nFirstFreeXclRow = maRowList.GetSize(); nFirstFreeXclRow <= nXclRow; ++nFirstFreeXclRow )
maRowList.AppendNewRecord( new XclExpRow(
GetRoot(), static_cast< sal_uInt16 >( nFirstFreeXclRow ), maOutlineBfr, bRowAlwaysEmpty ) );
mpLastUsedRow = maRowList.GetRecord( nXclRow ).get();
mnLastUsedXclRow = nXclRow;
}
return *mpLastUsedRow;
}
// ============================================================================
// Cell Table
// ============================================================================
XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) :
XclExpRoot( rRoot ),
maColInfoBfr( rRoot ),
maRowBfr( rRoot ),
maArrayBfr( rRoot ),
maShrfmlaBfr( rRoot ),
maTableopBfr( rRoot ),
mxDefrowheight( new XclExpDefrowheight ),
mxGuts( new XclExpGuts( rRoot ) ),
mxNoteList( new XclExpNoteList ),
mxMergedcells( new XclExpMergedcells( rRoot ) ),
mxHyperlinkList( new XclExpHyperlinkList ),
mxDval( new XclExpDval( rRoot ) )
{
ScDocument& rDoc = GetDoc();
SCTAB nScTab = GetCurrScTab();
SvNumberFormatter& rFormatter = GetFormatter();
// maximum sheet limits
SCCOL nMaxScCol = GetMaxPos().Col();
SCROW nMaxScRow = GetMaxPos().Row();
// find used area (non-empty cells)
SCCOL nLastUsedScCol;
SCROW nLastUsedScRow;
rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
ScRange aUsedRange( 0, 0, nScTab, nLastUsedScCol, nLastUsedScRow, nScTab );
GetAddressConverter().ValidateRange( aUsedRange, true );
nLastUsedScCol = aUsedRange.aEnd.Col();
nLastUsedScRow = aUsedRange.aEnd.Row();
// first row without any set attributes (height/hidden/...)
SCROW nFirstUnflaggedScRow = rDoc.GetLastFlaggedRow( nScTab ) + 1;
// find range of outlines
SCROW nFirstUngroupedScRow = 0;
if( const ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable( nScTab ) )
{
SCCOLROW nScStartPos, nScEndPos;
if( const ScOutlineArray* pRowArray = pOutlineTable->GetRowArray() )
{
pRowArray->GetRange( nScStartPos, nScEndPos );
// +1 because open/close button is in next row in Excel, +1 for "end->first unused"
nFirstUngroupedScRow = static_cast< SCROW >( nScEndPos + 2 );
}
}
// column settings
/* #i30411# Files saved with SO7/OOo1.x with nonstandard default column
formatting cause big Excel files, because all rows from row 1 to row
32000 are exported. Now, if the used area goes exactly to row 32000,
use this row as default and ignore all rows >32000.
#i59220# Tolerance of +-128 rows for inserted/removed rows. */
if( (31871 <= nLastUsedScRow) && (nLastUsedScRow <= 32127) && (nFirstUnflaggedScRow < nLastUsedScRow) && (nFirstUngroupedScRow <= nLastUsedScRow) )
nMaxScRow = nLastUsedScRow;
maColInfoBfr.Initialize( nMaxScRow );
// range for cell iterator
SCCOL nLastIterScCol = nMaxScCol;
SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow + 128, nMaxScRow );
// modified for 119707 by zhanglu
SCCOL rEndColAtt = 0;
SCROW rEndRowAtt = 0;
rDoc.GetLastAttrCell( nScTab, rEndColAtt,rEndRowAtt ); // To get the real last cell's row number, which has visual data or attribute.
if( rEndRowAtt > nLastIterScRow )
nLastIterScRow = rEndRowAtt;
if (nLastIterScRow > nMaxScRow)
nLastIterScRow = nMaxScRow;
// modified for 119707 end
ScUsedAreaIterator aIt( &rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow );
// activate the correct segment and sub segment at the progress bar
GetProgressBar().ActivateCreateRowsSegment();
for( bool bIt = aIt.GetNext(); bIt; bIt = aIt.GetNext() )
{
SCCOL nScCol = aIt.GetStartCol();
SCROW nScRow = aIt.GetRow();
SCCOL nLastScCol = aIt.GetEndCol();
ScAddress aScPos( nScCol, nScRow, nScTab );
XclAddress aXclPos( static_cast< sal_uInt16 >( nScCol ), static_cast< sal_uInt16 >( nScRow ) );
sal_uInt16 nLastXclCol = static_cast< sal_uInt16 >( nLastScCol );
const ScBaseCell* pScCell = aIt.GetCell();
XclExpCellRef xCell;
const ScPatternAttr* pPattern = aIt.GetPattern();
// handle overlapped merged cells before creating the cell record
sal_uInt32 nMergeBaseXFId = EXC_XFID_NOTFOUND;
bool bIsMergedBase = false;
if( pPattern )
{
const SfxItemSet& rItemSet = pPattern->GetItemSet();
// base cell in a merged range
const ScMergeAttr& rMergeItem = GETITEM( rItemSet, ScMergeAttr, ATTR_MERGE );
bIsMergedBase = rMergeItem.IsMerged();
/* overlapped cell in a merged range; in Excel all merged cells
must contain same XF index, for correct border */
const ScMergeFlagAttr& rMergeFlagItem = GETITEM( rItemSet, ScMergeFlagAttr, ATTR_MERGE_FLAG );
if( rMergeFlagItem.IsOverlapped() )
nMergeBaseXFId = mxMergedcells->GetBaseXFId( aScPos );
}
String aAddNoteText; // additional text to be appended to a note
CellType eCellType = pScCell ? pScCell->GetCellType() : CELLTYPE_NONE;
switch( eCellType )
{
case CELLTYPE_VALUE:
{
double fValue = static_cast< const ScValueCell* >( pScCell )->GetValue();
// try to create a Boolean cell
if( pPattern && ((fValue == 0.0) || (fValue == 1.0)) )
{
sal_uLong nScNumFmt = GETITEMVALUE( pPattern->GetItemSet(), SfxUInt32Item, ATTR_VALUE_FORMAT, sal_uLong );
if( rFormatter.GetType( nScNumFmt ) == NUMBERFORMAT_LOGICAL )
xCell.reset( new XclExpBooleanCell(
GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue != 0.0 ) );
}
// try to create an RK value (compressed floating-point number)
sal_Int32 nRkValue;
if( !xCell && XclTools::GetRKFromDouble( nRkValue, fValue ) )
xCell.reset( new XclExpRkCell(
GetRoot(), aXclPos, pPattern, nMergeBaseXFId, nRkValue ) );
// else: simple floating-point number cell
if( !xCell )
xCell.reset( new XclExpNumberCell(
GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue ) );
}
break;
case CELLTYPE_STRING:
{
const ScStringCell& rScStrCell = *static_cast< const ScStringCell* >( pScCell );
xCell.reset( new XclExpLabelCell(
GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScStrCell ) );
}
break;
case CELLTYPE_EDIT:
{
const ScEditCell& rScEditCell = *static_cast< const ScEditCell* >( pScCell );
XclExpHyperlinkHelper aLinkHelper( GetRoot(), aScPos );
xCell.reset( new XclExpLabelCell(
GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScEditCell, aLinkHelper ) );
// add a single created HLINK record to the record list
if( aLinkHelper.HasLinkRecord() )
mxHyperlinkList->AppendRecord( aLinkHelper.GetLinkRecord() );
// add list of multiple URLs to the additional cell note text
if( aLinkHelper.HasMultipleUrls() )
ScGlobal::AddToken( aAddNoteText, aLinkHelper.GetUrlList(), '\n', 2 );
}
break;
case CELLTYPE_FORMULA:
{
const ScFormulaCell& rScFmlaCell = *static_cast< const ScFormulaCell* >( pScCell );
xCell.reset( new XclExpFormulaCell(
GetRoot(), aXclPos, pPattern, nMergeBaseXFId,
rScFmlaCell, maArrayBfr, maShrfmlaBfr, maTableopBfr ) );
}
break;
default:
DBG_ERRORFILE( "XclExpCellTable::XclExpCellTable - unknown cell type" );
// run-through!
case CELLTYPE_NONE:
case CELLTYPE_NOTE:
{
xCell.reset( new XclExpBlankCell(
GetRoot(), aXclPos, nLastXclCol, pPattern, nMergeBaseXFId ) );
}
break;
}
// insert the cell into the current row
if( xCell.is() )
maRowBfr.AppendCell( xCell, bIsMergedBase );
// notes
const ScPostIt* pScNote = pScCell ? pScCell->GetNote() : 0;
if( pScNote || (aAddNoteText.Len() > 0) )
mxNoteList->AppendNewRecord( new XclExpNote( GetRoot(), aScPos, pScNote, aAddNoteText ) );
// other sheet contents
if( pPattern )
{
const SfxItemSet& rItemSet = pPattern->GetItemSet();
// base cell in a merged range
if( bIsMergedBase )
{
const ScMergeAttr& rMergeItem = GETITEM( rItemSet, ScMergeAttr, ATTR_MERGE );
ScRange aScRange( aScPos );
aScRange.aEnd.IncCol( rMergeItem.GetColMerge() - 1 );
aScRange.aEnd.IncRow( rMergeItem.GetRowMerge() - 1 );
sal_uInt32 nXFId = xCell.is() ? xCell->GetFirstXFId() : EXC_XFID_NOTFOUND;
// #120156# blank cells merged vertically may occur repeatedly
DBG_ASSERT( (aScRange.aStart.Col() == aScRange.aEnd.Col()) || (nScCol == nLastScCol),
"XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" );
for( SCCOL nIndex = nScCol; nIndex <= nLastScCol; ++nIndex )
{
mxMergedcells->AppendRange( aScRange, nXFId );
aScRange.aStart.IncCol();
aScRange.aEnd.IncCol();
}
}
// data validation
if( ScfTools::CheckItem( rItemSet, ATTR_VALIDDATA, false ) )
{
sal_uLong nScHandle = GETITEMVALUE( rItemSet, SfxUInt32Item, ATTR_VALIDDATA, sal_uLong );
ScRange aScRange( aScPos );
aScRange.aEnd.SetCol( nLastScCol );
mxDval->InsertCellRange( aScRange, nScHandle );
}
}
}
// create missing row settings for rows anyhow flagged or with outlines
maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, nFirstUngroupedScRow ) );
}
void XclExpCellTable::Finalize()
{
// Finalize multiple operations.
maTableopBfr.Finalize();
/* Finalize column buffer. This calculates column default XF indexes from
the XF identifiers and fills a vector with these XF indexes. */
ScfUInt16Vec aColXFIndexes;
maColInfoBfr.Finalize( aColXFIndexes );
/* Finalize row buffer. This calculates all cell XF indexes from the XF
identifiers. Then the XF index vector aColXFIndexes (filled above) is
used to calculate the row default formats. With this, all unneeded blank
cell records (equal to row default or column default) will be removed.
The function returns the (most used) default row format in aDefRowData. */
XclExpDefaultRowData aDefRowData;
maRowBfr.Finalize( aDefRowData, aColXFIndexes );
// Initialize the DEFROWHEIGHT record.
mxDefrowheight->SetDefaultData( aDefRowData );
}
XclExpRecordRef XclExpCellTable::CreateRecord( sal_uInt16 nRecId ) const
{
XclExpRecordRef xRec;
switch( nRecId )
{
case EXC_ID3_DIMENSIONS: xRec.reset( new XclExpDelegatingRecord( const_cast<XclExpRowBuffer*>(&maRowBfr)->GetDimensions() ) ); break;
case EXC_ID2_DEFROWHEIGHT: xRec = mxDefrowheight; break;
case EXC_ID_GUTS: xRec = mxGuts; break;
case EXC_ID_NOTE: xRec = mxNoteList; break;
case EXC_ID_MERGEDCELLS: xRec = mxMergedcells; break;
case EXC_ID_HLINK: xRec = mxHyperlinkList; break;
case EXC_ID_DVAL: xRec = mxDval; break;
default: DBG_ERRORFILE( "XclExpCellTable::CreateRecord - unknown record id" );
}
return xRec;
}
void XclExpCellTable::Save( XclExpStream& rStrm )
{
// DEFCOLWIDTH and COLINFOs
maColInfoBfr.Save( rStrm );
// ROWs and cell records
maRowBfr.Save( rStrm );
}
void XclExpCellTable::SaveXml( XclExpXmlStream& rStrm )
{
maColInfoBfr.SaveXml( rStrm );
maRowBfr.SaveXml( rStrm );
}
// ============================================================================