blob: 4d260b54d2c25093f5ee564dc619dfb3c33db93c [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"
#include "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_Conditional );
// 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_Conditional );
// 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() )
{
sal_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();
sal_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_Conditional );
// 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_Conditional );
// 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
sal_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 = ';';
String aQuotedPairs( RTL_CONSTASCII_USTRINGPARAM( "\"\"" ) );
xub_StrLen nTokenCnt = aTables.GetQuotedTokenCount( aQuotedPairs, ',' );
maTables.Erase();
xub_StrLen nStringIx = 0;
for( xub_StrLen nToken = 0; nToken < nTokenCnt; ++nToken )
{
String aToken( aTables.GetQuotedToken( 0, aQuotedPairs, ',', 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().RequestEncryptionData( *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;
}
// ============================================================================