blob: 1aa0db7bb9196cb16f7c69b975bec68084b48b5f [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 "xecontent.hxx"
#include <list>
#include <algorithm>
#include <com/sun/star/container/XIndexAccess.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/sheet/XAreaLinks.hpp>
#include <com/sun/star/sheet/XAreaLink.hpp>
#include <sfx2/objsh.hxx>
#include <tools/urlobj.hxx>
#include <svl/itemset.hxx>
#include <formula/grammar.hxx>
#include "scitems.hxx"
#include <editeng/eeitem.hxx>
#include <editeng/flditem.hxx>
#include "document.hxx"
#include "validat.hxx"
#include "unonames.hxx"
#include "convuno.hxx"
#include "rangenam.hxx"
#include "tokenarray.hxx"
#include "stlpool.hxx"
#include "patattr.hxx"
#include "fapihelper.hxx"
#include "xehelper.hxx"
#include "xestyle.hxx"
#include "xename.hxx"
using namespace ::oox;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::Any;
using ::com::sun::star::uno::UNO_QUERY;
using ::com::sun::star::beans::XPropertySet;
using ::com::sun::star::container::XIndexAccess;
using ::com::sun::star::frame::XModel;
using ::com::sun::star::table::CellRangeAddress;
using ::com::sun::star::sheet::XAreaLinks;
using ::com::sun::star::sheet::XAreaLink;
using ::rtl::OString;
using ::rtl::OUString;
using ::rtl::OUStringBuffer;
// Shared string table ========================================================
// 1 = SST hash table statistics prompt
#define EXC_INCL_SST_STATISTICS 0
// ----------------------------------------------------------------------------
/** A single string entry in the hash table. */
struct XclExpHashEntry
{
const XclExpString* mpString; /// Pointer to the string (no ownership).
sal_uInt32 mnSstIndex; /// The SST index of this string.
inline explicit XclExpHashEntry( const XclExpString* pString = 0, sal_uInt32 nSstIndex = 0 ) :
mpString( pString ), mnSstIndex( nSstIndex ) {}
};
/** Function object for strict weak ordering. */
struct XclExpHashEntrySWO
{
inline bool operator()( const XclExpHashEntry& rLeft, const XclExpHashEntry& rRight ) const
{ return *rLeft.mpString < *rRight.mpString; }
};
// ----------------------------------------------------------------------------
/** Implementation of the SST export.
@descr Stores all passed strings in a hash table and prevents repeated
insertion of equal strings. */
class XclExpSstImpl
{
public:
explicit XclExpSstImpl();
/** Inserts the passed string, if not already inserted, and returns the unique SST index. */
sal_uInt32 Insert( XclExpStringRef xString );
/** Writes the complete SST and EXTSST records. */
void Save( XclExpStream& rStrm );
void SaveXml( XclExpXmlStream& rStrm );
private:
typedef ::std::list< XclExpStringRef > XclExpStringList;
typedef ::std::vector< XclExpHashEntry > XclExpHashVec;
typedef ::std::vector< XclExpHashVec > XclExpHashTab;
XclExpStringList maStringList; /// List of unique strings (in SST ID order).
XclExpHashTab maHashTab; /// Hashed table that manages string pointers.
sal_uInt32 mnTotal; /// Total count of strings (including doubles).
sal_uInt32 mnSize; /// Size of the SST (count of unique strings).
};
// ----------------------------------------------------------------------------
const sal_uInt32 EXC_SST_HASHTABLE_SIZE = 2048;
XclExpSstImpl::XclExpSstImpl() :
maHashTab( EXC_SST_HASHTABLE_SIZE ),
mnTotal( 0 ),
mnSize( 0 )
{
}
sal_uInt32 XclExpSstImpl::Insert( XclExpStringRef xString )
{
DBG_ASSERT( xString.get(), "XclExpSstImpl::Insert - empty pointer not allowed" );
if( !xString.get() )
xString.reset( new XclExpString );
++mnTotal;
sal_uInt32 nSstIndex = 0;
// calculate hash value in range [0,EXC_SST_HASHTABLE_SIZE)
sal_uInt16 nHash = xString->GetHash();
(nHash ^= (nHash / EXC_SST_HASHTABLE_SIZE)) %= EXC_SST_HASHTABLE_SIZE;
XclExpHashVec& rVec = maHashTab[ nHash ];
XclExpHashEntry aEntry( xString.get(), mnSize );
XclExpHashVec::iterator aIt = ::std::lower_bound( rVec.begin(), rVec.end(), aEntry, XclExpHashEntrySWO() );
if( (aIt == rVec.end()) || (*aIt->mpString != *xString) )
{
nSstIndex = mnSize;
maStringList.push_back( xString );
rVec.insert( aIt, aEntry );
++mnSize;
}
else
{
nSstIndex = aIt->mnSstIndex;
}
return nSstIndex;
}
void XclExpSstImpl::Save( XclExpStream& rStrm )
{
if( maStringList.empty() )
return;
#if (OSL_DEBUG_LEVEL > 1) && EXC_INCL_SST_STATISTICS
{ // own scope for the statistics
#define APPENDINT( value ) Append( ByteString::CreateFromInt32( value ) )
ScfUInt32Vec aVec;
size_t nPerBucket = mnSize / EXC_SST_HASHTABLE_SIZE + 1, nEff = 0;
for( XclExpHashTab::const_iterator aTIt = maHashTab.begin(), aTEnd = maHashTab.end(); aTIt != aTEnd; ++aTIt )
{
size_t nSize = aTIt->size();
if( nSize >= aVec.size() ) aVec.resize( nSize + 1, 0 );
++aVec[ nSize ];
if( nSize > nPerBucket ) nEff += nSize - nPerBucket;
}
ByteString aStr( "SST HASHING STATISTICS\n\n" );
aStr.Append( "Total count:\t" ).APPENDINT( mnTotal ).Append( " strings\n" );
aStr.Append( "Reduced to:\t" ).APPENDINT( mnSize ).Append( " strings (" );
aStr.APPENDINT( 100 * mnSize / mnTotal ).Append( "%)\n" );
aStr.Append( "Effectivity:\t\t" ).APPENDINT( 100 - 100 * nEff / mnSize );
aStr.Append( "% (best: " ).APPENDINT( nPerBucket ).Append( " strings per bucket)\n" );
aStr.Append( "\t\tCount of buckets\nBucket size\ttotal\tmax\tTotal strings\n" );
for( size_t nIx = 0, nSize = aVec.size(), nInc = 1; nIx < nSize; nIx += nInc )
{
if( (nIx == 10) || (nIx == 100) || (nIx == 1000) ) nInc = nIx;
size_t nMaxIx = ::std::min( nIx + nInc, nSize ), nCount = 0, nMaxCount = 0, nStrings = 0;
for( size_t nSubIx = nIx; nSubIx < nMaxIx; ++nSubIx )
{
nCount += aVec[ nSubIx ];
if( aVec[ nSubIx ] > nMaxCount ) nMaxCount = aVec[ nSubIx ];
nStrings += nSubIx * aVec[ nSubIx ];
}
if( nMaxCount )
{
aStr.APPENDINT( nIx );
if( nMaxIx - nIx > 1 ) aStr.Append( '-' ).APPENDINT( nMaxIx - 1 );
aStr.Append( "\t\t" ).APPENDINT( nCount ).Append( '\t' ).APPENDINT( nMaxCount );
aStr.Append( '\t' ).APPENDINT( nStrings ).Append( '\n' );
}
}
DBG_ERRORFILE( aStr.GetBuffer() );
#undef APPENDINT
}
#endif
SvMemoryStream aExtSst( 8192 );
sal_uInt32 nBucket = mnSize;
while( nBucket > 0x0100 )
nBucket /= 2;
sal_uInt16 nPerBucket = llimit_cast< sal_uInt16 >( nBucket, 8 );
sal_uInt16 nBucketIndex = 0;
// *** write the SST record ***
rStrm.StartRecord( EXC_ID_SST, 8 );
rStrm << mnTotal << mnSize;
for( XclExpStringList::const_iterator aIt = maStringList.begin(), aEnd = maStringList.end(); aIt != aEnd; ++aIt )
{
if( !nBucketIndex )
{
// write bucket info before string to get correct record position
sal_uInt32 nStrmPos = static_cast< sal_uInt32 >( rStrm.GetSvStreamPos() );
sal_uInt16 nRecPos = rStrm.GetRawRecPos() + 4;
aExtSst << nStrmPos // stream position
<< nRecPos // position from start of SST or CONTINUE
<< sal_uInt16( 0 ); // reserved
}
rStrm << **aIt;
if( ++nBucketIndex == nPerBucket )
nBucketIndex = 0;
}
rStrm.EndRecord();
// *** write the EXTSST record ***
rStrm.StartRecord( EXC_ID_EXTSST, 0 );
rStrm << nPerBucket;
rStrm.SetSliceSize( 8 ); // size of one bucket info
aExtSst.Seek( STREAM_SEEK_TO_BEGIN );
rStrm.CopyFromStream( aExtSst );
rStrm.EndRecord();
}
void XclExpSstImpl::SaveXml( XclExpXmlStream& rStrm )
{
if( maStringList.empty() )
return;
sax_fastparser::FSHelperPtr pSst = rStrm.CreateOutputStream(
OUString::createFromAscii( "xl/sharedStrings.xml" ),
OUString::createFromAscii( "sharedStrings.xml" ),
rStrm.GetCurrentStream()->getOutputStream(),
"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" );
rStrm.PushStream( pSst );
pSst->startElement( XML_sst,
XML_xmlns, "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
XML_count, OString::valueOf( (sal_Int32) mnTotal ).getStr(),
XML_uniqueCount, OString::valueOf( (sal_Int32) mnSize ).getStr(),
FSEND );
for( XclExpStringList::const_iterator aIt = maStringList.begin(), aEnd = maStringList.end(); aIt != aEnd; ++aIt )
{
pSst->startElement( XML_si, FSEND );
(*aIt)->WriteXml( rStrm );
pSst->endElement( XML_si );
}
pSst->endElement( XML_sst );
rStrm.PopStream();
}
// ----------------------------------------------------------------------------
XclExpSst::XclExpSst() :
mxImpl( new XclExpSstImpl )
{
}
XclExpSst::~XclExpSst()
{
}
sal_uInt32 XclExpSst::Insert( XclExpStringRef xString )
{
return mxImpl->Insert( xString );
}
void XclExpSst::Save( XclExpStream& rStrm )
{
mxImpl->Save( rStrm );
}
void XclExpSst::SaveXml( XclExpXmlStream& rStrm )
{
mxImpl->SaveXml( rStrm );
}
// Merged cells ===============================================================
XclExpMergedcells::XclExpMergedcells( const XclExpRoot& rRoot ) :
XclExpRoot( rRoot )
{
}
void XclExpMergedcells::AppendRange( const ScRange& rRange, sal_uInt32 nBaseXFId )
{
if( GetBiff() == EXC_BIFF8 )
{
maMergedRanges.Append( rRange );
maBaseXFIds.push_back( nBaseXFId );
}
}
sal_uInt32 XclExpMergedcells::GetBaseXFId( const ScAddress& rPos ) const
{
DBG_ASSERT( maBaseXFIds.size() == maMergedRanges.Count(), "XclExpMergedcells::GetBaseXFId - invalid lists" );
ScfUInt32Vec::const_iterator aIt = maBaseXFIds.begin();
ScRangeList& rNCRanges = const_cast< ScRangeList& >( maMergedRanges );
for( const ScRange* pScRange = rNCRanges.First(); pScRange; pScRange = rNCRanges.Next(), ++aIt )
if( pScRange->In( rPos ) )
return *aIt;
return EXC_XFID_NOTFOUND;
}
void XclExpMergedcells::Save( XclExpStream& rStrm )
{
if( GetBiff() == EXC_BIFF8 )
{
XclRangeList aXclRanges;
GetAddressConverter().ConvertRangeList( aXclRanges, maMergedRanges, true );
size_t nFirstRange = 0;
size_t nRemainingRanges = aXclRanges.size();
while( nRemainingRanges > 0 )
{
size_t nRangeCount = ::std::min< size_t >( nRemainingRanges, EXC_MERGEDCELLS_MAXCOUNT );
rStrm.StartRecord( EXC_ID_MERGEDCELLS, 2 + 8 * nRangeCount );
aXclRanges.WriteSubList( rStrm, nFirstRange, nRangeCount );
rStrm.EndRecord();
nFirstRange += nRangeCount;
nRemainingRanges -= nRangeCount;
}
}
}
void XclExpMergedcells::SaveXml( XclExpXmlStream& rStrm )
{
sal_uLong nCount = maMergedRanges.Count();
if( !nCount )
return;
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
rWorksheet->startElement( XML_mergeCells,
XML_count, OString::valueOf( (sal_Int32) nCount ).getStr(),
FSEND );
for( sal_uLong i = 0; i < nCount; ++i )
{
if( const ScRange* pRange = maMergedRanges.GetObject( i ) )
{
rWorksheet->singleElement( XML_mergeCell,
XML_ref, XclXmlUtils::ToOString( *pRange ).getStr(),
FSEND );
}
}
rWorksheet->endElement( XML_mergeCells );
}
// Hyperlinks =================================================================
XclExpHyperlink::XclExpHyperlink( const XclExpRoot& rRoot, const SvxURLField& rUrlField, const ScAddress& rScPos ) :
XclExpRecord( EXC_ID_HLINK ),
maScPos( rScPos ),
mxVarData( new SvMemoryStream ),
mnFlags( 0 )
{
const String& rUrl = rUrlField.GetURL();
const String& rRepr = rUrlField.GetRepresentation();
INetURLObject aUrlObj( rUrl );
const INetProtocol eProtocol = aUrlObj.GetProtocol();
bool bWithRepr = rRepr.Len() > 0;
XclExpStream aXclStrm( *mxVarData, rRoot ); // using in raw write mode.
// description
if( bWithRepr )
{
XclExpString aDescr( rRepr, EXC_STR_FORCEUNICODE, 255 );
aXclStrm << sal_uInt32( aDescr.Len() + 1 ); // string length + 1 trailing zero word
aDescr.WriteBuffer( aXclStrm ); // NO flags
aXclStrm << sal_uInt16( 0 );
mnFlags |= EXC_HLINK_DESCR;
mxRepr.reset( new String( rRepr ) );
}
// file link or URL
if( eProtocol == INET_PROT_FILE )
{
sal_uInt16 nLevel;
bool bRel;
String aFileName( BuildFileName( nLevel, bRel, rUrl, rRoot ) );
if( !bRel )
mnFlags |= EXC_HLINK_ABS;
mnFlags |= EXC_HLINK_BODY;
ByteString aAsciiLink( aFileName, rRoot.GetTextEncoding() );
XclExpString aLink( aFileName, EXC_STR_FORCEUNICODE, 255 );
aXclStrm << XclTools::maGuidFileMoniker
<< nLevel
<< sal_uInt32( aAsciiLink.Len() + 1 ); // string length + 1 trailing zero byte
aXclStrm.Write( aAsciiLink.GetBuffer(), aAsciiLink.Len() );
aXclStrm << sal_uInt8( 0 )
<< sal_uInt32( 0xDEADFFFF );
aXclStrm.WriteZeroBytes( 20 );
aXclStrm << sal_uInt32( aLink.GetBufferSize() + 6 )
<< sal_uInt32( aLink.GetBufferSize() ) // byte count, not string length
<< sal_uInt16( 0x0003 );
aLink.WriteBuffer( aXclStrm ); // NO flags
if( !mxRepr.get() )
mxRepr.reset( new String( aFileName ) );
msTarget = XclXmlUtils::ToOUString( aLink );
}
else if( eProtocol != INET_PROT_NOT_VALID )
{
XclExpString aUrl( aUrlObj.GetURLNoMark(), EXC_STR_FORCEUNICODE, 255 );
aXclStrm << XclTools::maGuidUrlMoniker
<< sal_uInt32( aUrl.GetBufferSize() + 2 ); // byte count + 1 trailing zero word
aUrl.WriteBuffer( aXclStrm ); // NO flags
aXclStrm << sal_uInt16( 0 );
mnFlags |= EXC_HLINK_BODY | EXC_HLINK_ABS;
if( !mxRepr.get() )
mxRepr.reset( new String( rUrl ) );
msTarget = XclXmlUtils::ToOUString( aUrl );
}
else if( rUrl.GetChar( 0 ) == '#' ) // hack for #89066#
{
String aTextMark( rUrl.Copy( 1 ) );
aTextMark.SearchAndReplace( '.', '!' );
mxTextMark.reset( new XclExpString( aTextMark, EXC_STR_FORCEUNICODE, 255 ) );
}
// text mark
if( !mxTextMark.get() && aUrlObj.HasMark() )
mxTextMark.reset( new XclExpString( aUrlObj.GetMark(), EXC_STR_FORCEUNICODE, 255 ) );
if( mxTextMark.get() )
{
aXclStrm << sal_uInt32( mxTextMark->Len() + 1 ); // string length + 1 trailing zero word
mxTextMark->WriteBuffer( aXclStrm ); // NO flags
aXclStrm << sal_uInt16( 0 );
mnFlags |= EXC_HLINK_MARK;
}
SetRecSize( 32 + mxVarData->Tell() );
}
XclExpHyperlink::~XclExpHyperlink()
{
}
String XclExpHyperlink::BuildFileName(
sal_uInt16& rnLevel, bool& rbRel, const String& rUrl, const XclExpRoot& rRoot ) const
{
String aDosName( INetURLObject( rUrl ).getFSysPath( INetURLObject::FSYS_DOS ) );
rnLevel = 0;
rbRel = rRoot.IsRelUrl();
if( rbRel )
{
// try to convert to relative file name
String aTmpName( aDosName );
aDosName = INetURLObject::GetRelURL( rRoot.GetBasePath(), rUrl,
INetURLObject::WAS_ENCODED, INetURLObject::DECODE_WITH_CHARSET );
if( aDosName.SearchAscii( INET_FILE_SCHEME ) == 0 )
{
// not converted to rel -> back to old, return absolute flag
aDosName = aTmpName;
rbRel = false;
}
else if( aDosName.SearchAscii( "./" ) == 0 )
{
aDosName.Erase( 0, 2 );
}
else
{
while( aDosName.SearchAndReplaceAscii( "../", EMPTY_STRING ) == 0 )
++rnLevel;
}
}
return aDosName;
}
void XclExpHyperlink::WriteBody( XclExpStream& rStrm )
{
sal_uInt16 nXclCol = static_cast< sal_uInt16 >( maScPos.Col() );
sal_uInt16 nXclRow = static_cast< sal_uInt16 >( maScPos.Row() );
mxVarData->Seek( STREAM_SEEK_TO_BEGIN );
rStrm << nXclRow << nXclRow << nXclCol << nXclCol
<< XclTools::maGuidStdLink
<< sal_uInt32( 2 )
<< mnFlags;
rStrm.CopyFromStream( *mxVarData );
}
void XclExpHyperlink::SaveXml( XclExpXmlStream& rStrm )
{
OUString sId = rStrm.addRelation( rStrm.GetCurrentStream()->getOutputStream(),
XclXmlUtils::ToOUString( "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" ),
msTarget,
true );
rStrm.GetCurrentStream()->singleElement( XML_hyperlink,
XML_ref, XclXmlUtils::ToOString( maScPos ).getStr(),
FSNS( XML_r, XML_id ), XclXmlUtils::ToOString( sId ).getStr(),
XML_location, mxTextMark.get() != NULL
? XclXmlUtils::ToOString( *mxTextMark ).getStr()
: NULL,
// OOXTODO: XML_tooltip, from record HLinkTooltip 800h wzTooltip
XML_display, XclXmlUtils::ToOString( *mxRepr ).getStr(),
FSEND );
}
// Label ranges ===============================================================
XclExpLabelranges::XclExpLabelranges( const XclExpRoot& rRoot ) :
XclExpRoot( rRoot )
{
SCTAB nScTab = GetCurrScTab();
// row label ranges
FillRangeList( maRowRanges, rRoot.GetDoc().GetRowNameRangesRef(), nScTab );
// row labels only over 1 column (restriction of Excel97/2000/XP)
for( ScRange* pScRange = maRowRanges.First(); pScRange; pScRange = maRowRanges.Next() )
if( pScRange->aStart.Col() != pScRange->aEnd.Col() )
pScRange->aEnd.SetCol( pScRange->aStart.Col() );
// col label ranges
FillRangeList( maColRanges, rRoot.GetDoc().GetColNameRangesRef(), nScTab );
}
void XclExpLabelranges::FillRangeList( ScRangeList& rScRanges,
ScRangePairListRef xLabelRangesRef, SCTAB nScTab )
{
for( const ScRangePair* pRangePair = xLabelRangesRef->First(); pRangePair; pRangePair = xLabelRangesRef->Next() )
{
const ScRange& rScRange = pRangePair->GetRange( 0 );
if( rScRange.aStart.Tab() == nScTab )
rScRanges.Append( rScRange );
}
}
void XclExpLabelranges::Save( XclExpStream& rStrm )
{
XclExpAddressConverter& rAddrConv = GetAddressConverter();
XclRangeList aRowXclRanges, aColXclRanges;
rAddrConv.ConvertRangeList( aRowXclRanges, maRowRanges, false );
rAddrConv.ConvertRangeList( aColXclRanges, maColRanges, false );
if( !aRowXclRanges.empty() || !aColXclRanges.empty() )
{
rStrm.StartRecord( EXC_ID_LABELRANGES, 4 + 8 * (aRowXclRanges.size() + aColXclRanges.size()) );
rStrm << aRowXclRanges << aColXclRanges;
rStrm.EndRecord();
}
}
// Conditional formatting ====================================================
/** Represents a CF record that contains one condition of a conditional format. */
class XclExpCFImpl : protected XclExpRoot
{
public:
explicit XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry );
/** Writes the body of the CF record. */
void WriteBody( XclExpStream& rStrm );
private:
const ScCondFormatEntry& mrFormatEntry; /// Calc conditional format entry.
XclFontData maFontData; /// Font formatting attributes.
XclExpCellBorder maBorder; /// Border formatting attributes.
XclExpCellArea maArea; /// Pattern formatting attributes.
XclTokenArrayRef mxTokArr1; /// Formula for first condition.
XclTokenArrayRef mxTokArr2; /// Formula for second condition.
sal_uInt32 mnFontColorId; /// Font color ID.
sal_uInt8 mnType; /// Type of the condition (cell/formula).
sal_uInt8 mnOperator; /// Comparison operator for cell type.
bool mbFontUsed; /// true = Any font attribute used.
bool mbHeightUsed; /// true = Font height used.
bool mbWeightUsed; /// true = Font weight used.
bool mbColorUsed; /// true = Font color used.
bool mbUnderlUsed; /// true = Font underline type used.
bool mbItalicUsed; /// true = Font posture used.
bool mbStrikeUsed; /// true = Font strikeout used.
bool mbBorderUsed; /// true = Border attribute used.
bool mbPattUsed; /// true = Pattern attribute used.
};
// ----------------------------------------------------------------------------
XclExpCFImpl::XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry ) :
XclExpRoot( rRoot ),
mrFormatEntry( rFormatEntry ),
mnFontColorId( 0 ),
mnType( EXC_CF_TYPE_CELL ),
mnOperator( EXC_CF_CMP_NONE ),
mbFontUsed( false ),
mbHeightUsed( false ),
mbWeightUsed( false ),
mbColorUsed( false ),
mbUnderlUsed( false ),
mbItalicUsed( false ),
mbStrikeUsed( false ),
mbBorderUsed( false ),
mbPattUsed( false )
{
/* Get formatting attributes here, and not in WriteBody(). This is needed to
correctly insert all colors into the palette. */
if( SfxStyleSheetBase* pStyleSheet = GetDoc().GetStyleSheetPool()->Find( mrFormatEntry.GetStyle(), SFX_STYLE_FAMILY_PARA ) )
{
const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
// font
mbHeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_HEIGHT, true );
mbWeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_WEIGHT, true );
mbColorUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_COLOR, true );
mbUnderlUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_UNDERLINE, true );
mbItalicUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_POSTURE, true );
mbStrikeUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_CROSSEDOUT, true );
mbFontUsed = mbHeightUsed || mbWeightUsed || mbColorUsed || mbUnderlUsed || mbItalicUsed || mbStrikeUsed;
if( mbFontUsed )
{
Font aFont;
ScPatternAttr::GetFont( aFont, rItemSet, SC_AUTOCOL_RAW );
maFontData.FillFromVclFont( aFont );
mnFontColorId = GetPalette().InsertColor( maFontData.maColor, EXC_COLOR_CELLTEXT );
}
// border
mbBorderUsed = ScfTools::CheckItem( rItemSet, ATTR_BORDER, true );
if( mbBorderUsed )
maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff() );
// pattern
mbPattUsed = ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true );
if( mbPattUsed )
maArea.FillFromItemSet( rItemSet, GetPalette(), GetBiff() );
}
// *** mode and comparison operator ***
bool bFmla2 = false;
switch( rFormatEntry.GetOperation() )
{
case SC_COND_NONE: mnType = EXC_CF_TYPE_NONE; break;
case SC_COND_BETWEEN: mnOperator = EXC_CF_CMP_BETWEEN; bFmla2 = true; break;
case SC_COND_NOTBETWEEN: mnOperator = EXC_CF_CMP_NOT_BETWEEN; bFmla2 = true; break;
case SC_COND_EQUAL: mnOperator = EXC_CF_CMP_EQUAL; break;
case SC_COND_NOTEQUAL: mnOperator = EXC_CF_CMP_NOT_EQUAL; break;
case SC_COND_GREATER: mnOperator = EXC_CF_CMP_GREATER; break;
case SC_COND_LESS: mnOperator = EXC_CF_CMP_LESS; break;
case SC_COND_EQGREATER: mnOperator = EXC_CF_CMP_GREATER_EQUAL; break;
case SC_COND_EQLESS: mnOperator = EXC_CF_CMP_LESS_EQUAL; break;
case SC_COND_DIRECT: mnType = EXC_CF_TYPE_FMLA; break;
default: mnType = EXC_CF_TYPE_NONE;
DBG_ERRORFILE( "XclExpCF::WriteBody - unknown condition type" );
}
// *** formulas ***
XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
::std::auto_ptr< ScTokenArray > xScTokArr( mrFormatEntry.CreateTokenArry( 0 ) );
mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
if( bFmla2 )
{
xScTokArr.reset( mrFormatEntry.CreateTokenArry( 1 ) );
mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
}
}
void XclExpCFImpl::WriteBody( XclExpStream& rStrm )
{
// *** mode and comparison operator ***
rStrm << mnType << mnOperator;
// *** formula sizes ***
sal_uInt16 nFmlaSize1 = mxTokArr1.get() ? mxTokArr1->GetSize() : 0;
sal_uInt16 nFmlaSize2 = mxTokArr2.get() ? mxTokArr2->GetSize() : 0;
rStrm << nFmlaSize1 << nFmlaSize2;
// *** formatting blocks ***
if( mbFontUsed || mbBorderUsed || mbPattUsed )
{
sal_uInt32 nFlags = EXC_CF_ALLDEFAULT;
::set_flag( nFlags, EXC_CF_BLOCK_FONT, mbFontUsed );
::set_flag( nFlags, EXC_CF_BLOCK_BORDER, mbBorderUsed );
::set_flag( nFlags, EXC_CF_BLOCK_AREA, mbPattUsed );
// attributes used -> set flags to 0.
::set_flag( nFlags, EXC_CF_BORDER_ALL, !mbBorderUsed );
::set_flag( nFlags, EXC_CF_AREA_ALL, !mbPattUsed );
rStrm << nFlags << sal_uInt16( 0 );
if( mbFontUsed )
{
// font height, 0xFFFFFFFF indicates unused
sal_uInt32 nHeight = mbHeightUsed ? maFontData.mnHeight : 0xFFFFFFFF;
// font style: italic and strikeout
sal_uInt32 nStyle = 0;
::set_flag( nStyle, EXC_CF_FONT_STYLE, maFontData.mbItalic );
::set_flag( nStyle, EXC_CF_FONT_STRIKEOUT, maFontData.mbStrikeout );
// font color, 0xFFFFFFFF indicates unused
sal_uInt32 nColor = mbColorUsed ? GetPalette().GetColorIndex( mnFontColorId ) : 0xFFFFFFFF;
// font used flags for italic, weight, and strikeout -> 0 = used, 1 = default
sal_uInt32 nFontFlags1 = EXC_CF_FONT_ALLDEFAULT;
::set_flag( nFontFlags1, EXC_CF_FONT_STYLE, !(mbItalicUsed || mbWeightUsed) );
::set_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT, !mbStrikeUsed );
// font used flag for underline -> 0 = used, 1 = default
sal_uInt32 nFontFlags3 = mbUnderlUsed ? 0 : EXC_CF_FONT_UNDERL;
rStrm.WriteZeroBytesToRecord( 64 );
rStrm << nHeight
<< nStyle
<< maFontData.mnWeight
<< EXC_FONTESC_NONE
<< maFontData.mnUnderline;
rStrm.WriteZeroBytesToRecord( 3 );
rStrm << nColor
<< sal_uInt32( 0 )
<< nFontFlags1
<< EXC_CF_FONT_ESCAPEM // escapement never used -> set the flag
<< nFontFlags3;
rStrm.WriteZeroBytesToRecord( 16 );
rStrm << sal_uInt16( 1 ); // must be 1
}
if( mbBorderUsed )
{
sal_uInt16 nLineStyle = 0;
sal_uInt32 nLineColor = 0;
maBorder.SetFinalColors( GetPalette() );
maBorder.FillToCF8( nLineStyle, nLineColor );
rStrm << nLineStyle << nLineColor << sal_uInt16( 0 );
}
if( mbPattUsed )
{
sal_uInt16 nPattern = 0, nColor = 0;
maArea.SetFinalColors( GetPalette() );
maArea.FillToCF8( nPattern, nColor );
rStrm << nPattern << nColor;
}
}
else
{
// no data blocks at all
rStrm << sal_uInt32( 0 ) << sal_uInt16( 0 );
}
// *** formulas ***
if( mxTokArr1.get() )
mxTokArr1->WriteArray( rStrm );
if( mxTokArr2.get() )
mxTokArr2->WriteArray( rStrm );
}
// ----------------------------------------------------------------------------
XclExpCF::XclExpCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry ) :
XclExpRecord( EXC_ID_CF ),
XclExpRoot( rRoot ),
mxImpl( new XclExpCFImpl( rRoot, rFormatEntry ) )
{
}
XclExpCF::~XclExpCF()
{
}
void XclExpCF::WriteBody( XclExpStream& rStrm )
{
mxImpl->WriteBody( rStrm );
}
// ----------------------------------------------------------------------------
XclExpCondfmt::XclExpCondfmt( const XclExpRoot& rRoot, const ScConditionalFormat& rCondFormat ) :
XclExpRecord( EXC_ID_CONDFMT ),
XclExpRoot( rRoot )
{
ScRangeList aScRanges;
GetDoc().FindConditionalFormat( rCondFormat.GetKey(), aScRanges, GetCurrScTab() );
GetAddressConverter().ConvertRangeList( maXclRanges, aScRanges, true );
if( !maXclRanges.empty() )
{
for( sal_uInt16 nIndex = 0, nCount = rCondFormat.Count(); nIndex < nCount; ++nIndex )
if( const ScCondFormatEntry* pEntry = rCondFormat.GetEntry( nIndex ) )
maCFList.AppendNewRecord( new XclExpCF( GetRoot(), *pEntry ) );
aScRanges.Format( msSeqRef, SCA_VALID, NULL, formula::FormulaGrammar::CONV_XL_A1 );
}
}
XclExpCondfmt::~XclExpCondfmt()
{
}
bool XclExpCondfmt::IsValid() const
{
return !maCFList.IsEmpty() && !maXclRanges.empty();
}
void XclExpCondfmt::Save( XclExpStream& rStrm )
{
if( IsValid() )
{
XclExpRecord::Save( rStrm );
maCFList.Save( rStrm );
}
}
void XclExpCondfmt::WriteBody( XclExpStream& rStrm )
{
DBG_ASSERT( !maCFList.IsEmpty(), "XclExpCondfmt::WriteBody - no CF records to write" );
DBG_ASSERT( !maXclRanges.empty(), "XclExpCondfmt::WriteBody - no cell ranges found" );
rStrm << static_cast< sal_uInt16 >( maCFList.GetSize() )
<< sal_uInt16( 1 )
<< maXclRanges.GetEnclosingRange()
<< maXclRanges;
}
void XclExpCondfmt::SaveXml( XclExpXmlStream& rStrm )
{
if( !IsValid() )
return;
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
rWorksheet->startElement( XML_conditionalFormatting,
XML_sqref, XclXmlUtils::ToOString( msSeqRef ).getStr(),
// OOXTODO: XML_pivot,
FSEND );
maCFList.SaveXml( rStrm );
// OOXTODO: XML_extLst
rWorksheet->endElement( XML_conditionalFormatting );
}
// ----------------------------------------------------------------------------
XclExpCondFormatBuffer::XclExpCondFormatBuffer( const XclExpRoot& rRoot ) :
XclExpRoot( rRoot )
{
if( const ScConditionalFormatList* pCondFmtList = GetDoc().GetCondFormList() )
{
if( const ScConditionalFormatPtr* ppCondFmt = pCondFmtList->GetData() )
{
const ScConditionalFormatPtr* ppCondEnd = ppCondFmt + pCondFmtList->Count();
for( ; ppCondFmt < ppCondEnd; ++ppCondFmt )
{
if( *ppCondFmt )
{
XclExpCondfmtList::RecordRefType xCondfmtRec( new XclExpCondfmt( GetRoot(), **ppCondFmt ) );
if( xCondfmtRec->IsValid() )
maCondfmtList.AppendRecord( xCondfmtRec );
}
}
}
}
}
void XclExpCondFormatBuffer::Save( XclExpStream& rStrm )
{
maCondfmtList.Save( rStrm );
}
void XclExpCondFormatBuffer::SaveXml( XclExpXmlStream& rStrm )
{
maCondfmtList.SaveXml( rStrm );
}
// Validation =================================================================
namespace {
/** Writes a formula for the DV record. */
void lclWriteDvFormula( XclExpStream& rStrm, const XclTokenArray* pXclTokArr )
{
sal_uInt16 nFmlaSize = pXclTokArr ? pXclTokArr->GetSize() : 0;
rStrm << nFmlaSize << sal_uInt16( 0 );
if( pXclTokArr )
pXclTokArr->WriteArray( rStrm );
}
/** Writes a formula for the DV record, based on a single string. */
void lclWriteDvFormula( XclExpStream& rStrm, const XclExpString& rString )
{
// fake a formula with a single tStr token
rStrm << static_cast< sal_uInt16 >( rString.GetSize() + 1 )
<< sal_uInt16( 0 )
<< EXC_TOKID_STR
<< rString;
}
const char* lcl_GetValidationType( sal_uInt32 nFlags )
{
switch( nFlags & EXC_DV_MODE_MASK )
{
case EXC_DV_MODE_ANY: return "none";
case EXC_DV_MODE_WHOLE: return "whole";
case EXC_DV_MODE_DECIMAL: return "decimal";
case EXC_DV_MODE_LIST: return "list";
case EXC_DV_MODE_DATE: return "date";
case EXC_DV_MODE_TIME: return "time";
case EXC_DV_MODE_TEXTLEN: return "textLength";
case EXC_DV_MODE_CUSTOM: return "custom";
}
return NULL;
}
const char* lcl_GetOperatorType( sal_uInt32 nFlags )
{
switch( nFlags & EXC_DV_COND_MASK )
{
case EXC_DV_COND_BETWEEN: return "between";
case EXC_DV_COND_NOTBETWEEN: return "notBetween";
case EXC_DV_COND_EQUAL: return "equal";
case EXC_DV_COND_NOTEQUAL: return "notEqual";
case EXC_DV_COND_GREATER: return "greaterThan";
case EXC_DV_COND_LESS: return "lessThan";
case EXC_DV_COND_EQGREATER: return "greaterThanOrEqual";
case EXC_DV_COND_EQLESS: return "lessThanOrEqual";
}
return NULL;
}
} // namespace
// ----------------------------------------------------------------------------
XclExpDV::XclExpDV( const XclExpRoot& rRoot, sal_uLong nScHandle ) :
XclExpRecord( EXC_ID_DV ),
XclExpRoot( rRoot ),
mnFlags( 0 ),
mnScHandle( nScHandle )
{
if( const ScValidationData* pValData = GetDoc().GetValidationEntry( mnScHandle ) )
{
// prompt box - empty string represented by single NUL character
String aTitle, aText;
bool bShowPrompt = (pValData->GetInput( aTitle, aText ) == sal_True);
if( aTitle.Len() )
maPromptTitle.Assign( aTitle );
else
maPromptTitle.Assign( '\0' );
if( aText.Len() )
maPromptText.Assign( aText );
else
maPromptText.Assign( '\0' );
// error box - empty string represented by single NUL character
ScValidErrorStyle eScErrorStyle;
bool bShowError = (pValData->GetErrMsg( aTitle, aText, eScErrorStyle ) == sal_True);
if( aTitle.Len() )
maErrorTitle.Assign( aTitle );
else
maErrorTitle.Assign( '\0' );
if( aText.Len() )
maErrorText.Assign( aText );
else
maErrorText.Assign( '\0' );
// flags
switch( pValData->GetDataMode() )
{
case SC_VALID_ANY: mnFlags |= EXC_DV_MODE_ANY; break;
case SC_VALID_WHOLE: mnFlags |= EXC_DV_MODE_WHOLE; break;
case SC_VALID_DECIMAL: mnFlags |= EXC_DV_MODE_DECIMAL; break;
case SC_VALID_LIST: mnFlags |= EXC_DV_MODE_LIST; break;
case SC_VALID_DATE: mnFlags |= EXC_DV_MODE_DATE; break;
case SC_VALID_TIME: mnFlags |= EXC_DV_MODE_TIME; break;
case SC_VALID_TEXTLEN: mnFlags |= EXC_DV_MODE_TEXTLEN; break;
case SC_VALID_CUSTOM: mnFlags |= EXC_DV_MODE_CUSTOM; break;
default: DBG_ERRORFILE( "XclExpDV::XclExpDV - unknown mode" );
}
switch( pValData->GetOperation() )
{
case SC_COND_NONE:
case SC_COND_EQUAL: mnFlags |= EXC_DV_COND_EQUAL; break;
case SC_COND_LESS: mnFlags |= EXC_DV_COND_LESS; break;
case SC_COND_GREATER: mnFlags |= EXC_DV_COND_GREATER; break;
case SC_COND_EQLESS: mnFlags |= EXC_DV_COND_EQLESS; break;
case SC_COND_EQGREATER: mnFlags |= EXC_DV_COND_EQGREATER; break;
case SC_COND_NOTEQUAL: mnFlags |= EXC_DV_COND_NOTEQUAL; break;
case SC_COND_BETWEEN: mnFlags |= EXC_DV_COND_BETWEEN; break;
case SC_COND_NOTBETWEEN: mnFlags |= EXC_DV_COND_NOTBETWEEN; break;
default: DBG_ERRORFILE( "XclExpDV::XclExpDV - unknown condition" );
}
switch( eScErrorStyle )
{
case SC_VALERR_STOP: mnFlags |= EXC_DV_ERROR_STOP; break;
case SC_VALERR_WARNING: mnFlags |= EXC_DV_ERROR_WARNING; break;
case SC_VALERR_INFO: mnFlags |= EXC_DV_ERROR_INFO; break;
case SC_VALERR_MACRO:
// #111781# set INFO for validity with macro call, delete title
mnFlags |= EXC_DV_ERROR_INFO;
maErrorTitle.Assign( '\0' ); // contains macro name
break;
default: DBG_ERRORFILE( "XclExpDV::XclExpDV - unknown error style" );
}
::set_flag( mnFlags, EXC_DV_IGNOREBLANK, pValData->IsIgnoreBlank() );
::set_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN, pValData->GetListType() == ValidListType::INVISIBLE );
::set_flag( mnFlags, EXC_DV_SHOWPROMPT, bShowPrompt );
::set_flag( mnFlags, EXC_DV_SHOWERROR, bShowError );
// formulas
XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
::std::auto_ptr< ScTokenArray > xScTokArr;
// first formula
xScTokArr.reset( pValData->CreateTokenArry( 0 ) );
if( xScTokArr.get() )
{
if( pValData->GetDataMode() == SC_VALID_LIST )
{
String aString;
if( XclTokenArrayHelper::GetStringList( aString, *xScTokArr, '\n' ) )
{
OUStringBuffer sFormulaBuf;
sFormulaBuf.append( (sal_Unicode) '"' );
/* Formula is a list of string tokens -> build the Excel string.
Data validity is BIFF8 only (important for the XclExpString object).
Excel uses the NUL character as string list separator. */
mxString1.reset( new XclExpString( EXC_STR_8BITLENGTH ) );
xub_StrLen nTokenCnt = aString.GetTokenCount( '\n' );
xub_StrLen nStringIx = 0;
for( xub_StrLen nToken = 0; nToken < nTokenCnt; ++nToken )
{
String aToken( aString.GetToken( 0, '\n', nStringIx ) );
if( nToken > 0 )
{
mxString1->Append( '\0' );
sFormulaBuf.append( (sal_Unicode) ',' );
}
mxString1->Append( aToken );
sFormulaBuf.append( XclXmlUtils::ToOUString( aToken ) );
}
::set_flag( mnFlags, EXC_DV_STRINGLIST );
sFormulaBuf.append( (sal_Unicode) '"' );
msFormula1 = sFormulaBuf.makeStringAndClear();
}
else
{
/* All other formulas in validation are stored like conditional
formatting formulas (with tRefN/tAreaN tokens as value or
array class). But NOT the cell references and defined names
in list validation - they are stored as reference class
tokens... Example:
1) Cell must be equal to A1 -> formula is =A1 -> writes tRefNV token
2) List is taken from A1 -> formula is =A1 -> writes tRefNR token
Formula compiler supports this by offering two different functions
CreateDataValFormula() and CreateListValFormula(). */
mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_LISTVAL, *xScTokArr );
msFormula1 = XclXmlUtils::ToOUString( GetDoc(), pValData->GetSrcPos(), xScTokArr.get() );
}
}
else
{
// no list validation -> convert the formula
mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
msFormula1 = XclXmlUtils::ToOUString( GetDoc(), pValData->GetSrcPos(), xScTokArr.get() );
}
}
// second formula
xScTokArr.reset( pValData->CreateTokenArry( 1 ) );
if( xScTokArr.get() )
{
mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
msFormula2 = XclXmlUtils::ToOUString( GetDoc(), pValData->GetSrcPos(), xScTokArr.get() );
}
}
else
{
DBG_ERRORFILE( "XclExpDV::XclExpDV - missing core data" );
mnScHandle = ULONG_MAX;
}
}
XclExpDV::~XclExpDV()
{
}
void XclExpDV::InsertCellRange( const ScRange& rRange )
{
maScRanges.Join( rRange );
}
bool XclExpDV::Finalize()
{
GetAddressConverter().ConvertRangeList( maXclRanges, maScRanges, true );
return (mnScHandle != ULONG_MAX) && !maXclRanges.empty();
}
void XclExpDV::WriteBody( XclExpStream& rStrm )
{
// flags and strings
rStrm << mnFlags << maPromptTitle << maErrorTitle << maPromptText << maErrorText;
// condition formulas
if( mxString1.get() )
lclWriteDvFormula( rStrm, *mxString1 );
else
lclWriteDvFormula( rStrm, mxTokArr1.get() );
lclWriteDvFormula( rStrm, mxTokArr2.get() );
// cell ranges
rStrm << maXclRanges;
}
void XclExpDV::SaveXml( XclExpXmlStream& rStrm )
{
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
rWorksheet->startElement( XML_dataValidation,
XML_allowBlank, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_DV_IGNOREBLANK ) ),
XML_error, XESTRING_TO_PSZ( maErrorText ),
// OOXTODO: XML_errorStyle,
XML_errorTitle, XESTRING_TO_PSZ( maErrorTitle ),
// OOXTODO: XML_imeMode,
XML_operator, lcl_GetOperatorType( mnFlags ),
XML_prompt, XESTRING_TO_PSZ( maPromptText ),
XML_promptTitle, XESTRING_TO_PSZ( maPromptTitle ),
XML_showDropDown, XclXmlUtils::ToPsz( ! ::get_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN ) ),
XML_showErrorMessage, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWERROR ) ),
XML_showInputMessage, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWPROMPT ) ),
XML_sqref, XclXmlUtils::ToOString( maScRanges ).getStr(),
XML_type, lcl_GetValidationType( mnFlags ),
FSEND );
if( msFormula1.getLength() )
{
rWorksheet->startElement( XML_formula1, FSEND );
rWorksheet->writeEscaped( msFormula1 );
rWorksheet->endElement( XML_formula1 );
}
if( msFormula2.getLength() )
{
rWorksheet->startElement( XML_formula2, FSEND );
rWorksheet->writeEscaped( msFormula2 );
rWorksheet->endElement( XML_formula2 );
}
rWorksheet->endElement( XML_dataValidation );
}
// ----------------------------------------------------------------------------
XclExpDval::XclExpDval( const XclExpRoot& rRoot ) :
XclExpRecord( EXC_ID_DVAL, 18 ),
XclExpRoot( rRoot )
{
}
XclExpDval::~XclExpDval()
{
}
void XclExpDval::InsertCellRange( const ScRange& rRange, sal_uLong nScHandle )
{
if( GetBiff() == EXC_BIFF8 )
{
XclExpDV& rDVRec = SearchOrCreateDv( nScHandle );
rDVRec.InsertCellRange( rRange );
}
}
void XclExpDval::Save( XclExpStream& rStrm )
{
// check all records
size_t nPos = maDVList.GetSize();
while( nPos )
{
--nPos; // backwards to keep nPos valid
XclExpDVRef xDVRec = maDVList.GetRecord( nPos );
if( !xDVRec->Finalize() )
maDVList.RemoveRecord( nPos );
}
// write the DVAL and the DV's
if( !maDVList.IsEmpty() )
{
XclExpRecord::Save( rStrm );
maDVList.Save( rStrm );
}
}
void XclExpDval::SaveXml( XclExpXmlStream& rStrm )
{
if( maDVList.IsEmpty() )
return;
sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
rWorksheet->startElement( XML_dataValidations,
XML_count, OString::valueOf( (sal_Int32) maDVList.GetSize() ).getStr(),
// OOXTODO: XML_disablePrompts,
// OOXTODO: XML_xWindow,
// OOXTODO: XML_yWindow,
FSEND );
maDVList.SaveXml( rStrm );
rWorksheet->endElement( XML_dataValidations );
}
XclExpDV& XclExpDval::SearchOrCreateDv( sal_uLong nScHandle )
{
// test last found record
if( mxLastFoundDV.get() && (mxLastFoundDV->GetScHandle() == nScHandle) )
return *mxLastFoundDV;
// binary search
size_t nCurrPos = 0;
if( !maDVList.IsEmpty() )
{
size_t nFirstPos = 0;
size_t nLastPos = maDVList.GetSize() - 1;
bool bLoop = true;
sal_uLong nCurrScHandle = ::std::numeric_limits< sal_uLong >::max();
while( (nFirstPos <= nLastPos) && bLoop )
{
nCurrPos = (nFirstPos + nLastPos) / 2;
mxLastFoundDV = maDVList.GetRecord( nCurrPos );
nCurrScHandle = mxLastFoundDV->GetScHandle();
if( nCurrScHandle == nScHandle )
bLoop = false;
else if( nCurrScHandle < nScHandle )
nFirstPos = nCurrPos + 1;
else if( nCurrPos )
nLastPos = nCurrPos - 1;
else // special case for nLastPos = -1
bLoop = false;
}
if( nCurrScHandle == nScHandle )
return *mxLastFoundDV;
else if( nCurrScHandle < nScHandle )
++nCurrPos;
}
// create new DV record
mxLastFoundDV.reset( new XclExpDV( *this, nScHandle ) );
maDVList.InsertRecord( mxLastFoundDV, nCurrPos );
return *mxLastFoundDV;
}
void XclExpDval::WriteBody( XclExpStream& rStrm )
{
rStrm.WriteZeroBytes( 10 );
rStrm << EXC_DVAL_NOOBJ << static_cast< sal_uInt32 >( maDVList.GetSize() );
}
// Web Queries ================================================================
XclExpWebQuery::XclExpWebQuery(
const String& rRangeName,
const String& rUrl,
const String& rSource,
sal_Int32 nRefrSecs ) :
maDestRange( rRangeName ),
maUrl( rUrl ),
// refresh delay time: seconds -> minutes
mnRefresh( ulimit_cast< sal_Int16 >( (nRefrSecs + 59L) / 60L ) ),
mbEntireDoc( false )
{
// comma separated list of HTML table names or indexes
xub_StrLen nTokenCnt = rSource.GetTokenCount( ';' );
String aNewTables, aAppendTable;
xub_StrLen nStringIx = 0;
bool bExitLoop = false;
for( xub_StrLen nToken = 0; (nToken < nTokenCnt) && !bExitLoop; ++nToken )
{
String aToken( rSource.GetToken( 0, ';', nStringIx ) );
mbEntireDoc = ScfTools::IsHTMLDocName( aToken );
bExitLoop = mbEntireDoc || ScfTools::IsHTMLTablesName( aToken );
if( !bExitLoop && ScfTools::GetHTMLNameFromName( aToken, aAppendTable ) )
ScGlobal::AddToken( aNewTables, aAppendTable, ',' );
}
if( !bExitLoop ) // neither HTML_all nor HTML_tables found
{
if( aNewTables.Len() )
mxQryTables.reset( new XclExpString( aNewTables ) );
else
mbEntireDoc = true;
}
}
XclExpWebQuery::~XclExpWebQuery()
{
}
void XclExpWebQuery::Save( XclExpStream& rStrm )
{
DBG_ASSERT( !mbEntireDoc || !mxQryTables.get(), "XclExpWebQuery::Save - illegal mode" );
sal_uInt16 nFlags;
// QSI record
rStrm.StartRecord( EXC_ID_QSI, 10 + maDestRange.GetSize() );
rStrm << EXC_QSI_DEFAULTFLAGS
<< sal_uInt16( 0x0010 )
<< sal_uInt16( 0x0012 )
<< sal_uInt32( 0x00000000 )
<< maDestRange;
rStrm.EndRecord();
// PARAMQRY record
nFlags = 0;
::insert_value( nFlags, EXC_PQRYTYPE_WEBQUERY, 0, 3 );
::set_flag( nFlags, EXC_PQRY_WEBQUERY );
::set_flag( nFlags, EXC_PQRY_TABLES, !mbEntireDoc );
rStrm.StartRecord( EXC_ID_PQRY, 12 );
rStrm << nFlags
<< sal_uInt16( 0x0000 )
<< sal_uInt16( 0x0001 );
rStrm.WriteZeroBytes( 6 );
rStrm.EndRecord();
// WQSTRING record
rStrm.StartRecord( EXC_ID_WQSTRING, maUrl.GetSize() );
rStrm << maUrl;
rStrm.EndRecord();
// unknown record 0x0802
rStrm.StartRecord( EXC_ID_0802, 16 + maDestRange.GetSize() );
rStrm << EXC_ID_0802; // repeated record id ?!?
rStrm.WriteZeroBytes( 6 );
rStrm << sal_uInt16( 0x0003 )
<< sal_uInt32( 0x00000000 )
<< sal_uInt16( 0x0010 )
<< maDestRange;
rStrm.EndRecord();
// WEBQRYSETTINGS record
nFlags = mxQryTables.get() ? EXC_WQSETT_SPECTABLES : EXC_WQSETT_ALL;
rStrm.StartRecord( EXC_ID_WQSETT, 28 );
rStrm << EXC_ID_WQSETT // repeated record id ?!?
<< sal_uInt16( 0x0000 )
<< sal_uInt16( 0x0004 )
<< sal_uInt16( 0x0000 )
<< EXC_WQSETT_DEFAULTFLAGS
<< nFlags;
rStrm.WriteZeroBytes( 10 );
rStrm << mnRefresh // refresh delay in minutes
<< EXC_WQSETT_FORMATFULL
<< sal_uInt16( 0x0000 );
rStrm.EndRecord();
// WEBQRYTABLES record
if( mxQryTables.get() )
{
rStrm.StartRecord( EXC_ID_WQTABLES, 4 + mxQryTables->GetSize() );
rStrm << EXC_ID_WQTABLES // repeated record id ?!?
<< sal_uInt16( 0x0000 )
<< *mxQryTables; // comma separated list of source tables
rStrm.EndRecord();
}
}
// ----------------------------------------------------------------------------
XclExpWebQueryBuffer::XclExpWebQueryBuffer( const XclExpRoot& rRoot )
{
SCTAB nScTab = rRoot.GetCurrScTab();
SfxObjectShell* pShell = rRoot.GetDocShell();
if( !pShell ) return;
ScfPropertySet aModelProp( pShell->GetModel() );
if( !aModelProp.Is() ) return;
Reference< XAreaLinks > xAreaLinks;
aModelProp.GetProperty( xAreaLinks, CREATE_OUSTRING( SC_UNO_AREALINKS ) );
Reference< XIndexAccess > xLinksIA( xAreaLinks, UNO_QUERY );
if( !xLinksIA.is() ) return;
for( sal_Int32 nIndex = 0, nCount = xLinksIA->getCount(); nIndex < nCount; ++nIndex )
{
Reference< XAreaLink > xAreaLink( xLinksIA->getByIndex( nIndex ), UNO_QUERY );
if( xAreaLink.is() )
{
CellRangeAddress aDestRange( xAreaLink->getDestArea() );
if( static_cast< SCTAB >( aDestRange.Sheet ) == nScTab )
{
ScfPropertySet aLinkProp( xAreaLink );
OUString aFilter;
if( aLinkProp.GetProperty( aFilter, CREATE_OUSTRING( SC_UNONAME_FILTER ) ) &&
(aFilter == CREATE_OUSTRING( EXC_WEBQRY_FILTER )) )
{
// get properties
OUString /*aFilterOpt,*/ aUrl;
sal_Int32 nRefresh = 0;
// aLinkProp.GetProperty( aFilterOpt, CREATE_OUSTRING( SC_UNONAME_FILTOPT ) );
aLinkProp.GetProperty( aUrl, CREATE_OUSTRING( SC_UNONAME_LINKURL ) );
aLinkProp.GetProperty( nRefresh, CREATE_OUSTRING( SC_UNONAME_REFDELAY ) );
String aAbsDoc( ScGlobal::GetAbsDocName( aUrl, pShell ) );
INetURLObject aUrlObj( aAbsDoc );
String aWebQueryUrl( aUrlObj.getFSysPath( INetURLObject::FSYS_DOS ) );
if( !aWebQueryUrl.Len() )
aWebQueryUrl = aAbsDoc;
// find range or create a new range
String aRangeName;
ScRange aScDestRange;
ScUnoConversion::FillScRange( aScDestRange, aDestRange );
if( const ScRangeData* pRangeData = rRoot.GetNamedRanges().GetRangeAtBlock( aScDestRange ) )
{
aRangeName = pRangeData->GetName();
}
else
{
XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
XclExpNameManager& rNameMgr = rRoot.GetNameManager();
// create a new unique defined name containing the range
XclTokenArrayRef xTokArr = rFmlaComp.CreateFormula( EXC_FMLATYPE_WQUERY, aScDestRange );
sal_uInt16 nNameIdx = rNameMgr.InsertUniqueName( aUrlObj.getBase(), xTokArr, nScTab );
aRangeName = rNameMgr.GetOrigName( nNameIdx );
}
// create and store the web query record
if( aRangeName.Len() )
AppendNewRecord( new XclExpWebQuery(
aRangeName, aWebQueryUrl, xAreaLink->getSourceArea(), nRefresh ) );
}
}
}
}
}
// ============================================================================