blob: c4d576f3a669aab6a238e5c4a5d1a4e3a9f4c837 [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, const ScAddress &rSrcPos = ScAddress() );
/** Writes the body of the CF record. */
void WriteBody( XclExpStream& rStrm );
void SaveXml( XclExpXmlStream &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.
String msVal1;
String msVal2;
String msFormula1;
String msFormula2;
bool mbIsStr1;
bool mbIsStr2;
double mnVal1;
double mnVal2;
bool mbFmla2;
static sal_uInt32 mnCount;
};
// ----------------------------------------------------------------------------
sal_uInt32 XclExpCFImpl::mnCount = 0;
XclExpCFImpl::XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry , const ScAddress &rSrcPos) :
XclExpRoot( rRoot ),
mrFormatEntry( ( ScCondFormatEntry &)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 ),
mbFmla2( 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" );
}
msVal1 = ( (const ScCondFormatEntry&)rFormatEntry ).GetStrVal1();
msVal2 = ( (const ScCondFormatEntry&)rFormatEntry ).GetStrVal2();
mnVal1 = ( (const ScCondFormatEntry&)rFormatEntry ).GetDoubleVal1();
mnVal2 = ( (const ScCondFormatEntry&)rFormatEntry ).GetDoubleVal2();
mbIsStr1 = ( (const ScCondFormatEntry&)rFormatEntry ).GetBoolIsStr1();
mbIsStr2 = ( (const ScCondFormatEntry&)rFormatEntry ).GetBoolIsStr2();
mnCount++;
// *** formulas ***
XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
::std::auto_ptr< ScTokenArray > xScTokArr( mrFormatEntry.CreateTokenArry( 0 ) );
mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
if( mbFmla2 )
{
xScTokArr.reset( mrFormatEntry.CreateTokenArry( 1 ) );
mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
}
msFormula1 = XclXmlUtils::ToOUString( GetDoc(), rSrcPos, mrFormatEntry.CreateTokenArry( 0 ),
formula::FormulaGrammar::GRAM_OOXML, GetRoot().GetUILanguage(), mxTokArr1->IsRecoverable() );
}
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 );
}
void XclExpCFImpl::SaveXml( XclExpXmlStream &rStrm )
{
sax_fastparser::FSHelperPtr &rWorksheet = rStrm.GetCurrentStream();
sal_Int32 dxfid = XclExpXFBuffer::GetDxfIdMap()[mrFormatEntry.GetStyle()];
if ( EXC_CF_TYPE_FMLA == mnType )
{
rWorksheet->startElement( XML_cfRule,
XML_priority, OString::valueOf((sal_Int32) mnCount).getStr(),
XML_dxfId, OString::valueOf((sal_Int32)dxfid).getStr(),
XML_type, "expression",
//XML_stopIfTrue, "1",
FSEND );
rWorksheet->startElement( XML_formula, FSEND );
rWorksheet->writeEscaped( XclXmlUtils::ToOUString(msFormula1).getStr() );
rWorksheet->endElement( XML_formula );
}
else
{
rWorksheet->startElement( XML_cfRule,
XML_operator, cf_cmplist[mnOperator].value,
XML_priority, OString::valueOf((sal_Int32) mnCount).getStr(),
XML_dxfId, OString::valueOf((sal_Int32)dxfid).getStr(),
XML_type, cf_typelist[mnType].value,
FSEND );
rWorksheet->startElement( XML_formula, FSEND );
if ( mbIsStr1 )
{
rWorksheet->write( XclXmlUtils::ToOUString("\"") );
rWorksheet->write( XclXmlUtils::ToOUString(msVal1) );
rWorksheet->write( XclXmlUtils::ToOUString("\"") );
}
else
{
rWorksheet->write( mnVal1 );
}
rWorksheet->endElement( XML_formula );
if (mbFmla2)
{
rWorksheet->startElement( XML_formula, FSEND );
if ( mbIsStr2 )
{
rWorksheet->write( XclXmlUtils::ToOUString("\"") );
rWorksheet->write( XclXmlUtils::ToOUString(msVal2) );
rWorksheet->write( XclXmlUtils::ToOUString("\"") );
}
else
{
rWorksheet->write( mnVal2 );
}
rWorksheet->endElement( XML_formula );
}
}
mnCount--;
rWorksheet->endElement( XML_cfRule );
}
// ----------------------------------------------------------------------------
XclExpCF::XclExpCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, const ScAddress &rSrcPos ) :
XclExpRecord( EXC_ID_CF ),
XclExpRoot( rRoot ),
mxImpl( new XclExpCFImpl( rRoot, rFormatEntry, rSrcPos) )
{
}
XclExpCF::~XclExpCF()
{
}
void XclExpCF::WriteBody( XclExpStream& rStrm )
{
mxImpl->WriteBody( rStrm );
}
void XclExpCF::SaveXml(XclExpXmlStream &rStrm)
{
mxImpl->SaveXml(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 );
ScRange *pScRange = NULL;
for (sal_uInt32 nPos = 0, nCount = aScRanges.Count(); nPos < nCount; ++nPos)
{
if (ScRange *tmpScRange = aScRanges.GetObject(nPos))
{
//firstly assign for pScRange or now ScRange less than pScRange
if (!pScRange || (*tmpScRange < *pScRange))
{
pScRange = tmpScRange;
}
}
}
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, pScRange->aStart ) );
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 );
if ( !maCFList.IsEmpty())
{
//Reversed save CFRule,in list
//the more front CFRule, the higher the priority, the smaller the value
for ( size_t i = maCFList.GetSize(); i > 0; --i )
{
maCFList.GetRecord(i-1)->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;
}
const char* lcl_GetErrorStyle( sal_uInt32 nFlags )
{
switch( nFlags & EXC_DV_ERROR_MASK )
{
case EXC_DV_ERROR_STOP: return "stop";
case EXC_DV_ERROR_WARNING: return "warning";
case EXC_DV_ERROR_INFO: return "information";
}
return NULL;
}
} // namespace
// ----------------------------------------------------------------------------
XclExpDV::XclExpDV( const XclExpRoot& rRoot, sal_uLong nScHandle, const ScRange& rRange ) :
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 );
/* Later the second param will be used to get the absolute position for the formula
range parameter. At last this absolute position will be formated as string range such as B4:B8.
Absolut position = relative position + DV positon(the second param).
But we cannot get a correct DV position form "pValData->GetSrcPos()". Because on importing 2007
file, the src pos for a DV obj is always be set as (0, 0, 0).
Here I pass the range start. It will be OK both for a single cell and a rang cell DV(Select a rang cell and
set DV for them together. It does not mean the range for formula). */
msFormula1 = XclXmlUtils::ToOUString( GetDoc(), rRange.aStart, xScTokArr.get(),
formula::FormulaGrammar::GRAM_OOXML, rRoot.GetUILanguage(), mxTokArr1.is() && mxTokArr1->IsRecoverable());
}
}
else
{
// no list validation -> convert the formula
mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
// Below is another selection to pass the recoverable param
// (EXC_TOKID_ERR == mxTokArr1->GetData()[0]) && (EXC_ERR_NA == mxTokArr1->GetData()[1])
msFormula1 = XclXmlUtils::ToOUString( GetDoc(), rRange.aStart, xScTokArr.get(),
formula::FormulaGrammar::GRAM_OOXML, rRoot.GetUILanguage(), mxTokArr1.is() && mxTokArr1->IsRecoverable() );
}
}
// second formula
xScTokArr.reset( pValData->CreateTokenArry( 1 ) );
if( xScTokArr.get() )
{
mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
msFormula2 = XclXmlUtils::ToOUString( GetDoc(), rRange.aStart, xScTokArr.get(),
formula::FormulaGrammar::GRAM_OOXML, rRoot.GetUILanguage(), mxTokArr1.is() && mxTokArr1->IsRecoverable());
}
}
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 ),
XML_errorStyle, lcl_GetErrorStyle( mnFlags ),//lijiany_ms_2007_dv
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, rRange );
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, const ScRange& rRange )
{
// 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, rRange ) );
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 ) );
}
}
}
}
}
// ============================================================================