| /************************************************************** |
| * |
| * 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 ); |
| } |
| |
| // ============================================================================ |
| |