diff options
Diffstat (limited to 'sc/source/filter/excel/xicontent.cxx')
-rw-r--r-- | sc/source/filter/excel/xicontent.cxx | 1319 |
1 files changed, 1319 insertions, 0 deletions
diff --git a/sc/source/filter/excel/xicontent.cxx b/sc/source/filter/excel/xicontent.cxx new file mode 100644 index 000000000000..fb1eb9a3bf44 --- /dev/null +++ b/sc/source/filter/excel/xicontent.cxx @@ -0,0 +1,1319 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xicontent.hxx" +#include <sfx2/objsh.hxx> +#include <sfx2/docfile.hxx> +#include <tools/urlobj.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editobj.hxx> +#include <sfx2/linkmgr.hxx> +#include <svl/itemset.hxx> +#include "scitems.hxx" +#include <editeng/eeitem.hxx> +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <editeng/flditem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/crsditem.hxx> +#include "document.hxx" +#include "editutil.hxx" +#include "cell.hxx" +#include "validat.hxx" +#include "patattr.hxx" +#include "docpool.hxx" +#include "rangenam.hxx" +#include "arealink.hxx" +#include "stlsheet.hxx" +#include "scextopt.hxx" +#include "xlformula.hxx" +#include "xltracer.hxx" +#include "xistream.hxx" +#include "xihelper.hxx" +#include "xistyle.hxx" +#include "xiescher.hxx" +#include "xiname.hxx" + +#include "excform.hxx" +#include "tabprotection.hxx" + +#include <memory> + +using ::com::sun::star::uno::Sequence; +using ::std::auto_ptr; + +// Shared string table ======================================================== + +XclImpSst::XclImpSst( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +void XclImpSst::ReadSst( XclImpStream& rStrm ) +{ + sal_uInt32 nStrCount; + rStrm.Ignore( 4 ); + rStrm >> nStrCount; + maStrings.clear(); + maStrings.reserve( static_cast< size_t >( nStrCount ) ); + while( (nStrCount > 0) && rStrm.IsValid() ) + { + XclImpString aString; + aString.Read( rStrm ); + maStrings.push_back( aString ); + --nStrCount; + } +} + +const XclImpString* XclImpSst::GetString( sal_uInt32 nSstIndex ) const +{ + return (nSstIndex < maStrings.size()) ? &maStrings[ nSstIndex ] : 0; +} + +ScBaseCell* XclImpSst::CreateCell( sal_uInt32 nSstIndex, sal_uInt16 nXFIndex ) const +{ + ScBaseCell* pCell = 0; + if( const XclImpString* pString = GetString( nSstIndex ) ) + pCell = XclImpStringHelper::CreateCell( *this, *pString, nXFIndex ); + return pCell; +} + +// Hyperlinks ================================================================= + +namespace { + +/** Reads character array and stores it into rString. + @param nChars Number of following characters (not byte count!). + @param b16Bit true = 16-bit characters, false = 8-bit characters. */ +void lclAppendString32( String& rString, XclImpStream& rStrm, sal_uInt32 nChars, bool b16Bit ) +{ + sal_uInt16 nReadChars = ulimit_cast< sal_uInt16 >( nChars ); + rString.Append( rStrm.ReadRawUniString( nReadChars, b16Bit ) ); + // ignore remaining chars + sal_Size nIgnore = nChars - nReadChars; + if( b16Bit ) + nIgnore *= 2; + rStrm.Ignore( nIgnore ); +} + +/** Reads 32-bit string length and the character array and stores it into rString. + @param b16Bit true = 16-bit characters, false = 8-bit characters. */ +void lclAppendString32( String& rString, XclImpStream& rStrm, bool b16Bit ) +{ + lclAppendString32( rString, rStrm, rStrm.ReaduInt32(), b16Bit ); +} + +/** Reads 32-bit string length and ignores following character array. + @param b16Bit true = 16-bit characters, false = 8-bit characters. */ +void lclIgnoreString32( XclImpStream& rStrm, bool b16Bit ) +{ + sal_uInt32 nChars; + rStrm >> nChars; + if( b16Bit ) + nChars *= 2; + rStrm.Ignore( nChars ); +} + +/** Converts a path to an absolute path. + @param rPath The source path. The resulting path is returned here. + @param nLevel Number of parent directories to add in front of the path. */ +void lclGetAbsPath( String& rPath, sal_uInt16 nLevel, SfxObjectShell* pDocShell ) +{ + String aTmpStr; + while( nLevel ) + { + aTmpStr.AppendAscii( "../" ); + --nLevel; + } + aTmpStr += rPath; + + if( pDocShell ) + { + bool bWasAbs = false; + rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr, bWasAbs ).GetMainURL( INetURLObject::NO_DECODE ); + // full path as stored in SvxURLField must be encoded + } + else + rPath = aTmpStr; +} + +/** Inserts the URL into a text cell. Does not modify value or formula cells. */ +void lclInsertUrl( const XclImpRoot& rRoot, const String& rUrl, SCCOL nScCol, SCROW nScRow, SCTAB nScTab ) +{ + ScDocument& rDoc = rRoot.GetDoc(); + ScAddress aScPos( nScCol, nScRow, nScTab ); + CellType eCellType = rDoc.GetCellType( aScPos ); + switch( eCellType ) + { + // #i54261# hyperlinks in string cells + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + { + String aDisplText; + rDoc.GetString( nScCol, nScRow, nScTab, aDisplText ); + if( !aDisplText.Len() ) + aDisplText = rUrl; + + ScEditEngineDefaulter& rEE = rRoot.GetEditEngine(); + SvxURLField aUrlField( rUrl, aDisplText, SVXURLFORMAT_APPDEFAULT ); + + const ScEditCell* pEditCell = (eCellType == CELLTYPE_EDIT) ? static_cast< const ScEditCell* >( rDoc.GetCell( aScPos ) ) : 0; + const EditTextObject* pEditObj = pEditCell ? pEditCell->GetData() : 0; + if( pEditObj ) + { + rEE.SetText( *pEditObj ); + rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0, 0, 0xFFFF, 0 ) ); + } + else + { + rEE.SetText( EMPTY_STRING ); + rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection() ); + if( const ScPatternAttr* pPattern = rDoc.GetPattern( aScPos.Col(), aScPos.Row(), nScTab ) ) + { + SfxItemSet aItemSet( rEE.GetEmptyItemSet() ); + pPattern->FillEditItemSet( &aItemSet ); + rEE.QuickSetAttribs( aItemSet, ESelection( 0, 0, 0xFFFF, 0 ) ); + } + } + ::std::auto_ptr< EditTextObject > xTextObj( rEE.CreateTextObject() ); + + ScEditCell* pCell = new ScEditCell( xTextObj.get(), &rDoc, rEE.GetEditTextObjectPool() ); + rDoc.PutCell( aScPos, pCell ); + } + break; + + // fix for #i31050# disabled, HYPERLINK is not able to return numeric value (#i91351#) +#if 0 + case CELLTYPE_VALUE: + { + // #i31050# replace number with HYPERLINK function + ScTokenArray aTokenArray; + aTokenArray.AddOpCode( ocHyperLink ); + aTokenArray.AddOpCode( ocOpen ); + aTokenArray.AddString( rUrl ); + aTokenArray.AddOpCode( ocSep ); + aTokenArray.AddDouble( rDoc.GetValue( aScPos ) ); + aTokenArray.AddOpCode( ocClose ); + rDoc.PutCell( aScPos, new ScFormulaCell( &rDoc, aScPos, &aTokenArray ) ); + } + break; +#endif + + default:; + } +} + +} // namespace + +// ---------------------------------------------------------------------------- + +void XclImpHyperlink::ReadHlink( XclImpStream& rStrm ) +{ + XclRange aXclRange( ScAddress::UNINITIALIZED ); + rStrm >> aXclRange; + // #i80006# Excel silently ignores invalid hi-byte of column index (TODO: everywhere?) + aXclRange.maFirst.mnCol &= 0xFF; + aXclRange.maLast.mnCol &= 0xFF; + String aString = ReadEmbeddedData( rStrm ); + if ( aString.Len() > 0 ) + rStrm.GetRoot().GetXFRangeBuffer().SetHyperlink( aXclRange, aString ); +} + +String XclImpHyperlink::ReadEmbeddedData( XclImpStream& rStrm ) +{ + const XclImpRoot& rRoot = rStrm.GetRoot(); + SfxObjectShell* pDocShell = rRoot.GetDocShell(); + + DBG_ASSERT_BIFF( rRoot.GetBiff() == EXC_BIFF8 ); + + sal_uInt32 nFlags; + XclGuid aGuid; + rStrm >> aGuid; + rStrm.Ignore( 4 ); + rStrm >> nFlags; + + DBG_ASSERT( aGuid == XclTools::maGuidStdLink, "XclImpHyperlink::ReadEmbeddedData - unknown header GUID" ); + + sal_uInt16 nLevel = 0; // counter for level to climb down in path + ::std::auto_ptr< String > xLongName; // link / file name + ::std::auto_ptr< String > xShortName; // 8.3-representation of file name + ::std::auto_ptr< String > xTextMark; // text mark + + // description (ignore) + if( ::get_flag( nFlags, EXC_HLINK_DESCR ) ) + lclIgnoreString32( rStrm, true ); + // target frame (ignore) !! DESCR/FRAME - is this the right order? (never seen them together) + if( ::get_flag( nFlags, EXC_HLINK_FRAME ) ) + lclIgnoreString32( rStrm, true ); + + // URL fields are zero-terminated - do not let the stream replace them + // in the lclAppendString32() with the '?' character. + rStrm.SetNulSubstChar( '\0' ); + + // UNC path + if( ::get_flag( nFlags, EXC_HLINK_UNC ) ) + { + xLongName.reset( new String ); + lclAppendString32( *xLongName, rStrm, true ); + lclGetAbsPath( *xLongName, 0, pDocShell ); + } + // file link or URL + else if( ::get_flag( nFlags, EXC_HLINK_BODY ) ) + { + rStrm >> aGuid; + + if( aGuid == XclTools::maGuidFileMoniker ) + { + rStrm >> nLevel; + xShortName.reset( new String ); + lclAppendString32( *xShortName, rStrm, false ); + rStrm.Ignore( 24 ); + + sal_uInt32 nStrLen; + rStrm >> nStrLen; + if( nStrLen ) + { + rStrm >> nStrLen; + nStrLen /= 2; // it's byte count here... + rStrm.Ignore( 2 ); + xLongName.reset( new String ); + lclAppendString32( *xLongName, rStrm, nStrLen, true ); + lclGetAbsPath( *xLongName, nLevel, pDocShell ); + } + else + lclGetAbsPath( *xShortName, nLevel, pDocShell ); + } + else if( aGuid == XclTools::maGuidUrlMoniker ) + { + sal_uInt32 nStrLen; + rStrm >> nStrLen; + nStrLen /= 2; // it's byte count here... + xLongName.reset( new String ); + lclAppendString32( *xLongName, rStrm, nStrLen, true ); + if( !::get_flag( nFlags, EXC_HLINK_ABS ) ) + lclGetAbsPath( *xLongName, 0, pDocShell ); + } + else + { + DBG_ERRORFILE( "XclImpHyperlink::ReadEmbeddedData - unknown content GUID" ); + } + } + + // text mark + if( ::get_flag( nFlags, EXC_HLINK_MARK ) ) + { + xTextMark.reset( new String ); + lclAppendString32( *xTextMark, rStrm, true ); + } + + rStrm.SetNulSubstChar(); // back to default + + DBG_ASSERT( rStrm.GetRecLeft() == 0, "XclImpHyperlink::ReadEmbeddedData - record size mismatch" ); + + if( !xLongName.get() && xShortName.get() ) + xLongName = xShortName; + else if( !xLongName.get() && xTextMark.get() ) + xLongName.reset( new String ); + + if( xLongName.get() ) + { + if( xTextMark.get() ) + { + if( xLongName->Len() == 0 ) + xTextMark->SearchAndReplaceAll( '!', '.' ); + xLongName->Append( '#' ); + xLongName->Append( *xTextMark ); + } + return *xLongName; + } + return String::EmptyString(); +} + +void XclImpHyperlink::ConvertToValidTabName(String& rUrl) +{ + xub_StrLen n = rUrl.Len(); + if (n < 4) + // Needs at least 4 characters. + return; + + sal_Unicode c = rUrl.GetChar(0); + if (c != sal_Unicode('#')) + // the 1st character must be '#'. + return; + + String aNewUrl(sal_Unicode('#')), aTabName; + + bool bInQuote = false; + bool bQuoteTabName = false; + for (xub_StrLen i = 1; i < n; ++i) + { + c = rUrl.GetChar(i); + if (c == sal_Unicode('\'')) + { + if (bInQuote && i+1 < n && rUrl.GetChar(i+1) == sal_Unicode('\'')) + { + // Two consecutive single quotes ('') signify a single literal + // quite. When this occurs, the whole table name needs to be + // quoted. + bQuoteTabName = true; + aTabName.Append(c); + aTabName.Append(c); + ++i; + continue; + } + + bInQuote = !bInQuote; + if (!bInQuote && aTabName.Len() > 0) + { + if (bQuoteTabName) + aNewUrl.Append(sal_Unicode('\'')); + aNewUrl.Append(aTabName); + if (bQuoteTabName) + aNewUrl.Append(sal_Unicode('\'')); + } + } + else if (bInQuote) + aTabName.Append(c); + else + aNewUrl.Append(c); + } + + if (bInQuote) + // It should be outside the quotes! + return; + + // All is good. Pass the new URL. + rUrl = aNewUrl; +} + +void XclImpHyperlink::InsertUrl( const XclImpRoot& rRoot, const XclRange& rXclRange, const String& rUrl ) +{ + String aUrl(rUrl); + ConvertToValidTabName(aUrl); + + SCTAB nScTab = rRoot.GetCurrScTab(); + ScRange aScRange( ScAddress::UNINITIALIZED ); + if( rRoot.GetAddressConverter().ConvertRange( aScRange, rXclRange, nScTab, nScTab, true ) ) + { + SCCOL nScCol1, nScCol2; + SCROW nScRow1, nScRow2; + aScRange.GetVars( nScCol1, nScRow1, nScTab, nScCol2, nScRow2, nScTab ); + for( SCCOL nScCol = nScCol1; nScCol <= nScCol2; ++nScCol ) + for( SCROW nScRow = nScRow1; nScRow <= nScRow2; ++nScRow ) + lclInsertUrl( rRoot, aUrl, nScCol, nScRow, nScTab ); + } +} + +// Label ranges =============================================================== + +void XclImpLabelranges::ReadLabelranges( XclImpStream& rStrm ) +{ + const XclImpRoot& rRoot = rStrm.GetRoot(); + DBG_ASSERT_BIFF( rRoot.GetBiff() == EXC_BIFF8 ); + + ScDocument& rDoc = rRoot.GetDoc(); + SCTAB nScTab = rRoot.GetCurrScTab(); + XclImpAddressConverter& rAddrConv = rRoot.GetAddressConverter(); + ScRangePairListRef xLabelRangesRef; + const ScRange* pScRange = 0; + + XclRangeList aRowXclRanges, aColXclRanges; + rStrm >> aRowXclRanges >> aColXclRanges; + + // row label ranges + ScRangeList aRowScRanges; + rAddrConv.ConvertRangeList( aRowScRanges, aRowXclRanges, nScTab, false ); + xLabelRangesRef = rDoc.GetRowNameRangesRef(); + for( pScRange = aRowScRanges.First(); pScRange; pScRange = aRowScRanges.Next() ) + { + ScRange aDataRange( *pScRange ); + if( aDataRange.aEnd.Col() < MAXCOL ) + { + aDataRange.aStart.SetCol( aDataRange.aEnd.Col() + 1 ); + aDataRange.aEnd.SetCol( MAXCOL ); + } + else if( aDataRange.aStart.Col() > 0 ) + { + aDataRange.aEnd.SetCol( aDataRange.aStart.Col() - 1 ); + aDataRange.aStart.SetCol( 0 ); + } + xLabelRangesRef->Append( ScRangePair( *pScRange, aDataRange ) ); + } + + // column label ranges + ScRangeList aColScRanges; + rAddrConv.ConvertRangeList( aColScRanges, aColXclRanges, nScTab, false ); + xLabelRangesRef = rDoc.GetColNameRangesRef(); + for( pScRange = aColScRanges.First(); pScRange; pScRange = aColScRanges.Next() ) + { + ScRange aDataRange( *pScRange ); + if( aDataRange.aEnd.Row() < MAXROW ) + { + aDataRange.aStart.SetRow( aDataRange.aEnd.Row() + 1 ); + aDataRange.aEnd.SetRow( MAXROW ); + } + else if( aDataRange.aStart.Row() > 0 ) + { + aDataRange.aEnd.SetRow( aDataRange.aStart.Row() - 1 ); + aDataRange.aStart.SetRow( 0 ); + } + xLabelRangesRef->Append( ScRangePair( *pScRange, aDataRange ) ); + } +} + +// Conditional formatting ===================================================== + +XclImpCondFormat::XclImpCondFormat( const XclImpRoot& rRoot, sal_uInt32 nFormatIndex ) : + XclImpRoot( rRoot ), + mnFormatIndex( nFormatIndex ), + mnCondCount( 0 ), + mnCondIndex( 0 ) +{ +} + +XclImpCondFormat::~XclImpCondFormat() +{ +} + +void XclImpCondFormat::ReadCondfmt( XclImpStream& rStrm ) +{ + DBG_ASSERT( !mnCondCount, "XclImpCondFormat::ReadCondfmt - already initialized" ); + XclRangeList aXclRanges; + rStrm >> mnCondCount; + rStrm.Ignore( 10 ); + rStrm >> aXclRanges; + GetAddressConverter().ConvertRangeList( maRanges, aXclRanges, GetCurrScTab(), true ); +} + +void XclImpCondFormat::ReadCF( XclImpStream& rStrm ) +{ + if( mnCondIndex >= mnCondCount ) + { + DBG_ERRORFILE( "XclImpCondFormat::ReadCF - CF without leading CONDFMT" ); + return; + } + + // entire conditional format outside of valid range? + if( !maRanges.Count() ) + return; + + sal_uInt8 nType, nOperator; + sal_uInt16 nFmlaSize1, nFmlaSize2; + sal_uInt32 nFlags; + + rStrm >> nType >> nOperator >> nFmlaSize1 >> nFmlaSize2 >> nFlags; + rStrm.Ignore( 2 ); + + // *** mode and comparison operator *** + + ScConditionMode eMode = SC_COND_NONE; + switch( nType ) + { + case EXC_CF_TYPE_CELL: + { + switch( nOperator ) + { + case EXC_CF_CMP_BETWEEN: eMode = SC_COND_BETWEEN; break; + case EXC_CF_CMP_NOT_BETWEEN: eMode = SC_COND_NOTBETWEEN; break; + case EXC_CF_CMP_EQUAL: eMode = SC_COND_EQUAL; break; + case EXC_CF_CMP_NOT_EQUAL: eMode = SC_COND_NOTEQUAL; break; + case EXC_CF_CMP_GREATER: eMode = SC_COND_GREATER; break; + case EXC_CF_CMP_LESS: eMode = SC_COND_LESS; break; + case EXC_CF_CMP_GREATER_EQUAL: eMode = SC_COND_EQGREATER; break; + case EXC_CF_CMP_LESS_EQUAL: eMode = SC_COND_EQLESS; break; + default: + DBG_ERROR1( "XclImpCondFormat::ReadCF - unknown CF comparison 0x%02hX", nOperator ); + } + } + break; + + case EXC_CF_TYPE_FMLA: + eMode = SC_COND_DIRECT; + break; + + default: + DBG_ERROR1( "XclImpCondFormat::ReadCF - unknown CF mode 0x%02hX", nType ); + return; + } + + // *** create style sheet *** + + String aStyleName( XclTools::GetCondFormatStyleName( GetCurrScTab(), mnFormatIndex, mnCondIndex ) ); + SfxItemSet& rStyleItemSet = ScfTools::MakeCellStyleSheet( GetStyleSheetPool(), aStyleName, true ).GetItemSet(); + + const XclImpPalette& rPalette = GetPalette(); + + // *** font block *** + + if( ::get_flag( nFlags, EXC_CF_BLOCK_FONT ) ) + { + XclImpFont aFont( GetRoot() ); + aFont.ReadCFFontBlock( rStrm ); + aFont.FillToItemSet( rStyleItemSet, EXC_FONTITEM_CELL ); + } + + // *** border block *** + + if( ::get_flag( nFlags, EXC_CF_BLOCK_BORDER ) ) + { + sal_uInt16 nLineStyle; + sal_uInt32 nLineColor; + rStrm >> nLineStyle >> nLineColor; + rStrm.Ignore( 2 ); + + XclImpCellBorder aBorder; + aBorder.FillFromCF8( nLineStyle, nLineColor, nFlags ); + aBorder.FillToItemSet( rStyleItemSet, rPalette ); + } + + // *** pattern block *** + + if( ::get_flag( nFlags, EXC_CF_BLOCK_AREA ) ) + { + sal_uInt16 nPattern, nColor; + rStrm >> nPattern >> nColor; + + XclImpCellArea aArea; + aArea.FillFromCF8( nPattern, nColor, nFlags ); + aArea.FillToItemSet( rStyleItemSet, rPalette ); + } + + // *** formulas *** + + const ScAddress& rPos = maRanges.GetObject( 0 )->aStart; // assured above that maRanges is not empty + ExcelToSc& rFmlaConv = GetOldFmlaConverter(); + + ::std::auto_ptr< ScTokenArray > xTokArr1; + if( nFmlaSize1 > 0 ) + { + const ScTokenArray* pTokArr = 0; + rFmlaConv.Reset( rPos ); + rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize1, false, FT_RangeName ); + // formula converter owns pTokArr -> create a copy of the token array + if( pTokArr ) + xTokArr1.reset( pTokArr->Clone() ); + } + + ::std::auto_ptr< ScTokenArray > pTokArr2; + if( nFmlaSize2 > 0 ) + { + const ScTokenArray* pTokArr = 0; + rFmlaConv.Reset( rPos ); + rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize2, false, FT_RangeName ); + // formula converter owns pTokArr -> create a copy of the token array + if( pTokArr ) + pTokArr2.reset( pTokArr->Clone() ); + } + + // *** create the Calc conditional formatting *** + + if( !mxScCondFmt.get() ) + { + ULONG nKey = 0; + mxScCondFmt.reset( new ScConditionalFormat( nKey, GetDocPtr() ) ); + } + + ScCondFormatEntry aEntry( eMode, xTokArr1.get(), pTokArr2.get(), GetDocPtr(), rPos, aStyleName ); + mxScCondFmt->AddEntry( aEntry ); + ++mnCondIndex; +} + +void XclImpCondFormat::Apply() +{ + if( mxScCondFmt.get() ) + { + ScDocument& rDoc = GetDoc(); + + ULONG nKey = rDoc.AddCondFormat( *mxScCondFmt ); + ScPatternAttr aPattern( rDoc.GetPool() ); + aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_CONDITIONAL, nKey ) ); + + // maRanges contains only valid cell ranges + for( const ScRange* pScRange = maRanges.First(); pScRange; pScRange = maRanges.Next() ) + { + rDoc.ApplyPatternAreaTab( + pScRange->aStart.Col(), pScRange->aStart.Row(), + pScRange->aEnd.Col(), pScRange->aEnd.Row(), + pScRange->aStart.Tab(), aPattern ); + } + } +} + +// ---------------------------------------------------------------------------- + +XclImpCondFormatManager::XclImpCondFormatManager( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +void XclImpCondFormatManager::ReadCondfmt( XclImpStream& rStrm ) +{ + XclImpCondFormat* pFmt = new XclImpCondFormat( GetRoot(), maCondFmtList.Count() ); + pFmt->ReadCondfmt( rStrm ); + maCondFmtList.Append( pFmt ); +} + +void XclImpCondFormatManager::ReadCF( XclImpStream& rStrm ) +{ + DBG_ASSERT( !maCondFmtList.Empty(), "XclImpCondFormatManager::ReadCF - CF without leading CONDFMT" ); + if( !maCondFmtList.Empty() ) + maCondFmtList.GetObject( maCondFmtList.Count() - 1 )->ReadCF( rStrm ); +} + +void XclImpCondFormatManager::Apply() +{ + for( XclImpCondFormat* pFmt = maCondFmtList.First(); pFmt; pFmt = maCondFmtList.Next() ) + pFmt->Apply(); + maCondFmtList.Clear(); +} + +// Data Validation ============================================================ + +void XclImpValidation::ReadDval( XclImpStream& rStrm ) +{ + const XclImpRoot& rRoot = rStrm.GetRoot(); + DBG_ASSERT_BIFF( rRoot.GetBiff() == EXC_BIFF8 ); + + sal_uInt32 nObjId; + rStrm.Ignore( 10 ); + rStrm >> nObjId; + if( nObjId != EXC_DVAL_NOOBJ ) + { + DBG_ASSERT( nObjId <= 0xFFFF, "XclImpValidation::ReadDval - invalid object ID" ); + rRoot.GetCurrSheetDrawing().SetSkipObj( static_cast< sal_uInt16 >( nObjId ) ); + } +} + +void XclImpValidation::ReadDV( XclImpStream& rStrm ) +{ + const XclImpRoot& rRoot = rStrm.GetRoot(); + DBG_ASSERT_BIFF( rRoot.GetBiff() == EXC_BIFF8 ); + + ScDocument& rDoc = rRoot.GetDoc(); + SCTAB nScTab = rRoot.GetCurrScTab(); + ExcelToSc& rFmlaConv = rRoot.GetOldFmlaConverter(); + + // flags + sal_uInt32 nFlags; + rStrm >> nFlags; + + // message strings + /* Empty strings are single NUL characters in Excel (string length is 1). + -> Do not let the stream replace them with '?' characters. */ + rStrm.SetNulSubstChar( '\0' ); + String aPromptTitle( rStrm.ReadUniString() ); + String aErrorTitle( rStrm.ReadUniString() ); + String aPromptMessage( rStrm.ReadUniString() ); + String aErrorMessage( rStrm.ReadUniString() ); + rStrm.SetNulSubstChar(); // back to default + + // formula(s) + if( rStrm.GetRecLeft() > 8 ) + { + sal_uInt16 nLen; + + // first formula + // string list is single tStr token with NUL separators -> replace them with LF + rStrm.SetNulSubstChar( '\n' ); + ::std::auto_ptr< ScTokenArray > xTokArr1; + rStrm >> nLen; + rStrm.Ignore( 2 ); + if( nLen > 0 ) + { + const ScTokenArray* pTokArr = 0; + rFmlaConv.Reset(); + rFmlaConv.Convert( pTokArr, rStrm, nLen, false, FT_RangeName ); + // formula converter owns pTokArr -> create a copy of the token array + if( pTokArr ) + xTokArr1.reset( pTokArr->Clone() ); + } + rStrm.SetNulSubstChar(); // back to default + + // second formula + ::std::auto_ptr< ScTokenArray > xTokArr2; + rStrm >> nLen; + rStrm.Ignore( 2 ); + if( nLen > 0 ) + { + const ScTokenArray* pTokArr = 0; + rFmlaConv.Reset(); + rFmlaConv.Convert( pTokArr, rStrm, nLen, false, FT_RangeName ); + // formula converter owns pTokArr -> create a copy of the token array + if( pTokArr ) + xTokArr2.reset( pTokArr->Clone() ); + } + + // read all cell ranges + XclRangeList aXclRanges; + rStrm >> aXclRanges; + + // convert to Calc range list + ScRangeList aScRanges; + rRoot.GetAddressConverter().ConvertRangeList( aScRanges, aXclRanges, nScTab, true ); + + // only continue if there are valid ranges + if( aScRanges.Count() ) + { + bool bIsValid = true; // valid settings in flags field + + ScValidationMode eValMode = SC_VALID_ANY; + switch( nFlags & EXC_DV_MODE_MASK ) + { + case EXC_DV_MODE_ANY: eValMode = SC_VALID_ANY; break; + case EXC_DV_MODE_WHOLE: eValMode = SC_VALID_WHOLE; break; + case EXC_DV_MODE_DECIMAL: eValMode = SC_VALID_DECIMAL; break; + case EXC_DV_MODE_LIST: eValMode = SC_VALID_LIST; break; + case EXC_DV_MODE_DATE: eValMode = SC_VALID_DATE; break; + case EXC_DV_MODE_TIME: eValMode = SC_VALID_TIME; break; + case EXC_DV_MODE_TEXTLEN: eValMode = SC_VALID_TEXTLEN; break; + case EXC_DV_MODE_CUSTOM: eValMode = SC_VALID_CUSTOM; break; + default: bIsValid = false; + } + rRoot.GetTracer().TraceDVType(eValMode == SC_VALID_CUSTOM); + + ScConditionMode eCondMode = SC_COND_BETWEEN; + switch( nFlags & EXC_DV_COND_MASK ) + { + case EXC_DV_COND_BETWEEN: eCondMode = SC_COND_BETWEEN; break; + case EXC_DV_COND_NOTBETWEEN:eCondMode = SC_COND_NOTBETWEEN; break; + case EXC_DV_COND_EQUAL: eCondMode = SC_COND_EQUAL; break; + case EXC_DV_COND_NOTEQUAL: eCondMode = SC_COND_NOTEQUAL; break; + case EXC_DV_COND_GREATER: eCondMode = SC_COND_GREATER; break; + case EXC_DV_COND_LESS: eCondMode = SC_COND_LESS; break; + case EXC_DV_COND_EQGREATER: eCondMode = SC_COND_EQGREATER; break; + case EXC_DV_COND_EQLESS: eCondMode = SC_COND_EQLESS; break; + default: bIsValid = false; + } + + if( bIsValid ) + { + // first range for base address for relative references + const ScRange& rScRange = *aScRanges.GetObject( 0 ); // aScRanges is not empty + + // process string list of a list validity (convert to list of string tokens) + if( xTokArr1.get() && (eValMode == SC_VALID_LIST) && ::get_flag( nFlags, EXC_DV_STRINGLIST ) ) + XclTokenArrayHelper::ConvertStringToList( *xTokArr1, '\n', true ); + + ScValidationData aValidData( eValMode, eCondMode, xTokArr1.get(), xTokArr2.get(), &rDoc, rScRange.aStart ); + + aValidData.SetIgnoreBlank( ::get_flag( nFlags, EXC_DV_IGNOREBLANK ) ); + aValidData.SetListType( ::get_flagvalue( nFlags, EXC_DV_SUPPRESSDROPDOWN, ValidListType::INVISIBLE, ValidListType::UNSORTED ) ); + + // *** prompt box *** + if( aPromptTitle.Len() || aPromptMessage.Len() ) + { + // set any text stored in the record + aValidData.SetInput( aPromptTitle, aPromptMessage ); + if( !::get_flag( nFlags, EXC_DV_SHOWPROMPT ) ) + aValidData.ResetInput(); + } + + // *** error box *** + ScValidErrorStyle eErrStyle = SC_VALERR_STOP; + switch( nFlags & EXC_DV_ERROR_MASK ) + { + case EXC_DV_ERROR_WARNING: eErrStyle = SC_VALERR_WARNING; break; + case EXC_DV_ERROR_INFO: eErrStyle = SC_VALERR_INFO; break; + } + // set texts and error style + aValidData.SetError( aErrorTitle, aErrorMessage, eErrStyle ); + if( !::get_flag( nFlags, EXC_DV_SHOWERROR ) ) + aValidData.ResetError(); + + // set the handle ID + ULONG nHandle = rDoc.AddValidationEntry( aValidData ); + ScPatternAttr aPattern( rDoc.GetPool() ); + aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nHandle ) ); + + // apply all ranges + for( const ScRange* pScRange = aScRanges.First(); pScRange; pScRange = aScRanges.Next() ) + rDoc.ApplyPatternAreaTab( pScRange->aStart.Col(), pScRange->aStart.Row(), + pScRange->aEnd.Col(), pScRange->aEnd.Row(), nScTab, aPattern ); + } + } + } +} + +// Web queries ================================================================ + +XclImpWebQuery::XclImpWebQuery( const ScRange& rDestRange ) : + maDestRange( rDestRange ), + meMode( xlWQUnknown ), + mnRefresh( 0 ) +{ +} + +void XclImpWebQuery::ReadParamqry( XclImpStream& rStrm ) +{ + sal_uInt16 nFlags = rStrm.ReaduInt16(); + sal_uInt16 nType = ::extract_value< sal_uInt16 >( nFlags, 0, 3 ); + if( (nType == EXC_PQRYTYPE_WEBQUERY) && ::get_flag( nFlags, EXC_PQRY_WEBQUERY ) ) + { + if( ::get_flag( nFlags, EXC_PQRY_TABLES ) ) + { + meMode = xlWQAllTables; + maTables = ScfTools::GetHTMLTablesName(); + } + else + { + meMode = xlWQDocument; + maTables = ScfTools::GetHTMLDocName(); + } + } +} + +void XclImpWebQuery::ReadWqstring( XclImpStream& rStrm ) +{ + maURL = rStrm.ReadUniString(); +} + +void XclImpWebQuery::ReadWqsettings( XclImpStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm.Ignore( 10 ); + rStrm >> nFlags; + rStrm.Ignore( 10 ); + rStrm >> mnRefresh; + + if( ::get_flag( nFlags, EXC_WQSETT_SPECTABLES ) && (meMode == xlWQAllTables) ) + meMode = xlWQSpecTables; +} + +void XclImpWebQuery::ReadWqtables( XclImpStream& rStrm ) +{ + if( meMode == xlWQSpecTables ) + { + rStrm.Ignore( 4 ); + String aTables( rStrm.ReadUniString() ); + + const sal_Unicode cSep = ';'; + aTables.SearchAndReplaceAll( ',', cSep ); + String aQuotedPairs( RTL_CONSTASCII_USTRINGPARAM( "\"\"" ) ); + xub_StrLen nTokenCnt = aTables.GetQuotedTokenCount( aQuotedPairs, cSep ); + maTables.Erase(); + xub_StrLen nStringIx = 0; + for( xub_StrLen nToken = 0; nToken < nTokenCnt; ++nToken ) + { + String aToken( aTables.GetQuotedToken( 0, aQuotedPairs, cSep, nStringIx ) ); + sal_Int32 nTabNum = CharClass::isAsciiNumeric( aToken ) ? aToken.ToInt32() : 0; + if( nTabNum > 0 ) + ScGlobal::AddToken( maTables, ScfTools::GetNameFromHTMLIndex( static_cast< sal_uInt32 >( nTabNum ) ), cSep ); + else + { + ScGlobal::EraseQuotes( aToken, '"', false ); + if( aToken.Len() ) + ScGlobal::AddToken( maTables, ScfTools::GetNameFromHTMLName( aToken ), cSep ); + } + } + } +} + +void XclImpWebQuery::Apply( ScDocument& rDoc, const String& rFilterName ) +{ + if( maURL.Len() && (meMode != xlWQUnknown) && rDoc.GetDocumentShell() ) + { + ScAreaLink* pLink = new ScAreaLink( rDoc.GetDocumentShell(), + maURL, rFilterName, EMPTY_STRING, maTables, maDestRange, mnRefresh * 60UL ); + rDoc.GetLinkManager()->InsertFileLink( *pLink, OBJECT_CLIENT_FILE, + maURL, &rFilterName, &maTables ); + } +} + +// ---------------------------------------------------------------------------- + +XclImpWebQueryBuffer::XclImpWebQueryBuffer( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +void XclImpWebQueryBuffer::ReadQsi( XclImpStream& rStrm ) +{ + if( GetBiff() == EXC_BIFF8 ) + { + rStrm.Ignore( 10 ); + String aXclName( rStrm.ReadUniString() ); + + // #i64794# Excel replaces spaces with underscores + aXclName.SearchAndReplaceAll( ' ', '_' ); + + // #101529# find the defined name used in Calc + if( const XclImpName* pName = GetNameManager().FindName( aXclName, GetCurrScTab() ) ) + { + if( const ScRangeData* pRangeData = pName->GetScRangeData() ) + { + ScRange aRange; + if( pRangeData->IsReference( aRange ) ) + maWQList.Append( new XclImpWebQuery( aRange ) ); + } + } + } + else + { + DBG_ERROR_BIFF(); + } +} + +void XclImpWebQueryBuffer::ReadParamqry( XclImpStream& rStrm ) +{ + if( XclImpWebQuery* pQuery = maWQList.Last() ) + pQuery->ReadParamqry( rStrm ); +} + +void XclImpWebQueryBuffer::ReadWqstring( XclImpStream& rStrm ) +{ + if( XclImpWebQuery* pQuery = maWQList.Last() ) + pQuery->ReadWqstring( rStrm ); +} + +void XclImpWebQueryBuffer::ReadWqsettings( XclImpStream& rStrm ) +{ + if( XclImpWebQuery* pQuery = maWQList.Last() ) + pQuery->ReadWqsettings( rStrm ); +} + +void XclImpWebQueryBuffer::ReadWqtables( XclImpStream& rStrm ) +{ + if( XclImpWebQuery* pQuery = maWQList.Last() ) + pQuery->ReadWqtables( rStrm ); +} + +void XclImpWebQueryBuffer::Apply() +{ + ScDocument& rDoc = GetDoc(); + String aFilterName( RTL_CONSTASCII_USTRINGPARAM( EXC_WEBQRY_FILTER ) ); + for( XclImpWebQuery* pQuery = maWQList.First(); pQuery; pQuery = maWQList.Next() ) + pQuery->Apply( rDoc, aFilterName ); +} + +// Decryption ================================================================= + +namespace { + +XclImpDecrypterRef lclReadFilepass5( XclImpStream& rStrm ) +{ + XclImpDecrypterRef xDecr; + DBG_ASSERT( rStrm.GetRecLeft() == 4, "lclReadFilepass5 - wrong record size" ); + if( rStrm.GetRecLeft() == 4 ) + { + sal_uInt16 nKey, nHash; + rStrm >> nKey >> nHash; + xDecr.reset( new XclImpBiff5Decrypter( nKey, nHash ) ); + } + return xDecr; +} + +XclImpDecrypterRef lclReadFilepass8_Standard( XclImpStream& rStrm ) +{ + XclImpDecrypterRef xDecr; + DBG_ASSERT( rStrm.GetRecLeft() == 48, "lclReadFilepass8 - wrong record size" ); + if( rStrm.GetRecLeft() == 48 ) + { + sal_uInt8 pnSalt[ 16 ]; + sal_uInt8 pnVerifier[ 16 ]; + sal_uInt8 pnVerifierHash[ 16 ]; + rStrm.Read( pnSalt, 16 ); + rStrm.Read( pnVerifier, 16 ); + rStrm.Read( pnVerifierHash, 16 ); + xDecr.reset( new XclImpBiff8Decrypter( pnSalt, pnVerifier, pnVerifierHash ) ); + } + return xDecr; +} + +XclImpDecrypterRef lclReadFilepass8_Strong( XclImpStream& /*rStrm*/ ) +{ + // not supported + return XclImpDecrypterRef(); +} + +XclImpDecrypterRef lclReadFilepass8( XclImpStream& rStrm ) +{ + XclImpDecrypterRef xDecr; + + sal_uInt16 nMode; + rStrm >> nMode; + switch( nMode ) + { + case EXC_FILEPASS_BIFF5: + xDecr = lclReadFilepass5( rStrm ); + break; + + case EXC_FILEPASS_BIFF8: + { + rStrm.Ignore( 2 ); + sal_uInt16 nSubMode; + rStrm >> nSubMode; + switch( nSubMode ) + { + case EXC_FILEPASS_BIFF8_STD: + xDecr = lclReadFilepass8_Standard( rStrm ); + break; + case EXC_FILEPASS_BIFF8_STRONG: + xDecr = lclReadFilepass8_Strong( rStrm ); + break; + default: + DBG_ERRORFILE( "lclReadFilepass8 - unknown BIFF8 encryption sub mode" ); + } + } + break; + + default: + DBG_ERRORFILE( "lclReadFilepass8 - unknown encryption mode" ); + } + + return xDecr; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +ErrCode XclImpDecryptHelper::ReadFilepass( XclImpStream& rStrm ) +{ + XclImpDecrypterRef xDecr; + rStrm.DisableDecryption(); + + // read the FILEPASS record and create a new decrypter object + switch( rStrm.GetRoot().GetBiff() ) + { + case EXC_BIFF2: + case EXC_BIFF3: + case EXC_BIFF4: + case EXC_BIFF5: xDecr = lclReadFilepass5( rStrm ); break; + case EXC_BIFF8: xDecr = lclReadFilepass8( rStrm ); break; + default: DBG_ERROR_BIFF(); + }; + + // set decrypter at import stream + rStrm.SetDecrypter( xDecr ); + + // request and verify a password (decrypter implements IDocPasswordVerifier) + if( xDecr.is() ) + rStrm.GetRoot().RequestPassword( *xDecr ); + + // return error code (success, wrong password, etc.) + return xDecr.is() ? xDecr->GetError() : EXC_ENCR_ERROR_UNSUPP_CRYPT; +} + +// Document protection ======================================================== + +XclImpDocProtectBuffer::XclImpDocProtectBuffer( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + mnPassHash(0x0000), + mbDocProtect(false), + mbWinProtect(false) +{ +} + +void XclImpDocProtectBuffer::ReadDocProtect( XclImpStream& rStrm ) +{ + mbDocProtect = rStrm.ReaduInt16() ? true : false; +} + +void XclImpDocProtectBuffer::ReadWinProtect( XclImpStream& rStrm ) +{ + mbWinProtect = rStrm.ReaduInt16() ? true : false; +} + +void XclImpDocProtectBuffer::ReadPasswordHash( XclImpStream& rStrm ) +{ + rStrm.EnableDecryption(); + mnPassHash = rStrm.ReaduInt16(); +} + +void XclImpDocProtectBuffer::Apply() const +{ + if (!mbDocProtect && !mbWinProtect) + // Excel requires either the structure or windows protection is set. + // If neither is set then the document is not protected at all. + return; + + auto_ptr<ScDocProtection> pProtect(new ScDocProtection); + pProtect->setProtected(true); + +#if ENABLE_SHEET_PROTECTION + if (mnPassHash) + { + // 16-bit password pash. + Sequence<sal_Int8> aPass(2); + aPass[0] = (mnPassHash >> 8) & 0xFF; + aPass[1] = mnPassHash & 0xFF; + pProtect->setPasswordHash(aPass, PASSHASH_XL); + } +#endif + + // document protection options + pProtect->setOption(ScDocProtection::STRUCTURE, mbDocProtect); + pProtect->setOption(ScDocProtection::WINDOWS, mbWinProtect); + + GetDoc().SetDocProtection(pProtect.get()); +} + +// Sheet Protection =========================================================== + +XclImpSheetProtectBuffer::Sheet::Sheet() : + mbProtected(false), + mnPasswordHash(0x0000), + mnOptions(0x4400) +{ +} + +// ---------------------------------------------------------------------------- + +XclImpSheetProtectBuffer::Sheet::Sheet(const Sheet& r) : + mbProtected(r.mbProtected), + mnPasswordHash(r.mnPasswordHash), + mnOptions(r.mnOptions) +{ +} + +XclImpSheetProtectBuffer::XclImpSheetProtectBuffer( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +void XclImpSheetProtectBuffer::ReadProtect( XclImpStream& rStrm, SCTAB nTab ) +{ + if ( rStrm.ReaduInt16() ) + { + Sheet* pSheet = GetSheetItem(nTab); + if (pSheet) + pSheet->mbProtected = true; + } +} + +void XclImpSheetProtectBuffer::ReadOptions( XclImpStream& rStrm, SCTAB nTab ) +{ + rStrm.Ignore(12); + + // feature type can be either 2 or 4. If 2, this record stores flag for + // enhanced protection, whereas if 4 it stores flag for smart tag. + sal_uInt16 nFeatureType; + rStrm >> nFeatureType; + if (nFeatureType != 2) + // We currently only support import of enhanced protection data. + return; + + rStrm.Ignore(1); // always 1 + + // The flag size specifies the size of bytes that follows that stores + // feature data. If -1 it depends on the feature type imported earlier. + // For enhanced protection data, the size is always 4. For the most xls + // documents out there this value is almost always -1. + sal_Int32 nFlagSize; + rStrm >> nFlagSize; + if (nFlagSize != -1) + return; + + // There are actually 4 bytes to read, but the upper 2 bytes currently + // don't store any bits. + sal_uInt16 nOptions; + rStrm >> nOptions; + + Sheet* pSheet = GetSheetItem(nTab); + if (pSheet) + pSheet->mnOptions = nOptions; +} + +void XclImpSheetProtectBuffer::ReadPasswordHash( XclImpStream& rStrm, SCTAB nTab ) +{ + sal_uInt16 nHash; + rStrm >> nHash; + Sheet* pSheet = GetSheetItem(nTab); + if (pSheet) + pSheet->mnPasswordHash = nHash; +} + +void XclImpSheetProtectBuffer::Apply() const +{ + for (ProtectedSheetMap::const_iterator itr = maProtectedSheets.begin(), itrEnd = maProtectedSheets.end(); + itr != itrEnd; ++itr) + { + if (!itr->second.mbProtected) + // This sheet is (for whatever reason) not protected. + continue; + + auto_ptr<ScTableProtection> pProtect(new ScTableProtection); + pProtect->setProtected(true); + +#if ENABLE_SHEET_PROTECTION + // 16-bit hash password + const sal_uInt16 nHash = itr->second.mnPasswordHash; + if (nHash) + { + Sequence<sal_Int8> aPass(2); + aPass[0] = (nHash >> 8) & 0xFF; + aPass[1] = nHash & 0xFF; + pProtect->setPasswordHash(aPass, PASSHASH_XL); + } +#endif + + // sheet protection options + const sal_uInt16 nOptions = itr->second.mnOptions; + pProtect->setOption( ScTableProtection::OBJECTS, (nOptions & 0x0001) ); + pProtect->setOption( ScTableProtection::SCENARIOS, (nOptions & 0x0002) ); + pProtect->setOption( ScTableProtection::FORMAT_CELLS, (nOptions & 0x0004) ); + pProtect->setOption( ScTableProtection::FORMAT_COLUMNS, (nOptions & 0x0008) ); + pProtect->setOption( ScTableProtection::FORMAT_ROWS, (nOptions & 0x0010) ); + pProtect->setOption( ScTableProtection::INSERT_COLUMNS, (nOptions & 0x0020) ); + pProtect->setOption( ScTableProtection::INSERT_ROWS, (nOptions & 0x0040) ); + pProtect->setOption( ScTableProtection::INSERT_HYPERLINKS, (nOptions & 0x0080) ); + pProtect->setOption( ScTableProtection::DELETE_COLUMNS, (nOptions & 0x0100) ); + pProtect->setOption( ScTableProtection::DELETE_ROWS, (nOptions & 0x0200) ); + pProtect->setOption( ScTableProtection::SELECT_LOCKED_CELLS, (nOptions & 0x0400) ); + pProtect->setOption( ScTableProtection::SORT, (nOptions & 0x0800) ); + pProtect->setOption( ScTableProtection::AUTOFILTER, (nOptions & 0x1000) ); + pProtect->setOption( ScTableProtection::PIVOT_TABLES, (nOptions & 0x2000) ); + pProtect->setOption( ScTableProtection::SELECT_UNLOCKED_CELLS, (nOptions & 0x4000) ); + + // all done. now commit. + GetDoc().SetTabProtection(itr->first, pProtect.get()); + } +} + +XclImpSheetProtectBuffer::Sheet* XclImpSheetProtectBuffer::GetSheetItem( SCTAB nTab ) +{ + ProtectedSheetMap::iterator itr = maProtectedSheets.find(nTab); + if (itr == maProtectedSheets.end()) + { + // new sheet + if ( !maProtectedSheets.insert( ProtectedSheetMap::value_type(nTab, Sheet()) ).second ) + return NULL; + + itr = maProtectedSheets.find(nTab); + } + + return &itr->second; +} + +// ============================================================================ + |