diff options
Diffstat (limited to 'sc/source/filter/excel')
58 files changed, 60277 insertions, 0 deletions
diff --git a/sc/source/filter/excel/colrowst.cxx b/sc/source/filter/excel/colrowst.cxx new file mode 100644 index 000000000000..01587c036dea --- /dev/null +++ b/sc/source/filter/excel/colrowst.cxx @@ -0,0 +1,318 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + + + +#include "colrowst.hxx" + +#include <string.h> + +#include "document.hxx" +#include "root.hxx" +#include "ftools.hxx" +#include "xltable.hxx" +#include "xistream.hxx" +#include "xistyle.hxx" +#include "queryparam.hxx" + +// for filter manager +#include "excimp8.hxx" + +// ============================================================================ + +const sal_uInt8 EXC_COLROW_USED = 0x01; +const sal_uInt8 EXC_COLROW_DEFAULT = 0x02; +const sal_uInt8 EXC_COLROW_HIDDEN = 0x04; +const sal_uInt8 EXC_COLROW_MAN = 0x08; + +// ============================================================================ + +XclImpColRowSettings::XclImpColRowSettings( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + maWidths( MAXCOLCOUNT, 0 ), + maColFlags( MAXCOLCOUNT, 0 ), + maHeights( MAXROWCOUNT, 0 ), + maRowFlags( MAXROWCOUNT, 0 ), + mnLastScRow( -1 ), + mnDefWidth( STD_COL_WIDTH ), + mnDefHeight( static_cast< sal_uInt16 >( STD_ROW_HEIGHT ) ), + mnDefRowFlags( EXC_DEFROW_DEFAULTFLAGS ), + mbHasStdWidthRec( false ), + mbHasDefHeight( false ), + mbDirty( true ) +{ +} + +XclImpColRowSettings::~XclImpColRowSettings() +{ +} + +void XclImpColRowSettings::SetDefWidth( sal_uInt16 nDefWidth, bool bStdWidthRec ) +{ + if( bStdWidthRec ) + { + // STANDARDWIDTH record overrides DEFCOLWIDTH record + mnDefWidth = nDefWidth; + mbHasStdWidthRec = true; + } + else if( !mbHasStdWidthRec ) + { + // use DEFCOLWIDTH record only, if no STANDARDWIDTH record exists + mnDefWidth = nDefWidth; + } +} + +void XclImpColRowSettings::SetWidthRange( SCCOL nScCol1, SCCOL nScCol2, sal_uInt16 nWidth ) +{ + DBG_ASSERT( (nScCol1 <= nScCol2) && ValidCol( nScCol2 ), "XclImpColRowSettings::SetColWidthRange - invalid column range" ); + nScCol2 = ::std::min( nScCol2, MAXCOL ); + if (nScCol2 == 256) + // In BIFF8, the column range is 0-255, and the use of 256 probably + // means the range should extend to the max column if the loading app + // support columns beyond 255. + nScCol2 = MAXCOL; + + nScCol1 = ::std::min( nScCol1, nScCol2 ); + ::std::fill( maWidths.begin() + nScCol1, maWidths.begin() + nScCol2 + 1, nWidth ); + for( ScfUInt8Vec::iterator aIt = maColFlags.begin() + nScCol1, aEnd = maColFlags.begin() + nScCol2 + 1; aIt != aEnd; ++aIt ) + ::set_flag( *aIt, EXC_COLROW_USED ); +} + +void XclImpColRowSettings::HideCol( SCCOL nScCol ) +{ + if( ValidCol( nScCol ) ) + ::set_flag( maColFlags[ nScCol ], EXC_COLROW_HIDDEN ); +} + +void XclImpColRowSettings::HideColRange( SCCOL nScCol1, SCCOL nScCol2 ) +{ + DBG_ASSERT( (nScCol1 <= nScCol2) && ValidCol( nScCol2 ), "XclImpColRowSettings::HideColRange - invalid column range" ); + nScCol2 = ::std::min( nScCol2, MAXCOL ); + nScCol1 = ::std::min( nScCol1, nScCol2 ); + for( ScfUInt8Vec::iterator aIt = maColFlags.begin() + nScCol1, aEnd = maColFlags.begin() + nScCol2 + 1; aIt != aEnd; ++aIt ) + ::set_flag( *aIt, EXC_COLROW_HIDDEN ); +} + +void XclImpColRowSettings::SetDefHeight( sal_uInt16 nDefHeight, sal_uInt16 nFlags ) +{ + mnDefHeight = nDefHeight; + mnDefRowFlags = nFlags; + if( mnDefHeight == 0 ) + { + mnDefHeight = static_cast< sal_uInt16 >( STD_ROW_HEIGHT ); + ::set_flag( mnDefRowFlags, EXC_DEFROW_HIDDEN ); + } + mbHasDefHeight = true; +} + +void XclImpColRowSettings::SetHeight( SCROW nScRow, sal_uInt16 nHeight ) +{ + if( ValidRow( nScRow ) ) + { + sal_uInt16 nRawHeight = nHeight & EXC_ROW_HEIGHTMASK; + bool bDefHeight = ::get_flag( nHeight, EXC_ROW_FLAGDEFHEIGHT ) || (nRawHeight == 0); + maHeights[ nScRow ] = nRawHeight; + sal_uInt8& rnFlags = maRowFlags[ nScRow ]; + ::set_flag( rnFlags, EXC_COLROW_USED ); + if( !bDefHeight && (nRawHeight == 0) ) + ::set_flag( rnFlags, EXC_COLROW_HIDDEN ); + ::set_flag( rnFlags, EXC_COLROW_DEFAULT, bDefHeight ); + if( nScRow > mnLastScRow ) + mnLastScRow = nScRow; + } +} + +void XclImpColRowSettings::SetRowSettings( SCROW nScRow, sal_uInt16 nHeight, sal_uInt16 nFlags ) +{ + if( ValidRow( nScRow ) ) + { + SetHeight( nScRow, nHeight ); + sal_uInt8& rnFlags = maRowFlags[ nScRow ]; + if( ::get_flag( nFlags, EXC_ROW_UNSYNCED ) ) + ::set_flag( rnFlags, EXC_COLROW_MAN ); + if( ::get_flag( nFlags, EXC_ROW_HIDDEN ) ) + ::set_flag( rnFlags, EXC_COLROW_HIDDEN ); + } +} + +void XclImpColRowSettings::SetManualRowHeight( SCROW nScRow ) +{ + if( ValidRow( nScRow ) ) + ::set_flag( maRowFlags[ nScRow ], EXC_COLROW_MAN ); +} + +void XclImpColRowSettings::SetDefaultXF( SCCOL nScCol1, SCCOL nScCol2, sal_uInt16 nXFIndex ) +{ + /* #109555# assign the default column formatting here to ensure that + explicit cell formatting is not overwritten. */ + DBG_ASSERT( (nScCol1 <= nScCol2) && ValidCol( nScCol2 ), "XclImpColRowSettings::SetDefaultXF - invalid column index" ); + nScCol2 = ::std::min( nScCol2, MAXCOL ); + nScCol1 = ::std::min( nScCol1, nScCol2 ); + XclImpXFRangeBuffer& rXFRangeBuffer = GetXFRangeBuffer(); + for( SCCOL nScCol = nScCol1; nScCol <= nScCol2; ++nScCol ) + rXFRangeBuffer.SetColumnDefXF( nScCol, nXFIndex ); +} + +void XclImpColRowSettings::Convert( SCTAB nScTab ) +{ + if( !mbDirty ) + return; + + ScDocument& rDoc = GetDoc(); + rDoc.IncSizeRecalcLevel( nScTab ); + + // column widths ---------------------------------------------------------- + + for( SCCOL nScCol = 0; nScCol <= MAXCOL; ++nScCol ) + { + sal_uInt16 nWidth = ::get_flag( maColFlags[ nScCol ], EXC_COLROW_USED ) ? maWidths[ nScCol ] : mnDefWidth; + /* Hidden columns: remember hidden state, but do not set hidden state + in document here. Needed for #i11776#, no HIDDEN flags in the + document, until filters and outlines are inserted. */ + if( nWidth == 0 ) + { + ::set_flag( maColFlags[ nScCol ], EXC_COLROW_HIDDEN ); + nWidth = mnDefWidth; + } + rDoc.SetColWidth( nScCol, nScTab, nWidth ); + } + + // row heights ------------------------------------------------------------ + + // #i54252# set default row height + rDoc.SetRowHeightOnly( 0, MAXROW, nScTab, mnDefHeight ); + if( ::get_flag( mnDefRowFlags, EXC_DEFROW_UNSYNCED ) ) + // first access to row flags, do not ask for old flags + rDoc.SetRowFlags( 0, MAXROW, nScTab, CR_MANUALSIZE ); + bool bDefHideRow = ::get_flag( mnDefRowFlags, EXC_DEFROW_HIDDEN ); + + SCROW nFirstScRow = -1; + sal_uInt16 nLastHeight = 0; + for( SCROW nScRow = 0; nScRow <= mnLastScRow ; ++nScRow ) + { + // get height and hidden state from cached data + sal_uInt8 nFlags = maRowFlags[ nScRow ]; + sal_uInt16 nHeight = 0; + bool bHideRow = false; + if( ::get_flag( nFlags, EXC_COLROW_USED ) ) + { + if( ::get_flag( nFlags, EXC_COLROW_DEFAULT ) ) + { + nHeight = mnDefHeight; + bHideRow = bDefHideRow; + } + else + { + nHeight = maHeights[ nScRow ]; + if( nHeight == 0 ) + { + nHeight = mnDefHeight; + bHideRow = true; + } + } + + if( ::get_flag( nFlags, EXC_COLROW_MAN ) ) + rDoc.SetRowFlags( nScRow, nScTab, rDoc.GetRowFlags( nScRow, nScTab ) | CR_MANUALSIZE ); + } + else + { + nHeight = mnDefHeight; + bHideRow = bDefHideRow; + } + + /* Hidden rows: remember hidden state, but do not set hidden state in + document here. Needed for #i11776#, no HIDDEN flags in the document, + until filters and outlines are inserted. */ + if( bHideRow ) + ::set_flag( maRowFlags[ nScRow ], EXC_COLROW_HIDDEN ); + + // set height range + if( (nLastHeight != nHeight) || (nScRow == 0) ) + { + DBG_ASSERT( (nScRow == 0) || (nFirstScRow >= 0), "XclImpColRowSettings::Convert - algorithm error" ); + if( nScRow > 0 ) + rDoc.SetRowHeightOnly( nFirstScRow, nScRow - 1, nScTab, nLastHeight ); + + nFirstScRow = nScRow; + nLastHeight = nHeight; + } + } + + // set row height of last portion + if( mnLastScRow >= 0 ) + rDoc.SetRowHeightOnly( nFirstScRow, mnLastScRow, nScTab, nLastHeight ); + + // ------------------------------------------------------------------------ + + mbDirty = false; + rDoc.DecSizeRecalcLevel( nScTab ); +} + +void XclImpColRowSettings::ConvertHiddenFlags( SCTAB nScTab ) +{ + ScDocument& rDoc = GetDoc(); + + // hide the columns + for( SCCOL nScCol = 0; nScCol <= MAXCOL; ++nScCol ) + if( ::get_flag( maColFlags[ nScCol ], EXC_COLROW_HIDDEN ) ) + rDoc.ShowCol( nScCol, nScTab, FALSE ); + + // #i38093# rows hidden by filter need extra flag + SCROW nFirstFilterScRow = SCROW_MAX; + SCROW nLastFilterScRow = SCROW_MAX; + if( GetBiff() == EXC_BIFF8 ) + { + const XclImpAutoFilterData* pFilter = GetFilterManager().GetByTab( nScTab ); + // #i70026# use IsFiltered() to set the CR_FILTERED flag for active filters only + if( pFilter && pFilter->IsActive() && pFilter->IsFiltered() ) + { + nFirstFilterScRow = pFilter->StartRow(); + nLastFilterScRow = pFilter->EndRow(); + } + } + + // hide the rows + for( SCROW nScRow = 0; nScRow <= mnLastScRow; ++nScRow ) + { + if( ::get_flag( maRowFlags[ nScRow ], EXC_COLROW_HIDDEN ) ) + { + // hide the row + rDoc.ShowRow( nScRow, nScTab, FALSE ); + // #i38093# rows hidden by filter need extra flag + if( (nFirstFilterScRow <= nScRow) && (nScRow <= nLastFilterScRow) ) + rDoc.SetRowFiltered(nScRow, nScRow, nScTab, true); + } + } + + // #i47438# if default row format is hidden, hide remaining rows + if( ::get_flag( mnDefRowFlags, EXC_DEFROW_HIDDEN ) && (mnLastScRow < MAXROW) ) + rDoc.ShowRows( mnLastScRow + 1, MAXROW, nScTab, FALSE ); +} + diff --git a/sc/source/filter/excel/excdoc.cxx b/sc/source/filter/excel/excdoc.cxx new file mode 100644 index 000000000000..458629979172 --- /dev/null +++ b/sc/source/filter/excel/excdoc.cxx @@ -0,0 +1,832 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + + +//------------------------------------------------------------------------ + +#include "scitems.hxx" + +#include <comphelper/processfactory.hxx> +#include <svx/svdobj.hxx> +#include <svx/svditer.hxx> +#include <svx/svdpage.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <svl/intitem.hxx> +#include <svl/zformat.hxx> +#include <sot/storage.hxx> +#include <sfx2/objsh.hxx> +#include <tools/urlobj.hxx> +#include <rtl/ustring.hxx> + +#include "cell.hxx" +#include "dociter.hxx" +#include "document.hxx" +#include "rangenam.hxx" +#include "dbcolect.hxx" +#include "global.hxx" +#include "globstr.hrc" +#include "progress.hxx" +#include "conditio.hxx" +#include "dpobject.hxx" +#include "attrib.hxx" +#include "scextopt.hxx" +#include "stlsheet.hxx" +#include "stlpool.hxx" +#include "olinetab.hxx" +#include "unonames.hxx" +#include "convuno.hxx" +#include "patattr.hxx" +#include "docoptio.hxx" +#include "tabprotection.hxx" + +#include "excdoc.hxx" +#include "namebuff.hxx" + +#include "xcl97rec.hxx" +#include "xcl97esc.hxx" +#include "xetable.hxx" +#include "xelink.hxx" +#include "xename.hxx" +#include "xepage.hxx" +#include "xeview.hxx" +#include "xecontent.hxx" +#include "xeescher.hxx" +#include "xepivot.hxx" +#include "XclExpChangeTrack.hxx" + +#include <math.h> + +#include <oox/core/tokens.hxx> + +using ::rtl::OString; + +static String lcl_GetVbaTabName( SCTAB n ) +{ + String aRet( RTL_CONSTASCII_USTRINGPARAM( "__VBA__" ) ); + aRet += String::CreateFromInt32( static_cast<sal_uInt16>(n) ); + return aRet; +} + + +static void lcl_AddBookviews( XclExpRecordList<>& aRecList, ExcTable& self ) +{ + aRecList.AppendNewRecord( new XclExpXmlStartElementRecord( XML_bookViews ) ); + aRecList.AppendNewRecord( new XclExpWindow1( self.GetRoot() ) ); + aRecList.AppendNewRecord( new XclExpXmlEndElementRecord( XML_bookViews ) ); +} + +static void lcl_AddCalcPr( XclExpRecordList<>& aRecList, ExcTable& self ) +{ + ScDocument& rDoc = self.GetDoc(); + + aRecList.AppendNewRecord( new XclExpXmlStartSingleElementRecord( XML_calcPr ) ); + // OOXTODO: calcCompleted, calcId, calcMode, calcOnSave, + // concurrentCalc, concurrentManualCount, + // forceFullCalc, fullCalcOnLoad, fullPrecision + aRecList.AppendNewRecord( new XclCalccount( rDoc ) ); + aRecList.AppendNewRecord( new XclRefmode( rDoc ) ); + aRecList.AppendNewRecord( new XclIteration( rDoc ) ); + aRecList.AppendNewRecord( new XclDelta( rDoc ) ); + aRecList.AppendNewRecord( new XclExpBoolRecord(0x005F, true) ); // SAVERECALC + aRecList.AppendNewRecord( new XclExpXmlEndSingleElementRecord() ); // XML_calcPr +} + +#if 0 +// removed during rebase, because scsheetprotection02 is not yet up-stream :-( +static void lcl_AddWorkbookProtection( XclExpRecordList<>& aRecList, ExcTable& self ) +{ + aRecList.AppendNewRecord( new XclExpXmlStartSingleElementRecord( XML_workbookProtection ) ); + const ScDocProtection* pProtect = self.GetDoc().GetDocProtection(); + if (pProtect && pProtect->isProtected()) + { + aRecList.AppendNewRecord( new XclExpWindowProtection(pProtect->isOptionEnabled(ScDocProtection::WINDOWS)) ); + aRecList.AppendNewRecord( new XclExpProtection(pProtect->isOptionEnabled(ScDocProtection::STRUCTURE)) ); + aRecList.AppendNewRecord( new XclExpPassHash(pProtect->getPasswordHash(PASSHASH_XL)) ); + } + + if( self.GetBiff() == EXC_BIFF8 ) + { + aRecList.AppendNewRecord( new XclExpProt4Rev ); + aRecList.AppendNewRecord( new XclExpProt4RevPass ); + } + aRecList.AppendNewRecord( new XclExpXmlEndSingleElementRecord() ); // XML_workbookProtection +} +#endif + +static void lcl_AddScenariosAndFilters( XclExpRecordList<>& aRecList, const XclExpRoot& rRoot, SCTAB nScTab ) +{ + // Scenarios + aRecList.AppendNewRecord( new ExcEScenarioManager( rRoot, nScTab ) ); + // filter + aRecList.AppendRecord( rRoot.GetFilterManager().CreateRecord( nScTab ) ); +} + + +ExcTable::ExcTable( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mnScTab( SCTAB_GLOBAL ), + nExcTab( EXC_NOTAB ), + pTabNames( new NameBuffer( 0, 16 ) ) +{ +} + + +ExcTable::ExcTable( const XclExpRoot& rRoot, SCTAB nScTab ) : + XclExpRoot( rRoot ), + mnScTab( nScTab ), + nExcTab( rRoot.GetTabInfo().GetXclTab( nScTab ) ), + pTabNames( new NameBuffer( 0, 16 ) ) +{ +} + + +ExcTable::~ExcTable() +{ + delete pTabNames; +} + + +void ExcTable::Add( XclExpRecordBase* pRec ) +{ + DBG_ASSERT( pRec, "-ExcTable::Add(): pRec ist NULL!" ); + aRecList.AppendNewRecord( pRec ); +} + + +void ExcTable::FillAsHeader( ExcBoundsheetList& rBoundsheetList ) +{ + InitializeGlobals(); + + RootData& rR = GetOldRoot(); + ScDocument& rDoc = GetDoc(); + XclExpTabInfo& rTabInfo = GetTabInfo(); + + if ( GetBiff() <= EXC_BIFF5 ) + Add( new ExcBofW ); + else + Add( new ExcBofW8 ); + + SCTAB nC; + String aTmpString; + SCTAB nScTabCount = rTabInfo.GetScTabCount(); + UINT16 nExcTabCount = rTabInfo.GetXclTabCount(); + UINT16 nCodenames = static_cast< UINT16 >( GetExtDocOptions().GetCodeNameCount() ); + + SfxObjectShell* pShell = GetDocShell(); + sal_uInt16 nWriteProtHash = pShell ? pShell->GetModifyPasswordHash() : 0; + bool bRecommendReadOnly = pShell && pShell->IsLoadReadonly(); + + if( (nWriteProtHash > 0) || bRecommendReadOnly ) + Add( new XclExpEmptyRecord( EXC_ID_WRITEPROT ) ); + + // TODO: correct codepage for BIFF5? + sal_uInt16 nCodePage = XclTools::GetXclCodePage( (GetBiff() <= EXC_BIFF5) ? RTL_TEXTENCODING_MS_1252 : RTL_TEXTENCODING_UNICODE ); + + if( GetBiff() <= EXC_BIFF5 ) + { + Add( new XclExpEmptyRecord( EXC_ID_INTERFACEHDR ) ); + Add( new XclExpUInt16Record( EXC_ID_MMS, 0 ) ); + Add( new XclExpEmptyRecord( EXC_ID_TOOLBARHDR ) ); + Add( new XclExpEmptyRecord( EXC_ID_TOOLBAREND ) ); + Add( new XclExpEmptyRecord( EXC_ID_INTERFACEEND ) ); + Add( new ExcDummy_00 ); + } + else + { + if( IsDocumentEncrypted() ) + Add( new XclExpFilePass( GetRoot() ) ); + Add( new XclExpInterfaceHdr( nCodePage ) ); + Add( new XclExpUInt16Record( EXC_ID_MMS, 0 ) ); + Add( new XclExpInterfaceEnd ); + Add( new XclExpWriteAccess ); + } + + Add( new XclExpFileSharing( GetRoot(), nWriteProtHash, bRecommendReadOnly ) ); + Add( new XclExpUInt16Record( EXC_ID_CODEPAGE, nCodePage ) ); + + if( GetBiff() == EXC_BIFF8 ) + { + Add( new XclExpBoolRecord( EXC_ID_DSF, false ) ); + Add( new XclExpEmptyRecord( EXC_ID_XL9FILE ) ); + rR.pTabId = new XclExpChTrTabId( Max( nExcTabCount, nCodenames ) ); + Add( rR.pTabId ); + if( HasVbaStorage() ) + { + Add( new XclObproj ); + const String& rCodeName = GetExtDocOptions().GetDocSettings().maGlobCodeName; + if( rCodeName.Len() ) + Add( new XclCodename( rCodeName ) ); + } + } + + Add( new XclExpUInt16Record( EXC_ID_FNGROUPCOUNT, 14 ) ); + + // erst Namen- und Tabellen-Eintraege aufbauen + String aName; + + for( nC = 0 ; nC < nScTabCount ; nC++ ) + if( rTabInfo.IsExportTab( nC ) ) + { + rDoc.GetName( nC, aTmpString ); + *pTabNames << aTmpString; + } + + if ( GetBiff() <= EXC_BIFF5 ) + { + // global link table: EXTERNCOUNT, EXTERNSHEET, NAME + aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) ); + aRecList.AppendRecord( CreateRecord( EXC_ID_NAME ) ); + } + + // document protection options + const ScDocProtection* pProtect = GetDoc().GetDocProtection(); + if (pProtect && pProtect->isProtected()) + { + Add( new XclExpWindowProtection(pProtect->isOptionEnabled(ScDocProtection::WINDOWS)) ); + Add( new XclExpProtection(pProtect->isOptionEnabled(ScDocProtection::STRUCTURE)) ); +#if ENABLE_SHEET_PROTECTION + Add( new XclExpPassHash(pProtect->getPasswordHash(PASSHASH_XL)) ); +#endif + } + + if( GetBiff() == EXC_BIFF8 ) + { + Add( new XclExpProt4Rev ); + Add( new XclExpProt4RevPass ); + } + + // document protection options + if( GetOutput() == EXC_OUTPUT_BINARY ) + { + //lcl_AddWorkbookProtection( aRecList, *this ); + lcl_AddBookviews( aRecList, *this ); + } + + Add( new XclExpXmlStartSingleElementRecord( XML_workbookPr ) ); + + if ( GetBiff() == EXC_BIFF8 ) + { + Add( new XclExpBoolRecord(0x0040, false) ); // BACKUP + Add( new XclExpBoolRecord(0x008D, false) ); // HIDEOBJ + } + + if( GetBiff() <= EXC_BIFF5 ) + { + Add( new ExcDummy_040 ); + Add( new Exc1904( rDoc ) ); + Add( new ExcDummy_041 ); + } + else + { + // BIFF8 + Add( new Exc1904( rDoc ) ); + Add( new XclExpBoolRecord( 0x000E, !rDoc.GetDocOptions().IsCalcAsShown() ) ); + Add( new XclExpBoolRecord(0x01B7, false) ); // REFRESHALL + Add( new XclExpBoolRecord(0x00DA, false) ); // BOOKBOOL + // OOXTODO: The following /workbook/workbookPr attributes are mapped + // to various BIFF records that are not currently supported: + // + // XML_allowRefreshQuery: QSISTAG 802h: fEnableRefresh + // XML_autoCompressPictures: COMPRESSPICTURES 89Bh: fAutoCompressPictures + // XML_checkCompatibility: COMPAT12 88Ch: fNoCompatChk + // XML_codeName: "Calc" + // XML_defaultThemeVersion: ??? + // XML_filterPrivacy: BOOKEXT 863h: fFilterPrivacy + // XML_hidePivotFieldList: BOOKBOOL DAh: fHidePivotTableFList + // XML_promptedSolutions: BOOKEXT 863h: fBuggedUserAboutSolution + // XML_publishItems: NAMEPUBLISH 893h: fPublished + // XML_saveExternalLinkValues: BOOKBOOL DAh: fNoSavSupp + // XML_showBorderUnselectedTables: BOOKBOOL DAh: fHideBorderUnsels + // XML_showInkAnnotation: BOOKEXT 863h: fShowInkAnnotation + // XML_showPivotChart: PIVOTCHARTBITS 859h: fGXHide?? + // XML_updateLinks: BOOKBOOL DAh: grbitUpdateLinks + } + Add( new XclExpXmlEndSingleElementRecord() ); // XML_workbookPr + + // Formatting: FONT, FORMAT, XF, STYLE, PALETTE + if( GetOutput() != EXC_OUTPUT_BINARY ) + { + aRecList.AppendNewRecord( new XclExpXmlStyleSheet( *this ) ); + } + else + { + aRecList.AppendRecord( CreateRecord( EXC_ID_FONTLIST ) ); + aRecList.AppendRecord( CreateRecord( EXC_ID_FORMATLIST ) ); + aRecList.AppendRecord( CreateRecord( EXC_ID_XFLIST ) ); + aRecList.AppendRecord( CreateRecord( EXC_ID_PALETTE ) ); + } + + + if( GetBiff() <= EXC_BIFF5 ) + { + // Bundlesheet + for( nC = 0 ; nC < nScTabCount ; nC++ ) + if( rTabInfo.IsExportTab( nC ) ) + { + ExcBoundsheetList::RecordRefType xBoundsheet( new ExcBundlesheet( rR, nC ) ); + aRecList.AppendRecord( xBoundsheet ); + rBoundsheetList.AppendRecord( xBoundsheet ); + } + } + else + { + // Pivot Cache + GetPivotTableManager().CreatePivotTables(); + aRecList.AppendRecord( GetPivotTableManager().CreatePivotCachesRecord() ); + + // Change tracking + if( rDoc.GetChangeTrack() ) + { + rR.pUserBViewList = new XclExpUserBViewList( *rDoc.GetChangeTrack() ); + Add( rR.pUserBViewList ); + } + + // Natural Language Formulas Flag + aRecList.AppendNewRecord( new XclExpBoolRecord( EXC_ID_USESELFS, GetDoc().GetDocOptions().IsLookUpColRowNames() ) ); + + if( GetOutput() != EXC_OUTPUT_BINARY ) + { + //lcl_AddWorkbookProtection( aRecList, *this ); + lcl_AddBookviews( aRecList, *this ); + } + + // Bundlesheet + aRecList.AppendNewRecord( new XclExpXmlStartElementRecord( XML_sheets ) ); + for( nC = 0 ; nC < nScTabCount ; nC++ ) + if( rTabInfo.IsExportTab( nC ) ) + { + ExcBoundsheetList::RecordRefType xBoundsheet( new ExcBundlesheet8( rR, nC ) ); + aRecList.AppendRecord( xBoundsheet ); + rBoundsheetList.AppendRecord( xBoundsheet ); + } + aRecList.AppendNewRecord( new XclExpXmlEndElementRecord( XML_sheets ) ); + + for( SCTAB nAdd = 0; nC < static_cast<SCTAB>(nCodenames) ; nC++, nAdd++ ) + { + aTmpString = lcl_GetVbaTabName( nAdd ); + ExcBoundsheetList::RecordRefType xBoundsheet( new ExcBundlesheet8( aTmpString ) ); + aRecList.AppendRecord( xBoundsheet ); + rBoundsheetList.AppendRecord( xBoundsheet ); + } + + // COUNTRY - in BIFF8 in workbook globals + Add( new XclExpCountry( GetRoot() ) ); + // link table: SUPBOOK, XCT, CRN, EXTERNNAME, EXTERNSHEET, NAME + aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) ); + aRecList.AppendRecord( CreateRecord( EXC_ID_NAME ) ); + + if( GetOutput() != EXC_OUTPUT_BINARY ) + lcl_AddCalcPr( aRecList, *this ); + + Add( new XclExpRecalcId ); + + // MSODRAWINGGROUP per-document data + aRecList.AppendRecord( GetObjectManager().CreateDrawingGroup() ); + // Shared string table: SST, EXTSST + aRecList.AppendRecord( CreateRecord( EXC_ID_SST ) ); + + Add( new XclExpBookExt ); + } + + Add( new ExcEof ); +} + + +void ExcTable::FillAsTable( SCTAB nCodeNameIdx ) +{ + InitializeTable( mnScTab ); + + RootData& rR = GetOldRoot(); + XclBiff eBiff = GetBiff(); + ScDocument& rDoc = GetDoc(); + + DBG_ASSERT( (mnScTab >= 0L) && (mnScTab <= MAXTAB), "-ExcTable::Table(): mnScTab - no ordinary table!" ); + DBG_ASSERT( nExcTab <= static_cast<sal_uInt16>(MAXTAB), "-ExcTable::Table(): nExcTab - no ordinary table!" ); + + // create a new OBJ list for this sheet (may be used by notes, autofilter, data validation) + if( eBiff == EXC_BIFF8 ) + GetObjectManager().StartSheet(); + + // cell table: DEFROWHEIGHT, DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records + mxCellTable.reset( new XclExpCellTable( GetRoot() ) ); + + if( GetOutput() != EXC_OUTPUT_BINARY ) + { + FillAsXmlTable( nCodeNameIdx ); + return; + } + + + // WSBOOL needs data from page settings, create it here, add it later + ScfRef< XclExpPageSettings > xPageSett( new XclExpPageSettings( GetRoot() ) ); + bool bFitToPages = xPageSett->GetPageData().mbFitToPages; + + if( eBiff <= EXC_BIFF5 ) + { + Add( new ExcBof ); + Add( new ExcDummy_02a ); + } + else + { + Add( new ExcBof8 ); + lcl_AddCalcPr( aRecList, *this ); + } + + // GUTS (count & size of outline icons) + aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_GUTS ) ); + // DEFROWHEIGHT, created by the cell table + aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID2_DEFROWHEIGHT ) ); + + // COUNTRY - in BIFF5/7 in every worksheet + if( eBiff <= EXC_BIFF5 ) + Add( new XclExpCountry( GetRoot() ) ); + + Add( new XclExpWsbool( bFitToPages ) ); + + // page settings (SETUP and various other records) + aRecList.AppendRecord( xPageSett ); + + const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnScTab); + if (pTabProtect && pTabProtect->isProtected()) + { + Add( new XclExpProtection(true) ); + Add( new XclExpBoolRecord(0x00DD, pTabProtect->isOptionEnabled(ScTableProtection::SCENARIOS)) ); + Add( new XclExpBoolRecord(0x0063, pTabProtect->isOptionEnabled(ScTableProtection::OBJECTS)) ); +#if ENABLE_SHEET_PROTECTION + Add( new XclExpPassHash(pTabProtect->getPasswordHash(PASSHASH_XL)) ); +#endif + } + + // local link table: EXTERNCOUNT, EXTERNSHEET + if( eBiff <= EXC_BIFF5 ) + aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) ); + + if ( eBiff == EXC_BIFF8 ) + lcl_AddScenariosAndFilters( aRecList, GetRoot(), mnScTab ); + + // cell table: DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records + aRecList.AppendRecord( mxCellTable ); + + // MERGEDCELLS record, generated by the cell table + aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_MERGEDCELLS ) ); + // label ranges + if( eBiff == EXC_BIFF8 ) + Add( new XclExpLabelranges( GetRoot() ) ); + // data validation (DVAL and list of DV records), generated by the cell table + aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_DVAL ) ); + + if( eBiff == EXC_BIFF8 ) + { + // all MSODRAWING and OBJ stuff of this sheet goes here + aRecList.AppendRecord( GetObjectManager().ProcessDrawing( GetSdrPage( mnScTab ) ) ); + // pivot tables + aRecList.AppendRecord( GetPivotTableManager().CreatePivotTablesRecord( mnScTab ) ); + } + + // list of NOTE records, generated by the cell table + aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_NOTE ) ); + + // sheet view settings: WINDOW2, SCL, PANE, SELECTION + aRecList.AppendNewRecord( new XclExpTabViewSettings( GetRoot(), mnScTab ) ); + + if( eBiff == EXC_BIFF8 ) + { + // sheet protection options + Add( new XclExpSheetProtectOptions( GetRoot(), mnScTab ) ); + + // web queries + Add( new XclExpWebQueryBuffer( GetRoot() ) ); + + // conditional formats + Add( new XclExpCondFormatBuffer( GetRoot() ) ); + + if( HasVbaStorage() ) + if( nCodeNameIdx < GetExtDocOptions().GetCodeNameCount() ) + Add( new XclCodename( GetExtDocOptions().GetCodeName( nCodeNameIdx ) ) ); + } + + // list of HLINK records, generated by the cell table + aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_HLINK ) ); + + // change tracking + if( rR.pUserBViewList ) + { + for( const XclExpUserBView* pBView = rR.pUserBViewList->First(); pBView; pBView = rR.pUserBViewList->Next() ) + { + Add( new XclExpUsersViewBegin( pBView->GetGUID(), nExcTab ) ); + Add( new XclExpUsersViewEnd ); + } + } + + // EOF + Add( new ExcEof ); +} + +void ExcTable::FillAsXmlTable( SCTAB nCodeNameIdx ) +{ + RootData& rR = GetOldRoot(); + + // WSBOOL needs data from page settings, create it here, add it later + ScfRef< XclExpPageSettings > xPageSett( new XclExpPageSettings( GetRoot() ) ); + bool bFitToPages = xPageSett->GetPageData().mbFitToPages; + + Add( new ExcBof8 ); + + Add( new XclExpWsbool( bFitToPages, mnScTab, &GetFilterManager() ) ); + + // GUTS (count & size of outline icons) + aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_GUTS ) ); + // DEFROWHEIGHT, created by the cell table + aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID2_DEFROWHEIGHT ) ); + + aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID3_DIMENSIONS ) ); + + // sheet view settings: WINDOW2, SCL, PANE, SELECTION + aRecList.AppendNewRecord( new XclExpTabViewSettings( GetRoot(), mnScTab ) ); + + // cell table: DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records + aRecList.AppendRecord( mxCellTable ); + + // label ranges + Add( new XclExpLabelranges( GetRoot() ) ); + + // DFF not needed in MSOOXML export +// GetObjectManager().AddSdrPage(); +// //! close Escher group shape and ESCHER_DgContainer +// //! opened by XclExpObjList ctor MSODRAWING +// rR.pObjRecs->EndSheet(); +// // all MSODRAWING and OBJ stuff of this sheet goes here +// Add( rR.pObjRecs ); + + // pivot tables + aRecList.AppendRecord( GetPivotTableManager().CreatePivotTablesRecord( mnScTab ) ); + + // list of NOTE records, generated by the cell table + XclExpRecordRef xNotes = mxCellTable->CreateRecord( EXC_ID_NOTE ); + XclExpRecordList< XclExpNote >* xNoteList = dynamic_cast< XclExpRecordList< XclExpNote >* >( xNotes.get() ); + if( xNoteList != NULL ) + aRecList.AppendNewRecord( new XclExpComments( mnScTab, *xNoteList ) ); + + // web queries + Add( new XclExpWebQueryBuffer( GetRoot() ) ); + + lcl_AddScenariosAndFilters( aRecList, GetRoot(), mnScTab ); + + // MERGEDCELLS record, generated by the cell table + aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_MERGEDCELLS ) ); + + // conditional formats + Add( new XclExpCondFormatBuffer( GetRoot() ) ); + + if( HasVbaStorage() ) + if( nCodeNameIdx < GetExtDocOptions().GetCodeNameCount() ) + Add( new XclCodename( GetExtDocOptions().GetCodeName( nCodeNameIdx ) ) ); + + // data validation (DVAL and list of DV records), generated by the cell table + aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_DVAL ) ); + + // list of HLINK records, generated by the cell table + XclExpRecordRef xHyperlinks = mxCellTable->CreateRecord( EXC_ID_HLINK ); + XclExpHyperlinkList* xHyperlinkList = dynamic_cast<XclExpHyperlinkList*>(xHyperlinks.get()); + if( xHyperlinkList != NULL && !xHyperlinkList->IsEmpty() ) + { + aRecList.AppendNewRecord( new XclExpXmlStartElementRecord( XML_hyperlinks ) ); + aRecList.AppendRecord( xHyperlinks ); + aRecList.AppendNewRecord( new XclExpXmlEndElementRecord( XML_hyperlinks ) ); + } + + aRecList.AppendRecord( xPageSett ); + + // change tracking + if( rR.pUserBViewList ) + { + for( const XclExpUserBView* pBView = rR.pUserBViewList->First(); pBView; pBView = rR.pUserBViewList->Next() ) + { + Add( new XclExpUsersViewBegin( pBView->GetGUID(), nExcTab ) ); + Add( new XclExpUsersViewEnd ); + } + } + + // EOF + Add( new ExcEof ); +} + + +void ExcTable::FillAsEmptyTable( SCTAB nCodeNameIdx ) +{ + InitializeTable( mnScTab ); + + if( HasVbaStorage() && (nCodeNameIdx < GetExtDocOptions().GetCodeNameCount()) ) + { + if( GetBiff() <= EXC_BIFF5 ) + { + Add( new ExcBof ); + } + else + { + Add( new ExcBof8 ); + Add( new XclCodename( GetExtDocOptions().GetCodeName( nCodeNameIdx ) ) ); + } + // sheet view settings: WINDOW2, SCL, PANE, SELECTION + aRecList.AppendNewRecord( new XclExpTabViewSettings( GetRoot(), mnScTab ) ); + Add( new ExcEof ); + } +} + + +void ExcTable::Write( XclExpStream& rStrm ) +{ + SetCurrScTab( mnScTab ); + if( mxCellTable.get() ) + mxCellTable->Finalize(); + aRecList.Save( rStrm ); +} + + +void ExcTable::WriteXml( XclExpXmlStream& rStrm ) +{ + if (GetTabInfo().IsExportTab( mnScTab ) ) + { + // worksheet export + String sSheetName = XclXmlUtils::GetStreamName( "xl/", "worksheets/sheet", mnScTab+1 ); + + sax_fastparser::FSHelperPtr pWorksheet = rStrm.GetStreamForPath( sSheetName ); + + rStrm.PushStream( pWorksheet ); + + pWorksheet->startElement( XML_worksheet, + XML_xmlns, "http://schemas.openxmlformats.org/spreadsheetml/2006/main", + FSNS( XML_xmlns, XML_r ), "http://schemas.openxmlformats.org/officeDocument/2006/relationships", + FSEND ); + } + + SetCurrScTab( mnScTab ); + if( mxCellTable.get() ) + mxCellTable->Finalize(); + aRecList.SaveXml( rStrm ); + + if (GetTabInfo().IsExportTab( mnScTab ) ) + { + rStrm.GetCurrentStream()->endElement( XML_worksheet ); + rStrm.PopStream(); + } +} + + +ExcDocument::ExcDocument( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + aHeader( rRoot ), + pExpChangeTrack( NULL ) +{ +} + + +ExcDocument::~ExcDocument() +{ + maTableList.RemoveAllRecords(); //! for the following assertion + delete pExpChangeTrack; +} + + +void ExcDocument::ReadDoc( void ) +{ + InitializeConvert(); + + aHeader.FillAsHeader( maBoundsheetList ); + + SCTAB nScTab = 0, nScTabCount = GetTabInfo().GetScTabCount(); + SCTAB nCodeNameIdx = 0, nCodeNameCount = GetExtDocOptions().GetCodeNameCount(); + + for( ; nScTab < nScTabCount; ++nScTab ) + { + if( GetTabInfo().IsExportTab( nScTab ) ) + { + ExcTableList::RecordRefType xTab( new ExcTable( GetRoot(), nScTab ) ); + maTableList.AppendRecord( xTab ); + xTab->FillAsTable( nCodeNameIdx ); + ++nCodeNameIdx; + } + } + for( ; nCodeNameIdx < nCodeNameCount; ++nScTab, ++nCodeNameIdx ) + { + ExcTableList::RecordRefType xTab( new ExcTable( GetRoot(), nScTab ) ); + maTableList.AppendRecord( xTab ); + xTab->FillAsEmptyTable( nCodeNameIdx ); + } + + if ( GetBiff() == EXC_BIFF8 ) + { + // complete temporary Escher stream + GetObjectManager().EndDocument(); + + // change tracking + if ( GetDoc().GetChangeTrack() ) + pExpChangeTrack = new XclExpChangeTrack( GetRoot() ); + } +} + + +void ExcDocument::Write( SvStream& rSvStrm ) +{ + if( !maTableList.IsEmpty() ) + { + InitializeSave(); + + XclExpStream aXclStrm( rSvStrm, GetRoot() ); + + aHeader.Write( aXclStrm ); + + DBG_ASSERT( maTableList.GetSize() == maBoundsheetList.GetSize(), + "ExcDocument::Write - different number of sheets and BOUNDSHEET records" ); + + for( size_t nTab = 0, nTabCount = maTableList.GetSize(); nTab < nTabCount; ++nTab ) + { + // set current stream position in BOUNDSHEET record + ExcBoundsheetRef xBoundsheet = maBoundsheetList.GetRecord( nTab ); + if( xBoundsheet.get() ) + xBoundsheet->SetStreamPos( aXclStrm.GetSvStreamPos() ); + // write the table + maTableList.GetRecord( nTab )->Write( aXclStrm ); + } + + // write the table stream positions into the BOUNDSHEET records + for( size_t nBSheet = 0, nBSheetCount = maBoundsheetList.GetSize(); nBSheet < nBSheetCount; ++nBSheet ) + maBoundsheetList.GetRecord( nBSheet )->UpdateStreamPos( aXclStrm ); + } + if( pExpChangeTrack ) + pExpChangeTrack->Write(); +} + +void ExcDocument::WriteXml( SvStream& rStrm ) +{ + if( !maTableList.IsEmpty() ) + { + InitializeSave(); + + XclExpXmlStream aStrm( ::comphelper::getProcessServiceFactory(), rStrm, GetRoot() ); + + sax_fastparser::FSHelperPtr& rWorkbook = aStrm.GetCurrentStream(); + rWorkbook->startElement( XML_workbook, + XML_xmlns, "http://schemas.openxmlformats.org/spreadsheetml/2006/main", + FSNS(XML_xmlns, XML_r), "http://schemas.openxmlformats.org/officeDocument/2006/relationships", + FSEND ); + rWorkbook->singleElement( XML_fileVersion, + XML_appName, "Calc", + // OOXTODO: XML_codeName + // OOXTODO: XML_lastEdited + // OOXTODO: XML_lowestEdited + // OOXTODO: XML_rupBuild + FSEND ); + + aHeader.WriteXml( aStrm ); + + for( size_t nTab = 0, nTabCount = maTableList.GetSize(); nTab < nTabCount; ++nTab ) + { + // set current stream position in BOUNDSHEET record +#if 0 + ExcBoundsheetRef xBoundsheet = maBoundsheetList.GetRecord( nTab ); + if( xBoundsheet.get() ) + xBoundsheet->SetStreamPos( aXclStrm.GetSvStreamPos() ); +#endif + // write the table + maTableList.GetRecord( nTab )->WriteXml( aStrm ); + } + + rWorkbook->endElement( XML_workbook ); + rWorkbook.reset(); + aStrm.commitStorage(); + } +#if 0 + if( pExpChangeTrack ) + pExpChangeTrack->WriteXml(); +#endif +} + diff --git a/sc/source/filter/excel/excel.cxx b/sc/source/filter/excel/excel.cxx new file mode 100644 index 000000000000..3a43a23cd624 --- /dev/null +++ b/sc/source/filter/excel/excel.cxx @@ -0,0 +1,304 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include <sfx2/docfile.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/app.hxx> +#include <sot/storage.hxx> +#include <sot/exchange.hxx> +#include <tools/globname.hxx> +#include <comphelper/mediadescriptor.hxx> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/document/XImporter.hpp> +#include "scitems.hxx" +#include <svl/stritem.hxx> +#include "filter.hxx" +#include "document.hxx" +#include "xistream.hxx" + +#include "scerrors.hxx" +#include "root.hxx" +#include "imp_op.hxx" +#include "excimp8.hxx" +#include "exp_op.hxx" + + +FltError ScFormatFilterPluginImpl::ScImportExcel( SfxMedium& rMedium, ScDocument* pDocument, const EXCIMPFORMAT eFormat ) +{ + // check the passed Calc document + DBG_ASSERT( pDocument, "::ScImportExcel - no document" ); + if( !pDocument ) return eERR_INTERN; // should not happen + + /* Import all BIFF versions regardless on eFormat, needed for import of + external cells (file type detection returns Excel4.0). */ + if( (eFormat != EIF_AUTO) && (eFormat != EIF_BIFF_LE4) && (eFormat != EIF_BIFF5) && (eFormat != EIF_BIFF8) ) + { + DBG_ERRORFILE( "::ScImportExcel - wrong file format specification" ); + return eERR_FORMAT; + } + + // check the input stream from medium + SvStream* pMedStrm = rMedium.GetInStream(); + DBG_ASSERT( pMedStrm, "::ScImportExcel - medium without input stream" ); + if( !pMedStrm ) return eERR_OPEN; // should not happen + +#if OSL_DEBUG_LEVEL > 0 + using namespace ::com::sun::star; + using namespace ::comphelper; + + /* Environment variable "OOO_OOXBIFFFILTER": + - "1" = use new OOX filter for import; + - undef/other = use old sc filter for import (OOX only as file dumper). */ + const sal_Char* pcFileName = ::getenv( "OOO_OOXBIFFFILTER" ); + bool bUseOoxFilter = pcFileName && (*pcFileName == '1') && (*(pcFileName + 1) == 0); + if( SfxObjectShell* pDocShell = pDocument->GetDocumentShell() ) try + { + uno::Reference< lang::XComponent > xComponent( pDocShell->GetModel(), uno::UNO_QUERY_THROW ); + + uno::Sequence< beans::NamedValue > aArgSeq( 1 ); + aArgSeq[ 0 ].Name = CREATE_OUSTRING( "UseBiffFilter" ); + aArgSeq[ 0 ].Value <<= bUseOoxFilter; + + uno::Sequence< uno::Any > aArgs( 2 ); + aArgs[ 0 ] <<= getProcessServiceFactory(); + aArgs[ 1 ] <<= aArgSeq; + uno::Reference< document::XImporter > xImporter( ScfApiHelper::CreateInstanceWithArgs( + CREATE_OUSTRING( "com.sun.star.comp.oox.ExcelBiffFilter" ), aArgs ), uno::UNO_QUERY_THROW ); + xImporter->setTargetDocument( xComponent ); + + MediaDescriptor aMediaDesc; + SfxItemSet* pItemSet = rMedium.GetItemSet(); + if( pItemSet ) + { + if( const SfxStringItem* pItem = static_cast< const SfxStringItem* >( pItemSet->GetItem( SID_FILE_NAME ) ) ) + aMediaDesc[ MediaDescriptor::PROP_URL() ] <<= ::rtl::OUString( pItem->GetValue() ); + if( const SfxStringItem* pItem = static_cast< const SfxStringItem* >( pItemSet->GetItem( SID_PASSWORD ) ) ) + aMediaDesc[ MediaDescriptor::PROP_PASSWORD() ] <<= ::rtl::OUString( pItem->GetValue() ); + } + aMediaDesc[ MediaDescriptor::PROP_INPUTSTREAM() ] <<= rMedium.GetInputStream(); + aMediaDesc[ MediaDescriptor::PROP_INTERACTIONHANDLER() ] <<= rMedium.GetInteractionHandler(); + + // call the filter + uno::Reference< document::XFilter > xFilter( xImporter, uno::UNO_QUERY_THROW ); + bool bResult = xFilter->filter( aMediaDesc.getAsConstPropertyValueList() ); + + // if filter returns false, document is invalid, or dumper has disabled import -> exit here + if( !bResult ) + return ERRCODE_ABORT; + + // if OOX filter has been used, exit with OK code + if( bUseOoxFilter ) + return eERR_OK; + } + catch( uno::Exception& ) + { + if( bUseOoxFilter ) + return ERRCODE_ABORT; + // else ignore exception and import the document with this filter + } +#endif + + SvStream* pBookStrm = 0; // The "Book"/"Workbook" stream containing main data. + XclBiff eBiff = EXC_BIFF_UNKNOWN; // The BIFF version of the main stream. + + // try to open an OLE storage + SotStorageRef xRootStrg; + SotStorageStreamRef xStrgStrm; + if( SotStorage::IsStorageFile( pMedStrm ) ) + { + xRootStrg = new SotStorage( pMedStrm, FALSE ); + if( xRootStrg->GetError() ) + xRootStrg = 0; + } + + // try to open "Book" or "Workbook" stream in OLE storage + if( xRootStrg.Is() ) + { + // try to open the "Book" stream + SotStorageStreamRef xBookStrm = ScfTools::OpenStorageStreamRead( xRootStrg, EXC_STREAM_BOOK ); + XclBiff eBookBiff = xBookStrm.Is() ? XclImpStream::DetectBiffVersion( *xBookStrm ) : EXC_BIFF_UNKNOWN; + + // try to open the "Workbook" stream + SotStorageStreamRef xWorkbookStrm = ScfTools::OpenStorageStreamRead( xRootStrg, EXC_STREAM_WORKBOOK ); + XclBiff eWorkbookBiff = xWorkbookStrm.Is() ? XclImpStream::DetectBiffVersion( *xWorkbookStrm ) : EXC_BIFF_UNKNOWN; + + // decide which stream to use + if( (eWorkbookBiff != EXC_BIFF_UNKNOWN) && ((eBookBiff == EXC_BIFF_UNKNOWN) || (eWorkbookBiff > eBookBiff)) ) + { + /* Only "Workbook" stream exists; or both streams exist, + and "Workbook" has higher BIFF version than "Book" stream. */ + xStrgStrm = xWorkbookStrm; + eBiff = eWorkbookBiff; + } + else if( eBookBiff != EXC_BIFF_UNKNOWN ) + { + /* Only "Book" stream exists; or both streams exist, + and "Book" has higher BIFF version than "Workbook" stream. */ + xStrgStrm = xBookStrm; + eBiff = eBookBiff; + } + + pBookStrm = xStrgStrm; + } + + // no "Book" or "Workbook" stream found, try plain input stream from medium (even for BIFF5+) + if( !pBookStrm ) + { + eBiff = XclImpStream::DetectBiffVersion( *pMedStrm ); + if( eBiff != EXC_BIFF_UNKNOWN ) + pBookStrm = pMedStrm; + } + + // try to import the file + FltError eRet = eERR_UNKN_BIFF; + if( pBookStrm ) + { + pBookStrm->SetBufferSize( 0x8000 ); // still needed? + + XclImpRootData aImpData( eBiff, rMedium, xRootStrg, *pDocument, RTL_TEXTENCODING_MS_1252 ); + ::std::auto_ptr< ImportExcel > xFilter; + switch( eBiff ) + { + case EXC_BIFF2: + case EXC_BIFF3: + case EXC_BIFF4: + case EXC_BIFF5: + xFilter.reset( new ImportExcel( aImpData, *pBookStrm ) ); + break; + case EXC_BIFF8: + xFilter.reset( new ImportExcel8( aImpData, *pBookStrm ) ); + break; + default: DBG_ERROR_BIFF(); + } + + eRet = xFilter.get() ? xFilter->Read() : eERR_INTERN; + } + + return eRet; +} + + +static FltError lcl_ExportExcelBiff( SfxMedium& rMedium, ScDocument *pDocument, + SvStream* pMedStrm, BOOL bBiff8, CharSet eNach ) +{ + // try to open an OLE storage + SotStorageRef xRootStrg = new SotStorage( pMedStrm, FALSE ); + if( xRootStrg->GetError() ) return eERR_OPEN; + + // create BIFF dependent strings + String aStrmName, aClipName, aClassName; + if( bBiff8 ) + { + aStrmName = EXC_STREAM_WORKBOOK; + aClipName = CREATE_STRING( "Biff8" ); + aClassName = CREATE_STRING( "Microsoft Excel 97-Tabelle" ); + } + else + { + aStrmName = EXC_STREAM_BOOK; + aClipName = CREATE_STRING( "Biff5" ); + aClassName = CREATE_STRING( "Microsoft Excel 5.0-Tabelle" ); + } + + // open the "Book"/"Workbook" stream + SotStorageStreamRef xStrgStrm = ScfTools::OpenStorageStreamWrite( xRootStrg, aStrmName ); + if( !xStrgStrm.Is() || xStrgStrm->GetError() ) return eERR_OPEN; + + xStrgStrm->SetBufferSize( 0x8000 ); // still needed? + + FltError eRet = eERR_UNKN_BIFF; + XclExpRootData aExpData( bBiff8 ? EXC_BIFF8 : EXC_BIFF5, rMedium, xRootStrg, *pDocument, eNach ); + if ( bBiff8 ) + { + ExportBiff8 aFilter( aExpData, *xStrgStrm ); + eRet = aFilter.Write(); + } + else + { + ExportBiff5 aFilter( aExpData, *xStrgStrm ); + eRet = aFilter.Write(); + } + + if( eRet == eERR_RNGOVRFLW ) + eRet = SCWARN_EXPORT_MAXROW; + + SvGlobalName aGlobName( 0x00020810, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 ); + sal_uInt32 nClip = SotExchange::RegisterFormatName( aClipName ); + xRootStrg->SetClass( aGlobName, nClip, aClassName ); + + xStrgStrm->Commit(); + xRootStrg->Commit(); + + return eRet; +} + +static FltError lcl_ExportExcel2007Xml( SfxMedium& rMedium, ScDocument *pDocument, + SvStream* pMedStrm, CharSet eNach ) +{ + SotStorageRef xRootStrg = (SotStorage*) 0; + + XclExpRootData aExpData( EXC_BIFF8, rMedium, xRootStrg, *pDocument, eNach ); + aExpData.meOutput = EXC_OUTPUT_XML_2007; + + ExportXml2007 aFilter( aExpData, *pMedStrm ); + + FltError eRet = aFilter.Write(); + + return eRet; +} + +FltError ScFormatFilterPluginImpl::ScExportExcel5( SfxMedium& rMedium, ScDocument *pDocument, + ExportFormatExcel eFormat, CharSet eNach ) +{ + if( eFormat != ExpBiff5 && eFormat != ExpBiff8 && eFormat != Exp2007Xml ) + return eERR_NI; + + // check the passed Calc document + DBG_ASSERT( pDocument, "::ScImportExcel - no document" ); + if( !pDocument ) return eERR_INTERN; // should not happen + + // check the output stream from medium + SvStream* pMedStrm = rMedium.GetOutStream(); + DBG_ASSERT( pMedStrm, "::ScExportExcel5 - medium without output stream" ); + if( !pMedStrm ) return eERR_OPEN; // should not happen + + FltError eRet = eERR_UNKN_BIFF; + if( eFormat == ExpBiff5 || eFormat == ExpBiff8 ) + eRet = lcl_ExportExcelBiff( rMedium, pDocument, pMedStrm, eFormat == ExpBiff8, eNach ); + else if( eFormat == Exp2007Xml ) + eRet = lcl_ExportExcel2007Xml( rMedium, pDocument, pMedStrm, eNach ); + + return eRet; +} + + + diff --git a/sc/source/filter/excel/excform.cxx b/sc/source/filter/excel/excform.cxx new file mode 100644 index 000000000000..0652363e8822 --- /dev/null +++ b/sc/source/filter/excel/excform.cxx @@ -0,0 +1,2013 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "excform.hxx" +#include <osl/endian.h> + +#include "cell.hxx" +#include "document.hxx" +#include "rangenam.hxx" +#include "global.hxx" +#include "formula/errorcodes.hxx" + +#include "imp_op.hxx" +#include "root.hxx" +#include "xltracer.hxx" +#include "xihelper.hxx" +#include "xilink.hxx" +#include "xiname.hxx" + +using ::std::vector; + +const UINT16 ExcelToSc::nRowMask = 0x3FFF; +const UINT16 ExcelToSc::nLastInd = 399; + + + + +void ImportExcel::Formula25() +{ + XclAddress aXclPos; + UINT16 nXF = 0, nFormLen; + double fCurVal; + BYTE nAttr0, nFlag0; + BOOL bShrFmla; + + aIn >> aXclPos; + + if( GetBiff() == EXC_BIFF2 ) + {// BIFF2 + BYTE nDummy; + + aIn.Ignore( 3 ); + + aIn >> fCurVal; + aIn.Ignore( 1 ); + aIn >> nDummy; + nFormLen = nDummy; + bShrFmla = FALSE; + nAttr0 = 0x01; // Always calculate + } + else + {// BIFF5 + aIn >> nXF >> fCurVal >> nFlag0; + aIn.Ignore( 5 ); + + aIn >> nFormLen; + + bShrFmla = nFlag0 & 0x08; // shared or not shared + } + + nLastXF = nXF; + + Formula( aXclPos, nXF, nFormLen, fCurVal, bShrFmla ); +} + + +void ImportExcel::Formula3() +{ + Formula4(); +} + + +void ImportExcel::Formula4() +{ + XclAddress aXclPos; + UINT16 nXF, nFormLen; + double fCurVal; + BYTE nFlag0; + + aIn >> aXclPos >> nXF >> fCurVal >> nFlag0; + aIn.Ignore( 1 ); + aIn >> nFormLen; + + nLastXF = nXF; + + Formula( aXclPos, nXF, nFormLen, fCurVal, FALSE ); +} + + +void ImportExcel::Formula( const XclAddress& rXclPos, + UINT16 nXF, UINT16 nFormLen, double& rCurVal, BOOL bShrFmla ) +{ + ConvErr eErr = ConvOK; + + ScAddress aScPos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScPos, rXclPos, GetCurrScTab(), true ) ) + { + // jetzt steht Lesemarke auf Formel, Laenge in nFormLen + const ScTokenArray* pErgebnis = 0; + BOOL bConvert; + + pFormConv->Reset( aScPos ); + + if( bShrFmla ) + bConvert = !pFormConv->GetShrFmla( pErgebnis, maStrm, nFormLen ); + else + bConvert = TRUE; + + if( bConvert ) + eErr = pFormConv->Convert( pErgebnis, maStrm, nFormLen, true, FT_CellFormula); + + ScFormulaCell* pZelle = NULL; + + if( pErgebnis ) + { + pZelle = new ScFormulaCell( pD, aScPos, pErgebnis ); + pD->PutCell( aScPos.Col(), aScPos.Row(), aScPos.Tab(), pZelle, (BOOL)TRUE ); + } + else + { + CellType eCellType; + ScBaseCell* pBaseCell; + pD->GetCellType( aScPos.Col(), aScPos.Row(), aScPos.Tab(), eCellType ); + if( eCellType == CELLTYPE_FORMULA ) + { + pD->GetCell( aScPos.Col(), aScPos.Row(), aScPos.Tab(), pBaseCell ); + pZelle = ( ScFormulaCell* ) pBaseCell; + if( pZelle ) + pZelle->AddRecalcMode( RECALCMODE_ONLOAD_ONCE ); + } + } + + if( pZelle ) + { + if( eErr != ConvOK ) + ExcelToSc::SetError( *pZelle, eErr ); +#if 0 + else + ExcelToSc::SetCurVal( *pZelle, rCurVal ); +#else + (void)rCurVal; +#endif + } + + GetXFRangeBuffer().SetXF( aScPos, nXF ); + } +} + + + + +ExcelToSc::ExcelToSc( const XclImpRoot& rRoot ) : + ExcelConverterBase( 512 ), + XclImpRoot( rRoot ), + maFuncProv( rRoot ), + meBiff( rRoot.GetBiff() ) +{ +} + +ExcelToSc::~ExcelToSc() +{ +} + +void ExcelToSc::GetDummy( const ScTokenArray*& pErgebnis ) +{ + aPool.Store( CREATE_STRING( "Dummy()" ) ); + aPool >> aStack; + pErgebnis = aPool[ aStack.Get() ]; +} + + +// if bAllowArrays is false stream seeks to first byte after <nFormulaLen> +// otherwise it will seek to the first byte after the additional content (eg +// inline arrays) following <nFormulaLen> +ConvErr ExcelToSc::Convert( const ScTokenArray*& pErgebnis, XclImpStream& aIn, sal_Size nFormulaLen, bool bAllowArrays, const FORMULA_TYPE eFT ) +{ + RootData& rR = GetOldRoot(); + BYTE nOp, nLen, nByte; + UINT16 nUINT16; + INT16 nINT16; + double fDouble; + String aString; + BOOL bError = FALSE; + BOOL bArrayFormula = FALSE; + TokenId nMerk0; + const BOOL bRangeName = eFT == FT_RangeName; + const BOOL bSharedFormula = eFT == FT_SharedFormula; + const BOOL bRNorSF = bRangeName || bSharedFormula; + + ScSingleRefData aSRD; + ScComplexRefData aCRD; + ExtensionTypeVec aExtensions; + + bExternName = FALSE; + + if( eStatus != ConvOK ) + { + aIn.Ignore( nFormulaLen ); + return eStatus; + } + + if( nFormulaLen == 0 ) + { + aPool.Store( CREATE_STRING( "-/-" ) ); + aPool >> aStack; + pErgebnis = aPool[ aStack.Get() ]; + return ConvOK; + } + + sal_Size nEndPos = aIn.GetRecPos() + nFormulaLen; + + while( (aIn.GetRecPos() < nEndPos) && !bError ) + { + aIn >> nOp; + + // #98524# always reset flags + aSRD.InitFlags(); + aCRD.InitFlags(); + + switch( nOp ) // Buch Seite: + { // SDK4 SDK5 + case 0x01: // Array Formula [325 ] + // Array Formula or Shared Formula [ 277] + case 0x02: // Data Table [325 277] + nUINT16 = 3; + + if( meBiff != EXC_BIFF2 ) + nUINT16++; + + aIn.Ignore( nUINT16 ); + + bArrayFormula = TRUE; + break; + case 0x03: // Addition [312 264] + aStack >> nMerk0; + aPool << aStack << ocAdd << nMerk0; + aPool >> aStack; + break; + case 0x04: // Subtraction [313 264] + // SECOMD-TOP minus TOP + aStack >> nMerk0; + aPool << aStack << ocSub << nMerk0; + aPool >> aStack; + break; + case 0x05: // Multiplication [313 264] + aStack >> nMerk0; + aPool << aStack << ocMul << nMerk0; + aPool >> aStack; + break; + case 0x06: // Division [313 264] + // divide TOP by SECOND-TOP + aStack >> nMerk0; + aPool << aStack << ocDiv << nMerk0; + aPool >> aStack; + break; + case 0x07: // Exponetiation [313 265] + // raise SECOND-TOP to power of TOP + aStack >> nMerk0; + aPool << aStack << ocPow << nMerk0; + aPool >> aStack; + break; + case 0x08: // Concatenation [313 265] + // append TOP to SECOND-TOP + aStack >> nMerk0; + aPool << aStack << ocAmpersand << nMerk0; + aPool >> aStack; + break; + case 0x09: // Less Than [313 265] + // SECOND-TOP < TOP + aStack >> nMerk0; + aPool << aStack << ocLess << nMerk0; + aPool >> aStack; + break; + case 0x0A: // Less Than or Equal [313 265] + // SECOND-TOP <= TOP + aStack >> nMerk0; + aPool << aStack << ocLessEqual << nMerk0; + aPool >> aStack; + break; + case 0x0B: // Equal [313 265] + // SECOND-TOP == TOP + aStack >> nMerk0; + aPool << aStack << ocEqual << nMerk0; + aPool >> aStack; + break; + case 0x0C: // Greater Than or Equal [313 265] + // SECOND-TOP == TOP + aStack >> nMerk0; + aPool << aStack << ocGreaterEqual << nMerk0; + aPool >> aStack; + break; + case 0x0D: // Greater Than [313 265] + // SECOND-TOP == TOP + aStack >> nMerk0; + aPool << aStack << ocGreater << nMerk0; + aPool >> aStack; + break; + case 0x0E: // Not Equal [313 265] + // SECOND-TOP == TOP + aStack >> nMerk0; + aPool << aStack << ocNotEqual << nMerk0; + aPool >> aStack; + break; + case 0x0F: // Intersection [314 265] + aStack >> nMerk0; + aPool << aStack << ocIntersect << nMerk0; + aPool >> aStack; + break; + case 0x10: // Union [314 265] + // ocSep behelfsweise statt 'ocUnion' + aStack >> nMerk0; +//#100928# aPool << ocOpen << aStack << ocSep << nMerk0 << ocClose; + aPool << aStack << ocSep << nMerk0; + // doesn't fit exactly, but is more Excel-like + aPool >> aStack; + break; + case 0x11: // Range [314 265] + aStack >> nMerk0; + aPool << aStack << ocRange << nMerk0; + aPool >> aStack; + break; + case 0x12: // Unary Plus [312 264] + aPool << ocAdd << aStack; + aPool >> aStack; + break; + case 0x13: // Unary Minus [312 264] + aPool << ocNegSub << aStack; + aPool >> aStack; + break; + case 0x14: // Percent Sign [312 264] + aPool << aStack << ocPercentSign; + aPool >> aStack; + break; + case 0x15: // Parenthesis [326 278] + aPool << ocOpen << aStack << ocClose; + aPool >> aStack; + break; + case 0x16: // Missing Argument [314 266] + aPool << ocMissing; + aPool >> aStack; + GetTracer().TraceFormulaMissingArg(); + break; + case 0x17: // String Constant [314 266] + aIn >> nLen; + aString = aIn.ReadRawByteString( nLen ); + + aStack << aPool.Store( aString ); + break; + case 0x19: // Special Attribute [327 279] + { + UINT16 nData, nFakt; + BYTE nOpt; + + aIn >> nOpt; + + if( meBiff == EXC_BIFF2 ) + { + nData = aIn.ReaduInt8(); + nFakt = 1; + } + else + { + aIn >> nData; + nFakt = 2; + } + + if( nOpt & 0x04 ) + {// nFakt -> Bytes oder Words ueberlesen AttrChoose + nData++; + aIn.Ignore( nData * nFakt ); + } + else if( nOpt & 0x10 ) // AttrSum + DoMulArgs( ocSum, 1 ); + } + break; + case 0x1A: // External Reference [330 ] + switch( meBiff ) + { + case EXC_BIFF2: aIn.Ignore( 7 ); break; + case EXC_BIFF3: + case EXC_BIFF4: aIn.Ignore( 10 ); break; + case EXC_BIFF5: + DBG_WARNING( "-ExcelToSc::Convert(): 0x1A gibt's nicht in Biff5!" ); + default: + DBG_WARNING( "-ExcelToSc::Convert(): Ein wenig vergesslich, was?" ); + } + break; + case 0x1B: // End External Reference [330 ] + switch( meBiff ) + { + case EXC_BIFF2: aIn.Ignore( 3 ); break; + case EXC_BIFF3: + case EXC_BIFF4: aIn.Ignore( 4 ); break; + case EXC_BIFF5: + DBG_WARNING( "-ExcelToSc::Convert(): 0x1B gibt's nicht in Biff5!" ); + default: + DBG_WARNING( "-ExcelToSc::Convert(): Ein wenig vergesslich, was?" ); + } + break; + case 0x1C: // Error Value [314 266] + { + aIn >> nByte; +#if 0 // erAck + aPool.StoreError( XclTools::GetScErrorCode( nByte ) ); +#else + DefTokenId eOc; + switch( nByte ) + { + case EXC_ERR_NULL: + case EXC_ERR_DIV0: + case EXC_ERR_VALUE: + case EXC_ERR_REF: + case EXC_ERR_NAME: + case EXC_ERR_NUM: eOc = ocStop; break; + case EXC_ERR_NA: eOc = ocNotAvail; break; + default: eOc = ocNoName; + } + aPool << eOc; + if( eOc != ocStop ) + aPool << ocOpen << ocClose; +#endif + aPool >> aStack; + } + break; + case 0x1D: // Boolean [315 266] + aIn >> nByte; + if( nByte == 0 ) + aPool << ocFalse << ocOpen << ocClose; + else + aPool << ocTrue << ocOpen << ocClose; + aPool >> aStack; + break; + case 0x1E: // Integer [315 266] + aIn >> nUINT16; + aStack << aPool.Store( ( double ) nUINT16 ); + break; + case 0x1F: // Number [315 266] + aIn >> fDouble; + aStack << aPool.Store( fDouble ); + break; + case 0x40: + case 0x60: + case 0x20: // Array Constant [317 268] + aIn >> nByte >> nUINT16; + aIn.Ignore( (meBiff == EXC_BIFF2) ? 3 : 4 ); + if( bAllowArrays ) + { + aStack << aPool.StoreMatrix(); + aExtensions.push_back( EXTENSION_ARRAY ); + } + else + { + aPool << ocBad; + aPool >> aStack; + } + break; + case 0x41: + case 0x61: + case 0x21: // Function, Fixed Number of Arguments [333 282] + { + sal_uInt16 nXclFunc; + if( meBiff <= EXC_BIFF3 ) + nXclFunc = aIn.ReaduInt8(); + else + aIn >> nXclFunc; + if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) ) + DoMulArgs( pFuncInfo->meOpCode, pFuncInfo->mnMaxParamCount, pFuncInfo->mnMinParamCount ); + else + DoMulArgs( ocNoName, 0 ); + } + break; + case 0x42: + case 0x62: + case 0x22: // Function, Variable Number of Arg. [333 283] + { + sal_uInt16 nXclFunc; + sal_uInt8 nParamCount; + aIn >> nParamCount; + nParamCount &= 0x7F; + if( meBiff <= EXC_BIFF3 ) + nXclFunc = aIn.ReaduInt8(); + else + aIn >> nXclFunc; + if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) ) + DoMulArgs( pFuncInfo->meOpCode, nParamCount, pFuncInfo->mnMinParamCount ); + else + DoMulArgs( ocNoName, 0 ); + } + break; + case 0x43: + case 0x63: + case 0x23: // Name [318 269] + { + aIn >> nUINT16; + switch( meBiff ) + { + case EXC_BIFF2: aIn.Ignore( 5 ); break; + case EXC_BIFF3: + case EXC_BIFF4: aIn.Ignore( 8 ); break; + case EXC_BIFF5: aIn.Ignore( 12 ); break; + default: + DBG_ERROR( + "-ExcelToSc::Convert(): Ein wenig vergesslich, was?" ); + } + const XclImpName* pName = GetNameManager().GetName( nUINT16 ); + if(pName && !pName->GetScRangeData()) + aStack << aPool.Store( ocMacro, pName->GetXclName() ); + else + aStack << aPool.Store( nUINT16 ); + } + break; + case 0x44: + case 0x64: + case 0x24: // Cell Reference [319 270] + case 0x4A: + case 0x6A: + case 0x2A: // Deleted Cell Reference [323 273] + aIn >> nUINT16 >> nByte; + aSRD.nCol = static_cast<SCsCOL>(nByte); + aSRD.nRow = nUINT16 & 0x3FFF; + aSRD.nRelTab = 0; + aSRD.SetTabRel( TRUE ); + aSRD.SetFlag3D( bRangeName ); + + ExcRelToScRel( nUINT16, nByte, aSRD, bRangeName ); + + switch ( nOp ) + { + case 0x4A: + case 0x6A: + case 0x2A: // Deleted Cell Reference [323 273] + // no information which part is deleted, set both + aSRD.SetColDeleted( TRUE ); + aSRD.SetRowDeleted( TRUE ); + } + + aStack << aPool.Store( aSRD ); + break; + case 0x45: + case 0x65: + case 0x25: // Area Reference [320 270] + case 0x4B: + case 0x6B: + case 0x2B: // Deleted Area Refernce [323 273] + { + UINT16 nRowFirst, nRowLast; + UINT8 nColFirst, nColLast; + ScSingleRefData& rSRef1 = aCRD.Ref1; + ScSingleRefData& rSRef2 = aCRD.Ref2; + + aIn >> nRowFirst >> nRowLast >> nColFirst >> nColLast; + + rSRef1.nRelTab = rSRef2.nRelTab = 0; + rSRef1.SetTabRel( TRUE ); + rSRef2.SetTabRel( TRUE ); + rSRef1.SetFlag3D( bRangeName ); + rSRef2.SetFlag3D( bRangeName ); + + ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName ); + ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName ); + + if( IsComplColRange( nColFirst, nColLast ) ) + SetComplCol( aCRD ); + else if( IsComplRowRange( nRowFirst, nRowLast ) ) + SetComplRow( aCRD ); + + switch ( nOp ) + { + case 0x4B: + case 0x6B: + case 0x2B: // Deleted Area Refernce [323 273] + // no information which part is deleted, set all + rSRef1.SetColDeleted( TRUE ); + rSRef1.SetRowDeleted( TRUE ); + rSRef2.SetColDeleted( TRUE ); + rSRef2.SetRowDeleted( TRUE ); + } + + aStack << aPool.Store( aCRD ); + } + break; + case 0x46: + case 0x66: + case 0x26: // Constant Reference Subexpression [321 271] + aExtensions.push_back( EXTENSION_MEMAREA ); + // fall through + + case 0x47: + case 0x67: + case 0x27: // Erroneous Constant Reference Subexpr. [322 272] + case 0x48: + case 0x68: + case 0x28: // Incomplete Constant Reference Subexpr.[331 281] + aIn.Ignore( (meBiff == EXC_BIFF2) ? 4 : 6 ); + break; + case 0x4C: + case 0x6C: + case 0x2C: // Cell Reference Within a Name [323 ] + // Cell Reference Within a Shared Formula[ 273] + { + aIn >> nUINT16 >> nByte; // >> Attribute, Row >> Col + + aSRD.nRelTab = 0; + aSRD.SetTabRel( TRUE ); + aSRD.SetFlag3D( bRangeName ); + + ExcRelToScRel( nUINT16, nByte, aSRD, bRNorSF ); + + aStack << aPool.Store( aSRD ); + } + break; + case 0x4D: + case 0x6D: + case 0x2D: // Area Reference Within a Name [324 ] + { // Area Reference Within a Shared Formula[ 274] + UINT16 nRowFirst, nRowLast; + UINT8 nColFirst, nColLast; + + aCRD.Ref1.nRelTab = aCRD.Ref2.nRelTab = 0; + aCRD.Ref1.SetTabRel( TRUE ); + aCRD.Ref2.SetTabRel( TRUE ); + aCRD.Ref1.SetFlag3D( bRangeName ); + aCRD.Ref2.SetFlag3D( bRangeName ); + + aIn >> nRowFirst >> nRowLast >> nColFirst >> nColLast; + + ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF ); + ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRNorSF ); + + if( IsComplColRange( nColFirst, nColLast ) ) + SetComplCol( aCRD ); + else if( IsComplRowRange( nRowFirst, nRowLast ) ) + SetComplRow( aCRD ); + + aStack << aPool.Store( aCRD ); + } + break; + case 0x49: + case 0x69: + case 0x29: // Variable Reference Subexpression [331 281] + case 0x4E: + case 0x6E: + case 0x2E: // Reference Subexpression Within a Name [332 282] + case 0x4F: + case 0x6F: + case 0x2F: // Incomplete Reference Subexpression... [332 282] + aIn.Ignore( (meBiff == EXC_BIFF2) ? 1 : 2 ); + break; + case 0x58: + case 0x78: + case 0x38: // Command-Equivalent Function [333 ] + aString.AssignAscii( "COMM_EQU_FUNC" ); + aIn >> nByte; + aString += String::CreateFromInt32( nByte ); + aIn >> nByte; + aStack << aPool.Store( aString ); + DoMulArgs( ocPush, nByte + 1 ); + break; + case 0x59: + case 0x79: + case 0x39: // Name or External Name [ 275] + aIn >> nINT16; + aIn.Ignore( 8 ); + aIn >> nUINT16; + if( nINT16 >= 0 ) + { + const ExtName* pExtName = rR.pExtNameBuff->GetNameByIndex( nINT16, nUINT16 ); + if( pExtName && pExtName->IsDDE() && + rR.pExtSheetBuff->IsLink( ( UINT16 ) nINT16 ) ) + { + String aAppl, aExtDoc; + TokenId nPar1, nPar2; + + rR.pExtSheetBuff->GetLink( ( UINT16 ) nINT16 , aAppl, aExtDoc ); + nPar1 = aPool.Store( aAppl ); + nPar2 = aPool.Store( aExtDoc ); + nMerk0 = aPool.Store( pExtName->aName ); + aPool << ocDde << ocOpen << nPar1 << ocSep << nPar2 << ocSep + << nMerk0 << ocClose; + + GetDoc().CreateDdeLink( aAppl, aExtDoc, pExtName->aName, SC_DDE_DEFAULT ); + } + else + aPool << ocBad; + + aPool >> aStack; + } + else + aStack << aPool.Store( nUINT16 ); + aIn.Ignore( 12 ); + break; + case 0x5A: + case 0x7A: + case 0x3A: // 3-D Cell Reference [ 275] + case 0x5C: + case 0x7C: + case 0x3C: // Deleted 3-D Cell Reference [ 277] + { + UINT16 nTabFirst, nTabLast, nRow; + INT16 nExtSheet; + BYTE nCol; + + aIn >> nExtSheet; + aIn.Ignore( 8 ); + aIn >> nTabFirst >> nTabLast >> nRow >> nCol; + + if( nExtSheet >= 0 ) + { // von extern + if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) ) + { + nTabFirst = nTabLast; + nExtSheet = 0; // gefunden + } + else + { + aPool << ocBad; + aPool >> aStack; + nExtSheet = 1; // verhindert Erzeugung einer SingleRef + } + } + + if( nExtSheet <= 0 ) + { // in aktuellem Workbook + aSRD.nTab = static_cast<SCTAB>(nTabFirst); + aSRD.SetFlag3D( TRUE ); + aSRD.SetTabRel( FALSE ); + + ExcRelToScRel( nRow, nCol, aSRD, bRangeName ); + + switch ( nOp ) + { + case 0x5C: + case 0x7C: + case 0x3C: // Deleted 3-D Cell Reference [ 277] + // no information which part is deleted, set both + aSRD.SetColDeleted( TRUE ); + aSRD.SetRowDeleted( TRUE ); + } + if ( !ValidTab(static_cast<SCTAB>(nTabFirst)) ) + aSRD.SetTabDeleted( TRUE ); + + if( nTabLast != nTabFirst ) + { + aCRD.Ref1 = aCRD.Ref2 = aSRD; + aCRD.Ref2.nTab = static_cast<SCTAB>(nTabLast); + aCRD.Ref2.SetTabDeleted( !ValidTab(static_cast<SCTAB>(nTabLast)) ); + aStack << aPool.Store( aCRD ); + } + else + aStack << aPool.Store( aSRD ); + } + } + + break; + case 0x5B: + case 0x7B: + case 0x3B: // 3-D Area Reference [ 276] + case 0x5D: + case 0x7D: + case 0x3D: // Deleted 3-D Area Reference [ 277] + { + UINT16 nTabFirst, nTabLast, nRowFirst, nRowLast; + INT16 nExtSheet; + BYTE nColFirst, nColLast; + + aIn >> nExtSheet; + aIn.Ignore( 8 ); + aIn >> nTabFirst >> nTabLast >> nRowFirst >> nRowLast + >> nColFirst >> nColLast; + + if( nExtSheet >= 0 ) + // von extern + { + if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) ) + { + nTabFirst = nTabLast; + nExtSheet = 0; // gefunden + } + else + { + aPool << ocBad; + aPool >> aStack; + nExtSheet = 1; // verhindert Erzeugung einer CompleteRef + } + } + + if( nExtSheet <= 0 ) + {// in aktuellem Workbook + // erster Teil des Bereichs + ScSingleRefData& rR1 = aCRD.Ref1; + ScSingleRefData& rR2 = aCRD.Ref2; + + rR1.nTab = static_cast<SCTAB>(nTabFirst); + rR2.nTab = static_cast<SCTAB>(nTabLast); + rR1.SetFlag3D( TRUE ); + rR1.SetTabRel( FALSE ); + rR2.SetFlag3D( nTabFirst != nTabLast ); + rR2.SetTabRel( FALSE ); + + ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName ); + ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName ); + + if( IsComplColRange( nColFirst, nColLast ) ) + SetComplCol( aCRD ); + else if( IsComplRowRange( nRowFirst, nRowLast ) ) + SetComplRow( aCRD ); + + switch ( nOp ) + { + case 0x5D: + case 0x7D: + case 0x3D: // Deleted 3-D Area Reference [ 277] + // no information which part is deleted, set all + rR1.SetColDeleted( TRUE ); + rR1.SetRowDeleted( TRUE ); + rR2.SetColDeleted( TRUE ); + rR2.SetRowDeleted( TRUE ); + } + if ( !ValidTab(static_cast<SCTAB>(nTabFirst)) ) + rR1.SetTabDeleted( TRUE ); + if ( !ValidTab(static_cast<SCTAB>(nTabLast)) ) + rR2.SetTabDeleted( TRUE ); + + aStack << aPool.Store( aCRD ); + }//ENDE in aktuellem Workbook + } + break; + default: bError = TRUE; + } + bError |= !aIn.IsValid(); + } + + ConvErr eRet; + + if( bError ) + { + aPool << ocBad; + aPool >> aStack; + pErgebnis = aPool[ aStack.Get() ]; + eRet = ConvErrNi; + } + else if( aIn.GetRecPos() != nEndPos ) + { + aPool << ocBad; + aPool >> aStack; + pErgebnis = aPool[ aStack.Get() ]; + eRet = ConvErrCount; + } + else if( bExternName ) + { + pErgebnis = aPool[ aStack.Get() ]; + eRet = ConvErrExternal; + } + else if( bArrayFormula ) + { + pErgebnis = NULL; + eRet = ConvOK; + } + else + { + pErgebnis = aPool[ aStack.Get() ]; + eRet = ConvOK; + } + + aIn.Seek( nEndPos ); + + if( eRet == ConvOK ) + ReadExtensions( aExtensions, aIn ); + + return eRet; +} + + +// stream seeks to first byte after <nFormulaLen> +ConvErr ExcelToSc::Convert( _ScRangeListTabs& rRangeList, XclImpStream& aIn, sal_Size nFormulaLen, const FORMULA_TYPE eFT ) +{ + RootData& rR = GetOldRoot(); + BYTE nOp, nLen; + sal_Size nIgnore; + UINT16 nUINT16; + UINT8 nByte; + BOOL bError = FALSE; + BOOL bArrayFormula = FALSE; + const BOOL bRangeName = eFT == FT_RangeName; + const BOOL bSharedFormula = eFT == FT_SharedFormula; + const BOOL bRNorSF = bRangeName || bSharedFormula; + + ScSingleRefData aSRD; + ScComplexRefData aCRD; + aCRD.Ref1.nTab = aCRD.Ref2.nTab = aEingPos.Tab(); + + bExternName = FALSE; + + if( eStatus != ConvOK ) + { + aIn.Ignore( nFormulaLen ); + return eStatus; + } + + if( nFormulaLen == 0 ) + return ConvOK; + + sal_Size nEndPos = aIn.GetRecPos() + nFormulaLen; + + while( (aIn.GetRecPos() < nEndPos) && !bError ) + { + aIn >> nOp; + nIgnore = 0; + + // #98524# always reset flags + aSRD.InitFlags(); + aCRD.InitFlags(); + + switch( nOp ) // Buch Seite: + { // SDK4 SDK5 + case 0x01: // Array Formula [325 ] + // Array Formula or Shared Formula [ 277] + nIgnore = (meBiff == EXC_BIFF2) ? 3 : 4; + bArrayFormula = TRUE; + break; + case 0x02: // Data Table [325 277] + nIgnore = (meBiff == EXC_BIFF2) ? 3 : 4; + break; + case 0x03: // Addition [312 264] + case 0x04: // Subtraction [313 264] + case 0x05: // Multiplication [313 264] + case 0x06: // Division [313 264] + case 0x07: // Exponetiation [313 265] + case 0x08: // Concatenation [313 265] + case 0x09: // Less Than [313 265] + case 0x0A: // Less Than or Equal [313 265] + case 0x0B: // Equal [313 265] + case 0x0C: // Greater Than or Equal [313 265] + case 0x0D: // Greater Than [313 265] + case 0x0E: // Not Equal [313 265] + case 0x0F: // Intersection [314 265] + case 0x10: // Union [314 265] + case 0x11: // Range [314 265] + case 0x12: // Unary Plus [312 264] + case 0x13: // Unary Minus [312 264] + case 0x14: // Percent Sign [312 264] + case 0x15: // Parenthesis [326 278] + case 0x16: // Missing Argument [314 266] + break; + case 0x17: // String Constant [314 266] + aIn >> nLen; + nIgnore = nLen; + break; + case 0x19: // Special Attribute [327 279] + { + UINT16 nData, nFakt; + BYTE nOpt; + + aIn >> nOpt; + + if( meBiff == EXC_BIFF2 ) + { + nData = aIn.ReaduInt8(); + nFakt = 1; + } + else + { + aIn >> nData; + nFakt = 2; + } + + if( nOpt & 0x04 ) + {// nFakt -> Bytes oder Words ueberlesen AttrChoose + nData++; + aIn.Ignore( nData * nFakt ); + } + } + break; + case 0x1A: // External Reference [330 ] + switch( meBiff ) + { + case EXC_BIFF2: nIgnore = 7; break; + case EXC_BIFF3: + case EXC_BIFF4: nIgnore = 10; break; + case EXC_BIFF5: DBG_WARNING( "-ExcelToSc::Convert(): 0x1A gibt's nicht in Biff5!" ); + default: DBG_WARNING( "-ExcelToSc::Convert(): Ein wenig vergesslich, was?" ); + } + break; + case 0x1B: // End External Reference [330 ] + switch( meBiff ) + { + case EXC_BIFF2: nIgnore = 3; break; + case EXC_BIFF3: + case EXC_BIFF4: nIgnore = 4; break; + case EXC_BIFF5: DBG_WARNING( "-ExcelToSc::Convert(): 0x1B gibt's nicht in Biff5!" ); + default: DBG_WARNING( "-ExcelToSc::Convert(): Ein wenig vergesslich, was?" ); + } + break; + case 0x1C: // Error Value [314 266] + case 0x1D: // Boolean [315 266] + nIgnore = 1; + break; + case 0x1E: // Integer [315 266] + nIgnore = 2; + break; + case 0x1F: // Number [315 266] + nIgnore = 8; + break; + case 0x40: + case 0x60: + case 0x20: // Array Constant [317 268] + nIgnore = (meBiff == EXC_BIFF2) ? 6 : 7; + break; + case 0x41: + case 0x61: + case 0x21: // Function, Fixed Number of Arguments [333 282] + nIgnore = (meBiff <= EXC_BIFF3) ? 1 : 2; + break; + case 0x42: + case 0x62: + case 0x22: // Function, Variable Number of Arg. [333 283] + nIgnore = (meBiff <= EXC_BIFF3) ? 2 : 3; + break; + case 0x43: + case 0x63: + case 0x23: // Name [318 269] + switch( meBiff ) + { + case EXC_BIFF2: nIgnore = 7; break; + case EXC_BIFF3: + case EXC_BIFF4: nIgnore = 10; break; + case EXC_BIFF5: nIgnore = 14; break; + default: DBG_ERROR( "-ExcelToSc::Convert(): Ein wenig vergesslich, was?" ); + } + break; + case 0x44: + case 0x64: + case 0x24: // Cell Reference [319 270] + aIn >> nUINT16 >> nByte; + aSRD.nCol = static_cast<SCsCOL>(nByte); + aSRD.nRow = nUINT16 & 0x3FFF; + aSRD.nRelTab = 0; + aSRD.SetTabRel( TRUE ); + aSRD.SetFlag3D( bRangeName ); + + ExcRelToScRel( nUINT16, nByte, aSRD, bRangeName ); + + rRangeList.Append( aSRD ); + break; + case 0x45: + case 0x65: + case 0x25: // Area Reference [320 270] + { + UINT16 nRowFirst, nRowLast; + UINT8 nColFirst, nColLast; + ScSingleRefData &rSRef1 = aCRD.Ref1; + ScSingleRefData &rSRef2 = aCRD.Ref2; + + aIn >> nRowFirst >> nRowLast >> nColFirst >> nColLast; + + rSRef1.nRelTab = rSRef2.nRelTab = 0; + rSRef1.SetTabRel( TRUE ); + rSRef2.SetTabRel( TRUE ); + rSRef1.SetFlag3D( bRangeName ); + rSRef2.SetFlag3D( bRangeName ); + + ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName ); + ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName ); + + if( IsComplColRange( nColFirst, nColLast ) ) + SetComplCol( aCRD ); + else if( IsComplRowRange( nRowFirst, nRowLast ) ) + SetComplRow( aCRD ); + + rRangeList.Append( aCRD ); + } + break; + case 0x46: + case 0x66: + case 0x26: // Constant Reference Subexpression [321 271] + case 0x47: + case 0x67: + case 0x27: // Erroneous Constant Reference Subexpr. [322 272] + case 0x48: + case 0x68: + case 0x28: // Incomplete Constant Reference Subexpr.[331 281] + nIgnore = (meBiff == EXC_BIFF2) ? 4 : 6; + break; + case 0x4A: + case 0x6A: + case 0x2A: // Deleted Cell Reference [323 273] + nIgnore = 3; + break; + case 0x4B: + case 0x6B: + case 0x2B: // Deleted Area Refernce [323 273] + nIgnore = 6; + break; + case 0x4C: + case 0x6C: + case 0x2C: // Cell Reference Within a Name [323 ] + // Cell Reference Within a Shared Formula[ 273] + { + aIn >> nUINT16 >> nByte; // >> Attribute, Row >> Col + + aSRD.nRelTab = 0; + aSRD.SetTabRel( TRUE ); + aSRD.SetFlag3D( bRangeName ); + + ExcRelToScRel( nUINT16, nByte, aSRD, bRNorSF ); + + rRangeList.Append( aSRD ); + } + break; + case 0x4D: + case 0x6D: + case 0x2D: // Area Reference Within a Name [324 ] + { // Area Reference Within a Shared Formula[ 274] + UINT16 nRowFirst, nRowLast; + UINT8 nColFirst, nColLast; + + aCRD.Ref1.nRelTab = aCRD.Ref2.nRelTab = 0; + aCRD.Ref1.SetTabRel( TRUE ); + aCRD.Ref2.SetTabRel( TRUE ); + aCRD.Ref1.SetFlag3D( bRangeName ); + aCRD.Ref2.SetFlag3D( bRangeName ); + + aIn >> nRowFirst >> nRowLast >> nColFirst >> nColLast; + + ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF ); + ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRNorSF ); + + if( IsComplColRange( nColFirst, nColLast ) ) + SetComplCol( aCRD ); + else if( IsComplRowRange( nRowFirst, nRowLast ) ) + SetComplRow( aCRD ); + + rRangeList.Append( aCRD ); + } + break; + case 0x49: + case 0x69: + case 0x29: // Variable Reference Subexpression [331 281] + case 0x4E: + case 0x6E: + case 0x2E: // Reference Subexpression Within a Name [332 282] + case 0x4F: + case 0x6F: + case 0x2F: // Incomplete Reference Subexpression... [332 282] + nIgnore = (meBiff == EXC_BIFF2) ? 1 : 2; + break; + case 0x58: + case 0x78: + case 0x38: // Command-Equivalent Function [333 ] + nIgnore = 2; + break; + case 0x59: + case 0x79: + case 0x39: // Name or External Name [ 275] + nIgnore = 24; + break; + case 0x5A: + case 0x7A: + case 0x3A: // 3-D Cell Reference [ 275] + { + UINT16 nTabFirst, nTabLast, nRow; + INT16 nExtSheet; + BYTE nCol; + + aIn >> nExtSheet; + aIn.Ignore( 8 ); + aIn >> nTabFirst >> nTabLast >> nRow >> nCol; + + if( nExtSheet >= 0 ) + // von extern + { + if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) ) + { + nTabFirst = nTabLast; + nExtSheet = 0; // gefunden + } + else + { + aPool << ocBad; + aPool >> aStack; + nExtSheet = 1; // verhindert Erzeugung einer SingleRef + } + } + + if( nExtSheet <= 0 ) + {// in aktuellem Workbook + BOOL b3D = ( static_cast<SCTAB>(nTabFirst) != aEingPos.Tab() ) || bRangeName; + aSRD.nTab = static_cast<SCTAB>(nTabFirst); + aSRD.SetFlag3D( b3D ); + aSRD.SetTabRel( FALSE ); + + ExcRelToScRel( nRow, nCol, aSRD, bRangeName ); + + if( nTabLast != nTabFirst ) + { + aCRD.Ref1 = aSRD; + aCRD.Ref2.nCol = aSRD.nCol; + aCRD.Ref2.nRow = aSRD.nRow; + aCRD.Ref2.nTab = static_cast<SCTAB>(nTabLast); + b3D = ( static_cast<SCTAB>(nTabLast) != aEingPos.Tab() ); + aCRD.Ref2.SetFlag3D( b3D ); + aCRD.Ref2.SetTabRel( FALSE ); + rRangeList.Append( aCRD ); + } + else + rRangeList.Append( aSRD ); + } + } + + break; + case 0x5B: + case 0x7B: + case 0x3B: // 3-D Area Reference [ 276] + { + UINT16 nTabFirst, nTabLast, nRowFirst, nRowLast; + INT16 nExtSheet; + BYTE nColFirst, nColLast; + + aIn >> nExtSheet; + aIn.Ignore( 8 ); + aIn >> nTabFirst >> nTabLast >> nRowFirst >> nRowLast + >> nColFirst >> nColLast; + + if( nExtSheet >= 0 ) + // von extern + { + if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) ) + { + nTabFirst = nTabLast; + nExtSheet = 0; // gefunden + } + else + { + aPool << ocBad; + aPool >> aStack; + nExtSheet = 1; // verhindert Erzeugung einer CompleteRef + } + } + + if( nExtSheet <= 0 ) + {// in aktuellem Workbook + // erster Teil des Bereichs + ScSingleRefData &rR1 = aCRD.Ref1; + ScSingleRefData &rR2 = aCRD.Ref2; + + rR1.nTab = static_cast<SCTAB>(nTabFirst); + rR2.nTab = static_cast<SCTAB>(nTabLast); + rR1.SetFlag3D( ( static_cast<SCTAB>(nTabFirst) != aEingPos.Tab() ) || bRangeName ); + rR1.SetTabRel( FALSE ); + rR2.SetFlag3D( ( static_cast<SCTAB>(nTabLast) != aEingPos.Tab() ) || bRangeName ); + rR2.SetTabRel( FALSE ); + + ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName ); + ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName ); + + if( IsComplColRange( nColFirst, nColLast ) ) + SetComplCol( aCRD ); + else if( IsComplRowRange( nRowFirst, nRowLast ) ) + SetComplRow( aCRD ); + + rRangeList.Append( aCRD ); + }//ENDE in aktuellem Workbook + } + break; + case 0x5C: + case 0x7C: + case 0x3C: // Deleted 3-D Cell Reference [ 277] + nIgnore = 17; + break; + case 0x5D: + case 0x7D: + case 0x3D: // Deleted 3-D Area Reference [ 277] + nIgnore = 20; + break; + default: bError = TRUE; + } + bError |= !aIn.IsValid(); + + aIn.Ignore( nIgnore ); + } + + ConvErr eRet; + + if( bError ) + eRet = ConvErrNi; + else if( aIn.GetRecPos() != nEndPos ) + eRet = ConvErrCount; + else if( bExternName ) + eRet = ConvErrExternal; + else if( bArrayFormula ) + eRet = ConvOK; + else + eRet = ConvOK; + + aIn.Seek( nEndPos ); + return eRet; +} + +ConvErr ExcelToSc::ConvertExternName( const ScTokenArray*& /*rpArray*/, XclImpStream& /*rStrm*/, sal_Size /*nFormulaLen*/, + const String& /*rUrl*/, const vector<String>& /*rTabNames*/ ) +{ + // not implemented ... + return ConvErrNi; +} + +BOOL ExcelToSc::GetAbsRefs( ScRangeList& rRangeList, XclImpStream& rStrm, sal_Size nLen ) +{ + DBG_ASSERT_BIFF( GetBiff() == EXC_BIFF5 ); + if( GetBiff() != EXC_BIFF5 ) + return FALSE; + + sal_uInt8 nOp; + sal_uInt16 nRow1, nRow2; + sal_uInt8 nCol1, nCol2; + SCTAB nTab1, nTab2; + sal_uInt16 nTabFirst, nTabLast; + sal_Int16 nRefIdx; + + sal_Size nSeek; + sal_Size nEndPos = rStrm.GetRecPos() + nLen; + + while( rStrm.IsValid() && (rStrm.GetRecPos() < nEndPos) ) + { + rStrm >> nOp; + nSeek = 0; + + switch( nOp ) + { + case 0x44: + case 0x64: + case 0x24: // Cell Reference [319 270] + case 0x4C: + case 0x6C: + case 0x2C: // Cell Reference Within a Name [323 ] + // Cell Reference Within a Shared Formula[ 273] + rStrm >> nRow1 >> nCol1; + + nRow2 = nRow1; + nCol2 = nCol1; + nTab1 = nTab2 = GetCurrScTab(); + goto _common; + case 0x45: + case 0x65: + case 0x25: // Area Reference [320 270] + case 0x4D: + case 0x6D: + case 0x2D: // Area Reference Within a Name [324 ] + // Area Reference Within a Shared Formula[ 274] + rStrm >> nRow1 >> nRow2 >> nCol1 >> nCol2; + + nTab1 = nTab2 = GetCurrScTab(); + goto _common; + case 0x5A: + case 0x7A: + case 0x3A: // 3-D Cell Reference [ 275] + rStrm >> nRefIdx; + rStrm.Ignore( 8 ); + rStrm >> nTabFirst >> nTabLast >> nRow1 >> nCol1; + + nRow2 = nRow1; + nCol2 = nCol1; + + goto _3d_common; + case 0x5B: + case 0x7B: + case 0x3B: // 3-D Area Reference [ 276] + rStrm >> nRefIdx; + rStrm.Ignore( 8 ); + rStrm >> nTabFirst >> nTabLast >> nRow1 >> nRow2 >> nCol1 >> nCol2; + + _3d_common: + nTab1 = static_cast< SCTAB >( nTabFirst ); + nTab2 = static_cast< SCTAB >( nTabLast ); + + // #122885# skip references to deleted sheets + if( (nRefIdx >= 0) || !ValidTab( nTab1 ) || (nTab1 != nTab2) ) + break; + + goto _common; + _common: + // do not check abs/rel flags, linked controls have set them! +// if( !(( nCol1 & 0xC000 ) || ( nCol2 & 0xC000 )) ) + { + ScRange aScRange; + nRow1 &= 0x3FFF; + nRow2 &= 0x3FFF; + if( GetAddressConverter().ConvertRange( aScRange, XclRange( nCol1, nRow1, nCol2, nRow2 ), nTab1, nTab2, true ) ) + rRangeList.Append( aScRange ); + } + break; + + case 0x03: // Addition [312 264] + case 0x04: // Subtraction [313 264] + case 0x05: // Multiplication [313 264] + case 0x06: // Division [313 264] + case 0x07: // Exponetiation [313 265] + case 0x08: // Concatenation [313 265] + case 0x09: // Less Than [313 265] + case 0x0A: // Less Than or Equal [313 265] + case 0x0B: // Equal [313 265] + case 0x0C: // Greater Than or Equal [313 265] + case 0x0D: // Greater Than [313 265] + case 0x0E: // Not Equal [313 265] + case 0x0F: // Intersection [314 265] + case 0x10: // Union [314 265] + case 0x11: // Range [314 265] + case 0x12: // Unary Plus [312 264] + case 0x13: // Unary Minus [312 264] + case 0x14: // Percent Sign [312 264] + case 0x15: // Parenthesis [326 278] + case 0x16: // Missing Argument [314 266] + break; + case 0x1C: // Error Value [314 266] + case 0x1D: // Boolean [315 266] + nSeek = 1; + break; + case 0x1E: // Integer [315 266] + case 0x41: + case 0x61: + case 0x21: // Function, Fixed Number of Arguments [333 282] + case 0x49: + case 0x69: + case 0x29: // Variable Reference Subexpression [331 281] + case 0x4E: + case 0x6E: + case 0x2E: // Reference Subexpression Within a Name [332 282] + case 0x4F: + case 0x6F: + case 0x2F: // Incomplete Reference Subexpression... [332 282] + case 0x58: + case 0x78: + case 0x38: // Command-Equivalent Function [333 ] + nSeek = 2; + break; + case 0x42: + case 0x62: + case 0x22: // Function, Variable Number of Arg. [333 283] + case 0x4A: + case 0x6A: + case 0x2A: // Deleted Cell Reference [323 273] + nSeek = 3; + break; + case 0x01: // Array Formula [325 ] + // Array Formula or Shared Formula [ 277] + case 0x02: // Data Table [325 277] + nSeek = 4; + break; + case 0x46: + case 0x66: + case 0x26: // Constant Reference Subexpression [321 271] + case 0x47: + case 0x67: + case 0x27: // Erroneous Constant Reference Subexpr. [322 272] + case 0x48: + case 0x68: + case 0x28: // Incomplete Constant Reference Subexpr.[331 281] + case 0x4B: + case 0x6B: + case 0x2B: // Deleted Area Refernce [323 273] + nSeek = 6; + break; + case 0x40: + case 0x60: + case 0x20: // Array Constant [317 268] + nSeek = 7; + break; + case 0x1F: // Number [315 266] + nSeek = 8; + break; + case 0x43: + case 0x63: + case 0x23: // Name [318 269] + nSeek = 14; + break; + case 0x5C: + case 0x7C: + case 0x3C: // Deleted 3-D Cell Reference [ 277] + nSeek = 17; + break; + case 0x5D: + case 0x7D: + case 0x3D: // Deleted 3-D Area Reference [ 277] + nSeek = 20; + break; + case 0x59: + case 0x79: + case 0x39: // Name or External Name [ 275] + nSeek = 24; + break; + case 0x17: // String Constant [314 266] + nSeek = rStrm.ReaduInt8(); + break; + case 0x19: // Special Attribute [327 279] + { + BYTE nOpt; + UINT16 nData; + rStrm >> nOpt >> nData; + if( nOpt & 0x04 ) + nSeek = nData * 2 + 2; + } + break; + } + + rStrm.Ignore( nSeek ); + } + rStrm.Seek( nEndPos ); + + return rRangeList.Count() != 0; +} + +void ExcelToSc::DoMulArgs( DefTokenId eId, sal_uInt8 nAnz, sal_uInt8 nMinParamCount ) +{ + TokenId eParam[ 256 ]; + INT32 nLauf; + + if( eId == ocCeil || eId == ocFloor ) + { + aStack << aPool.Store( 1.0 ); // default, da in Excel nicht vorhanden + nAnz++; + } + + for( nLauf = 0; aStack.HasMoreTokens() && (nLauf < nAnz); nLauf++ ) + aStack >> eParam[ nLauf ]; + // #i70925# reduce parameter count, if no more tokens available on token stack + if( nLauf < nAnz ) + nAnz = static_cast< sal_uInt8 >( nLauf ); + + if( nAnz > 0 && eId == ocExternal ) + { + TokenId n = eParam[ nAnz - 1 ]; +//##### GRUETZE FUER BASIC-FUNCS RICHTEN! + if( const String* pExt = aPool.GetExternal( n ) ) + { + if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclMacroName( *pExt ) ) + aPool << pFuncInfo->meOpCode; + else + aPool << n; + nAnz--; + } + else + aPool << eId; + } + else + aPool << eId; + + aPool << ocOpen; + + if( nAnz > 0 ) + { + // attention: 0 = last parameter, nAnz-1 = first parameter + INT16 nNull = -1; // skip this parameter + INT16 nSkipEnd = -1; // skip all parameters <= nSkipEnd + + INT16 nLast = nAnz - 1; + + // Funktionen, bei denen Parameter wegfallen muessen + if( eId == ocPercentrank && nAnz == 3 ) + nSkipEnd = 0; // letzten Parameter bei Bedarf weglassen + + // Joost-Spezialfaelle + else if( eId == ocIf ) + { + UINT16 nNullParam = 0; + for( nLauf = 0 ; nLauf < nAnz ; nLauf++ ) + { + if( aPool.IsSingleOp( eParam[ nLauf ], ocMissing ) ) + { + if( !nNullParam ) + nNullParam = (UINT16) aPool.Store( ( double ) 0.0 ); + eParam[ nLauf ] = nNullParam; + } + } + } + + // FIXME: ideally we'd want to import all missing args, but this + // conflicts with lots of fn's understanding of nParams - we need + // a function table, and pre-call argument normalisation 1st. + INT16 nLastRemovable = nLast - nMinParamCount; + + // #84453# skip missing parameters at end of parameter list + while( nSkipEnd < nLastRemovable && + aPool.IsSingleOp( eParam[ nSkipEnd + 1 ], ocMissing ) ) + nSkipEnd++; + +// fprintf (stderr, "Fn %d nSkipEnd %d nLast %d nMinParamCnt %d %d\n", +// eId, nSkipEnd, nLast, nMinParamCount, nLastRemovable); + + // [Parameter{;Parameter}] + if( nLast > nSkipEnd ) + { + aPool << eParam[ nLast ]; + for( nLauf = nLast - 1 ; nLauf > nSkipEnd ; nLauf-- ) + { + if( nLauf != nNull ) + aPool << ocSep << eParam[ nLauf ]; + } + } + } + aPool << ocClose; + + aPool >> aStack; +} + + +void ExcelToSc::ExcRelToScRel( UINT16 nRow, UINT8 nCol, ScSingleRefData &rSRD, const BOOL bName ) +{ + if( bName ) + { + // C O L + if( nRow & 0x4000 ) + {// rel Col + rSRD.SetColRel( TRUE ); + rSRD.nRelCol = static_cast<SCsCOL>(static_cast<INT8>(nCol)); + } + else + {// abs Col + rSRD.SetColRel( FALSE ); + rSRD.nCol = static_cast<SCCOL>(nCol); + } + + // R O W + if( nRow & 0x8000 ) + {// rel Row + rSRD.SetRowRel( TRUE ); + if( nRow & 0x2000 ) // Bit 13 gesetzt? + // -> Row negativ + rSRD.nRelRow = static_cast<SCsROW>(static_cast<INT16>(nRow | 0xC000)); + else + // -> Row positiv + rSRD.nRelRow = static_cast<SCsROW>(nRow & nRowMask); + } + else + {// abs Row + rSRD.SetRowRel( FALSE ); + rSRD.nRow = static_cast<SCROW>(nRow & nRowMask); + } + + // T A B + // #67965# abs needed if rel in shared formula for ScCompiler UpdateNameReference + if ( rSRD.IsTabRel() && !rSRD.IsFlag3D() ) + rSRD.nTab = GetCurrScTab(); + } + else + { + // C O L + rSRD.SetColRel( ( nRow & 0x4000 ) > 0 ); + rSRD.nCol = static_cast<SCsCOL>(nCol); + + // R O W + rSRD.SetRowRel( ( nRow & 0x8000 ) > 0 ); + rSRD.nRow = static_cast<SCsROW>(nRow & nRowMask); + + if ( rSRD.IsColRel() ) + rSRD.nRelCol = rSRD.nCol - aEingPos.Col(); + if ( rSRD.IsRowRel() ) + rSRD.nRelRow = rSRD.nRow - aEingPos.Row(); + + // T A B + // #i10184# abs needed if rel in shared formula for ScCompiler UpdateNameReference + if ( rSRD.IsTabRel() && !rSRD.IsFlag3D() ) + rSRD.nTab = GetCurrScTab() + rSRD.nRelTab; + } +} + + +const ScTokenArray* ExcelToSc::GetBoolErr( XclBoolError eType ) +{ + UINT16 nError; + aPool.Reset(); + aStack.Reset(); + + DefTokenId eOc; + + switch( eType ) + { + case xlErrNull: eOc = ocStop; nError = errNoCode; break; + case xlErrDiv0: eOc = ocStop; nError = errDivisionByZero; break; + case xlErrValue: eOc = ocStop; nError = errNoValue; break; + case xlErrRef: eOc = ocStop; nError = errNoRef; break; + case xlErrName: eOc = ocStop; nError = errNoName; break; + case xlErrNum: eOc = ocStop; nError = errIllegalFPOperation; break; + case xlErrNA: eOc = ocNotAvail; nError = NOTAVAILABLE; break; + case xlErrTrue: eOc = ocTrue; nError = 0; break; + case xlErrFalse: eOc = ocFalse; nError = 0; break; + case xlErrUnknown: eOc = ocStop; nError = errUnknownState; break; + default: + DBG_ERROR( "ExcelToSc::GetBoolErr - wrong enum!" ); + eOc = ocNoName; + nError = errUnknownState; + } + + aPool << eOc; + if( eOc != ocStop ) + aPool << ocOpen << ocClose; + + aPool >> aStack; + + const ScTokenArray* pErgebnis = aPool[ aStack.Get() ]; + if( nError ) + ( ( ScTokenArray* ) pErgebnis )->SetCodeError( nError ); + + ( ( ScTokenArray* ) pErgebnis )->SetRecalcModeNormal(); + + return pErgebnis; +} + + +// if a shared formula was found, stream seeks to first byte after <nFormulaLen>, +// else stream pointer stays unchanged +BOOL ExcelToSc::GetShrFmla( const ScTokenArray*& rpErgebnis, XclImpStream& aIn, sal_Size nFormulaLen ) +{ + BYTE nOp; + BOOL bRet = TRUE; + + if( nFormulaLen == 0 ) + bRet = FALSE; + else + { + aIn.PushPosition(); + + aIn >> nOp; + + if( nOp == 0x01 ) // Shared Formula [ 277] + { + UINT16 nCol, nRow; + + aIn >> nRow >> nCol; + + aStack << aPool.Store( GetOldRoot().pShrfmlaBuff->Find( + ScAddress( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), GetCurrScTab() ) ) ); + + bRet = TRUE; + } + else + bRet = FALSE; + + aIn.PopPosition(); + } + + if( bRet ) + { + aIn.Ignore( nFormulaLen ); + rpErgebnis = aPool[ aStack.Get() ]; + } + else + rpErgebnis = NULL; + + return bRet; +} + + +#if 0 +BOOL ExcelToSc::SetCurVal( ScFormulaCell &rCell, double &rfCurVal ) +{ + UINT16 nInd; + BYTE nType; + BYTE nVal; + BOOL bString = FALSE; + +#ifdef OSL_BIGENDIAN + // Code fuer alle anstaendigen Prozessoren + nType = *( ( ( BYTE * ) &rfCurVal ) + 7 ); + nVal = *( ( ( BYTE * ) &rfCurVal ) + 5 ); + nInd = *( ( UINT16 * ) &rfCurVal ); +#else + // fuer Schund-Prozessoren + nType = *( ( BYTE * ) &rfCurVal ); + nVal = *( ( ( BYTE * ) &rfCurVal ) + 2 ); + nInd = *( ( ( UINT16 * ) &rfCurVal ) + 3 ); +#endif + + if( ( UINT16 ) ~nInd ) + // Wert ist Float + rCell.SetHybridDouble( rfCurVal ); + else + { + switch( nType ) + { + case 0: // String + bString = TRUE; + break; + case 1: // Bool + if( nVal ) + rfCurVal = 1.0; + else + rfCurVal = 0.0; + rCell.SetHybridDouble( rfCurVal ); + break; + case 2: // Error + rCell.SetErrCode( XclTools::GetScErrorCode( nVal ) ); + break; + } + } + + return bString; +} +#endif + + +void ExcelToSc::SetError( ScFormulaCell &rCell, const ConvErr eErr ) +{ + UINT16 nInd; + + switch( eErr ) + { + case ConvErrNi: nInd = errUnknownToken; break; + case ConvErrNoMem: nInd = errCodeOverflow; break; + case ConvErrExternal: nInd = errNoName; break; + case ConvErrCount: nInd = errCodeOverflow; break; + default: nInd = errNoCode; // hier fiel mir nichts + // Besseres ein... + } + + rCell.SetErrCode( nInd ); +} + + +void ExcelToSc::SetComplCol( ScComplexRefData &rCRD ) +{ + ScSingleRefData &rSRD = rCRD.Ref2; + if( rSRD.IsColRel() ) + rSRD.nRelCol = MAXCOL - aEingPos.Col(); + else + rSRD.nCol = MAXCOL; +} + + +void ExcelToSc::SetComplRow( ScComplexRefData &rCRD ) +{ + ScSingleRefData &rSRD = rCRD.Ref2; + if( rSRD.IsRowRel() ) + rSRD.nRelRow = MAXROW - aEingPos.Row(); + else + rSRD.nRow = MAXROW; +} + +void ExcelToSc::ReadExtensionArray( unsigned int n, XclImpStream& aIn ) +{ + // printf( "inline array;\n" ); + + BYTE nByte; + UINT16 nUINT16; + double fDouble; + String aString; + ScMatrix* pMatrix; + + aIn >> nByte >> nUINT16; + + SCSIZE nC, nCols; + SCSIZE nR, nRows; + if( GetBiff() == EXC_BIFF8 ) + { + nCols = nByte + 1; + nRows = nUINT16 + 1; + } + else + { + nCols = nByte ? nByte : 256; + nRows = nUINT16; + } + + pMatrix = aPool.GetMatrix( n ); + + if( NULL != pMatrix ) + { + pMatrix->Resize(nCols, nRows); + pMatrix->GetDimensions( nC, nR); + if( nC != nCols || nR != nRows ) + { + DBG_ERRORFILE( "ExcelToSc::ReadExtensionArray - matrix size mismatch" ); + pMatrix = NULL; + } + } + else + { + DBG_ERRORFILE( "ExcelToSc::ReadExtensionArray - missing matrix" ); + } + + for( nR = 0 ; nR < nRows; nR++ ) + { + for( nC = 0 ; nC < nCols; nC++ ) + { + aIn >> nByte; + switch( nByte ) + { + case EXC_CACHEDVAL_EMPTY: + aIn.Ignore( 8 ); + if( NULL != pMatrix ) + { + pMatrix->PutEmpty( nC, nR ); + } + break; + + case EXC_CACHEDVAL_DOUBLE: + aIn >> fDouble; + if( NULL != pMatrix ) + { + pMatrix->PutDouble( fDouble, nC, nR ); + } + break; + + case EXC_CACHEDVAL_STRING: + if( GetBiff() == EXC_BIFF8 ) + { + aIn >> nUINT16; + aString = aIn.ReadUniString( nUINT16 ); + } + else + { + aIn >> nByte; + aString = aIn.ReadRawByteString( nByte ); + } + if( NULL != pMatrix ) + { + pMatrix->PutString( aString, nC, nR ); + } + break; + + case EXC_CACHEDVAL_BOOL: + aIn >> nByte; + aIn.Ignore( 7 ); + if( NULL != pMatrix ) + { + pMatrix->PutBoolean( nByte != 0, nC, nR ); + } + break; + + case EXC_CACHEDVAL_ERROR: + aIn >> nByte; + aIn.Ignore( 7 ); + if( NULL != pMatrix ) + { + pMatrix->PutError( XclTools::GetScErrorCode( nByte ), nC, nR ); + } + break; + } + } + } +} + +void ExcelToSc::ReadExtensionNlr( XclImpStream& aIn ) +{ + // printf( "natural lang fmla;\n" ); + + sal_uInt32 nFlags; + aIn >> nFlags; + + sal_uInt32 nCount = nFlags & EXC_TOK_NLR_ADDMASK; + aIn.Ignore( nCount * 4 ); // Drop the cell positions +} + +void ExcelToSc::ReadExtensionMemArea( XclImpStream& aIn ) +{ + // printf( "mem area;\n" ); + + sal_uInt16 nCount; + aIn >> nCount; + + aIn.Ignore( nCount * ((GetBiff() == EXC_BIFF8) ? 8 : 6) ); // drop the ranges +} + +void ExcelToSc::ReadExtensions( const ExtensionTypeVec& rExtensions, + XclImpStream& aIn ) +{ + unsigned int nArray = 0; + + for( unsigned int i = 0 ; i < rExtensions.size() ; i++ ) + { + ExtensionType eType = rExtensions[i]; + + switch( eType ) + { + case EXTENSION_ARRAY: + ReadExtensionArray( nArray++, aIn ); + break; + + case EXTENSION_NLR: + ReadExtensionNlr( aIn ); + break; + + case EXTENSION_MEMAREA: + ReadExtensionMemArea( aIn ); + break; + } + } +} + diff --git a/sc/source/filter/excel/excform8.cxx b/sc/source/filter/excel/excform8.cxx new file mode 100644 index 000000000000..148b1542913a --- /dev/null +++ b/sc/source/filter/excel/excform8.cxx @@ -0,0 +1,1596 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "excform.hxx" + +#include "cell.hxx" +#include "document.hxx" +#include "rangenam.hxx" +#include "xltracer.hxx" +#include "xistream.hxx" +#include "xihelper.hxx" +#include "xilink.hxx" +#include "xiname.hxx" + +#include "externalrefmgr.hxx" + +#include <vector> + +using ::std::vector; + +ExcelToSc8::ExternalTabInfo::ExternalTabInfo() : + mnFileId(0), mbExternal(false) +{ +} + +// ============================================================================ + +ExcelToSc8::ExcelToSc8( const XclImpRoot& rRoot ) : + ExcelToSc( rRoot ), + rLinkMan( rRoot.GetLinkManager() ) +{ +} + + +ExcelToSc8::~ExcelToSc8() +{ +} + +bool ExcelToSc8::GetExternalFileIdFromXti( UINT16 nIxti, sal_uInt16& rFileId ) const +{ + const String* pFileUrl = rLinkMan.GetSupbookUrl(nIxti); + if (!pFileUrl || pFileUrl->Len() == 0 || !GetDocShell()) + return false; + + String aFileUrl = ScGlobal::GetAbsDocName(*pFileUrl, GetDocShell()); + ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager(); + rFileId = pRefMgr->getExternalFileId(aFileUrl); + + return true; +} + +bool ExcelToSc8::Read3DTabReference( UINT16 nIxti, SCTAB& rFirstTab, SCTAB& rLastTab, ExternalTabInfo& rExtInfo ) +{ + rFirstTab = rLastTab = 0; + rExtInfo.mbExternal = !rLinkMan.IsSelfRef(nIxti); + bool bSuccess = rLinkMan.GetScTabRange(rFirstTab, rLastTab, nIxti); + if (!bSuccess) + return false; + + if (!rExtInfo.mbExternal) + // This is internal reference. Stop here. + return true; + + rExtInfo.maTabName = rLinkMan.GetSupbookTabName(nIxti, rFirstTab); + return GetExternalFileIdFromXti(nIxti, rExtInfo.mnFileId); +} + + +// if bAllowArrays is false stream seeks to first byte after <nFormulaLen> +// otherwise it will seek to the first byte past additional content after <nFormulaLen> +ConvErr ExcelToSc8::Convert( const ScTokenArray*& rpTokArray, XclImpStream& aIn, sal_Size nFormulaLen, bool bAllowArrays, const FORMULA_TYPE eFT ) +{ + BYTE nOp, nLen, nByte; + UINT16 nUINT16; + double fDouble; + String aString; + BOOL bError = FALSE; + BOOL bArrayFormula = FALSE; + TokenId nMerk0; + const BOOL bRangeName = eFT == FT_RangeName; + const BOOL bSharedFormula = eFT == FT_SharedFormula; + const BOOL bRNorSF = bRangeName || bSharedFormula; + + ScSingleRefData aSRD; + ScComplexRefData aCRD; + ExtensionTypeVec aExtensions; + + if( eStatus != ConvOK ) + { + aIn.Ignore( nFormulaLen ); + return eStatus; + } + + if( nFormulaLen == 0 ) + { + aPool.Store( CREATE_STRING( "-/-" ) ); + aPool >> aStack; + rpTokArray = aPool[ aStack.Get() ]; + return ConvOK; + } + + sal_Size nEndPos = aIn.GetRecPos() + nFormulaLen; + + while( (aIn.GetRecPos() < nEndPos) && !bError ) + { + aIn >> nOp; + + // #98524# always reset flags + aSRD.InitFlags(); + aCRD.InitFlags(); + + switch( nOp ) // Buch Seite: + { // SDK4 SDK5 + case 0x01: // Array Formula [325 ] + // Array Formula or Shared Formula [ 277] + case 0x02: // Data Table [325 277] + aIn.Ignore( 4 ); + + bArrayFormula = TRUE; + break; + case 0x03: // Addition [312 264] + aStack >> nMerk0; + aPool << aStack << ocAdd << nMerk0; + aPool >> aStack; + break; + case 0x04: // Subtraction [313 264] + // SECOMD-TOP minus TOP + aStack >> nMerk0; + aPool << aStack << ocSub << nMerk0; + aPool >> aStack; + break; + case 0x05: // Multiplication [313 264] + aStack >> nMerk0; + aPool << aStack << ocMul << nMerk0; + aPool >> aStack; + break; + case 0x06: // Division [313 264] + // divide TOP by SECOND-TOP + aStack >> nMerk0; + aPool << aStack << ocDiv << nMerk0; + aPool >> aStack; + break; + case 0x07: // Exponetiation [313 265] + // raise SECOND-TOP to power of TOP + aStack >> nMerk0; + aPool << aStack << ocPow << nMerk0; + aPool >> aStack; + break; + case 0x08: // Concatenation [313 265] + // append TOP to SECOND-TOP + aStack >> nMerk0; + aPool << aStack << ocAmpersand << nMerk0; + aPool >> aStack; + break; + case 0x09: // Less Than [313 265] + // SECOND-TOP < TOP + aStack >> nMerk0; + aPool << aStack << ocLess << nMerk0; + aPool >> aStack; + break; + case 0x0A: // Less Than or Equal [313 265] + // SECOND-TOP <= TOP + aStack >> nMerk0; + aPool << aStack << ocLessEqual << nMerk0; + aPool >> aStack; + break; + case 0x0B: // Equal [313 265] + // SECOND-TOP == TOP + aStack >> nMerk0; + aPool << aStack << ocEqual << nMerk0; + aPool >> aStack; + break; + case 0x0C: // Greater Than or Equal [313 265] + // SECOND-TOP == TOP + aStack >> nMerk0; + aPool << aStack << ocGreaterEqual << nMerk0; + aPool >> aStack; + break; + case 0x0D: // Greater Than [313 265] + // SECOND-TOP == TOP + aStack >> nMerk0; + aPool << aStack << ocGreater << nMerk0; + aPool >> aStack; + break; + case 0x0E: // Not Equal [313 265] + // SECOND-TOP == TOP + aStack >> nMerk0; + aPool << aStack << ocNotEqual << nMerk0; + aPool >> aStack; + break; + case 0x0F: // Intersection [314 265] + aStack >> nMerk0; + aPool << aStack << ocIntersect << nMerk0; + aPool >> aStack; + break; + case 0x10: // Union [314 265] + // ocSep behelfsweise statt 'ocUnion' + aStack >> nMerk0; +//#100928# aPool << ocOpen << aStack << ocSep << nMerk0 << ocClose; + aPool << aStack << ocSep << nMerk0; + // doesn't fit exactly, but is more Excel-like + aPool >> aStack; + break; + case 0x11: // Range [314 265] + aStack >> nMerk0; + aPool << aStack << ocRange << nMerk0; + aPool >> aStack; + break; + case 0x12: // Unary Plus [312 264] + aPool << ocAdd << aStack; + aPool >> aStack; + break; + case 0x13: // Unary Minus [312 264] + aPool << ocNegSub << aStack; + aPool >> aStack; + break; + case 0x14: // Percent Sign [312 264] + aPool << aStack << ocPercentSign; + aPool >> aStack; + break; + case 0x15: // Parenthesis [326 278] + aPool << ocOpen << aStack << ocClose; + aPool >> aStack; + break; + case 0x16: // Missing Argument [314 266] + aPool << ocMissing; + aPool >> aStack; + GetTracer().TraceFormulaMissingArg(); + break; + case 0x17: // String Constant [314 266] + aIn >> nLen; // und? + aString = aIn.ReadUniString( nLen ); // reads Grbit even if nLen==0 + + aStack << aPool.Store( aString ); + break; + case 0x18: // natural language formula + { + UINT8 nEptg; + UINT16 nCol, nRow; + aIn >> nEptg; + switch( nEptg ) + { // name size ext type + case 0x01: // Lel 4 - err + aIn.Ignore( 4 ); + aPool << ocBad; + aPool >> aStack; + break; + case 0x02: // Rw 4 - ref + case 0x03: // Col 4 - ref + case 0x06: // RwV 4 - val + case 0x07: // ColV 4 - val + aIn >> nRow >> nCol; + + aSRD.InitAddress( ScAddress( static_cast<SCCOL>(nCol & 0xFF), static_cast<SCROW>(nRow), aEingPos.Tab() ) ); + + if( nEptg == 0x02 || nEptg == 0x06 ) + aSRD.SetRowRel( TRUE ); + else + aSRD.SetColRel( TRUE ); + + aSRD.CalcRelFromAbs( aEingPos ); + + aStack << aPool.StoreNlf( aSRD ); + break; + case 0x0A: // Radical 13 - ref + aIn >> nRow >> nCol; + aIn.Ignore( 9 ); + + aSRD.InitAddress( ScAddress( static_cast<SCCOL>(nCol & 0xFF), static_cast<SCROW>(nRow), aEingPos.Tab() ) ); + + aSRD.SetColRel( TRUE ); + + aSRD.CalcRelFromAbs( aEingPos ); + + aStack << aPool.StoreNlf( aSRD ); + break; + case 0x0B: // RadicalS 13 x ref + aIn.Ignore( 13 ); + aExtensions.push_back( EXTENSION_NLR ); + aPool << ocBad; + aPool >> aStack; + break; + case 0x0C: // RwS 4 x ref + case 0x0D: // ColS 4 x ref + case 0x0E: // RwSV 4 x val + case 0x0F: // ColSV 4 x val + aIn.Ignore( 4 ); + aExtensions.push_back( EXTENSION_NLR ); + aPool << ocBad; + aPool >> aStack; + break; + case 0x10: // RadicalLel 4 - err + case 0x1D: // SxName 4 - val + aIn.Ignore( 4 ); + aPool << ocBad; + aPool >> aStack; + break; + default: + aPool << ocBad; + aPool >> aStack; + } + } + break; + case 0x19: // Special Attribute [327 279] + { + UINT16 nData, nFakt; + BYTE nOpt; + + aIn >> nOpt >> nData; + nFakt = 2; + + if( nOpt & 0x04 ) + {// nFakt -> Bytes oder Words ueberlesen AttrChoose + nData++; + aIn.Ignore( nData * nFakt ); + } + else if( nOpt & 0x10 ) // AttrSum + DoMulArgs( ocSum, 1 ); + } + break; + case 0x1C: // Error Value [314 266] + { + aIn >> nByte; +#if 0 // erAck + aPool.StoreError( XclTools::GetScErrorCode( nByte ) ); +#else + DefTokenId eOc; + switch( nByte ) + { + case EXC_ERR_NULL: + case EXC_ERR_DIV0: + case EXC_ERR_VALUE: + case EXC_ERR_REF: + case EXC_ERR_NAME: + case EXC_ERR_NUM: eOc = ocStop; break; + case EXC_ERR_NA: eOc = ocNotAvail; break; + default: eOc = ocNoName; + } + aPool << eOc; + if( eOc != ocStop ) + aPool << ocOpen << ocClose; +#endif + aPool >> aStack; + } + break; + case 0x1D: // Boolean [315 266] + aIn >> nByte; + if( nByte == 0 ) + aPool << ocFalse << ocOpen << ocClose; + else + aPool << ocTrue << ocOpen << ocClose; + aPool >> aStack; + break; + case 0x1E: // Integer [315 266] + aIn >> nUINT16; + aStack << aPool.Store( ( double ) nUINT16 ); + break; + case 0x1F: // Number [315 266] + aIn >> fDouble; + aStack << aPool.Store( fDouble ); + break; + case 0x40: + case 0x60: + case 0x20: // Array Constant [317 268] + aIn >> nByte >> nUINT16; + aIn.Ignore( 4 ); + if( bAllowArrays ) + { + aStack << aPool.StoreMatrix(); + aExtensions.push_back( EXTENSION_ARRAY ); + } + else + { + aPool << ocBad; + aPool >> aStack; + } + break; + case 0x41: + case 0x61: + case 0x21: // Function, Fixed Number of Arguments [333 282] + { + sal_uInt16 nXclFunc; + aIn >> nXclFunc; + if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) ) + DoMulArgs( pFuncInfo->meOpCode, pFuncInfo->mnMaxParamCount ); + else + DoMulArgs( ocNoName, 0 ); + } + break; + case 0x42: + case 0x62: + case 0x22: // Function, Variable Number of Arg. [333 283] + { + sal_uInt16 nXclFunc; + sal_uInt8 nParamCount; + aIn >> nParamCount >> nXclFunc; + nParamCount &= 0x7F; + if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) ) + DoMulArgs( pFuncInfo->meOpCode, nParamCount, pFuncInfo->mnMinParamCount ); + else + DoMulArgs( ocNoName, 0 ); + } + break; + case 0x43: + case 0x63: + case 0x23: // Name [318 269] + aIn >> nUINT16; + { + aIn.Ignore( 2 ); + //Determine if this is a user-defined Macro name. + const XclImpName* pName = GetNameManager().GetName( nUINT16 ); + if(pName && !pName->GetScRangeData()) + aStack << aPool.Store( ocMacro, pName->GetXclName() ); + else + aStack << aPool.Store( nUINT16 ); + } + break; + case 0x44: + case 0x64: + case 0x24: // Cell Reference [319 270] + case 0x4A: + case 0x6A: + case 0x2A: // Deleted Cell Reference [323 273] + { + UINT16 nCol, nRow; + + aIn >> nRow >> nCol; + + aSRD.nCol = static_cast<SCCOL>(nCol); + aSRD.nRow = nRow & 0x3FFF; + aSRD.nRelTab = 0; + aSRD.SetTabRel( TRUE ); + aSRD.SetFlag3D( bRangeName ); + + ExcRelToScRel8( nRow, nCol, aSRD, bRangeName ); + + switch ( nOp ) + { + case 0x4A: + case 0x6A: + case 0x2A: // Deleted Cell Reference [323 273] + // no information which part is deleted, set both + aSRD.SetColDeleted( TRUE ); + aSRD.SetRowDeleted( TRUE ); + } + + aStack << aPool.Store( aSRD ); + } + break; + case 0x45: + case 0x65: + case 0x25: // Area Reference [320 270] + case 0x4B: + case 0x6B: + case 0x2B: // Deleted Area Refernce [323 273] + { + UINT16 nRowFirst, nRowLast; + UINT16 nColFirst, nColLast; + ScSingleRefData &rSRef1 = aCRD.Ref1; + ScSingleRefData &rSRef2 = aCRD.Ref2; + + aIn >> nRowFirst >> nRowLast >> nColFirst >> nColLast; + + rSRef1.nRelTab = rSRef2.nRelTab = 0; + rSRef1.SetTabRel( TRUE ); + rSRef2.SetTabRel( TRUE ); + rSRef1.SetFlag3D( bRangeName ); + rSRef2.SetFlag3D( bRangeName ); + + ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRangeName ); + ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRangeName ); + + if( IsComplColRange( nColFirst, nColLast ) ) + SetComplCol( aCRD ); + else if( IsComplRowRange( nRowFirst, nRowLast ) ) + SetComplRow( aCRD ); + + switch ( nOp ) + { + case 0x4B: + case 0x6B: + case 0x2B: // Deleted Area Refernce [323 273] + // no information which part is deleted, set all + rSRef1.SetColDeleted( TRUE ); + rSRef1.SetRowDeleted( TRUE ); + rSRef2.SetColDeleted( TRUE ); + rSRef2.SetRowDeleted( TRUE ); + } + + aStack << aPool.Store( aCRD ); + } + break; + case 0x46: + case 0x66: + case 0x26: // Constant Reference Subexpression [321 271] + aExtensions.push_back( EXTENSION_MEMAREA ); + aIn.Ignore( 6 ); // mehr steht da nicht! + break; + case 0x47: + case 0x67: + case 0x27: // Erroneous Constant Reference Subexpr. [322 272] + aIn.Ignore( 6 ); // mehr steht da nicht! +// aPool << ocBad; +// aPool >> aStack; + break; + case 0x48: + case 0x68: + case 0x28: // Incomplete Constant Reference Subexpr.[331 281] + aIn.Ignore( 6 ); // mehr steht da nicht! +// aPool << ocBad; +// aPool >> aStack; + break; + case 0x49: + case 0x69: + case 0x29: // Variable Reference Subexpression [331 281] + aIn.Ignore( 2 ); // mehr steht da nicht! + break; + case 0x4C: + case 0x6C: + case 0x2C: // Cell Reference Within a Name [323 ] + // Cell Reference Within a Shared Formula[ 273] + { + UINT16 nRow, nCol; + + aIn >> nRow >> nCol; + + aSRD.nRelTab = 0; + aSRD.SetTabRel( TRUE ); + aSRD.SetFlag3D( bRangeName ); + + ExcRelToScRel8( nRow, nCol, aSRD, bRNorSF ); + + aStack << aPool.Store( aSRD ); + } + break; + case 0x4D: + case 0x6D: + case 0x2D: // Area Reference Within a Name [324 ] + { // Area Reference Within a Shared Formula[ 274] + UINT16 nRowFirst, nRowLast; + UINT16 nColFirst, nColLast; + + aCRD.Ref1.nRelTab = aCRD.Ref2.nRelTab = 0; + aCRD.Ref1.SetTabRel( TRUE ); + aCRD.Ref2.SetTabRel( TRUE ); + aCRD.Ref1.SetFlag3D( bRangeName ); + aCRD.Ref2.SetFlag3D( bRangeName ); + + aIn >> nRowFirst >> nRowLast >> nColFirst >> nColLast; + + ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF ); + ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRNorSF ); + + if( IsComplColRange( nColFirst, nColLast ) ) + SetComplCol( aCRD ); + else if( IsComplRowRange( nRowFirst, nRowLast ) ) + SetComplRow( aCRD ); + + aStack << aPool.Store( aCRD ); + } + break; + case 0x4E: + case 0x6E: + case 0x2E: // Reference Subexpression Within a Name [332 282] + aIn.Ignore( 2 ); // mehr steht da nicht! +// aPool << ocBad; +// aPool >> aStack; + break; + case 0x4F: + case 0x6F: + case 0x2F: // Incomplete Reference Subexpression... [332 282] + aIn.Ignore( 2 ); // mehr steht da nicht! +// aPool << ocBad; +// aPool >> aStack; + break; + case 0x58: + case 0x78: + case 0x38: // Command-Equivalent Function [333 ] + aString.AssignAscii( "COMM_EQU_FUNC" ); + aIn >> nByte; + aString += String::CreateFromInt32( nByte ); + aIn >> nByte; + aStack << aPool.Store( aString ); + DoMulArgs( ocPush, nByte + 1 ); + break; + case 0x59: + case 0x79: + case 0x39: // Name or External Name [ 275] + { + sal_uInt16 nXtiIndex, nNameIdx; + aIn >> nXtiIndex >> nNameIdx; + aIn.Ignore( 2 ); + + if( rLinkMan.IsSelfRef( nXtiIndex ) ) + { + // internal defined name with explicit sheet, i.e.: =Sheet1!AnyName + const XclImpName* pName = GetNameManager().GetName( nNameIdx ); + if( pName && !pName->GetScRangeData() ) + aStack << aPool.Store( ocMacro, pName->GetXclName() ); + else + aStack << aPool.Store( nNameIdx ); + } + else if( const XclImpExtName* pExtName = rLinkMan.GetExternName( nXtiIndex, nNameIdx ) ) + { + switch( pExtName->GetType() ) + { + case xlExtName: + { + /* FIXME: enable this code for #i4385# once + * external name reference can be stored in ODF, + * which remains to be done for #i3740#. Until then + * create a #NAME? token. */ +#if 0 + sal_uInt16 nFileId; + if (!GetExternalFileIdFromXti(nXtiIndex, nFileId) || !pExtName->HasFormulaTokens()) + { + aStack << aPool.Store(ocNoName, pExtName->GetName()); + break; + } + + aStack << aPool.StoreExtName(nFileId, pExtName->GetName()); + pExtName->CreateExtNameData(GetDoc(), nFileId); +#else + aStack << aPool.Store( ocNoName, pExtName->GetName() ); +#endif + } + break; + + case xlExtAddIn: + { + aStack << aPool.Store( ocExternal, pExtName->GetName() ); + } + break; + + case xlExtDDE: + { + String aApplic, aTopic; + if( rLinkMan.GetLinkData( aApplic, aTopic, nXtiIndex ) ) + { + TokenId nPar1 = aPool.Store( aApplic ); + TokenId nPar2 = aPool.Store( aTopic ); + nMerk0 = aPool.Store( pExtName->GetName() ); + aPool << ocDde << ocOpen << nPar1 << ocSep << nPar2 << ocSep + << nMerk0 << ocClose; + aPool >> aStack; + pExtName->CreateDdeData( GetDoc(), aApplic, aTopic ); + } + } + break; + + case xlExtEuroConvert: + { + aStack << aPool.Store( ocEuroConvert, String() ); + } + break; + + default: // OLE link + { + aPool << ocBad; + aPool >> aStack; + } + } + } + else + { + //aStack << ocNoName; + aPool << ocBad; + aPool >> aStack; + } + } + break; + case 0x5A: + case 0x7A: + case 0x3A: // 3-D Cell Reference [ 275] + case 0x5C: + case 0x7C: + case 0x3C: // Deleted 3-D Cell Reference [ 277] + { + UINT16 nIxti, nRw, nGrbitCol; + SCTAB nTabFirst, nTabLast; + + aIn >> nIxti >> nRw >> nGrbitCol; + + ExternalTabInfo aExtInfo; + if (!Read3DTabReference(nIxti, nTabFirst, nTabLast, aExtInfo)) + { + aPool << ocBad; + aPool >> aStack; + break; + } + + aSRD.nTab = nTabFirst; + aSRD.SetFlag3D( TRUE ); + aSRD.SetTabRel( FALSE ); + + ExcRelToScRel8( nRw, nGrbitCol, aSRD, bRangeName ); + + switch ( nOp ) + { + case 0x5C: + case 0x7C: + case 0x3C: // Deleted 3-D Cell Reference [ 277] + // no information which part is deleted, set both + aSRD.SetColDeleted( TRUE ); + aSRD.SetRowDeleted( TRUE ); + } + + if (aExtInfo.mbExternal) + { + // nTabFirst and nTabLast are the indices of the refernced + // sheets in the SUPBOOK record, hence do not represent + // the actual indices of the original sheets since the + // SUPBOOK record only stores referenced sheets and skips + // the ones that are not referenced. + + if (nTabLast != nTabFirst) + { + aCRD.Ref1 = aCRD.Ref2 = aSRD; + aCRD.Ref2.nTab = nTabLast; + aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aCRD); + } + else + aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aSRD); + } + else + { + if ( !ValidTab(nTabFirst)) + aSRD.SetTabDeleted( TRUE ); + + if( nTabLast != nTabFirst ) + { + aCRD.Ref1 = aCRD.Ref2 = aSRD; + aCRD.Ref2.nTab = nTabLast; + aCRD.Ref2.SetTabDeleted( !ValidTab(nTabLast) ); + aStack << aPool.Store( aCRD ); + } + else + aStack << aPool.Store( aSRD ); + } + } + break; + case 0x5B: + case 0x7B: + case 0x3B: // 3-D Area Reference [ 276] + case 0x5D: + case 0x7D: + case 0x3D: // Deleted 3-D Area Reference [ 277] + { + UINT16 nIxti, nRw1, nGrbitCol1, nRw2, nGrbitCol2; + SCTAB nTabFirst, nTabLast; + aIn >> nIxti >> nRw1 >> nRw2 >> nGrbitCol1 >> nGrbitCol2; + + ExternalTabInfo aExtInfo; + if (!Read3DTabReference(nIxti, nTabFirst, nTabLast, aExtInfo)) + { + aPool << ocBad; + aPool >> aStack; + break; + } + ScSingleRefData &rR1 = aCRD.Ref1; + ScSingleRefData &rR2 = aCRD.Ref2; + + + rR1.nTab = nTabFirst; + rR2.nTab = nTabLast; + rR1.SetFlag3D( TRUE ); + rR1.SetTabRel( FALSE ); + rR2.SetFlag3D( nTabFirst != nTabLast ); + rR2.SetTabRel( FALSE ); + + ExcRelToScRel8( nRw1, nGrbitCol1, aCRD.Ref1, bRangeName ); + ExcRelToScRel8( nRw2, nGrbitCol2, aCRD.Ref2, bRangeName ); + + if( IsComplColRange( nGrbitCol1, nGrbitCol2 ) ) + SetComplCol( aCRD ); + else if( IsComplRowRange( nRw1, nRw2 ) ) + SetComplRow( aCRD ); + + switch ( nOp ) + { + case 0x5D: + case 0x7D: + case 0x3D: // Deleted 3-D Area Reference [ 277] + // no information which part is deleted, set all + rR1.SetColDeleted( TRUE ); + rR1.SetRowDeleted( TRUE ); + rR2.SetColDeleted( TRUE ); + rR2.SetRowDeleted( TRUE ); + } + + if (aExtInfo.mbExternal) + { + aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aCRD); + } + else + { + if ( !ValidTab(nTabFirst) ) + rR1.SetTabDeleted( TRUE ); + if ( !ValidTab(nTabLast) ) + rR2.SetTabDeleted( TRUE ); + + aStack << aPool.Store( aCRD ); + } + } + break; + default: bError = TRUE; + } + bError |= !aIn.IsValid(); + } + + ConvErr eRet; + + if( bError ) + { + aPool << ocBad; + aPool >> aStack; + rpTokArray = aPool[ aStack.Get() ]; + eRet = ConvErrNi; + } + else if( aIn.GetRecPos() != nEndPos ) + { + aPool << ocBad; + aPool >> aStack; + rpTokArray = aPool[ aStack.Get() ]; + eRet = ConvErrCount; + } + else if( bArrayFormula ) + { + rpTokArray = NULL; + eRet = ConvOK; + } + else + { + rpTokArray = aPool[ aStack.Get() ]; + eRet = ConvOK; + } + + aIn.Seek( nEndPos ); + + if( eRet == ConvOK) + ReadExtensions( aExtensions, aIn ); + + return eRet; +} + + +// stream seeks to first byte after <nFormulaLen> +ConvErr ExcelToSc8::Convert( _ScRangeListTabs& rRangeList, XclImpStream& aIn, sal_Size nFormulaLen, const FORMULA_TYPE eFT ) +{ + BYTE nOp, nLen;//, nByte; + BOOL bError = FALSE; + BOOL bArrayFormula = FALSE; + const BOOL bRangeName = eFT == FT_RangeName; + const BOOL bSharedFormula = eFT == FT_SharedFormula; + const BOOL bRNorSF = bRangeName || bSharedFormula; + + ScSingleRefData aSRD; + ScComplexRefData aCRD; + + bExternName = FALSE; + + if( eStatus != ConvOK ) + { + aIn.Ignore( nFormulaLen ); + return eStatus; + } + + if( nFormulaLen == 0 ) + return ConvOK; + + sal_Size nEndPos = aIn.GetRecPos() + nFormulaLen; + + while( (aIn.GetRecPos() < nEndPos) && !bError ) + { + aIn >> nOp; + + // #98524# always reset flags + aSRD.InitFlags(); + aCRD.InitFlags(); + + switch( nOp ) // Buch Seite: + { // SDK4 SDK5 + case 0x01: // Array Formula [325 ] + // Array Formula or Shared Formula [ 277] + aIn.Ignore( 4 ); + + bArrayFormula = TRUE; + break; + case 0x02: // Data Table [325 277] + aIn.Ignore( 4 ); + break; + case 0x03: // Addition [312 264] + case 0x04: // Subtraction [313 264] + case 0x05: // Multiplication [313 264] + case 0x06: // Division [313 264] + case 0x07: // Exponetiation [313 265] + case 0x08: // Concatenation [313 265] + case 0x09: // Less Than [313 265] + case 0x0A: // Less Than or Equal [313 265] + case 0x0B: // Equal [313 265] + case 0x0C: // Greater Than or Equal [313 265] + case 0x0D: // Greater Than [313 265] + case 0x0E: // Not Equal [313 265] + case 0x0F: // Intersection [314 265] + case 0x10: // Union [314 265] + case 0x11: // Range [314 265] + case 0x12: // Unary Plus [312 264] + case 0x13: // Unary Minus [312 264] + case 0x14: // Percent Sign [312 264] + case 0x15: // Parenthesis [326 278] + case 0x16: // Missing Argument [314 266] + break; + case 0x17: // String Constant [314 266] + aIn >> nLen; // und? + + aIn.IgnoreUniString( nLen ); // reads Grbit even if nLen==0 + break; + case 0x19: // Special Attribute [327 279] + { + UINT16 nData, nFakt; + BYTE nOpt; + + aIn >> nOpt >> nData; + nFakt = 2; + + if( nOpt & 0x04 ) + {// nFakt -> Bytes oder Words ueberlesen AttrChoose + nData++; + aIn.Ignore( nData * nFakt ); + } + } + break; + case 0x1C: // Error Value [314 266] + case 0x1D: // Boolean [315 266] + aIn.Ignore( 1 ); + break; + case 0x1E: // Integer [315 266] + aIn.Ignore( 2 ); + break; + case 0x1F: // Number [315 266] + aIn.Ignore( 8 ); + break; + case 0x40: + case 0x60: + case 0x20: // Array Constant [317 268] + aIn.Ignore( 7 ); + break; + case 0x41: + case 0x61: + case 0x21: // Function, Fixed Number of Arguments [333 282] + aIn.Ignore( 2 ); + break; + case 0x42: + case 0x62: + case 0x22: // Function, Variable Number of Arg. [333 283] + aIn.Ignore( 3 ); + break; + case 0x43: + case 0x63: + case 0x23: // Name [318 269] + aIn.Ignore( 4 ); + break; + case 0x44: + case 0x64: + case 0x24: // Cell Reference [319 270] + { + UINT16 nCol, nRow; + + aIn >> nRow >> nCol; + + aSRD.nCol = static_cast<SCCOL>(nCol); + aSRD.nRow = nRow & 0x3FFF; + aSRD.nRelTab = 0; + aSRD.SetTabRel( TRUE ); + aSRD.SetFlag3D( bRangeName ); + + ExcRelToScRel8( nRow, nCol, aSRD, bRangeName ); + + rRangeList.Append( aSRD ); + } + break; + case 0x45: + case 0x65: + case 0x25: // Area Reference [320 270] + { + UINT16 nRowFirst, nRowLast; + UINT16 nColFirst, nColLast; + ScSingleRefData &rSRef1 = aCRD.Ref1; + ScSingleRefData &rSRef2 = aCRD.Ref2; + + aIn >> nRowFirst >> nRowLast >> nColFirst >> nColLast; + + rSRef1.nRelTab = rSRef2.nRelTab = 0; + rSRef1.SetTabRel( TRUE ); + rSRef2.SetTabRel( TRUE ); + rSRef1.SetFlag3D( bRangeName ); + rSRef2.SetFlag3D( bRangeName ); + + ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRangeName ); + ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRangeName ); + + if( IsComplColRange( nColFirst, nColLast ) ) + SetComplCol( aCRD ); + else if( IsComplRowRange( nRowFirst, nRowLast ) ) + SetComplRow( aCRD ); + + rRangeList.Append( aCRD ); + } + break; + case 0x46: + case 0x66: + case 0x26: // Constant Reference Subexpression [321 271] + case 0x47: + case 0x67: + case 0x27: // Erroneous Constant Reference Subexpr. [322 272] + case 0x48: + case 0x68: + case 0x28: // Incomplete Constant Reference Subexpr.[331 281] + aIn.Ignore( 6 ); // mehr steht da nicht! + break; + case 0x49: + case 0x69: + case 0x29: // Variable Reference Subexpression [331 281] + aIn.Ignore( 2 ); // mehr steht da nicht! + break; + case 0x4A: + case 0x6A: + case 0x2A: // Deleted Cell Reference [323 273] + aIn.Ignore( 3 ); + break; + case 0x4B: + case 0x6B: + case 0x2B: // Deleted Area Refernce [323 273] + aIn.Ignore( 6 ); + break; + case 0x4C: + case 0x6C: + case 0x2C: // Cell Reference Within a Name [323 ] + // Cell Reference Within a Shared Formula[ 273] + { + UINT16 nRow, nCol; + + aIn >> nRow >> nCol; + + aSRD.nRelTab = 0; + aSRD.SetTabRel( TRUE ); + aSRD.SetFlag3D( bRangeName ); + + ExcRelToScRel8( nRow, nCol, aSRD, bRNorSF ); + + rRangeList.Append( aSRD ); + } + break; + case 0x4D: + case 0x6D: + case 0x2D: // Area Reference Within a Name [324 ] + { // Area Reference Within a Shared Formula[ 274] + UINT16 nRowFirst, nRowLast; + UINT16 nColFirst, nColLast; + + aCRD.Ref1.nRelTab = aCRD.Ref2.nRelTab = 0; + aCRD.Ref1.SetTabRel( TRUE ); + aCRD.Ref2.SetTabRel( TRUE ); + aCRD.Ref1.SetFlag3D( bRangeName ); + aCRD.Ref2.SetFlag3D( bRangeName ); + + aIn >> nRowFirst >> nRowLast >> nColFirst >> nColLast; + + ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF ); + ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRNorSF ); + + if( IsComplColRange( nColFirst, nColLast ) ) + SetComplCol( aCRD ); + else if( IsComplRowRange( nRowFirst, nRowLast ) ) + SetComplRow( aCRD ); + + rRangeList.Append( aCRD ); + } + break; + case 0x4E: + case 0x6E: + case 0x2E: // Reference Subexpression Within a Name [332 282] + case 0x4F: + case 0x6F: + case 0x2F: // Incomplete Reference Subexpression... [332 282] + case 0x58: + case 0x78: + case 0x38: // Command-Equivalent Function [333 ] + aIn.Ignore( 2 ); + break; + case 0x59: + case 0x79: + case 0x39: // Name or External Name [ 275] + aIn.Ignore( 24 ); + break; + case 0x5A: + case 0x7A: + case 0x3A: // 3-D Cell Reference [ 275] + { + UINT16 nIxti, nRw, nGrbitCol; + + aIn >> nIxti >> nRw >> nGrbitCol; + + SCTAB nFirstScTab, nLastScTab; + if( rLinkMan.GetScTabRange( nFirstScTab, nLastScTab, nIxti ) ) + { + aSRD.nTab = nFirstScTab; + aSRD.SetFlag3D( TRUE ); + aSRD.SetTabRel( FALSE ); + + ExcRelToScRel8( nRw, nGrbitCol, aSRD, bRangeName ); + + if( nFirstScTab != nLastScTab ) + { + aCRD.Ref1 = aSRD; + aCRD.Ref2.nCol = aSRD.nCol; + aCRD.Ref2.nRow = aSRD.nRow; + aCRD.Ref2.nTab = nLastScTab; + rRangeList.Append( aCRD ); + } + else + rRangeList.Append( aSRD ); + } + } + break; + case 0x5B: + case 0x7B: + case 0x3B: // 3-D Area Reference [ 276] + { + UINT16 nIxti, nRw1, nGrbitCol1, nRw2, nGrbitCol2; + + aIn >> nIxti >> nRw1 >> nRw2 >> nGrbitCol1 >> nGrbitCol2; + + SCTAB nFirstScTab, nLastScTab; + if( rLinkMan.GetScTabRange( nFirstScTab, nLastScTab, nIxti ) ) + { + ScSingleRefData &rR1 = aCRD.Ref1; + ScSingleRefData &rR2 = aCRD.Ref2; + + rR1.nTab = nFirstScTab; + rR2.nTab = nLastScTab; + rR1.SetFlag3D( TRUE ); + rR1.SetTabRel( FALSE ); + rR2.SetFlag3D( nFirstScTab != nLastScTab ); + rR2.SetTabRel( FALSE ); + + ExcRelToScRel8( nRw1, nGrbitCol1, aCRD.Ref1, bRangeName ); + ExcRelToScRel8( nRw2, nGrbitCol2, aCRD.Ref2, bRangeName ); + + if( IsComplColRange( nGrbitCol1, nGrbitCol2 ) ) + SetComplCol( aCRD ); + else if( IsComplRowRange( nRw1, nRw2 ) ) + SetComplRow( aCRD ); + + rRangeList.Append( aCRD ); + } + } + break; + case 0x5C: + case 0x7C: + case 0x3C: // Deleted 3-D Cell Reference [ 277] + aIn.Ignore( 6 ); + break; + case 0x5D: + case 0x7D: + case 0x3D: // Deleted 3-D Area Reference [ 277] + aIn.Ignore( 10 ); + break; + default: + bError = TRUE; + } + bError |= !aIn.IsValid(); + } + + ConvErr eRet; + + if( bError ) + eRet = ConvErrNi; + else if( aIn.GetRecPos() != nEndPos ) + eRet = ConvErrCount; + else if( bExternName ) + eRet = ConvErrExternal; + else + eRet = ConvOK; + + aIn.Seek( nEndPos ); + return eRet; +} + +ConvErr ExcelToSc8::ConvertExternName( const ScTokenArray*& rpArray, XclImpStream& rStrm, sal_Size nFormulaLen, + const String& rUrl, const vector<String>& rTabNames ) +{ + if( !GetDocShell() ) + return ConvErrNi; + + String aFileUrl = ScGlobal::GetAbsDocName(rUrl, GetDocShell()); + + sal_uInt8 nOp, nByte; + bool bError = false; + + ScSingleRefData aSRD; + ScComplexRefData aCRD; + + if (eStatus != ConvOK) + { + rStrm.Ignore(nFormulaLen); + return eStatus; + } + + if (nFormulaLen == 0) + { + aPool.Store(CREATE_STRING("-/-")); + aPool >> aStack; + rpArray = aPool[aStack.Get()]; + return ConvOK; + } + + ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager(); + sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFileUrl); + sal_uInt16 nTabCount = static_cast< sal_uInt16 >( rTabNames.size() ); + + sal_Size nEndPos = rStrm.GetRecPos() + nFormulaLen; + + while( (rStrm.GetRecPos() < nEndPos) && !bError ) + { + rStrm >> nOp; + + // #98524# always reset flags + aSRD.InitFlags(); + aCRD.InitFlags(); + + switch( nOp ) + { + case 0x1C: // Error Value + { + rStrm >> nByte; + DefTokenId eOc; + switch( nByte ) + { + case EXC_ERR_NULL: + case EXC_ERR_DIV0: + case EXC_ERR_VALUE: + case EXC_ERR_REF: + case EXC_ERR_NAME: + case EXC_ERR_NUM: eOc = ocStop; break; + case EXC_ERR_NA: eOc = ocNotAvail; break; + default: eOc = ocNoName; + } + aPool << eOc; + if( eOc != ocStop ) + aPool << ocOpen << ocClose; + aPool >> aStack; + } + break; + case 0x3A: + { + // cell reference in external range name + sal_uInt16 nExtTab1, nExtTab2, nRow, nGrbitCol; + rStrm >> nExtTab1 >> nExtTab2 >> nRow >> nGrbitCol; + if (nExtTab1 >= nTabCount || nExtTab2 >= nTabCount) + { + bError = true; + break; + } + + aSRD.nTab = nExtTab1; + aSRD.SetFlag3D(true); + aSRD.SetTabRel(false); + ExcRelToScRel8(nRow, nGrbitCol, aSRD, true); + aCRD.Ref1 = aCRD.Ref2 = aSRD; + String aTabName = rTabNames[nExtTab1]; + + if (nExtTab1 == nExtTab2) + { + // single cell reference + aStack << aPool.StoreExtRef(nFileId, aTabName, aSRD); + } + else + { + // area reference + aCRD.Ref2.nTab = nExtTab2; + aStack << aPool.StoreExtRef(nFileId, aTabName, aCRD); + } + } + break; + case 0x3B: + { + // area reference + sal_uInt16 nExtTab1, nExtTab2, nRow1, nRow2, nGrbitCol1, nGrbitCol2; + rStrm >> nExtTab1 >> nExtTab2 >> nRow1 >> nRow2 >> nGrbitCol1 >> nGrbitCol2; + ScSingleRefData& rR1 = aCRD.Ref1; + ScSingleRefData& rR2 = aCRD.Ref2; + + rR1.nTab = nExtTab1; + rR1.SetFlag3D(true); + rR1.SetTabRel(false); + ExcRelToScRel8(nRow1, nGrbitCol1, rR1, true); + + rR2.nTab = nExtTab2; + rR2.SetFlag3D(true); + rR2.SetTabRel(false); + ExcRelToScRel8(nRow2, nGrbitCol2, rR2, true); + + String aTabName = rTabNames[nExtTab1]; + aStack << aPool.StoreExtRef(nFileId, aTabName, aCRD); + } + break; + default: + bError = true; + } + bError |= !rStrm.IsValid(); + } + + ConvErr eRet; + + if( bError ) + { + aPool << ocBad; + aPool >> aStack; + rpArray = aPool[ aStack.Get() ]; + eRet = ConvErrNi; + } + else if( rStrm.GetRecPos() != nEndPos ) + { + aPool << ocBad; + aPool >> aStack; + rpArray = aPool[ aStack.Get() ]; + eRet = ConvErrCount; + } + else + { + rpArray = aPool[ aStack.Get() ]; + eRet = ConvOK; + } + + rStrm.Seek(nEndPos); + return eRet; +} + +void ExcelToSc8::ExcRelToScRel8( UINT16 nRow, UINT16 nC, ScSingleRefData &rSRD, const BOOL bName ) +{ + const BOOL bColRel = ( nC & 0x4000 ) != 0; + const BOOL bRowRel = ( nC & 0x8000 ) != 0; + const UINT8 nCol = static_cast<UINT8>(nC); + + rSRD.SetColRel( bColRel ); + rSRD.SetRowRel( bRowRel ); + + if( bName ) + { + // C O L + if( bColRel ) + // rel Col + rSRD.nRelCol = static_cast<SCsCOL>(static_cast<INT8>(nC)); + else + // abs Col + rSRD.nCol = static_cast<SCCOL>(nCol); + + // R O W + if( bRowRel ) + // rel Row + rSRD.nRelRow = static_cast<SCsROW>(static_cast<INT16>(nRow)); + else + // abs Row + rSRD.nRow = Min( static_cast<SCROW>(nRow), MAXROW); + + // T A B + // #67965# abs needed if rel in shared formula for ScCompiler UpdateNameReference + if ( rSRD.IsTabRel() && !rSRD.IsFlag3D() ) + rSRD.nTab = GetCurrScTab(); + } + else + { + // C O L + if ( bColRel ) + rSRD.nRelCol = static_cast<SCsCOL>(nCol) - aEingPos.Col(); + else + rSRD.nCol = static_cast<SCCOL>(nCol); + + // R O W + if ( bRowRel ) + rSRD.nRelRow = static_cast<SCsROW>(nRow) - aEingPos.Row(); + else + rSRD.nRow = static_cast<SCROW>(nRow); + + // T A B + // #i10184# abs needed if rel in shared formula for ScCompiler UpdateNameReference + if ( rSRD.IsTabRel() && !rSRD.IsFlag3D() ) + rSRD.nTab = GetCurrScTab() + rSRD.nRelTab; + } +} + + +// stream seeks to first byte after <nLen> +BOOL ExcelToSc8::GetAbsRefs( ScRangeList& r, XclImpStream& aIn, sal_Size nLen ) +{ + UINT8 nOp; + UINT16 nRow1, nRow2, nCol1, nCol2; + SCTAB nTab1, nTab2; + UINT16 nIxti; + + sal_Size nSeek; + + sal_Size nEndPos = aIn.GetRecPos() + nLen; + + while( aIn.IsValid() && (aIn.GetRecPos() < nEndPos) ) + { + aIn >> nOp; + nSeek = 0; + + switch( nOp ) + { + case 0x44: + case 0x64: + case 0x24: // Cell Reference [319 270] + case 0x4C: + case 0x6C: + case 0x2C: // Cell Reference Within a Name [323 ] + // Cell Reference Within a Shared Formula[ 273] + aIn >> nRow1 >> nCol1; + + nRow2 = nRow1; + nCol2 = nCol1; + nTab1 = nTab2 = GetCurrScTab(); + goto _common; + case 0x45: + case 0x65: + case 0x25: // Area Reference [320 270] + case 0x4D: + case 0x6D: + case 0x2D: // Area Reference Within a Name [324 ] + // Area Reference Within a Shared Formula[ 274] + aIn >> nRow1 >> nRow2 >> nCol1 >> nCol2; + + nTab1 = nTab2 = GetCurrScTab(); + goto _common; + case 0x5A: + case 0x7A: + case 0x3A: // 3-D Cell Reference [ 275] + aIn >> nIxti >> nRow1 >> nCol1; + + nRow2 = nRow1; + nCol2 = nCol1; + + goto _3d_common; + case 0x5B: + case 0x7B: + case 0x3B: // 3-D Area Reference [ 276] + aIn >> nIxti >> nRow1 >> nRow2 >> nCol1 >> nCol2; + + _3d_common: + // #122885# skip references to deleted sheets + if( !rLinkMan.GetScTabRange( nTab1, nTab2, nIxti ) || !ValidTab( nTab1 ) || !ValidTab( nTab2 ) ) + break; + + goto _common; + _common: + // do not check abs/rel flags, linked controls have set them! +// if( !(( nCol1 & 0xC000 ) || ( nCol2 & 0xC000 )) ) + { + ScRange aScRange; + nCol1 &= 0x3FFF; + nCol2 &= 0x3FFF; + if( GetAddressConverter().ConvertRange( aScRange, XclRange( nCol1, nRow1, nCol2, nRow2 ), nTab1, nTab2, true ) ) + r.Append( aScRange ); + } + break; + case 0x1C: // Error Value [314 266] + case 0x1D: // Boolean [315 266] + nSeek = 1; + break; + case 0x1E: // Integer [315 266] + case 0x41: + case 0x61: + case 0x21: // Function, Fixed Number of Arguments [333 282] + case 0x49: + case 0x69: + case 0x29: // Variable Reference Subexpression [331 281] + case 0x4E: + case 0x6E: + case 0x2E: // Reference Subexpression Within a Name [332 282] + case 0x4F: + case 0x6F: + case 0x2F: // Incomplete Reference Subexpression... [332 282] + case 0x58: + case 0x78: + case 0x38: // Command-Equivalent Function [333 ] + nSeek = 2; + break; + case 0x42: + case 0x62: + case 0x22: // Function, Variable Number of Arg. [333 283] + nSeek = 3; + break; + case 0x01: // Array Formula [325 ] + case 0x02: // Data Table [325 277] + case 0x43: + case 0x63: + case 0x23: // Name [318 269] + case 0x4A: + case 0x6A: + case 0x2A: // Deleted Cell Reference [323 273] + nSeek = 4; + break; + case 0x46: + case 0x66: + case 0x26: // Constant Reference Subexpression [321 271] + case 0x47: + case 0x67: + case 0x27: // Erroneous Constant Reference Subexpr. [322 272] + case 0x48: + case 0x68: + case 0x28: // Incomplete Constant Reference Subexpr.[331 281] + case 0x5C: + case 0x7C: + case 0x3C: // Deleted 3-D Cell Reference [ 277] + case 0x59: + case 0x79: + case 0x39: // Name or External Name [ 275] + nSeek = 6; + break; + case 0x40: + case 0x60: + case 0x20: // Array Constant [317 268] + nSeek = 7; + break; + case 0x1F: // Number [315 266] + case 0x4B: + case 0x6B: + case 0x2B: // Deleted Area Refernce [323 273] + nSeek = 8; + break; + case 0x5D: + case 0x7D: + case 0x3D: // Deleted 3-D Area Reference [ 277] + nSeek = 10; + break; + case 0x17: // String Constant [314 266] + { + UINT8 nStrLen; + aIn >> nStrLen; + aIn.IgnoreUniString( nStrLen ); // reads Grbit even if nLen==0 + nSeek = 0; + } + break; + case 0x19: // Special Attribute [327 279] + { + UINT16 nData; + UINT8 nOpt; + aIn >> nOpt >> nData; + if( nOpt & 0x04 ) + {// nFakt -> Bytes oder Words ueberlesen AttrChoose + nData++; + nSeek = nData * 2; + } + } + break; + } + + aIn.Ignore( nSeek ); + } + aIn.Seek( nEndPos ); + + return r.Count() != 0; +} + + + + + diff --git a/sc/source/filter/excel/excimp8.cxx b/sc/source/filter/excel/excimp8.cxx new file mode 100644 index 000000000000..d75a0e669603 --- /dev/null +++ b/sc/source/filter/excel/excimp8.cxx @@ -0,0 +1,727 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "excimp8.hxx" + +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> + +#include <scitems.hxx> +#include <comphelper/processfactory.hxx> +#include <unotools/fltrcfg.hxx> + +#include <svtools/wmf.hxx> + +#include <editeng/eeitem.hxx> + +#include <sfx2/docfile.hxx> +#include <sfx2/objsh.hxx> +#include <editeng/brshitem.hxx> +#include <editeng/editdata.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editobj.hxx> +#include <editeng/editstat.hxx> +#include <editeng/colritem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/crsditem.hxx> +#include <editeng/flditem.hxx> +#include <svx/xflclit.hxx> +#include <filter/msfilter/svxmsbas.hxx> +#include <basic/basmgr.hxx> + +#include <vcl/graph.hxx> +#include <vcl/bmpacc.hxx> +#include <sot/exchange.hxx> + +#include <sfx2/docinf.hxx> + +#include <tools/string.hxx> +#include <tools/urlobj.hxx> +#include <rtl/math.hxx> +#include <rtl/ustrbuf.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/charclass.hxx> +#include <drwlayer.hxx> + +#include <boost/scoped_array.hpp> + +#include "cell.hxx" +#include "document.hxx" +#include "patattr.hxx" +#include "docpool.hxx" +#include "attrib.hxx" +#include "conditio.hxx" +#include "dbcolect.hxx" +#include "editutil.hxx" +#include "markdata.hxx" +#include "rangenam.hxx" +#include "docoptio.hxx" +#include "globstr.hrc" +#include "fprogressbar.hxx" +#include "xltracer.hxx" +#include "xihelper.hxx" +#include "xipage.hxx" +#include "xicontent.hxx" +#include "xilink.hxx" +#include "xiescher.hxx" +#include "xipivot.hxx" + +#include "excform.hxx" +#include "scextopt.hxx" +#include "stlpool.hxx" +#include "stlsheet.hxx" +#include "detfunc.hxx" + +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <cppuhelper/component_context.hxx> +#include <sfx2/app.hxx> + +using namespace com::sun::star; +using ::rtl::OUString; + + +ImportExcel8::ImportExcel8( XclImpRootData& rImpData, SvStream& rStrm ) : + ImportExcel( rImpData, rStrm ) +{ + // replace BIFF2-BIFF5 formula importer with BIFF8 formula importer + delete pFormConv; + pFormConv = pExcRoot->pFmlaConverter = new ExcelToSc8( GetRoot() ); +} + + +ImportExcel8::~ImportExcel8() +{ +} + + +void ImportExcel8::Calccount( void ) +{ + ScDocOptions aOpt = pD->GetDocOptions(); + aOpt.SetIterCount( aIn.ReaduInt16() ); + pD->SetDocOptions( aOpt ); +} + + +void ImportExcel8::Precision( void ) +{ + ScDocOptions aOpt = pD->GetDocOptions(); + aOpt.SetCalcAsShown( aIn.ReaduInt16() == 0 ); + pD->SetDocOptions( aOpt ); +} + + +void ImportExcel8::Delta( void ) +{ + ScDocOptions aOpt = pD->GetDocOptions(); + aOpt.SetIterEps( aIn.ReadDouble() ); + pD->SetDocOptions( aOpt ); +} + + +void ImportExcel8::Iteration( void ) +{ + ScDocOptions aOpt = pD->GetDocOptions(); + aOpt.SetIter( aIn.ReaduInt16() == 1 ); + pD->SetDocOptions( aOpt ); +} + + +void ImportExcel8::Boundsheet( void ) +{ + UINT8 nLen; + UINT16 nGrbit; + + aIn.DisableDecryption(); + maSheetOffsets.push_back( aIn.ReaduInt32() ); + aIn.EnableDecryption(); + aIn >> nGrbit >> nLen; + + String aName( aIn.ReadUniString( nLen ) ); + GetTabInfo().AppendXclTabName( aName, nBdshtTab ); + + SCTAB nScTab = static_cast< SCTAB >( nBdshtTab ); + if( nScTab > 0 ) + { + DBG_ASSERT( !pD->HasTable( nScTab ), "ImportExcel8::Boundsheet - sheet exists already" ); + pD->MakeTable( nScTab ); + } + + if( ( nGrbit & 0x0001 ) || ( nGrbit & 0x0002 ) ) + pD->SetVisible( nScTab, FALSE ); + + if( !pD->RenameTab( nScTab, aName ) ) + { + pD->CreateValidTabName( aName ); + pD->RenameTab( nScTab, aName ); + } + + nBdshtTab++; +} + + +void ImportExcel8::Scenman( void ) +{ + UINT16 nLastDispl; + + aIn.Ignore( 4 ); + aIn >> nLastDispl; + + aScenList.SetLast( nLastDispl ); +} + + +void ImportExcel8::Scenario( void ) +{ + aScenList.Append( new ExcScenario( aIn, *pExcRoot ) ); +} + + +void ImportExcel8::Labelsst( void ) +{ + XclAddress aXclPos; + UINT16 nXF; + UINT32 nSst; + + aIn >> aXclPos >> nXF >> nSst; + + ScAddress aScPos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) ) + { + GetXFRangeBuffer().SetXF( aScPos, nXF ); + if( ScBaseCell* pCell = GetSst().CreateCell( nSst, nXF ) ) + GetDoc().PutCell( aScPos.Col(), aScPos.Row(), aScPos.Tab(), pCell ); + } +} + + +void ImportExcel8::SheetProtection( void ) +{ + GetSheetProtectBuffer().ReadOptions( aIn, GetCurrScTab() ); +} + +void ImportExcel8::ReadBasic( void ) +{ + SfxObjectShell* pShell = GetDocShell(); + SotStorageRef xRootStrg = GetRootStorage(); + SvtFilterOptions* pFilterOpt = SvtFilterOptions::Get(); + if( pShell && xRootStrg.Is() && pFilterOpt ) + { + bool bLoadCode = pFilterOpt->IsLoadExcelBasicCode(); + bool bLoadExecutable = pFilterOpt->IsLoadExcelBasicExecutable(); + bool bLoadStrg = pFilterOpt->IsLoadExcelBasicStorage(); + if( bLoadCode || bLoadStrg ) + { + SvxImportMSVBasic aBasicImport( *pShell, *xRootStrg, bLoadCode, bLoadStrg ); + bool bAsComment = !bLoadExecutable; + aBasicImport.Import( EXC_STORAGE_VBA_PROJECT, EXC_STORAGE_VBA, bAsComment ); + } + } +} + + +void ImportExcel8::EndSheet( void ) +{ + GetCondFormatManager().Apply(); + ImportExcel::EndSheet(); +} + + +void ImportExcel8::PostDocLoad( void ) +{ + // reading basic has been delayed until sheet objects (codenames etc.) are read + if( HasBasic() ) + ReadBasic(); + // #i11776# filtered ranges before outlines and hidden rows + if( pExcRoot->pAutoFilterBuffer ) + pExcRoot->pAutoFilterBuffer->Apply(); + + GetWebQueryBuffer().Apply(); //! test if extant + GetSheetProtectBuffer().Apply(); + GetDocProtectBuffer().Apply(); + + ImportExcel::PostDocLoad(); + + // Scenarien bemachen! ACHTUNG: Hier wird Tabellen-Anzahl im Dokument erhoeht!! + if( !pD->IsClipboard() && aScenList.Count() ) + { + pD->UpdateChartListenerCollection(); // references in charts must be updated + + aScenList.Apply( GetRoot() ); + } + + // read doc info (no docshell while pasting from clipboard) + LoadDocumentProperties(); + + // #i45843# Pivot tables are now handled outside of PostDocLoad, so they are available + // when formula cells are calculated, for the GETPIVOTDATA function. +} + +void ImportExcel8::LoadDocumentProperties() +{ + // no docshell while pasting from clipboard + if( SfxObjectShell* pShell = GetDocShell() ) + { + // BIFF5+ without storage is possible + SotStorageRef xRootStrg = GetRootStorage(); + if( xRootStrg.Is() ) try + { + uno::Reference< document::XDocumentPropertiesSupplier > xDPS( pShell->GetModel(), uno::UNO_QUERY_THROW ); + uno::Reference< document::XDocumentProperties > xDocProps( xDPS->getDocumentProperties(), uno::UNO_SET_THROW ); + sfx2::LoadOlePropertySet( xDocProps, xRootStrg ); + } + catch( uno::Exception& ) + { + } + } +} + +//___________________________________________________________________ +// autofilter + +void ImportExcel8::FilterMode( void ) +{ + // The FilterMode record exists: if either the AutoFilter + // record exists or an Advanced Filter is saved and stored + // in the sheet. Thus if the FilterMode records only exists + // then the latter is true.. + if( !pExcRoot->pAutoFilterBuffer ) return; + + pExcRoot->pAutoFilterBuffer->IncrementActiveAF(); + + XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() ); + if( pData ) + pData->SetAutoOrAdvanced(); +} + +void ImportExcel8::AutoFilterInfo( void ) +{ + if( !pExcRoot->pAutoFilterBuffer ) return; + + XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() ); + if( pData ) + { + pData->SetAdvancedRange( NULL ); + pData->Activate(); + } +} + +void ImportExcel8::AutoFilter( void ) +{ + if( !pExcRoot->pAutoFilterBuffer ) return; + + XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() ); + if( pData ) + pData->ReadAutoFilter( aIn ); +} + + + +XclImpAutoFilterData::XclImpAutoFilterData( RootData* pRoot, const ScRange& rRange, const String& rName ) : + ExcRoot( pRoot ), + pCurrDBData(NULL), + nFirstEmpty( 0 ), + bActive( FALSE ), + bHasConflict( FALSE ), + bCriteria( FALSE ), + bAutoOrAdvanced(FALSE), + aFilterName(rName) +{ + aParam.nCol1 = rRange.aStart.Col(); + aParam.nRow1 = rRange.aStart.Row(); + aParam.nTab = rRange.aStart.Tab(); + aParam.nCol2 = rRange.aEnd.Col(); + aParam.nRow2 = rRange.aEnd.Row(); + + aParam.bInplace = TRUE; + +} + +void XclImpAutoFilterData::CreateFromDouble( String& rStr, double fVal ) +{ + rStr += String( ::rtl::math::doubleToUString( fVal, + rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, + ScGlobal::pLocaleData->getNumDecimalSep().GetChar(0), TRUE)); +} + +void XclImpAutoFilterData::SetCellAttribs() +{ + ScDocument& rDoc = pExcRoot->pIR->GetDoc(); + for ( SCCOL nCol = StartCol(); nCol <= EndCol(); nCol++ ) + { + INT16 nFlag = ((ScMergeFlagAttr*) rDoc.GetAttr( nCol, StartRow(), Tab(), ATTR_MERGE_FLAG ))->GetValue(); + rDoc.ApplyAttr( nCol, StartRow(), Tab(), ScMergeFlagAttr( nFlag | SC_MF_AUTO) ); + } +} + +void XclImpAutoFilterData::InsertQueryParam() +{ + if( pCurrDBData && !bHasConflict ) + { + ScRange aAdvRange; + BOOL bHasAdv = pCurrDBData->GetAdvancedQuerySource( aAdvRange ); + if( bHasAdv ) + pExcRoot->pIR->GetDoc().CreateQueryParam( aAdvRange.aStart.Col(), + aAdvRange.aStart.Row(), aAdvRange.aEnd.Col(), aAdvRange.aEnd.Row(), + aAdvRange.aStart.Tab(), aParam ); + + pCurrDBData->SetQueryParam( aParam ); + if( bHasAdv ) + pCurrDBData->SetAdvancedQuerySource( &aAdvRange ); + else + { + pCurrDBData->SetAutoFilter( TRUE ); + SetCellAttribs(); + } + } +} + +static void ExcelQueryToOooQuery( ScQueryEntry& rEntry ) +{ + if( ( rEntry.eOp != SC_EQUAL && rEntry.eOp != SC_NOT_EQUAL ) || rEntry.pStr == NULL ) + return; + else + { + xub_StrLen nLen = rEntry.pStr->Len(); + sal_Unicode nStart = rEntry.pStr->GetChar( 0 ); + sal_Unicode nEnd = rEntry.pStr->GetChar( nLen-1 ); + if( nLen >2 && nStart == '*' && nEnd == '*' ) + { + rEntry.pStr->Erase( nLen-1, 1 ); + rEntry.pStr->Erase( 0, 1 ); + rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_CONTAINS : SC_DOES_NOT_CONTAIN; + } + else if( nLen > 1 && nStart == '*' && nEnd != '*' ) + { + rEntry.pStr->Erase( 0, 1 ); + rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_ENDS_WITH : SC_DOES_NOT_END_WITH; + } + else if( nLen > 1 && nStart != '*' && nEnd == '*' ) + { + rEntry.pStr->Erase( nLen-1, 1 ); + rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_BEGINS_WITH : SC_DOES_NOT_BEGIN_WITH; + } + else if( nLen == 2 && nStart == '*' && nEnd == '*' ) + { + rEntry.pStr->Erase( 0, 1 ); + } + } +} + +void XclImpAutoFilterData::ReadAutoFilter( XclImpStream& rStrm ) +{ + UINT16 nCol, nFlags; + rStrm >> nCol >> nFlags; + + ScQueryConnect eConn = ::get_flagvalue( nFlags, EXC_AFFLAG_ANDORMASK, SC_OR, SC_AND ); + BOOL bTop10 = ::get_flag( nFlags, EXC_AFFLAG_TOP10 ); + BOOL bTopOfTop10 = ::get_flag( nFlags, EXC_AFFLAG_TOP10TOP ); + BOOL bPercent = ::get_flag( nFlags, EXC_AFFLAG_TOP10PERC ); + UINT16 nCntOfTop10 = nFlags >> 7; + SCSIZE nCount = aParam.GetEntryCount(); + + if( bTop10 ) + { + if( nFirstEmpty < nCount ) + { + ScQueryEntry& aEntry = aParam.GetEntry( nFirstEmpty ); + aEntry.bDoQuery = TRUE; + aEntry.bQueryByString = TRUE; + aEntry.nField = static_cast<SCCOLROW>(StartCol() + static_cast<SCCOL>(nCol)); + aEntry.eOp = bTopOfTop10 ? + (bPercent ? SC_TOPPERC : SC_TOPVAL) : (bPercent ? SC_BOTPERC : SC_BOTVAL); + aEntry.eConnect = SC_AND; + aEntry.pStr->Assign( String::CreateFromInt32( (sal_Int32) nCntOfTop10 ) ); + + rStrm.Ignore( 20 ); + nFirstEmpty++; + } + } + else + { + UINT8 nE, nType, nOper, nBoolErr, nVal; + INT32 nRK; + double fVal; + BOOL bIgnore; + + UINT8 nStrLen[ 2 ] = { 0, 0 }; + ScQueryEntry *pQueryEntries[ 2 ] = { NULL, NULL }; + + for( nE = 0; nE < 2; nE++ ) + { + if( nFirstEmpty < nCount ) + { + ScQueryEntry& aEntry = aParam.GetEntry( nFirstEmpty ); + pQueryEntries[ nE ] = &aEntry; + bIgnore = FALSE; + + rStrm >> nType >> nOper; + switch( nOper ) + { + case EXC_AFOPER_LESS: + aEntry.eOp = SC_LESS; + break; + case EXC_AFOPER_EQUAL: + aEntry.eOp = SC_EQUAL; + break; + case EXC_AFOPER_LESSEQUAL: + aEntry.eOp = SC_LESS_EQUAL; + break; + case EXC_AFOPER_GREATER: + aEntry.eOp = SC_GREATER; + break; + case EXC_AFOPER_NOTEQUAL: + aEntry.eOp = SC_NOT_EQUAL; + break; + case EXC_AFOPER_GREATEREQUAL: + aEntry.eOp = SC_GREATER_EQUAL; + break; + default: + aEntry.eOp = SC_EQUAL; + } + + switch( nType ) + { + case EXC_AFTYPE_RK: + rStrm >> nRK; + rStrm.Ignore( 4 ); + CreateFromDouble( *aEntry.pStr, XclTools::GetDoubleFromRK( nRK ) ); + break; + case EXC_AFTYPE_DOUBLE: + rStrm >> fVal; + CreateFromDouble( *aEntry.pStr, fVal ); + break; + case EXC_AFTYPE_STRING: + rStrm.Ignore( 4 ); + rStrm >> nStrLen[ nE ]; + rStrm.Ignore( 3 ); + aEntry.pStr->Erase(); + break; + case EXC_AFTYPE_BOOLERR: + rStrm >> nBoolErr >> nVal; + rStrm.Ignore( 6 ); + aEntry.pStr->Assign( String::CreateFromInt32( (sal_Int32) nVal ) ); + bIgnore = (BOOL) nBoolErr; + break; + case EXC_AFTYPE_EMPTY: + aEntry.bQueryByString = FALSE; + aEntry.nVal = SC_EMPTYFIELDS; + aEntry.eOp = SC_EQUAL; + break; + case EXC_AFTYPE_NOTEMPTY: + aEntry.bQueryByString = FALSE; + aEntry.nVal = SC_NONEMPTYFIELDS; + aEntry.eOp = SC_EQUAL; + break; + default: + rStrm.Ignore( 8 ); + bIgnore = TRUE; + } + + /* #i39464# conflict, if two conditions of one column are 'OR'ed, + and they follow conditions of other columns. + Example: Let A1 be a condition of column A, and B1 and B2 + conditions of column B, connected with OR. Excel performs + 'A1 AND (B1 OR B2)' in this case, but Calc would do + '(A1 AND B1) OR B2' instead. */ + if( (nFirstEmpty > 1) && nE && (eConn == SC_OR) && !bIgnore ) + bHasConflict = TRUE; + if( !bHasConflict && !bIgnore ) + { + aEntry.bDoQuery = TRUE; + aEntry.bQueryByString = TRUE; + aEntry.nField = static_cast<SCCOLROW>(StartCol() + static_cast<SCCOL>(nCol)); + aEntry.eConnect = nE ? eConn : SC_AND; + nFirstEmpty++; + } + } + else + rStrm.Ignore( 10 ); + } + + for( nE = 0; nE < 2; nE++ ) + if( nStrLen[ nE ] && pQueryEntries[ nE ] ) + { + pQueryEntries[ nE ]->pStr->Assign ( rStrm.ReadUniString( nStrLen[ nE ] ) ); + ExcelQueryToOooQuery( *pQueryEntries[ nE ] ); + } + + } +} + +void XclImpAutoFilterData::SetAdvancedRange( const ScRange* pRange ) +{ + if (pRange) + { + aCriteriaRange = *pRange; + bCriteria = TRUE; + } + else + bCriteria = FALSE; +} + +void XclImpAutoFilterData::SetExtractPos( const ScAddress& rAddr ) +{ + aParam.nDestCol = rAddr.Col(); + aParam.nDestRow = rAddr.Row(); + aParam.nDestTab = rAddr.Tab(); + aParam.bInplace = FALSE; + aParam.bDestPers = TRUE; +} + +void XclImpAutoFilterData::Apply( const BOOL bUseUnNamed ) +{ + CreateScDBData(bUseUnNamed); + + if( bActive ) + { + InsertQueryParam(); + + // #i38093# rows hidden by filter need extra flag, but CR_FILTERED is not set here yet +// SCROW nRow1 = StartRow(); +// SCROW nRow2 = EndRow(); +// size_t nRows = nRow2 - nRow1 + 1; +// boost::scoped_array<BYTE> pFlags( new BYTE[nRows]); +// pExcRoot->pDoc->GetRowFlagsArray( Tab()).FillDataArray( nRow1, nRow2, +// pFlags.get()); +// for (size_t j=0; j<nRows; ++j) +// { +// if ((pFlags[j] & CR_HIDDEN) && !(pFlags[j] & CR_FILTERED)) +// pExcRoot->pDoc->SetRowFlags( nRow1 + j, Tab(), +// pFlags[j] | CR_FILTERED ); +// } + } +} + +void XclImpAutoFilterData::CreateScDBData( const BOOL bUseUnNamed ) +{ + + // Create the ScDBData() object if the AutoFilter is activated + // or if we need to create the Advanced Filter. + if( bActive || bCriteria) + { + ScDBCollection& rColl = pExcRoot->pIR->GetDatabaseRanges(); + pCurrDBData = rColl.GetDBAtArea( Tab(), StartCol(), StartRow(), EndCol(), EndRow() ); + if( !pCurrDBData ) + { + AmendAFName(bUseUnNamed); + + pCurrDBData = new ScDBData( aFilterName, Tab(), StartCol(), StartRow(), EndCol(), EndRow() ); + + if( pCurrDBData ) + { + if(bCriteria) + { + EnableRemoveFilter(); + + pCurrDBData->SetQueryParam( aParam ); + pCurrDBData->SetAdvancedQuerySource(&aCriteriaRange); + } + else + pCurrDBData->SetAdvancedQuerySource(NULL); + rColl.Insert( pCurrDBData ); + } + } + } + +} + +void XclImpAutoFilterData::EnableRemoveFilter() +{ + // only if this is a saved Advanced filter + if( !bActive && bAutoOrAdvanced ) + { + ScQueryEntry& aEntry = aParam.GetEntry( nFirstEmpty ); + aEntry.bDoQuery = TRUE; + ++nFirstEmpty; + } + + // TBD: force the automatic activation of the + // "Remove Filter" by setting a virtual mouse click + // inside the advanced range +} + +void XclImpAutoFilterData::AmendAFName(const BOOL bUseUnNamed) +{ + // If-and-only-if we have one AF filter then + // use the Calc "unnamed" range name. Calc + // only supports one in total while Excel + // supports one per sheet. + if( bUseUnNamed && bAutoOrAdvanced ) + aFilterName = ScGlobal::GetRscString(STR_DB_NONAME); +} + +XclImpAutoFilterBuffer::XclImpAutoFilterBuffer() : + nAFActiveCount( 0 ) +{ +} + +XclImpAutoFilterBuffer::~XclImpAutoFilterBuffer() +{ + for( XclImpAutoFilterData* pData = _First(); pData; pData = _Next() ) + delete pData; +} + +void XclImpAutoFilterBuffer::Insert( RootData* pRoot, const ScRange& rRange, + const String& rName ) +{ + if( !GetByTab( rRange.aStart.Tab() ) ) + Append( new XclImpAutoFilterData( pRoot, rRange, rName ) ); +} + +void XclImpAutoFilterBuffer::AddAdvancedRange( const ScRange& rRange ) +{ + XclImpAutoFilterData* pData = GetByTab( rRange.aStart.Tab() ); + if( pData ) + pData->SetAdvancedRange( &rRange ); +} + +void XclImpAutoFilterBuffer::AddExtractPos( const ScRange& rRange ) +{ + XclImpAutoFilterData* pData = GetByTab( rRange.aStart.Tab() ); + if( pData ) + pData->SetExtractPos( rRange.aStart ); +} + +void XclImpAutoFilterBuffer::Apply() +{ + for( XclImpAutoFilterData* pData = _First(); pData; pData = _Next() ) + pData->Apply(UseUnNamed()); +} + +XclImpAutoFilterData* XclImpAutoFilterBuffer::GetByTab( SCTAB nTab ) +{ + for( XclImpAutoFilterData* pData = _First(); pData; pData = _Next() ) + if( pData->Tab() == nTab ) + return pData; + return NULL; +} + diff --git a/sc/source/filter/excel/excrecds.cxx b/sc/source/filter/excel/excrecds.cxx new file mode 100644 index 000000000000..9a66325db419 --- /dev/null +++ b/sc/source/filter/excel/excrecds.cxx @@ -0,0 +1,1051 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + + + +//------------------------------------------------------------------------ + +#include "excrecds.hxx" + +#include <map> +#include <filter/msfilter/countryid.hxx> + +#include "scitems.hxx" +#include <editeng/eeitem.hxx> + +#include <sfx2/objsh.hxx> + +#include <editeng/editdata.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editobj.hxx> +#include <editeng/editstat.hxx> + +#include <editeng/flditem.hxx> +#include <editeng/flstitem.hxx> + +#include <svx/algitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/brshitem.hxx> +#include <svx/pageitem.hxx> +#include <editeng/paperinf.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/escpitem.hxx> +#include <svl/intitem.hxx> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <svtools/ctrltool.hxx> + +#define _SVSTDARR_USHORTS +#include <svl/svstdarr.hxx> + +#include <string.h> + +#include "global.hxx" +#include "globstr.hrc" +#include "docpool.hxx" +#include "patattr.hxx" +#include "cell.hxx" +#include "document.hxx" +#include "scextopt.hxx" +#include "patattr.hxx" +#include "attrib.hxx" +#include "progress.hxx" +#include "dociter.hxx" +#include "rangenam.hxx" +#include "dbcolect.hxx" +#include "stlsheet.hxx" +#include "stlpool.hxx" +#include "editutil.hxx" +#include "formula/errorcodes.hxx" + +#include "excdoc.hxx" +#include "xeescher.hxx" +#include "xeformula.hxx" +#include "xelink.hxx" +#include "xename.hxx" +#include "xecontent.hxx" + +#include "xcl97rec.hxx" + +#include <oox/core/tokens.hxx> + +using ::com::sun::star::uno::Sequence; + + +using ::rtl::OString; + +//--------------------------------------------------------- class ExcDummy_00 - +const BYTE ExcDummy_00::pMyData[] = { + 0x5c, 0x00, 0x20, 0x00, 0x04, 'C', 'a', 'l', 'c', // WRITEACCESS + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 +}; +const sal_Size ExcDummy_00::nMyLen = sizeof( ExcDummy_00::pMyData ); + +//-------------------------------------------------------- class ExcDummy_04x - +const BYTE ExcDummy_040::pMyData[] = { + 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, // BACKUP + 0x8d, 0x00, 0x02, 0x00, 0x00, 0x00, // HIDEOBJ +}; +const sal_Size ExcDummy_040::nMyLen = sizeof( ExcDummy_040::pMyData ); + +const BYTE ExcDummy_041::pMyData[] = { + 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, // PRECISION + 0xda, 0x00, 0x02, 0x00, 0x00, 0x00 // BOOKBOOL +}; +const sal_Size ExcDummy_041::nMyLen = sizeof( ExcDummy_041::pMyData ); + +//-------------------------------------------------------- class ExcDummy_02a - +const BYTE ExcDummy_02a::pMyData[] = { + 0x0d, 0x00, 0x02, 0x00, 0x01, 0x00, // CALCMODE + 0x0c, 0x00, 0x02, 0x00, 0x64, 0x00, // CALCCOUNT + 0x0f, 0x00, 0x02, 0x00, 0x01, 0x00, // REFMODE + 0x11, 0x00, 0x02, 0x00, 0x00, 0x00, // ITERATION + 0x10, 0x00, 0x08, 0x00, 0xfc, 0xa9, 0xf1, 0xd2, 0x4d, // DELTA + 0x62, 0x50, 0x3f, + 0x5f, 0x00, 0x02, 0x00, 0x01, 0x00 // SAVERECALC +}; +const sal_Size ExcDummy_02a::nMyLen = sizeof( ExcDummy_02a::pMyData ); + +//----------------------------------------------------------- class ExcRecord - + +void ExcRecord::Save( XclExpStream& rStrm ) +{ + SetRecHeader( GetNum(), GetLen() ); + XclExpRecord::Save( rStrm ); +} + +void ExcRecord::SaveCont( XclExpStream& /*rStrm*/ ) +{ +} + +void ExcRecord::WriteBody( XclExpStream& rStrm ) +{ + SaveCont( rStrm ); +} + + +//--------------------------------------------------------- class ExcEmptyRec - + +void ExcEmptyRec::Save( XclExpStream& /*rStrm*/ ) +{ +} + + +UINT16 ExcEmptyRec::GetNum() const +{ + return 0; +} + + +sal_Size ExcEmptyRec::GetLen() const +{ + return 0; +} + + + +//------------------------------------------------------- class ExcRecordList - + +ExcRecordList::~ExcRecordList() +{ + for( ExcRecord* pRec = First(); pRec; pRec = Next() ) + delete pRec; +} + + +void ExcRecordList::Save( XclExpStream& rStrm ) +{ + for( ExcRecord* pRec = First(); pRec; pRec = Next() ) + pRec->Save( rStrm ); +} + + + +//--------------------------------------------------------- class ExcDummyRec - + +void ExcDummyRec::Save( XclExpStream& rStrm ) +{ + rStrm.Write( GetData(), GetLen() ); // raw write mode +} + + +UINT16 ExcDummyRec::GetNum( void ) const +{ + return 0x0000; +} + + + +//------------------------------------------------------- class ExcBoolRecord - + +void ExcBoolRecord::SaveCont( XclExpStream& rStrm ) +{ + rStrm << (UINT16)(bVal ? 0x0001 : 0x0000); +} + + +sal_Size ExcBoolRecord::GetLen( void ) const +{ + return 2; +} + + + + +//--------------------------------------------------------- class ExcBof_Base - + +ExcBof_Base::ExcBof_Base() : + nRupBuild( 0x096C ), // copied from Excel + nRupYear( 0x07C9 ) // copied from Excel +{ +} + + + +//-------------------------------------------------------------- class ExcBof - + +ExcBof::ExcBof( void ) +{ + nDocType = 0x0010; + nVers = 0x0500; +} + + +void ExcBof::SaveCont( XclExpStream& rStrm ) +{ + rStrm << nVers << nDocType << nRupBuild << nRupYear; +} + + +UINT16 ExcBof::GetNum( void ) const +{ + return 0x0809; +} + + +sal_Size ExcBof::GetLen( void ) const +{ + return 8; +} + + + +//------------------------------------------------------------- class ExcBofW - + +ExcBofW::ExcBofW( void ) +{ + nDocType = 0x0005; + nVers = 0x0500; +} + + +void ExcBofW::SaveCont( XclExpStream& rStrm ) +{ + rStrm << nVers << nDocType << nRupBuild << nRupYear; +} + + + +UINT16 ExcBofW::GetNum( void ) const +{ + return 0x0809; +} + + + +sal_Size ExcBofW::GetLen( void ) const +{ + return 8; +} + + + +//-------------------------------------------------------------- class ExcEof - + +UINT16 ExcEof::GetNum( void ) const +{ + return 0x000A; +} + + +sal_Size ExcEof::GetLen( void ) const +{ + return 0; +} + + + +//--------------------------------------------------------- class ExcDummy_00 - + +sal_Size ExcDummy_00::GetLen( void ) const +{ + return nMyLen; +} + + +const BYTE* ExcDummy_00::GetData( void ) const +{ + return pMyData; +} + + + +//-------------------------------------------------------- class ExcDummy_04x - + +sal_Size ExcDummy_040::GetLen( void ) const +{ + return nMyLen; +} + + +const BYTE* ExcDummy_040::GetData( void ) const +{ + return pMyData; +} + + + + +sal_Size ExcDummy_041::GetLen( void ) const +{ + return nMyLen; +} + + +const BYTE* ExcDummy_041::GetData( void ) const +{ + return pMyData; +} + + + +//------------------------------------------------------------- class Exc1904 - + +Exc1904::Exc1904( ScDocument& rDoc ) +{ + Date* pDate = rDoc.GetFormatTable()->GetNullDate(); + bVal = pDate ? (*pDate == Date( 1, 1, 1904 )) : FALSE; +} + + +UINT16 Exc1904::GetNum( void ) const +{ + return 0x0022; +} + + +void Exc1904::SaveXml( XclExpXmlStream& rStrm ) +{ + rStrm.WriteAttributes( + XML_date1904, XclXmlUtils::ToPsz( bVal ), + FSEND ); +} + + + +//------------------------------------------------------ class ExcBundlesheet - + +ExcBundlesheetBase::ExcBundlesheetBase( RootData& rRootData, SCTAB nTabNum ) : + nStrPos( STREAM_SEEK_TO_END ), + nOwnPos( STREAM_SEEK_TO_END ), + nGrbit( rRootData.pER->GetTabInfo().IsVisibleTab( nTabNum ) ? 0x0000 : 0x0001 ), + nTab( nTabNum ) +{ +} + + +ExcBundlesheetBase::ExcBundlesheetBase() : + nStrPos( STREAM_SEEK_TO_END ), + nOwnPos( STREAM_SEEK_TO_END ), + nGrbit( 0x0000 ), + nTab( SCTAB_GLOBAL ) +{ +} + + +void ExcBundlesheetBase::UpdateStreamPos( XclExpStream& rStrm ) +{ + rStrm.SetSvStreamPos( nOwnPos ); + rStrm.DisableEncryption(); + rStrm << static_cast<sal_uInt32>(nStrPos); + rStrm.EnableEncryption(); +} + + +UINT16 ExcBundlesheetBase::GetNum( void ) const +{ + return 0x0085; +} + + + + +ExcBundlesheet::ExcBundlesheet( RootData& rRootData, SCTAB _nTab ) : + ExcBundlesheetBase( rRootData, _nTab ) +{ + String sTabName = rRootData.pER->GetTabInfo().GetScTabName( _nTab ); + DBG_ASSERT( sTabName.Len() < 256, "ExcBundlesheet::ExcBundlesheet - table name too long" ); + aName = ByteString( sTabName, rRootData.pER->GetTextEncoding() ); +} + + +void ExcBundlesheet::SaveCont( XclExpStream& rStrm ) +{ + nOwnPos = rStrm.GetSvStreamPos(); + rStrm << (UINT32) 0x00000000 // dummy (stream position of the sheet) + << nGrbit; + rStrm.WriteByteString( aName ); // 8 bit length, max 255 chars +} + + +sal_Size ExcBundlesheet::GetLen() const +{ + return 7 + Min( aName.Len(), (xub_StrLen) 255 ); +} + + +//--------------------------------------------------------- class ExcDummy_02 - + +sal_Size ExcDummy_02a::GetLen( void ) const +{ + return nMyLen; +} + +const BYTE* ExcDummy_02a::GetData( void ) const +{ + return pMyData; +} +//--------------------------------------------------------- class ExcDummy_02 - + +XclExpCountry::XclExpCountry( const XclExpRoot& rRoot ) : + XclExpRecord( EXC_ID_COUNTRY, 4 ) +{ + /* #i31530# set document country as UI country too - + needed for correct behaviour of number formats. */ + mnUICountry = mnDocCountry = static_cast< sal_uInt16 >( + ::msfilter::ConvertLanguageToCountry( rRoot.GetDocLanguage() ) ); +} + +void XclExpCountry::WriteBody( XclExpStream& rStrm ) +{ + rStrm << mnUICountry << mnDocCountry; +} + +// XclExpWsbool =============================================================== + +XclExpWsbool::XclExpWsbool( bool bFitToPages, SCTAB nScTab, XclExpFilterManager* pManager ) + : XclExpUInt16Record( EXC_ID_WSBOOL, EXC_WSBOOL_DEFAULTFLAGS ) + , mnScTab( nScTab ) + , mpManager( pManager ) +{ + if( bFitToPages ) + SetValue( GetValue() | EXC_WSBOOL_FITTOPAGE ); +} + +void XclExpWsbool::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + rWorksheet->startElement( XML_sheetPr, + // OOXTODO: XML_syncHorizontal, + // OOXTODO: XML_syncVertical, + // OOXTODO: XML_syncRef, + // OOXTODO: XML_transitionEvaluation, + // OOXTODO: XML_transitionEntry, + // OOXTODO: XML_published, + // OOXTODO: XML_codeName, + XML_filterMode, mpManager ? XclXmlUtils::ToPsz( mpManager->HasFilterMode( mnScTab ) ) : NULL, + // OOXTODO: XML_enableFormatConditionsCalculation, + FSEND ); + // OOXTODO: elements XML_tabColor, XML_outlinePr + rWorksheet->singleElement( XML_pageSetUpPr, + // OOXTODO: XML_autoPageBreaks, + XML_fitToPage, XclXmlUtils::ToPsz( GetValue() & EXC_WSBOOL_FITTOPAGE ), + FSEND ); + rWorksheet->endElement( XML_sheetPr ); +} + + +// XclExpWindowProtection =============================================================== + +XclExpWindowProtection::XclExpWindowProtection(bool bValue) : + XclExpBoolRecord(EXC_ID_WINDOWPROTECT, bValue) +{ +} + +void XclExpWindowProtection::SaveXml( XclExpXmlStream& rStrm ) +{ + rStrm.WriteAttributes( + XML_lockWindows, XclXmlUtils::ToPsz( GetBool() ), + FSEND ); +} + +// XclExpDocProtection =============================================================== + +XclExpProtection::XclExpProtection(bool bValue) : + XclExpBoolRecord(EXC_ID_PROTECT, bValue) +{ +} + +// ============================================================================ + +XclExpPassHash::XclExpPassHash(const Sequence<sal_Int8>& aHash) : + XclExpRecord(EXC_ID_PASSWORD, 2), + mnHash(0x0000) +{ + if (aHash.getLength() >= 2) + { + mnHash = ((aHash[0] << 8) & 0xFFFF); + mnHash |= (aHash[1] & 0xFF); + } +} + +XclExpPassHash::~XclExpPassHash() +{ +} + +void XclExpPassHash::WriteBody(XclExpStream& rStrm) +{ + rStrm << mnHash; +} + +// ============================================================================ + +XclExpFiltermode::XclExpFiltermode() : + XclExpEmptyRecord( EXC_ID_FILTERMODE ) +{ +} + +// ---------------------------------------------------------------------------- + +XclExpAutofilterinfo::XclExpAutofilterinfo( const ScAddress& rStartPos, SCCOL nScCol ) : + XclExpUInt16Record( EXC_ID_AUTOFILTERINFO, static_cast< sal_uInt16 >( nScCol ) ), + maStartPos( rStartPos ) +{ +} + +// ---------------------------------------------------------------------------- + +ExcFilterCondition::ExcFilterCondition() : + nType( EXC_AFTYPE_NOTUSED ), + nOper( EXC_AFOPER_EQUAL ), + fVal( 0.0 ), + pText( NULL ) +{ +} + +ExcFilterCondition::~ExcFilterCondition() +{ + if( pText ) + delete pText; +} + +sal_Size ExcFilterCondition::GetTextBytes() const +{ + return pText ? (1 + pText->GetBufferSize()) : 0; +} + +void ExcFilterCondition::SetCondition( UINT8 nTp, UINT8 nOp, double fV, String* pT ) +{ + nType = nTp; + nOper = nOp; + fVal = fV; + + delete pText; + pText = pT ? new XclExpString( *pT, EXC_STR_8BITLENGTH ) : NULL; +} + +void ExcFilterCondition::Save( XclExpStream& rStrm ) +{ + rStrm << nType << nOper; + switch( nType ) + { + case EXC_AFTYPE_DOUBLE: + rStrm << fVal; + break; + case EXC_AFTYPE_STRING: + DBG_ASSERT( pText, "ExcFilterCondition::Save() -- pText is NULL!" ); + rStrm << (UINT32)0 << (UINT8) pText->Len() << (UINT16)0 << (UINT8)0; + break; + case EXC_AFTYPE_BOOLERR: + rStrm << (UINT8)0 << (UINT8)((fVal != 0) ? 1 : 0) << (UINT32)0 << (UINT16)0; + break; + default: + rStrm << (UINT32)0 << (UINT32)0; + } +} + +static const char* lcl_GetOperator( UINT8 nOper ) +{ + switch( nOper ) + { + case EXC_AFOPER_EQUAL: return "equal"; + case EXC_AFOPER_GREATER: return "greaterThan"; + case EXC_AFOPER_GREATEREQUAL: return "greaterThanOrEqual"; + case EXC_AFOPER_LESS: return "lessThan"; + case EXC_AFOPER_LESSEQUAL: return "lessThanOrEqual"; + case EXC_AFOPER_NOTEQUAL: return "notEqual"; + case EXC_AFOPER_NONE: + default: return "**none**"; + } +} + +static OString lcl_GetValue( UINT8 nType, double fVal, XclExpString* pStr ) +{ + switch( nType ) + { + case EXC_AFTYPE_STRING: return XclXmlUtils::ToOString( *pStr ); + case EXC_AFTYPE_DOUBLE: return OString::valueOf( fVal ); + case EXC_AFTYPE_BOOLERR: return OString::valueOf( (sal_Int32) ( fVal != 0 ? 1 : 0 ) ); + default: return OString(); + } +} + +void ExcFilterCondition::SaveXml( XclExpXmlStream& rStrm ) +{ + if( IsEmpty() ) + return; + + rStrm.GetCurrentStream()->singleElement( XML_customFilter, + XML_operator, lcl_GetOperator( nOper ), + XML_val, lcl_GetValue( nType, fVal, pText ).getStr(), + FSEND ); +} + +void ExcFilterCondition::SaveText( XclExpStream& rStrm ) +{ + if( nType == EXC_AFTYPE_STRING ) + { + DBG_ASSERT( pText, "ExcFilterCondition::SaveText() -- pText is NULL!" ); + pText->WriteFlagField( rStrm ); + pText->WriteBuffer( rStrm ); + } +} + +// ---------------------------------------------------------------------------- + +XclExpAutofilter::XclExpAutofilter( const XclExpRoot& rRoot, UINT16 nC ) : + XclExpRecord( EXC_ID_AUTOFILTER, 24 ), + XclExpRoot( rRoot ), + nCol( nC ), + nFlags( 0 ) +{ +} + +BOOL XclExpAutofilter::AddCondition( ScQueryConnect eConn, UINT8 nType, UINT8 nOp, + double fVal, String* pText, BOOL bSimple ) +{ + if( !aCond[ 1 ].IsEmpty() ) + return FALSE; + + UINT16 nInd = aCond[ 0 ].IsEmpty() ? 0 : 1; + + if( nInd == 1 ) + nFlags |= (eConn == SC_OR) ? EXC_AFFLAG_OR : EXC_AFFLAG_AND; + if( bSimple ) + nFlags |= (nInd == 0) ? EXC_AFFLAG_SIMPLE1 : EXC_AFFLAG_SIMPLE2; + + aCond[ nInd ].SetCondition( nType, nOp, fVal, pText ); + + AddRecSize( aCond[ nInd ].GetTextBytes() ); + + return TRUE; +} + +BOOL XclExpAutofilter::AddEntry( const ScQueryEntry& rEntry ) +{ + BOOL bConflict = FALSE; + String sText; + + if( rEntry.pStr ) + { + sText.Assign( *rEntry.pStr ); + switch( rEntry.eOp ) + { + case SC_CONTAINS: + case SC_DOES_NOT_CONTAIN: + { + sText.InsertAscii( "*" , 0 ); + sText.AppendAscii( "*" ); + } + break; + case SC_BEGINS_WITH: + case SC_DOES_NOT_BEGIN_WITH: + sText.AppendAscii( "*" ); + break; + case SC_ENDS_WITH: + case SC_DOES_NOT_END_WITH: + sText.InsertAscii( "*" , 0 ); + break; + default: + { + //nothing + } + } + } + + BOOL bLen = sText.Len() > 0; + + // empty/nonempty fields + if( !bLen && (rEntry.nVal == SC_EMPTYFIELDS) ) + bConflict = !AddCondition( rEntry.eConnect, EXC_AFTYPE_EMPTY, EXC_AFOPER_NONE, 0.0, NULL, TRUE ); + else if( !bLen && (rEntry.nVal == SC_NONEMPTYFIELDS) ) + bConflict = !AddCondition( rEntry.eConnect, EXC_AFTYPE_NOTEMPTY, EXC_AFOPER_NONE, 0.0, NULL, TRUE ); + // other conditions + else + { + double fVal = 0.0; + sal_uInt32 nIndex = 0; + BOOL bIsNum = bLen ? GetFormatter().IsNumberFormat( sText, nIndex, fVal ) : TRUE; + String* pText = bIsNum ? NULL : &sText; + + // top10 flags + UINT16 nNewFlags = 0x0000; + switch( rEntry.eOp ) + { + case SC_TOPVAL: + nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP); + break; + case SC_BOTVAL: + nNewFlags = EXC_AFFLAG_TOP10; + break; + case SC_TOPPERC: + nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP | EXC_AFFLAG_TOP10PERC); + break; + case SC_BOTPERC: + nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10PERC); + break; + default:; + } + BOOL bNewTop10 = ::get_flag( nNewFlags, EXC_AFFLAG_TOP10 ); + + bConflict = HasTop10() && bNewTop10; + if( !bConflict ) + { + if( bNewTop10 ) + { + if( fVal < 0 ) fVal = 0; + if( fVal >= 501 ) fVal = 500; + nFlags |= (nNewFlags | (UINT16)(fVal) << 7); + } + // normal condition + else + { + UINT8 nType = bIsNum ? EXC_AFTYPE_DOUBLE : EXC_AFTYPE_STRING; + UINT8 nOper = EXC_AFOPER_NONE; + + switch( rEntry.eOp ) + { + case SC_EQUAL: nOper = EXC_AFOPER_EQUAL; break; + case SC_LESS: nOper = EXC_AFOPER_LESS; break; + case SC_GREATER: nOper = EXC_AFOPER_GREATER; break; + case SC_LESS_EQUAL: nOper = EXC_AFOPER_LESSEQUAL; break; + case SC_GREATER_EQUAL: nOper = EXC_AFOPER_GREATEREQUAL; break; + case SC_NOT_EQUAL: nOper = EXC_AFOPER_NOTEQUAL; break; + case SC_CONTAINS: + case SC_BEGINS_WITH: + case SC_ENDS_WITH: + nOper = EXC_AFOPER_EQUAL; break; + case SC_DOES_NOT_CONTAIN: + case SC_DOES_NOT_BEGIN_WITH: + case SC_DOES_NOT_END_WITH: + nOper = EXC_AFOPER_NOTEQUAL; break; + default:; + } + bConflict = !AddCondition( rEntry.eConnect, nType, nOper, fVal, pText ); + } + } + } + return bConflict; +} + +void XclExpAutofilter::WriteBody( XclExpStream& rStrm ) +{ + rStrm << nCol << nFlags; + aCond[ 0 ].Save( rStrm ); + aCond[ 1 ].Save( rStrm ); + aCond[ 0 ].SaveText( rStrm ); + aCond[ 1 ].SaveText( rStrm ); +} + +void XclExpAutofilter::SaveXml( XclExpXmlStream& rStrm ) +{ + if( !HasCondition() ) + return; + + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + + rWorksheet->startElement( XML_filterColumn, + XML_colId, OString::valueOf( (sal_Int32) nCol ).getStr(), + // OOXTODO: XML_hiddenButton, AutoFilter12 fHideArrow? + // OOXTODO: XML_showButton, + FSEND ); + + if( HasTop10() ) + { + rWorksheet->singleElement( XML_top10, + XML_top, XclXmlUtils::ToPsz( get_flag( nFlags, EXC_AFFLAG_TOP10TOP ) ), + XML_percent, XclXmlUtils::ToPsz( get_flag( nFlags, EXC_AFFLAG_TOP10PERC ) ), + XML_val, OString::valueOf( (sal_Int32) (nFlags >> 7 ) ).getStr(), + // OOXTODO: XML_filterVal, + FSEND ); + } + + rWorksheet->startElement( XML_customFilters, + XML_and, XclXmlUtils::ToPsz( (nFlags & EXC_AFFLAG_ANDORMASK) == EXC_AFFLAG_AND ), + FSEND ); + aCond[ 0 ].SaveXml( rStrm ); + aCond[ 1 ].SaveXml( rStrm ); + rWorksheet->endElement( XML_customFilters ); + // OOXTODO: XLM_colorFilter, XML_dynamicFilter, + // XML_extLst, XML_filters, XML_iconFilter, XML_top10 + rWorksheet->endElement( XML_filterColumn ); +} + +// ---------------------------------------------------------------------------- + +ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot& rRoot, SCTAB nTab ) : + XclExpRoot( rRoot ), + pFilterMode( NULL ), + pFilterInfo( NULL ) +{ + ScDBCollection& rDBColl = GetDatabaseRanges(); + XclExpNameManager& rNameMgr = GetNameManager(); + + // search for first DB-range with filter + UINT16 nIndex = 0; + BOOL bFound = FALSE; + BOOL bAdvanced = FALSE; + ScDBData* pData = NULL; + ScRange aAdvRange; + while( (nIndex < rDBColl.GetCount()) && !bFound ) + { + pData = rDBColl[ nIndex ]; + if( pData ) + { + ScRange aRange; + pData->GetArea( aRange ); + bAdvanced = pData->GetAdvancedQuerySource( aAdvRange ); + bFound = (aRange.aStart.Tab() == nTab) && + (pData->HasQueryParam() || pData->HasAutoFilter() || bAdvanced); + } + if( !bFound ) + nIndex++; + } + + if( pData && bFound ) + { + ScQueryParam aParam; + pData->GetQueryParam( aParam ); + + ScRange aRange( aParam.nCol1, aParam.nRow1, aParam.nTab, + aParam.nCol2, aParam.nRow2, aParam.nTab ); + SCCOL nColCnt = aParam.nCol2 - aParam.nCol1 + 1; + + maRef = aRange; + + // #i2394# #100489# built-in defined names must be sorted by containing sheet name + rNameMgr.InsertBuiltInName( EXC_BUILTIN_FILTERDATABASE, aRange ); + + // advanced filter + if( bAdvanced ) + { + // filter criteria, excel allows only same table + if( aAdvRange.aStart.Tab() == nTab ) + rNameMgr.InsertBuiltInName( EXC_BUILTIN_CRITERIA, aAdvRange ); + + // filter destination range, excel allows only same table + if( !aParam.bInplace ) + { + ScRange aDestRange( aParam.nDestCol, aParam.nDestRow, aParam.nDestTab ); + aDestRange.aEnd.IncCol( nColCnt - 1 ); + if( aDestRange.aStart.Tab() == nTab ) + rNameMgr.InsertBuiltInName( EXC_BUILTIN_EXTRACT, aDestRange ); + } + + pFilterMode = new XclExpFiltermode; + } + // AutoFilter + else + { + BOOL bConflict = FALSE; + BOOL bContLoop = TRUE; + BOOL bHasOr = FALSE; + SCCOLROW nFirstField = aParam.GetEntry( 0 ).nField; + + // create AUTOFILTER records for filtered columns + for( SCSIZE nEntry = 0; !bConflict && bContLoop && (nEntry < aParam.GetEntryCount()); nEntry++ ) + { + const ScQueryEntry& rEntry = aParam.GetEntry( nEntry ); + + bContLoop = rEntry.bDoQuery; + if( bContLoop ) + { + XclExpAutofilter* pFilter = GetByCol( static_cast<SCCOL>(rEntry.nField) - aRange.aStart.Col() ); + + if( nEntry > 0 ) + bHasOr |= (rEntry.eConnect == SC_OR); + + bConflict = (nEntry > 1) && bHasOr; + if( !bConflict ) + bConflict = (nEntry == 1) && (rEntry.eConnect == SC_OR) && + (nFirstField != rEntry.nField); + if( !bConflict ) + bConflict = pFilter->AddEntry( rEntry ); + } + } + + // additional tests for conflicts + for( size_t nPos = 0, nSize = maFilterList.GetSize(); !bConflict && (nPos < nSize); ++nPos ) + { + XclExpAutofilterRef xFilter = maFilterList.GetRecord( nPos ); + bConflict = xFilter->HasCondition() && xFilter->HasTop10(); + } + + if( bConflict ) + maFilterList.RemoveAllRecords(); + + if( !maFilterList.IsEmpty() ) + pFilterMode = new XclExpFiltermode; + pFilterInfo = new XclExpAutofilterinfo( aRange.aStart, nColCnt ); + } + } +} + +ExcAutoFilterRecs::~ExcAutoFilterRecs() +{ + delete pFilterMode; + delete pFilterInfo; +} + +XclExpAutofilter* ExcAutoFilterRecs::GetByCol( SCCOL nCol ) +{ + XclExpAutofilterRef xFilter; + for( size_t nPos = 0, nSize = maFilterList.GetSize(); nPos < nSize; ++nPos ) + { + xFilter = maFilterList.GetRecord( nPos ); + if( xFilter->GetCol() == static_cast<sal_uInt16>(nCol) ) + return xFilter.get(); + } + xFilter.reset( new XclExpAutofilter( GetRoot(), static_cast<sal_uInt16>(nCol) ) ); + maFilterList.AppendRecord( xFilter ); + return xFilter.get(); +} + +BOOL ExcAutoFilterRecs::IsFiltered( SCCOL nCol ) +{ + for( size_t nPos = 0, nSize = maFilterList.GetSize(); nPos < nSize; ++nPos ) + if( maFilterList.GetRecord( nPos )->GetCol() == static_cast<sal_uInt16>(nCol) ) + return TRUE; + return FALSE; +} + +void ExcAutoFilterRecs::AddObjRecs() +{ + if( pFilterInfo ) + { + ScAddress aAddr( pFilterInfo->GetStartPos() ); + for( SCCOL nObj = 0, nCount = pFilterInfo->GetColCount(); nObj < nCount; nObj++ ) + { + XclObj* pObjRec = new XclObjDropDown( GetObjectManager(), aAddr, IsFiltered( nObj ) ); + GetObjectManager().AddObj( pObjRec ); + aAddr.IncCol( 1 ); + } + } +} + +void ExcAutoFilterRecs::Save( XclExpStream& rStrm ) +{ + if( pFilterMode ) + pFilterMode->Save( rStrm ); + if( pFilterInfo ) + pFilterInfo->Save( rStrm ); + maFilterList.Save( rStrm ); +} + +void ExcAutoFilterRecs::SaveXml( XclExpXmlStream& rStrm ) +{ + if( maFilterList.IsEmpty() ) + return; + + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + rWorksheet->startElement( XML_autoFilter, + XML_ref, XclXmlUtils::ToOString( maRef ).getStr(), + FSEND ); + // OOXTODO: XML_extLst, XML_sortState + maFilterList.SaveXml( rStrm ); + rWorksheet->endElement( XML_autoFilter ); +} + +bool ExcAutoFilterRecs::HasFilterMode() const +{ + return pFilterMode != NULL; +} + +// ---------------------------------------------------------------------------- + +XclExpFilterManager::XclExpFilterManager( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ) +{ +} + +void XclExpFilterManager::InitTabFilter( SCTAB nScTab ) +{ + maFilterMap[ nScTab ].reset( new ExcAutoFilterRecs( GetRoot(), nScTab ) ); +} + +XclExpRecordRef XclExpFilterManager::CreateRecord( SCTAB nScTab ) +{ + XclExpTabFilterRef xRec; + XclExpTabFilterMap::iterator aIt = maFilterMap.find( nScTab ); + if( aIt != maFilterMap.end() ) + { + xRec = aIt->second; + xRec->AddObjRecs(); + } + return xRec; +} + +bool XclExpFilterManager::HasFilterMode( SCTAB nScTab ) +{ + XclExpTabFilterRef xRec; + XclExpTabFilterMap::iterator aIt = maFilterMap.find( nScTab ); + if( aIt != maFilterMap.end() ) + { + return aIt->second->HasFilterMode(); + } + return false; +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/exctools.cxx b/sc/source/filter/excel/exctools.cxx new file mode 100644 index 000000000000..e6453c2f11e2 --- /dev/null +++ b/sc/source/filter/excel/exctools.cxx @@ -0,0 +1,434 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + + + +//------------------------------------------------------------------------ + +#include "scitems.hxx" +#include <editeng/eeitem.hxx> + +#include <editeng/editdata.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editobj.hxx> +#include <editeng/editstat.hxx> + +#include "document.hxx" +#include "patattr.hxx" +#include "attrib.hxx" +#include "globstr.hrc" +#include "scextopt.hxx" +#include "progress.hxx" +#include "rangenam.hxx" +#include "editutil.hxx" + +#include "excrecds.hxx" +#include "root.hxx" +#include "imp_op.hxx" +#include "excimp8.hxx" +#include "otlnbuff.hxx" +#include "xcl97rec.hxx" +#include "formel.hxx" +#include "xilink.hxx" +#include "xecontent.hxx" + +// - ALLGEMEINE ---------------------------------------------------------- + +RootData::RootData( void ) +{ + eDateiTyp = BiffX; + pExtSheetBuff = NULL; + pShrfmlaBuff = NULL; + pExtNameBuff = NULL; + pFmlaConverter = NULL; + + pAutoFilterBuffer = NULL; + pPrintRanges = new _ScRangeListTabs; + pPrintTitles = new _ScRangeListTabs; + + pTabId = NULL; + pUserBViewList = NULL; + + pIR = NULL; + pER = NULL; +} + +RootData::~RootData() +{ + delete pExtSheetBuff; + delete pShrfmlaBuff; + delete pExtNameBuff; + delete pAutoFilterBuffer; + delete pPrintRanges; + delete pPrintTitles; +} + + + + +XclImpOutlineBuffer::XclImpOutlineBuffer( SCSIZE nNewSize ) +{ + DBG_ASSERT( nNewSize > 0, "-OutlineBuffer::Ctor: nNewSize == 0!" ); + + nSize = nNewSize + 1; + pLevel = new BYTE[ nSize ]; + pOuted = new BOOL[ nSize ]; + pHidden = new BOOL[ nSize ]; + pOutlineArray = NULL; + + Reset(); +} + + +XclImpOutlineBuffer::~XclImpOutlineBuffer() +{ + delete[] pLevel; + delete[] pOuted; + delete[] pHidden; +} + + +void XclImpOutlineBuffer::SetLevel( SCSIZE nIndex, BYTE nVal, BOOL bOuted, BOOL bHidden ) +{ + if( nIndex < nSize ) + { + pLevel[ nIndex ] = nVal; + pOuted[ nIndex ] = bOuted; + pHidden[ nIndex ] = bHidden; + + if( nIndex > nLast ) + nLast = nIndex; + if( nVal > nMaxLevel ) + nMaxLevel = nVal; + } +} + + +void XclImpOutlineBuffer::SetOutlineArray( ScOutlineArray* pOArray ) +{ + pOutlineArray = pOArray; +} + + +// transtorm xcl-outline into SC-outline +void XclImpOutlineBuffer::MakeScOutline( void ) +{ + if( !pOutlineArray || !HasOutline() ) + return; + + const UINT16 nNumLev = 8; + BOOL bPreOutedLevel = FALSE; + BYTE nCurrLevel = 0; + BOOL bMakeHidden[ nNumLev ]; + BOOL bMakeVisible[ nNumLev + 1 ]; + + sal_uInt16 nLevel; + for( nLevel = 0; nLevel < nNumLev; ++nLevel ) + bMakeHidden[ nLevel ] = FALSE; + for( nLevel = 0; nLevel <= nNumLev; ++nLevel ) + bMakeVisible[ nLevel ] = TRUE; + if( nLast < (nSize - 1) ) + nLast++; + + // search for hidden attributes at end of level, move them to begin + if( bButtonNormal ) + { + for( BYTE nWorkLevel = 1; nWorkLevel <= nMaxLevel; nWorkLevel++ ) + { + UINT16 nStartPos = 0; + BYTE nCurrLevel2 = 0; + BYTE nPrevLevel = 0; + + for( SCSIZE nC = 0 ; nC <= nLast ; nC++ ) + { + nPrevLevel = nCurrLevel2; + nCurrLevel2 = pLevel[ nC ]; + if( (nPrevLevel < nWorkLevel) && (nCurrLevel2 >= nWorkLevel) ) + nStartPos = static_cast< sal_uInt16 >( nC ); + else if( (nPrevLevel >= nWorkLevel) && (nCurrLevel2 < nWorkLevel) ) + { + if( pOuted[ nC ] && pHidden[ nStartPos ] ) + { + if( nStartPos ) + pOuted[ nStartPos - 1 ] = TRUE; + else + bPreOutedLevel = TRUE; + pOuted[ nC ] = FALSE; + } + } + } + } + } + else + bPreOutedLevel = pHidden[ 0 ]; + + // generate SC outlines + UINT16 nPrevC; + UINT16 nStart[ nNumLev ]; + BOOL bDummy; + BOOL bPrevOuted = bPreOutedLevel; + BOOL bCurrHidden = FALSE; + BOOL bPrevHidden = FALSE; + + for( SCSIZE nC = 0; nC <= nLast; nC++ ) + { + BYTE nWorkLevel = pLevel[ nC ]; + + nPrevC = static_cast< sal_uInt16 >( nC ? nC - 1 : 0 ); + bPrevHidden = bCurrHidden; + bCurrHidden = pHidden[ nC ]; + + // open new levels + while( nWorkLevel > nCurrLevel ) + { + nCurrLevel++; + bMakeHidden[ nCurrLevel ] = bPrevOuted; + bMakeVisible[ nCurrLevel + 1 ] = + bMakeVisible[ nCurrLevel ] && !bMakeHidden[ nCurrLevel ]; + nStart[ nCurrLevel ] = static_cast< sal_uInt16 >( nC ); + } + // close levels + while( nWorkLevel < nCurrLevel ) + { + BOOL bLastLevel = (nWorkLevel == (nCurrLevel - 1)); + BOOL bRealHidden = (bMakeHidden[ nCurrLevel ] && bPrevHidden ); + BOOL bRealVisible = (bMakeVisible[ nCurrLevel ] || + (!bCurrHidden && bLastLevel)); + + pOutlineArray->Insert( nStart[ nCurrLevel ], nPrevC , bDummy, + bRealHidden, bRealVisible ); + nCurrLevel--; + } + + bPrevOuted = pOuted[ nC ]; + } +} + + +void XclImpOutlineBuffer::SetLevelRange( SCSIZE nF, SCSIZE nL, BYTE nVal, + BOOL bOuted, BOOL bHidden ) +{ + DBG_ASSERT( nF <= nL, "+OutlineBuffer::SetLevelRange(): Last < First!" ); + + if( nL < nSize ) + { + if( nL > nLast ) + nLast = nL; + + BYTE* pLevelCount; + BYTE* pLast; + BOOL* pOutedCount; + BOOL* pHiddenCount; + + pLevelCount = &pLevel[ nF ]; + pLast = &pLevel[ nL ]; + pOutedCount = &pOuted[ nF ]; + pHiddenCount = &pHidden[ nF ]; + + while( pLevelCount <= pLast ) + { + *( pLevelCount++ ) = nVal; + *( pOutedCount++ ) = bOuted; + *( pHiddenCount++ ) = bHidden; + } + + if( nVal > nMaxLevel ) + nMaxLevel = nVal; + } +} + + +void XclImpOutlineBuffer::Reset( void ) +{ + for( SCSIZE nC = 0 ; nC < nSize ; nC++ ) + { + pLevel[ nC ] = 0; + pOuted[ nC ] = pHidden[ nC ] = FALSE; + } + nLast = 0; + nMaxLevel = 0; +} + + +//___________________________________________________________________ + + +ExcScenarioCell::ExcScenarioCell( const UINT16 nC, const UINT16 nR ) : nCol( nC ), nRow( nR ) +{ +} + + +void ExcScenarioCell::SetValue( const String& r ) +{ + aValue = r; +} + + + + +#define EXCSCAPPEND(EXCSCCELL) (List::Insert(EXCSCCELL,LIST_APPEND)) +#define EXCSCFIRST() ((ExcScenarioCell*)List::First()) +#define EXCSCNEXT() ((ExcScenarioCell*)List::Next()) + + +ExcScenario::ExcScenario( XclImpStream& rIn, const RootData& rR ) : nTab( rR.pIR->GetCurrScTab() ) +{ + UINT16 nCref; + UINT8 nName, nComment; + + rIn >> nCref; + rIn >> nProtected; + rIn.Ignore( 1 ); // Hide + rIn >> nName >> nComment; + rIn.Ignore( 1 ); // statt nUser! + + if( nName ) + pName = new String( rIn.ReadUniString( nName ) ); + else + { + pName = new String( RTL_CONSTASCII_USTRINGPARAM( "Scenery" ) ); + rIn.Ignore( 1 ); + } + + pUserName = new String( rIn.ReadUniString() ); + + if( nComment ) + pComment = new String( rIn.ReadUniString() ); + else + pComment = new String; + + UINT16 n = nCref; + UINT16 nC, nR; + while( n ) + { + rIn >> nR >> nC; + + EXCSCAPPEND( new ExcScenarioCell( nC, nR ) ); + + n--; + } + + n = nCref; + ExcScenarioCell* p = EXCSCFIRST(); + while( p ) + { + p->SetValue( rIn.ReadUniString() ); + + p = EXCSCNEXT(); + } +} + + +ExcScenario::~ExcScenario() +{ + ExcScenarioCell* p = EXCSCFIRST(); + + while( p ) + { + delete p; + p = EXCSCNEXT(); + } + + if( pName ) + delete pName; + if( pComment ) + delete pComment; + if( pUserName ) + delete pUserName; +} + + +void ExcScenario::Apply( const XclImpRoot& rRoot, const BOOL bLast ) +{ + ScDocument& r = rRoot.GetDoc(); + ExcScenarioCell* p = EXCSCFIRST(); + String aSzenName( *pName ); + UINT16 nNewTab = nTab + 1; + + if( !r.InsertTab( nNewTab, aSzenName ) ) + return; + + r.SetScenario( nNewTab, TRUE ); + // #112621# do not show scenario frames + r.SetScenarioData( nNewTab, *pComment, COL_LIGHTGRAY, /*SC_SCENARIO_SHOWFRAME|*/SC_SCENARIO_COPYALL|(nProtected ? SC_SCENARIO_PROTECT : 0) ); + + while( p ) + { + UINT16 nCol = p->nCol; + UINT16 nRow = p->nRow; + String aVal = p->GetValue(); + + r.ApplyFlagsTab( nCol, nRow, nCol, nRow, nNewTab, SC_MF_SCENARIO ); + + r.SetString( nCol, nRow, nNewTab, aVal ); + + p = EXCSCNEXT(); + } + + if( bLast ) + r.SetActiveScenario( nNewTab, TRUE ); + + // #111896# modify what the Active tab is set to if the new + // scenario tab occurs before the active tab. + ScExtDocSettings& rDocSett = rRoot.GetExtDocOptions().GetDocSettings(); + if( (static_cast< SCCOL >( nTab ) < rDocSett.mnDisplTab) && (rDocSett.mnDisplTab < MAXTAB) ) + ++rDocSett.mnDisplTab; + rRoot.GetTabInfo().InsertScTab( nNewTab ); +} + + + + +ExcScenarioList::~ExcScenarioList() +{ + ExcScenario* p = _First(); + + while( p ) + { + delete p; + p = _Next(); + } +} + + +void ExcScenarioList::Apply( const XclImpRoot& rRoot ) +{ + ExcScenario* p = _Last(); + UINT16 n = ( UINT16 ) Count(); + + while( p ) + { + n--; + p->Apply( rRoot, ( BOOL ) ( n == nLastScenario ) ); + p = _Prev(); + } +} + + diff --git a/sc/source/filter/excel/expop2.cxx b/sc/source/filter/excel/expop2.cxx new file mode 100644 index 000000000000..dbe70ae6a0ee --- /dev/null +++ b/sc/source/filter/excel/expop2.cxx @@ -0,0 +1,219 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + + + +//------------------------------------------------------------------------ + +#include <unotools/fltrcfg.hxx> + +#include <sfx2/objsh.hxx> +#include <sfx2/docinf.hxx> +#include <filter/msfilter/svxmsbas.hxx> + +#include "scerrors.hxx" +#include "scextopt.hxx" + +#include "root.hxx" +#include "excdoc.hxx" +#include "exp_op.hxx" + +#include "xcl97esc.hxx" + +#include "document.hxx" +#include "rangenam.hxx" +#include "filtopt.hxx" +#include "xltools.hxx" +#include "xelink.hxx" + +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> + + +ExportBiff5::ExportBiff5( XclExpRootData& rExpData, SvStream& rStrm ): + ExportTyp( rStrm, &rExpData.mrDoc, rExpData.meTextEnc ), + XclExpRoot( rExpData ) +{ + // nur Teil der Root-Daten gebraucht + pExcRoot = &GetOldRoot(); + pExcRoot->pER = this; // ExcRoot -> XclExpRoot + pExcRoot->eDateiTyp = Biff5; + pExcDoc = new ExcDocument( *this ); +} + + +ExportBiff5::~ExportBiff5() +{ + delete pExcDoc; +} + + +FltError ExportBiff5::Write() +{ + SfxObjectShell* pDocShell = GetDocShell(); + DBG_ASSERT( pDocShell, "ExportBiff5::Write - no document shell" ); + + SotStorageRef xRootStrg = GetRootStorage(); + DBG_ASSERT( xRootStrg.Is(), "ExportBiff5::Write - no root storage" ); + + bool bWriteBasicCode = false; + bool bWriteBasicStrg = false; + if( GetBiff() == EXC_BIFF8 ) + { + if( SvtFilterOptions* pFilterOpt = SvtFilterOptions::Get() ) + { + bWriteBasicCode = pFilterOpt->IsLoadExcelBasicCode(); + bWriteBasicStrg = pFilterOpt->IsLoadExcelBasicStorage(); + } + } + + if( pDocShell && xRootStrg.Is() && bWriteBasicStrg ) + { + SvxImportMSVBasic aBasicImport( *pDocShell, *xRootStrg, bWriteBasicCode, bWriteBasicStrg ); + ULONG nErr = aBasicImport.SaveOrDelMSVBAStorage( TRUE, EXC_STORAGE_VBA_PROJECT ); + if( nErr != ERRCODE_NONE ) + pDocShell->SetError( nErr, ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ) ); + } + + pExcDoc->ReadDoc(); // ScDoc -> ExcDoc + pExcDoc->Write( aOut ); // wechstreamen + + if( pDocShell && xRootStrg.Is() ) + { + // #i88642# update doc info (revision etc) + pDocShell->UpdateDocInfoForSave(); + + using namespace ::com::sun::star; + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps + = xDPS->getDocumentProperties(); + if ( SvtFilterOptions::Get()->IsEnableCalcPreview() ) + { + ::boost::shared_ptr<GDIMetaFile> pMetaFile = + pDocShell->GetPreviewMetaFile (sal_False); + uno::Sequence<sal_uInt8> metaFile( + sfx2::convertMetaFile(pMetaFile.get())); + sfx2::SaveOlePropertySet(xDocProps, xRootStrg, &metaFile); + } + else + sfx2::SaveOlePropertySet(xDocProps, xRootStrg ); + } + + //! TODO: separate warnings for columns and sheets + const XclExpAddressConverter& rAddrConv = GetAddressConverter(); + if( rAddrConv.IsColTruncated() || rAddrConv.IsRowTruncated() || rAddrConv.IsTabTruncated() ) + return SCWARN_EXPORT_MAXROW; + + return eERR_OK; +} + + + +ExportBiff8::ExportBiff8( XclExpRootData& rExpData, SvStream& rStrm ) : + ExportBiff5( rExpData, rStrm ) +{ + pExcRoot->eDateiTyp = Biff8; +} + + +ExportBiff8::~ExportBiff8() +{ +} + + +ExportXml2007::ExportXml2007( XclExpRootData& rExpData, SvStream& rStrm ) + : ExportTyp( rStrm, &rExpData.mrDoc, rExpData.meTextEnc ) + , XclExpRoot( rExpData ) +{ + pExcRoot = &GetOldRoot(); + pExcRoot->pER = this; + pExcRoot->eDateiTyp = Biff8; + pExcDoc = new ExcDocument( *this ); +} + + +ExportXml2007::~ExportXml2007() +{ + delete pExcDoc; +} + + +FltError ExportXml2007::Write() +{ + SfxObjectShell* pDocShell = GetDocShell(); + DBG_ASSERT( pDocShell, "ExportXml2007::Write - no document shell" ); + + SotStorageRef xRootStrg = GetRootStorage(); + DBG_ASSERT( xRootStrg.Is(), "ExportXml2007::Write - no root storage" ); + + bool bWriteBasicCode = false; + bool bWriteBasicStrg = false; + + if( SvtFilterOptions* pFilterOpt = SvtFilterOptions::Get() ) + { + bWriteBasicCode = pFilterOpt->IsLoadExcelBasicCode(); + bWriteBasicStrg = pFilterOpt->IsLoadExcelBasicStorage(); + } + + if( pDocShell && xRootStrg.Is() && bWriteBasicStrg ) + { + SvxImportMSVBasic aBasicImport( *pDocShell, *xRootStrg, bWriteBasicCode, bWriteBasicStrg ); + ULONG nErr = aBasicImport.SaveOrDelMSVBAStorage( TRUE, EXC_STORAGE_VBA_PROJECT ); + if( nErr != ERRCODE_NONE ) + pDocShell->SetError( nErr, ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ) ); + } + + pExcDoc->ReadDoc(); // ScDoc -> ExcDoc + pExcDoc->WriteXml( aOut ); // wechstreamen + + if( pDocShell && xRootStrg.Is() ) + { + using namespace ::com::sun::star; + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps + = xDPS->getDocumentProperties(); + ::boost::shared_ptr<GDIMetaFile> pMetaFile = + pDocShell->GetPreviewMetaFile (sal_False); + uno::Sequence<sal_uInt8> metaFile( + sfx2::convertMetaFile(pMetaFile.get())); + sfx2::SaveOlePropertySet(xDocProps, xRootStrg, &metaFile); + } + + //! TODO: separate warnings for columns and sheets + const XclExpAddressConverter& rAddrConv = GetAddressConverter(); + if( rAddrConv.IsColTruncated() || rAddrConv.IsRowTruncated() || rAddrConv.IsTabTruncated() ) + return SCWARN_EXPORT_MAXROW; + + return eERR_OK; +} + + diff --git a/sc/source/filter/excel/fontbuff.cxx b/sc/source/filter/excel/fontbuff.cxx new file mode 100644 index 000000000000..dca6723181ae --- /dev/null +++ b/sc/source/filter/excel/fontbuff.cxx @@ -0,0 +1,163 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "lotfntbf.hxx" + +#include "scitems.hxx" +#include <editeng/cntritem.hxx> +#include <editeng/crsditem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/escpitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/wghtitem.hxx> +#include <sfx2/printer.hxx> + +#include "attrib.hxx" +#include "document.hxx" +#include "global.hxx" +#include "docpool.hxx" +#include "patattr.hxx" +#include "ftools.hxx" + +const UINT16 LotusFontBuffer::nSize = 8; + +void LotusFontBuffer::Fill( const UINT8 nIndex, SfxItemSet& rItemSet ) +{ + UINT8 nIntIndex = nIndex & 0x07; + + ENTRY* pAkt = pData + nIntIndex; + + if( pAkt->pFont ) + rItemSet.Put( *pAkt->pFont ); + + if( pAkt->pHeight ) + rItemSet.Put( *pAkt->pHeight ); + + if( pAkt->pColor ) + rItemSet.Put( *pAkt->pColor ); + + if( nIndex & 0x08 ) + { + SvxWeightItem aWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ); + rItemSet.Put( aWeightItem ); + } + + if( nIndex & 0x10 ) + { + SvxPostureItem aAttr( ITALIC_NORMAL, ATTR_FONT_POSTURE ); + rItemSet.Put( aAttr ); + } + + FontUnderline eUnderline; + switch( nIndex & 0x60 ) // Bit 5+6 + { + case 0x60: + case 0x20: eUnderline = UNDERLINE_SINGLE; break; + case 0x40: eUnderline = UNDERLINE_DOUBLE; break; + default: eUnderline = UNDERLINE_NONE; + } + if( eUnderline != UNDERLINE_NONE ) + { + SvxUnderlineItem aUndItem( eUnderline, ATTR_FONT_UNDERLINE ); + rItemSet.Put( aUndItem ); + } +} + + +void LotusFontBuffer::SetName( const UINT16 nIndex, const String& rName ) +{ + DBG_ASSERT( nIndex < nSize, "*LotusFontBuffer::SetName(): Array zu klein!" ); + if( nIndex < nSize ) + { + register ENTRY* pEntry = pData + nIndex; + pEntry->TmpName( rName ); + + if( pEntry->nType >= 0 ) + MakeFont( pEntry ); + } +} + + +void LotusFontBuffer::SetHeight( const UINT16 nIndex, const UINT16 nHeight ) +{ + DBG_ASSERT( nIndex < nSize, "*LotusFontBuffer::SetHeight(): Array zu klein!" ); + if( nIndex < nSize ) + pData[ nIndex ].Height( *( new SvxFontHeightItem( ( ULONG ) nHeight * 20, 100, ATTR_FONT_HEIGHT ) ) ); +} + + +void LotusFontBuffer::SetType( const UINT16 nIndex, const UINT16 nType ) +{ + DBG_ASSERT( nIndex < nSize, "*LotusFontBuffer::SetType(): Array zu klein!" ); + if( nIndex < nSize ) + { + register ENTRY* pEntry = pData + nIndex; + pEntry->Type( nType ); + + if( pEntry->pTmpName ) + MakeFont( pEntry ); + } +} + + +void LotusFontBuffer::MakeFont( ENTRY* pEntry ) +{ + FontFamily eFamily = FAMILY_DONTKNOW; + FontPitch ePitch = PITCH_DONTKNOW; + CharSet eCharSet = RTL_TEXTENCODING_DONTKNOW; + + switch( pEntry->nType ) + { + case 0x00: // Helvetica + eFamily = FAMILY_SWISS; + ePitch = PITCH_VARIABLE; + break; + case 0x01: // Times Roman + eFamily = FAMILY_ROMAN; + ePitch = PITCH_VARIABLE; + break; + case 0x02: // Courier + ePitch = PITCH_FIXED; + break; + case 0x03: // Symbol + eCharSet = RTL_TEXTENCODING_SYMBOL; + break; + } + + pEntry->pFont = new SvxFontItem( eFamily, *pEntry->pTmpName, EMPTY_STRING, ePitch, eCharSet, ATTR_FONT ); + + delete pEntry->pTmpName; + pEntry->pTmpName = NULL; +} + + + diff --git a/sc/source/filter/excel/frmbase.cxx b/sc/source/filter/excel/frmbase.cxx new file mode 100644 index 000000000000..78883b4a5c76 --- /dev/null +++ b/sc/source/filter/excel/frmbase.cxx @@ -0,0 +1,282 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + + + + +#include "formel.hxx" + + + + +_ScRangeList::~_ScRangeList() +{ + ScRange* p = ( ScRange* ) First(); + + while( p ) + { + delete p; + p = ( ScRange* ) Next(); + } +} + + + + +_ScRangeListTabs::_ScRangeListTabs( void ) +{ + ppTabLists = new _ScRangeList*[ MAXTAB + 1 ]; + + for( UINT16 n = 0 ; n <= MAXTAB ; n++ ) + ppTabLists[ n ] = NULL; + + bHasRanges = FALSE; + pAct = NULL; + nAct = 0; +} + + +_ScRangeListTabs::~_ScRangeListTabs() +{ + if( bHasRanges ) + { + for( UINT16 n = 0 ; n <= MAXTAB ; n++ ) + { + if( ppTabLists[ n ] ) + delete ppTabLists[ n ]; + } + } + + delete[] ppTabLists; +} + + +void _ScRangeListTabs::Append( ScSingleRefData a, const BOOL b ) +{ + if( b ) + { + if( a.nTab > MAXTAB ) + a.nTab = MAXTAB; + + if( a.nCol > MAXCOL ) + a.nCol = MAXCOL; + + if( a.nRow > MAXROW ) + a.nRow = MAXROW; + } + else + { + DBG_ASSERT( ValidTab(a.nTab), "-_ScRangeListTabs::Append(): Luegen haben kurze Abstuerze!" ); + } + + bHasRanges = TRUE; + + if( a.nTab >= 0 ) + { + _ScRangeList* p = ppTabLists[ a.nTab ]; + + if( !p ) + p = ppTabLists[ a.nTab ] = new _ScRangeList; + + p->Append( a ); + } +} + + +void _ScRangeListTabs::Append( ScComplexRefData a, const BOOL b ) +{ + if( b ) + { + // #96263# ignore 3D ranges + if( a.Ref1.nTab != a.Ref2.nTab ) + return; + + SCsTAB& rTab = a.Ref1.nTab; + if( rTab > MAXTAB ) + rTab = MAXTAB; + else if( rTab < 0 ) + rTab = 0; + + SCsCOL& rCol1 = a.Ref1.nCol; + if( rCol1 > MAXCOL ) + rCol1 = MAXCOL; + else if( rCol1 < 0 ) + rCol1 = 0; + + SCsROW& rRow1 = a.Ref1.nRow; + if( rRow1 > MAXROW ) + rRow1 = MAXROW; + else if( rRow1 < 0 ) + rRow1 = 0; + + SCsCOL& rCol2 = a.Ref2.nCol; + if( rCol2 > MAXCOL ) + rCol2 = MAXCOL; + else if( rCol2 < 0 ) + rCol2 = 0; + + SCsROW& rRow2 = a.Ref2.nRow; + if( rRow2 > MAXROW ) + rRow2 = MAXROW; + else if( rRow2 < 0 ) + rRow2 = 0; + } + else + { + DBG_ASSERT( ValidTab(a.Ref1.nTab), + "-_ScRangeListTabs::Append(): Luegen haben kurze Abstuerze!" ); + DBG_ASSERT( a.Ref1.nTab == a.Ref2.nTab, + "+_ScRangeListTabs::Append(): 3D-Ranges werden in SC nicht unterstuetzt!" ); + } + + bHasRanges = TRUE; + + if( a.Ref1.nTab >= 0 ) + { + _ScRangeList* p = ppTabLists[ a.Ref1.nTab ]; + + if( !p ) + p = ppTabLists[ a.Ref1.nTab ] = new _ScRangeList; + + p->Append( a ); + } +} + + +const ScRange* _ScRangeListTabs::First( const UINT16 n ) +{ + DBG_ASSERT( ValidTab(n), "-_ScRangeListTabs::First(): Und tschuessssssss!" ); + + if( ppTabLists[ n ] ) + { + pAct = ppTabLists[ n ]; + nAct = n; + return pAct->First(); + } + else + { + pAct = NULL; + nAct = 0; + return NULL; + } +} + + +const ScRange* _ScRangeListTabs::Next( void ) +{ + if( pAct ) + return pAct->Next(); + else + return NULL; +} + + + + +ConverterBase::ConverterBase( UINT16 nNewBuffer ) : + aEingPos( 0, 0, 0 ), + eStatus( ConvOK ), + nBufferSize( nNewBuffer ) +{ + DBG_ASSERT( nNewBuffer > 0, "ConverterBase::ConverterBase - nNewBuffer == 0!" ); + pBuffer = new sal_Char[ nNewBuffer ]; +} + +ConverterBase::~ConverterBase() +{ + delete[] pBuffer; +} + +void ConverterBase::Reset() +{ + eStatus = ConvOK; + aPool.Reset(); + aStack.Reset(); +} + + + + +ExcelConverterBase::ExcelConverterBase( UINT16 nNewBuffer ) : + ConverterBase( nNewBuffer ) +{ +} + +ExcelConverterBase::~ExcelConverterBase() +{ +} + +void ExcelConverterBase::Reset( const ScAddress& rEingPos ) +{ + ConverterBase::Reset(); + aEingPos = rEingPos; +} + +void ExcelConverterBase::Reset() +{ + ConverterBase::Reset(); + aEingPos.Set( 0, 0, 0 ); +} + + + + +LotusConverterBase::LotusConverterBase( SvStream &rStr, UINT16 nNewBuffer ) : + ConverterBase( nNewBuffer ), + aIn( rStr ), + nBytesLeft( 0 ) +{ +} + +LotusConverterBase::~LotusConverterBase() +{ +} + +//UNUSED2008-05 void LotusConverterBase::Reset( INT32 nLen, const ScAddress& rEingPos ) +//UNUSED2008-05 { +//UNUSED2008-05 ConverterBase::Reset(); +//UNUSED2008-05 nBytesLeft = nLen; +//UNUSED2008-05 aEingPos = rEingPos; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 void LotusConverterBase::Reset( INT32 nLen ) +//UNUSED2008-05 { +//UNUSED2008-05 ConverterBase::Reset(); +//UNUSED2008-05 nBytesLeft = nLen; +//UNUSED2008-05 aEingPos.Set( 0, 0, 0 ); +//UNUSED2008-05 } + +void LotusConverterBase::Reset( const ScAddress& rEingPos ) +{ + ConverterBase::Reset(); + nBytesLeft = 0; + aEingPos = rEingPos; +} + diff --git a/sc/source/filter/excel/impop.cxx b/sc/source/filter/excel/impop.cxx new file mode 100644 index 000000000000..209d24ac043d --- /dev/null +++ b/sc/source/filter/excel/impop.cxx @@ -0,0 +1,1334 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "imp_op.hxx" + +#include <filter/msfilter/countryid.hxx> + +#include "scitems.hxx" +#include <editeng/eeitem.hxx> + +#include <editeng/editdata.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editobj.hxx> +#include <editeng/editstat.hxx> +#include <editeng/flditem.hxx> +#include <svx/pageitem.hxx> +#include <editeng/colritem.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/docfile.hxx> +#include <svl/zforlist.hxx> + +#include <sfx2/objsh.hxx> +#include "docuno.hxx" + +#include "cell.hxx" +#include "document.hxx" +#include "rangenam.hxx" +#include "compiler.hxx" +#include "patattr.hxx" +#include "attrib.hxx" +#include "globstr.hrc" +#include "global.hxx" +#include "markdata.hxx" +#include "olinetab.hxx" +#include "stlsheet.hxx" +#include "stlpool.hxx" +#include "compiler.hxx" +#include "viewopti.hxx" +#include "docoptio.hxx" +#include "scextopt.hxx" +#include "editutil.hxx" +#include "filtopt.hxx" +#include "scerrors.hxx" +#include "unonames.hxx" +#include "paramisc.hxx" +#include "postit.hxx" + +#include "fapihelper.hxx" +#include "xltools.hxx" +#include "xltable.hxx" +#include "xlview.hxx" +#include "xltracer.hxx" +#include "xihelper.hxx" +#include "xipage.hxx" +#include "xiview.hxx" +#include "xilink.hxx" +#include "xiescher.hxx" +#include "xicontent.hxx" + +#include "excimp8.hxx" +#include "excform.hxx" + +#if defined( WNT ) || defined( WIN ) +#include <math.h> +#else +#include <stdlib.h> +#endif + +using namespace ::com::sun::star; + + +const double ImportExcel::fExcToTwips = + ( double ) TWIPS_PER_CHAR / 256.0; + + +ImportTyp::ImportTyp( ScDocument* pDoc, CharSet eQ ) +{ + eQuellChar = eQ; + pD = pDoc; +} + + +ImportTyp::~ImportTyp() +{ +} + + +FltError ImportTyp::Read() +{ + return eERR_INTERN; +} + + +ImportExcel::ImportExcel( XclImpRootData& rImpData, SvStream& rStrm ): + ImportTyp( &rImpData.mrDoc, rImpData.meTextEnc ), + XclImpRoot( rImpData ), + maStrm( rStrm, GetRoot() ), + aIn( maStrm ), + maScOleSize( ScAddress::INITIALIZE_INVALID ) +{ + mnLastRefIdx = 0; + nBdshtTab = 0; + nIxfeIndex = 0; // zur Sicherheit auf 0 + + // Root-Daten fuellen - nach new's ohne Root als Parameter + pExcRoot = &GetOldRoot(); + pExcRoot->pIR = this; // ExcRoot -> XclImpRoot + pExcRoot->eDateiTyp = BiffX; + pExcRoot->pExtSheetBuff = new ExtSheetBuffer( pExcRoot ); //&aExtSheetBuff; + pExcRoot->pShrfmlaBuff = new ShrfmlaBuffer( pExcRoot ); //&aShrfrmlaBuff; + pExcRoot->pExtNameBuff = new ExtNameBuff ( *this ); + + pExtNameBuff = new NameBuffer( pExcRoot ); //#94039# prevent empty rootdata + pExtNameBuff->SetBase( 1 ); + + pOutlineListBuffer = new XclImpOutlineListBuffer( ); + + // ab Biff8 + pFormConv = pExcRoot->pFmlaConverter = new ExcelToSc( GetRoot() ); + + bTabTruncated = FALSE; + + // Excel-Dokument per Default auf 31.12.1899, entspricht Excel-Einstellungen mit 1.1.1900 + ScDocOptions aOpt = pD->GetDocOptions(); + aOpt.SetDate( 30, 12, 1899 ); + pD->SetDocOptions( aOpt ); + pD->GetFormatTable()->ChangeNullDate( 30, 12, 1899 ); + + ScDocOptions aDocOpt( pD->GetDocOptions() ); + aDocOpt.SetIgnoreCase( TRUE ); // always in Excel + aDocOpt.SetFormulaRegexEnabled( FALSE ); // regular expressions? what's that? + aDocOpt.SetLookUpColRowNames( FALSE ); // default: no natural language refs + pD->SetDocOptions( aDocOpt ); +} + + +ImportExcel::~ImportExcel( void ) +{ + GetDoc().SetSrcCharSet( GetTextEncoding() ); + + delete pExtNameBuff; + + delete pOutlineListBuffer; + + delete pFormConv; +} + + +void ImportExcel::ReadFileSharing() +{ + sal_uInt16 nRecommendReadOnly, nPasswordHash; + maStrm >> nRecommendReadOnly >> nPasswordHash; + + if( (nRecommendReadOnly != 0) || (nPasswordHash != 0) ) + { + if( SfxItemSet* pItemSet = GetMedium().GetItemSet() ) + pItemSet->Put( SfxBoolItem( SID_DOC_READONLY, TRUE ) ); + + if( SfxObjectShell* pShell = GetDocShell() ) + { + if( nRecommendReadOnly != 0 ) + pShell->SetLoadReadonly( sal_True ); + if( nPasswordHash != 0 ) + pShell->SetModifyPasswordHash( nPasswordHash ); + } + } +} + +sal_uInt16 ImportExcel::ReadXFIndex( bool bBiff2 ) +{ + sal_uInt16 nXFIdx = 0; + if( bBiff2 ) + { + sal_uInt8 nXFIdx2; + maStrm >> nXFIdx2; + maStrm.Ignore( 2 ); + nXFIdx = nXFIdx2 & 0x3F; + if( nXFIdx == 63 ) + nXFIdx = nIxfeIndex; + } + else + aIn >> nXFIdx; + return nXFIdx; +} + +void ImportExcel::ReadDimensions() +{ + XclRange aXclUsedArea( ScAddress::UNINITIALIZED ); + if( (maStrm.GetRecId() == EXC_ID2_DIMENSIONS) || (GetBiff() <= EXC_BIFF5) ) + { + maStrm >> aXclUsedArea; + if( (aXclUsedArea.GetColCount() > 1) && (aXclUsedArea.GetRowCount() > 1) ) + { + // Excel stores first unused row/column index + --aXclUsedArea.maLast.mnCol; + --aXclUsedArea.maLast.mnRow; + // create the Calc range + SCTAB nScTab = GetCurrScTab(); + ScRange& rScUsedArea = GetExtDocOptions().GetOrCreateTabSettings( nScTab ).maUsedArea; + GetAddressConverter().ConvertRange( rScUsedArea, aXclUsedArea, nScTab, nScTab, false ); + // if any error occurs in ConvertRange(), rScUsedArea keeps untouched + } + } + else + { + sal_uInt32 nXclRow1, nXclRow2; + maStrm >> nXclRow1 >> nXclRow2 >> aXclUsedArea.maFirst.mnCol >> aXclUsedArea.maLast.mnCol; + if( (nXclRow1 < nXclRow2) && (aXclUsedArea.GetColCount() > 1) && + (nXclRow1 <= static_cast< sal_uInt32 >( GetScMaxPos().Row() )) ) + { + // Excel stores first unused row/column index + --nXclRow2; + --aXclUsedArea.maLast.mnCol; + // convert row indexes to 16-bit values + aXclUsedArea.maFirst.mnRow = static_cast< sal_uInt16 >( nXclRow1 ); + aXclUsedArea.maLast.mnRow = limit_cast< sal_uInt16 >( nXclRow2, aXclUsedArea.maFirst.mnRow, SAL_MAX_UINT16 ); + // create the Calc range + SCTAB nScTab = GetCurrScTab(); + ScRange& rScUsedArea = GetExtDocOptions().GetOrCreateTabSettings( nScTab ).maUsedArea; + GetAddressConverter().ConvertRange( rScUsedArea, aXclUsedArea, nScTab, nScTab, false ); + // if any error occurs in ConvertRange(), rScUsedArea keeps untouched + } + } +} + +void ImportExcel::ReadBlank() +{ + XclAddress aXclPos; + aIn >> aXclPos; + + ScAddress aScPos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) ) + { + sal_uInt16 nXFIdx = ReadXFIndex( maStrm.GetRecId() == EXC_ID2_BLANK ); + + GetXFRangeBuffer().SetBlankXF( aScPos, nXFIdx ); + } +} + +void ImportExcel::ReadInteger() +{ + XclAddress aXclPos; + maStrm >> aXclPos; + + ScAddress aScPos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) ) + { + sal_uInt16 nXFIdx = ReadXFIndex( true ); + sal_uInt16 nValue; + maStrm >> nValue; + + GetXFRangeBuffer().SetXF( aScPos, nXFIdx ); + GetDoc().PutCell( aScPos, new ScValueCell( nValue ) ); + } +} + +void ImportExcel::ReadNumber() +{ + XclAddress aXclPos; + maStrm >> aXclPos; + + ScAddress aScPos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) ) + { + sal_uInt16 nXFIdx = ReadXFIndex( maStrm.GetRecId() == EXC_ID2_NUMBER ); + double fValue; + maStrm >> fValue; + + GetXFRangeBuffer().SetXF( aScPos, nXFIdx ); + GetDoc().PutCell( aScPos, new ScValueCell( fValue ) ); + } +} + +void ImportExcel::ReadLabel() +{ + XclAddress aXclPos; + maStrm >> aXclPos; + + ScAddress aScPos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) ) + { + /* Record ID BIFF XF type String type + 0x0004 2-7 3 byte 8-bit length, byte string + 0x0004 8 3 byte 16-bit length, unicode string + 0x0204 2-7 2 byte 16-bit length, byte string + 0x0204 8 2 byte 16-bit length, unicode string */ + bool bBiff2 = maStrm.GetRecId() == EXC_ID2_LABEL; + sal_uInt16 nXFIdx = ReadXFIndex( bBiff2 ); + XclStrFlags nFlags = (bBiff2 && (GetBiff() <= EXC_BIFF5)) ? EXC_STR_8BITLENGTH : EXC_STR_DEFAULT; + XclImpString aString; + + // #i63105# use text encoding from FONT record + rtl_TextEncoding eOldTextEnc = GetTextEncoding(); + if( const XclImpFont* pFont = GetXFBuffer().GetFont( nXFIdx ) ) + SetTextEncoding( pFont->GetFontEncoding() ); + aString.Read( maStrm, nFlags ); + SetTextEncoding( eOldTextEnc ); + + GetXFRangeBuffer().SetXF( aScPos, nXFIdx ); + if( ScBaseCell* pCell = XclImpStringHelper::CreateCell( GetRoot(), aString, nXFIdx ) ) + GetDoc().PutCell( aScPos, pCell ); + } +} + +void ImportExcel::ReadBoolErr() +{ + XclAddress aXclPos; + maStrm >> aXclPos; + + ScAddress aScPos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) ) + { + sal_uInt16 nXFIdx = ReadXFIndex( maStrm.GetRecId() == EXC_ID2_BOOLERR ); + sal_uInt8 nValue, nType; + maStrm >> nValue >> nType; + + if( nType == EXC_BOOLERR_BOOL ) + GetXFRangeBuffer().SetBoolXF( aScPos, nXFIdx ); + else + GetXFRangeBuffer().SetXF( aScPos, nXFIdx ); + + double fValue; + const ScTokenArray* pScTokArr = ErrorToFormula( nType, nValue, fValue ); + ScFormulaCell* pCell = new ScFormulaCell( pD, aScPos, pScTokArr ); + pCell->SetHybridDouble( fValue ); + GetDoc().PutCell( aScPos, pCell ); + } +} + +void ImportExcel::ReadRk() +{ + XclAddress aXclPos; + maStrm >> aXclPos; + + ScAddress aScPos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) ) + { + sal_uInt16 nXFIdx = ReadXFIndex( false ); + sal_Int32 nRk; + maStrm >> nRk; + + GetXFRangeBuffer().SetXF( aScPos, nXFIdx ); + GetDoc().PutCell( aScPos, new ScValueCell( XclTools::GetDoubleFromRK( nRk ) ) ); + } +} + + +void ImportExcel::Window1() +{ + GetDocViewSettings().ReadWindow1( maStrm ); +} + + + + +void ImportExcel::Row25( void ) +{ + UINT16 nRow, nRowHeight; + + aIn >> nRow; + aIn.Ignore( 4 ); // Mic und Mac ueberspringen + + if( ValidRow( nRow ) ) + { + aIn >> nRowHeight; // direkt in Twips angegeben + aIn.Ignore( 2 ); + + if( GetBiff() == EXC_BIFF2 ) + {// -------------------- BIFF2 + pColRowBuff->SetHeight( nRow, nRowHeight ); + } + else + {// -------------------- BIFF5 + UINT16 nGrbit; + + aIn.Ignore( 2 ); // reserved + aIn >> nGrbit; + + sal_uInt8 nLevel = ::extract_value< sal_uInt8 >( nGrbit, 0, 3 ); + pRowOutlineBuff->SetLevel( nRow, nLevel, + ::get_flag( nGrbit, EXC_ROW_COLLAPSED ), ::get_flag( nGrbit, EXC_ROW_HIDDEN ) ); + + pColRowBuff->SetRowSettings( nRow, nRowHeight, nGrbit ); + } + } +} + + +void ImportExcel::Bof2( void ) +{ + sal_uInt16 nSubType; + maStrm.DisableDecryption(); + maStrm.Ignore( 2 ); + maStrm >> nSubType; + + if( nSubType == 0x0020 ) // Chart + pExcRoot->eDateiTyp = Biff2C; + else if( nSubType == 0x0040 ) // Macro + pExcRoot->eDateiTyp = Biff2M; + else // #i51490# Excel interprets invalid indexes as worksheet + pExcRoot->eDateiTyp = Biff2; +} + + +void ImportExcel::Eof( void ) +{ + // POST: darf nur nach einer GUELTIGEN Tabelle gerufen werden! + EndSheet(); + IncCurrScTab(); +} + + +void ImportExcel::SheetPassword( void ) +{ + if (GetRoot().GetBiff() != EXC_BIFF8) + return; + + GetRoot().GetSheetProtectBuffer().ReadPasswordHash( aIn, GetCurrScTab() ); +} + + +void ImportExcel::Externsheet( void ) +{ + String aUrl, aTabName; + bool bSameWorkBook; + String aEncodedUrl( aIn.ReadByteString( false ) ); + XclImpUrlHelper::DecodeUrl( aUrl, aTabName, bSameWorkBook, *pExcRoot->pIR, aEncodedUrl ); + mnLastRefIdx = pExcRoot->pExtSheetBuff->Add( aUrl, aTabName, bSameWorkBook ); +} + + +void ImportExcel:: WinProtection( void ) +{ + if (GetRoot().GetBiff() != EXC_BIFF8) + return; + + GetRoot().GetDocProtectBuffer().ReadWinProtect( aIn ); +} + + +void ImportExcel::Columndefault( void ) +{// Default Cell Attributes + UINT16 nColMic, nColMac; + BYTE nOpt0; + + aIn >> nColMic >> nColMac; + + DBG_ASSERT( aIn.GetRecLeft() == (sal_Size)(nColMac - nColMic) * 3 + 2, + "ImportExcel::Columndefault - wrong record size" ); + + nColMac--; + + if( nColMac > MAXCOL ) + nColMac = static_cast<UINT16>(MAXCOL); + + for( UINT16 nCol = nColMic ; nCol <= nColMac ; nCol++ ) + { + aIn >> nOpt0; + aIn.Ignore( 2 ); // nur 0. Attribut-Byte benutzt + + if( nOpt0 & 0x80 ) // Col hidden? + pColRowBuff->HideCol( nCol ); + } +} + + +void ImportExcel::Array25( void ) +{ + UINT16 nFirstRow, nLastRow, nFormLen; + BYTE nFirstCol, nLastCol; + + aIn >> nFirstRow >> nLastRow >> nFirstCol >> nLastCol; + + if( GetBiff() == EXC_BIFF2 ) + {// BIFF2 + aIn.Ignore( 1 ); + nFormLen = aIn.ReaduInt8(); + } + else + {// BIFF5 + aIn.Ignore( 6 ); + aIn >> nFormLen; + } + + if( ValidColRow( nLastCol, nLastRow ) ) + { + // jetzt steht Lesemarke auf Formel, Laenge in nFormLen + const ScTokenArray* pErgebnis; + + pFormConv->Reset( ScAddress( static_cast<SCCOL>(nFirstCol), + static_cast<SCROW>(nFirstRow), GetCurrScTab() ) ); + pFormConv->Convert( pErgebnis, maStrm, nFormLen, true, FT_CellFormula); + + DBG_ASSERT( pErgebnis, "*ImportExcel::Array25(): ScTokenArray ist NULL!" ); + + ScMarkData aMarkData; + aMarkData.SelectOneTable( GetCurrScTab() ); + pD->InsertMatrixFormula( static_cast<SCCOL>(nFirstCol), + static_cast<SCROW>(nFirstRow), static_cast<SCCOL>(nLastCol), + static_cast<SCROW>(nLastRow), aMarkData, EMPTY_STRING, + pErgebnis ); + } +} + + +void ImportExcel::Rec1904( void ) +{ + UINT16 n1904; + + aIn >> n1904; + + if( n1904 ) + {// 1904 date system + ScDocOptions aOpt = pD->GetDocOptions(); + aOpt.SetDate( 1, 1, 1904 ); + pD->SetDocOptions( aOpt ); + pD->GetFormatTable()->ChangeNullDate( 1, 1, 1904 ); + } +} + + +void ImportExcel::Externname25( void ) +{ + UINT32 nRes; + UINT16 nOpt; + + aIn >> nOpt >> nRes; + + String aName( aIn.ReadByteString( FALSE ) ); + + if( ( nOpt & 0x0001 ) || ( ( nOpt & 0xFFFE ) == 0x0000 ) ) + {// external name + ScfTools::ConvertToScDefinedName( aName ); + pExcRoot->pExtNameBuff->AddName( aName, mnLastRefIdx ); + } + else if( nOpt & 0x0010 ) + {// ole link + pExcRoot->pExtNameBuff->AddOLE( aName, mnLastRefIdx, nRes ); // nRes is storage ID + } + else + {// dde link + pExcRoot->pExtNameBuff->AddDDE( aName, mnLastRefIdx ); + } +} + + +void ImportExcel::Colwidth( void ) +{// Column Width + BYTE nColFirst, nColLast; + UINT16 nColWidth; + + aIn >> nColFirst >> nColLast >> nColWidth; + +//! TODO: add a check for the unlikely case of changed MAXCOL (-> XclImpAddressConverter) +// if( nColLast > MAXCOL ) +// nColLast = static_cast<UINT16>(MAXCOL); + + USHORT nScWidth = XclTools::GetScColumnWidth( nColWidth, GetCharWidth() ); + pColRowBuff->SetWidthRange( nColFirst, nColLast, nScWidth ); +} + + +void ImportExcel::Defrowheight2( void ) +{ + sal_uInt16 nDefHeight; + maStrm >> nDefHeight; + nDefHeight &= 0x7FFF; + pColRowBuff->SetDefHeight( nDefHeight, EXC_DEFROW_UNSYNCED ); +} + + +void ImportExcel::SheetProtect( void ) +{ + if (GetRoot().GetBiff() != EXC_BIFF8) + return; + + GetRoot().GetSheetProtectBuffer().ReadProtect( aIn, GetCurrScTab() ); +} + +void ImportExcel::DocProtect( void ) +{ + if (GetRoot().GetBiff() != EXC_BIFF8) + return; + + GetRoot().GetDocProtectBuffer().ReadDocProtect( aIn ); +} + +void ImportExcel::DocPasssword( void ) +{ + if (GetRoot().GetBiff() != EXC_BIFF8) + return; + + GetRoot().GetDocProtectBuffer().ReadPasswordHash( aIn ); +} + +void ImportExcel::Codepage( void ) +{ + SetCodePage( maStrm.ReaduInt16() ); +} + + +void ImportExcel::Ixfe( void ) +{ + aIn >> nIxfeIndex; +} + + +void ImportExcel::DefColWidth( void ) +{ + // stored as entire characters -> convert to 1/256 of characters (as in COLINFO) + double fDefWidth = 256.0 * maStrm.ReaduInt16(); + + // #i3006# additional space for default width - Excel adds space depending on font size + long nFontHt = GetFontBuffer().GetAppFontData().mnHeight; + fDefWidth += XclTools::GetXclDefColWidthCorrection( nFontHt ); + + USHORT nScWidth = XclTools::GetScColumnWidth( limit_cast< sal_uInt16 >( fDefWidth ), GetCharWidth() ); + pColRowBuff->SetDefWidth( nScWidth ); +} + + +void ImportExcel::Builtinfmtcnt( void ) +{ +} + + +void ImportExcel::Colinfo( void ) +{// Column Formatting Information + UINT16 nColFirst, nColLast, nColWidth, nXF; + UINT16 nOpt; + + aIn >> nColFirst >> nColLast >> nColWidth >> nXF >> nOpt; + + if( nColFirst > MAXCOL ) + return; + + if( nColLast > MAXCOL ) + nColLast = static_cast<UINT16>(MAXCOL); + + bool bHidden = ::get_flag( nOpt, EXC_COLINFO_HIDDEN ); + bool bCollapsed = ::get_flag( nOpt, EXC_COLINFO_COLLAPSED ); + sal_uInt8 nLevel = ::extract_value< sal_uInt8 >( nOpt, 8, 3 ); + pColOutlineBuff->SetLevelRange( nColFirst, nColLast, nLevel, bCollapsed, bHidden ); + + if( bHidden ) + pColRowBuff->HideColRange( nColFirst, nColLast ); + + USHORT nScWidth = XclTools::GetScColumnWidth( nColWidth, GetCharWidth() ); + pColRowBuff->SetWidthRange( nColFirst, nColLast, nScWidth ); + pColRowBuff->SetDefaultXF( nColFirst, nColLast, nXF ); +} + + +void ImportExcel::Wsbool( void ) +{ + UINT16 nFlags; + aIn >> nFlags; + + pRowOutlineBuff->SetButtonMode( ::get_flag( nFlags, EXC_WSBOOL_ROWBELOW ) ); + pColOutlineBuff->SetButtonMode( ::get_flag( nFlags, EXC_WSBOOL_COLBELOW ) ); + + GetPageSettings().SetFitToPages( ::get_flag( nFlags, EXC_WSBOOL_FITTOPAGE ) ); +} + + +void ImportExcel::Boundsheet( void ) +{ + UINT16 nGrbit = 0; + + if( GetBiff() == EXC_BIFF5 ) + { + aIn.DisableDecryption(); + maSheetOffsets.push_back( aIn.ReaduInt32() ); + aIn.EnableDecryption(); + aIn >> nGrbit; + } + + String aName( aIn.ReadByteString( FALSE ) ); + + SCTAB nScTab = static_cast< SCTAB >( nBdshtTab ); + if( nScTab > 0 ) + { + DBG_ASSERT( !pD->HasTable( nScTab ), "ImportExcel::Boundsheet - sheet exists already" ); + pD->MakeTable( nScTab ); + } + + if( ( nGrbit & 0x0001 ) || ( nGrbit & 0x0002 ) ) + pD->SetVisible( nScTab, FALSE ); + + if( !pD->RenameTab( nScTab, aName ) ) + { + pD->CreateValidTabName( aName ); + pD->RenameTab( nScTab, aName ); + } + + nBdshtTab++; +} + + +void ImportExcel::Country( void ) +{ + sal_uInt16 nUICountry, nDocCountry; + maStrm >> nUICountry >> nDocCountry; + + // Store system language in XclRoot + LanguageType eLanguage = ::msfilter::ConvertCountryToLanguage( static_cast< ::msfilter::CountryId >( nDocCountry ) ); + if( eLanguage != LANGUAGE_DONTKNOW ) + SetDocLanguage( eLanguage ); + + // Set Excel UI language in add-in name translator + eLanguage = ::msfilter::ConvertCountryToLanguage( static_cast< ::msfilter::CountryId >( nUICountry ) ); + if( eLanguage != LANGUAGE_DONTKNOW ) + SetUILanguage( eLanguage ); +} + + +void ImportExcel::ReadUsesElfs() +{ + if( maStrm.ReaduInt16() != 0 ) + { + ScDocOptions aDocOpt = GetDoc().GetDocOptions(); + aDocOpt.SetLookUpColRowNames( TRUE ); + GetDoc().SetDocOptions( aDocOpt ); + } +} + + +void ImportExcel::Hideobj( void ) +{ + UINT16 nHide; + ScVObjMode eOle, eChart, eDraw; + + aIn >> nHide; + + ScViewOptions aOpts( pD->GetViewOptions() ); + + switch( nHide ) + { + case 1: // Placeholders + eOle = VOBJ_MODE_SHOW; // in Excel 97 werden nur Charts als Platzhalter angezeigt + eChart = VOBJ_MODE_SHOW; //#i80528# VOBJ_MODE_DUMMY replaced by VOBJ_MODE_SHOW now + eDraw = VOBJ_MODE_SHOW; + break; + case 2: // Hide all + eOle = VOBJ_MODE_HIDE; + eChart = VOBJ_MODE_HIDE; + eDraw = VOBJ_MODE_HIDE; + break; + default: // Show all + eOle = VOBJ_MODE_SHOW; + eChart = VOBJ_MODE_SHOW; + eDraw = VOBJ_MODE_SHOW; + break; + } + + aOpts.SetObjMode( VOBJ_TYPE_OLE, eOle ); + aOpts.SetObjMode( VOBJ_TYPE_CHART, eChart ); + aOpts.SetObjMode( VOBJ_TYPE_DRAW, eDraw ); + + pD->SetViewOptions( aOpts ); +} + + +void ImportExcel::Bundleheader( void ) +{ +} + + +void ImportExcel::Standardwidth( void ) +{ + USHORT nScWidth = XclTools::GetScColumnWidth( maStrm.ReaduInt16(), GetCharWidth() ); + pColRowBuff->SetDefWidth( nScWidth, TRUE ); +} + + +void ImportExcel::Shrfmla( void ) +{ + UINT16 nFirstRow, nLastRow, nLenExpr; + BYTE nFirstCol, nLastCol; + + aIn >> nFirstRow >> nLastRow >> nFirstCol >> nLastCol; + aIn.Ignore( 2 ); + aIn >> nLenExpr; + + // jetzt steht Lesemarke an der Formel + + const ScTokenArray* pErgebnis; + + pFormConv->Reset(); + pFormConv->Convert( pErgebnis, maStrm, nLenExpr, true, FT_SharedFormula ); + + + DBG_ASSERT( pErgebnis, "+ImportExcel::Shrfmla(): ScTokenArray ist NULL!" ); + + pExcRoot->pShrfmlaBuff->Store( ScRange( static_cast<SCCOL>(nFirstCol), + static_cast<SCROW>(nFirstRow), GetCurrScTab(), + static_cast<SCCOL>(nLastCol), static_cast<SCROW>(nLastRow), + GetCurrScTab()), *pErgebnis ); +} + + +void ImportExcel::Mulrk( void ) +{ + XclAddress aXclPos; + UINT16 nXF; + INT32 nRkNum; + + aIn >> aXclPos; + + for( XclAddress aCurrXclPos( aXclPos ); (aXclPos.mnCol <= aCurrXclPos.mnCol) && (aIn.GetRecLeft() > 2); ++aCurrXclPos.mnCol ) + { + aIn >> nXF >> nRkNum; + + ScAddress aScPos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScPos, aCurrXclPos, GetCurrScTab(), true ) ) + { + GetXFRangeBuffer().SetXF( aScPos, nXF ); + GetDoc().PutCell( aScPos, new ScValueCell( XclTools::GetDoubleFromRK( nRkNum ) ) ); + } + } +} + + +void ImportExcel::Mulblank( void ) +{ + XclAddress aXclPos; + UINT16 nXF; + + aIn >> aXclPos; + + for( XclAddress aCurrXclPos( aXclPos ); (aXclPos.mnCol <= aCurrXclPos.mnCol) && (aIn.GetRecLeft() > 2); ++aCurrXclPos.mnCol ) + { + aIn >> nXF; + + ScAddress aScPos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScPos, aCurrXclPos, GetCurrScTab(), true ) ) + GetXFRangeBuffer().SetBlankXF( aScPos, nXF ); + } +} + + +void ImportExcel::Rstring( void ) +{ + XclAddress aXclPos; + sal_uInt16 nXFIdx; + aIn >> aXclPos >> nXFIdx; + + ScAddress aScPos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) ) + { + // unformatted Unicode string with separate formatting information + XclImpString aString; + + // #i63105# use text encoding from FONT record + rtl_TextEncoding eOldTextEnc = GetTextEncoding(); + if( const XclImpFont* pFont = GetXFBuffer().GetFont( nXFIdx ) ) + SetTextEncoding( pFont->GetFontEncoding() ); + aString.Read( maStrm ); + SetTextEncoding( eOldTextEnc ); + + // character formatting runs + if( !aString.IsRich() ) + aString.ReadFormats( maStrm ); + + GetXFRangeBuffer().SetXF( aScPos, nXFIdx ); + if( ScBaseCell* pCell = XclImpStringHelper::CreateCell( *this, aString, nXFIdx ) ) + GetDoc().PutCell( aScPos, pCell ); + } +} + + +void ImportExcel::Cellmerging() +{ + XclImpAddressConverter& rAddrConv = GetAddressConverter(); + SCTAB nScTab = GetCurrScTab(); + + sal_uInt16 nCount; + maStrm >> nCount; + for( sal_uInt16 nIdx = 0; (nIdx < nCount) && (maStrm.GetRecLeft() >= 8); ++nIdx ) + { + XclRange aXclRange; + maStrm >> aXclRange; // 16-bit rows and columns + ScRange aScRange( ScAddress::UNINITIALIZED ); + if( rAddrConv.ConvertRange( aScRange, aXclRange, nScTab, nScTab, true ) ) + GetXFRangeBuffer().SetMerge( aScRange.aStart.Col(), aScRange.aStart.Row(), aScRange.aEnd.Col(), aScRange.aEnd.Row() ); + } +} + + +void ImportExcel::Olesize( void ) +{ + XclRange aXclOleSize( ScAddress::UNINITIALIZED ); + maStrm.Ignore( 2 ); + aXclOleSize.Read( maStrm, false ); + + SCTAB nScTab = GetCurrScTab(); + GetAddressConverter().ConvertRange( maScOleSize, aXclOleSize, nScTab, nScTab, false ); +} + + +void ImportExcel::Row34( void ) +{ + UINT16 nRow, nRowHeight, nGrbit, nXF; + + aIn >> nRow; + aIn.Ignore( 4 ); // Mic und Mac ueberspringen + + SCROW nScRow = static_cast< SCROW >( nRow ); + + if( ValidRow( nScRow ) ) + { + aIn >> nRowHeight; // direkt in Twips angegeben + aIn.Ignore( 4 ); + + aIn >> nGrbit >> nXF; + + sal_uInt8 nLevel = ::extract_value< sal_uInt8 >( nGrbit, 0, 3 ); + pRowOutlineBuff->SetLevel( nScRow, nLevel, + ::get_flag( nGrbit, EXC_ROW_COLLAPSED ), ::get_flag( nGrbit, EXC_ROW_HIDDEN ) ); + + pColRowBuff->SetRowSettings( nScRow, nRowHeight, nGrbit ); + + if( nGrbit & EXC_ROW_USEDEFXF ) + GetXFRangeBuffer().SetRowDefXF( nScRow, nXF & EXC_ROW_XFMASK ); + } +} + + +void ImportExcel::Bof3( void ) +{ + sal_uInt16 nSubType; + maStrm.DisableDecryption(); + maStrm.Ignore( 2 ); + maStrm >> nSubType; + + DBG_ASSERT( nSubType != 0x0100, "*ImportExcel::Bof3(): Biff3 als Workbook?!" ); + if( nSubType == 0x0100 ) // Book + pExcRoot->eDateiTyp = Biff3W; + else if( nSubType == 0x0020 ) // Chart + pExcRoot->eDateiTyp = Biff3C; + else if( nSubType == 0x0040 ) // Macro + pExcRoot->eDateiTyp = Biff3M; + else // #i51490# Excel interprets invalid indexes as worksheet + pExcRoot->eDateiTyp = Biff3; +} + + +void ImportExcel::Array34( void ) +{ + UINT16 nFirstRow, nLastRow, nFormLen; + BYTE nFirstCol, nLastCol; + + aIn >> nFirstRow >> nLastRow >> nFirstCol >> nLastCol; + aIn.Ignore( (GetBiff() >= EXC_BIFF5) ? 6 : 2 ); + aIn >> nFormLen; + + if( ValidColRow( nLastCol, nLastRow ) ) + { + // jetzt steht Lesemarke auf Formel, Laenge in nFormLen + const ScTokenArray* pErgebnis; + + pFormConv->Reset( ScAddress( static_cast<SCCOL>(nFirstCol), + static_cast<SCROW>(nFirstRow), GetCurrScTab() ) ); + pFormConv->Convert( pErgebnis, maStrm, nFormLen, true, FT_CellFormula); + + DBG_ASSERT( pErgebnis, "+ImportExcel::Array34(): ScTokenArray ist NULL!" ); + + ScMarkData aMarkData; + aMarkData.SelectOneTable( GetCurrScTab() ); + pD->InsertMatrixFormula( static_cast<SCCOL>(nFirstCol), + static_cast<SCROW>(nFirstRow), static_cast<SCCOL>(nLastCol), + static_cast<SCROW>(nLastRow), aMarkData, EMPTY_STRING, + pErgebnis); + } +} + + +void ImportExcel::Externname34( void ) +{ +} + + +void ImportExcel::Defrowheight345( void ) +{ + sal_uInt16 nFlags, nDefHeight; + maStrm >> nFlags >> nDefHeight; + pColRowBuff->SetDefHeight( nDefHeight, nFlags ); +} + + +void ImportExcel::TableOp( void ) +{ + UINT16 nFirstRow, nLastRow; + UINT8 nFirstCol, nLastCol; + UINT16 nGrbit; + UINT16 nInpRow, nInpCol, nInpRow2, nInpCol2; + + aIn >> nFirstRow >> nLastRow >> nFirstCol >> nLastCol >> nGrbit + >> nInpRow >> nInpCol >> nInpRow2 >> nInpCol2; + + if( ValidColRow( nLastCol, nLastRow ) ) + { + if( nFirstCol && nFirstRow ) + { + ScTabOpParam aTabOpParam; + aTabOpParam.nMode = (nGrbit & EXC_TABLEOP_BOTH) ? 2 : ((nGrbit & EXC_TABLEOP_ROW) ? 1 : 0 ); + USHORT nCol = nFirstCol - 1; + USHORT nRow = nFirstRow - 1; + SCTAB nTab = GetCurrScTab(); + switch( aTabOpParam.nMode ) + { + case 0: // COL + aTabOpParam.aRefFormulaCell.Set( + static_cast<SCCOL>(nFirstCol), + static_cast<SCROW>(nFirstRow - 1), nTab, FALSE, + FALSE, FALSE ); + aTabOpParam.aRefFormulaEnd.Set( + static_cast<SCCOL>(nLastCol), + static_cast<SCROW>(nFirstRow - 1), nTab, FALSE, + FALSE, FALSE ); + aTabOpParam.aRefColCell.Set( static_cast<SCCOL>(nInpCol), + static_cast<SCROW>(nInpRow), nTab, FALSE, FALSE, + FALSE ); + nRow++; + break; + case 1: // ROW + aTabOpParam.aRefFormulaCell.Set( + static_cast<SCCOL>(nFirstCol - 1), + static_cast<SCROW>(nFirstRow), nTab, FALSE, FALSE, + FALSE ); + aTabOpParam.aRefFormulaEnd.Set( + static_cast<SCCOL>(nFirstCol - 1), + static_cast<SCROW>(nLastRow), nTab, FALSE, FALSE, + FALSE ); + aTabOpParam.aRefRowCell.Set( static_cast<SCCOL>(nInpCol), + static_cast<SCROW>(nInpRow), nTab, FALSE, FALSE, + FALSE ); + nCol++; + break; + case 2: // TWO-INPUT + aTabOpParam.aRefFormulaCell.Set( + static_cast<SCCOL>(nFirstCol - 1), + static_cast<SCROW>(nFirstRow - 1), nTab, FALSE, + FALSE, FALSE ); + aTabOpParam.aRefRowCell.Set( static_cast<SCCOL>(nInpCol), + static_cast<SCROW>(nInpRow), nTab, FALSE, FALSE, + FALSE ); + aTabOpParam.aRefColCell.Set( static_cast<SCCOL>(nInpCol2), + static_cast<SCROW>(nInpRow2), nTab, FALSE, FALSE, + FALSE ); + break; + } + + ScMarkData aMarkData; + aMarkData.SelectOneTable( nTab ); + pD->InsertTableOp( aTabOpParam, static_cast<SCCOL>(nCol), + static_cast<SCROW>(nRow), static_cast<SCCOL>(nLastCol), + static_cast<SCROW>(nLastRow), aMarkData ); + } + } + else + { + bTabTruncated = TRUE; + GetTracer().TraceInvalidRow(GetCurrScTab(), nLastRow, MAXROW); + } +} + + +void ImportExcel::Bof4( void ) +{ + sal_uInt16 nSubType; + maStrm.DisableDecryption(); + maStrm.Ignore( 2 ); + maStrm >> nSubType; + + if( nSubType == 0x0100 ) // Book + pExcRoot->eDateiTyp = Biff4W; + else if( nSubType == 0x0020 ) // Chart + pExcRoot->eDateiTyp = Biff4C; + else if( nSubType == 0x0040 ) // Macro + pExcRoot->eDateiTyp = Biff4M; + else // #i51490# Excel interprets invalid indexes as worksheet + pExcRoot->eDateiTyp = Biff4; +} + + +void ImportExcel::Bof5( void ) +{ + //POST: eDateiTyp = Typ der zu lesenden Datei + UINT16 nSubType, nVers; + BiffTyp eDatei; + + maStrm.DisableDecryption(); + maStrm >> nVers >> nSubType; + + switch( nSubType ) + { + case 0x0005: eDatei = Biff5W; break; // workbook globals + case 0x0006: eDatei = Biff5V; break; // VB module + case 0x0010: eDatei = Biff5; break; // worksheet + case 0x0020: eDatei = Biff5C; break; // chart + case 0x0040: eDatei = Biff5M4; break; // macro sheet + default: + pExcRoot->eDateiTyp = BiffX; + return; + } + + if( nVers == 0x0600 && (GetBiff() == EXC_BIFF8) ) + eDatei = ( BiffTyp ) ( eDatei - Biff5 + Biff8 ); + + pExcRoot->eDateiTyp = eDatei; +} + +void ImportExcel::EndSheet( void ) +{ + pExcRoot->pExtSheetBuff->Reset(); + + if( GetBiff() <= EXC_BIFF5 ) + { + pExcRoot->pExtNameBuff->Reset(); + mnLastRefIdx = 0; + } + + FinalizeTable(); +} + + +void ImportExcel::NeueTabelle( void ) +{ + SCTAB nTab = GetCurrScTab(); + if( nTab > 0 && !pD->HasTable( nTab ) ) + pD->MakeTable( nTab ); + + pExcRoot->pShrfmlaBuff->Clear(); + + InitializeTable( nTab ); + + pOutlineListBuffer->Append( new XclImpOutlineDataBuffer( GetRoot(), nTab ) ); + + pExcRoot->pColRowBuff = pColRowBuff = pOutlineListBuffer->Last()->GetColRowBuff(); + pColOutlineBuff = pOutlineListBuffer->Last()->GetColOutline(); + pRowOutlineBuff = pOutlineListBuffer->Last()->GetRowOutline(); +} + + +const ScTokenArray* ImportExcel::ErrorToFormula( BYTE bErrOrVal, BYTE nError, double& rVal ) +{ + return pFormConv->GetBoolErr( XclTools::ErrorToEnum( rVal, bErrOrVal, nError ) ); +} + + +void ImportExcel::AdjustRowHeight() +{ + /* #93255# Speed up chart import: import all sheets without charts, then + update row heights (here), last load all charts -> do not any longer + update inside of ScDocShell::ConvertFrom() (causes update of existing + charts during each and every change of row height). */ + if( ScModelObj* pDocObj = GetDocModelObj() ) + pDocObj->UpdateAllRowHeights(); +} + + +void ImportExcel::PostDocLoad( void ) +{ + /* Set automatic page numbering in Default page style (default is "page number = 1"). + Otherwise hidden tables (i.e. for scenarios) which have Default page style will + break automatic page numbering. */ + if( SfxStyleSheetBase* pStyleSheet = GetStyleSheetPool().Find( ScGlobal::GetRscString( STR_STYLENAME_STANDARD ), SFX_STYLE_FAMILY_PAGE ) ) + pStyleSheet->GetItemSet().Put( SfxUInt16Item( ATTR_PAGE_FIRSTPAGENO, 0 ) ); + + // outlines for all sheets, sets hidden rows and columns (#i11776# after filtered ranges) + for( XclImpOutlineDataBuffer* pBuffer = pOutlineListBuffer->First(); pBuffer; pBuffer = pOutlineListBuffer->Next() ) + pBuffer->Convert(); + + // document view settings (before visible OLE area) + GetDocViewSettings().Finalize(); + + // process all drawing objects (including OLE, charts, controls; after hiding rows/columns; before visible OLE area) + GetObjectManager().ConvertObjects(); + + // visible area (used if this document is an embedded OLE object) + if( SfxObjectShell* pDocShell = GetDocShell() ) + { + // visible area if embedded + const ScExtDocSettings& rDocSett = GetExtDocOptions().GetDocSettings(); + SCTAB nDisplScTab = rDocSett.mnDisplTab; + + /* #i44077# If a new OLE object is inserted from file, there is no + OLESIZE record in the Excel file. Calculate used area from file + contents (used cells and drawing objects). */ + if( !maScOleSize.IsValid() ) + { + // used area of displayed sheet (cell contents) + if( const ScExtTabSettings* pTabSett = GetExtDocOptions().GetTabSettings( nDisplScTab ) ) + maScOleSize = pTabSett->maUsedArea; + // add all valid drawing objects + ScRange aScObjArea = GetObjectManager().GetUsedArea( nDisplScTab ); + if( aScObjArea.IsValid() ) + maScOleSize.ExtendTo( aScObjArea ); + } + + // valid size found - set it at the document + if( maScOleSize.IsValid() ) + { + pDocShell->SetVisArea( GetDoc().GetMMRect( + maScOleSize.aStart.Col(), maScOleSize.aStart.Row(), + maScOleSize.aEnd.Col(), maScOleSize.aEnd.Row(), nDisplScTab ) ); + GetDoc().SetVisibleTab( nDisplScTab ); + } + } + + // #111099# open forms in alive mode (has no effect, if no controls in document) + if( ScModelObj* pDocObj = GetDocModelObj() ) + pDocObj->setPropertyValue( CREATE_OUSTRING( SC_UNO_APPLYFMDES ), uno::Any( false ) ); + + // enables extended options to be set to the view after import + GetExtDocOptions().SetChanged( true ); + + // root data owns the extended document options -> create a new object + GetDoc().SetExtDocOptions( new ScExtDocOptions( GetExtDocOptions() ) ); + + const SCTAB nLast = pD->GetTableCount(); + const ScRange* p; + + if( pExcRoot->pPrintRanges->HasRanges() ) + { + for( SCTAB n = 0 ; n < nLast ; n++ ) + { + p = pExcRoot->pPrintRanges->First( static_cast<UINT16>(n) ); + if( p ) + { + DBG_ASSERT( pExcRoot->pPrintRanges->GetActList(), + "-ImportExcel::PostDocLoad(): Imaginaere Tabelle gefunden!" ); + + pD->ClearPrintRanges( n ); + while( p ) + { + pD->AddPrintRange( n, *p ); + p = pExcRoot->pPrintRanges->Next(); + } + } + else + { + // #i4063# no print ranges -> print entire sheet + pD->SetPrintEntireSheet( n ); + } + } + GetTracer().TracePrintRange(); + } + + if( pExcRoot->pPrintTitles->HasRanges() ) + { + for( SCTAB n = 0 ; n < nLast ; n++ ) + { + p = pExcRoot->pPrintTitles->First( static_cast<UINT16>(n) ); + if( p ) + { + DBG_ASSERT( pExcRoot->pPrintTitles->GetActList(), + "-ImportExcel::PostDocLoad(): Imaginaere Tabelle gefunden!" ); + + BOOL bRowVirgin = TRUE; + BOOL bColVirgin = TRUE; + + while( p ) + { + if( p->aStart.Col() == 0 && p->aEnd.Col() == MAXCOL && bRowVirgin ) + { + pD->SetRepeatRowRange( n, p ); + bRowVirgin = FALSE; + } + + if( p->aStart.Row() == 0 && p->aEnd.Row() == MAXROW && bColVirgin ) + { + pD->SetRepeatColRange( n, p ); + bColVirgin = FALSE; + } + + p = pExcRoot->pPrintTitles->Next(); + } + } + } + } +} + +XclImpOutlineDataBuffer::XclImpOutlineDataBuffer( const XclImpRoot& rRoot, SCTAB nScTab ) : + XclImpRoot( rRoot ), + mxColOutlineBuff( new XclImpOutlineBuffer( MAXCOLCOUNT ) ), + mxRowOutlineBuff( new XclImpOutlineBuffer( MAXROWCOUNT ) ), + mxColRowBuff( new XclImpColRowSettings( rRoot ) ), + mnScTab( nScTab ) +{ +} + +XclImpOutlineDataBuffer::~XclImpOutlineDataBuffer() +{ +} + +void XclImpOutlineDataBuffer::Convert() +{ + mxColOutlineBuff->SetOutlineArray( GetDoc().GetOutlineTable( mnScTab, TRUE )->GetColArray() ); + mxColOutlineBuff->MakeScOutline(); + + mxRowOutlineBuff->SetOutlineArray( GetDoc().GetOutlineTable( mnScTab, TRUE )->GetRowArray() ); + mxRowOutlineBuff->MakeScOutline(); + + mxColRowBuff->ConvertHiddenFlags( mnScTab ); +} diff --git a/sc/source/filter/excel/makefile.mk b/sc/source/filter/excel/makefile.mk new file mode 100644 index 000000000000..04ba76d4a68a --- /dev/null +++ b/sc/source/filter/excel/makefile.mk @@ -0,0 +1,160 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=sc +TARGET=excel + +AUTOSEG=true + +PROJECTPCH4DLL=TRUE +PROJECTPCH=filt_pch +PROJECTPCHSOURCE=..\pch\filt_pch + +VISIBILITY_HIDDEN=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : scpre.mk +.INCLUDE : settings.mk +.INCLUDE : sc.mk + +# --- Files -------------------------------------------------------- + +SLOFILES = \ + $(SLO)$/colrowst.obj \ + $(SLO)$/excdoc.obj \ + $(SLO)$/excel.obj \ + $(SLO)$/excform.obj \ + $(SLO)$/excform8.obj \ + $(SLO)$/excimp8.obj \ + $(SLO)$/excrecds.obj \ + $(SLO)$/exctools.obj \ + $(SLO)$/expop2.obj \ + $(SLO)$/fontbuff.obj \ + $(SLO)$/frmbase.obj \ + $(SLO)$/impop.obj \ + $(SLO)$/namebuff.obj \ + $(SLO)$/read.obj \ + $(SLO)$/tokstack.obj \ + $(SLO)$/xechart.obj \ + $(SLO)$/xecontent.obj \ + $(SLO)$/xeescher.obj \ + $(SLO)$/xeformula.obj \ + $(SLO)$/xehelper.obj \ + $(SLO)$/xelink.obj \ + $(SLO)$/xename.obj \ + $(SLO)$/xepage.obj \ + $(SLO)$/xepivot.obj \ + $(SLO)$/xerecord.obj \ + $(SLO)$/xeroot.obj \ + $(SLO)$/xestream.obj \ + $(SLO)$/xestring.obj \ + $(SLO)$/xestyle.obj \ + $(SLO)$/xetable.obj \ + $(SLO)$/xeview.obj \ + $(SLO)$/xichart.obj \ + $(SLO)$/xicontent.obj \ + $(SLO)$/xiescher.obj \ + $(SLO)$/xiformula.obj \ + $(SLO)$/xihelper.obj \ + $(SLO)$/xilink.obj \ + $(SLO)$/xiname.obj \ + $(SLO)$/xipage.obj \ + $(SLO)$/xipivot.obj \ + $(SLO)$/xiroot.obj \ + $(SLO)$/xistream.obj \ + $(SLO)$/xistring.obj \ + $(SLO)$/xistyle.obj \ + $(SLO)$/xiview.obj \ + $(SLO)$/xladdress.obj \ + $(SLO)$/xlchart.obj \ + $(SLO)$/xlescher.obj \ + $(SLO)$/xlformula.obj \ + $(SLO)$/xlpage.obj \ + $(SLO)$/xlpivot.obj \ + $(SLO)$/xlroot.obj \ + $(SLO)$/xlstyle.obj \ + $(SLO)$/xltools.obj \ + $(SLO)$/xltracer.obj \ + $(SLO)$/xlview.obj + +.IF "$(OS)$(COM)$(CPUNAME)"=="LINUXGCCSPARC" +NOOPTFILES = \ + $(SLO)$/xiescher.obj +.ENDIF + +EXCEPTIONSFILES = \ + $(SLO)$/excdoc.obj \ + $(SLO)$/excel.obj \ + $(SLO)$/excform.obj \ + $(SLO)$/excform8.obj \ + $(SLO)$/excimp8.obj \ + $(SLO)$/excrecds.obj \ + $(SLO)$/expop2.obj \ + $(SLO)$/impop.obj \ + $(SLO)$/namebuff.obj \ + $(SLO)$/tokstack.obj \ + $(SLO)$/xecontent.obj \ + $(SLO)$/xeescher.obj \ + $(SLO)$/xeformula.obj \ + $(SLO)$/xehelper.obj \ + $(SLO)$/xelink.obj \ + $(SLO)$/xename.obj \ + $(SLO)$/xepage.obj \ + $(SLO)$/xepivot.obj \ + $(SLO)$/xechart.obj \ + $(SLO)$/xestream.obj \ + $(SLO)$/xestring.obj \ + $(SLO)$/xestyle.obj \ + $(SLO)$/xetable.obj \ + $(SLO)$/xeview.obj \ + $(SLO)$/xichart.obj \ + $(SLO)$/xicontent.obj \ + $(SLO)$/xiescher.obj \ + $(SLO)$/xihelper.obj \ + $(SLO)$/xilink.obj \ + $(SLO)$/xipage.obj \ + $(SLO)$/xipivot.obj \ + $(SLO)$/xistream.obj \ + $(SLO)$/xistring.obj \ + $(SLO)$/xistyle.obj \ + $(SLO)$/xladdress.obj \ + $(SLO)$/xiescher.obj \ + $(SLO)$/xlchart.obj \ + $(SLO)$/xlformula.obj \ + $(SLO)$/xlpivot.obj \ + $(SLO)$/xlroot.obj \ + $(SLO)$/xlstyle.obj \ + $(SLO)$/xltools.obj \ + $(SLO)$/xlview.obj + +# --- Targets ------------------------------------------------------- + +.INCLUDE : target.mk + diff --git a/sc/source/filter/excel/namebuff.cxx b/sc/source/filter/excel/namebuff.cxx new file mode 100644 index 000000000000..7db7d17ed307 --- /dev/null +++ b/sc/source/filter/excel/namebuff.cxx @@ -0,0 +1,344 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "namebuff.hxx" + +#include <tools/urlobj.hxx> +#include <string.h> + +#include "rangenam.hxx" +#include "document.hxx" +#include "compiler.hxx" +#include "scextopt.hxx" + +#include "root.hxx" +#include "tokstack.hxx" +#include "xltools.hxx" +#include "xiroot.hxx" + + +UINT32 StringHashEntry::MakeHashCode( const String& r ) +{ + register UINT32 n = 0; + const sal_Unicode* pAkt = r.GetBuffer(); + register sal_Unicode cAkt = *pAkt; + + while( cAkt ) + { + n *= 70; + n += ( UINT32 ) cAkt; + pAkt++; + cAkt = *pAkt; + } + + return n; +} + + + + +NameBuffer::~NameBuffer() +{ + register StringHashEntry* pDel = ( StringHashEntry* ) List::First(); + while( pDel ) + { + delete pDel; + pDel = ( StringHashEntry* ) List::Next(); + } +} + + +//void NameBuffer::operator <<( const SpString &rNewString ) +void NameBuffer::operator <<( const String &rNewString ) +{ + DBG_ASSERT( Count() + nBase < 0xFFFF, + "*NameBuffer::GetLastIndex(): Ich hab' die Nase voll!" ); + + List::Insert( new StringHashEntry( rNewString ), LIST_APPEND ); +} + + +#ifdef DBG_UTIL +UINT16 nShrCnt; +#endif + + +size_t ShrfmlaBuffer::ScAddressHashFunc::operator() (const ScAddress &addr) const +{ + // Use something simple, it is just a hash. + return static_cast< UINT16 >( addr.Row() ) | (static_cast< UINT8 >( addr.Col() ) << 16); +} + +const size_t nBase = 16384; // Range~ und Shared~ Dingens mit jeweils der Haelfte Ids +ShrfmlaBuffer::ShrfmlaBuffer( RootData* pRD ) : + ExcRoot( pRD ), + mnCurrIdx (nBase) +{ +#ifdef DBG_UTIL + nShrCnt = 0; +#endif +} + +ShrfmlaBuffer::~ShrfmlaBuffer() +{ +} + +void ShrfmlaBuffer::Clear() +{ + index_hash.clear(); + // do not clear index_list, index calculation depends on complete list size... + // do not change mnCurrIdx +} + +void ShrfmlaBuffer::Store( const ScRange& rRange, const ScTokenArray& rToken ) +{ + String aName( CreateName( rRange.aStart ) ); + + DBG_ASSERT( mnCurrIdx <= 0xFFFF, "*ShrfmlaBuffer::Store(): Gleich wird mir schlecht...!" ); + + ScRangeData* pData = new ScRangeData( pExcRoot->pIR->GetDocPtr(), aName, rToken, rRange.aStart, RT_SHARED ); + const ScAddress& rMaxPos = pExcRoot->pIR->GetMaxPos(); + pData->SetMaxCol(rMaxPos.Col()); + pData->SetMaxRow(rMaxPos.Row()); + pData->SetIndex( static_cast< USHORT >( mnCurrIdx ) ); + pExcRoot->pIR->GetNamedRanges().Insert( pData ); + index_hash[rRange.aStart] = static_cast< USHORT >( mnCurrIdx ); + index_list.push_front (rRange); + ++mnCurrIdx; +} + + +USHORT ShrfmlaBuffer::Find( const ScAddress & aAddr ) const +{ + ShrfmlaHash::const_iterator hash = index_hash.find (aAddr); + if (hash != index_hash.end()) + return hash->second; + + // It was not hashed on the top left corner ? do a brute force search + unsigned int ind = nBase; + for (ShrfmlaList::const_iterator ptr = index_list.end(); ptr != index_list.begin() ; ind++) + if ((--ptr)->In (aAddr)) + return static_cast< USHORT >( ind ); + return static_cast< USHORT >( mnCurrIdx ); +} + + +#define SHRFMLA_BASENAME "SHARED_FORMULA_" + +String ShrfmlaBuffer::CreateName( const ScRange& r ) +{ + String aName( RTL_CONSTASCII_USTRINGPARAM( SHRFMLA_BASENAME ) ); + aName += String::CreateFromInt32( r.aStart.Col() ); + aName.Append( '_' ); + aName += String::CreateFromInt32( r.aStart.Row() ); + aName.Append( '_' ); + aName += String::CreateFromInt32( r.aEnd.Col() ); + aName.Append( '_' ); + aName += String::CreateFromInt32( r.aEnd.Row() ); + aName.Append( '_' ); + aName += String::CreateFromInt32( r.aStart.Tab() ); + + return aName; +} + + +ExtSheetBuffer::~ExtSheetBuffer() +{ + Cont *pAkt = ( Cont * ) List::First(); + while( pAkt ) + { + delete pAkt; + pAkt = ( Cont * ) List::Next(); + } +} + + +sal_Int16 ExtSheetBuffer::Add( const String& rFPAN, const String& rTN, const BOOL bSWB ) +{ + List::Insert( new Cont( rFPAN, rTN, bSWB ), LIST_APPEND ); + // return 1-based index of EXTERNSHEET + return static_cast< sal_Int16 >( List::Count() ); +} + + +BOOL ExtSheetBuffer::GetScTabIndex( UINT16 nExcIndex, UINT16& rScIndex ) +{ + DBG_ASSERT( nExcIndex, + "*ExtSheetBuffer::GetScTabIndex(): Sheet-Index == 0!" ); + + nExcIndex--; + Cont* pCur = ( Cont * ) List::GetObject( nExcIndex ); + UINT16& rTabNum = pCur->nTabNum; + + if( pCur ) + { + if( rTabNum < 0xFFFD ) + { + rScIndex = rTabNum; + return TRUE; + } + + if( rTabNum == 0xFFFF ) + {// neue Tabelle erzeugen + SCTAB nNewTabNum; + if( pCur->bSWB ) + {// Tabelle ist im selben Workbook! + if( pExcRoot->pIR->GetDoc().GetTable( pCur->aTab, nNewTabNum ) ) + { + rScIndex = rTabNum = static_cast<UINT16>(nNewTabNum); + return TRUE; + } + else + rTabNum = 0xFFFD; + } + else if( pExcRoot->pIR->GetDocShell() ) + {// Tabelle ist 'echt' extern + if( pExcRoot->pIR->GetExtDocOptions().GetDocSettings().mnLinkCnt == 0 ) + { + String aURL( ScGlobal::GetAbsDocName( pCur->aFile, + pExcRoot->pIR->GetDocShell() ) ); + String aTabName( ScGlobal::GetDocTabName( aURL, pCur->aTab ) ); + if( pExcRoot->pIR->GetDoc().LinkExternalTab( nNewTabNum, aTabName, aURL, pCur->aTab ) ) + { + rScIndex = rTabNum = static_cast<UINT16>(nNewTabNum); + return TRUE; + } + else + rTabNum = 0xFFFE; // Tabelle einmal nicht angelegt -> wird + // wohl auch nicht mehr gehen... + } + else + rTabNum = 0xFFFE; + + } + } + } + + return FALSE; +} + + +BOOL ExtSheetBuffer::IsLink( const UINT16 nExcIndex ) const +{ + DBG_ASSERT( nExcIndex > 0, "*ExtSheetBuffer::IsLink(): Index muss >0 sein!" ); + Cont* pRet = ( Cont * ) List::GetObject( nExcIndex - 1 ); + + if( pRet ) + return pRet->bLink; + else + return FALSE; +} + + +BOOL ExtSheetBuffer::GetLink( const UINT16 nExcIndex, String& rAppl, String& rDoc ) const +{ + DBG_ASSERT( nExcIndex > 0, "*ExtSheetBuffer::GetLink(): Index muss >0 sein!" ); + Cont* pRet = ( Cont * ) List::GetObject( nExcIndex - 1 ); + + if( pRet ) + { + rAppl = pRet->aFile; + rDoc = pRet->aTab; + return TRUE; + } + else + return FALSE; +} + + +void ExtSheetBuffer::Reset( void ) +{ + Cont *pAkt = ( Cont * ) List::First(); + while( pAkt ) + { + delete pAkt; + pAkt = ( Cont * ) List::Next(); + } + + List::Clear(); +} + + + + +BOOL ExtName::IsDDE( void ) const +{ + return ( nFlags & 0x0001 ) != 0; +} + + +BOOL ExtName::IsOLE( void ) const +{ + return ( nFlags & 0x0002 ) != 0; +} + + +ExtNameBuff::ExtNameBuff( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + + +void ExtNameBuff::AddDDE( const String& rName, sal_Int16 nRefIdx ) +{ + ExtName aNew( rName, 0x0001 ); + maExtNames[ nRefIdx ].push_back( aNew ); +} + + +void ExtNameBuff::AddOLE( const String& rName, sal_Int16 nRefIdx, UINT32 nStorageId ) +{ + ExtName aNew( rName, 0x0002 ); + aNew.nStorageId = nStorageId; + maExtNames[ nRefIdx ].push_back( aNew ); +} + + +void ExtNameBuff::AddName( const String& rName, sal_Int16 nRefIdx ) +{ + ExtName aNew( GetScAddInName( rName ), 0x0004 ); + maExtNames[ nRefIdx ].push_back( aNew ); +} + + +const ExtName* ExtNameBuff::GetNameByIndex( sal_Int16 nRefIdx, sal_uInt16 nNameIdx ) const +{ + DBG_ASSERT( nNameIdx > 0, "ExtNameBuff::GetNameByIndex() - invalid name index" ); + ExtNameMap::const_iterator aIt = maExtNames.find( nRefIdx ); + return ((aIt != maExtNames.end()) && (0 < nNameIdx) && (nNameIdx <= aIt->second.size())) ? &aIt->second[ nNameIdx - 1 ] : 0; +} + + +void ExtNameBuff::Reset( void ) +{ + maExtNames.clear(); +} + + diff --git a/sc/source/filter/excel/ooxml-export-TODO.txt b/sc/source/filter/excel/ooxml-export-TODO.txt new file mode 100644 index 000000000000..5b04efb97007 --- /dev/null +++ b/sc/source/filter/excel/ooxml-export-TODO.txt @@ -0,0 +1,148 @@ +TODO/Unimplemented Calc OOXML Export Features: +============================================= + +Partially implemented features are not mentioned here; grep for OOXTODO within +sc/source/filter/*. + +In updated OfficeFileFormatsProtocols.zip [MS-XLS].pdf, +Section §2.3.1 (p.154) provides the record name :: record number mapping, and +Section §2.3.2 (p.165) provides the record number :: record name mapping. + +Elements: + - Workbook (§3.2): + - customWorkbookViews (§3.2.3) + - ext (§3.2.7) + - externalReference (§3.2.8) + - externalReferences (§3.2.9) + - extLst (§3.2.10) + - fileRecoveryPr (§3.2.11) [ CRASHRECERR? 865h ] + - fileSharing (§3.2.12) [ FILESHARING 5Bh ] + - functionGroup (§3.2.14) [ FNGRP12 898h; FNGROUPNAME 9Ah ] + - functionGroups (§3.2.15) [ FNGROUPCOUNT: 9Ch ] + - oleSize (§3.2.16) [ OLESIZE DEh ] + - smartTagPr (§3.2.21) [ BOOKEXT 863h ] + - smartTagType (§3.2.22) [ unknown record ] + - smartTagTypes (§3.2.23) [ unknown record ] + - webPublishing (§3.2.24) [ WOPT 80Bh ] + - webPublishObject (§3.2.25) [ WEBPUB 801h ] + - webPublishObjects (§3.2.26) [ unsupported ] + - Worksheets (§3.3.1): + - autoFilter (§3.3.1.1) [ AutoFilter 9Eh ] + - cellSmartTag (§3.3.1.4) [ FEAT 868h ] + - cellSmartTagPr (§3.3.1.5) [ FEAT? 868h ] + - cellSmartTags (§3.3.1.6) [ FEAT 868h ] + - cellWatch (§3.3.1.7) [ CELLWATCH 86Ch ] + - cellWatches (§3.3.1.8) [ CELLWATCH 86Ch ] + - cfRule (§3.3.1.9) [ CF 1B1h ] + - cfvo (§3.3.1.10) [ CF12 87Ah ] + - chartsheet (§3.3.1.11) [ CHARTFRTINFO 850h, FRTWRAPPER 851h...] + - color (§3.3.1.14) [ DXF 88Dh xfpropBorder? + XFEXT 87Dh xclrType? ] + - colorScale (§3.3.1.15) [ DXF 88Dh? ] + - control (§3.3.1.18) [ ??? ] + - controls (§3.3.1.19) [ ??? ] + - customPr (§3.3.1.20) [ ??? ] + - customProperties (§3.3.1.21) [ ??? ] + - customSheetView (§3.3.1.22) [ ???; for charts; see chartsheet? ] + - customSheetView (§3.3.1.23) [ ??? ] + - customSheetViews (§3.3.1.24) [ ???; for charts; see chartsheet? ] + - customSheetViews (§3.3.1.25) [ ??? ] + - dataBar (§3.3.1.26) [ CF12 87Ah ct=Databar ] + - dataConsolidate (§3.3.1.27) [ DCON 50h ] + - dataRef (§3.3.1.28) [ DCONBIN 1B5h ] + - dataRefs (§3.3.1.29) [ ??? ] + - dialogsheet (§3.3.1.32) [ ??? ] + - drawing (§3.3.1.34) [ ??? ] + - evenFooter (§3.3.1.35) [ HeaderFooter 89Ch ] + - evenHeader (§3.3.1.36) [ HeaderFooter 89Ch ] + - firstFooter (§3.3.1.38) [ HeaderFooter 89Ch ] + - firstHeader (§3.3.1.39) [ HeaderFooter 89Ch ] + - formula (§3.3.1.40) [ CF 1B1h ] + - iconSet (§3.3.1.46) [ CF12 87Ah ct=CFMultistate ] + - ignoredError (§3.3.1.47) [ Feat/FeatForumulaErr2/FFErrorCheck 868h ] + - ignoredErrors (§3.3.1.48) [ Feat 868h ] + - legacyDrawing (§3.3.1.51) [ MsoDrawing ECh ] + - legacyDrawingHF (§3.3.1.52) [ ??? ] + - oleObject (§3.3.1.57) [ ??? ] + - oleObjects (§3.3.1.58) [ ??? ] + - outlinePr (§3.3.1.59) [ ??? ] + - pageSetup (§3.3.1.62) [ ???; for charts; see chartsheet? ] + - picture (§3.3.1.65) [ BkHim E9h; see XclExpBitmap ] + - pivotArea (§3.3.1.66) [ ??? ] + - pivotSelection (§3.3.1.67) [ ??? ] + - protectedRange (§3.3.1.69) [ ??? ] + - protectedRanges (§3.3.1.70) [ ??? ] + - sheetCalcPr (§3.3.1.76) [ REFRESHALL?? ] + - sheetFormatPr (§3.3.1.78) [ lots of records? ] + @defaultColWidth: DefColWidth + @defaultRowHeight: DEFROWHEIGHT + @baseColWidth: ColInfo/coldx? + @customHeight: ColInfo/fUserSet? + @zeroHeight: ColInfo/fHidden? + @thickTop: ? + @thickBottom: ? + @outlineLevelRow: ? + @outlineLevelCol: ColInfo/iOutLevel? + - sheetPr (§3.3.1.80) [ ??? ; for charts ] + - sheetView (§3.3.1.84) [ ??? ; for charts ] + - sheetViews (§3.3.1.86) [ ??? ; for charts ] + - smartTags (§3.3.1.87) [ FEAT 868h; isf=ISFFACTOID ] + - sortCondition (§3.3.1.88) [ SortData 895h? ] + - sortState (§3.3.1.89) [ Sort 90h ] + - tabColor (§3.3.1.90) [ SheetExt 862h ] + - tablePart (§3.3.1.91) [ ??? ] + - tableParts (§3.3.1.92) [ ??? ] + - webPublishItem (§3.3.1.94) [ WebPub 801h ] + - webPublishItems (§3.3.1.95) + - AutoFilter Settings (§3.3.2): + - colorFilter (§3.3.2.1) [ AutoFilter12 87Eh, + DXFN12NoCB struct ] + - dateGroupItem (§3.3.2.4) [ AutoFilter12 87Eh, + AF12DateInfo struct ] + - dynamicFilter (§3.3.2.5) [ AutoFilter12 87Eh, cft field ] + - filter (§3.3.2.6) [ AutoFilter12 87Eh, rgCriteria? ] + - filters (§3.3.2.9) [ AutoFilter12 87Eh, rgCriteria? ] + - iconFilter (§3.3.2.9) [ AutoFilter12 87Eh, + AF12CellIcon struct ] + - Shared String Table (§3.4): + - phoneticPr (§3.4.3) + - rPh (§3.4.6) + - Tables (§3.5.1): + - calculatedColumnFormula (§3.5.1.1) + [ ??? ] + - table (§3.5.1.2) [ ??? ] + - tableColumn (§3.5.1.3) [ ??? ] + - tableColumns (§3.5.1.4) [ ??? ] + - tableStyleInfo (§3.5.1.5) [ ??? ] + - totalRowFormula (§3.5.1.6) [ ??? ] + - xmlColumnPr (§3.5.1.7) [ ??? ] + - Single Cell Tables (§3.5.2): + - singleXmlCell (§3.5.2.1) [ ??? ] + - singleXmlCells (§3.5.2.2) [ ??? ] + - xmlCellPr (§3.5.2.3) [ ??? ] + - xmlPr (§3.5.2.4) [ ??? ] + - Calculation Chain (§3.6): + - c (§3.6.1) [ ??? ] + - calcChain (§3.6.2) [ ??? ] + - Comments (§3.7): + - Note: Excel *requires* that there be a drawing object associated + with the comment before it will show it. If you _just_ generate the + <comments/> XML part and create a <Relationship/> for it, Excell + will NOT display the comment. + - As drawing is not currently implemented, comments support is + incomplete. + - TODO: text formatting. Currently we only write unformatted text + into comments?.xml, as I'm not sure how formatted text is handled. + - Styles (§3.8): + - dxf (§3.8.14): [ DXF 88Dh; unsupported ] + - dxfs (§3.8.15): [ DXF 88Dh ] + - gradientFill (§3.8.23): [ ??? ] + - horizontal (§3.8.24): [ DXF 88Dh fNewBorder, xfprops ] + - mruColors (§3.8.28): [ ??? ] + - scheme (§3.8.36): [ ??? ] + - stop (§3.8.38): [ ??? ] + - tableStyle (§3.8.40): [ TableStyle 88Fh; unsupported ] + - tableStyleElement (§3.8.41): [ TableStyleElement 890h; unsupported ] + - tableStyles (§3.8.42): [ TableStyles 88Eh; unsupported ] + - vertical (§3.8.44): [ DXF 88Dh fNewBorder, xfprops ] + diff --git a/sc/source/filter/excel/read.cxx b/sc/source/filter/excel/read.cxx new file mode 100644 index 000000000000..cfe5aedb867e --- /dev/null +++ b/sc/source/filter/excel/read.cxx @@ -0,0 +1,1243 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + + + +//------------------------------------------------------------------------ + +#include <stdlib.h> +#include <stdio.h> + +#include "document.hxx" +#include "scerrors.hxx" +#include "fprogressbar.hxx" +#include "xltracer.hxx" +#include "xltable.hxx" +#include "xihelper.hxx" +#include "xipage.hxx" +#include "xiview.hxx" +#include "xilink.hxx" +#include "xiname.hxx" +#include "xicontent.hxx" +#include "xiescher.hxx" +#include "xipivot.hxx" +#include "XclImpChangeTrack.hxx" + +#include "root.hxx" +#include "imp_op.hxx" +#include "excimp8.hxx" + +FltError ImportExcel::Read( void ) +{ + XclImpPageSettings& rPageSett = GetPageSettings(); + XclImpTabViewSettings& rTabViewSett = GetTabViewSettings(); + XclImpPalette& rPal = GetPalette(); + XclImpFontBuffer& rFontBfr = GetFontBuffer(); + XclImpNumFmtBuffer& rNumFmtBfr = GetNumFmtBuffer(); + XclImpXFBuffer& rXFBfr = GetXFBuffer(); + XclImpNameManager& rNameMgr = GetNameManager(); + XclImpObjectManager& rObjMgr = GetObjectManager(); + (void)rObjMgr; + // call to GetCurrSheetDrawing() cannot be cached (changes in new sheets) + + enum Zustand { + Z_BiffNull, // Nicht in gueltigem Biff-Format + Z_Biff2, // Biff2: nur eine Tabelle + + Z_Biff3, // Biff3: nur eine Tabelle + + Z_Biff4, // Biff4: nur eine Tabelle + Z_Biff4W, // Biff4 Workbook: Globals + Z_Biff4T, // Biff4 Workbook: eine Tabelle selbst + Z_Biff4E, // Biff4 Workbook: zwischen den Tabellen + + Z_Biff5WPre,// Biff5: Prefetch Workbook + Z_Biff5W, // Biff5: Globals + Z_Biff5TPre,// Biff5: Prefetch fuer Shrfmla/Array Formula + Z_Biff5T, // Biff5: eine Tabelle selbst + Z_Biff5E, // Biff5: zwischen den Tabellen + Z_Biffn0, // Alle Biffs: Tabelle bis naechstesss EOF ueberlesen + Z_Ende }; + + Zustand eAkt = Z_BiffNull, ePrev = Z_BiffNull; + + FltError eLastErr = eERR_OK; + UINT16 nOpcode; + UINT16 nBofLevel = 0; + BOOL bBiff4Workbook = FALSE; + + DBG_ASSERT( &aIn != NULL, "-ImportExcel::Read(): Kein Stream - wie dass?!" ); + + ::std::auto_ptr< ScfSimpleProgressBar > pProgress( new ScfSimpleProgressBar( + aIn.GetSvStreamSize(), GetDocShell(), STR_LOAD_DOC ) ); + + /* #i104057# Need to track a base position for progress bar calculation, + because sheet substreams may not be in order of sheets. */ + sal_Size nProgressBasePos = 0; + sal_Size nProgressBaseSize = 0; + + while( eAkt != Z_Ende ) + { + if( eAkt == Z_Biff5E ) + { + sal_uInt16 nScTab = GetCurrScTab(); + if( nScTab < maSheetOffsets.size() ) + { + nProgressBaseSize += (aIn.GetSvStreamPos() - nProgressBasePos); + nProgressBasePos = maSheetOffsets[ nScTab ]; + aIn.StartNextRecord( nProgressBasePos ); + } + else + eAkt = Z_Ende; + } + else + aIn.StartNextRecord(); + + nOpcode = aIn.GetRecId(); + + if( !aIn.IsValid() ) + { + // #124240# finalize table if EOF is missing + switch( eAkt ) + { + case Z_Biff2: + case Z_Biff3: + case Z_Biff4: + case Z_Biff4T: + case Z_Biff5TPre: + case Z_Biff5T: + rNumFmtBfr.CreateScFormats(); + Eof(); + break; + default:; + }; + eAkt = Z_Ende; + break; + } + + if( eAkt == Z_Ende ) + break; + + if( eAkt != Z_Biff5TPre && eAkt != Z_Biff5WPre ) + pProgress->ProgressAbs( nProgressBaseSize + aIn.GetSvStreamPos() - nProgressBasePos ); + + switch( eAkt ) + { + // ---------------------------------------------------------------- + case Z_BiffNull: // ------------------------------- Z_BiffNull - + { + switch( nOpcode ) + { + case EXC_ID2_BOF: + case EXC_ID3_BOF: + case EXC_ID4_BOF: + case EXC_ID5_BOF: + { + // #i23425# don't rely on the record ID, but on the detected BIFF version + switch( GetBiff() ) + { + case EXC_BIFF2: + Bof2(); + if( pExcRoot->eDateiTyp == Biff2 ) + { + eAkt = Z_Biff2; + NeueTabelle(); + } + break; + case EXC_BIFF3: + Bof3(); + if( pExcRoot->eDateiTyp == Biff3 ) + { + eAkt = Z_Biff3; + NeueTabelle(); + } + break; + case EXC_BIFF4: + Bof4(); + if( pExcRoot->eDateiTyp == Biff4 ) + { + eAkt = Z_Biff4; + NeueTabelle(); + } + else if( pExcRoot->eDateiTyp == Biff4W ) + { + eAkt = Z_Biff4W; + bBiff4Workbook = TRUE; + } + break; + case EXC_BIFF5: + Bof5(); + if( pExcRoot->eDateiTyp == Biff5W ) + { + eAkt = Z_Biff5WPre; + + nBdshtTab = 0; + + aIn.StoreGlobalPosition(); // und Position merken + } + else if( pExcRoot->eDateiTyp == Biff5 ) + { + // #i62752# possible to have BIFF5 sheet without globals + NeueTabelle(); + eAkt = Z_Biff5TPre; // Shrfmla Prefetch, Row-Prefetch + nBofLevel = 0; + aIn.StoreGlobalPosition(); // und Position merken + } + break; + default: + DBG_ERROR_BIFF(); + } + } + break; + } + } + break; + // ---------------------------------------------------------------- + case Z_Biff2: // ---------------------------------- Z_Biff2 - + { + switch( nOpcode ) + { + case EXC_ID2_DIMENSIONS: + case EXC_ID3_DIMENSIONS: ReadDimensions(); break; + case EXC_ID2_BLANK: + case EXC_ID3_BLANK: ReadBlank(); break; + case EXC_ID2_INTEGER: ReadInteger(); break; + case EXC_ID2_NUMBER: + case EXC_ID3_NUMBER: ReadNumber(); break; + case EXC_ID2_LABEL: + case EXC_ID3_LABEL: ReadLabel(); break; + case EXC_ID2_BOOLERR: + case EXC_ID3_BOOLERR: ReadBoolErr(); break; + case EXC_ID_RK: ReadRk(); break; + + case 0x06: Formula25(); break; // FORMULA [ 2 5] + case 0x08: Row25(); break; // ROW [ 2 5] + case 0x0A: // EOF [ 2345] + rNumFmtBfr.CreateScFormats(); + Eof(); + eAkt = Z_Ende; + break; + case 0x14: + case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break; + case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345] + case 0x18: rNameMgr.ReadName( maStrm ); break; + case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break; + case 0x1D: rTabViewSett.ReadSelection( maStrm ); break; + case 0x1E: rNumFmtBfr.ReadFormat( maStrm ); break; + case 0x20: Columndefault(); break; // COLUMNDEFAULT[ 2 ] + case 0x21: Array25(); break; // ARRAY [ 2 5] + case 0x23: Externname25(); break; // EXTERNNAME [ 2 5] + case 0x24: Colwidth(); break; // COLWIDTH [ 2 ] + case 0x25: Defrowheight2(); break; // DEFAULTROWHEI[ 2 ] + case 0x26: + case 0x27: + case 0x28: + case 0x29: rPageSett.ReadMargin( maStrm ); break; + case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break; + case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break; + case 0x2F: // FILEPASS [ 2345] + eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm ); + if( eLastErr != ERRCODE_NONE ) + eAkt = Z_Ende; + break; + case EXC_ID2_FONT: rFontBfr.ReadFont( maStrm ); break; + case EXC_ID_EFONT: rFontBfr.ReadEfont( maStrm ); break; + case 0x3E: rTabViewSett.ReadWindow2( maStrm, false );break; + case 0x41: rTabViewSett.ReadPane( maStrm ); break; + case 0x42: Codepage(); break; // CODEPAGE [ 2345] + case 0x43: rXFBfr.ReadXF( maStrm ); break; + case 0x44: Ixfe(); break; // IXFE [ 2 ] + } + } + break; + // ---------------------------------------------------------------- + case Z_Biff3: // ---------------------------------- Z_Biff3 - + { + switch( nOpcode ) + { + // skip chart substream + case EXC_ID2_BOF: + case EXC_ID3_BOF: + case EXC_ID4_BOF: + case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break; + + case EXC_ID2_DIMENSIONS: + case EXC_ID3_DIMENSIONS: ReadDimensions(); break; + case EXC_ID2_BLANK: + case EXC_ID3_BLANK: ReadBlank(); break; + case EXC_ID2_INTEGER: ReadInteger(); break; + case EXC_ID2_NUMBER: + case EXC_ID3_NUMBER: ReadNumber(); break; + case EXC_ID2_LABEL: + case EXC_ID3_LABEL: ReadLabel(); break; + case EXC_ID2_BOOLERR: + case EXC_ID3_BOOLERR: ReadBoolErr(); break; + case EXC_ID_RK: ReadRk(); break; + + case 0x0A: // EOF [ 2345] + rNumFmtBfr.CreateScFormats(); + Eof(); + eAkt = Z_Ende; + break; + case 0x14: + case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break; + case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345] + case 0x1A: + case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break; + case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break; + case 0x1D: rTabViewSett.ReadSelection( maStrm ); break; + case 0x1E: rNumFmtBfr.ReadFormat( maStrm ); break; + case 0x22: Rec1904(); break; // 1904 [ 2345] + case 0x26: + case 0x27: + case 0x28: + case 0x29: rPageSett.ReadMargin( maStrm ); break; + case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break; + case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break; + case 0x2F: // FILEPASS [ 2345] + eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm ); + if( eLastErr != ERRCODE_NONE ) + eAkt = Z_Ende; + break; + case EXC_ID_FILESHARING: ReadFileSharing(); break; + case 0x41: rTabViewSett.ReadPane( maStrm ); break; + case 0x42: Codepage(); break; // CODEPAGE [ 2345] + case 0x56: Builtinfmtcnt(); break; // BUILTINFMTCNT[ 34 ] + case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break; + case 0x7D: Colinfo(); break; // COLINFO [ 345] + case 0x8C: Country(); break; // COUNTRY [ 345] + case 0x92: rPal.ReadPalette( maStrm ); break; + case 0x0206: Formula3(); break; // FORMULA [ 3 ] + case 0x0208: Row34(); break; // ROW [ 34 ] + case 0x0218: rNameMgr.ReadName( maStrm ); break; + case 0x0221: Array34(); break; // ARRAY [ 34 ] + case 0x0223: Externname34(); break; // EXTERNNAME [ 34 ] + case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345] + case 0x0231: rFontBfr.ReadFont( maStrm ); break; + case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break; + case 0x0243: rXFBfr.ReadXF( maStrm ); break; + case 0x0293: rXFBfr.ReadStyle( maStrm ); break; + } + } + break; + // ---------------------------------------------------------------- + case Z_Biff4: // ---------------------------------- Z_Biff4 - + { + switch( nOpcode ) + { + // skip chart substream + case EXC_ID2_BOF: + case EXC_ID3_BOF: + case EXC_ID4_BOF: + case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break; + + case EXC_ID2_DIMENSIONS: + case EXC_ID3_DIMENSIONS: ReadDimensions(); break; + case EXC_ID2_BLANK: + case EXC_ID3_BLANK: ReadBlank(); break; + case EXC_ID2_INTEGER: ReadInteger(); break; + case EXC_ID2_NUMBER: + case EXC_ID3_NUMBER: ReadNumber(); break; + case EXC_ID2_LABEL: + case EXC_ID3_LABEL: ReadLabel(); break; + case EXC_ID2_BOOLERR: + case EXC_ID3_BOOLERR: ReadBoolErr(); break; + case EXC_ID_RK: ReadRk(); break; + + case 0x0A: // EOF [ 2345] + rNumFmtBfr.CreateScFormats(); + Eof(); + eAkt = Z_Ende; + break; + case 0x12: SheetProtect(); break; // SHEET PROTECTION + case 0x14: + case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break; + case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345] + case 0x1A: + case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break; + case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break; + case 0x1D: rTabViewSett.ReadSelection( maStrm ); break; + case 0x22: Rec1904(); break; // 1904 [ 2345] + case 0x26: + case 0x27: + case 0x28: + case 0x29: rPageSett.ReadMargin( maStrm ); break; + case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break; + case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break; + case 0x2F: // FILEPASS [ 2345] + eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm ); + if( eLastErr != ERRCODE_NONE ) + eAkt = Z_Ende; + break; + case EXC_ID_FILESHARING: ReadFileSharing(); break; + case 0x41: rTabViewSett.ReadPane( maStrm ); break; + case 0x42: Codepage(); break; // CODEPAGE [ 2345] + case 0x55: DefColWidth(); break; + case 0x56: Builtinfmtcnt(); break; // BUILTINFMTCNT[ 34 ] + case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break; + case 0x7D: Colinfo(); break; // COLINFO [ 345] + case 0x8C: Country(); break; // COUNTRY [ 345] + case 0x92: rPal.ReadPalette( maStrm ); break; + case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45] + case 0xA1: rPageSett.ReadSetup( maStrm ); break; + case 0x0208: Row34(); break; // ROW [ 34 ] + case 0x0218: rNameMgr.ReadName( maStrm ); break; + case 0x0221: Array34(); break; // ARRAY [ 34 ] + case 0x0223: Externname34(); break; // EXTERNNAME [ 34 ] + case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345] + case 0x0231: rFontBfr.ReadFont( maStrm ); break; + case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break; + case 0x0406: Formula4(); break; // FORMULA [ 4 ] + case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break; + case 0x0443: rXFBfr.ReadXF( maStrm ); break; + case 0x0293: rXFBfr.ReadStyle( maStrm ); break; + } + } + break; + // ---------------------------------------------------------------- + case Z_Biff4W: // --------------------------------- Z_Biff4W - + { + switch( nOpcode ) + { + case 0x0A: // EOF [ 2345] + eAkt = Z_Ende; + break; + case 0x12: DocProtect(); break; // PROTECT [ 5] + case 0x2F: // FILEPASS [ 2345] + eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm ); + if( eLastErr != ERRCODE_NONE ) + eAkt = Z_Ende; + break; + case EXC_ID_FILESHARING: ReadFileSharing(); break; + case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345] + case 0x42: Codepage(); break; // CODEPAGE [ 2345] + case 0x55: DefColWidth(); break; + case 0x56: Builtinfmtcnt(); break; // BUILTINFMTCNT[ 34 ] + case 0x8C: Country(); break; // COUNTRY [ 345] + case 0x8F: Bundleheader(); break; // BUNDLEHEADER [ 4 ] + case 0x92: rPal.ReadPalette( maStrm ); break; + case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45] + case 0x0218: rNameMgr.ReadName( maStrm ); break; + case 0x0223: Externname34(); break; // EXTERNNAME [ 34 ] + case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345] + case 0x0231: rFontBfr.ReadFont( maStrm ); break; + case 0x0409: // BOF [ 4 ] + Bof4(); + if( pExcRoot->eDateiTyp == Biff4 ) + { + eAkt = Z_Biff4T; + NeueTabelle(); + } + else + eAkt = Z_Ende; + break; + case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break; + case 0x0443: rXFBfr.ReadXF( maStrm ); break; + case 0x0293: rXFBfr.ReadStyle( maStrm ); break; + } + + } + break; + // ---------------------------------------------------------------- + case Z_Biff4T: // --------------------------------- Z_Biff4T - + { + switch( nOpcode ) + { + // skip chart substream + case EXC_ID2_BOF: + case EXC_ID3_BOF: + case EXC_ID4_BOF: + case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break; + + case EXC_ID2_DIMENSIONS: + case EXC_ID3_DIMENSIONS: ReadDimensions(); break; + case EXC_ID2_BLANK: + case EXC_ID3_BLANK: ReadBlank(); break; + case EXC_ID2_INTEGER: ReadInteger(); break; + case EXC_ID2_NUMBER: + case EXC_ID3_NUMBER: ReadNumber(); break; + case EXC_ID2_LABEL: + case EXC_ID3_LABEL: ReadLabel(); break; + case EXC_ID2_BOOLERR: + case EXC_ID3_BOOLERR: ReadBoolErr(); break; + case EXC_ID_RK: ReadRk(); break; + + case 0x0A: // EOF [ 2345] + Eof(); + eAkt = Z_Biff4E; + break; + case 0x12: SheetProtect(); break; // SHEET PROTECTION + case 0x14: + case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break; + case 0x1A: + case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break; + case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break; + case 0x1D: rTabViewSett.ReadSelection( maStrm ); break; + case 0x2F: // FILEPASS [ 2345] + eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm ); + if( eLastErr != ERRCODE_NONE ) + eAkt = Z_Ende; + break; + case 0x41: rTabViewSett.ReadPane( maStrm ); break; + case 0x42: Codepage(); break; // CODEPAGE [ 2345] + case 0x55: DefColWidth(); break; + case 0x56: Builtinfmtcnt(); break; // BUILTINFMTCNT[ 34 ] + case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break; + case 0x7D: Colinfo(); break; // COLINFO [ 345] + case 0x8C: Country(); break; // COUNTRY [ 345] + case 0x8F: Bundleheader(); break; // BUNDLEHEADER [ 4 ] + case 0x92: rPal.ReadPalette( maStrm ); break; + case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45] + case 0xA1: rPageSett.ReadSetup( maStrm ); break; + case 0x0208: Row34(); break; // ROW [ 34 ] + case 0x0218: rNameMgr.ReadName( maStrm ); break; + case 0x0221: Array34(); break; + case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345] + case 0x0231: rFontBfr.ReadFont( maStrm ); break; + case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break; + case 0x0406: Formula4(); break; + case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break; + case 0x0443: rXFBfr.ReadXF( maStrm ); break; + case 0x0293: rXFBfr.ReadStyle( maStrm ); break; + } + + } + break; + // ---------------------------------------------------------------- + case Z_Biff4E: // --------------------------------- Z_Biff4E - + { + switch( nOpcode ) + { + case 0x0A: // EOF [ 2345] + eAkt = Z_Ende; + break; + case 0x8F: break; // BUNDLEHEADER [ 4 ] + case 0x0409: // BOF [ 4 ] + Bof4(); + NeueTabelle(); + if( pExcRoot->eDateiTyp == Biff4 ) + { + eAkt = Z_Biff4T; + } + else + { + ePrev = eAkt; + eAkt = Z_Biffn0; + } + break; + } + + } + break; + case Z_Biff5WPre: // ------------------------------ Z_Biff5WPre - + { + switch( nOpcode ) + { + case 0x0A: // EOF [ 2345] + eAkt = Z_Biff5W; + aIn.SeekGlobalPosition(); // und zurueck an alte Position + break; + case 0x12: DocProtect(); break; // PROTECT [ 5] + case 0x2F: // FILEPASS [ 2345] + eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm ); + if( eLastErr != ERRCODE_NONE ) + eAkt = Z_Ende; + break; + case EXC_ID_FILESHARING: ReadFileSharing(); break; + case 0x3D: Window1(); break; + case 0x42: Codepage(); break; // CODEPAGE [ 2345] + case 0x85: Boundsheet(); break; // BOUNDSHEET [ 5] + case 0x8C: Country(); break; // COUNTRY [ 345] + // PALETTE follows XFs, but already needed while reading the XFs + case 0x92: rPal.ReadPalette( maStrm ); break; + } + } + break; + case Z_Biff5W: // --------------------------------- Z_Biff5W - + { + switch( nOpcode ) + { + case 0x0A: // EOF [ 2345] + rNumFmtBfr.CreateScFormats(); + rXFBfr.CreateUserStyles(); + eAkt = Z_Biff5E; + break; + case 0x18: rNameMgr.ReadName( maStrm ); break; + case 0x1E: rNumFmtBfr.ReadFormat( maStrm ); break; + case 0x22: Rec1904(); break; // 1904 [ 2345] + case 0x31: rFontBfr.ReadFont( maStrm ); break; + case 0x56: Builtinfmtcnt(); break; // BUILTINFMTCNT[ 34 ] + case 0x8D: Hideobj(); break; // HIDEOBJ [ 345] + case 0xDE: Olesize(); break; + case 0xE0: rXFBfr.ReadXF( maStrm ); break; + case 0x0293: rXFBfr.ReadStyle( maStrm ); break; + case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break; + } + + } + break; + // ---------------------------------------------------------------- + case Z_Biff5TPre: // ------------------------------- Z_Biff5Pre - + { + if( nOpcode == 0x0809 ) + nBofLevel++; + else if( (nOpcode == 0x000A) && nBofLevel ) + nBofLevel--; + else if( !nBofLevel ) // don't read chart records + { + switch( nOpcode ) + { + case EXC_ID2_DIMENSIONS: + case EXC_ID3_DIMENSIONS: ReadDimensions(); break; + case 0x08: Row25(); break; // ROW [ 2 5] + case 0x0A: // EOF [ 2345] + eAkt = Z_Biff5T; + aIn.SeekGlobalPosition(); // und zurueck an alte Position + break; + case 0x12: SheetProtect(); break; // SHEET PROTECTION + case 0x1A: + case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break; + case 0x1D: rTabViewSett.ReadSelection( maStrm ); break; + case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345] + case 0x21: Array25(); break; // ARRAY [ 2 5] + case 0x23: Externname25(); break; // EXTERNNAME [ 2 5] + case 0x41: rTabViewSett.ReadPane( maStrm ); break; + case 0x42: Codepage(); break; // CODEPAGE [ 2345] + case 0x55: DefColWidth(); break; + case 0x7D: Colinfo(); break; // COLINFO [ 345] + case 0x81: Wsbool(); break; // WSBOOL [ 2345] + case 0x8C: Country(); break; // COUNTRY [ 345] + case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45] + case 0x0208: Row34(); break; // ROW [ 34 ] + case 0x0221: Array34(); break; // ARRAY [ 34 ] + case 0x0223: Externname34(); break; // EXTERNNAME [ 34 ] + case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345] + case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break; + case 0x04BC: Shrfmla(); break; // SHRFMLA [ 5] + } + } + } + break; + // ---------------------------------------------------------------- + case Z_Biff5T: // --------------------------------- Z_Biff5T - + { + switch( nOpcode ) + { + case EXC_ID2_BLANK: + case EXC_ID3_BLANK: ReadBlank(); break; + case EXC_ID2_INTEGER: ReadInteger(); break; + case EXC_ID2_NUMBER: + case EXC_ID3_NUMBER: ReadNumber(); break; + case EXC_ID2_LABEL: + case EXC_ID3_LABEL: ReadLabel(); break; + case EXC_ID2_BOOLERR: + case EXC_ID3_BOOLERR: ReadBoolErr(); break; + case EXC_ID_RK: ReadRk(); break; + + case 0x0006: + case 0x0206: + case 0x0406: Formula25(); break; + case 0x0A: Eof(); eAkt = Z_Biff5E; break; + case 0x14: + case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break; + case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345] + case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break; + case 0x1D: rTabViewSett.ReadSelection( maStrm ); break; + case 0x23: Externname25(); break; // EXTERNNAME [ 2 5] + case 0x26: + case 0x27: + case 0x28: + case 0x29: rPageSett.ReadMargin( maStrm ); break; + case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break; + case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break; + case 0x2F: // FILEPASS [ 2345] + eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm ); + if( eLastErr != ERRCODE_NONE ) + eAkt = Z_Ende; + break; + case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break; + case 0x83: + case 0x84: rPageSett.ReadCenter( maStrm ); break; + case 0xA0: rTabViewSett.ReadScl( maStrm ); break; + case 0xA1: rPageSett.ReadSetup( maStrm ); break; + case 0xBD: Mulrk(); break; // MULRK [ 5] + case 0xBE: Mulblank(); break; // MULBLANK [ 5] + case 0xD6: Rstring(); break; // RSTRING [ 5] + case 0x00E5: Cellmerging(); break; // #i62300# + case 0x0236: TableOp(); break; // TABLE [ 5] + case 0x0809: // BOF [ 5] + XclTools::SkipSubStream( maStrm ); + break; + } + + } + break; + // ---------------------------------------------------------------- + case Z_Biff5E: // --------------------------------- Z_Biff5E - + { + switch( nOpcode ) + { + case 0x0809: // BOF [ 5] + Bof5(); + NeueTabelle(); + switch( pExcRoot->eDateiTyp ) + { + case Biff5: + case Biff5M4: + eAkt = Z_Biff5TPre; // Shrfmla Prefetch, Row-Prefetch + nBofLevel = 0; + aIn.StoreGlobalPosition(); // und Position merken + break; + case Biff5C: // chart sheet + GetCurrSheetDrawing().ReadTabChart( maStrm ); + Eof(); + GetTracer().TraceChartOnlySheet(); + break; + case Biff5V: + default: + pD->SetVisible( GetCurrScTab(), FALSE ); + ePrev = eAkt; + eAkt = Z_Biffn0; + } + DBG_ASSERT( pExcRoot->eDateiTyp != Biff5W, + "+ImportExcel::Read(): Doppel-Whopper-Workbook!" ); + + break; + } + + } + break; + case Z_Biffn0: // --------------------------------- Z_Biffn0 - + { + switch( nOpcode ) + { + case 0x0A: // EOF [ 2345] + eAkt = ePrev; + IncCurrScTab(); + break; + } + + } + break; + // ---------------------------------------------------------------- + case Z_Ende: // ----------------------------------- Z_Ende - + DBG_ERROR( "*ImportExcel::Read(): Not possible state!" ); + break; + default: DBG_ERROR( "-ImportExcel::Read(): Zustand vergessen!" ); + } + } + + if( eLastErr == eERR_OK ) + { + pProgress.reset(); + + AdjustRowHeight(); + PostDocLoad(); + + pD->CalcAfterLoad(); + + const XclImpAddressConverter& rAddrConv = GetAddressConverter(); + if( rAddrConv.IsTabTruncated() ) + eLastErr = SCWARN_IMPORT_SHEET_OVERFLOW; + else if( bTabTruncated || rAddrConv.IsRowTruncated() ) + eLastErr = SCWARN_IMPORT_ROW_OVERFLOW; + else if( rAddrConv.IsColTruncated() ) + eLastErr = SCWARN_IMPORT_COLUMN_OVERFLOW; + } + + return eLastErr; +} + + +//___________________________________________________________________ + +FltError ImportExcel8::Read( void ) +{ +#if EXC_INCL_DUMPER + { + Biff8RecDumper aDumper( GetRoot(), TRUE ); + if( aDumper.Dump( aIn ) ) + return ERRCODE_ABORT; + } +#endif + // read the entire BIFF8 stream + // don't look too close - this stuff seriously needs to be reworked + + XclImpPageSettings& rPageSett = GetPageSettings(); + XclImpTabViewSettings& rTabViewSett = GetTabViewSettings(); + XclImpPalette& rPal = GetPalette(); + XclImpFontBuffer& rFontBfr = GetFontBuffer(); + XclImpNumFmtBuffer& rNumFmtBfr = GetNumFmtBuffer(); + XclImpXFBuffer& rXFBfr = GetXFBuffer(); + XclImpSst& rSst = GetSst(); + XclImpTabInfo& rTabInfo = GetTabInfo(); + XclImpNameManager& rNameMgr = GetNameManager(); + XclImpLinkManager& rLinkMgr = GetLinkManager(); + XclImpObjectManager& rObjMgr = GetObjectManager(); + // call to GetCurrSheetDrawing() cannot be cached (changes in new sheets) + XclImpCondFormatManager& rCondFmtMgr = GetCondFormatManager(); + XclImpPivotTableManager& rPTableMgr = GetPivotTableManager(); + XclImpWebQueryBuffer& rWQBfr = GetWebQueryBuffer(); + + bool bInUserView = false; // true = In USERSVIEW(BEGIN|END) record block. + + enum XclImpReadState + { + EXC_STATE_BEFORE_GLOBALS, /// Before workbook globals (wait for initial BOF). + EXC_STATE_GLOBALS_PRE, /// Prefetch for workbook globals. + EXC_STATE_GLOBALS, /// Workbook globals. + EXC_STATE_BEFORE_SHEET, /// Before worksheet (wait for new worksheet BOF). + EXC_STATE_SHEET_PRE, /// Prefetch for worksheet. + EXC_STATE_SHEET, /// Worksheet. + EXC_STATE_END /// Stop reading. + }; + + XclImpReadState eAkt = EXC_STATE_BEFORE_GLOBALS; + + FltError eLastErr = eERR_OK; + + ::std::auto_ptr< ScfSimpleProgressBar > pProgress( new ScfSimpleProgressBar( + aIn.GetSvStreamSize(), GetDocShell(), STR_LOAD_DOC ) ); + + /* #i104057# Need to track a base position for progress bar calculation, + because sheet substreams may not be in order of sheets. */ + sal_Size nProgressBasePos = 0; + sal_Size nProgressBaseSize = 0; + + while( eAkt != EXC_STATE_END ) + { + if( eAkt == EXC_STATE_BEFORE_SHEET ) + { + sal_uInt16 nScTab = GetCurrScTab(); + if( nScTab < maSheetOffsets.size() ) + { + nProgressBaseSize += (aIn.GetSvStreamPos() - nProgressBasePos); + nProgressBasePos = maSheetOffsets[ nScTab ]; + aIn.StartNextRecord( nProgressBasePos ); + } + else + eAkt = EXC_STATE_END; + } + else + aIn.StartNextRecord(); + + if( !aIn.IsValid() ) + { + // #124240# #i63591# finalize table if EOF is missing + switch( eAkt ) + { + case EXC_STATE_SHEET_PRE: + eAkt = EXC_STATE_SHEET; + aIn.SeekGlobalPosition(); + continue; // next iteration in while loop +// break; // unxsols warning: statement unreachable + case EXC_STATE_SHEET: + Eof(); + eAkt = EXC_STATE_END; + break; + default: + eAkt = EXC_STATE_END; + } + } + + if( eAkt == EXC_STATE_END ) + break; + + if( eAkt != EXC_STATE_SHEET_PRE && eAkt != EXC_STATE_GLOBALS_PRE ) + pProgress->ProgressAbs( nProgressBaseSize + aIn.GetSvStreamPos() - nProgressBasePos ); + + sal_uInt16 nRecId = aIn.GetRecId(); + + /* #i39464# Ignore records between USERSVIEWBEGIN and USERSVIEWEND + completely (user specific view settings). Otherwise view settings + and filters are loaded multiple times, which at least causes + problems in auto-filters. */ + switch( nRecId ) + { + case EXC_ID_USERSVIEWBEGIN: + DBG_ASSERT( !bInUserView, "ImportExcel8::Read - nested user view settings" ); + bInUserView = true; + break; + case EXC_ID_USERSVIEWEND: + DBG_ASSERT( bInUserView, "ImportExcel8::Read - not in user view settings" ); + bInUserView = false; + break; + } + + if( !bInUserView ) switch( eAkt ) + { + // ---------------------------------------------------------------- + // before workbook globals: wait for initial workbook globals BOF + case EXC_STATE_BEFORE_GLOBALS: + { + if( nRecId == EXC_ID5_BOF ) + { + DBG_ASSERT( GetBiff() == EXC_BIFF8, "ImportExcel8::Read - wrong BIFF version" ); + Bof5(); + if( pExcRoot->eDateiTyp == Biff8W ) + { + eAkt = EXC_STATE_GLOBALS_PRE; + maStrm.StoreGlobalPosition(); + nBdshtTab = 0; + } + else if( pExcRoot->eDateiTyp == Biff8 ) + { + // #i62752# possible to have BIFF8 sheet without globals + NeueTabelle(); + eAkt = EXC_STATE_SHEET_PRE; // Shrfmla Prefetch, Row-Prefetch + aIn.StoreGlobalPosition(); + } + } + } + break; + + // ---------------------------------------------------------------- + // prefetch for workbook globals + case EXC_STATE_GLOBALS_PRE: + { + switch( nRecId ) + { + case EXC_ID_EOF: + case EXC_ID_EXTSST: + /* #i56376# evil hack: if EOF for globals is missing, + simulate it. This hack works only for the bugdoc + given in the issue, where the sheet substreams + start directly after the EXTSST record. A future + implementation should be more robust against + missing EOFs. */ + if( (nRecId == EXC_ID_EOF) || + ((nRecId == EXC_ID_EXTSST) && (maStrm.GetNextRecId() == EXC_ID5_BOF)) ) + { + eAkt = EXC_STATE_GLOBALS; + aIn.SeekGlobalPosition(); + } + break; + case 0x12: DocProtect(); break; // PROTECT [ 5678] + case 0x13: DocPasssword(); break; + case 0x19: WinProtection(); break; + case 0x2F: // FILEPASS [ 2345 ] + eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm ); + if( eLastErr != ERRCODE_NONE ) + eAkt = EXC_STATE_END; + break; + case EXC_ID_FILESHARING: ReadFileSharing(); break; + case 0x3D: Window1(); break; + case 0x42: Codepage(); break; // CODEPAGE [ 2345 ] + case 0x85: Boundsheet(); break; // BOUNDSHEET [ 5 ] + case 0x8C: Country(); break; // COUNTRY [ 345 ] + + // PALETTE follows XFs, but already needed while reading the XFs + case EXC_ID_PALETTE: rPal.ReadPalette( maStrm ); break; + } + } + break; + + // ---------------------------------------------------------------- + // workbook globals + case EXC_STATE_GLOBALS: + { + switch( nRecId ) + { + case EXC_ID_EOF: + case EXC_ID_EXTSST: + /* #i56376# evil hack: if EOF for globals is missing, + simulate it. This hack works only for the bugdoc + given in the issue, where the sheet substreams + start directly after the EXTSST record. A future + implementation should be more robust against + missing EOFs. */ + if( (nRecId == EXC_ID_EOF) || + ((nRecId == EXC_ID_EXTSST) && (maStrm.GetNextRecId() == EXC_ID5_BOF)) ) + { + rNumFmtBfr.CreateScFormats(); + rXFBfr.CreateUserStyles(); + rPTableMgr.ReadPivotCaches( maStrm ); + eAkt = EXC_STATE_BEFORE_SHEET; + } + break; + case 0x0E: Precision(); break; // PRECISION + case 0x22: Rec1904(); break; // 1904 [ 2345 ] + case 0x56: Builtinfmtcnt(); break; // BUILTINFMTCNT[ 34 ] + case 0x8D: Hideobj(); break; // HIDEOBJ [ 345 ] + case 0xD3: SetHasBasic(); break; + case 0xDE: Olesize(); break; + + case EXC_ID_CODENAME: ReadCodeName( aIn, true ); break; + case EXC_ID_USESELFS: ReadUsesElfs(); break; + + case EXC_ID2_FONT: rFontBfr.ReadFont( maStrm ); break; + case EXC_ID4_FORMAT: rNumFmtBfr.ReadFormat( maStrm ); break; + case EXC_ID5_XF: rXFBfr.ReadXF( maStrm ); break; + case EXC_ID_STYLE: rXFBfr.ReadStyle( maStrm ); break; + + case EXC_ID_SST: rSst.ReadSst( maStrm ); break; + case EXC_ID_TABID: rTabInfo.ReadTabid( maStrm ); break; + case EXC_ID_NAME: rNameMgr.ReadName( maStrm ); break; + + case EXC_ID_EXTERNSHEET: rLinkMgr.ReadExternsheet( maStrm ); break; + case EXC_ID_SUPBOOK: rLinkMgr.ReadSupbook( maStrm ); break; + case EXC_ID_XCT: rLinkMgr.ReadXct( maStrm ); break; + case EXC_ID_CRN: rLinkMgr.ReadCrn( maStrm ); break; + case EXC_ID_EXTERNNAME: rLinkMgr.ReadExternname( maStrm, pFormConv ); break; + + case EXC_ID_MSODRAWINGGROUP:rObjMgr.ReadMsoDrawingGroup( maStrm ); break; + + case EXC_ID_SXIDSTM: rPTableMgr.ReadSxidstm( maStrm ); break; + case EXC_ID_SXVS: rPTableMgr.ReadSxvs( maStrm ); break; + case EXC_ID_DCONREF: rPTableMgr.ReadDconref( maStrm ); break; + } + + } + break; + + // ---------------------------------------------------------------- + // before worksheet: wait for new worksheet BOF + case EXC_STATE_BEFORE_SHEET: + { + if( nRecId == EXC_ID5_BOF ) + { + // #94191# import only 256 sheets + if( GetCurrScTab() > GetScMaxPos().Tab() ) + { + XclTools::SkipSubStream( maStrm ); + // #i29930# show warning box + GetAddressConverter().CheckScTab( GetCurrScTab(), true ); + eAkt = EXC_STATE_END; + } + else + { + Bof5(); + NeueTabelle(); + switch( pExcRoot->eDateiTyp ) + { + case Biff8: // worksheet + case Biff8M4: // macro sheet + eAkt = EXC_STATE_SHEET_PRE; // Shrfmla Prefetch, Row-Prefetch + aIn.StoreGlobalPosition(); + break; + case Biff8C: // chart sheet + GetCurrSheetDrawing().ReadTabChart( maStrm ); + Eof(); + GetTracer().TraceChartOnlySheet(); + break; + case Biff8W: // workbook + DBG_ERRORFILE( "ImportExcel8::Read - double workbook globals" ); + // run through + case Biff8V: // VB module + default: + // TODO: do not create a sheet in the Calc document + pD->SetVisible( GetCurrScTab(), FALSE ); + XclTools::SkipSubStream( maStrm ); + IncCurrScTab(); + } + } + } + } + break; + + // ---------------------------------------------------------------- + // prefetch for worksheet + case EXC_STATE_SHEET_PRE: + { + switch( nRecId ) + { + // skip chart substream + case EXC_ID2_BOF: + case EXC_ID3_BOF: + case EXC_ID4_BOF: + case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break; + + case EXC_ID_WINDOW2: rTabViewSett.ReadWindow2( maStrm, false );break; + case EXC_ID_SCL: rTabViewSett.ReadScl( maStrm ); break; + case EXC_ID_PANE: rTabViewSett.ReadPane( maStrm ); break; + case EXC_ID_SELECTION: rTabViewSett.ReadSelection( maStrm ); break; + + case EXC_ID2_DIMENSIONS: + case EXC_ID3_DIMENSIONS: ReadDimensions(); break; + + case EXC_ID_CODENAME: ReadCodeName( aIn, false ); break; + + case 0x0A: // EOF [ 2345 ] + eAkt = EXC_STATE_SHEET; + aIn.SeekGlobalPosition(); // und zurueck an alte Position + break; + case 0x12: SheetProtect(); break; + case 0x13: SheetPassword(); break; + case 0x42: Codepage(); break; // CODEPAGE [ 2345 ] + case 0x55: DefColWidth(); break; + case 0x7D: Colinfo(); break; // COLINFO [ 345 ] + case 0x81: Wsbool(); break; // WSBOOL [ 2345 ] + case 0x8C: Country(); break; // COUNTRY [ 345 ] + case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45 ] + case 0x9B: FilterMode(); break; // FILTERMODE + case 0x9D: AutoFilterInfo(); break;// AUTOFILTERINFO + case 0x9E: AutoFilter(); break; // AUTOFILTER + case 0x0208: Row34(); break; // ROW [ 34 ] + case 0x0021: + case 0x0221: Array34(); break; // ARRAY [ 34 ] + case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345 ] + case 0x04BC: Shrfmla(); break; // SHRFMLA [ 5 ] + case 0x0867: SheetProtection(); break; // SHEETPROTECTION + } + } + break; + + // ---------------------------------------------------------------- + // worksheet + case EXC_STATE_SHEET: + { + switch( nRecId ) + { + // skip unknown substreams + case EXC_ID2_BOF: + case EXC_ID3_BOF: + case EXC_ID4_BOF: + case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break; + + case EXC_ID_EOF: Eof(); eAkt = EXC_STATE_BEFORE_SHEET; break; + + case EXC_ID2_BLANK: + case EXC_ID3_BLANK: ReadBlank(); break; + case EXC_ID2_INTEGER: ReadInteger(); break; + case EXC_ID2_NUMBER: + case EXC_ID3_NUMBER: ReadNumber(); break; + case EXC_ID2_LABEL: + case EXC_ID3_LABEL: ReadLabel(); break; + case EXC_ID2_BOOLERR: + case EXC_ID3_BOOLERR: ReadBoolErr(); break; + case EXC_ID_RK: ReadRk(); break; + + case 0x0006: + case 0x0206: + case 0x0406: Formula25(); break; // FORMULA [ 2 5 ] + case 0x000C: Calccount(); break; // CALCCOUNT + case 0x0010: Delta(); break; // DELTA + case 0x0011: Iteration(); break; // ITERATION + case 0x007E: + case 0x00AE: Scenman(); break; // SCENMAN + case 0x00AF: Scenario(); break; // SCENARIO + case 0x00BD: Mulrk(); break; // MULRK [ 5 ] + case 0x00BE: Mulblank(); break; // MULBLANK [ 5 ] + case 0x00D6: Rstring(); break; // RSTRING [ 5 ] + case 0x00E5: Cellmerging(); break; // CELLMERGING + case 0x00FD: Labelsst(); break; // LABELSST [ 8 ] + case 0x0236: TableOp(); break; // TABLE + + case EXC_ID_HORPAGEBREAKS: + case EXC_ID_VERPAGEBREAKS: rPageSett.ReadPageBreaks( maStrm ); break; + case EXC_ID_HEADER: + case EXC_ID_FOOTER: rPageSett.ReadHeaderFooter( maStrm ); break; + case EXC_ID_LEFTMARGIN: + case EXC_ID_RIGHTMARGIN: + case EXC_ID_TOPMARGIN: + case EXC_ID_BOTTOMMARGIN: rPageSett.ReadMargin( maStrm ); break; + case EXC_ID_PRINTHEADERS: rPageSett.ReadPrintHeaders( maStrm ); break; + case EXC_ID_PRINTGRIDLINES: rPageSett.ReadPrintGridLines( maStrm ); break; + case EXC_ID_HCENTER: + case EXC_ID_VCENTER: rPageSett.ReadCenter( maStrm ); break; + case EXC_ID_SETUP: rPageSett.ReadSetup( maStrm ); break; + case EXC_ID8_IMGDATA: rPageSett.ReadImgData( maStrm ); break; + + case EXC_ID_MSODRAWING: GetCurrSheetDrawing().ReadMsoDrawing( maStrm ); break; + // #i61786# weird documents: OBJ without MSODRAWING -> read in BIFF5 format + case EXC_ID_OBJ: GetCurrSheetDrawing().ReadObj( maStrm ); break; + case EXC_ID_NOTE: GetCurrSheetDrawing().ReadNote( maStrm ); break; + + case EXC_ID_HLINK: XclImpHyperlink::ReadHlink( maStrm ); break; + case EXC_ID_LABELRANGES: XclImpLabelranges::ReadLabelranges( maStrm ); break; + + case EXC_ID_CONDFMT: rCondFmtMgr.ReadCondfmt( maStrm ); break; + case EXC_ID_CF: rCondFmtMgr.ReadCF( maStrm ); break; + + case EXC_ID_DVAL: XclImpValidation::ReadDval( maStrm ); break; + case EXC_ID_DV: XclImpValidation::ReadDV( maStrm ); break; + + case EXC_ID_QSI: rWQBfr.ReadQsi( maStrm ); break; + case EXC_ID_WQSTRING: rWQBfr.ReadWqstring( maStrm ); break; + case EXC_ID_PQRY: rWQBfr.ReadParamqry( maStrm ); break; + case EXC_ID_WQSETT: rWQBfr.ReadWqsettings( maStrm ); break; + case EXC_ID_WQTABLES: rWQBfr.ReadWqtables( maStrm ); break; + + case EXC_ID_SXVIEW: rPTableMgr.ReadSxview( maStrm ); break; + case EXC_ID_SXVD: rPTableMgr.ReadSxvd( maStrm ); break; + case EXC_ID_SXVI: rPTableMgr.ReadSxvi( maStrm ); break; + case EXC_ID_SXIVD: rPTableMgr.ReadSxivd( maStrm ); break; + case EXC_ID_SXPI: rPTableMgr.ReadSxpi( maStrm ); break; + case EXC_ID_SXDI: rPTableMgr.ReadSxdi( maStrm ); break; + case EXC_ID_SXVDEX: rPTableMgr.ReadSxvdex( maStrm ); break; + case EXC_ID_SXEX: rPTableMgr.ReadSxex( maStrm ); break; + case EXC_ID_SHEETEXT: rTabViewSett.ReadTabBgColor( maStrm, rPal ); break; + case EXC_ID_SXVIEWEX9: rPTableMgr.ReadSxViewEx9( maStrm ); break; + } + } + break; + + // ---------------------------------------------------------------- + default:; + } + } + + if( eLastErr == eERR_OK ) + { + // #i45843# Convert pivot tables before calculation, so they are available + // for the GETPIVOTDATA function. + if( GetBiff() == EXC_BIFF8 ) + GetPivotTableManager().ConvertPivotTables(); + + pProgress.reset(); + + if (pD->IsAdjustHeightEnabled()) + AdjustRowHeight(); + + PostDocLoad(); + + pD->CalcAfterLoad(); + + // import change tracking data + XclImpChangeTrack aImpChTr( GetRoot(), maStrm ); + aImpChTr.Apply(); + + const XclImpAddressConverter& rAddrConv = GetAddressConverter(); + if( rAddrConv.IsTabTruncated() ) + eLastErr = SCWARN_IMPORT_SHEET_OVERFLOW; + else if( bTabTruncated || rAddrConv.IsRowTruncated() ) + eLastErr = SCWARN_IMPORT_ROW_OVERFLOW; + else if( rAddrConv.IsColTruncated() ) + eLastErr = SCWARN_IMPORT_COLUMN_OVERFLOW; + + if( GetBiff() == EXC_BIFF8 ) + GetPivotTableManager().MaybeRefreshPivotTables(); + } + + return eLastErr; +} + +//___________________________________________________________________ + diff --git a/sc/source/filter/excel/tokstack.cxx b/sc/source/filter/excel/tokstack.cxx new file mode 100644 index 000000000000..b61a844d02a5 --- /dev/null +++ b/sc/source/filter/excel/tokstack.cxx @@ -0,0 +1,888 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + + +#ifndef PCH +#include <string.h> +#endif + +#include "compiler.hxx" +#include "tokstack.hxx" +#include "global.hxx" +#include "scmatrix.hxx" + +#include <stdio.h> // printf + +const UINT16 TokenPool::nScTokenOff = 8192; + + +TokenStack::TokenStack( UINT16 nNewSize ) +{ + pStack = new TokenId[ nNewSize ]; + + Reset(); + nSize = nNewSize; +} + + +TokenStack::~TokenStack() +{ + delete[] pStack; +} + + + + +//------------------------------------------------------------------------ + +// !ACHTUNG!: nach Aussen hin beginnt die Nummerierung mit 1! +// !ACHTUNG!: SC-Token werden mit einem Offset nScTokenOff abgelegt +// -> Unterscheidung von anderen Token + + +TokenPool::TokenPool( void ) +{ + UINT16 nLauf = nScTokenOff; + + // Sammelstelle fuer Id-Folgen + nP_Id = 256; + pP_Id = new UINT16[ nP_Id ]; + + // Sammelstelle fuer Ids + nElement = 32; + pElement = new UINT16[ nElement ]; + pType = new E_TYPE[ nElement ]; + pSize = new UINT16[ nElement ]; + nP_IdLast = 0; + + // Sammelstelle fuer Strings + nP_Str = 4; + ppP_Str = new String *[ nP_Str ]; + for( nLauf = 0 ; nLauf < nP_Str ; nLauf++ ) + ppP_Str[ nLauf ] = NULL; + + // Sammelstelle fuer double + nP_Dbl = 8; + pP_Dbl = new double[ nP_Dbl ]; + + // Sammelstelle fuer error codes + nP_Err = 8; + pP_Err = new USHORT[ nP_Err ]; + + // Sammelstellen fuer Referenzen + nP_RefTr = 32; + ppP_RefTr = new ScSingleRefData *[ nP_RefTr ]; + for( nLauf = 0 ; nLauf < nP_RefTr ; nLauf++ ) + ppP_RefTr[ nLauf ] = NULL; + + nP_Ext = 32; + ppP_Ext = new EXTCONT*[ nP_Ext ]; + memset( ppP_Ext, 0, sizeof( EXTCONT* ) * nP_Ext ); + + nP_Nlf = 16; + ppP_Nlf = new NLFCONT*[ nP_Nlf ]; + memset( ppP_Nlf, 0, sizeof( NLFCONT* ) * nP_Nlf ); + + nP_Matrix = 16; + ppP_Matrix = new ScMatrix*[ nP_Matrix ]; + memset( ppP_Matrix, 0, sizeof( ScMatrix* ) * nP_Matrix ); + + pScToken = new ScTokenArray; + + Reset(); +} + + +TokenPool::~TokenPool() +{ + UINT16 n; + + delete[] pP_Id; + delete[] pElement; + delete[] pType; + delete[] pSize; + delete[] pP_Dbl; + delete[] pP_Err; + + for( n = 0 ; n < nP_RefTr ; n++/*, pAktTr++*/ ) + { + if( ppP_RefTr[ n ] ) + delete ppP_RefTr[ n ]; + } + delete[] ppP_RefTr; + + for( n = 0 ; n < nP_Str ; n++/*, pAktStr++*/ ) + { + if( ppP_Str[ n ] ) + delete ppP_Str[ n ]; + } + delete[] ppP_Str; + + for( n = 0 ; n < nP_Ext ; n++ ) + { + if( ppP_Ext[ n ] ) + delete ppP_Ext[ n ]; + } + delete[] ppP_Ext; + + for( n = 0 ; n < nP_Nlf ; n++ ) + { + if( ppP_Nlf[ n ] ) + delete ppP_Nlf[ n ]; + } + delete[] ppP_Nlf; + + for( n = 0 ; n < nP_Matrix ; n++ ) + { + if( ppP_Matrix[ n ] ) + ppP_Matrix[ n ]->DecRef( ); + } + delete[] ppP_Matrix; + + delete pScToken; +} + + +void TokenPool::GrowString( void ) +{ + UINT16 nP_StrNew = nP_Str * 2; + UINT16 nL; + + String** ppP_StrNew = new String *[ nP_StrNew ]; + + for( nL = 0 ; nL < nP_Str ; nL++ ) + ppP_StrNew[ nL ] = ppP_Str[ nL ]; + for( nL = nP_Str ; nL < nP_StrNew ; nL++ ) + ppP_StrNew[ nL ] = NULL; + + nP_Str = nP_StrNew; + + delete[] ppP_Str; + ppP_Str = ppP_StrNew; +} + + +void TokenPool::GrowDouble( void ) +{ + UINT16 nP_DblNew = nP_Dbl * 2; + + double* pP_DblNew = new double[ nP_DblNew ]; + + for( UINT16 nL = 0 ; nL < nP_Dbl ; nL++ ) + pP_DblNew[ nL ] = pP_Dbl[ nL ]; + + nP_Dbl = nP_DblNew; + + delete[] pP_Dbl; + pP_Dbl = pP_DblNew; +} + + +//UNUSED2009-05 void TokenPool::GrowError( void ) +//UNUSED2009-05 { +//UNUSED2009-05 UINT16 nP_ErrNew = nP_Err * 2; +//UNUSED2009-05 +//UNUSED2009-05 USHORT* pP_ErrNew = new USHORT[ nP_ErrNew ]; +//UNUSED2009-05 +//UNUSED2009-05 for( UINT16 nL = 0 ; nL < nP_Err ; nL++ ) +//UNUSED2009-05 pP_ErrNew[ nL ] = pP_Err[ nL ]; +//UNUSED2009-05 +//UNUSED2009-05 nP_Err = nP_ErrNew; +//UNUSED2009-05 +//UNUSED2009-05 delete[] pP_Err; +//UNUSED2009-05 pP_Err = pP_ErrNew; +//UNUSED2009-05 } + + +void TokenPool::GrowTripel( void ) +{ + UINT16 nP_RefTrNew = nP_RefTr * 2; + UINT16 nL; + + ScSingleRefData** ppP_RefTrNew = new ScSingleRefData *[ nP_RefTrNew ]; + + for( nL = 0 ; nL < nP_RefTr ; nL++ ) + ppP_RefTrNew[ nL ] = ppP_RefTr[ nL ]; + for( nL = nP_RefTr ; nL < nP_RefTrNew ; nL++ ) + ppP_RefTrNew[ nL ] = NULL; + + nP_RefTr = nP_RefTrNew; + + delete[] ppP_RefTr; + ppP_RefTr = ppP_RefTrNew; +} + + +void TokenPool::GrowId( void ) +{ + UINT16 nP_IdNew = nP_Id * 2; + + UINT16* pP_IdNew = new UINT16[ nP_IdNew ]; + + for( UINT16 nL = 0 ; nL < nP_Id ; nL++ ) + pP_IdNew[ nL ] = pP_Id[ nL ]; + + nP_Id = nP_IdNew; + + delete[] pP_Id; + pP_Id = pP_IdNew; +} + + +void TokenPool::GrowElement( void ) +{ + UINT16 nElementNew = nElement * 2; + + UINT16* pElementNew = new UINT16[ nElementNew ]; + E_TYPE* pTypeNew = new E_TYPE[ nElementNew ]; + UINT16* pSizeNew = new UINT16[ nElementNew ]; + + for( UINT16 nL = 0 ; nL < nElement ; nL++ ) + { + pElementNew[ nL ] = pElement[ nL ]; + pTypeNew[ nL ] = pType[ nL ]; + pSizeNew[ nL ] = pSize[ nL ]; + } + + nElement = nElementNew; + + delete[] pElement; + delete[] pType; + delete[] pSize; + pElement = pElementNew; + pType = pTypeNew; + pSize = pSizeNew; +} + + +void TokenPool::GrowExt( void ) +{ + UINT16 nNewSize = nP_Ext * 2; + + EXTCONT** ppNew = new EXTCONT*[ nNewSize ]; + + memset( ppNew, 0, sizeof( EXTCONT* ) * nNewSize ); + memcpy( ppNew, ppP_Ext, sizeof( EXTCONT* ) * nP_Ext ); + + delete[] ppP_Ext; + ppP_Ext = ppNew; + nP_Ext = nNewSize; +} + + +void TokenPool::GrowNlf( void ) +{ + UINT16 nNewSize = nP_Nlf * 2; + + NLFCONT** ppNew = new NLFCONT*[ nNewSize ]; + + memset( ppNew, 0, sizeof( NLFCONT* ) * nNewSize ); + memcpy( ppNew, ppP_Nlf, sizeof( NLFCONT* ) * nP_Nlf ); + + delete[] ppP_Nlf; + ppP_Nlf = ppNew; + nP_Nlf = nNewSize; +} + + +void TokenPool::GrowMatrix( void ) +{ + UINT16 nNewSize = nP_Matrix * 2; + + ScMatrix** ppNew = new ScMatrix*[ nNewSize ]; + + memset( ppNew, 0, sizeof( ScMatrix* ) * nNewSize ); + memcpy( ppNew, ppP_Matrix, sizeof( ScMatrix* ) * nP_Matrix ); + + delete[] ppP_Matrix; + ppP_Matrix = ppNew; + nP_Matrix = nNewSize; +} + +void TokenPool::GetElement( const UINT16 nId ) +{ + DBG_ASSERT( nId < nElementAkt, "*TokenPool::GetElement(): Id zu gross!?" ); + + if( pType[ nId ] == T_Id ) + GetElementRek( nId ); + else + { + switch( pType[ nId ] ) + { +#ifdef DBG_UTIL + case T_Id: + DBG_ERROR( "-TokenPool::GetElement(): hier hast Du nichts zu suchen!" ); + break; +#endif + case T_Str: + pScToken->AddString( ppP_Str[ pElement[ nId ] ]->GetBuffer() ); + break; + case T_D: + pScToken->AddDouble( pP_Dbl[ pElement[ nId ] ] ); + break; + case T_Err: +#if 0 // erAck + pScToken->AddError( pP_Err[ pElement[ nId ] ] ); +#endif + break; + case T_RefC: + pScToken->AddSingleReference( *ppP_RefTr[ pElement[ (UINT16) nId ] ] ); + break; + case T_RefA: + { + ScComplexRefData aScComplexRefData; + aScComplexRefData.Ref1 = *ppP_RefTr[ pElement[ nId ] ]; + aScComplexRefData.Ref2 = *ppP_RefTr[ pElement[ nId ] + 1 ]; + pScToken->AddDoubleReference( aScComplexRefData ); + } + break; + case T_RN: + pScToken->AddName( pElement[ nId ] ); + break; + case T_Ext: + { + UINT16 n = pElement[ nId ]; + EXTCONT* p = ( n < nP_Ext )? ppP_Ext[ n ] : NULL; + + if( p ) + { + if( p->eId == ocEuroConvert ) + pScToken->AddOpCode( p->eId ); + else + pScToken->AddExternal( p->aText, p->eId ); + } + } + break; + case T_Nlf: + { + UINT16 n = pElement[ nId ]; + NLFCONT* p = ( n < nP_Nlf )? ppP_Nlf[ n ] : NULL; + + if( p ) + pScToken->AddColRowName( p->aRef ); + } + break; + case T_Matrix: + { + UINT16 n = pElement[ nId ]; + ScMatrix* p = ( n < nP_Matrix )? ppP_Matrix[ n ] : NULL; + + if( p ) + pScToken->AddMatrix( p ); + } + break; + case T_ExtName: + { + UINT16 n = pElement[nId]; + if (n < maExtNames.size()) + { + const ExtName& r = maExtNames[n]; + pScToken->AddExternalName(r.mnFileId, r.maName); + } + } + case T_ExtRefC: + { + UINT16 n = pElement[nId]; + if (n < maExtCellRefs.size()) + { + const ExtCellRef& r = maExtCellRefs[n]; + pScToken->AddExternalSingleReference(r.mnFileId, r.maTabName, r.maRef); + } + } + case T_ExtRefA: + { + UINT16 n = pElement[nId]; + if (n < maExtAreaRefs.size()) + { + const ExtAreaRef& r = maExtAreaRefs[n]; + pScToken->AddExternalDoubleReference(r.mnFileId, r.maTabName, r.maRef); + } + } + break; + default: + DBG_ERROR("-TokenPool::GetElement(): Zustand undefiniert!?"); + } + } +} + + +void TokenPool::GetElementRek( const UINT16 nId ) +{ +#ifdef DBG_UTIL + nRek++; + DBG_ASSERT( nRek <= nP_Id, "*TokenPool::GetElement(): Rekursion loopt!?" ); +#endif + + DBG_ASSERT( nId < nElementAkt, "*TokenPool::GetElementRek(): Id zu gross!?" ); + + DBG_ASSERT( pType[ nId ] == T_Id, "-TokenPool::GetElementRek(): nId nicht Id-Folge!" ); + + + UINT16 nAnz = pSize[ nId ]; + UINT16* pAkt = &pP_Id[ pElement[ nId ] ]; + for( ; nAnz > 0 ; nAnz--, pAkt++ ) + { + if( *pAkt < nScTokenOff ) + {// Rekursion oder nicht? + switch( pType[ *pAkt ] ) + { + case T_Id: + GetElementRek( *pAkt ); + break; + case T_Str: + pScToken->AddString( ppP_Str[ pElement[ *pAkt ] ]->GetBuffer() ); + break; + case T_D: + pScToken->AddDouble( pP_Dbl[ pElement[ *pAkt ] ] ); + break; + case T_Err: +#if 0 // erAck + pScToken->AddError( pP_Err[ pElement[ *pAkt ] ] ); +#endif + break; + case T_RefC: + pScToken->AddSingleReference( *ppP_RefTr[ pElement[ *pAkt ] ] ); + break; + case T_RefA: + { + ScComplexRefData aScComplexRefData; + aScComplexRefData.Ref1 = *ppP_RefTr[ pElement[ *pAkt ] ]; + aScComplexRefData.Ref2 = *ppP_RefTr[ pElement[ *pAkt ] + 1 ]; + pScToken->AddDoubleReference( aScComplexRefData ); + } + break; + case T_RN: + pScToken->AddName( pElement[ *pAkt ] ); + break; + case T_Ext: + { + UINT16 n = pElement[ *pAkt ]; + EXTCONT* p = ( n < nP_Ext )? ppP_Ext[ n ] : NULL; + + if( p ) + pScToken->AddExternal( p->aText, p->eId ); + } + break; + case T_Nlf: + { + UINT16 n = pElement[ *pAkt ]; + NLFCONT* p = ( n < nP_Nlf )? ppP_Nlf[ n ] : NULL; + + if( p ) + pScToken->AddColRowName( p->aRef ); + } + break; + case T_Matrix: + { + UINT16 n = pElement[ *pAkt ]; + ScMatrix* p = ( n < nP_Matrix )? ppP_Matrix[ n ] : NULL; + + if( p ) + pScToken->AddMatrix( p ); + } + break; + case T_ExtName: + { + UINT16 n = pElement[*pAkt]; + if (n < maExtNames.size()) + { + const ExtName& r = maExtNames[n]; + pScToken->AddExternalName(r.mnFileId, r.maName); + } + } + case T_ExtRefC: + { + UINT16 n = pElement[*pAkt]; + if (n < maExtCellRefs.size()) + { + const ExtCellRef& r = maExtCellRefs[n]; + pScToken->AddExternalSingleReference(r.mnFileId, r.maTabName, r.maRef); + } + } + case T_ExtRefA: + { + UINT16 n = pElement[*pAkt]; + if (n < maExtAreaRefs.size()) + { + const ExtAreaRef& r = maExtAreaRefs[n]; + pScToken->AddExternalDoubleReference(r.mnFileId, r.maTabName, r.maRef); + } + } + break; + default: + DBG_ERROR("-TokenPool::GetElementRek(): Zustand undefiniert!?"); + } + } + else // elementarer SC_Token + pScToken->AddOpCode( ( DefTokenId ) ( *pAkt - nScTokenOff ) ); + } + + +#ifdef DBG_UTIL + nRek--; +#endif +} + + +void TokenPool::operator >>( TokenId& rId ) +{ + rId = ( TokenId ) ( nElementAkt + 1 ); + + if( nElementAkt >= nElement ) + GrowElement(); + + pElement[ nElementAkt ] = nP_IdLast; // Start der Token-Folge + pType[ nElementAkt ] = T_Id; // Typinfo eintragen + pSize[ nElementAkt ] = nP_IdAkt - nP_IdLast; + // von nP_IdLast bis nP_IdAkt-1 geschrieben -> Laenge der Folge + + nElementAkt++; // Startwerte fuer naechste Folge + nP_IdLast = nP_IdAkt; +} + + +const TokenId TokenPool::Store( const double& rDouble ) +{ + if( nElementAkt >= nElement ) + GrowElement(); + + if( nP_DblAkt >= nP_Dbl ) + GrowDouble(); + + pElement[ nElementAkt ] = nP_DblAkt; // Index in Double-Array + pType[ nElementAkt ] = T_D; // Typinfo Double eintragen + + pP_Dbl[ nP_DblAkt ] = rDouble; + + pSize[ nElementAkt ] = 1; // eigentlich Banane + + nElementAkt++; + nP_DblAkt++; + + return ( const TokenId ) nElementAkt; // Ausgabe von altem Wert + 1! +} + + +const TokenId TokenPool::Store( const UINT16 nIndex ) +{ + if( nElementAkt >= nElement ) + GrowElement(); + + pElement[ nElementAkt ] = nIndex; // Daten direkt im Index! + pType[ nElementAkt ] = T_RN; // Typinfo Range Name eintragen + + nElementAkt++; + return ( const TokenId ) nElementAkt; // Ausgabe von altem Wert + 1! +} + + +const TokenId TokenPool::Store( const String& rString ) +{ + // weitgehend nach Store( const sal_Char* ) kopiert, zur Vermeidung + // eines temporaeren Strings in " + if( nElementAkt >= nElement ) + GrowElement(); + + if( nP_StrAkt >= nP_Str ) + GrowString(); + + pElement[ nElementAkt ] = nP_StrAkt; // Index in String-Array + pType[ nElementAkt ] = T_Str; // Typinfo String eintragen + + // String anlegen + if( !ppP_Str[ nP_StrAkt ] ) + //...aber nur, wenn noch nicht vorhanden + ppP_Str[ nP_StrAkt ] = new String( rString ); + else + //...ansonsten nur kopieren + *ppP_Str[ nP_StrAkt ] = rString; + + DBG_ASSERT( sizeof( xub_StrLen ) <= 2, "*TokenPool::Store(): StrLen doesn't match!" ); + + pSize[ nElementAkt ] = ( UINT16 ) ppP_Str[ nP_StrAkt ]->Len(); + + nElementAkt++; + nP_StrAkt++; + + return ( const TokenId ) nElementAkt; // Ausgabe von altem Wert + 1! +} + + +const TokenId TokenPool::Store( const ScSingleRefData& rTr ) +{ + if( nElementAkt >= nElement ) + GrowElement(); + + if( nP_RefTrAkt >= nP_RefTr ) + GrowTripel(); + + pElement[ nElementAkt ] = nP_RefTrAkt; + pType[ nElementAkt ] = T_RefC; // Typinfo Cell-Reff eintragen + + if( !ppP_RefTr[ nP_RefTrAkt ] ) + ppP_RefTr[ nP_RefTrAkt ] = new ScSingleRefData( rTr ); + else + *ppP_RefTr[ nP_RefTrAkt ] = rTr; + + nElementAkt++; + nP_RefTrAkt++; + + return ( const TokenId ) nElementAkt; // Ausgabe von altem Wert + 1! +} + + +const TokenId TokenPool::Store( const ScComplexRefData& rTr ) +{ + if( nElementAkt >= nElement ) + GrowElement(); + + if( nP_RefTrAkt + 1 >= nP_RefTr ) + GrowTripel(); + + pElement[ nElementAkt ] = nP_RefTrAkt; + pType[ nElementAkt ] = T_RefA; // Typinfo Area-Reff eintragen + + if( !ppP_RefTr[ nP_RefTrAkt ] ) + ppP_RefTr[ nP_RefTrAkt ] = new ScSingleRefData( rTr.Ref1 ); + else + *ppP_RefTr[ nP_RefTrAkt ] = rTr.Ref1; + nP_RefTrAkt++; + + if( !ppP_RefTr[ nP_RefTrAkt ] ) + ppP_RefTr[ nP_RefTrAkt ] = new ScSingleRefData( rTr.Ref2 ); + else + *ppP_RefTr[ nP_RefTrAkt ] = rTr.Ref2; + nP_RefTrAkt++; + + nElementAkt++; + + return ( const TokenId ) nElementAkt; // Ausgabe von altem Wert + 1! +} + + +const TokenId TokenPool::Store( const DefTokenId e, const String& r ) +{ + if( nElementAkt >= nElement ) + GrowElement(); + + if( nP_ExtAkt >= nP_Ext ) + GrowExt(); + + pElement[ nElementAkt ] = nP_ExtAkt; + pType[ nElementAkt ] = T_Ext; // Typinfo String eintragen + + if( ppP_Ext[ nP_ExtAkt ] ) + { + ppP_Ext[ nP_ExtAkt ]->eId = e; + ppP_Ext[ nP_ExtAkt ]->aText = r; + } + else + ppP_Ext[ nP_ExtAkt ] = new EXTCONT( e, r ); + + nElementAkt++; + nP_ExtAkt++; + + return ( const TokenId ) nElementAkt; // Ausgabe von altem Wert + 1! +} + + +const TokenId TokenPool::StoreNlf( const ScSingleRefData& rTr ) +{ + if( nElementAkt >= nElement ) + GrowElement(); + + if( nP_NlfAkt >= nP_Nlf ) + GrowNlf(); + + pElement[ nElementAkt ] = nP_NlfAkt; + pType[ nElementAkt ] = T_Nlf; + + if( ppP_Nlf[ nP_NlfAkt ] ) + { + ppP_Nlf[ nP_NlfAkt ]->aRef = rTr; + } + else + ppP_Nlf[ nP_NlfAkt ] = new NLFCONT( rTr ); + + nElementAkt++; + nP_NlfAkt++; + + return ( const TokenId ) nElementAkt; +} + +const TokenId TokenPool::StoreMatrix() +{ + ScMatrix* pM; + + if( nElementAkt >= nElement ) + GrowElement(); + + if( nP_MatrixAkt >= nP_Matrix ) + GrowMatrix(); + + pElement[ nElementAkt ] = nP_MatrixAkt; + pType[ nElementAkt ] = T_Matrix; + + pM = new ScMatrix( 0, 0 ); + pM->IncRef( ); + ppP_Matrix[ nP_MatrixAkt ] = pM; + + nElementAkt++; + nP_MatrixAkt++; + + return ( const TokenId ) nElementAkt; +} + +const TokenId TokenPool::StoreExtName( sal_uInt16 nFileId, const String& rName ) +{ + if ( nElementAkt >= nElement ) + GrowElement(); + + pElement[nElementAkt] = static_cast<UINT16>(maExtNames.size()); + pType[nElementAkt] = T_ExtName; + + maExtNames.push_back(ExtName()); + ExtName& r = maExtNames.back(); + r.mnFileId = nFileId; + r.maName = rName; + + ++nElementAkt; + + return static_cast<const TokenId>(nElementAkt); +} + +const TokenId TokenPool::StoreExtRef( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef ) +{ + if ( nElementAkt >= nElement ) + GrowElement(); + + pElement[nElementAkt] = static_cast<UINT16>(maExtCellRefs.size()); + pType[nElementAkt] = T_ExtRefC; + + maExtCellRefs.push_back(ExtCellRef()); + ExtCellRef& r = maExtCellRefs.back(); + r.mnFileId = nFileId; + r.maTabName = rTabName; + r.maRef = rRef; + + ++nElementAkt; + + return static_cast<const TokenId>(nElementAkt); +} + +const TokenId TokenPool::StoreExtRef( sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef ) +{ + if ( nElementAkt >= nElement ) + GrowElement(); + + pElement[nElementAkt] = static_cast<UINT16>(maExtAreaRefs.size()); + pType[nElementAkt] = T_ExtRefA; + + maExtAreaRefs.push_back(ExtAreaRef()); + ExtAreaRef& r = maExtAreaRefs.back(); + r.mnFileId = nFileId; + r.maTabName = rTabName; + r.maRef = rRef; + + ++nElementAkt; + + return static_cast<const TokenId>(nElementAkt); +} + +void TokenPool::Reset( void ) +{ + nP_IdAkt = nP_IdLast = nElementAkt = nP_StrAkt = nP_DblAkt = nP_ErrAkt = nP_RefTrAkt = nP_ExtAkt = nP_NlfAkt = nP_MatrixAkt = 0; + maExtNames.clear(); + maExtCellRefs.clear(); + maExtAreaRefs.clear(); +} + + +BOOL TokenPool::IsSingleOp( const TokenId& rId, const DefTokenId eId ) const +{ + UINT16 nId = (UINT16) rId; + if( nId && nId <= nElementAkt ) + {// existent? + nId--; + if( T_Id == pType[ nId ] ) + {// Tokenfolge? + if( pSize[ nId ] == 1 ) + {// GENAU 1 Token + UINT16 nSecId = pP_Id[ pElement[ nId ] ]; + if( nSecId >= nScTokenOff ) + {// Default-Token? + return ( DefTokenId ) ( nSecId - nScTokenOff ) == eId; // Gesuchter? + } + } + } + } + + return FALSE; +} + + +const String* TokenPool::GetExternal( const TokenId& rId ) const +{ + const String* p = NULL; + UINT16 n = (UINT16) rId; + if( n && n <= nElementAkt ) + { + n--; + if( (pType[ n ] == T_Ext) && ppP_Ext[ pElement[ n ] ] ) + p = &ppP_Ext[ pElement[ n ] ]->aText; + } + + return p; +} + + +//UNUSED2008-05 const String* TokenPool::GetString( const TokenId& r ) const +//UNUSED2008-05 { +//UNUSED2008-05 const String* p = NULL; +//UNUSED2008-05 UINT16 n = (UINT16) r; +//UNUSED2008-05 if( n && n <= nElementAkt ) +//UNUSED2008-05 { +//UNUSED2008-05 n--; +//UNUSED2008-05 if( pType[ n ] == T_Str ) +//UNUSED2008-05 p = ppP_Str[ pElement[ n ] ]; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 return p; +//UNUSED2008-05 } + +ScMatrix* TokenPool::GetMatrix( unsigned int n ) const +{ + if( n < nP_MatrixAkt ) + return ppP_Matrix[ n ]; + else + printf ("GETMATRIX %d >= %d\n", n, nP_MatrixAkt); + return NULL; +} + diff --git a/sc/source/filter/excel/xechart.cxx b/sc/source/filter/excel/xechart.cxx new file mode 100644 index 000000000000..b6b136da4b1e --- /dev/null +++ b/sc/source/filter/excel/xechart.cxx @@ -0,0 +1,3303 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "xechart.hxx" + +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/chart/ChartAxisLabelPosition.hpp> +#include <com/sun/star/chart/ChartAxisPosition.hpp> +#include <com/sun/star/chart/DataLabelPlacement.hpp> +#include <com/sun/star/chart/ErrorBarStyle.hpp> +#include <com/sun/star/chart/MissingValueTreatment.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/chart/XDiagramPositioning.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/XDiagram.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XDataSeriesContainer.hpp> +#include <com/sun/star/chart2/XRegressionCurveContainer.hpp> +#include <com/sun/star/chart2/XTitled.hpp> +#include <com/sun/star/chart2/XColorScheme.hpp> +#include <com/sun/star/chart2/data/XDataSource.hpp> +#include <com/sun/star/chart2/AxisType.hpp> +#include <com/sun/star/chart2/CurveStyle.hpp> +#include <com/sun/star/chart2/DataPointGeometry3D.hpp> +#include <com/sun/star/chart2/DataPointLabel.hpp> +#include <com/sun/star/chart2/LegendExpansion.hpp> +#include <com/sun/star/chart2/LegendPosition.hpp> +#include <com/sun/star/chart2/RelativePosition.hpp> +#include <com/sun/star/chart2/StackingDirection.hpp> +#include <com/sun/star/chart2/TickmarkStyle.hpp> + +#include <vcl/outdev.hxx> +#include <filter/msfilter/escherex.hxx> + +#include "document.hxx" +#include "rangelst.hxx" +#include "rangeutl.hxx" +#include "compiler.hxx" +#include "tokenarray.hxx" +#include "token.hxx" +#include "xeescher.hxx" +#include "xeformula.hxx" +#include "xehelper.hxx" +#include "xepage.hxx" +#include "xestyle.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::UNO_SET_THROW; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::i18n::XBreakIterator; +using ::com::sun::star::frame::XModel; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::drawing::XShapes; + +using ::com::sun::star::chart2::IncrementData; +using ::com::sun::star::chart2::RelativePosition; +using ::com::sun::star::chart2::ScaleData; +using ::com::sun::star::chart2::SubIncrement; +using ::com::sun::star::chart2::XAxis; +using ::com::sun::star::chart2::XChartDocument; +using ::com::sun::star::chart2::XChartTypeContainer; +using ::com::sun::star::chart2::XColorScheme; +using ::com::sun::star::chart2::XCoordinateSystem; +using ::com::sun::star::chart2::XCoordinateSystemContainer; +using ::com::sun::star::chart2::XChartType; +using ::com::sun::star::chart2::XDataSeries; +using ::com::sun::star::chart2::XDataSeriesContainer; +using ::com::sun::star::chart2::XDiagram; +using ::com::sun::star::chart2::XFormattedString; +using ::com::sun::star::chart2::XLegend; +using ::com::sun::star::chart2::XRegressionCurve; +using ::com::sun::star::chart2::XRegressionCurveContainer; +using ::com::sun::star::chart2::XScaling; +using ::com::sun::star::chart2::XTitle; +using ::com::sun::star::chart2::XTitled; + +using ::com::sun::star::chart2::data::XDataSequence; +using ::com::sun::star::chart2::data::XDataSource; +using ::com::sun::star::chart2::data::XLabeledDataSequence; + +using ::formula::FormulaGrammar; +using ::formula::FormulaToken; + +namespace cssc = ::com::sun::star::chart; +namespace cssc2 = ::com::sun::star::chart2; + +// Helpers ==================================================================== + +namespace { + +XclExpStream& operator<<( XclExpStream& rStrm, const XclChRectangle& rRect ) +{ + return rStrm << rRect.mnX << rRect.mnY << rRect.mnWidth << rRect.mnHeight; +} + +inline void lclSaveRecord( XclExpStream& rStrm, XclExpRecordRef xRec ) +{ + if( xRec.is() ) + xRec->Save( rStrm ); +} + +/** Saves the passed record (group) together with a leading value record. */ +template< typename Type > +void lclSaveRecord( XclExpStream& rStrm, XclExpRecordRef xRec, sal_uInt16 nRecId, Type nValue ) +{ + if( xRec.is() ) + { + XclExpValueRecord< Type >( nRecId, nValue ).Save( rStrm ); + xRec->Save( rStrm ); + } +} + +void lclWriteChFrBlockRecord( XclExpStream& rStrm, const XclChFrBlock& rFrBlock, bool bBegin ) +{ + sal_uInt16 nRecId = bBegin ? EXC_ID_CHFRBLOCKBEGIN : EXC_ID_CHFRBLOCKEND; + rStrm.StartRecord( nRecId, 12 ); + rStrm << nRecId << EXC_FUTUREREC_EMPTYFLAGS << rFrBlock.mnType << rFrBlock.mnContext << rFrBlock.mnValue1 << rFrBlock.mnValue2; + rStrm.EndRecord(); +} + +template< typename Type > +inline bool lclIsAutoAnyOrGetValue( Type& rValue, const Any& rAny ) +{ + return !rAny.hasValue() || !(rAny >>= rValue); +} + +bool lclIsAutoAnyOrGetScaledValue( double& rfValue, const Any& rAny, bool bLogScale ) +{ + bool bIsAuto = lclIsAutoAnyOrGetValue( rfValue, rAny ); + if( !bIsAuto && bLogScale ) + rfValue = log( rfValue ) / log( 10.0 ); + return bIsAuto; +} + +} // namespace + +// Common ===================================================================== + +/** Stores global data needed in various classes of the Chart export filter. */ +struct XclExpChRootData : public XclChRootData +{ + typedef ::std::vector< XclChFrBlock > XclChFrBlockVector; + + XclExpChChart& mrChartData; /// The chart data object. + XclChFrBlockVector maWrittenFrBlocks; /// Stack of future record levels already written out. + XclChFrBlockVector maUnwrittenFrBlocks; /// Stack of future record levels not yet written out. + + inline explicit XclExpChRootData( XclExpChChart& rChartData ) : mrChartData( rChartData ) {} + + /** Registers a new future record level. */ + void RegisterFutureRecBlock( const XclChFrBlock& rFrBlock ); + /** Initializes the current future record level (writes all unwritten CHFRBLOCKBEGIN records). */ + void InitializeFutureRecBlock( XclExpStream& rStrm ); + /** Finalizes the current future record level (writes CHFRBLOCKEND record if needed). */ + void FinalizeFutureRecBlock( XclExpStream& rStrm ); +}; + +// ---------------------------------------------------------------------------- + +void XclExpChRootData::RegisterFutureRecBlock( const XclChFrBlock& rFrBlock ) +{ + maUnwrittenFrBlocks.push_back( rFrBlock ); +} + +void XclExpChRootData::InitializeFutureRecBlock( XclExpStream& rStrm ) +{ + // first call from a future record writes all missing CHFRBLOCKBEGIN records + if( !maUnwrittenFrBlocks.empty() ) + { + // write the leading CHFRINFO record + if( maWrittenFrBlocks.empty() ) + { + rStrm.StartRecord( EXC_ID_CHFRINFO, 20 ); + rStrm << EXC_ID_CHFRINFO << EXC_FUTUREREC_EMPTYFLAGS << EXC_CHFRINFO_EXCELXP2003 << EXC_CHFRINFO_EXCELXP2003 << sal_uInt16( 3 ); + rStrm << sal_uInt16( 0x0850 ) << sal_uInt16( 0x085A ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x086A ) << sal_uInt16( 0x086B ); + rStrm.EndRecord(); + } + // write all unwritten CHFRBLOCKBEGIN records + for( XclChFrBlockVector::const_iterator aIt = maUnwrittenFrBlocks.begin(), aEnd = maUnwrittenFrBlocks.end(); aIt != aEnd; ++aIt ) + { + DBG_ASSERT( aIt->mnType != EXC_CHFRBLOCK_TYPE_UNKNOWN, "XclExpChRootData::InitializeFutureRecBlock - unknown future record block type" ); + lclWriteChFrBlockRecord( rStrm, *aIt, true ); + } + // move all record infos to vector of written blocks + maWrittenFrBlocks.insert( maWrittenFrBlocks.end(), maUnwrittenFrBlocks.begin(), maUnwrittenFrBlocks.end() ); + maUnwrittenFrBlocks.clear(); + } +} + +void XclExpChRootData::FinalizeFutureRecBlock( XclExpStream& rStrm ) +{ + DBG_ASSERT( !maUnwrittenFrBlocks.empty() || !maWrittenFrBlocks.empty(), "XclExpChRootData::FinalizeFutureRecBlock - no future record level found" ); + if( !maUnwrittenFrBlocks.empty() ) + { + // no future record has been written, just forget the topmost level + maUnwrittenFrBlocks.pop_back(); + } + else if( !maWrittenFrBlocks.empty() ) + { + // write the CHFRBLOCKEND record for the topmost block and delete it + lclWriteChFrBlockRecord( rStrm, maWrittenFrBlocks.back(), false ); + maWrittenFrBlocks.pop_back(); + } +} + +// ---------------------------------------------------------------------------- + +XclExpChRoot::XclExpChRoot( const XclExpRoot& rRoot, XclExpChChart& rChartData ) : + XclExpRoot( rRoot ), + mxChData( new XclExpChRootData( rChartData ) ) +{ +} + +XclExpChRoot::~XclExpChRoot() +{ +} + +Reference< XChartDocument > XclExpChRoot::GetChartDocument() const +{ + return mxChData->mxChartDoc; +} + +XclExpChChart& XclExpChRoot::GetChartData() const +{ + return mxChData->mrChartData; +} + +const XclChTypeInfo& XclExpChRoot::GetChartTypeInfo( XclChTypeId eType ) const +{ + return mxChData->mxTypeInfoProv->GetTypeInfo( eType ); +} + +const XclChTypeInfo& XclExpChRoot::GetChartTypeInfo( const OUString& rServiceName ) const +{ + return mxChData->mxTypeInfoProv->GetTypeInfoFromService( rServiceName ); +} + +const XclChFormatInfo& XclExpChRoot::GetFormatInfo( XclChObjectType eObjType ) const +{ + return mxChData->mxFmtInfoProv->GetFormatInfo( eObjType ); +} + +void XclExpChRoot::InitConversion( XChartDocRef xChartDoc, const Rectangle& rChartRect ) const +{ + mxChData->InitConversion( GetRoot(), xChartDoc, rChartRect ); +} + +void XclExpChRoot::FinishConversion() const +{ + mxChData->FinishConversion(); +} + +bool XclExpChRoot::IsSystemColor( const Color& rColor, sal_uInt16 nSysColorIdx ) const +{ + XclExpPalette& rPal = GetPalette(); + return rPal.IsSystemColor( nSysColorIdx ) && (rColor == rPal.GetDefColor( nSysColorIdx )); +} + +void XclExpChRoot::SetSystemColor( Color& rColor, sal_uInt32& rnColorId, sal_uInt16 nSysColorIdx ) const +{ + DBG_ASSERT( GetPalette().IsSystemColor( nSysColorIdx ), "XclExpChRoot::SetSystemColor - invalid color index" ); + rColor = GetPalette().GetDefColor( nSysColorIdx ); + rnColorId = XclExpPalette::GetColorIdFromIndex( nSysColorIdx ); +} + +sal_Int32 XclExpChRoot::CalcChartXFromHmm( sal_Int32 nPosX ) const +{ + return ::limit_cast< sal_Int32, double >( (nPosX - mxChData->mnBorderGapX) / mxChData->mfUnitSizeX, 0, EXC_CHART_TOTALUNITS ); +} + +sal_Int32 XclExpChRoot::CalcChartYFromHmm( sal_Int32 nPosY ) const +{ + return ::limit_cast< sal_Int32, double >( (nPosY - mxChData->mnBorderGapY) / mxChData->mfUnitSizeY, 0, EXC_CHART_TOTALUNITS ); +} + +XclChRectangle XclExpChRoot::CalcChartRectFromHmm( const ::com::sun::star::awt::Rectangle& rRect ) const +{ + XclChRectangle aRect; + aRect.mnX = CalcChartXFromHmm( rRect.X ); + aRect.mnY = CalcChartYFromHmm( rRect.Y ); + aRect.mnWidth = CalcChartXFromHmm( rRect.Width ); + aRect.mnHeight = CalcChartYFromHmm( rRect.Height ); + return aRect; +} + +sal_Int32 XclExpChRoot::CalcChartXFromRelative( double fPosX ) const +{ + return CalcChartXFromHmm( static_cast< sal_Int32 >( fPosX * mxChData->maChartRect.GetWidth() + 0.5 ) ); +} + +sal_Int32 XclExpChRoot::CalcChartYFromRelative( double fPosY ) const +{ + return CalcChartYFromHmm( static_cast< sal_Int32 >( fPosY * mxChData->maChartRect.GetHeight() + 0.5 ) ); +} + +void XclExpChRoot::ConvertLineFormat( XclChLineFormat& rLineFmt, + const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const +{ + GetChartPropSetHelper().ReadLineProperties( + rLineFmt, *mxChData->mxLineDashTable, rPropSet, ePropMode ); +} + +bool XclExpChRoot::ConvertAreaFormat( XclChAreaFormat& rAreaFmt, + const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const +{ + return GetChartPropSetHelper().ReadAreaProperties( rAreaFmt, rPropSet, ePropMode ); +} + +void XclExpChRoot::ConvertEscherFormat( + XclChEscherFormat& rEscherFmt, XclChPicFormat& rPicFmt, + const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const +{ + GetChartPropSetHelper().ReadEscherProperties( rEscherFmt, rPicFmt, + *mxChData->mxGradientTable, *mxChData->mxHatchTable, *mxChData->mxBitmapTable, rPropSet, ePropMode ); +} + +sal_uInt16 XclExpChRoot::ConvertFont( const ScfPropertySet& rPropSet, sal_Int16 nScript ) const +{ + XclFontData aFontData; + GetFontPropSetHelper().ReadFontProperties( aFontData, rPropSet, EXC_FONTPROPSET_CHART, nScript ); + return GetFontBuffer().Insert( aFontData, EXC_COLOR_CHARTTEXT ); +} + +sal_uInt16 XclExpChRoot::ConvertPieRotation( const ScfPropertySet& rPropSet ) +{ + sal_Int32 nApiRot = 0; + rPropSet.GetProperty( nApiRot, EXC_CHPROP_STARTINGANGLE ); + return static_cast< sal_uInt16 >( (450 - (nApiRot % 360)) % 360 ); +} + +void XclExpChRoot::RegisterFutureRecBlock( const XclChFrBlock& rFrBlock ) +{ + mxChData->RegisterFutureRecBlock( rFrBlock ); +} + +void XclExpChRoot::InitializeFutureRecBlock( XclExpStream& rStrm ) +{ + mxChData->InitializeFutureRecBlock( rStrm ); +} + +void XclExpChRoot::FinalizeFutureRecBlock( XclExpStream& rStrm ) +{ + mxChData->FinalizeFutureRecBlock( rStrm ); +} + +// ---------------------------------------------------------------------------- + +XclExpChGroupBase::XclExpChGroupBase( const XclExpChRoot& rRoot, + sal_uInt16 nFrType, sal_uInt16 nRecId, sal_Size nRecSize ) : + XclExpRecord( nRecId, nRecSize ), + XclExpChRoot( rRoot ), + maFrBlock( nFrType ) +{ +} + +XclExpChGroupBase::~XclExpChGroupBase() +{ +} + +void XclExpChGroupBase::Save( XclExpStream& rStrm ) +{ + // header record + XclExpRecord::Save( rStrm ); + // group records + if( HasSubRecords() ) + { + // register the future record context corresponding to this record group + RegisterFutureRecBlock( maFrBlock ); + // CHBEGIN record + XclExpEmptyRecord( EXC_ID_CHBEGIN ).Save( rStrm ); + // embedded records + WriteSubRecords( rStrm ); + // finalize the future records, must be done before the closing CHEND + FinalizeFutureRecBlock( rStrm ); + // CHEND record + XclExpEmptyRecord( EXC_ID_CHEND ).Save( rStrm ); + } +} + +bool XclExpChGroupBase::HasSubRecords() const +{ + return true; +} + +void XclExpChGroupBase::SetFutureRecordContext( sal_uInt16 nFrContext, sal_uInt16 nFrValue1, sal_uInt16 nFrValue2 ) +{ + maFrBlock.mnContext = nFrContext; + maFrBlock.mnValue1 = nFrValue1; + maFrBlock.mnValue2 = nFrValue2; +} + +// ---------------------------------------------------------------------------- + +XclExpChFutureRecordBase::XclExpChFutureRecordBase( const XclExpChRoot& rRoot, + XclFutureRecType eRecType, sal_uInt16 nRecId, sal_Size nRecSize ) : + XclExpFutureRecord( eRecType, nRecId, nRecSize ), + XclExpChRoot( rRoot ) +{ +} + +void XclExpChFutureRecordBase::Save( XclExpStream& rStrm ) +{ + InitializeFutureRecBlock( rStrm ); + XclExpFutureRecord::Save( rStrm ); +} + +// Frame formatting =========================================================== + +XclExpChFramePos::XclExpChFramePos( sal_uInt16 nTLMode, sal_uInt16 nBRMode ) : + XclExpRecord( EXC_ID_CHFRAMEPOS, 20 ) +{ + maData.mnTLMode = nTLMode; + maData.mnBRMode = nBRMode; +} + +void XclExpChFramePos::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnTLMode << maData.mnBRMode << maData.maRect; +} + +// ---------------------------------------------------------------------------- + +XclExpChLineFormat::XclExpChLineFormat( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHLINEFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 12 : 10 ), + mnColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ) +{ +} + +void XclExpChLineFormat::SetDefault( XclChFrameType eDefFrameType ) +{ + switch( eDefFrameType ) + { + case EXC_CHFRAMETYPE_AUTO: + SetAuto( true ); + break; + case EXC_CHFRAMETYPE_INVISIBLE: + SetAuto( false ); + maData.mnPattern = EXC_CHLINEFORMAT_NONE; + break; + default: + DBG_ERRORFILE( "XclExpChLineFormat::SetDefault - unknown frame type" ); + } +} + +void XclExpChLineFormat::Convert( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType ); + rRoot.ConvertLineFormat( maData, rPropSet, rFmtInfo.mePropMode ); + if( HasLine() ) + { + // detect system color, set color identifier (TODO: detect automatic series line) + if( (eObjType != EXC_CHOBJTYPE_LINEARSERIES) && rRoot.IsSystemColor( maData.maColor, rFmtInfo.mnAutoLineColorIdx ) ) + { + // store color index from automatic format data + mnColorId = XclExpPalette::GetColorIdFromIndex( rFmtInfo.mnAutoLineColorIdx ); + // try to set automatic mode + bool bAuto = (maData.mnPattern == EXC_CHLINEFORMAT_SOLID) && (maData.mnWeight == rFmtInfo.mnAutoLineWeight); + ::set_flag( maData.mnFlags, EXC_CHLINEFORMAT_AUTO, bAuto ); + } + else + { + // user defined color - register in palette + mnColorId = rRoot.GetPalette().InsertColor( maData.maColor, EXC_COLOR_CHARTLINE ); + } + } + else + { + // no line - set default system color + rRoot.SetSystemColor( maData.maColor, mnColorId, EXC_COLOR_CHWINDOWTEXT ); + } +} + +bool XclExpChLineFormat::IsDefault( XclChFrameType eDefFrameType ) const +{ + return + ((eDefFrameType == EXC_CHFRAMETYPE_INVISIBLE) && !HasLine()) || + ((eDefFrameType == EXC_CHFRAMETYPE_AUTO) && IsAuto()); +} + +void XclExpChLineFormat::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.maColor << maData.mnPattern << maData.mnWeight << maData.mnFlags; + if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ) + rStrm << rStrm.GetRoot().GetPalette().GetColorIndex( mnColorId ); +} + +namespace { + +/** Creates a CHLINEFORMAT record from the passed property set. */ +XclExpChLineFormatRef lclCreateLineFormat( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + XclExpChLineFormatRef xLineFmt( new XclExpChLineFormat( rRoot ) ); + xLineFmt->Convert( rRoot, rPropSet, eObjType ); + const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType ); + if( rFmtInfo.mbDeleteDefFrame && xLineFmt->IsDefault( rFmtInfo.meDefFrameType ) ) + xLineFmt.reset(); + return xLineFmt; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpChAreaFormat::XclExpChAreaFormat( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHAREAFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 16 : 12 ), + mnPattColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ), + mnBackColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ) +{ +} + +bool XclExpChAreaFormat::Convert( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType ); + bool bComplexFill = rRoot.ConvertAreaFormat( maData, rPropSet, rFmtInfo.mePropMode ); + if( HasArea() ) + { + bool bSolid = maData.mnPattern == EXC_PATT_SOLID; + // detect system color, set color identifier (TODO: detect automatic series area) + if( (eObjType != EXC_CHOBJTYPE_FILLEDSERIES) && rRoot.IsSystemColor( maData.maPattColor, rFmtInfo.mnAutoPattColorIdx ) ) + { + // store color index from automatic format data + mnPattColorId = XclExpPalette::GetColorIdFromIndex( rFmtInfo.mnAutoPattColorIdx ); + // set automatic mode + ::set_flag( maData.mnFlags, EXC_CHAREAFORMAT_AUTO, bSolid ); + } + else + { + // user defined color - register color in palette + mnPattColorId = rRoot.GetPalette().InsertColor( maData.maPattColor, EXC_COLOR_CHARTAREA ); + } + // background color (default system color for solid fills) + if( bSolid ) + rRoot.SetSystemColor( maData.maBackColor, mnBackColorId, EXC_COLOR_CHWINDOWTEXT ); + else + mnBackColorId = rRoot.GetPalette().InsertColor( maData.maBackColor, EXC_COLOR_CHARTAREA ); + } + else + { + // no area - set default system colors + rRoot.SetSystemColor( maData.maPattColor, mnPattColorId, EXC_COLOR_CHWINDOWBACK ); + rRoot.SetSystemColor( maData.maBackColor, mnBackColorId, EXC_COLOR_CHWINDOWTEXT ); + } + return bComplexFill; +} + +void XclExpChAreaFormat::SetDefault( XclChFrameType eDefFrameType ) +{ + switch( eDefFrameType ) + { + case EXC_CHFRAMETYPE_AUTO: + SetAuto( true ); + break; + case EXC_CHFRAMETYPE_INVISIBLE: + SetAuto( false ); + maData.mnPattern = EXC_PATT_NONE; + break; + default: + DBG_ERRORFILE( "XclExpChAreaFormat::SetDefault - unknown frame type" ); + } +} + +bool XclExpChAreaFormat::IsDefault( XclChFrameType eDefFrameType ) const +{ + return + ((eDefFrameType == EXC_CHFRAMETYPE_INVISIBLE) && !HasArea()) || + ((eDefFrameType == EXC_CHFRAMETYPE_AUTO) && IsAuto()); +} + +void XclExpChAreaFormat::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.maPattColor << maData.maBackColor << maData.mnPattern << maData.mnFlags; + if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ) + { + const XclExpPalette& rPal = rStrm.GetRoot().GetPalette(); + rStrm << rPal.GetColorIndex( mnPattColorId ) << rPal.GetColorIndex( mnBackColorId ); + } +} + +// ---------------------------------------------------------------------------- + +XclExpChEscherFormat::XclExpChEscherFormat( const XclExpChRoot& rRoot ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_UNKNOWN, EXC_ID_CHESCHERFORMAT ), + mnColor1Id( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ), + mnColor2Id( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ) +{ + DBG_ASSERT_BIFF( GetBiff() == EXC_BIFF8 ); +} + +void XclExpChEscherFormat::Convert( const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + const XclChFormatInfo& rFmtInfo = GetFormatInfo( eObjType ); + ConvertEscherFormat( maData, maPicFmt, rPropSet, rFmtInfo.mePropMode ); + // register colors in palette + mnColor1Id = RegisterColor( ESCHER_Prop_fillColor ); + mnColor2Id = RegisterColor( ESCHER_Prop_fillBackColor ); +} + +bool XclExpChEscherFormat::IsValid() const +{ + return maData.mxEscherSet.is(); +} + +void XclExpChEscherFormat::Save( XclExpStream& rStrm ) +{ + if( maData.mxEscherSet.is() ) + { + // replace RGB colors with palette indexes in the Escher container + const XclExpPalette& rPal = GetPalette(); + maData.mxEscherSet->AddOpt( ESCHER_Prop_fillColor, 0x08000000 | rPal.GetColorIndex( mnColor1Id ) ); + maData.mxEscherSet->AddOpt( ESCHER_Prop_fillBackColor, 0x08000000 | rPal.GetColorIndex( mnColor2Id ) ); + + // save the record group + XclExpChGroupBase::Save( rStrm ); + } +} + +bool XclExpChEscherFormat::HasSubRecords() const +{ + // no subrecords for gradients + return maPicFmt.mnBmpMode != EXC_CHPICFORMAT_NONE; +} + +void XclExpChEscherFormat::WriteSubRecords( XclExpStream& rStrm ) +{ + rStrm.StartRecord( EXC_ID_CHPICFORMAT, 14 ); + rStrm << maPicFmt.mnBmpMode << maPicFmt.mnFormat << maPicFmt.mnFlags << maPicFmt.mfScale; + rStrm.EndRecord(); +} + +sal_uInt32 XclExpChEscherFormat::RegisterColor( sal_uInt16 nPropId ) +{ + sal_uInt32 nBGRValue; + if( maData.mxEscherSet.is() && maData.mxEscherSet->GetOpt( nPropId, nBGRValue ) ) + { + // swap red and blue + Color aColor( RGB_COLORDATA( + COLORDATA_BLUE( nBGRValue ), + COLORDATA_GREEN( nBGRValue ), + COLORDATA_RED( nBGRValue ) ) ); + return GetPalette().InsertColor( aColor, EXC_COLOR_CHARTAREA ); + } + return XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ); +} + +void XclExpChEscherFormat::WriteBody( XclExpStream& rStrm ) +{ + DBG_ASSERT( maData.mxEscherSet.is(), "XclExpChEscherFormat::WriteBody - missing property container" ); + // write Escher property container via temporary memory stream + SvMemoryStream aMemStrm; + maData.mxEscherSet->Commit( aMemStrm ); + aMemStrm.Seek( STREAM_SEEK_TO_BEGIN ); + rStrm.CopyFromStream( aMemStrm ); +} + +// ---------------------------------------------------------------------------- + +XclExpChFrameBase::XclExpChFrameBase() +{ +} + +XclExpChFrameBase::~XclExpChFrameBase() +{ +} + +void XclExpChFrameBase::ConvertFrameBase( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + // line format + mxLineFmt.reset( new XclExpChLineFormat( rRoot ) ); + mxLineFmt->Convert( rRoot, rPropSet, eObjType ); + // area format (only for frame objects) + if( rRoot.GetFormatInfo( eObjType ).mbIsFrame ) + { + mxAreaFmt.reset( new XclExpChAreaFormat( rRoot ) ); + bool bComplexFill = mxAreaFmt->Convert( rRoot, rPropSet, eObjType ); + if( (rRoot.GetBiff() == EXC_BIFF8) && bComplexFill ) + { + mxEscherFmt.reset( new XclExpChEscherFormat( rRoot ) ); + mxEscherFmt->Convert( rPropSet, eObjType ); + if( mxEscherFmt->IsValid() ) + mxAreaFmt->SetAuto( false ); + else + mxEscherFmt.reset(); + } + } +} + +void XclExpChFrameBase::SetDefaultFrameBase( const XclExpChRoot& rRoot, + XclChFrameType eDefFrameType, bool bIsFrame ) +{ + // line format + mxLineFmt.reset( new XclExpChLineFormat( rRoot ) ); + mxLineFmt->SetDefault( eDefFrameType ); + // area format (only for frame objects) + if( bIsFrame ) + { + mxAreaFmt.reset( new XclExpChAreaFormat( rRoot ) ); + mxAreaFmt->SetDefault( eDefFrameType ); + mxEscherFmt.reset(); + } +} + +bool XclExpChFrameBase::IsDefaultFrameBase( XclChFrameType eDefFrameType ) const +{ + return + (!mxLineFmt || mxLineFmt->IsDefault( eDefFrameType )) && + (!mxAreaFmt || mxAreaFmt->IsDefault( eDefFrameType )); +} + +void XclExpChFrameBase::WriteFrameRecords( XclExpStream& rStrm ) +{ + lclSaveRecord( rStrm, mxLineFmt ); + lclSaveRecord( rStrm, mxAreaFmt ); + lclSaveRecord( rStrm, mxEscherFmt ); +} + +// ---------------------------------------------------------------------------- + +XclExpChFrame::XclExpChFrame( const XclExpChRoot& rRoot, XclChObjectType eObjType ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_FRAME, EXC_ID_CHFRAME, 4 ), + meObjType( eObjType ) +{ +} + +void XclExpChFrame::Convert( const ScfPropertySet& rPropSet ) +{ + ConvertFrameBase( GetChRoot(), rPropSet, meObjType ); +} + +bool XclExpChFrame::IsDefault() const +{ + return IsDefaultFrameBase( GetFormatInfo( meObjType ).meDefFrameType ); +} + +bool XclExpChFrame::IsDeleteable() const +{ + return IsDefault() && GetFormatInfo( meObjType ).mbDeleteDefFrame; +} + +void XclExpChFrame::Save( XclExpStream& rStrm ) +{ + switch( meObjType ) + { + // wall/floor frame without CHFRAME header record + case EXC_CHOBJTYPE_WALL3D: + case EXC_CHOBJTYPE_FLOOR3D: + WriteFrameRecords( rStrm ); + break; + default: + XclExpChGroupBase::Save( rStrm ); + } +} + +void XclExpChFrame::WriteSubRecords( XclExpStream& rStrm ) +{ + WriteFrameRecords( rStrm ); +} + +void XclExpChFrame::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnFormat << maData.mnFlags; +} + +namespace { + +/** Creates a CHFRAME record from the passed property set. */ +XclExpChFrameRef lclCreateFrame( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + XclExpChFrameRef xFrame( new XclExpChFrame( rRoot, eObjType ) ); + xFrame->Convert( rPropSet ); + if( xFrame->IsDeleteable() ) + xFrame.reset(); + return xFrame; +} + +} // namespace + +// Source links =============================================================== + +namespace { + +void lclAddDoubleRefData( + ScTokenArray& orArray, const FormulaToken& rToken, + SCsTAB nScTab1, SCsCOL nScCol1, SCsROW nScRow1, + SCsTAB nScTab2, SCsCOL nScCol2, SCsROW nScRow2 ) +{ + ScComplexRefData aComplexRef; + aComplexRef.InitFlags(); + aComplexRef.Ref1.SetFlag3D( true ); + aComplexRef.Ref1.nTab = nScTab1; + aComplexRef.Ref1.nCol = nScCol1; + aComplexRef.Ref1.nRow = nScRow1; + aComplexRef.Ref2.nTab = nScTab2; + aComplexRef.Ref2.nCol = nScCol2; + aComplexRef.Ref2.nRow = nScRow2; + + if( orArray.GetLen() > 0 ) + orArray.AddOpCode( ocUnion ); + + DBG_ASSERT( (rToken.GetType() == ::formula::svDoubleRef) || (rToken.GetType() == ::formula::svExternalDoubleRef), + "lclAddDoubleRefData - double reference token expected"); + if( rToken.GetType() == ::formula::svExternalDoubleRef ) + orArray.AddExternalDoubleReference( rToken.GetIndex(), rToken.GetString(), aComplexRef ); + else + orArray.AddDoubleReference( aComplexRef ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpChSourceLink::XclExpChSourceLink( const XclExpChRoot& rRoot, sal_uInt8 nDestType ) : + XclExpRecord( EXC_ID_CHSOURCELINK ), + XclExpChRoot( rRoot ) +{ + maData.mnDestType = nDestType; + maData.mnLinkType = EXC_CHSRCLINK_DIRECTLY; +} + +sal_uInt16 XclExpChSourceLink::ConvertDataSequence( Reference< XDataSequence > xDataSeq, bool bSplitToColumns, sal_uInt16 nDefCount ) +{ + mxLinkFmla.reset(); + maData.mnLinkType = EXC_CHSRCLINK_DEFAULT; + + if( !xDataSeq.is() ) + return nDefCount; + + // Compile the range representation string into token array. Note that the + // source range text depends on the current grammar. + OUString aRangeRepr = xDataSeq->getSourceRangeRepresentation(); + ScCompiler aComp( GetDocPtr(), ScAddress() ); + aComp.SetGrammar( GetDocPtr()->GetGrammar() ); + ScTokenArray* pArray = aComp.CompileString( aRangeRepr ); + if( !pArray ) + return nDefCount; + + ScTokenArray aArray; + sal_uInt32 nValueCount = 0; + pArray->Reset(); + for( const FormulaToken* pToken = pArray->First(); pToken; pToken = pArray->Next() ) + { + switch( pToken->GetType() ) + { + case ::formula::svSingleRef: + case ::formula::svExternalSingleRef: + // for a single ref token, just add it to the new token array as is + if( aArray.GetLen() > 0 ) + aArray.AddOpCode( ocUnion ); + aArray.AddToken( *pToken ); + ++nValueCount; + break; + + case ::formula::svDoubleRef: + case ::formula::svExternalDoubleRef: + { + // split 3-dimensional ranges into single sheets + const ScComplexRefData& rComplexRef = static_cast< const ScToken* >( pToken )->GetDoubleRef(); + const ScSingleRefData& rRef1 = rComplexRef.Ref1; + const ScSingleRefData& rRef2 = rComplexRef.Ref2; + for( SCsTAB nScTab = rRef1.nTab; nScTab <= rRef2.nTab; ++nScTab ) + { + // split 2-dimensional ranges into single columns + if( bSplitToColumns && (rRef1.nCol < rRef2.nCol) && (rRef1.nRow < rRef2.nRow) ) + for( SCsCOL nScCol = rRef1.nCol; nScCol <= rRef2.nCol; ++nScCol ) + lclAddDoubleRefData( aArray, *pToken, nScTab, nScCol, rRef1.nRow, nScTab, nScCol, rRef2.nRow ); + else + lclAddDoubleRefData( aArray, *pToken, nScTab, rRef1.nCol, rRef1.nRow, nScTab, rRef2.nCol, rRef2.nRow ); + } + sal_uInt32 nTabs = static_cast< sal_uInt32 >( rRef2.nTab - rRef1.nTab + 1 ); + sal_uInt32 nCols = static_cast< sal_uInt32 >( rRef2.nCol - rRef1.nCol + 1 ); + sal_uInt32 nRows = static_cast< sal_uInt32 >( rRef2.nRow - rRef1.nRow + 1 ); + nValueCount += nCols * nRows * nTabs; + } + break; + + default:; + } + } + + const ScAddress aBaseCell; + mxLinkFmla = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CHART, aArray, &aBaseCell ); + maData.mnLinkType = EXC_CHSRCLINK_WORKSHEET; + return ulimit_cast< sal_uInt16 >( nValueCount, EXC_CHDATAFORMAT_MAXPOINTCOUNT ); +} + +sal_uInt16 XclExpChSourceLink::ConvertStringSequence( const Sequence< Reference< XFormattedString > >& rStringSeq ) +{ + mxString.reset(); + sal_uInt16 nFontIdx = EXC_FONT_APP; + if( rStringSeq.hasElements() ) + { + mxString = XclExpStringHelper::CreateString( GetRoot(), String::EmptyString(), EXC_STR_FORCEUNICODE | EXC_STR_8BITLENGTH | EXC_STR_SEPARATEFORMATS ); + Reference< XBreakIterator > xBreakIt = GetDoc().GetBreakIterator(); + namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + + // convert all formatted string entries from the sequence + const Reference< XFormattedString >* pBeg = rStringSeq.getConstArray(); + const Reference< XFormattedString >* pEnd = pBeg + rStringSeq.getLength(); + for( const Reference< XFormattedString >* pIt = pBeg; pIt != pEnd; ++pIt ) + { + if( pIt->is() ) + { + sal_uInt16 nWstrnFontIdx = EXC_FONT_NOTFOUND; + sal_uInt16 nAsianFontIdx = EXC_FONT_NOTFOUND; + sal_uInt16 nCmplxFontIdx = EXC_FONT_NOTFOUND; + OUString aText = (*pIt)->getString(); + ScfPropertySet aStrProp( *pIt ); + + // #i63255# get script type for leading weak characters + sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( GetRoot(), aText ); + + // process all script portions + sal_Int32 nPortionPos = 0; + sal_Int32 nTextLen = aText.getLength(); + while( nPortionPos < nTextLen ) + { + // get script type and end position of next script portion + sal_Int16 nScript = xBreakIt->getScriptType( aText, nPortionPos ); + sal_Int32 nPortionEnd = xBreakIt->endOfScript( aText, nPortionPos, nScript ); + + // reuse previous script for following weak portions + if( nScript == ApiScriptType::WEAK ) + nScript = nLastScript; + + // Excel start position of this portion + sal_uInt16 nXclPortionStart = mxString->Len(); + // add portion text to Excel string + XclExpStringHelper::AppendString( *mxString, GetRoot(), aText.copy( nPortionPos, nPortionEnd - nPortionPos ) ); + if( nXclPortionStart < mxString->Len() ) + { + // find font index variable dependent on script type + sal_uInt16& rnFontIdx = (nScript == ApiScriptType::COMPLEX) ? nCmplxFontIdx : + ((nScript == ApiScriptType::ASIAN) ? nAsianFontIdx : nWstrnFontIdx); + + // insert font into buffer (if not yet done) + if( rnFontIdx == EXC_FONT_NOTFOUND ) + rnFontIdx = ConvertFont( aStrProp, nScript ); + + // insert font index into format run vector + mxString->AppendFormat( nXclPortionStart, rnFontIdx ); + } + + // go to next script portion + nLastScript = nScript; + nPortionPos = nPortionEnd; + } + } + } + if( !mxString->IsEmpty() ) + { + // get leading font index + const XclFormatRunVec& rFormats = mxString->GetFormats(); + DBG_ASSERT( !rFormats.empty() && (rFormats.front().mnChar == 0), + "XclExpChSourceLink::ConvertStringSequenc - missing leading format" ); + // remove leading format run, if entire string is equally formatted + if( rFormats.size() == 1 ) + nFontIdx = mxString->RemoveLeadingFont(); + else if( !rFormats.empty() ) + nFontIdx = rFormats.front().mnFontIdx; + // add trailing format run, if string is rich-formatted + if( mxString->IsRich() ) + mxString->AppendTrailingFormat( EXC_FONT_APP ); + } + } + return nFontIdx; +} + +void XclExpChSourceLink::ConvertNumFmt( const ScfPropertySet& rPropSet, bool bPercent ) +{ + sal_Int32 nApiNumFmt = 0; + if( bPercent ? rPropSet.GetProperty( nApiNumFmt, EXC_CHPROP_PERCENTAGENUMFMT ) : rPropSet.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) ) + { + ::set_flag( maData.mnFlags, EXC_CHSRCLINK_NUMFMT ); + maData.mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) ); + } +} + +void XclExpChSourceLink::Save( XclExpStream& rStrm ) +{ + // CHFORMATRUNS record + if( mxString.is() && mxString->IsRich() ) + { + sal_Size nRecSize = (1 + mxString->GetFormatsCount()) * ((GetBiff() == EXC_BIFF8) ? 2 : 1); + rStrm.StartRecord( EXC_ID_CHFORMATRUNS, nRecSize ); + mxString->WriteFormats( rStrm, true ); + rStrm.EndRecord(); + } + // CHSOURCELINK record + XclExpRecord::Save( rStrm ); + // CHSTRING record + if( mxString.is() && !mxString->IsEmpty() ) + { + rStrm.StartRecord( EXC_ID_CHSTRING, 2 + mxString->GetSize() ); + rStrm << sal_uInt16( 0 ) << *mxString; + rStrm.EndRecord(); + } +} + +void XclExpChSourceLink::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnDestType + << maData.mnLinkType + << maData.mnFlags + << maData.mnNumFmtIdx + << mxLinkFmla; +} + +// Text ======================================================================= + +XclExpChFont::XclExpChFont( sal_uInt16 nFontIdx ) : + XclExpUInt16Record( EXC_ID_CHFONT, nFontIdx ) +{ +} + +// ---------------------------------------------------------------------------- + +XclExpChObjectLink::XclExpChObjectLink( sal_uInt16 nLinkTarget, const XclChDataPointPos& rPointPos ) : + XclExpRecord( EXC_ID_CHOBJECTLINK, 6 ) +{ + maData.mnTarget = nLinkTarget; + maData.maPointPos = rPointPos; +} + +void XclExpChObjectLink::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnTarget << maData.maPointPos.mnSeriesIdx << maData.maPointPos.mnPointIdx; +} + +// ---------------------------------------------------------------------------- + +XclExpChFrLabelProps::XclExpChFrLabelProps( const XclExpChRoot& rRoot ) : + XclExpChFutureRecordBase( rRoot, EXC_FUTUREREC_UNUSEDREF, EXC_ID_CHFRLABELPROPS, 4 ) +{ +} + +void XclExpChFrLabelProps::Convert( const ScfPropertySet& rPropSet, bool bShowSeries, + bool bShowCateg, bool bShowValue, bool bShowPercent, bool bShowBubble ) +{ + // label value flags + ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWSERIES, bShowSeries ); + ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWCATEG, bShowCateg ); + ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWVALUE, bShowValue ); + ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWPERCENT, bShowPercent ); + ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWBUBBLE, bShowBubble ); + + // label value separator + rPropSet.GetStringProperty( maData.maSeparator, EXC_CHPROP_LABELSEPARATOR ); + if( maData.maSeparator.Len() == 0 ) + maData.maSeparator = String( sal_Unicode( ' ' ) ); +} + +void XclExpChFrLabelProps::WriteBody( XclExpStream& rStrm ) +{ + XclExpString aXclSep( maData.maSeparator, EXC_STR_FORCEUNICODE | EXC_STR_SMARTFLAGS ); + rStrm << maData.mnFlags << aXclSep; +} + +// ---------------------------------------------------------------------------- + +XclExpChFontBase::~XclExpChFontBase() +{ +} + +void XclExpChFontBase::ConvertFontBase( const XclExpChRoot& rRoot, sal_uInt16 nFontIdx ) +{ + if( const XclExpFont* pFont = rRoot.GetFontBuffer().GetFont( nFontIdx ) ) + { + XclExpChFontRef xFont( new XclExpChFont( nFontIdx ) ); + SetFont( xFont, pFont->GetFontData().maColor, pFont->GetFontColorId() ); + } +} + +void XclExpChFontBase::ConvertFontBase( const XclExpChRoot& rRoot, const ScfPropertySet& rPropSet ) +{ + ConvertFontBase( rRoot, rRoot.ConvertFont( rPropSet, rRoot.GetDefApiScript() ) ); +} + +void XclExpChFontBase::ConvertRotationBase( + const XclExpChRoot& rRoot, const ScfPropertySet& rPropSet, bool bSupportsStacked ) +{ + sal_uInt16 nRotation = rRoot.GetChartPropSetHelper().ReadRotationProperties( rPropSet, bSupportsStacked ); + SetRotation( nRotation ); +} + +// ---------------------------------------------------------------------------- + +XclExpChText::XclExpChText( const XclExpChRoot& rRoot ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_TEXT, EXC_ID_CHTEXT, (rRoot.GetBiff() == EXC_BIFF8) ? 32 : 26 ), + mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ) +{ +} + +void XclExpChText::SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId ) +{ + mxFont = xFont; + maData.maTextColor = rColor; + ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR, rColor == COL_AUTO ); + mnTextColorId = nColorId; +} + +void XclExpChText::SetRotation( sal_uInt16 nRotation ) +{ + maData.mnRotation = nRotation; + ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 8, 3 ); +} + +void XclExpChText::ConvertTitle( Reference< XTitle > xTitle, sal_uInt16 nTarget ) +{ + switch( nTarget ) + { + case EXC_CHOBJLINK_TITLE: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_TITLE ); break; + case EXC_CHOBJLINK_YAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 1 ); break; + case EXC_CHOBJLINK_XAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 0 ); break; + case EXC_CHOBJLINK_ZAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 2 ); break; + } + + mxSrcLink.reset(); + mxObjLink.reset( new XclExpChObjectLink( nTarget, XclChDataPointPos( 0, 0 ) ) ); + + if( xTitle.is() ) + { + // title frame formatting + ScfPropertySet aTitleProp( xTitle ); + mxFrame = lclCreateFrame( GetChRoot(), aTitleProp, EXC_CHOBJTYPE_TEXT ); + + // string sequence + mxSrcLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE ) ); + sal_uInt16 nFontIdx = mxSrcLink->ConvertStringSequence( xTitle->getText() ); + ConvertFontBase( GetChRoot(), nFontIdx ); + + // rotation + ConvertRotationBase( GetChRoot(), aTitleProp, true ); + + // manual text position - only for main title + mxFramePos.reset( new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT, EXC_CHFRAMEPOS_PARENT ) ); + if( nTarget == EXC_CHOBJLINK_TITLE ) + { + Any aRelPos; + if( aTitleProp.GetAnyProperty( aRelPos, EXC_CHPROP_RELATIVEPOSITION ) && aRelPos.has< RelativePosition >() ) try + { + // calculate absolute position for CHTEXT record + Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW ); + Reference< XShape > xTitleShape( xChart1Doc->getTitle(), UNO_SET_THROW ); + ::com::sun::star::awt::Point aPos = xTitleShape->getPosition(); + ::com::sun::star::awt::Size aSize = xTitleShape->getSize(); + ::com::sun::star::awt::Rectangle aRect( aPos.X, aPos.Y, aSize.Width, aSize.Height ); + maData.maRect = CalcChartRectFromHmm( aRect ); + ::insert_value( maData.mnFlags2, EXC_CHTEXT_POS_MOVED, 0, 4 ); + // manual title position implies manual plot area + GetChartData().SetManualPlotArea(); + // calculate the default title position in chart units + sal_Int32 nDefPosX = ::std::max< sal_Int32 >( (EXC_CHART_TOTALUNITS - maData.maRect.mnWidth) / 2, 0 ); + sal_Int32 nDefPosY = 85; + // set the position relative to the standard position + XclChRectangle& rFrameRect = mxFramePos->GetFramePosData().maRect; + rFrameRect.mnX = maData.maRect.mnX - nDefPosX; + rFrameRect.mnY = maData.maRect.mnY - nDefPosY; + } + catch( Exception& ) + { + } + } + } + else + { + ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED ); + } +} + +void XclExpChText::ConvertLegend( const ScfPropertySet& rPropSet ) +{ + ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOGEN ); + ConvertFontBase( GetChRoot(), rPropSet ); +} + +bool XclExpChText::ConvertDataLabel( const ScfPropertySet& rPropSet, + const XclChTypeInfo& rTypeInfo, const XclChDataPointPos& rPointPos ) +{ + SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_DATALABEL, rPointPos.mnPointIdx, rPointPos.mnSeriesIdx ); + + cssc2::DataPointLabel aPointLabel; + if( !rPropSet.GetProperty( aPointLabel, EXC_CHPROP_LABEL ) ) + return false; + + // percentage only allowed in pie and donut charts + bool bIsPie = rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE; + // bubble sizes only allowed in bubble charts + bool bIsBubble = rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES; + DBG_ASSERT( (GetBiff() == EXC_BIFF8) || !bIsBubble, "XclExpChText::ConvertDataLabel - bubble charts only in BIFF8" ); + + // raw show flags + bool bShowValue = !bIsBubble && aPointLabel.ShowNumber; // Chart2 uses 'ShowNumber' for bubble size + bool bShowPercent = bIsPie && aPointLabel.ShowNumberInPercent; // percentage only in pie/donut charts + bool bShowCateg = aPointLabel.ShowCategoryName; + bool bShowBubble = bIsBubble && aPointLabel.ShowNumber; // Chart2 uses 'ShowNumber' for bubble size + bool bShowAny = bShowValue || bShowPercent || bShowCateg || bShowBubble; + + // create the CHFRLABELPROPS record for extended settings in BIFF8 + if( bShowAny && (GetBiff() == EXC_BIFF8) ) + { + mxLabelProps.reset( new XclExpChFrLabelProps( GetChRoot() ) ); + mxLabelProps->Convert( rPropSet, false, bShowCateg, bShowValue, bShowPercent, bShowBubble ); + } + + // restrict to combinations allowed in CHTEXT + if( bShowPercent ) bShowValue = false; // percent wins over value + if( bShowValue ) bShowCateg = false; // value wins over category + if( bShowValue || bShowCateg ) bShowBubble = false; // value or category wins over bubble size + + // set all flags + ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE, bShowValue ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT, bShowPercent ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG, bShowCateg ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bShowPercent && bShowCateg ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWBUBBLE, bShowBubble ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL, bShowAny && aPointLabel.ShowLegendSymbol ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bShowAny ); + + if( bShowAny ) + { + // font settings + ConvertFontBase( GetChRoot(), rPropSet ); + ConvertRotationBase( GetChRoot(), rPropSet, false ); + // label placement + sal_Int32 nPlacement = 0; + sal_uInt16 nLabelPos = EXC_CHTEXT_POS_AUTO; + if( rPropSet.GetProperty( nPlacement, EXC_CHPROP_LABELPLACEMENT ) ) + { + using namespace ::com::sun::star::chart::DataLabelPlacement; + if( nPlacement == rTypeInfo.mnDefaultLabelPos ) + { + nLabelPos = EXC_CHTEXT_POS_DEFAULT; + } + else switch( nPlacement ) + { + case AVOID_OVERLAP: nLabelPos = EXC_CHTEXT_POS_AUTO; break; + case CENTER: nLabelPos = EXC_CHTEXT_POS_CENTER; break; + case TOP: nLabelPos = EXC_CHTEXT_POS_ABOVE; break; + case TOP_LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break; + case LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break; + case BOTTOM_LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break; + case BOTTOM: nLabelPos = EXC_CHTEXT_POS_BELOW; break; + case BOTTOM_RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break; + case RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break; + case TOP_RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break; + case INSIDE: nLabelPos = EXC_CHTEXT_POS_INSIDE; break; + case OUTSIDE: nLabelPos = EXC_CHTEXT_POS_OUTSIDE; break; + case NEAR_ORIGIN: nLabelPos = EXC_CHTEXT_POS_AXIS; break; + default: DBG_ERRORFILE( "XclExpChText::ConvertDataLabel - unknown label placement type" ); + } + } + ::insert_value( maData.mnFlags2, nLabelPos, 0, 4 ); + // source link (contains number format) + mxSrcLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE ) ); + if( bShowValue || bShowPercent ) + // percentage format wins over value format + mxSrcLink->ConvertNumFmt( rPropSet, bShowPercent ); + // object link + mxObjLink.reset( new XclExpChObjectLink( EXC_CHOBJLINK_DATA, rPointPos ) ); + } + + /* Return true to indicate valid label settings: + - for existing labels at entire series + - for any settings at single data point (to be able to delete a point label) */ + return bShowAny || (rPointPos.mnPointIdx != EXC_CHDATAFORMAT_ALLPOINTS); +} + +void XclExpChText::ConvertTrendLineEquation( const ScfPropertySet& rPropSet, const XclChDataPointPos& rPointPos ) +{ + // required flags + ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT ); + if( GetBiff() == EXC_BIFF8 ) + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG ); // must set this to make equation visible in Excel + // frame formatting + mxFrame = lclCreateFrame( GetChRoot(), rPropSet, EXC_CHOBJTYPE_TEXT ); + // font settings + maData.mnHAlign = EXC_CHTEXT_ALIGN_TOPLEFT; + maData.mnVAlign = EXC_CHTEXT_ALIGN_TOPLEFT; + ConvertFontBase( GetChRoot(), rPropSet ); + // source link (contains number format) + mxSrcLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE ) ); + mxSrcLink->ConvertNumFmt( rPropSet, false ); + // object link + mxObjLink.reset( new XclExpChObjectLink( EXC_CHOBJLINK_DATA, rPointPos ) ); +} + +sal_uInt16 XclExpChText::GetAttLabelFlags() const +{ + sal_uInt16 nFlags = 0; + ::set_flag( nFlags, EXC_CHATTLABEL_SHOWVALUE, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE ) ); + ::set_flag( nFlags, EXC_CHATTLABEL_SHOWPERCENT, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT ) ); + ::set_flag( nFlags, EXC_CHATTLABEL_SHOWCATEGPERC, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC ) ); + ::set_flag( nFlags, EXC_CHATTLABEL_SHOWCATEG, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG ) ); + return nFlags; +} + +void XclExpChText::WriteSubRecords( XclExpStream& rStrm ) +{ + // CHFRAMEPOS record + lclSaveRecord( rStrm, mxFramePos ); + // CHFONT record + lclSaveRecord( rStrm, mxFont ); + // CHSOURCELINK group + lclSaveRecord( rStrm, mxSrcLink ); + // CHFRAME group + lclSaveRecord( rStrm, mxFrame ); + // CHOBJECTLINK record + lclSaveRecord( rStrm, mxObjLink ); + // CHFRLABELPROPS record + lclSaveRecord( rStrm, mxLabelProps ); +} + +void XclExpChText::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnHAlign + << maData.mnVAlign + << maData.mnBackMode + << maData.maTextColor + << maData.maRect + << maData.mnFlags; + + if( GetBiff() == EXC_BIFF8 ) + { + rStrm << GetPalette().GetColorIndex( mnTextColorId ) + << maData.mnFlags2 + << maData.mnRotation; + } +} + +// ---------------------------------------------------------------------------- + +namespace { + +/** Creates and returns an Excel text object from the passed title. */ +XclExpChTextRef lclCreateTitle( const XclExpChRoot& rRoot, Reference< XTitled > xTitled, sal_uInt16 nTarget ) +{ + Reference< XTitle > xTitle; + if( xTitled.is() ) + xTitle = xTitled->getTitleObject(); + + XclExpChTextRef xText( new XclExpChText( rRoot ) ); + xText->ConvertTitle( xTitle, nTarget ); + /* Do not delete the CHTEXT group for the main title. A missing CHTEXT + will be interpreted as auto-generated title showing the series title in + charts that contain exactly one data series. */ + if( (nTarget != EXC_CHOBJLINK_TITLE) && !xText->HasString() ) + xText.reset(); + + return xText; +} + +} + +// Data series ================================================================ + +XclExpChMarkerFormat::XclExpChMarkerFormat( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHMARKERFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 20 : 12 ), + mnLineColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ), + mnFillColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ) +{ +} + +void XclExpChMarkerFormat::Convert( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) +{ + rRoot.GetChartPropSetHelper().ReadMarkerProperties( maData, rPropSet, nFormatIdx ); + /* Set marker line/fill color to series line color. + TODO: remove this if OOChart supports own colors in markers. */ + Color aLineColor; + if( rPropSet.GetColorProperty( aLineColor, EXC_CHPROP_COLOR ) ) + maData.maLineColor = maData.maFillColor = aLineColor; + // register colors in palette + RegisterColors( rRoot ); +} + +void XclExpChMarkerFormat::ConvertStockSymbol( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, bool bCloseSymbol ) +{ + // clear the automatic flag + ::set_flag( maData.mnFlags, EXC_CHMARKERFORMAT_AUTO, false ); + // symbol type and color + if( bCloseSymbol ) + { + // set symbol type for the 'close' data series + maData.mnMarkerType = EXC_CHMARKERFORMAT_DOWJ; + maData.mnMarkerSize = EXC_CHMARKERFORMAT_DOUBLESIZE; + // set symbol line/fill color to series line color + Color aLineColor; + if( rPropSet.GetColorProperty( aLineColor, EXC_CHPROP_COLOR ) ) + { + maData.maLineColor = maData.maFillColor = aLineColor; + RegisterColors( rRoot ); + } + } + else + { + // set invisible symbol + maData.mnMarkerType = EXC_CHMARKERFORMAT_NOSYMBOL; + } +} + +void XclExpChMarkerFormat::RegisterColors( const XclExpChRoot& rRoot ) +{ + if( HasMarker() ) + { + if( HasLineColor() ) + mnLineColorId = rRoot.GetPalette().InsertColor( maData.maLineColor, EXC_COLOR_CHARTLINE ); + if( HasFillColor() ) + mnFillColorId = rRoot.GetPalette().InsertColor( maData.maFillColor, EXC_COLOR_CHARTAREA ); + } +} + +void XclExpChMarkerFormat::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.maLineColor << maData.maFillColor << maData.mnMarkerType << maData.mnFlags; + if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ) + { + const XclExpPalette& rPal = rStrm.GetRoot().GetPalette(); + rStrm << rPal.GetColorIndex( mnLineColorId ) << rPal.GetColorIndex( mnFillColorId ) << maData.mnMarkerSize; + } +} + +// ---------------------------------------------------------------------------- + +XclExpChPieFormat::XclExpChPieFormat() : + XclExpUInt16Record( EXC_ID_CHPIEFORMAT, 0 ) +{ +} + +void XclExpChPieFormat::Convert( const ScfPropertySet& rPropSet ) +{ + double fApiDist(0.0); + if( rPropSet.GetProperty( fApiDist, EXC_CHPROP_OFFSET ) ) + SetValue( limit_cast< sal_uInt16 >( fApiDist * 100.0, 0, 100 ) ); +} + +// ---------------------------------------------------------------------------- + +XclExpCh3dDataFormat::XclExpCh3dDataFormat() : + XclExpRecord( EXC_ID_CH3DDATAFORMAT, 2 ) +{ +} + +void XclExpCh3dDataFormat::Convert( const ScfPropertySet& rPropSet ) +{ + sal_Int32 nApiType(0); + if( rPropSet.GetProperty( nApiType, EXC_CHPROP_GEOMETRY3D ) ) + { + using namespace ::com::sun::star::chart2::DataPointGeometry3D; + switch( nApiType ) + { + case CUBOID: + maData.mnBase = EXC_CH3DDATAFORMAT_RECT; + maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT; + break; + case PYRAMID: + maData.mnBase = EXC_CH3DDATAFORMAT_RECT; + maData.mnTop = EXC_CH3DDATAFORMAT_SHARP; + break; + case CYLINDER: + maData.mnBase = EXC_CH3DDATAFORMAT_CIRC; + maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT; + break; + case CONE: + maData.mnBase = EXC_CH3DDATAFORMAT_CIRC; + maData.mnTop = EXC_CH3DDATAFORMAT_SHARP; + break; + default: + DBG_ERRORFILE( "XclExpCh3dDataFormat::Convert - unknown 3D bar format" ); + } + } +} + +void XclExpCh3dDataFormat::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnBase << maData.mnTop; +} + +// ---------------------------------------------------------------------------- + +XclExpChAttachedLabel::XclExpChAttachedLabel( sal_uInt16 nFlags ) : + XclExpUInt16Record( EXC_ID_CHATTACHEDLABEL, nFlags ) +{ +} + +// ---------------------------------------------------------------------------- + +XclExpChDataFormat::XclExpChDataFormat( const XclExpChRoot& rRoot, + const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_DATAFORMAT, EXC_ID_CHDATAFORMAT, 8 ) +{ + maData.maPointPos = rPointPos; + maData.mnFormatIdx = nFormatIdx; +} + +void XclExpChDataFormat::ConvertDataSeries( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo ) +{ + // line and area formatting + ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType() ); + + // data point symbols + bool bIsFrame = rTypeInfo.IsSeriesFrameFormat(); + if( !bIsFrame ) + { + mxMarkerFmt.reset( new XclExpChMarkerFormat( GetChRoot() ) ); + mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx ); + } + + // pie segments + if( rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE ) + { + mxPieFmt.reset( new XclExpChPieFormat ); + mxPieFmt->Convert( rPropSet ); + } + + // 3D bars (only allowed for entire series in BIFF8) + if( IsSeriesFormat() && (GetBiff() == EXC_BIFF8) && rTypeInfo.mb3dChart && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR) ) + { + mx3dDataFmt.reset( new XclExpCh3dDataFormat ); + mx3dDataFmt->Convert( rPropSet ); + } + + // spline + if( IsSeriesFormat() && rTypeInfo.mbSpline && !bIsFrame ) + mxSeriesFmt.reset( new XclExpUInt16Record( EXC_ID_CHSERIESFORMAT, EXC_CHSERIESFORMAT_SMOOTHED ) ); + + // data point labels + XclExpChTextRef xLabel( new XclExpChText( GetChRoot() ) ); + if( xLabel->ConvertDataLabel( rPropSet, rTypeInfo, maData.maPointPos ) ) + { + // CHTEXT groups for data labels are stored in global CHCHART group + GetChartData().SetDataLabel( xLabel ); + mxAttLabel.reset( new XclExpChAttachedLabel( xLabel->GetAttLabelFlags() ) ); + } +} + +void XclExpChDataFormat::ConvertStockSeries( const ScfPropertySet& rPropSet, bool bCloseSymbol ) +{ + // set line format to invisible + SetDefaultFrameBase( GetChRoot(), EXC_CHFRAMETYPE_INVISIBLE, false ); + // set symbols to invisible or to 'close' series symbol + mxMarkerFmt.reset( new XclExpChMarkerFormat( GetChRoot() ) ); + mxMarkerFmt->ConvertStockSymbol( GetChRoot(), rPropSet, bCloseSymbol ); +} + +void XclExpChDataFormat::ConvertLine( const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + ConvertFrameBase( GetChRoot(), rPropSet, eObjType ); +} + +void XclExpChDataFormat::WriteSubRecords( XclExpStream& rStrm ) +{ + lclSaveRecord( rStrm, mx3dDataFmt ); + WriteFrameRecords( rStrm ); + lclSaveRecord( rStrm, mxPieFmt ); + lclSaveRecord( rStrm, mxMarkerFmt ); + lclSaveRecord( rStrm, mxSeriesFmt ); + lclSaveRecord( rStrm, mxAttLabel ); +} + +void XclExpChDataFormat::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.maPointPos.mnPointIdx + << maData.maPointPos.mnSeriesIdx + << maData.mnFormatIdx + << maData.mnFlags; +} + +// ---------------------------------------------------------------------------- + +XclExpChSerTrendLine::XclExpChSerTrendLine( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHSERTRENDLINE, 28 ), + XclExpChRoot( rRoot ) +{ +} + +bool XclExpChSerTrendLine::Convert( Reference< XRegressionCurve > xRegCurve, sal_uInt16 nSeriesIdx ) +{ + if( !xRegCurve.is() ) + return false; + + // trend line type + ScfPropertySet aCurveProp( xRegCurve ); + OUString aService = aCurveProp.GetServiceName(); + if( aService == SERVICE_CHART2_LINEARREGCURVE ) + { + maData.mnLineType = EXC_CHSERTREND_POLYNOMIAL; + maData.mnOrder = 1; + } + else if( aService == SERVICE_CHART2_EXPREGCURVE ) + maData.mnLineType = EXC_CHSERTREND_EXPONENTIAL; + else if( aService == SERVICE_CHART2_LOGREGCURVE ) + maData.mnLineType = EXC_CHSERTREND_LOGARITHMIC; + else if( aService == SERVICE_CHART2_POTREGCURVE ) + maData.mnLineType = EXC_CHSERTREND_POWER; + else + return false; + + // line formatting + XclChDataPointPos aPointPos( nSeriesIdx ); + mxDataFmt.reset( new XclExpChDataFormat( GetChRoot(), aPointPos, 0 ) ); + mxDataFmt->ConvertLine( aCurveProp, EXC_CHOBJTYPE_TRENDLINE ); + + // #i83100# show equation and correlation coefficient + ScfPropertySet aEquationProp( xRegCurve->getEquationProperties() ); + maData.mnShowEquation = aEquationProp.GetBoolProperty( EXC_CHPROP_SHOWEQUATION ) ? 1 : 0; + maData.mnShowRSquared = aEquationProp.GetBoolProperty( EXC_CHPROP_SHOWCORRELATION ) ? 1 : 0; + + // #i83100# formatting of the equation text box + if( (maData.mnShowEquation != 0) || (maData.mnShowRSquared != 0) ) + { + mxLabel.reset( new XclExpChText( GetChRoot() ) ); + mxLabel->ConvertTrendLineEquation( aEquationProp, aPointPos ); + } + + // missing features + // #i20819# polynomial trend lines + // #i66819# moving average trend lines + // #i5085# manual trend line size + // #i34093# manual crossing point + return true; +} + +void XclExpChSerTrendLine::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnLineType + << maData.mnOrder + << maData.mfIntercept + << maData.mnShowEquation + << maData.mnShowRSquared + << maData.mfForecastFor + << maData.mfForecastBack; +} + +// ---------------------------------------------------------------------------- + +XclExpChSerErrorBar::XclExpChSerErrorBar( const XclExpChRoot& rRoot, sal_uInt8 nBarType ) : + XclExpRecord( EXC_ID_CHSERERRORBAR, 14 ), + XclExpChRoot( rRoot ) +{ + maData.mnBarType = nBarType; +} + +bool XclExpChSerErrorBar::Convert( XclExpChSourceLink& rValueLink, sal_uInt16& rnValueCount, const ScfPropertySet& rPropSet ) +{ + sal_Int32 nBarStyle = 0; + bool bOk = rPropSet.GetProperty( nBarStyle, EXC_CHPROP_ERRORBARSTYLE ); + if( bOk ) + { + switch( nBarStyle ) + { + case cssc::ErrorBarStyle::ABSOLUTE: + maData.mnSourceType = EXC_CHSERERR_FIXED; + rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_POSITIVEERROR ); + break; + case cssc::ErrorBarStyle::RELATIVE: + maData.mnSourceType = EXC_CHSERERR_PERCENT; + rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_POSITIVEERROR ); + break; + case cssc::ErrorBarStyle::STANDARD_DEVIATION: + maData.mnSourceType = EXC_CHSERERR_STDDEV; + rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_WEIGHT ); + break; + case cssc::ErrorBarStyle::STANDARD_ERROR: + maData.mnSourceType = EXC_CHSERERR_STDERR; + break; + case cssc::ErrorBarStyle::FROM_DATA: + { + bOk = false; + maData.mnSourceType = EXC_CHSERERR_CUSTOM; + Reference< XDataSource > xDataSource( rPropSet.GetApiPropertySet(), UNO_QUERY ); + if( xDataSource.is() ) + { + // find first sequence with current role + OUString aRole = XclChartHelper::GetErrorBarValuesRole( maData.mnBarType ); + Reference< XDataSequence > xValueSeq; + + Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences(); + const Reference< XLabeledDataSequence >* pBeg = aLabeledSeqVec.getConstArray(); + const Reference< XLabeledDataSequence >* pEnd = pBeg + aLabeledSeqVec.getLength(); + for( const Reference< XLabeledDataSequence >* pIt = pBeg; !xValueSeq.is() && (pIt != pEnd); ++pIt ) + { + Reference< XDataSequence > xTmpValueSeq = (*pIt)->getValues(); + ScfPropertySet aValueProp( xTmpValueSeq ); + OUString aCurrRole; + if( aValueProp.GetProperty( aCurrRole, EXC_CHPROP_ROLE ) && (aCurrRole == aRole) ) + xValueSeq = xTmpValueSeq; + } + if( xValueSeq.is() ) + { + // #i86465# pass value count back to series + rnValueCount = maData.mnValueCount = rValueLink.ConvertDataSequence( xValueSeq, true ); + bOk = maData.mnValueCount > 0; + } + } + } + break; + default: + bOk = false; + } + } + return bOk; +} + +void XclExpChSerErrorBar::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnBarType + << maData.mnSourceType + << maData.mnLineEnd + << sal_uInt8( 1 ) // must be 1 to make line visible + << maData.mfValue + << maData.mnValueCount; +} + +// ---------------------------------------------------------------------------- + +namespace { + +/** Returns the property set of the specified data point. */ +ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > xDataSeries, sal_Int32 nPointIdx ) +{ + ScfPropertySet aPropSet; + try + { + aPropSet.Set( xDataSeries->getDataPointByIndex( nPointIdx ) ); + } + catch( Exception& ) + { + DBG_ERRORFILE( "lclGetPointPropSet - no data point property set" ); + } + return aPropSet; +} + +} // namespace + +XclExpChSeries::XclExpChSeries( const XclExpChRoot& rRoot, sal_uInt16 nSeriesIdx ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_SERIES, EXC_ID_CHSERIES, (rRoot.GetBiff() == EXC_BIFF8) ? 12 : 8 ), + mnGroupIdx( EXC_CHSERGROUP_NONE ), + mnSeriesIdx( nSeriesIdx ), + mnParentIdx( EXC_CHSERIES_INVALID ) +{ + // CHSOURCELINK records are always required, even if unused + mxTitleLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE ) ); + mxValueLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_VALUES ) ); + mxCategLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_CATEGORY ) ); + if( GetBiff() == EXC_BIFF8 ) + mxBubbleLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_BUBBLES ) ); +} + +bool XclExpChSeries::ConvertDataSeries( + Reference< XDiagram > xDiagram, Reference< XDataSeries > xDataSeries, + const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx ) +{ + bool bOk = false; + Reference< XDataSource > xDataSource( xDataSeries, UNO_QUERY ); + if( xDataSource.is() ) + { + Reference< XDataSequence > xYValueSeq, xTitleSeq, xXValueSeq, xBubbleSeq; + + // find first sequence with role 'values-y' + Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences(); + const Reference< XLabeledDataSequence >* pBeg = aLabeledSeqVec.getConstArray(); + const Reference< XLabeledDataSequence >* pEnd = pBeg + aLabeledSeqVec.getLength(); + for( const Reference< XLabeledDataSequence >* pIt = pBeg; pIt != pEnd; ++pIt ) + { + Reference< XDataSequence > xTmpValueSeq = (*pIt)->getValues(); + ScfPropertySet aValueProp( xTmpValueSeq ); + OUString aRole; + if( aValueProp.GetProperty( aRole, EXC_CHPROP_ROLE ) ) + { + if( !xYValueSeq.is() && (aRole == EXC_CHPROP_ROLE_YVALUES) ) + { + xYValueSeq = xTmpValueSeq; + if( !xTitleSeq.is() ) + xTitleSeq = (*pIt)->getLabel(); // ignore role of label sequence + } + else if( !xXValueSeq.is() && !rTypeInfo.mbCategoryAxis && (aRole == EXC_CHPROP_ROLE_XVALUES) ) + { + xXValueSeq = xTmpValueSeq; + } + else if( !xBubbleSeq.is() && (rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES) && (aRole == EXC_CHPROP_ROLE_SIZEVALUES) ) + { + xBubbleSeq = xTmpValueSeq; + xTitleSeq = (*pIt)->getLabel(); // ignore role of label sequence + } + } + } + + bOk = xYValueSeq.is(); + if( bOk ) + { + // chart type group index + mnGroupIdx = nGroupIdx; + + // convert source links + maData.mnValueCount = mxValueLink->ConvertDataSequence( xYValueSeq, true ); + mxTitleLink->ConvertDataSequence( xTitleSeq, true ); + + // X values of XY charts + maData.mnCategCount = mxCategLink->ConvertDataSequence( xXValueSeq, false, maData.mnValueCount ); + + // size values of bubble charts + if( mxBubbleLink.is() ) + mxBubbleLink->ConvertDataSequence( xBubbleSeq, false, maData.mnValueCount ); + + // series formatting + XclChDataPointPos aPointPos( mnSeriesIdx ); + ScfPropertySet aSeriesProp( xDataSeries ); + mxSeriesFmt.reset( new XclExpChDataFormat( GetChRoot(), aPointPos, nFormatIdx ) ); + mxSeriesFmt->ConvertDataSeries( aSeriesProp, rTypeInfo ); + + // trend lines + CreateTrendLines( xDataSeries ); + + // error bars + CreateErrorBars( aSeriesProp, EXC_CHPROP_ERRORBARX, EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS ); + CreateErrorBars( aSeriesProp, EXC_CHPROP_ERRORBARY, EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS ); + + if( maData.mnValueCount > 0 ) + { + const sal_Int32 nMaxPointCount = maData.mnValueCount; + + /* #i91063# Create missing fill properties in pie/doughnut charts. + If freshly created (never saved to ODF), these charts show + varying point colors but do not return these points via API. */ + if( xDiagram.is() && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE) ) + { + Reference< XColorScheme > xColorScheme = xDiagram->getDefaultColorScheme(); + if( xColorScheme.is() ) + { + const OUString aFillStyleName = CREATE_OUSTRING( "FillStyle" ); + const OUString aColorName = CREATE_OUSTRING( "Color" ); + namespace cssd = ::com::sun::star::drawing; + for( sal_Int32 nPointIdx = 0; nPointIdx < nMaxPointCount; ++nPointIdx ) + { + aPointPos.mnPointIdx = static_cast< sal_uInt16 >( nPointIdx ); + ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx ); + // test that the point fill style is solid, but no color is set + cssd::FillStyle eFillStyle = cssd::FillStyle_NONE; + if( aPointProp.GetProperty( eFillStyle, aFillStyleName ) && + (eFillStyle == cssd::FillStyle_SOLID) && + !aPointProp.HasProperty( aColorName ) ) + { + aPointProp.SetProperty( aColorName, xColorScheme->getColorByIndex( nPointIdx ) ); + } + } + } + } + + // data point formatting + Sequence< sal_Int32 > aPointIndexes; + if( aSeriesProp.GetProperty( aPointIndexes, EXC_CHPROP_ATTRIBDATAPOINTS ) && aPointIndexes.hasElements() ) + { + const sal_Int32* pnBeg = aPointIndexes.getConstArray(); + const sal_Int32* pnEnd = pnBeg + aPointIndexes.getLength(); + for( const sal_Int32* pnIt = pnBeg; (pnIt != pnEnd) && (*pnIt < nMaxPointCount); ++pnIt ) + { + aPointPos.mnPointIdx = static_cast< sal_uInt16 >( *pnIt ); + ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, *pnIt ); + XclExpChDataFormatRef xPointFmt( new XclExpChDataFormat( GetChRoot(), aPointPos, nFormatIdx ) ); + xPointFmt->ConvertDataSeries( aPointProp, rTypeInfo ); + maPointFmts.AppendRecord( xPointFmt ); + } + } + } + } + } + return bOk; +} + +bool XclExpChSeries::ConvertStockSeries( XDataSeriesRef xDataSeries, + const OUString& rValueRole, sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx, bool bCloseSymbol ) +{ + bool bOk = false; + Reference< XDataSource > xDataSource( xDataSeries, UNO_QUERY ); + if( xDataSource.is() ) + { + Reference< XDataSequence > xYValueSeq, xTitleSeq; + + // find first sequence with passed role + Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences(); + const Reference< XLabeledDataSequence >* pBeg = aLabeledSeqVec.getConstArray(); + const Reference< XLabeledDataSequence >* pEnd = pBeg + aLabeledSeqVec.getLength(); + for( const Reference< XLabeledDataSequence >* pIt = pBeg; !xYValueSeq.is() && (pIt != pEnd); ++pIt ) + { + Reference< XDataSequence > xTmpValueSeq = (*pIt)->getValues(); + ScfPropertySet aValueProp( xTmpValueSeq ); + OUString aRole; + if( aValueProp.GetProperty( aRole, EXC_CHPROP_ROLE ) && (aRole == rValueRole) ) + { + xYValueSeq = xTmpValueSeq; + xTitleSeq = (*pIt)->getLabel(); // ignore role of label sequence + } + } + + bOk = xYValueSeq.is(); + if( bOk ) + { + // chart type group index + mnGroupIdx = nGroupIdx; + // convert source links + maData.mnValueCount = mxValueLink->ConvertDataSequence( xYValueSeq, true ); + mxTitleLink->ConvertDataSequence( xTitleSeq, true ); + // series formatting + ScfPropertySet aSeriesProp( xDataSeries ); + mxSeriesFmt.reset( new XclExpChDataFormat( GetChRoot(), XclChDataPointPos( mnSeriesIdx ), nFormatIdx ) ); + mxSeriesFmt->ConvertStockSeries( aSeriesProp, bCloseSymbol ); + } + } + return bOk; +} + +bool XclExpChSeries::ConvertTrendLine( const XclExpChSeries& rParent, Reference< XRegressionCurve > xRegCurve ) +{ + InitFromParent( rParent ); + mxTrendLine.reset( new XclExpChSerTrendLine( GetChRoot() ) ); + bool bOk = mxTrendLine->Convert( xRegCurve, mnSeriesIdx ); + if( bOk ) + { + mxSeriesFmt = mxTrendLine->GetDataFormat(); + GetChartData().SetDataLabel( mxTrendLine->GetDataLabel() ); + } + return bOk; +} + +bool XclExpChSeries::ConvertErrorBar( const XclExpChSeries& rParent, const ScfPropertySet& rPropSet, sal_uInt8 nBarId ) +{ + InitFromParent( rParent ); + // error bar settings + mxErrorBar.reset( new XclExpChSerErrorBar( GetChRoot(), nBarId ) ); + bool bOk = mxErrorBar->Convert( *mxValueLink, maData.mnValueCount, rPropSet ); + if( bOk ) + { + // error bar formatting + mxSeriesFmt.reset( new XclExpChDataFormat( GetChRoot(), XclChDataPointPos( mnSeriesIdx ), 0 ) ); + mxSeriesFmt->ConvertLine( rPropSet, EXC_CHOBJTYPE_ERRORBAR ); + } + return bOk; +} + +void XclExpChSeries::ConvertCategSequence( Reference< XLabeledDataSequence > xCategSeq ) +{ + if( xCategSeq.is() ) + maData.mnCategCount = mxCategLink->ConvertDataSequence( xCategSeq->getValues(), false ); +} + +void XclExpChSeries::WriteSubRecords( XclExpStream& rStrm ) +{ + lclSaveRecord( rStrm, mxTitleLink ); + lclSaveRecord( rStrm, mxValueLink ); + lclSaveRecord( rStrm, mxCategLink ); + lclSaveRecord( rStrm, mxBubbleLink ); + lclSaveRecord( rStrm, mxSeriesFmt ); + maPointFmts.Save( rStrm ); + if( mnGroupIdx != EXC_CHSERGROUP_NONE ) + XclExpUInt16Record( EXC_ID_CHSERGROUP, mnGroupIdx ).Save( rStrm ); + if( mnParentIdx != EXC_CHSERIES_INVALID ) + XclExpUInt16Record( EXC_ID_CHSERPARENT, mnParentIdx ).Save( rStrm ); + lclSaveRecord( rStrm, mxTrendLine ); + lclSaveRecord( rStrm, mxErrorBar ); +} + +void XclExpChSeries::InitFromParent( const XclExpChSeries& rParent ) +{ + // index to parent series is stored 1-based + mnParentIdx = rParent.mnSeriesIdx + 1; + /* #i86465# MSO2007 SP1 expects correct point counts in child series + (there was no problem in Excel2003 or Excel2007 without SP1...) */ + maData.mnCategCount = rParent.maData.mnCategCount; + maData.mnValueCount = rParent.maData.mnValueCount; +} + +void XclExpChSeries::CreateTrendLines( XDataSeriesRef xDataSeries ) +{ + Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY ); + if( xRegCurveCont.is() ) + { + Sequence< Reference< XRegressionCurve > > aRegCurveSeq = xRegCurveCont->getRegressionCurves(); + const Reference< XRegressionCurve >* pBeg = aRegCurveSeq.getConstArray(); + const Reference< XRegressionCurve >* pEnd = pBeg + aRegCurveSeq.getLength(); + for( const Reference< XRegressionCurve >* pIt = pBeg; pIt != pEnd; ++pIt ) + { + XclExpChSeriesRef xSeries = GetChartData().CreateSeries(); + if( xSeries.is() && !xSeries->ConvertTrendLine( *this, *pIt ) ) + GetChartData().RemoveLastSeries(); + } + } +} + +void XclExpChSeries::CreateErrorBars( const ScfPropertySet& rPropSet, + const OUString& rBarPropName, sal_uInt8 nPosBarId, sal_uInt8 nNegBarId ) +{ + Reference< XPropertySet > xErrorBar; + if( rPropSet.GetProperty( xErrorBar, rBarPropName ) && xErrorBar.is() ) + { + ScfPropertySet aErrorProp( xErrorBar ); + CreateErrorBar( aErrorProp, EXC_CHPROP_SHOWPOSITIVEERROR, nPosBarId ); + CreateErrorBar( aErrorProp, EXC_CHPROP_SHOWNEGATIVEERROR, nNegBarId ); + } +} + +void XclExpChSeries::CreateErrorBar( const ScfPropertySet& rPropSet, + const OUString& rShowPropName, sal_uInt8 nBarId ) +{ + if( rPropSet.GetBoolProperty( rShowPropName ) ) + { + XclExpChSeriesRef xSeries = GetChartData().CreateSeries(); + if( xSeries.is() && !xSeries->ConvertErrorBar( *this, rPropSet, nBarId ) ) + GetChartData().RemoveLastSeries(); + } +} + +void XclExpChSeries::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnCategType << maData.mnValueType << maData.mnCategCount << maData.mnValueCount; + if( GetBiff() == EXC_BIFF8 ) + rStrm << maData.mnBubbleType << maData.mnBubbleCount; +} + +// Chart type groups ========================================================== + +XclExpChType::XclExpChType( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHUNKNOWN ), + XclExpChRoot( rRoot ), + maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) ) +{ +} + +void XclExpChType::Convert( Reference< XDiagram > xDiagram, Reference< XChartType > xChartType, + sal_Int32 nApiAxesSetIdx, bool bSwappedAxesSet, bool bHasXLabels ) +{ + if( xChartType.is() ) + { + maTypeInfo = GetChartTypeInfo( xChartType->getChartType() ); + // special handling for some chart types + switch( maTypeInfo.meTypeCateg ) + { + case EXC_CHTYPECATEG_BAR: + { + maTypeInfo = GetChartTypeInfo( bSwappedAxesSet ? EXC_CHTYPEID_HORBAR : EXC_CHTYPEID_BAR ); + ::set_flag( maData.mnFlags, EXC_CHBAR_HORIZONTAL, bSwappedAxesSet ); + ScfPropertySet aTypeProp( xChartType ); + Sequence< sal_Int32 > aInt32Seq; + maData.mnOverlap = 0; + if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_OVERLAPSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) ) + maData.mnOverlap = limit_cast< sal_Int16 >( -aInt32Seq[ nApiAxesSetIdx ], -100, 100 ); + maData.mnGap = 150; + if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_GAPWIDTHSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) ) + maData.mnGap = limit_cast< sal_uInt16 >( aInt32Seq[ nApiAxesSetIdx ], 0, 500 ); + } + break; + case EXC_CHTYPECATEG_RADAR: + ::set_flag( maData.mnFlags, EXC_CHRADAR_AXISLABELS, bHasXLabels ); + break; + case EXC_CHTYPECATEG_PIE: + { + ScfPropertySet aTypeProp( xChartType ); + bool bDonut = aTypeProp.GetBoolProperty( EXC_CHPROP_USERINGS ); + maTypeInfo = GetChartTypeInfo( bDonut ? EXC_CHTYPEID_DONUT : EXC_CHTYPEID_PIE ); + maData.mnPieHole = bDonut ? 50 : 0; + // #i85166# starting angle of first pie slice + ScfPropertySet aDiaProp( xDiagram ); + maData.mnRotation = XclExpChRoot::ConvertPieRotation( aDiaProp ); + } + break; + case EXC_CHTYPECATEG_SCATTER: + if( GetBiff() == EXC_BIFF8 ) + ::set_flag( maData.mnFlags, EXC_CHSCATTER_BUBBLES, maTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES ); + break; + default:; + } + SetRecId( maTypeInfo.mnRecId ); + } +} + +void XclExpChType::SetStacked( bool bPercent ) +{ + switch( maTypeInfo.meTypeCateg ) + { + case EXC_CHTYPECATEG_LINE: + ::set_flag( maData.mnFlags, EXC_CHLINE_STACKED ); + ::set_flag( maData.mnFlags, EXC_CHLINE_PERCENT, bPercent ); + break; + case EXC_CHTYPECATEG_BAR: + ::set_flag( maData.mnFlags, EXC_CHBAR_STACKED ); + ::set_flag( maData.mnFlags, EXC_CHBAR_PERCENT, bPercent ); + maData.mnOverlap = -100; + break; + default:; + } +} + +void XclExpChType::WriteBody( XclExpStream& rStrm ) +{ + switch( GetRecId() ) + { + case EXC_ID_CHBAR: + rStrm << maData.mnOverlap << maData.mnGap << maData.mnFlags; + break; + + case EXC_ID_CHLINE: + case EXC_ID_CHAREA: + case EXC_ID_CHRADARLINE: + case EXC_ID_CHRADARAREA: + rStrm << maData.mnFlags; + break; + + case EXC_ID_CHPIE: + rStrm << maData.mnRotation << maData.mnPieHole; + if( GetBiff() == EXC_BIFF8 ) + rStrm << maData.mnFlags; + break; + + case EXC_ID_CHSCATTER: + if( GetBiff() == EXC_BIFF8 ) + rStrm << maData.mnBubbleSize << maData.mnBubbleType << maData.mnFlags; + break; + + default: + DBG_ERRORFILE( "XclExpChType::WriteBody - unknown chart type" ); + } +} + +// ---------------------------------------------------------------------------- + +XclExpChChart3d::XclExpChChart3d() : + XclExpRecord( EXC_ID_CHCHART3D, 14 ) +{ +} + +void XclExpChChart3d::Convert( const ScfPropertySet& rPropSet, bool b3dWallChart ) +{ + sal_Int32 nRotationY = 0; + rPropSet.GetProperty( nRotationY, EXC_CHPROP_ROTATIONVERTICAL ); + sal_Int32 nRotationX = 0; + rPropSet.GetProperty( nRotationX, EXC_CHPROP_ROTATIONHORIZONTAL ); + sal_Int32 nPerspective = 15; + rPropSet.GetProperty( nPerspective, EXC_CHPROP_PERSPECTIVE ); + + if( b3dWallChart ) + { + // Y rotation (Excel [0..359], Chart2 [-179,180]) + if( nRotationY < 0 ) nRotationY += 360; + maData.mnRotation = static_cast< sal_uInt16 >( nRotationY ); + // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180]) + maData.mnElevation = limit_cast< sal_Int16 >( nRotationX, -90, 90 ); + // perspective (Excel and Chart2 [0,100]) + maData.mnEyeDist = limit_cast< sal_uInt16 >( nPerspective, 0, 100 ); + // flags + maData.mnFlags = 0; + ::set_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D, !rPropSet.GetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES ) ); + ::set_flag( maData.mnFlags, EXC_CHCHART3D_AUTOHEIGHT ); + ::set_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS ); + } + else + { + // Y rotation not used in pie charts, but 'first pie slice angle' + maData.mnRotation = XclExpChRoot::ConvertPieRotation( rPropSet ); + // X rotation a.k.a. elevation (map Chart2 [-80,-10] to Excel [10..80]) + maData.mnElevation = limit_cast< sal_Int16 >( (nRotationX + 270) % 180, 10, 80 ); + // perspective (Excel and Chart2 [0,100]) + maData.mnEyeDist = limit_cast< sal_uInt16 >( nPerspective, 0, 100 ); + // flags + maData.mnFlags = 0; + } +} + +void XclExpChChart3d::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnRotation + << maData.mnElevation + << maData.mnEyeDist + << maData.mnRelHeight + << maData.mnRelDepth + << maData.mnDepthGap + << maData.mnFlags; +} + +// ---------------------------------------------------------------------------- + +XclExpChLegend::XclExpChLegend( const XclExpChRoot& rRoot ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_LEGEND, EXC_ID_CHLEGEND, 20 ) +{ +} + +void XclExpChLegend::Convert( const ScfPropertySet& rPropSet ) +{ + // frame properties + mxFrame = lclCreateFrame( GetChRoot(), rPropSet, EXC_CHOBJTYPE_LEGEND ); + // text properties + mxText.reset( new XclExpChText( GetChRoot() ) ); + mxText->ConvertLegend( rPropSet ); + + // legend position + Any aRelPosAny; + rPropSet.GetAnyProperty( aRelPosAny, EXC_CHPROP_RELATIVEPOSITION ); + if( aRelPosAny.has< RelativePosition >() ) + { + try + { + /* The 'RelativePosition' property is used as indicator of manually + changed legend position, but due to the different anchor modes + used by this property (in the RelativePosition.Anchor member) + it cannot be used to calculate the position easily. For this, + the Chart1 API will be used instead. */ + Reference< ::com::sun::star::chart::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW ); + Reference< XShape > xChart1Legend( xChart1Doc->getLegend(), UNO_SET_THROW ); + // coordinates in CHLEGEND record written but not used by Excel + mxFramePos.reset( new XclExpChFramePos( EXC_CHFRAMEPOS_CHARTSIZE, EXC_CHFRAMEPOS_PARENT ) ); + XclChFramePos& rFramePos = mxFramePos->GetFramePosData(); + rFramePos.maRect.mnX = maData.maRect.mnX = CalcChartXFromHmm( xChart1Legend->getPosition().X ); + rFramePos.maRect.mnY = maData.maRect.mnY = CalcChartYFromHmm( xChart1Legend->getPosition().Y ); + // manual legend position implies manual plot area + GetChartData().SetManualPlotArea(); + maData.mnDockMode = EXC_CHLEGEND_NOTDOCKED; + } + catch( Exception& ) + { + OSL_ENSURE( false, "XclExpChLegend::Convert - cannot get legend shape" ); + maData.mnDockMode = EXC_CHLEGEND_RIGHT; + } + } + else + { + cssc2::LegendPosition eApiPos = cssc2::LegendPosition_CUSTOM; + rPropSet.GetProperty( eApiPos, EXC_CHPROP_ANCHORPOSITION ); + switch( eApiPos ) + { + case cssc2::LegendPosition_LINE_START: maData.mnDockMode = EXC_CHLEGEND_LEFT; break; + case cssc2::LegendPosition_LINE_END: maData.mnDockMode = EXC_CHLEGEND_RIGHT; break; + case cssc2::LegendPosition_PAGE_START: maData.mnDockMode = EXC_CHLEGEND_TOP; break; + case cssc2::LegendPosition_PAGE_END: maData.mnDockMode = EXC_CHLEGEND_BOTTOM; break; + default: + OSL_ENSURE( false, "XclExpChLegend::Convert - unrecognized legend position" ); + maData.mnDockMode = EXC_CHLEGEND_RIGHT; + } + } + + // legend expansion + cssc2::LegendExpansion eApiExpand = cssc2::LegendExpansion_BALANCED; + rPropSet.GetProperty( eApiExpand, EXC_CHPROP_EXPANSION ); + ::set_flag( maData.mnFlags, EXC_CHLEGEND_STACKED, eApiExpand != cssc2::LegendExpansion_WIDE ); + + // other flags + ::set_flag( maData.mnFlags, EXC_CHLEGEND_AUTOSERIES ); + const sal_uInt16 nAutoFlags = EXC_CHLEGEND_DOCKED | EXC_CHLEGEND_AUTOPOSX | EXC_CHLEGEND_AUTOPOSY; + ::set_flag( maData.mnFlags, nAutoFlags, maData.mnDockMode != EXC_CHLEGEND_NOTDOCKED ); +} + +void XclExpChLegend::WriteSubRecords( XclExpStream& rStrm ) +{ + lclSaveRecord( rStrm, mxFramePos ); + lclSaveRecord( rStrm, mxText ); + lclSaveRecord( rStrm, mxFrame ); +} + +void XclExpChLegend::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.maRect << maData.mnDockMode << maData.mnSpacing << maData.mnFlags; +} + +// ---------------------------------------------------------------------------- + +XclExpChDropBar::XclExpChDropBar( const XclExpChRoot& rRoot, XclChObjectType eObjType ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_DROPBAR, EXC_ID_CHDROPBAR, 2 ), + meObjType( eObjType ), + mnBarDist( 100 ) +{ +} + +void XclExpChDropBar::Convert( const ScfPropertySet& rPropSet ) +{ + if( rPropSet.Is() ) + ConvertFrameBase( GetChRoot(), rPropSet, meObjType ); + else + SetDefaultFrameBase( GetChRoot(), EXC_CHFRAMETYPE_INVISIBLE, true ); +} + +void XclExpChDropBar::WriteSubRecords( XclExpStream& rStrm ) +{ + WriteFrameRecords( rStrm ); +} + +void XclExpChDropBar::WriteBody( XclExpStream& rStrm ) +{ + rStrm << mnBarDist; +} + +// ---------------------------------------------------------------------------- + +XclExpChTypeGroup::XclExpChTypeGroup( const XclExpChRoot& rRoot, sal_uInt16 nGroupIdx ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_TYPEGROUP, EXC_ID_CHTYPEGROUP, 20 ), + maType( rRoot ), + maTypeInfo( maType.GetTypeInfo() ) +{ + maData.mnGroupIdx = nGroupIdx; +} + +void XclExpChTypeGroup::ConvertType( + Reference< XDiagram > xDiagram, Reference< XChartType > xChartType, + sal_Int32 nApiAxesSetIdx, bool b3dChart, bool bSwappedAxesSet, bool bHasXLabels ) +{ + // chart type settings + maType.Convert( xDiagram, xChartType, nApiAxesSetIdx, bSwappedAxesSet, bHasXLabels ); + + // spline - TODO: get from single series (#i66858#) + ScfPropertySet aTypeProp( xChartType ); + ::com::sun::star::chart2::CurveStyle eCurveStyle; + bool bSpline = aTypeProp.GetProperty( eCurveStyle, EXC_CHPROP_CURVESTYLE ) && + (eCurveStyle != ::com::sun::star::chart2::CurveStyle_LINES); + + // extended type info + maTypeInfo.Set( maType.GetTypeInfo(), b3dChart, bSpline ); + + // 3d chart settings + if( maTypeInfo.mb3dChart ) // only true, if Excel chart supports 3d mode + { + mxChart3d.reset( new XclExpChChart3d ); + ScfPropertySet aDiaProp( xDiagram ); + mxChart3d->Convert( aDiaProp, Is3dWallChart() ); + } +} + +void XclExpChTypeGroup::ConvertSeries( + Reference< XDiagram > xDiagram, Reference< XChartType > xChartType, + sal_Int32 nGroupAxesSetIdx, bool bPercent, bool bConnectBars ) +{ + Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY ); + if( xSeriesCont.is() ) + { + typedef ::std::vector< Reference< XDataSeries > > XDataSeriesVec; + XDataSeriesVec aSeriesVec; + + // copy data series attached to the current axes set to the vector + Sequence< Reference< XDataSeries > > aSeriesSeq = xSeriesCont->getDataSeries(); + const Reference< XDataSeries >* pBeg = aSeriesSeq.getConstArray(); + const Reference< XDataSeries >* pEnd = pBeg + aSeriesSeq.getLength(); + for( const Reference< XDataSeries >* pIt = pBeg; pIt != pEnd; ++pIt ) + { + ScfPropertySet aSeriesProp( *pIt ); + sal_Int32 nSeriesAxesSetIdx(0); + if( aSeriesProp.GetProperty( nSeriesAxesSetIdx, EXC_CHPROP_ATTAXISINDEX ) && (nSeriesAxesSetIdx == nGroupAxesSetIdx) ) + aSeriesVec.push_back( *pIt ); + } + + // Are there any series in the current axes set? + if( !aSeriesVec.empty() ) + { + // stacking direction (stacked/percent/deep 3d) from first series + ScfPropertySet aSeriesProp( aSeriesVec.front() ); + cssc2::StackingDirection eStacking; + if( !aSeriesProp.GetProperty( eStacking, EXC_CHPROP_STACKINGDIR ) ) + eStacking = cssc2::StackingDirection_NO_STACKING; + + // stacked or percent chart + if( maTypeInfo.mbSupportsStacking && (eStacking == cssc2::StackingDirection_Y_STACKING) ) + { + // percent overrides simple stacking + maType.SetStacked( bPercent ); + + // connected data points (only in stacked bar charts) + if( bConnectBars && (maTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR) ) + maChartLines[ EXC_CHCHARTLINE_CONNECT ].reset( new XclExpChLineFormat( GetChRoot() ) ); + } + else + { + // reverse series order for some unstacked 2D chart types + if( maTypeInfo.mbReverseSeries && !Is3dChart() ) + ::std::reverse( aSeriesVec.begin(), aSeriesVec.end() ); + } + + // deep 3d chart or clustered 3d chart (stacked is not clustered) + if( (eStacking == cssc2::StackingDirection_NO_STACKING) && Is3dWallChart() ) + mxChart3d->SetClustered(); + + // varied point colors + ::set_flag( maData.mnFlags, EXC_CHTYPEGROUP_VARIEDCOLORS, aSeriesProp.GetBoolProperty( EXC_CHPROP_VARYCOLORSBY ) ); + + // process all series + for( XDataSeriesVec::const_iterator aIt = aSeriesVec.begin(), aEnd = aSeriesVec.end(); aIt != aEnd; ++aIt ) + { + // create Excel series object, stock charts need special processing + if( maTypeInfo.meTypeId == EXC_CHTYPEID_STOCK ) + CreateAllStockSeries( xChartType, *aIt ); + else + CreateDataSeries( xDiagram, *aIt ); + } + } + } +} + +void XclExpChTypeGroup::ConvertCategSequence( Reference< XLabeledDataSequence > xCategSeq ) +{ + for( size_t nIdx = 0, nSize = maSeries.GetSize(); nIdx < nSize; ++nIdx ) + maSeries.GetRecord( nIdx )->ConvertCategSequence( xCategSeq ); +} + +void XclExpChTypeGroup::ConvertLegend( const ScfPropertySet& rPropSet ) +{ + if( rPropSet.GetBoolProperty( EXC_CHPROP_SHOW ) ) + { + mxLegend.reset( new XclExpChLegend( GetChRoot() ) ); + mxLegend->Convert( rPropSet ); + } +} + +void XclExpChTypeGroup::WriteSubRecords( XclExpStream& rStrm ) +{ + maType.Save( rStrm ); + lclSaveRecord( rStrm, mxChart3d ); + lclSaveRecord( rStrm, mxLegend ); + lclSaveRecord( rStrm, mxUpBar ); + lclSaveRecord( rStrm, mxDownBar ); + for( XclExpChLineFormatMap::iterator aLIt = maChartLines.begin(), aLEnd = maChartLines.end(); aLIt != aLEnd; ++aLIt ) + lclSaveRecord( rStrm, aLIt->second, EXC_ID_CHCHARTLINE, aLIt->first ); +} + +sal_uInt16 XclExpChTypeGroup::GetFreeFormatIdx() const +{ + return static_cast< sal_uInt16 >( maSeries.GetSize() ); +} + +void XclExpChTypeGroup::CreateDataSeries( + Reference< XDiagram > xDiagram, Reference< XDataSeries > xDataSeries ) +{ + // let chart create series object with correct series index + XclExpChSeriesRef xSeries = GetChartData().CreateSeries(); + if( xSeries.is() ) + { + if( xSeries->ConvertDataSeries( xDiagram, xDataSeries, maTypeInfo, GetGroupIdx(), GetFreeFormatIdx() ) ) + maSeries.AppendRecord( xSeries ); + else + GetChartData().RemoveLastSeries(); + } +} + +void XclExpChTypeGroup::CreateAllStockSeries( + Reference< XChartType > xChartType, Reference< XDataSeries > xDataSeries ) +{ + // create existing series objects + bool bHasOpen = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_OPENVALUES, false ); + bool bHasHigh = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_HIGHVALUES, false ); + bool bHasLow = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_LOWVALUES, false ); + bool bHasClose = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_CLOSEVALUES, !bHasOpen ); + + // formatting of special stock chart elements + ScfPropertySet aTypeProp( xChartType ); + // hi-lo lines + if( bHasHigh && bHasLow && aTypeProp.GetBoolProperty( EXC_CHPROP_SHOWHIGHLOW ) ) + { + ScfPropertySet aSeriesProp( xDataSeries ); + XclExpChLineFormatRef xLineFmt( new XclExpChLineFormat( GetChRoot() ) ); + xLineFmt->Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE ); + maChartLines[ EXC_CHCHARTLINE_HILO ] = xLineFmt; + } + // dropbars + if( bHasOpen && bHasClose ) + { + // dropbar type is dependent on position in the file - always create both + Reference< XPropertySet > xWhitePropSet, xBlackPropSet; + // white dropbar format + aTypeProp.GetProperty( xWhitePropSet, EXC_CHPROP_WHITEDAY ); + ScfPropertySet aWhiteProp( xWhitePropSet ); + mxUpBar.reset( new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_WHITEDROPBAR ) ); + mxUpBar->Convert( aWhiteProp ); + // black dropbar format + aTypeProp.GetProperty( xBlackPropSet, EXC_CHPROP_BLACKDAY ); + ScfPropertySet aBlackProp( xBlackPropSet ); + mxDownBar.reset( new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_BLACKDROPBAR ) ); + mxDownBar->Convert( aBlackProp ); + } +} + +bool XclExpChTypeGroup::CreateStockSeries( Reference< XDataSeries > xDataSeries, + const OUString& rValueRole, bool bCloseSymbol ) +{ + bool bOk = false; + // let chart create series object with correct series index + XclExpChSeriesRef xSeries = GetChartData().CreateSeries(); + if( xSeries.is() ) + { + bOk = xSeries->ConvertStockSeries( xDataSeries, + rValueRole, GetGroupIdx(), GetFreeFormatIdx(), bCloseSymbol ); + if( bOk ) + maSeries.AppendRecord( xSeries ); + else + GetChartData().RemoveLastSeries(); + } + return bOk; +} + +void XclExpChTypeGroup::WriteBody( XclExpStream& rStrm ) +{ + rStrm.WriteZeroBytes( 16 ); + rStrm << maData.mnFlags << maData.mnGroupIdx; +} + +// Axes ======================================================================= + +XclExpChLabelRange::XclExpChLabelRange( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHLABELRANGE, 8 ), + XclExpChRoot( rRoot ) +{ +} + +void XclExpChLabelRange::Convert( const ScaleData& rScaleData, bool bMirrorOrient ) +{ + // origin + double fOrigin = 0.0; + if( !lclIsAutoAnyOrGetValue( fOrigin, rScaleData.Origin ) ) + maData.mnCross = limit_cast< sal_uInt16 >( fOrigin, 1, 31999 ); + + // reverse order + if( (rScaleData.Orientation == ::com::sun::star::chart2::AxisOrientation_REVERSE) != bMirrorOrient ) + ::set_flag( maData.mnFlags, EXC_CHLABELRANGE_REVERSE ); +} + +void XclExpChLabelRange::ConvertAxisPosition( const ScfPropertySet& rPropSet ) +{ + cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE; + rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION ); + double fCrossingPos = 1.0; + rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE ); + switch( eAxisPos ) + { + case cssc::ChartAxisPosition_ZERO: maData.mnCross = 1; break; + case cssc::ChartAxisPosition_START: maData.mnCross = 1; break; + case cssc::ChartAxisPosition_END: ::set_flag( maData.mnFlags, EXC_CHLABELRANGE_MAXCROSS ); break; + case cssc::ChartAxisPosition_VALUE: maData.mnCross = limit_cast< sal_uInt16 >( fCrossingPos, 1, 31999 ); break; + default: maData.mnCross = 1; + } +} + +void XclExpChLabelRange::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnCross << maData.mnLabelFreq << maData.mnTickFreq << maData.mnFlags; +} + +// ---------------------------------------------------------------------------- + +XclExpChValueRange::XclExpChValueRange( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHVALUERANGE, 42 ), + XclExpChRoot( rRoot ) +{ +} + +void XclExpChValueRange::Convert( const ScaleData& rScaleData ) +{ + // scaling algorithm + bool bLogScale = ScfApiHelper::GetServiceName( rScaleData.Scaling ) == SERVICE_CHART2_LOGSCALING; + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, bLogScale ); + + // min/max + bool bAutoMin = lclIsAutoAnyOrGetScaledValue( maData.mfMin, rScaleData.Minimum, bLogScale ); + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN, bAutoMin ); + bool bAutoMax = lclIsAutoAnyOrGetScaledValue( maData.mfMax, rScaleData.Maximum, bLogScale ); + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX, bAutoMax ); + + // origin + bool bAutoCross = lclIsAutoAnyOrGetScaledValue( maData.mfCross, rScaleData.Origin, bLogScale ); + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, bAutoCross ); + + // major increment + const IncrementData& rIncrementData = rScaleData.IncrementData; + bool bAutoMajor = lclIsAutoAnyOrGetValue( maData.mfMajorStep, rIncrementData.Distance ) || (maData.mfMajorStep <= 0.0); + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAJOR, bAutoMajor ); + // minor increment + const Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements; + sal_Int32 nCount = 0; + bool bAutoMinor = bLogScale || bAutoMajor || (rSubIncrementSeq.getLength() < 1) || + lclIsAutoAnyOrGetValue( nCount, rSubIncrementSeq[ 0 ].IntervalCount ) || (nCount < 1); + if( !bAutoMinor ) + maData.mfMinorStep = maData.mfMajorStep / nCount; + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMINOR, bAutoMinor ); + + // reverse order + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE, rScaleData.Orientation == cssc2::AxisOrientation_REVERSE ); +} + +void XclExpChValueRange::ConvertAxisPosition( const ScfPropertySet& rPropSet ) +{ + cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE; + double fCrossingPos = 0.0; + if( rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION ) && rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE ) ) + { + switch( eAxisPos ) + { + case cssc::ChartAxisPosition_ZERO: + case cssc::ChartAxisPosition_START: + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS ); + break; + case cssc::ChartAxisPosition_END: + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_MAXCROSS ); + break; + case cssc::ChartAxisPosition_VALUE: + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, false ); + maData.mfCross = ::get_flagvalue< double >( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, log( fCrossingPos ) / log( 10.0 ), fCrossingPos ); + break; + default: + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS ); + } + } +} + +void XclExpChValueRange::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mfMin + << maData.mfMax + << maData.mfMajorStep + << maData.mfMinorStep + << maData.mfCross + << maData.mnFlags; +} + +// ---------------------------------------------------------------------------- + +namespace { + +sal_uInt8 lclGetXclTickPos( sal_Int32 nApiTickmarks ) +{ + using namespace ::com::sun::star::chart2::TickmarkStyle; + sal_uInt8 nXclTickPos = 0; + ::set_flag( nXclTickPos, EXC_CHTICK_INSIDE, ::get_flag( nApiTickmarks, INNER ) ); + ::set_flag( nXclTickPos, EXC_CHTICK_OUTSIDE, ::get_flag( nApiTickmarks, OUTER ) ); + return nXclTickPos; +} + +} // namespace + +XclExpChTick::XclExpChTick( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHTICK, (rRoot.GetBiff() == EXC_BIFF8) ? 30 : 26 ), + XclExpChRoot( rRoot ), + mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ) +{ +} + +void XclExpChTick::Convert( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nAxisType ) +{ + // tick mark style + sal_Int32 nApiTickmarks = 0; + if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MAJORTICKS ) ) + maData.mnMajor = lclGetXclTickPos( nApiTickmarks ); + if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MINORTICKS ) ) + maData.mnMinor = lclGetXclTickPos( nApiTickmarks ); + + // axis labels + if( (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) && (nAxisType == EXC_CHAXIS_X) ) + { + /* Radar charts disable their category labels via chart type, not via + axis, and axis labels are always 'near axis'. */ + maData.mnLabelPos = EXC_CHTICK_NEXT; + } + else if( !rPropSet.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS ) ) + { + // no labels + maData.mnLabelPos = EXC_CHTICK_NOLABEL; + } + else if( rTypeInfo.mb3dChart && (nAxisType == EXC_CHAXIS_Y) ) + { + // Excel expects 'near axis' at Y axes in 3D charts + maData.mnLabelPos = EXC_CHTICK_NEXT; + } + else + { + cssc::ChartAxisLabelPosition eApiLabelPos = cssc::ChartAxisLabelPosition_NEAR_AXIS; + rPropSet.GetProperty( eApiLabelPos, EXC_CHPROP_LABELPOSITION ); + switch( eApiLabelPos ) + { + case cssc::ChartAxisLabelPosition_NEAR_AXIS: + case cssc::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE: maData.mnLabelPos = EXC_CHTICK_NEXT; break; + case cssc::ChartAxisLabelPosition_OUTSIDE_START: maData.mnLabelPos = EXC_CHTICK_LOW; break; + case cssc::ChartAxisLabelPosition_OUTSIDE_END: maData.mnLabelPos = EXC_CHTICK_HIGH; break; + default: maData.mnLabelPos = EXC_CHTICK_NEXT; + } + } +} + +void XclExpChTick::SetFontColor( const Color& rColor, sal_uInt32 nColorId ) +{ + maData.maTextColor = rColor; + ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOCOLOR, rColor == COL_AUTO ); + mnTextColorId = nColorId; +} + +void XclExpChTick::SetRotation( sal_uInt16 nRotation ) +{ + maData.mnRotation = nRotation; + ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOROT, false ); + ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 2, 3 ); +} + +void XclExpChTick::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnMajor + << maData.mnMinor + << maData.mnLabelPos + << maData.mnBackMode; + rStrm.WriteZeroBytes( 16 ); + rStrm << maData.maTextColor + << maData.mnFlags; + if( GetBiff() == EXC_BIFF8 ) + rStrm << GetPalette().GetColorIndex( mnTextColorId ) << maData.mnRotation; +} + +// ---------------------------------------------------------------------------- + +namespace { + +/** Returns an API axis object from the passed coordinate system. */ +Reference< XAxis > lclGetApiAxis( Reference< XCoordinateSystem > xCoordSystem, + sal_Int32 nApiAxisDim, sal_Int32 nApiAxesSetIdx ) +{ + Reference< XAxis > xAxis; + if( (nApiAxisDim >= 0) && xCoordSystem.is() ) try + { + xAxis = xCoordSystem->getAxisByDimension( nApiAxisDim, nApiAxesSetIdx ); + } + catch( Exception& ) + { + } + return xAxis; +} + +} // namespace + +XclExpChAxis::XclExpChAxis( const XclExpChRoot& rRoot, sal_uInt16 nAxisType ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXIS, EXC_ID_CHAXIS, 18 ), + mnNumFmtIdx( EXC_FORMAT_NOTFOUND ) +{ + maData.mnType = nAxisType; +} + +void XclExpChAxis::SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId ) +{ + mxFont = xFont; + if( mxTick.is() ) + mxTick->SetFontColor( rColor, nColorId ); +} + +void XclExpChAxis::SetRotation( sal_uInt16 nRotation ) +{ + if( mxTick.is() ) + mxTick->SetRotation( nRotation ); +} + +void XclExpChAxis::Convert( Reference< XAxis > xAxis, Reference< XAxis > xCrossingAxis, const XclChExtTypeInfo& rTypeInfo ) +{ + ScfPropertySet aAxisProp( xAxis ); + bool bCategoryAxis = ((GetAxisType() == EXC_CHAXIS_X) && rTypeInfo.mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z); + + // axis line format ------------------------------------------------------- + + mxAxisLine.reset( new XclExpChLineFormat( GetChRoot() ) ); + mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE ); + // #i58688# axis enabled + mxAxisLine->SetShowAxis( aAxisProp.GetBoolProperty( EXC_CHPROP_SHOW ) ); + + // axis scaling and increment --------------------------------------------- + + ScfPropertySet aCrossingProp( xCrossingAxis ); + if( bCategoryAxis ) + { + mxLabelRange.reset( new XclExpChLabelRange( GetChRoot() ) ); + mxLabelRange->SetTicksBetweenCateg( rTypeInfo.mbTicksBetweenCateg ); + if( xAxis.is() ) + // #i71684# radar charts have reversed rotation direction + mxLabelRange->Convert( xAxis->getScaleData(), (GetAxisType() == EXC_CHAXIS_X) && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) ); + // get position of crossing axis on this axis from passed axis object + if( aCrossingProp.Is() ) + mxLabelRange->ConvertAxisPosition( aCrossingProp ); + } + else + { + mxValueRange.reset( new XclExpChValueRange( GetChRoot() ) ); + if( xAxis.is() ) + mxValueRange->Convert( xAxis->getScaleData() ); + // get position of crossing axis on this axis from passed axis object + if( aCrossingProp.Is() ) + mxValueRange->ConvertAxisPosition( aCrossingProp ); + } + + // axis caption text ------------------------------------------------------ + + // axis ticks properties + mxTick.reset( new XclExpChTick( GetChRoot() ) ); + mxTick->Convert( aAxisProp, rTypeInfo, GetAxisType() ); + + // axis label formatting and rotation + ConvertFontBase( GetChRoot(), aAxisProp ); + ConvertRotationBase( GetChRoot(), aAxisProp, true ); + + // axis number format + sal_Int32 nApiNumFmt = 0; + if( !bCategoryAxis && aAxisProp.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) ) + mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) ); + + // grid ------------------------------------------------------------------- + + if( xAxis.is() ) + { + // main grid + ScfPropertySet aGridProp( xAxis->getGridProperties() ); + if( aGridProp.GetBoolProperty( EXC_CHPROP_SHOW ) ) + mxMajorGrid = lclCreateLineFormat( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE ); + // sub grid + Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties(); + if( aSubGridPropSeq.hasElements() ) + { + ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] ); + if( aSubGridProp.GetBoolProperty( EXC_CHPROP_SHOW ) ) + mxMinorGrid = lclCreateLineFormat( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE ); + } + } +} + +void XclExpChAxis::ConvertWall( XDiagramRef xDiagram ) +{ + if( xDiagram.is() ) switch( GetAxisType() ) + { + case EXC_CHAXIS_X: + { + ScfPropertySet aWallProp( xDiagram->getWall() ); + mxWallFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_WALL3D ); + } + break; + case EXC_CHAXIS_Y: + { + ScfPropertySet aFloorProp( xDiagram->getFloor() ); + mxWallFrame = lclCreateFrame( GetChRoot(), aFloorProp, EXC_CHOBJTYPE_FLOOR3D ); + } + break; + default: + mxWallFrame.reset(); + } +} + +void XclExpChAxis::WriteSubRecords( XclExpStream& rStrm ) +{ + lclSaveRecord( rStrm, mxLabelRange ); + lclSaveRecord( rStrm, mxValueRange ); + if( mnNumFmtIdx != EXC_FORMAT_NOTFOUND ) + XclExpUInt16Record( EXC_ID_CHFORMAT, mnNumFmtIdx ).Save( rStrm ); + lclSaveRecord( rStrm, mxTick ); + lclSaveRecord( rStrm, mxFont ); + lclSaveRecord( rStrm, mxAxisLine, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_AXISLINE ); + lclSaveRecord( rStrm, mxMajorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MAJORGRID ); + lclSaveRecord( rStrm, mxMinorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MINORGRID ); + lclSaveRecord( rStrm, mxWallFrame, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_WALLS ); +} + +void XclExpChAxis::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnType; + rStrm.WriteZeroBytes( 16 ); +} + +// ---------------------------------------------------------------------------- + +XclExpChAxesSet::XclExpChAxesSet( const XclExpChRoot& rRoot, sal_uInt16 nAxesSetId ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXESSET, EXC_ID_CHAXESSET, 18 ) +{ + maData.mnAxesSetId = nAxesSetId; + SetFutureRecordContext( 0, nAxesSetId ); + + /* Need to set a reasonable size for the plot area, otherwise Excel will + move away embedded shapes while auto-sizing the plot area. This is just + a wild guess, but will be fixed with implementing manual positioning of + chart elements. */ + maData.maRect.mnX = 262; + maData.maRect.mnY = 626; + maData.maRect.mnWidth = 3187; + maData.maRect.mnHeight = 2633; +} + +sal_uInt16 XclExpChAxesSet::Convert( Reference< XDiagram > xDiagram, sal_uInt16 nFirstGroupIdx ) +{ + /* First unused chart type group index is passed to be able to continue + counting of chart type groups for secondary axes set. */ + sal_uInt16 nGroupIdx = nFirstGroupIdx; + Reference< XCoordinateSystemContainer > xCoordSysCont( xDiagram, UNO_QUERY ); + if( xCoordSysCont.is() ) + { + Sequence< Reference< XCoordinateSystem > > aCoordSysSeq = xCoordSysCont->getCoordinateSystems(); + if( aCoordSysSeq.getLength() > 0 ) + { + /* Process first coordinate system only. Import filter puts all + chart types into one coordinate system. */ + Reference< XCoordinateSystem > xCoordSystem = aCoordSysSeq[ 0 ]; + sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex(); + + // 3d mode + bool b3dChart = xCoordSystem.is() && (xCoordSystem->getDimension() == 3); + + // percent charts + namespace ApiAxisType = ::com::sun::star::chart2::AxisType; + Reference< XAxis > xApiYAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_Y, nApiAxesSetIdx ); + bool bPercent = xApiYAxis.is() && (xApiYAxis->getScaleData().AxisType == ApiAxisType::PERCENT); + + // connector lines in bar charts + ScfPropertySet aDiaProp( xDiagram ); + bool bConnectBars = aDiaProp.GetBoolProperty( EXC_CHPROP_CONNECTBARS ); + + // swapped axes sets + ScfPropertySet aCoordSysProp( xCoordSystem ); + bool bSwappedAxesSet = aCoordSysProp.GetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS ); + + // X axis for later use + Reference< XAxis > xApiXAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_X, nApiAxesSetIdx ); + // X axis labels + ScfPropertySet aXAxisProp( xApiXAxis ); + bool bHasXLabels = aXAxisProp.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS ); + + // process chart types + Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY ); + if( xChartTypeCont.is() ) + { + Sequence< Reference< XChartType > > aChartTypeSeq = xChartTypeCont->getChartTypes(); + const Reference< XChartType >* pBeg = aChartTypeSeq.getConstArray(); + const Reference< XChartType >* pEnd = pBeg + aChartTypeSeq.getLength(); + for( const Reference< XChartType >* pIt = pBeg; pIt != pEnd; ++pIt ) + { + XclExpChTypeGroupRef xTypeGroup( new XclExpChTypeGroup( GetChRoot(), nGroupIdx ) ); + xTypeGroup->ConvertType( xDiagram, *pIt, nApiAxesSetIdx, b3dChart, bSwappedAxesSet, bHasXLabels ); + /* If new chart type group cannot be inserted into a combination + chart with existing type groups, insert all series into last + contained chart type group instead of creating a new group. */ + XclExpChTypeGroupRef xLastGroup = GetLastTypeGroup(); + if( xLastGroup.is() && !(xTypeGroup->IsCombinable2d() && xLastGroup->IsCombinable2d()) ) + { + xLastGroup->ConvertSeries( xDiagram, *pIt, nApiAxesSetIdx, bPercent, bConnectBars ); + } + else + { + xTypeGroup->ConvertSeries( xDiagram, *pIt, nApiAxesSetIdx, bPercent, bConnectBars ); + if( xTypeGroup->IsValidGroup() ) + { + maTypeGroups.AppendRecord( xTypeGroup ); + ++nGroupIdx; + } + } + } + } + + if( XclExpChTypeGroup* pGroup = GetFirstTypeGroup().get() ) + { + const XclChExtTypeInfo& rTypeInfo = pGroup->GetTypeInfo(); + + // create axes according to chart type (no axes for pie and donut charts) + if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE ) + { + ConvertAxis( mxXAxis, EXC_CHAXIS_X, mxXAxisTitle, EXC_CHOBJLINK_XAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_Y ); + ConvertAxis( mxYAxis, EXC_CHAXIS_Y, mxYAxisTitle, EXC_CHOBJLINK_YAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_X ); + if( pGroup->Is3dDeepChart() ) + ConvertAxis( mxZAxis, EXC_CHAXIS_Z, mxZAxisTitle, EXC_CHOBJLINK_ZAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_NONE ); + } + + // X axis category ranges + if( rTypeInfo.mbCategoryAxis && xApiXAxis.is() ) + { + const ScaleData aScaleData = xApiXAxis->getScaleData(); + for( size_t nIdx = 0, nSize = maTypeGroups.GetSize(); nIdx < nSize; ++nIdx ) + maTypeGroups.GetRecord( nIdx )->ConvertCategSequence( aScaleData.Categories ); + } + + // legend + if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) ) + { + Reference< XLegend > xLegend = xDiagram->getLegend(); + if( xLegend.is() ) + { + ScfPropertySet aLegendProp( xLegend ); + pGroup->ConvertLegend( aLegendProp ); + } + } + } + } + } + + // wall/floor/diagram frame formatting + if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) ) + { + XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup(); + if( xTypeGroup.is() && xTypeGroup->Is3dWallChart() ) + { + // wall/floor formatting (3D charts) + if( mxXAxis.is() ) + mxXAxis->ConvertWall( xDiagram ); + if( mxYAxis.is() ) + mxYAxis->ConvertWall( xDiagram ); + } + else + { + // diagram background formatting + ScfPropertySet aWallProp( xDiagram->getWall() ); + mxPlotFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_PLOTFRAME ); + } + } + + // inner and outer plot area position and size + try + { + Reference< ::com::sun::star::chart::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW ); + Reference< ::com::sun::star::chart::XDiagramPositioning > xPositioning( xChart1Doc->getDiagram(), UNO_QUERY_THROW ); + // set manual flag in chart data + if( !xPositioning->isAutomaticDiagramPositioning() ) + GetChartData().SetManualPlotArea(); + // the CHAXESSET record contains the inner plot area + maData.maRect = CalcChartRectFromHmm( xPositioning->calculateDiagramPositionExcludingAxes() ); + // the embedded CHFRAMEPOS record contains the outer plot area + mxFramePos.reset( new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT, EXC_CHFRAMEPOS_PARENT ) ); + // for pie charts, always use inner plot area size to exclude the data labels as Excel does + const XclExpChTypeGroup* pFirstTypeGroup = GetFirstTypeGroup().get(); + bool bPieChart = pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE); + mxFramePos->GetFramePosData().maRect = bPieChart ? maData.maRect : + CalcChartRectFromHmm( xPositioning->calculateDiagramPositionIncludingAxes() ); + } + catch( Exception& ) + { + } + + // return first unused chart type group index for next axes set + return nGroupIdx; +} + +bool XclExpChAxesSet::Is3dChart() const +{ + XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup(); + return xTypeGroup.is() && xTypeGroup->Is3dChart(); +} + +void XclExpChAxesSet::WriteSubRecords( XclExpStream& rStrm ) +{ + lclSaveRecord( rStrm, mxFramePos ); + lclSaveRecord( rStrm, mxXAxis ); + lclSaveRecord( rStrm, mxYAxis ); + lclSaveRecord( rStrm, mxZAxis ); + lclSaveRecord( rStrm, mxXAxisTitle ); + lclSaveRecord( rStrm, mxYAxisTitle ); + lclSaveRecord( rStrm, mxZAxisTitle ); + if( mxPlotFrame.is() ) + { + XclExpEmptyRecord( EXC_ID_CHPLOTFRAME ).Save( rStrm ); + mxPlotFrame->Save( rStrm ); + } + maTypeGroups.Save( rStrm ); +} + +XclExpChTypeGroupRef XclExpChAxesSet::GetFirstTypeGroup() const +{ + return maTypeGroups.GetFirstRecord(); +} + +XclExpChTypeGroupRef XclExpChAxesSet::GetLastTypeGroup() const +{ + return maTypeGroups.GetLastRecord(); +} + +void XclExpChAxesSet::ConvertAxis( + XclExpChAxisRef& rxChAxis, sal_uInt16 nAxisType, + XclExpChTextRef& rxChAxisTitle, sal_uInt16 nTitleTarget, + Reference< XCoordinateSystem > xCoordSystem, const XclChExtTypeInfo& rTypeInfo, + sal_Int32 nCrossingAxisDim ) +{ + // create and convert axis object + rxChAxis.reset( new XclExpChAxis( GetChRoot(), nAxisType ) ); + sal_Int32 nApiAxisDim = rxChAxis->GetApiAxisDimension(); + sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex(); + Reference< XAxis > xAxis = lclGetApiAxis( xCoordSystem, nApiAxisDim, nApiAxesSetIdx ); + Reference< XAxis > xCrossingAxis = lclGetApiAxis( xCoordSystem, nCrossingAxisDim, nApiAxesSetIdx ); + rxChAxis->Convert( xAxis, xCrossingAxis, rTypeInfo ); + + // create and convert axis title + Reference< XTitled > xTitled( xAxis, UNO_QUERY ); + rxChAxisTitle = lclCreateTitle( GetChRoot(), xTitled, nTitleTarget ); +} + +void XclExpChAxesSet::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnAxesSetId << maData.maRect; +} + +// The chart object =========================================================== + +XclExpChChart::XclExpChChart( const XclExpRoot& rRoot, + Reference< XChartDocument > xChartDoc, const Rectangle& rChartRect ) : + XclExpChGroupBase( XclExpChRoot( rRoot, *this ), EXC_CHFRBLOCK_TYPE_CHART, EXC_ID_CHCHART, 16 ) +{ + Size aPtSize = OutputDevice::LogicToLogic( rChartRect.GetSize(), MapMode( MAP_100TH_MM ), MapMode( MAP_POINT ) ); + // rectangle is stored in 16.16 fixed-point format + maRect.mnX = maRect.mnY = 0; + maRect.mnWidth = static_cast< sal_Int32 >( aPtSize.Width() << 16 ); + maRect.mnHeight = static_cast< sal_Int32 >( aPtSize.Height() << 16 ); + + // global chart properties (default values) + ::set_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY, false ); + ::set_flag( maProps.mnFlags, EXC_CHPROPS_MANPLOTAREA ); + maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP; + + // always create both axes set objects + mxPrimAxesSet.reset( new XclExpChAxesSet( GetChRoot(), EXC_CHAXESSET_PRIMARY ) ); + mxSecnAxesSet.reset( new XclExpChAxesSet( GetChRoot(), EXC_CHAXESSET_SECONDARY ) ); + + if( xChartDoc.is() ) + { + Reference< XDiagram > xDiagram = xChartDoc->getFirstDiagram(); + + // global chart properties (only 'include hidden cells' attribute for now) + ScfPropertySet aDiagramProp( xDiagram ); + bool bIncludeHidden = aDiagramProp.GetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS ); + ::set_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY, !bIncludeHidden ); + + // initialize API conversion (remembers xChartDoc and rChartRect internally) + InitConversion( xChartDoc, rChartRect ); + + // chart frame + ScfPropertySet aFrameProp( xChartDoc->getPageBackground() ); + mxFrame = lclCreateFrame( GetChRoot(), aFrameProp, EXC_CHOBJTYPE_BACKGROUND ); + + // chart title + Reference< XTitled > xTitled( xChartDoc, UNO_QUERY ); + mxTitle = lclCreateTitle( GetChRoot(), xTitled, EXC_CHOBJLINK_TITLE ); + + // diagrams (axes sets) + sal_uInt16 nFreeGroupIdx = mxPrimAxesSet->Convert( xDiagram, 0 ); + if( !mxPrimAxesSet->Is3dChart() ) + mxSecnAxesSet->Convert( xDiagram, nFreeGroupIdx ); + + // treatment of missing values + ScfPropertySet aDiaProp( xDiagram ); + sal_Int32 nMissingValues = 0; + if( aDiaProp.GetProperty( nMissingValues, EXC_CHPROP_MISSINGVALUETREATMENT ) ) + { + using namespace ::com::sun::star::chart::MissingValueTreatment; + switch( nMissingValues ) + { + case LEAVE_GAP: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP; break; + case USE_ZERO: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_ZERO; break; + case CONTINUE: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_INTERPOLATE; break; + } + } + + // finish API conversion + FinishConversion(); + } +} + +XclExpChSeriesRef XclExpChChart::CreateSeries() +{ + XclExpChSeriesRef xSeries; + sal_uInt16 nSeriesIdx = static_cast< sal_uInt16 >( maSeries.GetSize() ); + if( nSeriesIdx <= EXC_CHSERIES_MAXSERIES ) + { + xSeries.reset( new XclExpChSeries( GetChRoot(), nSeriesIdx ) ); + maSeries.AppendRecord( xSeries ); + } + return xSeries; +} + +void XclExpChChart::RemoveLastSeries() +{ + if( !maSeries.IsEmpty() ) + maSeries.RemoveRecord( maSeries.GetSize() - 1 ); +} + +void XclExpChChart::SetDataLabel( XclExpChTextRef xText ) +{ + if( xText.is() ) + maLabels.AppendRecord( xText ); +} + +void XclExpChChart::SetManualPlotArea() +{ + // this flag does not exist in BIFF5 + if( GetBiff() == EXC_BIFF8 ) + ::set_flag( maProps.mnFlags, EXC_CHPROPS_USEMANPLOTAREA ); +} + +void XclExpChChart::WriteSubRecords( XclExpStream& rStrm ) +{ + // background format + lclSaveRecord( rStrm, mxFrame ); + + // data series + maSeries.Save( rStrm ); + + // CHPROPERTIES record + rStrm.StartRecord( EXC_ID_CHPROPERTIES, 4 ); + rStrm << maProps.mnFlags << maProps.mnEmptyMode << sal_uInt8( 0 ); + rStrm.EndRecord(); + + // axes sets (always save primary axes set) + sal_uInt16 nUsedAxesSets = mxSecnAxesSet->IsValidAxesSet() ? 2 : 1; + XclExpUInt16Record( EXC_ID_CHUSEDAXESSETS, nUsedAxesSets ).Save( rStrm ); + mxPrimAxesSet->Save( rStrm ); + if( mxSecnAxesSet->IsValidAxesSet() ) + mxSecnAxesSet->Save( rStrm ); + + // chart title and data labels + lclSaveRecord( rStrm, mxTitle ); + maLabels.Save( rStrm ); +} + +void XclExpChChart::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maRect; +} + +// ---------------------------------------------------------------------------- + +XclExpChartDrawing::XclExpChartDrawing( const XclExpRoot& rRoot, + const Reference< XModel >& rxModel, const Size& rChartSize ) : + XclExpRoot( rRoot ) +{ + if( (rChartSize.Width() > 0) && (rChartSize.Height() > 0) ) + { + ScfPropertySet aPropSet( rxModel ); + Reference< XShapes > xShapes; + if( aPropSet.GetProperty( xShapes, EXC_CHPROP_ADDITIONALSHAPES ) && xShapes.is() && (xShapes->getCount() > 0) ) + { + /* Create a new independent object manager with own DFF stream for the + DGCONTAINER, pass global manager as parent for shared usage of + global DFF data (picture container etc.). */ + mxObjMgr.reset( new XclExpEmbeddedObjectManager( GetObjectManager(), rChartSize, EXC_CHART_TOTALUNITS, EXC_CHART_TOTALUNITS ) ); + // initialize the drawing object list + mxObjMgr->StartSheet(); + // process the draw page (convert all shapes) + mxObjRecs = mxObjMgr->ProcessDrawing( xShapes ); + // finalize the DFF stream + mxObjMgr->EndDocument(); + } + } +} + +XclExpChartDrawing::~XclExpChartDrawing() +{ +} + +void XclExpChartDrawing::Save( XclExpStream& rStrm ) +{ + if( mxObjRecs.is() ) + mxObjRecs->Save( rStrm ); +} + +// ---------------------------------------------------------------------------- + +XclExpChart::XclExpChart( const XclExpRoot& rRoot, Reference< XModel > xModel, const Rectangle& rChartRect ) : + XclExpSubStream( EXC_BOF_CHART ), + XclExpRoot( rRoot ) +{ + AppendNewRecord( new XclExpChartPageSettings( rRoot ) ); + AppendNewRecord( new XclExpBoolRecord( EXC_ID_PROTECT, false ) ); + AppendNewRecord( new XclExpChartDrawing( rRoot, xModel, rChartRect.GetSize() ) ); + AppendNewRecord( new XclExpUInt16Record( EXC_ID_CHUNITS, EXC_CHUNITS_TWIPS ) ); + + Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY ); + AppendNewRecord( new XclExpChChart( rRoot, xChartDoc, rChartRect ) ); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xecontent.cxx b/sc/source/filter/excel/xecontent.cxx new file mode 100644 index 000000000000..7bfd57be7290 --- /dev/null +++ b/sc/source/filter/excel/xecontent.cxx @@ -0,0 +1,1462 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "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" + +#include <oox/core/tokens.hxx> + +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 ) +{ + 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( 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, + XclXmlUtils::ToOUString( "External" ) ); + 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( USHORT 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, 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 ) == 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 ) == 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, 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( 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; + ULONG nCurrScHandle = ::std::numeric_limits< 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 ) ); + } + } + } + } +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xeescher.cxx b/sc/source/filter/excel/xeescher.cxx new file mode 100644 index 000000000000..b2801df7f54a --- /dev/null +++ b/sc/source/filter/excel/xeescher.cxx @@ -0,0 +1,1284 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "xeescher.hxx" + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/awt/ScrollBarOrientation.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/form/binding/XBindableValue.hpp> +#include <com/sun/star/form/binding/XValueBinding.hpp> +#include <com/sun/star/form/binding/XListEntrySink.hpp> +#include <com/sun/star/form/binding/XListEntrySource.hpp> +#include <com/sun/star/script/ScriptEventDescriptor.hpp> + +#include <set> +#include <rtl/ustrbuf.h> +#include <vcl/bmpacc.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdocapt.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/editobj.hxx> +#include <unotools/tempfile.hxx> +#include <unotools/ucbstreamhelper.hxx> + +#include "editutil.hxx" +#include "unonames.hxx" +#include "convuno.hxx" +#include "postit.hxx" + +#include "fapihelper.hxx" +#include "xechart.hxx" +#include "xeformula.hxx" +#include "xelink.hxx" +#include "xename.hxx" +#include "xestyle.hxx" + +#include <oox/core/tokens.hxx> + +using ::rtl::OString; +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::XServiceInfo; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::drawing::XShapes; +using ::com::sun::star::frame::XModel; +using ::com::sun::star::embed::XEmbeddedObject; +using ::com::sun::star::awt::XControlModel; +using ::com::sun::star::form::binding::XBindableValue; +using ::com::sun::star::form::binding::XValueBinding; +using ::com::sun::star::form::binding::XListEntrySink; +using ::com::sun::star::form::binding::XListEntrySource; +using ::com::sun::star::script::ScriptEventDescriptor; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; + +// Escher client anchor ======================================================= + +XclExpDffAnchorBase::XclExpDffAnchorBase( const XclExpRoot& rRoot, sal_uInt16 nFlags ) : + XclExpRoot( rRoot ), + mnFlags( nFlags ) +{ +} + +void XclExpDffAnchorBase::SetFlags( const SdrObject& rSdrObj ) +{ + ImplSetFlags( rSdrObj ); +} + +void XclExpDffAnchorBase::SetSdrObject( const SdrObject& rSdrObj ) +{ + ImplSetFlags( rSdrObj ); + ImplCalcAnchorRect( rSdrObj.GetCurrentBoundRect(), MAP_100TH_MM ); +} + +void XclExpDffAnchorBase::WriteDffData( EscherEx& rEscherEx ) const +{ + rEscherEx.AddAtom( 18, ESCHER_ClientAnchor ); + rEscherEx.GetStream() << mnFlags << maAnchor; +} + +void XclExpDffAnchorBase::WriteData( EscherEx& rEscherEx, const Rectangle& rRect ) +{ + // the passed rectangle is in twips + ImplCalcAnchorRect( rRect, MAP_TWIP ); + WriteDffData( rEscherEx ); +} + +void XclExpDffAnchorBase::ImplSetFlags( const SdrObject& ) +{ + OSL_ENSURE( false, "XclExpDffAnchorBase::ImplSetFlags - not implemented" ); +} + +void XclExpDffAnchorBase::ImplCalcAnchorRect( const Rectangle&, MapUnit ) +{ + OSL_ENSURE( false, "XclExpDffAnchorBase::ImplCalcAnchorRect - not implemented" ); +} + +// ---------------------------------------------------------------------------- + +XclExpDffSheetAnchor::XclExpDffSheetAnchor( const XclExpRoot& rRoot ) : + XclExpDffAnchorBase( rRoot ), + mnScTab( rRoot.GetCurrScTab() ) +{ +} + +void XclExpDffSheetAnchor::ImplSetFlags( const SdrObject& rSdrObj ) +{ + // Special case "page anchor" (X==0,Y==1) -> lock pos and size. + const Point& rPos = rSdrObj.GetAnchorPos(); + mnFlags = ((rPos.X() == 0) && (rPos.Y() == 1)) ? EXC_ESC_ANCHOR_LOCKED : 0; +} + +void XclExpDffSheetAnchor::ImplCalcAnchorRect( const Rectangle& rRect, MapUnit eMapUnit ) +{ + maAnchor.SetRect( GetRoot(), mnScTab, rRect, eMapUnit ); +} + +// ---------------------------------------------------------------------------- + +XclExpDffEmbeddedAnchor::XclExpDffEmbeddedAnchor( const XclExpRoot& rRoot, + const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY ) : + XclExpDffAnchorBase( rRoot ), + maPageSize( rPageSize ), + mnScaleX( nScaleX ), + mnScaleY( nScaleY ) +{ +} + +void XclExpDffEmbeddedAnchor::ImplSetFlags( const SdrObject& /*rSdrObj*/ ) +{ + // TODO (unsupported feature): fixed size +} + +void XclExpDffEmbeddedAnchor::ImplCalcAnchorRect( const Rectangle& rRect, MapUnit eMapUnit ) +{ + maAnchor.SetRect( maPageSize, mnScaleX, mnScaleY, rRect, eMapUnit, true ); +} + +// ---------------------------------------------------------------------------- + +XclExpDffNoteAnchor::XclExpDffNoteAnchor( const XclExpRoot& rRoot, const Rectangle& rRect ) : + XclExpDffAnchorBase( rRoot, EXC_ESC_ANCHOR_SIZELOCKED ) +{ + maAnchor.SetRect( rRoot, rRoot.GetCurrScTab(), rRect, MAP_100TH_MM ); +} + +// ---------------------------------------------------------------------------- + +XclExpDffDropDownAnchor::XclExpDffDropDownAnchor( const XclExpRoot& rRoot, const ScAddress& rScPos ) : + XclExpDffAnchorBase( rRoot, EXC_ESC_ANCHOR_POSLOCKED ) +{ + GetAddressConverter().ConvertAddress( maAnchor.maFirst, rScPos, true ); + maAnchor.maLast.mnCol = maAnchor.maFirst.mnCol + 1; + maAnchor.maLast.mnRow = maAnchor.maFirst.mnRow + 1; + maAnchor.mnLX = maAnchor.mnTY = maAnchor.mnRX = maAnchor.mnBY = 0; +} + +// MSODRAWING* records ======================================================== + +XclExpMsoDrawingBase::XclExpMsoDrawingBase( XclEscherEx& rEscherEx, sal_uInt16 nRecId ) : + XclExpRecord( nRecId ), + mrEscherEx( rEscherEx ), + mnFragmentKey( rEscherEx.InitNextDffFragment() ) +{ +} + +void XclExpMsoDrawingBase::WriteBody( XclExpStream& rStrm ) +{ + OSL_ENSURE( mrEscherEx.GetStreamPos() == mrEscherEx.GetDffFragmentPos( mnFragmentKey ), + "XclExpMsoDrawingBase::WriteBody - DFF stream position mismatch" ); + rStrm.CopyFromStream( mrEscherEx.GetStream(), mrEscherEx.GetDffFragmentSize( mnFragmentKey ) ); +} + +// ---------------------------------------------------------------------------- + +XclExpMsoDrawingGroup::XclExpMsoDrawingGroup( XclEscherEx& rEscherEx ) : + XclExpMsoDrawingBase( rEscherEx, EXC_ID_MSODRAWINGGROUP ) +{ + SvStream& rDffStrm = mrEscherEx.GetStream(); + + // write the DGGCONTAINER with some default settings + mrEscherEx.OpenContainer( ESCHER_DggContainer ); + + // TODO: stuff the OPT atom with our own document defaults? + static const sal_uInt8 spnDffOpt[] = { + 0xBF, 0x00, 0x08, 0x00, 0x08, 0x00, 0x81, 0x01, + 0x09, 0x00, 0x00, 0x08, 0xC0, 0x01, 0x40, 0x00, + 0x00, 0x08 + }; + mrEscherEx.AddAtom( sizeof( spnDffOpt ), ESCHER_OPT, 3, 3 ); + rDffStrm.Write( spnDffOpt, sizeof( spnDffOpt ) ); + + // SPLITMENUCOLORS contains colors in toolbar + static const sal_uInt8 spnDffSplitMenuColors[] = { + 0x0D, 0x00, 0x00, 0x08, 0x0C, 0x00, 0x00, 0x08, + 0x17, 0x00, 0x00, 0x08, 0xF7, 0x00, 0x00, 0x10 + }; + mrEscherEx.AddAtom( sizeof( spnDffSplitMenuColors ), ESCHER_SplitMenuColors, 0, 4 ); + rDffStrm.Write( spnDffSplitMenuColors, sizeof( spnDffSplitMenuColors ) ); + + // close the DGGCONTAINER + mrEscherEx.CloseContainer(); + mrEscherEx.UpdateDffFragmentEnd(); +} + +// ---------------------------------------------------------------------------- + +XclExpMsoDrawing::XclExpMsoDrawing( XclEscherEx& rEscherEx ) : + XclExpMsoDrawingBase( rEscherEx, EXC_ID_MSODRAWING ) +{ +} + +// ============================================================================ + +XclExpImgData::XclExpImgData( const Graphic& rGraphic, sal_uInt16 nRecId ) : + maGraphic( rGraphic ), + mnRecId( nRecId ) +{ +} + +void XclExpImgData::Save( XclExpStream& rStrm ) +{ + Bitmap aBmp = maGraphic.GetBitmap(); + if( aBmp.GetBitCount() != 24 ) + aBmp.Convert( BMP_CONVERSION_24BIT ); + + if( BitmapReadAccess* pAccess = aBmp.AcquireReadAccess() ) + { + sal_Int32 nWidth = ::std::min< sal_Int32 >( pAccess->Width(), 0xFFFF ); + sal_Int32 nHeight = ::std::min< sal_Int32 >( pAccess->Height(), 0xFFFF ); + if( (nWidth > 0) && (nHeight > 0) ) + { + sal_uInt8 nPadding = static_cast< sal_uInt8 >( nWidth & 0x03 ); + sal_uInt32 nTmpSize = static_cast< sal_uInt32 >( (nWidth * 3 + nPadding) * nHeight + 12 ); + + rStrm.StartRecord( mnRecId, nTmpSize + 4 ); + + rStrm << EXC_IMGDATA_BMP // BMP format + << EXC_IMGDATA_WIN // Windows + << nTmpSize // size after _this_ field + << sal_uInt32( 12 ) // BITMAPCOREHEADER size + << static_cast< sal_uInt16 >( nWidth ) // width + << static_cast< sal_uInt16 >( nHeight ) // height + << sal_uInt16( 1 ) // planes + << sal_uInt16( 24 ); // bits per pixel + + for( sal_Int32 nY = nHeight - 1; nY >= 0; --nY ) + { + for( sal_Int32 nX = 0; nX < nWidth; ++nX ) + { + const BitmapColor& rBmpColor = pAccess->GetPixel( nY, nX ); + rStrm << rBmpColor.GetBlue() << rBmpColor.GetGreen() << rBmpColor.GetRed(); + } + rStrm.WriteZeroBytes( nPadding ); + } + + rStrm.EndRecord(); + } + aBmp.ReleaseAccess( pAccess ); + } +} + +// ============================================================================ + +XclExpControlHelper::XclExpControlHelper( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mnEntryCount( 0 ) +{ +} + +XclExpControlHelper::~XclExpControlHelper() +{ +} + +void XclExpControlHelper::ConvertSheetLinks( Reference< XShape > xShape ) +{ + mxCellLink.reset(); + mxSrcRange.reset(); + mnEntryCount = 0; + + // get control model + Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape ); + if( !xCtrlModel.is() ) + return; + + // *** cell link *** ------------------------------------------------------ + + Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY ); + if( xBindable.is() ) + { + Reference< XServiceInfo > xServInfo( xBindable->getValueBinding(), UNO_QUERY ); + if( xServInfo.is() && xServInfo->supportsService( CREATE_OUSTRING( SC_SERVICENAME_VALBIND ) ) ) + { + ScfPropertySet aBindProp( xServInfo ); + CellAddress aApiAddress; + if( aBindProp.GetProperty( aApiAddress, CREATE_OUSTRING( SC_UNONAME_BOUNDCELL ) ) ) + { + ScAddress aCellLink; + ScUnoConversion::FillScAddress( aCellLink, aApiAddress ); + if( GetTabInfo().IsExportTab( aCellLink.Tab() ) ) + mxCellLink = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CONTROL, aCellLink ); + } + } + } + + // *** source range *** --------------------------------------------------- + + Reference< XListEntrySink > xEntrySink( xCtrlModel, UNO_QUERY ); + if( xEntrySink.is() ) + { + Reference< XServiceInfo > xServInfo( xEntrySink->getListEntrySource(), UNO_QUERY ); + if( xServInfo.is() && xServInfo->supportsService( CREATE_OUSTRING( SC_SERVICENAME_LISTSOURCE ) ) ) + { + ScfPropertySet aSinkProp( xServInfo ); + CellRangeAddress aApiRange; + if( aSinkProp.GetProperty( aApiRange, CREATE_OUSTRING( SC_UNONAME_CELLRANGE ) ) ) + { + ScRange aSrcRange; + ScUnoConversion::FillScRange( aSrcRange, aApiRange ); + if( (aSrcRange.aStart.Tab() == aSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( aSrcRange.aStart.Tab() ) ) + mxSrcRange = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CONTROL, aSrcRange ); + mnEntryCount = static_cast< sal_uInt16 >( aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1 ); + } + } + } +} + +void XclExpControlHelper::WriteFormula( XclExpStream& rStrm, const XclTokenArray& rTokArr ) const +{ + sal_uInt16 nFmlaSize = rTokArr.GetSize(); + rStrm << nFmlaSize << sal_uInt32( 0 ); + rTokArr.WriteArray( rStrm ); + if( nFmlaSize & 1 ) // pad to 16-bit + rStrm << sal_uInt8( 0 ); +} + +void XclExpControlHelper::WriteFormulaSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId, const XclTokenArray& rTokArr ) const +{ + rStrm.StartRecord( nSubRecId, (rTokArr.GetSize() + 5) & ~1 ); + WriteFormula( rStrm, rTokArr ); + rStrm.EndRecord(); +} + +// ---------------------------------------------------------------------------- + +#if EXC_EXP_OCX_CTRL + +XclExpOcxControlObj::XclExpOcxControlObj( XclExpObjectManager& rObjMgr, Reference< XShape > xShape, + const Rectangle* pChildAnchor, const String& rClassName, sal_uInt32 nStrmStart, sal_uInt32 nStrmSize ) : + XclObj( rObjMgr, EXC_OBJTYPE_PICTURE, true ), + XclExpControlHelper( rObjMgr.GetRoot() ), + maClassName( rClassName ), + mnStrmStart( nStrmStart ), + mnStrmSize( nStrmSize ) +{ + ScfPropertySet aCtrlProp( XclControlHelper::GetControlModel( xShape ) ); + + // OBJ record flags + SetLocked( TRUE ); + SetPrintable( aCtrlProp.GetBoolProperty( CREATE_OUSTRING( "Printable" ) ) ); + SetAutoFill( FALSE ); + SetAutoLine( FALSE ); + + // fill DFF property set + mrEscherEx.OpenContainer( ESCHER_SpContainer ); + mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, SHAPEFLAG_HAVESPT | SHAPEFLAG_HAVEANCHOR | SHAPEFLAG_OLESHAPE ); + Rectangle aDummyRect; + EscherPropertyContainer aPropOpt( mrEscherEx, mrEscherEx.QueryPicStream(), aDummyRect ); + aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); // bool field + aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x08000040 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field + + // #i51348# name of the control, may overwrite shape name + OUString aCtrlName; + if( aCtrlProp.GetProperty( aCtrlName, CREATE_OUSTRING( "Name" ) ) && (aCtrlName.getLength() > 0) ) + aPropOpt.AddOpt( ESCHER_Prop_wzName, aCtrlName ); + + // meta file + //! TODO - needs check + Reference< XPropertySet > xShapePS( xShape, UNO_QUERY ); + if( xShapePS.is() && aPropOpt.CreateGraphicProperties( xShapePS, CREATE_STRING( "MetaFile" ), sal_False ) ) + { + sal_uInt32 nBlipId; + if( aPropOpt.GetOpt( ESCHER_Prop_pib, nBlipId ) ) + aPropOpt.AddOpt( ESCHER_Prop_pictureId, nBlipId ); + } + + // write DFF property set to stream + aPropOpt.Commit( mrEscherEx.GetStream() ); + + // anchor + ImplWriteAnchor( GetRoot(), SdrObject::getSdrObjectFromXShape( xShape ), pChildAnchor ); + + mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record + mrEscherEx.CloseContainer(); // ESCHER_SpContainer + mrEscherEx.UpdateDffFragmentEnd(); + + // spreadsheet links + ConvertSheetLinks( xShape ); +} + +void XclExpOcxControlObj::WriteSubRecs( XclExpStream& rStrm ) +{ + // OBJCF - clipboard format + rStrm.StartRecord( EXC_ID_OBJCF, 2 ); + rStrm << sal_uInt16( 2 ); + rStrm.EndRecord(); + + // OBJFLAGS + rStrm.StartRecord( EXC_ID_OBJFLAGS, 2 ); + rStrm << sal_uInt16( 0x0031 ); + rStrm.EndRecord(); + + // OBJPICTFMLA + XclExpString aClass( maClassName ); + sal_uInt16 nClassNameSize = static_cast< sal_uInt16 >( aClass.GetSize() ); + sal_uInt16 nClassNamePad = nClassNameSize & 1; + sal_uInt16 nFirstPartSize = 12 + nClassNameSize + nClassNamePad; + + const XclTokenArray* pCellLink = GetCellLinkTokArr(); + sal_uInt16 nCellLinkSize = pCellLink ? ((pCellLink->GetSize() + 7) & 0xFFFE) : 0; + + const XclTokenArray* pSrcRange = GetSourceRangeTokArr(); + sal_uInt16 nSrcRangeSize = pSrcRange ? ((pSrcRange->GetSize() + 7) & 0xFFFE) : 0; + + sal_uInt16 nPictFmlaSize = nFirstPartSize + nCellLinkSize + nSrcRangeSize + 18; + rStrm.StartRecord( EXC_ID_OBJPICTFMLA, nPictFmlaSize ); + + rStrm << sal_uInt16( nFirstPartSize ) // size of first part + << sal_uInt16( 5 ) // formula size + << sal_uInt32( 0 ) // unknown ID + << sal_uInt8( 0x02 ) << sal_uInt32( 0 ) // tTbl token with unknown ID + << sal_uInt8( 3 ) // pad to word + << aClass; // "Forms.***.1" + rStrm.WriteZeroBytes( nClassNamePad ); // pad to word + rStrm << mnStrmStart // start in 'Ctls' stream + << mnStrmSize // size in 'Ctls' stream + << sal_uInt32( 0 ); // class ID size + // cell link + rStrm << nCellLinkSize; + if( pCellLink ) + WriteFormula( rStrm, *pCellLink ); + // list source range + rStrm << nSrcRangeSize; + if( pSrcRange ) + WriteFormula( rStrm, *pSrcRange ); + + rStrm.EndRecord(); +} + +#else + +XclExpTbxControlObj::XclExpTbxControlObj( XclExpObjectManager& rObjMgr, Reference< XShape > xShape, const Rectangle* pChildAnchor ) : + XclObj( rObjMgr, EXC_OBJTYPE_UNKNOWN, true ), + XclExpControlHelper( rObjMgr.GetRoot() ), + mnHeight( 0 ), + mnState( 0 ), + mnLineCount( 0 ), + mnSelEntry( 0 ), + mnScrollValue( 0 ), + mnScrollMin( 0 ), + mnScrollMax( 100 ), + mnScrollStep( 1 ), + mnScrollPage( 10 ), + mbFlatButton( false ), + mbFlatBorder( false ), + mbMultiSel( false ), + mbScrollHor( false ) +{ + namespace FormCompType = ::com::sun::star::form::FormComponentType; + namespace AwtVisualEffect = ::com::sun::star::awt::VisualEffect; + namespace AwtScrollOrient = ::com::sun::star::awt::ScrollBarOrientation; + + ScfPropertySet aCtrlProp( XclControlHelper::GetControlModel( xShape ) ); + if( !xShape.is() || !aCtrlProp.Is() ) + return; + + mnHeight = xShape->getSize().Height; + if( mnHeight <= 0 ) + return; + + // control type + sal_Int16 nClassId = 0; + if( aCtrlProp.GetProperty( nClassId, CREATE_OUSTRING( "ClassId" ) ) ) + { + switch( nClassId ) + { + case FormCompType::COMMANDBUTTON: mnObjType = EXC_OBJTYPE_BUTTON; meEventType = EXC_TBX_EVENT_ACTION; break; + case FormCompType::RADIOBUTTON: mnObjType = EXC_OBJTYPE_OPTIONBUTTON; meEventType = EXC_TBX_EVENT_ACTION; break; + case FormCompType::CHECKBOX: mnObjType = EXC_OBJTYPE_CHECKBOX; meEventType = EXC_TBX_EVENT_ACTION; break; + case FormCompType::LISTBOX: mnObjType = EXC_OBJTYPE_LISTBOX; meEventType = EXC_TBX_EVENT_CHANGE; break; + case FormCompType::COMBOBOX: mnObjType = EXC_OBJTYPE_DROPDOWN; meEventType = EXC_TBX_EVENT_CHANGE; break; + case FormCompType::GROUPBOX: mnObjType = EXC_OBJTYPE_GROUPBOX; meEventType = EXC_TBX_EVENT_MOUSE; break; + case FormCompType::FIXEDTEXT: mnObjType = EXC_OBJTYPE_LABEL; meEventType = EXC_TBX_EVENT_MOUSE; break; + case FormCompType::SCROLLBAR: mnObjType = EXC_OBJTYPE_SCROLLBAR; meEventType = EXC_TBX_EVENT_VALUE; break; + case FormCompType::SPINBUTTON: mnObjType = EXC_OBJTYPE_SPIN; meEventType = EXC_TBX_EVENT_VALUE; break; + } + } + if( mnObjType == EXC_OBJTYPE_UNKNOWN ) + return; + + // OBJ record flags + SetLocked( TRUE ); + SetPrintable( aCtrlProp.GetBoolProperty( CREATE_OUSTRING( "Printable" ) ) ); + SetAutoFill( FALSE ); + SetAutoLine( FALSE ); + + // fill DFF property set + mrEscherEx.OpenContainer( ESCHER_SpContainer ); + mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, SHAPEFLAG_HAVEANCHOR | SHAPEFLAG_HAVESPT ); + EscherPropertyContainer aPropOpt; + bool bVisible = aCtrlProp.GetBoolProperty( CREATE_OUSTRING( "EnableVisible" ) ); + aPropOpt.AddOpt( ESCHER_Prop_fPrint, bVisible ? 0x00080000 : 0x00080002 ); // visible flag + + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01000100 ); // bool field + aPropOpt.AddOpt( ESCHER_Prop_lTxid, 0 ); // Text ID + aPropOpt.AddOpt( ESCHER_Prop_WrapText, 0x00000001 ); + aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x001A0008 ); // bool field + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00100000 ); // bool field + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field + + // #i51348# name of the control, may overwrite shape name + OUString aCtrlName; + if( aCtrlProp.GetProperty( aCtrlName, CREATE_OUSTRING( "Name" ) ) && (aCtrlName.getLength() > 0) ) + aPropOpt.AddOpt( ESCHER_Prop_wzName, aCtrlName ); + + // write DFF property set to stream + aPropOpt.Commit( mrEscherEx.GetStream() ); + + // anchor + ImplWriteAnchor( GetRoot(), SdrObject::getSdrObjectFromXShape( xShape ), pChildAnchor ); + + mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record + mrEscherEx.UpdateDffFragmentEnd(); + + // control label + OUString aString; + if( aCtrlProp.GetProperty( aString, CREATE_OUSTRING( "Label" ) ) ) + { + /* Be sure to construct the MSODRAWING record containing the + ClientTextbox atom after the base OBJ's MSODRAWING record data is + completed. */ + pClientTextbox = new XclExpMsoDrawing( mrEscherEx ); + mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox ); // TXO record + mrEscherEx.UpdateDffFragmentEnd(); + + sal_uInt16 nXclFont = EXC_FONT_APP; + if( aString.getLength() > 0 ) + { + XclFontData aFontData; + GetFontPropSetHelper().ReadFontProperties( aFontData, aCtrlProp, EXC_FONTPROPSET_CONTROL ); + if( (aFontData.maName.Len() > 0) && (aFontData.mnHeight > 0) ) + nXclFont = GetFontBuffer().Insert( aFontData, EXC_COLOR_CTRLTEXT ); + } + + pTxo = new XclTxo( aString, nXclFont ); + pTxo->SetHorAlign( (mnObjType == EXC_OBJTYPE_BUTTON) ? EXC_OBJ_HOR_CENTER : EXC_OBJ_HOR_LEFT ); + pTxo->SetVerAlign( EXC_OBJ_VER_CENTER ); + } + + mrEscherEx.CloseContainer(); // ESCHER_SpContainer + + // other properties + aCtrlProp.GetProperty( mnLineCount, CREATE_OUSTRING( "LineCount" ) ); + + // border style + sal_Int16 nApiButton = AwtVisualEffect::LOOK3D; + sal_Int16 nApiBorder = AwtVisualEffect::LOOK3D; + switch( nClassId ) + { + case FormCompType::LISTBOX: + case FormCompType::COMBOBOX: + aCtrlProp.GetProperty( nApiBorder, CREATE_OUSTRING( "Border" ) ); + break; + case FormCompType::CHECKBOX: + case FormCompType::RADIOBUTTON: + aCtrlProp.GetProperty( nApiButton, CREATE_OUSTRING( "VisualEffect" ) ); + nApiBorder = AwtVisualEffect::NONE; + break; + // Push button cannot be set to flat in Excel + case FormCompType::COMMANDBUTTON: + nApiBorder = AwtVisualEffect::LOOK3D; + break; + // Label does not support a border in Excel + case FormCompType::FIXEDTEXT: + nApiBorder = AwtVisualEffect::NONE; + break; + /* Scroll bar and spin button have a "Border" property, but it is + really used for a border, and not for own 3D/flat look (#i34712#). */ + case FormCompType::SCROLLBAR: + case FormCompType::SPINBUTTON: + nApiButton = AwtVisualEffect::LOOK3D; + nApiBorder = AwtVisualEffect::NONE; + break; + // Group box does not support flat style (#i34712#) + case FormCompType::GROUPBOX: + nApiBorder = AwtVisualEffect::LOOK3D; + break; + } + mbFlatButton = nApiButton != AwtVisualEffect::LOOK3D; + mbFlatBorder = nApiBorder != AwtVisualEffect::LOOK3D; + + // control state + sal_Int16 nApiState = 0; + if( aCtrlProp.GetProperty( nApiState, CREATE_OUSTRING( "State" ) ) ) + { + switch( nApiState ) + { + case 0: mnState = EXC_OBJ_CHECKBOX_UNCHECKED; break; + case 1: mnState = EXC_OBJ_CHECKBOX_CHECKED; break; + case 2: mnState = EXC_OBJ_CHECKBOX_TRISTATE; break; + } + } + + // special control contents + switch( nClassId ) + { + case FormCompType::LISTBOX: + { + mbMultiSel = aCtrlProp.GetBoolProperty( CREATE_OUSTRING( "MultiSelection" ) ); + Sequence< sal_Int16 > aSelection; + if( aCtrlProp.GetProperty( aSelection, CREATE_OUSTRING( "SelectedItems" ) ) ) + { + sal_Int32 nLen = aSelection.getLength(); + if( nLen > 0 ) + { + mnSelEntry = aSelection[ 0 ] + 1; + maMultiSel.resize( nLen ); + const sal_Int16* pnBegin = aSelection.getConstArray(); + ::std::copy( pnBegin, pnBegin + nLen, maMultiSel.begin() ); + } + } + + // convert listbox with dropdown button to Excel dropdown + if( aCtrlProp.GetBoolProperty( CREATE_OUSTRING( "Dropdown" ) ) ) + mnObjType = EXC_OBJTYPE_DROPDOWN; + } + break; + + case FormCompType::COMBOBOX: + { + Sequence< OUString > aStringList; + OUString aDefText; + if( aCtrlProp.GetProperty( aStringList, CREATE_OUSTRING( "StringItemList" ) ) && + aCtrlProp.GetProperty( aDefText, CREATE_OUSTRING( "Text" ) ) && + aStringList.getLength() && aDefText.getLength() ) + { + const OUString* pBegin = aStringList.getConstArray(); + const OUString* pEnd = pBegin + aStringList.getLength(); + const OUString* pString = ::std::find( pBegin, pEnd, aDefText ); + if( pString != pEnd ) + mnSelEntry = static_cast< sal_Int16 >( pString - pBegin + 1 ); // 1-based + if( mnSelEntry > 0 ) + maMultiSel.resize( 1, mnSelEntry - 1 ); + } + + // convert combobox without dropdown button to Excel listbox + if( !aCtrlProp.GetBoolProperty( CREATE_OUSTRING( "Dropdown" ) ) ) + mnObjType = EXC_OBJTYPE_LISTBOX; + } + break; + + case FormCompType::SCROLLBAR: + { + sal_Int32 nApiValue = 0; + if( aCtrlProp.GetProperty( nApiValue, CREATE_OUSTRING( "ScrollValueMin" ) ) ) + mnScrollMin = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, CREATE_OUSTRING( "ScrollValueMax" ) ) ) + mnScrollMax = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, EXC_OBJ_SCROLLBAR_MIN ); + if( aCtrlProp.GetProperty( nApiValue, CREATE_OUSTRING( "ScrollValue" ) ) ) + mnScrollValue = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, mnScrollMax ); + if( aCtrlProp.GetProperty( nApiValue, CREATE_OUSTRING( "LineIncrement" ) ) ) + mnScrollStep = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, CREATE_OUSTRING( "BlockIncrement" ) ) ) + mnScrollPage = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, CREATE_OUSTRING( "Orientation" ) ) ) + mbScrollHor = nApiValue == AwtScrollOrient::HORIZONTAL; + } + break; + + case FormCompType::SPINBUTTON: + { + sal_Int32 nApiValue = 0; + if( aCtrlProp.GetProperty( nApiValue, CREATE_OUSTRING( "SpinValueMin" ) ) ) + mnScrollMin = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, CREATE_OUSTRING( "SpinValueMax" ) ) ) + mnScrollMax = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, CREATE_OUSTRING( "SpinValue" ) ) ) + mnScrollValue = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, mnScrollMax ); + if( aCtrlProp.GetProperty( nApiValue, CREATE_OUSTRING( "SpinIncrement" ) ) ) + mnScrollStep = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, CREATE_OUSTRING( "Orientation" ) ) ) + mbScrollHor = nApiValue == AwtScrollOrient::HORIZONTAL; + } + break; + } + + // spreadsheet links + ConvertSheetLinks( xShape ); +} + +bool XclExpTbxControlObj::SetMacroLink( const ScriptEventDescriptor& rEvent ) +{ + String aMacroName = XclControlHelper::ExtractFromMacroDescriptor( rEvent, meEventType ); + if( aMacroName.Len() ) + { + sal_uInt16 nExtSheet = GetLocalLinkManager().FindExtSheet( EXC_EXTSH_OWNDOC ); + sal_uInt16 nNameIdx = GetNameManager().InsertMacroCall( aMacroName, true, false ); + mxMacroLink = GetFormulaCompiler().CreateNameXFormula( nExtSheet, nNameIdx ); + return true; + } + return false; +} + +void XclExpTbxControlObj::WriteSubRecs( XclExpStream& rStrm ) +{ + switch( mnObjType ) + { + // *** Push buttons, labels *** + + case EXC_OBJTYPE_BUTTON: + case EXC_OBJTYPE_LABEL: + // ftMacro - macro link + WriteMacroSubRec( rStrm ); + break; + + // *** Check boxes, option buttons *** + + case EXC_OBJTYPE_CHECKBOX: + case EXC_OBJTYPE_OPTIONBUTTON: + { + // ftCbls - box properties + sal_uInt16 nStyle = 0; + ::set_flag( nStyle, EXC_OBJ_CHECKBOX_FLAT, mbFlatButton ); + + rStrm.StartRecord( EXC_ID_OBJCBLS, 12 ); + rStrm << mnState; + rStrm.WriteZeroBytes( 8 ); + rStrm << nStyle; + rStrm.EndRecord(); + + // ftMacro - macro link + WriteMacroSubRec( rStrm ); + // ftCblsFmla subrecord - cell link + WriteCellLinkSubRec( rStrm, EXC_ID_OBJCBLSFMLA ); + + // ftCblsData subrecord - box properties, again + rStrm.StartRecord( EXC_ID_OBJCBLS, 8 ); + rStrm << mnState; + rStrm.WriteZeroBytes( 4 ); + rStrm << nStyle; + rStrm.EndRecord(); + } + break; + + // *** List boxes, combo boxes *** + + case EXC_OBJTYPE_LISTBOX: + case EXC_OBJTYPE_DROPDOWN: + { + sal_uInt16 nEntryCount = GetSourceEntryCount(); + + // ftSbs subrecord - Scroll bars + sal_Int32 nLineHeight = XclTools::GetHmmFromTwips( 200 ); // always 10pt + if( mnObjType == EXC_OBJTYPE_LISTBOX ) + mnLineCount = static_cast< sal_uInt16 >( mnHeight / nLineHeight ); + mnScrollValue = 0; + mnScrollMin = 0; + sal_uInt16 nInvisLines = (nEntryCount >= mnLineCount) ? (nEntryCount - mnLineCount) : 0; + mnScrollMax = limit_cast< sal_uInt16 >( nInvisLines, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + mnScrollStep = 1; + mnScrollPage = limit_cast< sal_uInt16 >( mnLineCount, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + mbScrollHor = false; + WriteSbs( rStrm ); + + // ftMacro - macro link + WriteMacroSubRec( rStrm ); + // ftSbsFmla subrecord - cell link + WriteCellLinkSubRec( rStrm, EXC_ID_OBJSBSFMLA ); + + // ftLbsData - source data range and box properties + sal_uInt16 nStyle = 0; + ::insert_value( nStyle, mbMultiSel ? EXC_OBJ_LISTBOX_MULTI : EXC_OBJ_LISTBOX_SINGLE, 4, 2 ); + ::set_flag( nStyle, EXC_OBJ_LISTBOX_FLAT, mbFlatBorder ); + + rStrm.StartRecord( EXC_ID_OBJLBSDATA, 0 ); + + if( const XclTokenArray* pSrcRange = GetSourceRangeTokArr() ) + { + rStrm << static_cast< sal_uInt16 >( (pSrcRange->GetSize() + 7) & 0xFFFE ); + WriteFormula( rStrm, *pSrcRange ); + } + else + rStrm << sal_uInt16( 0 ); + + rStrm << nEntryCount << mnSelEntry << nStyle << sal_uInt16( 0 ); + if( mnObjType == EXC_OBJTYPE_LISTBOX ) + { + if( nEntryCount ) + { + ScfUInt8Vec aSelEx( nEntryCount, 0 ); + for( ScfInt16Vec::const_iterator aIt = maMultiSel.begin(), aEnd = maMultiSel.end(); aIt != aEnd; ++aIt ) + if( *aIt < nEntryCount ) + aSelEx[ *aIt ] = 1; + rStrm.Write( &aSelEx[ 0 ], aSelEx.size() ); + } + } + else if( mnObjType == EXC_OBJTYPE_DROPDOWN ) + { + rStrm << sal_uInt16( 0 ) << mnLineCount << sal_uInt16( 0 ) << sal_uInt16( 0 ); + } + + rStrm.EndRecord(); + } + break; + + // *** Spin buttons, scrollbars *** + + case EXC_OBJTYPE_SPIN: + case EXC_OBJTYPE_SCROLLBAR: + { + // ftSbs subrecord - scroll bars + WriteSbs( rStrm ); + // ftMacro - macro link + WriteMacroSubRec( rStrm ); + // ftSbsFmla subrecord - cell link + WriteCellLinkSubRec( rStrm, EXC_ID_OBJSBSFMLA ); + } + break; + + // *** Group boxes *** + + case EXC_OBJTYPE_GROUPBOX: + { + // ftMacro - macro link + WriteMacroSubRec( rStrm ); + + // ftGboData subrecord - group box properties + sal_uInt16 nStyle = 0; + ::set_flag( nStyle, EXC_OBJ_GROUPBOX_FLAT, mbFlatBorder ); + + rStrm.StartRecord( EXC_ID_OBJGBODATA, 6 ); + rStrm << sal_uInt32( 0 ) + << nStyle; + rStrm.EndRecord(); + } + break; + } +} + +void XclExpTbxControlObj::WriteMacroSubRec( XclExpStream& rStrm ) +{ + if( mxMacroLink.is() ) + WriteFormulaSubRec( rStrm, EXC_ID_OBJMACRO, *mxMacroLink ); +} + +void XclExpTbxControlObj::WriteCellLinkSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId ) +{ + if( const XclTokenArray* pCellLink = GetCellLinkTokArr() ) + WriteFormulaSubRec( rStrm, nSubRecId, *pCellLink ); +} + +void XclExpTbxControlObj::WriteSbs( XclExpStream& rStrm ) +{ + sal_uInt16 nOrient = 0; + ::set_flag( nOrient, EXC_OBJ_SCROLLBAR_HOR, mbScrollHor ); + sal_uInt16 nStyle = EXC_OBJ_SCROLLBAR_DEFFLAGS; + ::set_flag( nStyle, EXC_OBJ_SCROLLBAR_FLAT, mbFlatButton ); + + rStrm.StartRecord( EXC_ID_OBJSBS, 20 ); + rStrm << sal_uInt32( 0 ) // reserved + << mnScrollValue // thumb position + << mnScrollMin // thumb min pos + << mnScrollMax // thumb max pos + << mnScrollStep // line increment + << mnScrollPage // page increment + << nOrient // 0 = vertical, 1 = horizontal + << sal_uInt16( 15 ) // thumb width + << nStyle; // flags/style + rStrm.EndRecord(); +} + +#endif + +// ---------------------------------------------------------------------------- + +XclExpChartObj::XclExpChartObj( XclExpObjectManager& rObjMgr, Reference< XShape > xShape, const Rectangle* pChildAnchor ) : + XclObj( rObjMgr, EXC_OBJTYPE_CHART ), + XclExpRoot( rObjMgr.GetRoot() ) +{ + // create the MSODRAWING record contents for the chart object + mrEscherEx.OpenContainer( ESCHER_SpContainer ); + mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, SHAPEFLAG_HAVEANCHOR | SHAPEFLAG_HAVESPT ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01040104 ); + aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); + aPropOpt.AddOpt( ESCHER_Prop_fillColor, 0x0800004E ); + aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x0800004D ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00110010 ); + aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x0800004D ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080008 ); + aPropOpt.AddOpt( ESCHER_Prop_fshadowObscured, 0x00020000 ); + aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x00080000 ); + aPropOpt.Commit( mrEscherEx.GetStream() ); + + // anchor + SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape ); + ImplWriteAnchor( GetRoot(), pSdrObj, pChildAnchor ); + + // client data (the following OBJ record) + mrEscherEx.AddAtom( 0, ESCHER_ClientData ); + mrEscherEx.CloseContainer(); // ESCHER_SpContainer + mrEscherEx.UpdateDffFragmentEnd(); + + // load the chart OLE object + if( SdrOle2Obj* pSdrOleObj = dynamic_cast< SdrOle2Obj* >( pSdrObj ) ) + svt::EmbeddedObjectRef::TryRunningState( pSdrOleObj->GetObjRef() ); + + // create the chart substream object + ScfPropertySet aShapeProp( xShape ); + Reference< XModel > xModel; + aShapeProp.GetProperty( xModel, CREATE_OUSTRING( "Model" ) ); + ::com::sun::star::awt::Rectangle aBoundRect; + aShapeProp.GetProperty( aBoundRect, CREATE_OUSTRING( "BoundRect" ) ); + Rectangle aChartRect( Point( aBoundRect.X, aBoundRect.Y ), Size( aBoundRect.Width, aBoundRect.Height ) ); + mxChart.reset( new XclExpChart( GetRoot(), xModel, aChartRect ) ); +} + +XclExpChartObj::~XclExpChartObj() +{ +} + +void XclExpChartObj::Save( XclExpStream& rStrm ) +{ + // content of OBJ record + XclObj::Save( rStrm ); + // chart substream + mxChart->Save( rStrm ); +} + +// ============================================================================ + +XclExpNote::XclExpNote( const XclExpRoot& rRoot, const ScAddress& rScPos, + const ScPostIt* pScNote, const String& rAddText ) : + XclExpRecord( EXC_ID_NOTE ), + maScPos( rScPos ), + mnObjId( EXC_OBJ_INVALID_ID ), + mbVisible( pScNote && pScNote->IsCaptionShown() ) +{ + // get the main note text + String aNoteText; + if( pScNote ) + aNoteText = pScNote->GetText(); + // append additional text + ScGlobal::AddToken( aNoteText, rAddText, '\n', 2 ); + maOrigNoteText = aNoteText; + + // initialize record dependent on BIFF type + switch( rRoot.GetBiff() ) + { + case EXC_BIFF5: + maNoteText = ByteString( aNoteText, rRoot.GetTextEncoding() ); + break; + + case EXC_BIFF8: + { + // TODO: additional text + if( pScNote ) + if( SdrCaptionObj* pCaption = pScNote->GetOrCreateCaption( maScPos ) ) + if( const OutlinerParaObject* pOPO = pCaption->GetOutlinerParaObject() ) + mnObjId = rRoot.GetObjectManager().AddObj( new XclObjComment( rRoot.GetObjectManager(), pCaption->GetLogicRect(), pOPO->GetTextObject(), pCaption, mbVisible ) ); + + SetRecSize( 9 + maAuthor.GetSize() ); + } + break; + + default: DBG_ERROR_BIFF(); + } +} + +void XclExpNote::Save( XclExpStream& rStrm ) +{ + switch( rStrm.GetRoot().GetBiff() ) + { + case EXC_BIFF5: + { + // write the NOTE record directly, there may be the need to create more than one + const sal_Char* pcBuffer = maNoteText.GetBuffer(); + sal_uInt16 nCharsLeft = static_cast< sal_uInt16 >( maNoteText.Len() ); + + while( nCharsLeft ) + { + sal_uInt16 nWriteChars = ::std::min( nCharsLeft, EXC_NOTE5_MAXLEN ); + + rStrm.StartRecord( EXC_ID_NOTE, 6 + nWriteChars ); + if( pcBuffer == maNoteText.GetBuffer() ) + { + // first record: row, col, length of complete text + rStrm << static_cast< sal_uInt16 >( maScPos.Row() ) + << static_cast< sal_uInt16 >( maScPos.Col() ) + << nCharsLeft; // still contains full length + } + else + { + // next records: -1, 0, length of current text segment + rStrm << sal_uInt16( 0xFFFF ) + << sal_uInt16( 0 ) + << nWriteChars; + } + rStrm.Write( pcBuffer, nWriteChars ); + rStrm.EndRecord(); + + pcBuffer += nWriteChars; + nCharsLeft = nCharsLeft - nWriteChars; + } + } + break; + + case EXC_BIFF8: + if( mnObjId != EXC_OBJ_INVALID_ID ) + XclExpRecord::Save( rStrm ); + break; + + default: DBG_ERROR_BIFF(); + } +} + +void XclExpNote::WriteBody( XclExpStream& rStrm ) +{ + // BIFF5/BIFF7 is written separately + DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ); + + sal_uInt16 nFlags = 0; + ::set_flag( nFlags, EXC_NOTE_VISIBLE, mbVisible ); + + rStrm << static_cast< sal_uInt16 >( maScPos.Row() ) + << static_cast< sal_uInt16 >( maScPos.Col() ) + << nFlags + << mnObjId + << maAuthor + << sal_uInt8( 0 ); +} + +void XclExpNote::WriteXml( sal_Int32 nAuthorId, XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr rComments = rStrm.GetCurrentStream(); + + rComments->startElement( XML_comment, + XML_ref, XclXmlUtils::ToOString( maScPos ).getStr(), + XML_authorId, OString::valueOf( nAuthorId ).getStr(), + // OOXTODO: XML_guid, + FSEND ); + rComments->startElement( XML_text, FSEND ); + // OOXTODO: phoneticPr, rPh, r + rComments->startElement( XML_t, FSEND ); + rComments->writeEscaped( XclXmlUtils::ToOUString( maOrigNoteText ) ); + rComments->endElement ( XML_t ); + rComments->endElement( XML_text ); + rComments->endElement( XML_comment ); +} + +// ============================================================================ + +XclExpComments::XclExpComments( SCTAB nTab, XclExpRecordList< XclExpNote >& rNotes ) + : mnTab( nTab ), mrNotes( rNotes ) +{ +} + +struct OUStringLess : public std::binary_function<OUString, OUString, bool> +{ + bool operator()(const OUString& x, const OUString& y) const + { + return x.compareTo( y ) <= 0; + } +}; + +void XclExpComments::SaveXml( XclExpXmlStream& rStrm ) +{ + if( mrNotes.IsEmpty() ) + return; + + sax_fastparser::FSHelperPtr rComments = rStrm.CreateOutputStream( + XclXmlUtils::GetStreamName( "xl/", "comments", mnTab + 1 ), + XclXmlUtils::GetStreamName( "../", "comments", mnTab + 1 ), + rStrm.GetCurrentStream()->getOutputStream(), + "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" ); + rStrm.PushStream( rComments ); + + rComments->startElement( XML_comments, + XML_xmlns, "http://schemas.openxmlformats.org/spreadsheetml/2006/main", + FSEND ); + rComments->startElement( XML_authors, FSEND ); + + typedef std::set< OUString, OUStringLess > Authors; + Authors aAuthors; + + size_t nNotes = mrNotes.GetSize(); + for( size_t i = 0; i < nNotes; ++i ) + { + aAuthors.insert( XclXmlUtils::ToOUString( mrNotes.GetRecord( i )->GetAuthor() ) ); + } + + for( Authors::const_iterator b = aAuthors.begin(), e = aAuthors.end(); b != e; ++b ) + { + rComments->startElement( XML_author, FSEND ); + rComments->writeEscaped( *b ); + rComments->endElement( XML_author ); + } + + rComments->endElement( XML_authors ); + rComments->startElement( XML_commentList, FSEND ); + + for( size_t i = 0; i < nNotes; ++i ) + { + XclExpNoteList::RecordRefType xNote = mrNotes.GetRecord( i ); + Authors::const_iterator aAuthor = aAuthors.find( + XclXmlUtils::ToOUString( xNote->GetAuthor() ) ); + sal_Int32 nAuthorId = distance( aAuthors.begin(), aAuthor ); + xNote->WriteXml( nAuthorId, rStrm ); + } + + rComments->endElement( XML_commentList ); + rComments->endElement( XML_comments ); + + rStrm.PopStream(); +} + +// object manager ============================================================= + +XclExpObjectManager::XclExpObjectManager( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ) +{ + InitStream( true ); + mxEscherEx.reset( new XclEscherEx( GetRoot(), *this, *mxDffStrm ) ); +} + +XclExpObjectManager::XclExpObjectManager( const XclExpObjectManager& rParent ) : + XclExpRoot( rParent.GetRoot() ) +{ + InitStream( false ); + mxEscherEx.reset( new XclEscherEx( GetRoot(), *this, *mxDffStrm, rParent.mxEscherEx.get() ) ); +} + +XclExpObjectManager::~XclExpObjectManager() +{ +} + +XclExpDffAnchorBase* XclExpObjectManager::CreateDffAnchor() const +{ + return new XclExpDffSheetAnchor( GetRoot() ); +} + +ScfRef< XclExpRecordBase > XclExpObjectManager::CreateDrawingGroup() +{ + return ScfRef< XclExpRecordBase >( new XclExpMsoDrawingGroup( *mxEscherEx ) ); +} + +void XclExpObjectManager::StartSheet() +{ + mxObjList.reset( new XclExpObjList( GetRoot(), *mxEscherEx ) ); +} + +ScfRef< XclExpRecordBase > XclExpObjectManager::ProcessDrawing( SdrPage* pSdrPage ) +{ + if( pSdrPage ) + mxEscherEx->AddSdrPage( *pSdrPage ); + // #106213# the first dummy object may still be open + DBG_ASSERT( mxEscherEx->GetGroupLevel() <= 1, "XclExpObjectManager::ProcessDrawing - still groups open?" ); + while( mxEscherEx->GetGroupLevel() ) + mxEscherEx->LeaveGroup(); + mxObjList->EndSheet(); + return mxObjList; +} + +ScfRef< XclExpRecordBase > XclExpObjectManager::ProcessDrawing( const Reference< XShapes >& rxShapes ) +{ + if( rxShapes.is() ) + mxEscherEx->AddUnoShapes( rxShapes ); + // #106213# the first dummy object may still be open + DBG_ASSERT( mxEscherEx->GetGroupLevel() <= 1, "XclExpObjectManager::ProcessDrawing - still groups open?" ); + while( mxEscherEx->GetGroupLevel() ) + mxEscherEx->LeaveGroup(); + mxObjList->EndSheet(); + return mxObjList; +} + +void XclExpObjectManager::EndDocument() +{ + mxEscherEx->EndDocument(); +} + +XclExpMsoDrawing* XclExpObjectManager::GetMsodrawingPerSheet() +{ + return mxObjList->GetMsodrawingPerSheet(); +} + +bool XclExpObjectManager::HasObj() const +{ + return mxObjList->Count() > 0; +} + +sal_uInt16 XclExpObjectManager::AddObj( XclObj* pObjRec ) +{ + return mxObjList->Add( pObjRec ); +} + +XclObj* XclExpObjectManager::RemoveLastObj() +{ + XclObj* pLastObj = static_cast< XclObj* >( mxObjList->Last() ); + mxObjList->Remove(); // remove current, which is the Last() + return pLastObj; +} + +void XclExpObjectManager::InitStream( bool bTempFile ) +{ + if( bTempFile ) + { + mxTempFile.reset( new ::utl::TempFile ); + if( mxTempFile->IsValid() ) + { + mxTempFile->EnableKillingFile(); + mxDffStrm.reset( ::utl::UcbStreamHelper::CreateStream( mxTempFile->GetURL(), STREAM_STD_READWRITE ) ); + } + } + + if( !mxDffStrm.get() ) + mxDffStrm.reset( new SvMemoryStream ); + + mxDffStrm->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); +} + +// ---------------------------------------------------------------------------- + +XclExpEmbeddedObjectManager::XclExpEmbeddedObjectManager( + const XclExpObjectManager& rParent, const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY ) : + XclExpObjectManager( rParent ), + maPageSize( rPageSize ), + mnScaleX( nScaleX ), + mnScaleY( nScaleY ) +{ +} + +XclExpDffAnchorBase* XclExpEmbeddedObjectManager::CreateDffAnchor() const +{ + return new XclExpDffEmbeddedAnchor( GetRoot(), maPageSize, mnScaleX, mnScaleY ); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xeformula.cxx b/sc/source/filter/excel/xeformula.cxx new file mode 100644 index 000000000000..34e48671a3bc --- /dev/null +++ b/sc/source/filter/excel/xeformula.cxx @@ -0,0 +1,2646 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +// XXX xelink.hxx MUST be included before xeformula.hxx because of the +// redifinition of the CREATE_OUSTRING() macro, which is in oox/helper.hxx +// (indirectly included via xelink.hxx) and ../inc/ftools.hxx (indirectly +// included via xeformula.hxx) that does an undef first. Ugly. +#include "xelink.hxx" +#include "xeformula.hxx" + +#include <list> +#include <map> +#include <memory> +#include "addincol.hxx" +#include "compiler.hxx" +#include "document.hxx" +#include "externalrefmgr.hxx" +#include "rangelst.hxx" +#include "token.hxx" +#include "tokenarray.hxx" +#include "xehelper.hxx" +#include "xename.hxx" +#include "xestream.hxx" + +using namespace ::formula; + +// External reference log ===================================================== + +XclExpRefLogEntry::XclExpRefLogEntry() : + mpUrl( 0 ), + mpFirstTab( 0 ), + mpLastTab( 0 ), + mnFirstXclTab( EXC_TAB_DELETED ), + mnLastXclTab( EXC_TAB_DELETED ) +{ +} + +// Formula compiler =========================================================== + +namespace { + +/** Wrapper structure for a processed Calc formula token with additional + settings (whitespaces). */ +struct XclExpScToken +{ + const FormulaToken* mpScToken; /// Currently processed Calc token. + sal_uInt8 mnSpaces; /// Number of spaces before the Calc token. + + inline explicit XclExpScToken() : mpScToken( 0 ), mnSpaces( 0 ) {} + inline bool Is() const { return mpScToken != 0; } + inline StackVar GetType() const { return mpScToken ? mpScToken->GetType() : static_cast< StackVar >( svUnknown ); } + inline OpCode GetOpCode() const { return mpScToken ? mpScToken->GetOpCode() : static_cast< OpCode >( ocNone ); } +}; + +// ---------------------------------------------------------------------------- + +/** Effective token class conversion types. */ +enum XclExpClassConv +{ + EXC_CLASSCONV_ORG, /// Keep original class of the token. + EXC_CLASSCONV_VAL, /// Convert ARR tokens to VAL class (REF remains uncahnged). + EXC_CLASSCONV_ARR /// Convert VAL tokens to ARR class (REF remains uncahnged). +}; + +// ---------------------------------------------------------------------------- + +/** Token class conversion and position of a token in the token array. */ +struct XclExpTokenConvInfo +{ + sal_uInt16 mnTokPos; /// Position of the token in the token array. + XclFuncParamConv meConv; /// Token class conversion type. + bool mbValType; /// Data type (false = REFTYPE, true = VALTYPE). +}; + +/** Vector of token position and conversion for all operands of an operator, + or for all parameters of a function. */ +struct XclExpOperandList : public ::std::vector< XclExpTokenConvInfo > +{ + inline explicit XclExpOperandList() { reserve( 2 ); } + void AppendOperand( sal_uInt16 nTokPos, XclFuncParamConv eConv, bool bValType ); +}; + +void XclExpOperandList::AppendOperand( sal_uInt16 nTokPos, XclFuncParamConv eConv, bool bValType ) +{ + resize( size() + 1 ); + XclExpTokenConvInfo& rConvInfo = back(); + rConvInfo.mnTokPos = nTokPos; + rConvInfo.meConv = eConv; + rConvInfo.mbValType = bValType; +} + +typedef ScfRef< XclExpOperandList > XclExpOperandListRef; +typedef ::std::vector< XclExpOperandListRef > XclExpOperandListVector; + +// ---------------------------------------------------------------------------- + +/** Encapsulates all data needed for a call to an external function (macro, add-in). */ +struct XclExpExtFuncData +{ + String maFuncName; /// Name of the function. + bool mbVBasic; /// True = Visual Basic macro call. + bool mbHidden; /// True = Create hidden defined name. + + inline explicit XclExpExtFuncData() : mbVBasic( false ), mbHidden( false ) {} + void Set( const String& rFuncName, bool bVBasic, bool bHidden ); +}; + +void XclExpExtFuncData::Set( const String& rFuncName, bool bVBasic, bool bHidden ) +{ + maFuncName = rFuncName; + mbVBasic = bVBasic; + mbHidden = bHidden; +} + +// ---------------------------------------------------------------------------- + +/** Encapsulates all data needed to process an entire function. */ +class XclExpFuncData +{ +public: + explicit XclExpFuncData( + const XclExpScToken& rTokData, + const XclFunctionInfo& rFuncInfo, + const XclExpExtFuncData& rExtFuncData ); + + inline const FormulaToken& GetScToken() const { return *mrTokData.mpScToken; } + inline OpCode GetOpCode() const { return mrFuncInfo.meOpCode; } + inline sal_uInt16 GetXclFuncIdx() const { return mrFuncInfo.mnXclFunc; } + inline bool IsVolatile() const { return mrFuncInfo.IsVolatile(); } + inline bool IsFixedParamCount() const { return mrFuncInfo.IsFixedParamCount(); } + inline bool IsMacroFunc() const { return mrFuncInfo.IsMacroFunc(); } + inline sal_uInt8 GetSpaces() const { return mrTokData.mnSpaces; } + inline const XclExpExtFuncData& GetExtFuncData() const { return maExtFuncData; } + inline sal_uInt8 GetReturnClass() const { return mrFuncInfo.mnRetClass; } + + const XclFuncParamInfo& GetParamInfo() const; + bool IsCalcOnlyParam() const; + bool IsExcelOnlyParam() const; + void IncParamInfoIdx(); + + inline sal_uInt8 GetMinParamCount() const { return mrFuncInfo.mnMinParamCount; } + inline sal_uInt8 GetMaxParamCount() const { return mrFuncInfo.mnMaxParamCount; } + inline sal_uInt8 GetParamCount() const { return static_cast< sal_uInt8 >( mxOperands->size() ); } + void FinishParam( sal_uInt16 nTokPos ); + inline XclExpOperandListRef GetOperandList() const { return mxOperands; } + + inline ScfUInt16Vec& GetAttrPosVec() { return maAttrPosVec; } + inline void AppendAttrPos( sal_uInt16 nPos ) { maAttrPosVec.push_back( nPos ); } + +private: + ScfUInt16Vec maAttrPosVec; /// Token array positions of tAttr tokens. + const XclExpScToken& mrTokData; /// Data about processed function name token. + const XclFunctionInfo& mrFuncInfo; /// Constant data about processed function. + XclExpExtFuncData maExtFuncData; /// Data for external functions (macro, add-in). + XclExpOperandListRef mxOperands; /// Class conversion and position of all parameters. + const XclFuncParamInfo* mpParamInfo; /// Information for current parameter. +}; + +XclExpFuncData::XclExpFuncData( const XclExpScToken& rTokData, + const XclFunctionInfo& rFuncInfo, const XclExpExtFuncData& rExtFuncData ) : + mrTokData( rTokData ), + mrFuncInfo( rFuncInfo ), + maExtFuncData( rExtFuncData ), + mxOperands( new XclExpOperandList ), + mpParamInfo( rFuncInfo.mpParamInfos ) +{ + DBG_ASSERT( mrTokData.mpScToken, "XclExpFuncData::XclExpFuncData - missing core token" ); + // set name of an add-in function + if( (maExtFuncData.maFuncName.Len() == 0) && dynamic_cast< const FormulaExternalToken* >( mrTokData.mpScToken ) ) + maExtFuncData.Set( GetScToken().GetExternal(), true, false ); +} + +const XclFuncParamInfo& XclExpFuncData::GetParamInfo() const +{ + static const XclFuncParamInfo saInvalidInfo = { EXC_PARAM_NONE, EXC_PARAMCONV_ORG, false }; + return mpParamInfo ? *mpParamInfo : saInvalidInfo; +} + +bool XclExpFuncData::IsCalcOnlyParam() const +{ + return mpParamInfo && (mpParamInfo->meValid == EXC_PARAM_CALCONLY); +} + +bool XclExpFuncData::IsExcelOnlyParam() const +{ + return mpParamInfo && (mpParamInfo->meValid == EXC_PARAM_EXCELONLY); +} + +void XclExpFuncData::IncParamInfoIdx() +{ + if( mpParamInfo ) + { + // move pointer to next entry, if something explicit follows + if( (static_cast<size_t>(mpParamInfo - mrFuncInfo.mpParamInfos + 1) < EXC_FUNCINFO_PARAMINFO_COUNT) && (mpParamInfo[ 1 ].meValid != EXC_PARAM_NONE) ) + ++mpParamInfo; + // if last parameter type is 'Excel-only' or 'Calc-only', do not repeat it + else if( IsExcelOnlyParam() || IsCalcOnlyParam() ) + mpParamInfo = 0; + // otherwise: repeat last parameter class + } +} + +void XclExpFuncData::FinishParam( sal_uInt16 nTokPos ) +{ + // write token class conversion info for this parameter + const XclFuncParamInfo& rParamInfo = GetParamInfo(); + mxOperands->AppendOperand( nTokPos, rParamInfo.meConv, rParamInfo.mbValType ); + // move to next parameter info structure + IncParamInfoIdx(); +} + +// compiler configuration ----------------------------------------------------- + +/** Type of token class handling. */ +enum XclExpFmlaClassType +{ + EXC_CLASSTYPE_CELL, /// Cell formula, shared formula. + EXC_CLASSTYPE_ARRAY, /// Array formula, conditional formatting, data validation. + EXC_CLASSTYPE_NAME /// Defined name, range list. +}; + +/** Configuration data of the formula compiler. */ +struct XclExpCompConfig +{ + XclFormulaType meType; /// Type of the formula to be created. + XclExpFmlaClassType meClassType; /// Token class handling type. + bool mbLocalLinkMgr; /// True = local (per-sheet) link manager, false = global. + bool mbFromCell; /// True = Any kind of cell formula (cell, array, shared). + bool mb3DRefOnly; /// True = Only 3D references allowed (e.g. names). + bool mbAllowArrays; /// True = Allow inline arrays. +}; + +/** The table containing configuration data for all formula types. */ +static const XclExpCompConfig spConfigTable[] = +{ + // formula type token class type lclLM inCell 3dOnly allowArray + { EXC_FMLATYPE_CELL, EXC_CLASSTYPE_CELL, true, true, false, true }, + { EXC_FMLATYPE_SHARED, EXC_CLASSTYPE_CELL, true, true, false, true }, + { EXC_FMLATYPE_MATRIX, EXC_CLASSTYPE_ARRAY, true, true, false, true }, + { EXC_FMLATYPE_CONDFMT, EXC_CLASSTYPE_ARRAY, true, false, false, false }, + { EXC_FMLATYPE_DATAVAL, EXC_CLASSTYPE_ARRAY, true, false, false, false }, + { EXC_FMLATYPE_NAME, EXC_CLASSTYPE_NAME, false, false, true, true }, + { EXC_FMLATYPE_CHART, EXC_CLASSTYPE_NAME, true, false, true, true }, + { EXC_FMLATYPE_CONTROL, EXC_CLASSTYPE_NAME, true, false, false, false }, + { EXC_FMLATYPE_WQUERY, EXC_CLASSTYPE_NAME, true, false, true, false }, + { EXC_FMLATYPE_LISTVAL, EXC_CLASSTYPE_NAME, true, false, false, false } +}; + +// ---------------------------------------------------------------------------- + +/** Working data of the formula compiler. Used to push onto a stack for recursive calls. */ +struct XclExpCompData +{ + typedef ScfRef< ScTokenArray > ScTokenArrayRef; + + const XclExpCompConfig& mrCfg; /// Configuration for current formula type. + ScTokenArrayRef mxOwnScTokArr; /// Own clone of a Calc token array. + XclTokenArrayIterator maTokArrIt; /// Iterator in Calc token array. + XclExpLinkManager* mpLinkMgr; /// Link manager for current context (local/global). + XclExpRefLog* mpRefLog; /// Log for external references. + const ScAddress* mpScBasePos; /// Current cell position of the formula. + + ScfUInt8Vec maTokVec; /// Byte vector containing token data. + ScfUInt8Vec maExtDataVec; /// Byte vector containing extended data (arrays, stacked NLRs). + XclExpOperandListVector maOpListVec; /// Formula structure, maps operators to their operands. + ScfUInt16Vec maOpPosStack; /// Stack with positions of operand tokens waiting for an operator. + bool mbStopAtSep; /// True = Stop subexpression creation at an ocSep token. + bool mbVolatile; /// True = Formula contains volatile function. + bool mbOk; /// Current state of the compiler. + + explicit XclExpCompData( const XclExpCompConfig* pCfg ); +}; + +XclExpCompData::XclExpCompData( const XclExpCompConfig* pCfg ) : + mrCfg( pCfg ? *pCfg : spConfigTable[ 0 ] ), + mpLinkMgr( 0 ), + mpRefLog( 0 ), + mpScBasePos( 0 ), + mbStopAtSep( false ), + mbVolatile( false ), + mbOk( pCfg != 0 ) +{ + DBG_ASSERT( pCfg, "XclExpFmlaCompImpl::Init - unknown formula type" ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +/** Implementation class of the export formula compiler. */ +class XclExpFmlaCompImpl : protected XclExpRoot, protected XclTokenArrayHelper +{ +public: + explicit XclExpFmlaCompImpl( const XclExpRoot& rRoot ); + + /** Creates an Excel token array from the passed Calc token array. */ + XclTokenArrayRef CreateFormula( + XclFormulaType eType, const ScTokenArray& rScTokArr, + const ScAddress* pScBasePos = 0, XclExpRefLog* pRefLog = 0 ); + /** Creates a single error token containing the passed error code. */ + XclTokenArrayRef CreateErrorFormula( sal_uInt8 nErrCode ); + /** Creates a single token for a special cell reference. */ + XclTokenArrayRef CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos ); + /** Creates a single tNameXR token for a reference to an external name. */ + XclTokenArrayRef CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName ); + + /** Returns true, if the passed formula type allows 3D references only. */ + bool Is3DRefOnly( XclFormulaType eType ) const; + + // ------------------------------------------------------------------------ +private: + const XclExpCompConfig* GetConfigForType( XclFormulaType eType ) const; + inline sal_uInt16 GetSize() const { return static_cast< sal_uInt16 >( mxData->maTokVec.size() ); } + + void Init( XclFormulaType eType ); + void Init( XclFormulaType eType, const ScTokenArray& rScTokArr, + const ScAddress* pScBasePos, XclExpRefLog* pRefLog ); + + void RecalcTokenClasses(); + void RecalcTokenClass( const XclExpTokenConvInfo& rConvInfo, XclFuncParamConv ePrevConv, XclExpClassConv ePrevClassConv, bool bWasRefClass ); + + void FinalizeFormula(); + XclTokenArrayRef CreateTokenArray(); + + // compiler --------------------------------------------------------------- + // XclExpScToken: pass-by-value and return-by-value is intended + + const FormulaToken* GetNextRawToken(); + const FormulaToken* PeekNextRawToken( bool bSkipSpaces ) const; + + bool GetNextToken( XclExpScToken& rTokData ); + XclExpScToken GetNextToken(); + + XclExpScToken Expression( XclExpScToken aTokData, bool bInParentheses, bool bStopAtSep ); + XclExpScToken SkipExpression( XclExpScToken aTokData, bool bStopAtSep ); + + XclExpScToken OrTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken AndTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken CompareTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken ConcatTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken AddSubTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken MulDivTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken PowTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken UnaryPostTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken UnaryPreTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken ListTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken IntersectTerm( XclExpScToken aTokData, bool& rbHasRefOp ); + XclExpScToken RangeTerm( XclExpScToken aTokData, bool& rbHasRefOp ); + XclExpScToken Factor( XclExpScToken aTokData ); + + // formula structure ------------------------------------------------------ + + void ProcessDouble( const XclExpScToken& rTokData ); + void ProcessString( const XclExpScToken& rTokData ); + void ProcessError( const XclExpScToken& rTokData ); + void ProcessMissing( const XclExpScToken& rTokData ); + void ProcessBad( const XclExpScToken& rTokData ); + void ProcessParentheses( const XclExpScToken& rTokData ); + void ProcessBoolean( const XclExpScToken& rTokData ); + void ProcessDdeLink( const XclExpScToken& rTokData ); + void ProcessExternal( const XclExpScToken& rTokData ); + void ProcessMatrix( const XclExpScToken& rTokData ); + + void ProcessFunction( const XclExpScToken& rTokData ); + void PrepareFunction( XclExpFuncData& rFuncData ); + void FinishFunction( XclExpFuncData& rFuncData, sal_uInt8 nCloseSpaces ); + void FinishIfFunction( XclExpFuncData& rFuncData ); + void FinishChooseFunction( XclExpFuncData& rFuncData ); + + XclExpScToken ProcessParam( XclExpScToken aTokData, XclExpFuncData& rFuncData ); + void PrepareParam( XclExpFuncData& rFuncData ); + void FinishParam( XclExpFuncData& rFuncData ); + void AppendDefaultParam( XclExpFuncData& rFuncData ); + void AppendTrailingParam( XclExpFuncData& rFuncData ); + + // reference handling ----------------------------------------------------- + + SCTAB GetScTab( const ScSingleRefData& rRefData ) const; + bool IsRef2D( const ScSingleRefData& rRefData ) const; + bool IsRef2D( const ScComplexRefData& rRefData ) const; + + void ConvertRefData( ScSingleRefData& rRefData, XclAddress& rXclPos, + bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const; + void ConvertRefData( ScComplexRefData& rRefData, XclRange& rXclRange, + bool bNatLangRef ) const; + + XclExpRefLogEntry* GetNewRefLogEntry(); + void ProcessCellRef( const XclExpScToken& rTokData ); + void ProcessRangeRef( const XclExpScToken& rTokData ); + void ProcessExternalCellRef( const XclExpScToken& rTokData ); + void ProcessExternalRangeRef( const XclExpScToken& rTokData ); + void ProcessDefinedName( const XclExpScToken& rTokData ); + void ProcessExternalName( const XclExpScToken& rTokData ); + void ProcessDatabaseArea( const XclExpScToken& rTokData ); + + // token vector ----------------------------------------------------------- + + void PushOperandPos( sal_uInt16 nTokPos ); + void PushOperatorPos( sal_uInt16 nTokPos, const XclExpOperandListRef& rxOperands ); + sal_uInt16 PopOperandPos(); + + void Append( sal_uInt8 nData ); + void Append( sal_uInt8 nData, size_t nCount ); + void Append( sal_uInt16 nData ); + void Append( sal_uInt32 nData ); + void Append( double fData ); + void Append( const String& rString ); + + void AppendAddress( const XclAddress& rXclPos ); + void AppendRange( const XclRange& rXclRange ); + + void AppendSpaceToken( sal_uInt8 nType, sal_uInt8 nCount ); + + void AppendOperandTokenId( sal_uInt8 nTokenId, sal_uInt8 nSpaces = 0 ); + void AppendIntToken( sal_uInt16 nValue, sal_uInt8 nSpaces = 0 ); + void AppendNumToken( double fValue, sal_uInt8 nSpaces = 0 ); + void AppendBoolToken( bool bValue, sal_uInt8 nSpaces = 0 ); + void AppendErrorToken( sal_uInt8 nErrCode, sal_uInt8 nSpaces = 0 ); + void AppendMissingToken( sal_uInt8 nSpaces = 0 ); + void AppendNameToken( sal_uInt16 nNameIdx, sal_uInt8 nSpaces = 0 ); + void AppendMissingNameToken( const String& rName, sal_uInt8 nSpaces = 0 ); + void AppendNameXToken( sal_uInt16 nExtSheet, sal_uInt16 nExtName, sal_uInt8 nSpaces = 0 ); + void AppendMacroCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces = 0 ); + void AppendAddInCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces = 0 ); + void AppendEuroToolCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces = 0 ); + + void AppendOperatorTokenId( sal_uInt8 nTokenId, const XclExpOperandListRef& rxOperands, sal_uInt8 nSpaces = 0 ); + void AppendUnaryOperatorToken( sal_uInt8 nTokenId, sal_uInt8 nSpaces = 0 ); + void AppendBinaryOperatorToken( sal_uInt8 nTokenId, bool bValType, sal_uInt8 nSpaces = 0 ); + void AppendLogicalOperatorToken( sal_uInt16 nXclFuncIdx, sal_uInt8 nOpCount ); + void AppendFuncToken( const XclExpFuncData& rFuncData ); + + void AppendParenToken( sal_uInt8 nOpenSpaces = 0, sal_uInt8 nCloseSpaces = 0 ); + void AppendJumpToken( XclExpFuncData& rFuncData, sal_uInt8 nAttrType ); + + void InsertZeros( sal_uInt16 nInsertPos, sal_uInt16 nInsertSize ); + void Overwrite( sal_uInt16 nWriteToPos, sal_uInt16 nOffset ); + + void UpdateAttrGoto( sal_uInt16 nAttrPos ); + + bool IsSpaceToken( sal_uInt16 nPos ) const; + void RemoveTrailingParen(); + + void AppendExt( sal_uInt8 nData ); + void AppendExt( sal_uInt8 nData, size_t nCount ); + void AppendExt( sal_uInt16 nData ); + void AppendExt( sal_uInt32 nData ); + void AppendExt( double fData ); + void AppendExt( const String& rString ); + + // ------------------------------------------------------------------------ +private: + typedef ::std::map< XclFormulaType, XclExpCompConfig > XclExpCompConfigMap; + typedef ScfRef< XclExpCompData > XclExpCompDataRef; + typedef ::std::vector< XclExpCompDataRef > XclExpCompDataVector; + + XclExpCompConfigMap maCfgMap; /// Compiler configuration map for all formula types. + XclFunctionProvider maFuncProv; /// Excel function data provider. + XclExpCompDataRef mxData; /// Working data for current formula. + XclExpCompDataVector maDataStack; /// Stack for working data, when compiler is called recursively. + const XclBiff meBiff; /// Cached BIFF version to save GetBiff() calls. + const SCsCOL mnMaxAbsCol; /// Maximum column index. + const SCsROW mnMaxAbsRow; /// Maximum row index. + const SCsCOL mnMaxScCol; /// Maximum column index in Calc itself. + const SCsROW mnMaxScRow; /// Maximum row index in Calc itself. + const sal_uInt16 mnMaxColMask; /// Mask to delete invalid bits in column fields. + const sal_uInt16 mnMaxRowMask; /// Mask to delete invalid bits in row fields. +}; + +// ---------------------------------------------------------------------------- + +XclExpFmlaCompImpl::XclExpFmlaCompImpl( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + maFuncProv( rRoot ), + meBiff( rRoot.GetBiff() ), + mnMaxAbsCol( static_cast< SCsCOL >( rRoot.GetXclMaxPos().Col() ) ), + mnMaxAbsRow( static_cast< SCsROW >( rRoot.GetXclMaxPos().Row() ) ), + mnMaxScCol( static_cast< SCsCOL >( rRoot.GetScMaxPos().Col() ) ), + mnMaxScRow( static_cast< SCsROW >( rRoot.GetScMaxPos().Row() ) ), + mnMaxColMask( static_cast< sal_uInt16 >( rRoot.GetXclMaxPos().Col() ) ), + mnMaxRowMask( static_cast< sal_uInt16 >( rRoot.GetXclMaxPos().Row() ) ) +{ + // build the configuration map + for( const XclExpCompConfig* pEntry = spConfigTable; pEntry != STATIC_TABLE_END( spConfigTable ); ++pEntry ) + maCfgMap[ pEntry->meType ] = *pEntry; +} + +XclTokenArrayRef XclExpFmlaCompImpl::CreateFormula( XclFormulaType eType, + const ScTokenArray& rScTokArr, const ScAddress* pScBasePos, XclExpRefLog* pRefLog ) +{ + // initialize the compiler + Init( eType, rScTokArr, pScBasePos, pRefLog ); + + // start compilation, if initialization didn't fail + if( mxData->mbOk ) + { + XclExpScToken aTokData( GetNextToken() ); + USHORT nScError = rScTokArr.GetCodeError(); + if( (nScError != 0) && (!aTokData.Is() || (aTokData.GetOpCode() == ocStop)) ) + { + // #i50253# convert simple ocStop token to error code formula (e.g. =#VALUE!) + AppendErrorToken( XclTools::GetXclErrorCode( nScError ), aTokData.mnSpaces ); + } + else if( aTokData.Is() ) + { + aTokData = Expression( aTokData, false, false ); + } + else + { + DBG_ERRORFILE( "XclExpFmlaCompImpl::CreateFormula - empty token array" ); + mxData->mbOk = false; + } + + if( mxData->mbOk ) + { + // #i44907# auto-generated SUBTOTAL formula cells have trailing ocStop token + mxData->mbOk = !aTokData.Is() || (aTokData.GetOpCode() == ocStop); + DBG_ASSERT( mxData->mbOk, "XclExpFmlaCompImpl::CreateFormula - unknown garbage behind formula" ); + } + } + + // finalize (add tAttrVolatile token, calculate all token classes) + RecalcTokenClasses(); + FinalizeFormula(); + + // leave recursive call, create and return the final token array + return CreateTokenArray(); +} + +XclTokenArrayRef XclExpFmlaCompImpl::CreateErrorFormula( sal_uInt8 nErrCode ) +{ + Init( EXC_FMLATYPE_NAME ); + AppendErrorToken( nErrCode ); + return CreateTokenArray(); +} + +XclTokenArrayRef XclExpFmlaCompImpl::CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos ) +{ + Init( EXC_FMLATYPE_NAME ); + AppendOperandTokenId( nTokenId ); + Append( rXclPos.mnRow ); + Append( rXclPos.mnCol ); // do not use AppendAddress(), we always need 16-bit column here + return CreateTokenArray(); +} + +XclTokenArrayRef XclExpFmlaCompImpl::CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) +{ + Init( EXC_FMLATYPE_NAME ); + AppendNameXToken( nExtSheet, nExtName ); + return CreateTokenArray(); +} + +bool XclExpFmlaCompImpl::Is3DRefOnly( XclFormulaType eType ) const +{ + const XclExpCompConfig* pCfg = GetConfigForType( eType ); + return pCfg && pCfg->mb3DRefOnly; +} + +// private -------------------------------------------------------------------- + +const XclExpCompConfig* XclExpFmlaCompImpl::GetConfigForType( XclFormulaType eType ) const +{ + XclExpCompConfigMap::const_iterator aIt = maCfgMap.find( eType ); + DBG_ASSERT( aIt != maCfgMap.end(), "XclExpFmlaCompImpl::GetConfigForType - unknown formula type" ); + return (aIt == maCfgMap.end()) ? 0 : &aIt->second; +} + +void XclExpFmlaCompImpl::Init( XclFormulaType eType ) +{ + // compiler invoked recursively? - store old working data + if( mxData.get() ) + maDataStack.push_back( mxData ); + // new compiler working data structure + mxData.reset( new XclExpCompData( GetConfigForType( eType ) ) ); +} + +void XclExpFmlaCompImpl::Init( XclFormulaType eType, const ScTokenArray& rScTokArr, + const ScAddress* pScBasePos, XclExpRefLog* pRefLog ) +{ + // common initialization + Init( eType ); + + // special initialization + if( mxData->mbOk ) switch( mxData->mrCfg.meType ) + { + case EXC_FMLATYPE_CELL: + case EXC_FMLATYPE_MATRIX: + case EXC_FMLATYPE_CHART: + mxData->mbOk = pScBasePos != 0; + DBG_ASSERT( mxData->mbOk, "XclExpFmlaCompImpl::Init - missing cell address" ); + mxData->mpScBasePos = pScBasePos; + break; + case EXC_FMLATYPE_SHARED: + mxData->mbOk = pScBasePos != 0; + DBG_ASSERT( mxData->mbOk, "XclExpFmlaCompImpl::Init - missing cell address" ); + // clone the passed token array, convert references relative to current cell position + mxData->mxOwnScTokArr.reset( rScTokArr.Clone() ); + ScCompiler::MoveRelWrap( *mxData->mxOwnScTokArr, GetDocPtr(), *pScBasePos, MAXCOL, MAXROW ); + // don't remember pScBasePos in mxData->mpScBasePos, shared formulas use real relative refs + break; + default:; + } + + if( mxData->mbOk ) + { + // link manager to be used + mxData->mpLinkMgr = mxData->mrCfg.mbLocalLinkMgr ? &GetLocalLinkManager() : &GetGlobalLinkManager(); + + // token array iterator (use cloned token array if present) + mxData->maTokArrIt.Init( mxData->mxOwnScTokArr.is() ? *mxData->mxOwnScTokArr : rScTokArr, false ); + mxData->mpRefLog = pRefLog; + } +} + +void XclExpFmlaCompImpl::RecalcTokenClasses() +{ + if( mxData->mbOk ) + { + mxData->mbOk = mxData->maOpPosStack.size() == 1; + DBG_ASSERT( mxData->mbOk, "XclExpFmlaCompImpl::RecalcTokenClasses - position of root token expected on stack" ); + if( mxData->mbOk ) + { + /* Cell and array formulas start with VAL conversion and VALTYPE + parameter type, defined names start with ARR conversion and + REFTYPE parameter type for the root token. */ + XclExpOperandList aOperands; + bool bNameFmla = mxData->mrCfg.meClassType == EXC_CLASSTYPE_NAME; + XclFuncParamConv eParamConv = bNameFmla ? EXC_PARAMCONV_ARR : EXC_PARAMCONV_VAL; + XclExpClassConv eClassConv = bNameFmla ? EXC_CLASSCONV_ARR : EXC_CLASSCONV_VAL; + XclExpTokenConvInfo aConvInfo = { PopOperandPos(), eParamConv, !bNameFmla }; + RecalcTokenClass( aConvInfo, eParamConv, eClassConv, bNameFmla ); + } + + // clear operand vectors (calls to the expensive InsertZeros() may follow) + mxData->maOpListVec.clear(); + mxData->maOpPosStack.clear(); + } +} + +void XclExpFmlaCompImpl::RecalcTokenClass( const XclExpTokenConvInfo& rConvInfo, + XclFuncParamConv ePrevConv, XclExpClassConv ePrevClassConv, bool bWasRefClass ) +{ + DBG_ASSERT( rConvInfo.mnTokPos < GetSize(), "XclExpFmlaCompImpl::RecalcTokenClass - invalid token position" ); + sal_uInt8& rnTokenId = mxData->maTokVec[ rConvInfo.mnTokPos ]; + sal_uInt8 nTokClass = GetTokenClass( rnTokenId ); + + // REF tokens in VALTYPE parameters behave like VAL tokens + if( rConvInfo.mbValType && (nTokClass == EXC_TOKCLASS_REF) ) + ChangeTokenClass( rnTokenId, nTokClass = EXC_TOKCLASS_VAL ); + + // replace RPO conversion of operator with parent conversion + XclFuncParamConv eConv = (rConvInfo.meConv == EXC_PARAMCONV_RPO) ? ePrevConv : rConvInfo.meConv; + + // find the effective token class conversion to be performed for this token + XclExpClassConv eClassConv = EXC_CLASSCONV_ORG; + switch( eConv ) + { + case EXC_PARAMCONV_ORG: + // conversion is forced independent of parent conversion + eClassConv = EXC_CLASSCONV_ORG; + break; + case EXC_PARAMCONV_VAL: + // conversion is forced independent of parent conversion + eClassConv = EXC_CLASSCONV_VAL; + break; + case EXC_PARAMCONV_ARR: + // conversion is forced independent of parent conversion + eClassConv = EXC_CLASSCONV_ARR; + break; + case EXC_PARAMCONV_RPT: + switch( ePrevConv ) + { + case EXC_PARAMCONV_ORG: + case EXC_PARAMCONV_VAL: + case EXC_PARAMCONV_ARR: + /* If parent token has REF class (REF token in REFTYPE + function parameter), then RPT does not repeat the + previous explicit ORG or ARR conversion, but always + falls back to VAL conversion. */ + eClassConv = bWasRefClass ? EXC_CLASSCONV_VAL : ePrevClassConv; + break; + case EXC_PARAMCONV_RPT: + // nested RPT repeats the previous effective conversion + eClassConv = ePrevClassConv; + break; + case EXC_PARAMCONV_RPX: + /* If parent token has REF class (REF token in REFTYPE + function parameter), then RPX repeats the previous + effective conversion (wich will be either ORG or ARR, + but never VAL), otherwise falls back to ORG conversion. */ + eClassConv = bWasRefClass ? ePrevClassConv : EXC_CLASSCONV_ORG; + break; + case EXC_PARAMCONV_RPO: // does not occur + break; + } + break; + case EXC_PARAMCONV_RPX: + /* If current token still has REF class, set previous effective + conversion as current conversion. This will not have an effect + on the REF token but is needed for RPT parameters of this + function that want to repeat this conversion type. If current + token is VAL or ARR class, the previous ARR conversion will be + repeated on the token, but VAL conversion will not. */ + eClassConv = ((nTokClass == EXC_TOKCLASS_REF) || (ePrevClassConv == EXC_CLASSCONV_ARR)) ? + ePrevClassConv : EXC_CLASSCONV_ORG; + break; + case EXC_PARAMCONV_RPO: // does not occur (see above) + break; + } + + // do the token class conversion + switch( eClassConv ) + { + case EXC_CLASSCONV_ORG: + /* Cell formulas: leave the current token class. Cell formulas + are the only type of formulas where all tokens can keep + their original token class. + Array and defined name formulas: convert VAL to ARR. */ + if( (mxData->mrCfg.meClassType != EXC_CLASSTYPE_CELL) && (nTokClass == EXC_TOKCLASS_VAL) ) + ChangeTokenClass( rnTokenId, nTokClass = EXC_TOKCLASS_ARR ); + break; + case EXC_CLASSCONV_VAL: + // convert ARR to VAL + if( nTokClass == EXC_TOKCLASS_ARR ) + ChangeTokenClass( rnTokenId, nTokClass = EXC_TOKCLASS_VAL ); + break; + case EXC_CLASSCONV_ARR: + // convert VAL to ARR + if( nTokClass == EXC_TOKCLASS_VAL ) + ChangeTokenClass( rnTokenId, nTokClass = EXC_TOKCLASS_ARR ); + break; + } + + // do conversion for nested operands, if token is an operator or function + if( rConvInfo.mnTokPos < mxData->maOpListVec.size() ) + if( const XclExpOperandList* pOperands = mxData->maOpListVec[ rConvInfo.mnTokPos ].get() ) + for( XclExpOperandList::const_iterator aIt = pOperands->begin(), aEnd = pOperands->end(); aIt != aEnd; ++aIt ) + RecalcTokenClass( *aIt, eConv, eClassConv, nTokClass == EXC_TOKCLASS_REF ); +} + +void XclExpFmlaCompImpl::FinalizeFormula() +{ + if( mxData->mbOk ) + { + // Volatile? Add a tAttrVolatile token at the beginning of the token array. + if( mxData->mbVolatile ) + { + // tAttrSpace token can be extended with volatile flag + if( !IsSpaceToken( 0 ) ) + { + InsertZeros( 0, 4 ); + mxData->maTokVec[ 0 ] = EXC_TOKID_ATTR; + } + mxData->maTokVec[ 1 ] |= EXC_TOK_ATTR_VOLATILE; + } + + // Token array too long? -> error + mxData->mbOk = mxData->maTokVec.size() <= EXC_TOKARR_MAXLEN; + } + + if( !mxData->mbOk ) + { + // Any unrecoverable error? -> Create a =#NA formula. + mxData->maTokVec.clear(); + mxData->maExtDataVec.clear(); + mxData->mbVolatile = false; + AppendErrorToken( EXC_ERR_NA ); + } +} + +XclTokenArrayRef XclExpFmlaCompImpl::CreateTokenArray() +{ + // create the Excel token array from working data before resetting mxData + DBG_ASSERT( mxData->mrCfg.mbAllowArrays || mxData->maExtDataVec.empty(), "XclExpFmlaCompImpl::CreateTokenArray - unexpected extended data" ); + if( !mxData->mrCfg.mbAllowArrays ) + mxData->maExtDataVec.clear(); + XclTokenArrayRef xTokArr( new XclTokenArray( mxData->maTokVec, mxData->maExtDataVec, mxData->mbVolatile ) ); + mxData.reset(); + + // compiler invoked recursively? - restore old working data + if( !maDataStack.empty() ) + { + mxData = maDataStack.back(); + maDataStack.pop_back(); + } + + return xTokArr; +} + +// compiler ------------------------------------------------------------------- + +const FormulaToken* XclExpFmlaCompImpl::GetNextRawToken() +{ + const FormulaToken* pScToken = mxData->maTokArrIt.Get(); + ++mxData->maTokArrIt; + return pScToken; +} + +const FormulaToken* XclExpFmlaCompImpl::PeekNextRawToken( bool bSkipSpaces ) const +{ + /* Returns pointer to next raw token in the token array. The token array + iterator already points to the next token (A call to GetNextToken() + always increases the iterator), so this function just returns the token + the iterator points to. To skip space tokens, a copy of the iterator is + created and set to the passed skip-spaces mode. If spaces have to be + skipped, and the iterator currently points to a space token, the + constructor will move it to the next non-space token. */ + XclTokenArrayIterator aTempIt( mxData->maTokArrIt, bSkipSpaces ); + return aTempIt.Get(); +} + +bool XclExpFmlaCompImpl::GetNextToken( XclExpScToken& rTokData ) +{ + rTokData.mpScToken = GetNextRawToken(); + rTokData.mnSpaces = (rTokData.GetOpCode() == ocSpaces) ? rTokData.mpScToken->GetByte() : 0; + while( rTokData.GetOpCode() == ocSpaces ) + rTokData.mpScToken = GetNextRawToken(); + return rTokData.Is(); +} + +XclExpScToken XclExpFmlaCompImpl::GetNextToken() +{ + XclExpScToken aTokData; + GetNextToken( aTokData ); + return aTokData; +} + +namespace { + +/** Returns the Excel token ID of a comparison operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetCompareTokenId( OpCode eOpCode ) +{ + switch( eOpCode ) + { + case ocLess: return EXC_TOKID_LT; + case ocLessEqual: return EXC_TOKID_LE; + case ocEqual: return EXC_TOKID_EQ; + case ocGreaterEqual: return EXC_TOKID_GE; + case ocGreater: return EXC_TOKID_GT; + case ocNotEqual: return EXC_TOKID_NE; + default:; + } + return EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a string concatenation operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetConcatTokenId( OpCode eOpCode ) +{ + return (eOpCode == ocAmpersand) ? EXC_TOKID_CONCAT : EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of an addition/subtraction operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetAddSubTokenId( OpCode eOpCode ) +{ + switch( eOpCode ) + { + case ocAdd: return EXC_TOKID_ADD; + case ocSub: return EXC_TOKID_SUB; + default:; + } + return EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a multiplication/division operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetMulDivTokenId( OpCode eOpCode ) +{ + switch( eOpCode ) + { + case ocMul: return EXC_TOKID_MUL; + case ocDiv: return EXC_TOKID_DIV; + default:; + } + return EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a power operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetPowTokenId( OpCode eOpCode ) +{ + return (eOpCode == ocPow) ? EXC_TOKID_POWER : EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a trailing unary operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetUnaryPostTokenId( OpCode eOpCode ) +{ + return (eOpCode == ocPercentSign) ? EXC_TOKID_PERCENT : EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a leading unary operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetUnaryPreTokenId( OpCode eOpCode ) +{ + switch( eOpCode ) + { + case ocAdd: return EXC_TOKID_UPLUS; // +(1) + case ocNeg: return EXC_TOKID_UMINUS; // NEG(1) + case ocNegSub: return EXC_TOKID_UMINUS; // -(1) + default:; + } + return EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a reference list operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetListTokenId( OpCode eOpCode, bool bStopAtSep ) +{ + return ((eOpCode == ocUnion) || (!bStopAtSep && (eOpCode == ocSep))) ? EXC_TOKID_LIST : EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a reference intersection operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetIntersectTokenId( OpCode eOpCode ) +{ + return (eOpCode == ocIntersect) ? EXC_TOKID_ISECT : EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a reference range operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetRangeTokenId( OpCode eOpCode ) +{ + return (eOpCode == ocRange) ? EXC_TOKID_RANGE : EXC_TOKID_NONE; +} + +} // namespace + +XclExpScToken XclExpFmlaCompImpl::Expression( XclExpScToken aTokData, bool bInParentheses, bool bStopAtSep ) +{ + if( mxData->mbOk && aTokData.Is() ) + { + // remember old stop-at-ocSep mode, restored below + bool bOldStopAtSep = mxData->mbStopAtSep; + mxData->mbStopAtSep = bStopAtSep; + // start compilation of the subexpression + aTokData = OrTerm( aTokData, bInParentheses ); + // restore old stop-at-ocSep mode + mxData->mbStopAtSep = bOldStopAtSep; + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::SkipExpression( XclExpScToken aTokData, bool bStopAtSep ) +{ + while( mxData->mbOk && aTokData.Is() && (aTokData.GetOpCode() != ocClose) && (!bStopAtSep || (aTokData.GetOpCode() != ocSep)) ) + { + if( aTokData.GetOpCode() == ocOpen ) + { + aTokData = SkipExpression( GetNextToken(), false ); + if( mxData->mbOk ) mxData->mbOk = aTokData.GetOpCode() == ocClose; + } + aTokData = GetNextToken(); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::OrTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = AndTerm( aTokData, bInParentheses ); + sal_uInt8 nParamCount = 1; + while( mxData->mbOk && (aTokData.GetOpCode() == ocOr) ) + { + RemoveTrailingParen(); + aTokData = AndTerm( GetNextToken(), bInParentheses ); + RemoveTrailingParen(); + ++nParamCount; + if( mxData->mbOk ) mxData->mbOk = nParamCount <= EXC_FUNC_MAXPARAM; + } + if( mxData->mbOk && (nParamCount > 1) ) + AppendLogicalOperatorToken( EXC_FUNCID_OR, nParamCount ); + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::AndTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = CompareTerm( aTokData, bInParentheses ); + sal_uInt8 nParamCount = 1; + while( mxData->mbOk && (aTokData.GetOpCode() == ocAnd) ) + { + RemoveTrailingParen(); + aTokData = CompareTerm( GetNextToken(), bInParentheses ); + RemoveTrailingParen(); + ++nParamCount; + if( mxData->mbOk ) mxData->mbOk = nParamCount <= EXC_FUNC_MAXPARAM; + } + if( mxData->mbOk && (nParamCount > 1) ) + AppendLogicalOperatorToken( EXC_FUNCID_AND, nParamCount ); + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::CompareTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = ConcatTerm( aTokData, bInParentheses ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetCompareTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = ConcatTerm( GetNextToken(), bInParentheses ); + AppendBinaryOperatorToken( nOpTokenId, true, nSpaces ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::ConcatTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = AddSubTerm( aTokData, bInParentheses ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetConcatTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = AddSubTerm( GetNextToken(), bInParentheses ); + AppendBinaryOperatorToken( nOpTokenId, true, nSpaces ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::AddSubTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = MulDivTerm( aTokData, bInParentheses ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetAddSubTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = MulDivTerm( GetNextToken(), bInParentheses ); + AppendBinaryOperatorToken( nOpTokenId, true, nSpaces ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::MulDivTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = PowTerm( aTokData, bInParentheses ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetMulDivTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = PowTerm( GetNextToken(), bInParentheses ); + AppendBinaryOperatorToken( nOpTokenId, true, nSpaces ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::PowTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = UnaryPostTerm( aTokData, bInParentheses ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetPowTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = UnaryPostTerm( GetNextToken(), bInParentheses ); + AppendBinaryOperatorToken( nOpTokenId, true, nSpaces ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::UnaryPostTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = UnaryPreTerm( aTokData, bInParentheses ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetUnaryPostTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + AppendUnaryOperatorToken( nOpTokenId, aTokData.mnSpaces ); + GetNextToken( aTokData ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::UnaryPreTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + sal_uInt8 nOpTokenId = mxData->mbOk ? lclGetUnaryPreTokenId( aTokData.GetOpCode() ) : EXC_TOKID_NONE; + if( nOpTokenId != EXC_TOKID_NONE ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = UnaryPreTerm( GetNextToken(), bInParentheses ); + AppendUnaryOperatorToken( nOpTokenId, nSpaces ); + } + else + { + aTokData = ListTerm( aTokData, bInParentheses ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::ListTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + sal_uInt16 nSubExprPos = GetSize(); + bool bHasAnyRefOp = false; + bool bHasListOp = false; + aTokData = IntersectTerm( aTokData, bHasAnyRefOp ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetListTokenId( aTokData.GetOpCode(), mxData->mbStopAtSep )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = IntersectTerm( GetNextToken(), bHasAnyRefOp ); + AppendBinaryOperatorToken( nOpTokenId, false, nSpaces ); + bHasAnyRefOp = bHasListOp = true; + } + if( bHasAnyRefOp ) + { + // add a tMemFunc token enclosing the entire reference subexpression + sal_uInt16 nSubExprSize = GetSize() - nSubExprPos; + InsertZeros( nSubExprPos, 3 ); + mxData->maTokVec[ nSubExprPos ] = GetTokenId( EXC_TOKID_MEMFUNC, EXC_TOKCLASS_REF ); + Overwrite( nSubExprPos + 1, nSubExprSize ); + // update the operand/operator stack (set the list expression as operand of the tMemFunc) + XclExpOperandListRef xOperands( new XclExpOperandList ); + xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_VAL, false ); + PushOperatorPos( nSubExprPos, xOperands ); + } + // #i86439# enclose list operator into parentheses, e.g. Calc's =AREAS(A1~A2) to Excel's =AREAS((A1;A2)) + if( bHasListOp && !bInParentheses ) + AppendParenToken(); + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::IntersectTerm( XclExpScToken aTokData, bool& rbHasRefOp ) +{ + aTokData = RangeTerm( aTokData, rbHasRefOp ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetIntersectTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = RangeTerm( GetNextToken(), rbHasRefOp ); + AppendBinaryOperatorToken( nOpTokenId, false, nSpaces ); + rbHasRefOp = true; + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::RangeTerm( XclExpScToken aTokData, bool& rbHasRefOp ) +{ + aTokData = Factor( aTokData ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetRangeTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = Factor( GetNextToken() ); + AppendBinaryOperatorToken( nOpTokenId, false, nSpaces ); + rbHasRefOp = true; + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::Factor( XclExpScToken aTokData ) +{ + if( !mxData->mbOk || !aTokData.Is() ) return XclExpScToken(); + + switch( aTokData.GetType() ) + { + case svUnknown: mxData->mbOk = false; break; + case svDouble: ProcessDouble( aTokData ); break; + case svString: ProcessString( aTokData ); break; +#if 0 // erAck + case svError: ProcessError( aTokData ); break; +#endif + case svSingleRef: ProcessCellRef( aTokData ); break; + case svDoubleRef: ProcessRangeRef( aTokData ); break; + case svExternalSingleRef: ProcessExternalCellRef( aTokData ); break; + case svExternalDoubleRef: ProcessExternalRangeRef( aTokData ); break; + case svExternalName: ProcessExternalName( aTokData ); break; + case svMatrix: ProcessMatrix( aTokData ); break; + case svExternal: ProcessExternal( aTokData ); break; + + default: switch( aTokData.GetOpCode() ) + { + case ocNone: /* do nothing */ break; + case ocMissing: ProcessMissing( aTokData ); break; + case ocBad: ProcessBad( aTokData ); break; + case ocOpen: ProcessParentheses( aTokData ); break; + case ocName: ProcessDefinedName( aTokData ); break; + case ocDBArea: ProcessDatabaseArea( aTokData ); break; + case ocFalse: + case ocTrue: ProcessBoolean( aTokData ); break; + case ocDde: ProcessDdeLink( aTokData ); break; + default: ProcessFunction( aTokData ); + } + } + + return GetNextToken(); +} + +// formula structure ---------------------------------------------------------- + +void XclExpFmlaCompImpl::ProcessDouble( const XclExpScToken& rTokData ) +{ + double fValue = rTokData.mpScToken->GetDouble(); + double fInt; + double fFrac = modf( fValue, &fInt ); + if( (fFrac == 0.0) && (0.0 <= fInt) && (fInt <= 65535.0) ) + AppendIntToken( static_cast< sal_uInt16 >( fInt ), rTokData.mnSpaces ); + else + AppendNumToken( fValue, rTokData.mnSpaces ); +} + +void XclExpFmlaCompImpl::ProcessString( const XclExpScToken& rTokData ) +{ + AppendOperandTokenId( EXC_TOKID_STR, rTokData.mnSpaces ); + Append( rTokData.mpScToken->GetString() ); +} + +void XclExpFmlaCompImpl::ProcessError( const XclExpScToken& rTokData ) +{ +#if 0 // erAck + AppendErrorToken( XclTools::GetXclErrorCode( rTokData.mpScToken->GetError() ), rTokData.mnSpaces ); +#else + (void)rTokData; // compiler warning +#endif +} + +void XclExpFmlaCompImpl::ProcessMissing( const XclExpScToken& rTokData ) +{ + AppendMissingToken( rTokData.mnSpaces ); +} + +void XclExpFmlaCompImpl::ProcessBad( const XclExpScToken& rTokData ) +{ + AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces ); +} + +void XclExpFmlaCompImpl::ProcessParentheses( const XclExpScToken& rTokData ) +{ + XclExpScToken aTokData = Expression( GetNextToken(), true, false ); + mxData->mbOk = aTokData.GetOpCode() == ocClose; + AppendParenToken( rTokData.mnSpaces, aTokData.mnSpaces ); +} + +void XclExpFmlaCompImpl::ProcessBoolean( const XclExpScToken& rTokData ) +{ + mxData->mbOk = GetNextToken().GetOpCode() == ocOpen; + if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocClose; + if( mxData->mbOk ) + AppendBoolToken( rTokData.GetOpCode() == ocTrue, rTokData.mnSpaces ); +} + +namespace { + +inline bool lclGetTokenString( String& rString, const XclExpScToken& rTokData ) +{ + bool bIsStr = (rTokData.GetType() == svString) && (rTokData.GetOpCode() == ocPush); + if( bIsStr ) + rString = rTokData.mpScToken->GetString(); + return bIsStr; +} + +} // namespace + +void XclExpFmlaCompImpl::ProcessDdeLink( const XclExpScToken& rTokData ) +{ + String aApplic, aTopic, aItem; + + mxData->mbOk = GetNextToken().GetOpCode() == ocOpen; + if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aApplic, GetNextToken() ); + if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocSep; + if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aTopic, GetNextToken() ); + if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocSep; + if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aItem, GetNextToken() ); + if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocClose; + if( mxData->mbOk ) mxData->mbOk = aApplic.Len() && aTopic.Len() && aItem.Len(); + if( mxData->mbOk ) + { + sal_uInt16 nExtSheet, nExtName; + if( mxData->mpLinkMgr && mxData->mpLinkMgr->InsertDde( nExtSheet, nExtName, aApplic, aTopic, aItem ) ) + AppendNameXToken( nExtSheet, nExtName, rTokData.mnSpaces ); + else + AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces ); + } +} + +void XclExpFmlaCompImpl::ProcessExternal( const XclExpScToken& rTokData ) +{ + /* #i47228# Excel import generates svExternal/ocMacro tokens for invalid + names and for external/invalid function calls. This function looks for + the next token in the token array. If it is an opening parenthesis, the + token is processed as external function call, otherwise as undefined name. */ + const FormulaToken* pNextScToken = PeekNextRawToken( true ); + if( !pNextScToken || (pNextScToken->GetOpCode() != ocOpen) ) + AppendMissingNameToken( rTokData.mpScToken->GetExternal(), rTokData.mnSpaces ); + else + ProcessFunction( rTokData ); +} + +void XclExpFmlaCompImpl::ProcessMatrix( const XclExpScToken& rTokData ) +{ + const ScMatrix* pMatrix = static_cast< const ScToken* >( rTokData.mpScToken )->GetMatrix(); + if( pMatrix && mxData->mrCfg.mbAllowArrays ) + { + SCSIZE nScCols, nScRows; + pMatrix->GetDimensions( nScCols, nScRows ); + DBG_ASSERT( (nScCols > 0) && (nScRows > 0), "XclExpFmlaCompImpl::ProcessMatrix - invalid matrix size" ); + sal_uInt16 nCols = ::limit_cast< sal_uInt16 >( nScCols, 0, 256 ); + sal_uInt16 nRows = ::limit_cast< sal_uInt16 >( nScRows, 0, 1024 ); + + // create the tArray token + AppendOperandTokenId( GetTokenId( EXC_TOKID_ARRAY, EXC_TOKCLASS_ARR ), rTokData.mnSpaces ); + Append( static_cast< sal_uInt8 >( (meBiff == EXC_BIFF8) ? (nCols - 1) : nCols ) ); + Append( static_cast< sal_uInt16 >( (meBiff == EXC_BIFF8) ? (nRows - 1) : nRows ) ); + Append( static_cast< sal_uInt32 >( 0 ) ); + + // create the extended data containing the array values + AppendExt( static_cast< sal_uInt8 >( (meBiff == EXC_BIFF8) ? (nCols - 1) : nCols ) ); + AppendExt( static_cast< sal_uInt16 >( (meBiff == EXC_BIFF8) ? (nRows - 1) : nRows ) ); + for( SCSIZE nScRow = 0; nScRow < nScRows; ++nScRow ) + { + for( SCSIZE nScCol = 0; nScCol < nScCols; ++nScCol ) + { + ScMatValType nType; + const ScMatrixValue* pMatVal = pMatrix->Get( nScCol, nScRow, nType ); + DBG_ASSERT( pMatVal, "XclExpFmlaCompImpl::ProcessMatrix - missing matrix value" ); + if( ScMatrix::IsValueType( nType ) ) // value, boolean, or error + { + if( ScMatrix::IsBooleanType( nType ) ) + { + AppendExt( EXC_CACHEDVAL_BOOL ); + AppendExt( static_cast< sal_uInt8 >( pMatVal->GetBoolean() ? 1 : 0 ) ); + AppendExt( 0, 7 ); + } + else if( USHORT nErr = pMatVal->GetError() ) + { + AppendExt( EXC_CACHEDVAL_ERROR ); + AppendExt( XclTools::GetXclErrorCode( nErr ) ); + AppendExt( 0, 7 ); + } + else + { + AppendExt( EXC_CACHEDVAL_DOUBLE ); + AppendExt( pMatVal->fVal ); + } + } + else // string or empty + { + const String& rStr = pMatVal->GetString(); + if( rStr.Len() == 0 ) + { + AppendExt( EXC_CACHEDVAL_EMPTY ); + AppendExt( 0, 8 ); + } + else + { + AppendExt( EXC_CACHEDVAL_STRING ); + AppendExt( rStr ); + } + } + } + } + } + else + { + // array in places that do not allow it (cond fmts, data validation) + AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces ); + } +} + +void XclExpFmlaCompImpl::ProcessFunction( const XclExpScToken& rTokData ) +{ + OpCode eOpCode = rTokData.GetOpCode(); + const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromOpCode( eOpCode ); + + XclExpExtFuncData aExtFuncData; + + // no exportable function found - try to create an external macro call + if( !pFuncInfo && (eOpCode >= SC_OPCODE_START_NO_PAR) ) + { + const String& rFuncName = ScCompiler::GetNativeSymbol( eOpCode ); + if( rFuncName.Len() ) + { + aExtFuncData.Set( rFuncName, true, false ); + pFuncInfo = maFuncProv.GetFuncInfoFromOpCode( ocMacro ); + } + } + + mxData->mbOk = pFuncInfo != 0; + if( !mxData->mbOk ) return; + + // functions simulated by a macro call in file format + if( pFuncInfo->IsMacroFunc() ) + aExtFuncData.Set( pFuncInfo->GetMacroFuncName(), false, true ); + + XclExpFuncData aFuncData( rTokData, *pFuncInfo, aExtFuncData ); + XclExpScToken aTokData; + + // preparations for special functions, before function processing starts + PrepareFunction( aFuncData ); + + enum { STATE_START, STATE_OPEN, STATE_PARAM, STATE_SEP, STATE_CLOSE, STATE_END } + eState = STATE_START; + while( eState != STATE_END ) switch( eState ) + { + case STATE_START: + mxData->mbOk = GetNextToken( aTokData ) && (aTokData.GetOpCode() == ocOpen); + eState = mxData->mbOk ? STATE_OPEN : STATE_END; + break; + case STATE_OPEN: + mxData->mbOk = GetNextToken( aTokData ); + eState = mxData->mbOk ? ((aTokData.GetOpCode() == ocClose) ? STATE_CLOSE : STATE_PARAM) : STATE_END; + break; + case STATE_PARAM: + aTokData = ProcessParam( aTokData, aFuncData ); + switch( aTokData.GetOpCode() ) + { + case ocSep: eState = STATE_SEP; break; + case ocClose: eState = STATE_CLOSE; break; + default: mxData->mbOk = false; + } + if( !mxData->mbOk ) eState = STATE_END; + break; + case STATE_SEP: + mxData->mbOk = (aFuncData.GetParamCount() < EXC_FUNC_MAXPARAM) && GetNextToken( aTokData ); + eState = mxData->mbOk ? STATE_PARAM : STATE_END; + break; + case STATE_CLOSE: + FinishFunction( aFuncData, aTokData.mnSpaces ); + eState = STATE_END; + break; + default:; + } +} + +void XclExpFmlaCompImpl::PrepareFunction( XclExpFuncData& rFuncData ) +{ + switch( rFuncData.GetOpCode() ) + { + case ocCot: // simulate COT(x) by (1/TAN(x)) + case ocCotHyp: // simulate COTH(x) by (1/TANH(x)) + AppendIntToken( 1 ); + break; + case ocArcCot: // simulate ACOT(x) by (PI/2-ATAN(x)) + AppendNumToken( F_PI2 ); + break; + default:; + } +} + +void XclExpFmlaCompImpl::FinishFunction( XclExpFuncData& rFuncData, sal_uInt8 nCloseSpaces ) +{ + // append missing parameters required in Excel, may modify param count + AppendTrailingParam( rFuncData ); + + // check if parameter count fits into the limits of the function + sal_uInt8 nParamCount = rFuncData.GetParamCount(); + if( (rFuncData.GetMinParamCount() <= nParamCount) && (nParamCount <= rFuncData.GetMaxParamCount()) ) + { + // first put the tAttrSpace tokens, they must not be included in tAttrGoto handling + AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_CLOSE, nCloseSpaces ); + AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, rFuncData.GetSpaces() ); + + // add tAttrGoto tokens for IF or CHOOSE functions + switch( rFuncData.GetOpCode() ) + { + case ocIf: + case ocChose: + AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO ); + break; + default:; + } + + // put the tFunc or tFuncVar token (or another special token, e.g. tAttrSum) + AppendFuncToken( rFuncData ); + + // update volatile flag - is set if at least one used function is volatile + mxData->mbVolatile |= rFuncData.IsVolatile(); + + // update jump tokens for specific functions, add additional tokens + switch( rFuncData.GetOpCode() ) + { + case ocIf: + FinishIfFunction( rFuncData ); + break; + case ocChose: + FinishChooseFunction( rFuncData ); + break; + + case ocCot: // simulate COT(x) by (1/TAN(x)) + case ocCotHyp: // simulate COTH(x) by (1/TANH(x)) + AppendBinaryOperatorToken( EXC_TOKID_DIV, true ); + AppendParenToken(); + break; + case ocArcCot: // simulate ACOT(x) by (PI/2-ATAN(x)) + AppendBinaryOperatorToken( EXC_TOKID_SUB, true ); + AppendParenToken(); + break; + + default:; + } + } + else + mxData->mbOk = false; +} + +void XclExpFmlaCompImpl::FinishIfFunction( XclExpFuncData& rFuncData ) +{ + sal_uInt16 nParamCount = rFuncData.GetParamCount(); + DBG_ASSERT( (nParamCount == 2) || (nParamCount == 3), "XclExpFmlaCompImpl::FinishIfFunction - wrong parameter count" ); + const ScfUInt16Vec& rAttrPos = rFuncData.GetAttrPosVec(); + DBG_ASSERT( nParamCount == rAttrPos.size(), "XclExpFmlaCompImpl::FinishIfFunction - wrong number of tAttr tokens" ); + // update tAttrIf token following the condition parameter + Overwrite( rAttrPos[ 0 ] + 2, static_cast< sal_uInt16 >( rAttrPos[ 1 ] - rAttrPos[ 0 ] ) ); + // update the tAttrGoto tokens following true and false parameters + UpdateAttrGoto( rAttrPos[ 1 ] ); + if( nParamCount == 3 ) + UpdateAttrGoto( rAttrPos[ 2 ] ); +} + +void XclExpFmlaCompImpl::FinishChooseFunction( XclExpFuncData& rFuncData ) +{ + sal_uInt16 nParamCount = rFuncData.GetParamCount(); + ScfUInt16Vec& rAttrPos = rFuncData.GetAttrPosVec(); + DBG_ASSERT( nParamCount == rAttrPos.size(), "XclExpFmlaCompImpl::FinishChooseFunction - wrong number of tAttr tokens" ); + // number of choices is parameter count minus 1 + sal_uInt16 nChoices = nParamCount - 1; + // tAttrChoose token contains number of choices + Overwrite( rAttrPos[ 0 ] + 2, nChoices ); + // cache position of the jump table (follows number of choices in tAttrChoose token) + sal_uInt16 nJumpArrPos = rAttrPos[ 0 ] + 4; + // size of jump table: number of choices, plus 1 for error position + sal_uInt16 nJumpArrSize = 2 * (nChoices + 1); + // insert the jump table into the tAttrChoose token + InsertZeros( nJumpArrPos, nJumpArrSize ); + // update positions of tAttrGoto tokens after jump table insertion + sal_uInt16 nIdx; + for( nIdx = 1; nIdx < nParamCount; ++nIdx ) + rAttrPos[ nIdx ] = rAttrPos[ nIdx ] + nJumpArrSize; + // update the tAttrGoto tokens (they contain a value one-less to real distance) + for( nIdx = 1; nIdx < nParamCount; ++nIdx ) + UpdateAttrGoto( rAttrPos[ nIdx ] ); + // update the distances in the jump table + Overwrite( nJumpArrPos, nJumpArrSize ); + for( nIdx = 1; nIdx < nParamCount; ++nIdx ) + Overwrite( nJumpArrPos + 2 * nIdx, static_cast< sal_uInt16 >( rAttrPos[ nIdx ] + 4 - nJumpArrPos ) ); +} + +XclExpScToken XclExpFmlaCompImpl::ProcessParam( XclExpScToken aTokData, XclExpFuncData& rFuncData ) +{ + if( rFuncData.IsCalcOnlyParam() ) + { + // skip Calc-only parameter, stop at next ocClose or ocSep + aTokData = SkipExpression( aTokData, true ); + rFuncData.IncParamInfoIdx(); + } + else + { + // insert Excel-only parameters, modifies param count and class in rFuncData + while( rFuncData.IsExcelOnlyParam() ) + AppendDefaultParam( rFuncData ); + + // process the parameter, stop at next ocClose or ocSep + PrepareParam( rFuncData ); + /* #i37355# insert tMissArg token for missing parameters -- + Excel import filter adds ocMissing token (handled in Factor()), + but Calc itself does not do this if a new formula is entered. */ + switch( aTokData.GetOpCode() ) + { + case ocSep: + case ocClose: AppendMissingToken(); break; // empty parameter + default: aTokData = Expression( aTokData, false, true ); + } + // finalize the parameter and add special tokens, e.g. for IF or CHOOSE parameters + if( mxData->mbOk ) FinishParam( rFuncData ); + } + return aTokData; +} + +void XclExpFmlaCompImpl::PrepareParam( XclExpFuncData& rFuncData ) +{ + // index of this parameter is equal to number of already finished parameters + sal_uInt8 nParamIdx = rFuncData.GetParamCount(); + + switch( rFuncData.GetOpCode() ) + { + case ocIf: + switch( nParamIdx ) + { + // add a tAttrIf token before true-parameter (second parameter) + case 1: AppendJumpToken( rFuncData, EXC_TOK_ATTR_IF ); break; + // add a tAttrGoto token before false-parameter (third parameter) + case 2: AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO ); break; + } + break; + + case ocChose: + switch( nParamIdx ) + { + // do nothing for first parameter + case 0: break; + // add a tAttrChoose token before first value parameter (second parameter) + case 1: AppendJumpToken( rFuncData, EXC_TOK_ATTR_CHOOSE ); break; + // add a tAttrGoto token before other value parameters + default: AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO ); + } + break; + + case ocArcCotHyp: // simulate ACOTH(x) by ATANH(1/(x)) + if( nParamIdx == 0 ) + AppendIntToken( 1 ); + break; + default:; + } +} + +void XclExpFmlaCompImpl::FinishParam( XclExpFuncData& rFuncData ) +{ + // increase parameter count, update operand stack + rFuncData.FinishParam( PopOperandPos() ); + + // append more tokens for parameters of some special functions + sal_uInt8 nParamIdx = rFuncData.GetParamCount() - 1; + switch( rFuncData.GetOpCode() ) + { + case ocArcCotHyp: // simulate ACOTH(x) by ATANH(1/(x)) + if( nParamIdx == 0 ) + { + AppendParenToken(); + AppendBinaryOperatorToken( EXC_TOKID_DIV, true ); + } + break; + default:; + } +} + +void XclExpFmlaCompImpl::AppendDefaultParam( XclExpFuncData& rFuncData ) +{ + // prepare parameters of some special functions + PrepareParam( rFuncData ); + + switch( rFuncData.GetOpCode() ) + { + case ocExternal: + AppendAddInCallToken( rFuncData.GetExtFuncData() ); + break; + case ocEuroConvert: + AppendEuroToolCallToken( rFuncData.GetExtFuncData() ); + break; + case ocMacro: + AppendMacroCallToken( rFuncData.GetExtFuncData() ); + break; + default: + { + DBG_ASSERT( rFuncData.IsMacroFunc(), "XclExpFmlaCompImpl::AppendDefaultParam - unknown opcode" ); + if( rFuncData.IsMacroFunc() ) + AppendMacroCallToken( rFuncData.GetExtFuncData() ); + else + AppendMissingToken(); // to keep parameter count valid + } + } + + // update parameter count, add special parameter tokens + FinishParam( rFuncData ); +} + +void XclExpFmlaCompImpl::AppendTrailingParam( XclExpFuncData& rFuncData ) +{ + sal_uInt8 nParamCount = rFuncData.GetParamCount(); + switch( rFuncData.GetOpCode() ) + { + case ocIf: + if( nParamCount == 1 ) + { + // #112262# Excel needs at least two parameters in IF function + PrepareParam( rFuncData ); + AppendBoolToken( true ); + FinishParam( rFuncData ); + } + break; + + case ocRound: + case ocRoundUp: + case ocRoundDown: + if( nParamCount == 1 ) + { + // ROUND, ROUNDUP, ROUNDDOWN functions are fixed to 2 parameters in Excel + PrepareParam( rFuncData ); + AppendIntToken( 0 ); + FinishParam( rFuncData ); + } + break; + + case ocIndex: + if( nParamCount == 1 ) + { + // INDEX function needs at least 2 parameters in Excel + PrepareParam( rFuncData ); + AppendMissingToken(); + FinishParam( rFuncData ); + } + break; + + case ocExternal: + case ocMacro: + // external or macro call without parameters needs the external name reference + if( nParamCount == 0 ) + AppendDefaultParam( rFuncData ); + break; + + case ocGammaDist: + if( nParamCount == 3 ) + { + // GAMMADIST function needs 4 parameters in Excel + PrepareParam( rFuncData ); + AppendIntToken( 1 ); + FinishParam( rFuncData ); + } + break; + + case ocPoissonDist: + if( nParamCount == 2 ) + { + // POISSON function needs 3 parameters in Excel + PrepareParam( rFuncData ); + AppendIntToken( 1 ); + FinishParam( rFuncData ); + } + break; + + case ocNormDist: + if( nParamCount == 3 ) + { + // NORMDIST function needs 4 parameters in Excel + PrepareParam( rFuncData ); + AppendBoolToken( true ); + FinishParam( rFuncData ); + } + break; + + case ocLogNormDist: + switch( nParamCount ) + { + // LOGNORMDIST function needs 3 parameters in Excel + case 1: + PrepareParam( rFuncData ); + AppendIntToken( 0 ); + FinishParam( rFuncData ); + // do not break, add next default parameter + case 2: + PrepareParam( rFuncData ); + AppendIntToken( 1 ); + FinishParam( rFuncData ); + break; + default:; + } + + break; + + default:; + } +} + +// reference handling --------------------------------------------------------- + +namespace { + +inline bool lclIsRefRel2D( const ScSingleRefData& rRefData ) +{ + return rRefData.IsColRel() || rRefData.IsRowRel(); +} + +inline bool lclIsRefDel2D( const ScSingleRefData& rRefData ) +{ + return rRefData.IsColDeleted() || rRefData.IsRowDeleted(); +} + +inline bool lclIsRefRel2D( const ScComplexRefData& rRefData ) +{ + return lclIsRefRel2D( rRefData.Ref1 ) || lclIsRefRel2D( rRefData.Ref2 ); +} + +inline bool lclIsRefDel2D( const ScComplexRefData& rRefData ) +{ + return lclIsRefDel2D( rRefData.Ref1 ) || lclIsRefDel2D( rRefData.Ref2 ); +} + +} // namespace + +SCTAB XclExpFmlaCompImpl::GetScTab( const ScSingleRefData& rRefData ) const +{ + bool bInvTab = rRefData.IsTabDeleted() || (!mxData->mpScBasePos && IsInGlobals() && rRefData.IsTabRel()); + return bInvTab ? SCTAB_INVALID : static_cast< SCTAB >( rRefData.nTab ); +} + +bool XclExpFmlaCompImpl::IsRef2D( const ScSingleRefData& rRefData ) const +{ + /* rRefData.IsFlag3D() determines if sheet name is always visible, even on + the own sheet. If 3D references are allowed, the passed reference does + not count as 2D reference. */ + return (!mxData->mpLinkMgr || !rRefData.IsFlag3D()) && !rRefData.IsTabDeleted() && + (rRefData.IsTabRel() ? (rRefData.nRelTab == 0) : (static_cast< SCTAB >( rRefData.nTab ) == GetCurrScTab())); +} + +bool XclExpFmlaCompImpl::IsRef2D( const ScComplexRefData& rRefData ) const +{ + return IsRef2D( rRefData.Ref1 ) && IsRef2D( rRefData.Ref2 ); +} + +void XclExpFmlaCompImpl::ConvertRefData( + ScSingleRefData& rRefData, XclAddress& rXclPos, + bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const +{ + if( mxData->mpScBasePos ) + { + // *** reference position exists (cell, matrix) - convert to absolute *** + rRefData.CalcAbsIfRel( *mxData->mpScBasePos ); + + // convert column index + SCsCOL& rnScCol = rRefData.nCol; + if( bTruncMaxCol && (rnScCol == mnMaxScCol) ) + rnScCol = mnMaxAbsCol; + else if( (rnScCol < 0) || (rnScCol > mnMaxAbsCol) ) + rRefData.SetColDeleted( TRUE ); + rXclPos.mnCol = static_cast< sal_uInt16 >( rnScCol ) & mnMaxColMask; + + // convert row index + SCsROW& rnScRow = rRefData.nRow; + if( bTruncMaxRow && (rnScRow == mnMaxScRow) ) + rnScRow = mnMaxAbsRow; + else if( (rnScRow < 0) || (rnScRow > mnMaxAbsRow) ) + rRefData.SetRowDeleted( TRUE ); + rXclPos.mnRow = static_cast< sal_uInt16 >( rnScRow ) & mnMaxRowMask; + } + else + { + // *** no reference position (shared, names, condfmt) - use relative values *** + + // convert column index (2-step-cast ScsCOL->sal_Int16->sal_uInt16 to get all bits correctly) + sal_Int16 nXclRelCol = static_cast< sal_Int16 >( rRefData.IsColRel() ? rRefData.nRelCol : rRefData.nCol ); + rXclPos.mnCol = static_cast< sal_uInt16 >( nXclRelCol ) & mnMaxColMask; + + // convert row index (2-step-cast ScsROW->sal_Int16->sal_uInt16 to get all bits correctly) + sal_Int16 nXclRelRow = static_cast< sal_Int16 >( rRefData.IsRowRel() ? rRefData.nRelRow : rRefData.nRow ); + rXclPos.mnRow = static_cast< sal_uInt16 >( nXclRelRow ) & mnMaxRowMask; + + // resolve relative tab index if possible + if( rRefData.IsTabRel() && !IsInGlobals() && (GetCurrScTab() < GetDoc().GetTableCount()) ) + rRefData.nTab = static_cast< SCsTAB >( GetCurrScTab() + rRefData.nRelTab ); + } + + // flags for relative column and row + if( bNatLangRef ) + { + DBG_ASSERT( meBiff == EXC_BIFF8, "XclExpFmlaCompImpl::ConvertRefData - NLRs only for BIFF8" ); + // Calc does not support absolute reference mode in natural language references + ::set_flag( rXclPos.mnCol, EXC_TOK_NLR_REL ); + } + else + { + sal_uInt16& rnRelField = (meBiff <= EXC_BIFF5) ? rXclPos.mnRow : rXclPos.mnCol; + ::set_flag( rnRelField, EXC_TOK_REF_COLREL, rRefData.IsColRel() ); + ::set_flag( rnRelField, EXC_TOK_REF_ROWREL, rRefData.IsRowRel() ); + } +} + +void XclExpFmlaCompImpl::ConvertRefData( + ScComplexRefData& rRefData, XclRange& rXclRange, bool bNatLangRef ) const +{ + // convert start and end of the range + ConvertRefData( rRefData.Ref1, rXclRange.maFirst, bNatLangRef, false, false ); + bool bTruncMaxCol = !rRefData.Ref1.IsColDeleted() && (rRefData.Ref1.nCol == 0); + bool bTruncMaxRow = !rRefData.Ref1.IsRowDeleted() && (rRefData.Ref1.nRow == 0); + ConvertRefData( rRefData.Ref2, rXclRange.maLast, bNatLangRef, bTruncMaxCol, bTruncMaxRow ); +} + +XclExpRefLogEntry* XclExpFmlaCompImpl::GetNewRefLogEntry() +{ + if( mxData->mpRefLog ) + { + mxData->mpRefLog->resize( mxData->mpRefLog->size() + 1 ); + return &mxData->mpRefLog->back(); + } + return 0; +} + +void XclExpFmlaCompImpl::ProcessCellRef( const XclExpScToken& rTokData ) +{ + // get the Excel address components, adjust internal data in aRefData + bool bNatLangRef = (meBiff == EXC_BIFF8) && mxData->mpScBasePos && (rTokData.GetOpCode() == ocColRowName); + ScSingleRefData aRefData = static_cast< const ScToken* >( rTokData.mpScToken )->GetSingleRef(); + XclAddress aXclPos( ScAddress::UNINITIALIZED ); + ConvertRefData( aRefData, aXclPos, bNatLangRef, false, false ); + + if( bNatLangRef ) + { + DBG_ASSERT( aRefData.IsColRel() != aRefData.IsRowRel(), + "XclExpFmlaCompImpl::ProcessCellRef - broken natural language reference" ); + // create tNlr token for natural language reference + sal_uInt8 nSubId = aRefData.IsColRel() ? EXC_TOK_NLR_COLV : EXC_TOK_NLR_ROWV; + AppendOperandTokenId( EXC_TOKID_NLR, rTokData.mnSpaces ); + Append( nSubId ); + AppendAddress( aXclPos ); + } + else + { + // store external cell contents in CRN records + if( mxData->mrCfg.mbFromCell && mxData->mpLinkMgr && mxData->mpScBasePos ) + mxData->mpLinkMgr->StoreCell( aRefData ); + + // create the tRef, tRefErr, tRefN, tRef3d, or tRefErr3d token + if( !mxData->mrCfg.mb3DRefOnly && IsRef2D( aRefData ) ) + { + // 2D reference (not in defined names, but allowed in range lists) + sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_REFN : + (lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR : EXC_TOKID_REF); + AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces ); + AppendAddress( aXclPos ); + } + else if( mxData->mpLinkMgr ) // 3D reference + { + // 1-based EXTERNSHEET index and 0-based Excel sheet index + sal_uInt16 nExtSheet, nXclTab; + mxData->mpLinkMgr->FindExtSheet( nExtSheet, nXclTab, GetScTab( aRefData ), GetNewRefLogEntry() ); + // write the token + sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR3D : EXC_TOKID_REF3D; + AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces ); + Append( nExtSheet ); + if( meBiff <= EXC_BIFF5 ) + { + Append( 0, 8 ); + Append( nXclTab ); + Append( nXclTab ); + } + AppendAddress( aXclPos ); + } + else + { + // 3D ref in cond. format, or 2D ref in name + AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces ); + } + } +} + +void XclExpFmlaCompImpl::ProcessRangeRef( const XclExpScToken& rTokData ) +{ + // get the Excel address components, adjust internal data in aRefData + ScComplexRefData aRefData = static_cast< const ScToken* >( rTokData.mpScToken )->GetDoubleRef(); + XclRange aXclRange( ScAddress::UNINITIALIZED ); + ConvertRefData( aRefData, aXclRange, false ); + + // store external cell contents in CRN records + if( mxData->mrCfg.mbFromCell && mxData->mpLinkMgr && mxData->mpScBasePos ) + mxData->mpLinkMgr->StoreCellRange( aRefData ); + + // create the tArea, tAreaErr, tAreaN, tArea3d, or tAreaErr3d token + if( !mxData->mrCfg.mb3DRefOnly && IsRef2D( aRefData ) ) + { + // 2D reference (not in name formulas, but allowed in range lists) + sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_AREAN : + (lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR : EXC_TOKID_AREA); + AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces ); + AppendRange( aXclRange ); + } + else if( mxData->mpLinkMgr ) // 3D reference + { + // 1-based EXTERNSHEET index and 0-based Excel sheet indexes + sal_uInt16 nExtSheet, nFirstXclTab, nLastXclTab; + mxData->mpLinkMgr->FindExtSheet( nExtSheet, nFirstXclTab, nLastXclTab, + GetScTab( aRefData.Ref1 ), GetScTab( aRefData.Ref2 ), GetNewRefLogEntry() ); + // write the token + sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR3D : EXC_TOKID_AREA3D; + AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces ); + Append( nExtSheet ); + if( meBiff <= EXC_BIFF5 ) + { + Append( 0, 8 ); + Append( nFirstXclTab ); + Append( nLastXclTab ); + } + AppendRange( aXclRange ); + } + else + { + // 3D ref in cond. format, or 2D ref in name + AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces ); + } +} + +void XclExpFmlaCompImpl::ProcessExternalCellRef( const XclExpScToken& rTokData ) +{ + if( mxData->mpLinkMgr ) + { + // get the Excel address components, adjust internal data in aRefData + ScSingleRefData aRefData = static_cast< const ScToken* >( rTokData.mpScToken )->GetSingleRef(); + XclAddress aXclPos( ScAddress::UNINITIALIZED ); + ConvertRefData( aRefData, aXclPos, false, false, false ); + + // store external cell contents in CRN records + USHORT nFileId = rTokData.mpScToken->GetIndex(); + const String& rTabName = rTokData.mpScToken->GetString(); + if( mxData->mrCfg.mbFromCell && mxData->mpScBasePos ) + mxData->mpLinkMgr->StoreCell( nFileId, rTabName, aRefData ); + + // 1-based EXTERNSHEET index and 0-based Excel sheet indexes + sal_uInt16 nExtSheet, nFirstSBTab, nLastSBTab; + mxData->mpLinkMgr->FindExtSheet( nFileId, rTabName, 1, nExtSheet, nFirstSBTab, nLastSBTab, GetNewRefLogEntry() ); + // write the token + sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR3D : EXC_TOKID_REF3D; + AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces ); + Append( nExtSheet ); + if( meBiff <= EXC_BIFF5 ) + { + Append( 0, 8 ); + Append( nFirstSBTab ); + Append( nLastSBTab ); + } + AppendAddress( aXclPos ); + } + else + { + AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces ); + } +} + +void XclExpFmlaCompImpl::ProcessExternalRangeRef( const XclExpScToken& rTokData ) +{ + if( mxData->mpLinkMgr ) + { + // get the Excel address components, adjust internal data in aRefData + ScComplexRefData aRefData = static_cast< const ScToken* >( rTokData.mpScToken )->GetDoubleRef(); + XclRange aXclRange( ScAddress::UNINITIALIZED ); + ConvertRefData( aRefData, aXclRange, false ); + + // store external cell contents in CRN records + USHORT nFileId = rTokData.mpScToken->GetIndex(); + const String& rTabName = rTokData.mpScToken->GetString(); + if( mxData->mrCfg.mbFromCell && mxData->mpScBasePos ) + mxData->mpLinkMgr->StoreCellRange( nFileId, rTabName, aRefData ); + + // 1-based EXTERNSHEET index and 0-based Excel sheet indexes + sal_uInt16 nExtSheet, nFirstSBTab, nLastSBTab; + sal_uInt16 nTabSpan = static_cast< sal_uInt16 >( aRefData.Ref2.nTab - aRefData.Ref1.nTab + 1 ); + mxData->mpLinkMgr->FindExtSheet( nFileId, rTabName, nTabSpan, nExtSheet, nFirstSBTab, nLastSBTab, GetNewRefLogEntry() ); + // write the token + sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR3D : EXC_TOKID_AREA3D; + AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces ); + Append( nExtSheet ); + if( meBiff <= EXC_BIFF5 ) + { + Append( 0, 8 ); + Append( nFirstSBTab ); + Append( nLastSBTab ); + } + AppendRange( aXclRange ); + } + else + { + AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces ); + } +} + +void XclExpFmlaCompImpl::ProcessDefinedName( const XclExpScToken& rTokData ) +{ + XclExpNameManager& rNameMgr = GetNameManager(); + sal_uInt16 nNameIdx = rNameMgr.InsertName( rTokData.mpScToken->GetIndex() ); + if( nNameIdx != 0 ) + { + // global names always with tName token, local names dependent on config + SCTAB nScTab = rNameMgr.GetScTab( nNameIdx ); + if( (nScTab == SCTAB_GLOBAL) || (!mxData->mrCfg.mb3DRefOnly && (nScTab == GetCurrScTab())) ) + { + AppendNameToken( nNameIdx, rTokData.mnSpaces ); + } + else if( mxData->mpLinkMgr ) + { + // use the same special EXTERNNAME to refer to any local name + sal_uInt16 nExtSheet = mxData->mpLinkMgr->FindExtSheet( EXC_EXTSH_OWNDOC ); + AppendNameXToken( nExtSheet, nNameIdx, rTokData.mnSpaces ); + } + else + AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces ); + // volatile names (containing volatile functions) + mxData->mbVolatile |= rNameMgr.IsVolatile( nNameIdx ); + } + else + AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces ); +} + +void XclExpFmlaCompImpl::ProcessExternalName( const XclExpScToken& rTokData ) +{ + if( mxData->mpLinkMgr ) + { + ScExternalRefManager& rExtRefMgr = *GetDoc().GetExternalRefManager(); + USHORT nFileId = rTokData.mpScToken->GetIndex(); + const String& rName = rTokData.mpScToken->GetString(); + ScExternalRefCache::TokenArrayRef xArray = rExtRefMgr.getRangeNameTokens( nFileId, rName ); + if( xArray.get() ) + { + // store external cell contents in CRN records + if( mxData->mpScBasePos ) + { + for( FormulaToken* pScToken = xArray->First(); pScToken; pScToken = xArray->Next() ) + { + if( pScToken->GetOpCode() == ocExternalRef ) + { + switch( pScToken->GetType() ) + { + case svExternalSingleRef: + { + ScSingleRefData aRefData = static_cast< ScToken* >( pScToken )->GetSingleRef(); + aRefData.CalcAbsIfRel( *mxData->mpScBasePos ); + mxData->mpLinkMgr->StoreCell( nFileId, pScToken->GetString(), aRefData ); + } + break; + case svExternalDoubleRef: + { + ScComplexRefData aRefData = static_cast< ScToken* >( pScToken )->GetDoubleRef(); + aRefData.CalcAbsIfRel( *mxData->mpScBasePos ); + mxData->mpLinkMgr->StoreCellRange( nFileId, pScToken->GetString(), aRefData ); + } + default: + ; // nothing, avoid compiler warning + } + } + } + } + + // insert the new external name and create the tNameX token + sal_uInt16 nExtSheet, nExtName; + const String* pFile = rExtRefMgr.getExternalFileName( nFileId ); + if( pFile && mxData->mpLinkMgr->InsertExtName( nExtSheet, nExtName, *pFile, rName, xArray ) ) + { + AppendNameXToken( nExtSheet, nExtName, rTokData.mnSpaces ); + return; + } + } + } + + // on any error: create a #NAME? error + AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces ); +} + +void XclExpFmlaCompImpl::ProcessDatabaseArea( const XclExpScToken& rTokData ) +{ + sal_uInt16 nNameIdx = GetNameManager().InsertDBRange( rTokData.mpScToken->GetIndex() ); + AppendNameToken( nNameIdx, rTokData.mnSpaces ); +} + +// token vector --------------------------------------------------------------- + +void XclExpFmlaCompImpl::PushOperandPos( sal_uInt16 nTokPos ) +{ + mxData->maOpPosStack.push_back( nTokPos ); +} + +void XclExpFmlaCompImpl::PushOperatorPos( sal_uInt16 nTokPos, const XclExpOperandListRef& rxOperands ) +{ + PushOperandPos( nTokPos ); + DBG_ASSERT( rxOperands.get(), "XclExpFmlaCompImpl::AppendOperatorTokenId - missing operand list" ); + if( mxData->maOpListVec.size() <= nTokPos ) + mxData->maOpListVec.resize( nTokPos + 1, XclExpOperandListRef() ); + mxData->maOpListVec[ nTokPos ] = rxOperands; +} + +sal_uInt16 XclExpFmlaCompImpl::PopOperandPos() +{ + DBG_ASSERT( !mxData->mbOk || !mxData->maOpPosStack.empty(), "XclExpFmlaCompImpl::PopOperandPos - token stack broken" ); + mxData->mbOk &= !mxData->maOpPosStack.empty(); + if( mxData->mbOk ) + { + sal_uInt16 nTokPos = mxData->maOpPosStack.back(); + mxData->maOpPosStack.pop_back(); + return nTokPos; + } + return 0; +} + +namespace { + +inline void lclAppend( ScfUInt8Vec& orVector, sal_uInt16 nData ) +{ + orVector.resize( orVector.size() + 2 ); + ShortToSVBT16( nData, &*(orVector.end() - 2) ); +} + +inline void lclAppend( ScfUInt8Vec& orVector, sal_uInt32 nData ) +{ + orVector.resize( orVector.size() + 4 ); + UInt32ToSVBT32( nData, &*(orVector.end() - 4) ); +} + +inline void lclAppend( ScfUInt8Vec& orVector, double fData ) +{ + orVector.resize( orVector.size() + 8 ); + DoubleToSVBT64( fData, &*(orVector.end() - 8) ); +} + +inline void lclAppend( ScfUInt8Vec& orVector, const XclExpRoot& rRoot, const String& rString, XclStrFlags nStrFlags ) +{ + XclExpStringRef xXclStr = XclExpStringHelper::CreateString( rRoot, rString, nStrFlags, EXC_TOK_STR_MAXLEN ); + size_t nSize = orVector.size(); + orVector.resize( nSize + xXclStr->GetSize() ); + xXclStr->WriteToMem( &orVector[ nSize ] ); +} + +} // namespace + +void XclExpFmlaCompImpl::Append( sal_uInt8 nData ) +{ + mxData->maTokVec.push_back( nData ); +} + +void XclExpFmlaCompImpl::Append( sal_uInt8 nData, size_t nCount ) +{ + mxData->maTokVec.resize( mxData->maTokVec.size() + nCount, nData ); +} + +void XclExpFmlaCompImpl::Append( sal_uInt16 nData ) +{ + lclAppend( mxData->maTokVec, nData ); +} + +void XclExpFmlaCompImpl::Append( sal_uInt32 nData ) +{ + lclAppend( mxData->maTokVec, nData ); +} + +void XclExpFmlaCompImpl::Append( double fData ) +{ + lclAppend( mxData->maTokVec, fData ); +} + +void XclExpFmlaCompImpl::Append( const String& rString ) +{ + lclAppend( mxData->maTokVec, GetRoot(), rString, EXC_STR_8BITLENGTH ); +} + +void XclExpFmlaCompImpl::AppendAddress( const XclAddress& rXclPos ) +{ + Append( rXclPos.mnRow ); + if( meBiff <= EXC_BIFF5 ) + Append( static_cast< sal_uInt8 >( rXclPos.mnCol ) ); + else + Append( rXclPos.mnCol ); +} + +void XclExpFmlaCompImpl::AppendRange( const XclRange& rXclRange ) +{ + Append( rXclRange.maFirst.mnRow ); + Append( rXclRange.maLast.mnRow ); + if( meBiff <= EXC_BIFF5 ) + { + Append( static_cast< sal_uInt8 >( rXclRange.maFirst.mnCol ) ); + Append( static_cast< sal_uInt8 >( rXclRange.maLast.mnCol ) ); + } + else + { + Append( rXclRange.maFirst.mnCol ); + Append( rXclRange.maLast.mnCol ); + } +} + +void XclExpFmlaCompImpl::AppendSpaceToken( sal_uInt8 nType, sal_uInt8 nCount ) +{ + if( nCount > 0 ) + { + Append( EXC_TOKID_ATTR ); + Append( EXC_TOK_ATTR_SPACE ); + Append( nType ); + Append( nCount ); + } +} + +void XclExpFmlaCompImpl::AppendOperandTokenId( sal_uInt8 nTokenId, sal_uInt8 nSpaces ) +{ + AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, nSpaces ); + PushOperandPos( GetSize() ); + Append( nTokenId ); +} + +void XclExpFmlaCompImpl::AppendIntToken( sal_uInt16 nValue, sal_uInt8 nSpaces ) +{ + AppendOperandTokenId( EXC_TOKID_INT, nSpaces ); + Append( nValue ); +} + +void XclExpFmlaCompImpl::AppendNumToken( double fValue, sal_uInt8 nSpaces ) +{ + AppendOperandTokenId( EXC_TOKID_NUM, nSpaces ); + Append( fValue ); +} + +void XclExpFmlaCompImpl::AppendBoolToken( bool bValue, sal_uInt8 nSpaces ) +{ + AppendOperandTokenId( EXC_TOKID_BOOL, nSpaces ); + Append( bValue ? EXC_TOK_BOOL_TRUE : EXC_TOK_BOOL_FALSE ); +} + +void XclExpFmlaCompImpl::AppendErrorToken( sal_uInt8 nErrCode, sal_uInt8 nSpaces ) +{ + AppendOperandTokenId( EXC_TOKID_ERR, nSpaces ); + Append( nErrCode ); +} + +void XclExpFmlaCompImpl::AppendMissingToken( sal_uInt8 nSpaces ) +{ + AppendOperandTokenId( EXC_TOKID_MISSARG, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendNameToken( sal_uInt16 nNameIdx, sal_uInt8 nSpaces ) +{ + if( nNameIdx > 0 ) + { + AppendOperandTokenId( GetTokenId( EXC_TOKID_NAME, EXC_TOKCLASS_REF ), nSpaces ); + Append( nNameIdx ); + Append( 0, (meBiff <= EXC_BIFF5) ? 12 : 2 ); + } + else + AppendErrorToken( EXC_ERR_NAME ); +} + +void XclExpFmlaCompImpl::AppendMissingNameToken( const String& rName, sal_uInt8 nSpaces ) +{ + sal_uInt16 nNameIdx = GetNameManager().InsertRawName( rName ); + AppendNameToken( nNameIdx, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendNameXToken( sal_uInt16 nExtSheet, sal_uInt16 nExtName, sal_uInt8 nSpaces ) +{ + AppendOperandTokenId( GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ), nSpaces ); + Append( nExtSheet ); + if( meBiff <= EXC_BIFF5 ) + Append( 0, 8 ); + Append( nExtName ); + Append( 0, (meBiff <= EXC_BIFF5) ? 12 : 2 ); +} + +void XclExpFmlaCompImpl::AppendMacroCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces ) +{ + sal_uInt16 nNameIdx = GetNameManager().InsertMacroCall( rExtFuncData.maFuncName, rExtFuncData.mbVBasic, true, rExtFuncData.mbHidden ); + AppendNameToken( nNameIdx, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendAddInCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces ) +{ + String aXclFuncName; + if( mxData->mpLinkMgr && ScGlobal::GetAddInCollection()->GetExcelName( rExtFuncData.maFuncName, GetUILanguage(), aXclFuncName ) ) + { + sal_uInt16 nExtSheet, nExtName; + if( mxData->mpLinkMgr->InsertAddIn( nExtSheet, nExtName, aXclFuncName ) ) + { + AppendNameXToken( nExtSheet, nExtName, nSpaces ); + return; + } + } + AppendMacroCallToken( rExtFuncData, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendEuroToolCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces ) +{ + sal_uInt16 nExtSheet, nExtName; + if( mxData->mpLinkMgr && mxData->mpLinkMgr->InsertEuroTool( nExtSheet, nExtName, rExtFuncData.maFuncName ) ) + AppendNameXToken( nExtSheet, nExtName, nSpaces ); + else + AppendMacroCallToken( rExtFuncData, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendOperatorTokenId( sal_uInt8 nTokenId, const XclExpOperandListRef& rxOperands, sal_uInt8 nSpaces ) +{ + AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, nSpaces ); + PushOperatorPos( GetSize(), rxOperands ); + Append( nTokenId ); +} + +void XclExpFmlaCompImpl::AppendUnaryOperatorToken( sal_uInt8 nTokenId, sal_uInt8 nSpaces ) +{ + XclExpOperandListRef xOperands( new XclExpOperandList ); + xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, true ); + AppendOperatorTokenId( nTokenId, xOperands, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendBinaryOperatorToken( sal_uInt8 nTokenId, bool bValType, sal_uInt8 nSpaces ) +{ + XclExpOperandListRef xOperands( new XclExpOperandList ); + xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, bValType ); + xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, bValType ); + AppendOperatorTokenId( nTokenId, xOperands, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendLogicalOperatorToken( sal_uInt16 nXclFuncIdx, sal_uInt8 nOpCount ) +{ + XclExpOperandListRef xOperands( new XclExpOperandList ); + for( sal_uInt8 nOpIdx = 0; nOpIdx < nOpCount; ++nOpIdx ) + xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPX, false ); + AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNCVAR, EXC_TOKCLASS_VAL ), xOperands ); + Append( nOpCount ); + Append( nXclFuncIdx ); +} + +void XclExpFmlaCompImpl::AppendFuncToken( const XclExpFuncData& rFuncData ) +{ + sal_uInt16 nXclFuncIdx = rFuncData.GetXclFuncIdx(); + sal_uInt8 nParamCount = rFuncData.GetParamCount(); + sal_uInt8 nRetClass = rFuncData.GetReturnClass(); + + if( (nXclFuncIdx == EXC_FUNCID_SUM) && (nParamCount == 1) ) + { + // SUM with only one parameter + AppendOperatorTokenId( EXC_TOKID_ATTR, rFuncData.GetOperandList() ); + Append( EXC_TOK_ATTR_SUM ); + Append( sal_uInt16( 0 ) ); + } + else if( rFuncData.IsFixedParamCount() ) + { + // fixed number of parameters + AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNC, nRetClass ), rFuncData.GetOperandList() ); + Append( nXclFuncIdx ); + } + else + { + // variable number of parameters + AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNCVAR, nRetClass ), rFuncData.GetOperandList() ); + Append( nParamCount ); + Append( nXclFuncIdx ); + } +} + +void XclExpFmlaCompImpl::AppendParenToken( sal_uInt8 nOpenSpaces, sal_uInt8 nCloseSpaces ) +{ + AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_OPEN, nOpenSpaces ); + AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_CLOSE, nCloseSpaces ); + Append( EXC_TOKID_PAREN ); +} + +void XclExpFmlaCompImpl::AppendJumpToken( XclExpFuncData& rFuncData, sal_uInt8 nAttrType ) +{ + // store the start position of the token + rFuncData.AppendAttrPos( GetSize() ); + // create the tAttr token + Append( EXC_TOKID_ATTR ); + Append( nAttrType ); + Append( sal_uInt16( 0 ) ); // placeholder that will be updated later +} + +void XclExpFmlaCompImpl::InsertZeros( sal_uInt16 nInsertPos, sal_uInt16 nInsertSize ) +{ + // insert zeros into the token array + DBG_ASSERT( nInsertPos < mxData->maTokVec.size(), "XclExpFmlaCompImpl::Insert - invalid position" ); + mxData->maTokVec.insert( mxData->maTokVec.begin() + nInsertPos, nInsertSize, 0 ); + + // update positions of operands waiting for an operator + for( ScfUInt16Vec::iterator aIt = mxData->maOpPosStack.begin(), aEnd = mxData->maOpPosStack.end(); aIt != aEnd; ++aIt ) + if( nInsertPos <= *aIt ) + *aIt = *aIt + nInsertSize; + + // update operand lists of all operator tokens + if( nInsertPos < mxData->maOpListVec.size() ) + mxData->maOpListVec.insert( mxData->maOpListVec.begin() + nInsertPos, nInsertSize, XclExpOperandListRef() ); + for( XclExpOperandListVector::iterator aIt = mxData->maOpListVec.begin(), aEnd = mxData->maOpListVec.end(); aIt != aEnd; ++aIt ) + if( aIt->get() ) + for( XclExpOperandList::iterator aIt2 = (*aIt)->begin(), aEnd2 = (*aIt)->end(); aIt2 != aEnd2; ++aIt2 ) + if( nInsertPos <= aIt2->mnTokPos ) + aIt2->mnTokPos = aIt2->mnTokPos + nInsertSize; +} + +void XclExpFmlaCompImpl::Overwrite( sal_uInt16 nWriteToPos, sal_uInt16 nOffset ) +{ + DBG_ASSERT( static_cast< size_t >( nWriteToPos + 1 ) < mxData->maTokVec.size(), "XclExpFmlaCompImpl::Overwrite - invalid position" ); + ShortToSVBT16( nOffset, &mxData->maTokVec[ nWriteToPos ] ); +} + +void XclExpFmlaCompImpl::UpdateAttrGoto( sal_uInt16 nAttrPos ) +{ + /* tAttrGoto contains distance from end of tAttr token to position behind + the function token (for IF or CHOOSE function), which is currently at + the end of the token array. Additionally this distance is decreased by + one, for whatever reason. So we have to subtract 4 and 1 from the + distance between the tAttr token start and the end of the token array. */ + Overwrite( nAttrPos + 2, static_cast< sal_uInt16 >( GetSize() - nAttrPos - 5 ) ); +} + +bool XclExpFmlaCompImpl::IsSpaceToken( sal_uInt16 nPos ) const +{ + return + (static_cast< size_t >( nPos + 4 ) <= mxData->maTokVec.size()) && + (mxData->maTokVec[ nPos ] == EXC_TOKID_ATTR) && + (mxData->maTokVec[ nPos + 1 ] == EXC_TOK_ATTR_SPACE); +} + +void XclExpFmlaCompImpl::RemoveTrailingParen() +{ + // remove trailing tParen token + if( !mxData->maTokVec.empty() && (mxData->maTokVec.back() == EXC_TOKID_PAREN) ) + mxData->maTokVec.pop_back(); + // remove remaining tAttrSpace tokens + while( (mxData->maTokVec.size() >= 4) && IsSpaceToken( GetSize() - 4 ) ) + mxData->maTokVec.erase( mxData->maTokVec.end() - 4, mxData->maTokVec.end() ); +} + +void XclExpFmlaCompImpl::AppendExt( sal_uInt8 nData ) +{ + mxData->maExtDataVec.push_back( nData ); +} + +void XclExpFmlaCompImpl::AppendExt( sal_uInt8 nData, size_t nCount ) +{ + mxData->maExtDataVec.resize( mxData->maExtDataVec.size() + nCount, nData ); +} + +void XclExpFmlaCompImpl::AppendExt( sal_uInt16 nData ) +{ + lclAppend( mxData->maExtDataVec, nData ); +} + +void XclExpFmlaCompImpl::AppendExt( sal_uInt32 nData ) +{ + lclAppend( mxData->maExtDataVec, nData ); +} + +void XclExpFmlaCompImpl::AppendExt( double fData ) +{ + lclAppend( mxData->maExtDataVec, fData ); +} + +void XclExpFmlaCompImpl::AppendExt( const String& rString ) +{ + lclAppend( mxData->maExtDataVec, GetRoot(), rString, (meBiff == EXC_BIFF8) ? EXC_STR_DEFAULT : EXC_STR_8BITLENGTH ); +} + +// ============================================================================ + +namespace { + +void lclInitOwnTab( ScSingleRefData& rRef, const ScAddress& rScPos, SCTAB nCurrScTab, bool b3DRefOnly ) +{ + if( b3DRefOnly ) + { + // no reduction to 2D reference, if global link manager is used + rRef.SetFlag3D( TRUE ); + } + else if( rScPos.Tab() == nCurrScTab ) + { + rRef.SetTabRel( TRUE ); + rRef.nRelTab = 0; + } +} + +void lclPutCellToTokenArray( ScTokenArray& rScTokArr, const ScAddress& rScPos, SCTAB nCurrScTab, bool b3DRefOnly ) +{ + ScSingleRefData aRef; + aRef.InitAddress( rScPos ); + lclInitOwnTab( aRef, rScPos, nCurrScTab, b3DRefOnly ); + rScTokArr.AddSingleReference( aRef ); +} + +void lclPutRangeToTokenArray( ScTokenArray& rScTokArr, const ScRange& rScRange, SCTAB nCurrScTab, bool b3DRefOnly ) +{ + if( rScRange.aStart == rScRange.aEnd ) + { + lclPutCellToTokenArray( rScTokArr, rScRange.aStart, nCurrScTab, b3DRefOnly ); + } + else + { + ScComplexRefData aRef; + aRef.InitRange( rScRange ); + lclInitOwnTab( aRef.Ref1, rScRange.aStart, nCurrScTab, b3DRefOnly ); + lclInitOwnTab( aRef.Ref2, rScRange.aEnd, nCurrScTab, b3DRefOnly ); + rScTokArr.AddDoubleReference( aRef ); + } +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpFormulaCompiler::XclExpFormulaCompiler( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mxImpl( new XclExpFmlaCompImpl( rRoot ) ) +{ +} + +XclExpFormulaCompiler::~XclExpFormulaCompiler() +{ +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( + XclFormulaType eType, const ScTokenArray& rScTokArr, + const ScAddress* pScBasePos, XclExpRefLog* pRefLog ) +{ + return mxImpl->CreateFormula( eType, rScTokArr, pScBasePos, pRefLog ); +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScAddress& rScPos ) +{ + ScTokenArray aScTokArr; + lclPutCellToTokenArray( aScTokArr, rScPos, GetCurrScTab(), mxImpl->Is3DRefOnly( eType ) ); + return mxImpl->CreateFormula( eType, aScTokArr ); +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScRange& rScRange ) +{ + ScTokenArray aScTokArr; + lclPutRangeToTokenArray( aScTokArr, rScRange, GetCurrScTab(), mxImpl->Is3DRefOnly( eType ) ); + return mxImpl->CreateFormula( eType, aScTokArr ); +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScRangeList& rScRanges ) +{ + ULONG nCount = rScRanges.Count(); + if( nCount == 0 ) + return XclTokenArrayRef(); + + ScTokenArray aScTokArr; + SCTAB nCurrScTab = GetCurrScTab(); + bool b3DRefOnly = mxImpl->Is3DRefOnly( eType ); + for( ULONG nIdx = 0; nIdx < nCount; ++nIdx ) + { + if( nIdx > 0 ) + aScTokArr.AddOpCode( ocUnion ); + lclPutRangeToTokenArray( aScTokArr, *rScRanges.GetObject( nIdx ), nCurrScTab, b3DRefOnly ); + } + return mxImpl->CreateFormula( eType, aScTokArr ); +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateErrorFormula( sal_uInt8 nErrCode ) +{ + return mxImpl->CreateErrorFormula( nErrCode ); +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateSpecialRefFormula( + sal_uInt8 nTokenId, const XclAddress& rXclPos ) +{ + return mxImpl->CreateSpecialRefFormula( nTokenId, rXclPos ); +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateNameXFormula( + sal_uInt16 nExtSheet, sal_uInt16 nExtName ) +{ + return mxImpl->CreateNameXFormula( nExtSheet, nExtName ); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xehelper.cxx b/sc/source/filter/excel/xehelper.cxx new file mode 100644 index 000000000000..bce037bd05c1 --- /dev/null +++ b/sc/source/filter/excel/xehelper.cxx @@ -0,0 +1,1144 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <sfx2/objsh.hxx> +#include <vcl/font.hxx> +#include <tools/urlobj.hxx> +#include <svl/itemset.hxx> +#include <svtools/ctrltool.hxx> +#include <svx/svdotext.hxx> +#include <editeng/outlobj.hxx> +#include "scitems.hxx" +#include <editeng/fhgtitem.hxx> +#include <editeng/flstitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/flditem.hxx> +#include <editeng/escpitem.hxx> +#include <editeng/svxfont.hxx> + +#define _SVSTDARR_USHORTS +#include <svl/svstdarr.hxx> +#include "document.hxx" +#include "docpool.hxx" +#include "cell.hxx" +#include "editutil.hxx" +#include "patattr.hxx" +#include "xestyle.hxx" +#include "fprogressbar.hxx" +#include "xltracer.hxx" +#include "xecontent.hxx" +#include "xelink.hxx" +#include "xehelper.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::i18n::XBreakIterator; + +// Export progress bar ======================================================== + +XclExpProgressBar::XclExpProgressBar( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mxProgress( new ScfProgressBar( rRoot.GetDocShell(), STR_SAVE_DOC ) ), + mpSubProgress( 0 ), + mpSubRowCreate( 0 ), + mpSubRowFinal( 0 ), + mnSegRowFinal( SCF_INV_SEGMENT ), + mnRowCount( 0 ) +{ +} + +XclExpProgressBar::~XclExpProgressBar() +{ +} + +void XclExpProgressBar::Initialize() +{ + const ScDocument& rDoc = GetDoc(); + const XclExpTabInfo& rTabInfo = GetTabInfo(); + SCTAB nScTabCount = rTabInfo.GetScTabCount(); + + // *** segment: creation of ROW records *** ------------------------------- + + sal_Int32 nSegRowCreate = mxProgress->AddSegment( 2000 ); + mpSubRowCreate = &mxProgress->GetSegmentProgressBar( nSegRowCreate ); + maSubSegRowCreate.resize( nScTabCount, SCF_INV_SEGMENT ); + + for( SCTAB nScTab = 0; nScTab < nScTabCount; ++nScTab ) + { + if( rTabInfo.IsExportTab( nScTab ) ) + { + SCCOL nLastUsedScCol; + SCROW nLastUsedScRow; + rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow ); + sal_Size nSegSize = static_cast< sal_Size >( nLastUsedScRow + 1 ); + maSubSegRowCreate[ nScTab ] = mpSubRowCreate->AddSegment( nSegSize ); + } + } + + // *** segment: writing all ROW records *** ------------------------------- + + mnSegRowFinal = mxProgress->AddSegment( 1000 ); + // sub progress bar and segment are created later in ActivateFinalRowsSegment() +} + +void XclExpProgressBar::IncRowRecordCount() +{ + ++mnRowCount; +} + +void XclExpProgressBar::ActivateCreateRowsSegment() +{ + DBG_ASSERT( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()), + "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" ); + sal_Int32 nSeg = maSubSegRowCreate[ GetCurrScTab() ]; + DBG_ASSERT( nSeg != SCF_INV_SEGMENT, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" ); + if( nSeg != SCF_INV_SEGMENT ) + { + mpSubProgress = mpSubRowCreate; + mpSubProgress->ActivateSegment( nSeg ); + } + else + mpSubProgress = 0; +} + +void XclExpProgressBar::ActivateFinalRowsSegment() +{ + if( !mpSubRowFinal && (mnRowCount > 0) ) + { + mpSubRowFinal = &mxProgress->GetSegmentProgressBar( mnSegRowFinal ); + mpSubRowFinal->AddSegment( mnRowCount ); + } + mpSubProgress = mpSubRowFinal; + if( mpSubProgress ) + mpSubProgress->Activate(); +} + +void XclExpProgressBar::Progress() +{ + if( mpSubProgress && !mpSubProgress->IsFull() ) + mpSubProgress->Progress(); +} + +// Calc->Excel cell address/range conversion ================================== + +namespace { + +/** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */ +inline void lclFillAddress( XclAddress& rXclPos, SCCOL nScCol, SCROW nScRow ) +{ + rXclPos.mnCol = static_cast< sal_uInt16 >( nScCol ); + rXclPos.mnRow = static_cast< sal_uInt16 >( nScRow ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot& rRoot ) : + XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetXclMaxPos() ) +{ +} + +// cell address --------------------------------------------------------------- + +bool XclExpAddressConverter::CheckAddress( const ScAddress& rScPos, bool bWarn ) +{ + // ScAddress::operator<=() doesn't do what we want here + bool bValidCol = (0 <= rScPos.Col()) && (rScPos.Col() <= maMaxPos.Col()); + bool bValidRow = (0 <= rScPos.Row()) && (rScPos.Row() <= maMaxPos.Row()); + bool bValidTab = (0 <= rScPos.Tab()) && (rScPos.Tab() <= maMaxPos.Tab()); + + bool bValid = bValidCol && bValidRow && bValidTab; + if( !bValid && bWarn ) + { + mbColTrunc |= !bValidCol; + mbRowTrunc |= !bValidRow; + mbTabTrunc |= (rScPos.Tab() > maMaxPos.Tab()); // do not warn for deleted refs + mrTracer.TraceInvalidAddress( rScPos, maMaxPos ); + } + return bValid; +} + +bool XclExpAddressConverter::ConvertAddress( XclAddress& rXclPos, + const ScAddress& rScPos, bool bWarn ) +{ + bool bValid = CheckAddress( rScPos, bWarn ); + if( bValid ) + lclFillAddress( rXclPos, rScPos.Col(), rScPos.Row() ); + return bValid; +} + +XclAddress XclExpAddressConverter::CreateValidAddress( const ScAddress& rScPos, bool bWarn ) +{ + XclAddress aXclPos( ScAddress::UNINITIALIZED ); + if( !ConvertAddress( aXclPos, rScPos, bWarn ) ) + lclFillAddress( aXclPos, ::std::min( rScPos.Col(), maMaxPos.Col() ), ::std::min( rScPos.Row(), maMaxPos.Row() ) ); + return aXclPos; +} + +// cell range ----------------------------------------------------------------- + +bool XclExpAddressConverter::CheckRange( const ScRange& rScRange, bool bWarn ) +{ + return CheckAddress( rScRange.aStart, bWarn ) && CheckAddress( rScRange.aEnd, bWarn ); +} + +bool XclExpAddressConverter::ValidateRange( ScRange& rScRange, bool bWarn ) +{ + rScRange.Justify(); + + // check start position + bool bValidStart = CheckAddress( rScRange.aStart, bWarn ); + if( bValidStart ) + { + // check & correct end position + ScAddress& rScEnd = rScRange.aEnd; + if( !CheckAddress( rScEnd, bWarn ) ) + { + rScEnd.SetCol( ::std::min( rScEnd.Col(), maMaxPos.Col() ) ); + rScEnd.SetRow( ::std::min( rScEnd.Row(), maMaxPos.Row() ) ); + rScEnd.SetTab( ::std::min( rScEnd.Tab(), maMaxPos.Tab() ) ); + } + } + + return bValidStart; +} + +bool XclExpAddressConverter::ConvertRange( XclRange& rXclRange, + const ScRange& rScRange, bool bWarn ) +{ + // check start position + bool bValidStart = CheckAddress( rScRange.aStart, bWarn ); + if( bValidStart ) + { + lclFillAddress( rXclRange.maFirst, rScRange.aStart.Col(), rScRange.aStart.Row() ); + + // check & correct end position + SCCOL nScCol2 = rScRange.aEnd.Col(); + SCROW nScRow2 = rScRange.aEnd.Row(); + if( !CheckAddress( rScRange.aEnd, bWarn ) ) + { + nScCol2 = ::std::min( nScCol2, maMaxPos.Col() ); + nScRow2 = ::std::min( nScRow2, maMaxPos.Row() ); + } + lclFillAddress( rXclRange.maLast, nScCol2, nScRow2 ); + } + return bValidStart; +} + +//UNUSED2008-05 XclRange XclExpAddressConverter::CreateValidRange( const ScRange& rScRange, bool bWarn ) +//UNUSED2008-05 { +//UNUSED2008-05 return XclRange( +//UNUSED2008-05 CreateValidAddress( rScRange.aStart, bWarn ), +//UNUSED2008-05 CreateValidAddress( rScRange.aEnd, bWarn ) ); +//UNUSED2008-05 } + +// cell range list ------------------------------------------------------------ + +//UNUSED2008-05 bool XclExpAddressConverter::CheckRangeList( const ScRangeList& rScRanges, bool bWarn ) +//UNUSED2008-05 { +//UNUSED2008-05 for( ULONG nIdx = 0, nSize = rScRanges.Count(); nIdx < nSize; ++nIdx ) +//UNUSED2008-05 if( const ScRange* pScRange = rScRanges.GetObject( nIdx ) ) +//UNUSED2008-05 if( !CheckRange( *pScRange, bWarn ) ) +//UNUSED2008-05 return false; +//UNUSED2008-05 return true; +//UNUSED2008-05 } + +void XclExpAddressConverter::ValidateRangeList( ScRangeList& rScRanges, bool bWarn ) +{ + ULONG nIdx = rScRanges.Count(); + while( nIdx ) + { + --nIdx; // backwards to keep nIdx valid + ScRange* pScRange = rScRanges.GetObject( nIdx ); + if( pScRange && !CheckRange( *pScRange, bWarn ) ) + delete rScRanges.Remove( nIdx ); + } +} + +void XclExpAddressConverter::ConvertRangeList( XclRangeList& rXclRanges, + const ScRangeList& rScRanges, bool bWarn ) +{ + rXclRanges.clear(); + for( ULONG nPos = 0, nCount = rScRanges.Count(); nPos < nCount; ++nPos ) + { + if( const ScRange* pScRange = rScRanges.GetObject( nPos ) ) + { + XclRange aXclRange( ScAddress::UNINITIALIZED ); + if( ConvertRange( aXclRange, *pScRange, bWarn ) ) + rXclRanges.push_back( aXclRange ); + } + } +} + +// EditEngine->String conversion ============================================== + +namespace { + +String lclGetUrlRepresentation( const SvxURLField& rUrlField ) +{ + String aRepr( rUrlField.GetRepresentation() ); + // no representation -> use URL + return aRepr.Len() ? aRepr : rUrlField.GetURL(); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos ) : + XclExpRoot( rRoot ), + maScPos( rScPos ), + mbMultipleUrls( false ) +{ +} + +XclExpHyperlinkHelper::~XclExpHyperlinkHelper() +{ +} + +String XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField& rUrlField ) +{ + String aUrlRepr; + + if( GetBiff() == EXC_BIFF8 ) // no HLINK records in BIFF2-BIFF7 + { + // there was/is already a HLINK record + mbMultipleUrls = mxLinkRec.is(); + + mxLinkRec.reset( new XclExpHyperlink( GetRoot(), rUrlField, maScPos ) ); + + if( const String* pRepr = mxLinkRec->GetRepr() ) + aUrlRepr = *pRepr; + + // add URL to note text + ScGlobal::AddToken( maUrlList, rUrlField.GetURL(), '\n' ); + } + + // no hyperlink representation from Excel HLINK record -> use it from text field + return aUrlRepr.Len() ? aUrlRepr : lclGetUrlRepresentation( rUrlField ); +} + +bool XclExpHyperlinkHelper::HasLinkRecord() const +{ + return !mbMultipleUrls && mxLinkRec.is(); +} + +XclExpHyperlinkHelper::XclExpHyperlinkRef XclExpHyperlinkHelper::GetLinkRecord() +{ + if( HasLinkRecord() ) + return mxLinkRec; + return XclExpHyperlinkRef(); +} + +// ---------------------------------------------------------------------------- + +namespace { + +/** Creates a new formatted string from the passed unformatted string. + + Creates a Unicode string or a byte string, depending on the current BIFF + version contained in the passed XclExpRoot object. May create a formatted + string object, if the text contains different script types. + + @param pCellAttr + Cell attributes used for font formatting. + @param nFlags + Modifiers for string export. + @param nMaxLen + The maximum number of characters to store in this string. + @return + The new string object. + */ +XclExpStringRef lclCreateFormattedString( + const XclExpRoot& rRoot, const String& rText, const ScPatternAttr* pCellAttr, + XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + /* Create an empty Excel string object with correctly initialized BIFF mode, + because this function only uses Append() functions that require this. */ + XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen ); + + // script type handling + Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator(); + namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + // #i63255# get script type for leading weak characters + sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rText ); + + // font buffer and cell item set + XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer(); + const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet(); + + // process all script portions + OUString aOUText( rText ); + sal_Int32 nPortionPos = 0; + sal_Int32 nTextLen = aOUText.getLength(); + while( nPortionPos < nTextLen ) + { + // get script type and end position of next script portion + sal_Int16 nScript = xBreakIt->getScriptType( aOUText, nPortionPos ); + sal_Int32 nPortionEnd = xBreakIt->endOfScript( aOUText, nPortionPos, nScript ); + + // reuse previous script for following weak portions + if( nScript == ApiScriptType::WEAK ) + nScript = nLastScript; + + // construct font from current text portion + SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, rItemSet, nScript ) ); + + // Excel start position of this portion + sal_uInt16 nXclPortionStart = xString->Len(); + // add portion text to Excel string + XclExpStringHelper::AppendString( *xString, rRoot, aOUText.copy( nPortionPos, nPortionEnd - nPortionPos ) ); + if( nXclPortionStart < xString->Len() ) + { + // insert font into buffer + sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT ); + // insert font index into format run vector + xString->AppendFormat( nXclPortionStart, nFontIdx ); + } + + // go to next script portion + nLastScript = nScript; + nPortionPos = nPortionEnd; + } + + return xString; +} + +/** Creates a new formatted string from an edit engine text object. + + Creates a Unicode string or a byte string, depending on the current BIFF + version contained in the passed XclExpRoot object. + + @param rEE + The edit engine in use. The text object must already be set. + @param nFlags + Modifiers for string export. + @param nMaxLen + The maximum number of characters to store in this string. + @return + The new string object. + */ +XclExpStringRef lclCreateFormattedString( + const XclExpRoot& rRoot, EditEngine& rEE, XclExpHyperlinkHelper* pLinkHelper, + XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + /* Create an empty Excel string object with correctly initialized BIFF mode, + because this function only uses Append() functions that require this. */ + XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen ); + + // font buffer and helper item set for edit engine -> Calc item conversion + XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer(); + SfxItemSet aItemSet( *rRoot.GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END ); + + // script type handling + Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator(); + namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + // #i63255# get script type for leading weak characters + sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rEE.GetText() ); + + // process all paragraphs + sal_uInt16 nParaCount = rEE.GetParagraphCount(); + for( sal_uInt16 nPara = 0; nPara < nParaCount; ++nPara ) + { + ESelection aSel( nPara, 0 ); + String aParaText( rEE.GetText( nPara ) ); + + SvUShorts aPosList; + rEE.GetPortions( nPara, aPosList ); + + // process all portions in the paragraph + sal_uInt16 nPosCount = aPosList.Count(); + for( sal_uInt16 nPos = 0; nPos < nPosCount; ++nPos ) + { + aSel.nEndPos = static_cast< xub_StrLen >( aPosList.GetObject( nPos ) ); + String aXclPortionText( aParaText, aSel.nStartPos, aSel.nEndPos - aSel.nStartPos ); + + aItemSet.ClearItem(); + SfxItemSet aEditSet( rEE.GetAttribs( aSel ) ); + ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet ); + + // get escapement value + short nEsc = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT ).GetEsc(); + + // process text fields + bool bIsHyperlink = false; + if( aSel.nStartPos + 1 == aSel.nEndPos ) + { + // test if the character is a text field + const SfxPoolItem* pItem; + if( aEditSet.GetItemState( EE_FEATURE_FIELD, FALSE, &pItem ) == SFX_ITEM_SET ) + { + const SvxFieldData* pField = static_cast< const SvxFieldItem* >( pItem )->GetField(); + if( const SvxURLField* pUrlField = PTR_CAST( SvxURLField, pField ) ) + { + // convert URL field to string representation + aXclPortionText = pLinkHelper ? + pLinkHelper->ProcessUrlField( *pUrlField ) : + lclGetUrlRepresentation( *pUrlField ); + bIsHyperlink = true; + } + else + { + DBG_ERRORFILE( "lclCreateFormattedString - unknown text field" ); + aXclPortionText.Erase(); + } + } + } + + // Excel start position of this portion + sal_uInt16 nXclPortionStart = xString->Len(); + // add portion text to Excel string + XclExpStringHelper::AppendString( *xString, rRoot, aXclPortionText ); + if( (nXclPortionStart < xString->Len()) || (aParaText.Len() == 0) ) + { + /* Construct font from current edit engine text portion. Edit engine + creates different portions for different script types, no need to loop. */ + sal_Int16 nScript = xBreakIt->getScriptType( aXclPortionText, 0 ); + if( nScript == ApiScriptType::WEAK ) + nScript = nLastScript; + SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, aItemSet, nScript ) ); + nLastScript = nScript; + + // add escapement + aFont.SetEscapement( nEsc ); + // modify automatic font color for hyperlinks + if( bIsHyperlink && (GETITEM( aItemSet, SvxColorItem, ATTR_FONT_COLOR ).GetValue().GetColor() == COL_AUTO) ) + aFont.SetColor( Color( COL_LIGHTBLUE ) ); + + // insert font into buffer + sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT ); + // insert font index into format run vector + xString->AppendFormat( nXclPortionStart, nFontIdx ); + } + + aSel.nStartPos = aSel.nEndPos; + } + + // add trailing newline (important for correct character index calculation) + if( nPara + 1 < nParaCount ) + XclExpStringHelper::AppendChar( *xString, rRoot, '\n' ); + } + + return xString; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpStringRef XclExpStringHelper::CreateString( + const XclExpRoot& rRoot, const String& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + XclExpStringRef xString( new XclExpString ); + if( rRoot.GetBiff() == EXC_BIFF8 ) + xString->Assign( rString, nFlags, nMaxLen ); + else + xString->AssignByte( rString, rRoot.GetTextEncoding(), nFlags, nMaxLen ); + return xString; +} + +XclExpStringRef XclExpStringHelper::CreateString( + const XclExpRoot& rRoot, sal_Unicode cChar, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + XclExpStringRef xString = CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen ); + AppendChar( *xString, rRoot, cChar ); + return xString; +} + +void XclExpStringHelper::AppendString( XclExpString& rXclString, const XclExpRoot& rRoot, const String& rString ) +{ + if( rRoot.GetBiff() == EXC_BIFF8 ) + rXclString.Append( rString ); + else + rXclString.AppendByte( rString, rRoot.GetTextEncoding() ); +} + +void XclExpStringHelper::AppendChar( XclExpString& rXclString, const XclExpRoot& rRoot, sal_Unicode cChar ) +{ + if( rRoot.GetBiff() == EXC_BIFF8 ) + rXclString.Append( cChar ); + else + rXclString.AppendByte( cChar, rRoot.GetTextEncoding() ); +} + +XclExpStringRef XclExpStringHelper::CreateCellString( + const XclExpRoot& rRoot, const ScStringCell& rStringCell, const ScPatternAttr* pCellAttr, + XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + String aCellText; + rStringCell.GetString( aCellText ); + return lclCreateFormattedString( rRoot, aCellText, pCellAttr, nFlags, nMaxLen ); +} + +XclExpStringRef XclExpStringHelper::CreateCellString( + const XclExpRoot& rRoot, const ScEditCell& rEditCell, const ScPatternAttr* pCellAttr, + XclExpHyperlinkHelper& rLinkHelper, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + XclExpStringRef xString; + if( const EditTextObject* pEditObj = rEditCell.GetData() ) + { + // formatted cell + ScEditEngineDefaulter& rEE = rRoot.GetEditEngine(); + BOOL bOldUpdateMode = rEE.GetUpdateMode(); + rEE.SetUpdateMode( TRUE ); + // default items + const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet(); + SfxItemSet* pEEItemSet = new SfxItemSet( rEE.GetEmptyItemSet() ); + ScPatternAttr::FillToEditItemSet( *pEEItemSet, rItemSet ); + rEE.SetDefaults( pEEItemSet ); // edit engine takes ownership + // create the string + rEE.SetText( *pEditObj ); + xString = lclCreateFormattedString( rRoot, rEE, &rLinkHelper, nFlags, nMaxLen ); + rEE.SetUpdateMode( bOldUpdateMode ); + } + else + { + // unformatted cell + String aCellText; + rEditCell.GetString( aCellText ); + xString = lclCreateFormattedString( rRoot, aCellText, pCellAttr, nFlags, nMaxLen ); + } + return xString; +} + +XclExpStringRef XclExpStringHelper::CreateString( + const XclExpRoot& rRoot, const SdrTextObj& rTextObj, + XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + XclExpStringRef xString; + if( const OutlinerParaObject* pParaObj = rTextObj.GetOutlinerParaObject() ) + { + EditEngine& rEE = rRoot.GetDrawEditEngine(); + BOOL bOldUpdateMode = rEE.GetUpdateMode(); + rEE.SetUpdateMode( TRUE ); + // create the string + rEE.SetText( pParaObj->GetTextObject() ); + xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen ); + rEE.SetUpdateMode( bOldUpdateMode ); + // limit formats - TODO: BIFF dependent + if( !xString->IsEmpty() ) + { + xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 ); + xString->AppendTrailingFormat( EXC_FONT_APP ); + } + } + else + { + DBG_ERRORFILE( "XclExpStringHelper::CreateString - textbox without para object" ); + // create BIFF dependent empty Excel string + xString = CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen ); + } + return xString; +} + +XclExpStringRef XclExpStringHelper::CreateString( + const XclExpRoot& rRoot, const EditTextObject& rEditObj, + XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + XclExpStringRef xString; + EditEngine& rEE = rRoot.GetDrawEditEngine(); + BOOL bOldUpdateMode = rEE.GetUpdateMode(); + rEE.SetUpdateMode( TRUE ); + rEE.SetText( rEditObj ); + xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen ); + rEE.SetUpdateMode( bOldUpdateMode ); + // limit formats - TODO: BIFF dependent + if( !xString->IsEmpty() ) + { + xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 ); + xString->AppendTrailingFormat( EXC_FONT_APP ); + } + return xString; +} + +sal_Int16 XclExpStringHelper::GetLeadingScriptType( const XclExpRoot& rRoot, const String& rString ) +{ + namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator(); + OUString aOUString( rString ); + sal_Int32 nStrPos = 0; + sal_Int32 nStrLen = aOUString.getLength(); + sal_Int16 nScript = ApiScriptType::WEAK; + while( (nStrPos < nStrLen) && (nScript == ApiScriptType::WEAK) ) + { + nScript = xBreakIt->getScriptType( aOUString, nStrPos ); + nStrPos = xBreakIt->endOfScript( aOUString, nStrPos, nScript ); + } + return (nScript == ApiScriptType::WEAK) ? rRoot.GetDefApiScript() : nScript; +} + +// Header/footer conversion =================================================== + +XclExpHFConverter::XclExpHFConverter( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mrEE( rRoot.GetHFEditEngine() ), + mnTotalHeight( 0 ) +{ +} + +void XclExpHFConverter::GenerateString( + const EditTextObject* pLeftObj, + const EditTextObject* pCenterObj, + const EditTextObject* pRightObj ) +{ + maHFString.Erase(); + mnTotalHeight = 0; + AppendPortion( pLeftObj, 'L' ); + AppendPortion( pCenterObj, 'C' ); + AppendPortion( pRightObj, 'R' ); +} + +void XclExpHFConverter::AppendPortion( const EditTextObject* pTextObj, sal_Unicode cPortionCode ) +{ + if( !pTextObj ) return; + + String aText; + sal_Int32 nHeight = 0; + SfxItemSet aItemSet( *GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END ); + + // edit engine + BOOL bOldUpdateMode = mrEE.GetUpdateMode(); + mrEE.SetUpdateMode( TRUE ); + mrEE.SetText( *pTextObj ); + + // font information + XclFontData aFontData, aNewData; + if( const XclExpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) ) + { + aFontData = pFirstFont->GetFontData(); + (aFontData.mnHeight += 10) /= 20; // using pt here, not twips + } + else + aFontData.mnHeight = 10; + + const FontList* pFontList = 0; + if( SfxObjectShell* pDocShell = GetDocShell() ) + { + if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >( + pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) ) + pFontList = pInfoItem->GetFontList(); + } + + sal_uInt16 nParaCount = mrEE.GetParagraphCount(); + for( sal_uInt16 nPara = 0; nPara < nParaCount; ++nPara ) + { + ESelection aSel( nPara, 0 ); + String aParaText; + sal_Int32 nParaHeight = 0; + SvUShorts aPosList; + mrEE.GetPortions( nPara, aPosList ); + + sal_uInt16 nPosCount = aPosList.Count(); + for( sal_uInt16 nPos = 0; nPos < nPosCount; ++nPos ) + { + aSel.nEndPos = static_cast< xub_StrLen >( aPosList.GetObject( nPos ) ); + if( aSel.nStartPos < aSel.nEndPos ) + { + +// --- font attributes --- + + Font aFont; + aItemSet.ClearItem(); + SfxItemSet aEditSet( mrEE.GetAttribs( aSel ) ); + ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet ); + ScPatternAttr::GetFont( aFont, aItemSet, SC_AUTOCOL_RAW ); + + // font name and style + aNewData.maName = XclTools::GetXclFontName( aFont.GetName() ); + aNewData.mnWeight = (aFont.GetWeight() > WEIGHT_NORMAL) ? EXC_FONTWGHT_BOLD : EXC_FONTWGHT_NORMAL; + aNewData.mbItalic = (aFont.GetItalic() != ITALIC_NONE); + bool bNewFont = !(aFontData.maName == aNewData.maName); + bool bNewStyle = (aFontData.mnWeight != aNewData.mnWeight) || + (aFontData.mbItalic != aNewData.mbItalic); + if( bNewFont || (bNewStyle && pFontList) ) + { + aParaText.AppendAscii( "&\"" ).Append( aNewData.maName ); + if( pFontList ) + { + FontInfo aFontInfo( pFontList->Get( + aNewData.maName, + (aNewData.mnWeight > EXC_FONTWGHT_NORMAL) ? WEIGHT_BOLD : WEIGHT_NORMAL, + aNewData.mbItalic ? ITALIC_NORMAL : ITALIC_NONE ) ); + aNewData.maStyle = pFontList->GetStyleName( aFontInfo ); + if( aNewData.maStyle.Len() ) + aParaText.Append( ',' ).Append( aNewData.maStyle ); + } + aParaText.Append( '"' ); + } + + // height + // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm + // -> get it directly from edit engine item set + aNewData.mnHeight = ulimit_cast< sal_uInt16 >( GETITEM( aEditSet, SvxFontHeightItem, EE_CHAR_FONTHEIGHT ).GetHeight() ); + (aNewData.mnHeight += 10) /= 20; + bool bFontHtChanged = (aFontData.mnHeight != aNewData.mnHeight); + if( bFontHtChanged ) + aParaText.Append( '&' ).Append( String::CreateFromInt32( aNewData.mnHeight ) ); + // update maximum paragraph height, convert to twips + nParaHeight = ::std::max< sal_Int32 >( nParaHeight, aNewData.mnHeight * 20 ); + + // underline + aNewData.mnUnderline = EXC_FONTUNDERL_NONE; + switch( aFont.GetUnderline() ) + { + case UNDERLINE_NONE: aNewData.mnUnderline = EXC_FONTUNDERL_NONE; break; + case UNDERLINE_SINGLE: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; break; + case UNDERLINE_DOUBLE: aNewData.mnUnderline = EXC_FONTUNDERL_DOUBLE; break; + default: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; + } + if( aFontData.mnUnderline != aNewData.mnUnderline ) + { + sal_uInt8 nTmpUnderl = (aNewData.mnUnderline == EXC_FONTUNDERL_NONE) ? + aFontData.mnUnderline : aNewData.mnUnderline; + aParaText.AppendAscii( (nTmpUnderl == EXC_FONTUNDERL_SINGLE) ? "&U" : "&E" ); + } + + // strikeout + aNewData.mbStrikeout = (aFont.GetStrikeout() != STRIKEOUT_NONE); + if( aFontData.mbStrikeout != aNewData.mbStrikeout ) + aParaText.AppendAscii( "&S" ); + + // super/sub script + const SvxEscapementItem& rEscapeItem = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT ); + aNewData.SetScEscapement( rEscapeItem.GetEsc() ); + if( aFontData.mnEscapem != aNewData.mnEscapem ) + { + switch(aNewData.mnEscapem) + { + // close the previous super/sub script. + case EXC_FONTESC_NONE: aParaText.AppendAscii( (aFontData.mnEscapem == EXC_FONTESC_SUPER) ? "&X" : "&Y" ); break; + case EXC_FONTESC_SUPER: aParaText.AppendAscii( "&X" ); break; + case EXC_FONTESC_SUB: aParaText.AppendAscii( "&Y" ); break; + default: break; + } + } + + aFontData = aNewData; + +// --- text content or text fields --- + + const SfxPoolItem* pItem; + if( (aSel.nStartPos + 1 == aSel.nEndPos) && // fields are single characters + (aEditSet.GetItemState( EE_FEATURE_FIELD, sal_False, &pItem ) == SFX_ITEM_SET) ) + { + if( const SvxFieldData* pFieldData = static_cast< const SvxFieldItem* >( pItem )->GetField() ) + { + if( pFieldData->ISA( SvxPageField ) ) + aParaText.AppendAscii( "&P" ); + else if( pFieldData->ISA( SvxPagesField ) ) + aParaText.AppendAscii( "&N" ); + else if( pFieldData->ISA( SvxDateField ) ) + aParaText.AppendAscii( "&D" ); + else if( pFieldData->ISA( SvxTimeField ) || pFieldData->ISA( SvxExtTimeField ) ) + aParaText.AppendAscii( "&T" ); + else if( pFieldData->ISA( SvxTableField ) ) + aParaText.AppendAscii( "&A" ); + else if( pFieldData->ISA( SvxFileField ) ) // title -> file name + aParaText.AppendAscii( "&F" ); + else if( const SvxExtFileField* pFileField = PTR_CAST( SvxExtFileField, pFieldData ) ) + { + switch( pFileField->GetFormat() ) + { + case SVXFILEFORMAT_NAME_EXT: + case SVXFILEFORMAT_NAME: + aParaText.AppendAscii( "&F" ); + break; + case SVXFILEFORMAT_PATH: + aParaText.AppendAscii( "&Z" ); + break; + case SVXFILEFORMAT_FULLPATH: + aParaText.AppendAscii( "&Z&F" ); + break; + default: + DBG_ERRORFILE( "XclExpHFConverter::AppendPortion - unknown file field" ); + } + } + } + } + else + { + String aPortionText( mrEE.GetText( aSel ) ); + aPortionText.SearchAndReplaceAll( String( '&' ), String( RTL_CONSTASCII_USTRINGPARAM( "&&" ) ) ); + // #i17440# space between font height and numbers in text + if( bFontHtChanged && aParaText.Len() && aPortionText.Len() ) + { + sal_Unicode cLast = aParaText.GetChar( aParaText.Len() - 1 ); + sal_Unicode cFirst = aPortionText.GetChar( 0 ); + if( ('0' <= cLast) && (cLast <= '9') && ('0' <= cFirst) && (cFirst <= '9') ) + aParaText.Append( ' ' ); + } + aParaText.Append( aPortionText ); + } + } + + aSel.nStartPos = aSel.nEndPos; + } + + ScGlobal::AddToken( aText, aParaText, '\n' ); + if( nParaHeight == 0 ) + nParaHeight = aFontData.mnHeight * 20; // points -> twips + nHeight += nParaHeight; + } + + mrEE.SetUpdateMode( bOldUpdateMode ); + + if( aText.Len() ) + { + maHFString.Append( '&' ).Append( cPortionCode ).Append( aText ); + mnTotalHeight = ::std::max( mnTotalHeight, nHeight ); + } +} + +// URL conversion ============================================================= + +namespace { + +/** Converts the file URL passed in rUrl to a URL in DOS notation (local or UNC). + @param rUrl (in/out-param) In: URL to convert; Out: Converted URL in DOS notation. + @param rBasePath Base path for relative URLs. + @param bSaveRelUrl Converts to a relative URL, using rBasePath. + @return True = Conversion successful, rUrl contains converted file URL. */ +bool lclConvertToDos( String& rUrl, const String& rBasePath, bool bSaveRelUrl ) +{ + String aDosUrl( INetURLObject( rUrl ).getFSysPath( INetURLObject::FSYS_DOS ) ); + bool bRet = (aDosUrl.Len() > 0); + if( bRet && bSaveRelUrl ) + { + // try to convert to relative path + String aDosBase( INetURLObject( rBasePath ).getFSysPath( INetURLObject::FSYS_DOS ) ); + if( aDosBase.Len() ) + { + xub_StrLen nPos; + + // --- 1st step: delete equal subdirectories --- + + // special handling for UNC + xub_StrLen nStartSearch = aDosBase.EqualsAscii( "\\\\", 0, 2 ) ? 2 : 0; + bool bEqualBase = false; + bool bLoop = true; + while( bLoop && ((nPos = aDosBase.Search( '\\', nStartSearch )) != STRING_NOTFOUND) ) + { + bLoop = (TRUE == aDosBase.Equals( aDosUrl, 0, nPos + 1 )); + if( bLoop ) + { + aDosBase.Erase( 0, nPos + 1 ); + aDosUrl.Erase( 0, nPos + 1 ); + nStartSearch = 0; + bEqualBase = true; + } + } + + // --- 2nd step: add parent directory levels --- + + if( bEqualBase ) + { + while( (nPos = aDosBase.Search( '\\' )) != STRING_NOTFOUND ) + { + aDosBase.Erase( 0, nPos + 1 ); + aDosUrl.InsertAscii( "..\\", 0 ); + } + } + } + rUrl = aDosUrl; + } + return bRet; +} + +/** Encodes special parts of the URL, i.e. directory separators and volume names. + @param pTableName Pointer to a table name to be encoded in this URL, or 0. */ +void lclEncodeDosUrl( XclBiff eBiff, String& rUrl, const String* pTableName = 0 ) +{ + if( rUrl.Len() ) + { + String aOldUrl( rUrl ); + rUrl = EXC_URLSTART_ENCODED; + + if( (aOldUrl.Len() > 2) && aOldUrl.EqualsAscii( "\\\\", 0, 2 ) ) + { + // UNC + rUrl.Append( EXC_URL_DOSDRIVE ).Append( '@' ); + aOldUrl.Erase( 0, 2 ); + } + else if( (aOldUrl.Len() > 2) && aOldUrl.EqualsAscii( ":\\", 1, 2 ) ) + { + // drive letter + rUrl.Append( EXC_URL_DOSDRIVE ).Append( aOldUrl.GetChar( 0 ) ); + aOldUrl.Erase( 0, 3 ); + } + + // directories + xub_StrLen nPos; + while( (nPos = aOldUrl.Search( '\\' )) != STRING_NOTFOUND ) + { + if( aOldUrl.EqualsAscii( "..", 0, 2 ) ) + rUrl.Append( EXC_URL_PARENTDIR ); // parent dir + else + rUrl.Append( aOldUrl.GetBuffer(), nPos ).Append( EXC_URL_SUBDIR ); + aOldUrl.Erase( 0, nPos + 1 ); + } + + // file name + if( pTableName ) // enclose file name in brackets if table name follows + rUrl.Append( '[' ).Append( aOldUrl ).Append( ']' ); + else + rUrl.Append( aOldUrl ); + } + else // empty URL -> self reference + { + switch( eBiff ) + { + case EXC_BIFF5: + rUrl = pTableName ? EXC_URLSTART_SELFENCODED : EXC_URLSTART_SELF; + break; + case EXC_BIFF8: + DBG_ASSERT( pTableName, "lclEncodeDosUrl - sheet name required for BIFF8" ); + rUrl = EXC_URLSTART_SELF; + break; + default: + DBG_ERROR_BIFF(); + } + } + + // table name + if( pTableName ) + rUrl.Append( *pTableName ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +String XclExpUrlHelper::EncodeUrl( const XclExpRoot& rRoot, const String& rAbsUrl, const String* pTableName ) +{ + String aDosUrl( rAbsUrl ); + if( !aDosUrl.Len() || lclConvertToDos( aDosUrl, rRoot.GetBasePath(), rRoot.IsRelUrl() ) ) + lclEncodeDosUrl( rRoot.GetBiff(), aDosUrl, pTableName ); + return aDosUrl; +} + +String XclExpUrlHelper::EncodeDde( const String& rApplic, const String rTopic ) +{ + String aDde( rApplic ); + aDde.Append( EXC_DDE_DELIM ).Append( rTopic ); + return aDde; +} + +// Cached Value Lists ========================================================= + +XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix& rMatrix ) + : mrMatrix( rMatrix ) +{ + mrMatrix.IncRef(); +} +XclExpCachedMatrix::~XclExpCachedMatrix() +{ + mrMatrix.DecRef(); +} + +void XclExpCachedMatrix::GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const +{ + mrMatrix.GetDimensions( nCols, nRows ); + + DBG_ASSERT( nCols && nRows, "XclExpCachedMatrix::GetDimensions - empty matrix" ); + DBG_ASSERT( nCols <= 256, "XclExpCachedMatrix::GetDimensions - too many columns" ); +} + +sal_Size XclExpCachedMatrix::GetSize() const +{ + SCSIZE nCols, nRows; + + GetDimensions( nCols, nRows ); + + /* The returned size may be wrong if the matrix contains strings. The only + effect is that the export stream has to update a wrong record size which is + faster than to iterate through all cached values and calculate their sizes. */ + return 3 + 9 * (nCols * nRows); +} + +void XclExpCachedMatrix::Save( XclExpStream& rStrm ) const +{ + SCSIZE nCols, nRows; + + GetDimensions( nCols, nRows ); + + if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 ) + // in BIFF2-BIFF7: 256 columns represented by 0 columns + rStrm << static_cast< sal_uInt8 >( nCols ) << static_cast< sal_uInt16 >( nRows ); + else + // in BIFF8: columns and rows decreaed by 1 + rStrm << static_cast< sal_uInt8 >( nCols - 1 ) << static_cast< sal_uInt16 >( nRows - 1 ); + + for( SCSIZE nRow = 0; nRow < nRows; ++nRow ) + { + for( SCSIZE nCol = 0; nCol < nCols; ++nCol ) + { + ScMatValType nMatValType = SC_MATVAL_VALUE; + const ScMatrixValue* pMatVal = mrMatrix.Get( nCol, nRow, nMatValType ); + + if( !pMatVal || SC_MATVAL_EMPTY == nMatValType ) + { + rStrm.SetSliceSize( 9 ); + rStrm << EXC_CACHEDVAL_EMPTY; + rStrm.WriteZeroBytes( 8 ); + } + else if( ScMatrix::IsNonValueType( nMatValType ) ) + { + XclExpString aStr( pMatVal->GetString(), EXC_STR_DEFAULT ); + rStrm.SetSliceSize( 6 ); + rStrm << EXC_CACHEDVAL_STRING << aStr; + } + else if( SC_MATVAL_BOOLEAN == nMatValType ) + { + sal_Int8 nBool = pMatVal->GetBoolean(); + rStrm.SetSliceSize( 9 ); + rStrm << EXC_CACHEDVAL_BOOL << nBool; + rStrm.WriteZeroBytes( 7 ); + } + else if( USHORT nScError = pMatVal->GetError() ) + { + sal_Int8 nError ( XclTools::GetXclErrorCode( nScError ) ); + rStrm.SetSliceSize( 9 ); + rStrm << EXC_CACHEDVAL_ERROR << nError; + rStrm.WriteZeroBytes( 7 ); + } + else + { + rStrm.SetSliceSize( 9 ); + rStrm << EXC_CACHEDVAL_DOUBLE << pMatVal->fVal; + } + } + } +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xelink.cxx b/sc/source/filter/excel/xelink.cxx new file mode 100644 index 000000000000..fcf738527025 --- /dev/null +++ b/sc/source/filter/excel/xelink.cxx @@ -0,0 +1,2371 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xelink.hxx" + +#include <algorithm> +#include <unotools/collatorwrapper.hxx> +#include <svl/zforlist.hxx> +#include "document.hxx" +#include "cell.hxx" +#include "scextopt.hxx" +#include "externalrefmgr.hxx" + +#include <vector> +#include <memory> + +using ::std::auto_ptr; +using ::std::find_if; +using ::std::vector; +using ::rtl::OUString; +using ::com::sun::star::uno::Any; + +// ============================================================================ +// *** Helper classes *** +// ============================================================================ + +// External names ============================================================= + +/** This is a base class for any external name (i.e. add-in names or DDE links). + @descr Derived classes implement creation and export of the external names. */ +class XclExpExtNameBase : public XclExpRecord, protected XclExpRoot +{ +public: + /** @param nFlags The flags to export. */ + explicit XclExpExtNameBase( const XclExpRoot& rRoot, + const String& rName, sal_uInt16 nFlags = 0 ); + virtual ~XclExpExtNameBase(); + + /** Returns the name string of the external name. */ + inline const String& GetName() const { return maName; } + +private: + /** Writes the start of the record that is equal in all EXTERNNAME records and calls WriteAddData(). */ + virtual void WriteBody( XclExpStream& rStrm ); + /** Called to write additional data following the common record contents. + @descr Derived classes should overwrite this function to write their data. */ + virtual void WriteAddData( XclExpStream& rStrm ); + +private: + String maName; /// Calc name (title) of the external name. + XclExpStringRef mxName; /// Excel name (title) of the external name. + sal_uInt16 mnFlags; /// Flags for record export. +}; + +// ---------------------------------------------------------------------------- + +/** Represents an EXTERNNAME record for an add-in function name. */ +class XclExpExtNameAddIn : public XclExpExtNameBase +{ +public: + explicit XclExpExtNameAddIn( const XclExpRoot& rRoot, const String& rName ); + +private: + /** Writes additional record contents. */ + virtual void WriteAddData( XclExpStream& rStrm ); +}; + +// ---------------------------------------------------------------------------- + +/** Represents an EXTERNNAME record for a DDE link. */ +class XclExpExtNameDde : public XclExpExtNameBase +{ +public: + explicit XclExpExtNameDde( const XclExpRoot& rRoot, const String& rName, + sal_uInt16 nFlags, const ScMatrix* pResults = 0 ); + +private: + /** Writes additional record contents. */ + virtual void WriteAddData( XclExpStream& rStrm ); + +private: + typedef ScfRef< XclExpCachedMatrix > XclExpCachedMatRef; + XclExpCachedMatRef mxMatrix; /// Cached results of the DDE link. +}; + +// ---------------------------------------------------------------------------- + +class XclExpSupbook; + +class XclExpExtName : public XclExpExtNameBase +{ +public: + explicit XclExpExtName( const XclExpRoot& rRoot, const XclExpSupbook& rSupbook, const String& rName, + const ScExternalRefCache::TokenArrayRef pArray ); + +private: + /** Writes additional record contents. */ + virtual void WriteAddData( XclExpStream& rStrm ); + +private: + const XclExpSupbook& mrSupbook; + auto_ptr<ScTokenArray> mpArray; +}; + +// List of external names ===================================================== + +/** List of all external names of a sheet. */ +class XclExpExtNameBuffer : public XclExpRecordBase, protected XclExpRoot +{ +public: + explicit XclExpExtNameBuffer( const XclExpRoot& rRoot ); + + /** Inserts an add-in function name + @return The 1-based (Excel-like) list index of the name. */ + sal_uInt16 InsertAddIn( const String& rName ); + /** InsertEuroTool */ + sal_uInt16 InsertEuroTool( const String& rName ); + /** Inserts a DDE link. + @return The 1-based (Excel-like) list index of the DDE link. */ + sal_uInt16 InsertDde( const String& rApplic, const String& rTopic, const String& rItem ); + + sal_uInt16 InsertExtName( const XclExpSupbook& rSupbook, const String& rName, const ScExternalRefCache::TokenArrayRef pArray ); + + /** Writes the EXTERNNAME record list. */ + virtual void Save( XclExpStream& rStrm ); + +private: + typedef XclExpRecordList< XclExpExtNameBase > XclExpExtNameList; + typedef XclExpExtNameList::RecordRefType XclExpExtNameRef; + +private: + /** Returns the 1-based (Excel-like) list index of the external name or 0, if not found. */ + sal_uInt16 GetIndex( const String& rName ) const; + /** Appends the passed newly crested external name. + @return The 1-based (Excel-like) list index of the appended name. */ + sal_uInt16 AppendNew( XclExpExtNameBase* pExtName ); + +private: + XclExpExtNameList maNameList; /// The list with all EXTERNNAME records. +}; + +// Cached external cells ====================================================== + +/** Stores the contents of a consecutive row of external cells (record CRN). */ +class XclExpCrn : public XclExpRecord +{ +public: + explicit XclExpCrn( SCCOL nScCol, SCROW nScRow, const Any& rValue ); + + /** Returns true, if the passed value could be appended to this record. */ + bool InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue ); + +private: + virtual void WriteBody( XclExpStream& rStrm ); + + void WriteBool( XclExpStream& rStrm, bool bValue ); + void WriteDouble( XclExpStream& rStrm, double fValue ); + void WriteString( XclExpStream& rStrm, const OUString& rValue ); + void WriteError( XclExpStream& rStrm, sal_uInt8 nErrCode ); + void WriteEmpty( XclExpStream& rStrm ); + +private: + typedef ::std::vector< Any > CachedValues; + + CachedValues maValues; /// All cached values. + SCCOL mnScCol; /// Column index of the first external cell. + SCROW mnScRow; /// Row index of the external cells. +}; + +// ---------------------------------------------------------------------------- + +/** Represents the record XCT which is the header record of a CRN record list. + */ +class XclExpXct : public XclExpRecordBase, protected XclExpRoot +{ +public: + explicit XclExpXct( const XclExpRoot& rRoot, + const String& rTabName, sal_uInt16 nSBTab, + ScExternalRefCache::TableTypeRef xCacheTable ); + + /** Returns the external sheet name. */ + inline const XclExpString& GetTabName() const { return maTabName; } + + /** Stores all cells in the given range in the CRN list. */ + void StoreCellRange( const ScRange& rRange ); + + void StoreCell( const ScAddress& rCell, const ::formula::FormulaToken& rToken ); + void StoreCellRange( const ScRange& rRange, const ::formula::FormulaToken& rToken ); + + /** Writes the XCT and all CRN records. */ + virtual void Save( XclExpStream& rStrm ); + +private: + ScExternalRefCache::TableTypeRef mxCacheTable; + ScMarkData maUsedCells; /// Contains addresses of all stored cells. + ScRange maBoundRange; /// Bounding box of maUsedCells. + XclExpString maTabName; /// Sheet name of the external sheet. + sal_uInt16 mnSBTab; /// Referred sheet index in SUPBOOK record. +}; + +// External documents (EXTERNSHEET/SUPBOOK), base class ======================= + +/** Base class for records representing external sheets/documents. + + In BIFF5/BIFF7, this record is the EXTERNSHEET record containing one sheet + of the own or an external document. In BIFF8, this record is the SUPBOOK + record representing the entire own or external document with all referenced + sheets. + */ +class XclExpExternSheetBase : public XclExpRecord, protected XclExpRoot +{ +public: + explicit XclExpExternSheetBase( const XclExpRoot& rRoot, + sal_uInt16 nRecId, sal_uInt32 nRecSize = 0 ); + +protected: + /** Creates and returns the list of EXTERNNAME records. */ + XclExpExtNameBuffer& GetExtNameBuffer(); + /** Creates and returns the list of EXTERNNAME records. */ + void WriteExtNameBuffer( XclExpStream& rStrm ); + +private: + typedef ScfRef< XclExpExtNameBuffer > XclExpExtNameBfrRef; + XclExpExtNameBfrRef mxExtNameBfr; /// List of EXTERNNAME records. +}; + +// External documents (EXTERNSHEET, BIFF5/BIFF7) ============================== + +/** Represents an EXTERNSHEET record containing the URL and sheet name of a sheet. + @descr This class is used up to BIFF7 only, writing a BIFF8 EXTERNSHEET + record is implemented directly in the link manager. */ +class XclExpExternSheet : public XclExpExternSheetBase +{ +public: + /** Creates an EXTERNSHEET record containing a special code (i.e. own document or sheet). */ + explicit XclExpExternSheet( const XclExpRoot& rRoot, sal_Unicode cCode ); + /** Creates an EXTERNSHEET record referring to an internal sheet. */ + explicit XclExpExternSheet( const XclExpRoot& rRoot, const String& rTabName ); + + /** Finds or inserts an EXTERNNAME record for add-ins. + @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */ + sal_uInt16 InsertAddIn( const String& rName ); + + /** Writes the EXTERNSHEET and all EXTERNNAME, XCT and CRN records. */ + virtual void Save( XclExpStream& rStrm ); + +private: + /** Initializes the record data with the passed encoded URL. */ + void Init( const String& rEncUrl ); + /** Writes the contents of the EXTERNSHEET record. */ + virtual void WriteBody( XclExpStream& rStrm ); + +private: + XclExpString maTabName; /// The name of the sheet. +}; + +// External documents (SUPBOOK, BIFF8) ======================================== + +/** The SUPBOOK record contains data for an external document (URL, sheet names, external values). */ +class XclExpSupbook : public XclExpExternSheetBase +{ +public: + /** Creates a SUPBOOK record for internal references. */ + explicit XclExpSupbook( const XclExpRoot& rRoot, sal_uInt16 nXclTabCount ); + /** Creates a SUPBOOK record for add-in functions. */ + explicit XclExpSupbook( const XclExpRoot& rRoot ); + /** EUROTOOL SUPBOOK */ + explicit XclExpSupbook( const XclExpRoot& rRoot, const String& rUrl, XclSupbookType ); + /** Creates a SUPBOOK record for an external document. */ + explicit XclExpSupbook( const XclExpRoot& rRoot, const String& rUrl ); + /** Creates a SUPBOOK record for a DDE link. */ + explicit XclExpSupbook( const XclExpRoot& rRoot, const String& rApplic, const String& rTopic ); + + /** Returns true, if this SUPBOOK contains the passed URL of an external document. */ + bool IsUrlLink( const String& rUrl ) const; + /** Returns true, if this SUPBOOK contains the passed DDE link. */ + bool IsDdeLink( const String& rApplic, const String& rTopic ) const; + /** Fills the passed reference log entry with the URL and sheet names. */ + void FillRefLogEntry( XclExpRefLogEntry& rRefLogEntry, + sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) const; + + /** Stores all cells in the given range in the CRN list of the specified SUPBOOK sheet. */ + void StoreCellRange( const ScRange& rRange, sal_uInt16 nSBTab ); + + void StoreCell( sal_uInt16 nSBTab, const ScAddress& rCell, const ::formula::FormulaToken& rToken ); + void StoreCellRange( sal_uInt16 nSBTab, const ScRange& rRange, const ::formula::FormulaToken& rToken ); + + sal_uInt16 GetTabIndex( const String& rTabName ) const; + sal_uInt16 GetTabCount() const; + + /** Inserts a new sheet name into the SUPBOOK and returns the SUPBOOK internal sheet index. */ + sal_uInt16 InsertTabName( const String& rTabName, ScExternalRefCache::TableTypeRef xCacheTable ); + /** Finds or inserts an EXTERNNAME record for add-ins. + @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */ + sal_uInt16 InsertAddIn( const String& rName ); + /** InsertEuroTool */ + sal_uInt16 InsertEuroTool( const String& rName ); + /** Finds or inserts an EXTERNNAME record for DDE links. + @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */ + sal_uInt16 InsertDde( const String& rItem ); + + sal_uInt16 InsertExtName( const String& rName, const ScExternalRefCache::TokenArrayRef pArray ); + + /** Writes the SUPBOOK and all EXTERNNAME, XCT and CRN records. */ + virtual void Save( XclExpStream& rStrm ); + +private: + /** Returns the sheet name inside of this SUPBOOK. */ + const XclExpString* GetTabName( sal_uInt16 nSBTab ) const; + + /** Writes the SUPBOOK record contents. */ + virtual void WriteBody( XclExpStream& rStrm ); + +private: + typedef XclExpRecordList< XclExpXct > XclExpXctList; + typedef XclExpXctList::RecordRefType XclExpXctRef; + + XclExpXctList maXctList; /// List of XCT records (which contain CRN records). + String maUrl; /// URL of the external document or application name for DDE. + String maDdeTopic; /// Topic of an DDE link. + XclExpString maUrlEncoded; /// Document name encoded for Excel. + XclSupbookType meType; /// Type of this SUPBOOK record. + sal_uInt16 mnXclTabCount; /// Number of internal sheets. +}; + +// All SUPBOOKS in a document ================================================= + +/** This struct contains a sheet index range for 3D references. + @descr This reference consists of an index to a SUPBOOK record and indexes + to SUPBOOK sheet names. */ +struct XclExpXti +{ + sal_uInt16 mnSupbook; /// Index to SUPBOOK record. + sal_uInt16 mnFirstSBTab; /// Index to the first sheet of the range in the SUPBOOK. + sal_uInt16 mnLastSBTab; /// Index to the last sheet of the range in the SUPBOOK. + + inline explicit XclExpXti() : mnSupbook( 0 ), mnFirstSBTab( 0 ), mnLastSBTab( 0 ) {} + inline explicit XclExpXti( sal_uInt16 nSupbook, sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) : + mnSupbook( nSupbook ), mnFirstSBTab( nFirstSBTab ), mnLastSBTab( nLastSBTab ) {} + + /** Writes this XTI structure (inside of the EXTERNSHEET record). */ + inline void Save( XclExpStream& rStrm ) const + { rStrm << mnSupbook << mnFirstSBTab << mnLastSBTab; } +}; + +inline bool operator==( const XclExpXti& rLeft, const XclExpXti& rRight ) +{ + return + (rLeft.mnSupbook == rRight.mnSupbook) && + (rLeft.mnFirstSBTab == rRight.mnFirstSBTab) && + (rLeft.mnLastSBTab == rRight.mnLastSBTab); +} + +// ---------------------------------------------------------------------------- + +/** Contains a list of all SUPBOOK records and index arrays of external sheets. */ +class XclExpSupbookBuffer : public XclExpRecordBase, protected XclExpRoot +{ +public: + explicit XclExpSupbookBuffer( const XclExpRoot& rRoot ); + + /** Finds SUPBOOK index and SUPBOOK sheet range from given Excel sheet range. + @return An XTI structure containing SUPBOOK and sheet indexes. */ + XclExpXti GetXti( sal_uInt16 nFirstXclTab, sal_uInt16 nLastXclTab, + XclExpRefLogEntry* pRefLogEntry = 0 ) const; + + /** Stores all cells in the given range in a CRN record list. */ + void StoreCellRange( const ScRange& rRange ); + + void StoreCell( sal_uInt16 nFileId, const String& rTabName, const ScAddress& rCell ); + void StoreCellRange( sal_uInt16 nFileId, const String& rTabName, const ScRange& rRange ); + + /** Finds or inserts an EXTERNNAME record for an add-in function name. + @param rnSupbook Returns the index of the SUPBOOK record which contains the add-in function name. + @param rnExtName Returns the 1-based EXTERNNAME record index. */ + bool InsertAddIn( + sal_uInt16& rnSupbook, sal_uInt16& rnExtName, + const String& rName ); + /** InsertEuroTool */ + bool InsertEuroTool( + sal_uInt16& rnSupbook, sal_uInt16& rnExtName, + const String& rName ); + /** Finds or inserts an EXTERNNAME record for DDE links. + @param rnSupbook Returns the index of the SUPBOOK record which contains the DDE link. + @param rnExtName Returns the 1-based EXTERNNAME record index. */ + bool InsertDde( + sal_uInt16& rnSupbook, sal_uInt16& rnExtName, + const String& rApplic, const String& rTopic, const String& rItem ); + + bool InsertExtName( + sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const String& rUrl, + const String& rName, const ScExternalRefCache::TokenArrayRef pArray ); + + XclExpXti GetXti( sal_uInt16 nFileId, const String& rTabName, sal_uInt16 nXclTabSpan, + XclExpRefLogEntry* pRefLogEntry = NULL ); + + /** Writes all SUPBOOK records with their sub records. */ + virtual void Save( XclExpStream& rStrm ); + + struct XclExpSBIndex + { + sal_uInt16 mnSupbook; /// SUPBOOK index for an Excel sheet. + sal_uInt16 mnSBTab; /// Sheet name index in SUPBOOK for an Excel sheet. + inline void Set( sal_uInt16 nSupbook, sal_uInt16 nSBTab ) + { mnSupbook = nSupbook; mnSBTab = nSBTab; } + }; + typedef ::std::vector< XclExpSBIndex > XclExpSBIndexVec; + +private: + typedef XclExpRecordList< XclExpSupbook > XclExpSupbookList; + typedef XclExpSupbookList::RecordRefType XclExpSupbookRef; + +private: + /** Searches for the SUPBOOK record containing the passed document URL. + @param rxSupbook (out-param) Returns a reference to the SUPBOOK record, or 0. + @param rnIndex (out-param) Returns the list index, if the SUPBOOK exists. + @return True, if the SUPBOOK record exists (out-parameters are valid). */ + bool GetSupbookUrl( XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex, + const String& rUrl ) const; + /** Searches for the SUPBOOK record containing the passed DDE link. + @param rxSupbook (out-param) Returns a reference to the SUPBOOK record, or 0. + @param rnIndex (out-param) Returns the list index, if the SUPBOOK exists. + @return True, if the SUPBOOK record exists (out-parameters are valid). */ + bool GetSupbookDde( XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex, + const String& rApplic, const String& rTopic ) const; + + /** Appends a new SUPBOOK to the list. + @return The list index of the SUPBOOK record. */ + sal_uInt16 Append( XclExpSupbookRef xSupbook ); + +private: + XclExpSupbookList maSupbookList; /// List of all SUPBOOK records. + XclExpSBIndexVec maSBIndexVec; /// SUPBOOK and sheet name index for each Excel sheet. + sal_uInt16 mnOwnDocSB; /// Index to SUPBOOK for own document. + sal_uInt16 mnAddInSB; /// Index to add-in SUPBOOK. +}; + +// Export link manager ======================================================== + +/** Abstract base class for implementation classes of the link manager. */ +class XclExpLinkManagerImpl : protected XclExpRoot +{ +public: + /** Derived classes search for an EXTSHEET structure for the given Calc sheet range. */ + virtual void FindExtSheet( sal_uInt16& rnExtSheet, + sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab, + SCTAB nFirstScTab, SCTAB nLastScTab, + XclExpRefLogEntry* pRefLogEntry ) = 0; + /** Derived classes search for a special EXTERNSHEET index for the own document. */ + virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) = 0; + + virtual void FindExtSheet( sal_uInt16 nFileId, const String& rTabName, sal_uInt16 nXclTabSpan, + sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab, + XclExpRefLogEntry* pRefLogEntry ) = 0; + + /** Derived classes store all cells in the given range in a CRN record list. */ + virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2 ) = 0; + + virtual void StoreCell( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef ) = 0; + virtual void StoreCellRange( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef1, const ScSingleRefData& rRef2 ) = 0; + + /** Derived classes find or insert an EXTERNNAME record for an add-in function name. */ + virtual bool InsertAddIn( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, + const String& rName ) = 0; + /** InsertEuroTool */ + virtual bool InsertEuroTool( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, + const String& rName ) = 0; + + /** Derived classes find or insert an EXTERNNAME record for DDE links. */ + virtual bool InsertDde( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, + const String& rApplic, const String& rTopic, const String& rItem ) = 0; + + virtual bool InsertExtName( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const String& rUrl, + const String& rName, const ScExternalRefCache::TokenArrayRef pArray ) = 0; + + /** Derived classes write the entire link table to the passed stream. */ + virtual void Save( XclExpStream& rStrm ) = 0; + +protected: + explicit XclExpLinkManagerImpl( const XclExpRoot& rRoot ); +}; + +// ---------------------------------------------------------------------------- + +/** Implementation of the link manager for BIFF5/BIFF7. */ +class XclExpLinkManagerImpl5 : public XclExpLinkManagerImpl +{ +public: + explicit XclExpLinkManagerImpl5( const XclExpRoot& rRoot ); + + virtual void FindExtSheet( sal_uInt16& rnExtSheet, + sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab, + SCTAB nFirstScTab, SCTAB nLastScTab, + XclExpRefLogEntry* pRefLogEntry ); + virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ); + + virtual void FindExtSheet( sal_uInt16 nFileId, const String& rTabName, sal_uInt16 nXclTabSpan, + sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab, + XclExpRefLogEntry* pRefLogEntry ); + + virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2 ); + + virtual void StoreCell( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef ); + virtual void StoreCellRange( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef1, const ScSingleRefData& rRef2 ); + + virtual bool InsertAddIn( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, + const String& rName ); + + /** InsertEuroTool */ + virtual bool InsertEuroTool( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, + const String& rName ); + + virtual bool InsertDde( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, + const String& rApplic, const String& rTopic, const String& rItem ); + + virtual bool InsertExtName( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const String& rUrl, + const String& rName, const ScExternalRefCache::TokenArrayRef pArray ); + + virtual void Save( XclExpStream& rStrm ); + +private: + typedef XclExpRecordList< XclExpExternSheet > XclExpExtSheetList; + typedef XclExpExtSheetList::RecordRefType XclExpExtSheetRef; + typedef ::std::map< SCTAB, sal_uInt16 > XclExpIntTabMap; + typedef ::std::map< sal_Unicode, sal_uInt16 > XclExpCodeMap; + +private: + /** Returns the number of EXTERNSHEET records. */ + sal_uInt16 GetExtSheetCount() const; + + /** Appends an internal EXTERNSHEET record and returns the one-based index. */ + sal_uInt16 AppendInternal( XclExpExtSheetRef xExtSheet ); + /** Creates all EXTERNSHEET records for internal sheets on first call. */ + void CreateInternal(); + + /** Returns the specified internal EXTERNSHEET record. */ + XclExpExtSheetRef GetInternal( sal_uInt16 nExtSheet ); + /** Returns the EXTERNSHEET index of an internal Calc sheet, or a deleted reference. */ + XclExpExtSheetRef FindInternal( sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab, SCTAB nScTab ); + /** Finds or creates the EXTERNSHEET index of an internal special EXTERNSHEET. */ + XclExpExtSheetRef FindInternal( sal_uInt16& rnExtSheet, sal_Unicode cCode ); + +private: + XclExpExtSheetList maExtSheetList; /// List with EXTERNSHEET records. + XclExpIntTabMap maIntTabMap; /// Maps internal Calc sheets to EXTERNSHEET records. + XclExpCodeMap maCodeMap; /// Maps special external codes to EXTERNSHEET records. +}; + +// ---------------------------------------------------------------------------- + +/** Implementation of the link manager for BIFF8. */ +class XclExpLinkManagerImpl8 : public XclExpLinkManagerImpl +{ +public: + explicit XclExpLinkManagerImpl8( const XclExpRoot& rRoot ); + + virtual void FindExtSheet( sal_uInt16& rnExtSheet, + sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab, + SCTAB nFirstScTab, SCTAB nLastScTab, + XclExpRefLogEntry* pRefLogEntry ); + virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ); + + virtual void FindExtSheet( sal_uInt16 nFileId, const String& rTabName, sal_uInt16 nXclTabSpan, + sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab, + XclExpRefLogEntry* pRefLogEntry ); + + virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2 ); + + virtual void StoreCell( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef ); + virtual void StoreCellRange( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef1, const ScSingleRefData& rRef2 ); + + virtual bool InsertAddIn( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, + const String& rName ); + /** InsertEuroTool */ + virtual bool InsertEuroTool( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, + const String& rName ); + + virtual bool InsertDde( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, + const String& rApplic, const String& rTopic, const String& rItem ); + + virtual bool InsertExtName( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const String& rUrl, + const String& rName, const ScExternalRefCache::TokenArrayRef pArray ); + + virtual void Save( XclExpStream& rStrm ); + +private: + /** Searches for or inserts a new XTI structure. + @return The 0-based list index of the XTI structure. */ + sal_uInt16 InsertXti( const XclExpXti& rXti ); + +private: + typedef ::std::vector< XclExpXti > XclExpXtiVec; + + XclExpSupbookBuffer maSBBuffer; /// List of all SUPBOOK records. + XclExpXtiVec maXtiVec; /// List of XTI structures for the EXTERNSHEET record. +}; + +// ============================================================================ +// *** Implementation *** +// ============================================================================ + +// Excel sheet indexes ======================================================== + +const sal_uInt8 EXC_TABBUF_IGNORE = 0x01; /// Sheet will be ignored completely. +const sal_uInt8 EXC_TABBUF_EXTERN = 0x02; /// Sheet is linked externally. +const sal_uInt8 EXC_TABBUF_SKIPMASK = 0x0F; /// Sheet will be skipped, if any flag is set. +const sal_uInt8 EXC_TABBUF_VISIBLE = 0x10; /// Sheet is visible. +const sal_uInt8 EXC_TABBUF_SELECTED = 0x20; /// Sheet is selected. +const sal_uInt8 EXC_TABBUF_MIRRORED = 0x40; /// Sheet is mirrored (right-to-left). + +// ---------------------------------------------------------------------------- + +XclExpTabInfo::XclExpTabInfo( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mnScCnt( 0 ), + mnXclCnt( 0 ), + mnXclExtCnt( 0 ), + mnXclSelCnt( 0 ), + mnDisplXclTab( 0 ), + mnFirstVisXclTab( 0 ) +{ + ScDocument& rDoc = GetDoc(); + ScExtDocOptions& rDocOpt = GetExtDocOptions(); + + mnScCnt = rDoc.GetTableCount(); + + SCTAB nScTab; + SCTAB nFirstVisScTab = SCTAB_INVALID; // first visible sheet + SCTAB nFirstExpScTab = SCTAB_INVALID; // first exported sheet + + // --- initialize the flags in the index buffer --- + + maTabInfoVec.resize( mnScCnt ); + for( nScTab = 0; nScTab < mnScCnt; ++nScTab ) + { + // ignored sheets (skipped by export, with invalid Excel sheet index) + if( rDoc.IsScenario( nScTab ) ) + { + SetFlag( nScTab, EXC_TABBUF_IGNORE ); + } + + // external sheets (skipped, but with valid Excel sheet index for ref's) + else if( rDoc.GetLinkMode( nScTab ) == SC_LINK_VALUE ) + { + SetFlag( nScTab, EXC_TABBUF_EXTERN ); + } + + // exported sheets + else + { + // sheet name + rDoc.GetName( nScTab, maTabInfoVec[ nScTab ].maScName ); + + // remember first exported sheet + if( nFirstExpScTab == SCTAB_INVALID ) + nFirstExpScTab = nScTab; + // remember first visible exported sheet + if( (nFirstVisScTab == SCTAB_INVALID) && rDoc.IsVisible( nScTab ) ) + nFirstVisScTab = nScTab; + + // sheet visible (only exported sheets) + SetFlag( nScTab, EXC_TABBUF_VISIBLE, rDoc.IsVisible( nScTab ) ); + + // sheet selected (only exported sheets) + if( const ScExtTabSettings* pTabSett = rDocOpt.GetTabSettings( nScTab ) ) + SetFlag( nScTab, EXC_TABBUF_SELECTED, pTabSett->mbSelected ); + + // sheet mirrored (only exported sheets) + SetFlag( nScTab, EXC_TABBUF_MIRRORED, rDoc.IsLayoutRTL( nScTab ) ); + } + } + + // --- visible/selected sheets --- + + SCTAB nDisplScTab = rDocOpt.GetDocSettings().mnDisplTab; + + // #112908# find first visible exported sheet + if( (nFirstVisScTab == SCTAB_INVALID) || !IsExportTab( nFirstVisScTab ) ) + { + // no exportable visible sheet -> use first exportable sheet + nFirstVisScTab = nFirstExpScTab; + if( (nFirstVisScTab == SCTAB_INVALID) || !IsExportTab( nFirstVisScTab ) ) + { + // no exportable sheet at all -> use active sheet and export it + nFirstVisScTab = nDisplScTab; + SetFlag( nFirstVisScTab, EXC_TABBUF_SKIPMASK, false ); // clear skip flags + } + SetFlag( nFirstVisScTab, EXC_TABBUF_VISIBLE ); // must be visible, even if originally hidden + } + + // find currently displayed sheet + if( !IsExportTab( nDisplScTab ) ) // selected sheet not exported (i.e. scenario) -> use first visible + nDisplScTab = nFirstVisScTab; + SetFlag( nDisplScTab, EXC_TABBUF_VISIBLE | EXC_TABBUF_SELECTED ); + + // number of selected sheets + for( nScTab = 0; nScTab < mnScCnt; ++nScTab ) + if( IsSelectedTab( nScTab ) ) + ++mnXclSelCnt; + + // --- calculate resulting Excel sheet indexes --- + + CalcXclIndexes(); + mnFirstVisXclTab = GetXclTab( nFirstVisScTab ); + mnDisplXclTab = GetXclTab( nDisplScTab ); + + // --- sorted vectors for index lookup --- + + CalcSortedIndexes(); +} + +bool XclExpTabInfo::IsExportTab( SCTAB nScTab ) const +{ + /* Check sheet index before to avoid assertion in GetFlag(). */ + return (nScTab < mnScCnt) && !GetFlag( nScTab, EXC_TABBUF_SKIPMASK ); +} + +bool XclExpTabInfo::IsExternalTab( SCTAB nScTab ) const +{ + /* Check sheet index before to avoid assertion (called from formula + compiler also for deleted references). */ + return (nScTab < mnScCnt) && GetFlag( nScTab, EXC_TABBUF_EXTERN ); +} + +bool XclExpTabInfo::IsVisibleTab( SCTAB nScTab ) const +{ + return GetFlag( nScTab, EXC_TABBUF_VISIBLE ); +} + +bool XclExpTabInfo::IsSelectedTab( SCTAB nScTab ) const +{ + return GetFlag( nScTab, EXC_TABBUF_SELECTED ); +} + +bool XclExpTabInfo::IsDisplayedTab( SCTAB nScTab ) const +{ + DBG_ASSERT( nScTab < mnScCnt, "XclExpTabInfo::IsActiveTab - sheet out of range" ); + return GetXclTab( nScTab ) == mnDisplXclTab; +} + +bool XclExpTabInfo::IsMirroredTab( SCTAB nScTab ) const +{ + return GetFlag( nScTab, EXC_TABBUF_MIRRORED ); +} + +const String& XclExpTabInfo::GetScTabName( SCTAB nScTab ) const +{ + DBG_ASSERT( nScTab < mnScCnt, "XclExpTabInfo::IsActiveTab - sheet out of range" ); + return (nScTab < mnScCnt) ? maTabInfoVec[ nScTab ].maScName : EMPTY_STRING; +} + +sal_uInt16 XclExpTabInfo::GetXclTab( SCTAB nScTab ) const +{ + return (nScTab < mnScCnt) ? maTabInfoVec[ nScTab ].mnXclTab : EXC_TAB_DELETED; +} + +SCTAB XclExpTabInfo::GetRealScTab( SCTAB nSortedScTab ) const +{ + DBG_ASSERT( nSortedScTab < mnScCnt, "XclExpTabInfo::GetRealScTab - sheet out of range" ); + return (nSortedScTab < mnScCnt) ? maFromSortedVec[ nSortedScTab ] : SCTAB_INVALID; +} + +//UNUSED2009-05 SCTAB XclExpTabInfo::GetSortedScTab( SCTAB nScTab ) const +//UNUSED2009-05 { +//UNUSED2009-05 DBG_ASSERT( nScTab < mnScCnt, "XclExpTabInfo::GetSortedScTab - sheet out of range" ); +//UNUSED2009-05 return (nScTab < mnScCnt) ? maToSortedVec[ nScTab ] : SCTAB_INVALID; +//UNUSED2009-05 } + +bool XclExpTabInfo::GetFlag( SCTAB nScTab, sal_uInt8 nFlags ) const +{ + DBG_ASSERT( nScTab < mnScCnt, "XclExpTabInfo::GetFlag - sheet out of range" ); + return (nScTab < mnScCnt) && ::get_flag( maTabInfoVec[ nScTab ].mnFlags, nFlags ); +} + +void XclExpTabInfo::SetFlag( SCTAB nScTab, sal_uInt8 nFlags, bool bSet ) +{ + DBG_ASSERT( nScTab < mnScCnt, "XclExpTabInfo::SetFlag - sheet out of range" ); + if( nScTab < mnScCnt ) + ::set_flag( maTabInfoVec[ nScTab ].mnFlags, nFlags, bSet ); +} + +void XclExpTabInfo::CalcXclIndexes() +{ + sal_uInt16 nXclTab = 0; + SCTAB nScTab = 0; + + // --- pass 1: process regular sheets --- + for( nScTab = 0; nScTab < mnScCnt; ++nScTab ) + { + if( IsExportTab( nScTab ) ) + { + maTabInfoVec[ nScTab ].mnXclTab = nXclTab; + ++nXclTab; + } + else + maTabInfoVec[ nScTab ].mnXclTab = EXC_TAB_DELETED; + } + mnXclCnt = nXclTab; + + // --- pass 2: process external sheets (nXclTab continues) --- + for( nScTab = 0; nScTab < mnScCnt; ++nScTab ) + { + if( IsExternalTab( nScTab ) ) + { + maTabInfoVec[ nScTab ].mnXclTab = nXclTab; + ++nXclTab; + ++mnXclExtCnt; + } + } + + // result: first occur all exported sheets, followed by all external sheets +} + +typedef ::std::pair< String, SCTAB > XclExpTabName; +typedef ::std::vector< XclExpTabName > XclExpTabNameVec; + +inline bool operator<( const XclExpTabName& rArg1, const XclExpTabName& rArg2 ) +{ + // compare the sheet names only + return ScGlobal::GetCollator()->compareString( rArg1.first, rArg2.first ) == COMPARE_LESS; +} + +void XclExpTabInfo::CalcSortedIndexes() +{ + ScDocument& rDoc = GetDoc(); + XclExpTabNameVec aVec( mnScCnt ); + SCTAB nScTab; + + // fill with sheet names + for( nScTab = 0; nScTab < mnScCnt; ++nScTab ) + { + rDoc.GetName( nScTab, aVec[ nScTab ].first ); + aVec[ nScTab ].second = nScTab; + } + ::std::sort( aVec.begin(), aVec.end() ); + + // fill index vectors from sorted sheet name vector + maFromSortedVec.resize( mnScCnt ); + maToSortedVec.resize( mnScCnt ); + for( nScTab = 0; nScTab < mnScCnt; ++nScTab ) + { + maFromSortedVec[ nScTab ] = aVec[ nScTab ].second; + maToSortedVec[ aVec[ nScTab ].second ] = nScTab; + } +} + +// External names ============================================================= + +XclExpExtNameBase::XclExpExtNameBase( + const XclExpRoot& rRoot, const String& rName, sal_uInt16 nFlags ) : + XclExpRecord( EXC_ID_EXTERNNAME ), + XclExpRoot( rRoot ), + maName( rName ), + mxName( XclExpStringHelper::CreateString( rRoot, rName, EXC_STR_8BITLENGTH ) ), + mnFlags( nFlags ) +{ + DBG_ASSERT( maName.Len() <= 255, "XclExpExtNameBase::XclExpExtNameBase - string too long" ); + SetRecSize( 6 + mxName->GetSize() ); +} + +XclExpExtNameBase::~XclExpExtNameBase() +{ +} + +void XclExpExtNameBase::WriteBody( XclExpStream& rStrm ) +{ + rStrm << mnFlags + << sal_uInt32( 0 ) + << *mxName; + WriteAddData( rStrm ); +} + +void XclExpExtNameBase::WriteAddData( XclExpStream& /*rStrm*/ ) +{ +} + +// ---------------------------------------------------------------------------- + +XclExpExtNameAddIn::XclExpExtNameAddIn( const XclExpRoot& rRoot, const String& rName ) : + XclExpExtNameBase( rRoot, rName ) +{ + AddRecSize( 4 ); +} + +void XclExpExtNameAddIn::WriteAddData( XclExpStream& rStrm ) +{ + // write a #REF! error formula + rStrm << sal_uInt16( 2 ) << EXC_TOKID_ERR << EXC_ERR_REF; +} + +// ---------------------------------------------------------------------------- + +XclExpExtNameDde::XclExpExtNameDde( const XclExpRoot& rRoot, + const String& rName, sal_uInt16 nFlags, const ScMatrix* pResults ) : + XclExpExtNameBase( rRoot, rName, nFlags ) +{ + if( pResults ) + { + mxMatrix.reset( new XclExpCachedMatrix( *pResults ) ); + AddRecSize( mxMatrix->GetSize() ); + } +} + +void XclExpExtNameDde::WriteAddData( XclExpStream& rStrm ) +{ + if( mxMatrix.is() ) + mxMatrix->Save( rStrm ); +} + +// ---------------------------------------------------------------------------- + +XclExpExtName::XclExpExtName( const XclExpRoot& rRoot, const XclExpSupbook& rSupbook, + const String& rName, const ScExternalRefCache::TokenArrayRef pArray ) : + XclExpExtNameBase( rRoot, rName ), + mrSupbook(rSupbook), + mpArray(pArray->Clone()) +{ +} + +void XclExpExtName::WriteAddData( XclExpStream& rStrm ) +{ + // Write only if it only has a single token that is either a cell or cell + // range address. Excel just writes '02 00 1C 17' for all the other types + // of external names. + + using namespace ::formula; + do + { + if (mpArray->GetLen() != 1) + break; + + const ScToken* p = static_cast<const ScToken*>(mpArray->First()); + if (p->GetOpCode() != ocExternalRef) + break; + + switch (p->GetType()) + { + case svExternalSingleRef: + { + const ScSingleRefData& rRef = p->GetSingleRef(); + if (rRef.IsTabRel()) + break; + + bool bColRel = rRef.IsColRel(); + bool bRowRel = rRef.IsRowRel(); + sal_uInt16 nCol = static_cast< sal_uInt16 >( bColRel ? rRef.nRelCol : rRef.nCol ); + sal_uInt16 nRow = static_cast< sal_uInt16 >( bRowRel ? rRef.nRelRow : rRef.nRow ); + if (bColRel) nCol |= 0x4000; + if (bRowRel) nCol |= 0x8000; + + const String& rTabName = p->GetString(); + sal_uInt16 nSBTab = mrSupbook.GetTabIndex(rTabName); + + // size is always 9 + rStrm << static_cast<sal_uInt16>(9); + // operator token (3A for cell reference) + rStrm << static_cast<sal_uInt8>(0x3A); + // cell address (Excel's address has 2 sheet IDs.) + rStrm << nSBTab << nSBTab << nRow << nCol; + return; + } + case svExternalDoubleRef: + { + const ScComplexRefData& rRef = p->GetDoubleRef(); + const ScSingleRefData& r1 = rRef.Ref1; + const ScSingleRefData& r2 = rRef.Ref2; + if (r1.IsTabRel() || r2.IsTabRel()) + break; + + sal_uInt16 nTab1 = r1.nTab; + sal_uInt16 nTab2 = r2.nTab; + bool bCol1Rel = r1.IsColRel(); + bool bRow1Rel = r1.IsRowRel(); + bool bCol2Rel = r2.IsColRel(); + bool bRow2Rel = r2.IsRowRel(); + + sal_uInt16 nCol1 = static_cast< sal_uInt16 >( bCol1Rel ? r1.nRelCol : r1.nCol ); + sal_uInt16 nCol2 = static_cast< sal_uInt16 >( bCol2Rel ? r2.nRelCol : r2.nCol ); + sal_uInt16 nRow1 = static_cast< sal_uInt16 >( bRow1Rel ? r1.nRelRow : r1.nRow ); + sal_uInt16 nRow2 = static_cast< sal_uInt16 >( bRow2Rel ? r2.nRelRow : r2.nRow ); + if (bCol1Rel) nCol1 |= 0x4000; + if (bRow1Rel) nCol1 |= 0x8000; + if (bCol2Rel) nCol2 |= 0x4000; + if (bRow2Rel) nCol2 |= 0x8000; + + const String& rTabName = p->GetString(); + sal_uInt16 nSBTab = mrSupbook.GetTabIndex(rTabName); + + // size is always 13 (0x0D) + rStrm << static_cast<sal_uInt16>(13); + // operator token (3B for area reference) + rStrm << static_cast<sal_uInt8>(0x3B); + // range (area) address + sal_uInt16 nSBTab2 = nSBTab + nTab2 - nTab1; + rStrm << nSBTab << nSBTab2 << nRow1 << nRow2 << nCol1 << nCol2; + return; + } + default: + ; // nothing + } + } + while (false); + + // special value for #REF! (02 00 1C 17) + rStrm << static_cast<sal_uInt16>(2) << EXC_TOKID_ERR << EXC_ERR_REF; +} + +// List of external names ===================================================== + +XclExpExtNameBuffer::XclExpExtNameBuffer( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ) +{ +} + +sal_uInt16 XclExpExtNameBuffer::InsertAddIn( const String& rName ) +{ + sal_uInt16 nIndex = GetIndex( rName ); + return nIndex ? nIndex : AppendNew( new XclExpExtNameAddIn( GetRoot(), rName ) ); +} + +sal_uInt16 XclExpExtNameBuffer::InsertEuroTool( const String& rName ) +{ + sal_uInt16 nIndex = GetIndex( rName ); + return nIndex ? nIndex : AppendNew( new XclExpExtNameBase( GetRoot(), rName ) ); +} + +sal_uInt16 XclExpExtNameBuffer::InsertDde( + const String& rApplic, const String& rTopic, const String& rItem ) +{ + sal_uInt16 nIndex = GetIndex( rItem ); + if( nIndex == 0 ) + { + USHORT nPos; + if( GetDoc().FindDdeLink( rApplic, rTopic, rItem, SC_DDE_IGNOREMODE, nPos ) ) + { + // create the leading 'StdDocumentName' EXTERNNAME record + if( maNameList.IsEmpty() ) + AppendNew( new XclExpExtNameDde( + GetRoot(), CREATE_STRING( "StdDocumentName" ), EXC_EXTN_EXPDDE_STDDOC ) ); + + // try to find DDE result array, but create EXTERNNAME record without them too + const ScMatrix* pScMatrix = GetDoc().GetDdeLinkResultMatrix( nPos ); + nIndex = AppendNew( new XclExpExtNameDde( GetRoot(), rItem, EXC_EXTN_EXPDDE, pScMatrix ) ); + } + } + return nIndex; +} + +sal_uInt16 XclExpExtNameBuffer::InsertExtName( const XclExpSupbook& rSupbook, + const String& rName, const ScExternalRefCache::TokenArrayRef pArray ) +{ + sal_uInt16 nIndex = GetIndex( rName ); + return nIndex ? nIndex : AppendNew( new XclExpExtName( GetRoot(), rSupbook, rName, pArray ) ); +} + +void XclExpExtNameBuffer::Save( XclExpStream& rStrm ) +{ + maNameList.Save( rStrm ); +} + +sal_uInt16 XclExpExtNameBuffer::GetIndex( const String& rName ) const +{ + for( size_t nPos = 0, nSize = maNameList.GetSize(); nPos < nSize; ++nPos ) + if( maNameList.GetRecord( nPos )->GetName() == rName ) + return static_cast< sal_uInt16 >( nPos + 1 ); + return 0; +} + +sal_uInt16 XclExpExtNameBuffer::AppendNew( XclExpExtNameBase* pExtName ) +{ + XclExpExtNameRef xExtName( pExtName ); + size_t nSize = maNameList.GetSize(); + if( nSize < 0x7FFF ) + { + maNameList.AppendRecord( xExtName ); + return static_cast< sal_uInt16 >( nSize + 1 ); + } + return 0; +} + +// Cached external cells ====================================================== + +XclExpCrn::XclExpCrn( SCCOL nScCol, SCROW nScRow, const Any& rValue ) : + XclExpRecord( EXC_ID_CRN, 4 ), + mnScCol( nScCol ), + mnScRow( nScRow ) +{ + maValues.push_back( rValue ); +} + +bool XclExpCrn::InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue ) +{ + if( (nScRow != mnScRow) || (nScCol != static_cast< SCCOL >( mnScCol + maValues.size() )) ) + return false; + maValues.push_back( rValue ); + return true; +} + +void XclExpCrn::WriteBody( XclExpStream& rStrm ) +{ + rStrm << static_cast< sal_uInt8 >( mnScCol + maValues.size() - 1 ) + << static_cast< sal_uInt8 >( mnScCol ) + << static_cast< sal_uInt16 >( mnScRow ); + for( CachedValues::iterator aIt = maValues.begin(), aEnd = maValues.end(); aIt != aEnd; ++aIt ) + { + if( aIt->has< bool >() ) + WriteBool( rStrm, aIt->get< bool >() ); + else if( aIt->has< double >() ) + WriteDouble( rStrm, aIt->get< double >() ); + else if( aIt->has< OUString >() ) + WriteString( rStrm, aIt->get< OUString >() ); + else + WriteEmpty( rStrm ); + } +} + +void XclExpCrn::WriteBool( XclExpStream& rStrm, bool bValue ) +{ + rStrm << EXC_CACHEDVAL_BOOL << sal_uInt8( bValue ? 1 : 0); + rStrm.WriteZeroBytes( 7 ); +} + +void XclExpCrn::WriteDouble( XclExpStream& rStrm, double fValue ) +{ + if( ::rtl::math::isNan( fValue ) ) + { + USHORT nScError = static_cast< USHORT >( reinterpret_cast< const sal_math_Double* >( &fValue )->nan_parts.fraction_lo ); + WriteError( rStrm, XclTools::GetXclErrorCode( nScError ) ); + } + else + { + rStrm << EXC_CACHEDVAL_DOUBLE << fValue; + } +} + +void XclExpCrn::WriteString( XclExpStream& rStrm, const OUString& rValue ) +{ + rStrm << EXC_CACHEDVAL_STRING << XclExpString( rValue ); +} + +void XclExpCrn::WriteError( XclExpStream& rStrm, sal_uInt8 nErrCode ) +{ + rStrm << EXC_CACHEDVAL_ERROR << nErrCode; + rStrm.WriteZeroBytes( 7 ); +} + +void XclExpCrn::WriteEmpty( XclExpStream& rStrm ) +{ + rStrm << EXC_CACHEDVAL_EMPTY; + rStrm.WriteZeroBytes( 8 ); +} + +// Cached cells of a sheet ==================================================== + +XclExpXct::XclExpXct( const XclExpRoot& rRoot, const String& rTabName, + sal_uInt16 nSBTab, ScExternalRefCache::TableTypeRef xCacheTable ) : + XclExpRoot( rRoot ), + mxCacheTable( xCacheTable ), + maBoundRange( ScAddress::INITIALIZE_INVALID ), + maTabName( rTabName ), + mnSBTab( nSBTab ) +{ +} + +void XclExpXct::StoreCellRange( const ScRange& rRange ) +{ + // #i70418# restrict size of external range to prevent memory overflow + if( (rRange.aEnd.Col() - rRange.aStart.Col()) * (rRange.aEnd.Row() - rRange.aStart.Row()) > 1024 ) + return; + + maUsedCells.SetMultiMarkArea( rRange ); + maBoundRange.ExtendTo( rRange ); +} + +void XclExpXct::StoreCell( const ScAddress& rCell, const ::formula::FormulaToken& rToken ) +{ + maUsedCells.SetMultiMarkArea( ScRange( rCell ) ); + maBoundRange.ExtendTo( ScRange( rCell ) ); + (void)rToken; +} + +void XclExpXct::StoreCellRange( const ScRange& rRange, const ::formula::FormulaToken& rToken ) +{ + maUsedCells.SetMultiMarkArea( rRange ); + maBoundRange.ExtendTo( rRange ); + (void)rToken; +} + +namespace { + +class XclExpCrnList : public XclExpRecordList< XclExpCrn > +{ +public: + /** Inserts the passed value into an existing or new CRN record. + @return True = value inserted successfully, false = CRN list is full. */ + bool InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue ); +}; + +bool XclExpCrnList::InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue ) +{ + RecordRefType xLastRec = GetLastRecord(); + if( xLastRec.is() && xLastRec->InsertValue( nScCol, nScRow, rValue ) ) + return true; + if( GetSize() == SAL_MAX_UINT16 ) + return false; + AppendNewRecord( new XclExpCrn( nScCol, nScRow, rValue ) ); + return true; +} + +} // namespace + +void XclExpXct::Save( XclExpStream& rStrm ) +{ + if( !mxCacheTable ) + return; + + /* Get the range of used rows in the cache table. This may help to + optimize building the CRN record list if the cache table does not + contain all referred cells, e.g. if big empty ranges are used in the + formulas. */ + ::std::pair< SCROW, SCROW > aRowRange = mxCacheTable->getRowRange(); + if( aRowRange.first >= aRowRange.second ) + return; + + /* Crop the bounding range of used cells in this table to Excel limits. + Return if there is no external cell inside these limits. */ + if( !GetAddressConverter().ValidateRange( maBoundRange, false ) ) + return; + + /* Find the resulting row range that needs to be processed. */ + SCROW nScRow1 = ::std::max( aRowRange.first, maBoundRange.aStart.Row() ); + SCROW nScRow2 = ::std::min( aRowRange.second - 1, maBoundRange.aEnd.Row() ); + if( nScRow1 > nScRow2 ) + return; + + /* Build and collect all CRN records before writing the XCT record. This + is needed to determine the total number of CRN records which must be + known when writing the XCT record (possibly encrypted, so seeking the + output strem back after writing the CRN records is not an option). */ + XclExpCrnList aCrnRecs; + SvNumberFormatter& rFormatter = GetFormatter(); + bool bValid = true; + for( SCROW nScRow = nScRow1; bValid && (nScRow <= nScRow2); ++nScRow ) + { + ::std::pair< SCCOL, SCCOL > aColRange = mxCacheTable->getColRange( nScRow ); + for( SCCOL nScCol = aColRange.first; bValid && (nScCol < aColRange.second); ++nScCol ) + { + if( maUsedCells.IsCellMarked( nScCol, nScRow, TRUE ) ) + { + sal_uInt32 nScNumFmt = 0; + ScExternalRefCache::TokenRef xToken = mxCacheTable->getCell( nScCol, nScRow, &nScNumFmt ); + using namespace ::formula; + if( xToken.get() ) switch( xToken->GetType() ) + { + case svDouble: + bValid = (rFormatter.GetType( nScNumFmt ) == NUMBERFORMAT_LOGICAL) ? + aCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetDouble() != 0 ) ) : + aCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetDouble() ) ); + break; + case svString: + // do not save empty strings (empty cells) to cache + if( xToken->GetString().Len() > 0 ) + bValid = aCrnRecs.InsertValue( nScCol, nScRow, Any( OUString( xToken->GetString() ) ) ); + break; + default: + break; + } + } + } + } + + // write the XCT record and the list of CRN records + rStrm.StartRecord( EXC_ID_XCT, 4 ); + rStrm << static_cast< sal_uInt16 >( aCrnRecs.GetSize() ) << mnSBTab; + rStrm.EndRecord(); + aCrnRecs.Save( rStrm ); +} + +// External documents (EXTERNSHEET/SUPBOOK), base class ======================= + +XclExpExternSheetBase::XclExpExternSheetBase( const XclExpRoot& rRoot, sal_uInt16 nRecId, sal_uInt32 nRecSize ) : + XclExpRecord( nRecId, nRecSize ), + XclExpRoot( rRoot ) +{ +} + +XclExpExtNameBuffer& XclExpExternSheetBase::GetExtNameBuffer() +{ + if( !mxExtNameBfr ) + mxExtNameBfr.reset( new XclExpExtNameBuffer( GetRoot() ) ); + return *mxExtNameBfr; +} + +void XclExpExternSheetBase::WriteExtNameBuffer( XclExpStream& rStrm ) +{ + if( mxExtNameBfr.is() ) + mxExtNameBfr->Save( rStrm ); +} + +// External documents (EXTERNSHEET, BIFF5/BIFF7) ============================== + +XclExpExternSheet::XclExpExternSheet( const XclExpRoot& rRoot, sal_Unicode cCode ) : + XclExpExternSheetBase( rRoot, EXC_ID_EXTERNSHEET ) +{ + Init( String( cCode ) ); +} + +XclExpExternSheet::XclExpExternSheet( const XclExpRoot& rRoot, const String& rTabName ) : + XclExpExternSheetBase( rRoot, EXC_ID_EXTERNSHEET ) +{ + // reference to own sheet: \03<sheetname> + Init( String( EXC_EXTSH_TABNAME ).Append( rTabName ) ); +} + +void XclExpExternSheet::Save( XclExpStream& rStrm ) +{ + // EXTERNSHEET record + XclExpRecord::Save( rStrm ); + // EXTERNNAME records + WriteExtNameBuffer( rStrm ); +} + +void XclExpExternSheet::Init( const String& rEncUrl ) +{ + DBG_ASSERT_BIFF( GetBiff() <= EXC_BIFF5 ); + maTabName.AssignByte( rEncUrl, GetTextEncoding(), EXC_STR_8BITLENGTH ); + SetRecSize( maTabName.GetSize() ); +} + +sal_uInt16 XclExpExternSheet::InsertAddIn( const String& rName ) +{ + return GetExtNameBuffer().InsertAddIn( rName ); +} + +void XclExpExternSheet::WriteBody( XclExpStream& rStrm ) +{ + sal_uInt8 nNameSize = static_cast< sal_uInt8 >( maTabName.Len() ); + // special case: reference to own sheet (starting with '\03') needs wrong string length + if( maTabName.GetChar( 0 ) == EXC_EXTSH_TABNAME ) + --nNameSize; + rStrm << nNameSize; + maTabName.WriteBuffer( rStrm ); +} + +// External document (SUPBOOK, BIFF8) ========================================= + +XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, sal_uInt16 nXclTabCount ) : + XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ), + meType( EXC_SBTYPE_SELF ), + mnXclTabCount( nXclTabCount ) +{ +} + +XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot ) : + XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ), + meType( EXC_SBTYPE_ADDIN ), + mnXclTabCount( 1 ) +{ +} + +XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const String& rUrl, XclSupbookType ) : + XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK ), + maUrl( rUrl ), + maUrlEncoded( rUrl ), + meType( EXC_SBTYPE_EUROTOOL ), + mnXclTabCount( 0 ) +{ + SetRecSize( 2 + maUrlEncoded.GetSize() ); +} + + +XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const String& rUrl ) : + XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK ), + maUrl( rUrl ), + maUrlEncoded( XclExpUrlHelper::EncodeUrl( rRoot, rUrl ) ), + meType( EXC_SBTYPE_EXTERN ), + mnXclTabCount( 0 ) +{ + SetRecSize( 2 + maUrlEncoded.GetSize() ); + + // We need to create all tables up front to ensure the correct table order. + ScExternalRefManager* pRefMgr = rRoot.GetDoc().GetExternalRefManager(); + sal_uInt16 nFileId = pRefMgr->getExternalFileId( rUrl ); + ScfStringVec aTabNames; + pRefMgr->getAllCachedTableNames( nFileId, aTabNames ); + for( ScfStringVec::const_iterator aBeg = aTabNames.begin(), aIt = aBeg, aEnd = aTabNames.end(); aIt != aEnd; ++aIt ) + InsertTabName( *aIt, pRefMgr->getCacheTable( nFileId, aIt - aBeg ) ); +} + +XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const String& rApplic, const String& rTopic ) : + XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ), + maUrl( rApplic ), + maDdeTopic( rTopic ), + maUrlEncoded( XclExpUrlHelper::EncodeDde( rApplic, rTopic ) ), + meType( EXC_SBTYPE_SPECIAL ), + mnXclTabCount( 0 ) +{ + SetRecSize( 2 + maUrlEncoded.GetSize() ); +} + +bool XclExpSupbook::IsUrlLink( const String& rUrl ) const +{ + return (meType == EXC_SBTYPE_EXTERN || meType == EXC_SBTYPE_EUROTOOL) && (maUrl == rUrl); +} + +bool XclExpSupbook::IsDdeLink( const String& rApplic, const String& rTopic ) const +{ + return (meType == EXC_SBTYPE_SPECIAL) && (maUrl == rApplic) && (maDdeTopic == rTopic); +} + +void XclExpSupbook::FillRefLogEntry( XclExpRefLogEntry& rRefLogEntry, + sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) const +{ + rRefLogEntry.mpUrl = maUrlEncoded.IsEmpty() ? 0 : &maUrlEncoded; + rRefLogEntry.mpFirstTab = GetTabName( nFirstSBTab ); + rRefLogEntry.mpLastTab = GetTabName( nLastSBTab ); +} + +void XclExpSupbook::StoreCellRange( const ScRange& rRange, sal_uInt16 nSBTab ) +{ + if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ).get() ) + pXct->StoreCellRange( rRange ); +} + +void XclExpSupbook::StoreCell( sal_uInt16 nSBTab, const ScAddress& rCell, const formula::FormulaToken& rToken ) +{ + if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ).get() ) + pXct->StoreCell( rCell, rToken ); +} + +void XclExpSupbook::StoreCellRange( sal_uInt16 nSBTab, const ScRange& rRange, const formula::FormulaToken& rToken ) +{ + // multi-table range is not allowed! + if( rRange.aStart.Tab() == rRange.aEnd.Tab() ) + if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ).get() ) + pXct->StoreCellRange( rRange, rToken ); +} + +sal_uInt16 XclExpSupbook::GetTabIndex( const String& rTabName ) const +{ + XclExpString aXclName(rTabName); + size_t nSize = maXctList.GetSize(); + for (size_t i = 0; i < nSize; ++i) + { + XclExpXctRef aRec = maXctList.GetRecord(i); + if (aXclName == aRec->GetTabName()) + return ulimit_cast<sal_uInt16>(i); + } + return EXC_NOTAB; +} + +sal_uInt16 XclExpSupbook::GetTabCount() const +{ + return ulimit_cast<sal_uInt16>(maXctList.GetSize()); +} + +sal_uInt16 XclExpSupbook::InsertTabName( const String& rTabName, ScExternalRefCache::TableTypeRef xCacheTable ) +{ + DBG_ASSERT( meType == EXC_SBTYPE_EXTERN, "XclExpSupbook::InsertTabName - don't insert sheet names here" ); + sal_uInt16 nSBTab = ulimit_cast< sal_uInt16 >( maXctList.GetSize() ); + XclExpXctRef xXct( new XclExpXct( GetRoot(), rTabName, nSBTab, xCacheTable ) ); + AddRecSize( xXct->GetTabName().GetSize() ); + maXctList.AppendRecord( xXct ); + return nSBTab; +} + +sal_uInt16 XclExpSupbook::InsertAddIn( const String& rName ) +{ + return GetExtNameBuffer().InsertAddIn( rName ); +} + +sal_uInt16 XclExpSupbook::InsertEuroTool( const String& rName ) +{ + return GetExtNameBuffer().InsertEuroTool( rName ); +} + +sal_uInt16 XclExpSupbook::InsertDde( const String& rItem ) +{ + return GetExtNameBuffer().InsertDde( maUrl, maDdeTopic, rItem ); +} + +sal_uInt16 XclExpSupbook::InsertExtName( const String& rName, const ScExternalRefCache::TokenArrayRef pArray ) +{ + return GetExtNameBuffer().InsertExtName(*this, rName, pArray); +} + +void XclExpSupbook::Save( XclExpStream& rStrm ) +{ + // SUPBOOK record + XclExpRecord::Save( rStrm ); + // XCT record, CRN records + maXctList.Save( rStrm ); + // EXTERNNAME records + WriteExtNameBuffer( rStrm ); +} + +const XclExpString* XclExpSupbook::GetTabName( sal_uInt16 nSBTab ) const +{ + XclExpXctRef xXct = maXctList.GetRecord( nSBTab ); + return xXct.is() ? &xXct->GetTabName() : 0; +} + +void XclExpSupbook::WriteBody( XclExpStream& rStrm ) +{ + switch( meType ) + { + case EXC_SBTYPE_SELF: + rStrm << mnXclTabCount << EXC_SUPB_SELF; + break; + case EXC_SBTYPE_EXTERN: + case EXC_SBTYPE_SPECIAL: + case EXC_SBTYPE_EUROTOOL: + { + sal_uInt16 nCount = ulimit_cast< sal_uInt16 >( maXctList.GetSize() ); + rStrm << nCount << maUrlEncoded; + + for( size_t nPos = 0, nSize = maXctList.GetSize(); nPos < nSize; ++nPos ) + rStrm << maXctList.GetRecord( nPos )->GetTabName(); + } + break; + case EXC_SBTYPE_ADDIN: + rStrm << mnXclTabCount << EXC_SUPB_ADDIN; + break; + default: + DBG_ERRORFILE( "XclExpSupbook::WriteBody - unknown SUPBOOK type" ); + } +} + +// All SUPBOOKS in a document ================================================= + +XclExpSupbookBuffer::XclExpSupbookBuffer( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mnOwnDocSB( SAL_MAX_UINT16 ), + mnAddInSB( SAL_MAX_UINT16 ) +{ + XclExpTabInfo& rTabInfo = GetTabInfo(); + sal_uInt16 nXclCnt = rTabInfo.GetXclTabCount(); + sal_uInt16 nCodeCnt = static_cast< sal_uInt16 >( GetExtDocOptions().GetCodeNameCount() ); + size_t nCount = nXclCnt + rTabInfo.GetXclExtTabCount(); + + DBG_ASSERT( nCount > 0, "XclExpSupbookBuffer::XclExpSupbookBuffer - no sheets to export" ); + if( nCount ) + { + maSBIndexVec.resize( nCount ); + + // self-ref SUPBOOK first of list + XclExpSupbookRef xSupbook( new XclExpSupbook( GetRoot(), ::std::max( nXclCnt, nCodeCnt ) ) ); + mnOwnDocSB = Append( xSupbook ); + for( sal_uInt16 nXclTab = 0; nXclTab < nXclCnt; ++nXclTab ) + maSBIndexVec[ nXclTab ].Set( mnOwnDocSB, nXclTab ); + } +} + +XclExpXti XclExpSupbookBuffer::GetXti( sal_uInt16 nFirstXclTab, sal_uInt16 nLastXclTab, + XclExpRefLogEntry* pRefLogEntry ) const +{ + XclExpXti aXti; + size_t nSize = maSBIndexVec.size(); + if( (nFirstXclTab < nSize) && (nLastXclTab < nSize) ) + { + // index of the SUPBOOK record + aXti.mnSupbook = maSBIndexVec[ nFirstXclTab ].mnSupbook; + + // all sheets in the same supbook? + bool bSameSB = true; + for( sal_uInt16 nXclTab = nFirstXclTab + 1; bSameSB && (nXclTab <= nLastXclTab); ++nXclTab ) + { + bSameSB = maSBIndexVec[ nXclTab ].mnSupbook == aXti.mnSupbook; + if( !bSameSB ) + nLastXclTab = nXclTab - 1; + } + aXti.mnFirstSBTab = maSBIndexVec[ nFirstXclTab ].mnSBTab; + aXti.mnLastSBTab = maSBIndexVec[ nLastXclTab ].mnSBTab; + + // fill external reference log entry (for change tracking) + if( pRefLogEntry ) + { + pRefLogEntry->mnFirstXclTab = nFirstXclTab; + pRefLogEntry->mnLastXclTab = nLastXclTab; + XclExpSupbookRef xSupbook = maSupbookList.GetRecord( aXti.mnSupbook ); + if( xSupbook.is() ) + xSupbook->FillRefLogEntry( *pRefLogEntry, aXti.mnFirstSBTab, aXti.mnLastSBTab ); + } + } + else + { + // special range, i.e. for deleted sheets or add-ins + aXti.mnSupbook = mnOwnDocSB; + aXti.mnFirstSBTab = nFirstXclTab; + aXti.mnLastSBTab = nLastXclTab; + } + + return aXti; +} + +void XclExpSupbookBuffer::StoreCellRange( const ScRange& rRange ) +{ + sal_uInt16 nXclTab = GetTabInfo().GetXclTab( rRange.aStart.Tab() ); + if( nXclTab < maSBIndexVec.size() ) + { + const XclExpSBIndex& rSBIndex = maSBIndexVec[ nXclTab ]; + XclExpSupbookRef xSupbook = maSupbookList.GetRecord( rSBIndex.mnSupbook ); + DBG_ASSERT( xSupbook.is(), "XclExpSupbookBuffer::StoreCellRange - missing SUPBOOK record" ); + if( xSupbook.is() ) + xSupbook->StoreCellRange( rRange, rSBIndex.mnSBTab ); + } +} + +namespace { + +class FindSBIndexEntry +{ +public: + explicit FindSBIndexEntry(sal_uInt16 nSupbookId, sal_uInt16 nTabId) : + mnSupbookId(nSupbookId), mnTabId(nTabId) {} + + bool operator()(const XclExpSupbookBuffer::XclExpSBIndex& r) const + { + return mnSupbookId == r.mnSupbook && mnTabId == r.mnSBTab; + } + +private: + sal_uInt16 mnSupbookId; + sal_uInt16 mnTabId; +}; + +} + +void XclExpSupbookBuffer::StoreCell( sal_uInt16 nFileId, const String& rTabName, const ScAddress& rCell ) +{ + ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager(); + const String* pUrl = pRefMgr->getExternalFileName(nFileId); + if (!pUrl) + return; + + XclExpSupbookRef xSupbook; + sal_uInt16 nSupbookId; + if (!GetSupbookUrl(xSupbook, nSupbookId, *pUrl)) + { + xSupbook.reset(new XclExpSupbook(GetRoot(), *pUrl)); + nSupbookId = Append(xSupbook); + } + + ScExternalRefCache::TokenRef pToken = pRefMgr->getSingleRefToken(nFileId, rTabName, rCell, NULL, NULL); + if (!pToken.get()) + return; + + sal_uInt16 nSheetId = xSupbook->GetTabIndex(rTabName); + if (nSheetId == EXC_NOTAB) + // specified table name not found in this SUPBOOK. + return; + + FindSBIndexEntry f(nSupbookId, nSheetId); + XclExpSBIndexVec::iterator itrEnd = maSBIndexVec.end(); + XclExpSBIndexVec::const_iterator itr = find_if(maSBIndexVec.begin(), itrEnd, f); + if (itr == itrEnd) + { + maSBIndexVec.push_back(XclExpSBIndex()); + XclExpSBIndex& r = maSBIndexVec.back(); + r.mnSupbook = nSupbookId; + r.mnSBTab = nSheetId; + } + + xSupbook->StoreCell(nSheetId, rCell, *pToken); +} + +void XclExpSupbookBuffer::StoreCellRange( sal_uInt16 nFileId, const String& rTabName, const ScRange& rRange ) +{ + ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager(); + const String* pUrl = pRefMgr->getExternalFileName(nFileId); + if (!pUrl) + return; + + XclExpSupbookRef xSupbook; + sal_uInt16 nSupbookId; + if (!GetSupbookUrl(xSupbook, nSupbookId, *pUrl)) + { + xSupbook.reset(new XclExpSupbook(GetRoot(), *pUrl)); + nSupbookId = Append(xSupbook); + } + + SCTAB nTabCount = rRange.aEnd.Tab() - rRange.aStart.Tab() + 1; + + // If this is a multi-table range, get token for each table. + using namespace ::formula; + vector<FormulaToken*> aMatrixList; + aMatrixList.reserve(nTabCount); + + // This is a new'ed instance, so we must manage its life cycle here. + ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(nFileId, rTabName, rRange, NULL); + if (!pArray.get()) + return; + + for (FormulaToken* p = pArray->First(); p; p = pArray->Next()) + { + if (p->GetType() == svMatrix) + aMatrixList.push_back(p); + else if (p->GetOpCode() != ocSep) + { + // This is supposed to be ocSep!!! + return; + } + } + + if (aMatrixList.size() != static_cast<size_t>(nTabCount)) + { + // matrix size mis-match ! + return; + } + + sal_uInt16 nFirstSheetId = xSupbook->GetTabIndex(rTabName); + + ScRange aRange(rRange); + aRange.aStart.SetTab(0); + aRange.aEnd.SetTab(0); + for (SCTAB nTab = 0; nTab < nTabCount; ++nTab) + { + sal_uInt16 nSheetId = nFirstSheetId + static_cast<sal_uInt16>(nTab); + FindSBIndexEntry f(nSupbookId, nSheetId); + XclExpSBIndexVec::iterator itrEnd = maSBIndexVec.end(); + XclExpSBIndexVec::const_iterator itr = find_if(maSBIndexVec.begin(), itrEnd, f); + if (itr == itrEnd) + { + maSBIndexVec.push_back(XclExpSBIndex()); + XclExpSBIndex& r = maSBIndexVec.back(); + r.mnSupbook = nSupbookId; + r.mnSBTab = nSheetId; + } + + xSupbook->StoreCellRange(nSheetId, aRange, *aMatrixList[nTab]); + } +} + +bool XclExpSupbookBuffer::InsertAddIn( + sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const String& rName ) +{ + XclExpSupbookRef xSupbook; + if( mnAddInSB == SAL_MAX_UINT16 ) + { + xSupbook.reset( new XclExpSupbook( GetRoot() ) ); + mnAddInSB = Append( xSupbook ); + } + else + xSupbook = maSupbookList.GetRecord( mnAddInSB ); + DBG_ASSERT( xSupbook.is(), "XclExpSupbookBuffer::InsertAddin - missing add-in supbook" ); + rnSupbook = mnAddInSB; + rnExtName = xSupbook->InsertAddIn( rName ); + return rnExtName > 0; +} + +bool XclExpSupbookBuffer::InsertEuroTool( + sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const String& rName ) +{ + XclExpSupbookRef xSupbook; + String aUrl( RTL_CONSTASCII_USTRINGPARAM("\001\010EUROTOOL.XLA")); + if( !GetSupbookUrl( xSupbook, rnSupbook, aUrl ) ) + { + xSupbook.reset( new XclExpSupbook( GetRoot(), aUrl, EXC_SBTYPE_EUROTOOL ) ); + rnSupbook = Append( xSupbook ); + } + rnExtName = xSupbook->InsertEuroTool( rName ); + return rnExtName > 0; +} + +bool XclExpSupbookBuffer::InsertDde( + sal_uInt16& rnSupbook, sal_uInt16& rnExtName, + const String& rApplic, const String& rTopic, const String& rItem ) +{ + XclExpSupbookRef xSupbook; + if( !GetSupbookDde( xSupbook, rnSupbook, rApplic, rTopic ) ) + { + xSupbook.reset( new XclExpSupbook( GetRoot(), rApplic, rTopic ) ); + rnSupbook = Append( xSupbook ); + } + rnExtName = xSupbook->InsertDde( rItem ); + return rnExtName > 0; +} + +bool XclExpSupbookBuffer::InsertExtName( + sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const String& rUrl, + const String& rName, const ScExternalRefCache::TokenArrayRef pArray ) +{ + XclExpSupbookRef xSupbook; + if (!GetSupbookUrl(xSupbook, rnSupbook, rUrl)) + { + xSupbook.reset( new XclExpSupbook(GetRoot(), rUrl) ); + rnSupbook = Append(xSupbook); + } + rnExtName = xSupbook->InsertExtName(rName, pArray); + return rnExtName > 0; +} + +XclExpXti XclExpSupbookBuffer::GetXti( sal_uInt16 nFileId, const String& rTabName, sal_uInt16 nXclTabSpan, + XclExpRefLogEntry* pRefLogEntry ) +{ + XclExpXti aXti(0, EXC_NOTAB, EXC_NOTAB); + ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager(); + const String* pUrl = pRefMgr->getExternalFileName(nFileId); + if (!pUrl) + return aXti; + + XclExpSupbookRef xSupbook; + sal_uInt16 nSupbookId; + if (!GetSupbookUrl(xSupbook, nSupbookId, *pUrl)) + { + xSupbook.reset(new XclExpSupbook(GetRoot(), *pUrl)); + nSupbookId = Append(xSupbook); + } + aXti.mnSupbook = nSupbookId; + + sal_uInt16 nFirstSheetId = xSupbook->GetTabIndex(rTabName); + if (nFirstSheetId == EXC_NOTAB) + { + // first sheet not found in SUPBOOK. + return aXti; + } + sal_uInt16 nSheetCount = xSupbook->GetTabCount(); + for (sal_uInt16 i = 0; i < nXclTabSpan; ++i) + { + sal_uInt16 nSheetId = nFirstSheetId + i; + if (nSheetId >= nSheetCount) + return aXti; + + FindSBIndexEntry f(nSupbookId, nSheetId); + XclExpSBIndexVec::iterator itrEnd = maSBIndexVec.end(); + XclExpSBIndexVec::const_iterator itr = find_if(maSBIndexVec.begin(), itrEnd, f); + if (itr == itrEnd) + { + maSBIndexVec.push_back(XclExpSBIndex()); + XclExpSBIndex& r = maSBIndexVec.back(); + r.mnSupbook = nSupbookId; + r.mnSBTab = nSheetId; + } + if (i == 0) + aXti.mnFirstSBTab = nSheetId; + if (i == nXclTabSpan - 1) + aXti.mnLastSBTab = nSheetId; + } + + if (pRefLogEntry) + { + pRefLogEntry->mnFirstXclTab = 0; + pRefLogEntry->mnLastXclTab = 0; + if (xSupbook.is()) + xSupbook->FillRefLogEntry(*pRefLogEntry, aXti.mnFirstSBTab, aXti.mnLastSBTab); + } + + return aXti; +} + +void XclExpSupbookBuffer::Save( XclExpStream& rStrm ) +{ + maSupbookList.Save( rStrm ); +} + +bool XclExpSupbookBuffer::GetSupbookUrl( + XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex, const String& rUrl ) const +{ + for( size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos ) + { + rxSupbook = maSupbookList.GetRecord( nPos ); + if( rxSupbook->IsUrlLink( rUrl ) ) + { + rnIndex = ulimit_cast< sal_uInt16 >( nPos ); + return true; + } + } + return false; +} + +bool XclExpSupbookBuffer::GetSupbookDde( XclExpSupbookRef& rxSupbook, + sal_uInt16& rnIndex, const String& rApplic, const String& rTopic ) const +{ + for( size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos ) + { + rxSupbook = maSupbookList.GetRecord( nPos ); + if( rxSupbook->IsDdeLink( rApplic, rTopic ) ) + { + rnIndex = ulimit_cast< sal_uInt16 >( nPos ); + return true; + } + } + return false; +} + +sal_uInt16 XclExpSupbookBuffer::Append( XclExpSupbookRef xSupbook ) +{ + maSupbookList.AppendRecord( xSupbook ); + return ulimit_cast< sal_uInt16 >( maSupbookList.GetSize() - 1 ); +} + +// Export link manager ======================================================== + +XclExpLinkManagerImpl::XclExpLinkManagerImpl( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ) +{ +} + +// ---------------------------------------------------------------------------- + +XclExpLinkManagerImpl5::XclExpLinkManagerImpl5( const XclExpRoot& rRoot ) : + XclExpLinkManagerImpl( rRoot ) +{ +} + +void XclExpLinkManagerImpl5::FindExtSheet( + sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab, + SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry ) +{ + FindInternal( rnExtSheet, rnFirstXclTab, nFirstScTab ); + if( (rnFirstXclTab == EXC_TAB_DELETED) || (nFirstScTab == nLastScTab) ) + { + rnLastXclTab = rnFirstXclTab; + } + else + { + sal_uInt16 nDummyExtSheet; + FindInternal( nDummyExtSheet, rnLastXclTab, nLastScTab ); + } + + (void)pRefLogEntry; // avoid compiler warning + DBG_ASSERT( !pRefLogEntry, "XclExpLinkManagerImpl5::FindExtSheet - fill reflog entry not implemented" ); +} + +sal_uInt16 XclExpLinkManagerImpl5::FindExtSheet( sal_Unicode cCode ) +{ + sal_uInt16 nExtSheet; + FindInternal( nExtSheet, cCode ); + return nExtSheet; +} + +void XclExpLinkManagerImpl5::FindExtSheet( + sal_uInt16 /*nFileId*/, const String& /*rTabName*/, sal_uInt16 /*nXclTabSpan*/, + sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnFirstSBTab*/, sal_uInt16& /*rnLastSBTab*/, + XclExpRefLogEntry* /*pRefLogEntry*/ ) +{ + // not implemented +} + +void XclExpLinkManagerImpl5::StoreCellRange( const ScSingleRefData& /*rRef1*/, const ScSingleRefData& /*rRef2*/ ) +{ + // not implemented +} + +void XclExpLinkManagerImpl5::StoreCell( sal_uInt16 /*nFileId*/, const String& /*rTabName*/, const ScSingleRefData& /*rRef*/ ) +{ + // not implemented +} + +void XclExpLinkManagerImpl5::StoreCellRange( sal_uInt16 /*nFileId*/, const String& /*rTabName*/, const ScSingleRefData& /*rRef1*/, const ScSingleRefData& /*rRef2*/ ) +{ + // not implemented +} + +bool XclExpLinkManagerImpl5::InsertAddIn( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const String& rName ) +{ + XclExpExtSheetRef xExtSheet = FindInternal( rnExtSheet, EXC_EXTSH_ADDIN ); + if( xExtSheet.is() ) + { + rnExtName = xExtSheet->InsertAddIn( rName ); + return rnExtName > 0; + } + return false; +} + +bool XclExpLinkManagerImpl5::InsertEuroTool( + sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnExtName*/, const String& /*rName*/ ) +{ + return false; +} + + +bool XclExpLinkManagerImpl5::InsertDde( + sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnExtName*/, + const String& /*rApplic*/, const String& /*rTopic*/, const String& /*rItem*/ ) +{ + // not implemented + return false; +} + +bool XclExpLinkManagerImpl5::InsertExtName( + sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnExtName*/, const String& /*rUrl*/, + const String& /*rName*/, const ScExternalRefCache::TokenArrayRef /*pArray*/ ) +{ + // not implemented + return false; +} + +void XclExpLinkManagerImpl5::Save( XclExpStream& rStrm ) +{ + if( sal_uInt16 nExtSheetCount = GetExtSheetCount() ) + { + // EXTERNCOUNT record + XclExpUInt16Record( EXC_ID_EXTERNCOUNT, nExtSheetCount ).Save( rStrm ); + // list of EXTERNSHEET records with EXTERNNAME, XCT, CRN records + maExtSheetList.Save( rStrm ); + } +} + +sal_uInt16 XclExpLinkManagerImpl5::GetExtSheetCount() const +{ + return static_cast< sal_uInt16 >( maExtSheetList.GetSize() ); +} + +sal_uInt16 XclExpLinkManagerImpl5::AppendInternal( XclExpExtSheetRef xExtSheet ) +{ + if( GetExtSheetCount() < 0x7FFF ) + { + maExtSheetList.AppendRecord( xExtSheet ); + // return negated one-based EXTERNSHEET index (i.e. 0xFFFD for 3rd record) + return static_cast< sal_uInt16 >( -GetExtSheetCount() ); + } + return 0; +} + +void XclExpLinkManagerImpl5::CreateInternal() +{ + if( maIntTabMap.empty() ) + { + // create EXTERNSHEET records for all internal exported sheets + XclExpTabInfo& rTabInfo = GetTabInfo(); + for( SCTAB nScTab = 0, nScCnt = rTabInfo.GetScTabCount(); nScTab < nScCnt; ++nScTab ) + { + if( rTabInfo.IsExportTab( nScTab ) ) + { + XclExpExtSheetRef xRec; + if( nScTab == GetCurrScTab() ) + xRec.reset( new XclExpExternSheet( GetRoot(), EXC_EXTSH_OWNTAB ) ); + else + xRec.reset( new XclExpExternSheet( GetRoot(), rTabInfo.GetScTabName( nScTab ) ) ); + maIntTabMap[ nScTab ] = AppendInternal( xRec ); + } + } + } +} + +XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::GetInternal( sal_uInt16 nExtSheet ) +{ + return maExtSheetList.GetRecord( static_cast< sal_uInt16 >( -nExtSheet - 1 ) ); +} + +XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::FindInternal( + sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab, SCTAB nScTab ) +{ + // create internal EXTERNSHEET records on demand + CreateInternal(); + + // try to find an EXTERNSHEET record - if not, return a "deleted sheet" reference + XclExpExtSheetRef xExtSheet; + XclExpIntTabMap::const_iterator aIt = maIntTabMap.find( nScTab ); + if( aIt == maIntTabMap.end() ) + { + xExtSheet = FindInternal( rnExtSheet, EXC_EXTSH_OWNDOC ); + rnXclTab = EXC_TAB_DELETED; + } + else + { + rnExtSheet = aIt->second; + xExtSheet = GetInternal( rnExtSheet ); + rnXclTab = GetTabInfo().GetXclTab( nScTab ); + } + return xExtSheet; +} + +XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::FindInternal( + sal_uInt16& rnExtSheet, sal_Unicode cCode ) +{ + XclExpExtSheetRef xExtSheet; + XclExpCodeMap::const_iterator aIt = maCodeMap.find( cCode ); + if( aIt == maCodeMap.end() ) + { + xExtSheet.reset( new XclExpExternSheet( GetRoot(), cCode ) ); + rnExtSheet = maCodeMap[ cCode ] = AppendInternal( xExtSheet ); + } + else + { + rnExtSheet = aIt->second; + xExtSheet = GetInternal( rnExtSheet ); + } + return xExtSheet; +} + +// ---------------------------------------------------------------------------- + +XclExpLinkManagerImpl8::XclExpLinkManagerImpl8( const XclExpRoot& rRoot ) : + XclExpLinkManagerImpl( rRoot ), + maSBBuffer( rRoot ) +{ +} + +void XclExpLinkManagerImpl8::FindExtSheet( + sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab, + SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry ) +{ + XclExpTabInfo& rTabInfo = GetTabInfo(); + rnFirstXclTab = rTabInfo.GetXclTab( nFirstScTab ); + rnLastXclTab = rTabInfo.GetXclTab( nLastScTab ); + rnExtSheet = InsertXti( maSBBuffer.GetXti( rnFirstXclTab, rnLastXclTab, pRefLogEntry ) ); +} + +sal_uInt16 XclExpLinkManagerImpl8::FindExtSheet( sal_Unicode cCode ) +{ + (void)cCode; // avoid compiler warning + DBG_ASSERT( (cCode == EXC_EXTSH_OWNDOC) || (cCode == EXC_EXTSH_ADDIN), + "XclExpLinkManagerImpl8::FindExtSheet - unknown externsheet code" ); + return InsertXti( maSBBuffer.GetXti( EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) ); +} + +void XclExpLinkManagerImpl8::FindExtSheet( + sal_uInt16 nFileId, const String& rTabName, sal_uInt16 nXclTabSpan, + sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab, + XclExpRefLogEntry* pRefLogEntry ) +{ + XclExpXti aXti = maSBBuffer.GetXti(nFileId, rTabName, nXclTabSpan, pRefLogEntry); + rnExtSheet = InsertXti(aXti); + rnFirstSBTab = aXti.mnFirstSBTab; + rnLastSBTab = aXti.mnLastSBTab; +} + +void XclExpLinkManagerImpl8::StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2 ) +{ + if( !rRef1.IsDeleted() && !rRef2.IsDeleted() && (rRef1.nTab >= 0) && (rRef2.nTab >= 0) ) + { + const XclExpTabInfo& rTabInfo = GetTabInfo(); + SCTAB nFirstScTab = static_cast< SCTAB >( rRef1.nTab ); + SCTAB nLastScTab = static_cast< SCTAB >( rRef2.nTab ); + ScRange aRange( + static_cast< SCCOL >( rRef1.nCol ), static_cast< SCROW >( rRef1.nRow ), 0, + static_cast< SCCOL >( rRef2.nCol ), static_cast< SCROW >( rRef2.nRow ), 0 ); + for( SCTAB nScTab = nFirstScTab; nScTab <= nLastScTab; ++nScTab ) + { + if( rTabInfo.IsExternalTab( nScTab ) ) + { + aRange.aStart.SetTab( nScTab ); + aRange.aEnd.SetTab( nScTab ); + maSBBuffer.StoreCellRange( aRange ); + } + } + } +} + +void XclExpLinkManagerImpl8::StoreCell( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef ) +{ + ScAddress aAddr(rRef.nCol, rRef.nRow, rRef.nTab); + maSBBuffer.StoreCell(nFileId, rTabName, aAddr); +} + +void XclExpLinkManagerImpl8::StoreCellRange( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef1, const ScSingleRefData& rRef2 ) +{ + ScRange aRange(static_cast<SCCOL>(rRef1.nCol), static_cast<SCROW>(rRef1.nRow), static_cast<SCTAB>(rRef1.nTab), + static_cast<SCCOL>(rRef2.nCol), static_cast<SCROW>(rRef2.nRow), static_cast<SCTAB>(rRef2.nTab)); + maSBBuffer.StoreCellRange(nFileId, rTabName, aRange); +} + +bool XclExpLinkManagerImpl8::InsertAddIn( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const String& rName ) +{ + sal_uInt16 nSupbook; + if( maSBBuffer.InsertAddIn( nSupbook, rnExtName, rName ) ) + { + rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) ); + return true; + } + return false; +} + +bool XclExpLinkManagerImpl8::InsertEuroTool( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const String& rName ) +{ + sal_uInt16 nSupbook; + if( maSBBuffer.InsertEuroTool( nSupbook, rnExtName, rName ) ) + { + rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) ); + return true; + } + return false; +} + + +bool XclExpLinkManagerImpl8::InsertDde( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, + const String& rApplic, const String& rTopic, const String& rItem ) +{ + sal_uInt16 nSupbook; + if( maSBBuffer.InsertDde( nSupbook, rnExtName, rApplic, rTopic, rItem ) ) + { + rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) ); + return true; + } + return false; +} + +bool XclExpLinkManagerImpl8::InsertExtName( sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, + const String& rName, const String& rUrl, const ScExternalRefCache::TokenArrayRef pArray ) +{ + sal_uInt16 nSupbook; + if( maSBBuffer.InsertExtName( nSupbook, rnExtName, rUrl, rName, pArray ) ) + { + rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) ); + return true; + } + return false; +} + +void XclExpLinkManagerImpl8::Save( XclExpStream& rStrm ) +{ + if( !maXtiVec.empty() ) + { + // SUPBOOKs, XCTs, CRNs, EXTERNNAMEs + maSBBuffer.Save( rStrm ); + + // EXTERNSHEET + sal_uInt16 nCount = ulimit_cast< sal_uInt16 >( maXtiVec.size() ); + rStrm.StartRecord( EXC_ID_EXTERNSHEET, 2 + 6 * nCount ); + rStrm << nCount; + rStrm.SetSliceSize( 6 ); + for( XclExpXtiVec::const_iterator aIt = maXtiVec.begin(), aEnd = maXtiVec.end(); aIt != aEnd; ++aIt ) + aIt->Save( rStrm ); + rStrm.EndRecord(); + } +} + +sal_uInt16 XclExpLinkManagerImpl8::InsertXti( const XclExpXti& rXti ) +{ + for( XclExpXtiVec::const_iterator aIt = maXtiVec.begin(), aEnd = maXtiVec.end(); aIt != aEnd; ++aIt ) + if( *aIt == rXti ) + return ulimit_cast< sal_uInt16 >( aIt - maXtiVec.begin() ); + maXtiVec.push_back( rXti ); + return ulimit_cast< sal_uInt16 >( maXtiVec.size() - 1 ); +} + +// ============================================================================ + +XclExpLinkManager::XclExpLinkManager( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ) +{ + switch( GetBiff() ) + { + case EXC_BIFF5: + mxImpl.reset( new XclExpLinkManagerImpl5( rRoot ) ); + break; + case EXC_BIFF8: + mxImpl.reset( new XclExpLinkManagerImpl8( rRoot ) ); + break; + default: + DBG_ERROR_BIFF(); + } +} + +XclExpLinkManager::~XclExpLinkManager() +{ +} + +void XclExpLinkManager::FindExtSheet( + sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab, + SCTAB nScTab, XclExpRefLogEntry* pRefLogEntry ) +{ + mxImpl->FindExtSheet( rnExtSheet, rnXclTab, rnXclTab, nScTab, nScTab, pRefLogEntry ); +} + +void XclExpLinkManager::FindExtSheet( + sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab, + SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry ) +{ + mxImpl->FindExtSheet( rnExtSheet, rnFirstXclTab, rnLastXclTab, nFirstScTab, nLastScTab, pRefLogEntry ); +} + +sal_uInt16 XclExpLinkManager::FindExtSheet( sal_Unicode cCode ) +{ + return mxImpl->FindExtSheet( cCode ); +} + +void XclExpLinkManager::FindExtSheet( sal_uInt16 nFileId, const String& rTabName, sal_uInt16 nXclTabSpan, + sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab, + XclExpRefLogEntry* pRefLogEntry ) +{ + mxImpl->FindExtSheet( nFileId, rTabName, nXclTabSpan, rnExtSheet, rnFirstSBTab, rnLastSBTab, pRefLogEntry ); +} + +void XclExpLinkManager::StoreCell( const ScSingleRefData& rRef ) +{ + mxImpl->StoreCellRange( rRef, rRef ); +} + +void XclExpLinkManager::StoreCellRange( const ScComplexRefData& rRef ) +{ + mxImpl->StoreCellRange( rRef.Ref1, rRef.Ref2 ); +} + +void XclExpLinkManager::StoreCell( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef ) +{ + mxImpl->StoreCell( nFileId, rTabName, rRef ); +} + +void XclExpLinkManager::StoreCellRange( sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef ) +{ + mxImpl->StoreCellRange( nFileId, rTabName, rRef.Ref1, rRef.Ref2 ); +} + +bool XclExpLinkManager::InsertAddIn( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const String& rName ) +{ + return mxImpl->InsertAddIn( rnExtSheet, rnExtName, rName ); +} + +bool XclExpLinkManager::InsertEuroTool( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const String& rName ) +{ + return mxImpl->InsertEuroTool( rnExtSheet, rnExtName, rName ); +} + +bool XclExpLinkManager::InsertDde( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, + const String& rApplic, const String& rTopic, const String& rItem ) +{ + return mxImpl->InsertDde( rnExtSheet, rnExtName, rApplic, rTopic, rItem ); +} + +bool XclExpLinkManager::InsertExtName( + sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const String& rName, const String& rUrl, + const ScExternalRefCache::TokenArrayRef pArray ) +{ + return mxImpl->InsertExtName( rnExtSheet, rnExtName, rUrl, rName, pArray ); +} + +void XclExpLinkManager::Save( XclExpStream& rStrm ) +{ + mxImpl->Save( rStrm ); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xename.cxx b/sc/source/filter/excel/xename.cxx new file mode 100644 index 000000000000..af275bef7eef --- /dev/null +++ b/sc/source/filter/excel/xename.cxx @@ -0,0 +1,791 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xename.hxx" + +#include <map> + +#include "globstr.hrc" +#include "document.hxx" +#include "rangenam.hxx" +#include "dbcolect.hxx" +#include "xehelper.hxx" +#include "xelink.hxx" + +// for filter manager +#include "excrecds.hxx" + +#include <oox/core/tokens.hxx> +#include <formula/grammar.hxx> + +using ::rtl::OString; + +// ============================================================================ +// *** Helper classes *** +// ============================================================================ + +/** Represents an internal defined name, supports writing it to a NAME record. */ +class XclExpName : public XclExpRecord, protected XclExpRoot +{ +public: + /** Creates a standard defined name. */ + explicit XclExpName( const XclExpRoot& rRoot, const String& rName ); + /** Creates a built-in defined name. */ + explicit XclExpName( const XclExpRoot& rRoot, sal_Unicode cBuiltIn ); + + /** Sets a token array containing the definition of this name. */ + void SetTokenArray( XclTokenArrayRef xTokArr ); + /** Changes this defined name to be local on the specified Calc sheet. */ + void SetLocalTab( SCTAB nScTab ); + /** Hides or unhides the defined name. */ + void SetHidden( bool bHidden = true ); + /** Changes this name to be the call to a VB macro function or procedure. + @param bVBasic true = Visual Basic macro, false = Sheet macro. + @param bFunc true = Macro function; false = Macro procedure. */ + void SetMacroCall( bool bVBasic, bool bFunc ); + + + /** Sets the name's symbol value + @param sValue the name's symbolic value */ + void SetSymbol( String sValue ); + /** Returns the name's symbol value */ + inline const String& GetSymbol() const { return msSymbol; } + + /** Returns the original name (title) of this defined name. */ + inline const String& GetOrigName() const { return maOrigName; } + /** Returns the Excel built-in name index of this defined name. + @return The built-in name index or EXC_BUILTIN_UNKNOWN for user-defined names. */ + inline sal_Unicode GetBuiltInName() const { return mcBuiltIn; } + + /** Returns the token array for this defined name. */ + inline XclTokenArrayRef GetTokenArray() const { return mxTokArr; } + + /** Returns true, if this is a document-global defined name. */ + inline bool IsGlobal() const { return mnXclTab == EXC_NAME_GLOBAL; } + /** Returns the Calc sheet of a local defined name. */ + inline SCTAB GetScTab() const { return mnScTab; } + + /** Returns true, if this defined name is volatile. */ + bool IsVolatile() const; + /** Returns true, if this defined name is hidden. */ + bool IsHidden() const; + /** Returns true, if this defined name describes a macro call. + @param bFunc true = Macro function; false = Macro procedure. */ + bool IsMacroCall( bool bVBasic, bool bFunc ) const; + + /** Writes the entire NAME record to the passed stream. */ + virtual void Save( XclExpStream& rStrm ); + + virtual void SaveXml( XclExpXmlStream& rStrm ); + +private: + /** Writes the body of the NAME record to the passed stream. */ + virtual void WriteBody( XclExpStream& rStrm ); + +private: + String maOrigName; /// The original user-defined name. + String msSymbol; /// The value of the symbol + XclExpStringRef mxName; /// The name as Excel string object. + XclTokenArrayRef mxTokArr; /// The definition of the defined name. + sal_Unicode mcBuiltIn; /// The built-in index for built-in names. + SCTAB mnScTab; /// The Calc sheet index for local names. + sal_uInt16 mnFlags; /// Additional flags for this defined name. + sal_uInt16 mnExtSheet; /// The 1-based index to a global EXTERNSHEET record. + sal_uInt16 mnXclTab; /// The 1-based Excel sheet index for local names. +}; + +// ---------------------------------------------------------------------------- + +class ScRangeData; +class ScDBData; + +/** Implementation class of the name manager. */ +class XclExpNameManagerImpl : protected XclExpRoot +{ +public: + explicit XclExpNameManagerImpl( const XclExpRoot& rRoot ); + + /** Creates NAME records for built-in and user defined names. */ + void Initialize(); + + /** Inserts the Calc name with the passed index and returns the Excel NAME index. */ + sal_uInt16 InsertName( USHORT nScNameIdx ); + /** Inserts the Calc database range with the passed index and returns the Excel NAME index. */ + sal_uInt16 InsertDBRange( USHORT nScDBRangeIdx ); + + /** Inserts a new built-in defined name. */ + sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, XclTokenArrayRef xTokArr, SCTAB nScTab ); + /** Inserts a new defined name. Sets another unused name, if rName already exists. */ + sal_uInt16 InsertUniqueName( const String& rName, XclTokenArrayRef xTokArr, SCTAB nScTab ); + /** Returns index of an existing name, or creates a name without definition. */ + sal_uInt16 InsertRawName( const String& rName ); + /** Searches or inserts a defined name describing a macro name. + @param bVBasic true = Visual Basic macro; false = Sheet macro. + @param bFunc true = Macro function; false = Macro procedure. */ + sal_uInt16 InsertMacroCall( const String& rMacroName, bool bVBasic, bool bFunc, bool bHidden ); + + /** Returns the NAME record at the specified position or 0 on error. */ + const XclExpName* GetName( sal_uInt16 nNameIdx ) const; + + /** Writes the entire list of NAME records. + @descr In BIFF7 and lower, writes the entire global link table, which + consists of an EXTERNCOUNT record, several EXTERNSHEET records, and + the list of NAME records. */ + void Save( XclExpStream& rStrm ); + + void SaveXml( XclExpXmlStream& rStrm ); + +private: + typedef XclExpRecordList< XclExpName > XclExpNameList; + typedef XclExpNameList::RecordRefType XclExpNameRef; + typedef ::std::map< USHORT, sal_uInt16 > XclExpIndexMap; + +private: + /** Finds the index of a NAME record from the passed Calc index in the specified map. */ + sal_uInt16 FindNameIdx( const XclExpIndexMap& rMap, USHORT nScIdx ) const; + /** Returns the index of an existing built-in NAME record with the passed definition, otherwise 0. */ + sal_uInt16 FindBuiltInNameIdx( const String& rName, + const XclTokenArray& rTokArr, bool bDBRange ) const; + /** Returns an unused name for the passed name. */ + String GetUnusedName( const String& rName ) const; + + /** Appends a new NAME record to the record list. + @return The 1-based NAME record index used elsewhere in the Excel file. */ + sal_uInt16 Append( XclExpNameRef xName ); + /** Creates a new NAME record for the passed user-defined name. + @return The 1-based NAME record index used elsewhere in the Excel file. */ + sal_uInt16 CreateName( const ScRangeData& rRangeData ); + /** Creates a new NAME record for the passed database range. + @return The 1-based NAME record index used elsewhere in the Excel file. */ + sal_uInt16 CreateName( const ScDBData& rDBData ); + + /** Creates NAME records for all built-in names in the document. */ + void CreateBuiltInNames(); + /** Creates NAME records for all user-defined names in the document. */ + void CreateUserNames(); + /** Creates NAME records for all database ranges in the document. */ + void CreateDatabaseNames(); + +private: + XclExpNameList maNameList; /// List of NAME records. + XclExpIndexMap maNameMap; /// Maps Calc defined names to Excel NAME records. + XclExpIndexMap maDBRangeMap; /// Maps Calc database ranges to Excel NAME records. + String maUnnamedDBName; /// Name of the hidden unnamed database range. + size_t mnFirstUserIdx; /// List index of first user-defined NAME record. +}; + +// ============================================================================ +// *** Implementation *** +// ============================================================================ + +XclExpName::XclExpName( const XclExpRoot& rRoot, const String& rName ) : + XclExpRecord( EXC_ID_NAME ), + XclExpRoot( rRoot ), + maOrigName( rName ), + mxName( XclExpStringHelper::CreateString( rRoot, rName, EXC_STR_8BITLENGTH ) ), + mcBuiltIn( EXC_BUILTIN_UNKNOWN ), + mnScTab( SCTAB_GLOBAL ), + mnFlags( EXC_NAME_DEFAULT ), + mnExtSheet( EXC_NAME_GLOBAL ), + mnXclTab( EXC_NAME_GLOBAL ) +{ +} + +XclExpName::XclExpName( const XclExpRoot& rRoot, sal_Unicode cBuiltIn ) : + XclExpRecord( EXC_ID_NAME ), + XclExpRoot( rRoot ), + mcBuiltIn( cBuiltIn ), + mnScTab( SCTAB_GLOBAL ), + mnFlags( EXC_NAME_DEFAULT ), + mnExtSheet( EXC_NAME_GLOBAL ), + mnXclTab( EXC_NAME_GLOBAL ) +{ + // filter source range is hidden in Excel + if( cBuiltIn == EXC_BUILTIN_FILTERDATABASE ) + SetHidden(); + + // special case for BIFF5/7 filter source range - name appears as plain text without built-in flag + if( (GetBiff() <= EXC_BIFF5) && (cBuiltIn == EXC_BUILTIN_FILTERDATABASE) ) + { + String aName( XclTools::GetXclBuiltInDefName( EXC_BUILTIN_FILTERDATABASE ) ); + mxName = XclExpStringHelper::CreateString( rRoot, aName, EXC_STR_8BITLENGTH ); + } + else + { + mxName = XclExpStringHelper::CreateString( rRoot, cBuiltIn, EXC_STR_8BITLENGTH ); + ::set_flag( mnFlags, EXC_NAME_BUILTIN ); + } +} + +void XclExpName::SetTokenArray( XclTokenArrayRef xTokArr ) +{ + mxTokArr = xTokArr; +} + +void XclExpName::SetLocalTab( SCTAB nScTab ) +{ + DBG_ASSERT( GetTabInfo().IsExportTab( nScTab ), "XclExpName::SetLocalTab - invalid sheet index" ); + if( GetTabInfo().IsExportTab( nScTab ) ) + { + mnScTab = nScTab; + GetGlobalLinkManager().FindExtSheet( mnExtSheet, mnXclTab, nScTab ); + + // special handling for NAME record + switch( GetBiff() ) + { + case EXC_BIFF5: // EXTERNSHEET index is positive in NAME record + mnExtSheet = ~mnExtSheet + 1; + break; + case EXC_BIFF8: // EXTERNSHEET index not used, but must be created in link table + mnExtSheet = 0; + break; + default: DBG_ERROR_BIFF(); + } + + // Excel sheet index is 1-based + ++mnXclTab; + } +} + +void XclExpName::SetHidden( bool bHidden ) +{ + ::set_flag( mnFlags, EXC_NAME_HIDDEN, bHidden ); +} + +void XclExpName::SetMacroCall( bool bVBasic, bool bFunc ) +{ + ::set_flag( mnFlags, EXC_NAME_PROC ); + ::set_flag( mnFlags, EXC_NAME_VB, bVBasic ); + ::set_flag( mnFlags, EXC_NAME_FUNC, bFunc ); +} + +void XclExpName::SetSymbol( String sSymbol ) +{ + msSymbol = sSymbol; +} + +bool XclExpName::IsVolatile() const +{ + return mxTokArr.is() && mxTokArr->IsVolatile(); +} + +bool XclExpName::IsHidden() const +{ + return ::get_flag( mnFlags, EXC_NAME_HIDDEN ); +} + +bool XclExpName::IsMacroCall( bool bVBasic, bool bFunc ) const +{ + return + (::get_flag( mnFlags, EXC_NAME_VB ) == bVBasic) && + (::get_flag( mnFlags, EXC_NAME_FUNC ) == bFunc); +} + +void XclExpName::Save( XclExpStream& rStrm ) +{ + DBG_ASSERT( mxName.is() && (mxName->Len() > 0), "XclExpName::Save - missing name" ); + DBG_ASSERT( !(IsGlobal() && ::get_flag( mnFlags, EXC_NAME_BUILTIN )), "XclExpName::Save - global built-in name" ); + SetRecSize( 11 + mxName->GetSize() + (mxTokArr.is() ? mxTokArr->GetSize() : 2) ); + XclExpRecord::Save( rStrm ); +} + +void XclExpName::SaveXml( XclExpXmlStream& rStrm ) +{ + // For some reason, AutoFilter creates exportable names where maOrigName=="" + if( maOrigName.Len() == 0 ) + return; + + sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream(); + rWorkbook->startElement( XML_definedName, + // OOXTODO: XML_comment, "", + // OOXTODO: XML_customMenu, "", + // OOXTODO: XML_description, "", + XML_function, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_NAME_VB ) ), + // OOXTODO: XML_functionGroupId, "", + // OOXTODO: XML_help, "", + XML_hidden, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_NAME_HIDDEN ) ), + XML_localSheetId, mnScTab == SCTAB_GLOBAL ? NULL : OString::valueOf( (sal_Int32)mnScTab ).getStr(), + XML_name, XclXmlUtils::ToOString( maOrigName ).getStr(), + // OOXTODO: XML_publishToServer, "", + // OOXTODO: XML_shortcutKey, "", + // OOXTODO: XML_statusBar, "", + XML_vbProcedure, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_NAME_VB ) ), + // OOXTODO: XML_workbookParameter, "", + // OOXTODO: XML_xlm, "", + FSEND ); + rWorkbook->writeEscaped( XclXmlUtils::ToOUString( msSymbol ) ); + rWorkbook->endElement( XML_definedName ); +} + +void XclExpName::WriteBody( XclExpStream& rStrm ) +{ + sal_uInt16 nFmlaSize = mxTokArr.is() ? mxTokArr->GetSize() : 0; + + rStrm << mnFlags // flags + << sal_uInt8( 0 ); // keyboard shortcut + mxName->WriteLenField( rStrm ); // length of name + rStrm << nFmlaSize // size of token array + << mnExtSheet // BIFF5/7: EXTSHEET index, BIFF8: not used + << mnXclTab // 1-based sheet index for local names + << sal_uInt32( 0 ); // length of menu/descr/help/status text + mxName->WriteFlagField( rStrm ); // BIFF8 flag field (no-op in <=BIFF7) + mxName->WriteBuffer( rStrm ); // character array of the name + if( mxTokArr.is() ) + mxTokArr->WriteArray( rStrm ); // token array without size +} + +// ---------------------------------------------------------------------------- + +XclExpNameManagerImpl::XclExpNameManagerImpl( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + maUnnamedDBName( ScGlobal::GetRscString( STR_DB_NONAME ) ), + mnFirstUserIdx( 0 ) +{ +} + +void XclExpNameManagerImpl::Initialize() +{ + CreateBuiltInNames(); + mnFirstUserIdx = maNameList.GetSize(); + CreateUserNames(); + CreateDatabaseNames(); +} + +sal_uInt16 XclExpNameManagerImpl::InsertName( USHORT nScNameIdx ) +{ + sal_uInt16 nNameIdx = FindNameIdx( maNameMap, nScNameIdx ); + if( nNameIdx == 0 ) + if( const ScRangeData* pRangeData = GetNamedRanges().FindIndex( nScNameIdx ) ) + nNameIdx = CreateName( *pRangeData ); + return nNameIdx; +} + +sal_uInt16 XclExpNameManagerImpl::InsertDBRange( USHORT nScDBRangeIdx ) +{ + sal_uInt16 nNameIdx = FindNameIdx( maDBRangeMap, nScDBRangeIdx ); + if( nNameIdx == 0 ) + if( const ScDBData* pDBData = GetDatabaseRanges().FindIndex( nScDBRangeIdx ) ) + nNameIdx = CreateName( *pDBData ); + return nNameIdx; +} + +sal_uInt16 XclExpNameManagerImpl::InsertBuiltInName( sal_Unicode cBuiltIn, XclTokenArrayRef xTokArr, SCTAB nScTab ) +{ + XclExpNameRef xName( new XclExpName( GetRoot(), cBuiltIn ) ); + xName->SetTokenArray( xTokArr ); + xName->SetLocalTab( nScTab ); + return Append( xName ); +} + +sal_uInt16 XclExpNameManagerImpl::InsertUniqueName( + const String& rName, XclTokenArrayRef xTokArr, SCTAB nScTab ) +{ + DBG_ASSERT( rName.Len(), "XclExpNameManagerImpl::InsertUniqueName - empty name" ); + XclExpNameRef xName( new XclExpName( GetRoot(), GetUnusedName( rName ) ) ); + xName->SetTokenArray( xTokArr ); + xName->SetLocalTab( nScTab ); + return Append( xName ); +} + +sal_uInt16 XclExpNameManagerImpl::InsertRawName( const String& rName ) +{ + // empty name? may occur in broken external Calc tokens + if( !rName.Len() ) + return 0; + + // try to find an existing NAME record, regardless of its type + for( size_t nListIdx = mnFirstUserIdx, nListSize = maNameList.GetSize(); nListIdx < nListSize; ++nListIdx ) + { + XclExpNameRef xName = maNameList.GetRecord( nListIdx ); + if( xName->IsGlobal() && (xName->GetOrigName() == rName) ) + return static_cast< sal_uInt16 >( nListIdx + 1 ); + } + + // create a new NAME record + XclExpNameRef xName( new XclExpName( GetRoot(), rName ) ); + return Append( xName ); +} + +sal_uInt16 XclExpNameManagerImpl::InsertMacroCall( const String& rMacroName, bool bVBasic, bool bFunc, bool bHidden ) +{ + // empty name? may occur in broken external Calc tokens + if( !rMacroName.Len() ) + return 0; + + // try to find an existing NAME record + for( size_t nListIdx = mnFirstUserIdx, nListSize = maNameList.GetSize(); nListIdx < nListSize; ++nListIdx ) + { + XclExpNameRef xName = maNameList.GetRecord( nListIdx ); + if( xName->IsMacroCall( bVBasic, bFunc ) && (xName->GetOrigName() == rMacroName) ) + return static_cast< sal_uInt16 >( nListIdx + 1 ); + } + + // create a new NAME record + XclExpNameRef xName( new XclExpName( GetRoot(), rMacroName ) ); + xName->SetMacroCall( bVBasic, bFunc ); + xName->SetHidden( bHidden ); + + // for sheet macros, add a #NAME! error + if( !bVBasic ) + xName->SetTokenArray( GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NAME ) ); + + return Append( xName ); +} + +const XclExpName* XclExpNameManagerImpl::GetName( sal_uInt16 nNameIdx ) const +{ + DBG_ASSERT( maNameList.HasRecord( nNameIdx - 1 ), "XclExpNameManagerImpl::GetName - wrong record index" ); + return maNameList.GetRecord( nNameIdx - 1 ).get(); +} + +void XclExpNameManagerImpl::Save( XclExpStream& rStrm ) +{ + maNameList.Save( rStrm ); +} + +void XclExpNameManagerImpl::SaveXml( XclExpXmlStream& rStrm ) +{ + if( maNameList.IsEmpty() ) + return; + sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream(); + rWorkbook->startElement( XML_definedNames, FSEND ); + maNameList.SaveXml( rStrm ); + rWorkbook->endElement( XML_definedNames ); +} + +// private -------------------------------------------------------------------- + +sal_uInt16 XclExpNameManagerImpl::FindNameIdx( const XclExpIndexMap& rMap, USHORT nScIdx ) const +{ + XclExpIndexMap::const_iterator aIt = rMap.find( nScIdx ); + return (aIt == rMap.end()) ? 0 : aIt->second; +} + +sal_uInt16 XclExpNameManagerImpl::FindBuiltInNameIdx( + const String& rName, const XclTokenArray& rTokArr, bool bDBRange ) const +{ + /* Get built-in index from the name. Special case: the database range + 'unnamed' will be mapped to Excel's built-in '_FilterDatabase' name. */ + sal_Unicode cBuiltIn = (bDBRange && (rName == maUnnamedDBName)) ? + EXC_BUILTIN_FILTERDATABASE : XclTools::GetBuiltInDefNameIndex( rName ); + + if( cBuiltIn < EXC_BUILTIN_UNKNOWN ) + { + // try to find the record in existing built-in NAME record list + for( size_t nPos = 0; nPos < mnFirstUserIdx; ++nPos ) + { + XclExpNameRef xName = maNameList.GetRecord( nPos ); + if( xName->GetBuiltInName() == cBuiltIn ) + { + XclTokenArrayRef xTokArr = xName->GetTokenArray(); + if( xTokArr.is() && (*xTokArr == rTokArr) ) + return static_cast< sal_uInt16 >( nPos + 1 ); + } + } + } + return 0; +} + +String XclExpNameManagerImpl::GetUnusedName( const String& rName ) const +{ + String aNewName( rName ); + sal_Int32 nAppIdx = 0; + bool bExist = true; + while( bExist ) + { + // search the list of user-defined names + bExist = false; + for( size_t nPos = mnFirstUserIdx, nSize = maNameList.GetSize(); !bExist && (nPos < nSize); ++nPos ) + { + XclExpNameRef xName = maNameList.GetRecord( nPos ); + bExist = xName->GetOrigName() == aNewName; + // name exists -> create a new name "<originalname>_<counter>" + if( bExist ) + aNewName.Assign( rName ).Append( '_' ).Append( String::CreateFromInt32( ++nAppIdx ) ); + } + } + return aNewName; +} + +sal_uInt16 XclExpNameManagerImpl::Append( XclExpNameRef xName ) +{ + if( maNameList.GetSize() == 0xFFFF ) + return 0; + maNameList.AppendRecord( xName ); + return static_cast< sal_uInt16 >( maNameList.GetSize() ); // 1-based +} + +sal_uInt16 XclExpNameManagerImpl::CreateName( const ScRangeData& rRangeData ) +{ + const String& rName = rRangeData.GetName(); + + /* #i38821# recursive names: first insert the (empty) name object, + otherwise a recursive call of this function from the formula compiler + with the same defined name will not find it and will create it again. */ + size_t nOldListSize = maNameList.GetSize(); + XclExpNameRef xName( new XclExpName( GetRoot(), rName ) ); + sal_uInt16 nNameIdx = Append( xName ); + // store the index of the NAME record in the lookup map + maNameMap[ rRangeData.GetIndex() ] = nNameIdx; + + /* Create the definition formula. + This may cause recursive creation of other defined names. */ + if( const ScTokenArray* pScTokArr = const_cast< ScRangeData& >( rRangeData ).GetCode() ) + { + XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, *pScTokArr ); + xName->SetTokenArray( xTokArr ); + + String sSymbol; + rRangeData.GetSymbol( sSymbol, formula::FormulaGrammar::GRAM_NATIVE_XL_A1 ); + xName->SetSymbol( sSymbol ); + + /* Try to replace by existing built-in name - complete token array is + needed for comparison, and due to the recursion problem above this + cannot be done earlier. If a built-in name is found, the created NAME + record for this name and all following records in the list must be + deleted, otherwise they may contain wrong name list indexes. */ + sal_uInt16 nBuiltInIdx = FindBuiltInNameIdx( rName, *xTokArr, false ); + if( nBuiltInIdx != 0 ) + { + // delete the new NAME records + while( maNameList.GetSize() > nOldListSize ) + maNameList.RemoveRecord( maNameList.GetSize() - 1 ); + // use index of the found built-in NAME record + maNameMap[ rRangeData.GetIndex() ] = nNameIdx = nBuiltInIdx; + } + } + + return nNameIdx; +} + +sal_uInt16 XclExpNameManagerImpl::CreateName( const ScDBData& rDBData ) +{ + // get name and source range, and create the definition formula + const String& rName = rDBData.GetName(); + ScRange aRange; + rDBData.GetArea( aRange ); + XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, aRange ); + + // try to use an existing built-in name + sal_uInt16 nNameIdx = FindBuiltInNameIdx( rName, *xTokArr, true ); + if( nNameIdx == 0 ) + { + // insert a new name into the list + XclExpNameRef xName( new XclExpName( GetRoot(), GetUnusedName( rName ) ) ); + xName->SetTokenArray( xTokArr ); + nNameIdx = Append( xName ); + } + + // store the index of the NAME record in the lookup map + maDBRangeMap[ rDBData.GetIndex() ] = nNameIdx; + return nNameIdx; +} + +void XclExpNameManagerImpl::CreateBuiltInNames() +{ + ScDocument& rDoc = GetDoc(); + XclExpTabInfo& rTabInfo = GetTabInfo(); + + /* #i2394# #100489# built-in defined names must be sorted by the name of the + containing sheet. Example: SheetA!Print_Range must be stored *before* + SheetB!Print_Range, regardless of the position of SheetA in the document! */ + for( SCTAB nScTabIdx = 0, nScTabCount = rTabInfo.GetScTabCount(); nScTabIdx < nScTabCount; ++nScTabIdx ) + { + // find real sheet index from the nScTabIdx counter + SCTAB nScTab = rTabInfo.GetRealScTab( nScTabIdx ); + // create NAME records for all built-in names of this sheet + if( rTabInfo.IsExportTab( nScTab ) ) + { + // *** 1) print ranges *** ---------------------------------------- + + if( rDoc.HasPrintRange() ) + { + ScRangeList aRangeList; + for( USHORT nIdx = 0, nCount = rDoc.GetPrintRangeCount( nScTab ); nIdx < nCount; ++nIdx ) + { + ScRange aRange( *rDoc.GetPrintRange( nScTab, nIdx ) ); + // Calc document does not care about sheet index in print ranges + aRange.aStart.SetTab( nScTab ); + aRange.aEnd.SetTab( nScTab ); + aRange.Justify(); + aRangeList.Append( aRange ); + } + // create the NAME record (do not warn if ranges are shrunken) + GetAddressConverter().ValidateRangeList( aRangeList, false ); + if( aRangeList.Count() > 0 ) + GetNameManager().InsertBuiltInName( EXC_BUILTIN_PRINTAREA, aRangeList ); + } + + // *** 2) print titles *** ---------------------------------------- + + ScRangeList aTitleList; + // repeated columns + if( const ScRange* pColRange = rDoc.GetRepeatColRange( nScTab ) ) + aTitleList.Append( ScRange( + pColRange->aStart.Col(), 0, nScTab, + pColRange->aEnd.Col(), GetXclMaxPos().Row(), nScTab ) ); + // repeated rows + if( const ScRange* pRowRange = rDoc.GetRepeatRowRange( nScTab ) ) + aTitleList.Append( ScRange( + 0, pRowRange->aStart.Row(), nScTab, + GetXclMaxPos().Col(), pRowRange->aEnd.Row(), nScTab ) ); + // create the NAME record + GetAddressConverter().ValidateRangeList( aTitleList, false ); + if( aTitleList.Count() > 0 ) + GetNameManager().InsertBuiltInName( EXC_BUILTIN_PRINTTITLES, aTitleList ); + + // *** 3) filter ranges *** --------------------------------------- + + if( GetBiff() == EXC_BIFF8 ) + GetFilterManager().InitTabFilter( nScTab ); + } + } +} + +void XclExpNameManagerImpl::CreateUserNames() +{ + const ScRangeName& rNamedRanges = GetNamedRanges(); + for( USHORT nNameIdx = 0, nNameCount = rNamedRanges.GetCount(); nNameIdx < nNameCount; ++nNameIdx ) + { + const ScRangeData* pRangeData = rNamedRanges[ nNameIdx ]; + DBG_ASSERT( rNamedRanges[ nNameIdx ], "XclExpNameManagerImpl::CreateUserNames - missing defined name" ); + // skip definitions of shared formulas + if( pRangeData && !pRangeData->HasType( RT_SHARED ) && !FindNameIdx( maNameMap, pRangeData->GetIndex() ) ) + CreateName( *pRangeData ); + } +} + +void XclExpNameManagerImpl::CreateDatabaseNames() +{ + const ScDBCollection& rDBRanges = GetDatabaseRanges(); + for( USHORT nDBIdx = 0, nDBCount = rDBRanges.GetCount(); nDBIdx < nDBCount; ++nDBIdx ) + { + const ScDBData* pDBData = rDBRanges[ nDBIdx ]; + DBG_ASSERT( pDBData, "XclExpNameManagerImpl::CreateDatabaseNames - missing database range" ); + // skip hidden "unnamed" range + if( pDBData && (pDBData->GetName() != maUnnamedDBName) && !FindNameIdx( maDBRangeMap, pDBData->GetIndex() ) ) + CreateName( *pDBData ); + } +} + +// ---------------------------------------------------------------------------- + +XclExpNameManager::XclExpNameManager( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mxImpl( new XclExpNameManagerImpl( rRoot ) ) +{ +} + +XclExpNameManager::~XclExpNameManager() +{ +} + +void XclExpNameManager::Initialize() +{ + mxImpl->Initialize(); +} + +sal_uInt16 XclExpNameManager::InsertName( USHORT nScNameIdx ) +{ + return mxImpl->InsertName( nScNameIdx ); +} + +sal_uInt16 XclExpNameManager::InsertDBRange( USHORT nScDBRangeIdx ) +{ + return mxImpl->InsertDBRange( nScDBRangeIdx ); +} + +//UNUSED2009-05 sal_uInt16 XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn, XclTokenArrayRef xTokArr, SCTAB nScTab ) +//UNUSED2009-05 { +//UNUSED2009-05 return mxImpl->InsertBuiltInName( cBuiltIn, xTokArr, nScTab ); +//UNUSED2009-05 } + +sal_uInt16 XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn, const ScRange& rRange ) +{ + XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, rRange ); + return mxImpl->InsertBuiltInName( cBuiltIn, xTokArr, rRange.aStart.Tab() ); +} + +sal_uInt16 XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn, const ScRangeList& rRangeList ) +{ + sal_uInt16 nNameIdx = 0; + if( rRangeList.Count() ) + { + XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, rRangeList ); + nNameIdx = mxImpl->InsertBuiltInName( cBuiltIn, xTokArr, rRangeList.GetObject( 0 )->aStart.Tab() ); + } + return nNameIdx; +} + +sal_uInt16 XclExpNameManager::InsertUniqueName( + const String& rName, XclTokenArrayRef xTokArr, SCTAB nScTab ) +{ + return mxImpl->InsertUniqueName( rName, xTokArr, nScTab ); +} + +sal_uInt16 XclExpNameManager::InsertRawName( const String& rName ) +{ + return mxImpl->InsertRawName( rName ); +} + +sal_uInt16 XclExpNameManager::InsertMacroCall( const String& rMacroName, bool bVBasic, bool bFunc, bool bHidden ) +{ + return mxImpl->InsertMacroCall( rMacroName, bVBasic, bFunc, bHidden ); +} + +const String& XclExpNameManager::GetOrigName( sal_uInt16 nNameIdx ) const +{ + const XclExpName* pName = mxImpl->GetName( nNameIdx ); + return pName ? pName->GetOrigName() : EMPTY_STRING; +} + +SCTAB XclExpNameManager::GetScTab( sal_uInt16 nNameIdx ) const +{ + const XclExpName* pName = mxImpl->GetName( nNameIdx ); + return pName ? pName->GetScTab() : SCTAB_GLOBAL; +} + +bool XclExpNameManager::IsVolatile( sal_uInt16 nNameIdx ) const +{ + const XclExpName* pName = mxImpl->GetName( nNameIdx ); + return pName && pName->IsVolatile(); +} + +void XclExpNameManager::Save( XclExpStream& rStrm ) +{ + mxImpl->Save( rStrm ); +} + +void XclExpNameManager::SaveXml( XclExpXmlStream& rStrm ) +{ + mxImpl->SaveXml( rStrm ); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xepage.cxx b/sc/source/filter/excel/xepage.cxx new file mode 100644 index 000000000000..6194a04c27f6 --- /dev/null +++ b/sc/source/filter/excel/xepage.cxx @@ -0,0 +1,414 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "xepage.hxx" +#include <svl/itemset.hxx> +#include "scitems.hxx" +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <svx/pageitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/brshitem.hxx> +#include "document.hxx" +#include "stlpool.hxx" +#include "stlsheet.hxx" +#include "attrib.hxx" +#include "xehelper.hxx" +#include "xeescher.hxx" + +#include <set> +#include <limits> + +#include <oox/core/tokens.hxx> + +using ::rtl::OString; +using ::std::set; +using ::std::numeric_limits; + +// Page settings records ====================================================== + +// Header/footer -------------------------------------------------------------- + +XclExpHeaderFooter::XclExpHeaderFooter( sal_uInt16 nRecId, const String& rHdrString ) : + XclExpRecord( nRecId ), + maHdrString( rHdrString ) +{ +} + +void XclExpHeaderFooter::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + sal_Int32 nElement = GetRecId() == EXC_ID_HEADER ? XML_oddHeader : XML_oddFooter; + rWorksheet->startElement( nElement, FSEND ); + rWorksheet->writeEscaped( XclXmlUtils::ToOUString( maHdrString ) ); + rWorksheet->endElement( nElement ); +} + +void XclExpHeaderFooter::WriteBody( XclExpStream& rStrm ) +{ + if( maHdrString.Len() ) + { + XclExpString aExString; + if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 ) + aExString.AssignByte( maHdrString, rStrm.GetRoot().GetTextEncoding(), EXC_STR_8BITLENGTH ); + else + aExString.Assign( maHdrString, EXC_STR_DEFAULT, 255 ); // 16-bit length, but max 255 chars + rStrm << aExString; + } +} + +// General page settings ------------------------------------------------------ + +XclExpSetup::XclExpSetup( const XclPageData& rPageData ) : + XclExpRecord( EXC_ID_SETUP, 34 ), + mrData( rPageData ) +{ +} + +void XclExpSetup::SaveXml( XclExpXmlStream& rStrm ) +{ + rStrm.GetCurrentStream()->singleElement( XML_pageSetup, + XML_paperSize, OString::valueOf( (sal_Int32) mrData.mnPaperSize ).getStr(), + XML_scale, OString::valueOf( (sal_Int32) mrData.mnScaling ).getStr(), + XML_firstPageNumber, OString::valueOf( (sal_Int32) mrData.mnStartPage ).getStr(), + XML_fitToWidth, OString::valueOf( (sal_Int32) mrData.mnFitToWidth ).getStr(), + XML_fitToHeight, OString::valueOf( (sal_Int32) mrData.mnFitToHeight ).getStr(), + XML_pageOrder, mrData.mbPrintInRows ? "overThenDown" : "downThenOver", + XML_orientation, mrData.mbPortrait ? "portrait" : "landscape", // OOXTODO: "default"? + XML_usePrinterDefaults, XclXmlUtils::ToPsz( !mrData.mbValid ), + XML_blackAndWhite, XclXmlUtils::ToPsz( mrData.mbBlackWhite ), + XML_draft, XclXmlUtils::ToPsz( mrData.mbDraftQuality ), + XML_cellComments, mrData.mbPrintNotes ? "atEnd" : "none", // OOXTODO: "asDisplayed"? + XML_useFirstPageNumber, XclXmlUtils::ToPsz( mrData.mbManualStart ), + // OOXTODO: XML_errors, // == displayed|blank|dash|NA + XML_horizontalDpi, OString::valueOf( (sal_Int32) mrData.mnHorPrintRes ).getStr(), + XML_verticalDpi, OString::valueOf( (sal_Int32) mrData.mnVerPrintRes ).getStr(), + XML_copies, OString::valueOf( (sal_Int32) mrData.mnCopies ).getStr(), + // OOXTODO: devMode settings part RelationshipId: FSNS( XML_r, XML_id ), + FSEND ); +} + +void XclExpSetup::WriteBody( XclExpStream& rStrm ) +{ + XclBiff eBiff = rStrm.GetRoot().GetBiff(); + + sal_uInt16 nFlags = 0; + ::set_flag( nFlags, EXC_SETUP_INROWS, mrData.mbPrintInRows ); + ::set_flag( nFlags, EXC_SETUP_PORTRAIT, mrData.mbPortrait ); + ::set_flag( nFlags, EXC_SETUP_INVALID, !mrData.mbValid ); + ::set_flag( nFlags, EXC_SETUP_BLACKWHITE, mrData.mbBlackWhite ); + if( eBiff >= EXC_BIFF5 ) + { + ::set_flag( nFlags, EXC_SETUP_DRAFT, mrData.mbDraftQuality ); + /* Set the Comments/Notes to "At end of sheet" if Print Notes is true. + We don't currently support "as displayed on sheet". Thus this value + will be re-interpreted to "At end of sheet". */ + const sal_uInt16 nNotes = EXC_SETUP_PRINTNOTES | EXC_SETUP_NOTES_END; + ::set_flag( nFlags, nNotes, mrData.mbPrintNotes ); + ::set_flag( nFlags, EXC_SETUP_STARTPAGE, mrData.mbManualStart ); + } + + rStrm << mrData.mnPaperSize << mrData.mnScaling << mrData.mnStartPage + << mrData.mnFitToWidth << mrData.mnFitToHeight << nFlags; + if( eBiff >= EXC_BIFF5 ) + { + rStrm << mrData.mnHorPrintRes << mrData.mnVerPrintRes + << mrData.mfHeaderMargin << mrData.mfFooterMargin << mrData.mnCopies; + } +} + +// Manual page breaks --------------------------------------------------------- + +XclExpPageBreaks::XclExpPageBreaks( sal_uInt16 nRecId, const ScfUInt16Vec& rPageBreaks, sal_uInt16 nMaxPos ) : + XclExpRecord( nRecId ), + mrPageBreaks( rPageBreaks ), + mnMaxPos( nMaxPos ) +{ +} + +void XclExpPageBreaks::Save( XclExpStream& rStrm ) +{ + if( !mrPageBreaks.empty() ) + { + SetRecSize( 2 + ((rStrm.GetRoot().GetBiff() <= EXC_BIFF5) ? 2 : 6) * mrPageBreaks.size() ); + XclExpRecord::Save( rStrm ); + } +} + +void XclExpPageBreaks::WriteBody( XclExpStream& rStrm ) +{ + bool bWriteRange = (rStrm.GetRoot().GetBiff() == EXC_BIFF8); + + rStrm << static_cast< sal_uInt16 >( mrPageBreaks.size() ); + for( ScfUInt16Vec::const_iterator aIt = mrPageBreaks.begin(), aEnd = mrPageBreaks.end(); aIt != aEnd; ++aIt ) + { + rStrm << *aIt; + if( bWriteRange ) + rStrm << sal_uInt16( 0 ) << mnMaxPos; + } +} + +void XclExpPageBreaks::SaveXml( XclExpXmlStream& rStrm ) +{ + if( mrPageBreaks.empty() ) + return; + + sal_Int32 nElement = GetRecId() == EXC_ID_HORPAGEBREAKS ? XML_rowBreaks : XML_colBreaks; + sax_fastparser::FSHelperPtr& pWorksheet = rStrm.GetCurrentStream(); + OString sNumPageBreaks = OString::valueOf( (sal_Int32) mrPageBreaks.size() ); + pWorksheet->startElement( nElement, + XML_count, sNumPageBreaks.getStr(), + XML_manualBreakCount, sNumPageBreaks.getStr(), + FSEND ); + for( ScfUInt16Vec::const_iterator aIt = mrPageBreaks.begin(), aEnd = mrPageBreaks.end(); aIt != aEnd; ++aIt ) + { + pWorksheet->singleElement( XML_brk, + XML_id, OString::valueOf( (sal_Int32) *aIt ).getStr(), + XML_man, "true", + XML_max, OString::valueOf( (sal_Int32) mnMaxPos ).getStr(), + XML_min, "0", + // OOXTODO: XML_pt, "", + FSEND ); + } + pWorksheet->endElement( nElement ); +} + +// Page settings ============================================================== + +XclExpPageSettings::XclExpPageSettings( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ) +{ + ScDocument& rDoc = GetDoc(); + SCTAB nScTab = GetCurrScTab(); + + if( SfxStyleSheetBase* pStyleSheet = GetStyleSheetPool().Find( rDoc.GetPageStyle( nScTab ), SFX_STYLE_FAMILY_PAGE ) ) + { + const SfxItemSet& rItemSet = pStyleSheet->GetItemSet(); + maData.mbValid = true; + + // *** page settings *** + + maData.mbPrintInRows = !GETITEMBOOL( rItemSet, ATTR_PAGE_TOPDOWN ); + maData.mbHorCenter = GETITEMBOOL( rItemSet, ATTR_PAGE_HORCENTER ); + maData.mbVerCenter = GETITEMBOOL( rItemSet, ATTR_PAGE_VERCENTER ); + maData.mbPrintHeadings = GETITEMBOOL( rItemSet, ATTR_PAGE_HEADERS ); + maData.mbPrintGrid = GETITEMBOOL( rItemSet, ATTR_PAGE_GRID ); + maData.mbPrintNotes = GETITEMBOOL( rItemSet, ATTR_PAGE_NOTES ); + + maData.mnStartPage = GETITEMVALUE( rItemSet, SfxUInt16Item, ATTR_PAGE_FIRSTPAGENO, sal_uInt16 ); + maData.mbManualStart = maData.mnStartPage && (!nScTab || rDoc.NeedPageResetAfterTab( nScTab - 1 )); + + const SvxLRSpaceItem& rLRItem = GETITEM( rItemSet, SvxLRSpaceItem, ATTR_LRSPACE ); + maData.mfLeftMargin = XclTools::GetInchFromTwips( rLRItem.GetLeft() ); + maData.mfRightMargin = XclTools::GetInchFromTwips( rLRItem.GetRight() ); + const SvxULSpaceItem& rULItem = GETITEM( rItemSet, SvxULSpaceItem, ATTR_ULSPACE ); + maData.mfTopMargin = XclTools::GetInchFromTwips( rULItem.GetUpper() ); + maData.mfBottomMargin = XclTools::GetInchFromTwips( rULItem.GetLower() ); + + const SvxPageItem& rPageItem = GETITEM( rItemSet, SvxPageItem, ATTR_PAGE ); + const SvxSizeItem& rSizeItem = GETITEM( rItemSet, SvxSizeItem, ATTR_PAGE_SIZE ); + maData.SetScPaperSize( rSizeItem.GetSize(), !rPageItem.IsLandscape() ); + + const ScPageScaleToItem& rScaleToItem = GETITEM( rItemSet, ScPageScaleToItem, ATTR_PAGE_SCALETO ); + sal_uInt16 nPages = GETITEMVALUE( rItemSet, SfxUInt16Item, ATTR_PAGE_SCALETOPAGES, sal_uInt16 ); + sal_uInt16 nScale = GETITEMVALUE( rItemSet, SfxUInt16Item, ATTR_PAGE_SCALE, sal_uInt16 ); + + if( ScfTools::CheckItem( rItemSet, ATTR_PAGE_SCALETO, false ) && rScaleToItem.IsValid() ) + { + maData.mnFitToWidth = rScaleToItem.GetWidth(); + maData.mnFitToHeight = rScaleToItem.GetHeight(); + maData.mbFitToPages = true; + + } + else if( ScfTools::CheckItem( rItemSet, ATTR_PAGE_SCALETOPAGES, false ) && nPages ) + { + maData.mnFitToWidth = 1; + maData.mnFitToHeight = nPages; + maData.mbFitToPages = true; + } + else if( nScale ) + { + maData.mnScaling = nScale; + maData.mbFitToPages = false; + } + + maData.mxBrushItem.reset( new SvxBrushItem( GETITEM( rItemSet, SvxBrushItem, ATTR_BACKGROUND ) ) ); + + // *** header and footer *** + + XclExpHFConverter aHFConv( GetRoot() ); + + // header + const SfxItemSet& rHdrItemSet = GETITEM( rItemSet, SvxSetItem, ATTR_PAGE_HEADERSET ).GetItemSet(); + if( GETITEMBOOL( rHdrItemSet, ATTR_PAGE_ON ) ) + { + const ScPageHFItem& rHFItem = GETITEM( rItemSet, ScPageHFItem, ATTR_PAGE_HEADERRIGHT ); + aHFConv.GenerateString( rHFItem.GetLeftArea(), rHFItem.GetCenterArea(), rHFItem.GetRightArea() ); + maData.maHeader = aHFConv.GetHFString(); + // header height (Excel excludes header from top margin) + sal_Int32 nHdrHeight = GETITEMBOOL( rHdrItemSet, ATTR_PAGE_DYNAMIC ) ? + // dynamic height: calculate header height, add header <-> sheet area distance + (aHFConv.GetTotalHeight() + GETITEM( rHdrItemSet, SvxULSpaceItem, ATTR_ULSPACE ).GetLower()) : + // static height: ATTR_PAGE_SIZE already includes header <-> sheet area distance + static_cast< sal_Int32 >( GETITEM( rHdrItemSet, SvxSizeItem, ATTR_PAGE_SIZE ).GetSize().Height() ); + maData.mfHeaderMargin = maData.mfTopMargin; + maData.mfTopMargin += XclTools::GetInchFromTwips( nHdrHeight ); + } + + // footer + const SfxItemSet& rFtrItemSet = GETITEM( rItemSet, SvxSetItem, ATTR_PAGE_FOOTERSET ).GetItemSet(); + if( GETITEMBOOL( rFtrItemSet, ATTR_PAGE_ON ) ) + { + const ScPageHFItem& rHFItem = GETITEM( rItemSet, ScPageHFItem, ATTR_PAGE_FOOTERRIGHT ); + aHFConv.GenerateString( rHFItem.GetLeftArea(), rHFItem.GetCenterArea(), rHFItem.GetRightArea() ); + maData.maFooter = aHFConv.GetHFString(); + // footer height (Excel excludes footer from bottom margin) + sal_Int32 nFtrHeight = GETITEMBOOL( rFtrItemSet, ATTR_PAGE_DYNAMIC ) ? + // dynamic height: calculate footer height, add sheet area <-> footer distance + (aHFConv.GetTotalHeight() + GETITEM( rFtrItemSet, SvxULSpaceItem, ATTR_ULSPACE ).GetUpper()) : + // static height: ATTR_PAGE_SIZE already includes sheet area <-> footer distance + static_cast< sal_Int32 >( GETITEM( rFtrItemSet, SvxSizeItem, ATTR_PAGE_SIZE ).GetSize().Height() ); + maData.mfFooterMargin = maData.mfBottomMargin; + maData.mfBottomMargin += XclTools::GetInchFromTwips( nFtrHeight ); + } + } + + // *** page breaks *** + + set<SCROW> aRowBreaks; + rDoc.GetAllRowBreaks(aRowBreaks, nScTab, false, true); + + SCROW nMaxRow = numeric_limits<sal_uInt16>::max(); + for (set<SCROW>::const_iterator itr = aRowBreaks.begin(), itrEnd = aRowBreaks.end(); itr != itrEnd; ++itr) + { + SCROW nRow = *itr; + if (nRow > nMaxRow) + break; + + maData.maHorPageBreaks.push_back(nRow); + } + + set<SCCOL> aColBreaks; + rDoc.GetAllColBreaks(aColBreaks, nScTab, false, true); + for (set<SCCOL>::const_iterator itr = aColBreaks.begin(), itrEnd = aColBreaks.end(); itr != itrEnd; ++itr) + maData.maVerPageBreaks.push_back(*itr); +} + +static void lcl_WriteHeaderFooter( XclExpXmlStream& rStrm ) +{ + // OOXTODO: we currently only emit oddHeader/oddFooter elements, and + // do not support the first/even/odd page distinction. + rStrm.WriteAttributes( + // OOXTODO: XML_alignWithMargins, + XML_differentFirst, "false", // OOXTODO + XML_differentOddEven, "false", // OOXTODO + // OOXTODO: XML_scaleWithDoc + FSEND ); + rStrm.GetCurrentStream()->write( ">" ); +} + +void XclExpPageSettings::Save( XclExpStream& rStrm ) +{ + XclExpBoolRecord( EXC_ID_PRINTHEADERS, maData.mbPrintHeadings ).Save( rStrm ); + XclExpBoolRecord( EXC_ID_PRINTGRIDLINES, maData.mbPrintGrid ).Save( rStrm ); + XclExpBoolRecord( EXC_ID_GRIDSET, true ).Save( rStrm ); + XclExpPageBreaks( EXC_ID_HORPAGEBREAKS, maData.maHorPageBreaks, static_cast< sal_uInt16 >( GetXclMaxPos().Col() ) ).Save( rStrm ); + XclExpPageBreaks( EXC_ID_VERPAGEBREAKS, maData.maVerPageBreaks, static_cast< sal_uInt16 >( GetXclMaxPos().Row() ) ).Save( rStrm ); + XclExpHeaderFooter( EXC_ID_HEADER, maData.maHeader ).Save( rStrm ); + XclExpHeaderFooter( EXC_ID_FOOTER, maData.maFooter ).Save( rStrm ); + XclExpBoolRecord( EXC_ID_HCENTER, maData.mbHorCenter ).Save( rStrm ); + XclExpBoolRecord( EXC_ID_VCENTER, maData.mbVerCenter ).Save( rStrm ); + XclExpDoubleRecord( EXC_ID_LEFTMARGIN, maData.mfLeftMargin ).Save( rStrm ); + XclExpDoubleRecord( EXC_ID_RIGHTMARGIN, maData.mfRightMargin ).Save( rStrm ); + XclExpDoubleRecord( EXC_ID_TOPMARGIN, maData.mfTopMargin ).Save( rStrm ); + XclExpDoubleRecord( EXC_ID_BOTTOMMARGIN, maData.mfBottomMargin ).Save( rStrm ); + XclExpSetup( maData ).Save( rStrm ); + + if( (GetBiff() == EXC_BIFF8) && maData.mxBrushItem.get() ) + if( const Graphic* pGraphic = maData.mxBrushItem->GetGraphic() ) + XclExpImgData( *pGraphic, EXC_ID8_IMGDATA ).Save( rStrm ); +} + +void XclExpPageSettings::SaveXml( XclExpXmlStream& rStrm ) +{ + XclExpXmlStartSingleElementRecord( XML_printOptions ).SaveXml( rStrm ); + XclExpBoolRecord( EXC_ID_PRINTHEADERS, maData.mbPrintHeadings, XML_headings ).SaveXml( rStrm ); + XclExpBoolRecord( EXC_ID_PRINTGRIDLINES, maData.mbPrintGrid, XML_gridLines ).SaveXml( rStrm ); + XclExpBoolRecord( EXC_ID_GRIDSET, true, XML_gridLinesSet ).SaveXml( rStrm ); + XclExpBoolRecord( EXC_ID_HCENTER, maData.mbHorCenter, XML_horizontalCentered ).SaveXml( rStrm ); + XclExpBoolRecord( EXC_ID_VCENTER, maData.mbVerCenter, XML_verticalCentered ).SaveXml( rStrm ); + XclExpXmlEndSingleElementRecord().SaveXml( rStrm ); // XML_printOptions + + XclExpXmlStartSingleElementRecord( XML_pageMargins ).SaveXml( rStrm ); + XclExpDoubleRecord( EXC_ID_LEFTMARGIN, maData.mfLeftMargin ).SetAttribute( XML_left )->SaveXml( rStrm ); + XclExpDoubleRecord( EXC_ID_RIGHTMARGIN, maData.mfRightMargin ).SetAttribute( XML_right )->SaveXml( rStrm ); + XclExpDoubleRecord( EXC_ID_TOPMARGIN, maData.mfTopMargin ).SetAttribute( XML_top )->SaveXml( rStrm ); + XclExpDoubleRecord( EXC_ID_BOTTOMMARGIN, maData.mfBottomMargin ).SetAttribute( XML_bottom )->SaveXml( rStrm ); + XclExpDoubleRecord( 0, maData.mfHeaderMargin).SetAttribute( XML_header )->SaveXml( rStrm ); + XclExpDoubleRecord( 0, maData.mfFooterMargin).SetAttribute( XML_footer )->SaveXml( rStrm ); + XclExpXmlEndSingleElementRecord().SaveXml( rStrm ); // XML_pageMargins + + XclExpSetup( maData ).SaveXml( rStrm ); + + XclExpXmlStartElementRecord( XML_headerFooter, lcl_WriteHeaderFooter ).SaveXml( rStrm ); + XclExpHeaderFooter( EXC_ID_HEADER, maData.maHeader ).SaveXml( rStrm ); + XclExpHeaderFooter( EXC_ID_FOOTER, maData.maFooter ).SaveXml( rStrm ); + XclExpXmlEndElementRecord( XML_headerFooter ).SaveXml( rStrm ); + + XclExpPageBreaks( EXC_ID_HORPAGEBREAKS, maData.maHorPageBreaks, + static_cast< sal_uInt16 >( GetXclMaxPos().Col() ) ).SaveXml( rStrm ); + XclExpPageBreaks( EXC_ID_VERPAGEBREAKS, maData.maVerPageBreaks, + static_cast< sal_uInt16 >( GetXclMaxPos().Row() ) ).SaveXml( rStrm ); + + if( const Graphic* pGraphic = maData.mxBrushItem->GetGraphic() ) + XclExpImgData( *pGraphic, EXC_ID8_IMGDATA ).SaveXml( rStrm ); +} + +// ---------------------------------------------------------------------------- + +XclExpChartPageSettings::XclExpChartPageSettings( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ) +{ +} + +void XclExpChartPageSettings::Save( XclExpStream& rStrm ) +{ + XclExpHeaderFooter( EXC_ID_HEADER, maData.maHeader ).Save( rStrm ); + XclExpHeaderFooter( EXC_ID_FOOTER, maData.maFooter ).Save( rStrm ); + XclExpBoolRecord( EXC_ID_HCENTER, maData.mbHorCenter ).Save( rStrm ); + XclExpBoolRecord( EXC_ID_VCENTER, maData.mbVerCenter ).Save( rStrm ); + XclExpSetup( maData ).Save( rStrm ); + XclExpUInt16Record( EXC_ID_PRINTSIZE, EXC_PRINTSIZE_FULL ).Save( rStrm ); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xepivot.cxx b/sc/source/filter/excel/xepivot.cxx new file mode 100644 index 000000000000..ddcdb3243167 --- /dev/null +++ b/sc/source/filter/excel/xepivot.cxx @@ -0,0 +1,1810 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xepivot.hxx" +#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp> +#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp> +#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp> +#include <com/sun/star/sheet/DataPilotFieldReference.hpp> +#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp> + +#include <algorithm> +#include <math.h> + +#include <rtl/math.hxx> +#include <tools/date.hxx> +#include <svl/zformat.hxx> +#include <sot/storage.hxx> +#include "document.hxx" +#include "dpobject.hxx" +#include "dpsave.hxx" +#include "dpdimsave.hxx" +#include "dpshttab.hxx" +#include "globstr.hrc" +#include "fapihelper.hxx" +#include "xestring.hxx" +#include "xelink.hxx" + +#include <oox/core/tokens.hxx> + +using ::com::sun::star::sheet::DataPilotFieldOrientation; +using ::com::sun::star::sheet::DataPilotFieldOrientation_HIDDEN; +using ::com::sun::star::sheet::DataPilotFieldOrientation_ROW; +using ::com::sun::star::sheet::DataPilotFieldOrientation_COLUMN; +using ::com::sun::star::sheet::DataPilotFieldOrientation_PAGE; +using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA; +using ::com::sun::star::sheet::GeneralFunction; +using ::com::sun::star::sheet::DataPilotFieldSortInfo; +using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo; +using ::com::sun::star::sheet::DataPilotFieldLayoutInfo; +using ::com::sun::star::sheet::DataPilotFieldReference; +using ::rtl::OUString; + +using ::rtl::OString; +using ::rtl::OUString; +using ::rtl::OUStringBuffer; + +// ============================================================================ +// Pivot cache +// ============================================================================ + +namespace { + +// constants to track occurence of specific data types +const sal_uInt16 EXC_PCITEM_DATA_STRING = 0x0001; /// String, empty, boolean, error. +const sal_uInt16 EXC_PCITEM_DATA_DOUBLE = 0x0002; /// Double with fraction. +const sal_uInt16 EXC_PCITEM_DATA_INTEGER = 0x0004; /// Integer, double without fraction. +const sal_uInt16 EXC_PCITEM_DATA_DATE = 0x0008; /// Date, time, date/time. + +/** Maps a bitfield consisting of EXC_PCITEM_DATA_* flags above to SXFIELD data type bitfield. */ +static const sal_uInt16 spnPCItemFlags[] = +{ // STR DBL INT DAT + EXC_SXFIELD_DATA_NONE, // + EXC_SXFIELD_DATA_STR, // x + EXC_SXFIELD_DATA_INT, // x + EXC_SXFIELD_DATA_STR_INT, // x x + EXC_SXFIELD_DATA_DBL, // x + EXC_SXFIELD_DATA_STR_DBL, // x x + EXC_SXFIELD_DATA_INT, // x x + EXC_SXFIELD_DATA_STR_INT, // x x x + EXC_SXFIELD_DATA_DATE, // x + EXC_SXFIELD_DATA_DATE_STR, // x x + EXC_SXFIELD_DATA_DATE_NUM, // x x + EXC_SXFIELD_DATA_DATE_STR, // x x x + EXC_SXFIELD_DATA_DATE_NUM, // x x + EXC_SXFIELD_DATA_DATE_STR, // x x x + EXC_SXFIELD_DATA_DATE_NUM, // x x x + EXC_SXFIELD_DATA_DATE_STR // x x x x +}; + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpPCItem::XclExpPCItem( const String& rText ) : + XclExpRecord( (rText.Len() > 0) ? EXC_ID_SXSTRING : EXC_ID_SXEMPTY, 0 ), + mnTypeFlag( EXC_PCITEM_DATA_STRING ) +{ + if( rText.Len() ) + SetText( rText ); + else + SetEmpty(); +} + +XclExpPCItem::XclExpPCItem( double fValue ) : + XclExpRecord( EXC_ID_SXDOUBLE, 8 ) +{ + SetDouble( fValue ); + mnTypeFlag = (fValue - floor( fValue ) == 0.0) ? + EXC_PCITEM_DATA_INTEGER : EXC_PCITEM_DATA_DOUBLE; +} + +XclExpPCItem::XclExpPCItem( const DateTime& rDateTime ) : + XclExpRecord( EXC_ID_SXDATETIME, 8 ) +{ + SetDateTime( rDateTime ); + mnTypeFlag = EXC_PCITEM_DATA_DATE; +} + +XclExpPCItem::XclExpPCItem( sal_Int16 nValue ) : + XclExpRecord( EXC_ID_SXINTEGER, 2 ), + mnTypeFlag( EXC_PCITEM_DATA_INTEGER ) +{ + SetInteger( nValue ); +} + +XclExpPCItem::XclExpPCItem( bool bValue ) : + XclExpRecord( EXC_ID_SXBOOLEAN, 2 ), + mnTypeFlag( EXC_PCITEM_DATA_STRING ) +{ + SetBool( bValue ); +} + +// ---------------------------------------------------------------------------- + +bool XclExpPCItem::EqualsText( const String& rText ) const +{ + return (rText.Len() == 0) ? IsEmpty() : (GetText() && (*GetText() == rText)); +} + +bool XclExpPCItem::EqualsDouble( double fValue ) const +{ + return GetDouble() && (*GetDouble() == fValue); +} + +bool XclExpPCItem::EqualsDateTime( const DateTime& rDateTime ) const +{ + return GetDateTime() && (*GetDateTime() == rDateTime); +} + +bool XclExpPCItem::EqualsBool( bool bValue ) const +{ + return GetBool() && (*GetBool() == bValue); +} + +// ---------------------------------------------------------------------------- + +void XclExpPCItem::WriteBody( XclExpStream& rStrm ) +{ + if( const String* pText = GetText() ) + { + rStrm << XclExpString( *pText ); + } + else if( const double* pfValue = GetDouble() ) + { + rStrm << *pfValue; + } + else if( const sal_Int16* pnValue = GetInteger() ) + { + rStrm << *pnValue; + } + else if( const DateTime* pDateTime = GetDateTime() ) + { + sal_uInt16 nYear = static_cast< sal_uInt16 >( pDateTime->GetYear() ); + sal_uInt16 nMonth = static_cast< sal_uInt16 >( pDateTime->GetMonth() ); + sal_uInt8 nDay = static_cast< sal_uInt8 >( pDateTime->GetDay() ); + sal_uInt8 nHour = static_cast< sal_uInt8 >( pDateTime->GetHour() ); + sal_uInt8 nMin = static_cast< sal_uInt8 >( pDateTime->GetMin() ); + sal_uInt8 nSec = static_cast< sal_uInt8 >( pDateTime->GetSec() ); + if( nYear < 1900 ) { nYear = 1900; nMonth = 1; nDay = 0; } + rStrm << nYear << nMonth << nDay << nHour << nMin << nSec; + } + else if( const bool* pbValue = GetBool() ) + { + rStrm << static_cast< sal_uInt16 >( *pbValue ? 1 : 0 ); + } + else + { + // nothing to do for SXEMPTY + DBG_ASSERT( IsEmpty(), "XclExpPCItem::WriteBody - no data found" ); + } +} + +// ============================================================================ + +XclExpPCField::XclExpPCField( + const XclExpRoot& rRoot, const XclExpPivotCache& rPCache, sal_uInt16 nFieldIdx, + const ScDPObject& rDPObj, const ScRange& rRange ) : + XclExpRecord( EXC_ID_SXFIELD ), + XclPCField( EXC_PCFIELD_STANDARD, nFieldIdx ), + XclExpRoot( rRoot ), + mrPCache( rPCache ), + mnTypeFlags( 0 ) +{ + // general settings for the standard field, insert all items from source range + InitStandardField( rRange ); + + // add special settings for inplace numeric grouping + if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() ) + { + if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() ) + { + if( const ScDPSaveNumGroupDimension* pNumGroupDim = pSaveDimData->GetNumGroupDim( GetFieldName() ) ) + { + const ScDPNumGroupInfo& rNumInfo = pNumGroupDim->GetInfo(); + const ScDPNumGroupInfo& rDateInfo = pNumGroupDim->GetDateInfo(); + DBG_ASSERT( !rNumInfo.Enable || !rDateInfo.Enable, + "XclExpPCField::XclExpPCField - numeric and date grouping enabled" ); + + if( rNumInfo.Enable ) + InitNumGroupField( rDPObj, rNumInfo ); + else if( rDateInfo.Enable ) + InitDateGroupField( rDPObj, rDateInfo, pNumGroupDim->GetDatePart() ); + } + } + } + + // final settings (flags, item numbers) + Finalize(); +} + +XclExpPCField::XclExpPCField( + const XclExpRoot& rRoot, const XclExpPivotCache& rPCache, sal_uInt16 nFieldIdx, + const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim, const XclExpPCField& rBaseField ) : + XclExpRecord( EXC_ID_SXFIELD ), + XclPCField( EXC_PCFIELD_STDGROUP, nFieldIdx ), + XclExpRoot( rRoot ), + mrPCache( rPCache ), + mnTypeFlags( 0 ) +{ + // add base field info (always using first base field, not predecessor of this field) *** + DBG_ASSERT( rBaseField.GetFieldName() == rGroupDim.GetSourceDimName(), + "XclExpPCField::FillFromGroup - wrong base cache field" ); + maFieldInfo.maName = rGroupDim.GetGroupDimName(); + maFieldInfo.mnGroupBase = rBaseField.GetFieldIndex(); + + // add standard group info or date group info + const ScDPNumGroupInfo& rDateInfo = rGroupDim.GetDateInfo(); + if( rDateInfo.Enable && (rGroupDim.GetDatePart() != 0) ) + InitDateGroupField( rDPObj, rDateInfo, rGroupDim.GetDatePart() ); + else + InitStdGroupField( rBaseField, rGroupDim ); + + // final settings (flags, item numbers) + Finalize(); +} + +XclExpPCField::~XclExpPCField() +{ +} + +void XclExpPCField::SetGroupChildField( const XclExpPCField& rChildField ) +{ + DBG_ASSERT( !::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ), + "XclExpPCField::SetGroupChildIndex - field already has a grouping child field" ); + ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ); + maFieldInfo.mnGroupChild = rChildField.GetFieldIndex(); +} + +sal_uInt16 XclExpPCField::GetItemCount() const +{ + return static_cast< sal_uInt16 >( GetVisItemList().GetSize() ); +} + +const XclExpPCItem* XclExpPCField::GetItem( sal_uInt16 nItemIdx ) const +{ + return GetVisItemList().GetRecord( nItemIdx ).get(); +} + +sal_uInt16 XclExpPCField::GetItemIndex( const String& rItemName ) const +{ + const XclExpPCItemList& rItemList = GetVisItemList(); + for( size_t nPos = 0, nSize = rItemList.GetSize(); nPos < nSize; ++nPos ) + if( rItemList.GetRecord( nPos )->ConvertToText() == rItemName ) + return static_cast< sal_uInt16 >( nPos ); + return EXC_PC_NOITEM; +} + +sal_Size XclExpPCField::GetIndexSize() const +{ + return Has16BitIndexes() ? 2 : 1; +} + +void XclExpPCField::WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const +{ + // only standard fields write item indexes + if( nSrcRow < maIndexVec.size() ) + { + sal_uInt16 nIndex = maIndexVec[ nSrcRow ]; + if( Has16BitIndexes() ) + rStrm << nIndex; + else + rStrm << static_cast< sal_uInt8 >( nIndex ); + } +} + +void XclExpPCField::Save( XclExpStream& rStrm ) +{ + DBG_ASSERT( IsSupportedField(), "XclExpPCField::Save - unknown field type" ); + // SXFIELD + XclExpRecord::Save( rStrm ); + // SXFDBTYPE + XclExpUInt16Record( EXC_ID_SXFDBTYPE, EXC_SXFDBTYPE_DEFAULT ).Save( rStrm ); + // list of grouping items + maGroupItemList.Save( rStrm ); + // SXGROUPINFO + WriteSxgroupinfo( rStrm ); + // SXNUMGROUP and additional grouping items (grouping limit settings) + WriteSxnumgroup( rStrm ); + // list of original items + maOrigItemList.Save( rStrm ); +} + +// private -------------------------------------------------------------------- + +const XclExpPCField::XclExpPCItemList& XclExpPCField::GetVisItemList() const +{ + DBG_ASSERT( IsStandardField() == maGroupItemList.IsEmpty(), + "XclExpPCField::GetVisItemList - unexpected additional items in standard field" ); + return IsStandardField() ? maOrigItemList : maGroupItemList; +} + +void XclExpPCField::InitStandardField( const ScRange& rRange ) +{ + DBG_ASSERT( IsStandardField(), "XclExpPCField::InitStandardField - only for standard fields" ); + DBG_ASSERT( rRange.aStart.Col() == rRange.aEnd.Col(), "XclExpPCField::InitStandardField - cell range with multiple columns" ); + + ScDocument& rDoc = GetDoc(); + SvNumberFormatter& rFormatter = GetFormatter(); + + // field name is in top cell of the range + ScAddress aPos( rRange.aStart ); + rDoc.GetString( aPos.Col(), aPos.Row(), aPos.Tab(), maFieldInfo.maName ); + // #i76047# maximum field name length in pivot cache is 255 + maFieldInfo.maName.Erase( ::std::min( maFieldInfo.maName.Len(), EXC_PC_MAXSTRLEN ) ); + + // loop over all cells, create pivot cache items + for( aPos.IncRow(); (aPos.Row() <= rRange.aEnd.Row()) && (maOrigItemList.GetSize() < EXC_PC_MAXITEMCOUNT); aPos.IncRow() ) + { + if( rDoc.HasValueData( aPos.Col(), aPos.Row(), aPos.Tab() ) ) + { + double fValue = rDoc.GetValue( aPos ); + short nFmtType = rFormatter.GetType( rDoc.GetNumberFormat( aPos ) ); + if( nFmtType == NUMBERFORMAT_LOGICAL ) + InsertOrigBoolItem( fValue != 0 ); + else if( nFmtType & NUMBERFORMAT_DATETIME ) + InsertOrigDateTimeItem( GetDateTimeFromDouble( ::std::max( fValue, 0.0 ) ) ); + else + InsertOrigDoubleItem( fValue ); + } + else + { + String aText; + rDoc.GetString( aPos.Col(), aPos.Row(), aPos.Tab(), aText ); + InsertOrigTextItem( aText ); + } + } +} + +void XclExpPCField::InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim ) +{ + DBG_ASSERT( IsGroupField(), "XclExpPCField::InitStdGroupField - only for standard grouping fields" ); + + maFieldInfo.mnBaseItems = rBaseField.GetItemCount(); + maGroupOrder.resize( maFieldInfo.mnBaseItems, EXC_PC_NOITEM ); + + // loop over all groups of this field + for( long nGroupIdx = 0, nGroupCount = rGroupDim.GetGroupCount(); nGroupIdx < nGroupCount; ++nGroupIdx ) + { + if( const ScDPSaveGroupItem* pGroupItem = rGroupDim.GetGroupByIndex( nGroupIdx ) ) + { + // the index of the new item containing the grouping name + sal_uInt16 nGroupItemIdx = EXC_PC_NOITEM; + // loop over all elements of one group + for( size_t nElemIdx = 0, nElemCount = pGroupItem->GetElementCount(); nElemIdx < nElemCount; ++nElemIdx ) + { + if( const String* pElemName = pGroupItem->GetElementByIndex( nElemIdx ) ) + { + // try to find the item that is part of the group in the base field + sal_uInt16 nBaseItemIdx = rBaseField.GetItemIndex( *pElemName ); + if( nBaseItemIdx < maFieldInfo.mnBaseItems ) + { + // add group name item only if there are any valid base items + if( nGroupItemIdx == EXC_PC_NOITEM ) + nGroupItemIdx = InsertGroupItem( new XclExpPCItem( pGroupItem->GetGroupName() ) ); + maGroupOrder[ nBaseItemIdx ] = nGroupItemIdx; + } + } + } + } + } + + // add items and base item indexes of all ungrouped elements + for( sal_uInt16 nBaseItemIdx = 0; nBaseItemIdx < maFieldInfo.mnBaseItems; ++nBaseItemIdx ) + // items that are not part of a group still have the EXC_PC_NOITEM entry + if( maGroupOrder[ nBaseItemIdx ] == EXC_PC_NOITEM ) + // try to find the base item + if( const XclExpPCItem* pBaseItem = rBaseField.GetItem( nBaseItemIdx ) ) + // create a clone of the base item, insert its index into item order list + maGroupOrder[ nBaseItemIdx ] = InsertGroupItem( new XclExpPCItem( *pBaseItem ) ); +} + +void XclExpPCField::InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo ) +{ + DBG_ASSERT( IsStandardField(), "XclExpPCField::InitNumGroupField - only for standard fields" ); + DBG_ASSERT( rNumInfo.Enable, "XclExpPCField::InitNumGroupField - numeric grouping not enabled" ); + + // new field type, date type, limit settings (min/max/step/auto) + if( rNumInfo.DateValues ) + { + // special case: group by days with step count + meFieldType = EXC_PCFIELD_DATEGROUP; + maNumGroupInfo.SetScDateType( com::sun::star::sheet::DataPilotFieldGroupBy::DAYS ); + SetDateGroupLimit( rNumInfo, true ); + } + else + { + meFieldType = EXC_PCFIELD_NUMGROUP; + maNumGroupInfo.SetNumType(); + SetNumGroupLimit( rNumInfo ); + } + + // generate visible items + InsertNumDateGroupItems( rDPObj, rNumInfo ); +} + +void XclExpPCField::InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart ) +{ + DBG_ASSERT( IsStandardField() || IsStdGroupField(), "XclExpPCField::InitDateGroupField - only for standard fields" ); + DBG_ASSERT( rDateInfo.Enable, "XclExpPCField::InitDateGroupField - date grouping not enabled" ); + + // new field type + meFieldType = IsStandardField() ? EXC_PCFIELD_DATEGROUP : EXC_PCFIELD_DATECHILD; + + // date type, limit settings (min/max/step/auto) + maNumGroupInfo.SetScDateType( nDatePart ); + SetDateGroupLimit( rDateInfo, false ); + + // generate visible items + InsertNumDateGroupItems( rDPObj, rDateInfo, nDatePart ); +} + +void XclExpPCField::InsertItemArrayIndex( size_t nListPos ) +{ + DBG_ASSERT( IsStandardField(), "XclExpPCField::InsertItemArrayIndex - only for standard fields" ); + maIndexVec.push_back( static_cast< sal_uInt16 >( nListPos ) ); +} + +void XclExpPCField::InsertOrigItem( XclExpPCItem* pNewItem ) +{ + size_t nItemIdx = maOrigItemList.GetSize(); + maOrigItemList.AppendNewRecord( pNewItem ); + InsertItemArrayIndex( nItemIdx ); + mnTypeFlags |= pNewItem->GetTypeFlag(); +} + +void XclExpPCField::InsertOrigTextItem( const String& rText ) +{ + size_t nPos = 0; + bool bFound = false; + // #i76047# maximum item text length in pivot cache is 255 + String aShortText( rText, 0, ::std::min( rText.Len(), EXC_PC_MAXSTRLEN ) ); + for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos ) + if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsText( aShortText )) == true ) + InsertItemArrayIndex( nPos ); + if( !bFound ) + InsertOrigItem( new XclExpPCItem( aShortText ) ); +} + +void XclExpPCField::InsertOrigDoubleItem( double fValue ) +{ + size_t nPos = 0; + bool bFound = false; + for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos ) + if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDouble( fValue )) == true ) + InsertItemArrayIndex( nPos ); + if( !bFound ) + InsertOrigItem( new XclExpPCItem( fValue ) ); +} + +void XclExpPCField::InsertOrigDateTimeItem( const DateTime& rDateTime ) +{ + size_t nPos = 0; + bool bFound = false; + for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos ) + if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDateTime( rDateTime )) == true ) + InsertItemArrayIndex( nPos ); + if( !bFound ) + InsertOrigItem( new XclExpPCItem( rDateTime ) ); +} + +void XclExpPCField::InsertOrigBoolItem( bool bValue ) +{ + size_t nPos = 0; + bool bFound = false; + for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos ) + if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsBool( bValue )) == true ) + InsertItemArrayIndex( nPos ); + if( !bFound ) + InsertOrigItem( new XclExpPCItem( bValue ) ); +} + +sal_uInt16 XclExpPCField::InsertGroupItem( XclExpPCItem* pNewItem ) +{ + maGroupItemList.AppendNewRecord( pNewItem ); + return static_cast< sal_uInt16 >( maGroupItemList.GetSize() - 1 ); +} + +void XclExpPCField::InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart ) +{ + DBG_ASSERT( rDPObj.GetSheetDesc(), "XclExpPCField::InsertNumDateGroupItems - cannot generate element list" ); + if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() ) + { + // get the string collection with original source elements + ScSheetDPData aDPData( GetDocPtr(), *pSrcDesc ); + // Wang Xu Ming - DataPilot migration + // 2009-05-08 + const std::vector< SCROW > aOrignial = aDPData.GetColumnEntries( static_cast< long >( GetBaseFieldIndex() ) ); + // get the string collection with generated grouping elements + ScDPNumGroupDimension aTmpDim( rNumInfo ); + if( nDatePart != 0 ) + aTmpDim.MakeDateHelper( rNumInfo, nDatePart ); + const std::vector< SCROW > aMemberIds = aTmpDim.GetNumEntries( static_cast< SCCOL >( GetBaseFieldIndex() ), aDPData.GetCacheTable().GetCache(), aOrignial ); + for ( size_t nIdx = 0 ; nIdx < aMemberIds.size(); nIdx++ ) + { + const ScDPItemData* pData = aDPData.GetMemberById( static_cast< long >( GetBaseFieldIndex() ) , aMemberIds[ nIdx] ); + if ( pData ) + InsertGroupItem( new XclExpPCItem( pData->GetString() ) ); + } +// End Comments + } +} + +void XclExpPCField::SetNumGroupLimit( const ScDPNumGroupInfo& rNumInfo ) +{ + ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rNumInfo.AutoStart ); + ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rNumInfo.AutoEnd ); + maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.Start ) ); + maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.End ) ); + maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.Step ) ); +} + +void XclExpPCField::SetDateGroupLimit( const ScDPNumGroupInfo& rDateInfo, bool bUseStep ) +{ + ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rDateInfo.AutoStart ); + ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rDateInfo.AutoEnd ); + maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.Start ) ) ); + maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.End ) ) ); + sal_Int16 nStep = bUseStep ? limit_cast< sal_Int16 >( rDateInfo.Step, 1, SAL_MAX_INT16 ) : 1; + maNumGroupLimits.AppendNewRecord( new XclExpPCItem( nStep ) ); +} + +void XclExpPCField::Finalize() +{ + // flags + ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS, !GetVisItemList().IsEmpty() ); + // Excel writes long indexes even for 0x0100 items (indexes from 0x00 to 0xFF) + ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT, maOrigItemList.GetSize() >= 0x0100 ); + ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP, IsNumGroupField() || IsDateGroupField() ); + /* mnTypeFlags is updated in all Insert***Item() functions. Now the flags + for the current combination of item types is added to the flags. */ + ::set_flag( maFieldInfo.mnFlags, spnPCItemFlags[ mnTypeFlags ] ); + + // item count fields + maFieldInfo.mnVisItems = static_cast< sal_uInt16 >( GetVisItemList().GetSize() ); + maFieldInfo.mnGroupItems = static_cast< sal_uInt16 >( maGroupItemList.GetSize() ); + // maFieldInfo.mnBaseItems set in InitStdGroupField() + maFieldInfo.mnOrigItems = static_cast< sal_uInt16 >( maOrigItemList.GetSize() ); +} + +void XclExpPCField::WriteSxnumgroup( XclExpStream& rStrm ) +{ + if( IsNumGroupField() || IsDateGroupField() ) + { + // SXNUMGROUP record + rStrm.StartRecord( EXC_ID_SXNUMGROUP, 2 ); + rStrm << maNumGroupInfo; + rStrm.EndRecord(); + + // limits (min/max/step) for numeric grouping + DBG_ASSERT( maNumGroupLimits.GetSize() == 3, + "XclExpPCField::WriteSxnumgroup - missing numeric grouping limits" ); + maNumGroupLimits.Save( rStrm ); + } +} + +void XclExpPCField::WriteSxgroupinfo( XclExpStream& rStrm ) +{ + DBG_ASSERT( IsStdGroupField() != maGroupOrder.empty(), + "XclExpPCField::WriteSxgroupinfo - missing grouping info" ); + if( IsStdGroupField() && !maGroupOrder.empty() ) + { + rStrm.StartRecord( EXC_ID_SXGROUPINFO, 2 * maGroupOrder.size() ); + for( ScfUInt16Vec::const_iterator aIt = maGroupOrder.begin(), aEnd = maGroupOrder.end(); aIt != aEnd; ++aIt ) + rStrm << *aIt; + rStrm.EndRecord(); + } +} + +void XclExpPCField::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maFieldInfo; +} + +// ============================================================================ + +XclExpPivotCache::XclExpPivotCache( const XclExpRoot& rRoot, const ScDPObject& rDPObj, sal_uInt16 nListIdx ) : + XclExpRoot( rRoot ), + mnListIdx( nListIdx ), + mbValid( false ) +{ + // source from sheet only + if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() ) + { + /* maOrigSrcRange: Range received from the DataPilot object. + maExpSrcRange: Range written to the DCONREF record. + maDocSrcRange: Range used to get source data from Calc document. + This range may be shorter than maExpSrcRange to improve export + performance (#i22541#). */ + maOrigSrcRange = maExpSrcRange = maDocSrcRange = pSrcDesc->aSourceRange; + + // internal sheet data only + SCTAB nScTab = maExpSrcRange.aStart.Tab(); + if( (nScTab == maExpSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( nScTab ) ) + { + // ValidateRange() restricts source range to valid Excel limits + if( GetAddressConverter().ValidateRange( maExpSrcRange, true ) ) + { + // #i22541# skip empty cell areas (performance) + SCCOL nDocCol1, nDocCol2; + SCROW nDocRow1, nDocRow2; + GetDoc().GetDataStart( nScTab, nDocCol1, nDocRow1 ); + GetDoc().GetPrintArea( nScTab, nDocCol2, nDocRow2, false ); + SCCOL nSrcCol1 = maExpSrcRange.aStart.Col(); + SCROW nSrcRow1 = maExpSrcRange.aStart.Row(); + SCCOL nSrcCol2 = maExpSrcRange.aEnd.Col(); + SCROW nSrcRow2 = maExpSrcRange.aEnd.Row(); + + // #i22541# do not store index list for too big ranges + if( 2 * (nDocRow2 - nDocRow1) < (nSrcRow2 - nSrcRow1) ) + ::set_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA, false ); + + // #160184# Excel must refresh tables to make drilldown working + ::set_flag( maPCInfo.mnFlags, EXC_SXDB_REFRESH_LOAD ); + + // adjust row indexes, keep one row of empty area to surely have the empty cache item + if( nSrcRow1 < nDocRow1 ) + nSrcRow1 = nDocRow1 - 1; + if( nSrcRow2 > nDocRow2 ) + nSrcRow2 = nDocRow2 + 1; + + maDocSrcRange.aStart.SetCol( ::std::max( nDocCol1, nSrcCol1 ) ); + maDocSrcRange.aStart.SetRow( nSrcRow1 ); + maDocSrcRange.aEnd.SetCol( ::std::min( nDocCol2, nSrcCol2 ) ); + maDocSrcRange.aEnd.SetRow( nSrcRow2 ); + + GetDoc().GetName( nScTab, maTabName ); + maPCInfo.mnSrcRecs = static_cast< sal_uInt32 >( maExpSrcRange.aEnd.Row() - maExpSrcRange.aStart.Row() ); + maPCInfo.mnStrmId = nListIdx + 1; + maPCInfo.mnSrcType = EXC_SXDB_SRC_SHEET; + + AddFields( rDPObj ); + + mbValid = true; + } + } + } +} + +bool XclExpPivotCache::HasItemIndexList() const +{ + return ::get_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA ); +} + +sal_uInt16 XclExpPivotCache::GetFieldCount() const +{ + return static_cast< sal_uInt16 >( maFieldList.GetSize() ); +} + +const XclExpPCField* XclExpPivotCache::GetField( sal_uInt16 nFieldIdx ) const +{ + return maFieldList.GetRecord( nFieldIdx ).get(); +} + +//UNUSED2009-05 const XclExpPCField* XclExpPivotCache::GetField( const String& rFieldName ) const +//UNUSED2009-05 { +//UNUSED2009-05 return const_cast< XclExpPivotCache* >( this )->GetFieldAcc( rFieldName ); +//UNUSED2009-05 } + +bool XclExpPivotCache::HasAddFields() const +{ + // pivot cache can be shared, if there are no additional cache fields + return maPCInfo.mnStdFields < maPCInfo.mnTotalFields; +} + +bool XclExpPivotCache::HasEqualDataSource( const ScDPObject& rDPObj ) const +{ + /* For now, only sheet sources are supported, therefore it is enough to + compare the ScSheetSourceDesc. Later, there should be done more complicated + comparisons regarding the source type of rDPObj and this cache. */ + if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() ) + return pSrcDesc->aSourceRange == maOrigSrcRange; + return false; +} + +void XclExpPivotCache::Save( XclExpStream& rStrm ) +{ + DBG_ASSERT( mbValid, "XclExpPivotCache::Save - invalid pivot cache" ); + // SXIDSTM + XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).Save( rStrm ); + // SXVS + XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).Save( rStrm ); + // DCONREF + WriteDconref( rStrm ); + // create the pivot cache storage stream + WriteCacheStream(); +} + +void XclExpPivotCache::SaveXml( XclExpXmlStream& rStrm ) +{ + DBG_ASSERT( mbValid, "XclExpPivotCache::Save - invalid pivot cache" ); + sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream(); + OUString sId = OUStringBuffer() + .appendAscii("rId") + .append( rStrm.GetUniqueIdOUString() ) + .makeStringAndClear(); + rWorkbook->startElement( XML_pivotCache, + XML_cacheId, OString::valueOf( (sal_Int32)maPCInfo.mnStrmId ).getStr(), + FSNS( XML_r, XML_id ), XclXmlUtils::ToOString( sId ).getStr(), + FSEND ); + // SXIDSTM + XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).SaveXml( rStrm ); + // SXVS + XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).SaveXml( rStrm ); + // DCONREF + // OOXTODO: WriteDconref( rStrm ); + // create the pivot cache storage stream + // OOXTODO: WriteCacheStream(); + rWorkbook->endElement( XML_pivotCache ); +} + +// private -------------------------------------------------------------------- + +XclExpPCField* XclExpPivotCache::GetFieldAcc( sal_uInt16 nFieldIdx ) +{ + return maFieldList.GetRecord( nFieldIdx ).get(); +} + +XclExpPCField* XclExpPivotCache::GetFieldAcc( const String& rFieldName ) +{ + XclExpPCField* pField = 0; + for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos ) + if( maFieldList.GetRecord( nPos )->GetFieldName() == rFieldName ) + pField = maFieldList.GetRecord( nPos ).get(); + return pField; +} + +void XclExpPivotCache::AddFields( const ScDPObject& rDPObj ) +{ + AddStdFields( rDPObj ); + maPCInfo.mnStdFields = GetFieldCount(); + AddGroupFields( rDPObj ); + AddCalcFields( rDPObj ); + maPCInfo.mnTotalFields = GetFieldCount(); +}; + +void XclExpPivotCache::AddStdFields( const ScDPObject& rDPObj ) +{ + // if item index list is not written, used shortened source range (maDocSrcRange) for performance + const ScRange& rRange = HasItemIndexList() ? maExpSrcRange : maDocSrcRange; + // create a standard pivot cache field for each source column + for( SCCOL nScCol = rRange.aStart.Col(), nEndScCol = rRange.aEnd.Col(); nScCol <= nEndScCol; ++nScCol ) + { + ScRange aColRange( rRange ); + aColRange.aStart.SetCol( nScCol ); + aColRange.aEnd.SetCol( nScCol ); + maFieldList.AppendNewRecord( new XclExpPCField( + GetRoot(), *this, GetFieldCount(), rDPObj, aColRange ) ); + } +} + +void XclExpPivotCache::AddGroupFields( const ScDPObject& rDPObj ) +{ + if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() ) + { + if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() ) + { + // loop over all existing standard fields to find their group fields + for( sal_uInt16 nFieldIdx = 0; nFieldIdx < maPCInfo.mnStdFields; ++nFieldIdx ) + { + if( XclExpPCField* pCurrStdField = GetFieldAcc( nFieldIdx ) ) + { + const ScDPSaveGroupDimension* pGroupDim = pSaveDimData->GetGroupDimForBase( pCurrStdField->GetFieldName() ); + XclExpPCField* pLastGroupField = pCurrStdField; + while( pGroupDim ) + { + // insert the new grouping field + XclExpPCFieldRef xNewGroupField( new XclExpPCField( + GetRoot(), *this, GetFieldCount(), rDPObj, *pGroupDim, *pCurrStdField ) ); + maFieldList.AppendRecord( xNewGroupField ); + + // register new grouping field at current grouping field, building a chain + pLastGroupField->SetGroupChildField( *xNewGroupField ); + + // next grouping dimension + pGroupDim = pSaveDimData->GetGroupDimForBase( pGroupDim->GetGroupDimName() ); + pLastGroupField = xNewGroupField.get(); + } + } + } + } + } +} + +void XclExpPivotCache::AddCalcFields( const ScDPObject& /*rDPObj*/ ) +{ + // not supported +} + +void XclExpPivotCache::WriteDconref( XclExpStream& rStrm ) const +{ + XclExpString aRef( XclExpUrlHelper::EncodeUrl( GetRoot(), EMPTY_STRING, &maTabName ) ); + rStrm.StartRecord( EXC_ID_DCONREF, 7 + aRef.GetSize() ); + rStrm << static_cast< sal_uInt16 >( maExpSrcRange.aStart.Row() ) + << static_cast< sal_uInt16 >( maExpSrcRange.aEnd.Row() ) + << static_cast< sal_uInt8 >( maExpSrcRange.aStart.Col() ) + << static_cast< sal_uInt8 >( maExpSrcRange.aEnd.Col() ) + << aRef + << sal_uInt8( 0 ); + rStrm.EndRecord(); +} + +void XclExpPivotCache::WriteCacheStream() +{ + SotStorageRef xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE ); + SotStorageStreamRef xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( maPCInfo.mnStrmId ) ); + if( xSvStrm.Is() ) + { + XclExpStream aStrm( *xSvStrm, GetRoot() ); + // SXDB + WriteSxdb( aStrm ); + // SXDBEX + WriteSxdbex( aStrm ); + // field list (SXFIELD and items) + maFieldList.Save( aStrm ); + // index table (list of SXINDEXLIST) + WriteSxindexlistList( aStrm ); + // EOF + XclExpEmptyRecord( EXC_ID_EOF ).Save( aStrm ); + } +} + +void XclExpPivotCache::WriteSxdb( XclExpStream& rStrm ) const +{ + rStrm.StartRecord( EXC_ID_SXDB, 21 ); + rStrm << maPCInfo; + rStrm.EndRecord(); +} + +void XclExpPivotCache::WriteSxdbex( XclExpStream& rStrm ) const +{ + rStrm.StartRecord( EXC_ID_SXDBEX, 12 ); + rStrm << EXC_SXDBEX_CREATION_DATE + << sal_uInt32( 0 ); // number of SXFORMULA records + rStrm.EndRecord(); +} + +void XclExpPivotCache::WriteSxindexlistList( XclExpStream& rStrm ) const +{ + if( HasItemIndexList() ) + { + sal_Size nRecSize = 0; + size_t nPos, nSize = maFieldList.GetSize(); + for( nPos = 0; nPos < nSize; ++nPos ) + nRecSize += maFieldList.GetRecord( nPos )->GetIndexSize(); + + for( sal_uInt32 nSrcRow = 0; nSrcRow < maPCInfo.mnSrcRecs; ++nSrcRow ) + { + rStrm.StartRecord( EXC_ID_SXINDEXLIST, nRecSize ); + for( nPos = 0; nPos < nSize; ++nPos ) + maFieldList.GetRecord( nPos )->WriteIndex( rStrm, nSrcRow ); + rStrm.EndRecord(); + } + } +} + +// ============================================================================ +// Pivot table +// ============================================================================ + +namespace { + +// ---------------------------------------------------------------------------- + +/** Returns a display string for a data field containing the field name and aggregation function. */ +String lclGetDataFieldCaption( const String& rFieldName, GeneralFunction eFunc ) +{ + String aCaption; + + USHORT nResIdx = 0; + using namespace ::com::sun::star::sheet; + switch( eFunc ) + { + case GeneralFunction_SUM: nResIdx = STR_FUN_TEXT_SUM; break; + case GeneralFunction_COUNT: nResIdx = STR_FUN_TEXT_COUNT; break; + case GeneralFunction_AVERAGE: nResIdx = STR_FUN_TEXT_AVG; break; + case GeneralFunction_MAX: nResIdx = STR_FUN_TEXT_MAX; break; + case GeneralFunction_MIN: nResIdx = STR_FUN_TEXT_MIN; break; + case GeneralFunction_PRODUCT: nResIdx = STR_FUN_TEXT_PRODUCT; break; + case GeneralFunction_COUNTNUMS: nResIdx = STR_FUN_TEXT_COUNT; break; + case GeneralFunction_STDEV: nResIdx = STR_FUN_TEXT_STDDEV; break; + case GeneralFunction_STDEVP: nResIdx = STR_FUN_TEXT_STDDEV; break; + case GeneralFunction_VAR: nResIdx = STR_FUN_TEXT_VAR; break; + case GeneralFunction_VARP: nResIdx = STR_FUN_TEXT_VAR; break; + default:; + } + if( nResIdx ) + aCaption.Assign( ScGlobal::GetRscString( nResIdx ) ).AppendAscii( RTL_CONSTASCII_STRINGPARAM( " - " ) ); + aCaption.Append( rFieldName ); + return aCaption; +} + +// ---------------------------------------------------------------------------- + +} // namespace + +// ============================================================================ + +XclExpPTItem::XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx ) : + XclExpRecord( EXC_ID_SXVI, 8 ), + mpCacheItem( rCacheField.GetItem( nCacheIdx ) ) +{ + maItemInfo.mnType = EXC_SXVI_TYPE_DATA; + maItemInfo.mnCacheIdx = nCacheIdx; + maItemInfo.maVisName.mbUseCache = mpCacheItem != 0; +} + +XclExpPTItem::XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx, bool bUseCache ) : + XclExpRecord( EXC_ID_SXVI, 8 ), + mpCacheItem( 0 ) +{ + maItemInfo.mnType = nItemType; + maItemInfo.mnCacheIdx = nCacheIdx; + maItemInfo.maVisName.mbUseCache = bUseCache; +} + +const String& XclExpPTItem::GetItemName() const +{ + return mpCacheItem ? mpCacheItem->ConvertToText() : EMPTY_STRING; +} + +void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem ) +{ + ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, !rSaveMem.GetIsVisible() ); + ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, !rSaveMem.GetShowDetails() ); + + // visible name + const OUString* pVisName = rSaveMem.GetLayoutName(); + if (pVisName && !pVisName->equals(GetItemName())) + maItemInfo.SetVisName(*pVisName); +} + +void XclExpPTItem::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maItemInfo; +} + +// ============================================================================ + +XclExpPTField::XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx ) : + mrPTable( rPTable ), + mpCacheField( rPTable.GetCacheField( nCacheIdx ) ) +{ + maFieldInfo.mnCacheIdx = nCacheIdx; + + // create field items + if( mpCacheField ) + for( sal_uInt16 nItemIdx = 0, nItemCount = mpCacheField->GetItemCount(); nItemIdx < nItemCount; ++nItemIdx ) + maItemList.AppendNewRecord( new XclExpPTItem( *mpCacheField, nItemIdx ) ); + maFieldInfo.mnItemCount = static_cast< sal_uInt16 >( maItemList.GetSize() ); +} + +// data access ---------------------------------------------------------------- + +const String& XclExpPTField::GetFieldName() const +{ + return mpCacheField ? mpCacheField->GetFieldName() : EMPTY_STRING; +} + +sal_uInt16 XclExpPTField::GetFieldIndex() const +{ + // field index always equal to cache index + return maFieldInfo.mnCacheIdx; +} + +sal_uInt16 XclExpPTField::GetLastDataInfoIndex() const +{ + DBG_ASSERT( !maDataInfoVec.empty(), "XclExpPTField::GetLastDataInfoIndex - no data info found" ); + // will return 0xFFFF for empty vector -> ok + return static_cast< sal_uInt16 >( maDataInfoVec.size() - 1 ); +} + +//UNUSED2009-05 const XclExpPTItem* XclExpPTField::GetItem( const String& rName ) const +//UNUSED2009-05 { +//UNUSED2009-05 return const_cast< XclExpPTField* >( this )->GetItemAcc( rName ); +//UNUSED2009-05 } + +sal_uInt16 XclExpPTField::GetItemIndex( const String& rName, sal_uInt16 nDefaultIdx ) const +{ + for( size_t nPos = 0, nSize = maItemList.GetSize(); nPos < nSize; ++nPos ) + if( maItemList.GetRecord( nPos )->GetItemName() == rName ) + return static_cast< sal_uInt16 >( nPos ); + return nDefaultIdx; +} + +// fill data -------------------------------------------------------------- + +/** + * Calc's subtotal names are escaped with backslashes ('\'), while Excel's + * are not escaped at all. + */ +static OUString lcl_convertCalcSubtotalName(const OUString& rName) +{ + OUStringBuffer aBuf; + const sal_Unicode* p = rName.getStr(); + sal_Int32 n = rName.getLength(); + bool bEscaped = false; + for (sal_Int32 i = 0; i < n; ++i) + { + const sal_Unicode c = p[i]; + if (!bEscaped && c == sal_Unicode('\\')) + { + bEscaped = true; + continue; + } + + aBuf.append(c); + bEscaped = false; + } + return aBuf.makeStringAndClear(); +} + +void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim ) +{ + // orientation + DataPilotFieldOrientation eOrient = static_cast< DataPilotFieldOrientation >( rSaveDim.GetOrientation() ); + DBG_ASSERT( eOrient != DataPilotFieldOrientation_DATA, "XclExpPTField::SetPropertiesFromDim - called for data field" ); + maFieldInfo.AddApiOrient( eOrient ); + + // show empty items + ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.GetShowEmpty() ); + + // visible name + const OUString* pLayoutName = rSaveDim.GetLayoutName(); + if (pLayoutName && !pLayoutName->equals(GetFieldName())) + maFieldInfo.SetVisName(*pLayoutName); + + const rtl::OUString* pSubtotalName = rSaveDim.GetSubtotalName(); + if (pSubtotalName) + { + OUString aSubName = lcl_convertCalcSubtotalName(*pSubtotalName); + maFieldExtInfo.mpFieldTotalName.reset(new rtl::OUString(aSubName)); + } + + // subtotals + XclPTSubtotalVec aSubtotals; + aSubtotals.reserve( static_cast< size_t >( rSaveDim.GetSubTotalsCount() ) ); + for( long nSubtIdx = 0, nSubtCount = rSaveDim.GetSubTotalsCount(); nSubtIdx < nSubtCount; ++nSubtIdx ) + aSubtotals.push_back( rSaveDim.GetSubTotalFunc( nSubtIdx ) ); + maFieldInfo.SetSubtotals( aSubtotals ); + + // sorting + if( const DataPilotFieldSortInfo* pSortInfo = rSaveDim.GetSortInfo() ) + { + maFieldExtInfo.SetApiSortMode( pSortInfo->Mode ); + if( pSortInfo->Mode == ::com::sun::star::sheet::DataPilotFieldSortMode::DATA ) + maFieldExtInfo.mnSortField = mrPTable.GetDataFieldIndex( pSortInfo->Field, EXC_SXVDEX_SORT_OWN ); + ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC, pSortInfo->IsAscending ); + } + + // auto show + if( const DataPilotFieldAutoShowInfo* pShowInfo = rSaveDim.GetAutoShowInfo() ) + { + ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW, pShowInfo->IsEnabled ); + maFieldExtInfo.SetApiAutoShowMode( pShowInfo->ShowItemsMode ); + maFieldExtInfo.SetApiAutoShowCount( pShowInfo->ItemCount ); + maFieldExtInfo.mnShowField = mrPTable.GetDataFieldIndex( pShowInfo->DataField, EXC_SXVDEX_SHOW_NONE ); + } + + // layout + if( const DataPilotFieldLayoutInfo* pLayoutInfo = rSaveDim.GetLayoutInfo() ) + { + maFieldExtInfo.SetApiLayoutMode( pLayoutInfo->LayoutMode ); + ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK, pLayoutInfo->AddEmptyLines ); + } + + // special page field properties + if( eOrient == DataPilotFieldOrientation_PAGE ) + { + maPageInfo.mnField = GetFieldIndex(); + + // selected item + if( rSaveDim.HasCurrentPage() ) + maPageInfo.mnSelItem = GetItemIndex( rSaveDim.GetCurrentPage(), EXC_SXPI_ALLITEMS ); + else + maPageInfo.mnSelItem = EXC_SXPI_ALLITEMS; + } + + // item properties + const ScDPSaveDimension::MemberList &rMembers = rSaveDim.GetMembers(); + for (ScDPSaveDimension::MemberList::const_iterator i=rMembers.begin(); i != rMembers.end() ; i++) + if( XclExpPTItem* pItem = GetItemAcc( (*i)->GetName() ) ) + pItem->SetPropertiesFromMember( **i ); +} + +void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim ) +{ + maDataInfoVec.push_back( XclPTDataFieldInfo() ); + XclPTDataFieldInfo& rDataInfo = maDataInfoVec.back(); + rDataInfo.mnField = GetFieldIndex(); + + // orientation + maFieldInfo.AddApiOrient( DataPilotFieldOrientation_DATA ); + + // aggregation function + GeneralFunction eFunc = static_cast< GeneralFunction >( rSaveDim.GetFunction() ); + rDataInfo.SetApiAggFunc( eFunc ); + + // visible name + const rtl::OUString* pVisName = rSaveDim.GetLayoutName(); + if (pVisName) + rDataInfo.SetVisName(*pVisName); + else + rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) ); + + // result field reference + if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() ) + { + rDataInfo.SetApiRefType( pFieldRef->ReferenceType ); + rDataInfo.SetApiRefItemType( pFieldRef->ReferenceItemType ); + if( const XclExpPTField* pRefField = mrPTable.GetField( pFieldRef->ReferenceField ) ) + { + rDataInfo.mnRefField = pRefField->GetFieldIndex(); + if( pFieldRef->ReferenceItemType == ::com::sun::star::sheet::DataPilotFieldReferenceItemType::NAMED ) + rDataInfo.mnRefItem = pRefField->GetItemIndex( pFieldRef->ReferenceItemName, 0 ); + } + } +} + +void XclExpPTField::AppendSubtotalItems() +{ + if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) AppendSubtotalItem( EXC_SXVI_TYPE_DEFAULT ); + if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_SUM ) AppendSubtotalItem( EXC_SXVI_TYPE_SUM ); + if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNT ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNT ); + if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) AppendSubtotalItem( EXC_SXVI_TYPE_AVERAGE ); + if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MAX ) AppendSubtotalItem( EXC_SXVI_TYPE_MAX ); + if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MIN ) AppendSubtotalItem( EXC_SXVI_TYPE_MIN ); + if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_PROD ) AppendSubtotalItem( EXC_SXVI_TYPE_PROD ); + if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNTNUM ); + if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEV ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEV ); + if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEVP ); + if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VAR ) AppendSubtotalItem( EXC_SXVI_TYPE_VAR ); + if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VARP ) AppendSubtotalItem( EXC_SXVI_TYPE_VARP ); +} + +// records -------------------------------------------------------------------- + +void XclExpPTField::WriteSxpiEntry( XclExpStream& rStrm ) const +{ + rStrm << maPageInfo; +} + +void XclExpPTField::WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const +{ + DBG_ASSERT( nDataInfoIdx < maDataInfoVec.size(), "XclExpPTField::WriteSxdi - data field not found" ); + if( nDataInfoIdx < maDataInfoVec.size() ) + { + rStrm.StartRecord( EXC_ID_SXDI, 12 ); + rStrm << maDataInfoVec[ nDataInfoIdx ]; + rStrm.EndRecord(); + } +} + +void XclExpPTField::Save( XclExpStream& rStrm ) +{ + // SXVD + WriteSxvd( rStrm ); + // list of SXVI records + maItemList.Save( rStrm ); + // SXVDEX + WriteSxvdex( rStrm ); +} + +// private -------------------------------------------------------------------- + +XclExpPTItem* XclExpPTField::GetItemAcc( const String& rName ) +{ + XclExpPTItem* pItem = 0; + for( size_t nPos = 0, nSize = maItemList.GetSize(); !pItem && (nPos < nSize); ++nPos ) + if( maItemList.GetRecord( nPos )->GetItemName() == rName ) + pItem = maItemList.GetRecord( nPos ).get(); + return pItem; +} + +void XclExpPTField::AppendSubtotalItem( sal_uInt16 nItemType ) +{ + maItemList.AppendNewRecord( new XclExpPTItem( nItemType, EXC_SXVI_DEFAULT_CACHE, true ) ); + ++maFieldInfo.mnItemCount; +} + +void XclExpPTField::WriteSxvd( XclExpStream& rStrm ) const +{ + rStrm.StartRecord( EXC_ID_SXVD, 10 ); + rStrm << maFieldInfo; + rStrm.EndRecord(); +} + +void XclExpPTField::WriteSxvdex( XclExpStream& rStrm ) const +{ + rStrm.StartRecord( EXC_ID_SXVDEX, 20 ); + rStrm << maFieldExtInfo; + rStrm.EndRecord(); +} + +// ============================================================================ + +XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& rDPObj, const XclExpPivotCache& rPCache ) : + XclExpRoot( rRoot ), + mrPCache( rPCache ), + maDataOrientField( *this, EXC_SXIVD_DATA ), + mnOutScTab( 0 ), + mbValid( false ), + mbFilterBtn( false ) +{ + const ScRange& rOutScRange = rDPObj.GetOutRange(); + if( GetAddressConverter().ConvertRange( maPTInfo.maOutXclRange, rOutScRange, true ) ) + { + // DataPilot properties ----------------------------------------------- + + // pivot table properties from DP object + mnOutScTab = rOutScRange.aStart.Tab(); + maPTInfo.maTableName = rDPObj.GetName(); + maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex(); + + maPTViewEx9Info.Init( rDPObj ); + + if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() ) + { + // additional properties from ScDPSaveData + SetPropertiesFromDP( *pSaveData ); + + // loop over all dimensions --------------------------------------- + + /* 1) Default-construct all pivot table fields for all pivot cache fields. */ + for( sal_uInt16 nFieldIdx = 0, nFieldCount = mrPCache.GetFieldCount(); nFieldIdx < nFieldCount; ++nFieldIdx ) + maFieldList.AppendNewRecord( new XclExpPTField( *this, nFieldIdx ) ); + + const List& rDimList = pSaveData->GetDimensions(); + ULONG nDimIdx, nDimCount = rDimList.Count(); + + /* 2) First process all data dimensions, they are needed for extended + settings of row/column/page fields (sorting/auto show). */ + for( nDimIdx = 0; nDimIdx < nDimCount; ++nDimIdx ) + if( const ScDPSaveDimension* pSaveDim = static_cast< const ScDPSaveDimension* >( rDimList.GetObject( nDimIdx ) ) ) + if( pSaveDim->GetOrientation() == DataPilotFieldOrientation_DATA ) + SetDataFieldPropertiesFromDim( *pSaveDim ); + + /* 3) Row/column/page/hidden fields. */ + for( nDimIdx = 0; nDimIdx < nDimCount; ++nDimIdx ) + if( const ScDPSaveDimension* pSaveDim = static_cast< const ScDPSaveDimension* >( rDimList.GetObject( nDimIdx ) ) ) + if( pSaveDim->GetOrientation() != DataPilotFieldOrientation_DATA ) + SetFieldPropertiesFromDim( *pSaveDim ); + + // Finalize ------------------------------------------------------- + + Finalize(); + mbValid = true; + } + } +} + +const XclExpPCField* XclExpPivotTable::GetCacheField( sal_uInt16 nCacheIdx ) const +{ + return mrPCache.GetField( nCacheIdx ); +} + +const XclExpPTField* XclExpPivotTable::GetField( sal_uInt16 nFieldIdx ) const +{ + return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : maFieldList.GetRecord( nFieldIdx ).get(); +} + +const XclExpPTField* XclExpPivotTable::GetField( const String& rName ) const +{ + return const_cast< XclExpPivotTable* >( this )->GetFieldAcc( rName ); +} + +sal_uInt16 XclExpPivotTable::GetDataFieldIndex( const String& rName, sal_uInt16 nDefaultIdx ) const +{ + for( XclPTDataFieldPosVec::const_iterator aIt = maDataFields.begin(), aEnd = maDataFields.end(); aIt != aEnd; ++aIt ) + if( const XclExpPTField* pField = GetField( aIt->first ) ) + if( pField->GetFieldName() == rName ) + return static_cast< sal_uInt16 >( aIt - maDataFields.begin() ); + return nDefaultIdx; +} + +void XclExpPivotTable::Save( XclExpStream& rStrm ) +{ + if( mbValid ) + { + // SXVIEW + WriteSxview( rStrm ); + // pivot table fields (SXVD, SXVDEX, and item records) + maFieldList.Save( rStrm ); + // SXIVD records for row and column fields + WriteSxivd( rStrm, maRowFields ); + WriteSxivd( rStrm, maColFields ); + // SXPI + WriteSxpi( rStrm ); + // list of SXDI records containing data field info + WriteSxdiList( rStrm ); + // SXLI records + WriteSxli( rStrm, maPTInfo.mnDataRows, maPTInfo.mnRowFields ); + WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields ); + // SXEX + WriteSxex( rStrm ); + // QSISXTAG + WriteQsiSxTag( rStrm ); + // SXVIEWEX9 + WriteSxViewEx9( rStrm ); + } +} + +// private -------------------------------------------------------------------- + +XclExpPTField* XclExpPivotTable::GetFieldAcc( const String& rName ) +{ + XclExpPTField* pField = 0; + for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos ) + if( maFieldList.GetRecord( nPos )->GetFieldName() == rName ) + pField = maFieldList.GetRecord( nPos ).get(); + return pField; +} + +XclExpPTField* XclExpPivotTable::GetFieldAcc( const ScDPSaveDimension& rSaveDim ) +{ + // data field orientation field? + if( rSaveDim.IsDataLayout() ) + return &maDataOrientField; + + // a real dimension + String aFieldName( rSaveDim.GetName() ); + return aFieldName.Len() ? GetFieldAcc( aFieldName ) : 0; +} + +// fill data -------------------------------------------------------------- + +void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData ) +{ + ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND, rSaveData.GetRowGrand() ); + ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() ); + ::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() ); + mbFilterBtn = rSaveData.GetFilterButton(); + const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension(); + if (!pDim) + return; + + const rtl::OUString* pLayoutName = pDim->GetLayoutName(); + if (pLayoutName) + maPTInfo.maDataName = *pLayoutName; + else + maPTInfo.maDataName = ScGlobal::GetRscString(STR_PIVOT_DATA); +} + +void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim ) +{ + if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) ) + { + // field properties + pField->SetPropertiesFromDim( rSaveDim ); + + // update the corresponding field position list + DataPilotFieldOrientation eOrient = static_cast< DataPilotFieldOrientation >( rSaveDim.GetOrientation() ); + sal_uInt16 nFieldIdx = pField->GetFieldIndex(); + bool bDataLayout = nFieldIdx == EXC_SXIVD_DATA; + bool bMultiData = maDataFields.size() > 1; + + if( !bDataLayout || bMultiData ) switch( eOrient ) + { + case DataPilotFieldOrientation_ROW: + maRowFields.push_back( nFieldIdx ); + if( bDataLayout ) + maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW; + break; + case DataPilotFieldOrientation_COLUMN: + maColFields.push_back( nFieldIdx ); + if( bDataLayout ) + maPTInfo.mnDataAxis = EXC_SXVD_AXIS_COL; + break; + case DataPilotFieldOrientation_PAGE: + maPageFields.push_back( nFieldIdx ); + DBG_ASSERT( !bDataLayout, "XclExpPivotTable::SetFieldPropertiesFromDim - wrong orientation for data fields" ); + break; + case DataPilotFieldOrientation_DATA: + DBG_ERRORFILE( "XclExpPivotTable::SetFieldPropertiesFromDim - called for data field" ); + break; + default:; + } + } +} + +void XclExpPivotTable::SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim ) +{ + if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) ) + { + // field properties + pField->SetDataPropertiesFromDim( rSaveDim ); + // update the data field position list + maDataFields.push_back( XclPTDataFieldPos( pField->GetFieldIndex(), pField->GetLastDataInfoIndex() ) ); + } +} + +void XclExpPivotTable::Finalize() +{ + // field numbers + maPTInfo.mnFields = static_cast< sal_uInt16 >( maFieldList.GetSize() ); + maPTInfo.mnRowFields = static_cast< sal_uInt16 >( maRowFields.size() ); + maPTInfo.mnColFields = static_cast< sal_uInt16 >( maColFields.size() ); + maPTInfo.mnPageFields = static_cast< sal_uInt16 >( maPageFields.size() ); + maPTInfo.mnDataFields = static_cast< sal_uInt16 >( maDataFields.size() ); + + maPTExtInfo.mnPagePerRow = maPTInfo.mnPageFields; + maPTExtInfo.mnPagePerCol = (maPTInfo.mnPageFields > 0) ? 1 : 0; + + // subtotal items + for( size_t nPos = 0, nSize = maFieldList.GetSize(); nPos < nSize; ++nPos ) + maFieldList.GetRecord( nPos )->AppendSubtotalItems(); + + // find data field orientation field + maPTInfo.mnDataPos = EXC_SXVIEW_DATALAST; + const ScfUInt16Vec* pFieldVec = 0; + switch( maPTInfo.mnDataAxis ) + { + case EXC_SXVD_AXIS_ROW: pFieldVec = &maRowFields; break; + case EXC_SXVD_AXIS_COL: pFieldVec = &maColFields; break; + } + + if( pFieldVec && !pFieldVec->empty() && (pFieldVec->back() != EXC_SXIVD_DATA) ) + { + ScfUInt16Vec::const_iterator aIt = ::std::find( pFieldVec->begin(), pFieldVec->end(), EXC_SXIVD_DATA ); + if( aIt != pFieldVec->end() ) + maPTInfo.mnDataPos = static_cast< sal_uInt16 >( aIt - pFieldVec->begin() ); + } + + // single data field is always row oriented + if( maPTInfo.mnDataAxis == EXC_SXVD_AXIS_NONE ) + maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW; + + // update output range (initialized in ctor) + sal_uInt16& rnXclCol1 = maPTInfo.maOutXclRange.maFirst.mnCol; + sal_uInt16& rnXclRow1 = maPTInfo.maOutXclRange.maFirst.mnRow; + sal_uInt16& rnXclCol2 = maPTInfo.maOutXclRange.maLast.mnCol; + sal_uInt16& rnXclRow2 = maPTInfo.maOutXclRange.maLast.mnRow; + // exclude page fields from output range + rnXclRow1 = rnXclRow1 + maPTInfo.mnPageFields; + // exclude filter button from output range + if( mbFilterBtn ) + ++rnXclRow1; + // exclude empty row between (filter button and/or page fields) and table + if( mbFilterBtn || maPTInfo.mnPageFields ) + ++rnXclRow1; + + // data area + sal_uInt16& rnDataXclCol = maPTInfo.maDataXclPos.mnCol; + sal_uInt16& rnDataXclRow = maPTInfo.maDataXclPos.mnRow; + rnDataXclCol = rnXclCol1 + maPTInfo.mnRowFields; + rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1; + if( maDataFields.empty() ) + ++rnDataXclRow; + + bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0); + if (bExtraHeaderRow) + // Insert an extra row only when there is no column field. + ++rnDataXclRow; + + rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol ); + rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow ); + maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1; + maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1; + + // first heading + maPTInfo.mnFirstHeadRow = rnXclRow1; + if (bExtraHeaderRow) + maPTInfo.mnFirstHeadRow += 2; +} + +// records ---------------------------------------------------------------- + +void XclExpPivotTable::WriteSxview( XclExpStream& rStrm ) const +{ + rStrm.StartRecord( EXC_ID_SXVIEW, 46 + maPTInfo.maTableName.Len() + maPTInfo.maDataName.Len() ); + rStrm << maPTInfo; + rStrm.EndRecord(); +} + +void XclExpPivotTable::WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields ) const +{ + if( !rFields.empty() ) + { + rStrm.StartRecord( EXC_ID_SXIVD, rFields.size() * 2 ); + for( ScfUInt16Vec::const_iterator aIt = rFields.begin(), aEnd = rFields.end(); aIt != aEnd; ++aIt ) + rStrm << *aIt; + rStrm.EndRecord(); + } +} + +void XclExpPivotTable::WriteSxpi( XclExpStream& rStrm ) const +{ + if( !maPageFields.empty() ) + { + rStrm.StartRecord( EXC_ID_SXPI, maPageFields.size() * 6 ); + rStrm.SetSliceSize( 6 ); + for( ScfUInt16Vec::const_iterator aIt = maPageFields.begin(), aEnd = maPageFields.end(); aIt != aEnd; ++aIt ) + { + XclExpPTFieldRef xField = maFieldList.GetRecord( *aIt ); + if( xField.is() ) + xField->WriteSxpiEntry( rStrm ); + } + rStrm.EndRecord(); + } +} + +void XclExpPivotTable::WriteSxdiList( XclExpStream& rStrm ) const +{ + for( XclPTDataFieldPosVec::const_iterator aIt = maDataFields.begin(), aEnd = maDataFields.end(); aIt != aEnd; ++aIt ) + { + XclExpPTFieldRef xField = maFieldList.GetRecord( aIt->first ); + if( xField.is() ) + xField->WriteSxdi( rStrm, aIt->second ); + } +} + +void XclExpPivotTable::WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount ) const +{ + if( nLineCount > 0 ) + { + sal_uInt16 nLineSize = 8 + 2 * nIndexCount; + rStrm.StartRecord( EXC_ID_SXLI, nLineSize * nLineCount ); + + /* #158444# Excel expects the records to be filled completely, do not + set a segment size... */ +// rStrm.SetSliceSize( nLineSize ); + + for( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine ) + { + // #106598# Excel XP needs a partly initialized SXLI record + rStrm << sal_uInt16( 0 ) // number of equal index entries + << EXC_SXVI_TYPE_DATA + << nIndexCount + << EXC_SXLI_DEFAULTFLAGS; + rStrm.WriteZeroBytes( 2 * nIndexCount ); + } + rStrm.EndRecord(); + } +} + +void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const +{ + rStrm.StartRecord( EXC_ID_SXEX, 24 ); + rStrm << maPTExtInfo; + rStrm.EndRecord(); +} + +void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const +{ + rStrm.StartRecord( 0x0802, 32 ); + + sal_uInt16 nRecordType = 0x0802; + sal_uInt16 nDummyFlags = 0x0000; + sal_uInt16 nTableType = 1; // 0 = query table : 1 = pivot table + + rStrm << nRecordType << nDummyFlags << nTableType; + + // General flags + bool bEnableRefresh = true; + bool bPCacheInvalid = false; + bool bOlapPTReport = false; + + sal_uInt16 nFlags = 0x0000; + if (bEnableRefresh) nFlags |= 0x0001; + if (bPCacheInvalid) nFlags |= 0x0002; + if (bOlapPTReport) nFlags |= 0x0004; + rStrm << nFlags; + + // Feature-specific options. The value differs depending on the table + // type, but we assume the table type is always pivot table. + sal_uInt32 nOptions = 0x00000000; + bool bNoStencil = false; + bool bHideTotal = false; + bool bEmptyRows = false; + bool bEmptyCols = false; + if (bNoStencil) nOptions |= 0x00000001; + if (bHideTotal) nOptions |= 0x00000002; + if (bEmptyRows) nOptions |= 0x00000008; + if (bEmptyCols) nOptions |= 0x00000010; + rStrm << nOptions; + + enum ExcelVersion + { + Excel2000 = 0, + ExcelXP = 1, + Excel2003 = 2, + Excel2007 = 3 + }; + ExcelVersion eXclVer = Excel2000; + sal_uInt8 nOffsetBytes = 16; + rStrm << static_cast<sal_uInt8>(eXclVer) // version table last refreshed + << static_cast<sal_uInt8>(eXclVer) // minimum version to refresh + << nOffsetBytes + << static_cast<sal_uInt8>(eXclVer); // first version created + + rStrm << XclExpString(maPTInfo.maTableName); + rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for. + + rStrm.EndRecord(); +} + +void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const +{ + // Until we sync the autoformat ids only export if using grid header layout + // That could only have been set via xls import so far. + if ( 0 == maPTViewEx9Info.mnGridLayout ) + { + rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 ); + rStrm << maPTViewEx9Info; + rStrm.EndRecord(); + } +} + +// ============================================================================ + +namespace { + +const SCTAB EXC_PTMGR_PIVOTCACHES = SCTAB_MAX; + +/** Record wrapper class to write the pivot caches or pivot tables. */ +class XclExpPivotRecWrapper : public XclExpRecordBase +{ +public: + explicit XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ); + virtual void Save( XclExpStream& rStrm ); + virtual void SaveXml( XclExpXmlStream& rStrm ); +private: + XclExpPivotTableManager& mrPTMgr; + SCTAB mnScTab; +}; + +XclExpPivotRecWrapper::XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ) : + mrPTMgr( rPTMgr ), + mnScTab( nScTab ) +{ +} + +void XclExpPivotRecWrapper::Save( XclExpStream& rStrm ) +{ + if( mnScTab == EXC_PTMGR_PIVOTCACHES ) + mrPTMgr.WritePivotCaches( rStrm ); + else + mrPTMgr.WritePivotTables( rStrm, mnScTab ); +} + +void XclExpPivotRecWrapper::SaveXml( XclExpXmlStream& rStrm ) +{ + if( mnScTab == EXC_PTMGR_PIVOTCACHES ) + mrPTMgr.WritePivotCachesXml( rStrm ); + else + mrPTMgr.WritePivotTablesXml( rStrm, mnScTab ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpPivotTableManager::XclExpPivotTableManager( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mbShareCaches( true ) +{ +} + +void XclExpPivotTableManager::CreatePivotTables() +{ + if( ScDPCollection* pDPColl = GetDoc().GetDPCollection() ) + for( USHORT nDPObj = 0, nCount = pDPColl->GetCount(); nDPObj < nCount; ++nDPObj ) + if( ScDPObject* pDPObj = (*pDPColl)[ nDPObj ] ) + if( const XclExpPivotCache* pPCache = CreatePivotCache( *pDPObj ) ) + maPTableList.AppendNewRecord( new XclExpPivotTable( GetRoot(), *pDPObj, *pPCache ) ); +} + +XclExpRecordRef XclExpPivotTableManager::CreatePivotCachesRecord() +{ + return XclExpRecordRef( new XclExpPivotRecWrapper( *this, EXC_PTMGR_PIVOTCACHES ) ); +} + +XclExpRecordRef XclExpPivotTableManager::CreatePivotTablesRecord( SCTAB nScTab ) +{ + return XclExpRecordRef( new XclExpPivotRecWrapper( *this, nScTab ) ); +} + +void XclExpPivotTableManager::WritePivotCaches( XclExpStream& rStrm ) +{ + maPCacheList.Save( rStrm ); +} + +void XclExpPivotTableManager::WritePivotCachesXml( XclExpXmlStream& rStrm ) +{ + if( maPCacheList.IsEmpty() ) + return; + sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream(); + rWorkbook->startElement( XML_pivotCaches, FSEND ); + maPCacheList.SaveXml( rStrm ); + rWorkbook->endElement( XML_pivotCaches ); +} + +void XclExpPivotTableManager::WritePivotTables( XclExpStream& rStrm, SCTAB nScTab ) +{ + for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos ) + { + XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos ); + if( xPTable->GetScTab() == nScTab ) + xPTable->Save( rStrm ); + } +} + +void XclExpPivotTableManager::WritePivotTablesXml( XclExpXmlStream& rStrm, SCTAB nScTab ) +{ + for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos ) + { + XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos ); + if( xPTable->GetScTab() == nScTab ) + xPTable->SaveXml( rStrm ); + } +} + +// private -------------------------------------------------------------------- + +const XclExpPivotCache* XclExpPivotTableManager::CreatePivotCache( const ScDPObject& rDPObj ) +{ + // try to find a pivot cache with the same data source + /* #i25110# In Excel, the pivot cache contains additional fields + (i.e. grouping info, calculated fields). If the passed DataPilot object + or the found cache contains this data, do not share the cache with + multiple pivot tables. */ + if( mbShareCaches ) + { + if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() ) + { + const ScDPDimensionSaveData* pDimSaveData = pSaveData->GetExistingDimensionData(); + // no dimension save data at all or save data does not contain grouping info + if( !pDimSaveData || !pDimSaveData->HasGroupDimensions() ) + { + // check all existing pivot caches + for( size_t nPos = 0, nSize = maPCacheList.GetSize(); nPos < nSize; ++nPos ) + { + XclExpPivotCacheRef xPCache = maPCacheList.GetRecord( nPos ); + // pivot cache does not have grouping info and source data is equal + if( !xPCache->HasAddFields() && xPCache->HasEqualDataSource( rDPObj ) ) + return xPCache.get(); + } + } + } + } + + // create a new pivot cache + sal_uInt16 nNewCacheIdx = static_cast< sal_uInt16 >( maPCacheList.GetSize() ); + XclExpPivotCacheRef xNewPCache( new XclExpPivotCache( GetRoot(), rDPObj, nNewCacheIdx ) ); + if( xNewPCache->IsValid() ) + { + maPCacheList.AppendRecord( xNewPCache ); + return xNewPCache.get(); + } + + return 0; +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xerecord.cxx b/sc/source/filter/excel/xerecord.cxx new file mode 100644 index 000000000000..fed58411aa5c --- /dev/null +++ b/sc/source/filter/excel/xerecord.cxx @@ -0,0 +1,302 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xerecord.hxx" +#include "xeroot.hxx" + +#include <oox/core/tokens.hxx> + +// Base classes to export Excel records ======================================= + +XclExpRecordBase::~XclExpRecordBase() +{ +} + +void XclExpRecordBase::Save( XclExpStream& /*rStrm*/ ) +{ +} + +void XclExpRecordBase::SaveXml( XclExpXmlStream& /*rStrm*/ ) +{ +} + +// ---------------------------------------------------------------------------- + +XclExpDelegatingRecord::XclExpDelegatingRecord( XclExpRecordBase* pRecord ) : + mpRecord( pRecord ) +{ +} + +XclExpDelegatingRecord::~XclExpDelegatingRecord() +{ + // Do Nothing; we use Delegating Record for other objects we "know" will + // survive... +} + +void XclExpDelegatingRecord::SaveXml( XclExpXmlStream& rStrm ) +{ + if( mpRecord ) + mpRecord->SaveXml( rStrm ); +} + +// ---------------------------------------------------------------------------- + +XclExpXmlElementRecord::XclExpXmlElementRecord( sal_Int32 nElement, void (*pAttributes)( XclExpXmlStream& rStrm) ) + : mnElement( nElement ), mpAttributes( pAttributes ) +{ +} + +XclExpXmlElementRecord::~XclExpXmlElementRecord() +{ +} + +// ---------------------------------------------------------------------------- + +XclExpXmlStartElementRecord::XclExpXmlStartElementRecord( sal_Int32 nElement, void (*pAttributes)( XclExpXmlStream& rStrm) ) + : XclExpXmlElementRecord( nElement, pAttributes ) +{ +} + +XclExpXmlStartElementRecord::~XclExpXmlStartElementRecord() +{ +} + +void XclExpXmlStartElementRecord::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& rStream = rStrm.GetCurrentStream(); + if( ! mpAttributes ) + { + rStream->startElement( mnElement, FSEND ); + } + else + { + rStream->write( "<" )->writeId( mnElement ); + (*mpAttributes)( rStrm ); + } +} + +// ---------------------------------------------------------------------------- + +XclExpXmlEndElementRecord::XclExpXmlEndElementRecord( sal_Int32 nElement ) + : XclExpXmlElementRecord( nElement ) +{ +} + +XclExpXmlEndElementRecord::~XclExpXmlEndElementRecord() +{ +} + +void XclExpXmlEndElementRecord::SaveXml( XclExpXmlStream& rStrm ) +{ + rStrm.GetCurrentStream()->endElement( mnElement ); +} + +// ---------------------------------------------------------------------------- + +XclExpXmlStartSingleElementRecord::XclExpXmlStartSingleElementRecord( sal_Int32 nElement, void (*pAttributes)( XclExpXmlStream& rStrm) ) + : XclExpXmlElementRecord( nElement, pAttributes ) +{ +} + +XclExpXmlStartSingleElementRecord::~XclExpXmlStartSingleElementRecord() +{ +} + +void XclExpXmlStartSingleElementRecord::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& rStream = rStrm.GetCurrentStream(); + rStream->write( "<" )->writeId( mnElement ); + if( mpAttributes ) + (*mpAttributes)( rStrm ); +} + +// ---------------------------------------------------------------------------- + +XclExpXmlEndSingleElementRecord::XclExpXmlEndSingleElementRecord() +{ +} + +XclExpXmlEndSingleElementRecord::~XclExpXmlEndSingleElementRecord() +{ +} + +void XclExpXmlEndSingleElementRecord::SaveXml( XclExpXmlStream& rStrm ) +{ + rStrm.GetCurrentStream()->write( "/>" ); +} + +// ---------------------------------------------------------------------------- + +XclExpRecord::XclExpRecord( sal_uInt16 nRecId, sal_Size nRecSize ) : + mnRecSize( nRecSize ), + mnRecId( nRecId ) +{ +} + +XclExpRecord::~XclExpRecord() +{ +} + +void XclExpRecord::SetRecHeader( sal_uInt16 nRecId, sal_Size nRecSize ) +{ + SetRecId( nRecId ); + SetRecSize( nRecSize ); +} + +void XclExpRecord::WriteBody( XclExpStream& /*rStrm*/ ) +{ +} + +void XclExpRecord::Save( XclExpStream& rStrm ) +{ + DBG_ASSERT( mnRecId != EXC_ID_UNKNOWN, "XclExpRecord::Save - record ID uninitialized" ); + rStrm.StartRecord( mnRecId, mnRecSize ); + WriteBody( rStrm ); + rStrm.EndRecord(); +} + +// ---------------------------------------------------------------------------- + +template<> +void XclExpValueRecord<double>::SaveXml( XclExpXmlStream& rStrm ) +{ + if( mnAttribute == -1 ) + return; + rStrm.WriteAttributes( + mnAttribute, rtl::OString::valueOf( maValue ).getStr(), + FSEND ); +} + +// ---------------------------------------------------------------------------- + +void XclExpBoolRecord::WriteBody( XclExpStream& rStrm ) +{ + rStrm << static_cast< sal_uInt16 >( mbValue ? 1 : 0 ); +} + +void XclExpBoolRecord::SaveXml( XclExpXmlStream& rStrm ) +{ + if( mnAttribute == -1 ) + return; + + rStrm.WriteAttributes( + // HACK: HIDEOBJ (excdoc.cxx) should be its own object to handle XML_showObjects + mnAttribute, mnAttribute == XML_showObjects ? "all" : XclXmlUtils::ToPsz( mbValue ), + FSEND ); +} + +// ---------------------------------------------------------------------------- + +XclExpDummyRecord::XclExpDummyRecord( sal_uInt16 nRecId, const void* pRecData, sal_Size nRecSize ) : + XclExpRecord( nRecId ) +{ + SetData( pRecData, nRecSize ); +} + +void XclExpDummyRecord::SetData( const void* pRecData, sal_Size nRecSize ) +{ + mpData = pRecData; + SetRecSize( pRecData ? nRecSize : 0 ); +} + +void XclExpDummyRecord::WriteBody( XclExpStream& rStrm ) +{ + rStrm.Write( mpData, GetRecSize() ); +} + +// Future records ============================================================= + +XclExpFutureRecord::XclExpFutureRecord( XclFutureRecType eRecType, sal_uInt16 nRecId, sal_Size nRecSize ) : + XclExpRecord( nRecId, nRecSize ), + meRecType( eRecType ) +{ +} + +void XclExpFutureRecord::Save( XclExpStream& rStrm ) +{ + rStrm.StartRecord( GetRecId(), GetRecSize() + ((meRecType == EXC_FUTUREREC_UNUSEDREF) ? 12 : 4) ); + rStrm << GetRecId() << sal_uInt16( 0 ); + if( meRecType == EXC_FUTUREREC_UNUSEDREF ) + rStrm.WriteZeroBytes( 8 ); + WriteBody( rStrm ); + rStrm.EndRecord(); +} + +// ============================================================================ + +XclExpSubStream::XclExpSubStream( sal_uInt16 nSubStrmType ) : + mnSubStrmType( nSubStrmType ) +{ +} + +void XclExpSubStream::Save( XclExpStream& rStrm ) +{ + // BOF record + switch( rStrm.GetRoot().GetBiff() ) + { + case EXC_BIFF2: + rStrm.StartRecord( EXC_ID2_BOF, 4 ); + rStrm << sal_uInt16( 7 ) << mnSubStrmType; + rStrm.EndRecord(); + break; + case EXC_BIFF3: + rStrm.StartRecord( EXC_ID3_BOF, 6 ); + rStrm << sal_uInt16( 0 ) << mnSubStrmType << sal_uInt16( 2104 ); + rStrm.EndRecord(); + break; + case EXC_BIFF4: + rStrm.StartRecord( EXC_ID4_BOF, 6 ); + rStrm << sal_uInt16( 0 ) << mnSubStrmType << sal_uInt16( 1705 ); + rStrm.EndRecord(); + break; + case EXC_BIFF5: + rStrm.StartRecord( EXC_ID5_BOF, 8 ); + rStrm << EXC_BOF_BIFF5 << mnSubStrmType << sal_uInt16( 4915 ) << sal_uInt16( 1994 ); + rStrm.EndRecord(); + break; + case EXC_BIFF8: + rStrm.StartRecord( EXC_ID5_BOF, 16 ); + rStrm << EXC_BOF_BIFF8 << mnSubStrmType << sal_uInt16( 3612 ) << sal_uInt16( 1996 ); + rStrm << sal_uInt32( 1 ) << sal_uInt32( 6 ); + rStrm.EndRecord(); + break; + default: + DBG_ERROR_BIFF(); + } + + // substream records + XclExpRecordList<>::Save( rStrm ); + + // EOF record + rStrm.StartRecord( EXC_ID_EOF, 0 ); + rStrm.EndRecord(); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xeroot.cxx b/sc/source/filter/excel/xeroot.cxx new file mode 100644 index 000000000000..50e07ae1f2ed --- /dev/null +++ b/sc/source/filter/excel/xeroot.cxx @@ -0,0 +1,269 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include <sfx2/docfile.hxx> +#include <sfx2/sfxsids.hrc> +#include <unotools/saveopt.hxx> +#include <svl/itemset.hxx> +#include <svl/stritem.hxx> +#include <svl/intitem.hxx> +#include <svl/eitem.hxx> +#include "xecontent.hxx" +#include "xltracer.hxx" +#include "xeescher.hxx" +#include "xeformula.hxx" +#include "xehelper.hxx" +#include "xelink.hxx" +#include "xename.hxx" +#include "xepivot.hxx" +#include "xestyle.hxx" +#include "xeroot.hxx" + +#include "excrecds.hxx" // for filter manager +#include "tabprotection.hxx" +#include "document.hxx" +#include "scextopt.hxx" + +// Global data ================================================================ + +XclExpRootData::XclExpRootData( XclBiff eBiff, SfxMedium& rMedium, + SotStorageRef xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc ) : + XclRootData( eBiff, rMedium, xRootStrg, rDoc, eTextEnc, true ) +{ + SvtSaveOptions aSaveOpt; + mbRelUrl = mrMedium.IsRemote() ? aSaveOpt.IsSaveRelINet() : aSaveOpt.IsSaveRelFSys(); +} + +XclExpRootData::~XclExpRootData() +{ +} + +// ---------------------------------------------------------------------------- + +XclExpRoot::XclExpRoot( XclExpRootData& rExpRootData ) : + XclRoot( rExpRootData ), + mrExpData( rExpRootData ) +{ +} + +XclExpTabInfo& XclExpRoot::GetTabInfo() const +{ + DBG_ASSERT( mrExpData.mxTabInfo.is(), "XclExpRoot::GetTabInfo - missing object (wrong BIFF?)" ); + return *mrExpData.mxTabInfo; +} + +XclExpAddressConverter& XclExpRoot::GetAddressConverter() const +{ + DBG_ASSERT( mrExpData.mxAddrConv.is(), "XclExpRoot::GetAddressConverter - missing object (wrong BIFF?)" ); + return *mrExpData.mxAddrConv; +} + +XclExpFormulaCompiler& XclExpRoot::GetFormulaCompiler() const +{ + DBG_ASSERT( mrExpData.mxFmlaComp.is(), "XclExpRoot::GetFormulaCompiler - missing object (wrong BIFF?)" ); + return *mrExpData.mxFmlaComp; +} + +XclExpProgressBar& XclExpRoot::GetProgressBar() const +{ + DBG_ASSERT( mrExpData.mxProgress.is(), "XclExpRoot::GetProgressBar - missing object (wrong BIFF?)" ); + return *mrExpData.mxProgress; +} + +XclExpSst& XclExpRoot::GetSst() const +{ + DBG_ASSERT( mrExpData.mxSst.is(), "XclExpRoot::GetSst - missing object (wrong BIFF?)" ); + return *mrExpData.mxSst; +} + +XclExpPalette& XclExpRoot::GetPalette() const +{ + DBG_ASSERT( mrExpData.mxPalette.is(), "XclExpRoot::GetPalette - missing object (wrong BIFF?)" ); + return *mrExpData.mxPalette; +} + +XclExpFontBuffer& XclExpRoot::GetFontBuffer() const +{ + DBG_ASSERT( mrExpData.mxFontBfr.is(), "XclExpRoot::GetFontBuffer - missing object (wrong BIFF?)" ); + return *mrExpData.mxFontBfr; +} + +XclExpNumFmtBuffer& XclExpRoot::GetNumFmtBuffer() const +{ + DBG_ASSERT( mrExpData.mxNumFmtBfr.is(), "XclExpRoot::GetNumFmtBuffer - missing object (wrong BIFF?)" ); + return *mrExpData.mxNumFmtBfr; +} + +XclExpXFBuffer& XclExpRoot::GetXFBuffer() const +{ + DBG_ASSERT( mrExpData.mxXFBfr.is(), "XclExpRoot::GetXFBuffer - missing object (wrong BIFF?)" ); + return *mrExpData.mxXFBfr; +} + +XclExpLinkManager& XclExpRoot::GetGlobalLinkManager() const +{ + DBG_ASSERT( mrExpData.mxGlobLinkMgr.is(), "XclExpRoot::GetGlobalLinkManager - missing object (wrong BIFF?)" ); + return *mrExpData.mxGlobLinkMgr; +} + +XclExpLinkManager& XclExpRoot::GetLocalLinkManager() const +{ + DBG_ASSERT( GetLocalLinkMgrRef().is(), "XclExpRoot::GetLocalLinkManager - missing object (wrong BIFF?)" ); + return *GetLocalLinkMgrRef(); +} + +XclExpNameManager& XclExpRoot::GetNameManager() const +{ + DBG_ASSERT( mrExpData.mxNameMgr.is(), "XclExpRoot::GetNameManager - missing object (wrong BIFF?)" ); + return *mrExpData.mxNameMgr; +} + +XclExpObjectManager& XclExpRoot::GetObjectManager() const +{ + DBG_ASSERT( mrExpData.mxObjMgr.is(), "XclExpRoot::GetObjectManager - missing object (wrong BIFF?)" ); + return *mrExpData.mxObjMgr; +} + +XclExpFilterManager& XclExpRoot::GetFilterManager() const +{ + DBG_ASSERT( mrExpData.mxFilterMgr.is(), "XclExpRoot::GetFilterManager - missing object (wrong BIFF?)" ); + return *mrExpData.mxFilterMgr; +} + +XclExpPivotTableManager& XclExpRoot::GetPivotTableManager() const +{ + DBG_ASSERT( mrExpData.mxPTableMgr.is(), "XclExpRoot::GetPivotTableManager - missing object (wrong BIFF?)" ); + return *mrExpData.mxPTableMgr; +} + +void XclExpRoot::InitializeConvert() +{ + mrExpData.mxTabInfo.reset( new XclExpTabInfo( GetRoot() ) ); + mrExpData.mxAddrConv.reset( new XclExpAddressConverter( GetRoot() ) ); + mrExpData.mxFmlaComp.reset( new XclExpFormulaCompiler( GetRoot() ) ); + mrExpData.mxProgress.reset( new XclExpProgressBar( GetRoot() ) ); + + GetProgressBar().Initialize(); +} + +void XclExpRoot::InitializeGlobals() +{ + SetCurrScTab( SCTAB_GLOBAL ); + + if( GetBiff() >= EXC_BIFF5 ) + { + mrExpData.mxPalette.reset( new XclExpPalette( GetRoot() ) ); + mrExpData.mxFontBfr.reset( new XclExpFontBuffer( GetRoot() ) ); + mrExpData.mxNumFmtBfr.reset( new XclExpNumFmtBuffer( GetRoot() ) ); + mrExpData.mxXFBfr.reset( new XclExpXFBuffer( GetRoot() ) ); + mrExpData.mxGlobLinkMgr.reset( new XclExpLinkManager( GetRoot() ) ); + mrExpData.mxNameMgr.reset( new XclExpNameManager( GetRoot() ) ); + } + + if( GetBiff() == EXC_BIFF8 ) + { + mrExpData.mxSst.reset( new XclExpSst ); + mrExpData.mxObjMgr.reset( new XclExpObjectManager( GetRoot() ) ); + mrExpData.mxFilterMgr.reset( new XclExpFilterManager( GetRoot() ) ); + mrExpData.mxPTableMgr.reset( new XclExpPivotTableManager( GetRoot() ) ); + // BIFF8: only one link manager for all sheets + mrExpData.mxLocLinkMgr = mrExpData.mxGlobLinkMgr; + } + + GetXFBuffer().Initialize(); + GetNameManager().Initialize(); +} + +void XclExpRoot::InitializeTable( SCTAB nScTab ) +{ + SetCurrScTab( nScTab ); + if( GetBiff() == EXC_BIFF5 ) + { + // local link manager per sheet + mrExpData.mxLocLinkMgr.reset( new XclExpLinkManager( GetRoot() ) ); + } +} + +void XclExpRoot::InitializeSave() +{ + GetPalette().Finalize(); + GetXFBuffer().Finalize(); +} + +XclExpRecordRef XclExpRoot::CreateRecord( sal_uInt16 nRecId ) const +{ + XclExpRecordRef xRec; + switch( nRecId ) + { + case EXC_ID_PALETTE: xRec = mrExpData.mxPalette; break; + case EXC_ID_FONTLIST: xRec = mrExpData.mxFontBfr; break; + case EXC_ID_FORMATLIST: xRec = mrExpData.mxNumFmtBfr; break; + case EXC_ID_XFLIST: xRec = mrExpData.mxXFBfr; break; + case EXC_ID_SST: xRec = mrExpData.mxSst; break; + case EXC_ID_EXTERNSHEET: xRec = GetLocalLinkMgrRef(); break; + case EXC_ID_NAME: xRec = mrExpData.mxNameMgr; break; + } + DBG_ASSERT( xRec.is(), "XclExpRoot::CreateRecord - unknown record ID or missing object" ); + return xRec; +} + +bool XclExpRoot::IsDocumentEncrypted() const +{ + // We need to encrypt the content when the document structure is protected. + const ScDocProtection* pDocProt = GetDoc().GetDocProtection(); + if (pDocProt && pDocProt->isProtected() && pDocProt->isOptionEnabled(ScDocProtection::STRUCTURE)) + return true; + + if (GetPassword().Len() > 0) + // Password is entered directly into the save dialog. + return true; + + return false; +} + +String XclExpRoot::GetPassword() const +{ + if( SfxItemSet* pItemSet = GetMedium().GetItemSet() ) + { + const SfxPoolItem* pItem = 0; + if( pItemSet->GetItemState( SID_PASSWORD, TRUE, &pItem ) == SFX_ITEM_SET ) + if( const SfxStringItem* pStrItem = dynamic_cast< const SfxStringItem* >( pItem ) ) + return pStrItem->GetValue(); + } + return String::EmptyString(); +} + +XclExpRootData::XclExpLinkMgrRef XclExpRoot::GetLocalLinkMgrRef() const +{ + return IsInGlobals() ? mrExpData.mxGlobLinkMgr : mrExpData.mxLocLinkMgr; +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xestream.cxx b/sc/source/filter/excel/xestream.cxx new file mode 100644 index 000000000000..f828079c8735 --- /dev/null +++ b/sc/source/filter/excel/xestream.cxx @@ -0,0 +1,1034 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <utility> + +#include <rtl/ustring.hxx> +#include <sax/fshelper.hxx> +#include <unotools/streamwrap.hxx> + +#include "precompiled_sc.hxx" +#include "docuno.hxx" +#include "xestream.hxx" +#include "xladdress.hxx" +#include "xlstring.hxx" +#include "xeroot.hxx" +#include "xestyle.hxx" +#include "rangelst.hxx" +#include "compiler.hxx" + +#include <oox/core/tokens.hxx> +#include <formula/grammar.hxx> + +#define DEBUG_XL_ENCRYPTION 0 + +using ::com::sun::star::beans::PropertyValue; +using ::com::sun::star::io::XOutputStream; +using ::com::sun::star::io::XStream; +using ::com::sun::star::lang::XComponent; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::lang::XServiceInfo; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY; +using ::rtl::OString; +using ::rtl::OUString; +using ::utl::OStreamWrapper; +using ::std::vector; + +using namespace formula; + +// ============================================================================ + +XclExpStream::XclExpStream( SvStream& rOutStrm, const XclExpRoot& rRoot, sal_uInt16 nMaxRecSize ) : + mrStrm( rOutStrm ), + mrRoot( rRoot ), + mnMaxRecSize( nMaxRecSize ), + mnCurrMaxSize( 0 ), + mnMaxSliceSize( 0 ), + mnHeaderSize( 0 ), + mnCurrSize( 0 ), + mnSliceSize( 0 ), + mnPredictSize( 0 ), + mnLastSizePos( 0 ), + mbInRec( false ) +{ + if( mnMaxRecSize == 0 ) + mnMaxRecSize = (mrRoot.GetBiff() <= EXC_BIFF5) ? EXC_MAXRECSIZE_BIFF5 : EXC_MAXRECSIZE_BIFF8; + mnMaxContSize = mnMaxRecSize; +} + +XclExpStream::~XclExpStream() +{ + mrStrm.Flush(); +} + +void XclExpStream::StartRecord( sal_uInt16 nRecId, sal_Size nRecSize ) +{ + DBG_ASSERT( !mbInRec, "XclExpStream::StartRecord - another record still open" ); + DisableEncryption(); + mnMaxContSize = mnCurrMaxSize = mnMaxRecSize; + mnPredictSize = nRecSize; + mbInRec = true; + InitRecord( nRecId ); + SetSliceSize( 0 ); + EnableEncryption(); +} + +void XclExpStream::EndRecord() +{ + DBG_ASSERT( mbInRec, "XclExpStream::EndRecord - no record open" ); + DisableEncryption(); + UpdateRecSize(); + mrStrm.Seek( STREAM_SEEK_TO_END ); + mbInRec = false; +} + +void XclExpStream::SetSliceSize( sal_uInt16 nSize ) +{ + mnMaxSliceSize = nSize; + mnSliceSize = 0; +} + +XclExpStream& XclExpStream::operator<<( sal_Int8 nValue ) +{ + PrepareWrite( 1 ); + if (mbUseEncrypter && HasValidEncrypter()) + mxEncrypter->Encrypt(mrStrm, nValue); + else + mrStrm << nValue; + return *this; +} + +XclExpStream& XclExpStream::operator<<( sal_uInt8 nValue ) +{ + PrepareWrite( 1 ); + if (mbUseEncrypter && HasValidEncrypter()) + mxEncrypter->Encrypt(mrStrm, nValue); + else + mrStrm << nValue; + return *this; +} + +XclExpStream& XclExpStream::operator<<( sal_Int16 nValue ) +{ + PrepareWrite( 2 ); + if (mbUseEncrypter && HasValidEncrypter()) + mxEncrypter->Encrypt(mrStrm, nValue); + else + mrStrm << nValue; + return *this; +} + +XclExpStream& XclExpStream::operator<<( sal_uInt16 nValue ) +{ + PrepareWrite( 2 ); + if (mbUseEncrypter && HasValidEncrypter()) + mxEncrypter->Encrypt(mrStrm, nValue); + else + mrStrm << nValue; + return *this; +} + +XclExpStream& XclExpStream::operator<<( sal_Int32 nValue ) +{ + PrepareWrite( 4 ); + if (mbUseEncrypter && HasValidEncrypter()) + mxEncrypter->Encrypt(mrStrm, nValue); + else + mrStrm << nValue; + return *this; +} + +XclExpStream& XclExpStream::operator<<( sal_uInt32 nValue ) +{ + PrepareWrite( 4 ); + if (mbUseEncrypter && HasValidEncrypter()) + mxEncrypter->Encrypt(mrStrm, nValue); + else + mrStrm << nValue; + return *this; +} + +XclExpStream& XclExpStream::operator<<( float fValue ) +{ + PrepareWrite( 4 ); + if (mbUseEncrypter && HasValidEncrypter()) + mxEncrypter->Encrypt(mrStrm, fValue); + else + mrStrm << fValue; + return *this; +} + +XclExpStream& XclExpStream::operator<<( double fValue ) +{ + PrepareWrite( 8 ); + if (mbUseEncrypter && HasValidEncrypter()) + mxEncrypter->Encrypt(mrStrm, fValue); + else + mrStrm << fValue; + return *this; +} + +sal_Size XclExpStream::Write( const void* pData, sal_Size nBytes ) +{ + sal_Size nRet = 0; + if( pData && (nBytes > 0) ) + { + if( mbInRec ) + { + const sal_uInt8* pBuffer = reinterpret_cast< const sal_uInt8* >( pData ); + sal_Size nBytesLeft = nBytes; + bool bValid = true; + + while( bValid && (nBytesLeft > 0) ) + { + sal_Size nWriteLen = ::std::min< sal_Size >( PrepareWrite(), nBytesLeft ); + sal_Size nWriteRet = nWriteLen; + if (mbUseEncrypter && HasValidEncrypter()) + { + DBG_ASSERT(nWriteLen > 0, "XclExpStream::Write: write length is 0!"); + vector<sal_uInt8> aBytes(nWriteLen); + memcpy(&aBytes[0], pBuffer, nWriteLen); + mxEncrypter->EncryptBytes(mrStrm, aBytes); + // TODO: How do I check if all the bytes have been successfully written ? + } + else + { + nWriteRet = mrStrm.Write( pBuffer, nWriteLen ); + bValid = (nWriteLen == nWriteRet); + DBG_ASSERT( bValid, "XclExpStream::Write - stream write error" ); + } + pBuffer += nWriteRet; + nRet += nWriteRet; + nBytesLeft -= nWriteRet; + UpdateSizeVars( nWriteRet ); + } + } + else + nRet = mrStrm.Write( pData, nBytes ); + } + return nRet; +} + +void XclExpStream::WriteZeroBytes( sal_Size nBytes ) +{ + if( mbInRec ) + { + sal_Size nBytesLeft = nBytes; + while( nBytesLeft > 0 ) + { + sal_Size nWriteLen = ::std::min< sal_Size >( PrepareWrite(), nBytesLeft ); + WriteRawZeroBytes( nWriteLen ); + nBytesLeft -= nWriteLen; + UpdateSizeVars( nWriteLen ); + } + } + else + WriteRawZeroBytes( nBytes ); +} + +void XclExpStream::WriteZeroBytesToRecord( sal_Size nBytes ) +{ + if (!mbInRec) + // not in record. + return; + + sal_uInt8 nZero = 0; + for (sal_Size i = 0; i < nBytes; ++i) + *this << nZero; +} + +sal_Size XclExpStream::CopyFromStream( SvStream& rInStrm, sal_Size nBytes ) +{ + sal_Size nStrmPos = rInStrm.Tell(); + rInStrm.Seek( STREAM_SEEK_TO_END ); + sal_Size nStrmSize = rInStrm.Tell(); + rInStrm.Seek( nStrmPos ); + + sal_Size nBytesLeft = ::std::min( nBytes, nStrmSize - nStrmPos ); + sal_Size nRet = 0; + if( nBytesLeft > 0 ) + { + const sal_Size nMaxBuffer = 4096; + sal_uInt8* pBuffer = new sal_uInt8[ ::std::min( nBytesLeft, nMaxBuffer ) ]; + bool bValid = true; + + while( bValid && (nBytesLeft > 0) ) + { + sal_Size nWriteLen = ::std::min( nBytesLeft, nMaxBuffer ); + rInStrm.Read( pBuffer, nWriteLen ); + sal_Size nWriteRet = Write( pBuffer, nWriteLen ); + bValid = (nWriteLen == nWriteRet); + nRet += nWriteRet; + nBytesLeft -= nWriteRet; + } + delete[] pBuffer; + } + return nRet; +} + +//UNUSED2008-05 void XclExpStream::WriteUnicodeBuffer( const sal_uInt16* pBuffer, sal_Size nChars, sal_uInt8 nFlags ) +//UNUSED2008-05 { +//UNUSED2008-05 SetSliceSize( 0 ); +//UNUSED2008-05 if( pBuffer && (nChars > 0) ) +//UNUSED2008-05 { +//UNUSED2008-05 sal_uInt16 nCharLen = (nFlags & EXC_STRF_16BIT) ? 2 : 1; +//UNUSED2008-05 for( sal_Size nIndex = 0; nIndex < nChars; ++nIndex ) +//UNUSED2008-05 { +//UNUSED2008-05 if( mbInRec && (mnCurrSize + nCharLen > mnCurrMaxSize) ) +//UNUSED2008-05 { +//UNUSED2008-05 StartContinue(); +//UNUSED2008-05 // repeat only 16bit flag +//UNUSED2008-05 operator<<( static_cast< sal_uInt8 >( nFlags & EXC_STRF_16BIT ) ); +//UNUSED2008-05 } +//UNUSED2008-05 if( nCharLen == 2 ) +//UNUSED2008-05 operator<<( pBuffer[ nIndex ] ); +//UNUSED2008-05 else +//UNUSED2008-05 operator<<( static_cast< sal_uInt8 >( pBuffer[ nIndex ] ) ); +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 } + +void XclExpStream::WriteUnicodeBuffer( const ScfUInt16Vec& rBuffer, sal_uInt8 nFlags ) +{ + SetSliceSize( 0 ); + nFlags &= EXC_STRF_16BIT; // repeat only 16bit flag + sal_uInt16 nCharLen = nFlags ? 2 : 1; + + ScfUInt16Vec::const_iterator aEnd = rBuffer.end(); + for( ScfUInt16Vec::const_iterator aIter = rBuffer.begin(); aIter != aEnd; ++aIter ) + { + if( mbInRec && (mnCurrSize + nCharLen > mnCurrMaxSize) ) + { + StartContinue(); + operator<<( nFlags ); + } + if( nCharLen == 2 ) + operator<<( *aIter ); + else + operator<<( static_cast< sal_uInt8 >( *aIter ) ); + } +} + +//UNUSED2008-05 void XclExpStream::WriteByteStringBuffer( const ByteString& rString, sal_uInt16 nMaxLen ) +//UNUSED2008-05 { +//UNUSED2008-05 SetSliceSize( 0 ); +//UNUSED2008-05 Write( rString.GetBuffer(), ::std::min< sal_Size >( rString.Len(), nMaxLen ) ); +//UNUSED2008-05 } + +// ER: #71367# Xcl has an obscure sense of whether starting a new record or not, +// and crashes if it encounters the string header at the very end of a record. +// Thus we add 1 to give some room, seems like they do it that way but with another count (10?) +void XclExpStream::WriteByteString( const ByteString& rString, sal_uInt16 nMaxLen, bool b16BitCount ) +{ + SetSliceSize( 0 ); + sal_Size nLen = ::std::min< sal_Size >( rString.Len(), nMaxLen ); + if( !b16BitCount ) + nLen = ::std::min< sal_Size >( nLen, 0xFF ); + + sal_uInt16 nLeft = PrepareWrite(); + sal_uInt16 nLenFieldSize = b16BitCount ? 2 : 1; + if( mbInRec && (nLeft <= nLenFieldSize) ) + StartContinue(); + + if( b16BitCount ) + operator<<( static_cast< sal_uInt16 >( nLen ) ); + else + operator<<( static_cast< sal_uInt8 >( nLen ) ); + Write( rString.GetBuffer(), nLen ); +} + +void XclExpStream::WriteCharBuffer( const ScfUInt8Vec& rBuffer ) +{ + SetSliceSize( 0 ); + Write( &rBuffer[ 0 ], rBuffer.size() ); +} + +void XclExpStream::SetEncrypter( XclExpEncrypterRef xEncrypter ) +{ + mxEncrypter = xEncrypter; +} + +bool XclExpStream::HasValidEncrypter() const +{ + return mxEncrypter.is() && mxEncrypter->IsValid(); +} + +void XclExpStream::EnableEncryption( bool bEnable ) +{ + mbUseEncrypter = bEnable && HasValidEncrypter(); +} + +void XclExpStream::DisableEncryption() +{ + EnableEncryption(false); +} + +sal_Size XclExpStream::SetSvStreamPos( sal_Size nPos ) +{ + DBG_ASSERT( !mbInRec, "XclExpStream::SetSvStreamPos - not allowed inside of a record" ); + return mbInRec ? 0 : mrStrm.Seek( nPos ); +} + +// private -------------------------------------------------------------------- + +void XclExpStream::InitRecord( sal_uInt16 nRecId ) +{ + mrStrm.Seek( STREAM_SEEK_TO_END ); + mrStrm << nRecId; + + mnLastSizePos = mrStrm.Tell(); + mnHeaderSize = static_cast< sal_uInt16 >( ::std::min< sal_Size >( mnPredictSize, mnCurrMaxSize ) ); + mrStrm << mnHeaderSize; + mnCurrSize = mnSliceSize = 0; +} + +void XclExpStream::UpdateRecSize() +{ + if( mnCurrSize != mnHeaderSize ) + { + mrStrm.Seek( mnLastSizePos ); + mrStrm << mnCurrSize; + } +} + +void XclExpStream::UpdateSizeVars( sal_Size nSize ) +{ + DBG_ASSERT( mnCurrSize + nSize <= mnCurrMaxSize, "XclExpStream::UpdateSizeVars - record overwritten" ); + mnCurrSize = mnCurrSize + static_cast< sal_uInt16 >( nSize ); + + if( mnMaxSliceSize > 0 ) + { + DBG_ASSERT( mnSliceSize + nSize <= mnMaxSliceSize, "XclExpStream::UpdateSizeVars - slice overwritten" ); + mnSliceSize = mnSliceSize + static_cast< sal_uInt16 >( nSize ); + if( mnSliceSize >= mnMaxSliceSize ) + mnSliceSize = 0; + } +} + +void XclExpStream::StartContinue() +{ + UpdateRecSize(); + mnCurrMaxSize = mnMaxContSize; + mnPredictSize -= mnCurrSize; + InitRecord( EXC_ID_CONT ); +} + +void XclExpStream::PrepareWrite( sal_uInt16 nSize ) +{ + if( mbInRec ) + { + if( (mnCurrSize + nSize > mnCurrMaxSize) || + ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) ) + StartContinue(); + UpdateSizeVars( nSize ); + } +} + +sal_uInt16 XclExpStream::PrepareWrite() +{ + sal_uInt16 nRet = 0; + if( mbInRec ) + { + if( (mnCurrSize >= mnCurrMaxSize) || + ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) ) + StartContinue(); + UpdateSizeVars( 0 ); + + nRet = (mnMaxSliceSize > 0) ? (mnMaxSliceSize - mnSliceSize) : (mnCurrMaxSize - mnCurrSize); + } + return nRet; +} + +void XclExpStream::WriteRawZeroBytes( sal_Size nBytes ) +{ + const sal_uInt32 nData = 0; + sal_Size nBytesLeft = nBytes; + while( nBytesLeft >= sizeof( nData ) ) + { + mrStrm << nData; + nBytesLeft -= sizeof( nData ); + } + if( nBytesLeft ) + mrStrm.Write( &nData, nBytesLeft ); +} + +// ============================================================================ + +XclExpBiff8Encrypter::XclExpBiff8Encrypter( const XclExpRoot& rRoot, const sal_uInt8 nDocId[16], + const sal_uInt8 nSalt[16] ) : + mrRoot(rRoot), + mnOldPos(STREAM_SEEK_TO_END), + mbValid(false) +{ + String aPass = rRoot.GetPassword(); + if (aPass.Len() == 0) + // Empty password. Get the default biff8 password. + aPass = rRoot.GetDefaultPassword(); + Init(aPass, nDocId, nSalt); +} + +XclExpBiff8Encrypter::~XclExpBiff8Encrypter() +{ +} + +bool XclExpBiff8Encrypter::IsValid() const +{ + return mbValid; +} + +void XclExpBiff8Encrypter::GetSaltDigest( sal_uInt8 nSaltDigest[16] ) const +{ + memcpy(nSaltDigest, mnSaltDigest, 16); +} + +void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt8 nData ) +{ + vector<sal_uInt8> aByte(1); + aByte[0] = nData; + EncryptBytes(rStrm, aByte); +} + +void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt16 nData ) +{ + ::std::vector<sal_uInt8> pnBytes(2); + pnBytes[0] = nData & 0xFF; + pnBytes[1] = (nData >> 8) & 0xFF; + EncryptBytes(rStrm, pnBytes); +} + +void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt32 nData ) +{ + ::std::vector<sal_uInt8> pnBytes(4); + pnBytes[0] = nData & 0xFF; + pnBytes[1] = (nData >> 8) & 0xFF; + pnBytes[2] = (nData >> 16) & 0xFF; + pnBytes[3] = (nData >> 24) & 0xFF; + EncryptBytes(rStrm, pnBytes); +} + +void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, float fValue ) +{ + ::std::vector<sal_uInt8> pnBytes(4); + memcpy(&pnBytes[0], &fValue, 4); + EncryptBytes(rStrm, pnBytes); +} + +void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, double fValue ) +{ + ::std::vector<sal_uInt8> pnBytes(8); + memcpy(&pnBytes[0], &fValue, 8); + EncryptBytes(rStrm, pnBytes); +} + +void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int8 nData ) +{ + Encrypt(rStrm, static_cast<sal_uInt8>(nData)); +} + +void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int16 nData ) +{ + Encrypt(rStrm, static_cast<sal_uInt16>(nData)); +} + +void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int32 nData ) +{ + Encrypt(rStrm, static_cast<sal_uInt32>(nData)); +} + +void XclExpBiff8Encrypter::Init( const String& aPass, const sal_uInt8 nDocId[16], + const sal_uInt8 nSalt[16] ) +{ + memset(mnSaltDigest, 0, sizeof(mnSaltDigest)); + + xub_StrLen nLen = aPass.Len(); + bool bValid = (0 < nLen) && (nLen < 16); + if ( bValid ) + { + // transform String to sal_uInt16 array + memset(mnPassw, 0, sizeof(mnPassw)); + for (xub_StrLen nChar = 0; nChar < nLen; ++nChar) + mnPassw[nChar] = static_cast<sal_uInt16>(aPass.GetChar(nChar)); + + // copy document ID + memcpy(mnDocId, nDocId, sizeof(mnDocId)); + + // init codec + maCodec.InitKey(mnPassw, mnDocId); + + // generate salt hash. + ::msfilter::MSCodec_Std97 aCodec; + aCodec.InitKey(mnPassw, mnDocId); + aCodec.CreateSaltDigest(nSalt, mnSaltDigest); + + // verify to make sure it's in good shape. + bValid = maCodec.VerifyKey(nSalt, mnSaltDigest); + } + + mbValid = bValid; +} + +sal_uInt32 XclExpBiff8Encrypter::GetBlockPos( sal_Size nStrmPos ) const +{ + return static_cast<sal_uInt32>(nStrmPos / EXC_ENCR_BLOCKSIZE); +} + +sal_uInt16 XclExpBiff8Encrypter::GetOffsetInBlock( sal_Size nStrmPos ) const +{ + return static_cast<sal_uInt16>(nStrmPos % EXC_ENCR_BLOCKSIZE); +} + +void XclExpBiff8Encrypter::EncryptBytes( SvStream& rStrm, vector<sal_uInt8>& aBytes ) +{ + sal_Size nStrmPos = rStrm.Tell(); + sal_uInt16 nBlockOffset = GetOffsetInBlock(nStrmPos); + sal_uInt32 nBlockPos = GetBlockPos(nStrmPos); + +#if DEBUG_XL_ENCRYPTION + fprintf(stdout, "XclExpBiff8Encrypter::EncryptBytes: stream pos = %ld offset in block = %d block pos = %ld\n", + nStrmPos, nBlockOffset, nBlockPos); +#endif + + sal_uInt16 nSize = static_cast< sal_uInt16 >( aBytes.size() ); + if (nSize == 0) + return; + +#if DEBUG_XL_ENCRYPTION + fprintf(stdout, "RAW: "); + for (sal_uInt16 i = 0; i < nSize; ++i) + fprintf(stdout, "%2.2X ", aBytes[i]); + fprintf(stdout, "\n"); +#endif + + if (mnOldPos != nStrmPos) + { + sal_uInt16 nOldOffset = GetOffsetInBlock(mnOldPos); + sal_uInt32 nOldBlockPos = GetBlockPos(mnOldPos); + + if ( (nBlockPos != nOldBlockPos) || (nBlockOffset < nOldOffset) ) + { + maCodec.InitCipher(nBlockPos); + nOldOffset = 0; + } + + if (nBlockOffset > nOldOffset) + maCodec.Skip(nBlockOffset - nOldOffset); + } + + sal_uInt16 nBytesLeft = nSize; + sal_uInt16 nPos = 0; + while (nBytesLeft > 0) + { + sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - nBlockOffset; + sal_uInt16 nEncBytes = ::std::min(nBlockLeft, nBytesLeft); + + bool bRet = maCodec.Encode(&aBytes[nPos], nEncBytes, &aBytes[nPos], nEncBytes); + DBG_ASSERT(bRet, "XclExpBiff8Encrypter::EncryptBytes: encryption failed!!"); + bRet = bRet; // to remove a silly compiler warning. + + sal_Size nRet = rStrm.Write(&aBytes[nPos], nEncBytes); + DBG_ASSERT(nRet == nEncBytes, "XclExpBiff8Encrypter::EncryptBytes: fail to write to stream!!"); + nRet = nRet; // to remove a silly compiler warning. + + nStrmPos = rStrm.Tell(); + nBlockOffset = GetOffsetInBlock(nStrmPos); + nBlockPos = GetBlockPos(nStrmPos); + if (nBlockOffset == 0) + maCodec.InitCipher(nBlockPos); + + nBytesLeft -= nEncBytes; + nPos += nEncBytes; + } + mnOldPos = nStrmPos; +} + +rtl::OUString XclXmlUtils::GetStreamName( const char* sStreamDir, const char* sStream, sal_Int32 nId ) +{ + rtl::OUStringBuffer sBuf; + if( sStreamDir ) + sBuf.appendAscii( sStreamDir ); + sBuf.appendAscii( sStream ); + if( nId ) + sBuf.append( nId ); + sBuf.appendAscii( ".xml" ); + return sBuf.makeStringAndClear(); +} + +rtl::OString XclXmlUtils::ToOString( const Color& rColor ) +{ + char buf[9]; + sprintf( buf, "%.2X%.2X%.2X%.2X", rColor.GetTransparency(), rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() ); + buf[8] = '\0'; + return OString( buf ); +} + +rtl::OString XclXmlUtils::ToOString( const ::rtl::OUString& s ) +{ + return OUStringToOString( s, RTL_TEXTENCODING_UTF8 ); +} + +rtl::OString XclXmlUtils::ToOString( const String& s ) +{ + return rtl::OString( s.GetBuffer(), s.Len(), RTL_TEXTENCODING_UTF8 ); +} + +rtl::OString XclXmlUtils::ToOString( const ScAddress& rAddress ) +{ + String sAddress; + rAddress.Format( sAddress, SCA_VALID, NULL, ScAddress::Details( FormulaGrammar::CONV_XL_A1 ) ); + return ToOString( sAddress ); +} + +rtl::OString XclXmlUtils::ToOString( const ScfUInt16Vec& rBuffer ) +{ + const sal_uInt16* pBuffer = &rBuffer [0]; + return ::rtl::OString( pBuffer, rBuffer.size(), RTL_TEXTENCODING_UTF8 ); +} + +rtl::OString XclXmlUtils::ToOString( const ScRange& rRange ) +{ + String sRange; + rRange.Format( sRange, SCA_VALID, NULL, ScAddress::Details( FormulaGrammar::CONV_XL_A1 ) ); + return ToOString( sRange ); +} + +rtl::OString XclXmlUtils::ToOString( const ScRangeList& rRangeList ) +{ + String s; + rRangeList.Format( s, SCA_VALID, NULL, FormulaGrammar::CONV_XL_A1, ' ' ); + return ToOString( s ); +} + +static ScAddress lcl_ToAddress( const XclAddress& rAddress ) +{ + ScAddress aAddress; + + // For some reason, ScRange::Format() returns omits row numbers if + // the row is >= MAXROW or the column is >= MAXCOL, and Excel doesn't + // like "A:IV" (i.e. no row numbers). Prevent this. + aAddress.SetRow( std::min<sal_Int32>( rAddress.mnRow, MAXROW-1 ) ); + aAddress.SetCol( static_cast<sal_Int16>(std::min<sal_Int32>( rAddress.mnCol, MAXCOL-1 )) ); + + return aAddress; +} + +rtl::OString XclXmlUtils::ToOString( const XclAddress& rAddress ) +{ + return ToOString( lcl_ToAddress( rAddress ) ); +} + +rtl::OString XclXmlUtils::ToOString( const XclExpString& s ) +{ + DBG_ASSERT( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" ); + return ToOString( s.GetUnicodeBuffer() ); +} + +static ScRange lcl_ToRange( const XclRange& rRange ) +{ + ScRange aRange; + + aRange.aStart = lcl_ToAddress( rRange.maFirst ); + aRange.aEnd = lcl_ToAddress( rRange.maLast ); + + return aRange; +} + +rtl::OString XclXmlUtils::ToOString( const XclRangeList& rRanges ) +{ + ScRangeList aRanges; + for( XclRangeList::const_iterator i = rRanges.begin(), end = rRanges.end(); + i != end; ++i ) + { + aRanges.Append( lcl_ToRange( *i ) ); + } + return ToOString( aRanges ); +} + +OUString XclXmlUtils::ToOUString( const char* s ) +{ + return OUString( s, (sal_Int32) strlen( s ), RTL_TEXTENCODING_ASCII_US ); +} + +OUString XclXmlUtils::ToOUString( const ScfUInt16Vec& rBuf, sal_Int32 nStart, sal_Int32 nLength ) +{ + if( nLength == -1 ) + nLength = rBuf.size(); + + return OUString( &rBuf[nStart], nLength ); +} + +OUString XclXmlUtils::ToOUString( const String& s ) +{ + return OUString( s.GetBuffer(), s.Len() ); +} + +rtl::OUString XclXmlUtils::ToOUString( ScDocument& rDocument, const ScAddress& rAddress, ScTokenArray* pTokenArray ) +{ + ScCompiler aCompiler( &rDocument, rAddress, *pTokenArray); + aCompiler.SetGrammar(FormulaGrammar::GRAM_NATIVE_XL_A1); + String s; + aCompiler.CreateStringFromTokenArray( s ); + return ToOUString( s ); +} + +OUString XclXmlUtils::ToOUString( const XclExpString& s ) +{ + DBG_ASSERT( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" ); + return ToOUString( s.GetUnicodeBuffer() ); +} + +const char* XclXmlUtils::ToPsz( bool b ) +{ + return b ? "true" : "false"; +} + +// ============================================================================ + +XclExpXmlStream::XclExpXmlStream( const Reference< XMultiServiceFactory >& rSMgr, SvStream& rStrm, const XclExpRoot& rRoot ) + : XmlFilterBase( rSMgr ) + , mrRoot( rRoot ) +{ + Sequence< PropertyValue > aArgs( 1 ); + const OUString sStream( RTL_CONSTASCII_USTRINGPARAM( "StreamForOutput" ) ); + aArgs[0].Name = sStream; + aArgs[0].Value <<= Reference< XStream > ( new OStreamWrapper( rStrm ) ); + + XServiceInfo* pInfo = rRoot.GetDocModelObj(); + Reference< XComponent > aComponent( pInfo, UNO_QUERY ); + setSourceDocument( aComponent ); + filter( aArgs ); + + PushStream( CreateOutputStream( + OUString::createFromAscii( "xl/workbook.xml" ), + OUString::createFromAscii( "xl/workbook.xml" ), + Reference< XOutputStream >(), + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" ) ); +} + +XclExpXmlStream::~XclExpXmlStream() +{ +} + +sax_fastparser::FSHelperPtr& XclExpXmlStream::GetCurrentStream() +{ + DBG_ASSERT( !maStreams.empty(), "XclExpXmlStream::GetCurrentStream - no current stream" ); + return maStreams.top(); +} + +void XclExpXmlStream::PushStream( sax_fastparser::FSHelperPtr aStream ) +{ + maStreams.push( aStream ); +} + +void XclExpXmlStream::PopStream() +{ + DBG_ASSERT( !maStreams.empty(), "XclExpXmlStream::PopStream - stack is empty!" ); + maStreams.pop(); +} + +OUString XclExpXmlStream::GetIdForPath( const OUString& sPath ) +{ + if( maOpenedStreamMap.find( sPath ) == maOpenedStreamMap.end() ) + return OUString(); + return maOpenedStreamMap[ sPath ].first; +} + +sax_fastparser::FSHelperPtr XclExpXmlStream::GetStreamForPath( const OUString& sPath ) +{ + if( maOpenedStreamMap.find( sPath ) == maOpenedStreamMap.end() ) + return sax_fastparser::FSHelperPtr(); + return maOpenedStreamMap[ sPath ].second; +} + +sax_fastparser::FSHelperPtr& XclExpXmlStream::WriteAttributes( sal_Int32 nAttribute, ... ) +{ + sax_fastparser::FSHelperPtr& rStream = GetCurrentStream(); + + va_list args; + va_start( args, nAttribute ); + do { + const char* pValue = va_arg( args, const char* ); + if( pValue ) + { + rStream->write( " " ) + ->writeId( nAttribute ) + ->write( "=\"" ) + ->writeEscaped( pValue ) + ->write( "\"" ); + } + + nAttribute = va_arg( args, sal_Int32 ); + if( nAttribute == FSEND ) + break; + } while( true ); + va_end( args ); + + return rStream; +} + +static void lcl_WriteValue( sax_fastparser::FSHelperPtr& rStream, sal_Int32 nElement, const char* pValue ) +{ + if( !pValue ) + return; + rStream->singleElement( nElement, + XML_val, pValue, + FSEND ); +} + +static const char* lcl_GetUnderlineStyle( FontUnderline eUnderline, bool& bHaveUnderline ) +{ + bHaveUnderline = true; + switch( eUnderline ) + { + // OOXTODO: doubleAccounting, singleAccounting + // OOXTODO: what should be done with the other FontUnderline values? + case UNDERLINE_SINGLE: return "single"; + case UNDERLINE_DOUBLE: return "double"; + case UNDERLINE_NONE: + default: bHaveUnderline = false; return "none"; + } +} + +static const char* lcl_ToVerticalAlignmentRun( SvxEscapement eEscapement, bool& bHaveAlignment ) +{ + bHaveAlignment = true; + switch( eEscapement ) + { + case SVX_ESCAPEMENT_SUPERSCRIPT: return "superscript"; + case SVX_ESCAPEMENT_SUBSCRIPT: return "subscript"; + case SVX_ESCAPEMENT_OFF: + default: bHaveAlignment = false; return "baseline"; + } +} + +sax_fastparser::FSHelperPtr& XclExpXmlStream::WriteFontData( const XclFontData& rFontData, sal_Int32 nFontId ) +{ + bool bHaveUnderline, bHaveVertAlign; + const char* pUnderline = lcl_GetUnderlineStyle( rFontData.GetScUnderline(), bHaveUnderline ); + const char* pVertAlign = lcl_ToVerticalAlignmentRun( rFontData.GetScEscapement(), bHaveVertAlign ); + + sax_fastparser::FSHelperPtr& rStream = GetCurrentStream(); + + lcl_WriteValue( rStream, nFontId, XclXmlUtils::ToOString( rFontData.maName ).getStr() ); + lcl_WriteValue( rStream, XML_charset, rFontData.mnCharSet != 0 ? OString::valueOf( (sal_Int32) rFontData.mnCharSet ).getStr() : NULL ); + lcl_WriteValue( rStream, XML_family, OString::valueOf( (sal_Int32) rFontData.mnFamily ).getStr() ); + lcl_WriteValue( rStream, XML_b, rFontData.mnWeight > 400 ? XclXmlUtils::ToPsz( rFontData.mnWeight > 400 ) : NULL ); + lcl_WriteValue( rStream, XML_i, rFontData.mbItalic ? XclXmlUtils::ToPsz( rFontData.mbItalic ) : NULL ); + lcl_WriteValue( rStream, XML_strike, rFontData.mbStrikeout ? XclXmlUtils::ToPsz( rFontData.mbStrikeout ) : NULL ); + lcl_WriteValue( rStream, XML_outline, rFontData.mbOutline ? XclXmlUtils::ToPsz( rFontData.mbOutline ) : NULL ); + lcl_WriteValue( rStream, XML_shadow, rFontData.mbShadow ? XclXmlUtils::ToPsz( rFontData.mbShadow ) : NULL ); + // OOXTODO: lcl_WriteValue( rStream, XML_condense, ); // mac compatibility setting + // OOXTODO: lcl_WriteValue( rStream, XML_extend, ); // compatibility setting + if( rFontData.maColor != Color( 0xFF, 0xFF, 0xFF, 0xFF ) ) + rStream->singleElement( XML_color, + // OOXTODO: XML_auto, bool + // OOXTODO: XML_indexed, uint + XML_rgb, XclXmlUtils::ToOString( rFontData.maColor ).getStr(), + // OOXTODO: XML_theme, index into <clrScheme/> + // OOXTODO: XML_tint, double + FSEND ); + lcl_WriteValue( rStream, XML_sz, OString::valueOf( (double) (rFontData.mnHeight / 20.0) ) ); // Twips->Pt + lcl_WriteValue( rStream, XML_u, bHaveUnderline ? pUnderline : NULL ); + lcl_WriteValue( rStream, XML_vertAlign, bHaveVertAlign ? pVertAlign : NULL ); + + return rStream; +} + +sax_fastparser::FSHelperPtr XclExpXmlStream::CreateOutputStream ( + const OUString& sFullStream, + const OUString& sRelativeStream, + const Reference< XOutputStream >& xParentRelation, + const char* sContentType, + const char* sRelationshipType, + ::rtl::OUString* pRelationshipId ) +{ + OUString sRelationshipId; + if (xParentRelation.is()) + sRelationshipId = addRelation( xParentRelation, OUString::createFromAscii( sRelationshipType), sRelativeStream ); + else + sRelationshipId = addRelation( OUString::createFromAscii( sRelationshipType ), sRelativeStream ); + + if( pRelationshipId ) + *pRelationshipId = sRelationshipId; + + sax_fastparser::FSHelperPtr p = openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) ); + + maOpenedStreamMap[ sFullStream ] = std::make_pair( sRelationshipId, p ); + + return p; +} + +bool XclExpXmlStream::importDocument() throw() +{ + return false; +} + +oox::vml::Drawing* XclExpXmlStream::getVmlDrawing() +{ + return 0; +} + +const oox::drawingml::Theme* XclExpXmlStream::getCurrentTheme() const +{ + return 0; +} + +const oox::drawingml::table::TableStyleListPtr XclExpXmlStream::getTableStyles() +{ + return oox::drawingml::table::TableStyleListPtr(); +} + +oox::drawingml::chart::ChartConverter& XclExpXmlStream::getChartConverter() +{ + // DO NOT CALL + return * (oox::drawingml::chart::ChartConverter*) NULL; +} + +bool XclExpXmlStream::exportDocument() throw() +{ + return false; +} + +::rtl::OUString XclExpXmlStream::implGetImplementationName() const +{ + return CREATE_OUSTRING( "TODO" ); +} + +void XclExpXmlStream::Trace( const char* format, ...) +{ + va_list ap; + va_start( ap, format ); + vfprintf( stderr, format, ap ); + va_end( ap ); +} + diff --git a/sc/source/filter/excel/xestring.cxx b/sc/source/filter/excel/xestring.cxx new file mode 100644 index 000000000000..cd0b083aa208 --- /dev/null +++ b/sc/source/filter/excel/xestring.cxx @@ -0,0 +1,656 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include <algorithm> +#include <stdio.h> +#include "xestream.hxx" +#include "xlstyle.hxx" +#include "xestyle.hxx" +#include "xestring.hxx" + +#include <oox/core/tokens.hxx> + +using ::rtl::OString; +using ::rtl::OUString; + +// ============================================================================ + +namespace { + +// compare vectors + +/** Compares two vectors. + @return A negative value, if rLeft<rRight; or a positive value, if rLeft>rRight; + or 0, if rLeft==rRight. */ +template< typename Type > +int lclCompareVectors( const ::std::vector< Type >& rLeft, const ::std::vector< Type >& rRight ) +{ + int nResult = 0; + + // 1st: compare all elements of the vectors + typedef typename ::std::vector< Type >::const_iterator CIT; + CIT aEndL = rLeft.end(), aEndR = rRight.end(); + for( CIT aItL = rLeft.begin(), aItR = rRight.begin(); !nResult && (aItL != aEndL) && (aItR != aEndR); ++aItL, ++aItR ) + nResult = static_cast< int >( *aItL ) - static_cast< int >( *aItR ); + + // 2nd: no differences found so far -> compare the vector sizes. Shorter vector is less + if( !nResult ) + nResult = static_cast< int >( rLeft.size() ) - static_cast< int >( rRight.size() ); + + return nResult; +} + +// hashing helpers + +/** Base class for value hashers. + @descr These function objects are used to hash any value to a sal_uInt32 value. */ +template< typename Type > +struct XclHasher : public ::std::unary_function< Type, sal_uInt32 > {}; + +template< typename Type > +struct XclDirectHasher : public XclHasher< Type > +{ + inline sal_uInt32 operator()( Type nVal ) const { return nVal; } +}; + +struct XclFormatRunHasher : public XclHasher< const XclFormatRun& > +{ + inline sal_uInt32 operator()( const XclFormatRun& rRun ) const + { return (rRun.mnChar << 8) ^ rRun.mnFontIdx; } +}; + +/** Calculates a hash value from a vector. + @descr Uses the passed hasher function object to calculate hash values from + all vector elements. */ +template< typename Type, typename ValueHasher > +sal_uInt16 lclHashVector( const ::std::vector< Type >& rVec, const ValueHasher& rHasher ) +{ + sal_uInt32 nHash = rVec.size(); + typedef typename ::std::vector< Type >::const_iterator CIT; + for( CIT aIt = rVec.begin(), aEnd = rVec.end(); aIt != aEnd; ++aIt ) + (nHash *= 31) += rHasher( *aIt ); + return static_cast< sal_uInt16 >( nHash ^ (nHash >> 16) ); +} + +/** Calculates a hash value from a vector. Uses XclDirectHasher to hash the vector elements. */ +template< typename Type > +inline sal_uInt16 lclHashVector( const ::std::vector< Type >& rVec ) +{ + return lclHashVector( rVec, XclDirectHasher< Type >() ); +} + +} // namespace + +// constructors --------------------------------------------------------------- + +XclExpString::XclExpString( XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + Init( 0, nFlags, nMaxLen, true ); +} + +XclExpString::XclExpString( const String& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + Assign( rString, nFlags, nMaxLen ); +} + +XclExpString::XclExpString( const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + Assign( rString, nFlags, nMaxLen ); +} + +//UNUSED2008-05 XclExpString::XclExpString( +//UNUSED2008-05 const String& rString, const XclFormatRunVec& rFormats, +//UNUSED2008-05 XclStrFlags nFlags, sal_uInt16 nMaxLen ) +//UNUSED2008-05 { +//UNUSED2008-05 Assign( rString, rFormats, nFlags, nMaxLen ); +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 XclExpString::XclExpString( +//UNUSED2008-05 const OUString& rString, const XclFormatRunVec& rFormats, +//UNUSED2008-05 XclStrFlags nFlags, sal_uInt16 nMaxLen ) +//UNUSED2008-05 { +//UNUSED2008-05 Assign( rString, rFormats, nFlags, nMaxLen ); +//UNUSED2008-05 } + +// assign --------------------------------------------------------------------- + +void XclExpString::Assign( const String& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + Build( rString.GetBuffer(), rString.Len(), nFlags, nMaxLen ); +} + +void XclExpString::Assign( + const String& rString, const XclFormatRunVec& rFormats, + XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + Assign( rString, nFlags, nMaxLen ); + SetFormats( rFormats ); +} + +void XclExpString::Assign( const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + Build( rString.getStr(), rString.getLength(), nFlags, nMaxLen ); +} + +void XclExpString::Assign( + const OUString& rString, const XclFormatRunVec& rFormats, + XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + Assign( rString, nFlags, nMaxLen ); + SetFormats( rFormats ); +} + +void XclExpString::Assign( sal_Unicode cChar, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + Build( &cChar, 1, nFlags, nMaxLen ); +} + +void XclExpString::AssignByte( + const String& rString, rtl_TextEncoding eTextEnc, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + ByteString aByteStr( rString, eTextEnc ); // length may differ from length of rString + Build( aByteStr.GetBuffer(), aByteStr.Len(), nFlags, nMaxLen ); +} + +//UNUSED2008-05 void XclExpString::AssignByte( sal_Unicode cChar, rtl_TextEncoding eTextEnc, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +//UNUSED2008-05 { +//UNUSED2008-05 if( !cChar ) +//UNUSED2008-05 { +//UNUSED2008-05 sal_Char cByteChar = 0; +//UNUSED2008-05 Build( &cByteChar, 1, nFlags, nMaxLen ); +//UNUSED2008-05 } +//UNUSED2008-05 else +//UNUSED2008-05 { +//UNUSED2008-05 ByteString aByteStr( &cChar, 1, eTextEnc ); // length may be >1 +//UNUSED2008-05 Build( aByteStr.GetBuffer(), aByteStr.Len(), nFlags, nMaxLen ); +//UNUSED2008-05 } +//UNUSED2008-05 } + +// append --------------------------------------------------------------------- + +void XclExpString::Append( const String& rString ) +{ + BuildAppend( rString.GetBuffer(), rString.Len() ); +} + +//UNUSED2008-05 void XclExpString::Append( const ::rtl::OUString& rString ) +//UNUSED2008-05 { +//UNUSED2008-05 BuildAppend( rString.getStr(), rString.getLength() ); +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 void XclExpString::Append( sal_Unicode cChar ) +//UNUSED2008-05 { +//UNUSED2008-05 BuildAppend( &cChar, 1 ); +//UNUSED2008-05 } + +void XclExpString::AppendByte( const String& rString, rtl_TextEncoding eTextEnc ) +{ + if( rString.Len() > 0 ) + { + ByteString aByteStr( rString, eTextEnc ); // length may differ from length of rString + BuildAppend( aByteStr.GetBuffer(), aByteStr.Len() ); + } +} + +void XclExpString::AppendByte( sal_Unicode cChar, rtl_TextEncoding eTextEnc ) +{ + if( !cChar ) + { + sal_Char cByteChar = 0; + BuildAppend( &cByteChar, 1 ); + } + else + { + ByteString aByteStr( &cChar, 1, eTextEnc ); // length may be >1 + BuildAppend( aByteStr.GetBuffer(), aByteStr.Len() ); + } +} + +// formatting runs ------------------------------------------------------------ + +void XclExpString::SetFormats( const XclFormatRunVec& rFormats ) +{ + maFormats = rFormats; +#ifdef DBG_UTIL + if( IsRich() ) + { + XclFormatRunVec::const_iterator aCurr = maFormats.begin(); + XclFormatRunVec::const_iterator aPrev = aCurr; + XclFormatRunVec::const_iterator aEnd = maFormats.end(); + for( ++aCurr; aCurr != aEnd; ++aCurr, ++aPrev ) + DBG_ASSERT( aPrev->mnChar < aCurr->mnChar, "XclExpString::SetFormats - invalid char order" ); + DBG_ASSERT( aPrev->mnChar <= mnLen, "XclExpString::SetFormats - invalid char index" ); + } +#endif + LimitFormatCount( mbIsBiff8 ? EXC_STR_MAXLEN : EXC_STR_MAXLEN_8BIT ); +} + +void XclExpString::AppendFormat( sal_uInt16 nChar, sal_uInt16 nFontIdx, bool bDropDuplicate ) +{ + DBG_ASSERT( maFormats.empty() || (maFormats.back().mnChar < nChar), "XclExpString::AppendFormat - invalid char index" ); + size_t nMaxSize = static_cast< size_t >( mbIsBiff8 ? EXC_STR_MAXLEN : EXC_STR_MAXLEN_8BIT ); + if( maFormats.empty() || ((maFormats.size() < nMaxSize) && (!bDropDuplicate || (maFormats.back().mnFontIdx != nFontIdx))) ) + maFormats.push_back( XclFormatRun( nChar, nFontIdx ) ); +} + +void XclExpString::AppendTrailingFormat( sal_uInt16 nFontIdx ) +{ + AppendFormat( mnLen, nFontIdx, false ); +} + +void XclExpString::LimitFormatCount( sal_uInt16 nMaxCount ) +{ + if( maFormats.size() > nMaxCount ) + maFormats.erase( maFormats.begin() + nMaxCount, maFormats.end() ); +} + +sal_uInt16 XclExpString::RemoveLeadingFont() +{ + sal_uInt16 nFontIdx = EXC_FONT_NOTFOUND; + if( !maFormats.empty() && (maFormats.front().mnChar == 0) ) + { + nFontIdx = maFormats.front().mnFontIdx; + maFormats.erase( maFormats.begin() ); + } + return nFontIdx; +} + +bool XclExpString::IsEqual( const XclExpString& rCmp ) const +{ + return + (mnLen == rCmp.mnLen) && + (mbIsBiff8 == rCmp.mbIsBiff8) && + (mbIsUnicode == rCmp.mbIsUnicode) && + (mbWrapped == rCmp.mbWrapped) && + ( + ( mbIsBiff8 && (maUniBuffer == rCmp.maUniBuffer)) || + (!mbIsBiff8 && (maCharBuffer == rCmp.maCharBuffer)) + ) && + (maFormats == rCmp.maFormats); +} + +bool XclExpString::IsLessThan( const XclExpString& rCmp ) const +{ + int nResult = mbIsBiff8 ? + lclCompareVectors( maUniBuffer, rCmp.maUniBuffer ) : + lclCompareVectors( maCharBuffer, rCmp.maCharBuffer ); + return (nResult != 0) ? (nResult < 0) : (maFormats < rCmp.maFormats); +} + +// get data ------------------------------------------------------------------- + +sal_uInt16 XclExpString::GetFormatsCount() const +{ + return static_cast< sal_uInt16 >( maFormats.size() ); +} + +sal_uInt8 XclExpString::GetFlagField() const +{ + return (mbIsUnicode ? EXC_STRF_16BIT : 0) | (IsWriteFormats() ? EXC_STRF_RICH : 0); +} + +sal_uInt16 XclExpString::GetHeaderSize() const +{ + return + (mb8BitLen ? 1 : 2) + // length field + (IsWriteFlags() ? 1 : 0) + // flag field + (IsWriteFormats() ? 2 : 0); // richtext formattting count +} + +sal_Size XclExpString::GetBufferSize() const +{ + return mnLen * (mbIsUnicode ? 2 : 1); +} + +sal_Size XclExpString::GetSize() const +{ + return + GetHeaderSize() + // header + GetBufferSize() + // character buffer + (IsWriteFormats() ? (4 * GetFormatsCount()) : 0); // richtext formattting +} + +sal_uInt16 XclExpString::GetChar( sal_uInt16 nCharIdx ) const +{ + DBG_ASSERT( nCharIdx < Len(), "XclExpString::GetChar - invalid character index" ); + return static_cast< sal_uInt16 >( mbIsBiff8 ? maUniBuffer[ nCharIdx ] : maCharBuffer[ nCharIdx ] ); +} + +sal_uInt16 XclExpString::GetHash() const +{ + return + (mbIsBiff8 ? lclHashVector( maUniBuffer ) : lclHashVector( maCharBuffer )) ^ + lclHashVector( maFormats, XclFormatRunHasher() ); +} + +// streaming ------------------------------------------------------------------ + +void XclExpString::WriteLenField( XclExpStream& rStrm ) const +{ + if( mb8BitLen ) + rStrm << static_cast< sal_uInt8 >( mnLen ); + else + rStrm << mnLen; +} + +void XclExpString::WriteFlagField( XclExpStream& rStrm ) const +{ + if( mbIsBiff8 ) + { + PrepareWrite( rStrm, 1 ); + rStrm << GetFlagField(); + rStrm.SetSliceSize( 0 ); + } +} + +void XclExpString::WriteHeader( XclExpStream& rStrm ) const +{ + DBG_ASSERT( !mb8BitLen || (mnLen < 256), "XclExpString::WriteHeader - string too long" ); + PrepareWrite( rStrm, GetHeaderSize() ); + // length + WriteLenField( rStrm ); + // flag field + if( IsWriteFlags() ) + rStrm << GetFlagField(); + // format run count + if( IsWriteFormats() ) + rStrm << GetFormatsCount(); + rStrm.SetSliceSize( 0 ); +} + +void XclExpString::WriteBuffer( XclExpStream& rStrm ) const +{ + if( mbIsBiff8 ) + rStrm.WriteUnicodeBuffer( maUniBuffer, GetFlagField() ); + else + rStrm.WriteCharBuffer( maCharBuffer ); +} + +void XclExpString::WriteFormats( XclExpStream& rStrm, bool bWriteSize ) const +{ + if( IsRich() ) + { + XclFormatRunVec::const_iterator aIt = maFormats.begin(), aEnd = maFormats.end(); + if( mbIsBiff8 ) + { + if( bWriteSize ) + rStrm << GetFormatsCount(); + rStrm.SetSliceSize( 4 ); + for( ; aIt != aEnd; ++aIt ) + rStrm << aIt->mnChar << aIt->mnFontIdx; + } + else + { + if( bWriteSize ) + rStrm << static_cast< sal_uInt8 >( GetFormatsCount() ); + rStrm.SetSliceSize( 2 ); + for( ; aIt != aEnd; ++aIt ) + rStrm << static_cast< sal_uInt8 >( aIt->mnChar ) << static_cast< sal_uInt8 >( aIt->mnFontIdx ); + } + rStrm.SetSliceSize( 0 ); + } +} + +void XclExpString::Write( XclExpStream& rStrm ) const +{ + if (!mbSkipHeader) + WriteHeader( rStrm ); + WriteBuffer( rStrm ); + if( IsWriteFormats() ) // only in BIFF8 included in string + WriteFormats( rStrm ); +} + +void XclExpString::WriteHeaderToMem( sal_uInt8* pnMem ) const +{ + DBG_ASSERT( pnMem, "XclExpString::WriteHeaderToMem - no memory to write to" ); + DBG_ASSERT( !mb8BitLen || (mnLen < 256), "XclExpString::WriteHeaderToMem - string too long" ); + DBG_ASSERT( !IsWriteFormats(), "XclExpString::WriteHeaderToMem - formatted strings not supported" ); + // length + if( mb8BitLen ) + { + *pnMem = static_cast< sal_uInt8 >( mnLen ); + ++pnMem; + } + else + { + ShortToSVBT16( mnLen, pnMem ); + pnMem += 2; + } + // flag field + if( IsWriteFlags() ) + *pnMem = GetFlagField(); +} + +void XclExpString::WriteBufferToMem( sal_uInt8* pnMem ) const +{ + DBG_ASSERT( pnMem, "XclExpString::WriteBufferToMem - no memory to write to" ); + if( !IsEmpty() ) + { + if( mbIsBiff8 ) + { + for( ScfUInt16Vec::const_iterator aIt = maUniBuffer.begin(), aEnd = maUniBuffer.end(); aIt != aEnd; ++aIt ) + { + sal_uInt16 nChar = *aIt; + *pnMem = static_cast< sal_uInt8 >( nChar ); + ++pnMem; + if( mbIsUnicode ) + { + *pnMem = static_cast< sal_uInt8 >( nChar >> 8 ); + ++pnMem; + } + } + } + else + memcpy( pnMem, &maCharBuffer[ 0 ], mnLen ); + } +} + +void XclExpString::WriteToMem( sal_uInt8* pnMem ) const +{ + WriteHeaderToMem( pnMem ); + WriteBufferToMem( pnMem + GetHeaderSize() ); +} + +static sal_uInt16 lcl_WriteRun( XclExpXmlStream& rStrm, const ScfUInt16Vec& rBuffer, sal_uInt16 nStart, sal_Int32 nLength, const XclExpFont* pFont ) +{ + if( nLength == 0 ) + return nStart; + + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + + rWorksheet->startElement( XML_r, FSEND ); + if( pFont ) + { + const XclFontData& rFontData = pFont->GetFontData(); + rWorksheet->startElement( XML_rPr, FSEND ); + rStrm.WriteFontData( rFontData, XML_rFont ); + rWorksheet->endElement( XML_rPr ); + } + rWorksheet->startElement( XML_t, + FSNS( XML_xml, XML_space ), "preserve", + FSEND ); + rWorksheet->writeEscaped( XclXmlUtils::ToOUString( rBuffer, nStart, nLength ) ); + rWorksheet->endElement( XML_t ); + rWorksheet->endElement( XML_r ); + return static_cast<sal_uInt16>(nStart + nLength); +} + +void XclExpString::WriteXml( XclExpXmlStream& rStrm ) const +{ + sax_fastparser::FSHelperPtr rWorksheet = rStrm.GetCurrentStream(); + + if( !IsWriteFormats() ) + { + rWorksheet->startElement( XML_t, FSEND ); + rWorksheet->writeEscaped( XclXmlUtils::ToOUString( *this ) ); + rWorksheet->endElement( XML_t ); + } + else + { + XclExpFontBuffer& rFonts = rStrm.GetRoot().GetFontBuffer(); + XclFormatRunVec::const_iterator aIt = maFormats.begin(), aEnd = maFormats.end(); + + sal_uInt16 nStart = 0; + const XclExpFont* pFont = NULL; + for ( ; aIt != aEnd; ++aIt ) + { + nStart = lcl_WriteRun( rStrm, GetUnicodeBuffer(), + nStart, aIt->mnChar-nStart, pFont ); + pFont = rFonts.GetFont( aIt->mnFontIdx ); + } + lcl_WriteRun( rStrm, GetUnicodeBuffer(), + nStart, GetUnicodeBuffer().size() - nStart, pFont ); + } +} + +// ---------------------------------------------------------------------------- + +bool XclExpString::IsWriteFlags() const +{ + return mbIsBiff8 && (!IsEmpty() || !mbSmartFlags); +} + +bool XclExpString::IsWriteFormats() const +{ + return mbIsBiff8 && !mbSkipFormats && IsRich(); +} + +void XclExpString::SetStrLen( sal_Int32 nNewLen ) +{ + sal_uInt16 nAllowedLen = (mb8BitLen && (mnMaxLen > 255)) ? 255 : mnMaxLen; + mnLen = limit_cast< sal_uInt16 >( nNewLen, 0, nAllowedLen ); +} + +void XclExpString::CharsToBuffer( const sal_Unicode* pcSource, sal_Int32 nBegin, sal_Int32 nLen ) +{ + DBG_ASSERT( maUniBuffer.size() >= static_cast< size_t >( nBegin + nLen ), + "XclExpString::CharsToBuffer - char buffer invalid" ); + ScfUInt16Vec::iterator aBeg = maUniBuffer.begin() + nBegin; + ScfUInt16Vec::iterator aEnd = aBeg + nLen; + const sal_Unicode* pcSrcChar = pcSource; + for( ScfUInt16Vec::iterator aIt = aBeg; aIt != aEnd; ++aIt, ++pcSrcChar ) + { + *aIt = static_cast< sal_uInt16 >( *pcSrcChar ); + if( *aIt & 0xFF00 ) + mbIsUnicode = true; + } + if( !mbWrapped ) + mbWrapped = ::std::find( aBeg, aEnd, EXC_LF ) != aEnd; +} + +void XclExpString::CharsToBuffer( const sal_Char* pcSource, sal_Int32 nBegin, sal_Int32 nLen ) +{ + DBG_ASSERT( maCharBuffer.size() >= static_cast< size_t >( nBegin + nLen ), + "XclExpString::CharsToBuffer - char buffer invalid" ); + ScfUInt8Vec::iterator aBeg = maCharBuffer.begin() + nBegin; + ScfUInt8Vec::iterator aEnd = aBeg + nLen; + const sal_Char* pcSrcChar = pcSource; + for( ScfUInt8Vec::iterator aIt = aBeg; aIt != aEnd; ++aIt, ++pcSrcChar ) + *aIt = static_cast< sal_uInt8 >( *pcSrcChar ); + mbIsUnicode = false; + if( !mbWrapped ) + mbWrapped = ::std::find( aBeg, aEnd, EXC_LF_C ) != aEnd; +} + +void XclExpString::Init( sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen, bool bBiff8 ) +{ + mbIsBiff8 = bBiff8; + mbIsUnicode = bBiff8 && ::get_flag( nFlags, EXC_STR_FORCEUNICODE ); + mb8BitLen = ::get_flag( nFlags, EXC_STR_8BITLENGTH ); + mbSmartFlags = bBiff8 && ::get_flag( nFlags, EXC_STR_SMARTFLAGS ); + mbSkipFormats = ::get_flag( nFlags, EXC_STR_SEPARATEFORMATS ); + mbWrapped = false; + mbSkipHeader = ::get_flag( nFlags, EXC_STR_NOHEADER ); + mnMaxLen = nMaxLen; + SetStrLen( nCurrLen ); + + maFormats.clear(); + if( mbIsBiff8 ) + { + maCharBuffer.clear(); + maUniBuffer.resize( mnLen ); + } + else + { + maUniBuffer.clear(); + maCharBuffer.resize( mnLen ); + } +} + +void XclExpString::Build( const sal_Unicode* pcSource, sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + Init( nCurrLen, nFlags, nMaxLen, true ); + CharsToBuffer( pcSource, 0, mnLen ); +} + +void XclExpString::Build( const sal_Char* pcSource, sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + Init( nCurrLen, nFlags, nMaxLen, false ); + CharsToBuffer( pcSource, 0, mnLen ); +} + +void XclExpString::InitAppend( sal_Int32 nAddLen ) +{ + SetStrLen( static_cast< sal_Int32 >( mnLen ) + nAddLen ); + if( mbIsBiff8 ) + maUniBuffer.resize( mnLen ); + else + maCharBuffer.resize( mnLen ); +} + +void XclExpString::BuildAppend( const sal_Unicode* pcSource, sal_Int32 nAddLen ) +{ + DBG_ASSERT( mbIsBiff8, "XclExpString::BuildAppend - must not be called at byte strings" ); + if( mbIsBiff8 ) + { + sal_uInt16 nOldLen = mnLen; + InitAppend( nAddLen ); + CharsToBuffer( pcSource, nOldLen, mnLen - nOldLen ); + } +} + +void XclExpString::BuildAppend( const sal_Char* pcSource, sal_Int32 nAddLen ) +{ + DBG_ASSERT( !mbIsBiff8, "XclExpString::BuildAppend - must not be called at unicode strings" ); + if( !mbIsBiff8 ) + { + sal_uInt16 nOldLen = mnLen; + InitAppend( nAddLen ); + CharsToBuffer( pcSource, nOldLen, mnLen - nOldLen ); + } +} + +void XclExpString::PrepareWrite( XclExpStream& rStrm, sal_uInt16 nBytes ) const +{ + rStrm.SetSliceSize( nBytes + (mbIsUnicode ? 2 : 1) ); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xestyle.cxx b/sc/source/filter/excel/xestyle.cxx new file mode 100644 index 000000000000..ccebe9a4280f --- /dev/null +++ b/sc/source/filter/excel/xestyle.cxx @@ -0,0 +1,2903 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xestyle.hxx" + +#include <algorithm> +#include <iterator> +#include <set> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <vcl/font.hxx> +#include <svl/zformat.hxx> +#include <svl/languageoptions.hxx> +#include <sfx2/printer.hxx> +#include "scitems.hxx" +#include <svx/algitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/bolnitem.hxx> +#include <svx/rotmodit.hxx> +#include <editeng/colritem.hxx> +#include <editeng/brshitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/escpitem.hxx> +#include "document.hxx" +#include "stlpool.hxx" +#include "stlsheet.hxx" +#include "patattr.hxx" +#include "attrib.hxx" +#include "globstr.hrc" +#include "xestring.hxx" + +#include <oox/core/tokens.hxx> + +using ::rtl::OString; +using ::rtl::OUString; + +// PALETTE record - color information ========================================= + +namespace { + +sal_uInt32 lclGetWeighting( XclExpColorType eType ) +{ + switch( eType ) + { + case EXC_COLOR_CHARTLINE: return 1; + case EXC_COLOR_CELLBORDER: + case EXC_COLOR_CHARTAREA: return 2; + case EXC_COLOR_CELLTEXT: + case EXC_COLOR_CHARTTEXT: + case EXC_COLOR_CTRLTEXT: return 10; + case EXC_COLOR_TABBG: + case EXC_COLOR_CELLAREA: return 20; + case EXC_COLOR_GRID: return 50; + default: DBG_ERRORFILE( "lclGetWeighting - unknown color type" ); + } + return 1; +} + +sal_Int32 lclGetColorDistance( const Color& rColor1, const Color& rColor2 ) +{ + sal_Int32 nDist = rColor1.GetRed() - rColor2.GetRed(); + nDist *= nDist * 77; + sal_Int32 nDummy = rColor1.GetGreen() - rColor2.GetGreen(); + nDist += nDummy * nDummy * 151; + nDummy = rColor1.GetBlue() - rColor2.GetBlue(); + nDist += nDummy * nDummy * 28; + return nDist; +} + +sal_uInt8 lclGetMergedColorComp( sal_uInt8 nComp1, sal_uInt32 nWeight1, sal_uInt8 nComp2, sal_uInt32 nWeight2 ) +{ + sal_uInt8 nComp1Dist = ::std::min< sal_uInt8 >( nComp1, 0xFF - nComp1 ); + sal_uInt8 nComp2Dist = ::std::min< sal_uInt8 >( nComp2, 0xFF - nComp2 ); + if( nComp1Dist != nComp2Dist ) + { + /* #i36945# One of the passed RGB components is nearer at the limits (0x00 or 0xFF). + Increase its weighting to prevent fading of the colors during reduction. */ + const sal_uInt8& rnCompNearer = (nComp1Dist < nComp2Dist) ? nComp1 : nComp2; + sal_uInt32& rnWeight = (nComp1Dist < nComp2Dist) ? nWeight1 : nWeight2; + rnWeight *= ((rnCompNearer - 0x80L) * (rnCompNearer - 0x7FL) / 0x1000L + 1); + } + sal_uInt32 nWSum = nWeight1 + nWeight2; + return static_cast< sal_uInt8 >( (nComp1 * nWeight1 + nComp2 * nWeight2 + nWSum / 2) / nWSum ); +} + +void lclSetMixedColor( Color& rDest, const Color& rSrc1, const Color& rSrc2 ) +{ + rDest.SetRed( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetRed() ) + rSrc2.GetRed()) / 2 ) ); + rDest.SetGreen( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetGreen() ) + rSrc2.GetGreen()) / 2 ) ); + rDest.SetBlue( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetBlue() ) + rSrc2.GetBlue()) / 2 ) ); +} + +} // namespace + +// additional classes for color reduction ------------------------------------- + +namespace { + +/** Represents an entry in a color list. + + The color stores a weighting value, which increases the more the color is + used in the document. Heavy-weighted colors will change less than others on + color reduction. + */ +class XclListColor +{ + DECL_FIXEDMEMPOOL_NEWDEL( XclListColor ) + +private: + Color maColor; /// The color value of this palette entry. + sal_uInt32 mnColorId; /// Unique color ID for color reduction. + sal_uInt32 mnWeight; /// Weighting for color reduction. + bool mbBaseColor; /// true = Handle as base color, (don't remove/merge). + +public: + explicit XclListColor( const Color& rColor, sal_uInt32 nColorId ); + + /** Returns the RGB color value of the color. */ + inline const Color& GetColor() const { return maColor; } + /** Returns the unique ID of the color. */ + inline sal_uInt32 GetColorId() const { return mnColorId; } + /** Returns the current weighting of the color. */ + inline sal_uInt32 GetWeighting() const { return mnWeight; } + /** Returns true, if this color is a base color, i.e. it will not be removed or merged. */ + inline bool IsBaseColor() const { return mbBaseColor; } + + /** Adds the passed weighting to this color. */ + inline void AddWeighting( sal_uInt32 nWeight ) { mnWeight += nWeight; } + /** Merges this color with rColor, regarding weighting settings. */ + void Merge( const XclListColor& rColor ); +}; + +IMPL_FIXEDMEMPOOL_NEWDEL( XclListColor, 100, 100 ) + +XclListColor::XclListColor( const Color& rColor, sal_uInt32 nColorId ) : + maColor( rColor ), + mnColorId( nColorId ), + mnWeight( 0 ) +{ + mbBaseColor = + ((rColor.GetRed() == 0x00) || (rColor.GetRed() == 0xFF)) && + ((rColor.GetGreen() == 0x00) || (rColor.GetGreen() == 0xFF)) && + ((rColor.GetBlue() == 0x00) || (rColor.GetBlue() == 0xFF)); +} + +void XclListColor::Merge( const XclListColor& rColor ) +{ + sal_uInt32 nWeight2 = rColor.GetWeighting(); + // do not change RGB value of base colors + if( !mbBaseColor ) + { + maColor.SetRed( lclGetMergedColorComp( maColor.GetRed(), mnWeight, rColor.maColor.GetRed(), nWeight2 ) ); + maColor.SetGreen( lclGetMergedColorComp( maColor.GetGreen(), mnWeight, rColor.maColor.GetGreen(), nWeight2 ) ); + maColor.SetBlue( lclGetMergedColorComp( maColor.GetBlue(), mnWeight, rColor.maColor.GetBlue(), nWeight2 ) ); + } + AddWeighting( nWeight2 ); +} + +// ---------------------------------------------------------------------------- + +/** Data for each inserted original color, represented by a color ID. */ +struct XclColorIdData +{ + Color maColor; /// The original inserted color. + sal_uInt32 mnIndex; /// Maps current color ID to color list or export color vector. + /** Sets the contents of this struct. */ + inline void Set( const Color& rColor, sal_uInt32 nIndex ) { maColor = rColor; mnIndex = nIndex; } +}; + +/** A color that will be written to the Excel file. */ +struct XclPaletteColor +{ + Color maColor; /// Resulting color to export. + bool mbUsed; /// true = Entry is used in the document. + + inline explicit XclPaletteColor( const Color& rColor ) : maColor( rColor ), mbUsed( false ) {} + inline void SetColor( const Color& rColor ) { maColor = rColor; mbUsed = true; } +}; + +/** Maps a color list index to a palette index. + @descr Used to remap the color ID data vector from list indexes to palette indexes. */ +struct XclRemap +{ + sal_uInt32 mnPalIndex; /// Index to palette. + bool mbProcessed; /// true = List color already processed. + + inline explicit XclRemap() : mnPalIndex( 0 ), mbProcessed( false ) {} + inline void SetIndex( sal_uInt32 nPalIndex ) + { mnPalIndex = nPalIndex; mbProcessed = true; } +}; + +/** Stores the nearest palette color index of a list color. */ +struct XclNearest +{ + sal_uInt32 mnPalIndex; /// Index to nearest palette color. + sal_Int32 mnDist; /// Distance to palette color. + + inline explicit XclNearest() : mnPalIndex( 0 ), mnDist( 0 ) {} +}; + +typedef ::std::vector< XclRemap > XclRemapVec; +typedef ::std::vector< XclNearest > XclNearestVec; + +} // namespace + +// ---------------------------------------------------------------------------- + +class XclExpPaletteImpl +{ +public: + explicit XclExpPaletteImpl( const XclDefaultPalette& rDefPal ); + + /** Inserts the color into the list and updates weighting. + @param nAutoDefault The Excel palette index for automatic color. + @return A unique ID for this color. */ + sal_uInt32 InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault = 0 ); + /** Returns the color ID representing a fixed Excel palette index (i.e. for auto colors). */ + static sal_uInt32 GetColorIdFromIndex( sal_uInt16 nIndex ); + + /** Reduces the color list to the maximum count of the current BIFF version. */ + void Finalize(); + + /** Returns the Excel palette index of the color with passed color ID. */ + sal_uInt16 GetColorIndex( sal_uInt32 nColorId ) const; + + /** Returns a foreground and background color for the two passed color IDs. + @descr If rnXclPattern contains a solid pattern, this function tries to find + the two best fitting colors and a mix pattern (25%, 50% or 75%) for nForeColorId. + This will result in a better approximation to the passed foreground color. */ + void GetMixedColors( + sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern, + sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const; + + /** Returns the RGB color data for a (non-zero-based) Excel palette entry. + @return The color from current or default palette or COL_AUTO, if nothing else found. */ + ColorData GetColorData( sal_uInt16 nXclIndex ) const; + /** Returns the color for a (non-zero-based) Excel palette entry. + @return The color from current or default palette or COL_AUTO, if nothing else found. */ + inline Color GetColor( sal_uInt16 nXclIndex ) const + { return Color( GetColorData( nXclIndex ) ); } + + /** Returns true, if all colors of the palette are equal to default palette colors. */ + bool IsDefaultPalette() const; + /** Writes the color list (contents of the palette record) to the passed stream. */ + void WriteBody( XclExpStream& rStrm ); + void SaveXml( XclExpXmlStream& rStrm ); + +private: + /** Returns the Excel index of a 0-based color index. */ + inline sal_uInt16 GetXclIndex( sal_uInt32 nIndex ) const + { return static_cast< sal_uInt16 >( nIndex + EXC_COLOR_USEROFFSET ); } + + /** Returns the original inserted color represented by the color ID nColorId. */ + const Color& GetOriginalColor( sal_uInt32 nColorId ) const; + + /** Searches for rColor, returns the ordered insertion index for rColor in rnIndex. */ + XclListColor* SearchListEntry( const Color& rColor, sal_uInt32& rnIndex ); + /** Creates and inserts a new color list entry at the specified list position. */ + XclListColor* CreateListEntry( const Color& rColor, sal_uInt32 nIndex ); + + /** Raw and fast reduction of the palette. */ + void RawReducePalette( sal_uInt32 nPass ); + /** Reduction of one color using advanced color merging based on color weighting. */ + void ReduceLeastUsedColor(); + + /** Finds the least used color and returns its current list index. */ + sal_uInt32 GetLeastUsedListColor() const; + /** Returns the list index of the color nearest to rColor. + @param nIgnore List index of a color which will be ignored. + @return The list index of the found color. */ + sal_uInt32 GetNearestListColor( const Color& rColor, sal_uInt32 nIgnore ) const; + /** Returns the list index of the color nearest to the color with list index nIndex. */ + sal_uInt32 GetNearestListColor( sal_uInt32 nIndex ) const; + + /** Returns in rnIndex the palette index of the color nearest to rColor. + @param bDefaultOnly true = Searches for default colors only (colors never replaced). + @return The distance from passed color to found color. */ + sal_Int32 GetNearestPaletteColor( + sal_uInt32& rnIndex, + const Color& rColor, bool bDefaultOnly ) const; + /** Returns in rnFirst and rnSecond the palette indexes of the two colors nearest to rColor. + @return The minimum distance from passed color to found colors. */ + sal_Int32 GetNearPaletteColors( + sal_uInt32& rnFirst, sal_uInt32& rnSecond, + const Color& rColor ) const; + +private: + typedef ScfDelList< XclListColor > XclListColorList; + typedef ScfRef< XclListColorList > XclListColorListRef; + typedef ::std::vector< XclColorIdData > XclColorIdDataVec; + typedef ::std::vector< XclPaletteColor > XclPaletteColorVec; + + const XclDefaultPalette& mrDefPal; /// The default palette for the current BIFF version. + XclListColorListRef mxColorList; /// Working color list. + XclColorIdDataVec maColorIdDataVec; /// Data of all CIDs. + XclPaletteColorVec maPalette; /// Contains resulting colors to export. + sal_uInt32 mnLastIdx; /// Last insertion index for search opt. +}; + +// ---------------------------------------------------------------------------- + +const sal_uInt32 EXC_PAL_INDEXBASE = 0xFFFF0000; +const sal_uInt32 EXC_PAL_MAXRAWSIZE = 1024; + +XclExpPaletteImpl::XclExpPaletteImpl( const XclDefaultPalette& rDefPal ) : + mrDefPal( rDefPal ), + mxColorList( new XclListColorList ), + mnLastIdx( 0 ) +{ + // initialize maPalette with default colors + sal_uInt16 nCount = static_cast< sal_uInt16 >( mrDefPal.GetColorCount() ); + maPalette.reserve( nCount ); + for( sal_uInt16 nIdx = 0; nIdx < nCount; ++nIdx ) + maPalette.push_back( XclPaletteColor( mrDefPal.GetDefColor( GetXclIndex( nIdx ) ) ) ); + + InsertColor( Color( COL_BLACK ), EXC_COLOR_CELLTEXT ); +} + +sal_uInt32 XclExpPaletteImpl::InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault ) +{ + if( rColor.GetColor() == COL_AUTO ) + return GetColorIdFromIndex( nAutoDefault ); + + sal_uInt32 nFoundIdx = 0; + XclListColor* pEntry = SearchListEntry( rColor, nFoundIdx ); + if( !pEntry || (pEntry->GetColor() != rColor) ) + pEntry = CreateListEntry( rColor, nFoundIdx ); + pEntry->AddWeighting( lclGetWeighting( eType ) ); + + return pEntry->GetColorId(); +} + +sal_uInt32 XclExpPaletteImpl::GetColorIdFromIndex( sal_uInt16 nIndex ) +{ + return EXC_PAL_INDEXBASE | nIndex; +} + +void XclExpPaletteImpl::Finalize() +{ +// --- build initial color ID data vector (maColorIdDataVec) --- + + sal_uInt32 nCount = mxColorList->Count(); + maColorIdDataVec.resize( nCount ); + for( sal_uInt32 nIdx = 0; nIdx < nCount; ++nIdx ) + { + XclListColor* pListColor = mxColorList->GetObject( nIdx ); + maColorIdDataVec[ pListColor->GetColorId() ].Set( pListColor->GetColor(), nIdx ); + } + +// --- loop as long as current color count does not fit into palette of current BIFF --- + + // phase 1: raw reduction (performance reasons, #i36945#) + sal_uInt32 nPass = 0; + while( mxColorList->Count() > EXC_PAL_MAXRAWSIZE ) + RawReducePalette( nPass++ ); + + // phase 2: precise reduction using advanced color merging based on color weighting + while( mxColorList->Count() > mrDefPal.GetColorCount() ) + ReduceLeastUsedColor(); + +// --- #104865# use default palette and replace colors with nearest used colors --- + + nCount = mxColorList->Count(); + XclRemapVec aRemapVec( nCount ); + XclNearestVec aNearestVec( nCount ); + + // in each run: search the best fitting color and replace a default color with it + for( sal_uInt32 nRun = 0; nRun < nCount; ++nRun ) + { + sal_uInt32 nIndex; + // find nearest unused default color for each unprocessed list color + for( nIndex = 0; nIndex < nCount; ++nIndex ) + aNearestVec[ nIndex ].mnDist = aRemapVec[ nIndex ].mbProcessed ? SAL_MAX_INT32 : + GetNearestPaletteColor( aNearestVec[ nIndex ].mnPalIndex, mxColorList->GetObject( nIndex )->GetColor(), true ); + // find the list color which is nearest to a default color + sal_uInt32 nFound = 0; + for( nIndex = 1; nIndex < nCount; ++nIndex ) + if( aNearestVec[ nIndex ].mnDist < aNearestVec[ nFound ].mnDist ) + nFound = nIndex; + // replace default color with list color + sal_uInt32 nNearest = aNearestVec[ nFound ].mnPalIndex; + DBG_ASSERT( mxColorList->GetObject( nFound ), "XclExpPaletteImpl::Finalize - missing a color" ); + DBG_ASSERT( nNearest < maPalette.size(), "XclExpPaletteImpl::Finalize - algorithm error" ); + maPalette[ nNearest ].SetColor( mxColorList->GetObject( nFound )->GetColor() ); + aRemapVec[ nFound ].SetIndex( nNearest ); + } + + // remap color ID data map (maColorIdDataVec) from list indexes to palette indexes + for( XclColorIdDataVec::iterator aIt = maColorIdDataVec.begin(), aEnd = maColorIdDataVec.end(); aIt != aEnd; ++aIt ) + aIt->mnIndex = aRemapVec[ aIt->mnIndex ].mnPalIndex; +} + +sal_uInt16 XclExpPaletteImpl::GetColorIndex( sal_uInt32 nColorId ) const +{ + sal_uInt16 nRet = 0; + if( nColorId >= EXC_PAL_INDEXBASE ) + nRet = static_cast< sal_uInt16 >( nColorId & ~EXC_PAL_INDEXBASE ); + else if( nColorId < maColorIdDataVec.size() ) + nRet = GetXclIndex( maColorIdDataVec[ nColorId ].mnIndex ); + return nRet; +} + +void XclExpPaletteImpl::GetMixedColors( + sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern, + sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const +{ + rnXclForeIx = GetColorIndex( nForeColorId ); + rnXclBackIx = GetColorIndex( nBackColorId ); + if( (rnXclPattern != EXC_PATT_SOLID) || (nForeColorId >= maColorIdDataVec.size()) ) + return; + + // now we have solid pattern, and a defined foreground (background doesn't care for solid pattern) + + sal_uInt32 nIndex1, nIndex2; + Color aForeColor( GetOriginalColor( nForeColorId ) ); + sal_Int32 nFirstDist = GetNearPaletteColors( nIndex1, nIndex2, aForeColor ); + if( (nIndex1 >= maPalette.size()) || (nIndex2 >= maPalette.size()) ) + return; + + Color aColorArr[ 5 ]; + aColorArr[ 0 ] = maPalette[ nIndex1 ].maColor; + aColorArr[ 4 ] = maPalette[ nIndex2 ].maColor; + lclSetMixedColor( aColorArr[ 2 ], aColorArr[ 0 ], aColorArr[ 4 ] ); + lclSetMixedColor( aColorArr[ 1 ], aColorArr[ 0 ], aColorArr[ 2 ] ); + lclSetMixedColor( aColorArr[ 3 ], aColorArr[ 2 ], aColorArr[ 4 ] ); + + sal_Int32 nMinDist = nFirstDist; + sal_uInt32 nMinIndex = 0; + for( sal_uInt32 nCnt = 1; nCnt < 4; ++nCnt ) + { + sal_Int32 nDist = lclGetColorDistance( aForeColor, aColorArr[ nCnt ] ); + if( nDist < nMinDist ) + { + nMinDist = nDist; + nMinIndex = nCnt; + } + } + rnXclForeIx = GetXclIndex( nIndex1 ); + rnXclBackIx = GetXclIndex( nIndex2 ); + if( nMinDist < nFirstDist ) + { + switch( nMinIndex ) + { + case 1: rnXclPattern = EXC_PATT_75_PERC; break; + case 2: rnXclPattern = EXC_PATT_50_PERC; break; + case 3: rnXclPattern = EXC_PATT_25_PERC; break; + } + } +} + +ColorData XclExpPaletteImpl::GetColorData( sal_uInt16 nXclIndex ) const +{ + if( nXclIndex >= EXC_COLOR_USEROFFSET ) + { + sal_uInt32 nIdx = nXclIndex - EXC_COLOR_USEROFFSET; + if( nIdx < maPalette.size() ) + return maPalette[ nIdx ].maColor.GetColor(); + } + return mrDefPal.GetDefColorData( nXclIndex ); +} + +bool XclExpPaletteImpl::IsDefaultPalette() const +{ + bool bDefault = true; + for( sal_uInt32 nIdx = 0, nSize = static_cast< sal_uInt32 >( maPalette.size() ); bDefault && (nIdx < nSize); ++nIdx ) + bDefault = maPalette[ nIdx ].maColor == mrDefPal.GetDefColor( GetXclIndex( nIdx ) ); + return bDefault; +} + +void XclExpPaletteImpl::WriteBody( XclExpStream& rStrm ) +{ + rStrm << static_cast< sal_uInt16 >( maPalette.size() ); + for( XclPaletteColorVec::const_iterator aIt = maPalette.begin(), aEnd = maPalette.end(); aIt != aEnd; ++aIt ) + rStrm << aIt->maColor; +} + +void XclExpPaletteImpl::SaveXml( XclExpXmlStream& rStrm ) +{ + if( !maPalette.size() ) + return; + + sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream(); + rStyleSheet->startElement( XML_colors, FSEND ); + rStyleSheet->startElement( XML_indexedColors, FSEND ); + for( XclPaletteColorVec::const_iterator aIt = maPalette.begin(), aEnd = maPalette.end(); aIt != aEnd; ++aIt ) + rStyleSheet->singleElement( XML_rgbColor, + XML_rgb, XclXmlUtils::ToOString( aIt->maColor ).getStr(), + FSEND ); + rStyleSheet->endElement( XML_indexedColors ); + rStyleSheet->endElement( XML_colors ); +} + +const Color& XclExpPaletteImpl::GetOriginalColor( sal_uInt32 nColorId ) const +{ + if( nColorId < maColorIdDataVec.size() ) + return maColorIdDataVec[ nColorId ].maColor; + return maPalette[ 0 ].maColor; +} + +XclListColor* XclExpPaletteImpl::SearchListEntry( const Color& rColor, sal_uInt32& rnIndex ) +{ + rnIndex = mnLastIdx; + XclListColor* pEntry = mxColorList->GetObject( rnIndex ); + + // search optimization for equal-colored objects occuring repeatedly + if( pEntry && (pEntry->GetColor() == rColor) ) + return pEntry; + + // binary search for color + sal_uInt32 nBegIdx = 0; + sal_uInt32 nEndIdx = mxColorList->Count(); + bool bFound = false; + while( !bFound && (nBegIdx < nEndIdx) ) + { + rnIndex = (nBegIdx + nEndIdx) / 2; + pEntry = mxColorList->GetObject( rnIndex ); + bFound = pEntry->GetColor() == rColor; + if( !bFound ) + { + if( pEntry->GetColor().GetColor() < rColor.GetColor() ) + nBegIdx = rnIndex + 1; + else + nEndIdx = rnIndex; + } + } + // not found - use end of range as new insertion position + if( !bFound ) + pEntry = mxColorList->GetObject( rnIndex = nEndIdx ); + + mnLastIdx = rnIndex; + return pEntry; +} + +XclListColor* XclExpPaletteImpl::CreateListEntry( const Color& rColor, sal_uInt32 nIndex ) +{ + XclListColor* pEntry = new XclListColor( rColor, mxColorList->Count() ); + mxColorList->Insert( pEntry, nIndex ); + return pEntry; +} + +void XclExpPaletteImpl::RawReducePalette( sal_uInt32 nPass ) +{ + /* Fast palette reduction - in each call of this function one RGB component + of each color is reduced to a lower number of distinct values. + Pass 0: Blue is reduced to 128 distinct values. + Pass 1: Red is reduced to 128 distinct values. + Pass 2: Green is reduced to 128 distinct values. + Pass 3: Blue is reduced to 64 distinct values. + Pass 4: Red is reduced to 64 distinct values. + Pass 5: Green is reduced to 64 distinct values. + And so on... + */ + + XclListColorListRef xOldList = mxColorList; + mxColorList.reset( new XclListColorList ); + + // maps old list indexes to new list indexes, used to update maColorIdDataVec + ScfUInt32Vec aListIndexMap; + aListIndexMap.reserve( xOldList->Count() ); + + // preparations + sal_uInt8 nR, nG, nB; + sal_uInt8& rnComp = ((nPass % 3 == 0) ? nB : ((nPass % 3 == 1) ? nR : nG)); + nPass /= 3; + DBG_ASSERT( nPass < 7, "XclExpPaletteImpl::RawReducePalette - reduction not terminated" ); + + static const sal_uInt8 spnFactor2[] = { 0x81, 0x82, 0x84, 0x88, 0x92, 0xAA, 0xFF }; + sal_uInt8 nFactor1 = static_cast< sal_uInt8 >( 0x02 << nPass ); + sal_uInt8 nFactor2 = spnFactor2[ nPass ]; + sal_uInt8 nFactor3 = static_cast< sal_uInt8 >( 0x40 >> nPass ); + + // process each color in the old color list + for( sal_uInt32 nIdx = 0, nCount = xOldList->Count(); nIdx < nCount; ++nIdx ) + { + // get the old list entry + const XclListColor* pOldEntry = xOldList->GetObject( nIdx ); + nR = pOldEntry->GetColor().GetRed(); + nG = pOldEntry->GetColor().GetGreen(); + nB = pOldEntry->GetColor().GetBlue(); + + /* Calculate the new RGB component (rnComp points to one of nR, nG, nB). + Using integer arithmetic with its rounding errors, the results of + this calculation are always exactly in the range 0x00 to 0xFF + (simply cutting the lower bits would darken the colors slightly). */ + sal_uInt32 nNewComp = rnComp; + nNewComp /= nFactor1; + nNewComp *= nFactor2; + nNewComp /= nFactor3; + rnComp = static_cast< sal_uInt8 >( nNewComp ); + Color aNewColor( nR, nG, nB ); + + // find or insert the new color + sal_uInt32 nFoundIdx = 0; + XclListColor* pNewEntry = SearchListEntry( aNewColor, nFoundIdx ); + if( !pNewEntry || (pNewEntry->GetColor() != aNewColor) ) + pNewEntry = CreateListEntry( aNewColor, nFoundIdx ); + pNewEntry->AddWeighting( pOldEntry->GetWeighting() ); + aListIndexMap.push_back( nFoundIdx ); + } + + // update color ID data map (maps color IDs to color list indexes), replace old by new list indexes + for( XclColorIdDataVec::iterator aIt = maColorIdDataVec.begin(), aEnd = maColorIdDataVec.end(); aIt != aEnd; ++aIt ) + aIt->mnIndex = aListIndexMap[ aIt->mnIndex ]; +} + +void XclExpPaletteImpl::ReduceLeastUsedColor() +{ + // find a list color to remove + sal_uInt32 nRemove = GetLeastUsedListColor(); + // find its nearest neighbor + sal_uInt32 nKeep = GetNearestListColor( nRemove ); + + // merge both colors to one color, remove one color from list + XclListColor* pKeepEntry = mxColorList->GetObject( nKeep ); + XclListColor* pRemoveEntry = mxColorList->GetObject( nRemove ); + if( pKeepEntry && pRemoveEntry ) + { + // merge both colors (if pKeepEntry is a base color, it will not change) + pKeepEntry->Merge( *pRemoveEntry ); + // remove the less used color, adjust nKeep index if kept color follows removed color + mxColorList->Delete( nRemove ); + if( nKeep > nRemove ) --nKeep; + + // recalculate color ID data map (maps color IDs to color list indexes) + for( XclColorIdDataVec::iterator aIt = maColorIdDataVec.begin(), aEnd = maColorIdDataVec.end(); aIt != aEnd; ++aIt ) + { + if( aIt->mnIndex > nRemove ) + --aIt->mnIndex; + else if( aIt->mnIndex == nRemove ) + aIt->mnIndex = nKeep; + } + } +} + +sal_uInt32 XclExpPaletteImpl::GetLeastUsedListColor() const +{ + sal_uInt32 nFound = 0; + sal_uInt32 nMinW = SAL_MAX_UINT32; + + for( sal_uInt32 nIdx = 0, nCount = mxColorList->Count(); nIdx < nCount; ++nIdx ) + { + XclListColor* pEntry = mxColorList->GetObject( nIdx ); + // ignore the base colors + if( !pEntry->IsBaseColor() && (pEntry->GetWeighting() < nMinW) ) + { + nFound = nIdx; + nMinW = pEntry->GetWeighting(); + } + } + return nFound; +} + +sal_uInt32 XclExpPaletteImpl::GetNearestListColor( const Color& rColor, sal_uInt32 nIgnore ) const +{ + sal_uInt32 nFound = 0; + sal_Int32 nMinD = SAL_MAX_INT32; + + for( sal_uInt32 nIdx = 0, nCount = mxColorList->Count(); nIdx < nCount; ++nIdx ) + { + if( nIdx != nIgnore ) + { + if( XclListColor* pEntry = mxColorList->GetObject( nIdx ) ) + { + sal_Int32 nDist = lclGetColorDistance( rColor, pEntry->GetColor() ); + if( nDist < nMinD ) + { + nFound = nIdx; + nMinD = nDist; + } + } + } + } + return nFound; +} + +sal_uInt32 XclExpPaletteImpl::GetNearestListColor( sal_uInt32 nIndex ) const +{ + XclListColor* pEntry = mxColorList->GetObject( nIndex ); + return pEntry ? GetNearestListColor( pEntry->GetColor(), nIndex ) : 0; +} + +sal_Int32 XclExpPaletteImpl::GetNearestPaletteColor( + sal_uInt32& rnIndex, const Color& rColor, bool bDefaultOnly ) const +{ + rnIndex = 0; + sal_Int32 nDist = SAL_MAX_INT32; + + for( XclPaletteColorVec::const_iterator aIt = maPalette.begin(), aEnd = maPalette.end(); + aIt != aEnd; ++aIt ) + { + if( !bDefaultOnly || !aIt->mbUsed ) + { + sal_Int32 nCurrDist = lclGetColorDistance( rColor, aIt->maColor ); + if( nCurrDist < nDist ) + { + rnIndex = aIt - maPalette.begin(); + nDist = nCurrDist; + } + } + } + return nDist; +} + +sal_Int32 XclExpPaletteImpl::GetNearPaletteColors( + sal_uInt32& rnFirst, sal_uInt32& rnSecond, const Color& rColor ) const +{ + rnFirst = rnSecond = 0; + sal_Int32 nDist1 = SAL_MAX_INT32; + sal_Int32 nDist2 = SAL_MAX_INT32; + + for( XclPaletteColorVec::const_iterator aIt = maPalette.begin(), aEnd = maPalette.end(); + aIt != aEnd; ++aIt ) + { + sal_Int32 nCurrDist = lclGetColorDistance( rColor, aIt->maColor ); + if( nCurrDist < nDist1 ) + { + rnSecond = rnFirst; + nDist2 = nDist1; + rnFirst = aIt - maPalette.begin(); + nDist1 = nCurrDist; + } + else if( nCurrDist < nDist2 ) + { + rnSecond = aIt - maPalette.begin(); + nDist2 = nCurrDist; + } + } + return nDist1; +} + +// ---------------------------------------------------------------------------- + +XclExpPalette::XclExpPalette( const XclExpRoot& rRoot ) : + XclDefaultPalette( rRoot ), + XclExpRecord( EXC_ID_PALETTE ) +{ + mxImpl.reset( new XclExpPaletteImpl( *this ) ); + SetRecSize( GetColorCount() * 4 + 2 ); +} + +XclExpPalette::~XclExpPalette() +{ +} + +sal_uInt32 XclExpPalette::InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault ) +{ + return mxImpl->InsertColor( rColor, eType, nAutoDefault ); +} + +sal_uInt32 XclExpPalette::GetColorIdFromIndex( sal_uInt16 nIndex ) +{ + return XclExpPaletteImpl::GetColorIdFromIndex( nIndex ); +} + +void XclExpPalette::Finalize() +{ + mxImpl->Finalize(); +} + +sal_uInt16 XclExpPalette::GetColorIndex( sal_uInt32 nColorId ) const +{ + return mxImpl->GetColorIndex( nColorId ); +} + +void XclExpPalette::GetMixedColors( + sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern, + sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const +{ + return mxImpl->GetMixedColors( rnXclForeIx, rnXclBackIx, rnXclPattern, nForeColorId, nBackColorId ); +} + +ColorData XclExpPalette::GetColorData( sal_uInt16 nXclIndex ) const +{ + return mxImpl->GetColorData( nXclIndex ); +} + +void XclExpPalette::Save( XclExpStream& rStrm ) +{ + if( !mxImpl->IsDefaultPalette() ) + XclExpRecord::Save( rStrm ); +} + +void XclExpPalette::SaveXml( XclExpXmlStream& rStrm ) +{ + if( !mxImpl->IsDefaultPalette() ) + mxImpl->SaveXml( rStrm ); +} + +void XclExpPalette::WriteBody( XclExpStream& rStrm ) +{ + mxImpl->WriteBody( rStrm ); +} + +// FONT record - font information ============================================= + +namespace { + +typedef ::std::pair< USHORT, sal_Int16 > WhichAndScript; + +sal_Int16 lclCheckFontItems( const SfxItemSet& rItemSet, + const WhichAndScript& rWAS1, const WhichAndScript& rWAS2, const WhichAndScript& rWAS3 ) +{ + if( ScfTools::CheckItem( rItemSet, rWAS1.first, false ) ) return rWAS1.second; + if( ScfTools::CheckItem( rItemSet, rWAS2.first, false ) ) return rWAS2.second; + if( ScfTools::CheckItem( rItemSet, rWAS3.first, false ) ) return rWAS3.second; + return 0; +}; + +} // namespace + +/*static*/ sal_Int16 XclExpFontHelper::GetFirstUsedScript( const XclExpRoot& rRoot, const SfxItemSet& rItemSet ) +{ + namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + + /* #i17050# #i107170# We need to determine which font items are set in the + item set, and which script type we should prefer according to the + current language settings. */ + + static const WhichAndScript WAS_LATIN( ATTR_FONT, ::com::sun::star::i18n::ScriptType::LATIN ); + static const WhichAndScript WAS_ASIAN( ATTR_CJK_FONT, ::com::sun::star::i18n::ScriptType::ASIAN ); + static const WhichAndScript WAS_CMPLX( ATTR_CTL_FONT, ::com::sun::star::i18n::ScriptType::COMPLEX ); + + /* #114008# do not let a font from a parent style override an explicit + cell font. */ + + sal_Int16 nDefScript = rRoot.GetDefApiScript(); + sal_Int16 nScript = 0; + const SfxItemSet* pCurrSet = &rItemSet; + + while( (nScript == 0) && pCurrSet ) + { + switch( nDefScript ) + { + case ApiScriptType::LATIN: + nScript = lclCheckFontItems( *pCurrSet, WAS_LATIN, WAS_CMPLX, WAS_ASIAN ); + break; + case ApiScriptType::ASIAN: + nScript = lclCheckFontItems( *pCurrSet, WAS_ASIAN, WAS_CMPLX, WAS_LATIN ); + break; + case ApiScriptType::COMPLEX: + nScript = lclCheckFontItems( *pCurrSet, WAS_CMPLX, WAS_ASIAN, WAS_LATIN ); + break; + default: + DBG_ERRORFILE( "XclExpFontHelper::GetFirstUsedScript - unknown script type" ); + nScript = ApiScriptType::LATIN; + }; + pCurrSet = pCurrSet->GetParent(); + } + + return nScript; +} + +/*static*/ Font XclExpFontHelper::GetFontFromItemSet( const XclExpRoot& rRoot, const SfxItemSet& rItemSet, sal_Int16 nScript ) +{ + namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + + // if WEAK is passed, guess script type from existing items in the item set + if( nScript == ApiScriptType::WEAK ) + nScript = GetFirstUsedScript( rRoot, rItemSet ); + + // convert to core script type constants + BYTE nScScript = SCRIPTTYPE_LATIN; + switch( nScript ) + { + case ApiScriptType::LATIN: nScScript = SCRIPTTYPE_LATIN; break; + case ApiScriptType::ASIAN: nScScript = SCRIPTTYPE_ASIAN; break; + case ApiScriptType::COMPLEX: nScScript = SCRIPTTYPE_COMPLEX; break; + default: DBG_ERRORFILE( "XclExpFontHelper::GetFontFromItemSet - unknown script type" ); + } + + // fill the font object + Font aFont; + ScPatternAttr::GetFont( aFont, rItemSet, SC_AUTOCOL_RAW, 0, 0, 0, nScScript ); + return aFont; +} + +/*static*/ bool XclExpFontHelper::CheckItems( const XclExpRoot& rRoot, const SfxItemSet& rItemSet, sal_Int16 nScript, bool bDeep ) +{ + static const USHORT pnCommonIds[] = { + ATTR_FONT_UNDERLINE, ATTR_FONT_CROSSEDOUT, ATTR_FONT_CONTOUR, + ATTR_FONT_SHADOWED, ATTR_FONT_COLOR, ATTR_FONT_LANGUAGE, 0 }; + static const USHORT pnLatinIds[] = { + ATTR_FONT, ATTR_FONT_HEIGHT, ATTR_FONT_WEIGHT, ATTR_FONT_POSTURE, 0 }; + static const USHORT pnAsianIds[] = { + ATTR_CJK_FONT, ATTR_CJK_FONT_HEIGHT, ATTR_CJK_FONT_WEIGHT, ATTR_CJK_FONT_POSTURE, 0 }; + static const USHORT pnComplexIds[] = { + ATTR_CTL_FONT, ATTR_CTL_FONT_HEIGHT, ATTR_CTL_FONT_WEIGHT, ATTR_CTL_FONT_POSTURE, 0 }; + + bool bUsed = ScfTools::CheckItems( rItemSet, pnCommonIds, bDeep ); + if( !bUsed ) + { + namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + // if WEAK is passed, guess script type from existing items in the item set + if( nScript == ApiScriptType::WEAK ) + nScript = GetFirstUsedScript( rRoot, rItemSet ); + // check the correct items + switch( nScript ) + { + case ApiScriptType::LATIN: bUsed = ScfTools::CheckItems( rItemSet, pnLatinIds, bDeep ); break; + case ApiScriptType::ASIAN: bUsed = ScfTools::CheckItems( rItemSet, pnAsianIds, bDeep ); break; + case ApiScriptType::COMPLEX: bUsed = ScfTools::CheckItems( rItemSet, pnComplexIds, bDeep ); break; + default: DBG_ERRORFILE( "XclExpFontHelper::CheckItems - unknown script type" ); + } + } + return bUsed; +} + +// ---------------------------------------------------------------------------- + +namespace { + +sal_uInt32 lclCalcHash( const XclFontData& rFontData ) +{ + sal_uInt32 nHash = rFontData.maName.Len(); + nHash += rFontData.maColor.GetColor() * 2; + nHash += rFontData.mnWeight * 3; + nHash += rFontData.mnCharSet * 5; + nHash += rFontData.mnFamily * 7; + nHash += rFontData.mnHeight * 11; + nHash += rFontData.mnUnderline * 13; + nHash += rFontData.mnEscapem * 17; + if( rFontData.mbItalic ) nHash += 19; + if( rFontData.mbStrikeout ) nHash += 23; + if( rFontData.mbOutline ) nHash += 29; + if( rFontData.mbShadow ) nHash += 31; + return nHash; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpFont::XclExpFont( const XclExpRoot& rRoot, + const XclFontData& rFontData, XclExpColorType eColorType ) : + XclExpRecord( EXC_ID2_FONT, 14 ), + XclExpRoot( rRoot ), + maData( rFontData ) +{ + // insert font color into palette + mnColorId = rRoot.GetPalette().InsertColor( rFontData.maColor, eColorType, EXC_COLOR_FONTAUTO ); + // hash value for faster comparison + mnHash = lclCalcHash( maData ); + // record size + sal_Size nStrLen = maData.maName.Len(); + SetRecSize( ((GetBiff() == EXC_BIFF8) ? (nStrLen * 2 + 1) : nStrLen) + 15 ); +} + +bool XclExpFont::Equals( const XclFontData& rFontData, sal_uInt32 nHash ) const +{ + return (mnHash == nHash) && (maData == rFontData); +} + +void XclExpFont::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream(); + rStyleSheet->startElement( XML_font, FSEND ); + rStrm.WriteFontData( maData, XML_name ); + // OOXTODO: XML_scheme; //scheme/@val values: "major", "minor", "none" + rStyleSheet->endElement( XML_font ); +} + +// private -------------------------------------------------------------------- + +void XclExpFont::WriteBody( XclExpStream& rStrm ) +{ + sal_uInt16 nAttr = EXC_FONTATTR_NONE; + ::set_flag( nAttr, EXC_FONTATTR_ITALIC, maData.mbItalic ); + ::set_flag( nAttr, EXC_FONTATTR_STRIKEOUT, maData.mbStrikeout ); + ::set_flag( nAttr, EXC_FONTATTR_OUTLINE, maData.mbOutline ); + ::set_flag( nAttr, EXC_FONTATTR_SHADOW, maData.mbShadow ); + + DBG_ASSERT( maData.maName.Len() < 256, "XclExpFont::WriteBody - font name too long" ); + XclExpString aFontName; + if( GetBiff() <= EXC_BIFF5 ) + aFontName.AssignByte( maData.maName, GetTextEncoding(), EXC_STR_8BITLENGTH ); + else + aFontName.Assign( maData.maName, EXC_STR_FORCEUNICODE | EXC_STR_8BITLENGTH ); + + rStrm << maData.mnHeight + << nAttr + << GetPalette().GetColorIndex( mnColorId ) + << maData.mnWeight + << maData.mnEscapem + << maData.mnUnderline + << maData.mnFamily + << maData.mnCharSet + << sal_uInt8( 0 ) + << aFontName; +} + +// ---------------------------------------------------------------------------- + +XclExpBlindFont::XclExpBlindFont( const XclExpRoot& rRoot ) : + XclExpFont( rRoot, XclFontData(), EXC_COLOR_CELLTEXT ) +{ +} + +bool XclExpBlindFont::Equals( const XclFontData& /*rFontData*/, sal_uInt32 /*nHash*/ ) const +{ + return false; +} + +void XclExpBlindFont::Save( XclExpStream& /*rStrm*/ ) +{ + // do nothing +} + +// ============================================================================ + +XclExpFontBuffer::XclExpFontBuffer( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mnXclMaxSize( 0 ) +{ + switch( GetBiff() ) + { + case EXC_BIFF4: mnXclMaxSize = EXC_FONT_MAXCOUNT4; break; + case EXC_BIFF5: mnXclMaxSize = EXC_FONT_MAXCOUNT5; break; + case EXC_BIFF8: mnXclMaxSize = EXC_FONT_MAXCOUNT8; break; + default: DBG_ERROR_BIFF(); + } + InitDefaultFonts(); +} + +const XclExpFont* XclExpFontBuffer::GetFont( sal_uInt16 nXclFont ) const +{ + return maFontList.GetRecord( nXclFont ).get(); +} + +const XclFontData& XclExpFontBuffer::GetAppFontData() const +{ + return maFontList.GetRecord( EXC_FONT_APP )->GetFontData(); // exists always +} + +sal_uInt16 XclExpFontBuffer::Insert( + const XclFontData& rFontData, XclExpColorType eColorType, bool bAppFont ) +{ + if( bAppFont ) + { + XclExpFontRef xFont( new XclExpFont( GetRoot(), rFontData, eColorType ) ); + maFontList.ReplaceRecord( xFont, EXC_FONT_APP ); + // #108487# set width of '0' character for column width export + SetCharWidth( xFont->GetFontData() ); + return EXC_FONT_APP; + } + + size_t nPos = Find( rFontData ); + if( nPos == EXC_FONTLIST_NOTFOUND ) + { + // not found in buffer - create new font + size_t nSize = maFontList.GetSize(); + if( nSize < mnXclMaxSize ) + { + // possible to insert + maFontList.AppendNewRecord( new XclExpFont( GetRoot(), rFontData, eColorType ) ); + nPos = nSize; // old size is last position now + } + else + { + // buffer is full - ignore new font, use default font + nPos = EXC_FONT_APP; + } + } + return static_cast< sal_uInt16 >( nPos ); +} + +sal_uInt16 XclExpFontBuffer::Insert( + const Font& rFont, XclExpColorType eColorType, bool bAppFont ) +{ + return Insert( XclFontData( rFont ), eColorType, bAppFont ); +} + +sal_uInt16 XclExpFontBuffer::Insert( + const SvxFont& rFont, XclExpColorType eColorType, bool bAppFont ) +{ + return Insert( XclFontData( rFont ), eColorType, bAppFont ); +} + +sal_uInt16 XclExpFontBuffer::Insert( const SfxItemSet& rItemSet, + sal_Int16 nScript, XclExpColorType eColorType, bool bAppFont ) +{ + // #i17050# #114008# #115495# script type now provided by caller + Font aFont = XclExpFontHelper::GetFontFromItemSet( GetRoot(), rItemSet, nScript ); + return Insert( aFont, eColorType, bAppFont ); +} + +sal_uInt16 XclExpFontBuffer::Insert( const ScPatternAttr& rPattern, + sal_Int16 nScript, XclExpColorType eColorType, bool bAppFont ) +{ + return Insert( rPattern.GetItemSet(), nScript, eColorType, bAppFont ); +} + +void XclExpFontBuffer::Save( XclExpStream& rStrm ) +{ + maFontList.Save( rStrm ); +} + +void XclExpFontBuffer::SaveXml( XclExpXmlStream& rStrm ) +{ + if( maFontList.IsEmpty() ) + return; + + sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream(); + rStyleSheet->startElement( XML_fonts, + XML_count, OString::valueOf( (sal_Int32) maFontList.GetSize() ).getStr(), + FSEND ); + + maFontList.SaveXml( rStrm ); + + rStyleSheet->endElement( XML_fonts ); +} + +// private -------------------------------------------------------------------- + +void XclExpFontBuffer::InitDefaultFonts() +{ + XclFontData aFontData; + aFontData.maName.AssignAscii( "Arial" ); + aFontData.SetScFamily( FAMILY_DONTKNOW ); + aFontData.SetFontEncoding( ScfTools::GetSystemTextEncoding() ); + aFontData.SetScHeight( 200 ); // 200 twips = 10 pt + aFontData.SetScWeight( WEIGHT_NORMAL ); + + switch( GetBiff() ) + { + case EXC_BIFF5: + { + maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) ); + aFontData.SetScWeight( WEIGHT_BOLD ); + maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) ); + aFontData.SetScWeight( WEIGHT_NORMAL ); + aFontData.SetScPosture( ITALIC_NORMAL ); + maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) ); + aFontData.SetScWeight( WEIGHT_BOLD ); + maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) ); + // the blind font with index 4 + maFontList.AppendNewRecord( new XclExpBlindFont( GetRoot() ) ); + // already add the first user defined font (Excel does it too) + aFontData.SetScWeight( WEIGHT_NORMAL ); + aFontData.SetScPosture( ITALIC_NONE ); + maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) ); + } + break; + case EXC_BIFF8: + { + XclExpFontRef xFont( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) ); + maFontList.AppendRecord( xFont ); + maFontList.AppendRecord( xFont ); + maFontList.AppendRecord( xFont ); + maFontList.AppendRecord( xFont ); + if( GetOutput() == EXC_OUTPUT_BINARY ) + // the blind font with index 4 + maFontList.AppendNewRecord( new XclExpBlindFont( GetRoot() ) ); + } + break; + default: + DBG_ERROR_BIFF(); + } +} + +size_t XclExpFontBuffer::Find( const XclFontData& rFontData ) +{ + sal_uInt32 nHash = lclCalcHash( rFontData ); + for( size_t nPos = 0, nSize = maFontList.GetSize(); nPos < nSize; ++nPos ) + if( maFontList.GetRecord( nPos )->Equals( rFontData, nHash ) ) + return nPos; + return EXC_FONTLIST_NOTFOUND; +} + +// FORMAT record - number formats ============================================= + +/** Predicate for search algorithm. */ +struct XclExpNumFmtPred +{ + ULONG mnScNumFmt; + inline explicit XclExpNumFmtPred( ULONG nScNumFmt ) : mnScNumFmt( nScNumFmt ) {} + inline bool operator()( const XclExpNumFmt& rFormat ) const + { return rFormat.mnScNumFmt == mnScNumFmt; } +}; + +// ---------------------------------------------------------------------------- + +XclExpNumFmtBuffer::XclExpNumFmtBuffer( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + /* Compiler needs a hint, this doesn't work: new NfKeywordTable; + cannot convert from 'class String *' to 'class String (*)[54]' + The effective result here is class String (*)[54*1] */ + mxFormatter( new SvNumberFormatter( rRoot.GetDoc().GetServiceManager(), LANGUAGE_ENGLISH_US ) ), + mpKeywordTable( new NfKeywordTable[ 1 ] ), + mnStdFmt( GetFormatter().GetStandardFormat( ScGlobal::eLnge ) ) +{ + switch( GetBiff() ) + { + case EXC_BIFF5: mnXclOffset = EXC_FORMAT_OFFSET5; break; + case EXC_BIFF8: mnXclOffset = EXC_FORMAT_OFFSET8; break; + default: DBG_ERROR_BIFF(); + } + + mxFormatter->FillKeywordTable( *mpKeywordTable, LANGUAGE_ENGLISH_US ); + // remap codes unknown to Excel + (*mpKeywordTable)[ NF_KEY_NN ] = String( RTL_CONSTASCII_USTRINGPARAM( "DDD" ) ); + (*mpKeywordTable)[ NF_KEY_NNN ] = String( RTL_CONSTASCII_USTRINGPARAM( "DDDD" ) ); + // NNNN gets a separator appended in SvNumberformat::GetMappedFormatString() + (*mpKeywordTable)[ NF_KEY_NNNN ] = String( RTL_CONSTASCII_USTRINGPARAM( "DDDD" ) ); + // Export the Thai T NatNum modifier. + (*mpKeywordTable)[ NF_KEY_THAI_T ] = String( RTL_CONSTASCII_USTRINGPARAM( "T" ) ); +} + +XclExpNumFmtBuffer::~XclExpNumFmtBuffer() +{ + delete[] mpKeywordTable; +} + +sal_uInt16 XclExpNumFmtBuffer::Insert( ULONG nScNumFmt ) +{ + XclExpNumFmtVec::const_iterator aIt = + ::std::find_if( maFormatMap.begin(), maFormatMap.end(), XclExpNumFmtPred( nScNumFmt ) ); + if( aIt != maFormatMap.end() ) + return aIt->mnXclNumFmt; + + size_t nSize = maFormatMap.size(); + if( nSize < static_cast< size_t >( 0xFFFF - mnXclOffset ) ) + { + sal_uInt16 nXclNumFmt = static_cast< sal_uInt16 >( nSize + mnXclOffset ); + maFormatMap.push_back( XclExpNumFmt( nScNumFmt, nXclNumFmt ) ); + return nXclNumFmt; + } + + return 0; +} + +void XclExpNumFmtBuffer::Save( XclExpStream& rStrm ) +{ + for( XclExpNumFmtVec::const_iterator aIt = maFormatMap.begin(), aEnd = maFormatMap.end(); aIt != aEnd; ++aIt ) + WriteFormatRecord( rStrm, *aIt ); +} + +void XclExpNumFmtBuffer::SaveXml( XclExpXmlStream& rStrm ) +{ + if( !maFormatMap.size() ) + return; + + sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream(); + rStyleSheet->startElement( XML_numFmts, + XML_count, OString::valueOf( (sal_Int32) maFormatMap.size() ).getStr(), + FSEND ); + for( XclExpNumFmtVec::const_iterator aIt = maFormatMap.begin(), aEnd = maFormatMap.end(); aIt != aEnd; ++aIt ) + { + rStyleSheet->singleElement( XML_numFmt, + XML_numFmtId, OString::valueOf( sal_Int32(aIt->mnXclNumFmt) ).getStr(), + XML_formatCode, XclXmlUtils::ToOString( GetFormatCode( *aIt ) ).getStr(), + FSEND ); + } + rStyleSheet->endElement( XML_numFmts ); +} + +void XclExpNumFmtBuffer::WriteFormatRecord( XclExpStream& rStrm, sal_uInt16 nXclNumFmt, const String& rFormatStr ) +{ + XclExpString aExpStr; + if( GetBiff() <= EXC_BIFF5 ) + aExpStr.AssignByte( rFormatStr, GetTextEncoding(), EXC_STR_8BITLENGTH ); + else + aExpStr.Assign( rFormatStr ); + + rStrm.StartRecord( EXC_ID4_FORMAT, 2 + aExpStr.GetSize() ); + rStrm << nXclNumFmt << aExpStr; + rStrm.EndRecord(); +} + +void XclExpNumFmtBuffer::WriteFormatRecord( XclExpStream& rStrm, const XclExpNumFmt& rFormat ) +{ + WriteFormatRecord( rStrm, rFormat.mnXclNumFmt, GetFormatCode( rFormat ) ); +} + +String XclExpNumFmtBuffer::GetFormatCode( const XclExpNumFmt& rFormat ) +{ + String aFormatStr; + + if( const SvNumberformat* pEntry = GetFormatter().GetEntry( rFormat.mnScNumFmt ) ) + { + if( pEntry->GetType() == NUMBERFORMAT_LOGICAL ) + { + // build Boolean number format + Color* pColor = 0; + String aTemp; + const_cast< SvNumberformat* >( pEntry )->GetOutputString( 1.0, aTemp, &pColor ); + aFormatStr.Append( '"' ).Append( aTemp ).AppendAscii( "\";\"" ).Append( aTemp ).AppendAscii( "\";\"" ); + const_cast< SvNumberformat* >( pEntry )->GetOutputString( 0.0, aTemp, &pColor ); + aFormatStr.Append( aTemp ).Append( '"' ); + } + else + { + LanguageType eLang = pEntry->GetLanguage(); + if( eLang != LANGUAGE_ENGLISH_US ) + { + xub_StrLen nCheckPos; + short nType = NUMBERFORMAT_DEFINED; + sal_uInt32 nKey; + String aTemp( pEntry->GetFormatstring() ); + mxFormatter->PutandConvertEntry( aTemp, nCheckPos, nType, nKey, eLang, LANGUAGE_ENGLISH_US ); + DBG_ASSERT( nCheckPos == 0, "XclExpNumFmtBuffer::WriteFormatRecord - format code not convertible" ); + pEntry = mxFormatter->GetEntry( nKey ); + } + + aFormatStr = pEntry->GetMappedFormatstring( *mpKeywordTable, *mxFormatter->GetLocaleData() ); + if( aFormatStr.EqualsAscii( "Standard" ) ) + aFormatStr.AssignAscii( "General" ); + } + } + else + { + DBG_ERRORFILE( "XclExpNumFmtBuffer::WriteFormatRecord - format not found" ); + aFormatStr.AssignAscii( "General" ); + } + + return aFormatStr; +} + +// XF, STYLE record - Cell formatting ========================================= + +bool XclExpCellProt::FillFromItemSet( const SfxItemSet& rItemSet, bool bStyle ) +{ + const ScProtectionAttr& rProtItem = GETITEM( rItemSet, ScProtectionAttr, ATTR_PROTECTION ); + mbLocked = rProtItem.GetProtection(); + mbHidden = rProtItem.GetHideFormula() || rProtItem.GetHideCell(); + return ScfTools::CheckItem( rItemSet, ATTR_PROTECTION, bStyle ); +} + +#if 0 +void XclExpCellProt::FillToXF2( sal_uInt8& rnNumFmt ) const +{ + ::set_flag( rnNumFmt, EXC_XF2_LOCKED, mbLocked ); + ::set_flag( rnNumFmt, EXC_XF2_HIDDEN, mbHidden ); +} +#endif + +void XclExpCellProt::FillToXF3( sal_uInt16& rnProt ) const +{ + ::set_flag( rnProt, EXC_XF_LOCKED, mbLocked ); + ::set_flag( rnProt, EXC_XF_HIDDEN, mbHidden ); +} + +void XclExpCellProt::SaveXml( XclExpXmlStream& rStrm ) const +{ + rStrm.GetCurrentStream()->singleElement( XML_protection, + XML_locked, XclXmlUtils::ToPsz( mbLocked ), + XML_hidden, XclXmlUtils::ToPsz( mbHidden ), + FSEND ); +} + +// ---------------------------------------------------------------------------- + +bool XclExpCellAlign::FillFromItemSet( + const SfxItemSet& rItemSet, bool bForceLineBreak, XclBiff eBiff, bool bStyle ) +{ + bool bUsed = false; + + switch( eBiff ) + { + // ALL 'case's - run through! + + case EXC_BIFF8: // attributes new in BIFF8 + { + // text indent + long nTmpIndent = GETITEMVALUE( rItemSet, SfxUInt16Item, ATTR_INDENT, sal_Int32 ); + (nTmpIndent += 100) /= 200; // 1 Excel unit == 10 pt == 200 twips + mnIndent = limit_cast< sal_uInt8 >( nTmpIndent, 0, 15 ); + bUsed |= ScfTools::CheckItem( rItemSet, ATTR_INDENT, bStyle ); + + // shrink to fit + mbShrink = GETITEMVALUE( rItemSet, SfxBoolItem, ATTR_SHRINKTOFIT, BOOL ); + bUsed |= ScfTools::CheckItem( rItemSet, ATTR_SHRINKTOFIT, bStyle ); + + // CTL text direction + SetScFrameDir( GETITEMVALUE( rItemSet, SvxFrameDirectionItem, ATTR_WRITINGDIR, SvxFrameDirection ) ); + bUsed |= ScfTools::CheckItem( rItemSet, ATTR_WRITINGDIR, bStyle ); + } + + case EXC_BIFF5: // attributes new in BIFF5 + case EXC_BIFF4: // attributes new in BIFF4 + { + // vertical alignment + SetScVerAlign( GETITEMVALUE( rItemSet, SvxVerJustifyItem, ATTR_VER_JUSTIFY, SvxCellVerJustify ) ); + bUsed |= ScfTools::CheckItem( rItemSet, ATTR_VER_JUSTIFY, bStyle ); + + // stacked/rotation + bool bStacked = GETITEMVALUE( rItemSet, SfxBoolItem, ATTR_STACKED, BOOL ); + bUsed |= ScfTools::CheckItem( rItemSet, ATTR_STACKED, bStyle ); + if( bStacked ) + { + mnRotation = EXC_ROT_STACKED; + } + else + { + // rotation + sal_Int32 nScRot = GETITEMVALUE( rItemSet, SfxInt32Item, ATTR_ROTATE_VALUE, sal_Int32 ); + mnRotation = XclTools::GetXclRotation( nScRot ); + bUsed |= ScfTools::CheckItem( rItemSet, ATTR_ROTATE_VALUE, bStyle ); + } + mnOrient = XclTools::GetXclOrientFromRot( mnRotation ); + } + + case EXC_BIFF3: // attributes new in BIFF3 + { + // text wrap + mbLineBreak = bForceLineBreak || GETITEMBOOL( rItemSet, ATTR_LINEBREAK ); + bUsed |= bForceLineBreak || ScfTools::CheckItem( rItemSet, ATTR_LINEBREAK, bStyle ); + } + + case EXC_BIFF2: // attributes new in BIFF2 + { + // horizontal alignment + SetScHorAlign( GETITEMVALUE( rItemSet, SvxHorJustifyItem, ATTR_HOR_JUSTIFY, SvxCellHorJustify ) ); + bUsed |= ScfTools::CheckItem( rItemSet, ATTR_HOR_JUSTIFY, bStyle ); + } + + break; + default: DBG_ERROR_BIFF(); + } + + return bUsed; +} + +#if 0 +void XclExpCellAlign::FillToXF2( sal_uInt8& rnFlags ) const +{ + ::insert_value( rnFlags, mnHorAlign, 0, 3 ); +} + +void XclExpCellAlign::FillToXF3( sal_uInt16& rnAlign ) const +{ + ::insert_value( rnAlign, mnHorAlign, 0, 3 ); + ::set_flag( rnAlign, EXC_XF_LINEBREAK, mbLineBreak ); +} + +void XclExpCellAlign::FillToXF4( sal_uInt16& rnAlign ) const +{ + FillToXF3( rnAlign ); + ::insert_value( rnAlign, mnVerAlign, 4, 2 ); + ::insert_value( rnAlign, mnOrient, 6, 2 ); +} +#endif + +void XclExpCellAlign::FillToXF5( sal_uInt16& rnAlign ) const +{ + ::insert_value( rnAlign, mnHorAlign, 0, 3 ); + ::set_flag( rnAlign, EXC_XF_LINEBREAK, mbLineBreak ); + ::insert_value( rnAlign, mnVerAlign, 4, 3 ); + ::insert_value( rnAlign, mnOrient, 8, 2 ); +} + +void XclExpCellAlign::FillToXF8( sal_uInt16& rnAlign, sal_uInt16& rnMiscAttrib ) const +{ + ::insert_value( rnAlign, mnHorAlign, 0, 3 ); + ::set_flag( rnAlign, EXC_XF_LINEBREAK, mbLineBreak ); + ::insert_value( rnAlign, mnVerAlign, 4, 3 ); + ::insert_value( rnAlign, mnRotation, 8, 8 ); + ::insert_value( rnMiscAttrib, mnIndent, 0, 4 ); + ::set_flag( rnMiscAttrib, EXC_XF8_SHRINK, mbShrink ); + ::insert_value( rnMiscAttrib, mnTextDir, 6, 2 ); +} + +static const char* ToHorizontalAlignment( sal_uInt8 nHorAlign ) +{ + switch( nHorAlign ) + { + case EXC_XF_HOR_GENERAL: return "general"; + case EXC_XF_HOR_LEFT: return "left"; + case EXC_XF_HOR_CENTER: return "center"; + case EXC_XF_HOR_RIGHT: return "right"; + case EXC_XF_HOR_FILL: return "fill"; + case EXC_XF_HOR_JUSTIFY: return "justify"; + case EXC_XF_HOR_CENTER_AS: return "centerContinuous"; + case EXC_XF_HOR_DISTRIB: return "distributed"; + } + return "*unknown*"; +} + +static const char* ToVerticalAlignment( sal_uInt8 nVerAlign ) +{ + switch( nVerAlign ) + { + case EXC_XF_VER_TOP: return "top"; + case EXC_XF_VER_CENTER: return "center"; + case EXC_XF_VER_BOTTOM: return "bottom"; + case EXC_XF_VER_JUSTIFY: return "justify"; + case EXC_XF_VER_DISTRIB: return "distributed"; + } + return "*unknown*"; +} + +void XclExpCellAlign::SaveXml( XclExpXmlStream& rStrm ) const +{ + rStrm.GetCurrentStream()->singleElement( XML_alignment, + XML_horizontal, ToHorizontalAlignment( mnHorAlign ), + XML_vertical, ToVerticalAlignment( mnVerAlign ), + XML_textRotation, OString::valueOf( (sal_Int32) mnRotation ).getStr(), + XML_wrapText, XclXmlUtils::ToPsz( mbLineBreak ), + XML_indent, OString::valueOf( (sal_Int32) mnIndent ).getStr(), + // OOXTODO: XML_relativeIndent, mnIndent? + // OOXTODO: XML_justifyLastLine, + XML_shrinkToFit, XclXmlUtils::ToPsz( mbShrink ), + // OOXTODO: XML_readingOrder, + FSEND ); +} + +// ---------------------------------------------------------------------------- + +namespace { + +void lclGetBorderLine( + sal_uInt8& rnXclLine, sal_uInt32& rnColorId, + const SvxBorderLine* pLine, XclExpPalette& rPalette, XclBiff eBiff ) +{ + rnXclLine = EXC_LINE_NONE; + if( pLine ) + { + sal_uInt16 nOuterWidth = pLine->GetOutWidth(); + sal_uInt16 nDistance = pLine->GetDistance(); + if( nDistance > 0 ) + rnXclLine = EXC_LINE_DOUBLE; + else if( nOuterWidth > DEF_LINE_WIDTH_2 ) + rnXclLine = EXC_LINE_THICK; + else if( nOuterWidth > DEF_LINE_WIDTH_1 ) + rnXclLine = EXC_LINE_MEDIUM; + else if( nOuterWidth > DEF_LINE_WIDTH_0 ) + rnXclLine = EXC_LINE_THIN; + else if( nOuterWidth > 0 ) + rnXclLine = EXC_LINE_HAIR; + else + rnXclLine = EXC_LINE_NONE; + } + if( (eBiff == EXC_BIFF2) && (rnXclLine != EXC_LINE_NONE) ) + rnXclLine = EXC_LINE_THIN; + + rnColorId = (pLine && (rnXclLine != EXC_LINE_NONE)) ? + rPalette.InsertColor( pLine->GetColor(), EXC_COLOR_CELLBORDER ) : + XclExpPalette::GetColorIdFromIndex( 0 ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpCellBorder::XclExpCellBorder() : + mnLeftColorId( XclExpPalette::GetColorIdFromIndex( mnLeftColor ) ), + mnRightColorId( XclExpPalette::GetColorIdFromIndex( mnRightColor ) ), + mnTopColorId( XclExpPalette::GetColorIdFromIndex( mnTopColor ) ), + mnBottomColorId( XclExpPalette::GetColorIdFromIndex( mnBottomColor ) ), + mnDiagColorId( XclExpPalette::GetColorIdFromIndex( mnDiagColor ) ) +{ +} + +bool XclExpCellBorder::FillFromItemSet( + const SfxItemSet& rItemSet, XclExpPalette& rPalette, XclBiff eBiff, bool bStyle ) +{ + bool bUsed = false; + + switch( eBiff ) + { + // ALL 'case's - run through! + + case EXC_BIFF8: // attributes new in BIFF8 + { + const SvxLineItem& rTLBRItem = GETITEM( rItemSet, SvxLineItem, ATTR_BORDER_TLBR ); + sal_uInt8 nTLBRLine; + sal_uInt32 nTLBRColorId; + lclGetBorderLine( nTLBRLine, nTLBRColorId, rTLBRItem.GetLine(), rPalette, eBiff ); + mbDiagTLtoBR = (nTLBRLine != EXC_LINE_NONE); + + const SvxLineItem& rBLTRItem = GETITEM( rItemSet, SvxLineItem, ATTR_BORDER_BLTR ); + sal_uInt8 nBLTRLine; + sal_uInt32 nBLTRColorId; + lclGetBorderLine( nBLTRLine, nBLTRColorId, rBLTRItem.GetLine(), rPalette, eBiff ); + mbDiagBLtoTR = (nBLTRLine != EXC_LINE_NONE); + + if( ::ScHasPriority( rTLBRItem.GetLine(), rBLTRItem.GetLine() ) ) + { + mnDiagLine = nTLBRLine; + mnDiagColorId = nTLBRColorId; + } + else + { + mnDiagLine = nBLTRLine; + mnDiagColorId = nBLTRColorId; + } + + bUsed |= ScfTools::CheckItem( rItemSet, ATTR_BORDER_TLBR, bStyle ) || + ScfTools::CheckItem( rItemSet, ATTR_BORDER_BLTR, bStyle ); + } + + case EXC_BIFF5: + case EXC_BIFF4: + case EXC_BIFF3: + case EXC_BIFF2: + { + const SvxBoxItem& rBoxItem = GETITEM( rItemSet, SvxBoxItem, ATTR_BORDER ); + lclGetBorderLine( mnLeftLine, mnLeftColorId, rBoxItem.GetLeft(), rPalette, eBiff ); + lclGetBorderLine( mnRightLine, mnRightColorId, rBoxItem.GetRight(), rPalette, eBiff ); + lclGetBorderLine( mnTopLine, mnTopColorId, rBoxItem.GetTop(), rPalette, eBiff ); + lclGetBorderLine( mnBottomLine, mnBottomColorId, rBoxItem.GetBottom(), rPalette, eBiff ); + bUsed |= ScfTools::CheckItem( rItemSet, ATTR_BORDER, bStyle ); + } + + break; + default: DBG_ERROR_BIFF(); + } + + return bUsed; +} + +void XclExpCellBorder::SetFinalColors( const XclExpPalette& rPalette ) +{ + mnLeftColor = rPalette.GetColorIndex( mnLeftColorId ); + mnRightColor = rPalette.GetColorIndex( mnRightColorId ); + mnTopColor = rPalette.GetColorIndex( mnTopColorId ); + mnBottomColor = rPalette.GetColorIndex( mnBottomColorId ); + mnDiagColor = rPalette.GetColorIndex( mnDiagColorId ); +} + +#if 0 +void XclExpCellBorder::FillToXF2( sal_uInt8& rnFlags ) const +{ + ::set_flag( rnFlags, EXC_XF2_LEFTLINE, mnLeftLine != EXC_LINE_NONE ); + ::set_flag( rnFlags, EXC_XF2_RIGHTLINE, mnRightLine != EXC_LINE_NONE ); + ::set_flag( rnFlags, EXC_XF2_TOPLINE, mnTopLine != EXC_LINE_NONE ); + ::set_flag( rnFlags, EXC_XF2_BOTTOMLINE, mnBottomLine != EXC_LINE_NONE ); +} + +void XclExpCellBorder::FillToXF3( sal_uInt32& rnBorder ) const +{ + ::insert_value( rnBorder, mnTopLine, 0, 3 ); + ::insert_value( rnBorder, mnLeftLine, 8, 3 ); + ::insert_value( rnBorder, mnBottomLine, 16, 3 ); + ::insert_value( rnBorder, mnRightLine, 24, 3 ); + ::insert_value( rnBorder, mnTopColor, 3, 5 ); + ::insert_value( rnBorder, mnLeftColor, 11, 5 ); + ::insert_value( rnBorder, mnBottomColor, 19, 5 ); + ::insert_value( rnBorder, mnRightColor, 27, 5 ); +} +#endif + +void XclExpCellBorder::FillToXF5( sal_uInt32& rnBorder, sal_uInt32& rnArea ) const +{ + ::insert_value( rnBorder, mnTopLine, 0, 3 ); + ::insert_value( rnBorder, mnLeftLine, 3, 3 ); + ::insert_value( rnArea, mnBottomLine, 22, 3 ); + ::insert_value( rnBorder, mnRightLine, 6, 3 ); + ::insert_value( rnBorder, mnTopColor, 9, 7 ); + ::insert_value( rnBorder, mnLeftColor, 16, 7 ); + ::insert_value( rnArea, mnBottomColor, 25, 7 ); + ::insert_value( rnBorder, mnRightColor, 23, 7 ); +} + +void XclExpCellBorder::FillToXF8( sal_uInt32& rnBorder1, sal_uInt32& rnBorder2 ) const +{ + ::insert_value( rnBorder1, mnLeftLine, 0, 4 ); + ::insert_value( rnBorder1, mnRightLine, 4, 4 ); + ::insert_value( rnBorder1, mnTopLine, 8, 4 ); + ::insert_value( rnBorder1, mnBottomLine, 12, 4 ); + ::insert_value( rnBorder1, mnLeftColor, 16, 7 ); + ::insert_value( rnBorder1, mnRightColor, 23, 7 ); + ::insert_value( rnBorder2, mnTopColor, 0, 7 ); + ::insert_value( rnBorder2, mnBottomColor, 7, 7 ); + ::insert_value( rnBorder2, mnDiagColor, 14, 7 ); + ::insert_value( rnBorder2, mnDiagLine, 21, 4 ); + ::set_flag( rnBorder1, EXC_XF_DIAGONAL_TL_TO_BR, mbDiagTLtoBR ); + ::set_flag( rnBorder1, EXC_XF_DIAGONAL_BL_TO_TR, mbDiagBLtoTR ); +} + +void XclExpCellBorder::FillToCF8( sal_uInt16& rnLine, sal_uInt32& rnColor ) const +{ + ::insert_value( rnLine, mnLeftLine, 0, 4 ); + ::insert_value( rnLine, mnRightLine, 4, 4 ); + ::insert_value( rnLine, mnTopLine, 8, 4 ); + ::insert_value( rnLine, mnBottomLine, 12, 4 ); + ::insert_value( rnColor, mnLeftColor, 0, 7 ); + ::insert_value( rnColor, mnRightColor, 7, 7 ); + ::insert_value( rnColor, mnTopColor, 16, 7 ); + ::insert_value( rnColor, mnBottomColor, 23, 7 ); +} + +static const char* ToLineStyle( sal_uInt8 nLineStyle ) +{ + switch( nLineStyle ) + { + case EXC_LINE_NONE: return "none"; + case EXC_LINE_THIN: return "thin"; + case EXC_LINE_MEDIUM: return "medium"; + case EXC_LINE_THICK: return "thick"; + case EXC_LINE_DOUBLE: return "double"; + case EXC_LINE_HAIR: return "hair"; + } + return "*unknown*"; +} + +static void lcl_WriteBorder( XclExpXmlStream& rStrm, sal_Int32 nElement, sal_uInt8 nLineStyle, const Color& rColor ) +{ + sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream(); + if( nLineStyle == EXC_LINE_NONE ) + rStyleSheet->singleElement( nElement, FSEND ); + else if( rColor == Color( 0, 0, 0, 0 ) ) + rStyleSheet->singleElement( nElement, + XML_style, ToLineStyle( nLineStyle ), + FSEND ); + else + { + rStyleSheet->startElement( nElement, + XML_style, ToLineStyle( nLineStyle ), + FSEND ); + rStyleSheet->singleElement( XML_color, + XML_rgb, XclXmlUtils::ToOString( rColor ).getStr(), + FSEND ); + rStyleSheet->endElement( nElement ); + } +} + +void XclExpCellBorder::SaveXml( XclExpXmlStream& rStrm ) const +{ + sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream(); + + XclExpPalette& rPalette = rStrm.GetRoot().GetPalette(); + + rStyleSheet->startElement( XML_border, + XML_diagonalUp, XclXmlUtils::ToPsz( mbDiagBLtoTR ), + XML_diagonalDown, XclXmlUtils::ToPsz( mbDiagTLtoBR ), + // OOXTODO: XML_outline, + FSEND ); + lcl_WriteBorder( rStrm, XML_left, mnLeftLine, rPalette.GetColor( mnLeftColor ) ); + lcl_WriteBorder( rStrm, XML_right, mnRightLine, rPalette.GetColor( mnRightColor ) ); + lcl_WriteBorder( rStrm, XML_top, mnTopLine, rPalette.GetColor( mnTopColor ) ); + lcl_WriteBorder( rStrm, XML_bottom, mnBottomLine, rPalette.GetColor( mnBottomColor ) ); + lcl_WriteBorder( rStrm, XML_diagonal, mnDiagLine, rPalette.GetColor( mnDiagColor ) ); + // OOXTODO: XML_vertical, XML_horizontal + rStyleSheet->endElement( XML_border ); +} + +// ---------------------------------------------------------------------------- + +XclExpCellArea::XclExpCellArea() : + mnForeColorId( XclExpPalette::GetColorIdFromIndex( mnForeColor ) ), + mnBackColorId( XclExpPalette::GetColorIdFromIndex( mnBackColor ) ) +{ +} + +bool XclExpCellArea::FillFromItemSet( const SfxItemSet& rItemSet, XclExpPalette& rPalette, bool bStyle ) +{ + const SvxBrushItem& rBrushItem = GETITEM( rItemSet, SvxBrushItem, ATTR_BACKGROUND ); + if( rBrushItem.GetColor().GetTransparency() ) + { + mnPattern = EXC_PATT_NONE; + mnForeColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT ); + mnBackColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWBACK ); + } + else + { + mnPattern = EXC_PATT_SOLID; + mnForeColorId = rPalette.InsertColor( rBrushItem.GetColor(), EXC_COLOR_CELLAREA ); + mnBackColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT ); + } + return ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, bStyle ); +} + +void XclExpCellArea::SetFinalColors( const XclExpPalette& rPalette ) +{ + rPalette.GetMixedColors( mnForeColor, mnBackColor, mnPattern, mnForeColorId, mnBackColorId ); +} + +#if 0 +void XclExpCellArea::FillToXF2( sal_uInt8& rnFlags ) const +{ + ::set_flag( rnFlags, EXC_XF2_BACKGROUND, mnPattern != EXC_PATT_NONE ); +} + +void XclExpCellArea::FillToXF3( sal_uInt16& rnArea ) const +{ + ::insert_value( rnArea, mnPattern, 0, 6 ); + ::insert_value( rnArea, mnForeColor, 6, 5 ); + ::insert_value( rnArea, mnBackColor, 11, 5 ); +} +#endif + +void XclExpCellArea::FillToXF5( sal_uInt32& rnArea ) const +{ + ::insert_value( rnArea, mnPattern, 16, 6 ); + ::insert_value( rnArea, mnForeColor, 0, 7 ); + ::insert_value( rnArea, mnBackColor, 7, 7 ); +} + +void XclExpCellArea::FillToXF8( sal_uInt32& rnBorder2, sal_uInt16& rnArea ) const +{ + ::insert_value( rnBorder2, mnPattern, 26, 6 ); + ::insert_value( rnArea, mnForeColor, 0, 7 ); + ::insert_value( rnArea, mnBackColor, 7, 7 ); +} + +void XclExpCellArea::FillToCF8( sal_uInt16& rnPattern, sal_uInt16& rnColor ) const +{ + XclCellArea aTmp( *this ); + if( !aTmp.IsTransparent() && (aTmp.mnBackColor == EXC_COLOR_WINDOWTEXT) ) + aTmp.mnBackColor = 0; + if( aTmp.mnPattern == EXC_PATT_SOLID ) + ::std::swap( aTmp.mnForeColor, aTmp.mnBackColor ); + ::insert_value( rnColor, aTmp.mnForeColor, 0, 7 ); + ::insert_value( rnColor, aTmp.mnBackColor, 7, 7 ); + ::insert_value( rnPattern, aTmp.mnPattern, 10, 6 ); +} + +static const char* ToPatternType( sal_uInt8 nPattern ) +{ + switch( nPattern ) + { + case EXC_PATT_NONE: return "none"; + case EXC_PATT_SOLID: return "solid"; + case EXC_PATT_50_PERC: return "mediumGray"; + case EXC_PATT_75_PERC: return "darkGray"; + case EXC_PATT_25_PERC: return "lightGray"; + case EXC_PATT_12_5_PERC: return "gray125"; + case EXC_PATT_6_25_PERC: return "gray0625"; + } + return "*unknown*"; +} + +void XclExpCellArea::SaveXml( XclExpXmlStream& rStrm ) const +{ + sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream(); + rStyleSheet->startElement( XML_fill, + FSEND ); + + // OOXTODO: XML_gradientFill + + XclExpPalette& rPalette = rStrm.GetRoot().GetPalette(); + + if( mnPattern == EXC_PATT_NONE || ( mnForeColor == 0 && mnBackColor == 0 ) ) + rStyleSheet->singleElement( XML_patternFill, + XML_patternType, ToPatternType( mnPattern ), + FSEND ); + else + { + rStyleSheet->startElement( XML_patternFill, + XML_patternType, ToPatternType( mnPattern ), + FSEND ); + rStyleSheet->singleElement( XML_fgColor, + XML_rgb, XclXmlUtils::ToOString( rPalette.GetColor( mnForeColor ) ).getStr(), + FSEND ); + rStyleSheet->singleElement( XML_bgColor, + XML_rgb, XclXmlUtils::ToOString( rPalette.GetColor( mnBackColor ) ).getStr(), + FSEND ); + rStyleSheet->endElement( XML_patternFill ); + } + + rStyleSheet->endElement( XML_fill ); +} + +// ---------------------------------------------------------------------------- + +XclExpXFId::XclExpXFId() : + mnXFId( XclExpXFBuffer::GetDefCellXFId() ), + mnXFIndex( EXC_XF_DEFAULTCELL ) +{ +} + +XclExpXFId::XclExpXFId( sal_uInt32 nXFId ) : + mnXFId( nXFId ), + mnXFIndex( EXC_XF_DEFAULTCELL ) +{ +} + +void XclExpXFId::ConvertXFIndex( const XclExpRoot& rRoot ) +{ + mnXFIndex = rRoot.GetXFBuffer().GetXFIndex( mnXFId ); +} + +// ---------------------------------------------------------------------------- + +XclExpXF::XclExpXF( + const XclExpRoot& rRoot, const ScPatternAttr& rPattern, sal_Int16 nScript, + ULONG nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) : + XclXFBase( true ), + XclExpRoot( rRoot ) +{ + mnParentXFId = GetXFBuffer().InsertStyle( rPattern.GetStyleSheet() ); + Init( rPattern.GetItemSet(), nScript, nForceScNumFmt, nForceXclFont, bForceLineBreak, false ); +} + +XclExpXF::XclExpXF( const XclExpRoot& rRoot, const SfxStyleSheetBase& rStyleSheet ) : + XclXFBase( false ), + XclExpRoot( rRoot ), + mnParentXFId( XclExpXFBuffer::GetXFIdFromIndex( EXC_XF_STYLEPARENT ) ) +{ + bool bDefStyle = (rStyleSheet.GetName() == ScGlobal::GetRscString( STR_STYLENAME_STANDARD )); + sal_Int16 nScript = bDefStyle ? GetDefApiScript() : ::com::sun::star::i18n::ScriptType::WEAK; + Init( const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet(), nScript, + NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND, false, bDefStyle ); +} + +XclExpXF::XclExpXF( const XclExpRoot& rRoot, bool bCellXF ) : + XclXFBase( bCellXF ), + XclExpRoot( rRoot ), + mnParentXFId( XclExpXFBuffer::GetXFIdFromIndex( EXC_XF_STYLEPARENT ) ) +{ + InitDefault(); +} + +bool XclExpXF::Equals( const ScPatternAttr& rPattern, + ULONG nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) const +{ + return IsCellXF() && (mpItemSet == &rPattern.GetItemSet()) && + (!bForceLineBreak || maAlignment.mbLineBreak) && + ((nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND) || (mnScNumFmt == nForceScNumFmt)) && + ((nForceXclFont == EXC_FONT_NOTFOUND) || (mnXclFont == nForceXclFont)); +} + +bool XclExpXF::Equals( const SfxStyleSheetBase& rStyleSheet ) const +{ + return IsStyleXF() && (mpItemSet == &const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet()); +} + +void XclExpXF::SetFinalColors() +{ + maBorder.SetFinalColors( GetPalette() ); + maArea.SetFinalColors( GetPalette() ); +} + +bool XclExpXF::Equals( const XclExpXF& rCmpXF ) const +{ + return XclXFBase::Equals( rCmpXF ) && + (maProtection == rCmpXF.maProtection) && (maAlignment == rCmpXF.maAlignment) && + (maBorder == rCmpXF.maBorder) && (maArea == rCmpXF.maArea) && + (mnXclFont == rCmpXF.mnXclFont) && (mnXclNumFmt == rCmpXF.mnXclNumFmt) && + (mnParentXFId == rCmpXF.mnParentXFId); +} + +void XclExpXF::InitDefault() +{ + SetRecHeader( EXC_ID5_XF, (GetBiff() == EXC_BIFF8) ? 20 : 16 ); + mpItemSet = 0; + mnScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND; + mnXclFont = mnXclNumFmt = 0; +} + +void XclExpXF::Init( const SfxItemSet& rItemSet, sal_Int16 nScript, + ULONG nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak, bool bDefStyle ) +{ + InitDefault(); + mpItemSet = &rItemSet; + + // cell protection + mbProtUsed = maProtection.FillFromItemSet( rItemSet, IsStyleXF() ); + + // font + if( nForceXclFont == EXC_FONT_NOTFOUND ) + { + mnXclFont = GetFontBuffer().Insert( rItemSet, nScript, EXC_COLOR_CELLTEXT, bDefStyle ); + mbFontUsed = XclExpFontHelper::CheckItems( GetRoot(), rItemSet, nScript, IsStyleXF() ); + } + else + { + mnXclFont = nForceXclFont; + mbFontUsed = true; + } + + // number format + mnScNumFmt = (nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND) ? + GETITEMVALUE( rItemSet, SfxUInt32Item, ATTR_VALUE_FORMAT, ULONG ) : nForceScNumFmt; + mnXclNumFmt = GetNumFmtBuffer().Insert( mnScNumFmt ); + mbFmtUsed = ScfTools::CheckItem( rItemSet, ATTR_VALUE_FORMAT, IsStyleXF() ); + + // alignment + mbAlignUsed = maAlignment.FillFromItemSet( rItemSet, bForceLineBreak, GetBiff(), IsStyleXF() ); + + // cell border + mbBorderUsed = maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff(), IsStyleXF() ); + + // background area + mbAreaUsed = maArea.FillFromItemSet( rItemSet, GetPalette(), IsStyleXF() ); + + // set all b***Used flags to true in "Default"/"Normal" style + if( bDefStyle ) + SetAllUsedFlags( true ); +} + +sal_uInt8 XclExpXF::GetUsedFlags() const +{ + sal_uInt8 nUsedFlags = 0; + /* In cell XFs a set bit means a used attribute, in style XFs a cleared bit. + "mbCellXF == mb***Used" evaluates to correct value in cell and style XFs. */ + ::set_flag( nUsedFlags, EXC_XF_DIFF_PROT, mbCellXF == mbProtUsed ); + ::set_flag( nUsedFlags, EXC_XF_DIFF_FONT, mbCellXF == mbFontUsed ); + ::set_flag( nUsedFlags, EXC_XF_DIFF_VALFMT, mbCellXF == mbFmtUsed ); + ::set_flag( nUsedFlags, EXC_XF_DIFF_ALIGN, mbCellXF == mbAlignUsed ); + ::set_flag( nUsedFlags, EXC_XF_DIFF_BORDER, mbCellXF == mbBorderUsed ); + ::set_flag( nUsedFlags, EXC_XF_DIFF_AREA, mbCellXF == mbAreaUsed ); + return nUsedFlags; +} + +void XclExpXF::WriteBody5( XclExpStream& rStrm ) +{ + sal_uInt16 nTypeProt = 0, nAlign = 0; + sal_uInt32 nArea = 0, nBorder = 0; + + ::set_flag( nTypeProt, EXC_XF_STYLE, IsStyleXF() ); + ::insert_value( nTypeProt, mnParent, 4, 12 ); + ::insert_value( nAlign, GetUsedFlags(), 10, 6 ); + + maProtection.FillToXF3( nTypeProt ); + maAlignment.FillToXF5( nAlign ); + maBorder.FillToXF5( nBorder, nArea ); + maArea.FillToXF5( nArea ); + + rStrm << mnXclFont << mnXclNumFmt << nTypeProt << nAlign << nArea << nBorder; +} + +void XclExpXF::WriteBody8( XclExpStream& rStrm ) +{ + sal_uInt16 nTypeProt = 0, nAlign = 0, nMiscAttrib = 0, nArea = 0; + sal_uInt32 nBorder1 = 0, nBorder2 = 0; + + ::set_flag( nTypeProt, EXC_XF_STYLE, IsStyleXF() ); + ::insert_value( nTypeProt, mnParent, 4, 12 ); + ::insert_value( nMiscAttrib, GetUsedFlags(), 10, 6 ); + + maProtection.FillToXF3( nTypeProt ); + maAlignment.FillToXF8( nAlign, nMiscAttrib ); + maBorder.FillToXF8( nBorder1, nBorder2 ); + maArea.FillToXF8( nBorder2, nArea ); + + rStrm << mnXclFont << mnXclNumFmt << nTypeProt << nAlign << nMiscAttrib << nBorder1 << nBorder2 << nArea; +} + +void XclExpXF::WriteBody( XclExpStream& rStrm ) +{ + XclExpXFId aParentId( mnParentXFId ); + aParentId.ConvertXFIndex( GetRoot() ); + mnParent = aParentId.mnXFIndex; + switch( GetBiff() ) + { + case EXC_BIFF5: WriteBody5( rStrm ); break; + case EXC_BIFF8: WriteBody8( rStrm ); break; + default: DBG_ERROR_BIFF(); + } +} + +void XclExpXF::SetXmlIds( sal_uInt32 nBorderId, sal_uInt32 nFillId ) +{ + mnBorderId = nBorderId; + mnFillId = nFillId; +} + +void XclExpXF::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream(); + + sal_Int32 nXfId = 0; + if( IsCellXF() ) + { + sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( mnParentXFId ); + nXfId = rStrm.GetRoot().GetXFBuffer().GetXmlStyleIndex( nXFIndex ); + } + + rStyleSheet->startElement( XML_xf, + XML_numFmtId, OString::valueOf( (sal_Int32) mnXclNumFmt ).getStr(), + XML_fontId, OString::valueOf( (sal_Int32) mnXclFont ).getStr(), + XML_fillId, OString::valueOf( (sal_Int32) mnFillId ).getStr(), + XML_borderId, OString::valueOf( (sal_Int32) mnBorderId ).getStr(), + XML_xfId, IsStyleXF() ? NULL : OString::valueOf( nXfId ).getStr(), + // OOXTODO: XML_quotePrefix, + // OOXTODO: XML_pivotButton, + // OOXTODO: XML_applyNumberFormat, ; + XML_applyFont, XclXmlUtils::ToPsz( mbFontUsed ), + // OOXTODO: XML_applyFill, + XML_applyBorder, XclXmlUtils::ToPsz( mbBorderUsed ), + XML_applyAlignment, XclXmlUtils::ToPsz( mbAlignUsed ), + XML_applyProtection, XclXmlUtils::ToPsz( mbProtUsed ), + FSEND ); + if( mbAlignUsed ) + maAlignment.SaveXml( rStrm ); + if( mbProtUsed ) + maProtection.SaveXml( rStrm ); + // OOXTODO: XML_extLst + rStyleSheet->endElement( XML_xf ); +} + +// ---------------------------------------------------------------------------- + +XclExpDefaultXF::XclExpDefaultXF( const XclExpRoot& rRoot, bool bCellXF ) : + XclExpXF( rRoot, bCellXF ) +{ +} + +//UNUSED2008-05 void XclExpDefaultXF::SetParent( sal_uInt32 nParentXFId ) +//UNUSED2008-05 { +//UNUSED2008-05 DBG_ASSERT( IsCellXF(), "XclExpDefaultXF::SetParent - not allowed in style XFs" ); +//UNUSED2008-05 if( IsCellXF() ) +//UNUSED2008-05 mnParentXFId = nParentXFId; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 void XclExpDefaultXF::SetUsedFlags( +//UNUSED2008-05 bool bProtUsed, bool bFontUsed, bool bFmtUsed, +//UNUSED2008-05 bool bAlignUsed, bool bBorderUsed, bool bAreaUsed ) +//UNUSED2008-05 { +//UNUSED2008-05 mbProtUsed = bProtUsed; +//UNUSED2008-05 mbFontUsed = bFontUsed; +//UNUSED2008-05 mbFmtUsed = bFmtUsed; +//UNUSED2008-05 mbAlignUsed = bAlignUsed; +//UNUSED2008-05 mbBorderUsed = bBorderUsed; +//UNUSED2008-05 mbAreaUsed = bAreaUsed; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 void XclExpDefaultXF::SetProtection( const XclExpCellProt& rProtection ) +//UNUSED2008-05 { +//UNUSED2008-05 maProtection = rProtection; +//UNUSED2008-05 mbProtUsed = true; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 void XclExpDefaultXF::SetAlignment( const XclExpCellAlign& rAlignment ) +//UNUSED2008-05 { +//UNUSED2008-05 maAlignment = rAlignment; +//UNUSED2008-05 mbAlignUsed = true; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 void XclExpDefaultXF::SetBorder( const XclExpCellBorder& rBorder ) +//UNUSED2008-05 { +//UNUSED2008-05 maBorder = rBorder; +//UNUSED2008-05 mbBorderUsed = true; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 void XclExpDefaultXF::SetArea( const XclExpCellArea& rArea ) +//UNUSED2008-05 { +//UNUSED2008-05 maArea = rArea; +//UNUSED2008-05 mbAreaUsed = true; +//UNUSED2008-05 } + +void XclExpDefaultXF::SetFont( sal_uInt16 nXclFont ) +{ + mnXclFont = nXclFont; + mbFontUsed = true; +} + +void XclExpDefaultXF::SetNumFmt( sal_uInt16 nXclNumFmt ) +{ + mnXclNumFmt = nXclNumFmt; + mbFmtUsed = true; +} + +// ---------------------------------------------------------------------------- + +XclExpStyle::XclExpStyle( sal_uInt32 nXFId, const String& rStyleName ) : + XclExpRecord( EXC_ID_STYLE, 4 ), + maName( rStyleName ), + maXFId( nXFId ), + mnStyleId( EXC_STYLE_USERDEF ), + mnLevel( EXC_STYLE_NOLEVEL ) +{ + DBG_ASSERT( maName.Len(), "XclExpStyle::XclExpStyle - empty style name" ); +#ifdef DBG_UTIL + sal_uInt8 nStyleId, nLevel; // do not use members for debug tests + DBG_ASSERT( !XclTools::GetBuiltInStyleId( nStyleId, nLevel, maName ), + "XclExpStyle::XclExpStyle - this is a built-in style" ); +#endif +} + +XclExpStyle::XclExpStyle( sal_uInt32 nXFId, sal_uInt8 nStyleId, sal_uInt8 nLevel ) : + XclExpRecord( EXC_ID_STYLE, 4 ), + maXFId( nXFId ), + mnStyleId( nStyleId ), + mnLevel( nLevel ) +{ +} + +void XclExpStyle::WriteBody( XclExpStream& rStrm ) +{ + maXFId.ConvertXFIndex( rStrm.GetRoot() ); + ::set_flag( maXFId.mnXFIndex, EXC_STYLE_BUILTIN, IsBuiltIn() ); + rStrm << maXFId.mnXFIndex; + + if( IsBuiltIn() ) + { + rStrm << mnStyleId << mnLevel; + } + else + { + XclExpString aNameEx; + if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ) + aNameEx.Assign( maName ); + else + aNameEx.AssignByte( maName, rStrm.GetRoot().GetTextEncoding(), EXC_STR_8BITLENGTH ); + rStrm << aNameEx; + } +} + +static const char* lcl_StyleNameFromId( sal_Int32 nStyleId ) +{ + switch( nStyleId ) + { + case 0: return "Normal"; + case 3: return "Comma"; + case 4: return "Currency"; + case 5: return "Percent"; + case 6: return "Comma [0]"; + case 7: return "Currency [0]"; + } + return "*unknown*"; +} + +void XclExpStyle::SaveXml( XclExpXmlStream& rStrm ) +{ + OString sName; + if( IsBuiltIn() ) + { + sName = OString( lcl_StyleNameFromId( mnStyleId ) ); + } + else + sName = XclXmlUtils::ToOString( maName ); + sal_Int32 nXFId = rStrm.GetRoot().GetXFBuffer().GetXmlStyleIndex( maXFId.mnXFId ); + rStrm.GetCurrentStream()->singleElement( XML_cellStyle, + XML_name, sName.getStr(), + XML_xfId, OString::valueOf( nXFId ).getStr(), + XML_builtinId, OString::valueOf( (sal_Int32) mnStyleId ).getStr(), + // OOXTODO: XML_iLevel, + // OOXTODO: XML_hidden, + XML_customBuiltin, XclXmlUtils::ToPsz( ! IsBuiltIn() ), + FSEND ); + // OOXTODO: XML_extLst +} + +// ---------------------------------------------------------------------------- + +namespace { + +const sal_uInt32 EXC_XFLIST_INDEXBASE = 0xFFFE0000; +/** Maximum count of XF records to store in the XF list (performance). */ +const sal_uInt32 EXC_XFLIST_HARDLIMIT = 256 * 1024; + +bool lclIsBuiltInStyle( const String& rStyleName ) +{ + return + XclTools::IsBuiltInStyleName( rStyleName ) || + XclTools::IsCondFormatStyleName( rStyleName ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpXFBuffer::XclExpBuiltInInfo::XclExpBuiltInInfo() : + mnStyleId( EXC_STYLE_USERDEF ), + mnLevel( EXC_STYLE_NOLEVEL ), + mbPredefined( true ), + mbHasStyleRec( false ) +{ +} + +// ---------------------------------------------------------------------------- + +/** Predicate for search algorithm. */ +struct XclExpBorderPred +{ + const XclExpCellBorder& + mrBorder; + inline explicit XclExpBorderPred( const XclExpCellBorder& rBorder ) : mrBorder( rBorder ) {} + bool operator()( const XclExpCellBorder& rBorder ) const; +}; + +bool XclExpBorderPred::operator()( const XclExpCellBorder& rBorder ) const +{ + return + mrBorder.mnLeftColor == rBorder.mnLeftColor && + mrBorder.mnRightColor == rBorder.mnRightColor && + mrBorder.mnTopColor == rBorder.mnTopColor && + mrBorder.mnBottomColor == rBorder.mnBottomColor && + mrBorder.mnDiagColor == rBorder.mnDiagColor && + mrBorder.mnLeftLine == rBorder.mnLeftLine && + mrBorder.mnRightLine == rBorder.mnRightLine && + mrBorder.mnTopLine == rBorder.mnTopLine && + mrBorder.mnBottomLine == rBorder.mnBottomLine && + mrBorder.mnDiagLine == rBorder.mnDiagLine && + mrBorder.mbDiagTLtoBR == rBorder.mbDiagTLtoBR && + mrBorder.mbDiagBLtoTR == rBorder.mbDiagBLtoTR && + mrBorder.mnLeftColorId == rBorder.mnLeftColorId && + mrBorder.mnRightColorId == rBorder.mnRightColorId && + mrBorder.mnTopColorId == rBorder.mnTopColorId && + mrBorder.mnBottomColorId == rBorder.mnBottomColorId && + mrBorder.mnDiagColorId == rBorder.mnDiagColorId; +} + +struct XclExpFillPred +{ + const XclExpCellArea& + mrFill; + inline explicit XclExpFillPred( const XclExpCellArea& rFill ) : mrFill( rFill ) {} + bool operator()( const XclExpCellArea& rFill ) const; +}; + +bool XclExpFillPred::operator()( const XclExpCellArea& rFill ) const +{ + return + mrFill.mnForeColor == rFill.mnForeColor && + mrFill.mnBackColor == rFill.mnBackColor && + mrFill.mnPattern == rFill.mnPattern && + mrFill.mnForeColorId == rFill.mnForeColorId && + mrFill.mnBackColorId == rFill.mnBackColorId; +} + +XclExpXFBuffer::XclExpXFBuffer( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ) +{ +} + +void XclExpXFBuffer::Initialize() +{ + InsertDefaultRecords(); + InsertUserStyles(); +} + +sal_uInt32 XclExpXFBuffer::Insert( const ScPatternAttr* pPattern, sal_Int16 nScript ) +{ + return InsertCellXF( pPattern, nScript, NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND, false ); +} + +sal_uInt32 XclExpXFBuffer::InsertWithFont( const ScPatternAttr* pPattern, sal_Int16 nScript, + sal_uInt16 nForceXclFont, bool bForceLineBreak ) +{ + return InsertCellXF( pPattern, nScript, NUMBERFORMAT_ENTRY_NOT_FOUND, nForceXclFont, bForceLineBreak ); +} + +sal_uInt32 XclExpXFBuffer::InsertWithNumFmt( const ScPatternAttr* pPattern, sal_Int16 nScript, ULONG nForceScNumFmt, bool bForceLineBreak ) +{ + return InsertCellXF( pPattern, nScript, nForceScNumFmt, EXC_FONT_NOTFOUND, bForceLineBreak ); +} + +sal_uInt32 XclExpXFBuffer::InsertStyle( const SfxStyleSheetBase* pStyleSheet ) +{ + return pStyleSheet ? InsertStyleXF( *pStyleSheet ) : GetXFIdFromIndex( EXC_XF_DEFAULTSTYLE ); +} + +sal_uInt32 XclExpXFBuffer::GetXFIdFromIndex( sal_uInt16 nXFIndex ) +{ + return EXC_XFLIST_INDEXBASE | nXFIndex; +} + +sal_uInt32 XclExpXFBuffer::GetDefCellXFId() +{ + return GetXFIdFromIndex( EXC_XF_DEFAULTCELL ); +} + +const XclExpXF* XclExpXFBuffer::GetXFById( sal_uInt32 nXFId ) const +{ + return maXFList.GetRecord( nXFId ).get(); +} + +void XclExpXFBuffer::Finalize() +{ + for( size_t nPos = 0, nSize = maXFList.GetSize(); nPos < nSize; ++nPos ) + maXFList.GetRecord( nPos )->SetFinalColors(); + + sal_uInt32 nTotalCount = static_cast< sal_uInt32 >( maXFList.GetSize() ); + sal_uInt32 nId; + maXFIndexVec.resize( nTotalCount, EXC_XF_DEFAULTCELL ); + maStyleIndexes.resize( nTotalCount, EXC_XF_DEFAULTCELL ); + maCellIndexes.resize( nTotalCount, EXC_XF_DEFAULTCELL ); + + XclExpBuiltInMap::const_iterator aBuiltInEnd = maBuiltInMap.end(); + /* nMaxBuiltInXFId used to decide faster whether an XF record is + user-defined. If the current XF ID is greater than this value, + maBuiltInMap doesn't need to be searched. */ + sal_uInt32 nMaxBuiltInXFId = maBuiltInMap.empty() ? 0 : maBuiltInMap.rbegin()->first; + + // *** map all built-in XF records (cell and style) *** ------------------- + + // do not change XF order -> std::map<> iterates elements in ascending order + for( XclExpBuiltInMap::const_iterator aIt = maBuiltInMap.begin(); aIt != aBuiltInEnd; ++aIt ) + AppendXFIndex( aIt->first ); + + // *** insert all user-defined style XF records, without reduce *** ------- + + sal_uInt32 nStyleXFCount = 0; // counts up to EXC_XF_MAXSTYLECOUNT limit + + for( nId = 0; nId < nTotalCount; ++nId ) + { + XclExpXFRef xXF = maXFList.GetRecord( nId ); + if( xXF->IsStyleXF() && ((nId > nMaxBuiltInXFId) || (maBuiltInMap.find( nId ) == aBuiltInEnd)) ) + { + if( nStyleXFCount < EXC_XF_MAXSTYLECOUNT ) + { + // maximum count of styles not reached + AppendXFIndex( nId ); + ++nStyleXFCount; + } + else + { + /* Maximum count of styles reached - do not append more + pointers to XFs; use default style XF instead; do not break + the loop to initialize all maXFIndexVec elements. */ + maXFIndexVec[ nId ] = EXC_XF_DEFAULTSTYLE; + } + } + } + + // *** insert all cell XF records *** ------------------------------------- + + // start position to search for equal inserted XF records + size_t nSearchStart = maSortedXFList.GetSize(); + + // break the loop if XF limit reached - maXFIndexVec is already initialized with default index + XclExpXFRef xDefCellXF = maXFList.GetRecord( EXC_XF_DEFAULTCELL ); + for( nId = 0; (nId < nTotalCount) && (maSortedXFList.GetSize() < EXC_XF_MAXCOUNT); ++nId ) + { + XclExpXFRef xXF = maXFList.GetRecord( nId ); + if( xXF->IsCellXF() && ((nId > nMaxBuiltInXFId) || (maBuiltInMap.find( nId ) == aBuiltInEnd)) ) + { + // try to find an XF record equal to *xXF, which is already inserted + sal_uInt16 nFoundIndex = EXC_XF_NOTFOUND; + + // first try if it is equal to the default cell XF + if( xDefCellXF->Equals( *xXF ) ) + { + nFoundIndex = EXC_XF_DEFAULTCELL; + } + else for( size_t nSearchPos = nSearchStart, nSearchEnd = maSortedXFList.GetSize(); + (nSearchPos < nSearchEnd) && (nFoundIndex == EXC_XF_NOTFOUND); ++nSearchPos ) + { + if( maSortedXFList.GetRecord( nSearchPos )->Equals( *xXF ) ) + nFoundIndex = static_cast< sal_uInt16 >( nSearchPos ); + } + + if( nFoundIndex != EXC_XF_NOTFOUND ) + // equal XF already in the list, use its resulting XF index + maXFIndexVec[ nId ] = nFoundIndex; + else + AppendXFIndex( nId ); + } + } + + sal_uInt16 nXmlStyleIndex = 0; + sal_uInt16 nXmlCellIndex = 0; + + size_t nXFCount = maSortedXFList.GetSize(); + for( size_t i = 0; i < nXFCount; ++i ) + { + XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i ); + if( xXF->IsStyleXF() ) + maStyleIndexes[ i ] = nXmlStyleIndex++; + else + maCellIndexes[ i ] = nXmlCellIndex++; + } +} + +sal_uInt16 XclExpXFBuffer::GetXFIndex( sal_uInt32 nXFId ) const +{ + sal_uInt16 nXFIndex = EXC_XF_DEFAULTSTYLE; + if( nXFId >= EXC_XFLIST_INDEXBASE ) + nXFIndex = static_cast< sal_uInt16 >( nXFId & ~EXC_XFLIST_INDEXBASE ); + else if( nXFId < maXFIndexVec.size() ) + nXFIndex = maXFIndexVec[ nXFId ]; + return nXFIndex; +} + +sal_Int32 XclExpXFBuffer::GetXmlStyleIndex( sal_uInt32 nXFIndex ) const +{ + DBG_ASSERT( nXFIndex < maStyleIndexes.size(), "XclExpXFBuffer::GetXmlStyleIndex - invalid index!" ); + if( nXFIndex > maStyleIndexes.size() ) + return 0; // should be caught/debugged via above assert; return "valid" index. + return maStyleIndexes[ nXFIndex ]; +} + +sal_Int32 XclExpXFBuffer::GetXmlCellIndex( sal_uInt32 nXFIndex ) const +{ + DBG_ASSERT( nXFIndex < maCellIndexes.size(), "XclExpXFBuffer::GetXmlStyleIndex - invalid index!" ); + if( nXFIndex > maCellIndexes.size() ) + return 0; // should be caught/debugged via above assert; return "valid" index. + return maCellIndexes[ nXFIndex ]; +} + +void XclExpXFBuffer::Save( XclExpStream& rStrm ) +{ + // save all XF records contained in the maSortedXFList vector (sorted by XF index) + maSortedXFList.Save( rStrm ); + // save all STYLE records + maStyleList.Save( rStrm ); +} + +static void lcl_GetCellCounts( const XclExpRecordList< XclExpXF >& rXFList, sal_Int32& rCells, sal_Int32& rStyles ) +{ + rCells = 0; + rStyles = 0; + size_t nXFCount = rXFList.GetSize(); + for( size_t i = 0; i < nXFCount; ++i ) + { + XclExpRecordList< XclExpXF >::RecordRefType xXF = rXFList.GetRecord( i ); + if( xXF->IsCellXF() ) + ++rCells; + else if( xXF->IsStyleXF() ) + ++rStyles; + } +} + +void XclExpXFBuffer::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream(); + + rStyleSheet->startElement( XML_fills, + XML_count, OString::valueOf( (sal_Int32) maFills.size() ).getStr(), + FSEND ); + for( XclExpFillList::iterator aIt = maFills.begin(), aEnd = maFills.end(); + aIt != aEnd; ++aIt ) + { + aIt->SaveXml( rStrm ); + } + rStyleSheet->endElement( XML_fills ); + + rStyleSheet->startElement( XML_borders, + XML_count, OString::valueOf( (sal_Int32) maBorders.size() ).getStr(), + FSEND ); + for( XclExpBorderList::iterator aIt = maBorders.begin(), aEnd = maBorders.end(); + aIt != aEnd; ++aIt ) + { + aIt->SaveXml( rStrm ); + } + rStyleSheet->endElement( XML_borders ); + + // save all XF records contained in the maSortedXFList vector (sorted by XF index) + sal_Int32 nCells, nStyles; + lcl_GetCellCounts( maSortedXFList, nCells, nStyles ); + + if( nStyles > 0 ) + { + rStyleSheet->startElement( XML_cellStyleXfs, + XML_count, OString::valueOf( nStyles ).getStr(), + FSEND ); + size_t nXFCount = maSortedXFList.GetSize(); + for( size_t i = 0; i < nXFCount; ++i ) + { + XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i ); + if( ! xXF->IsStyleXF() ) + continue; + SaveXFXml( rStrm, *xXF ); + } + rStyleSheet->endElement( XML_cellStyleXfs ); + } + + if( nCells > 0 ) + { + rStyleSheet->startElement( XML_cellXfs, + XML_count, OString::valueOf( nCells ).getStr(), + FSEND ); + size_t nXFCount = maSortedXFList.GetSize(); + for( size_t i = 0; i < nXFCount; ++i ) + { + XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i ); + if( ! xXF->IsCellXF() ) + continue; + SaveXFXml( rStrm, *xXF ); + } + rStyleSheet->endElement( XML_cellXfs ); + } + + // save all STYLE records + rStyleSheet->startElement( XML_cellStyles, + XML_count, OString::valueOf( (sal_Int32) maStyleList.GetSize() ).getStr(), + FSEND ); + maStyleList.SaveXml( rStrm ); + rStyleSheet->endElement( XML_cellStyles ); +} + +void XclExpXFBuffer::SaveXFXml( XclExpXmlStream& rStrm, XclExpXF& rXF ) +{ + XclExpBorderList::iterator aBorderPos = + std::find_if( maBorders.begin(), maBorders.end(), XclExpBorderPred( rXF.GetBorderData() ) ); + DBG_ASSERT( aBorderPos != maBorders.end(), "XclExpXFBuffer::SaveXml - Invalid @borderId!" ); + XclExpFillList::iterator aFillPos = + std::find_if( maFills.begin(), maFills.end(), XclExpFillPred( rXF.GetAreaData() ) ); + DBG_ASSERT( aFillPos != maFills.end(), "XclExpXFBuffer::SaveXml - Invalid @fillId!" ); + + sal_Int32 nBorderId = 0, nFillId = 0; + if( aBorderPos != maBorders.end() ) + nBorderId = std::distance( maBorders.begin(), aBorderPos ); + if( aFillPos != maFills.end() ) + nFillId = std::distance( maFills.begin(), aFillPos ); + + rXF.SetXmlIds( nBorderId, nFillId ); + rXF.SaveXml( rStrm ); +} + +sal_uInt32 XclExpXFBuffer::FindXF( const ScPatternAttr& rPattern, + ULONG nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) const +{ + for( size_t nPos = 0, nSize = maXFList.GetSize(); nPos < nSize; ++nPos ) + if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) ) + return static_cast< sal_uInt32 >( nPos ); + return EXC_XFID_NOTFOUND; +} + +sal_uInt32 XclExpXFBuffer::FindXF( const SfxStyleSheetBase& rStyleSheet ) const +{ + for( size_t nPos = 0, nSize = maXFList.GetSize(); nPos < nSize; ++nPos ) + if( maXFList.GetRecord( nPos )->Equals( rStyleSheet ) ) + return static_cast< sal_uInt32 >( nPos ); + return EXC_XFID_NOTFOUND; +} + +sal_uInt32 XclExpXFBuffer::FindBuiltInXF( sal_uInt8 nStyleId, sal_uInt8 nLevel ) const +{ + for( XclExpBuiltInMap::const_iterator aIt = maBuiltInMap.begin(), aEnd = maBuiltInMap.end(); aIt != aEnd; ++aIt ) + if( (aIt->second.mnStyleId == nStyleId) && (aIt->second.mnLevel == nLevel) ) + return aIt->first; + return EXC_XFID_NOTFOUND; +} + +sal_uInt32 XclExpXFBuffer::InsertCellXF( const ScPatternAttr* pPattern, sal_Int16 nScript, + ULONG nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) +{ + const ScPatternAttr* pDefPattern = GetDoc().GetDefPattern(); + if( !pPattern ) + pPattern = pDefPattern; + + // special handling for default cell formatting + if( (pPattern == pDefPattern) && !bForceLineBreak && + (nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND) && + (nForceXclFont == EXC_FONT_NOTFOUND) ) + { + // Is it the first try to insert the default cell format? + bool& rbPredefined = maBuiltInMap[ EXC_XF_DEFAULTCELL ].mbPredefined; + if( rbPredefined ) + { + // replace default cell pattern + XclExpXFRef xNewXF( new XclExpXF( GetRoot(), *pPattern, nScript ) ); + maXFList.ReplaceRecord( xNewXF, EXC_XF_DEFAULTCELL ); + rbPredefined = false; + } + return GetDefCellXFId(); + } + + sal_uInt32 nXFId = FindXF( *pPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ); + if( nXFId == EXC_XFID_NOTFOUND ) + { + // not found - insert new cell XF + if( maXFList.GetSize() < EXC_XFLIST_HARDLIMIT ) + { + maXFList.AppendNewRecord( new XclExpXF( + GetRoot(), *pPattern, nScript, nForceScNumFmt, nForceXclFont, bForceLineBreak ) ); + // do not set nXFId before the AppendNewRecord() call - it may insert 2 XFs (style+cell) + nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() - 1 ); + } + else + { + // list full - fall back to default cell XF + nXFId = GetDefCellXFId(); + } + } + return nXFId; +} + +sal_uInt32 XclExpXFBuffer::InsertStyleXF( const SfxStyleSheetBase& rStyleSheet ) +{ + // *** try, if it is a built-in style - create new XF or replace existing predefined XF *** + + sal_uInt8 nStyleId, nLevel; + if( XclTools::GetBuiltInStyleId( nStyleId, nLevel, rStyleSheet.GetName() ) ) + { + // try to find the built-in XF record (if already created in InsertDefaultRecords()) + sal_uInt32 nXFId = FindBuiltInXF( nStyleId, nLevel ); + if( nXFId == EXC_XFID_NOTFOUND ) + { + // built-in style XF not yet created - do it now + XclExpXFRef xXF( new XclExpXF( GetRoot(), rStyleSheet ) ); + nXFId = AppendBuiltInXFWithStyle( xXF, nStyleId, nLevel ); + // this new XF record is not predefined + maBuiltInMap[ nXFId ].mbPredefined = false; + } + else + { + DBG_ASSERT( maXFList.HasRecord( nXFId ), "XclExpXFBuffer::InsertStyleXF - built-in XF not found" ); + // XF record still predefined? -> Replace with real XF + bool& rbPredefined = maBuiltInMap[ nXFId ].mbPredefined; + if( rbPredefined ) + { + // replace predefined built-in style (ReplaceRecord() deletes old record) + maXFList.ReplaceRecord( XclExpXFRef( new XclExpXF( GetRoot(), rStyleSheet ) ), nXFId ); + rbPredefined = false; + } + } + + // STYLE already inserted? (may be not, i.e. for RowLevel/ColLevel or Hyperlink styles) + bool& rbHasStyleRec = maBuiltInMap[ nXFId ].mbHasStyleRec; + if( !rbHasStyleRec ) + { + maStyleList.AppendNewRecord( new XclExpStyle( nXFId, nStyleId, nLevel ) ); + rbHasStyleRec = true; + } + + return nXFId; + } + + // *** try to find the XF record of a user-defined style *** + + sal_uInt32 nXFId = FindXF( rStyleSheet ); + if( nXFId == EXC_XFID_NOTFOUND ) + { + // not found - insert new style XF and STYLE + nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() ); + if( nXFId < EXC_XFLIST_HARDLIMIT ) + { + maXFList.AppendNewRecord( new XclExpXF( GetRoot(), rStyleSheet ) ); + // create the STYLE record + if( rStyleSheet.GetName().Len() ) + maStyleList.AppendNewRecord( new XclExpStyle( nXFId, rStyleSheet.GetName() ) ); + } + else + // list full - fall back to default style XF + nXFId = GetXFIdFromIndex( EXC_XF_DEFAULTSTYLE ); + } + return nXFId; +} + +void XclExpXFBuffer::InsertUserStyles() +{ + SfxStyleSheetIterator aStyleIter( GetDoc().GetStyleSheetPool(), SFX_STYLE_FAMILY_PARA ); + for( SfxStyleSheetBase* pStyleSheet = aStyleIter.First(); pStyleSheet; pStyleSheet = aStyleIter.Next() ) + if( pStyleSheet->IsUserDefined() && !lclIsBuiltInStyle( pStyleSheet->GetName() ) ) + InsertStyleXF( *pStyleSheet ); +} + +sal_uInt32 XclExpXFBuffer::AppendBuiltInXF( XclExpXFRef xXF, sal_uInt8 nStyleId, sal_uInt8 nLevel ) +{ + sal_uInt32 nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() ); + maXFList.AppendRecord( xXF ); + XclExpBuiltInInfo& rInfo = maBuiltInMap[ nXFId ]; + rInfo.mnStyleId = nStyleId; + rInfo.mnLevel = nLevel; + rInfo.mbPredefined = true; + return nXFId; +} + +sal_uInt32 XclExpXFBuffer::AppendBuiltInXFWithStyle( XclExpXFRef xXF, sal_uInt8 nStyleId, sal_uInt8 nLevel ) +{ + sal_uInt32 nXFId = AppendBuiltInXF( xXF, nStyleId, nLevel ); + maStyleList.AppendNewRecord( new XclExpStyle( nXFId, nStyleId, nLevel ) ); + maBuiltInMap[ nXFId ].mbHasStyleRec = true; // mark existing STYLE record + return nXFId; +} + +static XclExpCellArea lcl_GetPatternFill_None() +{ + XclExpCellArea aFill; + aFill.mnPattern = EXC_PATT_NONE; + return aFill; +} + +static XclExpCellArea lcl_GetPatternFill_Gray125() +{ + XclExpCellArea aFill; + aFill.mnPattern = EXC_PATT_12_5_PERC; + aFill.mnForeColor = 0; + aFill.mnBackColor = 0; + return aFill; +} + +void XclExpXFBuffer::InsertDefaultRecords() +{ + maFills.push_back( lcl_GetPatternFill_None() ); + maFills.push_back( lcl_GetPatternFill_Gray125() ); + + // index 0: default style + if( SfxStyleSheetBase* pDefStyleSheet = GetStyleSheetPool().Find( ScGlobal::GetRscString( STR_STYLENAME_STANDARD ), SFX_STYLE_FAMILY_PARA ) ) + { + XclExpXFRef xDefStyle( new XclExpXF( GetRoot(), *pDefStyleSheet ) ); + sal_uInt32 nXFId = AppendBuiltInXFWithStyle( xDefStyle, EXC_STYLE_NORMAL ); + // mark this XF as not predefined, prevents overwriting + maBuiltInMap[ nXFId ].mbPredefined = false; + } + else + { + DBG_ERRORFILE( "XclExpXFBuffer::InsertDefaultRecords - default style not found" ); + XclExpXFRef xDefStyle( new XclExpDefaultXF( GetRoot(), false ) ); + xDefStyle->SetAllUsedFlags( true ); + AppendBuiltInXFWithStyle( xDefStyle, EXC_STYLE_NORMAL ); + } + + // index 1-14: RowLevel and ColLevel styles (without STYLE records) + XclExpDefaultXF aLevelStyle( GetRoot(), false ); + // RowLevel_1, ColLevel_1 + aLevelStyle.SetFont( 1 ); + AppendBuiltInXF( XclExpXFRef( new XclExpDefaultXF( aLevelStyle ) ), EXC_STYLE_ROWLEVEL, 0 ); + AppendBuiltInXF( XclExpXFRef( new XclExpDefaultXF( aLevelStyle ) ), EXC_STYLE_COLLEVEL, 0 ); + // RowLevel_2, ColLevel_2 + aLevelStyle.SetFont( 2 ); + AppendBuiltInXF( XclExpXFRef( new XclExpDefaultXF( aLevelStyle ) ), EXC_STYLE_ROWLEVEL, 1 ); + AppendBuiltInXF( XclExpXFRef( new XclExpDefaultXF( aLevelStyle ) ), EXC_STYLE_COLLEVEL, 1 ); + // RowLevel_3, ColLevel_3 ... RowLevel_7, ColLevel_7 + aLevelStyle.SetFont( 0 ); + for( sal_uInt8 nLevel = 2; nLevel < EXC_STYLE_LEVELCOUNT; ++nLevel ) + { + AppendBuiltInXF( XclExpXFRef( new XclExpDefaultXF( aLevelStyle ) ), EXC_STYLE_ROWLEVEL, nLevel ); + AppendBuiltInXF( XclExpXFRef( new XclExpDefaultXF( aLevelStyle ) ), EXC_STYLE_COLLEVEL, nLevel ); + } + + // index 15: default hard cell format, placeholder to be able to add more built-in styles + maXFList.AppendNewRecord( new XclExpDefaultXF( GetRoot(), true ) ); + maBuiltInMap[ EXC_XF_DEFAULTCELL ].mbPredefined = true; + + // index 16-20: other built-in styles + XclExpDefaultXF aFormatStyle( GetRoot(), false ); + aFormatStyle.SetFont( 1 ); + aFormatStyle.SetNumFmt( 43 ); + AppendBuiltInXFWithStyle( XclExpXFRef( new XclExpDefaultXF( aFormatStyle ) ), EXC_STYLE_COMMA ); + aFormatStyle.SetNumFmt( 41 ); + AppendBuiltInXFWithStyle( XclExpXFRef( new XclExpDefaultXF( aFormatStyle ) ), EXC_STYLE_COMMA_0 ); + aFormatStyle.SetNumFmt( 44 ); + AppendBuiltInXFWithStyle( XclExpXFRef( new XclExpDefaultXF( aFormatStyle ) ), EXC_STYLE_CURRENCY ); + aFormatStyle.SetNumFmt( 42 ); + AppendBuiltInXFWithStyle( XclExpXFRef( new XclExpDefaultXF( aFormatStyle ) ), EXC_STYLE_CURRENCY_0 ); + aFormatStyle.SetNumFmt( 9 ); + AppendBuiltInXFWithStyle( XclExpXFRef( new XclExpDefaultXF( aFormatStyle ) ), EXC_STYLE_PERCENT ); + + // other built-in style XF records (i.e. Hyperlink styles) are created on demand + + /* Insert the real default hard cell format -> 0 is document default pattern. + Do it here (and not already above) to really have all built-in styles. */ + Insert( 0, GetDefApiScript() ); +} + +void XclExpXFBuffer::AppendXFIndex( sal_uInt32 nXFId ) +{ + DBG_ASSERT( nXFId < maXFIndexVec.size(), "XclExpXFBuffer::AppendXFIndex - XF ID out of range" ); + maXFIndexVec[ nXFId ] = static_cast< sal_uInt16 >( maSortedXFList.GetSize() ); + XclExpXFRef xXF = maXFList.GetRecord( nXFId ); + AddBorderAndFill( *xXF ); + maSortedXFList.AppendRecord( xXF ); + DBG_ASSERT( maXFList.HasRecord( nXFId ), "XclExpXFBuffer::AppendXFIndex - XF not found" ); +} + +void XclExpXFBuffer::AddBorderAndFill( const XclExpXF& rXF ) +{ + if( std::find_if( maBorders.begin(), maBorders.end(), XclExpBorderPred( rXF.GetBorderData() ) ) == maBorders.end() ) + { + maBorders.push_back( rXF.GetBorderData() ); + } + + if( std::find_if( maFills.begin(), maFills.end(), XclExpFillPred( rXF.GetAreaData() ) ) == maFills.end() ) + { + maFills.push_back( rXF.GetAreaData() ); + } +} + +// ============================================================================ + +XclExpXmlStyleSheet::XclExpXmlStyleSheet( const XclExpRoot& rRoot ) + : XclExpRoot( rRoot ) +{ +} + +void XclExpXmlStyleSheet::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr aStyleSheet = rStrm.CreateOutputStream( + OUString::createFromAscii( "xl/styles.xml" ), + OUString::createFromAscii( "styles.xml" ), + rStrm.GetCurrentStream()->getOutputStream(), + "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" ); + rStrm.PushStream( aStyleSheet ); + + aStyleSheet->startElement( XML_styleSheet, + XML_xmlns, "http://schemas.openxmlformats.org/spreadsheetml/2006/main", + FSEND ); + + CreateRecord( EXC_ID_FORMATLIST )->SaveXml( rStrm ); + CreateRecord( EXC_ID_FONTLIST )->SaveXml( rStrm ); + CreateRecord( EXC_ID_XFLIST )->SaveXml( rStrm ); + CreateRecord( EXC_ID_PALETTE )->SaveXml( rStrm ); + + aStyleSheet->endElement( XML_styleSheet ); + + rStrm.PopStream(); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xetable.cxx b/sc/source/filter/excel/xetable.cxx new file mode 100644 index 000000000000..b5ca318a91b4 --- /dev/null +++ b/sc/source/filter/excel/xetable.cxx @@ -0,0 +1,2579 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xetable.hxx" + +#include <map> +#include <com/sun/star/i18n/ScriptType.hpp> +#include "scitems.hxx" +#include <svl/intitem.hxx> +#include "document.hxx" +#include "dociter.hxx" +#include "olinetab.hxx" +#include "cell.hxx" +#include "patattr.hxx" +#include "attrib.hxx" +#include "xehelper.hxx" +#include "xecontent.hxx" +#include "xeescher.hxx" + +#include <oox/core/tokens.hxx> + +using ::rtl::OString; +using ::rtl::OUString; +using ::rtl::OUStringBuffer; + +namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + +// ============================================================================ +// Helper records for cell records +// ============================================================================ + +XclExpStringRec::XclExpStringRec( const XclExpRoot& rRoot, const String& rResult ) : + XclExpRecord( EXC_ID3_STRING ), + mxResult( XclExpStringHelper::CreateString( rRoot, rResult ) ) +{ + DBG_ASSERT( (rRoot.GetBiff() <= EXC_BIFF5) || (mxResult->Len() > 0), + "XclExpStringRec::XclExpStringRec - empty result not allowed in BIFF8+" ); + SetRecSize( mxResult->GetSize() ); +} + +void XclExpStringRec::WriteBody( XclExpStream& rStrm ) +{ + rStrm << *mxResult; +} + +// Additional records for special formula ranges ============================== + +XclExpRangeFmlaBase::XclExpRangeFmlaBase( + sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScAddress& rScPos ) : + XclExpRecord( nRecId, nRecSize ), + maXclRange( ScAddress::UNINITIALIZED ), + maBaseXclPos( ScAddress::UNINITIALIZED ) +{ + maBaseXclPos.Set( static_cast< sal_uInt16 >( rScPos.Col() ), static_cast< sal_uInt16 >( rScPos.Row() ) ); + maXclRange.maFirst = maXclRange.maLast = maBaseXclPos; +} + +XclExpRangeFmlaBase::XclExpRangeFmlaBase( + sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScRange& rScRange ) : + XclExpRecord( nRecId, nRecSize ), + maXclRange( ScAddress::UNINITIALIZED ), + maBaseXclPos( ScAddress::UNINITIALIZED ) +{ + maXclRange.Set( + static_cast< sal_uInt16 >( rScRange.aStart.Col() ), + static_cast< sal_uInt16 >( rScRange.aStart.Row() ), + static_cast< sal_uInt16 >( rScRange.aEnd.Col() ), + static_cast< sal_uInt16 >( rScRange.aEnd.Row() ) ); + maBaseXclPos = maXclRange.maFirst; +} + +bool XclExpRangeFmlaBase::IsBasePos( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const +{ + return (maBaseXclPos.mnCol == nXclCol) && (maBaseXclPos.mnRow == nXclRow); +} + +void XclExpRangeFmlaBase::Extend( const ScAddress& rScPos ) +{ + sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() ); + sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() ); + maXclRange.maFirst.mnCol = ::std::min( maXclRange.maFirst.mnCol, nXclCol ); + maXclRange.maFirst.mnRow = ::std::min( maXclRange.maFirst.mnRow, nXclRow ); + maXclRange.maLast.mnCol = ::std::max( maXclRange.maLast.mnCol, nXclCol ); + maXclRange.maLast.mnRow = ::std::max( maXclRange.maLast.mnRow, nXclRow ); +} + +void XclExpRangeFmlaBase::WriteRangeAddress( XclExpStream& rStrm ) const +{ + maXclRange.Write( rStrm, false ); +} + +// Array formulas ============================================================= + +XclExpArray::XclExpArray( XclTokenArrayRef xTokArr, const ScRange& rScRange ) : + XclExpRangeFmlaBase( EXC_ID3_ARRAY, 14 + xTokArr->GetSize(), rScRange ), + mxTokArr( xTokArr ) +{ +} + +XclTokenArrayRef XclExpArray::CreateCellTokenArray( const XclExpRoot& rRoot ) const +{ + return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos ); +} + +bool XclExpArray::IsVolatile() const +{ + return mxTokArr->IsVolatile(); +} + +void XclExpArray::WriteBody( XclExpStream& rStrm ) +{ + WriteRangeAddress( rStrm ); + sal_uInt16 nFlags = EXC_ARRAY_DEFAULTFLAGS; + ::set_flag( nFlags, EXC_ARRAY_RECALC_ALWAYS, IsVolatile() ); + rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr; +} + +// ---------------------------------------------------------------------------- + +XclExpArrayBuffer::XclExpArrayBuffer( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ) +{ +} + +XclExpArrayRef XclExpArrayBuffer::CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange ) +{ + const ScAddress& rScPos = rScRange.aStart; + XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_MATRIX, rScTokArr, &rScPos ); + + DBG_ASSERT( maRecMap.find( rScPos ) == maRecMap.end(), "XclExpArrayBuffer::CreateArray - array exists already" ); + XclExpArrayRef& rxRec = maRecMap[ rScPos ]; + rxRec.reset( new XclExpArray( xTokArr, rScRange ) ); + return rxRec; +} + +XclExpArrayRef XclExpArrayBuffer::FindArray( const ScTokenArray& rScTokArr ) const +{ + XclExpArrayRef xRec; + // try to extract a matrix reference token + if( rScTokArr.GetLen() == 1 ) + { + const formula::FormulaToken* pToken = rScTokArr.GetArray()[ 0 ]; + if( pToken && (pToken->GetOpCode() == ocMatRef) ) + { + const ScSingleRefData& rRef = static_cast<const ScToken*>(pToken)->GetSingleRef(); + ScAddress aBasePos( rRef.nCol, rRef.nRow, GetCurrScTab() ); + XclExpArrayMap::const_iterator aIt = maRecMap.find( aBasePos ); + if( aIt != maRecMap.end() ) + xRec = aIt->second; + } + } + return xRec; +} + +// Shared formulas ============================================================ + +XclExpShrfmla::XclExpShrfmla( XclTokenArrayRef xTokArr, const ScAddress& rScPos ) : + XclExpRangeFmlaBase( EXC_ID_SHRFMLA, 10 + xTokArr->GetSize(), rScPos ), + mxTokArr( xTokArr ), + mnUsedCount( 1 ) +{ +} + +void XclExpShrfmla::ExtendRange( const ScAddress& rScPos ) +{ + Extend( rScPos ); + ++mnUsedCount; +} + +XclTokenArrayRef XclExpShrfmla::CreateCellTokenArray( const XclExpRoot& rRoot ) const +{ + return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos ); +} + +bool XclExpShrfmla::IsVolatile() const +{ + return mxTokArr->IsVolatile(); +} + +void XclExpShrfmla::WriteBody( XclExpStream& rStrm ) +{ + WriteRangeAddress( rStrm ); + rStrm << sal_uInt8( 0 ) << mnUsedCount << *mxTokArr; +} + +// ---------------------------------------------------------------------------- + +XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ) +{ +} + +XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla( + const ScTokenArray& rScTokArr, const ScAddress& rScPos ) +{ + XclExpShrfmlaRef xRec; + if( const ScTokenArray* pShrdScTokArr = XclTokenArrayHelper::GetSharedFormula( GetRoot(), rScTokArr ) ) + { + XclExpShrfmlaMap::iterator aIt = maRecMap.find( pShrdScTokArr ); + if( aIt == maRecMap.end() ) + { + // create a new record + XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_SHARED, *pShrdScTokArr, &rScPos ); + xRec.reset( new XclExpShrfmla( xTokArr, rScPos ) ); + maRecMap[ pShrdScTokArr ] = xRec; + } + else + { + // extend existing record + DBG_ASSERT( aIt->second, "XclExpShrfmlaBuffer::CreateOrExtendShrfmla - missing record" ); + xRec = aIt->second; + xRec->ExtendRange( rScPos ); + } + } + return xRec; +} + +// Multiple operations ======================================================== + +XclExpTableop::XclExpTableop( const ScAddress& rScPos, + const XclMultipleOpRefs& rRefs, sal_uInt8 nScMode ) : + XclExpRangeFmlaBase( EXC_ID3_TABLEOP, 16, rScPos ), + mnLastAppXclCol( static_cast< sal_uInt16 >( rScPos.Col() ) ), + mnColInpXclCol( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Col() ) ), + mnColInpXclRow( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Row() ) ), + mnRowInpXclCol( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Col() ) ), + mnRowInpXclRow( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Row() ) ), + mnScMode( nScMode ), + mbValid( false ) +{ +} + +bool XclExpTableop::TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs ) +{ + sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() ); + sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() ); + + bool bOk = IsAppendable( nXclCol, nXclRow ); + if( bOk ) + { + SCCOL nFirstScCol = static_cast< SCCOL >( maXclRange.maFirst.mnCol ); + SCROW nFirstScRow = static_cast< SCROW >( maXclRange.maFirst.mnRow ); + SCCOL nColInpScCol = static_cast< SCCOL >( mnColInpXclCol ); + SCROW nColInpScRow = static_cast< SCROW >( mnColInpXclRow ); + SCCOL nRowInpScCol = static_cast< SCCOL >( mnRowInpXclCol ); + SCROW nRowInpScRow = static_cast< SCROW >( mnRowInpXclRow ); + + bOk = ((mnScMode == 2) == rRefs.mbDblRefMode) && + (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) && + (nColInpScCol == rRefs.maColFirstScPos.Col()) && + (nColInpScRow == rRefs.maColFirstScPos.Row()) && + (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) && + (rScPos.Tab() == rRefs.maColRelScPos.Tab()); + + if( bOk ) switch( mnScMode ) + { + case 0: + bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col()) && + (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) && + (nFirstScCol == rRefs.maColRelScPos.Col() + 1) && + (rScPos.Row() == rRefs.maColRelScPos.Row()); + break; + case 1: + bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) && + (rScPos.Row() == rRefs.maFmlaScPos.Row()) && + (rScPos.Col() == rRefs.maColRelScPos.Col()) && + (nFirstScRow == rRefs.maColRelScPos.Row() + 1); + break; + case 2: + bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) && + (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) && + (nFirstScCol == rRefs.maColRelScPos.Col() + 1) && + (rScPos.Row() == rRefs.maColRelScPos.Row()) && + (nRowInpScCol == rRefs.maRowFirstScPos.Col()) && + (nRowInpScRow == rRefs.maRowFirstScPos.Row()) && + (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) && + (rScPos.Col() == rRefs.maRowRelScPos.Col()) && + (nFirstScRow == rRefs.maRowRelScPos.Row() + 1) && + (rScPos.Tab() == rRefs.maRowRelScPos.Tab()); + break; + default: + bOk = false; + } + + if( bOk ) + { + // extend the cell range + DBG_ASSERT( IsAppendable( nXclCol, nXclRow ), "XclExpTableop::TryExtend - wrong cell address" ); + Extend( rScPos ); + mnLastAppXclCol = nXclCol; + } + } + + return bOk; +} + +void XclExpTableop::Finalize() +{ + // is the range complete? (last appended cell is in last column) + mbValid = maXclRange.maLast.mnCol == mnLastAppXclCol; + // if last row is incomplete, try to shorten the used range + if( !mbValid && (maXclRange.maFirst.mnRow < maXclRange.maLast.mnRow) ) + { + --maXclRange.maLast.mnRow; + mbValid = true; + } + + // check if referred cells are outside of own range + if( mbValid ) switch( mnScMode ) + { + case 0: + mbValid = (mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) || + (mnColInpXclRow < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow); + break; + case 1: + mbValid = (mnColInpXclCol < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) || + (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow); + break; + case 2: + mbValid = ((mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) || + (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow)) && + ((mnRowInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnRowInpXclCol > maXclRange.maLast.mnCol) || + (mnRowInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnRowInpXclRow > maXclRange.maLast.mnRow)); + break; + } +} + +XclTokenArrayRef XclExpTableop::CreateCellTokenArray( const XclExpRoot& rRoot ) const +{ + XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler(); + return mbValid ? + rFmlaComp.CreateSpecialRefFormula( EXC_TOKID_TBL, maBaseXclPos ) : + rFmlaComp.CreateErrorFormula( EXC_ERR_NA ); +} + +bool XclExpTableop::IsVolatile() const +{ + return true; +} + +void XclExpTableop::Save( XclExpStream& rStrm ) +{ + if( mbValid ) + XclExpRangeFmlaBase::Save( rStrm ); +} + +bool XclExpTableop::IsAppendable( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const +{ + return ((nXclCol == mnLastAppXclCol + 1) && (nXclRow == maXclRange.maFirst.mnRow)) || + ((nXclCol == mnLastAppXclCol + 1) && (nXclCol <= maXclRange.maLast.mnCol) && (nXclRow == maXclRange.maLast.mnRow)) || + ((mnLastAppXclCol == maXclRange.maLast.mnCol) && (nXclCol == maXclRange.maFirst.mnCol) && (nXclRow == maXclRange.maLast.mnRow + 1)); +} + +void XclExpTableop::WriteBody( XclExpStream& rStrm ) +{ + sal_uInt16 nFlags = EXC_TABLEOP_DEFAULTFLAGS; + ::set_flag( nFlags, EXC_TABLEOP_RECALC_ALWAYS, IsVolatile() ); + switch( mnScMode ) + { + case 1: ::set_flag( nFlags, EXC_TABLEOP_ROW ); break; + case 2: ::set_flag( nFlags, EXC_TABLEOP_BOTH ); break; + } + + WriteRangeAddress( rStrm ); + rStrm << nFlags; + if( mnScMode == 2 ) + rStrm << mnRowInpXclRow << mnRowInpXclCol << mnColInpXclRow << mnColInpXclCol; + else + rStrm << mnColInpXclRow << mnColInpXclCol << sal_uInt32( 0 ); +} + +// ---------------------------------------------------------------------------- + +XclExpTableopBuffer::XclExpTableopBuffer( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ) +{ +} + +XclExpTableopRef XclExpTableopBuffer::CreateOrExtendTableop( + const ScTokenArray& rScTokArr, const ScAddress& rScPos ) +{ + XclExpTableopRef xRec; + + // try to extract cell references of a multiple operations formula + XclMultipleOpRefs aRefs; + if( XclTokenArrayHelper::GetMultipleOpRefs( aRefs, rScTokArr ) ) + { + // try to find an existing TABLEOP record for this cell position + for( size_t nPos = 0, nSize = maTableopList.GetSize(); !xRec && (nPos < nSize); ++nPos ) + { + XclExpTableopRef xTempRec = maTableopList.GetRecord( nPos ); + if( xTempRec->TryExtend( rScPos, aRefs ) ) + xRec = xTempRec; + } + + // no record found, or found record not extensible + if( !xRec ) + xRec = TryCreate( rScPos, aRefs ); + } + + return xRec; +} + +void XclExpTableopBuffer::Finalize() +{ + for( size_t nPos = 0, nSize = maTableopList.GetSize(); nPos < nSize; ++nPos ) + maTableopList.GetRecord( nPos )->Finalize(); +} + +XclExpTableopRef XclExpTableopBuffer::TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs ) +{ + sal_uInt8 nScMode = 0; + bool bOk = (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) && + (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) && + (rScPos.Tab() == rRefs.maColRelScPos.Tab()); + + if( bOk ) + { + if( rRefs.mbDblRefMode ) + { + nScMode = 2; + bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) && + (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) && + (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) && + (rScPos.Row() == rRefs.maColRelScPos.Row()) && + (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) && + (rScPos.Col() == rRefs.maRowRelScPos.Col()) && + (rScPos.Row() == rRefs.maRowRelScPos.Row() + 1) && + (rScPos.Tab() == rRefs.maRowRelScPos.Tab()); + } + else if( (rScPos.Col() == rRefs.maFmlaScPos.Col()) && + (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) && + (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) && + (rScPos.Row() == rRefs.maColRelScPos.Row()) ) + { + nScMode = 0; + } + else if( (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) && + (rScPos.Row() == rRefs.maFmlaScPos.Row()) && + (rScPos.Col() == rRefs.maColRelScPos.Col()) && + (rScPos.Row() == rRefs.maColRelScPos.Row() + 1) ) + { + nScMode = 1; + } + else + { + bOk = false; + } + } + + XclExpTableopRef xRec; + if( bOk ) + { + xRec.reset( new XclExpTableop( rScPos, rRefs, nScMode ) ); + maTableopList.AppendRecord( xRec ); + } + + return xRec; +} + +// ============================================================================ +// Cell records +// ============================================================================ + +XclExpCellBase::XclExpCellBase( + sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos ) : + XclExpRecord( nRecId, nContSize + 4 ), + maXclPos( rXclPos ) +{ +} + +bool XclExpCellBase::IsMultiLineText() const +{ + return false; +} + +bool XclExpCellBase::TryMerge( const XclExpCellBase& /*rCell*/ ) +{ + return false; +} + +void XclExpCellBase::GetBlankXFIndexes( ScfUInt16Vec& /*rXFIndexes*/ ) const +{ + // default: do nothing +} + +void XclExpCellBase::RemoveUnusedBlankCells( const ScfUInt16Vec& /*rXFIndexes*/ ) +{ + // default: do nothing +} + +// Single cell records ======================================================== + +XclExpSingleCellBase::XclExpSingleCellBase( + sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos, sal_uInt32 nXFId ) : + XclExpCellBase( nRecId, 2, rXclPos ), + maXFId( nXFId ), + mnContSize( nContSize ) +{ +} + +XclExpSingleCellBase::XclExpSingleCellBase( const XclExpRoot& rRoot, + sal_uInt16 nRecId, sal_Size nContSize, const XclAddress& rXclPos, + const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForcedXFId ) : + XclExpCellBase( nRecId, 2, rXclPos ), + maXFId( nForcedXFId ), + mnContSize( nContSize ) +{ + if( GetXFId() == EXC_XFID_NOTFOUND ) + SetXFId( rRoot.GetXFBuffer().Insert( pPattern, nScript ) ); +} + +sal_uInt16 XclExpSingleCellBase::GetLastXclCol() const +{ + return GetXclCol(); +} + +sal_uInt32 XclExpSingleCellBase::GetFirstXFId() const +{ + return GetXFId(); +} + +bool XclExpSingleCellBase::IsEmpty() const +{ + return false; +} + +void XclExpSingleCellBase::ConvertXFIndexes( const XclExpRoot& rRoot ) +{ + maXFId.ConvertXFIndex( rRoot ); +} + +void XclExpSingleCellBase::Save( XclExpStream& rStrm ) +{ + DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 ); + AddRecSize( mnContSize ); + XclExpCellBase::Save( rStrm ); +} + +void XclExpSingleCellBase::WriteBody( XclExpStream& rStrm ) +{ + rStrm << GetXclRow() << GetXclCol() << maXFId.mnXFIndex; + WriteContents( rStrm ); +} + +// ---------------------------------------------------------------------------- + +IMPL_FIXEDMEMPOOL_NEWDEL( XclExpNumberCell, 256, 256 ) + +XclExpNumberCell::XclExpNumberCell( + const XclExpRoot& rRoot, const XclAddress& rXclPos, + const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, double fValue ) : + // #i41210# always use latin script for number cells - may look wrong for special number formats... + XclExpSingleCellBase( rRoot, EXC_ID3_NUMBER, 8, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ), + mfValue( fValue ) +{ +} + +static OString lcl_GetStyleId( XclExpXmlStream& rStrm, sal_uInt32 nXFIndex ) +{ + return OString::valueOf( rStrm.GetRoot().GetXFBuffer() + .GetXmlCellIndex( nXFIndex ) ); +} + +static OString lcl_GetStyleId( XclExpXmlStream& rStrm, const XclExpCellBase& rCell ) +{ + sal_uInt32 nXFId = rCell.GetFirstXFId(); + sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( nXFId ); + return lcl_GetStyleId( rStrm, nXFIndex ); +} + +void XclExpNumberCell::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + rWorksheet->startElement( XML_c, + XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(), + XML_s, lcl_GetStyleId( rStrm, *this ).getStr(), + XML_t, "n", + // OOXTODO: XML_cm, XML_vm, XML_ph + FSEND ); + rWorksheet->startElement( XML_v, FSEND ); + rWorksheet->write( mfValue ); + rWorksheet->endElement( XML_v ); + rWorksheet->endElement( XML_c ); +} + +void XclExpNumberCell::WriteContents( XclExpStream& rStrm ) +{ + rStrm << mfValue; +} + +// ---------------------------------------------------------------------------- + +IMPL_FIXEDMEMPOOL_NEWDEL( XclExpBooleanCell, 256, 256 ) + +XclExpBooleanCell::XclExpBooleanCell( + const XclExpRoot rRoot, const XclAddress& rXclPos, + const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, bool bValue ) : + // #i41210# always use latin script for boolean cells + XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ), + mbValue( bValue ) +{ +} + +void XclExpBooleanCell::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + rWorksheet->startElement( XML_c, + XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(), + XML_s, lcl_GetStyleId( rStrm, *this ).getStr(), + XML_t, "b", + // OOXTODO: XML_cm, XML_vm, XML_ph + FSEND ); + rWorksheet->startElement( XML_v, FSEND ); + rWorksheet->write( mbValue ? "1" : "0" ); + rWorksheet->endElement( XML_v ); + rWorksheet->endElement( XML_c ); +} + +void XclExpBooleanCell::WriteContents( XclExpStream& rStrm ) +{ + rStrm << sal_uInt16( mbValue ? 1 : 0 ) << EXC_BOOLERR_BOOL; +} + +// ---------------------------------------------------------------------------- + +//UNUSED2009-05 IMPL_FIXEDMEMPOOL_NEWDEL( XclExpErrorCell, 256, 256 ) +//UNUSED2009-05 +//UNUSED2009-05 XclExpErrorCell::XclExpErrorCell( +//UNUSED2009-05 const XclExpRoot rRoot, const XclAddress& rXclPos, +//UNUSED2009-05 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_uInt8 nErrCode ) : +//UNUSED2009-05 // #i41210# always use latin script for error cells +//UNUSED2009-05 XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ), +//UNUSED2009-05 mnErrCode( nErrCode ) +//UNUSED2009-05 { +//UNUSED2009-05 } +//UNUSED2009-05 +//UNUSED2009-05 void XclExpErrorCell::SaveXml( XclExpXmlStream& rStrm ) +//UNUSED2009-05 { +//UNUSED2009-05 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); +//UNUSED2009-05 rWorksheet->startElement( XML_c, +//UNUSED2009-05 XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(), +//UNUSED2009-05 XML_s, lcl_GetStyleId( rStrm, *this ).getStr(), +//UNUSED2009-05 XML_t, "e", +//UNUSED2009-05 // OOXTODO: XML_cm, XML_vm, XML_ph +//UNUSED2009-05 FSEND ); +//UNUSED2009-05 rWorksheet->startElement( XML_v, FSEND ); +//UNUSED2009-05 rWorksheet->write( (sal_Int32) mnErrCode ); +//UNUSED2009-05 rWorksheet->endElement( XML_v ); +//UNUSED2009-05 rWorksheet->endElement( XML_c ); +//UNUSED2009-05 } +//UNUSED2009-05 +//UNUSED2009-05 void XclExpErrorCell::WriteContents( XclExpStream& rStrm ) +//UNUSED2009-05 { +//UNUSED2009-05 rStrm << mnErrCode << EXC_BOOLERR_ERROR; +//UNUSED2009-05 } + +// ---------------------------------------------------------------------------- + +IMPL_FIXEDMEMPOOL_NEWDEL( XclExpLabelCell, 256, 256 ) + +XclExpLabelCell::XclExpLabelCell( + const XclExpRoot& rRoot, const XclAddress& rXclPos, + const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, const ScStringCell& rCell ) : + XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId ) +{ + sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN; + XclExpStringRef xText = XclExpStringHelper::CreateCellString( rRoot, rCell, pPattern, EXC_STR_DEFAULT, nMaxLen ); + Init( rRoot, pPattern, xText ); +} + +XclExpLabelCell::XclExpLabelCell( + const XclExpRoot& rRoot, const XclAddress& rXclPos, + const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, + const ScEditCell& rCell, XclExpHyperlinkHelper& rLinkHelper ) : + XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId ) +{ + sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN; + XclExpStringRef xText = XclExpStringHelper::CreateCellString( rRoot, rCell, pPattern, rLinkHelper, EXC_STR_DEFAULT, nMaxLen ); + Init( rRoot, pPattern, xText ); +} + +bool XclExpLabelCell::IsMultiLineText() const +{ + return mbLineBreak || mxText->IsWrapped(); +} + +void XclExpLabelCell::Init( const XclExpRoot& rRoot, + const ScPatternAttr* pPattern, XclExpStringRef xText ) +{ + DBG_ASSERT( xText.is() && xText->Len(), "XclExpLabelCell::XclExpLabelCell - empty string passed" ); + mxText = xText; + mnSstIndex = 0; + + // create the cell format + sal_uInt16 nXclFont = mxText->RemoveLeadingFont(); + if( GetXFId() == EXC_XFID_NOTFOUND ) + { + DBG_ASSERT( nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found" ); + bool bForceLineBreak = mxText->IsWrapped(); + SetXFId( rRoot.GetXFBuffer().InsertWithFont( pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak ) ); + } + + // get auto-wrap attribute from cell format + const XclExpXF* pXF = rRoot.GetXFBuffer().GetXFById( GetXFId() ); + mbLineBreak = pXF && pXF->GetAlignmentData().mbLineBreak; + + // initialize the record contents + switch( rRoot.GetBiff() ) + { + case EXC_BIFF5: + // BIFF5-BIFF7: create a LABEL or RSTRING record + DBG_ASSERT( mxText->Len() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::XclExpLabelCell - string too long" ); + SetContSize( mxText->GetSize() ); + // formatted string is exported in an RSTRING record + if( mxText->IsRich() ) + { + DBG_ASSERT( mxText->GetFormatsCount() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::WriteContents - too many formats" ); + mxText->LimitFormatCount( EXC_LABEL_MAXLEN ); + SetRecId( EXC_ID_RSTRING ); + SetContSize( GetContSize() + 1 + 2 * mxText->GetFormatsCount() ); + } + break; + case EXC_BIFF8: + // BIFF8+: create a LABELSST record + mnSstIndex = rRoot.GetSst().Insert( xText ); + SetRecId( EXC_ID_LABELSST ); + SetContSize( 4 ); + break; + default: DBG_ERROR_BIFF(); + } +} + +void XclExpLabelCell::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + rWorksheet->startElement( XML_c, + XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(), + XML_s, lcl_GetStyleId( rStrm, *this ).getStr(), + XML_t, "s", + // OOXTODO: XML_cm, XML_vm, XML_ph + FSEND ); + rWorksheet->startElement( XML_v, FSEND ); + rWorksheet->write( (sal_Int32) mnSstIndex ); + rWorksheet->endElement( XML_v ); + rWorksheet->endElement( XML_c ); +} + +void XclExpLabelCell::WriteContents( XclExpStream& rStrm ) +{ + switch( rStrm.GetRoot().GetBiff() ) + { + case EXC_BIFF5: + rStrm << *mxText; + if( mxText->IsRich() ) + { + rStrm << static_cast< sal_uInt8 >( mxText->GetFormatsCount() ); + mxText->WriteFormats( rStrm ); + } + break; + case EXC_BIFF8: + rStrm << mnSstIndex; + break; + default: DBG_ERROR_BIFF(); + } +} + +// ---------------------------------------------------------------------------- + +IMPL_FIXEDMEMPOOL_NEWDEL( XclExpFormulaCell, 256, 256 ) + +XclExpFormulaCell::XclExpFormulaCell( + const XclExpRoot& rRoot, const XclAddress& rXclPos, + const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, + const ScFormulaCell& rScFmlaCell, + XclExpArrayBuffer& rArrayBfr, + XclExpShrfmlaBuffer& rShrfmlaBfr, + XclExpTableopBuffer& rTableopBfr ) : + XclExpSingleCellBase( EXC_ID2_FORMULA, 0, rXclPos, nForcedXFId ), + mrScFmlaCell( const_cast< ScFormulaCell& >( rScFmlaCell ) ) +{ + // *** Find result number format overwriting cell number format *** ------- + + if( GetXFId() == EXC_XFID_NOTFOUND ) + { + SvNumberFormatter& rFormatter = rRoot.GetFormatter(); + XclExpNumFmtBuffer& rNumFmtBfr = rRoot.GetNumFmtBuffer(); + + // current cell number format + ULONG nScNumFmt = pPattern ? + GETITEMVALUE( pPattern->GetItemSet(), SfxUInt32Item, ATTR_VALUE_FORMAT, ULONG ) : + rNumFmtBfr.GetStandardFormat(); + + // alternative number format passed to XF buffer + ULONG nAltScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND; + /* #73420# Xcl doesn't know Boolean number formats, we write + "TRUE";"FALSE" (language dependent). Don't do it for automatic + formula formats, because Excel gets them right. */ + /* #i8640# Don't set text format, if we have string results. */ + short nFormatType = mrScFmlaCell.GetFormatType(); + if( ((nScNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0) && + (nFormatType != NUMBERFORMAT_LOGICAL) && + (nFormatType != NUMBERFORMAT_TEXT) ) + nAltScNumFmt = mrScFmlaCell.GetStandardFormat( rFormatter, nScNumFmt ); + /* #73420# If cell number format is Boolean and automatic formula + format is Boolean don't write that ugly special format. */ + else if( (nFormatType == NUMBERFORMAT_LOGICAL) && + (rFormatter.GetType( nScNumFmt ) == NUMBERFORMAT_LOGICAL) ) + nAltScNumFmt = rNumFmtBfr.GetStandardFormat(); + + // #i41420# find script type according to result type (always latin for numeric results) + sal_Int16 nScript = ApiScriptType::LATIN; + bool bForceLineBreak = false; + if( nFormatType == NUMBERFORMAT_TEXT ) + { + String aResult; + mrScFmlaCell.GetString( aResult ); + bForceLineBreak = mrScFmlaCell.IsMultilineResult(); + nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult ); + } + SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) ); + } + + // *** Convert the formula token array *** -------------------------------- + + ScAddress aScPos( static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), rRoot.GetCurrScTab() ); + const ScTokenArray& rScTokArr = *mrScFmlaCell.GetCode(); + + // first try to create multiple operations + mxAddRec = rTableopBfr.CreateOrExtendTableop( rScTokArr, aScPos ); + + // no multiple operation found - try to create matrix formula + if( !mxAddRec ) switch( static_cast< ScMatrixMode >( mrScFmlaCell.GetMatrixFlag() ) ) + { + case MM_FORMULA: + { + // origin of the matrix - find the used matrix range + SCCOL nMatWidth; + SCROW nMatHeight; + mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight ); + DBG_ASSERT( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" ); + ScRange aMatScRange( aScPos ); + ScAddress& rMatEnd = aMatScRange.aEnd; + rMatEnd.IncCol( static_cast< SCsCOL >( nMatWidth - 1 ) ); + rMatEnd.IncRow( static_cast< SCsROW >( nMatHeight - 1 ) ); + // reduce to valid range (range keeps valid, because start position IS valid) + rRoot.GetAddressConverter().ValidateRange( aMatScRange, true ); + // create the ARRAY record + mxAddRec = rArrayBfr.CreateArray( rScTokArr, aMatScRange ); + } + break; + case MM_REFERENCE: + { + // other formula cell covered by a matrix - find the ARRAY record + mxAddRec = rArrayBfr.FindArray( rScTokArr ); + // should always be found, if Calc document is not broken + DBG_ASSERT( mxAddRec.is(), "XclExpFormulaCell::XclExpFormulaCell - no matrix found" ); + } + break; + default:; + } + + // no matrix found - try to create shared formula + if( !mxAddRec ) + mxAddRec = rShrfmlaBfr.CreateOrExtendShrfmla( rScTokArr, aScPos ); + + // no shared formula found - create a simple cell formula + if( !mxAddRec ) + mxTokArr = rRoot.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL, rScTokArr, &aScPos ); +} + +void XclExpFormulaCell::Save( XclExpStream& rStrm ) +{ + // create token array for FORMULA cells with additional record + if( mxAddRec.is() ) + mxTokArr = mxAddRec->CreateCellTokenArray( rStrm.GetRoot() ); + + // FORMULA record itself + DBG_ASSERT( mxTokArr.is(), "XclExpFormulaCell::Save - missing token array" ); + if( !mxTokArr ) + mxTokArr = rStrm.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA ); + SetContSize( 16 + mxTokArr->GetSize() ); + XclExpSingleCellBase::Save( rStrm ); + + // additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record + if( mxAddRec.is() && mxAddRec->IsBasePos( GetXclCol(), GetXclRow() ) ) + mxAddRec->Save( rStrm ); + + // STRING record for string result + if( mxStringRec.is() ) + mxStringRec->Save( rStrm ); +} + +static const char* lcl_GetErrorString( USHORT nScErrCode ) +{ + sal_uInt8 nXclErrCode = XclTools::GetXclErrorCode( nScErrCode ); + switch( nXclErrCode ) + { + case EXC_ERR_NULL: return "#NULL!"; + case EXC_ERR_DIV0: return "#DIV/0!"; + case EXC_ERR_VALUE: return "#VALUE!"; + case EXC_ERR_REF: return "#REF!"; + case EXC_ERR_NAME: return "#NAME?"; + case EXC_ERR_NUM: return "#NUM!"; + case EXC_ERR_NA: + default: return "#N/A"; + } +} + +static void lcl_GetFormulaInfo( ScFormulaCell& rCell, const char** pType, OUString& rValue) +{ + switch( rCell.GetFormatType() ) + { + case NUMBERFORMAT_NUMBER: + { + // either value or error code + USHORT nScErrCode = rCell.GetErrCode(); + if( nScErrCode ) + { + *pType = "e"; + rValue = XclXmlUtils::ToOUString( lcl_GetErrorString( nScErrCode ) ); + } + else + { + *pType = "n"; + rValue = OUString::valueOf( rCell.GetValue() ); + } + } + break; + + case NUMBERFORMAT_TEXT: + { + *pType = "str"; + String aResult; + rCell.GetString( aResult ); + rValue = XclXmlUtils::ToOUString( aResult ); + } + break; + + case NUMBERFORMAT_LOGICAL: + { + *pType = "b"; + rValue = XclXmlUtils::ToOUString( rCell.GetValue() == 0.0 ? "0" : "1" ); + } + break; + + default: + { + *pType = "inlineStr"; + String aResult; + rCell.GetString( aResult ); + rValue = XclXmlUtils::ToOUString( aResult ); + } + break; + } +} + +void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm ) +{ + const char* sType = NULL; + OUString sValue; + + lcl_GetFormulaInfo( mrScFmlaCell, &sType, sValue ); + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + rWorksheet->startElement( XML_c, + XML_r, XclXmlUtils::ToOString( GetXclPos() ).getStr(), + XML_s, lcl_GetStyleId( rStrm, *this ).getStr(), + XML_t, sType, + // OOXTODO: XML_cm, XML_vm, XML_ph + FSEND ); + + rWorksheet->startElement( XML_f, + // OOXTODO: XML_t, ST_CellFormulaType + XML_aca, XclXmlUtils::ToPsz( mxTokArr->IsVolatile() || (mxAddRec.is() && mxAddRec->IsVolatile()) ), + // OOXTODO: XML_ref, ST_Ref + // OOXTODO: XML_dt2D, bool + // OOXTODO: XML_dtr, bool + // OOXTODO: XML_del1, bool + // OOXTODO: XML_del2, bool + // OOXTODO: XML_r1, ST_CellRef + // OOXTODO: XML_r2, ST_CellRef + // OOXTODO: XML_ca, bool + // OOXTODO: XML_si, uint + // OOXTODO: XML_bx bool + FSEND ); + rWorksheet->writeEscaped( XclXmlUtils::ToOUString( *mrScFmlaCell.GetDocument(), mrScFmlaCell.aPos, mrScFmlaCell.GetCode() ) ); + rWorksheet->endElement( XML_f ); + if( strcmp( sType, "inlineStr" ) == 0 ) + { + rWorksheet->startElement( XML_is, FSEND ); + rWorksheet->startElement( XML_t, FSEND ); + rWorksheet->writeEscaped( sValue ); + rWorksheet->endElement( XML_t ); + rWorksheet->endElement( XML_is ); + } + else + { + rWorksheet->startElement( XML_v, FSEND ); + rWorksheet->writeEscaped( sValue ); + rWorksheet->endElement( XML_v ); + } + rWorksheet->endElement( XML_c ); +} + +void XclExpFormulaCell::WriteContents( XclExpStream& rStrm ) +{ + // result of the formula + switch( mrScFmlaCell.GetFormatType() ) + { + case NUMBERFORMAT_NUMBER: + { + // either value or error code + USHORT nScErrCode = mrScFmlaCell.GetErrCode(); + if( nScErrCode ) + rStrm << EXC_FORMULA_RES_ERROR << sal_uInt8( 0 ) + << XclTools::GetXclErrorCode( nScErrCode ) + << sal_uInt8( 0 ) << sal_uInt16( 0 ) + << sal_uInt16( 0xFFFF ); + else + rStrm << mrScFmlaCell.GetValue(); + } + break; + + case NUMBERFORMAT_TEXT: + { + String aResult; + mrScFmlaCell.GetString( aResult ); + if( aResult.Len() || (rStrm.GetRoot().GetBiff() <= EXC_BIFF5) ) + { + rStrm << EXC_FORMULA_RES_STRING; + mxStringRec.reset( new XclExpStringRec( rStrm.GetRoot(), aResult ) ); + } + else + rStrm << EXC_FORMULA_RES_EMPTY; // BIFF8 only + rStrm << sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF ); + } + break; + + case NUMBERFORMAT_LOGICAL: + { + sal_uInt8 nXclValue = (mrScFmlaCell.GetValue() == 0.0) ? 0 : 1; + rStrm << EXC_FORMULA_RES_BOOL << sal_uInt8( 0 ) + << nXclValue << sal_uInt8( 0 ) << sal_uInt16( 0 ) + << sal_uInt16( 0xFFFF ); + } + break; + + default: + rStrm << mrScFmlaCell.GetValue(); + } + + // flags and formula token array + sal_uInt16 nFlags = EXC_FORMULA_DEFAULTFLAGS; + ::set_flag( nFlags, EXC_FORMULA_RECALC_ALWAYS, mxTokArr->IsVolatile() || (mxAddRec.is() && mxAddRec->IsVolatile()) ); + ::set_flag( nFlags, EXC_FORMULA_SHARED, mxAddRec.is() && (mxAddRec->GetRecId() == EXC_ID_SHRFMLA) ); + rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr; +} + +// Multiple cell records ====================================================== + +XclExpMultiCellBase::XclExpMultiCellBase( + sal_uInt16 nRecId, sal_uInt16 nMulRecId, sal_Size nContSize, const XclAddress& rXclPos ) : + XclExpCellBase( nRecId, 0, rXclPos ), + mnMulRecId( nMulRecId ), + mnContSize( nContSize ) +{ +} + +sal_uInt16 XclExpMultiCellBase::GetLastXclCol() const +{ + return GetXclCol() + GetCellCount() - 1; +} + +sal_uInt32 XclExpMultiCellBase::GetFirstXFId() const +{ + return maXFIds.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds.front().mnXFId; +} + +bool XclExpMultiCellBase::IsEmpty() const +{ + return maXFIds.empty(); +} + +void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot& rRoot ) +{ + for( XclExpMultiXFIdDeq::iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt ) + aIt->ConvertXFIndex( rRoot ); +} + +void XclExpMultiCellBase::Save( XclExpStream& rStrm ) +{ + DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 ); + + XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end(); + XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin(); + XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg; + sal_uInt16 nBegXclCol = GetXclCol(); + sal_uInt16 nEndXclCol = nBegXclCol; + + while( aRangeEnd != aEnd ) + { + // find begin of next used XF range + aRangeBeg = aRangeEnd; + nBegXclCol = nEndXclCol; + while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) ) + { + nBegXclCol = nBegXclCol + aRangeBeg->mnCount; + ++aRangeBeg; + } + // find end of next used XF range + aRangeEnd = aRangeBeg; + nEndXclCol = nBegXclCol; + while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) ) + { + nEndXclCol = nEndXclCol + aRangeEnd->mnCount; + ++aRangeEnd; + } + + // export this range as a record + if( aRangeBeg != aRangeEnd ) + { + sal_uInt16 nCount = nEndXclCol - nBegXclCol; + bool bIsMulti = nCount > 1; + sal_Size nTotalSize = GetRecSize() + (2 + mnContSize) * nCount; + if( bIsMulti ) nTotalSize += 2; + + rStrm.StartRecord( bIsMulti ? mnMulRecId : GetRecId(), nTotalSize ); + rStrm << GetXclRow() << nBegXclCol; + + sal_uInt16 nRelCol = nBegXclCol - GetXclCol(); + for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt ) + { + for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx ) + { + rStrm << aIt->mnXFIndex; + WriteContents( rStrm, nRelCol ); + ++nRelCol; + } + } + if( bIsMulti ) + rStrm << static_cast< sal_uInt16 >( nEndXclCol - 1 ); + rStrm.EndRecord(); + } + } +} + +void XclExpMultiCellBase::SaveXml( XclExpXmlStream& rStrm ) +{ + XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end(); + XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin(); + XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg; + sal_uInt16 nBegXclCol = GetXclCol(); + sal_uInt16 nEndXclCol = nBegXclCol; + + while( aRangeEnd != aEnd ) + { + // find begin of next used XF range + aRangeBeg = aRangeEnd; + nBegXclCol = nEndXclCol; + while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) ) + { + nBegXclCol = nBegXclCol + aRangeBeg->mnCount; + ++aRangeBeg; + } + // find end of next used XF range + aRangeEnd = aRangeBeg; + nEndXclCol = nBegXclCol; + while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) ) + { + nEndXclCol = nEndXclCol + aRangeEnd->mnCount; + ++aRangeEnd; + } + + // export this range as a record + if( aRangeBeg != aRangeEnd ) + { + sal_uInt16 nRelColIdx = nBegXclCol - GetXclCol(); + sal_Int32 nRelCol = 0; + for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt ) + { + for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx ) + { + WriteXmlContents( + rStrm, + XclAddress( static_cast<sal_uInt16>(nBegXclCol + nRelCol), GetXclRow() ), + aIt->mnXFIndex, + nRelColIdx ); + ++nRelCol; + ++nRelColIdx; + } + } + } + } +} + +sal_uInt16 XclExpMultiCellBase::GetCellCount() const +{ + sal_uInt16 nCount = 0; + for( XclExpMultiXFIdDeq::const_iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt ) + nCount = nCount + aIt->mnCount; + return nCount; +} + +void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId& rXFId ) +{ + if( maXFIds.empty() || (maXFIds.back().mnXFId != rXFId.mnXFId) ) + maXFIds.push_back( rXFId ); + else + maXFIds.back().mnCount = maXFIds.back().mnCount + rXFId.mnCount; +} + +void XclExpMultiCellBase::AppendXFId( const XclExpRoot& rRoot, + const ScPatternAttr* pPattern, sal_uInt16 nScript, sal_uInt32 nForcedXFId, sal_uInt16 nCount ) +{ + sal_uInt32 nXFId = (nForcedXFId == EXC_XFID_NOTFOUND) ? + rRoot.GetXFBuffer().Insert( pPattern, nScript ) : nForcedXFId; + AppendXFId( XclExpMultiXFId( nXFId, nCount ) ); +} + +bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase& rCell ) +{ + if( GetLastXclCol() + 1 == rCell.GetXclCol() ) + { + maXFIds.insert( maXFIds.end(), rCell.maXFIds.begin(), rCell.maXFIds.end() ); + return true; + } + return false; +} + +void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const +{ + DBG_ASSERT( GetLastXclCol() < rXFIndexes.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" ); + ScfUInt16Vec::iterator aDestIt = rXFIndexes.begin() + GetXclCol(); + for( XclExpMultiXFIdDeq::const_iterator aIt = maXFIds.begin(), aEnd = maXFIds.end(); aIt != aEnd; ++aIt ) + { + ::std::fill( aDestIt, aDestIt + aIt->mnCount, aIt->mnXFIndex ); + aDestIt += aIt->mnCount; + } +} + +void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes ) +{ + // save last column before calling maXFIds.clear() + sal_uInt16 nLastXclCol = GetLastXclCol(); + DBG_ASSERT( nLastXclCol < rXFIndexes.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" ); + + // build new XF index vector, containing passed XF indexes + maXFIds.clear(); + XclExpMultiXFId aXFId( 0 ); + for( ScfUInt16Vec::const_iterator aIt = rXFIndexes.begin() + GetXclCol(), aEnd = rXFIndexes.begin() + nLastXclCol + 1; aIt != aEnd; ++aIt ) + { + // AppendXFId() tests XclExpXFIndex::mnXFId, set it too + aXFId.mnXFId = aXFId.mnXFIndex = *aIt; + AppendXFId( aXFId ); + } + + // remove leading and trailing unused XF indexes + if( !maXFIds.empty() && (maXFIds.front().mnXFIndex == EXC_XF_NOTFOUND) ) + { + SetXclCol( GetXclCol() + maXFIds.front().mnCount ); + maXFIds.pop_front(); + } + if( !maXFIds.empty() && (maXFIds.back().mnXFIndex == EXC_XF_NOTFOUND) ) + maXFIds.pop_back(); + + // The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND. +} + +// ---------------------------------------------------------------------------- + +IMPL_FIXEDMEMPOOL_NEWDEL( XclExpBlankCell, 256, 256 ) + +XclExpBlankCell::XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId ) : + XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos ) +{ + DBG_ASSERT( rXFId.mnCount > 0, "XclExpBlankCell::XclExpBlankCell - invalid count" ); + AppendXFId( rXFId ); +} + +XclExpBlankCell::XclExpBlankCell( + const XclExpRoot& rRoot, const XclAddress& rXclPos, sal_uInt16 nLastXclCol, + const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId ) : + XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos ) +{ + DBG_ASSERT( rXclPos.mnCol <= nLastXclCol, "XclExpBlankCell::XclExpBlankCell - invalid column range" ); + // #i46627# use default script type instead of ApiScriptType::WEAK + AppendXFId( rRoot, pPattern, rRoot.GetDefApiScript(), nForcedXFId, nLastXclCol - rXclPos.mnCol + 1 ); +} + +bool XclExpBlankCell::TryMerge( const XclExpCellBase& rCell ) +{ + const XclExpBlankCell* pBlankCell = dynamic_cast< const XclExpBlankCell* >( &rCell ); + return pBlankCell && TryMergeXFIds( *pBlankCell ); +} + +void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const +{ + GetXFIndexes( rXFIndexes ); +} + +void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes ) +{ + RemoveUnusedXFIndexes( rXFIndexes ); +} + +void XclExpBlankCell::WriteContents( XclExpStream& /*rStrm*/, sal_uInt16 /*nRelCol*/ ) +{ +} + +void XclExpBlankCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 /* nRelCol */ ) +{ + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + rWorksheet->singleElement( XML_c, + XML_r, XclXmlUtils::ToOString( rAddress ).getStr(), + XML_s, lcl_GetStyleId( rStrm, nXFId ).getStr(), + FSEND ); +} + +// ---------------------------------------------------------------------------- + +IMPL_FIXEDMEMPOOL_NEWDEL( XclExpRkCell, 256, 256 ) + +XclExpRkCell::XclExpRkCell( + const XclExpRoot& rRoot, const XclAddress& rXclPos, + const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_Int32 nRkValue ) : + XclExpMultiCellBase( EXC_ID_RK, EXC_ID_MULRK, 4, rXclPos ) +{ + // #i41210# always use latin script for number cells - may look wrong for special number formats... + AppendXFId( rRoot, pPattern, ApiScriptType::LATIN, nForcedXFId ); + maRkValues.push_back( nRkValue ); +} + +bool XclExpRkCell::TryMerge( const XclExpCellBase& rCell ) +{ + const XclExpRkCell* pRkCell = dynamic_cast< const XclExpRkCell* >( &rCell ); + if( pRkCell && TryMergeXFIds( *pRkCell ) ) + { + maRkValues.insert( maRkValues.end(), pRkCell->maRkValues.begin(), pRkCell->maRkValues.end() ); + return true; + } + return false; +} + +void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol ) +{ + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + rWorksheet->startElement( XML_c, + XML_r, XclXmlUtils::ToOString( rAddress ).getStr(), + XML_s, lcl_GetStyleId( rStrm, nXFId ).getStr(), + XML_t, "n", + // OOXTODO: XML_cm, XML_vm, XML_ph + FSEND ); + rWorksheet->startElement( XML_v, FSEND ); + rWorksheet->write( XclTools::GetDoubleFromRK( maRkValues[ nRelCol ] ) ); + rWorksheet->endElement( XML_v ); + rWorksheet->endElement( XML_c ); +} + +void XclExpRkCell::WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol ) +{ + DBG_ASSERT( nRelCol < maRkValues.size(), "XclExpRkCell::WriteContents - overflow error" ); + rStrm << maRkValues[ nRelCol ]; +} + +// ============================================================================ +// Rows and Columns +// ============================================================================ + +XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows ) : + mpScOLArray( 0 ), + maLevelInfos( SC_OL_MAXDEPTH ), + mnCurrLevel( 0 ), + mbCurrCollapse( false ) +{ + if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) ) + mpScOLArray = bRows ? pOutlineTable->GetRowArray() : pOutlineTable->GetColArray(); + + if( mpScOLArray ) + for( USHORT nLevel = 0; nLevel < SC_OL_MAXDEPTH; ++nLevel ) + if( ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nLevel, 0 ) ) + maLevelInfos[ nLevel ].mnScEndPos = pEntry->GetEnd(); +} + +void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos ) +{ + if( mpScOLArray ) + { + // find open level index for passed position + USHORT nNewOpenScLevel = 0; // new open level (0-based Calc index) + sal_uInt8 nNewLevel = 0; // new open level (1-based Excel index) + + if( mpScOLArray->FindTouchedLevel( nScPos, nScPos, nNewOpenScLevel ) ) + nNewLevel = static_cast< sal_uInt8 >( nNewOpenScLevel + 1 ); + // else nNewLevel keeps 0 to show that there are no groups + + mbCurrCollapse = false; + if( nNewLevel >= mnCurrLevel ) + { + // new level(s) opened, or no level closed - update all level infos + for( USHORT nScLevel = 0; nScLevel <= nNewOpenScLevel; ++nScLevel ) + { + /* In each level: check if a new group is started (there may be + neighbored groups without gap - therefore check ALL levels). */ + if( maLevelInfos[ nScLevel ].mnScEndPos < nScPos ) + { + if( ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nScLevel, nScPos ) ) + { + maLevelInfos[ nScLevel ].mnScEndPos = pEntry->GetEnd(); + maLevelInfos[ nScLevel ].mbHidden = pEntry->IsHidden(); + } + } + } + } + else + { + // level(s) closed - check if any of the closed levels are collapsed + // Calc uses 0-based level indexes + USHORT nOldOpenScLevel = mnCurrLevel - 1; + for( USHORT nScLevel = nNewOpenScLevel + 1; !mbCurrCollapse && (nScLevel <= nOldOpenScLevel); ++nScLevel ) + mbCurrCollapse = maLevelInfos[ nScLevel ].mbHidden; + } + + // cache new opened level + mnCurrLevel = nNewLevel; + } +} + +// ---------------------------------------------------------------------------- + +XclExpGuts::XclExpGuts( const XclExpRoot& rRoot ) : + XclExpRecord( EXC_ID_GUTS, 8 ), + mnColLevels( 0 ), + mnColWidth( 0 ), + mnRowLevels( 0 ), + mnRowWidth( 0 ) +{ + if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) ) + { + // column outline groups + if( const ScOutlineArray* pColArray = pOutlineTable->GetColArray() ) + mnColLevels = ulimit_cast< sal_uInt16 >( pColArray->GetDepth(), EXC_OUTLINE_MAX ); + if( mnColLevels ) + { + ++mnColLevels; + mnColWidth = 12 * mnColLevels + 5; + } + + // row outline groups + if( const ScOutlineArray* pRowArray = pOutlineTable->GetRowArray() ) + mnRowLevels = ulimit_cast< sal_uInt16 >( pRowArray->GetDepth(), EXC_OUTLINE_MAX ); + if( mnRowLevels ) + { + ++mnRowLevels; + mnRowWidth = 12 * mnRowLevels + 5; + } + } +} + +void XclExpGuts::WriteBody( XclExpStream& rStrm ) +{ + rStrm << mnRowWidth << mnColWidth << mnRowLevels << mnColLevels; +} + +// ---------------------------------------------------------------------------- + +XclExpDimensions::XclExpDimensions( const XclExpRoot& rRoot ) : + mnFirstUsedXclRow( 0 ), + mnFirstFreeXclRow( 0 ), + mnFirstUsedXclCol( 0 ), + mnFirstFreeXclCol( 0 ) +{ + switch( rRoot.GetBiff() ) + { + case EXC_BIFF2: SetRecHeader( EXC_ID2_DIMENSIONS, 8 ); break; + case EXC_BIFF3: + case EXC_BIFF4: + case EXC_BIFF5: SetRecHeader( EXC_ID3_DIMENSIONS, 10 ); break; + case EXC_BIFF8: SetRecHeader( EXC_ID3_DIMENSIONS, 14 ); break; + default: DBG_ERROR_BIFF(); + } +} + +void XclExpDimensions::SetDimensions( + sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow, + sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow ) +{ + mnFirstUsedXclRow = nFirstUsedXclRow; + mnFirstFreeXclRow = nFirstFreeXclRow; + mnFirstUsedXclCol = nFirstUsedXclCol; + mnFirstFreeXclCol = nFirstFreeXclCol; +} + +void XclExpDimensions::SaveXml( XclExpXmlStream& rStrm ) +{ + ScRange aRange; + aRange.aStart.SetRow( (SCROW) mnFirstUsedXclRow ); + aRange.aStart.SetCol( (SCCOL) mnFirstUsedXclCol ); + + if( mnFirstFreeXclRow != mnFirstUsedXclRow && mnFirstFreeXclCol != mnFirstUsedXclCol ) + { + aRange.aEnd.SetRow( (SCROW) (mnFirstFreeXclRow-1) ); + aRange.aEnd.SetCol( (SCCOL) (mnFirstFreeXclCol-1) ); + } + + rStrm.GetCurrentStream()->singleElement( XML_dimension, + XML_ref, XclXmlUtils::ToOString( aRange ).getStr(), + FSEND ); +} + +void XclExpDimensions::WriteBody( XclExpStream& rStrm ) +{ + XclBiff eBiff = rStrm.GetRoot().GetBiff(); + if( eBiff == EXC_BIFF8 ) + rStrm << mnFirstUsedXclRow << mnFirstFreeXclRow; + else + rStrm << static_cast< sal_uInt16 >( mnFirstUsedXclRow ) << static_cast< sal_uInt16 >( mnFirstFreeXclRow ); + rStrm << mnFirstUsedXclCol << mnFirstFreeXclCol; + if( eBiff >= EXC_BIFF3 ) + rStrm << sal_uInt16( 0 ); +} + +// ============================================================================ + +namespace { + +double lclGetCorrectedColWidth( const XclExpRoot& rRoot, sal_uInt16 nXclColWidth ) +{ + long nFontHt = rRoot.GetFontBuffer().GetAppFontData().mnHeight; + return nXclColWidth - XclTools::GetXclDefColWidthCorrection( nFontHt ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot& rRoot ) : + XclExpUInt16Record( EXC_ID_DEFCOLWIDTH, EXC_DEFCOLWIDTH_DEF ), + XclExpRoot( rRoot ) +{ +} + +bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth ) const +{ + double fNewColWidth = lclGetCorrectedColWidth( GetRoot(), nXclColWidth ); + // exactly matched, if difference is less than 1/16 of a character to the left or to the right + return Abs( static_cast< long >( GetValue() * 256.0 - fNewColWidth + 0.5 ) ) < 16; +} + +void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth ) +{ + double fNewColWidth = lclGetCorrectedColWidth( GetRoot(), nXclColWidth ); + SetValue( limit_cast< sal_uInt16 >( fNewColWidth / 256.0 + 0.5 ) ); +} + +// ---------------------------------------------------------------------------- + +XclExpColinfo::XclExpColinfo( const XclExpRoot& rRoot, + SCCOL nScCol, SCROW nLastScRow, XclExpColOutlineBuffer& rOutlineBfr ) : + XclExpRecord( EXC_ID_COLINFO, 12 ), + XclExpRoot( rRoot ), + mnWidth( 0 ), + mnFlags( 0 ), + mnFirstXclCol( static_cast< sal_uInt16 >( nScCol ) ), + mnLastXclCol( static_cast< sal_uInt16 >( nScCol ) ) +{ + ScDocument& rDoc = GetDoc(); + SCTAB nScTab = GetCurrScTab(); + + // column default format + maXFId.mnXFId = GetXFBuffer().Insert( + rDoc.GetMostUsedPattern( nScCol, 0, nLastScRow, nScTab ), GetDefApiScript() ); + + // column width + USHORT nScWidth = rDoc.GetColWidth( nScCol, nScTab ); + mnWidth = XclTools::GetXclColumnWidth( nScWidth, GetCharWidth() ); + + // column flags + ::set_flag( mnFlags, EXC_COLINFO_HIDDEN, rDoc.ColHidden(nScCol, nScTab) ); + + // outline data + rOutlineBfr.Update( nScCol ); + ::set_flag( mnFlags, EXC_COLINFO_COLLAPSED, rOutlineBfr.IsCollapsed() ); + ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 8, 3 ); +} + +sal_uInt16 XclExpColinfo::ConvertXFIndexes() +{ + maXFId.ConvertXFIndex( GetRoot() ); + return maXFId.mnXFIndex; +} + +bool XclExpColinfo::IsDefault( const XclExpDefcolwidth& rDefColWidth ) const +{ + return (maXFId.mnXFIndex == EXC_XF_DEFAULTCELL) && (mnFlags == 0) && rDefColWidth.IsDefWidth( mnWidth ); +} + +bool XclExpColinfo::TryMerge( const XclExpColinfo& rColInfo ) +{ + if( (maXFId.mnXFIndex == rColInfo.maXFId.mnXFIndex) && + (mnWidth == rColInfo.mnWidth) && + (mnFlags == rColInfo.mnFlags) && + (mnLastXclCol + 1 == rColInfo.mnFirstXclCol) ) + { + mnLastXclCol = rColInfo.mnLastXclCol; + return true; + } + return false; +} + +void XclExpColinfo::WriteBody( XclExpStream& rStrm ) +{ + // if last column is equal to last possible column, Excel adds one more + sal_uInt16 nLastXclCol = mnLastXclCol; + if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) ) + ++nLastXclCol; + + rStrm << mnFirstXclCol + << nLastXclCol + << mnWidth + << maXFId.mnXFIndex + << mnFlags + << sal_uInt16( 0 ); +} + +void XclExpColinfo::SaveXml( XclExpXmlStream& rStrm ) +{ + // if last column is equal to last possible column, Excel adds one more + sal_uInt16 nLastXclCol = mnLastXclCol; + if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) ) + ++nLastXclCol; + + rStrm.GetCurrentStream()->singleElement( XML_col, + // OOXTODO: XML_bestFit, + XML_collapsed, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_COLINFO_COLLAPSED ) ), + // OOXTODO: XML_customWidth, + XML_hidden, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_COLINFO_HIDDEN ) ), + XML_max, OString::valueOf( (sal_Int32) (nLastXclCol+1) ).getStr(), + XML_min, OString::valueOf( (sal_Int32) (mnFirstXclCol+1) ).getStr(), + // OOXTODO: XML_outlineLevel, + // OOXTODO: XML_phonetic, + XML_style, lcl_GetStyleId( rStrm, maXFId.mnXFIndex ).getStr(), + XML_width, OString::valueOf( (double) (mnWidth / 255.0) ).getStr(), + FSEND ); +} + +// ---------------------------------------------------------------------------- + +XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + maDefcolwidth( rRoot ), + maOutlineBfr( rRoot ) +{ +} + +void XclExpColinfoBuffer::Initialize( SCROW nLastScRow ) +{ + + for( sal_uInt16 nScCol = 0, nLastScCol = GetMaxPos().Col(); nScCol <= nLastScCol; ++nScCol ) + maColInfos.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol, nLastScRow, maOutlineBfr ) ); +} + +void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes ) +{ + rXFIndexes.clear(); + rXFIndexes.reserve( maColInfos.GetSize() ); + + size_t nPos, nSize; + + // do not cache the record list size, it may change in the loop + for( nPos = 0; nPos < maColInfos.GetSize(); ++nPos ) + { + XclExpColinfoRef xRec = maColInfos.GetRecord( nPos ); + xRec->ConvertXFIndexes(); + + // try to merge with previous record + if( nPos > 0 ) + { + XclExpColinfoRef xPrevRec = maColInfos.GetRecord( nPos - 1 ); + if( xPrevRec->TryMerge( *xRec ) ) + // adjust nPos to get the next COLINFO record at the same position + maColInfos.RemoveRecord( nPos-- ); + } + } + + // put XF indexes into passed vector, collect use count of all different widths + typedef ::std::map< sal_uInt16, sal_uInt16 > XclExpWidthMap; + XclExpWidthMap aWidthMap; + sal_uInt16 nMaxColCount = 0; + sal_uInt16 nMaxUsedWidth = 0; + for( nPos = 0, nSize = maColInfos.GetSize(); nPos < nSize; ++nPos ) + { + XclExpColinfoRef xRec = maColInfos.GetRecord( nPos ); + sal_uInt16 nColCount = xRec->GetColCount(); + + // add XF index to passed vector + rXFIndexes.resize( rXFIndexes.size() + nColCount, xRec->GetXFIndex() ); + + // collect use count of column width + sal_uInt16 nWidth = xRec->GetColWidth(); + sal_uInt16& rnMapCount = aWidthMap[ nWidth ]; + rnMapCount = rnMapCount + nColCount; + if( rnMapCount > nMaxColCount ) + { + nMaxColCount = rnMapCount; + nMaxUsedWidth = nWidth; + } + } + maDefcolwidth.SetDefWidth( nMaxUsedWidth ); + + // remove all default COLINFO records + nPos = 0; + while( nPos < maColInfos.GetSize() ) + { + XclExpColinfoRef xRec = maColInfos.GetRecord( nPos ); + if( xRec->IsDefault( maDefcolwidth ) ) + maColInfos.RemoveRecord( nPos ); + else + ++nPos; + } +} + +void XclExpColinfoBuffer::Save( XclExpStream& rStrm ) +{ + // DEFCOLWIDTH + maDefcolwidth.Save( rStrm ); + // COLINFO records + maColInfos.Save( rStrm ); +} + +void XclExpColinfoBuffer::SaveXml( XclExpXmlStream& rStrm ) +{ + if( maColInfos.IsEmpty() ) + return; + + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + rWorksheet->startElement( XML_cols, + FSEND ); + maColInfos.SaveXml( rStrm ); + rWorksheet->endElement( XML_cols ); +} + +// ============================================================================ + +XclExpDefaultRowData::XclExpDefaultRowData() : + mnFlags( EXC_DEFROW_DEFAULTFLAGS ), + mnHeight( EXC_DEFROW_DEFAULTHEIGHT ) +{ +} + +XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow& rRow ) : + mnFlags( EXC_DEFROW_DEFAULTFLAGS ), + mnHeight( rRow.GetHeight() ) +{ + ::set_flag( mnFlags, EXC_DEFROW_HIDDEN, rRow.IsHidden() ); + ::set_flag( mnFlags, EXC_DEFROW_UNSYNCED, rRow.IsUnsynced() ); +} + +bool operator<( const XclExpDefaultRowData& rLeft, const XclExpDefaultRowData& rRight ) +{ + return (rLeft.mnHeight < rRight.mnHeight) || + ((rLeft.mnHeight == rRight.mnHeight) && (rLeft.mnFlags < rRight.mnFlags)); +} + +// ---------------------------------------------------------------------------- + +XclExpDefrowheight::XclExpDefrowheight() : + XclExpRecord( EXC_ID3_DEFROWHEIGHT, 4 ) +{ +} + +void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData& rDefData ) +{ + maDefData = rDefData; +} + +void XclExpDefrowheight::WriteBody( XclExpStream& rStrm ) +{ + DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 ); + rStrm << maDefData.mnFlags << maDefData.mnHeight; +} + +// ---------------------------------------------------------------------------- + +XclExpRow::XclExpRow( const XclExpRoot& rRoot, sal_uInt16 nXclRow, + XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty ) : + XclExpRecord( EXC_ID3_ROW, 16 ), + XclExpRoot( rRoot ), + mnXclRow( nXclRow ), + mnHeight( 0 ), + mnFlags( EXC_ROW_DEFAULTFLAGS ), + mnXFIndex( EXC_XF_DEFAULTCELL ), + mnOutlineLevel( 0 ), + mbAlwaysEmpty( bAlwaysEmpty ), + mbEnabled( true ) +{ + SCTAB nScTab = GetCurrScTab(); + SCROW nScRow = static_cast< SCROW >( mnXclRow ); + + // *** Row flags *** ------------------------------------------------------ + + BYTE nRowFlags = GetDoc().GetRowFlags( nScRow, nScTab ); + bool bUserHeight = ::get_flag< BYTE >( nRowFlags, CR_MANUALSIZE ); + bool bHidden = GetDoc().RowHidden(nScRow, nScTab); + ::set_flag( mnFlags, EXC_ROW_UNSYNCED, bUserHeight ); + ::set_flag( mnFlags, EXC_ROW_HIDDEN, bHidden ); + + // *** Row height *** ----------------------------------------------------- + + if (bUserHeight) + mnHeight = GetDoc().GetRowHeight(nScRow, nScTab, false); + else + mnHeight = EXC_ROW_DEFAULTHEIGHT; + + // #76250# not usable in Applix +// ::set_flag( mnHeight, EXC_ROW_FLAGDEFHEIGHT, !bUserHeight ); + + // *** Outline data *** --------------------------------------------------- + + rOutlineBfr.Update( nScRow ); + ::set_flag( mnFlags, EXC_ROW_COLLAPSED, rOutlineBfr.IsCollapsed() ); + ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 0, 3 ); + mnOutlineLevel = rOutlineBfr.GetLevel(); + + // *** Progress bar *** --------------------------------------------------- + + XclExpProgressBar& rProgress = GetProgressBar(); + rProgress.IncRowRecordCount(); + rProgress.Progress(); +} + +void XclExpRow::AppendCell( XclExpCellRef xCell, bool bIsMergedBase ) +{ + DBG_ASSERT( !mbAlwaysEmpty, "XclExpRow::AppendCell - row is marked to be always empty" ); + // try to merge with last existing cell + InsertCell( xCell, maCellList.GetSize(), bIsMergedBase ); +} + +void XclExpRow::Finalize( const ScfUInt16Vec& rColXFIndexes ) +{ + size_t nPos, nSize; + + // *** Convert XF identifiers *** ----------------------------------------- + + // additionally collect the blank XF indexes + size_t nColCount = GetMaxPos().Col() + 1; + DBG_ASSERT( rColXFIndexes.size() == nColCount, "XclExpRow::Finalize - wrong column XF index count" ); + + ScfUInt16Vec aXFIndexes( nColCount, EXC_XF_NOTFOUND ); + for( nPos = 0, nSize = maCellList.GetSize(); nPos < nSize; ++nPos ) + { + XclExpCellRef xCell = maCellList.GetRecord( nPos ); + xCell->ConvertXFIndexes( GetRoot() ); + xCell->GetBlankXFIndexes( aXFIndexes ); + } + + // *** Fill gaps with BLANK/MULBLANK cell records *** --------------------- + + /* This is needed because nonexistant cells in Calc are not formatted at all, + but in Excel they would have the column default format. Blank cells that + are equal to the respective column default are removed later in this function. */ + if( !mbAlwaysEmpty ) + { + // XF identifier representing default cell XF + XclExpMultiXFId aXFId( XclExpXFBuffer::GetDefCellXFId() ); + aXFId.ConvertXFIndex( GetRoot() ); + + nPos = 0; + while( nPos <= maCellList.GetSize() ) // don't cache list size, may change in the loop + { + // get column index that follows previous cell + sal_uInt16 nFirstFreeXclCol = (nPos > 0) ? (maCellList.GetRecord( nPos - 1 )->GetLastXclCol() + 1) : 0; + // get own column index + sal_uInt16 nNextUsedXclCol = (nPos < maCellList.GetSize()) ? maCellList.GetRecord( nPos )->GetXclCol() : (GetMaxPos().Col() + 1); + + // is there a gap? + if( nFirstFreeXclCol < nNextUsedXclCol ) + { + aXFId.mnCount = nNextUsedXclCol - nFirstFreeXclCol; + XclExpCellRef xNewCell( new XclExpBlankCell( XclAddress( nFirstFreeXclCol, mnXclRow ), aXFId ) ); + // insert the cell, InsertCell() may merge it with existing BLANK records + InsertCell( xNewCell, nPos, false ); + // insert default XF indexes into aXFIndexes + ::std::fill( aXFIndexes.begin() + nFirstFreeXclCol, + aXFIndexes.begin() + nNextUsedXclCol, aXFId.mnXFIndex ); + // don't step forward with nPos, InsertCell() may remove records + } + else + ++nPos; + } + } + + // *** Find default row format *** ---------------------------------------- + + ScfUInt16Vec::iterator aCellBeg = aXFIndexes.begin(), aCellEnd = aXFIndexes.end(), aCellIt; + ScfUInt16Vec::const_iterator aColBeg = rColXFIndexes.begin(), aColIt; + + // find most used XF index in the row + typedef ::std::map< sal_uInt16, size_t > XclExpXFIndexMap; + XclExpXFIndexMap aIndexMap; + sal_uInt16 nRowXFIndex = EXC_XF_DEFAULTCELL; + size_t nMaxXFCount = 0; + for( aCellIt = aCellBeg; aCellIt != aCellEnd; ++aCellIt ) + { + if( *aCellIt != EXC_XF_NOTFOUND ) + { + size_t& rnCount = aIndexMap[ *aCellIt ]; + ++rnCount; + if( rnCount > nMaxXFCount ) + { + nRowXFIndex = *aCellIt; + nMaxXFCount = rnCount; + } + } + } + + // decide whether to use the row default XF index or column default XF indexes + bool bUseColDefXFs = nRowXFIndex == EXC_XF_DEFAULTCELL; + if( !bUseColDefXFs ) + { + // count needed XF indexes for blank cells with and without row default XF index + size_t nXFCountWithRowDefXF = 0; + size_t nXFCountWithoutRowDefXF = 0; + for( aCellIt = aCellBeg, aColIt = aColBeg; aCellIt != aCellEnd; ++aCellIt, ++aColIt ) + { + sal_uInt16 nXFIndex = *aCellIt; + if( nXFIndex != EXC_XF_NOTFOUND ) + { + if( nXFIndex != nRowXFIndex ) + ++nXFCountWithRowDefXF; // with row default XF index + if( nXFIndex != *aColIt ) + ++nXFCountWithoutRowDefXF; // without row default XF index + } + } + + // use column XF indexes if this would cause less or equal number of BLANK records + bUseColDefXFs = nXFCountWithoutRowDefXF <= nXFCountWithRowDefXF; + } + + // *** Remove unused BLANK cell records *** ------------------------------- + + if( bUseColDefXFs ) + { + // use column default XF indexes + // #i194#: remove cell XF indexes equal to column default XF indexes + for( aCellIt = aCellBeg, aColIt = aColBeg; aCellIt != aCellEnd; ++aCellIt, ++aColIt ) + if( *aCellIt == *aColIt ) + *aCellIt = EXC_XF_NOTFOUND; + } + else + { + // use row default XF index + mnXFIndex = nRowXFIndex; + ::set_flag( mnFlags, EXC_ROW_USEDEFXF ); + // #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index + for( aCellIt = aCellBeg; aCellIt != aCellEnd; ++aCellIt ) + if( *aCellIt == nRowXFIndex ) + *aCellIt = EXC_XF_NOTFOUND; + } + + // remove unused parts of BLANK/MULBLANK cell records + nPos = 0; + while( nPos < maCellList.GetSize() ) // do not cache list size, may change in the loop + { + XclExpCellRef xCell = maCellList.GetRecord( nPos ); + xCell->RemoveUnusedBlankCells( aXFIndexes ); + if( xCell->IsEmpty() ) + maCellList.RemoveRecord( nPos ); + else + ++nPos; + } + + // progress bar includes disabled rows + GetProgressBar().Progress(); +} + +sal_uInt16 XclExpRow::GetFirstUsedXclCol() const +{ + return maCellList.IsEmpty() ? 0 : maCellList.GetFirstRecord()->GetXclCol(); +} + +sal_uInt16 XclExpRow::GetFirstFreeXclCol() const +{ + return maCellList.IsEmpty() ? 0 : (maCellList.GetLastRecord()->GetLastXclCol() + 1); +} + +bool XclExpRow::IsDefaultable() const +{ + const sal_uInt16 nAllowedFlags = EXC_ROW_DEFAULTFLAGS | EXC_ROW_HIDDEN | EXC_ROW_UNSYNCED; + return !::get_flag( mnFlags, static_cast< sal_uInt16 >( ~nAllowedFlags ) ) && IsEmpty(); +} + +void XclExpRow::DisableIfDefault( const XclExpDefaultRowData& rDefRowData ) +{ + mbEnabled = !IsDefaultable() || + (mnHeight != rDefRowData.mnHeight) || + (IsHidden() != rDefRowData.IsHidden()) || + (IsUnsynced() != rDefRowData.IsUnsynced()); +} + +void XclExpRow::WriteCellList( XclExpStream& rStrm ) +{ + DBG_ASSERT( mbEnabled || maCellList.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" ); + maCellList.Save( rStrm ); +} + +void XclExpRow::Save( XclExpStream& rStrm ) +{ + if( mbEnabled ) + XclExpRecord::Save( rStrm ); +} + +void XclExpRow::InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase ) +{ + DBG_ASSERT( xCell.is(), "XclExpRow::InsertCell - missing cell" ); + + /* #109751# If we have a multi-line text in a merged cell, and the resulting + row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED + flag to be true to ensure Excel works correctly. */ + if( bIsMergedBase && xCell->IsMultiLineText() ) + ::set_flag( mnFlags, EXC_ROW_UNSYNCED ); + + // try to merge with previous cell, insert the new cell if not successful + XclExpCellRef xPrevCell = maCellList.GetRecord( nPos - 1 ); + if( xPrevCell.is() && xPrevCell->TryMerge( *xCell ) ) + xCell = xPrevCell; + else + maCellList.InsertRecord( xCell, nPos++ ); + // nPos points now to following cell + + // try to merge with following cell, remove it if successful + XclExpCellRef xNextCell = maCellList.GetRecord( nPos ); + if( xNextCell.is() && xCell->TryMerge( *xNextCell ) ) + maCellList.RemoveRecord( nPos ); +} + +void XclExpRow::WriteBody( XclExpStream& rStrm ) +{ + rStrm << mnXclRow + << GetFirstUsedXclCol() + << GetFirstFreeXclCol() + << mnHeight + << sal_uInt32( 0 ) + << mnFlags + << mnXFIndex; +} + +void XclExpRow::SaveXml( XclExpXmlStream& rStrm ) +{ + if( !mbEnabled ) + return; + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + bool haveFormat = ::get_flag( mnFlags, EXC_ROW_USEDEFXF ); + rWorksheet->startElement( XML_row, + XML_r, OString::valueOf( (sal_Int32) (mnXclRow+1) ).getStr(), + // OOXTODO: XML_spans, optional + XML_s, haveFormat ? lcl_GetStyleId( rStrm, mnXFIndex ).getStr() : NULL, + XML_customFormat, XclXmlUtils::ToPsz( haveFormat ), + XML_ht, OString::valueOf( (double) mnHeight / 20.0 ).getStr(), + XML_hidden, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_HIDDEN ) ), + XML_customHeight, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_UNSYNCED ) ), + XML_outlineLevel, OString::valueOf( (sal_Int32) mnOutlineLevel ).getStr(), + XML_collapsed, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_ROW_COLLAPSED ) ), + // OOXTODO: XML_thickTop, bool + // OOXTODO: XML_thickBot, bool + // OOXTODO: XML_ph, bool + FSEND ); + // OOXTODO: XML_extLst + maCellList.SaveXml( rStrm ); + rWorksheet->endElement( XML_row ); +} + +// ---------------------------------------------------------------------------- + +XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + maOutlineBfr( rRoot ), + maDimensions( rRoot ), + mpLastUsedRow( 0 ), + mnLastUsedXclRow( 0 ) +{ +} + +void XclExpRowBuffer::AppendCell( XclExpCellRef xCell, bool bIsMergedBase ) +{ + DBG_ASSERT( xCell.is(), "XclExpRowBuffer::AppendCell - missing cell" ); + GetOrCreateRow( xCell->GetXclRow(), false ).AppendCell( xCell, bIsMergedBase ); +} + +void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow ) +{ + if( nFirstFreeScRow > 0 ) + GetOrCreateRow( static_cast< sal_uInt16 >( nFirstFreeScRow - 1 ), true ); +} + +void XclExpRowBuffer::Finalize( XclExpDefaultRowData& rDefRowData, const ScfUInt16Vec& rColXFIndexes ) +{ + size_t nPos, nSize; + + // *** Finalize all rows *** ---------------------------------------------- + + GetProgressBar().ActivateFinalRowsSegment(); + + // unused blank cell records will be removed + for( nPos = 0, nSize = maRowList.GetSize(); nPos < nSize; ++nPos ) + maRowList.GetRecord( nPos )->Finalize( rColXFIndexes ); + + // *** Default row format *** --------------------------------------------- + + typedef ::std::map< XclExpDefaultRowData, size_t > XclExpDefRowDataMap; + XclExpDefRowDataMap aDefRowMap; + + // find default row format for rows beyond used area + sal_uInt32 nDefaultXclRow = maRowList.IsEmpty() ? 0 : (maRowList.GetLastRecord()->GetXclRow() + 1); + XclExpDefaultRowData aMaxDefData; + size_t nMaxDefCount = 0; + /* #i30411# Files saved with SO7/OOo1.x with nonstandard default column + formatting cause big Excel files, because all rows from row 1 to row + 32000 are exported. Now, if the used area goes exactly to row 32000, + ignore all rows >32000. + #i59220# Tolerance of +-128 rows for inserted/removed rows. */ + if( (nDefaultXclRow < 31872) || (nDefaultXclRow > 32128) ) + { + sal_uInt16 nLastXclRow = static_cast< sal_uInt16 >( GetMaxPos().Row() ); + if( nDefaultXclRow <= nLastXclRow ) + { + // create a dummy ROW record and fill aMaxDefData + XclExpRowOutlineBuffer aOutlineBfr( GetRoot() ); + XclExpRow aRow( GetRoot(), nLastXclRow, aOutlineBfr, true ); + aMaxDefData = XclExpDefaultRowData( aRow ); + aDefRowMap[ aMaxDefData ] = nMaxDefCount = + static_cast< size_t >( nLastXclRow - nDefaultXclRow + 1 ); + } + } + + // only look for default format in existing rows, if there are more than unused + nSize = maRowList.GetSize(); + if( nMaxDefCount < nSize ) + { + for( nPos = 0; nPos < nSize; ++nPos ) + { + XclExpRowRef xRow = maRowList.GetRecord( nPos ); + /* Collect formats of unused rows (rows without cells), which are able + to be defaulted (i.e. no explicit format or outline level). */ + if( xRow->IsDefaultable() ) + { + XclExpDefaultRowData aDefData( *xRow ); + size_t& rnDefCount = aDefRowMap[ aDefData ]; + ++rnDefCount; + if( rnDefCount > nMaxDefCount ) + { + nMaxDefCount = rnDefCount; + aMaxDefData = aDefData; + } + } + } + } + + // return the default row format to caller + rDefRowData = aMaxDefData; + + // *** Disable unused ROW records, find used area *** --------------------- + + sal_uInt16 nFirstUsedXclCol = SAL_MAX_UINT16; + sal_uInt16 nFirstFreeXclCol = 0; + sal_uInt32 nFirstUsedXclRow = SAL_MAX_UINT32; + sal_uInt32 nFirstFreeXclRow = 0; + + for( nPos = 0, nSize = maRowList.GetSize(); nPos < nSize; ++nPos ) + { + XclExpRowRef xRow = maRowList.GetRecord( nPos ); + + // disable unused rows + xRow->DisableIfDefault( aMaxDefData ); + + // find used column range + if( !xRow->IsEmpty() ) // empty rows return (0...0) as used range + { + nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, xRow->GetFirstUsedXclCol() ); + nFirstFreeXclCol = ::std::max( nFirstFreeXclCol, xRow->GetFirstFreeXclCol() ); + } + + // find used row range + if( xRow->IsEnabled() ) + { + sal_uInt16 nXclRow = xRow->GetXclRow(); + nFirstUsedXclRow = ::std::min< sal_uInt32 >( nFirstUsedXclRow, nXclRow ); + nFirstFreeXclRow = ::std::max< sal_uInt32 >( nFirstFreeXclRow, nXclRow + 1 ); + } + } + + // adjust start position, if there are no or only empty/disabled ROW records + nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, nFirstFreeXclCol ); + nFirstUsedXclRow = ::std::min( nFirstUsedXclRow, nFirstFreeXclRow ); + + // initialize the DIMENSIONS record + maDimensions.SetDimensions( + nFirstUsedXclCol, nFirstUsedXclRow, nFirstFreeXclCol, nFirstFreeXclRow ); +} + +void XclExpRowBuffer::Save( XclExpStream& rStrm ) +{ + // DIMENSIONS record + maDimensions.Save( rStrm ); + + // save in blocks of 32 rows, each block contains first all ROWs, then all cells + size_t nSize = maRowList.GetSize(); + size_t nBlockStart = 0; + sal_uInt16 nStartXclRow = (nSize == 0) ? 0 : maRowList.GetRecord( 0 )->GetXclRow(); + + while( nBlockStart < nSize ) + { + // find end of row block + size_t nBlockEnd = nBlockStart + 1; + while( (nBlockEnd < nSize) && (maRowList.GetRecord( nBlockEnd )->GetXclRow() - nStartXclRow < EXC_ROW_ROWBLOCKSIZE) ) + ++nBlockEnd; + + // write the ROW records + size_t nPos; + for( nPos = nBlockStart; nPos < nBlockEnd; ++nPos ) + maRowList.GetRecord( nPos )->Save( rStrm ); + + // write the cell records + for( nPos = nBlockStart; nPos < nBlockEnd; ++nPos ) + maRowList.GetRecord( nPos )->WriteCellList( rStrm ); + + nBlockStart = nBlockEnd; + nStartXclRow += EXC_ROW_ROWBLOCKSIZE; + } +} + +void XclExpRowBuffer::SaveXml( XclExpXmlStream& rStrm ) +{ + sal_Int32 nNonEmpty = 0; + + size_t nRows = maRowList.GetSize(); + for( size_t i = 0; i < nRows; ++i) + if( maRowList.GetRecord( i )->IsEnabled() ) + ++nNonEmpty; + + if( nNonEmpty == 0 ) + { + rStrm.GetCurrentStream()->singleElement( XML_sheetData, FSEND ); + } + else + { + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + rWorksheet->startElement( XML_sheetData, FSEND ); + maRowList.SaveXml( rStrm ); + rWorksheet->endElement( XML_sheetData ); + } +} + +XclExpDimensions* XclExpRowBuffer::GetDimensions() +{ + return &maDimensions; +} + +XclExpRow& XclExpRowBuffer::GetOrCreateRow( sal_uInt16 nXclRow, bool bRowAlwaysEmpty ) +{ + if( !mpLastUsedRow || (mnLastUsedXclRow != nXclRow) ) + { + // fill up missing ROW records + // do not use sal_uInt16 for nFirstFreeXclRow, would cause loop in full sheets + for( size_t nFirstFreeXclRow = maRowList.GetSize(); nFirstFreeXclRow <= nXclRow; ++nFirstFreeXclRow ) + maRowList.AppendNewRecord( new XclExpRow( + GetRoot(), static_cast< sal_uInt16 >( nFirstFreeXclRow ), maOutlineBfr, bRowAlwaysEmpty ) ); + + mpLastUsedRow = maRowList.GetRecord( nXclRow ).get(); + mnLastUsedXclRow = nXclRow; + } + return *mpLastUsedRow; +} + +// ============================================================================ +// Cell Table +// ============================================================================ + +XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + maColInfoBfr( rRoot ), + maRowBfr( rRoot ), + maArrayBfr( rRoot ), + maShrfmlaBfr( rRoot ), + maTableopBfr( rRoot ), + mxDefrowheight( new XclExpDefrowheight ), + mxGuts( new XclExpGuts( rRoot ) ), + mxNoteList( new XclExpNoteList ), + mxMergedcells( new XclExpMergedcells( rRoot ) ), + mxHyperlinkList( new XclExpHyperlinkList ), + mxDval( new XclExpDval( rRoot ) ) +{ + ScDocument& rDoc = GetDoc(); + SCTAB nScTab = GetCurrScTab(); + SvNumberFormatter& rFormatter = GetFormatter(); + + // maximum sheet limits + SCCOL nMaxScCol = GetMaxPos().Col(); + SCROW nMaxScRow = GetMaxPos().Row(); + + // find used area (non-empty cells) + SCCOL nLastUsedScCol; + SCROW nLastUsedScRow; + rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow ); + + ScRange aUsedRange( 0, 0, nScTab, nLastUsedScCol, nLastUsedScRow, nScTab ); + GetAddressConverter().ValidateRange( aUsedRange, true ); + nLastUsedScCol = aUsedRange.aEnd.Col(); + nLastUsedScRow = aUsedRange.aEnd.Row(); + + // first row without any set attributes (height/hidden/...) + SCROW nFirstUnflaggedScRow = rDoc.GetLastFlaggedRow( nScTab ) + 1; + + // find range of outlines + SCROW nFirstUngroupedScRow = 0; + if( const ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable( nScTab ) ) + { + SCCOLROW nScStartPos, nScEndPos; + if( const ScOutlineArray* pRowArray = pOutlineTable->GetRowArray() ) + { + pRowArray->GetRange( nScStartPos, nScEndPos ); + // +1 because open/close button is in next row in Excel, +1 for "end->first unused" + nFirstUngroupedScRow = static_cast< SCROW >( nScEndPos + 2 ); + } + } + + // column settings + /* #i30411# Files saved with SO7/OOo1.x with nonstandard default column + formatting cause big Excel files, because all rows from row 1 to row + 32000 are exported. Now, if the used area goes exactly to row 32000, + use this row as default and ignore all rows >32000. + #i59220# Tolerance of +-128 rows for inserted/removed rows. */ + if( (31871 <= nLastUsedScRow) && (nLastUsedScRow <= 32127) && (nFirstUnflaggedScRow < nLastUsedScRow) && (nFirstUngroupedScRow <= nLastUsedScRow) ) + nMaxScRow = nLastUsedScRow; + maColInfoBfr.Initialize( nMaxScRow ); + + // range for cell iterator + SCCOL nLastIterScCol = nMaxScCol; + SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow + 128, nMaxScRow ); + ScUsedAreaIterator aIt( &rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow ); + + // activate the correct segment and sub segment at the progress bar + GetProgressBar().ActivateCreateRowsSegment(); + + for( bool bIt = aIt.GetNext(); bIt; bIt = aIt.GetNext() ) + { + SCCOL nScCol = aIt.GetStartCol(); + SCROW nScRow = aIt.GetRow(); + SCCOL nLastScCol = aIt.GetEndCol(); + ScAddress aScPos( nScCol, nScRow, nScTab ); + + XclAddress aXclPos( static_cast< sal_uInt16 >( nScCol ), static_cast< sal_uInt16 >( nScRow ) ); + sal_uInt16 nLastXclCol = static_cast< sal_uInt16 >( nLastScCol ); + + const ScBaseCell* pScCell = aIt.GetCell(); + XclExpCellRef xCell; + + const ScPatternAttr* pPattern = aIt.GetPattern(); + + // handle overlapped merged cells before creating the cell record + sal_uInt32 nMergeBaseXFId = EXC_XFID_NOTFOUND; + bool bIsMergedBase = false; + if( pPattern ) + { + const SfxItemSet& rItemSet = pPattern->GetItemSet(); + // base cell in a merged range + const ScMergeAttr& rMergeItem = GETITEM( rItemSet, ScMergeAttr, ATTR_MERGE ); + bIsMergedBase = rMergeItem.IsMerged(); + /* overlapped cell in a merged range; in Excel all merged cells + must contain same XF index, for correct border */ + const ScMergeFlagAttr& rMergeFlagItem = GETITEM( rItemSet, ScMergeFlagAttr, ATTR_MERGE_FLAG ); + if( rMergeFlagItem.IsOverlapped() ) + nMergeBaseXFId = mxMergedcells->GetBaseXFId( aScPos ); + } + + String aAddNoteText; // additional text to be appended to a note + + CellType eCellType = pScCell ? pScCell->GetCellType() : CELLTYPE_NONE; + switch( eCellType ) + { + case CELLTYPE_VALUE: + { + double fValue = static_cast< const ScValueCell* >( pScCell )->GetValue(); + + // try to create a Boolean cell + if( pPattern && ((fValue == 0.0) || (fValue == 1.0)) ) + { + ULONG nScNumFmt = GETITEMVALUE( pPattern->GetItemSet(), SfxUInt32Item, ATTR_VALUE_FORMAT, ULONG ); + if( rFormatter.GetType( nScNumFmt ) == NUMBERFORMAT_LOGICAL ) + xCell.reset( new XclExpBooleanCell( + GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue != 0.0 ) ); + } + + // try to create an RK value (compressed floating-point number) + sal_Int32 nRkValue; + if( !xCell && XclTools::GetRKFromDouble( nRkValue, fValue ) ) + xCell.reset( new XclExpRkCell( + GetRoot(), aXclPos, pPattern, nMergeBaseXFId, nRkValue ) ); + + // else: simple floating-point number cell + if( !xCell ) + xCell.reset( new XclExpNumberCell( + GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue ) ); + } + break; + + case CELLTYPE_STRING: + { + const ScStringCell& rScStrCell = *static_cast< const ScStringCell* >( pScCell ); + xCell.reset( new XclExpLabelCell( + GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScStrCell ) ); + } + break; + + case CELLTYPE_EDIT: + { + const ScEditCell& rScEditCell = *static_cast< const ScEditCell* >( pScCell ); + XclExpHyperlinkHelper aLinkHelper( GetRoot(), aScPos ); + xCell.reset( new XclExpLabelCell( + GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScEditCell, aLinkHelper ) ); + + // add a single created HLINK record to the record list + if( aLinkHelper.HasLinkRecord() ) + mxHyperlinkList->AppendRecord( aLinkHelper.GetLinkRecord() ); + // add list of multiple URLs to the additional cell note text + if( aLinkHelper.HasMultipleUrls() ) + ScGlobal::AddToken( aAddNoteText, aLinkHelper.GetUrlList(), '\n', 2 ); + } + break; + + case CELLTYPE_FORMULA: + { + const ScFormulaCell& rScFmlaCell = *static_cast< const ScFormulaCell* >( pScCell ); + xCell.reset( new XclExpFormulaCell( + GetRoot(), aXclPos, pPattern, nMergeBaseXFId, + rScFmlaCell, maArrayBfr, maShrfmlaBfr, maTableopBfr ) ); + } + break; + + default: + DBG_ERRORFILE( "XclExpCellTable::XclExpCellTable - unknown cell type" ); + // run-through! + case CELLTYPE_NONE: + case CELLTYPE_NOTE: + { + xCell.reset( new XclExpBlankCell( + GetRoot(), aXclPos, nLastXclCol, pPattern, nMergeBaseXFId ) ); + } + break; + } + + // insert the cell into the current row + if( xCell.is() ) + maRowBfr.AppendCell( xCell, bIsMergedBase ); + + // notes + const ScPostIt* pScNote = pScCell ? pScCell->GetNote() : 0; + if( pScNote || (aAddNoteText.Len() > 0) ) + mxNoteList->AppendNewRecord( new XclExpNote( GetRoot(), aScPos, pScNote, aAddNoteText ) ); + + // other sheet contents + if( pPattern ) + { + const SfxItemSet& rItemSet = pPattern->GetItemSet(); + + // base cell in a merged range + if( bIsMergedBase ) + { + const ScMergeAttr& rMergeItem = GETITEM( rItemSet, ScMergeAttr, ATTR_MERGE ); + ScRange aScRange( aScPos ); + aScRange.aEnd.IncCol( rMergeItem.GetColMerge() - 1 ); + aScRange.aEnd.IncRow( rMergeItem.GetRowMerge() - 1 ); + sal_uInt32 nXFId = xCell.is() ? xCell->GetFirstXFId() : EXC_XFID_NOTFOUND; + // #120156# blank cells merged vertically may occur repeatedly + DBG_ASSERT( (aScRange.aStart.Col() == aScRange.aEnd.Col()) || (nScCol == nLastScCol), + "XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" ); + for( SCCOL nIndex = nScCol; nIndex <= nLastScCol; ++nIndex ) + { + mxMergedcells->AppendRange( aScRange, nXFId ); + aScRange.aStart.IncCol(); + aScRange.aEnd.IncCol(); + } + } + + // data validation + if( ScfTools::CheckItem( rItemSet, ATTR_VALIDDATA, false ) ) + { + ULONG nScHandle = GETITEMVALUE( rItemSet, SfxUInt32Item, ATTR_VALIDDATA, ULONG ); + ScRange aScRange( aScPos ); + aScRange.aEnd.SetCol( nLastScCol ); + mxDval->InsertCellRange( aScRange, nScHandle ); + } + } + } + + // create missing row settings for rows anyhow flagged or with outlines + maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, nFirstUngroupedScRow ) ); +} + +void XclExpCellTable::Finalize() +{ + // Finalize multiple operations. + maTableopBfr.Finalize(); + + /* Finalize column buffer. This calculates column default XF indexes from + the XF identifiers and fills a vector with these XF indexes. */ + ScfUInt16Vec aColXFIndexes; + maColInfoBfr.Finalize( aColXFIndexes ); + + /* Finalize row buffer. This calculates all cell XF indexes from the XF + identifiers. Then the XF index vector aColXFIndexes (filled above) is + used to calculate the row default formats. With this, all unneeded blank + cell records (equal to row default or column default) will be removed. + The function returns the (most used) default row format in aDefRowData. */ + XclExpDefaultRowData aDefRowData; + maRowBfr.Finalize( aDefRowData, aColXFIndexes ); + + // Initialize the DEFROWHEIGHT record. + mxDefrowheight->SetDefaultData( aDefRowData ); +} + +XclExpRecordRef XclExpCellTable::CreateRecord( sal_uInt16 nRecId ) const +{ + XclExpRecordRef xRec; + switch( nRecId ) + { + case EXC_ID3_DIMENSIONS: xRec.reset( new XclExpDelegatingRecord( const_cast<XclExpRowBuffer*>(&maRowBfr)->GetDimensions() ) ); break; + case EXC_ID2_DEFROWHEIGHT: xRec = mxDefrowheight; break; + case EXC_ID_GUTS: xRec = mxGuts; break; + case EXC_ID_NOTE: xRec = mxNoteList; break; + case EXC_ID_MERGEDCELLS: xRec = mxMergedcells; break; + case EXC_ID_HLINK: xRec = mxHyperlinkList; break; + case EXC_ID_DVAL: xRec = mxDval; break; + default: DBG_ERRORFILE( "XclExpCellTable::CreateRecord - unknown record id" ); + } + return xRec; +} + +void XclExpCellTable::Save( XclExpStream& rStrm ) +{ + // DEFCOLWIDTH and COLINFOs + maColInfoBfr.Save( rStrm ); + // ROWs and cell records + maRowBfr.Save( rStrm ); +} + +void XclExpCellTable::SaveXml( XclExpXmlStream& rStrm ) +{ + maColInfoBfr.SaveXml( rStrm ); + maRowBfr.SaveXml( rStrm ); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xeview.cxx b/sc/source/filter/excel/xeview.cxx new file mode 100644 index 000000000000..035afd0a5830 --- /dev/null +++ b/sc/source/filter/excel/xeview.cxx @@ -0,0 +1,538 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xeview.hxx" +#include "document.hxx" +#include "scextopt.hxx" +#include "viewopti.hxx" +#include "xelink.hxx" +#include "xestyle.hxx" + +#include <oox/core/tokens.hxx> + +using ::rtl::OString; + +// Workbook view settings records ============================================= + +XclExpWindow1::XclExpWindow1( const XclExpRoot& rRoot ) : + XclExpRecord( EXC_ID_WINDOW1, 18 ), + mnFlags( 0 ), + mnTabBarSize( 600 ) +{ + const ScViewOptions& rViewOpt = rRoot.GetDoc().GetViewOptions(); + ::set_flag( mnFlags, EXC_WIN1_HOR_SCROLLBAR, rViewOpt.GetOption( VOPT_HSCROLL ) ); + ::set_flag( mnFlags, EXC_WIN1_VER_SCROLLBAR, rViewOpt.GetOption( VOPT_VSCROLL ) ); + ::set_flag( mnFlags, EXC_WIN1_TABBAR, rViewOpt.GetOption( VOPT_TABCONTROLS ) ); + + double fTabBarWidth = rRoot.GetExtDocOptions().GetDocSettings().mfTabBarWidth; + if( (0.0 <= fTabBarWidth) && (fTabBarWidth <= 1.0) ) + mnTabBarSize = static_cast< sal_uInt16 >( fTabBarWidth * 1000.0 + 0.5 ); +} + +void XclExpWindow1::SaveXml( XclExpXmlStream& rStrm ) +{ + const XclExpTabInfo& rTabInfo = rStrm.GetRoot().GetTabInfo(); + + rStrm.GetCurrentStream()->singleElement( XML_workbookView, + // OOXTODO: XML_visibility, // ST_visibilty + // OOXTODO: XML_minimized, // bool + XML_showHorizontalScroll, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_WIN1_HOR_SCROLLBAR ) ), + XML_showVerticalScroll, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_WIN1_VER_SCROLLBAR ) ), + XML_showSheetTabs, XclXmlUtils::ToPsz( ::get_flag( mnFlags, EXC_WIN1_TABBAR ) ), + XML_xWindow, "0", + XML_yWindow, "0", + XML_windowWidth, OString::valueOf( (sal_Int32)0x4000 ).getStr(), + XML_windowHeight, OString::valueOf( (sal_Int32)0x2000 ).getStr(), + XML_tabRatio, OString::valueOf( (sal_Int32)mnTabBarSize ).getStr(), + XML_firstSheet, OString::valueOf( (sal_Int32)rTabInfo.GetFirstVisXclTab() ).getStr(), + XML_activeTab, OString::valueOf( (sal_Int32)rTabInfo.GetDisplayedXclTab() ).getStr(), + // OOXTODO: XML_autoFilterDateGrouping, // bool; AUTOFILTER12? 87Eh + FSEND ); +} + +void XclExpWindow1::WriteBody( XclExpStream& rStrm ) +{ + const XclExpTabInfo& rTabInfo = rStrm.GetRoot().GetTabInfo(); + + rStrm << sal_uInt16( 0 ) // X position of the window + << sal_uInt16( 0 ) // Y position of the window + << sal_uInt16( 0x4000 ) // width of the window + << sal_uInt16( 0x2000 ) // height of the window + << mnFlags + << rTabInfo.GetDisplayedXclTab() + << rTabInfo.GetFirstVisXclTab() + << rTabInfo.GetXclSelectedCount() + << mnTabBarSize; +} + +// Sheet view settings records ================================================ + +XclExpWindow2::XclExpWindow2( const XclExpRoot& rRoot, + const XclTabViewData& rData, sal_uInt32 nGridColorId ) : + XclExpRecord( EXC_ID_WINDOW2, (rRoot.GetBiff() == EXC_BIFF8) ? 18 : 10 ), + maGridColor( rData.maGridColor ), + mnGridColorId( nGridColorId ), + mnFlags( 0 ), + maFirstXclPos( rData.maFirstXclPos ), + mnNormalZoom( rData.mnNormalZoom ), + mnPageZoom( rData.mnPageZoom ) +{ + ::set_flag( mnFlags, EXC_WIN2_SHOWFORMULAS, rData.mbShowFormulas ); + ::set_flag( mnFlags, EXC_WIN2_SHOWGRID, rData.mbShowGrid ); + ::set_flag( mnFlags, EXC_WIN2_SHOWHEADINGS, rData.mbShowHeadings ); + ::set_flag( mnFlags, EXC_WIN2_FROZEN, rData.mbFrozenPanes ); + ::set_flag( mnFlags, EXC_WIN2_SHOWZEROS, rData.mbShowZeros ); + ::set_flag( mnFlags, EXC_WIN2_DEFGRIDCOLOR, rData.mbDefGridColor ); + ::set_flag( mnFlags, EXC_WIN2_MIRRORED, rData.mbMirrored ); + ::set_flag( mnFlags, EXC_WIN2_SHOWOUTLINE, rData.mbShowOutline ); + ::set_flag( mnFlags, EXC_WIN2_FROZENNOSPLIT, rData.mbFrozenPanes ); + ::set_flag( mnFlags, EXC_WIN2_SELECTED, rData.mbSelected ); + ::set_flag( mnFlags, EXC_WIN2_DISPLAYED, rData.mbDisplayed ); + ::set_flag( mnFlags, EXC_WIN2_PAGEBREAKMODE, rData.mbPageMode ); +} + +void XclExpWindow2::WriteBody( XclExpStream& rStrm ) +{ + const XclExpRoot& rRoot = rStrm.GetRoot(); + + rStrm << mnFlags + << maFirstXclPos; + + switch( rRoot.GetBiff() ) + { + case EXC_BIFF3: + case EXC_BIFF4: + case EXC_BIFF5: + rStrm << maGridColor; + break; + case EXC_BIFF8: + rStrm << rRoot.GetPalette().GetColorIndex( mnGridColorId ) + << sal_uInt16( 0 ) + << mnPageZoom + << mnNormalZoom + << sal_uInt32( 0 ); + break; + default: DBG_ERROR_BIFF(); + } +} + +// ---------------------------------------------------------------------------- + +XclExpScl::XclExpScl( sal_uInt16 nZoom ) : + XclExpRecord( EXC_ID_SCL, 4 ), + mnNum( nZoom ), + mnDenom( 100 ) +{ + Shorten( 2 ); + Shorten( 5 ); +} + +void XclExpScl::Shorten( sal_uInt16 nFactor ) +{ + while( (mnNum % nFactor == 0) && (mnDenom % nFactor == 0) ) + { + mnNum = mnNum / nFactor; + mnDenom = mnDenom / nFactor; + } +} + +void XclExpScl::WriteBody( XclExpStream& rStrm ) +{ + DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF4 ); + rStrm << mnNum << mnDenom; +} + +// ---------------------------------------------------------------------------- + +XclExpPane::XclExpPane( const XclTabViewData& rData ) : + XclExpRecord( EXC_ID_PANE, 10 ), + mnSplitX( rData.mnSplitX ), + mnSplitY( rData.mnSplitY ), + maSecondXclPos( rData.maSecondXclPos ), + mnActivePane( rData.mnActivePane ) +{ + DBG_ASSERT( rData.IsSplit(), "XclExpPane::XclExpPane - no PANE record for unsplit view" ); +} + +static const char* lcl_GetActivePane( sal_uInt8 nActivePane ) +{ + switch( nActivePane ) + { + case EXC_PANE_TOPLEFT: return "topLeft"; //break; + case EXC_PANE_TOPRIGHT: return "topRight"; //break; + case EXC_PANE_BOTTOMLEFT: return "bottomLeft"; //break; + case EXC_PANE_BOTTOMRIGHT: return "bottomRight"; //break; + } + return "**error: lcl_GetActivePane"; +} + +void XclExpPane::SaveXml( XclExpXmlStream& rStrm ) +{ + rStrm.GetCurrentStream()->singleElement( XML_pane, + XML_xSplit, OString::valueOf( (sal_Int32)mnSplitX ).getStr(), + XML_ySplit, OString::valueOf( (sal_Int32)mnSplitY ).getStr(), + XML_topLeftCell, XclXmlUtils::ToOString( maSecondXclPos ).getStr(), + XML_activePane, lcl_GetActivePane( mnActivePane ), + // OOXTODO: XML_state, + FSEND ); +} + +void XclExpPane::WriteBody( XclExpStream& rStrm ) +{ + rStrm << mnSplitX + << mnSplitY + << maSecondXclPos + << mnActivePane; + if( rStrm.GetRoot().GetBiff() >= EXC_BIFF5 ) + rStrm << sal_uInt8( 0 ); +} + +// ---------------------------------------------------------------------------- + +XclExpSelection::XclExpSelection( const XclTabViewData& rData, sal_uInt8 nPane ) : + XclExpRecord( EXC_ID_SELECTION, 15 ), + mnPane( nPane ) +{ + if( const XclSelectionData* pSelData = rData.GetSelectionData( nPane ) ) + maSelData = *pSelData; + + // find the cursor position in the selection list (or add it) + XclRangeList& rXclSel = maSelData.maXclSelection; + bool bFound = false; + for( XclRangeList::const_iterator aIt = rXclSel.begin(), aEnd = rXclSel.end(); !bFound && (aIt != aEnd); ++aIt ) + if( (bFound = aIt->Contains( maSelData.maXclCursor )) == true ) + maSelData.mnCursorIdx = static_cast< sal_uInt16 >( aIt - rXclSel.begin() ); + /* Cursor cell not found in list? (e.g. inactive pane, or removed in + ConvertRangeList(), because Calc cursor on invalid pos) + -> insert the valid Excel cursor. */ + if( !bFound ) + { + maSelData.mnCursorIdx = static_cast< sal_uInt16 >( rXclSel.size() ); + rXclSel.push_back( XclRange( maSelData.maXclCursor ) ); + } +} + +void XclExpSelection::SaveXml( XclExpXmlStream& rStrm ) +{ + rStrm.GetCurrentStream()->singleElement( XML_selection, + XML_pane, lcl_GetActivePane( mnPane ), + XML_activeCell, XclXmlUtils::ToOString( maSelData.maXclCursor ).getStr(), + XML_activeCellId, OString::valueOf( (sal_Int32) maSelData.mnCursorIdx ).getStr(), + XML_sqref, XclXmlUtils::ToOString( maSelData.maXclSelection ).getStr(), + FSEND ); +} + +void XclExpSelection::WriteBody( XclExpStream& rStrm ) +{ + rStrm << mnPane // pane for this selection + << maSelData.maXclCursor // cell cursor + << maSelData.mnCursorIdx; // index to range containing cursor + maSelData.maXclSelection.Write( rStrm, false ); +} + +// ---------------------------------------------------------------------------- + +XclExpTabBgColor::XclExpTabBgColor( const XclTabViewData& rTabViewData ) : + XclExpRecord( EXC_ID_SHEETEXT, 18 ), + mrTabViewData( rTabViewData ) +{ +} +//TODO Fix savexml... +/*void XclExpTabBgColor::SaveXml( XclExpXmlStream& rStrm ) +{ +}*/ + +void XclExpTabBgColor::WriteBody( XclExpStream& rStrm ) +{ + if ( mrTabViewData.IsDefaultTabBgColor() ) + return; + sal_uInt16 rt = 0x0862; //rt + sal_uInt16 grbitFrt = 0x0000; //grbit must be set to 0 + sal_uInt32 unused = 0x00000000; //Use twice... + sal_uInt32 cb = 0x00000014; // Record Size, may be larger in future... + sal_uInt16 reserved = 0x0000; //trailing bits are 0 + sal_uInt16 TabBgColorIndex; + XclExpPalette& rPal = rStrm.GetRoot().GetPalette(); + TabBgColorIndex = rPal.GetColorIndex(mrTabViewData.mnTabBgColorId); + if (TabBgColorIndex < 8 || TabBgColorIndex > 63 ) // only numbers 8 - 63 are valid numbers + TabBgColorIndex = 127; //Excel specs: 127 makes excel ignore tab color information. + rStrm << rt << grbitFrt << unused << unused << cb << TabBgColorIndex << reserved; +} + +// Sheet view settings ======================================================== + +namespace { + +/** Converts a Calc zoom factor into an Excel zoom factor. Returns 0 for a default zoom value. */ +sal_uInt16 lclGetXclZoom( long nScZoom, sal_uInt16 nDefXclZoom ) +{ + sal_uInt16 nXclZoom = limit_cast< sal_uInt16 >( nScZoom, EXC_ZOOM_MIN, EXC_ZOOM_MAX ); + return (nXclZoom == nDefXclZoom) ? 0 : nXclZoom; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpTabViewSettings::XclExpTabViewSettings( const XclExpRoot& rRoot, SCTAB nScTab ) : + XclExpRoot( rRoot ), + mnGridColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT ) ) +{ + // *** sheet flags *** + + const XclExpTabInfo& rTabInfo = GetTabInfo(); + maData.mbSelected = rTabInfo.IsSelectedTab( nScTab ); + maData.mbDisplayed = rTabInfo.IsDisplayedTab( nScTab ); + maData.mbMirrored = rTabInfo.IsMirroredTab( nScTab ); + + const ScViewOptions& rViewOpt = GetDoc().GetViewOptions(); + maData.mbShowFormulas = rViewOpt.GetOption( VOPT_FORMULAS ); + maData.mbShowGrid = rViewOpt.GetOption( VOPT_GRID ); + maData.mbShowHeadings = rViewOpt.GetOption( VOPT_HEADER ); + maData.mbShowZeros = rViewOpt.GetOption( VOPT_NULLVALS ); + maData.mbShowOutline = rViewOpt.GetOption( VOPT_OUTLINER ); + + // *** sheet options: cursor, selection, splits, grid color, zoom *** + + if( const ScExtTabSettings* pTabSett = GetExtDocOptions().GetTabSettings( nScTab ) ) + { + const ScExtTabSettings& rTabSett = *pTabSett; + XclExpAddressConverter& rAddrConv = GetAddressConverter(); + + // first visible cell in top-left pane + if( (rTabSett.maFirstVis.Col() >= 0) && (rTabSett.maFirstVis.Row() >= 0) ) + maData.maFirstXclPos = rAddrConv.CreateValidAddress( rTabSett.maFirstVis, false ); + + // first visible cell in additional pane(s) + if( (rTabSett.maSecondVis.Col() >= 0) && (rTabSett.maSecondVis.Row() >= 0) ) + maData.maSecondXclPos = rAddrConv.CreateValidAddress( rTabSett.maSecondVis, false ); + + // active pane + switch( rTabSett.meActivePane ) + { + case SCEXT_PANE_TOPLEFT: maData.mnActivePane = EXC_PANE_TOPLEFT; break; + case SCEXT_PANE_TOPRIGHT: maData.mnActivePane = EXC_PANE_TOPRIGHT; break; + case SCEXT_PANE_BOTTOMLEFT: maData.mnActivePane = EXC_PANE_BOTTOMLEFT; break; + case SCEXT_PANE_BOTTOMRIGHT: maData.mnActivePane = EXC_PANE_BOTTOMRIGHT; break; + } + + // freeze/split position + maData.mbFrozenPanes = rTabSett.mbFrozenPanes; + if( maData.mbFrozenPanes ) + { + /* Frozen panes: handle split position as row/column positions. + #i35812# Excel uses number of visible rows/columns, Calc uses position of freeze. */ + SCCOL nFreezeScCol = rTabSett.maFreezePos.Col(); + if( (0 < nFreezeScCol) && (nFreezeScCol <= GetXclMaxPos().Col()) ) + maData.mnSplitX = static_cast< sal_uInt16 >( nFreezeScCol ) - maData.maFirstXclPos.mnCol; + SCROW nFreezeScRow = rTabSett.maFreezePos.Row(); + if( (0 < nFreezeScRow) && (nFreezeScRow <= GetXclMaxPos().Row()) ) + maData.mnSplitY = static_cast< sal_uInt16 >( nFreezeScRow ) - maData.maFirstXclPos.mnRow; + // if both splits are left out (address overflow), remove the frozen flag + maData.mbFrozenPanes = maData.IsSplit(); + + // #i20671# frozen panes: mostright/mostbottom pane is active regardless of cursor position + if( maData.HasPane( EXC_PANE_BOTTOMRIGHT ) ) + maData.mnActivePane = EXC_PANE_BOTTOMRIGHT; + else if( maData.HasPane( EXC_PANE_TOPRIGHT ) ) + maData.mnActivePane = EXC_PANE_TOPRIGHT; + else if( maData.HasPane( EXC_PANE_BOTTOMLEFT ) ) + maData.mnActivePane = EXC_PANE_BOTTOMLEFT; + } + else + { + // split window: position is in twips + maData.mnSplitX = ulimit_cast< sal_uInt16 >( rTabSett.maSplitPos.X() ); + maData.mnSplitY = ulimit_cast< sal_uInt16 >( rTabSett.maSplitPos.Y() ); + } + + // selection + CreateSelectionData( EXC_PANE_TOPLEFT, rTabSett.maCursor, rTabSett.maSelection ); + CreateSelectionData( EXC_PANE_TOPRIGHT, rTabSett.maCursor, rTabSett.maSelection ); + CreateSelectionData( EXC_PANE_BOTTOMLEFT, rTabSett.maCursor, rTabSett.maSelection ); + CreateSelectionData( EXC_PANE_BOTTOMRIGHT, rTabSett.maCursor, rTabSett.maSelection ); + + // grid color + const Color& rGridColor = rTabSett.maGridColor; + maData.mbDefGridColor = rGridColor.GetColor() == COL_AUTO; + if( !maData.mbDefGridColor ) + { + if( GetBiff() == EXC_BIFF8 ) + mnGridColorId = GetPalette().InsertColor( rGridColor, EXC_COLOR_GRID ); + else + maData.maGridColor = rGridColor; + } + + // view mode and zoom + maData.mbPageMode = (GetBiff() == EXC_BIFF8) && rTabSett.mbPageMode; + maData.mnNormalZoom = lclGetXclZoom( rTabSett.mnNormalZoom, EXC_WIN2_NORMALZOOM_DEF ); + maData.mnPageZoom = lclGetXclZoom( rTabSett.mnPageZoom, EXC_WIN2_PAGEZOOM_DEF ); + maData.mnCurrentZoom = maData.mbPageMode ? maData.mnPageZoom : maData.mnNormalZoom; + } + + // Tab Bg Color + if ( GetBiff() == EXC_BIFF8 && !GetDoc().IsDefaultTabBgColor(nScTab) ) + { + XclExpPalette& rPal = GetPalette(); + maData.maTabBgColor = GetDoc().GetTabBgColor(nScTab); + maData.mnTabBgColorId = rPal.InsertColor(maData.maTabBgColor, EXC_COLOR_TABBG, EXC_COLOR_NOTABBG ); + } +} + +void XclExpTabViewSettings::Save( XclExpStream& rStrm ) +{ + WriteWindow2( rStrm ); + WriteScl( rStrm ); + WritePane( rStrm ); + WriteSelection( rStrm, EXC_PANE_TOPLEFT ); + WriteSelection( rStrm, EXC_PANE_TOPRIGHT ); + WriteSelection( rStrm, EXC_PANE_BOTTOMLEFT ); + WriteSelection( rStrm, EXC_PANE_BOTTOMRIGHT ); + WriteTabBgColor( rStrm ); +} + +static void lcl_WriteSelection( XclExpXmlStream& rStrm, const XclTabViewData& rData, sal_uInt8 nPane ) +{ + if( rData.HasPane( nPane ) ) + XclExpSelection( rData, nPane ).SaveXml( rStrm ); +} + +OString lcl_GetZoom( sal_uInt16 nZoom ) +{ + if( nZoom ) + return OString::valueOf( (sal_Int32)nZoom ); + return OString( "100" ); +} + +void XclExpTabViewSettings::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + rWorksheet->startElement( XML_sheetViews, FSEND ); + rWorksheet->startElement( XML_sheetView, + XML_windowProtection, XclXmlUtils::ToPsz( maData.mbFrozenPanes ), + XML_showFormulas, XclXmlUtils::ToPsz( maData.mbShowFormulas ), + XML_showGridLines, XclXmlUtils::ToPsz( maData.mbShowGrid ), + XML_showRowColHeaders, XclXmlUtils::ToPsz( maData.mbShowHeadings ), + XML_showZeros, XclXmlUtils::ToPsz( maData.mbShowZeros ), + XML_rightToLeft, XclXmlUtils::ToPsz( maData.mbMirrored ), + XML_tabSelected, XclXmlUtils::ToPsz( maData.mbSelected ), + // OOXTODO: XML_showRuler, + XML_showOutlineSymbols, XclXmlUtils::ToPsz( maData.mbShowOutline ), + XML_defaultGridColor, mnGridColorId == XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT ) ? "true" : "false", + // OOXTODO: XML_showWhiteSpace, + XML_view, maData.mbPageMode ? "pageBreakPreview" : "normal", // OOXTODO: pageLayout + XML_topLeftCell, XclXmlUtils::ToOString( maData.maFirstXclPos ).getStr(), + XML_colorId, OString::valueOf( (sal_Int32) rStrm.GetRoot().GetPalette().GetColorIndex( mnGridColorId ) ).getStr(), + XML_zoomScale, lcl_GetZoom( maData.mnCurrentZoom ).getStr(), + XML_zoomScaleNormal, lcl_GetZoom( maData.mnNormalZoom ).getStr(), + // OOXTODO: XML_zoomScaleSheetLayoutView, + XML_zoomScalePageLayoutView, lcl_GetZoom( maData.mnPageZoom ).getStr(), + XML_workbookViewId, "0", // OOXTODO? 0-based index of document(xl/workbook.xml)/workbook/bookviews/workbookView + // should always be 0, as we only generate 1 such element. + FSEND ); + if( maData.IsSplit() ) + { + XclExpPane aPane( maData ); + aPane.SaveXml( rStrm ); + } + lcl_WriteSelection( rStrm, maData, EXC_PANE_TOPLEFT ); + lcl_WriteSelection( rStrm, maData, EXC_PANE_TOPRIGHT ); + lcl_WriteSelection( rStrm, maData, EXC_PANE_BOTTOMLEFT ); + lcl_WriteSelection( rStrm, maData, EXC_PANE_BOTTOMRIGHT ); + rWorksheet->endElement( XML_sheetView ); + // OOXTODO: XML_extLst + rWorksheet->endElement( XML_sheetViews ); +} + +// private -------------------------------------------------------------------- + +void XclExpTabViewSettings::CreateSelectionData( sal_uInt8 nPane, + const ScAddress& rCursor, const ScRangeList& rSelection ) +{ + if( maData.HasPane( nPane ) ) + { + XclSelectionData& rSelData = maData.CreateSelectionData( nPane ); + + // first step: use top-left visible cell as cursor + rSelData.maXclCursor.mnCol = ((nPane == EXC_PANE_TOPLEFT) || (nPane == EXC_PANE_BOTTOMLEFT)) ? + maData.maFirstXclPos.mnCol : maData.maSecondXclPos.mnCol; + rSelData.maXclCursor.mnRow = ((nPane == EXC_PANE_TOPLEFT) || (nPane == EXC_PANE_TOPRIGHT)) ? + maData.maFirstXclPos.mnRow : maData.maSecondXclPos.mnRow; + + // second step, active pane: create actual selection data with current cursor position + if( nPane == maData.mnActivePane ) + { + XclExpAddressConverter& rAddrConv = GetAddressConverter(); + // cursor position (keep top-left pane position from above, if rCursor is invalid) + if( (rCursor.Col() >= 0) && (rCursor.Row() >= 0) ) + rSelData.maXclCursor = rAddrConv.CreateValidAddress( rCursor, false ); + // selection + rAddrConv.ConvertRangeList( rSelData.maXclSelection, rSelection, false ); + } + } +} + +void XclExpTabViewSettings::WriteWindow2( XclExpStream& rStrm ) const +{ +// #i43553# GCC 3.3 parse error +// XclExpWindow2( GetRoot(), maData, mnGridColorId ).Save( rStrm ); + XclExpWindow2 aWindow2( GetRoot(), maData, mnGridColorId ); + aWindow2.Save( rStrm ); +} + +void XclExpTabViewSettings::WriteScl( XclExpStream& rStrm ) const +{ + if( maData.mnCurrentZoom != 0 ) + XclExpScl( maData.mnCurrentZoom ).Save( rStrm ); +} + +void XclExpTabViewSettings::WritePane( XclExpStream& rStrm ) const +{ + if( maData.IsSplit() ) +// #i43553# GCC 3.3 parse error +// XclExpPane( GetRoot(), maData ).Save( rStrm ); + { + XclExpPane aPane( maData ); + aPane.Save( rStrm ); + } +} + +void XclExpTabViewSettings::WriteSelection( XclExpStream& rStrm, sal_uInt8 nPane ) const +{ + if( maData.HasPane( nPane ) ) + XclExpSelection( maData, nPane ).Save( rStrm ); +} + +void XclExpTabViewSettings::WriteTabBgColor( XclExpStream& rStrm ) const +{ + if ( !maData.IsDefaultTabBgColor() ) + XclExpTabBgColor( maData ).Save( rStrm ); +} +// ============================================================================ + diff --git a/sc/source/filter/excel/xichart.cxx b/sc/source/filter/excel/xichart.cxx new file mode 100755 index 000000000000..7cbd74a836d2 --- /dev/null +++ b/sc/source/filter/excel/xichart.cxx @@ -0,0 +1,4147 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "xichart.hxx" + +#include <algorithm> +#include <memory> + +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/drawing/Direction3D.hpp> +#include <com/sun/star/drawing/ProjectionMode.hpp> +#include <com/sun/star/drawing/ShadeMode.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/chart/ChartAxisArrangeOrderType.hpp> +#include <com/sun/star/chart/ChartAxisLabelPosition.hpp> +#include <com/sun/star/chart/ChartAxisMarkPosition.hpp> +#include <com/sun/star/chart/ChartAxisPosition.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/chart/XDiagramPositioning.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/XDiagram.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XDataSeriesContainer.hpp> +#include <com/sun/star/chart2/XRegressionCurveContainer.hpp> +#include <com/sun/star/chart2/XTitled.hpp> +#include <com/sun/star/chart2/data/XDataProvider.hpp> +#include <com/sun/star/chart2/data/XDataReceiver.hpp> +#include <com/sun/star/chart2/data/XDataSink.hpp> +#include <com/sun/star/chart2/AxisType.hpp> +#include <com/sun/star/chart2/CurveStyle.hpp> +#include <com/sun/star/chart2/DataPointGeometry3D.hpp> +#include <com/sun/star/chart2/DataPointLabel.hpp> +#include <com/sun/star/chart2/LegendExpansion.hpp> +#include <com/sun/star/chart2/LegendPosition.hpp> +#include <com/sun/star/chart2/StackingDirection.hpp> +#include <com/sun/star/chart2/TickmarkStyle.hpp> +#include <com/sun/star/chart2/RelativePosition.hpp> +#include <com/sun/star/chart/DataLabelPlacement.hpp> +#include <com/sun/star/chart/ErrorBarStyle.hpp> +#include <com/sun/star/chart/MissingValueTreatment.hpp> + +#include <sfx2/objsh.hxx> +#include <svx/svdpage.hxx> +#include <svx/unoapi.hxx> + +#include "document.hxx" +#include "drwlayer.hxx" +#include "rangeutl.hxx" +#include "tokenarray.hxx" +#include "token.hxx" +#include "compiler.hxx" +#include "reftokenhelper.hxx" +#include "chartlis.hxx" +#include "fprogressbar.hxx" +#include "xltracer.hxx" +#include "xistream.hxx" +#include "xiformula.hxx" +#include "xistyle.hxx" +#include "xipage.hxx" +#include "xiview.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::UNO_SET_THROW; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::frame::XModel; +using ::com::sun::star::util::XNumberFormatsSupplier; +using ::com::sun::star::drawing::XDrawPage; +using ::com::sun::star::drawing::XDrawPageSupplier; +using ::com::sun::star::drawing::XShape; + +using ::com::sun::star::chart2::IncrementData; +using ::com::sun::star::chart2::RelativePosition; +using ::com::sun::star::chart2::ScaleData; +using ::com::sun::star::chart2::SubIncrement; +using ::com::sun::star::chart2::XAxis; +using ::com::sun::star::chart2::XChartDocument; +using ::com::sun::star::chart2::XChartType; +using ::com::sun::star::chart2::XChartTypeContainer; +using ::com::sun::star::chart2::XCoordinateSystem; +using ::com::sun::star::chart2::XCoordinateSystemContainer; +using ::com::sun::star::chart2::XDataSeries; +using ::com::sun::star::chart2::XDataSeriesContainer; +using ::com::sun::star::chart2::XDiagram; +using ::com::sun::star::chart2::XFormattedString; +using ::com::sun::star::chart2::XLegend; +using ::com::sun::star::chart2::XRegressionCurve; +using ::com::sun::star::chart2::XRegressionCurveContainer; +using ::com::sun::star::chart2::XScaling; +using ::com::sun::star::chart2::XTitle; +using ::com::sun::star::chart2::XTitled; + +using ::com::sun::star::chart2::data::XDataProvider; +using ::com::sun::star::chart2::data::XDataReceiver; +using ::com::sun::star::chart2::data::XDataSequence; +using ::com::sun::star::chart2::data::XDataSink; +using ::com::sun::star::chart2::data::XLabeledDataSequence; + +using ::formula::FormulaToken; +using ::formula::StackVar; + +namespace cssc = ::com::sun::star::chart; +namespace cssc2 = ::com::sun::star::chart2; + +// Helpers ==================================================================== + +namespace { + +XclImpStream& operator>>( XclImpStream& rStrm, XclChRectangle& rRect ) +{ + return rStrm >> rRect.mnX >> rRect.mnY >> rRect.mnWidth >> rRect.mnHeight; +} + +template< typename Type > +void lclSetValueOrClearAny( Any& rAny, const Type& rValue, bool bClear ) +{ + if( bClear ) + rAny.clear(); + else + rAny <<= rValue; +} + +void lclSetExpValueOrClearAny( Any& rAny, double fValue, bool bLogScale, bool bClear ) +{ + if( !bClear && bLogScale ) + fValue = pow( 10.0, fValue ); + lclSetValueOrClearAny( rAny, fValue, bClear ); +} + +} // namespace + +// Common ===================================================================== + +/** Stores global data needed in various classes of the Chart import filter. */ +struct XclImpChRootData : public XclChRootData +{ + XclImpChChart& mrChartData; /// The chart data object. + + inline explicit XclImpChRootData( XclImpChChart& rChartData ) : mrChartData( rChartData ) {} +}; + +// ---------------------------------------------------------------------------- + +XclImpChRoot::XclImpChRoot( const XclImpRoot& rRoot, XclImpChChart& rChartData ) : + XclImpRoot( rRoot ), + mxChData( new XclImpChRootData( rChartData ) ) +{ +} + +XclImpChRoot::~XclImpChRoot() +{ +} + +XclImpChChart& XclImpChRoot::GetChartData() const +{ + return mxChData->mrChartData; +} + +const XclChTypeInfo& XclImpChRoot::GetChartTypeInfo( XclChTypeId eType ) const +{ + return mxChData->mxTypeInfoProv->GetTypeInfo( eType ); +} + +const XclChTypeInfo& XclImpChRoot::GetChartTypeInfo( sal_uInt16 nRecId ) const +{ + return mxChData->mxTypeInfoProv->GetTypeInfoFromRecId( nRecId ); +} + +const XclChFormatInfo& XclImpChRoot::GetFormatInfo( XclChObjectType eObjType ) const +{ + return mxChData->mxFmtInfoProv->GetFormatInfo( eObjType ); +} + +Color XclImpChRoot::GetFontAutoColor() const +{ + return GetPalette().GetColor( EXC_COLOR_CHWINDOWTEXT ); +} + +Color XclImpChRoot::GetSeriesLineAutoColor( sal_uInt16 nFormatIdx ) const +{ + return GetPalette().GetColor( XclChartHelper::GetSeriesLineAutoColorIdx( nFormatIdx ) ); +} + +Color XclImpChRoot::GetSeriesFillAutoColor( sal_uInt16 nFormatIdx ) const +{ + const XclImpPalette& rPal = GetPalette(); + Color aColor = rPal.GetColor( XclChartHelper::GetSeriesFillAutoColorIdx( nFormatIdx ) ); + sal_uInt8 nTrans = XclChartHelper::GetSeriesFillAutoTransp( nFormatIdx ); + return ScfTools::GetMixedColor( aColor, rPal.GetColor( EXC_COLOR_CHWINDOWBACK ), nTrans ); +} + +void XclImpChRoot::InitConversion( Reference< XChartDocument > xChartDoc, const Rectangle& rChartRect ) const +{ + // create formatting object tables + mxChData->InitConversion( GetRoot(), xChartDoc, rChartRect ); + + // lock the model to suppress any internal updates + Reference< XModel > xModel( xChartDoc, UNO_QUERY ); + if( xModel.is() ) + xModel->lockControllers(); + + SfxObjectShell* pDocShell = GetDocShell(); + Reference< XDataReceiver > xDataRec( xChartDoc, UNO_QUERY ); + if( pDocShell && xDataRec.is() ) + { + // create and register a data provider + Reference< XDataProvider > xDataProv( + ScfApiHelper::CreateInstance( pDocShell, SERVICE_CHART2_DATAPROVIDER ), UNO_QUERY ); + if( xDataProv.is() ) + xDataRec->attachDataProvider( xDataProv ); + // attach the number formatter + Reference< XNumberFormatsSupplier > xNumFmtSupp( pDocShell->GetModel(), UNO_QUERY ); + if( xNumFmtSupp.is() ) + xDataRec->attachNumberFormatsSupplier( xNumFmtSupp ); + } +} + +void XclImpChRoot::FinishConversion( XclImpDffConverter& rDffConv ) const +{ + rDffConv.Progress( EXC_CHART_PROGRESS_SIZE ); + // unlock the model + Reference< XModel > xModel( mxChData->mxChartDoc, UNO_QUERY ); + if( xModel.is() ) + xModel->unlockControllers(); + rDffConv.Progress( EXC_CHART_PROGRESS_SIZE ); + + mxChData->FinishConversion(); +} + +Reference< XDataProvider > XclImpChRoot::GetDataProvider() const +{ + return mxChData->mxChartDoc->getDataProvider(); +} + +Reference< XShape > XclImpChRoot::GetTitleShape( const XclChTextKey& rTitleKey ) const +{ + return mxChData->GetTitleShape( rTitleKey ); +} + +sal_Int32 XclImpChRoot::CalcHmmFromChartX( sal_Int32 nPosX ) const +{ + return static_cast< sal_Int32 >( mxChData->mfUnitSizeX * nPosX + mxChData->mnBorderGapX + 0.5 ); +} + +sal_Int32 XclImpChRoot::CalcHmmFromChartY( sal_Int32 nPosY ) const +{ + return static_cast< sal_Int32 >( mxChData->mfUnitSizeY * nPosY + mxChData->mnBorderGapY + 0.5 ); +} + +::com::sun::star::awt::Rectangle XclImpChRoot::CalcHmmFromChartRect( const XclChRectangle& rRect ) const +{ + return ::com::sun::star::awt::Rectangle( + CalcHmmFromChartX( rRect.mnX ), + CalcHmmFromChartY( rRect.mnY ), + CalcHmmFromChartX( rRect.mnWidth ), + CalcHmmFromChartY( rRect.mnHeight ) ); +} + +double XclImpChRoot::CalcRelativeFromChartX( sal_Int32 nPosX ) const +{ + return static_cast< double >( CalcHmmFromChartX( nPosX ) ) / mxChData->maChartRect.GetWidth(); +} + +double XclImpChRoot::CalcRelativeFromChartY( sal_Int32 nPosY ) const +{ + return static_cast< double >( CalcHmmFromChartY( nPosY ) ) / mxChData->maChartRect.GetHeight(); +} + +void XclImpChRoot::ConvertLineFormat( ScfPropertySet& rPropSet, + const XclChLineFormat& rLineFmt, XclChPropertyMode ePropMode ) const +{ + GetChartPropSetHelper().WriteLineProperties( + rPropSet, *mxChData->mxLineDashTable, rLineFmt, ePropMode ); +} + +void XclImpChRoot::ConvertAreaFormat( ScfPropertySet& rPropSet, + const XclChAreaFormat& rAreaFmt, XclChPropertyMode ePropMode ) const +{ + GetChartPropSetHelper().WriteAreaProperties( rPropSet, rAreaFmt, ePropMode ); +} + +void XclImpChRoot::ConvertEscherFormat( ScfPropertySet& rPropSet, + const XclChEscherFormat& rEscherFmt, const XclChPicFormat& rPicFmt, + XclChPropertyMode ePropMode ) const +{ + GetChartPropSetHelper().WriteEscherProperties( rPropSet, + *mxChData->mxGradientTable, *mxChData->mxHatchTable, *mxChData->mxBitmapTable, + rEscherFmt, rPicFmt, ePropMode ); +} + +void XclImpChRoot::ConvertFont( ScfPropertySet& rPropSet, + sal_uInt16 nFontIdx, const Color* pFontColor ) const +{ + GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CHART, nFontIdx, pFontColor ); +} + +void XclImpChRoot::ConvertPieRotation( ScfPropertySet& rPropSet, sal_uInt16 nAngle ) +{ + sal_Int32 nApiRot = (450 - (nAngle % 360)) % 360; + rPropSet.SetProperty( EXC_CHPROP_STARTINGANGLE, nApiRot ); +} + +// ---------------------------------------------------------------------------- + +XclImpChGroupBase::~XclImpChGroupBase() +{ +} + +void XclImpChGroupBase::ReadRecordGroup( XclImpStream& rStrm ) +{ + // read contents of the header record + ReadHeaderRecord( rStrm ); + + // only read sub records, if the next record is a CHBEGIN + if( rStrm.GetNextRecId() == EXC_ID_CHBEGIN ) + { + // read the CHBEGIN record, may be used for special initial processing + rStrm.StartNextRecord(); + ReadSubRecord( rStrm ); + + // read the nested records + bool bLoop = true; + while( bLoop && rStrm.StartNextRecord() ) + { + sal_uInt16 nRecId = rStrm.GetRecId(); + bLoop = nRecId != EXC_ID_CHEND; + // skip unsupported nested blocks + if( nRecId == EXC_ID_CHBEGIN ) + SkipBlock( rStrm ); + else + ReadSubRecord( rStrm ); + } + } + /* Returns with current CHEND record or unchanged stream, if no record + group present. In every case another call to StartNextRecord() will go + to next record of interest. */ +} + +void XclImpChGroupBase::SkipBlock( XclImpStream& rStrm ) +{ + DBG_ASSERT( rStrm.GetRecId() == EXC_ID_CHBEGIN, "XclImpChGroupBase::SkipBlock - no CHBEGIN record" ); + // do nothing if current record is not CHBEGIN + bool bLoop = rStrm.GetRecId() == EXC_ID_CHBEGIN; + while( bLoop && rStrm.StartNextRecord() ) + { + sal_uInt16 nRecId = rStrm.GetRecId(); + bLoop = nRecId != EXC_ID_CHEND; + // skip nested record groups + if( nRecId == EXC_ID_CHBEGIN ) + SkipBlock( rStrm ); + } +} + +// Frame formatting =========================================================== + +void XclImpChFramePos::ReadChFramePos( XclImpStream& rStrm ) +{ + rStrm >> maData.mnTLMode >> maData.mnBRMode; + /* According to the spec, the upper 16 bits of all members in the + rectangle are unused and may contain garbage. */ + maData.maRect.mnX = rStrm.ReadInt16(); rStrm.Ignore( 2 ); + maData.maRect.mnY = rStrm.ReadInt16(); rStrm.Ignore( 2 ); + maData.maRect.mnWidth = rStrm.ReadInt16(); rStrm.Ignore( 2 ); + maData.maRect.mnHeight = rStrm.ReadInt16(); rStrm.Ignore( 2 ); +} + +// ---------------------------------------------------------------------------- + +void XclImpChLineFormat::ReadChLineFormat( XclImpStream& rStrm ) +{ + rStrm >> maData.maColor >> maData.mnPattern >> maData.mnWeight >> maData.mnFlags; + + const XclImpRoot& rRoot = rStrm.GetRoot(); + if( rRoot.GetBiff() == EXC_BIFF8 ) + // #116397# BIFF8: index into palette used instead of RGB data + maData.maColor = rRoot.GetPalette().GetColor( rStrm.ReaduInt16() ); +} + +void XclImpChLineFormat::Convert( const XclImpChRoot& rRoot, + ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const +{ + const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType ); + if( IsAuto() ) + { + XclChLineFormat aLineFmt; + aLineFmt.maColor = (eObjType == EXC_CHOBJTYPE_LINEARSERIES) ? + rRoot.GetSeriesLineAutoColor( nFormatIdx ) : + rRoot.GetPalette().GetColor( rFmtInfo.mnAutoLineColorIdx ); + aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID; + aLineFmt.mnWeight = rFmtInfo.mnAutoLineWeight; + rRoot.ConvertLineFormat( rPropSet, aLineFmt, rFmtInfo.mePropMode ); + } + else + { + rRoot.ConvertLineFormat( rPropSet, maData, rFmtInfo.mePropMode ); + } +} + +// ---------------------------------------------------------------------------- + +void XclImpChAreaFormat::ReadChAreaFormat( XclImpStream& rStrm ) +{ + rStrm >> maData.maPattColor >> maData.maBackColor >> maData.mnPattern >> maData.mnFlags; + + const XclImpRoot& rRoot = rStrm.GetRoot(); + if( rRoot.GetBiff() == EXC_BIFF8 ) + { + // #116397# BIFF8: index into palette used instead of RGB data + const XclImpPalette& rPal = rRoot.GetPalette(); + maData.maPattColor = rPal.GetColor( rStrm.ReaduInt16() ); + maData.maBackColor = rPal.GetColor( rStrm.ReaduInt16()); + } +} + +void XclImpChAreaFormat::Convert( const XclImpChRoot& rRoot, + ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const +{ + const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType ); + if( IsAuto() ) + { + XclChAreaFormat aAreaFmt; + aAreaFmt.maPattColor = (eObjType == EXC_CHOBJTYPE_FILLEDSERIES) ? + rRoot.GetSeriesFillAutoColor( nFormatIdx ) : + rRoot.GetPalette().GetColor( rFmtInfo.mnAutoPattColorIdx ); + aAreaFmt.mnPattern = EXC_PATT_SOLID; + rRoot.ConvertAreaFormat( rPropSet, aAreaFmt, rFmtInfo.mePropMode ); + } + else + { + rRoot.ConvertAreaFormat( rPropSet, maData, rFmtInfo.mePropMode ); + } +} + +// ---------------------------------------------------------------------------- + +XclImpChEscherFormat::XclImpChEscherFormat( const XclImpRoot& rRoot ) +{ + maData.mxItemSet.reset( + new SfxItemSet( rRoot.GetDoc().GetDrawLayer()->GetItemPool() ) ); +} + +void XclImpChEscherFormat::ReadHeaderRecord( XclImpStream& rStrm ) +{ + // read from stream - CHESCHERFORMAT uses own ID for record continuation + XclImpDffPropSet aPropSet( rStrm.GetRoot() ); + rStrm.ResetRecord( true, rStrm.GetRecId() ); + rStrm >> aPropSet; + // get the data + aPropSet.FillToItemSet( *maData.mxItemSet ); + // get bitmap mode from DFF item set + sal_uInt32 nType = aPropSet.GetPropertyValue( DFF_Prop_fillType, mso_fillSolid ); + maPicFmt.mnBmpMode = (nType == mso_fillPicture) ? EXC_CHPICFORMAT_STRETCH : EXC_CHPICFORMAT_STACK; +} + +void XclImpChEscherFormat::ReadSubRecord( XclImpStream& rStrm ) +{ + switch( rStrm.GetRecId() ) + { + case EXC_ID_CHPICFORMAT: + rStrm >> maPicFmt.mnBmpMode >> maPicFmt.mnFormat >> maPicFmt.mnFlags >> maPicFmt.mfScale; + break; + } +} + +void XclImpChEscherFormat::Convert( const XclImpChRoot& rRoot, + ScfPropertySet& rPropSet, XclChObjectType eObjType ) const +{ + const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType ); + rRoot.ConvertEscherFormat( rPropSet, maData, maPicFmt, rFmtInfo.mePropMode ); +} + +// ---------------------------------------------------------------------------- + +XclImpChFrameBase::XclImpChFrameBase( const XclChFormatInfo& rFmtInfo ) +{ + if( rFmtInfo.mbCreateDefFrame ) switch( rFmtInfo.meDefFrameType ) + { + case EXC_CHFRAMETYPE_AUTO: + mxLineFmt.reset( new XclImpChLineFormat ); + if( rFmtInfo.mbIsFrame ) + mxAreaFmt.reset( new XclImpChAreaFormat ); + break; + case EXC_CHFRAMETYPE_INVISIBLE: + { + XclChLineFormat aLineFmt; + ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, false ); + aLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE; + mxLineFmt.reset( new XclImpChLineFormat( aLineFmt ) ); + if( rFmtInfo.mbIsFrame ) + { + XclChAreaFormat aAreaFmt; + ::set_flag( aAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, false ); + aAreaFmt.mnPattern = EXC_PATT_NONE; + mxAreaFmt.reset( new XclImpChAreaFormat( aAreaFmt ) ); + } + } + break; + default: + DBG_ERRORFILE( "XclImpChFrameBase::XclImpChFrameBase - unknown frame type" ); + } +} + +void XclImpChFrameBase::ReadSubRecord( XclImpStream& rStrm ) +{ + switch( rStrm.GetRecId() ) + { + case EXC_ID_CHLINEFORMAT: + mxLineFmt.reset( new XclImpChLineFormat ); + mxLineFmt->ReadChLineFormat( rStrm ); + break; + case EXC_ID_CHAREAFORMAT: + mxAreaFmt.reset( new XclImpChAreaFormat ); + mxAreaFmt->ReadChAreaFormat( rStrm ); + break; + case EXC_ID_CHESCHERFORMAT: + mxEscherFmt.reset( new XclImpChEscherFormat( rStrm.GetRoot() ) ); + mxEscherFmt->ReadRecordGroup( rStrm ); + break; + } +} + +void XclImpChFrameBase::ConvertLineBase( const XclImpChRoot& rRoot, + ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const +{ + if( mxLineFmt.is() ) + mxLineFmt->Convert( rRoot, rPropSet, eObjType, nFormatIdx ); +} + +void XclImpChFrameBase::ConvertAreaBase( const XclImpChRoot& rRoot, + ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const +{ + if( rRoot.GetFormatInfo( eObjType ).mbIsFrame ) + { + // CHESCHERFORMAT overrides CHAREAFORMAT (even if it is auto) + if( mxEscherFmt.is() ) + mxEscherFmt->Convert( rRoot, rPropSet, eObjType ); + else if( mxAreaFmt.is() ) + mxAreaFmt->Convert( rRoot, rPropSet, eObjType, nFormatIdx ); + } +} + +void XclImpChFrameBase::ConvertFrameBase( const XclImpChRoot& rRoot, + ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const +{ + ConvertLineBase( rRoot, rPropSet, eObjType, nFormatIdx ); + ConvertAreaBase( rRoot, rPropSet, eObjType, nFormatIdx ); +} + +// ---------------------------------------------------------------------------- + +XclImpChFrame::XclImpChFrame( const XclImpChRoot& rRoot, XclChObjectType eObjType ) : + XclImpChFrameBase( rRoot.GetFormatInfo( eObjType ) ), + XclImpChRoot( rRoot ), + meObjType( eObjType ) +{ +} + +void XclImpChFrame::ReadHeaderRecord( XclImpStream& rStrm ) +{ + rStrm >> maData.mnFormat >> maData.mnFlags; +} + +void XclImpChFrame::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData ) +{ + const XclImpPalette& rPal = GetPalette(); + + if( rLineData.IsVisible() && (!mxLineFmt || !mxLineFmt->HasLine()) ) + { + // line formatting + XclChLineFormat aLineFmt; + aLineFmt.maColor = rPal.GetColor( rLineData.mnColorIdx ); + switch( rLineData.mnStyle ) + { + case EXC_OBJ_LINE_SOLID: aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID; break; + case EXC_OBJ_LINE_DASH: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASH; break; + case EXC_OBJ_LINE_DOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DOT; break; + case EXC_OBJ_LINE_DASHDOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASHDOT; break; + case EXC_OBJ_LINE_DASHDOTDOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASHDOTDOT; break; + case EXC_OBJ_LINE_MEDTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_MEDTRANS; break; + case EXC_OBJ_LINE_DARKTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DARKTRANS; break; + case EXC_OBJ_LINE_LIGHTTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_LIGHTTRANS; break; + case EXC_OBJ_LINE_NONE: aLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE; break; + default: aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID; + } + switch( rLineData.mnWidth ) + { + case EXC_OBJ_LINE_HAIR: aLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR; break; + case EXC_OBJ_LINE_THIN: aLineFmt.mnWeight = EXC_CHLINEFORMAT_SINGLE; break; + case EXC_OBJ_LINE_MEDIUM: aLineFmt.mnWeight = EXC_CHLINEFORMAT_DOUBLE; break; + case EXC_OBJ_LINE_THICK: aLineFmt.mnWeight = EXC_CHLINEFORMAT_TRIPLE; break; + default: aLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR; + } + ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, rLineData.IsAuto() ); + mxLineFmt.reset( new XclImpChLineFormat( aLineFmt ) ); + } + + if( rFillData.IsFilled() && (!mxAreaFmt || !mxAreaFmt->HasArea()) && !mxEscherFmt ) + { + // area formatting + XclChAreaFormat aAreaFmt; + aAreaFmt.maPattColor = rPal.GetColor( rFillData.mnPattColorIdx ); + aAreaFmt.maBackColor = rPal.GetColor( rFillData.mnBackColorIdx ); + aAreaFmt.mnPattern = rFillData.mnPattern; + ::set_flag( aAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, rFillData.IsAuto() ); + mxAreaFmt.reset( new XclImpChAreaFormat( aAreaFmt ) ); + } +} + +void XclImpChFrame::Convert( ScfPropertySet& rPropSet ) const +{ + ConvertFrameBase( GetChRoot(), rPropSet, meObjType ); +} + +// Source links =============================================================== + +namespace { + +/** Creates a labeled data sequence object, adds link for series title if present. */ +Reference< XLabeledDataSequence > lclCreateLabeledDataSequence( + XclImpChSourceLinkRef xValueLink, const OUString& rValueRole, + const XclImpChSourceLink* pTitleLink = 0 ) +{ + // create data sequence for values and title + Reference< XDataSequence > xValueSeq; + if( xValueLink.is() ) + xValueSeq = xValueLink->CreateDataSequence( rValueRole ); + Reference< XDataSequence > xTitleSeq; + if( pTitleLink ) + xTitleSeq = pTitleLink->CreateDataSequence( EXC_CHPROP_ROLE_LABEL ); + + // create the labeled data sequence, if values or title are present + Reference< XLabeledDataSequence > xLabeledSeq; + if( xValueSeq.is() || xTitleSeq.is() ) + xLabeledSeq.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_LABELEDDATASEQ ), UNO_QUERY ); + if( xLabeledSeq.is() ) + { + if( xValueSeq.is() ) + xLabeledSeq->setValues( xValueSeq ); + if( xTitleSeq.is() ) + xLabeledSeq->setLabel( xTitleSeq ); + } + return xLabeledSeq; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclImpChSourceLink::XclImpChSourceLink( const XclImpChRoot& rRoot ) : + XclImpChRoot( rRoot ) +{ +} + +XclImpChSourceLink::~XclImpChSourceLink() +{ +} + +void XclImpChSourceLink::ReadChSourceLink( XclImpStream& rStrm ) +{ + rStrm >> maData.mnDestType + >> maData.mnLinkType + >> maData.mnFlags + >> maData.mnNumFmtIdx; + + mxTokenArray.reset(); + if( GetLinkType() == EXC_CHSRCLINK_WORKSHEET ) + { + // read token array + XclTokenArray aXclTokArr; + rStrm >> aXclTokArr; + + // convert BIFF formula tokens to Calc token array + if( const ScTokenArray* pTokens = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CHART, aXclTokArr ) ) + mxTokenArray.reset( pTokens->Clone() ); + } + + // try to read a following CHSTRING record + if( (rStrm.GetNextRecId() == EXC_ID_CHSTRING) && rStrm.StartNextRecord() ) + { + mxString.reset( new XclImpString ); + rStrm.Ignore( 2 ); + mxString->Read( rStrm, EXC_STR_8BITLENGTH | EXC_STR_SEPARATEFORMATS ); + } +} + +void XclImpChSourceLink::SetString( const String& rString ) +{ + if( !mxString ) + mxString.reset( new XclImpString ); + mxString->SetText( rString ); +} + +void XclImpChSourceLink::SetTextFormats( const XclFormatRunVec& rFormats ) +{ + if( mxString.is() ) + mxString->SetFormats( rFormats ); +} + +sal_uInt16 XclImpChSourceLink::GetCellCount() const +{ + sal_uInt32 nCellCount = 0; + if( mxTokenArray.is() ) + { + mxTokenArray->Reset(); + for( const FormulaToken* pToken = mxTokenArray->First(); pToken; pToken = mxTokenArray->Next() ) + { + switch( pToken->GetType() ) + { + case ::formula::svSingleRef: + case ::formula::svExternalSingleRef: + // single cell + ++nCellCount; + break; + case ::formula::svDoubleRef: + case ::formula::svExternalDoubleRef: + { + // cell range + const ScComplexRefData& rComplexRef = static_cast< const ScToken* >( pToken )->GetDoubleRef(); + const ScSingleRefData& rRef1 = rComplexRef.Ref1; + const ScSingleRefData& rRef2 = rComplexRef.Ref2; + sal_uInt32 nTabs = static_cast< sal_uInt32 >( rRef2.nTab - rRef1.nTab + 1 ); + sal_uInt32 nCols = static_cast< sal_uInt32 >( rRef2.nCol - rRef1.nCol + 1 ); + sal_uInt32 nRows = static_cast< sal_uInt32 >( rRef2.nRow - rRef1.nRow + 1 ); + nCellCount += nCols * nRows * nTabs; + } + break; + default: ; + } + } + } + return limit_cast< sal_uInt16 >( nCellCount ); +} + +void XclImpChSourceLink::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const +{ + bool bLinkToSource = ::get_flag( maData.mnFlags, EXC_CHSRCLINK_NUMFMT ); + sal_uInt32 nScNumFmt = bLinkToSource ? GetNumFmtBuffer().GetScFormat( maData.mnNumFmtIdx ) : NUMBERFORMAT_ENTRY_NOT_FOUND; + OUString aPropName = bPercent ? EXC_CHPROP_PERCENTAGENUMFMT : EXC_CHPROP_NUMBERFORMAT; + if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND ) + rPropSet.SetProperty( aPropName, static_cast< sal_Int32 >( nScNumFmt ) ); + else + // restore 'link to source' at data point (series may contain manual number format) + rPropSet.SetAnyProperty( aPropName, Any() ); +} + +Reference< XDataSequence > XclImpChSourceLink::CreateDataSequence( const OUString& rRole ) const +{ + Reference< XDataSequence > xDataSeq; + Reference< XDataProvider > xDataProv = GetDataProvider(); + if( xDataProv.is() && mxTokenArray.is() ) + { + ScCompiler aComp( GetDocPtr(), ScAddress(), *mxTokenArray ); + aComp.SetGrammar( ::formula::FormulaGrammar::GRAM_ENGLISH ); + OUStringBuffer aRangeRep; + aComp.CreateStringFromTokenArray( aRangeRep ); + try + { + xDataSeq = xDataProv->createDataSequenceByRangeRepresentation( aRangeRep.makeStringAndClear() ); + // set sequence role + ScfPropertySet aSeqProp( xDataSeq ); + aSeqProp.SetProperty( EXC_CHPROP_ROLE, rRole ); + } + catch( Exception& ) + { +// DBG_ERRORFILE( "XclImpChSourceLink::CreateDataSequence - cannot create data sequence" ); + } + } + return xDataSeq; +} + +Sequence< Reference< XFormattedString > > XclImpChSourceLink::CreateStringSequence( + const XclImpChRoot& rRoot, sal_uInt16 nLeadFontIdx, const Color& rLeadFontColor ) const +{ + ::std::vector< Reference< XFormattedString > > aStringVec; + if( mxString.is() ) + { + for( XclImpStringIterator aIt( *mxString ); aIt.Is(); ++aIt ) + { + Reference< XFormattedString > xFmtStr( + ScfApiHelper::CreateInstance( SERVICE_CHART2_FORMATTEDSTRING ), UNO_QUERY ); + if( xFmtStr.is() ) + { + // set text data + xFmtStr->setString( aIt.GetPortionText() ); + + // set font formatting and font color + ScfPropertySet aStringProp( xFmtStr ); + sal_uInt16 nFontIdx = aIt.GetPortionFont(); + if( (nFontIdx == EXC_FONT_NOTFOUND) && (aIt.GetPortionIndex() == 0) ) + // leading unformatted portion - use passed font settings + rRoot.ConvertFont( aStringProp, nLeadFontIdx, &rLeadFontColor ); + else + rRoot.ConvertFont( aStringProp, nFontIdx ); + + // add string to vector of strings + aStringVec.push_back( xFmtStr ); + } + } + } + return ScfApiHelper::VectorToSequence( aStringVec ); +} + +void XclImpChSourceLink::FillSourceLink( ::std::vector< ScSharedTokenRef >& rTokens ) const +{ + if( !mxTokenArray.is() ) + // no links to fill. + return; + + mxTokenArray->Reset(); + for (FormulaToken* p = mxTokenArray->First(); p; p = mxTokenArray->Next()) + { + ScSharedTokenRef pToken(static_cast<ScToken*>(p->Clone())); + if (ScRefTokenHelper::isRef(pToken)) + // This is a reference token. Store it. + ScRefTokenHelper::join(rTokens, pToken); + } +} + +// Text ======================================================================= + +XclImpChFontBase::~XclImpChFontBase() +{ +} + +void XclImpChFontBase::ConvertFontBase( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const +{ + Color aFontColor = GetFontColor(); + rRoot.ConvertFont( rPropSet, GetFontIndex(), &aFontColor ); +} + +void XclImpChFontBase::ConvertRotationBase( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet, bool bSupportsStacked ) const +{ + rRoot.GetChartPropSetHelper().WriteRotationProperties( rPropSet, GetRotation(), bSupportsStacked ); +} + +// ---------------------------------------------------------------------------- + +XclImpChFont::XclImpChFont() : + mnFontIdx( EXC_FONT_NOTFOUND ) +{ +} + +void XclImpChFont::ReadChFont( XclImpStream& rStrm ) +{ + rStrm >> mnFontIdx; +} + +// ---------------------------------------------------------------------------- + +XclImpChText::XclImpChText( const XclImpChRoot& rRoot ) : + XclImpChRoot( rRoot ) +{ +} + +void XclImpChText::ReadHeaderRecord( XclImpStream& rStrm ) +{ + rStrm >> maData.mnHAlign + >> maData.mnVAlign + >> maData.mnBackMode + >> maData.maTextColor + >> maData.maRect + >> maData.mnFlags; + + if( GetBiff() == EXC_BIFF8 ) + { + // #116397# BIFF8: index into palette used instead of RGB data + maData.maTextColor = GetPalette().GetColor( rStrm.ReaduInt16() ); + // placement and rotation + rStrm >> maData.mnFlags2 >> maData.mnRotation; + } + else + { + // BIFF2-BIFF7: get rotation from text orientation + sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 8, 3 ); + maData.mnRotation = XclTools::GetXclRotFromOrient( nOrient ); + } +} + +void XclImpChText::ReadSubRecord( XclImpStream& rStrm ) +{ + switch( rStrm.GetRecId() ) + { + case EXC_ID_CHFRAMEPOS: + mxFramePos.reset( new XclImpChFramePos ); + mxFramePos->ReadChFramePos( rStrm ); + break; + case EXC_ID_CHFONT: + mxFont.reset( new XclImpChFont ); + mxFont->ReadChFont( rStrm ); + break; + case EXC_ID_CHFORMATRUNS: + if( GetBiff() == EXC_BIFF8 ) + XclImpString::ReadFormats( rStrm, maFormats ); + break; + case EXC_ID_CHSOURCELINK: + mxSrcLink.reset( new XclImpChSourceLink( GetChRoot() ) ); + mxSrcLink->ReadChSourceLink( rStrm ); + break; + case EXC_ID_CHFRAME: + mxFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_TEXT ) ); + mxFrame->ReadRecordGroup( rStrm ); + break; + case EXC_ID_CHOBJECTLINK: + rStrm >> maObjLink.mnTarget >> maObjLink.maPointPos.mnSeriesIdx >> maObjLink.maPointPos.mnPointIdx; + break; + case EXC_ID_CHFRLABELPROPS: + ReadChFrLabelProps( rStrm ); + break; + case EXC_ID_CHEND: + if( mxSrcLink.is() && !maFormats.empty() ) + mxSrcLink->SetTextFormats( maFormats ); + break; + } +} + +sal_uInt16 XclImpChText::GetFontIndex() const +{ + return mxFont.is() ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND; +} + +Color XclImpChText::GetFontColor() const +{ + return ::get_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR ) ? GetFontAutoColor() : maData.maTextColor; +} + +sal_uInt16 XclImpChText::GetRotation() const +{ + return maData.mnRotation; +} + +void XclImpChText::SetString( const String& rString ) +{ + if( !mxSrcLink ) + mxSrcLink.reset( new XclImpChSourceLink( GetChRoot() ) ); + mxSrcLink->SetString( rString ); +} + +void XclImpChText::UpdateText( const XclImpChText* pParentText ) +{ + if( pParentText ) + { + // update missing members + if( !mxFrame ) + mxFrame = pParentText->mxFrame; + if( !mxFont ) + { + mxFont = pParentText->mxFont; + // text color is taken from CHTEXT record, not from font in CHFONT + ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR, ::get_flag( pParentText->maData.mnFlags, EXC_CHTEXT_AUTOCOLOR ) ); + maData.maTextColor = pParentText->maData.maTextColor; + } + } +} + +void XclImpChText::UpdateDataLabel( bool bCateg, bool bValue, bool bPercent ) +{ + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG, bCateg ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE, bValue ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT, bPercent ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bCateg && bPercent ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bCateg && !bValue && !bPercent ); +} + +void XclImpChText::ConvertFont( ScfPropertySet& rPropSet ) const +{ + ConvertFontBase( GetChRoot(), rPropSet ); +} + +void XclImpChText::ConvertRotation( ScfPropertySet& rPropSet, bool bSupportsStacked ) const +{ + ConvertRotationBase( GetChRoot(), rPropSet, bSupportsStacked ); +} + +void XclImpChText::ConvertFrame( ScfPropertySet& rPropSet ) const +{ + if( mxFrame.is() ) + mxFrame->Convert( rPropSet ); +} + +void XclImpChText::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const +{ + if( mxSrcLink.is() ) + mxSrcLink->ConvertNumFmt( rPropSet, bPercent ); +} + +void XclImpChText::ConvertDataLabel( ScfPropertySet& rPropSet, const XclChTypeInfo& rTypeInfo ) const +{ + // existing CHFRLABELPROPS record wins over flags from CHTEXT + sal_uInt16 nShowFlags = mxLabelProps.is() ? mxLabelProps->mnFlags : maData.mnFlags; + sal_uInt16 SHOWANYCATEG = mxLabelProps.is() ? EXC_CHFRLABELPROPS_SHOWCATEG : (EXC_CHTEXT_SHOWCATEGPERC | EXC_CHTEXT_SHOWCATEG); + sal_uInt16 SHOWANYVALUE = mxLabelProps.is() ? EXC_CHFRLABELPROPS_SHOWVALUE : EXC_CHTEXT_SHOWVALUE; + sal_uInt16 SHOWANYPERCENT = mxLabelProps.is() ? EXC_CHFRLABELPROPS_SHOWPERCENT : (EXC_CHTEXT_SHOWPERCENT | EXC_CHTEXT_SHOWCATEGPERC); + sal_uInt16 SHOWANYBUBBLE = mxLabelProps.is() ? EXC_CHFRLABELPROPS_SHOWBUBBLE : EXC_CHTEXT_SHOWBUBBLE; + + // get raw flags for label values + bool bShowNone = IsDeleted(); + bool bShowCateg = !bShowNone && ::get_flag( nShowFlags, SHOWANYCATEG ); + bool bShowPercent = !bShowNone && ::get_flag( nShowFlags, SHOWANYPERCENT ); + bool bShowValue = !bShowNone && ::get_flag( nShowFlags, SHOWANYVALUE ); + bool bShowBubble = !bShowNone && ::get_flag( nShowFlags, SHOWANYBUBBLE ); + + // adjust to Chart2 behaviour + if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES ) + bShowValue = bShowBubble; // Chart2 bubble charts show bubble size if 'ShowValue' is set + + // other flags + bool bShowAny = bShowValue || bShowPercent || bShowCateg; + bool bShowSymbol = bShowAny && ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL ); + + // create API struct for label values, set API label separator + cssc2::DataPointLabel aPointLabel( bShowValue, bShowPercent, bShowCateg, bShowSymbol ); + rPropSet.SetProperty( EXC_CHPROP_LABEL, aPointLabel ); + String aSep = mxLabelProps.is() ? mxLabelProps->maSeparator : String( sal_Unicode( '\n' ) ); + if( aSep.Len() == 0 ) + aSep = CREATE_STRING( "; " ); + rPropSet.SetStringProperty( EXC_CHPROP_LABELSEPARATOR, aSep ); + + // text properties of attached label + if( bShowAny ) + { + ConvertFont( rPropSet ); + ConvertRotation( rPropSet, false ); + // label placement + using namespace cssc::DataLabelPlacement; + sal_Int32 nPlacement = rTypeInfo.mnDefaultLabelPos; + switch( ::extract_value< sal_uInt16 >( maData.mnFlags2, 0, 4 ) ) + { + case EXC_CHTEXT_POS_DEFAULT: nPlacement = rTypeInfo.mnDefaultLabelPos; break; + case EXC_CHTEXT_POS_OUTSIDE: nPlacement = OUTSIDE; break; + case EXC_CHTEXT_POS_INSIDE: nPlacement = INSIDE; break; + case EXC_CHTEXT_POS_CENTER: nPlacement = CENTER; break; + case EXC_CHTEXT_POS_AXIS: nPlacement = NEAR_ORIGIN; break; + case EXC_CHTEXT_POS_ABOVE: nPlacement = TOP; break; + case EXC_CHTEXT_POS_BELOW: nPlacement = BOTTOM; break; + case EXC_CHTEXT_POS_LEFT: nPlacement = LEFT; break; + case EXC_CHTEXT_POS_RIGHT: nPlacement = RIGHT; break; + case EXC_CHTEXT_POS_AUTO: nPlacement = AVOID_OVERLAP; break; + } + rPropSet.SetProperty( EXC_CHPROP_LABELPLACEMENT, nPlacement ); + // label number format (percentage format wins over value format) + if( bShowPercent || bShowValue ) + ConvertNumFmt( rPropSet, bShowPercent ); + } +} + +Reference< XTitle > XclImpChText::CreateTitle() const +{ + Reference< XTitle > xTitle; + if( mxSrcLink.is() && mxSrcLink->HasString() ) + { + // create the formatted strings + Sequence< Reference< XFormattedString > > aStringSeq( + mxSrcLink->CreateStringSequence( GetChRoot(), GetFontIndex(), GetFontColor() ) ); + if( aStringSeq.hasElements() ) + { + // create the title object + xTitle.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_TITLE ), UNO_QUERY ); + if( xTitle.is() ) + { + // set the formatted strings + xTitle->setText( aStringSeq ); + // more title formatting properties + ScfPropertySet aTitleProp( xTitle ); + ConvertFrame( aTitleProp ); + ConvertRotation( aTitleProp, true ); + } + } + } + return xTitle; +} + +void XclImpChText::ConvertTitlePosition( const XclChTextKey& rTitleKey ) const +{ + if( !mxFramePos ) return; + + const XclChFramePos& rPosData = mxFramePos->GetFramePosData(); + OSL_ENSURE( (rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rPosData.mnBRMode == EXC_CHFRAMEPOS_PARENT), + "XclImpChText::ConvertTitlePosition - unexpected frame position mode" ); + + /* Check if title is moved manually. To get the actual position of the + title, we do some kind of hack and use the values from the CHTEXT + record, effectively ignoring the contents of the CHFRAMEPOS record + which contains the position relative to the default title position + (according to the spec, the CHFRAMEPOS supersedes the CHTEXT record). + Especially when it comes to axis titles, things would become very + complicated here, because the relative title position is stored in a + measurement unit that is dependent on the size of the inner plot area, + the interpretation of the X and Y coordinate is dependent on the + direction of the axis, and in 3D charts, and the title default + positions are dependent on the 3D view settings (rotation, elevation, + and perspective). Thus, it is easier to assume that the creator has + written out the correct absolute position and size of the title in the + CHTEXT record. This is assured by checking that the shape size stored + in the CHTEXT record is non-zero. */ + if( (rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) && + ((rPosData.maRect.mnX != 0) || (rPosData.maRect.mnY != 0)) && + (maData.maRect.mnWidth > 0) && (maData.maRect.mnHeight > 0) ) try + { + Reference< XShape > xTitleShape( GetTitleShape( rTitleKey ), UNO_SET_THROW ); + // the call to XShape.getSize() may recalc the chart view + ::com::sun::star::awt::Size aTitleSize = xTitleShape->getSize(); + // rotated titles need special handling... + sal_Int32 nScRot = XclTools::GetScRotation( GetRotation(), 0 ); + double fRad = nScRot * F_PI18000; + double fSin = fabs( sin( fRad ) ); + double fCos = fabs( cos( fRad ) ); + ::com::sun::star::awt::Size aBoundSize( + static_cast< sal_Int32 >( fCos * aTitleSize.Width + fSin * aTitleSize.Height + 0.5 ), + static_cast< sal_Int32 >( fSin * aTitleSize.Width + fCos * aTitleSize.Height + 0.5 ) ); + // calculate the title position from the values in the CHTEXT record + ::com::sun::star::awt::Point aTitlePos( + CalcHmmFromChartX( maData.maRect.mnX ), + CalcHmmFromChartY( maData.maRect.mnY ) ); + // add part of height to X direction, if title is rotated down (clockwise) + if( nScRot > 18000 ) + aTitlePos.X += static_cast< sal_Int32 >( fSin * aTitleSize.Height + 0.5 ); + // add part of width to Y direction, if title is rotated up (counterclockwise) + else if( nScRot > 0 ) + aTitlePos.Y += static_cast< sal_Int32 >( fSin * aTitleSize.Width + 0.5 ); + // set the resulting position at the title shape + xTitleShape->setPosition( aTitlePos ); + } + catch( Exception& ) + { + } +} + +void XclImpChText::ReadChFrLabelProps( XclImpStream& rStrm ) +{ + if( GetBiff() == EXC_BIFF8 ) + { + mxLabelProps.reset( new XclChFrLabelProps ); + sal_uInt16 nSepLen; + rStrm.Ignore( 12 ); + rStrm >> mxLabelProps->mnFlags >> nSepLen; + if( nSepLen > 0 ) + mxLabelProps->maSeparator = rStrm.ReadUniString( nSepLen ); + } +} + +namespace { + +void lclUpdateText( XclImpChTextRef& rxText, XclImpChTextRef xDefText ) +{ + if( rxText.is() ) + rxText->UpdateText( xDefText.get() ); + else + rxText = xDefText; +} + +void lclFinalizeTitle( XclImpChTextRef& rxTitle, XclImpChTextRef xDefText, const String& rAutoTitle ) +{ + /* Do not update a title, if it is not visible (if rxTitle is null). + Existing reference indicates enabled title. */ + if( rxTitle.is() ) + { + if( !rxTitle->HasString() ) + rxTitle->SetString( rAutoTitle ); + if( rxTitle->HasString() ) + rxTitle->UpdateText( xDefText.get() ); + else + rxTitle.reset(); + } +} + +} // namespace + +// Data series ================================================================ + +void XclImpChMarkerFormat::ReadChMarkerFormat( XclImpStream& rStrm ) +{ + rStrm >> maData.maLineColor >> maData.maFillColor >> maData.mnMarkerType >> maData.mnFlags; + + const XclImpRoot& rRoot = rStrm.GetRoot(); + if( rRoot.GetBiff() == EXC_BIFF8 ) + { + // #116397# BIFF8: index into palette used instead of RGB data + const XclImpPalette& rPal = rRoot.GetPalette(); + maData.maLineColor = rPal.GetColor( rStrm.ReaduInt16() ); + maData.maFillColor = rPal.GetColor( rStrm.ReaduInt16() ); + // marker size + rStrm >> maData.mnMarkerSize; + } +} + +void XclImpChMarkerFormat::Convert( const XclImpChRoot& rRoot, + ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx, sal_Int16 nLineWeight ) const +{ + if( IsAuto() ) + { + XclChMarkerFormat aMarkerFmt; + // line and fill color of the symbol are equal to series line color + //! TODO: Excel sets no fill color for specific symbols (e.g. cross) + aMarkerFmt.maLineColor = aMarkerFmt.maFillColor = rRoot.GetSeriesLineAutoColor( nFormatIdx ); + switch( nLineWeight ) + { + case EXC_CHLINEFORMAT_HAIR: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_HAIRSIZE; break; + case EXC_CHLINEFORMAT_SINGLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_SINGLESIZE; break; + case EXC_CHLINEFORMAT_DOUBLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_DOUBLESIZE; break; + case EXC_CHLINEFORMAT_TRIPLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_TRIPLESIZE; break; + default: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_SINGLESIZE; + } + aMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx ); + rRoot.GetChartPropSetHelper().WriteMarkerProperties( rPropSet, aMarkerFmt ); + } + else + { + rRoot.GetChartPropSetHelper().WriteMarkerProperties( rPropSet, maData ); + } +} + +void XclImpChMarkerFormat::ConvertColor( const XclImpChRoot& rRoot, + ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const +{ + Color aLineColor = IsAuto() ? rRoot.GetSeriesLineAutoColor( nFormatIdx ) : maData.maFillColor; + rPropSet.SetColorProperty( EXC_CHPROP_COLOR, aLineColor ); +} + +// ---------------------------------------------------------------------------- + +XclImpChPieFormat::XclImpChPieFormat() : + mnPieDist( 0 ) +{ +} + +void XclImpChPieFormat::ReadChPieFormat( XclImpStream& rStrm ) +{ + rStrm >> mnPieDist; +} + +void XclImpChPieFormat::Convert( ScfPropertySet& rPropSet ) const +{ + double fApiDist = ::std::min< double >( mnPieDist / 100.0, 1.0 ); + rPropSet.SetProperty( EXC_CHPROP_OFFSET, fApiDist ); +} + +// ---------------------------------------------------------------------------- + +XclImpChSeriesFormat::XclImpChSeriesFormat() : + mnFlags( 0 ) +{ +} + +void XclImpChSeriesFormat::ReadChSeriesFormat( XclImpStream& rStrm ) +{ + rStrm >> mnFlags; +} + +// ---------------------------------------------------------------------------- + +void XclImpCh3dDataFormat::ReadCh3dDataFormat( XclImpStream& rStrm ) +{ + rStrm >> maData.mnBase >> maData.mnTop; +} + +void XclImpCh3dDataFormat::Convert( ScfPropertySet& rPropSet ) const +{ + using namespace ::com::sun::star::chart2::DataPointGeometry3D; + sal_Int32 nApiType = (maData.mnBase == EXC_CH3DDATAFORMAT_RECT) ? + ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CUBOID : PYRAMID) : + ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CYLINDER : CONE); + rPropSet.SetProperty( EXC_CHPROP_GEOMETRY3D, nApiType ); +} + +// ---------------------------------------------------------------------------- + +XclImpChAttachedLabel::XclImpChAttachedLabel( const XclImpChRoot& rRoot ) : + XclImpChRoot( rRoot ), + mnFlags( 0 ) +{ +} + +void XclImpChAttachedLabel::ReadChAttachedLabel( XclImpStream& rStrm ) +{ + rStrm >> mnFlags; +} + +XclImpChTextRef XclImpChAttachedLabel::CreateDataLabel( XclImpChTextRef xParent ) const +{ + const sal_uInt16 EXC_CHATTLABEL_SHOWANYVALUE = EXC_CHATTLABEL_SHOWVALUE; + const sal_uInt16 EXC_CHATTLABEL_SHOWANYPERCENT = EXC_CHATTLABEL_SHOWPERCENT | EXC_CHATTLABEL_SHOWCATEGPERC; + const sal_uInt16 EXC_CHATTLABEL_SHOWANYCATEG = EXC_CHATTLABEL_SHOWCATEG | EXC_CHATTLABEL_SHOWCATEGPERC; + + XclImpChTextRef xLabel( xParent.is() ? new XclImpChText( *xParent ) : new XclImpChText( GetChRoot() ) ); + xLabel->UpdateDataLabel( + ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYCATEG ), + ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYVALUE ), + ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYPERCENT ) ); + return xLabel; +} + +// ---------------------------------------------------------------------------- + +XclImpChDataFormat::XclImpChDataFormat( const XclImpChRoot& rRoot ) : + XclImpChRoot( rRoot ) +{ +} + +void XclImpChDataFormat::ReadHeaderRecord( XclImpStream& rStrm ) +{ + rStrm >> maData.maPointPos.mnPointIdx + >> maData.maPointPos.mnSeriesIdx + >> maData.mnFormatIdx + >> maData.mnFlags; +} + +void XclImpChDataFormat::ReadSubRecord( XclImpStream& rStrm ) +{ + switch( rStrm.GetRecId() ) + { + case EXC_ID_CHMARKERFORMAT: + mxMarkerFmt.reset( new XclImpChMarkerFormat ); + mxMarkerFmt->ReadChMarkerFormat( rStrm ); + break; + case EXC_ID_CHPIEFORMAT: + mxPieFmt.reset( new XclImpChPieFormat ); + mxPieFmt->ReadChPieFormat( rStrm ); + break; + case EXC_ID_CHSERIESFORMAT: + mxSeriesFmt.reset( new XclImpChSeriesFormat ); + mxSeriesFmt->ReadChSeriesFormat( rStrm ); + break; + case EXC_ID_CH3DDATAFORMAT: + mx3dDataFmt.reset( new XclImpCh3dDataFormat ); + mx3dDataFmt->ReadCh3dDataFormat( rStrm ); + break; + case EXC_ID_CHATTACHEDLABEL: + mxAttLabel.reset( new XclImpChAttachedLabel( GetChRoot() ) ); + mxAttLabel->ReadChAttachedLabel( rStrm ); + break; + default: + XclImpChFrameBase::ReadSubRecord( rStrm ); + } +} + +void XclImpChDataFormat::SetPointPos( const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx ) +{ + maData.maPointPos = rPointPos; + maData.mnFormatIdx = nFormatIdx; +} + +void XclImpChDataFormat::UpdateGroupFormat( const XclChExtTypeInfo& rTypeInfo ) +{ + // remove formats not used for the current chart type + RemoveUnusedFormats( rTypeInfo ); +} + +void XclImpChDataFormat::UpdateSeriesFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pGroupFmt ) +{ + // update missing formats from passed chart type group format + if( pGroupFmt ) + { + if( !mxLineFmt ) + mxLineFmt = pGroupFmt->mxLineFmt; + if( !mxAreaFmt && !mxEscherFmt ) + { + mxAreaFmt = pGroupFmt->mxAreaFmt; + mxEscherFmt = pGroupFmt->mxEscherFmt; + } + if( !mxMarkerFmt ) + mxMarkerFmt = pGroupFmt->mxMarkerFmt; + if( !mxPieFmt ) + mxPieFmt = pGroupFmt->mxPieFmt; + if( !mxSeriesFmt ) + mxSeriesFmt = pGroupFmt->mxSeriesFmt; + if( !mx3dDataFmt ) + mx3dDataFmt = pGroupFmt->mx3dDataFmt; + if( !mxAttLabel ) + mxAttLabel = pGroupFmt->mxAttLabel; + } + + /* Create missing but required formats. Existing line, area, and marker + format objects are needed to create automatic series formatting. */ + if( !mxLineFmt ) + mxLineFmt.reset( new XclImpChLineFormat ); + if( !mxAreaFmt && !mxEscherFmt ) + mxAreaFmt.reset( new XclImpChAreaFormat ); + if( !mxMarkerFmt ) + mxMarkerFmt.reset( new XclImpChMarkerFormat ); + + // remove formats not used for the current chart type + RemoveUnusedFormats( rTypeInfo ); + // update data label + UpdateDataLabel( pGroupFmt ); +} + +void XclImpChDataFormat::UpdatePointFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pSeriesFmt ) +{ + // remove formats if they are automatic in this and in the passed series format + if( pSeriesFmt ) + { + if( IsAutoLine() && pSeriesFmt->IsAutoLine() ) + mxLineFmt.reset(); + if( IsAutoArea() && pSeriesFmt->IsAutoArea() ) + mxAreaFmt.reset(); + if( IsAutoMarker() && pSeriesFmt->IsAutoMarker() ) + mxMarkerFmt.reset(); + mxSeriesFmt.reset(); + } + + // Excel ignores 3D bar format for single data points + mx3dDataFmt.reset(); + // remove point line formats for linear chart types, TODO: implement in OOChart + if( !rTypeInfo.IsSeriesFrameFormat() ) + mxLineFmt.reset(); + + // remove formats not used for the current chart type + RemoveUnusedFormats( rTypeInfo ); + // update data label + UpdateDataLabel( pSeriesFmt ); +} + +void XclImpChDataFormat::UpdateTrendLineFormat() +{ + if( !mxLineFmt ) + mxLineFmt.reset( new XclImpChLineFormat ); + mxAreaFmt.reset(); + mxEscherFmt.reset(); + mxMarkerFmt.reset(); + mxPieFmt.reset(); + mxSeriesFmt.reset(); + mx3dDataFmt.reset(); + mxAttLabel.reset(); + // update data label + UpdateDataLabel( 0 ); +} + +void XclImpChDataFormat::Convert( ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo ) const +{ + // line and area format + ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType(), maData.mnFormatIdx ); +#if EXC_CHART2_3DBAR_HAIRLINES_ONLY + // #i83151# only hair lines in 3D charts with filled data points + if( rTypeInfo.mb3dChart && rTypeInfo.IsSeriesFrameFormat() && mxLineFmt.is() && mxLineFmt->HasLine() ) + rPropSet.SetProperty< sal_Int32 >( CREATE_OUSTRING( "BorderWidth" ), 0 ); +#endif + + // other formatting + if( mxMarkerFmt.is() ) + mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx, GetLineWeight() ); + if( mxPieFmt.is() ) + mxPieFmt->Convert( rPropSet ); + if( mx3dDataFmt.is() ) + mx3dDataFmt->Convert( rPropSet ); + if( mxLabel.is() ) + mxLabel->ConvertDataLabel( rPropSet, rTypeInfo ); + + // 3D settings + rPropSet.SetProperty< sal_Int16 >( EXC_CHPROP_PERCENTDIAGONAL, 0 ); + + /* Special case: set marker color as line color, if series line is not + visible. This makes the color visible in the marker area. + TODO: remove this if OOChart supports own colors in markers. */ + if( !rTypeInfo.IsSeriesFrameFormat() && !HasLine() && mxMarkerFmt.is() ) + mxMarkerFmt->ConvertColor( GetChRoot(), rPropSet, maData.mnFormatIdx ); +} + +void XclImpChDataFormat::ConvertLine( ScfPropertySet& rPropSet, XclChObjectType eObjType ) const +{ + ConvertLineBase( GetChRoot(), rPropSet, eObjType ); +} + +void XclImpChDataFormat::ConvertArea( ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const +{ + ConvertAreaBase( GetChRoot(), rPropSet, EXC_CHOBJTYPE_FILLEDSERIES, nFormatIdx ); +} + +void XclImpChDataFormat::RemoveUnusedFormats( const XclChExtTypeInfo& rTypeInfo ) +{ + // data point marker only in linear 2D charts + if( rTypeInfo.IsSeriesFrameFormat() ) + mxMarkerFmt.reset(); + // pie format only in pie/donut charts + if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE ) + mxPieFmt.reset(); + // 3D format only in 3D bar charts + if( !rTypeInfo.mb3dChart || (rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_BAR) ) + mx3dDataFmt.reset(); +} + +void XclImpChDataFormat::UpdateDataLabel( const XclImpChDataFormat* pParentFmt ) +{ + /* CHTEXT groups linked to data labels override existing CHATTACHEDLABEL + records. Only if there is a CHATTACHEDLABEL record without a CHTEXT + group, the contents of the CHATTACHEDLABEL record are used. In this + case a new CHTEXT group is created and filled with the settings from + the CHATTACHEDLABEL record. */ + XclImpChTextRef xDefText; + if( pParentFmt ) + xDefText = pParentFmt->GetDataLabel(); + if( !xDefText ) + xDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_DATALABEL ); + if( mxLabel.is() ) + mxLabel->UpdateText( xDefText.get() ); + else if( mxAttLabel.is() ) + mxLabel = mxAttLabel->CreateDataLabel( xDefText ); +} + +// ---------------------------------------------------------------------------- + +XclImpChSerTrendLine::XclImpChSerTrendLine( const XclImpChRoot& rRoot ) : + XclImpChRoot( rRoot ) +{ +} + +void XclImpChSerTrendLine::ReadChSerTrendLine( XclImpStream& rStrm ) +{ + rStrm >> maData.mnLineType + >> maData.mnOrder + >> maData.mfIntercept + >> maData.mnShowEquation + >> maData.mnShowRSquared + >> maData.mfForecastFor + >> maData.mfForecastBack; +} + +Reference< XRegressionCurve > XclImpChSerTrendLine::CreateRegressionCurve() const +{ + // trend line type + OUString aService; + switch( maData.mnLineType ) + { + case EXC_CHSERTREND_POLYNOMIAL: + // TODO: only linear trend lines are supported by OOChart (#i20819#) + if( maData.mnOrder == 1 ) + aService = SERVICE_CHART2_LINEARREGCURVE; + break; + case EXC_CHSERTREND_EXPONENTIAL: + aService = SERVICE_CHART2_EXPREGCURVE; + break; + case EXC_CHSERTREND_LOGARITHMIC: + aService = SERVICE_CHART2_LOGREGCURVE; + break; + case EXC_CHSERTREND_POWER: + aService = SERVICE_CHART2_POTREGCURVE; + break; + } + Reference< XRegressionCurve > xRegCurve; + if( aService.getLength() > 0 ) + xRegCurve.set( ScfApiHelper::CreateInstance( aService ), UNO_QUERY ); + + // trend line formatting + if( xRegCurve.is() && mxDataFmt.is() ) + { + ScfPropertySet aPropSet( xRegCurve ); + mxDataFmt->ConvertLine( aPropSet, EXC_CHOBJTYPE_TRENDLINE ); + + // #i83100# show equation and correlation coefficient + ScfPropertySet aLabelProp( xRegCurve->getEquationProperties() ); + aLabelProp.SetBoolProperty( EXC_CHPROP_SHOWEQUATION, maData.mnShowEquation != 0 ); + aLabelProp.SetBoolProperty( EXC_CHPROP_SHOWCORRELATION, maData.mnShowRSquared != 0 ); + + // #i83100# formatting of the equation text box + if( const XclImpChText* pLabel = mxDataFmt->GetDataLabel().get() ) + { + pLabel->ConvertFont( aLabelProp ); + pLabel->ConvertFrame( aLabelProp ); + pLabel->ConvertNumFmt( aLabelProp, false ); + } + } + + // missing features + // #i20819# polynomial trend lines + // #i66819# moving average trend lines + // #i5085# manual trend line size + // #i34093# manual crossing point + + return xRegCurve; +} + +// ---------------------------------------------------------------------------- + +XclImpChSerErrorBar::XclImpChSerErrorBar( const XclImpChRoot& rRoot ) : + XclImpChRoot( rRoot ) +{ +} + +void XclImpChSerErrorBar::ReadChSerErrorBar( XclImpStream& rStrm ) +{ + rStrm >> maData.mnBarType >> maData.mnSourceType >> maData.mnLineEnd; + rStrm.Ignore( 1 ); + rStrm >> maData.mfValue >> maData.mnValueCount; +} + +void XclImpChSerErrorBar::SetSeriesData( XclImpChSourceLinkRef xValueLink, XclImpChDataFormatRef xDataFmt ) +{ + mxValueLink = xValueLink; + mxDataFmt = xDataFmt; +} + +Reference< XLabeledDataSequence > XclImpChSerErrorBar::CreateValueSequence() const +{ + return lclCreateLabeledDataSequence( mxValueLink, XclChartHelper::GetErrorBarValuesRole( maData.mnBarType ) ); +} + +Reference< XPropertySet > XclImpChSerErrorBar::CreateErrorBar( const XclImpChSerErrorBar* pPosBar, const XclImpChSerErrorBar* pNegBar ) +{ + Reference< XPropertySet > xErrorBar; + + if( const XclImpChSerErrorBar* pPrimaryBar = pPosBar ? pPosBar : pNegBar ) + { + xErrorBar.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_ERRORBAR ), UNO_QUERY ); + ScfPropertySet aBarProp( xErrorBar ); + + // plus/minus bars visible? + aBarProp.SetBoolProperty( EXC_CHPROP_SHOWPOSITIVEERROR, pPosBar != 0 ); + aBarProp.SetBoolProperty( EXC_CHPROP_SHOWNEGATIVEERROR, pNegBar != 0 ); + + // type of displayed error + switch( pPrimaryBar->maData.mnSourceType ) + { + case EXC_CHSERERR_PERCENT: + aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::RELATIVE ); + aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue ); + aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue ); + break; + case EXC_CHSERERR_FIXED: + aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::ABSOLUTE ); + aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue ); + aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue ); + break; + case EXC_CHSERERR_STDDEV: + aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_DEVIATION ); + aBarProp.SetProperty( EXC_CHPROP_WEIGHT, pPrimaryBar->maData.mfValue ); + break; + case EXC_CHSERERR_STDERR: + aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_ERROR ); + break; + case EXC_CHSERERR_CUSTOM: + { + aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::FROM_DATA ); + // attach data sequences to erorr bar + Reference< XDataSink > xDataSink( xErrorBar, UNO_QUERY ); + if( xDataSink.is() ) + { + // create vector of all value sequences + ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec; + // add positive values + if( pPosBar ) + { + Reference< XLabeledDataSequence > xValueSeq = pPosBar->CreateValueSequence(); + if( xValueSeq.is() ) + aLabeledSeqVec.push_back( xValueSeq ); + } + // add negative values + if( pNegBar ) + { + Reference< XLabeledDataSequence > xValueSeq = pNegBar->CreateValueSequence(); + if( xValueSeq.is() ) + aLabeledSeqVec.push_back( xValueSeq ); + } + // attach labeled data sequences to series + if( aLabeledSeqVec.empty() ) + xErrorBar.clear(); + else + xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) ); + } + } + break; + default: + xErrorBar.clear(); + } + + // error bar formatting + if( pPrimaryBar->mxDataFmt.is() && xErrorBar.is() ) + pPrimaryBar->mxDataFmt->ConvertLine( aBarProp, EXC_CHOBJTYPE_ERRORBAR ); + } + + return xErrorBar; +} + +// ---------------------------------------------------------------------------- + +XclImpChSeries::XclImpChSeries( const XclImpChRoot& rRoot, sal_uInt16 nSeriesIdx ) : + XclImpChRoot( rRoot ), + mnGroupIdx( EXC_CHSERGROUP_NONE ), + mnSeriesIdx( nSeriesIdx ), + mnParentIdx( EXC_CHSERIES_INVALID ) +{ +} + +void XclImpChSeries::ReadHeaderRecord( XclImpStream& rStrm ) +{ + rStrm >> maData.mnCategType >> maData.mnValueType >> maData.mnCategCount >> maData.mnValueCount; + if( GetBiff() == EXC_BIFF8 ) + rStrm >> maData.mnBubbleType >> maData.mnBubbleCount; +} + +void XclImpChSeries::ReadSubRecord( XclImpStream& rStrm ) +{ + switch( rStrm.GetRecId() ) + { + case EXC_ID_CHSOURCELINK: + ReadChSourceLink( rStrm ); + break; + case EXC_ID_CHDATAFORMAT: + ReadChDataFormat( rStrm ); + break; + case EXC_ID_CHSERGROUP: + rStrm >> mnGroupIdx; + break; + case EXC_ID_CHSERPARENT: + ReadChSerParent( rStrm ); + break; + case EXC_ID_CHSERTRENDLINE: + ReadChSerTrendLine( rStrm ); + break; + case EXC_ID_CHSERERRORBAR: + ReadChSerErrorBar( rStrm ); + break; + } +} + +void XclImpChSeries::SetDataFormat( XclImpChDataFormatRef xDataFmt ) +{ + if( xDataFmt.is() ) + { + XclImpChDataFormatRef* pxDataFmt = GetDataFormatRef( xDataFmt->GetPointPos().mnPointIdx ); + // do not overwrite existing data format + if( pxDataFmt && !*pxDataFmt ) + { + *pxDataFmt = xDataFmt; + // #i51639# register series format index at chart type group + if( (pxDataFmt == &mxSeriesFmt) && !HasParentSeries() ) + if( XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() ) + pTypeGroup->SetUsedFormatIndex( xDataFmt->GetFormatIdx() ); + } + } +} + +void XclImpChSeries::SetDataLabel( XclImpChTextRef xLabel ) +{ + if( xLabel.is() ) + { + XclImpChTextRef* pxLabel = GetDataLabelRef( xLabel->GetPointPos().mnPointIdx ); + if( pxLabel && !*pxLabel ) + *pxLabel = xLabel; + } +} + +void XclImpChSeries::AddChildSeries( const XclImpChSeries& rSeries ) +{ + DBG_ASSERT( !HasParentSeries(), "XclImpChSeries::AddChildSeries - not allowed for child series" ); + + /* In Excel, trend lines and error bars are stored as own series. In Calc, + these are properties of the parent series. This function adds the + settings of the passed series to this series. */ + maTrendLines.insert( maTrendLines.end(), rSeries.maTrendLines.begin(), rSeries.maTrendLines.end() ); + maErrorBars.insert( rSeries.maErrorBars.begin(), rSeries.maErrorBars.end() ); +} + +void XclImpChSeries::FinalizeDataFormats() +{ + if( HasParentSeries() ) + { + // *** series is a child series, e.g. trend line or error bar *** + + // create missing series format + if( !mxSeriesFmt ) + mxSeriesFmt = CreateDataFormat( EXC_CHDATAFORMAT_ALLPOINTS, 0 ); + + if( mxSeriesFmt.is() ) + { + // #i83100# set text label format, e.g. for trend line equations + mxSeriesFmt->SetDataLabel( maLabels.get( EXC_CHDATAFORMAT_ALLPOINTS ) ); + // create missing automatic formats + mxSeriesFmt->UpdateTrendLineFormat(); + } + + // copy series formatting to child objects + for( XclImpChSerTrendLineList::iterator aLIt = maTrendLines.begin(), aLEnd = maTrendLines.end(); aLIt != aLEnd; ++aLIt ) + (*aLIt)->SetDataFormat( mxSeriesFmt ); + for( XclImpChSerErrorBarMap::iterator aMIt = maErrorBars.begin(), aMEnd = maErrorBars.end(); aMIt != aMEnd; ++aMIt ) + aMIt->second->SetSeriesData( mxValueLink, mxSeriesFmt ); + } + else if( XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() ) + { + // *** series is a regular data series *** + + // create missing series format + if( !mxSeriesFmt ) + { + // #i51639# use a new unused format index to create series default format + sal_uInt16 nFormatIdx = pTypeGroup->PopUnusedFormatIndex(); + mxSeriesFmt = CreateDataFormat( EXC_CHDATAFORMAT_ALLPOINTS, nFormatIdx ); + } + + // set text labels to data formats + for( XclImpChTextMap::iterator aTIt = maLabels.begin(), aTEnd = maLabels.end(); aTIt != aTEnd; ++aTIt ) + { + if( XclImpChDataFormatRef* pxDataFmt = GetDataFormatRef( aTIt->first ) ) + { + if( !*pxDataFmt ) + *pxDataFmt = CreateDataFormat( aTIt->first, EXC_CHDATAFORMAT_DEFAULT ); + (*pxDataFmt)->SetDataLabel( aTIt->second ); + } + } + + // update series format (copy missing formatting from group default format) + if( mxSeriesFmt.is() ) + mxSeriesFmt->UpdateSeriesFormat( pTypeGroup->GetTypeInfo(), pTypeGroup->GetGroupFormat().get() ); + + // update data point formats (removes unchanged automatic formatting) + for( XclImpChDataFormatMap::iterator aFIt = maPointFmts.begin(), aFEnd = maPointFmts.end(); aFIt != aFEnd; ++aFIt ) + aFIt->second->UpdatePointFormat( pTypeGroup->GetTypeInfo(), mxSeriesFmt.get() ); + } +} + +namespace { + +/** Returns the property set of the specified data point. */ +ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > xDataSeries, sal_uInt16 nPointIdx ) +{ + ScfPropertySet aPropSet; + try + { + aPropSet.Set( xDataSeries->getDataPointByIndex( static_cast< sal_Int32 >( nPointIdx ) ) ); + } + catch( Exception& ) + { + DBG_ERRORFILE( "lclGetPointPropSet - no data point property set" ); + } + return aPropSet; +} + +} // namespace + +Reference< XLabeledDataSequence > XclImpChSeries::CreateValueSequence( const OUString& rValueRole ) const +{ + return lclCreateLabeledDataSequence( mxValueLink, rValueRole, mxTitleLink.get() ); +} + +Reference< XLabeledDataSequence > XclImpChSeries::CreateCategSequence( const OUString& rCategRole ) const +{ + return lclCreateLabeledDataSequence( mxCategLink, rCategRole ); +} + +Reference< XDataSeries > XclImpChSeries::CreateDataSeries() const +{ + Reference< XDataSeries > xDataSeries; + if( const XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() ) + { + const XclChExtTypeInfo& rTypeInfo = pTypeGroup->GetTypeInfo(); + + // create the data series object + xDataSeries.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY ); + + // attach data and title sequences to series + Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY ); + if( xDataSink.is() ) + { + // create vector of all value sequences + ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec; + // add Y values + Reference< XLabeledDataSequence > xYValueSeq = + CreateValueSequence( EXC_CHPROP_ROLE_YVALUES ); + if( xYValueSeq.is() ) + aLabeledSeqVec.push_back( xYValueSeq ); + // add X values + if( !rTypeInfo.mbCategoryAxis ) + { + Reference< XLabeledDataSequence > xXValueSeq = + CreateCategSequence( EXC_CHPROP_ROLE_XVALUES ); + if( xXValueSeq.is() ) + aLabeledSeqVec.push_back( xXValueSeq ); + // add size values of bubble charts + if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES ) + { + Reference< XLabeledDataSequence > xSizeValueSeq = + lclCreateLabeledDataSequence( mxBubbleLink, EXC_CHPROP_ROLE_SIZEVALUES, mxTitleLink.get() ); + if( xSizeValueSeq.is() ) + aLabeledSeqVec.push_back( xSizeValueSeq ); + } + } + // attach labeled data sequences to series + if( !aLabeledSeqVec.empty() ) + xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) ); + } + + // series formatting + ScfPropertySet aSeriesProp( xDataSeries ); + if( mxSeriesFmt.is() ) + mxSeriesFmt->Convert( aSeriesProp, rTypeInfo ); + + // trend lines + ConvertTrendLines( xDataSeries ); + + // error bars + Reference< XPropertySet > xErrorBarX = CreateErrorBar( EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS ); + if( xErrorBarX.is() ) + aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARX, xErrorBarX ); + Reference< XPropertySet > xErrorBarY = CreateErrorBar( EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS ); + if( xErrorBarY.is() ) + aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARY, xErrorBarY ); + + // own area formatting for every data point (TODO: varying line color not supported) + bool bVarPointFmt = pTypeGroup->HasVarPointFormat() && rTypeInfo.IsSeriesFrameFormat(); +#if EXC_CHART2_VARYCOLORSBY_PROP + aSeriesProp.SetBoolProperty( EXC_CHPROP_VARYCOLORSBY, bVarPointFmt ); +#else + aSeriesProp.SetBoolProperty( EXC_CHPROP_VARYCOLORSBY, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE ); +#endif + // #i91271# always set area formatting for every point in pie/doughnut charts + if( mxSeriesFmt.is() && ((bVarPointFmt && mxSeriesFmt->IsAutoArea()) || (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE)) ) + { + for( sal_uInt16 nPointIdx = 0, nPointCount = mxValueLink->GetCellCount(); nPointIdx < nPointCount; ++nPointIdx ) + { + ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx ); + mxSeriesFmt->ConvertArea( aPointProp, bVarPointFmt ? nPointIdx : mnSeriesIdx ); + } + } + + // data point formatting + for( XclImpChDataFormatMap::const_iterator aIt = maPointFmts.begin(), aEnd = maPointFmts.end(); aIt != aEnd; ++aIt ) + { + ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, aIt->first ); + aIt->second->Convert( aPointProp, rTypeInfo ); + } + } + return xDataSeries; +} + +void XclImpChSeries::FillAllSourceLinks( ::std::vector< ScSharedTokenRef >& rTokens ) const +{ + if( mxValueLink.is() ) + mxValueLink->FillSourceLink( rTokens ); + if( mxCategLink.is() ) + mxCategLink->FillSourceLink( rTokens ); + if( mxTitleLink.is() ) + mxTitleLink->FillSourceLink( rTokens ); + if( mxBubbleLink.is() ) + mxBubbleLink->FillSourceLink( rTokens ); +} + +void XclImpChSeries::ReadChSourceLink( XclImpStream& rStrm ) +{ + XclImpChSourceLinkRef xSrcLink( new XclImpChSourceLink( GetChRoot() ) ); + xSrcLink->ReadChSourceLink( rStrm ); + switch( xSrcLink->GetDestType() ) + { + case EXC_CHSRCLINK_TITLE: mxTitleLink = xSrcLink; break; + case EXC_CHSRCLINK_VALUES: mxValueLink = xSrcLink; break; + case EXC_CHSRCLINK_CATEGORY: mxCategLink = xSrcLink; break; + case EXC_CHSRCLINK_BUBBLES: mxBubbleLink = xSrcLink; break; + } +} + +void XclImpChSeries::ReadChDataFormat( XclImpStream& rStrm ) +{ + // #i51639# chart stores all data formats and assigns them later to the series + GetChartData().ReadChDataFormat( rStrm ); +} + +void XclImpChSeries::ReadChSerParent( XclImpStream& rStrm ) +{ + rStrm >> mnParentIdx; + // index to parent series is 1-based, convert it to 0-based + if( mnParentIdx > 0 ) + --mnParentIdx; + else + mnParentIdx = EXC_CHSERIES_INVALID; +} + +void XclImpChSeries::ReadChSerTrendLine( XclImpStream& rStrm ) +{ + XclImpChSerTrendLineRef xTrendLine( new XclImpChSerTrendLine( GetChRoot() ) ); + xTrendLine->ReadChSerTrendLine( rStrm ); + maTrendLines.push_back( xTrendLine ); +} + +void XclImpChSeries::ReadChSerErrorBar( XclImpStream& rStrm ) +{ + XclImpChSerErrorBarRef xErrorBar( new XclImpChSerErrorBar( GetChRoot() ) ); + xErrorBar->ReadChSerErrorBar( rStrm ); + maErrorBars[ xErrorBar->GetBarType() ] = xErrorBar; +} + +XclImpChDataFormatRef XclImpChSeries::CreateDataFormat( sal_uInt16 nPointIdx, sal_uInt16 nFormatIdx ) +{ + XclImpChDataFormatRef xDataFmt( new XclImpChDataFormat( GetChRoot() ) ); + xDataFmt->SetPointPos( XclChDataPointPos( mnSeriesIdx, nPointIdx ), nFormatIdx ); + return xDataFmt; +} + +XclImpChDataFormatRef* XclImpChSeries::GetDataFormatRef( sal_uInt16 nPointIdx ) +{ + if( nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS ) + return &mxSeriesFmt; + if( nPointIdx < EXC_CHDATAFORMAT_MAXPOINTCOUNT ) + return &maPointFmts[ nPointIdx ]; + return 0; +} + +XclImpChTextRef* XclImpChSeries::GetDataLabelRef( sal_uInt16 nPointIdx ) +{ + if( (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS) || (nPointIdx < EXC_CHDATAFORMAT_MAXPOINTCOUNT) ) + return &maLabels[ nPointIdx ]; + return 0; +} + +void XclImpChSeries::ConvertTrendLines( Reference< XDataSeries > xDataSeries ) const +{ + Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY ); + if( xRegCurveCont.is() ) + { + for( XclImpChSerTrendLineList::const_iterator aIt = maTrendLines.begin(), aEnd = maTrendLines.end(); aIt != aEnd; ++aIt ) + { + try + { + Reference< XRegressionCurve > xRegCurve = (*aIt)->CreateRegressionCurve(); + if( xRegCurve.is() ) + xRegCurveCont->addRegressionCurve( xRegCurve ); + } + catch( Exception& ) + { + DBG_ERRORFILE( "XclImpChSeries::ConvertTrendLines - cannot add regression curve" ); + } + } + } +} + +Reference< XPropertySet > XclImpChSeries::CreateErrorBar( sal_uInt8 nPosBarId, sal_uInt8 nNegBarId ) const +{ + return XclImpChSerErrorBar::CreateErrorBar( maErrorBars.get( nPosBarId ).get(), maErrorBars.get( nNegBarId ).get() ); +} + +// Chart type groups ========================================================== + +XclImpChType::XclImpChType( const XclImpChRoot& rRoot ) : + XclImpChRoot( rRoot ), + mnRecId( EXC_ID_CHUNKNOWN ), + maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) ) +{ +} + +void XclImpChType::ReadChType( XclImpStream& rStrm ) +{ + sal_uInt16 nRecId = rStrm.GetRecId(); + bool bKnownType = true; + + switch( nRecId ) + { + case EXC_ID_CHBAR: + rStrm >> maData.mnOverlap >> maData.mnGap >> maData.mnFlags; + break; + + case EXC_ID_CHLINE: + case EXC_ID_CHAREA: + case EXC_ID_CHRADARLINE: + case EXC_ID_CHRADARAREA: + rStrm >> maData.mnFlags; + break; + + case EXC_ID_CHPIE: + rStrm >> maData.mnRotation >> maData.mnPieHole; + if( GetBiff() == EXC_BIFF8 ) + rStrm >> maData.mnFlags; + else + maData.mnFlags = 0; + break; + + case EXC_ID_CHPIEEXT: + maData.mnRotation = 0; + maData.mnPieHole = 0; + maData.mnFlags = 0; + break; + + case EXC_ID_CHSCATTER: + if( GetBiff() == EXC_BIFF8 ) + rStrm >> maData.mnBubbleSize >> maData.mnBubbleType >> maData.mnFlags; + else + maData.mnFlags = 0; + break; + + case EXC_ID_CHSURFACE: + rStrm >> maData.mnFlags; + break; + + default: + bKnownType = false; + } + + if( bKnownType ) + mnRecId = nRecId; +} + +void XclImpChType::Finalize( bool bStockChart ) +{ + switch( mnRecId ) + { + case EXC_ID_CHLINE: + maTypeInfo = GetChartTypeInfo( bStockChart ? + EXC_CHTYPEID_STOCK : EXC_CHTYPEID_LINE ); + break; + case EXC_ID_CHBAR: + maTypeInfo = GetChartTypeInfo( ::get_flagvalue( + maData.mnFlags, EXC_CHBAR_HORIZONTAL, + EXC_CHTYPEID_HORBAR, EXC_CHTYPEID_BAR ) ); + break; + case EXC_ID_CHPIE: + maTypeInfo = GetChartTypeInfo( (maData.mnPieHole > 0) ? + EXC_CHTYPEID_DONUT : EXC_CHTYPEID_PIE ); + break; + case EXC_ID_CHSCATTER: + maTypeInfo = GetChartTypeInfo( ::get_flagvalue( + maData.mnFlags, EXC_CHSCATTER_BUBBLES, + EXC_CHTYPEID_BUBBLES, EXC_CHTYPEID_SCATTER ) ); + break; + default: + maTypeInfo = GetChartTypeInfo( mnRecId ); + } + + switch( maTypeInfo.meTypeId ) + { + case EXC_CHTYPEID_PIEEXT: + case EXC_CHTYPEID_BUBBLES: + case EXC_CHTYPEID_SURFACE: + case EXC_CHTYPEID_UNKNOWN: + GetTracer().TraceChartUnKnownType(); + break; + default:; + } +} + +bool XclImpChType::IsStacked() const +{ + bool bStacked = false; + if( maTypeInfo.mbSupportsStacking ) switch( maTypeInfo.meTypeCateg ) + { + case EXC_CHTYPECATEG_LINE: + bStacked = + ::get_flag( maData.mnFlags, EXC_CHLINE_STACKED ) && + !::get_flag( maData.mnFlags, EXC_CHLINE_PERCENT ); + break; + case EXC_CHTYPECATEG_BAR: + bStacked = + ::get_flag( maData.mnFlags, EXC_CHBAR_STACKED ) && + !::get_flag( maData.mnFlags, EXC_CHBAR_PERCENT ); + break; + default:; + } + return bStacked; +} + +bool XclImpChType::IsPercent() const +{ + bool bPercent = false; + if( maTypeInfo.mbSupportsStacking ) switch( maTypeInfo.meTypeCateg ) + { + case EXC_CHTYPECATEG_LINE: + bPercent = + ::get_flag( maData.mnFlags, EXC_CHLINE_STACKED ) && + ::get_flag( maData.mnFlags, EXC_CHLINE_PERCENT ); + break; + case EXC_CHTYPECATEG_BAR: + bPercent = + ::get_flag( maData.mnFlags, EXC_CHBAR_STACKED ) && + ::get_flag( maData.mnFlags, EXC_CHBAR_PERCENT ); + break; + default:; + } + return bPercent; +} + +bool XclImpChType::HasCategoryLabels() const +{ + // radar charts disable category labels in chart type, not in CHTICK of X axis + return (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_RADAR) || ::get_flag( maData.mnFlags, EXC_CHRADAR_AXISLABELS ); +} + +Reference< XCoordinateSystem > XclImpChType::CreateCoordSystem( bool b3dChart ) const +{ + // service name + OUString aCoordSysService; + if( maTypeInfo.mbPolarCoordSystem ) + { + if( b3dChart ) + aCoordSysService = SERVICE_CHART2_POLARCOORDSYS3D; + else + aCoordSysService = SERVICE_CHART2_POLARCOORDSYS2D; + } + else + { + if( b3dChart ) + aCoordSysService = SERVICE_CHART2_CARTESIANCOORDSYS3D; + else + aCoordSysService = SERVICE_CHART2_CARTESIANCOORDSYS2D; + } + + // create the coordinate system object + Reference< XCoordinateSystem > xCoordSystem( ScfApiHelper::CreateInstance( aCoordSysService ), UNO_QUERY ); + + // swap X and Y axis + if( maTypeInfo.mbSwappedAxesSet ) + { + ScfPropertySet aCoordSysProp( xCoordSystem ); + aCoordSysProp.SetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS, true ); + } + + return xCoordSystem; +} + +Reference< XChartType > XclImpChType::CreateChartType( Reference< XDiagram > xDiagram, bool b3dChart ) const +{ + OUString aService = OUString::createFromAscii( maTypeInfo.mpcServiceName ); + Reference< XChartType > xChartType( ScfApiHelper::CreateInstance( aService ), UNO_QUERY ); + + // additional properties + switch( maTypeInfo.meTypeCateg ) + { + case EXC_CHTYPECATEG_BAR: + { + ScfPropertySet aTypeProp( xChartType ); + Sequence< sal_Int32 > aInt32Seq( 2 ); + aInt32Seq[ 0 ] = aInt32Seq[ 1 ] = -maData.mnOverlap; + aTypeProp.SetProperty( EXC_CHPROP_OVERLAPSEQ, aInt32Seq ); + aInt32Seq[ 0 ] = aInt32Seq[ 1 ] = maData.mnGap; + aTypeProp.SetProperty( EXC_CHPROP_GAPWIDTHSEQ, aInt32Seq ); + } + break; + case EXC_CHTYPECATEG_PIE: + { + ScfPropertySet aTypeProp( xChartType ); + aTypeProp.SetBoolProperty( EXC_CHPROP_USERINGS, maTypeInfo.meTypeId == EXC_CHTYPEID_DONUT ); + /* #i85166# starting angle of first pie slice. 3D pie charts use Y + rotation setting in view3D element. Of-pie charts do not + support pie rotation. */ + if( !b3dChart && (maTypeInfo.meTypeId != EXC_CHTYPEID_PIEEXT) ) + { + ScfPropertySet aDiaProp( xDiagram ); + XclImpChRoot::ConvertPieRotation( aDiaProp, maData.mnRotation ); + } + } + break; + default:; + } + + return xChartType; +} + +// ---------------------------------------------------------------------------- + +void XclImpChChart3d::ReadChChart3d( XclImpStream& rStrm ) +{ + rStrm >> maData.mnRotation + >> maData.mnElevation + >> maData.mnEyeDist + >> maData.mnRelHeight + >> maData.mnRelDepth + >> maData.mnDepthGap + >> maData.mnFlags; +} + +void XclImpChChart3d::Convert( ScfPropertySet& rPropSet, bool b3dWallChart ) const +{ + namespace cssd = ::com::sun::star::drawing; + +// #i104057# do not assert this, written by broken external generators +// DBG_ASSERT( ::get_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS ) == b3dWallChart, "XclImpChChart3d::Convert - wrong wall flag" ); + + sal_Int32 nRotationY = 0; + sal_Int32 nRotationX = 0; + sal_Int32 nPerspective = 15; + bool bRightAngled = false; + cssd::ProjectionMode eProjMode = cssd::ProjectionMode_PERSPECTIVE; + Color aAmbientColor, aLightColor; + + if( b3dWallChart ) + { + // Y rotation (Excel [0..359], Chart2 [-179,180]) + nRotationY = maData.mnRotation % 360; + if( nRotationY > 180 ) nRotationY -= 360; + // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180]) + nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, -90, 90 ); + // perspective (Excel and Chart2 [0,100]) + nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 ); + // right-angled axes + bRightAngled = !::get_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D ); + // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%) + bool bParallel = bRightAngled || (nPerspective == 0); + eProjMode = bParallel ? cssd::ProjectionMode_PARALLEL : cssd::ProjectionMode_PERSPECTIVE; + // ambient color (Gray 20%) + aAmbientColor.SetColor( RGB_COLORDATA( 204, 204, 204 ) ); + // light color (Gray 60%) + aLightColor.SetColor( RGB_COLORDATA( 102, 102, 102 ) ); + } + else + { + // Y rotation not used in pie charts, but 'first pie slice angle' + nRotationY = 0; + XclImpChRoot::ConvertPieRotation( rPropSet, maData.mnRotation ); + // X rotation a.k.a. elevation (map Excel [10..80] to Chart2 [-80,-10]) + nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, 10, 80 ) - 90; + // perspective (Excel and Chart2 [0,100]) + nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 ); + // no right-angled axes in pie charts, but parallel projection + bRightAngled = false; + eProjMode = cssd::ProjectionMode_PARALLEL; + // ambient color (Gray 30%) + aAmbientColor.SetColor( RGB_COLORDATA( 179, 179, 179 ) ); + // light color (Gray 70%) + aLightColor.SetColor( RGB_COLORDATA( 76, 76, 76 ) ); + } + + // properties + rPropSet.SetProperty( EXC_CHPROP_ROTATIONVERTICAL, nRotationY ); + rPropSet.SetProperty( EXC_CHPROP_ROTATIONHORIZONTAL, nRotationX ); + rPropSet.SetProperty( EXC_CHPROP_PERSPECTIVE, nPerspective ); + rPropSet.SetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES, bRightAngled ); + rPropSet.SetProperty( EXC_CHPROP_D3DSCENEPERSPECTIVE, eProjMode ); + + // light settings + rPropSet.SetProperty( EXC_CHPROP_D3DSCENESHADEMODE, cssd::ShadeMode_FLAT ); + rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENEAMBIENTCOLOR, aAmbientColor ); + rPropSet.SetBoolProperty( EXC_CHPROP_D3DSCENELIGHTON1, false ); + rPropSet.SetBoolProperty( EXC_CHPROP_D3DSCENELIGHTON2, true ); + rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENELIGHTCOLOR2, aLightColor ); + rPropSet.SetProperty( EXC_CHPROP_D3DSCENELIGHTDIR2, cssd::Direction3D( 0.2, 0.4, 1.0 ) ); +} + +// ---------------------------------------------------------------------------- + +XclImpChLegend::XclImpChLegend( const XclImpChRoot& rRoot ) : + XclImpChRoot( rRoot ) +{ +} + +void XclImpChLegend::ReadHeaderRecord( XclImpStream& rStrm ) +{ + rStrm >> maData.maRect >> maData.mnDockMode >> maData.mnSpacing >> maData.mnFlags; + + // trace unsupported features + if( GetTracer().IsEnabled() ) + { + if( maData.mnDockMode == EXC_CHLEGEND_NOTDOCKED ) + GetTracer().TraceChartLegendPosition(); + if( ::get_flag( maData.mnFlags, EXC_CHLEGEND_DATATABLE ) ) + GetTracer().TraceChartDataTable(); + } +} + +void XclImpChLegend::ReadSubRecord( XclImpStream& rStrm ) +{ + switch( rStrm.GetRecId() ) + { + case EXC_ID_CHFRAMEPOS: + mxFramePos.reset( new XclImpChFramePos ); + mxFramePos->ReadChFramePos( rStrm ); + break; + case EXC_ID_CHTEXT: + mxText.reset( new XclImpChText( GetChRoot() ) ); + mxText->ReadRecordGroup( rStrm ); + break; + case EXC_ID_CHFRAME: + mxFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_LEGEND ) ); + mxFrame->ReadRecordGroup( rStrm ); + break; + } +} + +void XclImpChLegend::Finalize() +{ + // legend default formatting differs in OOChart and Excel, missing frame means automatic + if( !mxFrame ) + mxFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_LEGEND ) ); + // Update text formatting. If mxText is empty, the passed default text is used. + lclUpdateText( mxText, GetChartData().GetDefaultText( EXC_CHTEXTTYPE_LEGEND ) ); +} + +Reference< XLegend > XclImpChLegend::CreateLegend() const +{ + Reference< XLegend > xLegend( ScfApiHelper::CreateInstance( SERVICE_CHART2_LEGEND ), UNO_QUERY ); + if( xLegend.is() ) + { + ScfPropertySet aLegendProp( xLegend ); + aLegendProp.SetBoolProperty( EXC_CHPROP_SHOW, true ); + + // frame properties + if( mxFrame.is() ) + mxFrame->Convert( aLegendProp ); + // text properties + if( mxText.is() ) + mxText->ConvertFont( aLegendProp ); + + /* Legend position and size. Default positions are used only if the + plot area is positioned automatically (Excel sets the plot area to + manual mode, if the legend is moved or resized). With manual plot + areas, Excel ignores the value in maData.mnDockMode completely. */ + cssc2::LegendPosition eApiPos = cssc2::LegendPosition_CUSTOM; + cssc2::LegendExpansion eApiExpand = cssc2::LegendExpansion_BALANCED; + if( !GetChartData().IsManualPlotArea() ) switch( maData.mnDockMode ) + { + case EXC_CHLEGEND_LEFT: eApiPos = cssc2::LegendPosition_LINE_START; eApiExpand = cssc2::LegendExpansion_HIGH; break; + case EXC_CHLEGEND_RIGHT: eApiPos = cssc2::LegendPosition_LINE_END; eApiExpand = cssc2::LegendExpansion_HIGH; break; + case EXC_CHLEGEND_TOP: eApiPos = cssc2::LegendPosition_PAGE_START; eApiExpand = cssc2::LegendExpansion_WIDE; break; + case EXC_CHLEGEND_BOTTOM: eApiPos = cssc2::LegendPosition_PAGE_END; eApiExpand = cssc2::LegendExpansion_WIDE; break; + // top-right not supported + case EXC_CHLEGEND_CORNER: eApiPos = cssc2::LegendPosition_LINE_END; eApiExpand = cssc2::LegendExpansion_HIGH; break; + } + + // no automatic position: try to find the correct position and size + if( eApiPos == cssc2::LegendPosition_CUSTOM ) + { + const XclChFramePos* pFramePos = mxFramePos.is() ? &mxFramePos->GetFramePosData() : 0; + + /* Legend position. Only the settings from the CHFRAMEPOS record + are used by Excel, the position in the CHLEGEND record will be + ignored. */ + if( pFramePos ) + { + RelativePosition aRelPos; + aRelPos.Primary = CalcRelativeFromChartX( pFramePos->maRect.mnX ); + aRelPos.Secondary = CalcRelativeFromChartY( pFramePos->maRect.mnY ); + aRelPos.Anchor = ::com::sun::star::drawing::Alignment_TOP_LEFT; + aLegendProp.SetProperty( EXC_CHPROP_RELATIVEPOSITION, aRelPos ); + } + else + { + // no manual position found, just go for the default + eApiPos = cssc2::LegendPosition_LINE_END; + } + + + /* Legend size. #i71697# It is not possible to set the legend size + directly in the Chart, do some magic here. */ + if( !pFramePos || (pFramePos->mnBRMode != EXC_CHFRAMEPOS_ABSSIZE_POINTS) || + (pFramePos->maRect.mnWidth == 0) || (pFramePos->maRect.mnHeight == 0) ) + { + // automatic size: determine entry direction from flags + eApiExpand = ::get_flagvalue( maData.mnFlags, EXC_CHLEGEND_STACKED, + cssc2::LegendExpansion_HIGH, cssc2::LegendExpansion_WIDE ); + } + else + { + // legend size is given in points, not in chart units + double fRatio = static_cast< double >( pFramePos->maRect.mnWidth ) / pFramePos->maRect.mnHeight; + if( fRatio > 1.5 ) + eApiExpand = cssc2::LegendExpansion_WIDE; + else if( fRatio < 0.75 ) + eApiExpand = cssc2::LegendExpansion_HIGH; + else + eApiExpand = cssc2::LegendExpansion_BALANCED; + } + } + aLegendProp.SetProperty( EXC_CHPROP_ANCHORPOSITION, eApiPos ); + aLegendProp.SetProperty( EXC_CHPROP_EXPANSION, eApiExpand ); + } + return xLegend; +} + +// ---------------------------------------------------------------------------- + +XclImpChDropBar::XclImpChDropBar( sal_uInt16 nDropBar ) : + mnDropBar( nDropBar ), + mnBarDist( 0 ) +{ +} + +void XclImpChDropBar::ReadHeaderRecord( XclImpStream& rStrm ) +{ + rStrm >> mnBarDist; +} + +void XclImpChDropBar::Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const +{ + XclChObjectType eObjType = EXC_CHOBJTYPE_BACKGROUND; + switch( mnDropBar ) + { + case EXC_CHDROPBAR_UP: eObjType = EXC_CHOBJTYPE_WHITEDROPBAR; break; + case EXC_CHDROPBAR_DOWN: eObjType = EXC_CHOBJTYPE_BLACKDROPBAR; break; + } + ConvertFrameBase( rRoot, rPropSet, eObjType ); +} + +// ---------------------------------------------------------------------------- + +XclImpChTypeGroup::XclImpChTypeGroup( const XclImpChRoot& rRoot ) : + XclImpChRoot( rRoot ), + maType( rRoot ), + maTypeInfo( maType.GetTypeInfo() ) +{ + // Initialize unused format indexes set. At this time, all formats are unused. + for( sal_uInt16 nFormatIdx = 0; nFormatIdx <= EXC_CHSERIES_MAXSERIES; ++nFormatIdx ) + maUnusedFormats.insert( maUnusedFormats.end(), nFormatIdx ); +} + +void XclImpChTypeGroup::ReadHeaderRecord( XclImpStream& rStrm ) +{ + rStrm.Ignore( 16 ); + rStrm >> maData.mnFlags >> maData.mnGroupIdx; +} + +void XclImpChTypeGroup::ReadSubRecord( XclImpStream& rStrm ) +{ + switch( rStrm.GetRecId() ) + { + case EXC_ID_CHCHART3D: + mxChart3d.reset( new XclImpChChart3d ); + mxChart3d->ReadChChart3d( rStrm ); + break; + case EXC_ID_CHLEGEND: + mxLegend.reset( new XclImpChLegend( GetChRoot() ) ); + mxLegend->ReadRecordGroup( rStrm ); + break; + case EXC_ID_CHDEFAULTTEXT: + GetChartData().ReadChDefaultText( rStrm ); + break; + case EXC_ID_CHDROPBAR: + ReadChDropBar( rStrm ); + break; + case EXC_ID_CHCHARTLINE: + ReadChChartLine( rStrm ); + break; + case EXC_ID_CHDATAFORMAT: + ReadChDataFormat( rStrm ); + break; + default: + maType.ReadChType( rStrm ); + } +} + +void XclImpChTypeGroup::Finalize() +{ + // check and set valid chart type + bool bStockChart = + (maType.GetRecId() == EXC_ID_CHLINE) && // must be a line chart + !mxChart3d && // must be a 2d chart + HasHiLoLine() && // must contain hi-lo lines + (maSeries.size() == static_cast<XclImpChSeriesVec::size_type>(HasDropBars() ? 4 : 3)); // correct series count + maType.Finalize( bStockChart ); + + // extended type info + maTypeInfo.Set( maType.GetTypeInfo(), mxChart3d.is(), false ); + + // reverse series order for some unstacked 2D chart types + if( maTypeInfo.mbReverseSeries && !Is3dChart() && !maType.IsStacked() && !maType.IsPercent() ) + ::std::reverse( maSeries.begin(), maSeries.end() ); + + // update chart type group format, may depend on chart type finalized above + if( mxGroupFmt.is() ) + mxGroupFmt->UpdateGroupFormat( maTypeInfo ); +} + +void XclImpChTypeGroup::AddSeries( XclImpChSeriesRef xSeries ) +{ + if( xSeries.is() ) + maSeries.push_back( xSeries ); + // store first inserted series separately, series order may be reversed later + if( !mxFirstSeries ) + mxFirstSeries = xSeries; +} + +void XclImpChTypeGroup::SetUsedFormatIndex( sal_uInt16 nFormatIdx ) +{ + maUnusedFormats.erase( nFormatIdx ); +} + +sal_uInt16 XclImpChTypeGroup::PopUnusedFormatIndex() +{ + DBG_ASSERT( !maUnusedFormats.empty(), "XclImpChTypeGroup::PopUnusedFormatIndex - no more format indexes available" ); + sal_uInt16 nFormatIdx = maUnusedFormats.empty() ? 0 : *maUnusedFormats.begin(); + SetUsedFormatIndex( nFormatIdx ); + return nFormatIdx; +} + +bool XclImpChTypeGroup::HasVarPointFormat() const +{ + return ::get_flag( maData.mnFlags, EXC_CHTYPEGROUP_VARIEDCOLORS ) && + ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_MULTI) || // multiple series allowed + ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_SINGLE) && // or exactly 1 series? + (maSeries.size() == 1))); +} + +bool XclImpChTypeGroup::HasConnectorLines() const +{ + // existence of connector lines (only in stacked bar charts) + bool bAnyStacked = maType.IsStacked() || maType.IsPercent(); + XclImpChLineFormatRef xConnLine = maChartLines.get( EXC_CHCHARTLINE_CONNECT ); + return bAnyStacked && (maTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR) && xConnLine.is() && xConnLine->HasLine(); +} + +const String& XclImpChTypeGroup::GetSingleSeriesTitle() const +{ + // no automatic title for series with trendlines or error bars + // pie charts always show an automatic title, even if more series exist + return (mxFirstSeries.is() && !mxFirstSeries->HasChildSeries() && (maTypeInfo.mbSingleSeriesVis || (maSeries.size() == 1))) ? + mxFirstSeries->GetTitle() : String::EmptyString(); +} + +void XclImpChTypeGroup::ConvertChart3d( ScfPropertySet& rPropSet ) const +{ + if( mxChart3d.is() ) + mxChart3d->Convert( rPropSet, Is3dWallChart() ); +} + +Reference< XCoordinateSystem > XclImpChTypeGroup::CreateCoordSystem() const +{ + return maType.CreateCoordSystem( Is3dChart() ); +} + +Reference< XChartType > XclImpChTypeGroup::CreateChartType( Reference< XDiagram > xDiagram, sal_Int32 nApiAxesSetIdx ) const +{ + DBG_ASSERT( IsValidGroup(), "XclImpChTypeGroup::CreateChartType - type group without series" ); + + // create the chart type object + Reference< XChartType > xChartType = maType.CreateChartType( xDiagram, Is3dChart() ); + + // bar chart connector lines + if( HasConnectorLines() ) + { + ScfPropertySet aDiaProp( xDiagram ); + aDiaProp.SetBoolProperty( EXC_CHPROP_CONNECTBARS, true ); + } + + /* Stock chart needs special processing. Create one 'big' series with + data sequences of different roles. */ + if( maTypeInfo.meTypeId == EXC_CHTYPEID_STOCK ) + CreateStockSeries( xChartType, nApiAxesSetIdx ); + else + CreateDataSeries( xChartType, nApiAxesSetIdx ); + + return xChartType; +} + +Reference< XLabeledDataSequence > XclImpChTypeGroup::CreateCategSequence() const +{ + Reference< XLabeledDataSequence > xLabeledSeq; + // create category sequence from first visible series + if( mxFirstSeries.is() ) + xLabeledSeq = mxFirstSeries->CreateCategSequence( EXC_CHPROP_ROLE_CATEG ); + return xLabeledSeq; +} + +void XclImpChTypeGroup::ReadChDropBar( XclImpStream& rStrm ) +{ + sal_uInt16 nDropBar = EXC_CHDROPBAR_NONE; + if( !maDropBars.has( EXC_CHDROPBAR_UP ) ) + nDropBar = EXC_CHDROPBAR_UP; + else if( !maDropBars.has( EXC_CHDROPBAR_DOWN ) ) + nDropBar = EXC_CHDROPBAR_DOWN; + + if( nDropBar != EXC_CHDROPBAR_NONE ) + { + XclImpChDropBarRef xDropBar( new XclImpChDropBar( nDropBar ) ); + xDropBar->ReadRecordGroup( rStrm ); + maDropBars[ nDropBar ] = xDropBar; + } +} + +void XclImpChTypeGroup::ReadChChartLine( XclImpStream& rStrm ) +{ + sal_uInt16 nLineId = rStrm.ReaduInt16(); + if( (rStrm.GetNextRecId() == EXC_ID_CHLINEFORMAT) && rStrm.StartNextRecord() ) + { + XclImpChLineFormatRef xLineFmt( new XclImpChLineFormat ); + xLineFmt->ReadChLineFormat( rStrm ); + maChartLines[ nLineId ] = xLineFmt; + } +} + +void XclImpChTypeGroup::ReadChDataFormat( XclImpStream& rStrm ) +{ + // global series and data point format + XclImpChDataFormatRef xDataFmt( new XclImpChDataFormat( GetChRoot() ) ); + xDataFmt->ReadRecordGroup( rStrm ); + const XclChDataPointPos& rPos = xDataFmt->GetPointPos(); + if( (rPos.mnSeriesIdx == 0) && (rPos.mnPointIdx == 0) && + (xDataFmt->GetFormatIdx() == EXC_CHDATAFORMAT_DEFAULT) ) + mxGroupFmt = xDataFmt; +} + + +void XclImpChTypeGroup::InsertDataSeries( Reference< XChartType > xChartType, + Reference< XDataSeries > xSeries, sal_Int32 nApiAxesSetIdx ) const +{ + Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY ); + if( xSeriesCont.is() && xSeries.is() ) + { + // series stacking mode + cssc2::StackingDirection eStacking = cssc2::StackingDirection_NO_STACKING; + // stacked overrides deep-3d + if( maType.IsStacked() || maType.IsPercent() ) + eStacking = cssc2::StackingDirection_Y_STACKING; + else if( Is3dDeepChart() ) + eStacking = cssc2::StackingDirection_Z_STACKING; + + // additional series properties + ScfPropertySet aSeriesProp( xSeries ); + aSeriesProp.SetProperty( EXC_CHPROP_STACKINGDIR, eStacking ); + aSeriesProp.SetProperty( EXC_CHPROP_ATTAXISINDEX, nApiAxesSetIdx ); + + // insert series into container + try + { + xSeriesCont->addDataSeries( xSeries ); + } + catch( Exception& ) + { + DBG_ERRORFILE( "XclImpChTypeGroup::InsertDataSeries - cannot add data series" ); + } + } +} + +void XclImpChTypeGroup::CreateDataSeries( Reference< XChartType > xChartType, sal_Int32 nApiAxesSetIdx ) const +{ + bool bSpline = false; + for( XclImpChSeriesVec::const_iterator aIt = maSeries.begin(), aEnd = maSeries.end(); aIt != aEnd; ++aIt ) + { + Reference< XDataSeries > xDataSeries = (*aIt)->CreateDataSeries(); + InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx ); + bSpline |= (*aIt)->HasSpline(); + } + // spline - TODO: set at single series (#i66858#) + if( bSpline && !maTypeInfo.IsSeriesFrameFormat() && (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_RADAR) ) + { + ScfPropertySet aTypeProp( xChartType ); + aTypeProp.SetProperty( EXC_CHPROP_CURVESTYLE, ::com::sun::star::chart2::CurveStyle_CUBIC_SPLINES ); + } +} + +void XclImpChTypeGroup::CreateStockSeries( Reference< XChartType > xChartType, sal_Int32 nApiAxesSetIdx ) const +{ + // create the data series object + Reference< XDataSeries > xDataSeries( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY ); + Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY ); + if( xDataSink.is() ) + { + // create a list of data sequences from all series + ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec; + DBG_ASSERT( maSeries.size() >= 3, "XclImpChTypeGroup::CreateChartType - missing stock series" ); + int nRoleIdx = (maSeries.size() == 3) ? 1 : 0; + for( XclImpChSeriesVec::const_iterator aIt = maSeries.begin(), aEnd = maSeries.end(); + (nRoleIdx < 4) && (aIt != aEnd); ++nRoleIdx, ++aIt ) + { + // create a data sequence with a specific role + OUString aRole; + switch( nRoleIdx ) + { + case 0: aRole = EXC_CHPROP_ROLE_OPENVALUES; break; + case 1: aRole = EXC_CHPROP_ROLE_HIGHVALUES; break; + case 2: aRole = EXC_CHPROP_ROLE_LOWVALUES; break; + case 3: aRole = EXC_CHPROP_ROLE_CLOSEVALUES; break; + } + Reference< XLabeledDataSequence > xDataSeq = (*aIt)->CreateValueSequence( aRole ); + if( xDataSeq.is() ) + aLabeledSeqVec.push_back( xDataSeq ); + } + + // attach labeled data sequences to series and insert series into chart type + xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) ); + + // formatting of special stock chart elements + ScfPropertySet aTypeProp( xChartType ); + aTypeProp.SetBoolProperty( EXC_CHPROP_JAPANESE, HasDropBars() ); + aTypeProp.SetBoolProperty( EXC_CHPROP_SHOWFIRST, HasDropBars() ); + aTypeProp.SetBoolProperty( EXC_CHPROP_SHOWHIGHLOW, true ); + // hi-lo line format + XclImpChLineFormatRef xHiLoLine = maChartLines.get( EXC_CHCHARTLINE_HILO ); + if( xHiLoLine.is() ) + { + ScfPropertySet aSeriesProp( xDataSeries ); + xHiLoLine->Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE ); + } + // white dropbar format + XclImpChDropBarRef xUpBar = maDropBars.get( EXC_CHDROPBAR_UP ); + Reference< XPropertySet > xWhitePropSet; + if( xUpBar.is() && aTypeProp.GetProperty( xWhitePropSet, EXC_CHPROP_WHITEDAY ) ) + { + ScfPropertySet aBarProp( xWhitePropSet ); + xUpBar->Convert( GetChRoot(), aBarProp ); + } + // black dropbar format + XclImpChDropBarRef xDownBar = maDropBars.get( EXC_CHDROPBAR_DOWN ); + Reference< XPropertySet > xBlackPropSet; + if( xDownBar.is() && aTypeProp.GetProperty( xBlackPropSet, EXC_CHPROP_BLACKDAY ) ) + { + ScfPropertySet aBarProp( xBlackPropSet ); + xDownBar->Convert( GetChRoot(), aBarProp ); + } + + // insert the series into the chart type object + InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx ); + } +} + +// Axes ======================================================================= + +XclImpChLabelRange::XclImpChLabelRange( const XclImpChRoot& rRoot ) : + XclImpChRoot( rRoot ) +{ +} + +void XclImpChLabelRange::ReadChLabelRange( XclImpStream& rStrm ) +{ + rStrm >> maData.mnCross >> maData.mnLabelFreq >> maData.mnTickFreq >> maData.mnFlags; +} + +void XclImpChLabelRange::Convert( ScfPropertySet& rPropSet, ScaleData& rScaleData, bool bMirrorOrient ) const +{ + // do not overlap text unless all labels are visible + rPropSet.SetBoolProperty( EXC_CHPROP_TEXTOVERLAP, maData.mnLabelFreq == 1 ); + // do not break text into several lines unless all labels are visible + rPropSet.SetBoolProperty( EXC_CHPROP_TEXTBREAK, maData.mnLabelFreq == 1 ); + // do not stagger labels in two lines + rPropSet.SetProperty( EXC_CHPROP_ARRANGEORDER, cssc::ChartAxisArrangeOrderType_SIDE_BY_SIDE ); + + // reverse order + bool bReverse = ::get_flag( maData.mnFlags, EXC_CHLABELRANGE_REVERSE ) != bMirrorOrient; + rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL; + + //! TODO #i58731# show n-th category +} + +void XclImpChLabelRange::ConvertAxisPosition( ScfPropertySet& rPropSet, bool b3dChart ) const +{ + /* Crossing mode (max-cross flag overrides other crossing settings). Excel + does not move the Y axis in 3D charts, regardless of actual settings. + But: the Y axis has to be moved to "end", if the X axis is mirrored, + to keep it at the left end of the chart. */ + bool bMaxCross = ::get_flag( maData.mnFlags, b3dChart ? EXC_CHLABELRANGE_REVERSE : EXC_CHLABELRANGE_MAXCROSS ); + cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE; + rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos ); + + // crossing position + double fCrossingPos = b3dChart ? 1.0 : maData.mnCross; + rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos ); +} + +// ---------------------------------------------------------------------------- + +XclImpChValueRange::XclImpChValueRange( const XclImpChRoot& rRoot ) : + XclImpChRoot( rRoot ) +{ +} + +void XclImpChValueRange::ReadChValueRange( XclImpStream& rStrm ) +{ + rStrm >> maData.mfMin + >> maData.mfMax + >> maData.mfMajorStep + >> maData.mfMinorStep + >> maData.mfCross + >> maData.mnFlags; +} + +void XclImpChValueRange::Convert( ScaleData& rScaleData, bool bMirrorOrient ) const +{ + // scaling algorithm + bool bLogScale = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE ); + OUString aScalingService = bLogScale ? SERVICE_CHART2_LOGSCALING : SERVICE_CHART2_LINEARSCALING; + rScaleData.Scaling.set( ScfApiHelper::CreateInstance( aScalingService ), UNO_QUERY ); + + // min/max + lclSetExpValueOrClearAny( rScaleData.Minimum, maData.mfMin, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN ) ); + lclSetExpValueOrClearAny( rScaleData.Maximum, maData.mfMax, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX ) ); + + // increment + bool bAutoMajor = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAJOR ); + bool bAutoMinor = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMINOR ); + // major increment + IncrementData& rIncrementData = rScaleData.IncrementData; + lclSetValueOrClearAny( rIncrementData.Distance, maData.mfMajorStep, bAutoMajor ); + // minor increment + Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements; + rSubIncrementSeq.realloc( 1 ); + Any& rIntervalCount = rSubIncrementSeq[ 0 ].IntervalCount; + if( bLogScale ) + { + if( !bAutoMinor ) + rIntervalCount <<= sal_Int32( 9 ); + } + else + { + sal_Int32 nCount = 0; + if( !bAutoMajor && !bAutoMinor && (0.0 < maData.mfMinorStep) && (maData.mfMinorStep <= maData.mfMajorStep) ) + { + double fCount = maData.mfMajorStep / maData.mfMinorStep + 0.5; + if( fCount < 1001.0 ) + nCount = static_cast< sal_Int32 >( fCount ); + } + lclSetValueOrClearAny( rIntervalCount, nCount, nCount == 0 ); + } + + // reverse order + bool bReverse = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE ) != bMirrorOrient; + rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL; +} + +void XclImpChValueRange::ConvertAxisPosition( ScfPropertySet& rPropSet ) const +{ + bool bMaxCross = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_MAXCROSS ); + bool bAutoCross = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS ); + bool bLogScale = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE ); + + // crossing mode (max-cross flag overrides other crossing settings) + cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE; + rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos ); + + // crossing position + double fCrossingPos = bAutoCross ? 0.0 : maData.mfCross; + if( bLogScale ) fCrossingPos = pow( 10.0, fCrossingPos ); + rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos ); +} + +// ---------------------------------------------------------------------------- + +namespace { + +sal_Int32 lclGetApiTickmarks( sal_uInt8 nXclTickPos ) +{ + using namespace ::com::sun::star::chart2::TickmarkStyle; + sal_Int32 nApiTickmarks = NONE; + ::set_flag( nApiTickmarks, INNER, ::get_flag( nXclTickPos, EXC_CHTICK_INSIDE ) ); + ::set_flag( nApiTickmarks, OUTER, ::get_flag( nXclTickPos, EXC_CHTICK_OUTSIDE ) ); + return nApiTickmarks; +} + +cssc::ChartAxisLabelPosition lclGetApiLabelPosition( sal_Int8 nXclLabelPos ) +{ + using namespace ::com::sun::star::chart; + switch( nXclLabelPos ) + { + case EXC_CHTICK_LOW: return ChartAxisLabelPosition_OUTSIDE_START; + case EXC_CHTICK_HIGH: return ChartAxisLabelPosition_OUTSIDE_END; + case EXC_CHTICK_NEXT: return ChartAxisLabelPosition_NEAR_AXIS; + } + return ChartAxisLabelPosition_NEAR_AXIS; +} + +} // namespace + +XclImpChTick::XclImpChTick( const XclImpChRoot& rRoot ) : + XclImpChRoot( rRoot ) +{ +} + +void XclImpChTick::ReadChTick( XclImpStream& rStrm ) +{ + rStrm >> maData.mnMajor + >> maData.mnMinor + >> maData.mnLabelPos + >> maData.mnBackMode; + rStrm.Ignore( 16 ); + rStrm >> maData.maTextColor + >> maData.mnFlags; + + if( GetBiff() == EXC_BIFF8 ) + { + // #116397# BIFF8: index into palette used instead of RGB data + maData.maTextColor = GetPalette().GetColor( rStrm.ReaduInt16() ); + // rotation + rStrm >> maData.mnRotation; + } + else + { + // BIFF2-BIFF7: get rotation from text orientation + sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 2, 3 ); + maData.mnRotation = XclTools::GetXclRotFromOrient( nOrient ); + } +} + +Color XclImpChTick::GetFontColor() const +{ + return ::get_flag( maData.mnFlags, EXC_CHTICK_AUTOCOLOR ) ? GetFontAutoColor() : maData.maTextColor; +} + +sal_uInt16 XclImpChTick::GetRotation() const +{ + return ::get_flag( maData.mnFlags, EXC_CHTICK_AUTOROT ) ? EXC_CHART_AUTOROTATION : maData.mnRotation; +} + +void XclImpChTick::Convert( ScfPropertySet& rPropSet ) const +{ + rPropSet.SetProperty( EXC_CHPROP_MAJORTICKS, lclGetApiTickmarks( maData.mnMajor ) ); + rPropSet.SetProperty( EXC_CHPROP_MINORTICKS, lclGetApiTickmarks( maData.mnMinor ) ); + rPropSet.SetProperty( EXC_CHPROP_LABELPOSITION, lclGetApiLabelPosition( maData.mnLabelPos ) ); + rPropSet.SetProperty( EXC_CHPROP_MARKPOSITION, cssc::ChartAxisMarkPosition_AT_AXIS ); +} + +// ---------------------------------------------------------------------------- + +XclImpChAxis::XclImpChAxis( const XclImpChRoot& rRoot, sal_uInt16 nAxisType ) : + XclImpChRoot( rRoot ), + mnNumFmtIdx( EXC_FORMAT_NOTFOUND ) +{ + maData.mnType = nAxisType; +} + +void XclImpChAxis::ReadHeaderRecord( XclImpStream& rStrm ) +{ + rStrm >> maData.mnType; +} + +void XclImpChAxis::ReadSubRecord( XclImpStream& rStrm ) +{ + switch( rStrm.GetRecId() ) + { + case EXC_ID_CHLABELRANGE: + mxLabelRange.reset( new XclImpChLabelRange( GetChRoot() ) ); + mxLabelRange->ReadChLabelRange( rStrm ); + break; + case EXC_ID_CHVALUERANGE: + mxValueRange.reset( new XclImpChValueRange( GetChRoot() ) ); + mxValueRange->ReadChValueRange( rStrm ); + break; + case EXC_ID_CHFORMAT: + rStrm >> mnNumFmtIdx; + break; + case EXC_ID_CHTICK: + mxTick.reset( new XclImpChTick( GetChRoot() ) ); + mxTick->ReadChTick( rStrm ); + break; + case EXC_ID_CHFONT: + mxFont.reset( new XclImpChFont ); + mxFont->ReadChFont( rStrm ); + break; + case EXC_ID_CHAXISLINE: + ReadChAxisLine( rStrm ); + break; + } +} + +void XclImpChAxis::Finalize() +{ + // add default scaling, needed e.g. to adjust rotation direction of pie and radar charts + if( !mxLabelRange ) + mxLabelRange.reset( new XclImpChLabelRange( GetChRoot() ) ); + if( !mxValueRange ) + mxValueRange.reset( new XclImpChValueRange( GetChRoot() ) ); + // remove invisible grid lines completely + if( mxMajorGrid.is() && !mxMajorGrid->HasLine() ) + mxMajorGrid.reset(); + if( mxMinorGrid.is() && !mxMinorGrid->HasLine() ) + mxMinorGrid.reset(); + // default tick settings different in OOChart and Excel + if( !mxTick ) + mxTick.reset( new XclImpChTick( GetChRoot() ) ); + // #i4140# different default axis line color + if( !mxAxisLine ) + { + XclChLineFormat aLineFmt; + // set "show axis" flag, default if line format record is missing + ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_SHOWAXIS ); + mxAxisLine.reset( new XclImpChLineFormat( aLineFmt ) ); + } + // add wall/floor frame for 3d charts + if( !mxWallFrame ) + CreateWallFrame(); +} + +sal_uInt16 XclImpChAxis::GetFontIndex() const +{ + return mxFont.is() ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND; +} + +Color XclImpChAxis::GetFontColor() const +{ + return mxTick.is() ? mxTick->GetFontColor() : GetFontAutoColor(); +} + +sal_uInt16 XclImpChAxis::GetRotation() const +{ + return mxTick.is() ? mxTick->GetRotation() : EXC_CHART_AUTOROTATION; +} + +Reference< XAxis > XclImpChAxis::CreateAxis( const XclImpChTypeGroup& rTypeGroup, const XclImpChAxis* pCrossingAxis ) const +{ + // create the axis object (always) + Reference< XAxis > xAxis( ScfApiHelper::CreateInstance( SERVICE_CHART2_AXIS ), UNO_QUERY ); + if( xAxis.is() ) + { + ScfPropertySet aAxisProp( xAxis ); + // #i58688# axis enabled + aAxisProp.SetBoolProperty( EXC_CHPROP_SHOW, IsActivated() ); + + // axis line properties + if( mxAxisLine.is() ) + mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE ); + // axis ticks properties + if( mxTick.is() ) + mxTick->Convert( aAxisProp ); + + // axis caption text -------------------------------------------------- + + // radar charts disable their category labels via chart type, not via axis + bool bHasLabels = HasLabels() && + ((GetAxisType() != EXC_CHAXIS_X) || rTypeGroup.HasCategoryLabels()); + aAxisProp.SetBoolProperty( EXC_CHPROP_DISPLAYLABELS, bHasLabels ); + if( bHasLabels ) + { + // font settings from CHFONT record or from default text + if( mxFont.is() ) + ConvertFontBase( GetChRoot(), aAxisProp ); + else if( const XclImpChText* pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_AXISLABEL ).get() ) + pDefText->ConvertFont( aAxisProp ); + // label text rotation + ConvertRotationBase( GetChRoot(), aAxisProp, true ); + // number format + sal_uInt32 nScNumFmt = GetNumFmtBuffer().GetScFormat( mnNumFmtIdx ); + if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND ) + aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT, static_cast< sal_Int32 >( nScNumFmt ) ); + } + + // axis scaling and increment ----------------------------------------- + + const XclChExtTypeInfo& rTypeInfo = rTypeGroup.GetTypeInfo(); + ScaleData aScaleData = xAxis->getScaleData(); + // set axis type + switch( GetAxisType() ) + { + case EXC_CHAXIS_X: + if( rTypeInfo.mbCategoryAxis ) + { + aScaleData.AxisType = cssc2::AxisType::CATEGORY; + aScaleData.Categories = rTypeGroup.CreateCategSequence(); + } + else + aScaleData.AxisType = cssc2::AxisType::REALNUMBER; + break; + case EXC_CHAXIS_Y: + aScaleData.AxisType = rTypeGroup.IsPercent() ? + cssc2::AxisType::PERCENT : cssc2::AxisType::REALNUMBER; + break; + case EXC_CHAXIS_Z: + aScaleData.AxisType = cssc2::AxisType::SERIES; + break; + } + // axis scaling settings, dependent on axis type + switch( aScaleData.AxisType ) + { + case cssc2::AxisType::CATEGORY: + case cssc2::AxisType::SERIES: + // #i71684# radar charts have reversed rotation direction + mxLabelRange->Convert( aAxisProp, aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR ); + break; + case cssc2::AxisType::REALNUMBER: + case cssc2::AxisType::PERCENT: + // #i85167# pie/donut charts have reversed rotation direction (at Y axis!) + mxValueRange->Convert( aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE ); + break; + default: + DBG_ERRORFILE( "XclImpChAxis::CreateAxis - unknown axis type" ); + } + + /* Do not set a value to the Origin member anymore (will be done via + new axis properties 'CrossoverPosition' and 'CrossoverValue'). */ + aScaleData.Origin.clear(); + + // write back + xAxis->setScaleData( aScaleData ); + + // grid --------------------------------------------------------------- + + // main grid + ScfPropertySet aGridProp( xAxis->getGridProperties() ); + aGridProp.SetBoolProperty( EXC_CHPROP_SHOW, HasMajorGrid() ); + if( mxMajorGrid.is() ) + mxMajorGrid->Convert( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE ); + // sub grid + Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties(); + if( aSubGridPropSeq.hasElements() ) + { + ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] ); + aSubGridProp.SetBoolProperty( EXC_CHPROP_SHOW, HasMinorGrid() ); + if( mxMinorGrid.is() ) + mxMinorGrid->Convert( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE ); + } + + // position of crossing axis ------------------------------------------ + + if( pCrossingAxis ) + pCrossingAxis->ConvertAxisPosition( aAxisProp, rTypeGroup ); + } + return xAxis; +} + +void XclImpChAxis::ConvertWall( ScfPropertySet& rPropSet ) const +{ + if( mxWallFrame.is() ) + mxWallFrame->Convert( rPropSet ); +} + +void XclImpChAxis::ConvertAxisPosition( ScfPropertySet& rPropSet, const XclImpChTypeGroup& rTypeGroup ) const +{ + if( ((GetAxisType() == EXC_CHAXIS_X) && rTypeGroup.GetTypeInfo().mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z) ) + mxLabelRange->ConvertAxisPosition( rPropSet, rTypeGroup.Is3dChart() ); + else + mxValueRange->ConvertAxisPosition( rPropSet ); +} + +void XclImpChAxis::ReadChAxisLine( XclImpStream& rStrm ) +{ + XclImpChLineFormatRef* pxLineFmt = 0; + bool bWallFrame = false; + switch( rStrm.ReaduInt16() ) + { + case EXC_CHAXISLINE_AXISLINE: pxLineFmt = &mxAxisLine; break; + case EXC_CHAXISLINE_MAJORGRID: pxLineFmt = &mxMajorGrid; break; + case EXC_CHAXISLINE_MINORGRID: pxLineFmt = &mxMinorGrid; break; + case EXC_CHAXISLINE_WALLS: bWallFrame = true; break; + } + if( bWallFrame ) + CreateWallFrame(); + + bool bLoop = pxLineFmt || bWallFrame; + while( bLoop ) + { + sal_uInt16 nRecId = rStrm.GetNextRecId(); + bLoop = ((nRecId == EXC_ID_CHLINEFORMAT) || + (nRecId == EXC_ID_CHAREAFORMAT) || + (nRecId == EXC_ID_CHESCHERFORMAT)) + && rStrm.StartNextRecord(); + if( bLoop ) + { + if( pxLineFmt && (nRecId == EXC_ID_CHLINEFORMAT) ) + { + pxLineFmt->reset( new XclImpChLineFormat ); + (*pxLineFmt)->ReadChLineFormat( rStrm ); + } + else if( bWallFrame && mxWallFrame.is() ) + { + mxWallFrame->ReadSubRecord( rStrm ); + } + } + } +} + +void XclImpChAxis::CreateWallFrame() +{ + switch( GetAxisType() ) + { + case EXC_CHAXIS_X: + mxWallFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_WALL3D ) ); + break; + case EXC_CHAXIS_Y: + mxWallFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_FLOOR3D ) ); + break; + default: + mxWallFrame.reset(); + } +} + +// ---------------------------------------------------------------------------- + +XclImpChAxesSet::XclImpChAxesSet( const XclImpChRoot& rRoot, sal_uInt16 nAxesSetId ) : + XclImpChRoot( rRoot ) +{ + maData.mnAxesSetId = nAxesSetId; +} + +void XclImpChAxesSet::ReadHeaderRecord( XclImpStream& rStrm ) +{ + rStrm >> maData.mnAxesSetId >> maData.maRect; +} + +void XclImpChAxesSet::ReadSubRecord( XclImpStream& rStrm ) +{ + switch( rStrm.GetRecId() ) + { + case EXC_ID_CHFRAMEPOS: + mxFramePos.reset( new XclImpChFramePos ); + mxFramePos->ReadChFramePos( rStrm ); + break; + case EXC_ID_CHAXIS: + ReadChAxis( rStrm ); + break; + case EXC_ID_CHTEXT: + ReadChText( rStrm ); + break; + case EXC_ID_CHPLOTFRAME: + ReadChPlotFrame( rStrm ); + break; + case EXC_ID_CHTYPEGROUP: + ReadChTypeGroup( rStrm ); + break; + } +} + +void XclImpChAxesSet::Finalize() +{ + if( IsValidAxesSet() ) + { + // finalize chart type groups, erase empty groups without series + XclImpChTypeGroupMap aValidGroups; + for( XclImpChTypeGroupMap::const_iterator aIt = maTypeGroups.begin(), aEnd = maTypeGroups.end(); aIt != aEnd; ++aIt ) + { + XclImpChTypeGroupRef xTypeGroup = aIt->second; + xTypeGroup->Finalize(); + if( xTypeGroup->IsValidGroup() ) + aValidGroups[ aIt->first ] = xTypeGroup; + } + maTypeGroups.swap( aValidGroups ); + } + + // invalid chart type groups are deleted now, check again with IsValidAxesSet() + if( IsValidAxesSet() ) + { + // always create missing axis objects + if( !mxXAxis ) + mxXAxis.reset( new XclImpChAxis( GetChRoot(), EXC_CHAXIS_X ) ); + if( !mxYAxis ) + mxYAxis.reset( new XclImpChAxis( GetChRoot(), EXC_CHAXIS_Y ) ); + if( !mxZAxis && GetFirstTypeGroup()->Is3dDeepChart() ) + mxZAxis.reset( new XclImpChAxis( GetChRoot(), EXC_CHAXIS_Z ) ); + + // finalize axes + if( mxXAxis.is() ) mxXAxis->Finalize(); + if( mxYAxis.is() ) mxYAxis->Finalize(); + if( mxZAxis.is() ) mxZAxis->Finalize(); + + // finalize axis titles + XclImpChTextRef xDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_AXISTITLE ); + String aAutoTitle = CREATE_STRING( "Axis Title" ); + lclFinalizeTitle( mxXAxisTitle, xDefText, aAutoTitle ); + lclFinalizeTitle( mxYAxisTitle, xDefText, aAutoTitle ); + lclFinalizeTitle( mxZAxisTitle, xDefText, aAutoTitle ); + + // #i47745# missing plot frame -> invisible border and area + if( !mxPlotFrame ) + mxPlotFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME ) ); + } +} + +XclImpChTypeGroupRef XclImpChAxesSet::GetFirstTypeGroup() const +{ + XclImpChTypeGroupRef xTypeGroup; + if( !maTypeGroups.empty() ) + xTypeGroup = maTypeGroups.begin()->second; + return xTypeGroup; +} + +XclImpChLegendRef XclImpChAxesSet::GetLegend() const +{ + XclImpChLegendRef xLegend; + for( XclImpChTypeGroupMap::const_iterator aIt = maTypeGroups.begin(), aEnd = maTypeGroups.end(); !xLegend && (aIt != aEnd); ++aIt ) + xLegend = aIt->second->GetLegend(); + return xLegend; +} + +const String& XclImpChAxesSet::GetSingleSeriesTitle() const +{ + return (maTypeGroups.size() == 1) ? maTypeGroups.begin()->second->GetSingleSeriesTitle() : String::EmptyString(); +} + +void XclImpChAxesSet::Convert( Reference< XDiagram > xDiagram ) const +{ + if( IsValidAxesSet() && xDiagram.is() ) + { + // diagram background formatting + if( GetAxesSetId() == EXC_CHAXESSET_PRIMARY ) + ConvertBackground( xDiagram ); + + // create the coordinate system, this inserts all chart types and series + Reference< XCoordinateSystem > xCoordSystem = CreateCoordSystem( xDiagram ); + if( xCoordSystem.is() ) + { + // insert coordinate system, if not already done + try + { + Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY_THROW ); + Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems(); + if( aCoordSystems.getLength() == 0 ) + xCoordSystemCont->addCoordinateSystem( xCoordSystem ); + } + catch( Exception& ) + { + DBG_ERRORFILE( "XclImpChAxesSet::Convert - cannot insert coordinate system" ); + } + + // create the axes with grids and axis titles and insert them into the diagram + ConvertAxis( mxXAxis, mxXAxisTitle, xCoordSystem, mxYAxis.get() ); + ConvertAxis( mxYAxis, mxYAxisTitle, xCoordSystem, mxXAxis.get() ); + ConvertAxis( mxZAxis, mxZAxisTitle, xCoordSystem, 0 ); + } + } +} + +void XclImpChAxesSet::ConvertTitlePositions() const +{ + if( mxXAxisTitle.is() ) + mxXAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_X ) ); + if( mxYAxisTitle.is() ) + mxYAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_Y ) ); + if( mxZAxisTitle.is() ) + mxZAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_Z ) ); +} + +void XclImpChAxesSet::ReadChAxis( XclImpStream& rStrm ) +{ + XclImpChAxisRef xAxis( new XclImpChAxis( GetChRoot() ) ); + xAxis->ReadRecordGroup( rStrm ); + + switch( xAxis->GetAxisType() ) + { + case EXC_CHAXIS_X: mxXAxis = xAxis; break; + case EXC_CHAXIS_Y: mxYAxis = xAxis; break; + case EXC_CHAXIS_Z: mxZAxis = xAxis; break; + } +} + +void XclImpChAxesSet::ReadChText( XclImpStream& rStrm ) +{ + XclImpChTextRef xText( new XclImpChText( GetChRoot() ) ); + xText->ReadRecordGroup( rStrm ); + + switch( xText->GetLinkTarget() ) + { + case EXC_CHOBJLINK_XAXIS: mxXAxisTitle = xText; break; + case EXC_CHOBJLINK_YAXIS: mxYAxisTitle = xText; break; + case EXC_CHOBJLINK_ZAXIS: mxZAxisTitle = xText; break; + } +} + +void XclImpChAxesSet::ReadChPlotFrame( XclImpStream& rStrm ) +{ + if( (rStrm.GetNextRecId() == EXC_ID_CHFRAME) && rStrm.StartNextRecord() ) + { + mxPlotFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME ) ); + mxPlotFrame->ReadRecordGroup( rStrm ); + } +} + +void XclImpChAxesSet::ReadChTypeGroup( XclImpStream& rStrm ) +{ + XclImpChTypeGroupRef xTypeGroup( new XclImpChTypeGroup( GetChRoot() ) ); + xTypeGroup->ReadRecordGroup( rStrm ); + maTypeGroups[ xTypeGroup->GetGroupIdx() ] = xTypeGroup; +} + +Reference< XCoordinateSystem > XclImpChAxesSet::CreateCoordSystem( Reference< XDiagram > xDiagram ) const +{ + Reference< XCoordinateSystem > xCoordSystem; + + /* Try to get existing ccordinate system. For now, all series from primary + and secondary axes sets are inserted into one coordinate system. Later, + this should be changed to use one coordinate system for each axes set. */ + Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY ); + if( xCoordSystemCont.is() ) + { + Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems(); + DBG_ASSERT( aCoordSystems.getLength() <= 1, "XclImpChAxesSet::CreateCoordSystem - too many existing coordinate systems" ); + if( aCoordSystems.getLength() > 0 ) + xCoordSystem = aCoordSystems[ 0 ]; + } + + // create the coordinate system according to the first chart type + if( !xCoordSystem.is() ) + { + XclImpChTypeGroupRef xTypeGroup = GetFirstTypeGroup(); + if( xTypeGroup.is() ) + { + xCoordSystem = xTypeGroup->CreateCoordSystem(); + // convert 3d chart settings + ScfPropertySet aDiaProp( xDiagram ); + xTypeGroup->ConvertChart3d( aDiaProp ); + } + } + + /* Create XChartType objects for all chart type groups. Each group will + add its series to the data provider attached to the chart document. */ + Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY ); + if( xChartTypeCont.is() ) + { + sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex(); + for( XclImpChTypeGroupMap::const_iterator aIt = maTypeGroups.begin(), aEnd = maTypeGroups.end(); aIt != aEnd; ++aIt ) + { + try + { + Reference< XChartType > xChartType = aIt->second->CreateChartType( xDiagram, nApiAxesSetIdx ); + if( xChartType.is() ) + xChartTypeCont->addChartType( xChartType ); + } + catch( Exception& ) + { + DBG_ERRORFILE( "XclImpChAxesSet::CreateCoordSystem - cannot add chart type" ); + } + } + } + + return xCoordSystem; +} + +void XclImpChAxesSet::ConvertAxis( + XclImpChAxisRef xChAxis, XclImpChTextRef xChAxisTitle, + Reference< XCoordinateSystem > xCoordSystem, const XclImpChAxis* pCrossingAxis ) const +{ + if( xChAxis.is() ) + { + // create and attach the axis object + Reference< XAxis > xAxis = CreateAxis( *xChAxis, pCrossingAxis ); + if( xAxis.is() ) + { + // create and attach the axis title + if( xChAxisTitle.is() ) try + { + Reference< XTitled > xTitled( xAxis, UNO_QUERY_THROW ); + Reference< XTitle > xTitle( xChAxisTitle->CreateTitle(), UNO_SET_THROW ); + xTitled->setTitleObject( xTitle ); + } + catch( Exception& ) + { + DBG_ERRORFILE( "XclImpChAxesSet::ConvertAxis - cannot set axis title" ); + } + + // insert axis into coordinate system + try + { + sal_Int32 nApiAxisDim = xChAxis->GetApiAxisDimension(); + sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex(); + xCoordSystem->setAxisByDimension( nApiAxisDim, xAxis, nApiAxesSetIdx ); + } + catch( Exception& ) + { + DBG_ERRORFILE( "XclImpChAxesSet::ConvertAxis - cannot set axis" ); + } + } + } +} + +Reference< XAxis > XclImpChAxesSet::CreateAxis( const XclImpChAxis& rChAxis, const XclImpChAxis* pCrossingAxis ) const +{ + Reference< XAxis > xAxis; + if( const XclImpChTypeGroup* pTypeGroup = GetFirstTypeGroup().get() ) + xAxis = rChAxis.CreateAxis( *pTypeGroup, pCrossingAxis ); + return xAxis; +} + +void XclImpChAxesSet::ConvertBackground( Reference< XDiagram > xDiagram ) const +{ + XclImpChTypeGroupRef xTypeGroup = GetFirstTypeGroup(); + if( xTypeGroup.is() && xTypeGroup->Is3dWallChart() ) + { + // wall/floor formatting (3D charts) + if( mxXAxis.is() ) + { + ScfPropertySet aWallProp( xDiagram->getWall() ); + mxXAxis->ConvertWall( aWallProp ); + } + if( mxYAxis.is() ) + { + ScfPropertySet aFloorProp( xDiagram->getFloor() ); + mxYAxis->ConvertWall( aFloorProp ); + } + } + else if( mxPlotFrame.is() ) + { + // diagram background formatting + ScfPropertySet aWallProp( xDiagram->getWall() ); + mxPlotFrame->Convert( aWallProp ); + } +} + +// The chart object =========================================================== + +XclImpChChart::XclImpChChart( const XclImpRoot& rRoot ) : + XclImpChRoot( rRoot, *this ) +{ + mxPrimAxesSet.reset( new XclImpChAxesSet( GetChRoot(), EXC_CHAXESSET_PRIMARY ) ); + mxSecnAxesSet.reset( new XclImpChAxesSet( GetChRoot(), EXC_CHAXESSET_SECONDARY ) ); +} + +XclImpChChart::~XclImpChChart() +{ +} + +void XclImpChChart::ReadHeaderRecord( XclImpStream& rStrm ) +{ + // coordinates are stored as 16.16 fixed point + rStrm >> maRect; +} + +void XclImpChChart::ReadSubRecord( XclImpStream& rStrm ) +{ + switch( rStrm.GetRecId() ) + { + case EXC_ID_CHFRAME: + mxFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND ) ); + mxFrame->ReadRecordGroup( rStrm ); + break; + case EXC_ID_CHSERIES: + ReadChSeries( rStrm ); + break; + case EXC_ID_CHPROPERTIES: + ReadChProperties( rStrm ); + break; + case EXC_ID_CHDEFAULTTEXT: + ReadChDefaultText( rStrm ); + break; + case EXC_ID_CHAXESSET: + ReadChAxesSet( rStrm ); + break; + case EXC_ID_CHTEXT: + ReadChText( rStrm ); + break; + case EXC_ID_CHEND: + Finalize(); // finalize the entire chart object + break; + } +} + +void XclImpChChart::ReadChDefaultText( XclImpStream& rStrm ) +{ + sal_uInt16 nTextId = rStrm.ReaduInt16(); + if( (rStrm.GetNextRecId() == EXC_ID_CHTEXT) && rStrm.StartNextRecord() ) + { + XclImpChTextRef xText( new XclImpChText( GetChRoot() ) ); + xText->ReadRecordGroup( rStrm ); + maDefTexts[ nTextId ] = xText; + } +} + +void XclImpChChart::ReadChDataFormat( XclImpStream& rStrm ) +{ + XclImpChDataFormatRef xDataFmt( new XclImpChDataFormat( GetChRoot() ) ); + xDataFmt->ReadRecordGroup( rStrm ); + if( xDataFmt->GetPointPos().mnSeriesIdx <= EXC_CHSERIES_MAXSERIES ) + { + XclImpChDataFormatRef& rxMapFmt = maDataFmts[ xDataFmt->GetPointPos() ]; + /* Do not overwrite existing data format group, Excel always uses the + first data format group occuring in any CHSERIES group. */ + if( !rxMapFmt ) + rxMapFmt = xDataFmt; + } +} + +void XclImpChChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData ) +{ + if( !mxFrame ) + mxFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND ) ); + mxFrame->UpdateObjFrame( rLineData, rFillData ); +} + +XclImpChTypeGroupRef XclImpChChart::GetTypeGroup( sal_uInt16 nGroupIdx ) const +{ + XclImpChTypeGroupRef xTypeGroup = mxPrimAxesSet->GetTypeGroup( nGroupIdx ); + if( !xTypeGroup ) xTypeGroup = mxSecnAxesSet->GetTypeGroup( nGroupIdx ); + if( !xTypeGroup ) xTypeGroup = mxPrimAxesSet->GetFirstTypeGroup(); + return xTypeGroup; +} + +XclImpChTextRef XclImpChChart::GetDefaultText( XclChTextType eTextType ) const +{ + sal_uInt16 nDefTextId = EXC_CHDEFTEXT_GLOBAL; + bool bBiff8 = GetBiff() == EXC_BIFF8; + switch( eTextType ) + { + case EXC_CHTEXTTYPE_TITLE: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break; + case EXC_CHTEXTTYPE_LEGEND: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break; + case EXC_CHTEXTTYPE_AXISTITLE: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break; + case EXC_CHTEXTTYPE_AXISLABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break; + case EXC_CHTEXTTYPE_DATALABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break; + } + return maDefTexts.get( nDefTextId ); +} + +bool XclImpChChart::IsManualPlotArea() const +{ + // there is no real automatic mode in BIFF5 charts + return (GetBiff() <= EXC_BIFF5) || ::get_flag( maProps.mnFlags, EXC_CHPROPS_USEMANPLOTAREA ); +} + +void XclImpChChart::Convert( Reference< XChartDocument > xChartDoc, + XclImpDffConverter& rDffConv, const OUString& rObjName, const Rectangle& rChartRect ) const +{ + // initialize conversion (locks the model to suppress any internal updates) + InitConversion( xChartDoc, rChartRect ); + + // chart frame formatting + if( mxFrame.is() ) + { + ScfPropertySet aFrameProp( xChartDoc->getPageBackground() ); + mxFrame->Convert( aFrameProp ); + } + + // chart title + if( mxTitle.is() ) try + { + Reference< XTitled > xTitled( xChartDoc, UNO_QUERY_THROW ); + Reference< XTitle > xTitle( mxTitle->CreateTitle(), UNO_SET_THROW ); + xTitled->setTitleObject( xTitle ); + } + catch( Exception& ) + { + } + + /* Create the diagram object and attach it to the chart document. Currently, + one diagram is used to carry all coordinate systems and data series. */ + Reference< XDiagram > xDiagram = CreateDiagram(); + xChartDoc->setFirstDiagram( xDiagram ); + + // coordinate systems and chart types, convert axis settings + mxPrimAxesSet->Convert( xDiagram ); + mxSecnAxesSet->Convert( xDiagram ); + + // legend + if( xDiagram.is() && mxLegend.is() ) + xDiagram->setLegend( mxLegend->CreateLegend() ); + + /* Following all conversions needing the old Chart1 API that involves full + initialization of the chart view. */ + Reference< cssc::XChartDocument > xChart1Doc( xChartDoc, UNO_QUERY ); + if( xChart1Doc.is() ) + { + Reference< cssc::XDiagram > xDiagram1 = xChart1Doc->getDiagram(); + + /* Set the 'IncludeHiddenCells' property via the old API as only this + ensures that the data provider and all created sequences get this + flag correctly. */ + ScfPropertySet aDiaProp( xDiagram1 ); + bool bShowVisCells = ::get_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY ); + aDiaProp.SetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS, !bShowVisCells ); + + // plot area position and size (there is no real automatic mode in BIFF5 charts) + XclImpChFramePosRef xPlotAreaPos = mxPrimAxesSet->GetPlotAreaFramePos(); + if( IsManualPlotArea() && xPlotAreaPos.is() ) try + { + const XclChFramePos& rFramePos = xPlotAreaPos->GetFramePosData(); + if( (rFramePos.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rFramePos.mnBRMode == EXC_CHFRAMEPOS_PARENT) ) + { + Reference< cssc::XDiagramPositioning > xPositioning( xDiagram1, UNO_QUERY_THROW ); + ::com::sun::star::awt::Rectangle aDiagramRect = CalcHmmFromChartRect( rFramePos.maRect ); + // for pie charts, always set inner plot area size to exclude the data labels as Excel does + const XclImpChTypeGroup* pFirstTypeGroup = mxPrimAxesSet->GetFirstTypeGroup().get(); + if( pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE) ) + xPositioning->setDiagramPositionExcludingAxes( aDiagramRect ); + else if( pFirstTypeGroup && pFirstTypeGroup->Is3dChart() ) + xPositioning->setDiagramPositionIncludingAxesAndAxisTitles( aDiagramRect ); + else + xPositioning->setDiagramPositionIncludingAxes( aDiagramRect ); + } + } + catch( Exception& ) + { + } + + // positions of all title objects + if( mxTitle.is() ) + mxTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_TITLE ) ); + mxPrimAxesSet->ConvertTitlePositions(); + mxSecnAxesSet->ConvertTitlePositions(); + } + + // unlock the model + FinishConversion( rDffConv ); + + // start listening to this chart + ScDocument& rDoc = GetRoot().GetDoc(); + if( ScChartListenerCollection* pChartCollection = rDoc.GetChartListenerCollection() ) + { + ::std::auto_ptr< ::std::vector< ScSharedTokenRef > > xRefTokens( new ::std::vector< ScSharedTokenRef > ); + for( XclImpChSeriesVec::const_iterator aIt = maSeries.begin(), aEnd = maSeries.end(); aIt != aEnd; ++aIt ) + (*aIt)->FillAllSourceLinks( *xRefTokens ); + if( !xRefTokens->empty() ) + { + ::std::auto_ptr< ScChartListener > xListener( new ScChartListener( rObjName, &rDoc, xRefTokens.release() ) ); + xListener->SetUsed( true ); + xListener->StartListeningTo(); + pChartCollection->Insert( xListener.release() ); + } + } +} + +void XclImpChChart::ReadChSeries( XclImpStream& rStrm ) +{ + sal_uInt16 nNewSeriesIdx = static_cast< sal_uInt16 >( maSeries.size() ); + XclImpChSeriesRef xSeries( new XclImpChSeries( GetChRoot(), nNewSeriesIdx ) ); + xSeries->ReadRecordGroup( rStrm ); + maSeries.push_back( xSeries ); +} + +void XclImpChChart::ReadChProperties( XclImpStream& rStrm ) +{ + rStrm >> maProps.mnFlags >> maProps.mnEmptyMode; +} + +void XclImpChChart::ReadChAxesSet( XclImpStream& rStrm ) +{ + XclImpChAxesSetRef xAxesSet( new XclImpChAxesSet( GetChRoot(), EXC_CHAXESSET_NONE ) ); + xAxesSet->ReadRecordGroup( rStrm ); + switch( xAxesSet->GetAxesSetId() ) + { + case EXC_CHAXESSET_PRIMARY: mxPrimAxesSet = xAxesSet; break; + case EXC_CHAXESSET_SECONDARY: mxSecnAxesSet = xAxesSet; break; + } +} + +void XclImpChChart::ReadChText( XclImpStream& rStrm ) +{ + XclImpChTextRef xText( new XclImpChText( GetChRoot() ) ); + xText->ReadRecordGroup( rStrm ); + switch( xText->GetLinkTarget() ) + { + case EXC_CHOBJLINK_TITLE: + mxTitle = xText; + break; + case EXC_CHOBJLINK_DATA: + { + sal_uInt16 nSeriesIdx = xText->GetPointPos().mnSeriesIdx; + if( nSeriesIdx < maSeries.size() ) + maSeries[ nSeriesIdx ]->SetDataLabel( xText ); + } + break; + } +} + +void XclImpChChart::Finalize() +{ + // finalize series (must be done first) + FinalizeSeries(); + // #i49218# legend may be attached to primary or secondary axes set + mxLegend = mxPrimAxesSet->GetLegend(); + if( !mxLegend ) + mxLegend = mxSecnAxesSet->GetLegend(); + if( mxLegend.is() ) + mxLegend->Finalize(); + // axes sets, updates chart type group default formats -> must be called before FinalizeDataFormats() + mxPrimAxesSet->Finalize(); + mxSecnAxesSet->Finalize(); + // formatting of all series + FinalizeDataFormats(); + // #i47745# missing frame -> invisible border and area + if( !mxFrame ) + mxFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND ) ); + // chart title + FinalizeTitle(); +} + +void XclImpChChart::FinalizeSeries() +{ + for( XclImpChSeriesVec::iterator aSIt = maSeries.begin(), aSEnd = maSeries.end(); aSIt != aSEnd; ++aSIt ) + { + XclImpChSeriesRef xSeries = *aSIt; + if( xSeries->HasParentSeries() ) + { + /* Process child series (trend lines and error bars). Data of + child series will be set at the connected parent series. */ + if( xSeries->GetParentIdx() < maSeries.size() ) + maSeries[ xSeries->GetParentIdx() ]->AddChildSeries( *xSeries ); + } + else + { + // insert the series into the related chart type group + if( XclImpChTypeGroup* pTypeGroup = GetTypeGroup( xSeries->GetGroupIdx() ).get() ) + pTypeGroup->AddSeries( xSeries ); + } + } +} + +void XclImpChChart::FinalizeDataFormats() +{ + /* #i51639# (part 1): CHDATAFORMAT groups are part of CHSERIES groups. + Each CHDATAFORMAT group specifies the series and data point it is + assigned to. This makes it possible to have a data format that is + related to another series, e.g. a CHDATAFORMAT group for series 2 is + part of a CHSERIES group that describes series 1. Therefore the chart + itself has collected all CHDATAFORMAT groups to be able to store data + format groups for series that have not been imported at that time. This + loop finally assigns these groups to the related series. */ + for( XclImpChDataFormatMap::const_iterator aMIt = maDataFmts.begin(), aMEnd = maDataFmts.end(); aMIt != aMEnd; ++aMIt ) + { + sal_uInt16 nSeriesIdx = aMIt->first.mnSeriesIdx; + if( nSeriesIdx < maSeries.size() ) + maSeries[ nSeriesIdx ]->SetDataFormat( aMIt->second ); + } + + /* #i51639# (part 2): Finalize data formats of all series. This adds for + example missing CHDATAFORMAT groups for entire series that are needed + for automatic colors of lines and areas. */ + for( XclImpChSeriesVec::iterator aVIt = maSeries.begin(), aVEnd = maSeries.end(); aVIt != aVEnd; ++aVIt ) + (*aVIt)->FinalizeDataFormats(); +} + +void XclImpChChart::FinalizeTitle() +{ + // special handling for auto-generated title + String aAutoTitle; + if( !mxTitle || (!mxTitle->IsDeleted() && !mxTitle->HasString()) ) + { + // automatic title from first series name (if there are no series on secondary axes set) + if( !mxSecnAxesSet->IsValidAxesSet() ) + aAutoTitle = mxPrimAxesSet->GetSingleSeriesTitle(); + if( mxTitle.is() || (aAutoTitle.Len() > 0) ) + { + if( !mxTitle ) + mxTitle.reset( new XclImpChText( GetChRoot() ) ); + if( aAutoTitle.Len() == 0 ) + aAutoTitle = CREATE_STRING( "Chart Title" ); + } + } + + // will reset mxTitle, if it does not contain a string and no auto title exists + lclFinalizeTitle( mxTitle, GetDefaultText( EXC_CHTEXTTYPE_TITLE ), aAutoTitle ); +} + +Reference< XDiagram > XclImpChChart::CreateDiagram() const +{ + // create a diagram object + Reference< XDiagram > xDiagram( ScfApiHelper::CreateInstance( SERVICE_CHART2_DIAGRAM ), UNO_QUERY ); + + // convert global chart settings + ScfPropertySet aDiaProp( xDiagram ); + + // treatment of missing values + using namespace cssc::MissingValueTreatment; + sal_Int32 nMissingValues = LEAVE_GAP; + switch( maProps.mnEmptyMode ) + { + case EXC_CHPROPS_EMPTY_SKIP: nMissingValues = LEAVE_GAP; break; + case EXC_CHPROPS_EMPTY_ZERO: nMissingValues = USE_ZERO; break; + case EXC_CHPROPS_EMPTY_INTERPOLATE: nMissingValues = CONTINUE; break; + } + aDiaProp.SetProperty( EXC_CHPROP_MISSINGVALUETREATMENT, nMissingValues ); + + return xDiagram; +} + +// ---------------------------------------------------------------------------- + +XclImpChartDrawing::XclImpChartDrawing( const XclImpRoot& rRoot, bool bOwnTab ) : + XclImpDrawing( rRoot, bOwnTab ), // sheet charts may contain OLE objects + mnScTab( rRoot.GetCurrScTab() ), + mbOwnTab( bOwnTab ) +{ +} + +void XclImpChartDrawing::ConvertObjects( XclImpDffConverter& rDffConv, + const Reference< XModel >& rxModel, const Rectangle& rChartRect ) +{ + maChartRect = rChartRect; // needed in CalcAnchorRect() callback + + SdrModel* pSdrModel = 0; + SdrPage* pSdrPage = 0; + if( mbOwnTab ) + { + // chart sheet: insert all shapes into the sheet, not into the chart object + pSdrModel = GetDoc().GetDrawLayer(); + pSdrPage = GetSdrPage( mnScTab ); + } + else + { + // embedded chart object: insert all shapes into the chart + try + { + Reference< XDrawPageSupplier > xDrawPageSupp( rxModel, UNO_QUERY_THROW ); + Reference< XDrawPage > xDrawPage( xDrawPageSupp->getDrawPage(), UNO_SET_THROW ); + pSdrPage = ::GetSdrPageFromXDrawPage( xDrawPage ); + pSdrModel = pSdrPage ? pSdrPage->GetModel() : 0; + } + catch( Exception& ) + { + } + } + + if( pSdrModel && pSdrPage ) + ImplConvertObjects( rDffConv, *pSdrModel, *pSdrPage ); +} + +Rectangle XclImpChartDrawing::CalcAnchorRect( const XclObjAnchor& rAnchor, bool bDffAnchor ) const +{ + /* In objects with DFF client anchor, the position of the shape is stored + in the cell address components of the client anchor. In old BIFF3-BIFF5 + objects, the position is stored in the offset components of the anchor. */ + Rectangle aRect( + static_cast< long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnCol : rAnchor.mnLX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ), + static_cast< long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnRow : rAnchor.mnTY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ), + static_cast< long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnCol : rAnchor.mnRX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ), + static_cast< long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnRow : rAnchor.mnBY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ) ); + aRect.Justify(); + // move shapes into chart area for sheet charts + if( mbOwnTab ) + aRect.Move( maChartRect.Left(), maChartRect.Top() ); + return aRect; +} + +void XclImpChartDrawing::OnObjectInserted( const XclImpDrawObjBase& ) +{ +} + +// ---------------------------------------------------------------------------- + +XclImpChart::XclImpChart( const XclImpRoot& rRoot, bool bOwnTab ) : + XclImpRoot( rRoot ), + mbOwnTab( bOwnTab ), + mbIsPivotChart( false ) +{ +} + +XclImpChart::~XclImpChart() +{ +} + +void XclImpChart::ReadChartSubStream( XclImpStream& rStrm ) +{ + XclImpPageSettings& rPageSett = GetPageSettings(); + XclImpTabViewSettings& rTabViewSett = GetTabViewSettings(); + + bool bLoop = true; + while( bLoop && rStrm.StartNextRecord() ) + { + // page settings - only for charts in entire sheet + if( mbOwnTab ) switch( rStrm.GetRecId() ) + { + case EXC_ID_HORPAGEBREAKS: + case EXC_ID_VERPAGEBREAKS: rPageSett.ReadPageBreaks( rStrm ); break; + case EXC_ID_HEADER: + case EXC_ID_FOOTER: rPageSett.ReadHeaderFooter( rStrm ); break; + case EXC_ID_LEFTMARGIN: + case EXC_ID_RIGHTMARGIN: + case EXC_ID_TOPMARGIN: + case EXC_ID_BOTTOMMARGIN: rPageSett.ReadMargin( rStrm ); break; + case EXC_ID_PRINTHEADERS: rPageSett.ReadPrintHeaders( rStrm ); break; + case EXC_ID_PRINTGRIDLINES: rPageSett.ReadPrintGridLines( rStrm ); break; + case EXC_ID_HCENTER: + case EXC_ID_VCENTER: rPageSett.ReadCenter( rStrm ); break; + case EXC_ID_SETUP: rPageSett.ReadSetup( rStrm ); break; + case EXC_ID8_IMGDATA: rPageSett.ReadImgData( rStrm ); break; + + case EXC_ID_WINDOW2: rTabViewSett.ReadWindow2( rStrm, true );break; + case EXC_ID_SCL: rTabViewSett.ReadScl( rStrm ); break; + + case EXC_ID_SHEETEXT: //0x0862 + { + // FIXME: do not need to pass palette, XclImpTabVieSettings is derived from root + XclImpPalette& rPal = GetPalette(); + rTabViewSett.ReadTabBgColor( rStrm, rPal); + } + break; + + case EXC_ID_CODENAME: ReadCodeName( rStrm, false ); break; + } + + // common records + switch( rStrm.GetRecId() ) + { + case EXC_ID_EOF: bLoop = false; break; + + // #i31882# ignore embedded chart objects + case EXC_ID2_BOF: + case EXC_ID3_BOF: + case EXC_ID4_BOF: + case EXC_ID5_BOF: XclTools::SkipSubStream( rStrm ); break; + + case EXC_ID_CHCHART: ReadChChart( rStrm ); break; + + case EXC_ID8_CHPIVOTREF: + GetTracer().TracePivotChartExists(); + mbIsPivotChart = true; + break; + + // BIFF specific records + default: switch( GetBiff() ) + { + case EXC_BIFF5: switch( rStrm.GetRecId() ) + { + case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break; + } + break; + case EXC_BIFF8: switch( rStrm.GetRecId() ) + { + case EXC_ID_MSODRAWING: GetChartDrawing().ReadMsoDrawing( rStrm ); break; + // #i61786# weird documents: OBJ without MSODRAWING -> read in BIFF5 format + case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break; + } + break; + default:; + } + } + } +} + +void XclImpChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData ) +{ + if( !mxChartData ) + mxChartData.reset( new XclImpChChart( GetRoot() ) ); + mxChartData->UpdateObjFrame( rLineData, rFillData ); +} + +sal_Size XclImpChart::GetProgressSize() const +{ + return + (mxChartData.is() ? mxChartData->GetProgressSize() : 0) + + (mxChartDrawing.is() ? mxChartDrawing->GetProgressSize() : 0); +} + +void XclImpChart::Convert( Reference< XModel > xModel, XclImpDffConverter& rDffConv, const OUString& rObjName, const Rectangle& rChartRect ) const +{ + Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY ); + if( xChartDoc.is() ) + { + if( mxChartData.is() ) + mxChartData->Convert( xChartDoc, rDffConv, rObjName, rChartRect ); + if( mxChartDrawing.is() ) + mxChartDrawing->ConvertObjects( rDffConv, xModel, rChartRect ); + } +} + +XclImpChartDrawing& XclImpChart::GetChartDrawing() +{ + if( !mxChartDrawing ) + mxChartDrawing.reset( new XclImpChartDrawing( GetRoot(), mbOwnTab ) ); + return *mxChartDrawing; +} + +void XclImpChart::ReadChChart( XclImpStream& rStrm ) +{ + mxChartData.reset( new XclImpChChart( GetRoot() ) ); + mxChartData->ReadRecordGroup( rStrm ); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xicontent.cxx b/sc/source/filter/excel/xicontent.cxx new file mode 100644 index 000000000000..fb1eb9a3bf44 --- /dev/null +++ b/sc/source/filter/excel/xicontent.cxx @@ -0,0 +1,1319 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xicontent.hxx" +#include <sfx2/objsh.hxx> +#include <sfx2/docfile.hxx> +#include <tools/urlobj.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editobj.hxx> +#include <sfx2/linkmgr.hxx> +#include <svl/itemset.hxx> +#include "scitems.hxx" +#include <editeng/eeitem.hxx> +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <editeng/flditem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/crsditem.hxx> +#include "document.hxx" +#include "editutil.hxx" +#include "cell.hxx" +#include "validat.hxx" +#include "patattr.hxx" +#include "docpool.hxx" +#include "rangenam.hxx" +#include "arealink.hxx" +#include "stlsheet.hxx" +#include "scextopt.hxx" +#include "xlformula.hxx" +#include "xltracer.hxx" +#include "xistream.hxx" +#include "xihelper.hxx" +#include "xistyle.hxx" +#include "xiescher.hxx" +#include "xiname.hxx" + +#include "excform.hxx" +#include "tabprotection.hxx" + +#include <memory> + +using ::com::sun::star::uno::Sequence; +using ::std::auto_ptr; + +// Shared string table ======================================================== + +XclImpSst::XclImpSst( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +void XclImpSst::ReadSst( XclImpStream& rStrm ) +{ + sal_uInt32 nStrCount; + rStrm.Ignore( 4 ); + rStrm >> nStrCount; + maStrings.clear(); + maStrings.reserve( static_cast< size_t >( nStrCount ) ); + while( (nStrCount > 0) && rStrm.IsValid() ) + { + XclImpString aString; + aString.Read( rStrm ); + maStrings.push_back( aString ); + --nStrCount; + } +} + +const XclImpString* XclImpSst::GetString( sal_uInt32 nSstIndex ) const +{ + return (nSstIndex < maStrings.size()) ? &maStrings[ nSstIndex ] : 0; +} + +ScBaseCell* XclImpSst::CreateCell( sal_uInt32 nSstIndex, sal_uInt16 nXFIndex ) const +{ + ScBaseCell* pCell = 0; + if( const XclImpString* pString = GetString( nSstIndex ) ) + pCell = XclImpStringHelper::CreateCell( *this, *pString, nXFIndex ); + return pCell; +} + +// Hyperlinks ================================================================= + +namespace { + +/** Reads character array and stores it into rString. + @param nChars Number of following characters (not byte count!). + @param b16Bit true = 16-bit characters, false = 8-bit characters. */ +void lclAppendString32( String& rString, XclImpStream& rStrm, sal_uInt32 nChars, bool b16Bit ) +{ + sal_uInt16 nReadChars = ulimit_cast< sal_uInt16 >( nChars ); + rString.Append( rStrm.ReadRawUniString( nReadChars, b16Bit ) ); + // ignore remaining chars + sal_Size nIgnore = nChars - nReadChars; + if( b16Bit ) + nIgnore *= 2; + rStrm.Ignore( nIgnore ); +} + +/** Reads 32-bit string length and the character array and stores it into rString. + @param b16Bit true = 16-bit characters, false = 8-bit characters. */ +void lclAppendString32( String& rString, XclImpStream& rStrm, bool b16Bit ) +{ + lclAppendString32( rString, rStrm, rStrm.ReaduInt32(), b16Bit ); +} + +/** Reads 32-bit string length and ignores following character array. + @param b16Bit true = 16-bit characters, false = 8-bit characters. */ +void lclIgnoreString32( XclImpStream& rStrm, bool b16Bit ) +{ + sal_uInt32 nChars; + rStrm >> nChars; + if( b16Bit ) + nChars *= 2; + rStrm.Ignore( nChars ); +} + +/** Converts a path to an absolute path. + @param rPath The source path. The resulting path is returned here. + @param nLevel Number of parent directories to add in front of the path. */ +void lclGetAbsPath( String& rPath, sal_uInt16 nLevel, SfxObjectShell* pDocShell ) +{ + String aTmpStr; + while( nLevel ) + { + aTmpStr.AppendAscii( "../" ); + --nLevel; + } + aTmpStr += rPath; + + if( pDocShell ) + { + bool bWasAbs = false; + rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr, bWasAbs ).GetMainURL( INetURLObject::NO_DECODE ); + // full path as stored in SvxURLField must be encoded + } + else + rPath = aTmpStr; +} + +/** Inserts the URL into a text cell. Does not modify value or formula cells. */ +void lclInsertUrl( const XclImpRoot& rRoot, const String& rUrl, SCCOL nScCol, SCROW nScRow, SCTAB nScTab ) +{ + ScDocument& rDoc = rRoot.GetDoc(); + ScAddress aScPos( nScCol, nScRow, nScTab ); + CellType eCellType = rDoc.GetCellType( aScPos ); + switch( eCellType ) + { + // #i54261# hyperlinks in string cells + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + { + String aDisplText; + rDoc.GetString( nScCol, nScRow, nScTab, aDisplText ); + if( !aDisplText.Len() ) + aDisplText = rUrl; + + ScEditEngineDefaulter& rEE = rRoot.GetEditEngine(); + SvxURLField aUrlField( rUrl, aDisplText, SVXURLFORMAT_APPDEFAULT ); + + const ScEditCell* pEditCell = (eCellType == CELLTYPE_EDIT) ? static_cast< const ScEditCell* >( rDoc.GetCell( aScPos ) ) : 0; + const EditTextObject* pEditObj = pEditCell ? pEditCell->GetData() : 0; + if( pEditObj ) + { + rEE.SetText( *pEditObj ); + rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0, 0, 0xFFFF, 0 ) ); + } + else + { + rEE.SetText( EMPTY_STRING ); + rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection() ); + if( const ScPatternAttr* pPattern = rDoc.GetPattern( aScPos.Col(), aScPos.Row(), nScTab ) ) + { + SfxItemSet aItemSet( rEE.GetEmptyItemSet() ); + pPattern->FillEditItemSet( &aItemSet ); + rEE.QuickSetAttribs( aItemSet, ESelection( 0, 0, 0xFFFF, 0 ) ); + } + } + ::std::auto_ptr< EditTextObject > xTextObj( rEE.CreateTextObject() ); + + ScEditCell* pCell = new ScEditCell( xTextObj.get(), &rDoc, rEE.GetEditTextObjectPool() ); + rDoc.PutCell( aScPos, pCell ); + } + break; + + // fix for #i31050# disabled, HYPERLINK is not able to return numeric value (#i91351#) +#if 0 + case CELLTYPE_VALUE: + { + // #i31050# replace number with HYPERLINK function + ScTokenArray aTokenArray; + aTokenArray.AddOpCode( ocHyperLink ); + aTokenArray.AddOpCode( ocOpen ); + aTokenArray.AddString( rUrl ); + aTokenArray.AddOpCode( ocSep ); + aTokenArray.AddDouble( rDoc.GetValue( aScPos ) ); + aTokenArray.AddOpCode( ocClose ); + rDoc.PutCell( aScPos, new ScFormulaCell( &rDoc, aScPos, &aTokenArray ) ); + } + break; +#endif + + default:; + } +} + +} // namespace + +// ---------------------------------------------------------------------------- + +void XclImpHyperlink::ReadHlink( XclImpStream& rStrm ) +{ + XclRange aXclRange( ScAddress::UNINITIALIZED ); + rStrm >> aXclRange; + // #i80006# Excel silently ignores invalid hi-byte of column index (TODO: everywhere?) + aXclRange.maFirst.mnCol &= 0xFF; + aXclRange.maLast.mnCol &= 0xFF; + String aString = ReadEmbeddedData( rStrm ); + if ( aString.Len() > 0 ) + rStrm.GetRoot().GetXFRangeBuffer().SetHyperlink( aXclRange, aString ); +} + +String XclImpHyperlink::ReadEmbeddedData( XclImpStream& rStrm ) +{ + const XclImpRoot& rRoot = rStrm.GetRoot(); + SfxObjectShell* pDocShell = rRoot.GetDocShell(); + + DBG_ASSERT_BIFF( rRoot.GetBiff() == EXC_BIFF8 ); + + sal_uInt32 nFlags; + XclGuid aGuid; + rStrm >> aGuid; + rStrm.Ignore( 4 ); + rStrm >> nFlags; + + DBG_ASSERT( aGuid == XclTools::maGuidStdLink, "XclImpHyperlink::ReadEmbeddedData - unknown header GUID" ); + + sal_uInt16 nLevel = 0; // counter for level to climb down in path + ::std::auto_ptr< String > xLongName; // link / file name + ::std::auto_ptr< String > xShortName; // 8.3-representation of file name + ::std::auto_ptr< String > xTextMark; // text mark + + // description (ignore) + if( ::get_flag( nFlags, EXC_HLINK_DESCR ) ) + lclIgnoreString32( rStrm, true ); + // target frame (ignore) !! DESCR/FRAME - is this the right order? (never seen them together) + if( ::get_flag( nFlags, EXC_HLINK_FRAME ) ) + lclIgnoreString32( rStrm, true ); + + // URL fields are zero-terminated - do not let the stream replace them + // in the lclAppendString32() with the '?' character. + rStrm.SetNulSubstChar( '\0' ); + + // UNC path + if( ::get_flag( nFlags, EXC_HLINK_UNC ) ) + { + xLongName.reset( new String ); + lclAppendString32( *xLongName, rStrm, true ); + lclGetAbsPath( *xLongName, 0, pDocShell ); + } + // file link or URL + else if( ::get_flag( nFlags, EXC_HLINK_BODY ) ) + { + rStrm >> aGuid; + + if( aGuid == XclTools::maGuidFileMoniker ) + { + rStrm >> nLevel; + xShortName.reset( new String ); + lclAppendString32( *xShortName, rStrm, false ); + rStrm.Ignore( 24 ); + + sal_uInt32 nStrLen; + rStrm >> nStrLen; + if( nStrLen ) + { + rStrm >> nStrLen; + nStrLen /= 2; // it's byte count here... + rStrm.Ignore( 2 ); + xLongName.reset( new String ); + lclAppendString32( *xLongName, rStrm, nStrLen, true ); + lclGetAbsPath( *xLongName, nLevel, pDocShell ); + } + else + lclGetAbsPath( *xShortName, nLevel, pDocShell ); + } + else if( aGuid == XclTools::maGuidUrlMoniker ) + { + sal_uInt32 nStrLen; + rStrm >> nStrLen; + nStrLen /= 2; // it's byte count here... + xLongName.reset( new String ); + lclAppendString32( *xLongName, rStrm, nStrLen, true ); + if( !::get_flag( nFlags, EXC_HLINK_ABS ) ) + lclGetAbsPath( *xLongName, 0, pDocShell ); + } + else + { + DBG_ERRORFILE( "XclImpHyperlink::ReadEmbeddedData - unknown content GUID" ); + } + } + + // text mark + if( ::get_flag( nFlags, EXC_HLINK_MARK ) ) + { + xTextMark.reset( new String ); + lclAppendString32( *xTextMark, rStrm, true ); + } + + rStrm.SetNulSubstChar(); // back to default + + DBG_ASSERT( rStrm.GetRecLeft() == 0, "XclImpHyperlink::ReadEmbeddedData - record size mismatch" ); + + if( !xLongName.get() && xShortName.get() ) + xLongName = xShortName; + else if( !xLongName.get() && xTextMark.get() ) + xLongName.reset( new String ); + + if( xLongName.get() ) + { + if( xTextMark.get() ) + { + if( xLongName->Len() == 0 ) + xTextMark->SearchAndReplaceAll( '!', '.' ); + xLongName->Append( '#' ); + xLongName->Append( *xTextMark ); + } + return *xLongName; + } + return String::EmptyString(); +} + +void XclImpHyperlink::ConvertToValidTabName(String& rUrl) +{ + xub_StrLen n = rUrl.Len(); + if (n < 4) + // Needs at least 4 characters. + return; + + sal_Unicode c = rUrl.GetChar(0); + if (c != sal_Unicode('#')) + // the 1st character must be '#'. + return; + + String aNewUrl(sal_Unicode('#')), aTabName; + + bool bInQuote = false; + bool bQuoteTabName = false; + for (xub_StrLen i = 1; i < n; ++i) + { + c = rUrl.GetChar(i); + if (c == sal_Unicode('\'')) + { + if (bInQuote && i+1 < n && rUrl.GetChar(i+1) == sal_Unicode('\'')) + { + // Two consecutive single quotes ('') signify a single literal + // quite. When this occurs, the whole table name needs to be + // quoted. + bQuoteTabName = true; + aTabName.Append(c); + aTabName.Append(c); + ++i; + continue; + } + + bInQuote = !bInQuote; + if (!bInQuote && aTabName.Len() > 0) + { + if (bQuoteTabName) + aNewUrl.Append(sal_Unicode('\'')); + aNewUrl.Append(aTabName); + if (bQuoteTabName) + aNewUrl.Append(sal_Unicode('\'')); + } + } + else if (bInQuote) + aTabName.Append(c); + else + aNewUrl.Append(c); + } + + if (bInQuote) + // It should be outside the quotes! + return; + + // All is good. Pass the new URL. + rUrl = aNewUrl; +} + +void XclImpHyperlink::InsertUrl( const XclImpRoot& rRoot, const XclRange& rXclRange, const String& rUrl ) +{ + String aUrl(rUrl); + ConvertToValidTabName(aUrl); + + SCTAB nScTab = rRoot.GetCurrScTab(); + ScRange aScRange( ScAddress::UNINITIALIZED ); + if( rRoot.GetAddressConverter().ConvertRange( aScRange, rXclRange, nScTab, nScTab, true ) ) + { + SCCOL nScCol1, nScCol2; + SCROW nScRow1, nScRow2; + aScRange.GetVars( nScCol1, nScRow1, nScTab, nScCol2, nScRow2, nScTab ); + for( SCCOL nScCol = nScCol1; nScCol <= nScCol2; ++nScCol ) + for( SCROW nScRow = nScRow1; nScRow <= nScRow2; ++nScRow ) + lclInsertUrl( rRoot, aUrl, nScCol, nScRow, nScTab ); + } +} + +// Label ranges =============================================================== + +void XclImpLabelranges::ReadLabelranges( XclImpStream& rStrm ) +{ + const XclImpRoot& rRoot = rStrm.GetRoot(); + DBG_ASSERT_BIFF( rRoot.GetBiff() == EXC_BIFF8 ); + + ScDocument& rDoc = rRoot.GetDoc(); + SCTAB nScTab = rRoot.GetCurrScTab(); + XclImpAddressConverter& rAddrConv = rRoot.GetAddressConverter(); + ScRangePairListRef xLabelRangesRef; + const ScRange* pScRange = 0; + + XclRangeList aRowXclRanges, aColXclRanges; + rStrm >> aRowXclRanges >> aColXclRanges; + + // row label ranges + ScRangeList aRowScRanges; + rAddrConv.ConvertRangeList( aRowScRanges, aRowXclRanges, nScTab, false ); + xLabelRangesRef = rDoc.GetRowNameRangesRef(); + for( pScRange = aRowScRanges.First(); pScRange; pScRange = aRowScRanges.Next() ) + { + ScRange aDataRange( *pScRange ); + if( aDataRange.aEnd.Col() < MAXCOL ) + { + aDataRange.aStart.SetCol( aDataRange.aEnd.Col() + 1 ); + aDataRange.aEnd.SetCol( MAXCOL ); + } + else if( aDataRange.aStart.Col() > 0 ) + { + aDataRange.aEnd.SetCol( aDataRange.aStart.Col() - 1 ); + aDataRange.aStart.SetCol( 0 ); + } + xLabelRangesRef->Append( ScRangePair( *pScRange, aDataRange ) ); + } + + // column label ranges + ScRangeList aColScRanges; + rAddrConv.ConvertRangeList( aColScRanges, aColXclRanges, nScTab, false ); + xLabelRangesRef = rDoc.GetColNameRangesRef(); + for( pScRange = aColScRanges.First(); pScRange; pScRange = aColScRanges.Next() ) + { + ScRange aDataRange( *pScRange ); + if( aDataRange.aEnd.Row() < MAXROW ) + { + aDataRange.aStart.SetRow( aDataRange.aEnd.Row() + 1 ); + aDataRange.aEnd.SetRow( MAXROW ); + } + else if( aDataRange.aStart.Row() > 0 ) + { + aDataRange.aEnd.SetRow( aDataRange.aStart.Row() - 1 ); + aDataRange.aStart.SetRow( 0 ); + } + xLabelRangesRef->Append( ScRangePair( *pScRange, aDataRange ) ); + } +} + +// Conditional formatting ===================================================== + +XclImpCondFormat::XclImpCondFormat( const XclImpRoot& rRoot, sal_uInt32 nFormatIndex ) : + XclImpRoot( rRoot ), + mnFormatIndex( nFormatIndex ), + mnCondCount( 0 ), + mnCondIndex( 0 ) +{ +} + +XclImpCondFormat::~XclImpCondFormat() +{ +} + +void XclImpCondFormat::ReadCondfmt( XclImpStream& rStrm ) +{ + DBG_ASSERT( !mnCondCount, "XclImpCondFormat::ReadCondfmt - already initialized" ); + XclRangeList aXclRanges; + rStrm >> mnCondCount; + rStrm.Ignore( 10 ); + rStrm >> aXclRanges; + GetAddressConverter().ConvertRangeList( maRanges, aXclRanges, GetCurrScTab(), true ); +} + +void XclImpCondFormat::ReadCF( XclImpStream& rStrm ) +{ + if( mnCondIndex >= mnCondCount ) + { + DBG_ERRORFILE( "XclImpCondFormat::ReadCF - CF without leading CONDFMT" ); + return; + } + + // entire conditional format outside of valid range? + if( !maRanges.Count() ) + return; + + sal_uInt8 nType, nOperator; + sal_uInt16 nFmlaSize1, nFmlaSize2; + sal_uInt32 nFlags; + + rStrm >> nType >> nOperator >> nFmlaSize1 >> nFmlaSize2 >> nFlags; + rStrm.Ignore( 2 ); + + // *** mode and comparison operator *** + + ScConditionMode eMode = SC_COND_NONE; + switch( nType ) + { + case EXC_CF_TYPE_CELL: + { + switch( nOperator ) + { + case EXC_CF_CMP_BETWEEN: eMode = SC_COND_BETWEEN; break; + case EXC_CF_CMP_NOT_BETWEEN: eMode = SC_COND_NOTBETWEEN; break; + case EXC_CF_CMP_EQUAL: eMode = SC_COND_EQUAL; break; + case EXC_CF_CMP_NOT_EQUAL: eMode = SC_COND_NOTEQUAL; break; + case EXC_CF_CMP_GREATER: eMode = SC_COND_GREATER; break; + case EXC_CF_CMP_LESS: eMode = SC_COND_LESS; break; + case EXC_CF_CMP_GREATER_EQUAL: eMode = SC_COND_EQGREATER; break; + case EXC_CF_CMP_LESS_EQUAL: eMode = SC_COND_EQLESS; break; + default: + DBG_ERROR1( "XclImpCondFormat::ReadCF - unknown CF comparison 0x%02hX", nOperator ); + } + } + break; + + case EXC_CF_TYPE_FMLA: + eMode = SC_COND_DIRECT; + break; + + default: + DBG_ERROR1( "XclImpCondFormat::ReadCF - unknown CF mode 0x%02hX", nType ); + return; + } + + // *** create style sheet *** + + String aStyleName( XclTools::GetCondFormatStyleName( GetCurrScTab(), mnFormatIndex, mnCondIndex ) ); + SfxItemSet& rStyleItemSet = ScfTools::MakeCellStyleSheet( GetStyleSheetPool(), aStyleName, true ).GetItemSet(); + + const XclImpPalette& rPalette = GetPalette(); + + // *** font block *** + + if( ::get_flag( nFlags, EXC_CF_BLOCK_FONT ) ) + { + XclImpFont aFont( GetRoot() ); + aFont.ReadCFFontBlock( rStrm ); + aFont.FillToItemSet( rStyleItemSet, EXC_FONTITEM_CELL ); + } + + // *** border block *** + + if( ::get_flag( nFlags, EXC_CF_BLOCK_BORDER ) ) + { + sal_uInt16 nLineStyle; + sal_uInt32 nLineColor; + rStrm >> nLineStyle >> nLineColor; + rStrm.Ignore( 2 ); + + XclImpCellBorder aBorder; + aBorder.FillFromCF8( nLineStyle, nLineColor, nFlags ); + aBorder.FillToItemSet( rStyleItemSet, rPalette ); + } + + // *** pattern block *** + + if( ::get_flag( nFlags, EXC_CF_BLOCK_AREA ) ) + { + sal_uInt16 nPattern, nColor; + rStrm >> nPattern >> nColor; + + XclImpCellArea aArea; + aArea.FillFromCF8( nPattern, nColor, nFlags ); + aArea.FillToItemSet( rStyleItemSet, rPalette ); + } + + // *** formulas *** + + const ScAddress& rPos = maRanges.GetObject( 0 )->aStart; // assured above that maRanges is not empty + ExcelToSc& rFmlaConv = GetOldFmlaConverter(); + + ::std::auto_ptr< ScTokenArray > xTokArr1; + if( nFmlaSize1 > 0 ) + { + const ScTokenArray* pTokArr = 0; + rFmlaConv.Reset( rPos ); + rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize1, false, FT_RangeName ); + // formula converter owns pTokArr -> create a copy of the token array + if( pTokArr ) + xTokArr1.reset( pTokArr->Clone() ); + } + + ::std::auto_ptr< ScTokenArray > pTokArr2; + if( nFmlaSize2 > 0 ) + { + const ScTokenArray* pTokArr = 0; + rFmlaConv.Reset( rPos ); + rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize2, false, FT_RangeName ); + // formula converter owns pTokArr -> create a copy of the token array + if( pTokArr ) + pTokArr2.reset( pTokArr->Clone() ); + } + + // *** create the Calc conditional formatting *** + + if( !mxScCondFmt.get() ) + { + ULONG nKey = 0; + mxScCondFmt.reset( new ScConditionalFormat( nKey, GetDocPtr() ) ); + } + + ScCondFormatEntry aEntry( eMode, xTokArr1.get(), pTokArr2.get(), GetDocPtr(), rPos, aStyleName ); + mxScCondFmt->AddEntry( aEntry ); + ++mnCondIndex; +} + +void XclImpCondFormat::Apply() +{ + if( mxScCondFmt.get() ) + { + ScDocument& rDoc = GetDoc(); + + ULONG nKey = rDoc.AddCondFormat( *mxScCondFmt ); + ScPatternAttr aPattern( rDoc.GetPool() ); + aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_CONDITIONAL, nKey ) ); + + // maRanges contains only valid cell ranges + for( const ScRange* pScRange = maRanges.First(); pScRange; pScRange = maRanges.Next() ) + { + rDoc.ApplyPatternAreaTab( + pScRange->aStart.Col(), pScRange->aStart.Row(), + pScRange->aEnd.Col(), pScRange->aEnd.Row(), + pScRange->aStart.Tab(), aPattern ); + } + } +} + +// ---------------------------------------------------------------------------- + +XclImpCondFormatManager::XclImpCondFormatManager( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +void XclImpCondFormatManager::ReadCondfmt( XclImpStream& rStrm ) +{ + XclImpCondFormat* pFmt = new XclImpCondFormat( GetRoot(), maCondFmtList.Count() ); + pFmt->ReadCondfmt( rStrm ); + maCondFmtList.Append( pFmt ); +} + +void XclImpCondFormatManager::ReadCF( XclImpStream& rStrm ) +{ + DBG_ASSERT( !maCondFmtList.Empty(), "XclImpCondFormatManager::ReadCF - CF without leading CONDFMT" ); + if( !maCondFmtList.Empty() ) + maCondFmtList.GetObject( maCondFmtList.Count() - 1 )->ReadCF( rStrm ); +} + +void XclImpCondFormatManager::Apply() +{ + for( XclImpCondFormat* pFmt = maCondFmtList.First(); pFmt; pFmt = maCondFmtList.Next() ) + pFmt->Apply(); + maCondFmtList.Clear(); +} + +// Data Validation ============================================================ + +void XclImpValidation::ReadDval( XclImpStream& rStrm ) +{ + const XclImpRoot& rRoot = rStrm.GetRoot(); + DBG_ASSERT_BIFF( rRoot.GetBiff() == EXC_BIFF8 ); + + sal_uInt32 nObjId; + rStrm.Ignore( 10 ); + rStrm >> nObjId; + if( nObjId != EXC_DVAL_NOOBJ ) + { + DBG_ASSERT( nObjId <= 0xFFFF, "XclImpValidation::ReadDval - invalid object ID" ); + rRoot.GetCurrSheetDrawing().SetSkipObj( static_cast< sal_uInt16 >( nObjId ) ); + } +} + +void XclImpValidation::ReadDV( XclImpStream& rStrm ) +{ + const XclImpRoot& rRoot = rStrm.GetRoot(); + DBG_ASSERT_BIFF( rRoot.GetBiff() == EXC_BIFF8 ); + + ScDocument& rDoc = rRoot.GetDoc(); + SCTAB nScTab = rRoot.GetCurrScTab(); + ExcelToSc& rFmlaConv = rRoot.GetOldFmlaConverter(); + + // flags + sal_uInt32 nFlags; + rStrm >> nFlags; + + // message strings + /* Empty strings are single NUL characters in Excel (string length is 1). + -> Do not let the stream replace them with '?' characters. */ + rStrm.SetNulSubstChar( '\0' ); + String aPromptTitle( rStrm.ReadUniString() ); + String aErrorTitle( rStrm.ReadUniString() ); + String aPromptMessage( rStrm.ReadUniString() ); + String aErrorMessage( rStrm.ReadUniString() ); + rStrm.SetNulSubstChar(); // back to default + + // formula(s) + if( rStrm.GetRecLeft() > 8 ) + { + sal_uInt16 nLen; + + // first formula + // string list is single tStr token with NUL separators -> replace them with LF + rStrm.SetNulSubstChar( '\n' ); + ::std::auto_ptr< ScTokenArray > xTokArr1; + rStrm >> nLen; + rStrm.Ignore( 2 ); + if( nLen > 0 ) + { + const ScTokenArray* pTokArr = 0; + rFmlaConv.Reset(); + rFmlaConv.Convert( pTokArr, rStrm, nLen, false, FT_RangeName ); + // formula converter owns pTokArr -> create a copy of the token array + if( pTokArr ) + xTokArr1.reset( pTokArr->Clone() ); + } + rStrm.SetNulSubstChar(); // back to default + + // second formula + ::std::auto_ptr< ScTokenArray > xTokArr2; + rStrm >> nLen; + rStrm.Ignore( 2 ); + if( nLen > 0 ) + { + const ScTokenArray* pTokArr = 0; + rFmlaConv.Reset(); + rFmlaConv.Convert( pTokArr, rStrm, nLen, false, FT_RangeName ); + // formula converter owns pTokArr -> create a copy of the token array + if( pTokArr ) + xTokArr2.reset( pTokArr->Clone() ); + } + + // read all cell ranges + XclRangeList aXclRanges; + rStrm >> aXclRanges; + + // convert to Calc range list + ScRangeList aScRanges; + rRoot.GetAddressConverter().ConvertRangeList( aScRanges, aXclRanges, nScTab, true ); + + // only continue if there are valid ranges + if( aScRanges.Count() ) + { + bool bIsValid = true; // valid settings in flags field + + ScValidationMode eValMode = SC_VALID_ANY; + switch( nFlags & EXC_DV_MODE_MASK ) + { + case EXC_DV_MODE_ANY: eValMode = SC_VALID_ANY; break; + case EXC_DV_MODE_WHOLE: eValMode = SC_VALID_WHOLE; break; + case EXC_DV_MODE_DECIMAL: eValMode = SC_VALID_DECIMAL; break; + case EXC_DV_MODE_LIST: eValMode = SC_VALID_LIST; break; + case EXC_DV_MODE_DATE: eValMode = SC_VALID_DATE; break; + case EXC_DV_MODE_TIME: eValMode = SC_VALID_TIME; break; + case EXC_DV_MODE_TEXTLEN: eValMode = SC_VALID_TEXTLEN; break; + case EXC_DV_MODE_CUSTOM: eValMode = SC_VALID_CUSTOM; break; + default: bIsValid = false; + } + rRoot.GetTracer().TraceDVType(eValMode == SC_VALID_CUSTOM); + + ScConditionMode eCondMode = SC_COND_BETWEEN; + switch( nFlags & EXC_DV_COND_MASK ) + { + case EXC_DV_COND_BETWEEN: eCondMode = SC_COND_BETWEEN; break; + case EXC_DV_COND_NOTBETWEEN:eCondMode = SC_COND_NOTBETWEEN; break; + case EXC_DV_COND_EQUAL: eCondMode = SC_COND_EQUAL; break; + case EXC_DV_COND_NOTEQUAL: eCondMode = SC_COND_NOTEQUAL; break; + case EXC_DV_COND_GREATER: eCondMode = SC_COND_GREATER; break; + case EXC_DV_COND_LESS: eCondMode = SC_COND_LESS; break; + case EXC_DV_COND_EQGREATER: eCondMode = SC_COND_EQGREATER; break; + case EXC_DV_COND_EQLESS: eCondMode = SC_COND_EQLESS; break; + default: bIsValid = false; + } + + if( bIsValid ) + { + // first range for base address for relative references + const ScRange& rScRange = *aScRanges.GetObject( 0 ); // aScRanges is not empty + + // process string list of a list validity (convert to list of string tokens) + if( xTokArr1.get() && (eValMode == SC_VALID_LIST) && ::get_flag( nFlags, EXC_DV_STRINGLIST ) ) + XclTokenArrayHelper::ConvertStringToList( *xTokArr1, '\n', true ); + + ScValidationData aValidData( eValMode, eCondMode, xTokArr1.get(), xTokArr2.get(), &rDoc, rScRange.aStart ); + + aValidData.SetIgnoreBlank( ::get_flag( nFlags, EXC_DV_IGNOREBLANK ) ); + aValidData.SetListType( ::get_flagvalue( nFlags, EXC_DV_SUPPRESSDROPDOWN, ValidListType::INVISIBLE, ValidListType::UNSORTED ) ); + + // *** prompt box *** + if( aPromptTitle.Len() || aPromptMessage.Len() ) + { + // set any text stored in the record + aValidData.SetInput( aPromptTitle, aPromptMessage ); + if( !::get_flag( nFlags, EXC_DV_SHOWPROMPT ) ) + aValidData.ResetInput(); + } + + // *** error box *** + ScValidErrorStyle eErrStyle = SC_VALERR_STOP; + switch( nFlags & EXC_DV_ERROR_MASK ) + { + case EXC_DV_ERROR_WARNING: eErrStyle = SC_VALERR_WARNING; break; + case EXC_DV_ERROR_INFO: eErrStyle = SC_VALERR_INFO; break; + } + // set texts and error style + aValidData.SetError( aErrorTitle, aErrorMessage, eErrStyle ); + if( !::get_flag( nFlags, EXC_DV_SHOWERROR ) ) + aValidData.ResetError(); + + // set the handle ID + ULONG nHandle = rDoc.AddValidationEntry( aValidData ); + ScPatternAttr aPattern( rDoc.GetPool() ); + aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nHandle ) ); + + // apply all ranges + for( const ScRange* pScRange = aScRanges.First(); pScRange; pScRange = aScRanges.Next() ) + rDoc.ApplyPatternAreaTab( pScRange->aStart.Col(), pScRange->aStart.Row(), + pScRange->aEnd.Col(), pScRange->aEnd.Row(), nScTab, aPattern ); + } + } + } +} + +// Web queries ================================================================ + +XclImpWebQuery::XclImpWebQuery( const ScRange& rDestRange ) : + maDestRange( rDestRange ), + meMode( xlWQUnknown ), + mnRefresh( 0 ) +{ +} + +void XclImpWebQuery::ReadParamqry( XclImpStream& rStrm ) +{ + sal_uInt16 nFlags = rStrm.ReaduInt16(); + sal_uInt16 nType = ::extract_value< sal_uInt16 >( nFlags, 0, 3 ); + if( (nType == EXC_PQRYTYPE_WEBQUERY) && ::get_flag( nFlags, EXC_PQRY_WEBQUERY ) ) + { + if( ::get_flag( nFlags, EXC_PQRY_TABLES ) ) + { + meMode = xlWQAllTables; + maTables = ScfTools::GetHTMLTablesName(); + } + else + { + meMode = xlWQDocument; + maTables = ScfTools::GetHTMLDocName(); + } + } +} + +void XclImpWebQuery::ReadWqstring( XclImpStream& rStrm ) +{ + maURL = rStrm.ReadUniString(); +} + +void XclImpWebQuery::ReadWqsettings( XclImpStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm.Ignore( 10 ); + rStrm >> nFlags; + rStrm.Ignore( 10 ); + rStrm >> mnRefresh; + + if( ::get_flag( nFlags, EXC_WQSETT_SPECTABLES ) && (meMode == xlWQAllTables) ) + meMode = xlWQSpecTables; +} + +void XclImpWebQuery::ReadWqtables( XclImpStream& rStrm ) +{ + if( meMode == xlWQSpecTables ) + { + rStrm.Ignore( 4 ); + String aTables( rStrm.ReadUniString() ); + + const sal_Unicode cSep = ';'; + aTables.SearchAndReplaceAll( ',', cSep ); + String aQuotedPairs( RTL_CONSTASCII_USTRINGPARAM( "\"\"" ) ); + xub_StrLen nTokenCnt = aTables.GetQuotedTokenCount( aQuotedPairs, cSep ); + maTables.Erase(); + xub_StrLen nStringIx = 0; + for( xub_StrLen nToken = 0; nToken < nTokenCnt; ++nToken ) + { + String aToken( aTables.GetQuotedToken( 0, aQuotedPairs, cSep, nStringIx ) ); + sal_Int32 nTabNum = CharClass::isAsciiNumeric( aToken ) ? aToken.ToInt32() : 0; + if( nTabNum > 0 ) + ScGlobal::AddToken( maTables, ScfTools::GetNameFromHTMLIndex( static_cast< sal_uInt32 >( nTabNum ) ), cSep ); + else + { + ScGlobal::EraseQuotes( aToken, '"', false ); + if( aToken.Len() ) + ScGlobal::AddToken( maTables, ScfTools::GetNameFromHTMLName( aToken ), cSep ); + } + } + } +} + +void XclImpWebQuery::Apply( ScDocument& rDoc, const String& rFilterName ) +{ + if( maURL.Len() && (meMode != xlWQUnknown) && rDoc.GetDocumentShell() ) + { + ScAreaLink* pLink = new ScAreaLink( rDoc.GetDocumentShell(), + maURL, rFilterName, EMPTY_STRING, maTables, maDestRange, mnRefresh * 60UL ); + rDoc.GetLinkManager()->InsertFileLink( *pLink, OBJECT_CLIENT_FILE, + maURL, &rFilterName, &maTables ); + } +} + +// ---------------------------------------------------------------------------- + +XclImpWebQueryBuffer::XclImpWebQueryBuffer( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +void XclImpWebQueryBuffer::ReadQsi( XclImpStream& rStrm ) +{ + if( GetBiff() == EXC_BIFF8 ) + { + rStrm.Ignore( 10 ); + String aXclName( rStrm.ReadUniString() ); + + // #i64794# Excel replaces spaces with underscores + aXclName.SearchAndReplaceAll( ' ', '_' ); + + // #101529# find the defined name used in Calc + if( const XclImpName* pName = GetNameManager().FindName( aXclName, GetCurrScTab() ) ) + { + if( const ScRangeData* pRangeData = pName->GetScRangeData() ) + { + ScRange aRange; + if( pRangeData->IsReference( aRange ) ) + maWQList.Append( new XclImpWebQuery( aRange ) ); + } + } + } + else + { + DBG_ERROR_BIFF(); + } +} + +void XclImpWebQueryBuffer::ReadParamqry( XclImpStream& rStrm ) +{ + if( XclImpWebQuery* pQuery = maWQList.Last() ) + pQuery->ReadParamqry( rStrm ); +} + +void XclImpWebQueryBuffer::ReadWqstring( XclImpStream& rStrm ) +{ + if( XclImpWebQuery* pQuery = maWQList.Last() ) + pQuery->ReadWqstring( rStrm ); +} + +void XclImpWebQueryBuffer::ReadWqsettings( XclImpStream& rStrm ) +{ + if( XclImpWebQuery* pQuery = maWQList.Last() ) + pQuery->ReadWqsettings( rStrm ); +} + +void XclImpWebQueryBuffer::ReadWqtables( XclImpStream& rStrm ) +{ + if( XclImpWebQuery* pQuery = maWQList.Last() ) + pQuery->ReadWqtables( rStrm ); +} + +void XclImpWebQueryBuffer::Apply() +{ + ScDocument& rDoc = GetDoc(); + String aFilterName( RTL_CONSTASCII_USTRINGPARAM( EXC_WEBQRY_FILTER ) ); + for( XclImpWebQuery* pQuery = maWQList.First(); pQuery; pQuery = maWQList.Next() ) + pQuery->Apply( rDoc, aFilterName ); +} + +// Decryption ================================================================= + +namespace { + +XclImpDecrypterRef lclReadFilepass5( XclImpStream& rStrm ) +{ + XclImpDecrypterRef xDecr; + DBG_ASSERT( rStrm.GetRecLeft() == 4, "lclReadFilepass5 - wrong record size" ); + if( rStrm.GetRecLeft() == 4 ) + { + sal_uInt16 nKey, nHash; + rStrm >> nKey >> nHash; + xDecr.reset( new XclImpBiff5Decrypter( nKey, nHash ) ); + } + return xDecr; +} + +XclImpDecrypterRef lclReadFilepass8_Standard( XclImpStream& rStrm ) +{ + XclImpDecrypterRef xDecr; + DBG_ASSERT( rStrm.GetRecLeft() == 48, "lclReadFilepass8 - wrong record size" ); + if( rStrm.GetRecLeft() == 48 ) + { + sal_uInt8 pnSalt[ 16 ]; + sal_uInt8 pnVerifier[ 16 ]; + sal_uInt8 pnVerifierHash[ 16 ]; + rStrm.Read( pnSalt, 16 ); + rStrm.Read( pnVerifier, 16 ); + rStrm.Read( pnVerifierHash, 16 ); + xDecr.reset( new XclImpBiff8Decrypter( pnSalt, pnVerifier, pnVerifierHash ) ); + } + return xDecr; +} + +XclImpDecrypterRef lclReadFilepass8_Strong( XclImpStream& /*rStrm*/ ) +{ + // not supported + return XclImpDecrypterRef(); +} + +XclImpDecrypterRef lclReadFilepass8( XclImpStream& rStrm ) +{ + XclImpDecrypterRef xDecr; + + sal_uInt16 nMode; + rStrm >> nMode; + switch( nMode ) + { + case EXC_FILEPASS_BIFF5: + xDecr = lclReadFilepass5( rStrm ); + break; + + case EXC_FILEPASS_BIFF8: + { + rStrm.Ignore( 2 ); + sal_uInt16 nSubMode; + rStrm >> nSubMode; + switch( nSubMode ) + { + case EXC_FILEPASS_BIFF8_STD: + xDecr = lclReadFilepass8_Standard( rStrm ); + break; + case EXC_FILEPASS_BIFF8_STRONG: + xDecr = lclReadFilepass8_Strong( rStrm ); + break; + default: + DBG_ERRORFILE( "lclReadFilepass8 - unknown BIFF8 encryption sub mode" ); + } + } + break; + + default: + DBG_ERRORFILE( "lclReadFilepass8 - unknown encryption mode" ); + } + + return xDecr; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +ErrCode XclImpDecryptHelper::ReadFilepass( XclImpStream& rStrm ) +{ + XclImpDecrypterRef xDecr; + rStrm.DisableDecryption(); + + // read the FILEPASS record and create a new decrypter object + switch( rStrm.GetRoot().GetBiff() ) + { + case EXC_BIFF2: + case EXC_BIFF3: + case EXC_BIFF4: + case EXC_BIFF5: xDecr = lclReadFilepass5( rStrm ); break; + case EXC_BIFF8: xDecr = lclReadFilepass8( rStrm ); break; + default: DBG_ERROR_BIFF(); + }; + + // set decrypter at import stream + rStrm.SetDecrypter( xDecr ); + + // request and verify a password (decrypter implements IDocPasswordVerifier) + if( xDecr.is() ) + rStrm.GetRoot().RequestPassword( *xDecr ); + + // return error code (success, wrong password, etc.) + return xDecr.is() ? xDecr->GetError() : EXC_ENCR_ERROR_UNSUPP_CRYPT; +} + +// Document protection ======================================================== + +XclImpDocProtectBuffer::XclImpDocProtectBuffer( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + mnPassHash(0x0000), + mbDocProtect(false), + mbWinProtect(false) +{ +} + +void XclImpDocProtectBuffer::ReadDocProtect( XclImpStream& rStrm ) +{ + mbDocProtect = rStrm.ReaduInt16() ? true : false; +} + +void XclImpDocProtectBuffer::ReadWinProtect( XclImpStream& rStrm ) +{ + mbWinProtect = rStrm.ReaduInt16() ? true : false; +} + +void XclImpDocProtectBuffer::ReadPasswordHash( XclImpStream& rStrm ) +{ + rStrm.EnableDecryption(); + mnPassHash = rStrm.ReaduInt16(); +} + +void XclImpDocProtectBuffer::Apply() const +{ + if (!mbDocProtect && !mbWinProtect) + // Excel requires either the structure or windows protection is set. + // If neither is set then the document is not protected at all. + return; + + auto_ptr<ScDocProtection> pProtect(new ScDocProtection); + pProtect->setProtected(true); + +#if ENABLE_SHEET_PROTECTION + if (mnPassHash) + { + // 16-bit password pash. + Sequence<sal_Int8> aPass(2); + aPass[0] = (mnPassHash >> 8) & 0xFF; + aPass[1] = mnPassHash & 0xFF; + pProtect->setPasswordHash(aPass, PASSHASH_XL); + } +#endif + + // document protection options + pProtect->setOption(ScDocProtection::STRUCTURE, mbDocProtect); + pProtect->setOption(ScDocProtection::WINDOWS, mbWinProtect); + + GetDoc().SetDocProtection(pProtect.get()); +} + +// Sheet Protection =========================================================== + +XclImpSheetProtectBuffer::Sheet::Sheet() : + mbProtected(false), + mnPasswordHash(0x0000), + mnOptions(0x4400) +{ +} + +// ---------------------------------------------------------------------------- + +XclImpSheetProtectBuffer::Sheet::Sheet(const Sheet& r) : + mbProtected(r.mbProtected), + mnPasswordHash(r.mnPasswordHash), + mnOptions(r.mnOptions) +{ +} + +XclImpSheetProtectBuffer::XclImpSheetProtectBuffer( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +void XclImpSheetProtectBuffer::ReadProtect( XclImpStream& rStrm, SCTAB nTab ) +{ + if ( rStrm.ReaduInt16() ) + { + Sheet* pSheet = GetSheetItem(nTab); + if (pSheet) + pSheet->mbProtected = true; + } +} + +void XclImpSheetProtectBuffer::ReadOptions( XclImpStream& rStrm, SCTAB nTab ) +{ + rStrm.Ignore(12); + + // feature type can be either 2 or 4. If 2, this record stores flag for + // enhanced protection, whereas if 4 it stores flag for smart tag. + sal_uInt16 nFeatureType; + rStrm >> nFeatureType; + if (nFeatureType != 2) + // We currently only support import of enhanced protection data. + return; + + rStrm.Ignore(1); // always 1 + + // The flag size specifies the size of bytes that follows that stores + // feature data. If -1 it depends on the feature type imported earlier. + // For enhanced protection data, the size is always 4. For the most xls + // documents out there this value is almost always -1. + sal_Int32 nFlagSize; + rStrm >> nFlagSize; + if (nFlagSize != -1) + return; + + // There are actually 4 bytes to read, but the upper 2 bytes currently + // don't store any bits. + sal_uInt16 nOptions; + rStrm >> nOptions; + + Sheet* pSheet = GetSheetItem(nTab); + if (pSheet) + pSheet->mnOptions = nOptions; +} + +void XclImpSheetProtectBuffer::ReadPasswordHash( XclImpStream& rStrm, SCTAB nTab ) +{ + sal_uInt16 nHash; + rStrm >> nHash; + Sheet* pSheet = GetSheetItem(nTab); + if (pSheet) + pSheet->mnPasswordHash = nHash; +} + +void XclImpSheetProtectBuffer::Apply() const +{ + for (ProtectedSheetMap::const_iterator itr = maProtectedSheets.begin(), itrEnd = maProtectedSheets.end(); + itr != itrEnd; ++itr) + { + if (!itr->second.mbProtected) + // This sheet is (for whatever reason) not protected. + continue; + + auto_ptr<ScTableProtection> pProtect(new ScTableProtection); + pProtect->setProtected(true); + +#if ENABLE_SHEET_PROTECTION + // 16-bit hash password + const sal_uInt16 nHash = itr->second.mnPasswordHash; + if (nHash) + { + Sequence<sal_Int8> aPass(2); + aPass[0] = (nHash >> 8) & 0xFF; + aPass[1] = nHash & 0xFF; + pProtect->setPasswordHash(aPass, PASSHASH_XL); + } +#endif + + // sheet protection options + const sal_uInt16 nOptions = itr->second.mnOptions; + pProtect->setOption( ScTableProtection::OBJECTS, (nOptions & 0x0001) ); + pProtect->setOption( ScTableProtection::SCENARIOS, (nOptions & 0x0002) ); + pProtect->setOption( ScTableProtection::FORMAT_CELLS, (nOptions & 0x0004) ); + pProtect->setOption( ScTableProtection::FORMAT_COLUMNS, (nOptions & 0x0008) ); + pProtect->setOption( ScTableProtection::FORMAT_ROWS, (nOptions & 0x0010) ); + pProtect->setOption( ScTableProtection::INSERT_COLUMNS, (nOptions & 0x0020) ); + pProtect->setOption( ScTableProtection::INSERT_ROWS, (nOptions & 0x0040) ); + pProtect->setOption( ScTableProtection::INSERT_HYPERLINKS, (nOptions & 0x0080) ); + pProtect->setOption( ScTableProtection::DELETE_COLUMNS, (nOptions & 0x0100) ); + pProtect->setOption( ScTableProtection::DELETE_ROWS, (nOptions & 0x0200) ); + pProtect->setOption( ScTableProtection::SELECT_LOCKED_CELLS, (nOptions & 0x0400) ); + pProtect->setOption( ScTableProtection::SORT, (nOptions & 0x0800) ); + pProtect->setOption( ScTableProtection::AUTOFILTER, (nOptions & 0x1000) ); + pProtect->setOption( ScTableProtection::PIVOT_TABLES, (nOptions & 0x2000) ); + pProtect->setOption( ScTableProtection::SELECT_UNLOCKED_CELLS, (nOptions & 0x4000) ); + + // all done. now commit. + GetDoc().SetTabProtection(itr->first, pProtect.get()); + } +} + +XclImpSheetProtectBuffer::Sheet* XclImpSheetProtectBuffer::GetSheetItem( SCTAB nTab ) +{ + ProtectedSheetMap::iterator itr = maProtectedSheets.find(nTab); + if (itr == maProtectedSheets.end()) + { + // new sheet + if ( !maProtectedSheets.insert( ProtectedSheetMap::value_type(nTab, Sheet()) ).second ) + return NULL; + + itr = maProtectedSheets.find(nTab); + } + + return &itr->second; +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xiescher.cxx b/sc/source/filter/excel/xiescher.cxx new file mode 100644 index 000000000000..8d09cae60941 --- /dev/null +++ b/sc/source/filter/excel/xiescher.cxx @@ -0,0 +1,4120 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "xiescher.hxx" + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/awt/PushButtonType.hpp> +#include <com/sun/star/awt/ScrollBarOrientation.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/style/HorizontalAlignment.hpp> +#include <com/sun/star/style/VerticalAlignment.hpp> +#include <com/sun/star/drawing/XControlShape.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <com/sun/star/form/XFormsSupplier.hpp> +#include <com/sun/star/form/binding/XBindableValue.hpp> +#include <com/sun/star/form/binding/XValueBinding.hpp> +#include <com/sun/star/form/binding/XListEntrySink.hpp> +#include <com/sun/star/form/binding/XListEntrySource.hpp> +#include <com/sun/star/script/ScriptEventDescriptor.hpp> +#include <com/sun/star/script/XEventAttacherManager.hpp> + +#include <rtl/logfile.hxx> +#include <sfx2/objsh.hxx> +#include <unotools/moduleoptions.hxx> +#include <unotools/fltrcfg.hxx> +#include <svtools/wmf.hxx> +#include <comphelper/types.hxx> +#include <comphelper/classids.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> + +#include <svx/svdopath.hxx> +#include <svx/svdocirc.hxx> +#include <svx/svdoedge.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdoashp.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdocapt.hxx> +#include <svx/svdouno.hxx> +#include <svx/svdpage.hxx> +#include <editeng/editobj.hxx> +#include <editeng/outliner.hxx> +#include <editeng/outlobj.hxx> +#include <svx/unoapi.hxx> +#include <svx/svditer.hxx> +#include <editeng/writingmodeitem.hxx> + +#include "scitems.hxx" +#include <editeng/eeitem.hxx> +#include <editeng/colritem.hxx> +#include <svx/xflclit.hxx> +#include <editeng/adjitem.hxx> +#include <svx/xlineit.hxx> +#include <svx/xlinjoit.hxx> +#include <svx/xlntrit.hxx> +#include <svx/xbtmpit.hxx> + +#include "document.hxx" +#include "drwlayer.hxx" +#include "userdat.hxx" +#include "chartarr.hxx" +#include "detfunc.hxx" +#include "unonames.hxx" +#include "convuno.hxx" +#include "postit.hxx" +#include "globstr.hrc" + +#include "fprogressbar.hxx" +#include "xltracer.hxx" +#include "xistream.hxx" +#include "xihelper.hxx" +#include "xiformula.hxx" +#include "xilink.hxx" +#include "xistyle.hxx" +#include "xipage.hxx" +#include "xichart.hxx" +#include "xicontent.hxx" +#include "namebuff.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::UNO_SET_THROW; +using ::com::sun::star::beans::NamedValue; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::container::XIndexContainer; +using ::com::sun::star::container::XNameContainer; +using ::com::sun::star::frame::XModel; +using ::com::sun::star::awt::XControlModel; +using ::com::sun::star::embed::XEmbeddedObject; +using ::com::sun::star::embed::XEmbedPersist; +using ::com::sun::star::drawing::XControlShape; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::form::XForm; +using ::com::sun::star::form::XFormComponent; +using ::com::sun::star::form::XFormsSupplier; +using ::com::sun::star::form::binding::XBindableValue; +using ::com::sun::star::form::binding::XValueBinding; +using ::com::sun::star::form::binding::XListEntrySink; +using ::com::sun::star::form::binding::XListEntrySource; +using ::com::sun::star::script::ScriptEventDescriptor; +using ::com::sun::star::script::XEventAttacherManager; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; + +// ============================================================================ + +namespace { + +/** Helper class which mimics the auto_ptr< SdrObject > semantics, but calls + SdrObject::Free instead of deleting the SdrObject directly. */ +template< typename SdrObjType > +class TSdrObjectPtr +{ +public: + inline explicit TSdrObjectPtr( SdrObjType* pObj = 0 ) : mpObj( pObj ) {} + inline ~TSdrObjectPtr() { free(); } + + inline const SdrObjType* operator->() const { return mpObj; } + inline SdrObjType* operator->() { return mpObj; } + + inline const SdrObjType* get() const { return mpObj; } + inline SdrObjType* get() { return mpObj; } + + inline const SdrObjType& operator*() const { return *mpObj; } + inline SdrObjType& operator*() { return *mpObj; } + + inline bool is() const { return mpObj != 0; } + inline bool operator!() const { return mpObj == 0; } + + inline void reset( SdrObjType* pObj = 0 ) { free(); mpObj = pObj; } + inline SdrObjType* release() { SdrObjType* pObj = mpObj; mpObj = 0; return pObj; } + +private: + TSdrObjectPtr( const TSdrObjectPtr& ); // not implemented + TSdrObjectPtr& operator=( TSdrObjectPtr& rxObj ); // not implemented + + inline void free() { SdrObject* pObj = mpObj; mpObj = 0; SdrObject::Free( pObj ); } + +private: + SdrObjType* mpObj; +}; + +typedef TSdrObjectPtr< SdrObject > SdrObjectPtr; + +} // namespace + +// Drawing objects ============================================================ + +XclImpDrawObjBase::XclImpDrawObjBase( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + mnObjId( EXC_OBJ_INVALID_ID ), + mnObjType( EXC_OBJTYPE_UNKNOWN ), + mnDffShapeId( 0 ), + mnDffFlags( 0 ), + mbHasAnchor( false ), + mbHidden( false ), + mbVisible( true ), + mbPrintable( true ), + mbAreaObj( false ), + mbAutoMargin( true ), + mbSimpleMacro( true ), + mbProcessSdr( true ), + mbInsertSdr( true ), + mbCustomDff( false ) +{ +} + +XclImpDrawObjBase::~XclImpDrawObjBase() +{ +} + +/*static*/ XclImpDrawObjRef XclImpDrawObjBase::ReadObj3( const XclImpRoot& rRoot, XclImpStream& rStrm ) +{ + XclImpDrawObjRef xDrawObj; + + if( rStrm.GetRecLeft() >= 30 ) + { + sal_uInt16 nObjType; + rStrm.Ignore( 4 ); + rStrm >> nObjType; + switch( nObjType ) + { + case EXC_OBJTYPE_GROUP: xDrawObj.reset( new XclImpGroupObj( rRoot ) ); break; + case EXC_OBJTYPE_LINE: xDrawObj.reset( new XclImpLineObj( rRoot ) ); break; + case EXC_OBJTYPE_RECTANGLE: xDrawObj.reset( new XclImpRectObj( rRoot ) ); break; + case EXC_OBJTYPE_OVAL: xDrawObj.reset( new XclImpOvalObj( rRoot ) ); break; + case EXC_OBJTYPE_ARC: xDrawObj.reset( new XclImpArcObj( rRoot ) ); break; + case EXC_OBJTYPE_CHART: xDrawObj.reset( new XclImpChartObj( rRoot ) ); break; + case EXC_OBJTYPE_TEXT: xDrawObj.reset( new XclImpTextObj( rRoot ) ); break; + case EXC_OBJTYPE_BUTTON: xDrawObj.reset( new XclImpButtonObj( rRoot ) ); break; + case EXC_OBJTYPE_PICTURE: xDrawObj.reset( new XclImpPictureObj( rRoot ) ); break; + default: + DBG_ERROR1( "XclImpDrawObjBase::ReadObj3 - unknown object type 0x%04hX", nObjType ); + rRoot.GetTracer().TraceUnsupportedObjects(); + xDrawObj.reset( new XclImpPhObj( rRoot ) ); + } + } + + xDrawObj->ImplReadObj3( rStrm ); + return xDrawObj; +} + +/*static*/ XclImpDrawObjRef XclImpDrawObjBase::ReadObj4( const XclImpRoot& rRoot, XclImpStream& rStrm ) +{ + XclImpDrawObjRef xDrawObj; + + if( rStrm.GetRecLeft() >= 30 ) + { + sal_uInt16 nObjType; + rStrm.Ignore( 4 ); + rStrm >> nObjType; + switch( nObjType ) + { + case EXC_OBJTYPE_GROUP: xDrawObj.reset( new XclImpGroupObj( rRoot ) ); break; + case EXC_OBJTYPE_LINE: xDrawObj.reset( new XclImpLineObj( rRoot ) ); break; + case EXC_OBJTYPE_RECTANGLE: xDrawObj.reset( new XclImpRectObj( rRoot ) ); break; + case EXC_OBJTYPE_OVAL: xDrawObj.reset( new XclImpOvalObj( rRoot ) ); break; + case EXC_OBJTYPE_ARC: xDrawObj.reset( new XclImpArcObj( rRoot ) ); break; + case EXC_OBJTYPE_CHART: xDrawObj.reset( new XclImpChartObj( rRoot ) ); break; + case EXC_OBJTYPE_TEXT: xDrawObj.reset( new XclImpTextObj( rRoot ) ); break; + case EXC_OBJTYPE_BUTTON: xDrawObj.reset( new XclImpButtonObj( rRoot ) ); break; + case EXC_OBJTYPE_PICTURE: xDrawObj.reset( new XclImpPictureObj( rRoot ) ); break; + case EXC_OBJTYPE_POLYGON: xDrawObj.reset( new XclImpPolygonObj( rRoot ) ); break; + default: + DBG_ERROR1( "XclImpDrawObjBase::ReadObj4 - unknown object type 0x%04hX", nObjType ); + rRoot.GetTracer().TraceUnsupportedObjects(); + xDrawObj.reset( new XclImpPhObj( rRoot ) ); + } + } + + xDrawObj->ImplReadObj4( rStrm ); + return xDrawObj; +} + +/*static*/ XclImpDrawObjRef XclImpDrawObjBase::ReadObj5( const XclImpRoot& rRoot, XclImpStream& rStrm ) +{ + XclImpDrawObjRef xDrawObj; + + if( rStrm.GetRecLeft() >= 34 ) + { + sal_uInt16 nObjType; + rStrm.Ignore( 4 ); + rStrm >> nObjType; + switch( nObjType ) + { + case EXC_OBJTYPE_GROUP: xDrawObj.reset( new XclImpGroupObj( rRoot ) ); break; + case EXC_OBJTYPE_LINE: xDrawObj.reset( new XclImpLineObj( rRoot ) ); break; + case EXC_OBJTYPE_RECTANGLE: xDrawObj.reset( new XclImpRectObj( rRoot ) ); break; + case EXC_OBJTYPE_OVAL: xDrawObj.reset( new XclImpOvalObj( rRoot ) ); break; + case EXC_OBJTYPE_ARC: xDrawObj.reset( new XclImpArcObj( rRoot ) ); break; + case EXC_OBJTYPE_CHART: xDrawObj.reset( new XclImpChartObj( rRoot ) ); break; + case EXC_OBJTYPE_TEXT: xDrawObj.reset( new XclImpTextObj( rRoot ) ); break; + case EXC_OBJTYPE_BUTTON: xDrawObj.reset( new XclImpButtonObj( rRoot ) ); break; + case EXC_OBJTYPE_PICTURE: xDrawObj.reset( new XclImpPictureObj( rRoot ) ); break; + case EXC_OBJTYPE_POLYGON: xDrawObj.reset( new XclImpPolygonObj( rRoot ) ); break; + case EXC_OBJTYPE_CHECKBOX: xDrawObj.reset( new XclImpCheckBoxObj( rRoot ) ); break; + case EXC_OBJTYPE_OPTIONBUTTON: xDrawObj.reset( new XclImpOptionButtonObj( rRoot ) ); break; + case EXC_OBJTYPE_EDIT: xDrawObj.reset( new XclImpEditObj( rRoot ) ); break; + case EXC_OBJTYPE_LABEL: xDrawObj.reset( new XclImpLabelObj( rRoot ) ); break; + case EXC_OBJTYPE_DIALOG: xDrawObj.reset( new XclImpDialogObj( rRoot ) ); break; + case EXC_OBJTYPE_SPIN: xDrawObj.reset( new XclImpSpinButtonObj( rRoot ) ); break; + case EXC_OBJTYPE_SCROLLBAR: xDrawObj.reset( new XclImpScrollBarObj( rRoot ) ); break; + case EXC_OBJTYPE_LISTBOX: xDrawObj.reset( new XclImpListBoxObj( rRoot ) ); break; + case EXC_OBJTYPE_GROUPBOX: xDrawObj.reset( new XclImpGroupBoxObj( rRoot ) ); break; + case EXC_OBJTYPE_DROPDOWN: xDrawObj.reset( new XclImpDropDownObj( rRoot ) ); break; + default: + DBG_ERROR1( "XclImpDrawObjBase::ReadObj5 - unknown object type 0x%04hX", nObjType ); + rRoot.GetTracer().TraceUnsupportedObjects(); + xDrawObj.reset( new XclImpPhObj( rRoot ) ); + } + } + + xDrawObj->ImplReadObj5( rStrm ); + return xDrawObj; +} + +/*static*/ XclImpDrawObjRef XclImpDrawObjBase::ReadObj8( const XclImpRoot& rRoot, XclImpStream& rStrm ) +{ + XclImpDrawObjRef xDrawObj; + + if( rStrm.GetRecLeft() >= 10 ) + { + sal_uInt16 nSubRecId, nSubRecSize, nObjType; + rStrm >> nSubRecId >> nSubRecSize >> nObjType; + DBG_ASSERT( nSubRecId == EXC_ID_OBJCMO, "XclImpDrawObjBase::ReadObj8 - OBJCMO subrecord expected" ); + if( (nSubRecId == EXC_ID_OBJCMO) && (nSubRecSize >= 6) ) + { + switch( nObjType ) + { + // in BIFF8, all simple objects support text + case EXC_OBJTYPE_LINE: + case EXC_OBJTYPE_ARC: + xDrawObj.reset( new XclImpTextObj( rRoot ) ); + // lines and arcs may be 2-dimensional + xDrawObj->SetAreaObj( false ); + break; + + // in BIFF8, all simple objects support text + case EXC_OBJTYPE_RECTANGLE: + case EXC_OBJTYPE_OVAL: + case EXC_OBJTYPE_POLYGON: + case EXC_OBJTYPE_DRAWING: + case EXC_OBJTYPE_TEXT: + xDrawObj.reset( new XclImpTextObj( rRoot ) ); + break; + + case EXC_OBJTYPE_GROUP: xDrawObj.reset( new XclImpGroupObj( rRoot ) ); break; + case EXC_OBJTYPE_CHART: xDrawObj.reset( new XclImpChartObj( rRoot ) ); break; + case EXC_OBJTYPE_BUTTON: xDrawObj.reset( new XclImpButtonObj( rRoot ) ); break; + case EXC_OBJTYPE_PICTURE: xDrawObj.reset( new XclImpPictureObj( rRoot ) ); break; + case EXC_OBJTYPE_CHECKBOX: xDrawObj.reset( new XclImpCheckBoxObj( rRoot ) ); break; + case EXC_OBJTYPE_OPTIONBUTTON: xDrawObj.reset( new XclImpOptionButtonObj( rRoot ) ); break; + case EXC_OBJTYPE_EDIT: xDrawObj.reset( new XclImpEditObj( rRoot ) ); break; + case EXC_OBJTYPE_LABEL: xDrawObj.reset( new XclImpLabelObj( rRoot ) ); break; + case EXC_OBJTYPE_DIALOG: xDrawObj.reset( new XclImpDialogObj( rRoot ) ); break; + case EXC_OBJTYPE_SPIN: xDrawObj.reset( new XclImpSpinButtonObj( rRoot ) ); break; + case EXC_OBJTYPE_SCROLLBAR: xDrawObj.reset( new XclImpScrollBarObj( rRoot ) ); break; + case EXC_OBJTYPE_LISTBOX: xDrawObj.reset( new XclImpListBoxObj( rRoot ) ); break; + case EXC_OBJTYPE_GROUPBOX: xDrawObj.reset( new XclImpGroupBoxObj( rRoot ) ); break; + case EXC_OBJTYPE_DROPDOWN: xDrawObj.reset( new XclImpDropDownObj( rRoot ) ); break; + case EXC_OBJTYPE_NOTE: xDrawObj.reset( new XclImpNoteObj( rRoot ) ); break; + + default: + DBG_ERROR1( "XclImpDrawObjBase::ReadObj8 - unknown object type 0x%04hX", nObjType ); + rRoot.GetTracer().TraceUnsupportedObjects(); + xDrawObj.reset( new XclImpPhObj( rRoot ) ); + } + } + } + + xDrawObj->ImplReadObj8( rStrm ); + return xDrawObj; +} + +void XclImpDrawObjBase::SetAnchor( const XclObjAnchor& rAnchor ) +{ + maAnchor = rAnchor; + mbHasAnchor = true; +} + +void XclImpDrawObjBase::SetDffData( const DffObjData& rDffObjData, const String& rObjName, const String& rHyperlink, bool bVisible, bool bAutoMargin ) +{ + mnDffShapeId = rDffObjData.nShapeId; + mnDffFlags = rDffObjData.nSpFlags; + maObjName = rObjName; + maHyperlink = rHyperlink; + mbVisible = bVisible; + mbAutoMargin = bAutoMargin; +} + +String XclImpDrawObjBase::GetObjName() const +{ + /* #118053# #i51348# Always return a non-empty name. Create English + default names depending on the object type. This is not implemented as + virtual functions in derived classes, as class type and object type may + not match. */ + return (maObjName.Len() > 0) ? maObjName : GetObjectManager().GetDefaultObjName( *this ); +} + +const XclObjAnchor* XclImpDrawObjBase::GetAnchor() const +{ + return mbHasAnchor ? &maAnchor : 0; +} + +bool XclImpDrawObjBase::IsValidSize( const Rectangle& rAnchorRect ) const +{ + // XclObjAnchor rounds up the width, width of 3 is the result of an Excel width of 0 + return mbAreaObj ? + ((rAnchorRect.GetWidth() > 3) && (rAnchorRect.GetHeight() > 1)) : + ((rAnchorRect.GetWidth() > 3) || (rAnchorRect.GetHeight() > 1)); +} + +ScRange XclImpDrawObjBase::GetUsedArea( SCTAB nScTab ) const +{ + ScRange aScUsedArea( ScAddress::INITIALIZE_INVALID ); + // #i44077# object inserted -> update used area for OLE object import + if( mbHasAnchor && GetAddressConverter().ConvertRange( aScUsedArea, maAnchor, nScTab, nScTab, false ) ) + { + // reduce range, if object ends directly on borders between two columns or rows + if( (maAnchor.mnRX == 0) && (aScUsedArea.aStart.Col() < aScUsedArea.aEnd.Col()) ) + aScUsedArea.aEnd.IncCol( -1 ); + if( (maAnchor.mnBY == 0) && (aScUsedArea.aStart.Row() < aScUsedArea.aEnd.Row()) ) + aScUsedArea.aEnd.IncRow( -1 ); + } + return aScUsedArea; +} + +sal_Size XclImpDrawObjBase::GetProgressSize() const +{ + return DoGetProgressSize(); +} + +SdrObject* XclImpDrawObjBase::CreateSdrObject( XclImpDffConverter& rDffConv, const Rectangle& rAnchorRect, bool bIsDff ) const +{ + SdrObjectPtr xSdrObj; + if( bIsDff && !mbCustomDff ) + { + rDffConv.Progress( GetProgressSize() ); + } + else + { + xSdrObj.reset( DoCreateSdrObj( rDffConv, rAnchorRect ) ); + if( xSdrObj.is() ) + xSdrObj->SetModel( rDffConv.GetModel() ); + } + return xSdrObj.release(); +} + +void XclImpDrawObjBase::PreProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const +{ + // default: front layer, derived classes may have to set other layer in DoPreProcessSdrObj() + rSdrObj.NbcSetLayer( SC_LAYER_FRONT ); + + // set object name (GetObjName() will always return a non-empty name) + rSdrObj.SetName( GetObjName() ); + + // #i39167# full width for all objects regardless of horizontal alignment + rSdrObj.SetMergedItem( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_BLOCK ) ); + + // automatic text margin + if( mbAutoMargin ) + { + sal_Int32 nMargin = rDffConv.GetDefaultTextMargin(); + rSdrObj.SetMergedItem( SdrTextLeftDistItem( nMargin ) ); + rSdrObj.SetMergedItem( SdrTextRightDistItem( nMargin ) ); + rSdrObj.SetMergedItem( SdrTextUpperDistItem( nMargin ) ); + rSdrObj.SetMergedItem( SdrTextLowerDistItem( nMargin ) ); + } + + // macro and hyperlink +#ifdef ISSUE66550_HLINK_FOR_SHAPES + if( mbSimpleMacro && ((maMacroName.Len() > 0) || (maHyperlink.getLength() > 0)) ) + { + if( ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( &rSdrObj, TRUE ) ) + { + pInfo->SetMacro( XclTools::GetSbMacroUrl( maMacroName, GetDocShell() ) ); + pInfo->SetHlink( maHyperlink ); + } + } +#else + if( mbSimpleMacro && (maMacroName.Len() > 0) ) + if( ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( &rSdrObj, TRUE ) ) + pInfo->SetMacro( XclTools::GetSbMacroUrl( maMacroName, GetDocShell() ) ); +#endif + + // call virtual function for object type specific processing + DoPreProcessSdrObj( rDffConv, rSdrObj ); +} + +void XclImpDrawObjBase::PostProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const +{ + // call virtual function for object type specific processing + DoPostProcessSdrObj( rDffConv, rSdrObj ); +} + +// protected ------------------------------------------------------------------ + +void XclImpDrawObjBase::ReadName5( XclImpStream& rStrm, sal_uInt16 nNameLen ) +{ + maObjName.Erase(); + if( nNameLen > 0 ) + { + // name length field is repeated before the name + maObjName = rStrm.ReadByteString( false ); + // skip padding byte for word boundaries + if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 ); + } +} + +void XclImpDrawObjBase::ReadMacro3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + maMacroName.Erase(); + rStrm.Ignore( nMacroSize ); + // skip padding byte for word boundaries, not contained in nMacroSize + if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 ); +} + +void XclImpDrawObjBase::ReadMacro4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + maMacroName.Erase(); + rStrm.Ignore( nMacroSize ); +} + +void XclImpDrawObjBase::ReadMacro5( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + maMacroName.Erase(); + rStrm.Ignore( nMacroSize ); +} + +void XclImpDrawObjBase::ReadMacro8( XclImpStream& rStrm ) +{ + maMacroName.Erase(); + if( rStrm.GetRecLeft() > 6 ) + { + // macro is stored in a tNameXR token containing a link to a defined name + sal_uInt16 nFmlaSize; + rStrm >> nFmlaSize; + rStrm.Ignore( 4 ); + DBG_ASSERT( nFmlaSize == 7, "XclImpDrawObjBase::ReadMacro - unexpected formula size" ); + if( nFmlaSize == 7 ) + { + sal_uInt8 nTokenId; + sal_uInt16 nExtSheet, nExtName; + rStrm >> nTokenId >> nExtSheet >> nExtName; + DBG_ASSERT( nTokenId == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ), + "XclImpDrawObjBase::ReadMacro - tNameXR token expected" ); + if( nTokenId == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ) ) + maMacroName = GetLinkManager().GetMacroName( nExtSheet, nExtName ); + } + } +} + +void XclImpDrawObjBase::ConvertLineStyle( SdrObject& rSdrObj, const XclObjLineData& rLineData ) const +{ + if( rLineData.IsAuto() ) + { + XclObjLineData aAutoData; + aAutoData.mnAuto = 0; + ConvertLineStyle( rSdrObj, aAutoData ); + } + else + { + long nLineWidth = 35 * ::std::min( rLineData.mnWidth, EXC_OBJ_LINE_THICK ); + rSdrObj.SetMergedItem( XLineWidthItem( nLineWidth ) ); + rSdrObj.SetMergedItem( XLineColorItem( EMPTY_STRING, GetPalette().GetColor( rLineData.mnColorIdx ) ) ); + rSdrObj.SetMergedItem( XLineJointItem( XLINEJOINT_MITER ) ); + + ULONG nDotLen = ::std::max< ULONG >( 70 * rLineData.mnWidth, 35 ); + ULONG nDashLen = 3 * nDotLen; + ULONG nDist = 2 * nDotLen; + + switch( rLineData.mnStyle ) + { + default: + case EXC_OBJ_LINE_SOLID: + rSdrObj.SetMergedItem( XLineStyleItem( XLINE_SOLID ) ); + break; + case EXC_OBJ_LINE_DASH: + rSdrObj.SetMergedItem( XLineStyleItem( XLINE_DASH ) ); + rSdrObj.SetMergedItem( XLineDashItem( EMPTY_STRING, XDash( XDASH_RECT, 0, nDotLen, 1, nDashLen, nDist ) ) ); + break; + case EXC_OBJ_LINE_DOT: + rSdrObj.SetMergedItem( XLineStyleItem( XLINE_DASH ) ); + rSdrObj.SetMergedItem( XLineDashItem( EMPTY_STRING, XDash( XDASH_RECT, 1, nDotLen, 0, nDashLen, nDist ) ) ); + break; + case EXC_OBJ_LINE_DASHDOT: + rSdrObj.SetMergedItem( XLineStyleItem( XLINE_DASH ) ); + rSdrObj.SetMergedItem( XLineDashItem( EMPTY_STRING, XDash( XDASH_RECT, 1, nDotLen, 1, nDashLen, nDist ) ) ); + break; + case EXC_OBJ_LINE_DASHDOTDOT: + rSdrObj.SetMergedItem( XLineStyleItem( XLINE_DASH ) ); + rSdrObj.SetMergedItem( XLineDashItem( EMPTY_STRING, XDash( XDASH_RECT, 2, nDotLen, 1, nDashLen, nDist ) ) ); + break; + case EXC_OBJ_LINE_MEDTRANS: + rSdrObj.SetMergedItem( XLineStyleItem( XLINE_SOLID ) ); + rSdrObj.SetMergedItem( XLineTransparenceItem( 50 ) ); + break; + case EXC_OBJ_LINE_DARKTRANS: + rSdrObj.SetMergedItem( XLineStyleItem( XLINE_SOLID ) ); + rSdrObj.SetMergedItem( XLineTransparenceItem( 25 ) ); + break; + case EXC_OBJ_LINE_LIGHTTRANS: + rSdrObj.SetMergedItem( XLineStyleItem( XLINE_SOLID ) ); + rSdrObj.SetMergedItem( XLineTransparenceItem( 75 ) ); + break; + case EXC_OBJ_LINE_NONE: + rSdrObj.SetMergedItem( XLineStyleItem( XLINE_NONE ) ); + break; + } + } +} + +void XclImpDrawObjBase::ConvertFillStyle( SdrObject& rSdrObj, const XclObjFillData& rFillData ) const +{ + if( rFillData.IsAuto() ) + { + XclObjFillData aAutoData; + aAutoData.mnAuto = 0; + ConvertFillStyle( rSdrObj, aAutoData ); + } + else if( rFillData.mnPattern == EXC_PATT_NONE ) + { + rSdrObj.SetMergedItem( XFillStyleItem( XFILL_NONE ) ); + } + else + { + Color aPattColor = GetPalette().GetColor( rFillData.mnPattColorIdx ); + Color aBackColor = GetPalette().GetColor( rFillData.mnBackColorIdx ); + if( (rFillData.mnPattern == EXC_PATT_SOLID) || (aPattColor == aBackColor) ) + { + rSdrObj.SetMergedItem( XFillStyleItem( XFILL_SOLID ) ); + rSdrObj.SetMergedItem( XFillColorItem( EMPTY_STRING, aPattColor ) ); + } + else + { + static const sal_uInt8 sppnPatterns[][ 8 ] = + { + { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 }, + { 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD }, + { 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22 }, + { 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00 }, + { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC }, + { 0x33, 0x66, 0xCC, 0x99, 0x33, 0x66, 0xCC, 0x99 }, + { 0xCC, 0x66, 0x33, 0x99, 0xCC, 0x66, 0x33, 0x99 }, + { 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33 }, + { 0xCC, 0xFF, 0x33, 0xFF, 0xCC, 0xFF, 0x33, 0xFF }, + { 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, + { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 }, + { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 }, + { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 }, + { 0xFF, 0x11, 0x11, 0x11, 0xFF, 0x11, 0x11, 0x11 }, + { 0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11 }, + { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 }, + { 0x80, 0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x00 } + }; + const sal_uInt8* const pnPattern = sppnPatterns[ ::std::min< size_t >( rFillData.mnPattern - 2, STATIC_TABLE_SIZE( sppnPatterns ) ) ]; + // create 2-colored 8x8 DIB + SvMemoryStream aMemStrm; + aMemStrm << sal_uInt32( 12 ) << sal_Int16( 8 ) << sal_Int16( 8 ) << sal_uInt16( 1 ) << sal_uInt16( 1 ); + aMemStrm << sal_uInt8( 0xFF ) << sal_uInt8( 0xFF ) << sal_uInt8( 0xFF ); + aMemStrm << sal_uInt8( 0x00 ) << sal_uInt8( 0x00 ) << sal_uInt8( 0x00 ); + for( size_t nIdx = 0; nIdx < 8; ++nIdx ) + aMemStrm << sal_uInt32( pnPattern[ nIdx ] ); // 32-bit little-endian + aMemStrm.Seek( STREAM_SEEK_TO_BEGIN ); + Bitmap aBitmap; + aBitmap.Read( aMemStrm, FALSE ); + XOBitmap aXOBitmap( aBitmap ); + aXOBitmap.Bitmap2Array(); + aXOBitmap.SetBitmapType( XBITMAP_8X8 ); + if( aXOBitmap.GetBackgroundColor().GetColor() == COL_BLACK ) + ::std::swap( aPattColor, aBackColor ); + aXOBitmap.SetPixelColor( aPattColor ); + aXOBitmap.SetBackgroundColor( aBackColor ); + rSdrObj.SetMergedItem( XFillStyleItem( XFILL_BITMAP ) ); + rSdrObj.SetMergedItem( XFillBitmapItem( EMPTY_STRING, aXOBitmap ) ); + } + } +} + +void XclImpDrawObjBase::ConvertFrameStyle( SdrObject& rSdrObj, sal_uInt16 nFrameFlags ) const +{ + if( ::get_flag( nFrameFlags, EXC_OBJ_FRAME_SHADOW ) ) + { + rSdrObj.SetMergedItem( SdrShadowItem( TRUE ) ); + rSdrObj.SetMergedItem( SdrShadowXDistItem( 35 ) ); + rSdrObj.SetMergedItem( SdrShadowYDistItem( 35 ) ); + rSdrObj.SetMergedItem( SdrShadowColorItem( EMPTY_STRING, GetPalette().GetColor( EXC_COLOR_WINDOWTEXT ) ) ); + } +} + +Color XclImpDrawObjBase::GetSolidLineColor( const XclObjLineData& rLineData ) const +{ + Color aColor( COL_TRANSPARENT ); + if( rLineData.IsAuto() ) + { + XclObjLineData aAutoData; + aAutoData.mnAuto = 0; + aColor = GetSolidLineColor( aAutoData ); + } + else if( rLineData.mnStyle != EXC_OBJ_LINE_NONE ) + { + aColor = GetPalette().GetColor( rLineData.mnColorIdx ); + } + return aColor; +} + +Color XclImpDrawObjBase::GetSolidFillColor( const XclObjFillData& rFillData ) const +{ + Color aColor( COL_TRANSPARENT ); + if( rFillData.IsAuto() ) + { + XclObjFillData aAutoData; + aAutoData.mnAuto = 0; + aColor = GetSolidFillColor( aAutoData ); + } + else if( rFillData.mnPattern != EXC_PATT_NONE ) + { + Color aPattColor = GetPalette().GetColor( rFillData.mnPattColorIdx ); + Color aBackColor = GetPalette().GetColor( rFillData.mnBackColorIdx ); + aColor = XclTools::GetPatternColor( aPattColor, aBackColor, rFillData.mnPattern ); + } + return aColor; +} + +void XclImpDrawObjBase::DoReadObj3( XclImpStream&, sal_uInt16 ) +{ +} + +void XclImpDrawObjBase::DoReadObj4( XclImpStream&, sal_uInt16 ) +{ +} + +void XclImpDrawObjBase::DoReadObj5( XclImpStream&, sal_uInt16, sal_uInt16 ) +{ +} + +void XclImpDrawObjBase::DoReadObj8SubRec( XclImpStream&, sal_uInt16, sal_uInt16 ) +{ +} + +sal_Size XclImpDrawObjBase::DoGetProgressSize() const +{ + return 1; +} + +SdrObject* XclImpDrawObjBase::DoCreateSdrObj( XclImpDffConverter& rDffConv, const Rectangle& ) const +{ + rDffConv.Progress( GetProgressSize() ); + return 0; +} + +void XclImpDrawObjBase::DoPreProcessSdrObj( XclImpDffConverter&, SdrObject& ) const +{ + // trace if object is not printable + if( !IsPrintable() ) + GetTracer().TraceObjectNotPrintable(); +} + +void XclImpDrawObjBase::DoPostProcessSdrObj( XclImpDffConverter&, SdrObject& ) const +{ +} + +void XclImpDrawObjBase::ImplReadObj3( XclImpStream& rStrm ) +{ + // back to offset 4 (ignore object count field) + rStrm.Seek( 4 ); + + sal_uInt16 nObjFlags, nMacroSize; + rStrm >> mnObjType >> mnObjId >> nObjFlags >> maAnchor >> nMacroSize; + rStrm.Ignore( 2 ); + + mbHasAnchor = true; + mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN ); + mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE ); + DoReadObj3( rStrm, nMacroSize ); +} + +void XclImpDrawObjBase::ImplReadObj4( XclImpStream& rStrm ) +{ + // back to offset 4 (ignore object count field) + rStrm.Seek( 4 ); + + sal_uInt16 nObjFlags, nMacroSize; + rStrm >> mnObjType >> mnObjId >> nObjFlags >> maAnchor >> nMacroSize; + rStrm.Ignore( 2 ); + + mbHasAnchor = true; + mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN ); + mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE ); + mbPrintable = ::get_flag( nObjFlags, EXC_OBJ_PRINTABLE ); + DoReadObj4( rStrm, nMacroSize ); +} + +void XclImpDrawObjBase::ImplReadObj5( XclImpStream& rStrm ) +{ + // back to offset 4 (ignore object count field) + rStrm.Seek( 4 ); + + sal_uInt16 nObjFlags, nMacroSize, nNameLen; + rStrm >> mnObjType >> mnObjId >> nObjFlags >> maAnchor >> nMacroSize; + rStrm.Ignore( 2 ); + rStrm >> nNameLen; + rStrm.Ignore( 2 ); + + mbHasAnchor = true; + mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN ); + mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE ); + mbPrintable = ::get_flag( nObjFlags, EXC_OBJ_PRINTABLE ); + DoReadObj5( rStrm, nNameLen, nMacroSize ); +} + +void XclImpDrawObjBase::ImplReadObj8( XclImpStream& rStrm ) +{ + // back to beginning + rStrm.Seek( EXC_REC_SEEK_TO_BEGIN ); + + bool bLoop = true; + while( bLoop && (rStrm.GetRecLeft() >= 4) ) + { + sal_uInt16 nSubRecId, nSubRecSize; + rStrm >> nSubRecId >> nSubRecSize; + rStrm.PushPosition(); + // sometimes the last subrecord has an invalid length (OBJLBSDATA) -> min() + nSubRecSize = static_cast< sal_uInt16 >( ::std::min< sal_Size >( nSubRecSize, rStrm.GetRecLeft() ) ); + + switch( nSubRecId ) + { + case EXC_ID_OBJCMO: + DBG_ASSERT( rStrm.GetRecPos() == 4, "XclImpDrawObjBase::ImplReadObj8 - unexpected OBJCMO subrecord" ); + if( (rStrm.GetRecPos() == 4) && (nSubRecSize >= 6) ) + { + sal_uInt16 nObjFlags; + rStrm >> mnObjType >> mnObjId >> nObjFlags; + mbPrintable = ::get_flag( nObjFlags, EXC_OBJCMO_PRINTABLE ); + } + break; + case EXC_ID_OBJMACRO: + ReadMacro8( rStrm ); + break; + case EXC_ID_OBJEND: + bLoop = false; + break; + default: + DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } + + rStrm.PopPosition(); + rStrm.Ignore( nSubRecSize ); + } + + /* Call DoReadObj8SubRec() with EXC_ID_OBJEND for further stream + processing (e.g. charts), even if the OBJEND subrecord is missing. */ + DoReadObj8SubRec( rStrm, EXC_ID_OBJEND, 0 ); + + /* Pictures that Excel reads from BIFF5 and writes to BIFF8 still have the + IMGDATA record following the OBJ record (but they use the image data + stored in DFF). The IMGDATA record may be continued by several CONTINUE + records. But the last CONTINUE record may be in fact an MSODRAWING + record that contains the DFF data of the next drawing object! So we + have to skip just enough CONTINUE records to look at the next + MSODRAWING/CONTINUE record. */ + if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() ) + { + sal_uInt32 nDataSize; + rStrm.Ignore( 4 ); + rStrm >> nDataSize; + nDataSize -= rStrm.GetRecLeft(); + // skip following CONTINUE records until IMGDATA ends + while( (nDataSize > 0) && (rStrm.GetNextRecId() == EXC_ID_CONT) && rStrm.StartNextRecord() ) + { + DBG_ASSERT( nDataSize >= rStrm.GetRecLeft(), "XclImpDrawObjBase::ImplReadObj8 - CONTINUE too long" ); + nDataSize -= ::std::min< sal_uInt32 >( rStrm.GetRecLeft(), nDataSize ); + } + DBG_ASSERT( nDataSize == 0, "XclImpDrawObjBase::ImplReadObj8 - missing CONTINUE records" ); + // next record may be MSODRAWING or CONTINUE or anything else + } +} + +// ---------------------------------------------------------------------------- + +void XclImpDrawObjVector::InsertGrouped( XclImpDrawObjRef xDrawObj ) +{ + if( !empty() ) + if( XclImpGroupObj* pGroupObj = dynamic_cast< XclImpGroupObj* >( back().get() ) ) + if( pGroupObj->TryInsert( xDrawObj ) ) + return; + push_back( xDrawObj ); +} + +sal_Size XclImpDrawObjVector::GetProgressSize() const +{ + sal_Size nProgressSize = 0; + for( const_iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + nProgressSize += (*aIt)->GetProgressSize(); + return nProgressSize; +} + +// ---------------------------------------------------------------------------- + +XclImpPhObj::XclImpPhObj( const XclImpRoot& rRoot ) : + XclImpDrawObjBase( rRoot ) +{ + SetProcessSdrObj( false ); +} + +// ---------------------------------------------------------------------------- + +XclImpGroupObj::XclImpGroupObj( const XclImpRoot& rRoot ) : + XclImpDrawObjBase( rRoot ), + mnFirstUngrouped( 0 ) +{ +} + +bool XclImpGroupObj::TryInsert( XclImpDrawObjRef xDrawObj ) +{ + if( xDrawObj->GetObjId() == mnFirstUngrouped ) + return false; + // insert into own list or into nested group + maChildren.InsertGrouped( xDrawObj ); + return true; +} + +void XclImpGroupObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + rStrm.Ignore( 4 ); + rStrm >> mnFirstUngrouped; + rStrm.Ignore( 16 ); + ReadMacro3( rStrm, nMacroSize ); +} + +void XclImpGroupObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + rStrm.Ignore( 4 ); + rStrm >> mnFirstUngrouped; + rStrm.Ignore( 16 ); + ReadMacro4( rStrm, nMacroSize ); +} + +void XclImpGroupObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + rStrm.Ignore( 4 ); + rStrm >> mnFirstUngrouped; + rStrm.Ignore( 16 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); +} + +sal_Size XclImpGroupObj::DoGetProgressSize() const +{ + return XclImpDrawObjBase::DoGetProgressSize() + maChildren.GetProgressSize(); +} + +SdrObject* XclImpGroupObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const Rectangle& /*rAnchorRect*/ ) const +{ + TSdrObjectPtr< SdrObjGroup > xSdrObj( new SdrObjGroup ); + // child objects in BIFF2-BIFF5 have absolute size, not needed to pass own anchor rectangle + SdrObjList& rObjList = *xSdrObj->GetSubList(); // SdrObjGroup always returns existing sublist + for( XclImpDrawObjVector::const_iterator aIt = maChildren.begin(), aEnd = maChildren.end(); aIt != aEnd; ++aIt ) + rDffConv.ProcessObject( rObjList, **aIt ); + rDffConv.Progress(); + return xSdrObj.release(); +} + +// ---------------------------------------------------------------------------- + +XclImpLineObj::XclImpLineObj( const XclImpRoot& rRoot ) : + XclImpDrawObjBase( rRoot ), + mnArrows( 0 ), + mnStartPoint( EXC_OBJ_LINE_TL ) +{ + SetAreaObj( false ); +} + +void XclImpLineObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + rStrm >> maLineData >> mnArrows >> mnStartPoint; + rStrm.Ignore( 1 ); + ReadMacro3( rStrm, nMacroSize ); +} + +void XclImpLineObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + rStrm >> maLineData >> mnArrows >> mnStartPoint; + rStrm.Ignore( 1 ); + ReadMacro4( rStrm, nMacroSize ); +} + +void XclImpLineObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + rStrm >> maLineData >> mnArrows >> mnStartPoint; + rStrm.Ignore( 1 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); +} + +SdrObject* XclImpLineObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const Rectangle& rAnchorRect ) const +{ + ::basegfx::B2DPolygon aB2DPolygon; + switch( mnStartPoint ) + { + default: + case EXC_OBJ_LINE_TL: + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Top() ) ); + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Bottom() ) ); + break; + case EXC_OBJ_LINE_TR: + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Top() ) ); + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Bottom() ) ); + break; + case EXC_OBJ_LINE_BR: + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Bottom() ) ); + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Top() ) ); + break; + case EXC_OBJ_LINE_BL: + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Bottom() ) ); + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Top() ) ); + break; + } + SdrObjectPtr xSdrObj( new SdrPathObj( OBJ_LINE, ::basegfx::B2DPolyPolygon( aB2DPolygon ) ) ); + ConvertLineStyle( *xSdrObj, maLineData ); + + // line ends + sal_uInt8 nArrowType = ::extract_value< sal_uInt8 >( mnArrows, 0, 4 ); + bool bLineStart = false; + bool bLineEnd = false; + bool bFilled = false; + switch( nArrowType ) + { + case EXC_OBJ_ARROW_OPEN: bLineStart = false; bLineEnd = true; bFilled = false; break; + case EXC_OBJ_ARROW_OPENBOTH: bLineStart = true; bLineEnd = true; bFilled = false; break; + case EXC_OBJ_ARROW_FILLED: bLineStart = false; bLineEnd = true; bFilled = true; break; + case EXC_OBJ_ARROW_FILLEDBOTH: bLineStart = true; bLineEnd = true; bFilled = true; break; + } + if( bLineStart || bLineEnd ) + { + sal_uInt8 nArrowWidth = ::extract_value< sal_uInt8 >( mnArrows, 4, 4 ); + double fArrowWidth = 3.0; + switch( nArrowWidth ) + { + case EXC_OBJ_ARROW_NARROW: fArrowWidth = 2.0; break; + case EXC_OBJ_ARROW_MEDIUM: fArrowWidth = 3.0; break; + case EXC_OBJ_ARROW_WIDE: fArrowWidth = 5.0; break; + } + + sal_uInt8 nArrowLength = ::extract_value< sal_uInt8 >( mnArrows, 8, 4 ); + double fArrowLength = 3.0; + switch( nArrowLength ) + { + case EXC_OBJ_ARROW_NARROW: fArrowLength = 2.5; break; + case EXC_OBJ_ARROW_MEDIUM: fArrowLength = 3.5; break; + case EXC_OBJ_ARROW_WIDE: fArrowLength = 6.0; break; + } + + ::basegfx::B2DPolygon aArrowPoly; +#define EXC_ARROW_POINT( x, y ) ::basegfx::B2DPoint( fArrowWidth * (x), fArrowLength * (y) ) + if( bFilled ) + { + aArrowPoly.append( EXC_ARROW_POINT( 0, 100 ) ); + aArrowPoly.append( EXC_ARROW_POINT( 50, 0 ) ); + aArrowPoly.append( EXC_ARROW_POINT( 100, 100 ) ); + } + else + { + sal_uInt8 nLineWidth = ::limit_cast< sal_uInt8 >( maLineData.mnWidth, EXC_OBJ_LINE_THIN, EXC_OBJ_LINE_THICK ); + aArrowPoly.append( EXC_ARROW_POINT( 50, 0 ) ); + aArrowPoly.append( EXC_ARROW_POINT( 100, 100 - 3 * nLineWidth ) ); + aArrowPoly.append( EXC_ARROW_POINT( 100 - 5 * nLineWidth, 100 ) ); + aArrowPoly.append( EXC_ARROW_POINT( 50, 12 * nLineWidth ) ); + aArrowPoly.append( EXC_ARROW_POINT( 5 * nLineWidth, 100 ) ); + aArrowPoly.append( EXC_ARROW_POINT( 0, 100 - 3 * nLineWidth ) ); + } +#undef EXC_ARROW_POINT + + ::basegfx::B2DPolyPolygon aArrowPolyPoly( aArrowPoly ); + long nWidth = static_cast< long >( 125 * fArrowWidth ); + if( bLineStart ) + { + xSdrObj->SetMergedItem( XLineStartItem( EMPTY_STRING, aArrowPolyPoly ) ); + xSdrObj->SetMergedItem( XLineStartWidthItem( nWidth ) ); + xSdrObj->SetMergedItem( XLineStartCenterItem( FALSE ) ); + } + if( bLineEnd ) + { + xSdrObj->SetMergedItem( XLineEndItem( EMPTY_STRING, aArrowPolyPoly ) ); + xSdrObj->SetMergedItem( XLineEndWidthItem( nWidth ) ); + xSdrObj->SetMergedItem( XLineEndCenterItem( FALSE ) ); + } + } + rDffConv.Progress(); + return xSdrObj.release(); +} + +// ---------------------------------------------------------------------------- + +XclImpRectObj::XclImpRectObj( const XclImpRoot& rRoot ) : + XclImpDrawObjBase( rRoot ), + mnFrameFlags( 0 ) +{ + SetAreaObj( true ); +} + +void XclImpRectObj::ReadFrameData( XclImpStream& rStrm ) +{ + rStrm >> maFillData >> maLineData >> mnFrameFlags; +} + +void XclImpRectObj::ConvertRectStyle( SdrObject& rSdrObj ) const +{ + ConvertLineStyle( rSdrObj, maLineData ); + ConvertFillStyle( rSdrObj, maFillData ); + ConvertFrameStyle( rSdrObj, mnFrameFlags ); +} + +void XclImpRectObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + ReadMacro3( rStrm, nMacroSize ); +} + +void XclImpRectObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + ReadMacro4( rStrm, nMacroSize ); +} + +void XclImpRectObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); +} + +SdrObject* XclImpRectObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const Rectangle& rAnchorRect ) const +{ + SdrObjectPtr xSdrObj( new SdrRectObj( rAnchorRect ) ); + ConvertRectStyle( *xSdrObj ); + rDffConv.Progress(); + return xSdrObj.release(); +} + +// ---------------------------------------------------------------------------- + +XclImpOvalObj::XclImpOvalObj( const XclImpRoot& rRoot ) : + XclImpRectObj( rRoot ) +{ +} + +SdrObject* XclImpOvalObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const Rectangle& rAnchorRect ) const +{ + SdrObjectPtr xSdrObj( new SdrCircObj( OBJ_CIRC, rAnchorRect ) ); + ConvertRectStyle( *xSdrObj ); + rDffConv.Progress(); + return xSdrObj.release(); +} + +// ---------------------------------------------------------------------------- + +XclImpArcObj::XclImpArcObj( const XclImpRoot& rRoot ) : + XclImpDrawObjBase( rRoot ), + mnQuadrant( EXC_OBJ_ARC_TR ) +{ + SetAreaObj( false ); // arc may be 2-dimensional +} + +void XclImpArcObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + rStrm >> maFillData >> maLineData >> mnQuadrant; + rStrm.Ignore( 1 ); + ReadMacro3( rStrm, nMacroSize ); +} + +void XclImpArcObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + rStrm >> maFillData >> maLineData >> mnQuadrant; + rStrm.Ignore( 1 ); + ReadMacro4( rStrm, nMacroSize ); +} + +void XclImpArcObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + rStrm >> maFillData >> maLineData >> mnQuadrant; + rStrm.Ignore( 1 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); +} + +SdrObject* XclImpArcObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const Rectangle& rAnchorRect ) const +{ + Rectangle aNewRect = rAnchorRect; + long nStartAngle = 0; + long nEndAngle = 0; + switch( mnQuadrant ) + { + default: + case EXC_OBJ_ARC_TR: + nStartAngle = 0; + nEndAngle = 9000; + aNewRect.Left() -= rAnchorRect.GetWidth(); + aNewRect.Bottom() += rAnchorRect.GetHeight(); + break; + case EXC_OBJ_ARC_TL: + nStartAngle = 9000; + nEndAngle = 18000; + aNewRect.Right() += rAnchorRect.GetWidth(); + aNewRect.Bottom() += rAnchorRect.GetHeight(); + break; + case EXC_OBJ_ARC_BL: + nStartAngle = 18000; + nEndAngle = 27000; + aNewRect.Right() += rAnchorRect.GetWidth(); + aNewRect.Top() -= rAnchorRect.GetHeight(); + break; + case EXC_OBJ_ARC_BR: + nStartAngle = 27000; + nEndAngle = 0; + aNewRect.Left() -= rAnchorRect.GetWidth(); + aNewRect.Top() -= rAnchorRect.GetHeight(); + break; + } + SdrObjKind eObjKind = maFillData.IsFilled() ? OBJ_SECT : OBJ_CARC; + SdrObjectPtr xSdrObj( new SdrCircObj( eObjKind, aNewRect, nStartAngle, nEndAngle ) ); + ConvertFillStyle( *xSdrObj, maFillData ); + ConvertLineStyle( *xSdrObj, maLineData ); + rDffConv.Progress(); + return xSdrObj.release(); +} + +// ---------------------------------------------------------------------------- + +XclImpPolygonObj::XclImpPolygonObj( const XclImpRoot& rRoot ) : + XclImpRectObj( rRoot ), + mnPolyFlags( 0 ), + mnPointCount( 0 ) +{ + SetAreaObj( false ); // polygon may be 2-dimensional +} + +void XclImpPolygonObj::ReadCoordList( XclImpStream& rStrm ) +{ + if( (rStrm.GetNextRecId() == EXC_ID_COORDLIST) && rStrm.StartNextRecord() ) + { + DBG_ASSERT( rStrm.GetRecLeft() / 4 == mnPointCount, "XclImpPolygonObj::ReadCoordList - wrong polygon point count" ); + while( rStrm.GetRecLeft() >= 4 ) + { + sal_uInt16 nX, nY; + rStrm >> nX >> nY; + maCoords.push_back( Point( nX, nY ) ); + } + } +} + +void XclImpPolygonObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + rStrm >> mnPolyFlags; + rStrm.Ignore( 10 ); + rStrm >> mnPointCount; + rStrm.Ignore( 8 ); + ReadMacro4( rStrm, nMacroSize ); + ReadCoordList( rStrm ); +} + +void XclImpPolygonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + rStrm >> mnPolyFlags; + rStrm.Ignore( 10 ); + rStrm >> mnPointCount; + rStrm.Ignore( 8 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); + ReadCoordList( rStrm ); +} + +namespace { + +::basegfx::B2DPoint lclGetPolyPoint( const Rectangle& rAnchorRect, const Point& rPoint ) +{ + return ::basegfx::B2DPoint( + rAnchorRect.Left() + static_cast< sal_Int32 >( ::std::min< double >( rPoint.X(), 16384.0 ) / 16384.0 * rAnchorRect.GetWidth() + 0.5 ), + rAnchorRect.Top() + static_cast< sal_Int32 >( ::std::min< double >( rPoint.Y(), 16384.0 ) / 16384.0 * rAnchorRect.GetHeight() + 0.5 ) ); +} + +} // namespace + +SdrObject* XclImpPolygonObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const Rectangle& rAnchorRect ) const +{ + SdrObjectPtr xSdrObj; + if( maCoords.size() >= 2 ) + { + // create the polygon + ::basegfx::B2DPolygon aB2DPolygon; + for( PointVector::const_iterator aIt = maCoords.begin(), aEnd = maCoords.end(); aIt != aEnd; ++aIt ) + aB2DPolygon.append( lclGetPolyPoint( rAnchorRect, *aIt ) ); + // close polygon if specified + if( ::get_flag( mnPolyFlags, EXC_OBJ_POLY_CLOSED ) && (maCoords.front() != maCoords.back()) ) + aB2DPolygon.append( lclGetPolyPoint( rAnchorRect, maCoords.front() ) ); + // create the SdrObject + SdrObjKind eObjKind = maFillData.IsFilled() ? OBJ_PATHPOLY : OBJ_PATHPLIN; + xSdrObj.reset( new SdrPathObj( eObjKind, ::basegfx::B2DPolyPolygon( aB2DPolygon ) ) ); + ConvertRectStyle( *xSdrObj ); + } + rDffConv.Progress(); + return xSdrObj.release(); +} + +// ---------------------------------------------------------------------------- + +void XclImpObjTextData::ReadByteString( XclImpStream& rStrm ) +{ + mxString.reset(); + if( maData.mnTextLen > 0 ) + { + mxString.reset( new XclImpString( rStrm.ReadRawByteString( maData.mnTextLen ) ) ); + // skip padding byte for word boundaries + if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 ); + } +} + +void XclImpObjTextData::ReadFormats( XclImpStream& rStrm ) +{ + if( mxString.is() ) + mxString->ReadObjFormats( rStrm, maData.mnFormatSize ); + else + rStrm.Ignore( maData.mnFormatSize ); +} + +// ---------------------------------------------------------------------------- + +XclImpTextObj::XclImpTextObj( const XclImpRoot& rRoot ) : + XclImpRectObj( rRoot ) +{ +} + +void XclImpTextObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + maTextData.maData.ReadObj3( rStrm ); + ReadMacro3( rStrm, nMacroSize ); + maTextData.ReadByteString( rStrm ); + maTextData.ReadFormats( rStrm ); +} + +void XclImpTextObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + maTextData.maData.ReadObj3( rStrm ); + ReadMacro4( rStrm, nMacroSize ); + maTextData.ReadByteString( rStrm ); + maTextData.ReadFormats( rStrm ); +} + +void XclImpTextObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + maTextData.maData.ReadObj5( rStrm ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); + maTextData.ReadByteString( rStrm ); + rStrm.Ignore( maTextData.maData.mnLinkSize ); // ignore text link formula + maTextData.ReadFormats( rStrm ); +} + +SdrObject* XclImpTextObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const Rectangle& rAnchorRect ) const +{ + TSdrObjectPtr< SdrObjCustomShape > xSdrObj( new SdrObjCustomShape ); + xSdrObj->NbcSetSnapRect( rAnchorRect ); + OUString aRectType = CREATE_OUSTRING( "rectangle" ); + xSdrObj->MergeDefaultAttributes( &aRectType ); + ConvertRectStyle( *xSdrObj ); + BOOL bAutoSize = ::get_flag( maTextData.maData.mnFlags, EXC_OBJ_TEXT_AUTOSIZE ); + xSdrObj->SetMergedItem( SdrTextAutoGrowWidthItem( bAutoSize ) ); + xSdrObj->SetMergedItem( SdrTextAutoGrowHeightItem( bAutoSize ) ); + xSdrObj->SetMergedItem( SdrTextWordWrapItem( TRUE ) ); + rDffConv.Progress(); + return xSdrObj.release(); +} + +void XclImpTextObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const +{ + // set text data + if( SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( &rSdrObj ) ) + { + if( maTextData.mxString.is() ) + { + if( maTextData.mxString->IsRich() ) + { + // rich text + ::std::auto_ptr< EditTextObject > xEditObj( + XclImpStringHelper::CreateTextObject( GetRoot(), *maTextData.mxString ) ); + OutlinerParaObject* pOutlineObj = new OutlinerParaObject( *xEditObj ); + pOutlineObj->SetOutlinerMode( OUTLINERMODE_TEXTOBJECT ); + // text object takes ownership of the outliner object + pTextObj->NbcSetOutlinerParaObject( pOutlineObj ); + } + else + { + // plain text + pTextObj->NbcSetText( maTextData.mxString->GetText() ); + } + + /* #i96858# Do not apply any formatting if there is no text. + SdrObjCustomShape::SetVerticalWriting (initiated from + SetMergedItem) calls SdrTextObj::ForceOutlinerParaObject which + ensures that we can erroneously write a ClientTextbox record + (with no content) while exporting to XLS, which can cause a + corrupted exported document. */ + + SvxAdjust eHorAlign = SVX_ADJUST_LEFT; + SdrTextVertAdjust eVerAlign = SDRTEXTVERTADJUST_TOP; + + // orientation (this is only a fake, drawing does not support real text orientation) + namespace csst = ::com::sun::star::text; + csst::WritingMode eWriteMode = csst::WritingMode_LR_TB; + switch( maTextData.maData.mnOrient ) + { + default: + case EXC_OBJ_ORIENT_NONE: + { + eWriteMode = csst::WritingMode_LR_TB; + switch( maTextData.maData.GetHorAlign() ) + { + case EXC_OBJ_HOR_LEFT: eHorAlign = SVX_ADJUST_LEFT; break; + case EXC_OBJ_HOR_CENTER: eHorAlign = SVX_ADJUST_CENTER; break; + case EXC_OBJ_HOR_RIGHT: eHorAlign = SVX_ADJUST_RIGHT; break; + case EXC_OBJ_HOR_JUSTIFY: eHorAlign = SVX_ADJUST_BLOCK; break; + } + switch( maTextData.maData.GetVerAlign() ) + { + case EXC_OBJ_VER_TOP: eVerAlign = SDRTEXTVERTADJUST_TOP; break; + case EXC_OBJ_VER_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break; + case EXC_OBJ_VER_BOTTOM: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break; + case EXC_OBJ_VER_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break; + } + } + break; + + case EXC_OBJ_ORIENT_90CCW: + { + if( SdrObjCustomShape* pObjCustomShape = dynamic_cast< SdrObjCustomShape* >( &rSdrObj ) ) + { + double fAngle = 180.0; + com::sun::star::beans::PropertyValue aTextRotateAngle; + aTextRotateAngle.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( "TextRotateAngle" ) ); + aTextRotateAngle.Value <<= fAngle; + SdrCustomShapeGeometryItem aGeometryItem((SdrCustomShapeGeometryItem&)pObjCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY )); + aGeometryItem.SetPropertyValue( aTextRotateAngle ); + pObjCustomShape->SetMergedItem( aGeometryItem ); + } + eWriteMode = csst::WritingMode_TB_RL; + switch( maTextData.maData.GetHorAlign() ) + { + case EXC_OBJ_HOR_LEFT: eVerAlign = SDRTEXTVERTADJUST_TOP; break; + case EXC_OBJ_HOR_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break; + case EXC_OBJ_HOR_RIGHT: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break; + case EXC_OBJ_HOR_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break; + } + MSO_Anchor eTextAnchor = (MSO_Anchor)rDffConv.GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop ); + switch( eTextAnchor ) + { + case mso_anchorTopCentered : + case mso_anchorMiddleCentered : + case mso_anchorBottomCentered : + { + eHorAlign = SVX_ADJUST_CENTER; + } + break; + + default: + { + switch( maTextData.maData.GetVerAlign() ) + { + case EXC_OBJ_VER_TOP: eHorAlign = SVX_ADJUST_RIGHT; break; + case EXC_OBJ_VER_CENTER: eHorAlign = SVX_ADJUST_CENTER; break; + case EXC_OBJ_VER_BOTTOM: eHorAlign = SVX_ADJUST_LEFT; break; + case EXC_OBJ_VER_JUSTIFY: eHorAlign = SVX_ADJUST_BLOCK; break; + } + } + } + } + break; + + case EXC_OBJ_ORIENT_STACKED: // PASSTHROUGH INTENDED + { + // sj: STACKED is not supported, maybe it can be optimized here a bit + } + case EXC_OBJ_ORIENT_90CW: + { + eWriteMode = csst::WritingMode_TB_RL; + switch( maTextData.maData.GetHorAlign() ) + { + case EXC_OBJ_HOR_LEFT: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break; + case EXC_OBJ_HOR_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break; + case EXC_OBJ_HOR_RIGHT: eVerAlign = SDRTEXTVERTADJUST_TOP; break; + case EXC_OBJ_HOR_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break; + } + MSO_Anchor eTextAnchor = (MSO_Anchor)rDffConv.GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop ); + switch ( eTextAnchor ) + { + case mso_anchorTopCentered : + case mso_anchorMiddleCentered : + case mso_anchorBottomCentered : + { + eHorAlign = SVX_ADJUST_CENTER; + } + break; + + default: + { + switch( maTextData.maData.GetVerAlign() ) + { + case EXC_OBJ_VER_TOP: eHorAlign = SVX_ADJUST_LEFT; break; + case EXC_OBJ_VER_CENTER: eHorAlign = SVX_ADJUST_CENTER; break; + case EXC_OBJ_VER_BOTTOM: eHorAlign = SVX_ADJUST_RIGHT; break; + case EXC_OBJ_VER_JUSTIFY: eHorAlign = SVX_ADJUST_BLOCK; break; + } + } + } + } + break; + } + rSdrObj.SetMergedItem( SvxAdjustItem( eHorAlign, EE_PARA_JUST ) ); + rSdrObj.SetMergedItem( SdrTextVertAdjustItem( eVerAlign ) ); + rSdrObj.SetMergedItem( SvxWritingModeItem( eWriteMode, SDRATTR_TEXTDIRECTION ) ); + } + } + // base class processing + XclImpRectObj::DoPreProcessSdrObj( rDffConv, rSdrObj ); +} + +// ---------------------------------------------------------------------------- + +XclImpChartObj::XclImpChartObj( const XclImpRoot& rRoot, bool bOwnTab ) : + XclImpRectObj( rRoot ), + mbOwnTab( bOwnTab ) +{ + SetSimpleMacro( false ); + SetCustomDffObj( true ); +} + +void XclImpChartObj::ReadChartSubStream( XclImpStream& rStrm ) +{ + if( mbOwnTab ? (rStrm.GetRecId() == EXC_ID5_BOF) : ((rStrm.GetNextRecId() == EXC_ID5_BOF) && rStrm.StartNextRecord()) ) + { + sal_uInt16 nBofType; + rStrm.Seek( 2 ); + rStrm >> nBofType; + DBG_ASSERT( nBofType == EXC_BOF_CHART, "XclImpChartObj::ReadChartSubStream - no chart BOF record" ); + + // read chart, even if BOF record contains wrong substream identifier + mxChart.reset( new XclImpChart( GetRoot(), mbOwnTab ) ); + mxChart->ReadChartSubStream( rStrm ); + if( mbOwnTab ) + FinalizeTabChart(); + } + else + { + DBG_ERRORFILE( "XclImpChartObj::ReadChartSubStream - missing chart substream" ); + } +} + +void XclImpChartObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + // read OBJ record and the following chart substream + ReadFrameData( rStrm ); + rStrm.Ignore( 18 ); + ReadMacro3( rStrm, nMacroSize ); +#if 0 + ReadChartSubStream( rStrm ); +#endif + // set frame format from OBJ record, it is used if chart itself is transparent + if( mxChart.is() ) + mxChart->UpdateObjFrame( maLineData, maFillData ); +} + +void XclImpChartObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + // read OBJ record and the following chart substream + ReadFrameData( rStrm ); + rStrm.Ignore( 18 ); + ReadMacro4( rStrm, nMacroSize ); +#if 0 + ReadChartSubStream( rStrm ); +#endif + // set frame format from OBJ record, it is used if chart itself is transparent + if( mxChart.is() ) + mxChart->UpdateObjFrame( maLineData, maFillData ); +} + +void XclImpChartObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + // read OBJ record and the following chart substream + ReadFrameData( rStrm ); + rStrm.Ignore( 18 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); + ReadChartSubStream( rStrm ); + // set frame format from OBJ record, it is used if chart itself is transparent + if( mxChart.is() ) + mxChart->UpdateObjFrame( maLineData, maFillData ); +} + +void XclImpChartObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 /*nSubRecSize*/ ) +{ + // read the following chart substream + if( nSubRecId == EXC_ID_OBJEND ) + { + // enable CONTINUE handling for the entire chart substream + rStrm.ResetRecord( true ); + ReadChartSubStream( rStrm ); + /* #90118# disable CONTINUE handling again to be able to read + following CONTINUE records as MSODRAWING records. */ + rStrm.ResetRecord( false ); + } +} + +sal_Size XclImpChartObj::DoGetProgressSize() const +{ + return mxChart.is() ? mxChart->GetProgressSize() : 1; +} + +SdrObject* XclImpChartObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const Rectangle& rAnchorRect ) const +{ + SdrObjectPtr xSdrObj; + SfxObjectShell* pDocShell = GetDocShell(); + if( rDffConv.SupportsOleObjects() && SvtModuleOptions().IsChart() && pDocShell && mxChart.is() && !mxChart->IsPivotChart() ) + { + // create embedded chart object + OUString aEmbObjName; + Reference< XEmbeddedObject > xEmbObj = pDocShell->GetEmbeddedObjectContainer(). + CreateEmbeddedObject( SvGlobalName( SO3_SCH_CLASSID ).GetByteSequence(), aEmbObjName ); + + /* Set the size to the embedded object, this prevents that font sizes + of text objects are changed in the chart when the object is + inserted into the draw page. */ + sal_Int64 nAspect = ::com::sun::star::embed::Aspects::MSOLE_CONTENT; + MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xEmbObj->getMapUnit( nAspect ) ); + Size aSize( Window::LogicToLogic( rAnchorRect.GetSize(), MapMode( MAP_100TH_MM ), MapMode( aUnit ) ) ); + ::com::sun::star::awt::Size aAwtSize( aSize.Width(), aSize.Height() ); + xEmbObj->setVisualAreaSize( nAspect, aAwtSize ); + + // create the container OLE object + xSdrObj.reset( new SdrOle2Obj( svt::EmbeddedObjectRef( xEmbObj, nAspect ), aEmbObjName, rAnchorRect ) ); + } + + return xSdrObj.release(); +} + +void XclImpChartObj::DoPostProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const +{ + const SdrOle2Obj* pSdrOleObj = dynamic_cast< const SdrOle2Obj* >( &rSdrObj ); + if( mxChart.is() && pSdrOleObj ) + { + Reference< XEmbeddedObject > xEmbObj = pSdrOleObj->GetObjRef(); + if( xEmbObj.is() && ::svt::EmbeddedObjectRef::TryRunningState( xEmbObj ) ) try + { + Reference< XEmbedPersist > xPersist( xEmbObj, UNO_QUERY_THROW ); + Reference< XModel > xModel( xEmbObj->getComponent(), UNO_QUERY_THROW ); + mxChart->Convert( xModel, rDffConv, xPersist->getEntryName(), rSdrObj.GetLogicRect() ); + xPersist->storeOwn(); + } + catch( Exception& ) + { + } + } +} + +void XclImpChartObj::FinalizeTabChart() +{ + /* #i44077# Calculate and store DFF anchor for sheet charts. + Needed to get used area if this chart is inserted as OLE object. */ + DBG_ASSERT( mbOwnTab, "XclImpChartObj::FinalizeTabChart - not allowed for embedded chart objects" ); + + // set uninitialized page to landscape + if( !GetPageSettings().GetPageData().mbValid ) + GetPageSettings().SetPaperSize( EXC_PAPERSIZE_DEFAULT, false ); + + // calculate size of the chart object + const XclPageData& rPageData = GetPageSettings().GetPageData(); + Size aPaperSize = rPageData.GetScPaperSize(); + + long nWidth = XclTools::GetHmmFromTwips( aPaperSize.Width() ); + long nHeight = XclTools::GetHmmFromTwips( aPaperSize.Height() ); + + // subtract page margins, give some more extra space + nWidth -= (XclTools::GetHmmFromInch( rPageData.mfLeftMargin + rPageData.mfRightMargin ) + 2000); + nHeight -= (XclTools::GetHmmFromInch( rPageData.mfTopMargin + rPageData.mfBottomMargin ) + 1000); + + // print column/row headers? + if( rPageData.mbPrintHeadings ) + { + nWidth -= 2000; + nHeight -= 1000; + } + + // create the object anchor + XclObjAnchor aAnchor; + aAnchor.SetRect( GetRoot(), GetCurrScTab(), Rectangle( 1000, 500, nWidth, nHeight ), MAP_100TH_MM ); + SetAnchor( aAnchor ); +} + +// ---------------------------------------------------------------------------- + +XclImpNoteObj::XclImpNoteObj( const XclImpRoot& rRoot ) : + XclImpTextObj( rRoot ), + maScPos( ScAddress::INITIALIZE_INVALID ), + mnNoteFlags( 0 ) +{ + SetSimpleMacro( false ); + // caption object will be created manually + SetInsertSdrObj( false ); +} + +void XclImpNoteObj::SetNoteData( const ScAddress& rScPos, sal_uInt16 nNoteFlags ) +{ + maScPos = rScPos; + mnNoteFlags = nNoteFlags; +} + +void XclImpNoteObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const +{ + // create formatted text + XclImpTextObj::DoPreProcessSdrObj( rDffConv, rSdrObj ); + OutlinerParaObject* pOutlinerObj = rSdrObj.GetOutlinerParaObject(); + if( maScPos.IsValid() && pOutlinerObj ) + { + // create cell note with all data from drawing object + ScNoteUtil::CreateNoteFromObjectData( + GetDoc(), maScPos, + rSdrObj.GetMergedItemSet().Clone(), // new object on heap expected + new OutlinerParaObject( *pOutlinerObj ), // new object on heap expected + rSdrObj.GetLogicRect(), + ::get_flag( mnNoteFlags, EXC_NOTE_VISIBLE ), + false ); + } +} + +// ---------------------------------------------------------------------------- + +XclImpControlHelper::XclImpControlHelper( const XclImpRoot& rRoot, XclCtrlBindMode eBindMode ) : + mrRoot( rRoot ), + meBindMode( eBindMode ) +{ +} + +XclImpControlHelper::~XclImpControlHelper() +{ +} + +SdrObject* XclImpControlHelper::CreateSdrObjectFromShape( + const Reference< XShape >& rxShape, const Rectangle& rAnchorRect ) const +{ + mxShape = rxShape; + SdrObjectPtr xSdrObj( SdrObject::getSdrObjectFromXShape( rxShape ) ); + if( xSdrObj.is() ) + { + xSdrObj->NbcSetSnapRect( rAnchorRect ); + // #i30543# insert into control layer + xSdrObj->NbcSetLayer( SC_LAYER_CONTROLS ); + } + return xSdrObj.release(); +} + +void XclImpControlHelper::ProcessControl( const XclImpDrawObjBase& rDrawObj ) const +{ + Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( mxShape ); + if( !xCtrlModel.is() ) + return; + + ScfPropertySet aPropSet( xCtrlModel ); + + // #118053# #i51348# set object name at control model + aPropSet.SetStringProperty( CREATE_OUSTRING( "Name" ), rDrawObj.GetObjName() ); + + // control visible and printable? + aPropSet.SetBoolProperty( CREATE_OUSTRING( "EnableVisible" ), rDrawObj.IsVisible() ); + aPropSet.SetBoolProperty( CREATE_OUSTRING( "Printable" ), rDrawObj.IsPrintable() ); + + // sheet links + if( SfxObjectShell* pDocShell = mrRoot.GetDocShell() ) + { + Reference< XMultiServiceFactory > xFactory( pDocShell->GetModel(), UNO_QUERY ); + if( xFactory.is() ) + { + // cell link + if( mxCellLink.is() ) try + { + Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY_THROW ); + + // create argument sequence for createInstanceWithArguments() + CellAddress aApiAddress; + ScUnoConversion::FillApiAddress( aApiAddress, *mxCellLink ); + + NamedValue aValue; + aValue.Name = CREATE_OUSTRING( SC_UNONAME_BOUNDCELL ); + aValue.Value <<= aApiAddress; + + Sequence< Any > aArgs( 1 ); + aArgs[ 0 ] <<= aValue; + + // create the CellValueBinding instance and set at the control model + OUString aServiceName; + switch( meBindMode ) + { + case EXC_CTRL_BINDCONTENT: aServiceName = CREATE_OUSTRING( SC_SERVICENAME_VALBIND ); break; + case EXC_CTRL_BINDPOSITION: aServiceName = CREATE_OUSTRING( SC_SERVICENAME_LISTCELLBIND ); break; + } + Reference< XValueBinding > xBinding( + xFactory->createInstanceWithArguments( aServiceName, aArgs ), UNO_QUERY_THROW ); + xBindable->setValueBinding( xBinding ); + } + catch( const Exception& ) + { + } + + // source range + if( mxSrcRange.is() ) try + { + Reference< XListEntrySink > xEntrySink( xCtrlModel, UNO_QUERY_THROW ); + + // create argument sequence for createInstanceWithArguments() + CellRangeAddress aApiRange; + ScUnoConversion::FillApiRange( aApiRange, *mxSrcRange ); + + NamedValue aValue; + aValue.Name = CREATE_OUSTRING( SC_UNONAME_CELLRANGE ); + aValue.Value <<= aApiRange; + + Sequence< Any > aArgs( 1 ); + aArgs[ 0 ] <<= aValue; + + // create the EntrySource instance and set at the control model + Reference< XListEntrySource > xEntrySource( xFactory->createInstanceWithArguments( + CREATE_OUSTRING( SC_SERVICENAME_LISTSOURCE ), aArgs ), UNO_QUERY_THROW ); + xEntrySink->setListEntrySource( xEntrySource ); + } + catch( const Exception& ) + { + } + } + } + + // virtual call for type specific processing + DoProcessControl( aPropSet ); +} + +void XclImpControlHelper::ReadCellLinkFormula( XclImpStream& rStrm, bool bWithBoundSize ) +{ + ScRangeList aScRanges; + ReadRangeList( aScRanges, rStrm, bWithBoundSize ); + // Use first cell of first range + if( const ScRange* pScRange = aScRanges.GetObject( 0 ) ) + mxCellLink.reset( new ScAddress( pScRange->aStart ) ); +} + +void XclImpControlHelper::ReadSourceRangeFormula( XclImpStream& rStrm, bool bWithBoundSize ) +{ + ScRangeList aScRanges; + ReadRangeList( aScRanges, rStrm, bWithBoundSize ); + // Use first range + if( const ScRange* pScRange = aScRanges.GetObject( 0 ) ) + mxSrcRange.reset( new ScRange( *pScRange ) ); +} + +void XclImpControlHelper::DoProcessControl( ScfPropertySet& ) const +{ +} + +void XclImpControlHelper::ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm ) +{ + XclTokenArray aXclTokArr; + aXclTokArr.ReadSize( rStrm ); + rStrm.Ignore( 4 ); + aXclTokArr.ReadArray( rStrm ); + mrRoot.GetFormulaCompiler().CreateRangeList( rScRanges, EXC_FMLATYPE_CONTROL, aXclTokArr, rStrm ); +} + +void XclImpControlHelper::ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm, bool bWithBoundSize ) +{ + if( bWithBoundSize ) + { + sal_uInt16 nSize; + rStrm >> nSize; + if( nSize > 0 ) + { + rStrm.PushPosition(); + ReadRangeList( rScRanges, rStrm ); + rStrm.PopPosition(); + rStrm.Ignore( nSize ); + } + } + else + { + ReadRangeList( rScRanges, rStrm ); + } +} + +// ---------------------------------------------------------------------------- + +XclImpTbxObjBase::XclImpTbxObjBase( const XclImpRoot& rRoot ) : + XclImpTextObj( rRoot ), + XclImpControlHelper( rRoot, EXC_CTRL_BINDPOSITION ) +{ + SetSimpleMacro( false ); + SetCustomDffObj( true ); +} + +namespace { + +void lclExtractColor( sal_uInt8& rnColorIdx, const DffPropSet& rDffPropSet, sal_uInt32 nPropId ) +{ + if( rDffPropSet.IsProperty( nPropId ) ) + { + sal_uInt32 nColor = rDffPropSet.GetPropertyValue( nPropId ); + if( (nColor & 0xFF000000) == 0x08000000 ) + rnColorIdx = ::extract_value< sal_uInt8 >( nColor, 0, 8 ); + } +} + +} // namespace + +void XclImpTbxObjBase::SetDffProperties( const DffPropSet& rDffPropSet ) +{ + maFillData.mnPattern = rDffPropSet.GetPropertyBool( DFF_Prop_fFilled ) ? EXC_PATT_SOLID : EXC_PATT_NONE; + lclExtractColor( maFillData.mnBackColorIdx, rDffPropSet, DFF_Prop_fillBackColor ); + lclExtractColor( maFillData.mnPattColorIdx, rDffPropSet, DFF_Prop_fillColor ); + ::set_flag( maFillData.mnAuto, EXC_OBJ_LINE_AUTO, false ); + + maLineData.mnStyle = rDffPropSet.GetPropertyBool( DFF_Prop_fLine ) ? EXC_OBJ_LINE_SOLID : EXC_OBJ_LINE_NONE; + lclExtractColor( maLineData.mnColorIdx, rDffPropSet, DFF_Prop_lineColor ); + ::set_flag( maLineData.mnAuto, EXC_OBJ_FILL_AUTO, false ); +} + +bool XclImpTbxObjBase::FillMacroDescriptor( ScriptEventDescriptor& rDescriptor ) const +{ + return XclControlHelper::FillMacroDescriptor( rDescriptor, DoGetEventType(), GetMacroName(), GetDocShell() ); +} + +void XclImpTbxObjBase::ConvertFont( ScfPropertySet& rPropSet ) const +{ + if( maTextData.mxString.is() ) + { + const XclFormatRunVec& rFormatRuns = maTextData.mxString->GetFormats(); + if( rFormatRuns.empty() ) + GetFontBuffer().WriteDefaultCtrlFontProperties( rPropSet ); + else + GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL, rFormatRuns.front().mnFontIdx ); + } +} + +void XclImpTbxObjBase::ConvertLabel( ScfPropertySet& rPropSet ) const +{ + if( maTextData.mxString.is() ) + { + String aLabel = maTextData.mxString->GetText(); + if( maTextData.maData.mnShortcut > 0 ) + { + xub_StrLen nPos = aLabel.Search( static_cast< sal_Unicode >( maTextData.maData.mnShortcut ) ); + if( nPos != STRING_NOTFOUND ) + aLabel.Insert( '~', nPos ); + } + rPropSet.SetStringProperty( CREATE_OUSTRING( "Label" ), aLabel ); + } + ConvertFont( rPropSet ); +} + +SdrObject* XclImpTbxObjBase::DoCreateSdrObj( XclImpDffConverter& rDffConv, const Rectangle& rAnchorRect ) const +{ + SdrObjectPtr xSdrObj( rDffConv.CreateSdrObject( *this, rAnchorRect ) ); + rDffConv.Progress(); + return xSdrObj.release(); +} + +void XclImpTbxObjBase::DoPreProcessSdrObj( XclImpDffConverter& /*rDffConv*/, SdrObject& /*rSdrObj*/ ) const +{ + // do not call DoPreProcessSdrObj() from base class (to skip text processing) + ProcessControl( *this ); +} + +// ---------------------------------------------------------------------------- + +XclImpButtonObj::XclImpButtonObj( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ) +{ +} + +void XclImpButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // label and text formatting + ConvertLabel( rPropSet ); + + /* Horizontal text alignment. For unknown reason, the property type is a + simple sal_Int16 and not a com.sun.star.style.HorizontalAlignment. */ + sal_Int16 nHorAlign = 1; + switch( maTextData.maData.GetHorAlign() ) + { + case EXC_OBJ_HOR_LEFT: nHorAlign = 0; break; + case EXC_OBJ_HOR_CENTER: nHorAlign = 1; break; + case EXC_OBJ_HOR_RIGHT: nHorAlign = 2; break; + } + rPropSet.SetProperty( CREATE_OUSTRING( "Align" ), nHorAlign ); + + // vertical text alignment + namespace csss = ::com::sun::star::style; + csss::VerticalAlignment eVerAlign = csss::VerticalAlignment_MIDDLE; + switch( maTextData.maData.GetVerAlign() ) + { + case EXC_OBJ_VER_TOP: eVerAlign = csss::VerticalAlignment_TOP; break; + case EXC_OBJ_VER_CENTER: eVerAlign = csss::VerticalAlignment_MIDDLE; break; + case EXC_OBJ_VER_BOTTOM: eVerAlign = csss::VerticalAlignment_BOTTOM; break; + } + rPropSet.SetProperty( CREATE_OUSTRING( "VerticalAlign" ), eVerAlign ); + + // always wrap text automatically + rPropSet.SetBoolProperty( CREATE_OUSTRING( "MultiLine" ), true ); + + // default button + bool bDefButton = ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_DEFAULT ); + rPropSet.SetBoolProperty( CREATE_OUSTRING( "DefaultButton" ), bDefButton ); + + // button type (flags cannot be combined in OOo) + namespace cssa = ::com::sun::star::awt; + cssa::PushButtonType eButtonType = cssa::PushButtonType_STANDARD; + if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_CLOSE ) ) + eButtonType = cssa::PushButtonType_OK; + else if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_CANCEL ) ) + eButtonType = cssa::PushButtonType_CANCEL; + else if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_HELP ) ) + eButtonType = cssa::PushButtonType_HELP; + // property type is short, not enum + rPropSet.SetProperty( CREATE_OUSTRING( "PushButtonType" ), sal_Int16( eButtonType ) ); +} + +OUString XclImpButtonObj::DoGetServiceName() const +{ + return CREATE_OUSTRING( "com.sun.star.form.component.CommandButton" ); +} + +XclTbxEventType XclImpButtonObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_ACTION; +} + +// ---------------------------------------------------------------------------- + +XclImpCheckBoxObj::XclImpCheckBoxObj( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ), + mnState( EXC_OBJ_CHECKBOX_UNCHECKED ), + mnCheckBoxFlags( 0 ) +{ +} + +void XclImpCheckBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + rStrm.Ignore( 10 ); + rStrm >> maTextData.maData.mnFlags; + rStrm.Ignore( 20 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // fist macro size invalid and unused + ReadCellLinkFormula( rStrm, true ); + rStrm >> maTextData.maData.mnTextLen; + maTextData.ReadByteString( rStrm ); + rStrm >> mnState >> maTextData.maData.mnShortcut >> maTextData.maData.mnShortcutEA >> mnCheckBoxFlags; +} + +void XclImpCheckBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJCBLS: + // do not read EXC_ID_OBJCBLSDATA, not written by OOo Excel export + rStrm >> mnState; + rStrm.Ignore( 4 ); + rStrm >> maTextData.maData.mnShortcut >> maTextData.maData.mnShortcutEA >> mnCheckBoxFlags; + break; + case EXC_ID_OBJCBLSFMLA: + ReadCellLinkFormula( rStrm, false ); + break; + default: + XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +void XclImpCheckBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // label and text formatting + ConvertLabel( rPropSet ); + + // state + bool bSupportsTristate = GetObjType() == EXC_OBJTYPE_CHECKBOX; + sal_Int16 nApiState = 0; + switch( mnState ) + { + case EXC_OBJ_CHECKBOX_UNCHECKED: nApiState = 0; break; + case EXC_OBJ_CHECKBOX_CHECKED: nApiState = 1; break; + case EXC_OBJ_CHECKBOX_TRISTATE: nApiState = bSupportsTristate ? 2 : 1; break; + } + if( bSupportsTristate ) + rPropSet.SetBoolProperty( CREATE_OUSTRING( "TriState" ), nApiState == 2 ); + rPropSet.SetProperty( CREATE_OUSTRING( "DefaultState" ), nApiState ); + + // box style + namespace AwtVisualEffect = ::com::sun::star::awt::VisualEffect; + sal_Int16 nEffect = ::get_flagvalue( mnCheckBoxFlags, EXC_OBJ_CHECKBOX_FLAT, AwtVisualEffect::FLAT, AwtVisualEffect::LOOK3D ); + rPropSet.SetProperty( CREATE_OUSTRING( "VisualEffect" ), nEffect ); + + // do not wrap text automatically + rPropSet.SetBoolProperty( CREATE_OUSTRING( "MultiLine" ), false ); + + // #i40279# always centered vertically + namespace csss = ::com::sun::star::style; + rPropSet.SetProperty( CREATE_OUSTRING( "VerticalAlign" ), csss::VerticalAlignment_MIDDLE ); + + // background color + if( maFillData.IsFilled() ) + { + sal_Int32 nColor = static_cast< sal_Int32 >( GetSolidFillColor( maFillData ).GetColor() ); + rPropSet.SetProperty( CREATE_OUSTRING( "BackgroundColor" ), nColor ); + } +} + +OUString XclImpCheckBoxObj::DoGetServiceName() const +{ + return CREATE_OUSTRING( "com.sun.star.form.component.CheckBox" ); +} + +XclTbxEventType XclImpCheckBoxObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_ACTION; +} + +// ---------------------------------------------------------------------------- + +XclImpOptionButtonObj::XclImpOptionButtonObj( const XclImpRoot& rRoot ) : + XclImpCheckBoxObj( rRoot ), + mnNextInGroup( 0 ), + mnFirstInGroup( 1 ) +{ +} + +void XclImpOptionButtonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + rStrm.Ignore( 10 ); + rStrm >> maTextData.maData.mnFlags; + rStrm.Ignore( 32 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // fist macro size invalid and unused + ReadCellLinkFormula( rStrm, true ); + rStrm >> maTextData.maData.mnTextLen; + maTextData.ReadByteString( rStrm ); + rStrm >> mnState >> maTextData.maData.mnShortcut >> maTextData.maData.mnShortcutEA; + rStrm >> mnCheckBoxFlags >> mnNextInGroup >> mnFirstInGroup; +} + +void XclImpOptionButtonObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJRBODATA: + rStrm >> mnNextInGroup >> mnFirstInGroup; + break; + default: + XclImpCheckBoxObj::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +void XclImpOptionButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + XclImpCheckBoxObj::DoProcessControl( rPropSet ); + // TODO: grouping +} + +OUString XclImpOptionButtonObj::DoGetServiceName() const +{ + return CREATE_OUSTRING( "com.sun.star.form.component.RadioButton" ); +} + +XclTbxEventType XclImpOptionButtonObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_ACTION; +} + +// ---------------------------------------------------------------------------- + +XclImpLabelObj::XclImpLabelObj( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ) +{ +} + +void XclImpLabelObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // label and text formatting + ConvertLabel( rPropSet ); + + // text alignment (always top/left aligned) + rPropSet.SetProperty( CREATE_OUSTRING( "Align" ), sal_Int16( 0 ) ); + namespace csss = ::com::sun::star::style; + rPropSet.SetProperty( CREATE_OUSTRING( "VerticalAlign" ), csss::VerticalAlignment_TOP ); + + // always wrap text automatically + rPropSet.SetBoolProperty( CREATE_OUSTRING( "MultiLine" ), true ); +} + +OUString XclImpLabelObj::DoGetServiceName() const +{ + return CREATE_OUSTRING( "com.sun.star.form.component.FixedText" ); +} + +XclTbxEventType XclImpLabelObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_MOUSE; +} + +// ---------------------------------------------------------------------------- + +XclImpGroupBoxObj::XclImpGroupBoxObj( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ), + mnGroupBoxFlags( 0 ) +{ +} + +void XclImpGroupBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + rStrm.Ignore( 10 ); + rStrm >> maTextData.maData.mnFlags; + rStrm.Ignore( 26 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // fist macro size invalid and unused + rStrm >> maTextData.maData.mnTextLen; + maTextData.ReadByteString( rStrm ); + rStrm >> maTextData.maData.mnShortcut >> maTextData.maData.mnShortcutEA >> mnGroupBoxFlags; +} + +void XclImpGroupBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJGBODATA: + rStrm >> maTextData.maData.mnShortcut >> maTextData.maData.mnShortcutEA >> mnGroupBoxFlags; + break; + default: + XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +void XclImpGroupBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // label and text formatting + ConvertLabel( rPropSet ); +} + +OUString XclImpGroupBoxObj::DoGetServiceName() const +{ + return CREATE_OUSTRING( "com.sun.star.form.component.GroupBox" ); +} + +XclTbxEventType XclImpGroupBoxObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_MOUSE; +} + +// ---------------------------------------------------------------------------- + +XclImpDialogObj::XclImpDialogObj( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ) +{ +} + +void XclImpDialogObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // label and text formatting + ConvertLabel( rPropSet ); +} + +OUString XclImpDialogObj::DoGetServiceName() const +{ + // dialog frame faked by a groupbox + return CREATE_OUSTRING( "com.sun.star.form.component.GroupBox" ); +} + +XclTbxEventType XclImpDialogObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_MOUSE; +} + +// ---------------------------------------------------------------------------- + +XclImpEditObj::XclImpEditObj( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ), + mnContentType( EXC_OBJ_EDIT_TEXT ), + mnMultiLine( 0 ), + mnScrollBar( 0 ), + mnListBoxObjId( 0 ) +{ +} + +bool XclImpEditObj::IsNumeric() const +{ + return (mnContentType == EXC_OBJ_EDIT_INTEGER) || (mnContentType == EXC_OBJ_EDIT_DOUBLE); +} + +void XclImpEditObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + rStrm.Ignore( 10 ); + rStrm >> maTextData.maData.mnFlags; + rStrm.Ignore( 14 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // fist macro size invalid and unused + rStrm >> maTextData.maData.mnTextLen; + maTextData.ReadByteString( rStrm ); + rStrm >> mnContentType >> mnMultiLine >> mnScrollBar >> mnListBoxObjId; +} + +void XclImpEditObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJEDODATA: + rStrm >> mnContentType >> mnMultiLine >> mnScrollBar >> mnListBoxObjId; + break; + default: + XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +void XclImpEditObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + if( maTextData.mxString.is() ) + { + OUString aText = maTextData.mxString->GetText(); + if( IsNumeric() ) + { + // TODO: OUString::toDouble() does not handle local decimal separator + rPropSet.SetProperty( CREATE_OUSTRING( "DefaultValue" ), aText.toDouble() ); + rPropSet.SetBoolProperty( CREATE_OUSTRING( "Spin" ), mnScrollBar != 0 ); + } + else + { + rPropSet.SetProperty( CREATE_OUSTRING( "DefaultText" ), aText ); + rPropSet.SetBoolProperty( CREATE_OUSTRING( "MultiLine" ), mnMultiLine != 0 ); + rPropSet.SetBoolProperty( CREATE_OUSTRING( "VScroll" ), mnScrollBar != 0 ); + } + } + ConvertFont( rPropSet ); +} + +OUString XclImpEditObj::DoGetServiceName() const +{ + return IsNumeric() ? + CREATE_OUSTRING( "com.sun.star.form.component.NumericField" ) : + CREATE_OUSTRING( "com.sun.star.form.component.TextField" ); +} + +XclTbxEventType XclImpEditObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_TEXT; +} + +// ---------------------------------------------------------------------------- + +XclImpTbxObjScrollableBase::XclImpTbxObjScrollableBase( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ), + mnValue( 0 ), + mnMin( 0 ), + mnMax( 100 ), + mnStep( 1 ), + mnPageStep( 10 ), + mnOrient( 0 ), + mnThumbWidth( 1 ), + mnScrollFlags( 0 ) +{ +} + +void XclImpTbxObjScrollableBase::ReadSbs( XclImpStream& rStrm ) +{ + rStrm.Ignore( 4 ); + rStrm >> mnValue >> mnMin >> mnMax >> mnStep >> mnPageStep >> mnOrient >> mnThumbWidth >> mnScrollFlags; +} + +void XclImpTbxObjScrollableBase::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJSBS: + ReadSbs( rStrm ); + break; + case EXC_ID_OBJSBSFMLA: + ReadCellLinkFormula( rStrm, false ); + break; + default: + XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +// ---------------------------------------------------------------------------- + +XclImpSpinButtonObj::XclImpSpinButtonObj( const XclImpRoot& rRoot ) : + XclImpTbxObjScrollableBase( rRoot ) +{ +} + +void XclImpSpinButtonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + ReadSbs( rStrm ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // fist macro size invalid and unused + ReadCellLinkFormula( rStrm, true ); +} + +void XclImpSpinButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // Calc's "Border" property is not the 3D/flat style effect in Excel (#i34712#) + rPropSet.SetProperty( CREATE_OUSTRING( "Border" ), ::com::sun::star::awt::VisualEffect::NONE ); + rPropSet.SetProperty< sal_Int32 >( CREATE_OUSTRING( "DefaultSpinValue" ), mnValue ); + rPropSet.SetProperty< sal_Int32 >( CREATE_OUSTRING( "SpinValueMin" ), mnMin ); + rPropSet.SetProperty< sal_Int32 >( CREATE_OUSTRING( "SpinValueMax" ), mnMax ); + rPropSet.SetProperty< sal_Int32 >( CREATE_OUSTRING( "SpinIncrement" ), mnStep ); + + // Excel spin buttons always vertical + rPropSet.SetProperty( CREATE_OUSTRING( "Orientation" ), ::com::sun::star::awt::ScrollBarOrientation::VERTICAL ); +} + +OUString XclImpSpinButtonObj::DoGetServiceName() const +{ + return CREATE_OUSTRING( "com.sun.star.form.component.SpinButton" ); +} + +XclTbxEventType XclImpSpinButtonObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_VALUE; +} + +// ---------------------------------------------------------------------------- + +XclImpScrollBarObj::XclImpScrollBarObj( const XclImpRoot& rRoot ) : + XclImpTbxObjScrollableBase( rRoot ) +{ +} + +void XclImpScrollBarObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + ReadSbs( rStrm ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // fist macro size invalid and unused + ReadCellLinkFormula( rStrm, true ); +} + +void XclImpScrollBarObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // Calc's "Border" property is not the 3D/flat style effect in Excel (#i34712#) + rPropSet.SetProperty( CREATE_OUSTRING( "Border" ), ::com::sun::star::awt::VisualEffect::NONE ); + rPropSet.SetProperty< sal_Int32 >( CREATE_OUSTRING( "DefaultScrollValue" ), mnValue ); + rPropSet.SetProperty< sal_Int32 >( CREATE_OUSTRING( "ScrollValueMin" ), mnMin ); + rPropSet.SetProperty< sal_Int32 >( CREATE_OUSTRING( "ScrollValueMax" ), mnMax ); + rPropSet.SetProperty< sal_Int32 >( CREATE_OUSTRING( "LineIncrement" ), mnStep ); + rPropSet.SetProperty< sal_Int32 >( CREATE_OUSTRING( "BlockIncrement" ), mnPageStep ); + rPropSet.SetProperty( CREATE_OUSTRING( "VisibleSize" ), ::std::min< sal_Int32 >( mnPageStep, 1 ) ); + + namespace AwtScrollOrient = ::com::sun::star::awt::ScrollBarOrientation; + sal_Int32 nApiOrient = ::get_flagvalue( mnOrient, EXC_OBJ_SCROLLBAR_HOR, AwtScrollOrient::HORIZONTAL, AwtScrollOrient::VERTICAL ); + rPropSet.SetProperty( CREATE_OUSTRING( "Orientation" ), nApiOrient ); +} + +OUString XclImpScrollBarObj::DoGetServiceName() const +{ + return CREATE_OUSTRING( "com.sun.star.form.component.ScrollBar" ); +} + +XclTbxEventType XclImpScrollBarObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_VALUE; +} + +// ---------------------------------------------------------------------------- + +XclImpTbxObjListBase::XclImpTbxObjListBase( const XclImpRoot& rRoot ) : + XclImpTbxObjScrollableBase( rRoot ), + mnEntryCount( 0 ), + mnSelEntry( 0 ), + mnListFlags( 0 ), + mnEditObjId( 0 ), + mbHasDefFontIdx( false ) +{ +} + +void XclImpTbxObjListBase::ReadLbsData( XclImpStream& rStrm ) +{ + ReadSourceRangeFormula( rStrm, true ); + rStrm >> mnEntryCount >> mnSelEntry >> mnListFlags >> mnEditObjId; +} + +void XclImpTbxObjListBase::SetBoxFormatting( ScfPropertySet& rPropSet ) const +{ + // border style + namespace AwtVisualEffect = ::com::sun::star::awt::VisualEffect; + sal_Int16 nApiBorder = ::get_flagvalue( mnListFlags, EXC_OBJ_LISTBOX_FLAT, AwtVisualEffect::FLAT, AwtVisualEffect::LOOK3D ); + rPropSet.SetProperty( CREATE_OUSTRING( "Border" ), nApiBorder ); + + // font formatting + if( mbHasDefFontIdx ) + GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL, maTextData.maData.mnDefFontIdx ); + else + GetFontBuffer().WriteDefaultCtrlFontProperties( rPropSet ); +} + +// ---------------------------------------------------------------------------- + +XclImpListBoxObj::XclImpListBoxObj( const XclImpRoot& rRoot ) : + XclImpTbxObjListBase( rRoot ) +{ +} + +void XclImpListBoxObj::ReadFullLbsData( XclImpStream& rStrm, sal_Size nRecLeft ) +{ + sal_Size nRecEnd = rStrm.GetRecPos() + nRecLeft; + ReadLbsData( rStrm ); + DBG_ASSERT( (rStrm.GetRecPos() == nRecEnd) || (rStrm.GetRecPos() + mnEntryCount == nRecEnd), + "XclImpListBoxObj::ReadFullLbsData - invalid size of OBJLBSDATA record" ); + while( rStrm.IsValid() && (rStrm.GetRecPos() < nRecEnd) ) + maSelection.push_back( rStrm.ReaduInt8() ); +} + +void XclImpListBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + ReadSbs( rStrm ); + rStrm.Ignore( 18 ); + rStrm >> maTextData.maData.mnDefFontIdx; + rStrm.Ignore( 4 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // fist macro size invalid and unused + ReadCellLinkFormula( rStrm, true ); + ReadFullLbsData( rStrm, rStrm.GetRecLeft() ); + mbHasDefFontIdx = true; +} + +void XclImpListBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJLBSDATA: + ReadFullLbsData( rStrm, nSubRecSize ); + break; + default: + XclImpTbxObjListBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +void XclImpListBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // listbox formatting + SetBoxFormatting( rPropSet ); + + // selection type + sal_uInt8 nSelType = ::extract_value< sal_uInt8 >( mnListFlags, 4, 2 ); + bool bMultiSel = nSelType != EXC_OBJ_LISTBOX_SINGLE; + rPropSet.SetBoolProperty( CREATE_OUSTRING( "MultiSelection" ), bMultiSel ); + + // selection (do not set, if listbox is linked to a cell) + if( !HasCellLink() ) + { + ScfInt16Vec aSelVec; + + // multi selection: API expects sequence of list entry indexes + if( bMultiSel ) + for( ScfUInt8Vec::const_iterator aBeg = maSelection.begin(), aIt = aBeg, aEnd = maSelection.end(); aIt != aEnd; ++aIt ) + if( *aIt != 0 ) + aSelVec.push_back( static_cast< sal_Int16 >( aIt - aBeg ) ); + // single selection: mnSelEntry is one-based, API expects zero-based + else if( mnSelEntry > 0 ) + aSelVec.push_back( static_cast< sal_Int16 >( mnSelEntry - 1 ) ); + + if( !aSelVec.empty() ) + { + Sequence< sal_Int16 > aSelSeq( &aSelVec.front(), static_cast< sal_Int32 >( aSelVec.size() ) ); + rPropSet.SetProperty( CREATE_OUSTRING( "DefaultSelection" ), aSelSeq ); + } + } +} + +OUString XclImpListBoxObj::DoGetServiceName() const +{ + return CREATE_OUSTRING( "com.sun.star.form.component.ListBox" ); +} + +XclTbxEventType XclImpListBoxObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_CHANGE; +} + +// ---------------------------------------------------------------------------- + +XclImpDropDownObj::XclImpDropDownObj( const XclImpRoot& rRoot ) : + XclImpTbxObjListBase( rRoot ), + mnLeft( 0 ), + mnTop( 0 ), + mnRight( 0 ), + mnBottom( 0 ), + mnDropDownFlags( 0 ), + mnLineCount( 0 ), + mnMinWidth( 0 ) +{ +} + +sal_uInt16 XclImpDropDownObj::GetDropDownType() const +{ + return ::extract_value< sal_uInt8 >( mnDropDownFlags, 0, 2 ); +} + +void XclImpDropDownObj::ReadFullLbsData( XclImpStream& rStrm ) +{ + ReadLbsData( rStrm ); + rStrm >> mnDropDownFlags >> mnLineCount >> mnMinWidth >> maTextData.maData.mnTextLen; + maTextData.ReadByteString( rStrm ); + // dropdowns of auto-filters have 'simple' style, they don't have a text area + if( GetDropDownType() == EXC_OBJ_DROPDOWN_SIMPLE ) + SetProcessSdrObj( false ); +} + +void XclImpDropDownObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + ReadSbs( rStrm ); + rStrm.Ignore( 18 ); + rStrm >> maTextData.maData.mnDefFontIdx; + rStrm.Ignore( 14 ); + rStrm >> mnLeft >> mnTop >> mnRight >> mnBottom; + rStrm.Ignore( 4 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // fist macro size invalid and unused + ReadCellLinkFormula( rStrm, true ); + ReadFullLbsData( rStrm ); + mbHasDefFontIdx = true; +} + +void XclImpDropDownObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJLBSDATA: + ReadFullLbsData( rStrm ); + break; + default: + XclImpTbxObjListBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +void XclImpDropDownObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // dropdown listbox formatting + SetBoxFormatting( rPropSet ); + // enable dropdown button + rPropSet.SetBoolProperty( CREATE_OUSTRING( "Dropdown" ), true ); + // dropdown line count + rPropSet.SetProperty( CREATE_OUSTRING( "LineCount" ), mnLineCount ); + + if( GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX ) + { + // text of editable combobox + if( maTextData.mxString.is() ) + rPropSet.SetStringProperty( CREATE_OUSTRING( "DefaultText" ), maTextData.mxString->GetText() ); + } + else + { + // selection (do not set, if dropdown is linked to a cell) + if( !HasCellLink() && (mnSelEntry > 0) ) + { + Sequence< sal_Int16 > aSelSeq( 1 ); + aSelSeq[ 0 ] = mnSelEntry - 1; + rPropSet.SetProperty( CREATE_OUSTRING( "DefaultSelection" ), aSelSeq ); + } + } +} + +OUString XclImpDropDownObj::DoGetServiceName() const +{ + return (GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX) ? + CREATE_OUSTRING( "com.sun.star.form.component.ComboBox" ) : + CREATE_OUSTRING( "com.sun.star.form.component.ListBox" ); +} + +XclTbxEventType XclImpDropDownObj::DoGetEventType() const +{ + return (GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX) ? EXC_TBX_EVENT_TEXT : EXC_TBX_EVENT_CHANGE; +} + +// ---------------------------------------------------------------------------- + +XclImpPictureObj::XclImpPictureObj( const XclImpRoot& rRoot ) : + XclImpRectObj( rRoot ), + XclImpControlHelper( rRoot, EXC_CTRL_BINDCONTENT ), + mnStorageId( 0 ), + mnCtlsStrmPos( 0 ), + mnCtlsStrmSize( 0 ), + mbEmbedded( false ), + mbLinked( false ), + mbSymbol( false ), + mbControl( false ), + mbUseCtlsStrm( false ) +{ + SetAreaObj( true ); + SetSimpleMacro( false ); + SetCustomDffObj( true ); +} + +String XclImpPictureObj::GetOleStorageName() const +{ + String aStrgName; + if( (mbEmbedded || mbLinked) && !mbControl && (mnStorageId > 0) ) + { + aStrgName = mbEmbedded ? EXC_STORAGE_OLE_EMBEDDED : EXC_STORAGE_OLE_LINKED; + static const sal_Char spcHexChars[] = "0123456789ABCDEF"; + for( sal_uInt8 nIndex = 32; nIndex > 0; nIndex -= 4 ) + aStrgName.Append( sal_Unicode( spcHexChars[ ::extract_value< sal_uInt8 >( mnStorageId, nIndex - 4, 4 ) ] ) ); + } + return aStrgName; +} + +void XclImpPictureObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + sal_uInt16 nLinkSize; + ReadFrameData( rStrm ); + rStrm.Ignore( 6 ); + rStrm >> nLinkSize; + rStrm.Ignore( 2 ); + ReadFlags3( rStrm ); + ReadMacro3( rStrm, nMacroSize ); + ReadPictFmla( rStrm, nLinkSize ); + + if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() ) + maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm ); +} + +void XclImpPictureObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + sal_uInt16 nLinkSize; + ReadFrameData( rStrm ); + rStrm.Ignore( 6 ); + rStrm >> nLinkSize; + rStrm.Ignore( 2 ); + ReadFlags3( rStrm ); + ReadMacro4( rStrm, nMacroSize ); + ReadPictFmla( rStrm, nLinkSize ); + + if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() ) + maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm ); +} + +void XclImpPictureObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + sal_uInt16 nLinkSize; + ReadFrameData( rStrm ); + rStrm.Ignore( 6 ); + rStrm >> nLinkSize; + rStrm.Ignore( 2 ); + ReadFlags3( rStrm ); + rStrm.Ignore( 4 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); + ReadPictFmla( rStrm, nLinkSize ); + + if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() ) + { + // page background is stored as hidden picture with name "__BkgndObj" + if( IsHidden() && (GetObjName() == CREATE_STRING( "__BkgndObj" )) ) + GetPageSettings().ReadImgData( rStrm ); + else + maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm ); + } +} + +void XclImpPictureObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJFLAGS: + ReadFlags8( rStrm ); + break; + case EXC_ID_OBJPICTFMLA: + ReadPictFmla( rStrm, rStrm.ReaduInt16() ); + break; + default: + XclImpDrawObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +SdrObject* XclImpPictureObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const Rectangle& rAnchorRect ) const +{ + // try to create an OLE object or form control + SdrObjectPtr xSdrObj( rDffConv.CreateSdrObject( *this, rAnchorRect ) ); + + // no OLE - create a plain picture from IMGDATA record data + if( !xSdrObj && (maGraphic.GetType() != GRAPHIC_NONE) ) + { + xSdrObj.reset( new SdrGrafObj( maGraphic, rAnchorRect ) ); + ConvertRectStyle( *xSdrObj ); + } + + rDffConv.Progress(); + return xSdrObj.release(); +} + +void XclImpPictureObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const +{ + if( IsOcxControl() ) + { + // do not call XclImpRectObj::DoPreProcessSdrObj(), it would trace missing "printable" feature + ProcessControl( *this ); + } + else if( mbEmbedded || mbLinked ) + { + // trace missing "printable" feature + XclImpRectObj::DoPreProcessSdrObj( rDffConv, rSdrObj ); + + SfxObjectShell* pDocShell = GetDocShell(); + SdrOle2Obj* pOleSdrObj = dynamic_cast< SdrOle2Obj* >( &rSdrObj ); + if( pOleSdrObj && pDocShell ) + { + comphelper::EmbeddedObjectContainer& rEmbObjCont = pDocShell->GetEmbeddedObjectContainer(); + Reference< XEmbeddedObject > xEmbObj = pOleSdrObj->GetObjRef(); + OUString aOldName( pOleSdrObj->GetPersistName() ); + + /* The object persistence should be already in the storage, but + the object still might not be inserted into the container. */ + if( rEmbObjCont.HasEmbeddedObject( aOldName ) ) + { + if( !rEmbObjCont.HasEmbeddedObject( xEmbObj ) ) + // filter code is allowed to call the following method + rEmbObjCont.AddEmbeddedObject( xEmbObj, aOldName ); + } + else + { + /* If the object is still not in container it must be inserted + there, the name must be generated in this case. */ + OUString aNewName; + rEmbObjCont.InsertEmbeddedObject( xEmbObj, aNewName ); + if( aOldName != aNewName ) + // #95381# SetPersistName, not SetName + pOleSdrObj->SetPersistName( aNewName ); + } + } + } +} + +void XclImpPictureObj::ReadFlags3( XclImpStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm >> nFlags; + mbSymbol = ::get_flag( nFlags, EXC_OBJ_PIC_SYMBOL ); +} + +void XclImpPictureObj::ReadFlags8( XclImpStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm >> nFlags; + mbSymbol = ::get_flag( nFlags, EXC_OBJ_PIC_SYMBOL ); + mbControl = ::get_flag( nFlags, EXC_OBJ_PIC_CONTROL ); + mbUseCtlsStrm = ::get_flag( nFlags, EXC_OBJ_PIC_CTLSSTREAM ); + DBG_ASSERT( mbControl || !mbUseCtlsStrm, "XclImpPictureObj::ReadFlags8 - CTLS stream for controls only" ); + SetProcessSdrObj( mbControl || !mbUseCtlsStrm ); +} + +void XclImpPictureObj::ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nLinkSize ) +{ + sal_Size nLinkEnd = rStrm.GetRecPos() + nLinkSize; + if( nLinkSize >= 6 ) + { + sal_uInt16 nFmlaSize; + rStrm >> nFmlaSize; + DBG_ASSERT( nFmlaSize > 0, "XclImpPictureObj::ReadPictFmla - missing link formula" ); + // BIFF3/BIFF4 do not support storages, nothing to do here + if( (nFmlaSize > 0) && (GetBiff() >= EXC_BIFF5) ) + { + rStrm.Ignore( 4 ); + sal_uInt8 nToken; + rStrm >> nToken; + + // different processing for linked vs. embedded OLE objects + if( nToken == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ) ) + { + mbLinked = true; + switch( GetBiff() ) + { + case EXC_BIFF5: + { + sal_Int16 nRefIdx; + sal_uInt16 nNameIdx; + rStrm >> nRefIdx; + rStrm.Ignore( 8 ); + rStrm >> nNameIdx; + rStrm.Ignore( 12 ); + const ExtName* pExtName = GetOldRoot().pExtNameBuff->GetNameByIndex( nRefIdx, nNameIdx ); + if( pExtName && pExtName->IsOLE() ) + mnStorageId = pExtName->nStorageId; + } + break; + case EXC_BIFF8: + { + sal_uInt16 nXti, nExtName; + rStrm >> nXti >> nExtName; + const XclImpExtName* pExtName = GetLinkManager().GetExternName( nXti, nExtName ); + if( pExtName && (pExtName->GetType() == xlExtOLE) ) + mnStorageId = pExtName->GetStorageId(); + } + break; + default: + DBG_ERROR_BIFF(); + } + } + else if( nToken == XclTokenArrayHelper::GetTokenId( EXC_TOKID_TBL, EXC_TOKCLASS_NONE ) ) + { + mbEmbedded = true; + DBG_ASSERT( nFmlaSize == 5, "XclImpPictureObj::ReadPictFmla - unexpected formula size" ); + rStrm.Ignore( nFmlaSize - 1 ); // token ID already read + if( nFmlaSize & 1 ) + rStrm.Ignore( 1 ); // padding byte + + // a class name may follow inside the picture link + if( rStrm.GetRecPos() + 2 <= nLinkEnd ) + { + sal_uInt16 nLen; + rStrm >> nLen; + if( nLen > 0 ) + maClassName = (GetBiff() == EXC_BIFF8) ? rStrm.ReadUniString( nLen ) : rStrm.ReadRawByteString( nLen ); + } + } + // else: ignore other formulas, e.g. pictures linked to cell ranges + } + } + + // seek behind picture link data + rStrm.Seek( nLinkEnd ); + + // read additional data for embedded OLE objects following the picture link + if( IsOcxControl() ) + { + // #i26521# form controls to be ignored + if( maClassName.EqualsAscii( "Forms.HTML:Hidden.1" ) ) + { + SetProcessSdrObj( false ); + return; + } + + if( rStrm.GetRecLeft() <= 8 ) return; + + // position and size of control data in 'Ctls' stream + mnCtlsStrmPos = static_cast< sal_Size >( rStrm.ReaduInt32() ); + mnCtlsStrmSize = static_cast< sal_Size >( rStrm.ReaduInt32() ); + + if( rStrm.GetRecLeft() <= 8 ) return; + + // additional string (16-bit characters), e.g. for progress bar control + sal_uInt32 nAddStrSize; + rStrm >> nAddStrSize; + DBG_ASSERT( rStrm.GetRecLeft() >= nAddStrSize + 4, "XclImpPictureObj::ReadPictFmla - missing data" ); + if( rStrm.GetRecLeft() >= nAddStrSize + 4 ) + { + rStrm.Ignore( nAddStrSize ); + // cell link and source range + ReadCellLinkFormula( rStrm, true ); + ReadSourceRangeFormula( rStrm, true ); + } + } + else if( mbEmbedded && (rStrm.GetRecLeft() >= 4) ) + { + rStrm >> mnStorageId; + } +} + +// DFF stream conversion ====================================================== + +//UNUSED2009-05 void XclImpSolverContainer::ReadSolverContainer( SvStream& rDffStrm ) +//UNUSED2009-05 { +//UNUSED2009-05 rDffStrm >> *this; +//UNUSED2009-05 } + +void XclImpSolverContainer::InsertSdrObjectInfo( SdrObject& rSdrObj, sal_uInt32 nDffShapeId, sal_uInt32 nDffFlags ) +{ + if( nDffShapeId > 0 ) + { + maSdrInfoMap[ nDffShapeId ].Set( &rSdrObj, nDffFlags ); + maSdrObjMap[ &rSdrObj ] = nDffShapeId; + } +} + +void XclImpSolverContainer::RemoveSdrObjectInfo( SdrObject& rSdrObj ) +{ + // remove info of passed object from the maps + XclImpSdrObjMap::iterator aIt = maSdrObjMap.find( &rSdrObj ); + if( aIt != maSdrObjMap.end() ) + { + maSdrInfoMap.erase( aIt->second ); + maSdrObjMap.erase( aIt ); + } + + // remove info of all child objects of a group object + if( SdrObjGroup* pGroupObj = dynamic_cast< SdrObjGroup* >( &rSdrObj ) ) + { + if( SdrObjList* pSubList = pGroupObj->GetSubList() ) + { + // iterate flat over the list because this function already works recursively + SdrObjListIter aObjIt( *pSubList, IM_FLAT ); + for( SdrObject* pChildObj = aObjIt.Next(); pChildObj; pChildObj = aObjIt.Next() ) + RemoveSdrObjectInfo( *pChildObj ); + } + } +} + +void XclImpSolverContainer::UpdateConnectorRules() +{ + for( SvxMSDffConnectorRule* pRule = GetFirstRule(); pRule; pRule = GetNextRule() ) + { + UpdateConnection( pRule->nShapeA, pRule->pAObj, &pRule->nSpFlagsA ); + UpdateConnection( pRule->nShapeB, pRule->pBObj, &pRule->nSpFlagsB ); + UpdateConnection( pRule->nShapeC, pRule->pCObj ); + } +} + +void XclImpSolverContainer::RemoveConnectorRules() +{ + // base class from SVX uses plain untyped tools/List + for( SvxMSDffConnectorRule* pRule = GetFirstRule(); pRule; pRule = GetNextRule() ) + delete pRule; + aCList.Clear(); + + maSdrInfoMap.clear(); + maSdrObjMap.clear(); +} + +SvxMSDffConnectorRule* XclImpSolverContainer::GetFirstRule() +{ + return static_cast< SvxMSDffConnectorRule* >( aCList.First() ); +} + +SvxMSDffConnectorRule* XclImpSolverContainer::GetNextRule() +{ + return static_cast< SvxMSDffConnectorRule* >( aCList.Next() ); +} + +void XclImpSolverContainer::UpdateConnection( sal_uInt32 nDffShapeId, SdrObject*& rpSdrObj, sal_uInt32* pnDffFlags ) +{ + XclImpSdrInfoMap::const_iterator aIt = maSdrInfoMap.find( nDffShapeId ); + if( aIt != maSdrInfoMap.end() ) + { + rpSdrObj = aIt->second.mpSdrObj; + if( pnDffFlags ) + *pnDffFlags = aIt->second.mnDffFlags; + } +} + +// ---------------------------------------------------------------------------- + +XclImpSimpleDffConverter::XclImpSimpleDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm ) : + SvxMSDffManager( rDffStrm, rRoot.GetBasePath(), 0, 0, rRoot.GetDoc().GetDrawLayer(), 1440, COL_DEFAULT, 24, 0, &rRoot.GetTracer().GetBaseTracer() ), + XclImpRoot( rRoot ) +{ + SetSvxMSDffSettings( SVXMSDFF_SETTINGS_CROP_BITMAPS | SVXMSDFF_SETTINGS_IMPORT_EXCEL ); +} + +XclImpSimpleDffConverter::~XclImpSimpleDffConverter() +{ +} + +FASTBOOL XclImpSimpleDffConverter::GetColorFromPalette( USHORT nIndex, Color& rColor ) const +{ + ColorData nColor = GetPalette().GetColorData( static_cast< sal_uInt16 >( nIndex ) ); + + if( nColor == COL_AUTO ) + return FALSE; + + rColor.SetColor( nColor ); + return TRUE; +} + +// ---------------------------------------------------------------------------- + +XclImpDffConverter::XclImpDffConvData::XclImpDffConvData( + XclImpDrawing& rDrawing, SdrModel& rSdrModel, SdrPage& rSdrPage ) : + mrDrawing( rDrawing ), + mrSdrModel( rSdrModel ), + mrSdrPage( rSdrPage ), + mnLastCtrlIndex( -1 ), + mbHasCtrlForm( false ) +{ +} + +// ---------------------------------------------------------------------------- + +XclImpDffConverter::XclImpDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm ) : + XclImpSimpleDffConverter( rRoot, rDffStrm ), + SvxMSConvertOCXControls( rRoot.GetDocShell(), 0 ), + maStdFormName( CREATE_OUSTRING( "Standard" ) ), + mnOleImpFlags( 0 ) +{ + if( SvtFilterOptions* pFilterOpt = SvtFilterOptions::Get() ) + { + if( pFilterOpt->IsMathType2Math() ) + mnOleImpFlags |= OLE_MATHTYPE_2_STARMATH; + if( pFilterOpt->IsWinWord2Writer() ) + mnOleImpFlags |= OLE_WINWORD_2_STARWRITER; + if( pFilterOpt->IsPowerPoint2Impress() ) + mnOleImpFlags |= OLE_POWERPOINT_2_STARIMPRESS; + } + + // try to open the 'Ctls' storage stream containing OCX control properties + mxCtlsStrm = OpenStream( EXC_STREAM_CTLS ); + + // default text margin (convert EMU to drawing layer units) + mnDefTextMargin = EXC_OBJ_TEXT_MARGIN; + ScaleEmu( mnDefTextMargin ); +} + +XclImpDffConverter::~XclImpDffConverter() +{ +} + +void XclImpDffConverter::StartProgressBar( sal_Size nProgressSize ) +{ + mxProgress.reset( new ScfProgressBar( GetDocShell(), STR_PROGRESS_CALCULATING ) ); + mxProgress->AddSegment( nProgressSize ); + mxProgress->Activate(); +} + +void XclImpDffConverter::Progress( sal_Size nDelta ) +{ + DBG_ASSERT( mxProgress.is(), "XclImpDffConverter::Progress - invalid call, no progress bar" ); + mxProgress->Progress( nDelta ); +} + +void XclImpDffConverter::InitializeDrawing( XclImpDrawing& rDrawing, SdrModel& rSdrModel, SdrPage& rSdrPage ) +{ + XclImpDffConvDataRef xConvData( new XclImpDffConvData( rDrawing, rSdrModel, rSdrPage ) ); + maDataStack.push_back( xConvData ); + SetModel( &xConvData->mrSdrModel, 1440 ); +} + +void XclImpDffConverter::ProcessObject( SdrObjList& rObjList, const XclImpDrawObjBase& rDrawObj ) +{ + if( rDrawObj.IsProcessSdrObj() ) + { + if( const XclObjAnchor* pAnchor = rDrawObj.GetAnchor() ) + { + Rectangle aAnchorRect = GetConvData().mrDrawing.CalcAnchorRect( *pAnchor, false ); + if( rDrawObj.IsValidSize( aAnchorRect ) ) + { + // CreateSdrObject() recursively creates embedded child objects + SdrObjectPtr xSdrObj( rDrawObj.CreateSdrObject( *this, aAnchorRect, false ) ); + if( xSdrObj.is() ) + rDrawObj.PreProcessSdrObject( *this, *xSdrObj ); + // call InsertSdrObject() also, if SdrObject is missing + InsertSdrObject( rObjList, rDrawObj, xSdrObj.release() ); + } + } + } +} + +void XclImpDffConverter::ProcessDrawing( const XclImpDrawObjVector& rDrawObjs ) +{ + SdrPage& rSdrPage = GetConvData().mrSdrPage; + for( XclImpDrawObjVector::const_iterator aIt = rDrawObjs.begin(), aEnd = rDrawObjs.end(); aIt != aEnd; ++aIt ) + ProcessObject( rSdrPage, **aIt ); +} + +void XclImpDffConverter::ProcessDrawing( SvStream& rDffStrm ) +{ + rDffStrm.Seek( STREAM_SEEK_TO_END ); + if( rDffStrm.Tell() > 0 ) + { + rDffStrm.Seek( STREAM_SEEK_TO_BEGIN ); + DffRecordHeader aHeader; + rDffStrm >> aHeader; + DBG_ASSERT( aHeader.nRecType == DFF_msofbtDgContainer, "XclImpDffConverter::ProcessDrawing - unexpected record" ); + if( aHeader.nRecType == DFF_msofbtDgContainer ) + ProcessDgContainer( rDffStrm, aHeader ); + } +} + +void XclImpDffConverter::FinalizeDrawing() +{ + DBG_ASSERT( !maDataStack.empty(), "XclImpDffConverter::FinalizeDrawing - no drawing manager on stack" ); + maDataStack.pop_back(); + // restore previous model at core DFF converter + if( !maDataStack.empty() ) + SetModel( &maDataStack.back()->mrSdrModel, 1440 ); +} + +SdrObject* XclImpDffConverter::CreateSdrObject( const XclImpTbxObjBase& rTbxObj, const Rectangle& rAnchorRect ) +{ + SdrObjectPtr xSdrObj; + + OUString aServiceName = rTbxObj.GetServiceName(); + if( SupportsOleObjects() && (aServiceName.getLength() > 0) ) try + { + // create the form control from scratch + Reference< XFormComponent > xFormComp( ScfApiHelper::CreateInstance( GetDocShell(), aServiceName ), UNO_QUERY_THROW ); + // set controls form, needed in virtual function InsertControl() + InitControlForm(); + // try to insert the control into the form + ::com::sun::star::awt::Size aDummySize; + Reference< XShape > xShape; + XclImpDffConvData& rConvData = GetConvData(); + if( rConvData.mxCtrlForm.is() && InsertControl( xFormComp, aDummySize, &xShape, TRUE ) ) + { + xSdrObj.reset( rTbxObj.CreateSdrObjectFromShape( xShape, rAnchorRect ) ); + // try to attach a macro to the control + ScriptEventDescriptor aDescriptor; + if( (rConvData.mnLastCtrlIndex >= 0) && rTbxObj.FillMacroDescriptor( aDescriptor ) ) + { + Reference< XEventAttacherManager > xEventMgr( rConvData.mxCtrlForm, UNO_QUERY_THROW ); + xEventMgr->registerScriptEvent( rConvData.mnLastCtrlIndex, aDescriptor ); + } + } + } + catch( Exception& ) + { + } + + return xSdrObj.release(); +} + +SdrObject* XclImpDffConverter::CreateSdrObject( const XclImpPictureObj& rPicObj, const Rectangle& rAnchorRect ) +{ + SdrObjectPtr xSdrObj; + + if( SupportsOleObjects() ) + { + if( rPicObj.IsOcxControl() ) + { + if( mxCtlsStrm.Is() ) try + { + /* set controls form, needed in virtual function InsertControl() + called from ReadOCXExcelKludgeStream() */ + InitControlForm(); + // seek to stream position of the extra data for this control + mxCtlsStrm->Seek( rPicObj.GetCtlsStreamPos() ); + // read from mxCtlsStrm into xShape, insert the control model into the form + Reference< XShape > xShape; + if( GetConvData().mxCtrlForm.is() && ReadOCXExcelKludgeStream( mxCtlsStrm, &xShape, TRUE ) ) + xSdrObj.reset( rPicObj.CreateSdrObjectFromShape( xShape, rAnchorRect ) ); + } + catch( Exception& ) + { + } + } + else + { + SfxObjectShell* pDocShell = GetDocShell(); + SotStorageRef xSrcStrg = GetRootStorage(); + String aStrgName = rPicObj.GetOleStorageName(); + if( pDocShell && xSrcStrg.Is() && (aStrgName.Len() > 0) ) + { + // first try to resolve graphic from DFF storage + Graphic aGraphic; + Rectangle aVisArea; + if( !GetBLIP( GetPropertyValue( DFF_Prop_pib ), aGraphic, &aVisArea ) ) + { + // if not found, use graphic from object (imported from IMGDATA record) + aGraphic = rPicObj.GetGraphic(); + aVisArea = rPicObj.GetVisArea(); + } + if( aGraphic.GetType() != GRAPHIC_NONE ) + { + ErrCode nError = ERRCODE_NONE; + namespace cssea = ::com::sun::star::embed::Aspects; + sal_Int64 nAspects = rPicObj.IsSymbol() ? cssea::MSOLE_ICON : cssea::MSOLE_CONTENT; + xSdrObj.reset( CreateSdrOLEFromStorage( + aStrgName, xSrcStrg, pDocShell->GetStorage(), aGraphic, + rAnchorRect, aVisArea, 0, nError, mnOleImpFlags, nAspects ) ); + } + } + } + } + + return xSdrObj.release(); +} + +bool XclImpDffConverter::SupportsOleObjects() const +{ + return GetConvData().mrDrawing.SupportsOleObjects(); +} + +// virtual functions ---------------------------------------------------------- + +void XclImpDffConverter::ProcessClientAnchor2( SvStream& rDffStrm, + DffRecordHeader& rHeader, void* /*pClientData*/, DffObjData& rObjData ) +{ + // find the OBJ record data related to the processed shape + XclImpDffConvData& rConvData = GetConvData(); + if( XclImpDrawObjBase* pDrawObj = rConvData.mrDrawing.FindDrawObj( rObjData.rSpHd ).get() ) + { + DBG_ASSERT( rHeader.nRecType == DFF_msofbtClientAnchor, "XclImpDffConverter::ProcessClientAnchor2 - no client anchor record" ); + XclObjAnchor aAnchor; + rHeader.SeekToContent( rDffStrm ); + rDffStrm.SeekRel( 2 ); // flags + rDffStrm >> aAnchor; // anchor format equal to BIFF5 OBJ records + pDrawObj->SetAnchor( aAnchor ); + rObjData.aChildAnchor = rConvData.mrDrawing.CalcAnchorRect( aAnchor, true ); + rObjData.bChildAnchor = sal_True; + } +} + +SdrObject* XclImpDffConverter::ProcessObj( SvStream& rDffStrm, DffObjData& rDffObjData, + void* pClientData, Rectangle& /*rTextRect*/, SdrObject* pOldSdrObj ) +{ + XclImpDffConvData& rConvData = GetConvData(); + + /* pOldSdrObj passes a generated SdrObject. This function owns this object + and can modify it. The function has either to return it back to caller + or to delete it by itself. */ + SdrObjectPtr xSdrObj( pOldSdrObj ); + + // find the OBJ record data related to the processed shape + XclImpDrawObjRef xDrawObj = rConvData.mrDrawing.FindDrawObj( rDffObjData.rSpHd ); + const Rectangle& rAnchorRect = rDffObjData.aChildAnchor; + + // #102378# Do not process the global page group shape (flag SP_FPATRIARCH) + bool bGlobalPageGroup = ::get_flag< sal_uInt32 >( rDffObjData.nSpFlags, SP_FPATRIARCH ); + if( !xDrawObj || !xDrawObj->IsProcessSdrObj() || bGlobalPageGroup ) + return 0; // simply return, xSdrObj will be destroyed + + /* Pass pointer to top-level object back to caller. If the processed + object is embedded in a group, the pointer is already set to the + top-level parent object. */ + XclImpDrawObjBase** ppTopLevelObj = reinterpret_cast< XclImpDrawObjBase** >( pClientData ); + bool bIsTopLevel = !ppTopLevelObj || !*ppTopLevelObj; + if( ppTopLevelObj && bIsTopLevel ) + *ppTopLevelObj = xDrawObj.get(); + + // #119010# connectors don't have to be area objects + if( dynamic_cast< SdrEdgeObj* >( xSdrObj.get() ) ) + xDrawObj->SetAreaObj( false ); + + /* Check for valid size for all objects. Needed to ignore lots of invisible + phantom objects from deleted rows or columns (for performance reasons). + #i30816# Include objects embedded in groups. + #i58780# Ignore group shapes, size is not initialized. */ + bool bEmbeddedGroup = !bIsTopLevel && dynamic_cast< SdrObjGroup* >( xSdrObj.get() ); + if( !bEmbeddedGroup && !xDrawObj->IsValidSize( rAnchorRect ) ) + return 0; // simply return, xSdrObj will be destroyed + + // set shape information from DFF stream + String aObjName = GetPropertyString( DFF_Prop_wzName, rDffStrm ); + String aHyperlink = ReadHlinkProperty( rDffStrm ); + bool bVisible = !GetPropertyBool( DFF_Prop_fHidden ); + bool bAutoMargin = GetPropertyBool( DFF_Prop_AutoTextMargin ); + xDrawObj->SetDffData( rDffObjData, aObjName, aHyperlink, bVisible, bAutoMargin ); + + /* Connect textbox data (string, alignment, text orientation) to object. + #98132# don't ask for a text-ID, DFF export doesn't set one. */ + if( XclImpTextObj* pTextObj = dynamic_cast< XclImpTextObj* >( xDrawObj.get() ) ) + if( const XclImpObjTextData* pTextData = rConvData.mrDrawing.FindTextData( rDffObjData.rSpHd ) ) + pTextObj->SetTextData( *pTextData ); + + // copy line and fill formatting of TBX form controls from DFF properties + if( XclImpTbxObjBase* pTbxObj = dynamic_cast< XclImpTbxObjBase* >( xDrawObj.get() ) ) + pTbxObj->SetDffProperties( *this ); + + // try to create a custom SdrObject that overwrites the passed object + SdrObjectPtr xNewSdrObj( xDrawObj->CreateSdrObject( *this, rAnchorRect, true ) ); + if( xNewSdrObj.is() ) + xSdrObj.reset( xNewSdrObj.release() ); + + // process the SdrObject + if( xSdrObj.is() ) + { + // filled without color -> set system window color + if( GetPropertyBool( DFF_Prop_fFilled ) && !IsProperty( DFF_Prop_fillColor ) ) + xSdrObj->SetMergedItem( XFillColorItem( EMPTY_STRING, GetPalette().GetColor( EXC_COLOR_WINDOWBACK ) ) ); + + // additional processing on the SdrObject + xDrawObj->PreProcessSdrObject( *this, *xSdrObj ); + + /* If the SdrObject will not be inserted into the draw page, delete it + here. Happens e.g. for notes: The PreProcessSdrObject() call above + has inserted the note into the document, and the SdrObject is not + needed anymore. */ + if( !xDrawObj->IsInsertSdrObj() ) + xSdrObj.reset(); + } + + if( xSdrObj.is() ) + { + /* Store the relation between shape ID and SdrObject for connectors. + Must be done here (and not in InsertSdrObject() function), + otherwise all SdrObjects embedded in groups would be lost. */ + rConvData.maSolverCont.InsertSdrObjectInfo( *xSdrObj, xDrawObj->GetDffShapeId(), xDrawObj->GetDffFlags() ); + + /* If the drawing object is embedded in a group object, call + PostProcessSdrObject() here. For top-level objects this will be + done automatically in InsertSdrObject() but grouped shapes are + inserted into their groups somewhere in the SvxMSDffManager base + class without chance of notification. Unfortunately, now this is + called before the object is really inserted into its group object, + but that should not have any effect for grouped objects. */ + if( !bIsTopLevel ) + xDrawObj->PostProcessSdrObject( *this, *xSdrObj ); + } + + return xSdrObj.release(); +} + +ULONG XclImpDffConverter::Calc_nBLIPPos( ULONG /*nOrgVal*/, ULONG nStreamPos ) const +{ + return nStreamPos + 4; +} + +sal_Bool XclImpDffConverter::InsertControl( const Reference< XFormComponent >& rxFormComp, + const ::com::sun::star::awt::Size& /*rSize*/, Reference< XShape >* pxShape, + BOOL /*bFloatingCtrl*/ ) +{ + if( GetDocShell() ) try + { + XclImpDffConvData& rConvData = GetConvData(); + Reference< XIndexContainer > xFormIC( rConvData.mxCtrlForm, UNO_QUERY_THROW ); + Reference< XControlModel > xCtrlModel( rxFormComp, UNO_QUERY_THROW ); + + // create the control shape + Reference< XShape > xShape( ScfApiHelper::CreateInstance( GetDocShell(), CREATE_OUSTRING( "com.sun.star.drawing.ControlShape" ) ), UNO_QUERY_THROW ); + Reference< XControlShape > xCtrlShape( xShape, UNO_QUERY_THROW ); + + // insert the new control into the form + sal_Int32 nNewIndex = xFormIC->getCount(); + xFormIC->insertByIndex( nNewIndex, Any( rxFormComp ) ); + // on success: store new index of the control for later use (macro events) + rConvData.mnLastCtrlIndex = nNewIndex; + + // set control model at control shape and pass back shape to caller + xCtrlShape->setControl( xCtrlModel ); + if( pxShape ) *pxShape = xShape; + return sal_True; + } + catch( Exception& ) + { + DBG_ERRORFILE( "XclImpDffConverter::InsertControl - cannot create form control" ); + } + + return sal_False; +} + +// private -------------------------------------------------------------------- + +XclImpDffConverter::XclImpDffConvData& XclImpDffConverter::GetConvData() +{ + DBG_ASSERT( !maDataStack.empty(), "XclImpDffConverter::GetConvData - no drawing manager on stack" ); + return *maDataStack.back(); +} + +const XclImpDffConverter::XclImpDffConvData& XclImpDffConverter::GetConvData() const +{ + DBG_ASSERT( !maDataStack.empty(), "XclImpDffConverter::GetConvData - no drawing manager on stack" ); + return *maDataStack.back(); +} + +String XclImpDffConverter::ReadHlinkProperty( SvStream& rDffStrm ) const +{ + /* Reads hyperlink data from a complex DFF property. Contents of this + property are equal to the HLINK record, import of this record is + implemented in class XclImpHyperlink. This function has to create an + instance of the XclImpStream class to be able to reuse the + functionality of XclImpHyperlink. */ + String aString; + sal_uInt32 nBufferSize = GetPropertyValue( DFF_Prop_pihlShape ); + if( (0 < nBufferSize) && (nBufferSize <= 0xFFFF) && SeekToContent( DFF_Prop_pihlShape, rDffStrm ) ) + { + // create a faked BIFF record that can be read by XclImpStream class + SvMemoryStream aMemStream; + aMemStream << sal_uInt16( 0 ) << static_cast< sal_uInt16 >( nBufferSize ); + + // copy from DFF stream to memory stream + ::std::vector< sal_uInt8 > aBuffer( nBufferSize ); + sal_uInt8* pnData = &aBuffer.front(); + if( rDffStrm.Read( pnData, nBufferSize ) == nBufferSize ) + { + aMemStream.Write( pnData, nBufferSize ); + + // create BIFF import stream to be able to use XclImpHyperlink class + XclImpStream aXclStrm( aMemStream, GetRoot() ); + if( aXclStrm.StartNextRecord() ) + aString = XclImpHyperlink::ReadEmbeddedData( aXclStrm ); + } + } + return aString; +} + +void XclImpDffConverter::ProcessDgContainer( SvStream& rDffStrm, const DffRecordHeader& rDgHeader ) +{ + sal_Size nEndPos = rDgHeader.GetRecEndFilePos(); + while( rDffStrm.Tell() < nEndPos ) + { + DffRecordHeader aHeader; + rDffStrm >> aHeader; + switch( aHeader.nRecType ) + { + case DFF_msofbtSolverContainer: + ProcessSolverContainer( rDffStrm, aHeader ); + break; + case DFF_msofbtSpgrContainer: + ProcessShGrContainer( rDffStrm, aHeader ); + break; + default: + aHeader.SeekToEndOfRecord( rDffStrm ); + } + } + // seek to end of drawing page container + rDgHeader.SeekToEndOfRecord( rDffStrm ); + + // #i12638# #i37900# connector rules + XclImpSolverContainer& rSolverCont = GetConvData().maSolverCont; + rSolverCont.UpdateConnectorRules(); + SolveSolver( rSolverCont ); + rSolverCont.RemoveConnectorRules(); +} + +void XclImpDffConverter::ProcessShGrContainer( SvStream& rDffStrm, const DffRecordHeader& rShGrHeader ) +{ + sal_Size nEndPos = rShGrHeader.GetRecEndFilePos(); + while( rDffStrm.Tell() < nEndPos ) + { + DffRecordHeader aHeader; + rDffStrm >> aHeader; + switch( aHeader.nRecType ) + { + case DFF_msofbtSpgrContainer: + case DFF_msofbtSpContainer: + ProcessShContainer( rDffStrm, aHeader ); + break; + default: + aHeader.SeekToEndOfRecord( rDffStrm ); + } + } + // seek to end of shape group container + rShGrHeader.SeekToEndOfRecord( rDffStrm ); +} + +void XclImpDffConverter::ProcessSolverContainer( SvStream& rDffStrm, const DffRecordHeader& rSolverHeader ) +{ + // solver container wants to read the solver container header again + rSolverHeader.SeekToBegOfRecord( rDffStrm ); + // read the entire solver container + rDffStrm >> GetConvData().maSolverCont; + // seek to end of solver container + rSolverHeader.SeekToEndOfRecord( rDffStrm ); +} + +void XclImpDffConverter::ProcessShContainer( SvStream& rDffStrm, const DffRecordHeader& rShHeader ) +{ + rShHeader.SeekToBegOfRecord( rDffStrm ); + Rectangle aDummy; + const XclImpDrawObjBase* pDrawObj = 0; + /* The call to ImportObj() creates and returns a new SdrObject for the + processed shape. We take ownership of the returned object here. If the + shape is a group object, all embedded objects are created recursively, + and the returned group object contains them all. ImportObj() calls the + virtual functions ProcessClientAnchor2() and ProcessObj() and writes + the pointer to the related draw object data (OBJ record) into pDrawObj. */ + SdrObjectPtr xSdrObj( ImportObj( rDffStrm, &pDrawObj, aDummy, aDummy, 0, 0 ) ); + if( pDrawObj && xSdrObj.is() ) + InsertSdrObject( GetConvData().mrSdrPage, *pDrawObj, xSdrObj.release() ); + rShHeader.SeekToEndOfRecord( rDffStrm ); +} + +void XclImpDffConverter::InsertSdrObject( SdrObjList& rObjList, const XclImpDrawObjBase& rDrawObj, SdrObject* pSdrObj ) +{ + XclImpDffConvData& rConvData = GetConvData(); + /* Take ownership of the passed object. If insertion fails (e.g. rDrawObj + states to skip insertion), the object is automatically deleted. */ + SdrObjectPtr xSdrObj( pSdrObj ); + if( xSdrObj.is() && rDrawObj.IsInsertSdrObj() ) + { + rObjList.NbcInsertObject( xSdrObj.release() ); + // callback to drawing manager for e.g. tracking of used sheet area + rConvData.mrDrawing.OnObjectInserted( rDrawObj ); + // callback to drawing object for post processing (use pSdrObj, xSdrObj already released) + rDrawObj.PostProcessSdrObject( *this, *pSdrObj ); + } + /* SdrObject still here? Insertion failed, remove data from shape ID map. + The SdrObject will be destructed then. */ + if( xSdrObj.is() ) + rConvData.maSolverCont.RemoveSdrObjectInfo( *xSdrObj ); +} + +void XclImpDffConverter::InitControlForm() +{ + XclImpDffConvData& rConvData = GetConvData(); + if( rConvData.mbHasCtrlForm ) + return; + + rConvData.mbHasCtrlForm = true; + if( SupportsOleObjects() ) try + { + Reference< XFormsSupplier > xFormsSupplier( rConvData.mrSdrPage.getUnoPage(), UNO_QUERY_THROW ); + Reference< XNameContainer > xFormsNC( xFormsSupplier->getForms(), UNO_SET_THROW ); + // find or create the Standard form used to insert the imported controls + if( xFormsNC->hasByName( maStdFormName ) ) + { + xFormsNC->getByName( maStdFormName ) >>= rConvData.mxCtrlForm; + } + else if( SfxObjectShell* pDocShell = GetDocShell() ) + { + rConvData.mxCtrlForm.set( ScfApiHelper::CreateInstance( pDocShell, CREATE_OUSTRING( "com.sun.star.form.component.Form" ) ), UNO_QUERY_THROW ); + xFormsNC->insertByName( maStdFormName, Any( rConvData.mxCtrlForm ) ); + } + } + catch( Exception& ) + { + } +} + +// Drawing manager ============================================================ + +XclImpDrawing::XclImpDrawing( const XclImpRoot& rRoot, bool bOleObjects ) : + XclImpRoot( rRoot ), + mbOleObjs( bOleObjects ) +{ +} + +XclImpDrawing::~XclImpDrawing() +{ +} + +/*static*/ Graphic XclImpDrawing::ReadImgData( const XclImpRoot& rRoot, XclImpStream& rStrm ) +{ + Graphic aGraphic; + sal_uInt16 nFormat, nEnv; + sal_uInt32 nDataSize; + rStrm >> nFormat >> nEnv >> nDataSize; + if( nDataSize <= rStrm.GetRecLeft() ) + { + switch( nFormat ) + { + case EXC_IMGDATA_WMF: ReadWmf( aGraphic, rRoot, rStrm ); break; + case EXC_IMGDATA_BMP: ReadBmp( aGraphic, rRoot, rStrm ); break; + default: DBG_ERRORFILE( "XclImpDrawing::ReadImgData - unknown image format" ); + } + } + return aGraphic; +} + +void XclImpDrawing::ReadObj( XclImpStream& rStrm ) +{ + XclImpDrawObjRef xDrawObj; + + /* #i61786# In BIFF8 streams, OBJ records may occur without MSODRAWING + records. In this case, the OBJ records are in BIFF5 format. Do a sanity + check here that there is no DFF data loaded before. */ + DBG_ASSERT( maDffStrm.Tell() == 0, "XclImpDrawing::ReadObj - unexpected DFF stream data, OBJ will be ignored" ); + if( maDffStrm.Tell() == 0 ) switch( GetBiff() ) + { + case EXC_BIFF3: + xDrawObj = XclImpDrawObjBase::ReadObj3( GetRoot(), rStrm ); + break; + case EXC_BIFF4: + xDrawObj = XclImpDrawObjBase::ReadObj4( GetRoot(), rStrm ); + break; + case EXC_BIFF5: + case EXC_BIFF8: + xDrawObj = XclImpDrawObjBase::ReadObj5( GetRoot(), rStrm ); + break; + default: + DBG_ERROR_BIFF(); + } + + if( xDrawObj.is() ) + { + // insert into maRawObjs or into the last open group object + maRawObjs.InsertGrouped( xDrawObj ); + // to be able to find objects by ID + maObjMapId[ xDrawObj->GetObjId() ] = xDrawObj; + } +} + +void XclImpDrawing::ReadMsoDrawing( XclImpStream& rStrm ) +{ + DBG_ASSERT_BIFF( GetBiff() == EXC_BIFF8 ); + // disable internal CONTINUE handling + rStrm.ResetRecord( false ); + // read leading MSODRAWING record + ReadDffRecord( rStrm ); + + // read following drawing records, but do not start following unrelated record + bool bLoop = true; + while( bLoop ) switch( rStrm.GetNextRecId() ) + { + case EXC_ID_MSODRAWING: + case EXC_ID_MSODRAWINGSEL: + case EXC_ID_CONT: + rStrm.StartNextRecord(); + ReadDffRecord( rStrm ); + break; + case EXC_ID_OBJ: + rStrm.StartNextRecord(); + ReadObj8( rStrm ); + break; + case EXC_ID_TXO: + rStrm.StartNextRecord(); + ReadTxo( rStrm ); + break; + default: + bLoop = false; + } + + // re-enable internal CONTINUE handling + rStrm.ResetRecord( true ); +} + +XclImpDrawObjRef XclImpDrawing::FindDrawObj( const DffRecordHeader& rHeader ) const +{ + /* maObjMap stores objects by position of the client data (OBJ record) in + the DFF stream, which is always behind shape start position of the + passed header. The function upper_bound() finds the first element in + the map whose key is greater than the start position of the header. Its + end position is used to test whether the found object is really related + to the shape. */ + XclImpDrawObjRef xDrawObj; + XclImpObjMap::const_iterator aIt = maObjMap.upper_bound( rHeader.GetRecBegFilePos() ); + if( (aIt != maObjMap.end()) && (aIt->first <= rHeader.GetRecEndFilePos()) ) + xDrawObj = aIt->second; + return xDrawObj; +} + +XclImpDrawObjRef XclImpDrawing::FindDrawObj( sal_uInt16 nObjId ) const +{ + XclImpDrawObjRef xDrawObj; + XclImpObjMapById::const_iterator aIt = maObjMapId.find( nObjId ); + if( aIt != maObjMapId.end() ) + xDrawObj = aIt->second; + return xDrawObj; +} + +const XclImpObjTextData* XclImpDrawing::FindTextData( const DffRecordHeader& rHeader ) const +{ + /* maTextMap stores textbox data by position of the client data (TXO + record) in the DFF stream, which is always behind shape start position + of the passed header. The function upper_bound() finds the first + element in the map whose key is greater than the start position of the + header. Its end position is used to test whether the found object is + really related to the shape. */ + XclImpObjTextMap::const_iterator aIt = maTextMap.upper_bound( rHeader.GetRecBegFilePos() ); + if( (aIt != maTextMap.end()) && (aIt->first <= rHeader.GetRecEndFilePos()) ) + return aIt->second.get(); + return 0; +} + +void XclImpDrawing::SetSkipObj( sal_uInt16 nObjId ) +{ + maSkipObjs.push_back( nObjId ); +} + +sal_Size XclImpDrawing::GetProgressSize() const +{ + sal_Size nProgressSize = maRawObjs.GetProgressSize(); + for( XclImpObjMap::const_iterator aIt = maObjMap.begin(), aEnd = maObjMap.end(); aIt != aEnd; ++aIt ) + nProgressSize += aIt->second->GetProgressSize(); + return nProgressSize; +} + +void XclImpDrawing::ImplConvertObjects( XclImpDffConverter& rDffConv, SdrModel& rSdrModel, SdrPage& rSdrPage ) +{ + // register this drawing manager at the passed (global) DFF manager + rDffConv.InitializeDrawing( *this, rSdrModel, rSdrPage ); + // process list of objects to be skipped + for( ScfUInt16Vec::const_iterator aIt = maSkipObjs.begin(), aEnd = maSkipObjs.end(); aIt != aEnd; ++aIt ) + if( XclImpDrawObjBase* pDrawObj = FindDrawObj( *aIt ).get() ) + pDrawObj->SetProcessSdrObj( false ); + // process drawing objects without DFF data + rDffConv.ProcessDrawing( maRawObjs ); + // process all objects in the DFF stream + rDffConv.ProcessDrawing( maDffStrm ); + // unregister this drawing manager at the passed (global) DFF manager + rDffConv.FinalizeDrawing(); +} + +// protected ------------------------------------------------------------------ + +void XclImpDrawing::AppendRawObject( const XclImpDrawObjRef& rxDrawObj ) +{ + DBG_ASSERT( rxDrawObj.is(), "XclImpDrawing::AppendRawObject - unexpected empty reference" ); + maRawObjs.push_back( rxDrawObj ); +} + +// private -------------------------------------------------------------------- + +void XclImpDrawing::ReadWmf( Graphic& rGraphic, const XclImpRoot&, XclImpStream& rStrm ) // static helper +{ + // extract graphic data from IMGDATA and following CONTINUE records + rStrm.Ignore( 8 ); + SvMemoryStream aMemStrm; + rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() ); + aMemStrm.Seek( STREAM_SEEK_TO_BEGIN ); + // import the graphic from memory stream + GDIMetaFile aGDIMetaFile; + if( ::ReadWindowMetafile( aMemStrm, aGDIMetaFile, 0 ) ) + rGraphic = aGDIMetaFile; +} + +void XclImpDrawing::ReadBmp( Graphic& rGraphic, const XclImpRoot& rRoot, XclImpStream& rStrm ) // static helper +{ + // extract graphic data from IMGDATA and following CONTINUE records + SvMemoryStream aMemStrm; + + /* Excel 3 and 4 seem to write broken BMP data. Usually they write a + DIBCOREHEADER (12 bytes) containing width, height, planes = 1, and + pixel depth = 32 bit. After that, 3 unused bytes are added before the + actual pixel data. This does even confuse Excel 5 and later, which + cannot read the image data correctly. */ + if( rRoot.GetBiff() <= EXC_BIFF4 ) + { + rStrm.PushPosition(); + sal_uInt32 nHdrSize; + sal_uInt16 nWidth, nHeight, nPlanes, nDepth; + rStrm >> nHdrSize >> nWidth >> nHeight >> nPlanes >> nDepth; + if( (nHdrSize == 12) && (nPlanes == 1) && (nDepth == 32) ) + { + rStrm.Ignore( 3 ); + aMemStrm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + aMemStrm << nHdrSize << nWidth << nHeight << nPlanes << nDepth; + rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() ); + } + rStrm.PopPosition(); + } + + // no special handling above -> just copy the remaining record data + if( aMemStrm.Tell() == 0 ) + rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() ); + + // import the graphic from memory stream + aMemStrm.Seek( STREAM_SEEK_TO_BEGIN ); + Bitmap aBitmap; + if( aBitmap.Read( aMemStrm, FALSE ) ) // read DIB without file header + rGraphic = aBitmap; +} + +void XclImpDrawing::ReadDffRecord( XclImpStream& rStrm ) +{ + maDffStrm.Seek( STREAM_SEEK_TO_END ); + rStrm.CopyRecordToStream( maDffStrm ); +} + +void XclImpDrawing::ReadObj8( XclImpStream& rStrm ) +{ + XclImpDrawObjRef xDrawObj = XclImpDrawObjBase::ReadObj8( GetRoot(), rStrm ); + // store the new object in the internal containers + maObjMap[ maDffStrm.Tell() ] = xDrawObj; + maObjMapId[ xDrawObj->GetObjId() ] = xDrawObj; +} + +void XclImpDrawing::ReadTxo( XclImpStream& rStrm ) +{ + XclImpObjTextRef xTextData( new XclImpObjTextData ); + maTextMap[ maDffStrm.Tell() ] = xTextData; + + // 1) read the TXO record + xTextData->maData.ReadTxo8( rStrm ); + + // 2) first CONTINUE with string + xTextData->mxString.reset(); + bool bValid = true; + if( xTextData->maData.mnTextLen > 0 ) + { + bValid = (rStrm.GetNextRecId() == EXC_ID_CONT) && rStrm.StartNextRecord(); + DBG_ASSERT( bValid, "XclImpDrawing::ReadTxo - missing CONTINUE record" ); + if( bValid ) + xTextData->mxString.reset( new XclImpString( rStrm.ReadUniString( xTextData->maData.mnTextLen ) ) ); + } + + // 3) second CONTINUE with formatting runs + if( xTextData->maData.mnFormatSize > 0 ) + { + bValid = (rStrm.GetNextRecId() == EXC_ID_CONT) && rStrm.StartNextRecord(); + DBG_ASSERT( bValid, "XclImpDrawing::ReadTxo - missing CONTINUE record" ); + if( bValid ) + xTextData->ReadFormats( rStrm ); + } +} + +// ---------------------------------------------------------------------------- + +XclImpSheetDrawing::XclImpSheetDrawing( const XclImpRoot& rRoot, SCTAB nScTab ) : + XclImpDrawing( rRoot, true ), + maScUsedArea( ScAddress::INITIALIZE_INVALID ) +{ + maScUsedArea.aStart.SetTab( nScTab ); + maScUsedArea.aEnd.SetTab( nScTab ); +} + +void XclImpSheetDrawing::ReadNote( XclImpStream& rStrm ) +{ + switch( GetBiff() ) + { + case EXC_BIFF2: + case EXC_BIFF3: + case EXC_BIFF4: + case EXC_BIFF5: + ReadNote3( rStrm ); + break; + case EXC_BIFF8: + ReadNote8( rStrm ); + break; + default: + DBG_ERROR_BIFF(); + } +} + +void XclImpSheetDrawing::ReadTabChart( XclImpStream& rStrm ) +{ + DBG_ASSERT_BIFF( GetBiff() >= EXC_BIFF5 ); + ScfRef< XclImpChartObj > xChartObj( new XclImpChartObj( GetRoot(), true ) ); + xChartObj->ReadChartSubStream( rStrm ); + // insert the chart as raw object without connected DFF data + AppendRawObject( xChartObj ); +} + +void XclImpSheetDrawing::ConvertObjects( XclImpDffConverter& rDffConv ) +{ + if( SdrModel* pSdrModel = GetDoc().GetDrawLayer() ) + if( SdrPage* pSdrPage = GetSdrPage( maScUsedArea.aStart.Tab() ) ) + ImplConvertObjects( rDffConv, *pSdrModel, *pSdrPage ); +} + +Rectangle XclImpSheetDrawing::CalcAnchorRect( const XclObjAnchor& rAnchor, bool /*bDffAnchor*/ ) const +{ + return rAnchor.GetRect( GetRoot(), maScUsedArea.aStart.Tab(), MAP_100TH_MM ); +} + +void XclImpSheetDrawing::OnObjectInserted( const XclImpDrawObjBase& rDrawObj ) +{ + ScRange aScObjArea = rDrawObj.GetUsedArea( maScUsedArea.aStart.Tab() ); + if( aScObjArea.IsValid() ) + maScUsedArea.ExtendTo( aScObjArea ); +} + +// private -------------------------------------------------------------------- + +void XclImpSheetDrawing::ReadNote3( XclImpStream& rStrm ) +{ + XclAddress aXclPos; + sal_uInt16 nTotalLen; + rStrm >> aXclPos >> nTotalLen; + + ScAddress aScNotePos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScNotePos, aXclPos, maScUsedArea.aStart.Tab(), true ) ) + { + sal_uInt16 nPartLen = ::std::min( nTotalLen, static_cast< sal_uInt16 >( rStrm.GetRecLeft() ) ); + String aNoteText = rStrm.ReadRawByteString( nPartLen ); + nTotalLen = nTotalLen - nPartLen; + while( (nTotalLen > 0) && (rStrm.GetNextRecId() == EXC_ID_NOTE) && rStrm.StartNextRecord() ) + { + rStrm >> aXclPos >> nPartLen; + DBG_ASSERT( aXclPos.mnRow == 0xFFFF, "XclImpObjectManager::ReadNote3 - missing continuation NOTE record" ); + if( aXclPos.mnRow == 0xFFFF ) + { + DBG_ASSERT( nPartLen <= nTotalLen, "XclImpObjectManager::ReadNote3 - string too long" ); + aNoteText.Append( rStrm.ReadRawByteString( nPartLen ) ); + nTotalLen = nTotalLen - ::std::min( nTotalLen, nPartLen ); + } + else + { + // seems to be a new note, record already started -> load the note + rStrm.Seek( EXC_REC_SEEK_TO_BEGIN ); + ReadNote( rStrm ); + nTotalLen = 0; + } + } + ScNoteUtil::CreateNoteFromString( GetDoc(), aScNotePos, aNoteText, false, false ); + } +} + +void XclImpSheetDrawing::ReadNote8( XclImpStream& rStrm ) +{ + XclAddress aXclPos; + sal_uInt16 nFlags, nObjId; + rStrm >> aXclPos >> nFlags >> nObjId; + + ScAddress aScNotePos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScNotePos, aXclPos, maScUsedArea.aStart.Tab(), true ) ) + if( nObjId != EXC_OBJ_INVALID_ID ) + if( XclImpNoteObj* pNoteObj = dynamic_cast< XclImpNoteObj* >( FindDrawObj( nObjId ).get() ) ) + pNoteObj->SetNoteData( aScNotePos, nFlags ); +} + +// The object manager ========================================================= + +XclImpObjectManager::XclImpObjectManager( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ + maDefObjNames[ EXC_OBJTYPE_GROUP ] = CREATE_STRING( "Group" ); + maDefObjNames[ EXC_OBJTYPE_LINE ] = CREATE_STRING( "Line" ); + maDefObjNames[ EXC_OBJTYPE_RECTANGLE ] = CREATE_STRING( "Rectangle" ); + maDefObjNames[ EXC_OBJTYPE_OVAL ] = CREATE_STRING( "Oval" ); + maDefObjNames[ EXC_OBJTYPE_ARC ] = CREATE_STRING( "Arc" ); + maDefObjNames[ EXC_OBJTYPE_CHART ] = CREATE_STRING( "Chart" ); + maDefObjNames[ EXC_OBJTYPE_TEXT ] = CREATE_STRING( "Text" ); + maDefObjNames[ EXC_OBJTYPE_BUTTON ] = CREATE_STRING( "Button" ); + maDefObjNames[ EXC_OBJTYPE_PICTURE ] = CREATE_STRING( "Picture" ); + maDefObjNames[ EXC_OBJTYPE_POLYGON ] = CREATE_STRING( "Freeform" ); + maDefObjNames[ EXC_OBJTYPE_CHECKBOX ] = CREATE_STRING( "Check Box" ); + maDefObjNames[ EXC_OBJTYPE_OPTIONBUTTON ] = CREATE_STRING( "Option Button" ); + maDefObjNames[ EXC_OBJTYPE_EDIT ] = CREATE_STRING( "Edit Box" ); + maDefObjNames[ EXC_OBJTYPE_LABEL ] = CREATE_STRING( "Label" ); + maDefObjNames[ EXC_OBJTYPE_DIALOG ] = CREATE_STRING( "Dialog Frame" ); + maDefObjNames[ EXC_OBJTYPE_SPIN ] = CREATE_STRING( "Spinner" ); + maDefObjNames[ EXC_OBJTYPE_SCROLLBAR ] = CREATE_STRING( "Scroll Bar" ); + maDefObjNames[ EXC_OBJTYPE_LISTBOX ] = CREATE_STRING( "List Box" ); + maDefObjNames[ EXC_OBJTYPE_GROUPBOX ] = CREATE_STRING( "Group Box" ); + maDefObjNames[ EXC_OBJTYPE_DROPDOWN ] = CREATE_STRING( "Drop Down" ); + maDefObjNames[ EXC_OBJTYPE_NOTE ] = CREATE_STRING( "Comment" ); + maDefObjNames[ EXC_OBJTYPE_DRAWING ] = CREATE_STRING( "AutoShape" ); +} + +XclImpObjectManager::~XclImpObjectManager() +{ +} + +void XclImpObjectManager::ReadMsoDrawingGroup( XclImpStream& rStrm ) +{ + DBG_ASSERT_BIFF( GetBiff() == EXC_BIFF8 ); + // Excel continues this record with MSODRAWINGGROUP and CONTINUE records, hmm. + rStrm.ResetRecord( true, EXC_ID_MSODRAWINGGROUP ); + maDggStrm.Seek( STREAM_SEEK_TO_END ); + rStrm.CopyRecordToStream( maDggStrm ); +} + +XclImpSheetDrawing& XclImpObjectManager::GetSheetDrawing( SCTAB nScTab ) +{ + XclImpSheetDrawingRef& rxDrawing = maSheetDrawings[ nScTab ]; + if( !rxDrawing ) + rxDrawing.reset( new XclImpSheetDrawing( GetRoot(), nScTab ) ); + return *rxDrawing; +} + +void XclImpObjectManager::ConvertObjects() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLog, "sc", "dr104026", "XclImpObjectManager::ConvertObjects" ); + + // do nothing if the document does not contain a drawing layer + if( !GetDoc().GetDrawLayer() ) + return; + + // get total progress bar size for all sheet drawing managers + sal_Size nProgressSize = 0; + for( XclImpSheetDrawingMap::iterator aIt = maSheetDrawings.begin(), aEnd = maSheetDrawings.end(); aIt != aEnd; ++aIt ) + nProgressSize += aIt->second->GetProgressSize(); + // nothing to do if progress bar is zero (no objects present) + if( nProgressSize == 0 ) + return; + + XclImpDffConverter aDffConv( GetRoot(), maDggStrm ); + aDffConv.StartProgressBar( nProgressSize ); + for( XclImpSheetDrawingMap::iterator aIt = maSheetDrawings.begin(), aEnd = maSheetDrawings.end(); aIt != aEnd; ++aIt ) + aIt->second->ConvertObjects( aDffConv ); + + // #i112436# don't call ScChartListenerCollection::SetDirty here, + // instead use InterpretDirtyCells in ScDocument::CalcAfterLoad. +} + +String XclImpObjectManager::GetDefaultObjName( const XclImpDrawObjBase& rDrawObj ) const +{ + String aDefName; + DefObjNameMap::const_iterator aIt = maDefObjNames.find( rDrawObj.GetObjType() ); + if( aIt != maDefObjNames.end() ) + aDefName.Append( aIt->second ); + return aDefName.Append( sal_Unicode( ' ' ) ).Append( String::CreateFromInt32( rDrawObj.GetObjId() ) ); +} + +ScRange XclImpObjectManager::GetUsedArea( SCTAB nScTab ) const +{ + XclImpSheetDrawingMap::const_iterator aIt = maSheetDrawings.find( nScTab ); + if( aIt != maSheetDrawings.end() ) + return aIt->second->GetUsedArea(); + return ScRange( ScAddress::INITIALIZE_INVALID ); +} + +// DFF property set helper ==================================================== + +XclImpDffPropSet::XclImpDffPropSet( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + maDffConv( rRoot, maDummyStrm ) +{ +} + +void XclImpDffPropSet::Read( XclImpStream& rStrm ) +{ + sal_uInt32 nPropSetSize; + + rStrm.PushPosition(); + rStrm.Ignore( 4 ); + rStrm >> nPropSetSize; + rStrm.PopPosition(); + + mxMemStrm.reset( new SvMemoryStream ); + rStrm.CopyToStream( *mxMemStrm, 8 + nPropSetSize ); + mxMemStrm->Seek( STREAM_SEEK_TO_BEGIN ); + maDffConv.ReadPropSet( *mxMemStrm, 0 ); +} + +sal_uInt32 XclImpDffPropSet::GetPropertyValue( sal_uInt16 nPropId, sal_uInt32 nDefault ) const +{ + return maDffConv.GetPropertyValue( nPropId, nDefault ); +} + +void XclImpDffPropSet::FillToItemSet( SfxItemSet& rItemSet ) const +{ + if( mxMemStrm.get() ) + maDffConv.ApplyAttributes( *mxMemStrm, rItemSet ); +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclImpDffPropSet& rPropSet ) +{ + rPropSet.Read( rStrm ); + return rStrm; +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xiformula.cxx b/sc/source/filter/excel/xiformula.cxx new file mode 100644 index 000000000000..40c6ef9be374 --- /dev/null +++ b/sc/source/filter/excel/xiformula.cxx @@ -0,0 +1,127 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_sc.hxx" +#include "xiformula.hxx" +#include "rangelst.hxx" +#include "xistream.hxx" + +#include "excform.hxx" + +// Formula compiler =========================================================== + +/** Implementation class of the export formula compiler. */ +class XclImpFmlaCompImpl : protected XclImpRoot, protected XclTokenArrayHelper +{ +public: + explicit XclImpFmlaCompImpl( const XclImpRoot& rRoot ); + + /** Creates a range list from the passed Excel token array. */ + void CreateRangeList( + ScRangeList& rScRanges, XclFormulaType eType, + const XclTokenArray& rXclTokArr, XclImpStream& rStrm ); + + const ScTokenArray* CreateFormula( XclFormulaType eType, const XclTokenArray& rXclTokArr ); + + // ------------------------------------------------------------------------ +private: + XclFunctionProvider maFuncProv; /// Excel function data provider. + const XclBiff meBiff; /// Cached BIFF version to save GetBiff() calls. +}; + +// ---------------------------------------------------------------------------- + +XclImpFmlaCompImpl::XclImpFmlaCompImpl( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + maFuncProv( rRoot ), + meBiff( rRoot.GetBiff() ) +{ +} + +void XclImpFmlaCompImpl::CreateRangeList( + ScRangeList& rScRanges, XclFormulaType /*eType*/, + const XclTokenArray& rXclTokArr, XclImpStream& /*rStrm*/ ) +{ + rScRanges.RemoveAll(); + + //! evil hack, using old formula import :-) + if( !rXclTokArr.Empty() ) + { + SvMemoryStream aMemStrm; + aMemStrm << EXC_ID_EOF << rXclTokArr.GetSize(); + aMemStrm.Write( rXclTokArr.GetData(), rXclTokArr.GetSize() ); + XclImpStream aFmlaStrm( aMemStrm, GetRoot() ); + aFmlaStrm.StartNextRecord(); + GetOldFmlaConverter().GetAbsRefs( rScRanges, aFmlaStrm, aFmlaStrm.GetRecSize() ); + } +} + +const ScTokenArray* XclImpFmlaCompImpl::CreateFormula( + XclFormulaType /*eType*/, const XclTokenArray& rXclTokArr ) +{ + if (rXclTokArr.Empty()) + return NULL; + + // evil hack! are we trying to phase out the old style formula converter ? + SvMemoryStream aMemStrm; + aMemStrm << EXC_ID_EOF << rXclTokArr.GetSize(); + aMemStrm.Write( rXclTokArr.GetData(), rXclTokArr.GetSize() ); + XclImpStream aFmlaStrm( aMemStrm, GetRoot() ); + aFmlaStrm.StartNextRecord(); + const ScTokenArray* pArray = NULL; + GetOldFmlaConverter().Reset(); + GetOldFmlaConverter().Convert(pArray, aFmlaStrm, aFmlaStrm.GetRecSize(), true); + return pArray; +} + +// ---------------------------------------------------------------------------- + +XclImpFormulaCompiler::XclImpFormulaCompiler( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + mxImpl( new XclImpFmlaCompImpl( rRoot ) ) +{ +} + +XclImpFormulaCompiler::~XclImpFormulaCompiler() +{ +} + +void XclImpFormulaCompiler::CreateRangeList( + ScRangeList& rScRanges, XclFormulaType eType, + const XclTokenArray& rXclTokArr, XclImpStream& rStrm ) +{ + mxImpl->CreateRangeList( rScRanges, eType, rXclTokArr, rStrm ); +} + +const ScTokenArray* XclImpFormulaCompiler::CreateFormula( + XclFormulaType eType, const XclTokenArray& rXclTokArr ) +{ + return mxImpl->CreateFormula(eType, rXclTokArr); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xihelper.cxx b/sc/source/filter/excel/xihelper.cxx new file mode 100644 index 000000000000..ad04a1f3930c --- /dev/null +++ b/sc/source/filter/excel/xihelper.cxx @@ -0,0 +1,918 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xihelper.hxx" +#include <svl/itemset.hxx> +#include <editeng/editobj.hxx> +#include <tools/urlobj.hxx> +#include "scitems.hxx" +#include <editeng/eeitem.hxx> +#include <editeng/flditem.hxx> +#include "document.hxx" +#include "cell.hxx" +#include "rangelst.hxx" +#include "editutil.hxx" +#include "attrib.hxx" +#include "xltracer.hxx" +#include "xistream.hxx" +#include "xistyle.hxx" + +#include "excform.hxx" + +// Excel->Calc cell address/range conversion ================================== + +namespace { + +/** Fills the passed Calc address with the passed Excel cell coordinates without checking any limits. */ +inline void lclFillAddress( ScAddress& rScPos, sal_uInt16 nXclCol, sal_uInt16 nXclRow, SCTAB nScTab ) +{ + rScPos.SetCol( static_cast< SCCOL >( nXclCol ) ); + rScPos.SetRow( static_cast< SCROW >( nXclRow ) ); + rScPos.SetTab( nScTab ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclImpAddressConverter::XclImpAddressConverter( const XclImpRoot& rRoot ) : + XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetScMaxPos() ) +{ +} + +// cell address --------------------------------------------------------------- + +bool XclImpAddressConverter::CheckAddress( const XclAddress& rXclPos, bool bWarn ) +{ + bool bValidCol = rXclPos.mnCol <= mnMaxCol; + bool bValidRow = rXclPos.mnRow <= mnMaxRow; + bool bValid = bValidCol && bValidRow; + if( !bValid && bWarn ) + { + mbColTrunc |= !bValidCol; + mbRowTrunc |= !bValidRow; + mrTracer.TraceInvalidAddress( ScAddress( + static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), 0 ), maMaxPos ); + } + return bValid; +} + +bool XclImpAddressConverter::ConvertAddress( ScAddress& rScPos, + const XclAddress& rXclPos, SCTAB nScTab, bool bWarn ) +{ + bool bValid = CheckAddress( rXclPos, bWarn ); + if( bValid ) + lclFillAddress( rScPos, rXclPos.mnCol, rXclPos.mnRow, nScTab ); + return bValid; +} + +ScAddress XclImpAddressConverter::CreateValidAddress( + const XclAddress& rXclPos, SCTAB nScTab, bool bWarn ) +{ + ScAddress aScPos( ScAddress::UNINITIALIZED ); + if( !ConvertAddress( aScPos, rXclPos, nScTab, bWarn ) ) + { + aScPos.SetCol( static_cast< SCCOL >( ::std::min( rXclPos.mnCol, mnMaxCol ) ) ); + aScPos.SetRow( static_cast< SCROW >( ::std::min( rXclPos.mnRow, mnMaxRow ) ) ); + aScPos.SetTab( limit_cast< SCTAB >( nScTab, 0, maMaxPos.Tab() ) ); + } + return aScPos; +} + +// cell range ----------------------------------------------------------------- + +bool XclImpAddressConverter::CheckRange( const XclRange& rXclRange, bool bWarn ) +{ + return CheckAddress( rXclRange.maFirst, bWarn ) && CheckAddress( rXclRange.maLast, bWarn ); +} + +bool XclImpAddressConverter::ConvertRange( ScRange& rScRange, + const XclRange& rXclRange, SCTAB nScTab1, SCTAB nScTab2, bool bWarn ) +{ + // check start position + bool bValidStart = CheckAddress( rXclRange.maFirst, bWarn ); + if( bValidStart ) + { + lclFillAddress( rScRange.aStart, rXclRange.maFirst.mnCol, rXclRange.maFirst.mnRow, nScTab1 ); + + // check & correct end position + sal_uInt16 nXclCol2 = rXclRange.maLast.mnCol; + sal_uInt16 nXclRow2 = rXclRange.maLast.mnRow; + if( !CheckAddress( rXclRange.maLast, bWarn ) ) + { + nXclCol2 = ::std::min( nXclCol2, mnMaxCol ); + nXclRow2 = ::std::min( nXclRow2, mnMaxRow ); + } + lclFillAddress( rScRange.aEnd, nXclCol2, nXclRow2, nScTab2 ); + } + return bValidStart; +} + +//UNUSED2009-05 ScRange XclImpAddressConverter::CreateValidRange( +//UNUSED2009-05 const XclRange& rXclRange, SCTAB nScTab1, SCTAB nScTab2, bool bWarn ) +//UNUSED2009-05 { +//UNUSED2009-05 return ScRange( +//UNUSED2009-05 CreateValidAddress( rXclRange.maFirst, nScTab1, bWarn ), +//UNUSED2009-05 CreateValidAddress( rXclRange.maLast, nScTab2, bWarn ) ); +//UNUSED2009-05 } + +// cell range list ------------------------------------------------------------ + +//UNUSED2009-05 bool XclImpAddressConverter::CheckRangeList( const XclRangeList& rXclRanges, bool bWarn ) +//UNUSED2009-05 { +//UNUSED2009-05 for( XclRangeList::const_iterator aIt = rXclRanges.begin(), aEnd = rXclRanges.end(); aIt != aEnd; ++aIt ) +//UNUSED2009-05 if( !CheckRange( *aIt, bWarn ) ) +//UNUSED2009-05 return false; +//UNUSED2009-05 return true; +//UNUSED2009-05 } + +void XclImpAddressConverter::ConvertRangeList( ScRangeList& rScRanges, + const XclRangeList& rXclRanges, SCTAB nScTab, bool bWarn ) +{ + rScRanges.RemoveAll(); + for( XclRangeList::const_iterator aIt = rXclRanges.begin(), aEnd = rXclRanges.end(); aIt != aEnd; ++aIt ) + { + ScRange aScRange( ScAddress::UNINITIALIZED ); + if( ConvertRange( aScRange, *aIt, nScTab, nScTab, bWarn ) ) + rScRanges.Append( aScRange ); + } +} + +// String->EditEngine conversion ============================================== + +namespace { + +EditTextObject* lclCreateTextObject( const XclImpRoot& rRoot, + const XclImpString& rString, XclFontItemType eType, sal_uInt16 nXFIndex ) +{ + EditTextObject* pTextObj = 0; + + const XclImpXFBuffer& rXFBuffer = rRoot.GetXFBuffer(); + const XclImpFont* pFirstFont = rXFBuffer.GetFont( nXFIndex ); + bool bFirstEscaped = pFirstFont && pFirstFont->HasEscapement(); + + if( rString.IsRich() || bFirstEscaped ) + { + const XclImpFontBuffer& rFontBuffer = rRoot.GetFontBuffer(); + const XclFormatRunVec& rFormats = rString.GetFormats(); + + ScEditEngineDefaulter& rEE = (eType == EXC_FONTITEM_NOTE) ? + static_cast< ScEditEngineDefaulter& >( rRoot.GetDoc().GetNoteEngine() ) : rRoot.GetEditEngine(); + rEE.SetText( rString.GetText() ); + + SfxItemSet aItemSet( rEE.GetEmptyItemSet() ); + if( bFirstEscaped ) + rFontBuffer.FillToItemSet( aItemSet, eType, rXFBuffer.GetFontIndex( nXFIndex ) ); + ESelection aSelection; + + XclFormatRun aNextRun; + XclFormatRunVec::const_iterator aIt = rFormats.begin(); + XclFormatRunVec::const_iterator aEnd = rFormats.end(); + + if( aIt != aEnd ) + aNextRun = *aIt++; + else + aNextRun.mnChar = 0xFFFF; + + xub_StrLen nLen = rString.GetText().Len(); + for( sal_uInt16 nChar = 0; nChar < nLen; ++nChar ) + { + // reached new different formatted text portion + if( nChar >= aNextRun.mnChar ) + { + // send items to edit engine + rEE.QuickSetAttribs( aItemSet, aSelection ); + + // start new item set + aItemSet.ClearItem(); + rFontBuffer.FillToItemSet( aItemSet, eType, aNextRun.mnFontIdx ); + + // read new formatting information + if( aIt != aEnd ) + aNextRun = *aIt++; + else + aNextRun.mnChar = 0xFFFF; + + // reset selection start to current position + aSelection.nStartPara = aSelection.nEndPara; + aSelection.nStartPos = aSelection.nEndPos; + } + + // set end of selection to current position + if( rString.GetText().GetChar( nChar ) == '\n' ) + { + ++aSelection.nEndPara; + aSelection.nEndPos = 0; + } + else + ++aSelection.nEndPos; + } + + // send items of last text portion to edit engine + rEE.QuickSetAttribs( aItemSet, aSelection ); + + pTextObj = rEE.CreateTextObject(); + } + + return pTextObj; +} + +} // namespace + +EditTextObject* XclImpStringHelper::CreateTextObject( + const XclImpRoot& rRoot, const XclImpString& rString ) +{ + return lclCreateTextObject( rRoot, rString, EXC_FONTITEM_EDITENG, 0 ); +} + +//UNUSED2009-05 EditTextObject* XclImpStringHelper::CreateNoteObject( +//UNUSED2009-05 const XclImpRoot& rRoot, const XclImpString& rString ) +//UNUSED2009-05 { +//UNUSED2009-05 return lclCreateTextObject( rRoot, rString, EXC_FONTITEM_NOTE, 0 ); +//UNUSED2009-05 } + +ScBaseCell* XclImpStringHelper::CreateCell( + const XclImpRoot& rRoot, const XclImpString& rString, sal_uInt16 nXFIndex ) +{ + ScBaseCell* pCell = 0; + + if( rString.GetText().Len() ) + { + ::std::auto_ptr< EditTextObject > pTextObj( lclCreateTextObject( rRoot, rString, EXC_FONTITEM_EDITENG, nXFIndex ) ); + ScDocument& rDoc = rRoot.GetDoc(); + + if( pTextObj.get() ) + // ScEditCell creates own copy of text object + pCell = new ScEditCell( pTextObj.get(), &rDoc, rRoot.GetEditEngine().GetEditTextObjectPool() ); + else + pCell = ScBaseCell::CreateTextCell( rString.GetText(), &rDoc ); + } + + return pCell; +} + +// Header/footer conversion =================================================== + +XclImpHFConverter::XclImpHFPortionInfo::XclImpHFPortionInfo() : + mnHeight( 0 ), + mnMaxLineHt( 0 ) +{ + maSel.nStartPara = maSel.nEndPara = 0; + maSel.nStartPos = maSel.nEndPos = 0; +} + +// ---------------------------------------------------------------------------- + +XclImpHFConverter::XclImpHFConverter( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + mrEE( rRoot.GetHFEditEngine() ), + mxFontData( new XclFontData ), + meCurrObj( EXC_HF_CENTER ) +{ +} + +XclImpHFConverter::~XclImpHFConverter() +{ +} + +void XclImpHFConverter::ParseString( const String& rHFString ) +{ + // edit engine objects + mrEE.SetText( EMPTY_STRING ); + maInfos.clear(); + maInfos.resize( EXC_HF_PORTION_COUNT ); + meCurrObj = EXC_HF_CENTER; + + // parser temporaries + maCurrText.Erase(); + String aReadFont; // current font name + String aReadStyle; // current font style + sal_uInt16 nReadHeight = 0; // current font height + ResetFontData(); + + /** State of the parser. */ + enum XclHFParserState + { + xlPSText, /// Read text, search for functions. + xlPSFunc, /// Read function (token following a '&'). + xlPSFont, /// Read font name ('&' is followed by '"', reads until next '"' or ','). + xlPSFontStyle, /// Read font style name (font part after ',', reads until next '"'). + xlPSHeight /// Read font height ('&' is followed by num. digits, reads until non-digit). + } eState = xlPSText; + + const sal_Unicode* pChar = rHFString.GetBuffer(); + const sal_Unicode* pNull = pChar + rHFString.Len(); // pointer to teminating null char + while( *pChar ) + { + switch( eState ) + { + +// --- read text character --- + + case xlPSText: + { + switch( *pChar ) + { + case '&': // new command + InsertText(); + eState = xlPSFunc; + break; + case '\n': // line break + InsertText(); + InsertLineBreak(); + break; + default: + maCurrText += *pChar; + } + } + break; + +// --- read control sequence --- + + case xlPSFunc: + { + eState = xlPSText; + switch( *pChar ) + { + case '&': maCurrText += '&'; break; // the '&' character + + case 'L': SetNewPortion( EXC_HF_LEFT ); break; // Left portion + case 'C': SetNewPortion( EXC_HF_CENTER ); break; // Center portion + case 'R': SetNewPortion( EXC_HF_RIGHT ); break; // Right portion + + case 'P': InsertField( SvxFieldItem( SvxPageField(), EE_FEATURE_FIELD ) ); break; // page + case 'N': InsertField( SvxFieldItem( SvxPagesField(), EE_FEATURE_FIELD ) ); break; // page count + case 'D': InsertField( SvxFieldItem( SvxDateField(), EE_FEATURE_FIELD ) ); break; // date + case 'T': InsertField( SvxFieldItem( SvxTimeField(), EE_FEATURE_FIELD ) ); break; // time + case 'A': InsertField( SvxFieldItem( SvxTableField(), EE_FEATURE_FIELD ) ); break; // table name + + case 'Z': // file path + InsertField( SvxFieldItem( SvxExtFileField(), EE_FEATURE_FIELD ) ); // convert to full name + if( (pNull - pChar >= 2) && (*(pChar + 1) == '&') && (*(pChar + 2) == 'F') ) + { + // &Z&F found - ignore the &F part + pChar += 2; + } + break; + case 'F': // file name + InsertField( SvxFieldItem( SvxExtFileField( EMPTY_STRING, SVXFILETYPE_VAR, SVXFILEFORMAT_NAME_EXT ), EE_FEATURE_FIELD ) ); + break; + + case 'U': // underline + SetAttribs(); + mxFontData->mnUnderline = (mxFontData->mnUnderline == EXC_FONTUNDERL_SINGLE) ? + EXC_FONTUNDERL_NONE : EXC_FONTUNDERL_SINGLE; + break; + case 'E': // double underline + SetAttribs(); + mxFontData->mnUnderline = (mxFontData->mnUnderline == EXC_FONTUNDERL_DOUBLE) ? + EXC_FONTUNDERL_NONE : EXC_FONTUNDERL_DOUBLE; + break; + case 'S': // strikeout + SetAttribs(); + mxFontData->mbStrikeout = !mxFontData->mbStrikeout; + break; + case 'X': // superscript + SetAttribs(); + mxFontData->mnEscapem = (mxFontData->mnEscapem == EXC_FONTESC_SUPER) ? + EXC_FONTESC_NONE : EXC_FONTESC_SUPER; + break; + case 'Y': // subsrcipt + SetAttribs(); + mxFontData->mnEscapem = (mxFontData->mnEscapem == EXC_FONTESC_SUB) ? + EXC_FONTESC_NONE : EXC_FONTESC_SUB; + break; + + case '\"': // font name + aReadFont.Erase(); + aReadStyle.Erase(); + eState = xlPSFont; + break; + default: + if( ('0' <= *pChar) && (*pChar <= '9') ) // font size + { + nReadHeight = *pChar - '0'; + eState = xlPSHeight; + } + } + } + break; + +// --- read font name --- + + case xlPSFont: + { + switch( *pChar ) + { + case '\"': + --pChar; + // run through + case ',': + eState = xlPSFontStyle; + break; + default: + aReadFont += *pChar; + } + } + break; + +// --- read font style --- + + case xlPSFontStyle: + { + switch( *pChar ) + { + case '\"': + SetAttribs(); + if( aReadFont.Len() ) + mxFontData->maName = aReadFont; + mxFontData->maStyle = aReadStyle; + eState = xlPSText; + break; + default: + aReadStyle += *pChar; + } + } + break; + +// --- read font height --- + + case xlPSHeight: + { + if( ('0' <= *pChar) && (*pChar <= '9') ) + { + if( nReadHeight != 0xFFFF ) + { + nReadHeight *= 10; + nReadHeight += (*pChar - '0'); + if( nReadHeight > 1600 ) // max 1600pt = 32000twips + nReadHeight = 0xFFFF; + } + } + else + { + if( (nReadHeight != 0) && (nReadHeight != 0xFFFF) ) + { + SetAttribs(); + mxFontData->mnHeight = nReadHeight * 20; + } + --pChar; + eState = xlPSText; + } + } + break; + } + ++pChar; + } + + // finalize + CreateCurrObject(); + maInfos[ EXC_HF_LEFT ].mnHeight += GetMaxLineHeight( EXC_HF_LEFT ); + maInfos[ EXC_HF_CENTER ].mnHeight += GetMaxLineHeight( EXC_HF_CENTER ); + maInfos[ EXC_HF_RIGHT ].mnHeight += GetMaxLineHeight( EXC_HF_RIGHT ); +} + +void XclImpHFConverter::FillToItemSet( SfxItemSet& rItemSet, sal_uInt16 nWhichId ) const +{ + ScPageHFItem aHFItem( nWhichId ); + if( maInfos[ EXC_HF_LEFT ].mxObj.get() ) + aHFItem.SetLeftArea( *maInfos[ EXC_HF_LEFT ].mxObj ); + if( maInfos[ EXC_HF_CENTER ].mxObj.get() ) + aHFItem.SetCenterArea( *maInfos[ EXC_HF_CENTER ].mxObj ); + if( maInfos[ EXC_HF_RIGHT ].mxObj.get() ) + aHFItem.SetRightArea( *maInfos[ EXC_HF_RIGHT ].mxObj ); + rItemSet.Put( aHFItem ); +} + +sal_Int32 XclImpHFConverter::GetTotalHeight() const +{ + return ::std::max( maInfos[ EXC_HF_LEFT ].mnHeight, + ::std::max( maInfos[ EXC_HF_CENTER ].mnHeight, maInfos[ EXC_HF_RIGHT ].mnHeight ) ); +} + +// private -------------------------------------------------------------------- + +sal_uInt16 XclImpHFConverter::GetMaxLineHeight( XclImpHFPortion ePortion ) const +{ + sal_uInt16 nMaxHt = maInfos[ ePortion ].mnMaxLineHt; + return (nMaxHt == 0) ? mxFontData->mnHeight : nMaxHt; +} + +sal_uInt16 XclImpHFConverter::GetCurrMaxLineHeight() const +{ + return GetMaxLineHeight( meCurrObj ); +} + +void XclImpHFConverter::UpdateMaxLineHeight( XclImpHFPortion ePortion ) +{ + sal_uInt16& rnMaxHt = maInfos[ ePortion ].mnMaxLineHt; + rnMaxHt = ::std::max( rnMaxHt, mxFontData->mnHeight ); +} + +void XclImpHFConverter::UpdateCurrMaxLineHeight() +{ + UpdateMaxLineHeight( meCurrObj ); +} + +void XclImpHFConverter::SetAttribs() +{ + ESelection& rSel = GetCurrSel(); + if( (rSel.nStartPara != rSel.nEndPara) || (rSel.nStartPos != rSel.nEndPos) ) + { + SfxItemSet aItemSet( mrEE.GetEmptyItemSet() ); + XclImpFont aFont( GetRoot(), *mxFontData ); + aFont.FillToItemSet( aItemSet, EXC_FONTITEM_HF ); + mrEE.QuickSetAttribs( aItemSet, rSel ); + rSel.nStartPara = rSel.nEndPara; + rSel.nStartPos = rSel.nEndPos; + } +} + +void XclImpHFConverter::ResetFontData() +{ + if( const XclImpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) ) + *mxFontData = pFirstFont->GetFontData(); + else + { + mxFontData->Clear(); + mxFontData->mnHeight = 200; + } +} + +void XclImpHFConverter::InsertText() +{ + if( maCurrText.Len() ) + { + ESelection& rSel = GetCurrSel(); + mrEE.QuickInsertText( maCurrText, ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) ); + rSel.nEndPos = rSel.nEndPos + maCurrText.Len(); + maCurrText.Erase(); + UpdateCurrMaxLineHeight(); + } +} + +void XclImpHFConverter::InsertField( const SvxFieldItem& rFieldItem ) +{ + ESelection& rSel = GetCurrSel(); + mrEE.QuickInsertField( rFieldItem, ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) ); + ++rSel.nEndPos; + UpdateCurrMaxLineHeight(); +} + +void XclImpHFConverter::InsertLineBreak() +{ + ESelection& rSel = GetCurrSel(); + mrEE.QuickInsertText( String( '\n' ), ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) ); + ++rSel.nEndPara; + rSel.nEndPos = 0; + GetCurrInfo().mnHeight += GetCurrMaxLineHeight(); + GetCurrInfo().mnMaxLineHt = 0; +} + +void XclImpHFConverter::CreateCurrObject() +{ + InsertText(); + SetAttribs(); + GetCurrObj().reset( mrEE.CreateTextObject() ); +} + +void XclImpHFConverter::SetNewPortion( XclImpHFPortion eNew ) +{ + if( eNew != meCurrObj ) + { + CreateCurrObject(); + meCurrObj = eNew; + if( GetCurrObj().get() ) + mrEE.SetText( *GetCurrObj() ); + else + mrEE.SetText( EMPTY_STRING ); + ResetFontData(); + } +} + +// URL conversion ============================================================= + +namespace { + +void lclAppendUrlChar( String& rUrl, sal_Unicode cChar ) +{ + // #126855# encode special characters + switch( cChar ) + { + case '#': rUrl.AppendAscii( "%23" ); break; + case '%': rUrl.AppendAscii( "%25" ); break; + default: rUrl.Append( cChar ); + } +} + +} // namespace + +void XclImpUrlHelper::DecodeUrl( + String& rUrl, String& rTabName, bool& rbSameWb, + const XclImpRoot& rRoot, const String& rEncodedUrl ) +{ + enum + { + xlUrlInit, /// Initial state, read string mode character. + xlUrlPath, /// Read URL path. + xlUrlFileName, /// Read file name. + xlUrlSheetName, /// Read sheet name. + xlUrlRaw /// Raw mode. No control characters will occur. + } eState = xlUrlInit; + + bool bEncoded = true; + rbSameWb = false; + + sal_Unicode cCurrDrive = 0; + String aDosBase( INetURLObject( rRoot.GetBasePath() ).getFSysPath( INetURLObject::FSYS_DOS ) ); + if( (aDosBase.Len() > 2) && aDosBase.EqualsAscii( ":\\", 1, 2 ) ) + cCurrDrive = aDosBase.GetChar( 0 ); + + const sal_Unicode* pChar = rEncodedUrl.GetBuffer(); + while( *pChar ) + { + switch( eState ) + { + +// --- first character --- + + case xlUrlInit: + { + switch( *pChar ) + { + case EXC_URLSTART_ENCODED: + eState = xlUrlPath; + break; + case EXC_URLSTART_SELF: + case EXC_URLSTART_SELFENCODED: + rbSameWb = true; + eState = xlUrlSheetName; + break; + case '[': + bEncoded = false; + eState = xlUrlFileName; + break; + default: + bEncoded = false; + lclAppendUrlChar( rUrl, *pChar ); + eState = xlUrlPath; + } + } + break; + +// --- URL path --- + + case xlUrlPath: + { + switch( *pChar ) + { + case EXC_URL_DOSDRIVE: + { + if( *(pChar + 1) ) + { + ++pChar; + if( *pChar == '@' ) + rUrl.AppendAscii( "\\\\" ); + else + { + lclAppendUrlChar( rUrl, *pChar ); + rUrl.AppendAscii( ":\\" ); + } + } + else + rUrl.AppendAscii( "<NULL-DRIVE!>" ); + } + break; + case EXC_URL_DRIVEROOT: + if( cCurrDrive ) + { + lclAppendUrlChar( rUrl, cCurrDrive ); + rUrl.Append( ':' ); + } + // run through + case EXC_URL_SUBDIR: + if( bEncoded ) + rUrl.Append( '\\' ); + else // control character in raw name -> DDE link + { + rUrl.Append( EXC_DDE_DELIM ); + eState = xlUrlRaw; + } + break; + case EXC_URL_PARENTDIR: + rUrl.AppendAscii( "..\\" ); + break; + case EXC_URL_RAW: + { + if( *(pChar + 1) ) + { + xub_StrLen nLen = *++pChar; + for( xub_StrLen nChar = 0; (nChar < nLen) && *(pChar + 1); ++nChar ) + lclAppendUrlChar( rUrl, *++pChar ); +// rUrl.Append( ':' ); + } + } + break; + case '[': + eState = xlUrlFileName; + break; + default: + lclAppendUrlChar( rUrl, *pChar ); + } + } + break; + +// --- file name --- + + case xlUrlFileName: + { + switch( *pChar ) + { + case ']': eState = xlUrlSheetName; break; + default: lclAppendUrlChar( rUrl, *pChar ); + } + } + break; + +// --- sheet name --- + + case xlUrlSheetName: + rTabName.Append( *pChar ); + break; + +// --- raw read mode --- + + case xlUrlRaw: + lclAppendUrlChar( rUrl, *pChar ); + break; + } + + ++pChar; + } +} + +void XclImpUrlHelper::DecodeUrl( + String& rUrl, bool& rbSameWb, const XclImpRoot& rRoot, const String& rEncodedUrl ) +{ + String aTabName; + DecodeUrl( rUrl, aTabName, rbSameWb, rRoot, rEncodedUrl ); + DBG_ASSERT( !aTabName.Len(), "XclImpUrlHelper::DecodeUrl - sheet name ignored" ); +} + +bool XclImpUrlHelper::DecodeLink( String& rApplic, String& rTopic, const String rEncUrl ) +{ + xub_StrLen nPos = rEncUrl.Search( EXC_DDE_DELIM ); + if( (nPos != STRING_NOTFOUND) && (0 < nPos) && (nPos + 1 < rEncUrl.Len()) ) + { + rApplic = rEncUrl.Copy( 0, nPos ); + rTopic = rEncUrl.Copy( nPos + 1 ); + return true; + } + return false; +} + +// Cached Values ============================================================== + +XclImpCachedValue::XclImpCachedValue( XclImpStream& rStrm ) : + mfValue( 0.0 ), + mnBoolErr( 0 ) +{ + rStrm >> mnType; + switch( mnType ) + { + case EXC_CACHEDVAL_EMPTY: + rStrm.Ignore( 8 ); + break; + case EXC_CACHEDVAL_DOUBLE: + rStrm >> mfValue; + break; + case EXC_CACHEDVAL_STRING: + mxStr.reset( new String( rStrm.ReadUniString() ) ); + break; + case EXC_CACHEDVAL_BOOL: + case EXC_CACHEDVAL_ERROR: + { + double fVal; + rStrm >> mnBoolErr; + rStrm.Ignore( 7 ); + + const ScTokenArray* pScTokArr = rStrm.GetRoot().GetOldFmlaConverter().GetBoolErr( + XclTools::ErrorToEnum( fVal, mnType == EXC_CACHEDVAL_ERROR, mnBoolErr ) ); + if( pScTokArr ) + mxTokArr.reset( pScTokArr->Clone() ); + } + break; + default: + DBG_ERRORFILE( "XclImpCachedValue::XclImpCachedValue - unknown data type" ); + } +} + +XclImpCachedValue::~XclImpCachedValue() +{ +} + +USHORT XclImpCachedValue::GetScError() const +{ + return (mnType == EXC_CACHEDVAL_ERROR) ? XclTools::GetScErrorCode( mnBoolErr ) : 0; +} + +// Matrix Cached Values ============================================================== + +XclImpCachedMatrix::XclImpCachedMatrix( XclImpStream& rStrm ) : + mnScCols( 0 ), + mnScRows( 0 ) +{ + mnScCols = rStrm.ReaduInt8(); + mnScRows = rStrm.ReaduInt16(); + + if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 ) + { + // in BIFF2-BIFF7: 256 columns represented by 0 columns + if( mnScCols == 0 ) + mnScCols = 256; + } + else + { + // in BIFF8: columns and rows decreaed by 1 + ++mnScCols; + ++mnScRows; + } + + for( SCSIZE nScRow = 0; nScRow < mnScRows; ++nScRow ) + for( SCSIZE nScCol = 0; nScCol < mnScCols; ++nScCol ) + maValueList.Append( new XclImpCachedValue( rStrm ) ); +} + +XclImpCachedMatrix::~XclImpCachedMatrix() +{ +} + +ScMatrixRef XclImpCachedMatrix::CreateScMatrix() const +{ + ScMatrixRef xScMatrix; + DBG_ASSERT( mnScCols * mnScRows == maValueList.Count(), "XclImpCachedMatrix::CreateScMatrix - element count mismatch" ); + if( mnScCols && mnScRows && static_cast< ULONG >( mnScCols * mnScRows ) <= maValueList.Count() ) + { + xScMatrix = new ScMatrix( mnScCols, mnScRows ); + const XclImpCachedValue* pValue = maValueList.First(); + for( SCSIZE nScRow = 0; nScRow < mnScRows; ++nScRow ) + { + for( SCSIZE nScCol = 0; nScCol < mnScCols; ++nScCol ) + { + switch( pValue->GetType() ) + { + case EXC_CACHEDVAL_EMPTY: + // Excel shows 0.0 here, not an empty cell + xScMatrix->PutEmpty( nScCol, nScRow ); + break; + case EXC_CACHEDVAL_DOUBLE: + xScMatrix->PutDouble( pValue->GetValue(), nScCol, nScRow ); + break; + case EXC_CACHEDVAL_STRING: + xScMatrix->PutString( pValue->GetString(), nScCol, nScRow ); + break; + case EXC_CACHEDVAL_BOOL: + xScMatrix->PutBoolean( pValue->GetBool(), nScCol, nScRow ); + break; + case EXC_CACHEDVAL_ERROR: + xScMatrix->PutError( pValue->GetScError(), nScCol, nScRow ); + break; + default: + DBG_ERRORFILE( "XclImpCachedMatrix::CreateScMatrix - unknown value type" ); + xScMatrix->PutEmpty( nScCol, nScRow ); + } + pValue = maValueList.Next(); + } + } + } + return xScMatrix; +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xilink.cxx b/sc/source/filter/excel/xilink.cxx new file mode 100644 index 000000000000..dc2234570ea6 --- /dev/null +++ b/sc/source/filter/excel/xilink.cxx @@ -0,0 +1,807 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xilink.hxx" +#include "document.hxx" +#include "cell.hxx" +#include "scextopt.hxx" +#include "tablink.hxx" +#include "xistream.hxx" +#include "xihelper.hxx" +#include "xiname.hxx" +#include "excform.hxx" +#include "tokenarray.hxx" +#include "externalrefmgr.hxx" + +#include <vector> + +using ::std::vector; + +// ============================================================================ +// *** Helper classes *** +// ============================================================================ + +// Cached external cells ====================================================== + +/** Contains the address and value of an external referenced cell. */ +class XclImpCrn : public XclImpCachedValue +{ +public: + /** Reads a cached value and stores it with its cell address. */ + explicit XclImpCrn( XclImpStream& rStrm, const XclAddress& rXclPos ); + + const XclAddress& GetAddress() const; + +private: + XclAddress maXclPos; /// Excel position of the cached cell. +}; + +// Sheet in an external document ============================================== + +/** Contains the name and sheet index of one sheet in an external document. */ +class XclImpSupbookTab +{ +public: + /** Stores the sheet name and marks the sheet index as invalid. + The sheet index is set while creating the Calc sheet with CreateTable(). */ + explicit XclImpSupbookTab( const String& rTabName ); + ~XclImpSupbookTab(); + + inline const String& GetTabName() const { return maTabName; } + + /** Reads a CRN record (external referenced cell) at the specified address. */ + void ReadCrn( XclImpStream& rStrm, const XclAddress& rXclPos ); + + void LoadCachedValues(ScExternalRefCache::TableTypeRef pCacheTable); + +private: + typedef ScfDelList< XclImpCrn > XclImpCrnList; + + XclImpCrnList maCrnList; /// List of CRN records (cached cell values). + String maTabName; /// Name of the external sheet. + SCTAB mnScTab; /// New sheet index in Calc document. +}; + +// External document (SUPBOOK) ================================================ + +/** This class represents an external linked document (record SUPBOOK). + @descr Contains a list of all referenced sheets in the document. */ +class XclImpSupbook : protected XclImpRoot +{ +public: + /** Reads the SUPBOOK record from stream. */ + explicit XclImpSupbook( XclImpStream& rStrm ); + + /** Reads an XCT record (count of following CRNs and current sheet). */ + void ReadXct( XclImpStream& rStrm ); + /** Reads a CRN record (external referenced cell). */ + void ReadCrn( XclImpStream& rStrm ); + /** Reads an EXTERNNAME record. */ + void ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv = NULL ); + + /** Returns the SUPBOOK record type. */ + inline XclSupbookType GetType() const { return meType; } + + /** Returns the URL of the external document. */ + inline const String& GetXclUrl() const { return maXclUrl; } + + /** Returns the external name specified by an index from the Excel document (one-based). */ + const XclImpExtName* GetExternName( sal_uInt16 nXclIndex ) const; + /** Tries to decode the URL to OLE or DDE link components. + @descr For DDE links: Decodes to application name and topic. + For OLE object links: Decodes to class name and document URL. + @return true = decoding was successful, returned strings are valid (not empty). */ + bool GetLinkData( String& rApplic, String& rDoc ) const; + /** Returns the specified macro name (1-based) or an empty string on error. */ + const String& GetMacroName( sal_uInt16 nXclNameIdx ) const; + + const String& GetTabName( sal_uInt16 nXtiTab ) const; + + sal_uInt16 GetTabCount() const; + + void LoadCachedValues(); + +private: + typedef ScfDelList< XclImpSupbookTab > XclImpSupbookTabList; + typedef ScfDelList< XclImpExtName > XclImpExtNameList; + + XclImpSupbookTabList maSupbTabList; /// All sheet names of the document. + XclImpExtNameList maExtNameList; /// All external names of the document. + String maXclUrl; /// URL of the external document (Excel mode). + String maFilterName; /// Detected filer name. + String maFilterOpt; /// Detected filer options. + XclSupbookType meType; /// Type of the supbook record. + sal_uInt16 mnSBTab; /// Current Excel sheet index from SUPBOOK for XCT/CRN records. +}; + +// Import link manager ======================================================== + +/** Contains the SUPBOOK index and sheet indexes of an external link. + @descr It is possible to enter a formula like =SUM(Sheet1:Sheet3!A1), + therefore here occurs a sheet range. */ +struct XclImpXti +{ + sal_uInt16 mnSupbook; /// Index to SUPBOOK record. + sal_uInt16 mnSBTabFirst; /// Index to the first sheet of the range in the SUPBOOK. + sal_uInt16 mnSBTabLast; /// Index to the last sheet of the range in the SUPBOOK. + inline explicit XclImpXti() : mnSupbook( SAL_MAX_UINT16 ), mnSBTabFirst( SAL_MAX_UINT16 ), mnSBTabLast( SAL_MAX_UINT16 ) {} +}; + +inline XclImpStream& operator>>( XclImpStream& rStrm, XclImpXti& rXti ) +{ + return rStrm >> rXti.mnSupbook >> rXti.mnSBTabFirst >> rXti.mnSBTabLast; +} + +// ---------------------------------------------------------------------------- + +/** Implementation of the link manager. */ +class XclImpLinkManagerImpl : protected XclImpRoot +{ +public: + explicit XclImpLinkManagerImpl( const XclImpRoot& rRoot ); + + /** Reads the EXTERNSHEET record. */ + void ReadExternsheet( XclImpStream& rStrm ); + /** Reads a SUPBOOK record. */ + void ReadSupbook( XclImpStream& rStrm ); + /** Reads an XCT record and appends it to the current SUPBOOK. */ + void ReadXct( XclImpStream& rStrm ); + /** Reads a CRN record and appends it to the current SUPBOOK. */ + void ReadCrn( XclImpStream& rStrm ); + /** Reads an EXTERNNAME record and appends it to the current SUPBOOK. */ + void ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv = NULL ); + + /** Returns true, if the specified XTI entry contains an internal reference. */ + bool IsSelfRef( sal_uInt16 nXtiIndex ) const; + /** Returns the Calc sheet index range of the specified XTI entry. + @return true = XTI data found, returned sheet index range is valid. */ + bool GetScTabRange( + SCTAB& rnFirstScTab, SCTAB& rnLastScTab, + sal_uInt16 nXtiIndex ) const; + /** Returns the specified external name or 0 on error. */ + const XclImpExtName* GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const; + + /** Returns the absolute file URL of a supporting workbook specified by + the index. */ + const String* GetSupbookUrl( sal_uInt16 nXtiIndex ) const; + + const String& GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const; + + /** Tries to decode the URL of the specified XTI entry to OLE or DDE link components. + @descr For DDE links: Decodes to application name and topic. + For OLE object links: Decodes to class name and document URL. + @return true = decoding was successful, returned strings are valid (not empty). */ + bool GetLinkData( String& rApplic, String& rTopic, sal_uInt16 nXtiIndex ) const; + /** Returns the specified macro name or an empty string on error. */ + const String& GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const; + +private: + /** Returns the specified XTI (link entry from BIFF8 EXTERNSHEET record). */ + const XclImpXti* GetXti( sal_uInt16 nXtiIndex ) const; + /** Returns the specified SUPBOOK (external document). */ + const XclImpSupbook* GetSupbook( sal_uInt16 nXtiIndex ) const; +//UNUSED2009-05 /** Returns the SUPBOOK (external workbook) specified by its URL. */ +//UNUSED2009-05 const XclImpSupbook* GetSupbook( const String& rUrl ) const; + + void LoadCachedValues(); + +//UNUSED2009-05 /** Finds the largest range of sheet indexes in a SUPBOOK after a start sheet index. +//UNUSED2009-05 @param rnSBTabFirst (out-param) The first sheet index of the range in SUPBOOK is returned here. +//UNUSED2009-05 @param rnSBTabLast (out-param) The last sheet index of the range in SUPBOOK is returned here (inclusive). +//UNUSED2009-05 @param nSupbook The list index of the SUPBOOK. +//UNUSED2009-05 @param nSBTabStart The first allowed sheet index. Sheet ranges with an earlier start index are ignored. +//UNUSED2009-05 @return true = the return values are valid; false = nothing found. */ +//UNUSED2009-05 bool FindNextTabRange( +//UNUSED2009-05 sal_uInt16& rnSBTabFirst, sal_uInt16& rnSBTabLast, +//UNUSED2009-05 sal_uInt16 nSupbook, sal_uInt16 nSBTabStart ) const; + +private: + typedef ::std::vector< XclImpXti > XclImpXtiVector; + typedef ScfDelList< XclImpSupbook > XclImpSupbookList; + + XclImpXtiVector maXtiList; /// List of all XTI structures. + XclImpSupbookList maSupbookList; /// List of external documents. + bool mbCreated; /// true = Calc sheets already created. +}; + +// ============================================================================ +// *** Implementation *** +// ============================================================================ + +// Excel sheet indexes ======================================================== + +// original Excel sheet names ------------------------------------------------- + +void XclImpTabInfo::AppendXclTabName( const String& rXclTabName, SCTAB nScTab ) +{ + maTabNames[ rXclTabName ] = nScTab; +} + +void XclImpTabInfo::InsertScTab( SCTAB nScTab ) +{ + for( XclTabNameMap::iterator aIt = maTabNames.begin(), aEnd = maTabNames.end(); aIt != aEnd; ++aIt ) + if( aIt->second >= nScTab ) + ++aIt->second; +} + +SCTAB XclImpTabInfo::GetScTabFromXclName( const String& rXclTabName ) const +{ + XclTabNameMap::const_iterator aIt = maTabNames.find( rXclTabName ); + return (aIt != maTabNames.end()) ? aIt->second : SCTAB_INVALID; +} + +// record creation order - TABID record --------------------------------------- + +void XclImpTabInfo::ReadTabid( XclImpStream& rStrm ) +{ + DBG_ASSERT_BIFF( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ); + if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ) + { + rStrm.EnableDecryption(); + sal_Size nReadCount = rStrm.GetRecLeft() / 2; + DBG_ASSERT( nReadCount <= 0xFFFF, "XclImpTabInfo::ReadTabid - record too long" ); + maTabIdVec.clear(); + maTabIdVec.reserve( nReadCount ); + for( sal_Size nIndex = 0; rStrm.IsValid() && (nIndex < nReadCount); ++nIndex ) + // #93471# zero index is not allowed in BIFF8, but it seems that it occurs in real life + maTabIdVec.push_back( rStrm.ReaduInt16() ); + } +} + +sal_uInt16 XclImpTabInfo::GetCurrentIndex( sal_uInt16 nCreatedId, sal_uInt16 nMaxTabId ) const +{ + sal_uInt16 nReturn = 0; + for( ScfUInt16Vec::const_iterator aIt = maTabIdVec.begin(), aEnd = maTabIdVec.end(); aIt != aEnd; ++aIt ) + { + sal_uInt16 nValue = *aIt; + if( nValue == nCreatedId ) + return nReturn; + if( nValue <= nMaxTabId ) + ++nReturn; + } + return 0; +} + +// External names ============================================================= + +XclImpExtName::XclImpExtName( const XclImpSupbook& rSupbook, XclImpStream& rStrm, XclSupbookType eSubType, ExcelToSc* pFormulaConv ) +{ + sal_uInt16 nFlags; + sal_uInt8 nLen; + + rStrm >> nFlags >> mnStorageId >> nLen ; + maName = rStrm.ReadUniString( nLen ); + if( ::get_flag( nFlags, EXC_EXTN_BUILTIN ) || !::get_flag( nFlags, EXC_EXTN_OLE_OR_DDE ) ) + { + if( eSubType == EXC_SBTYPE_ADDIN ) + { + meType = xlExtAddIn; + maName = rStrm.GetRoot().GetScAddInName( maName ); + } + else if ( (eSubType == EXC_SBTYPE_EUROTOOL) && + maName.EqualsIgnoreCaseAscii( "EUROCONVERT" ) ) + meType = xlExtEuroConvert; + else + { + meType = xlExtName; + ScfTools::ConvertToScDefinedName( maName ); + } + } + else + { + meType = ::get_flagvalue( nFlags, EXC_EXTN_OLE, xlExtOLE, xlExtDDE ); + } + + if( (meType == xlExtDDE) && (rStrm.GetRecLeft() > 1) ) + mxDdeMatrix.reset( new XclImpCachedMatrix( rStrm ) ); + + if (meType == xlExtName) + { + // TODO: For now, only global external names are supported. In future + // we should extend this to supporting per-sheet external names. + if (mnStorageId == 0) + { + if (pFormulaConv) + { + const ScTokenArray* pArray = NULL; + sal_uInt16 nFmlaLen; + rStrm >> nFmlaLen; + vector<String> aTabNames; + sal_uInt16 nCount = rSupbook.GetTabCount(); + aTabNames.reserve(nCount); + for (sal_uInt16 i = 0; i < nCount; ++i) + aTabNames.push_back(rSupbook.GetTabName(i)); + + pFormulaConv->ConvertExternName(pArray, rStrm, nFmlaLen, rSupbook.GetXclUrl(), aTabNames); + if (pArray) + mxArray.reset(pArray->Clone()); + } + } + } +} + +XclImpExtName::~XclImpExtName() +{ +} + +void XclImpExtName::CreateDdeData( ScDocument& rDoc, const String& rApplic, const String& rTopic ) const +{ + ScMatrixRef xResults; + if( mxDdeMatrix.get() ) + xResults = mxDdeMatrix->CreateScMatrix(); + rDoc.CreateDdeLink( rApplic, rTopic, maName, SC_DDE_DEFAULT, xResults ); +} + +void XclImpExtName::CreateExtNameData( ScDocument& rDoc, sal_uInt16 nFileId ) const +{ + if (!mxArray.get()) + return; + + ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager(); + pRefMgr->storeRangeNameTokens(nFileId, maName, *mxArray); +} + +bool XclImpExtName::HasFormulaTokens() const +{ + return (mxArray.get() != NULL); +} + +// Cached external cells ====================================================== + +XclImpCrn::XclImpCrn( XclImpStream& rStrm, const XclAddress& rXclPos ) : + XclImpCachedValue( rStrm ), + maXclPos( rXclPos ) +{ +} + +const XclAddress& XclImpCrn::GetAddress() const +{ + return maXclPos; +} + +// Sheet in an external document ============================================== + +XclImpSupbookTab::XclImpSupbookTab( const String& rTabName ) : + maTabName( rTabName ), + mnScTab( SCTAB_INVALID ) +{ +} + +XclImpSupbookTab::~XclImpSupbookTab() +{ +} + +void XclImpSupbookTab::ReadCrn( XclImpStream& rStrm, const XclAddress& rXclPos ) +{ + maCrnList.Append( new XclImpCrn( rStrm, rXclPos ) ); +} + +void XclImpSupbookTab::LoadCachedValues(ScExternalRefCache::TableTypeRef pCacheTable) +{ + if (maCrnList.Empty()) + return; + + for (XclImpCrn* p = maCrnList.First(); p; p = maCrnList.Next()) + { + const XclAddress& rAddr = p->GetAddress(); + switch (p->GetType()) + { + case EXC_CACHEDVAL_BOOL: + { + bool b = p->GetBool(); + ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(b ? 1.0 : 0.0)); + pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken); + } + break; + case EXC_CACHEDVAL_DOUBLE: + { + double f = p->GetValue(); + ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(f)); + pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken); + } + break; + case EXC_CACHEDVAL_ERROR: + { + double fError = XclTools::ErrorToDouble( p->GetXclError() ); + ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(fError)); + pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken); + } + break; + case EXC_CACHEDVAL_STRING: + { + const String& rStr = p->GetString(); + ScExternalRefCache::TokenRef pToken(new formula::FormulaStringToken(rStr)); + pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken); + } + break; + default: + ; + } + } +} + +// External document (SUPBOOK) ================================================ + +XclImpSupbook::XclImpSupbook( XclImpStream& rStrm ) : + XclImpRoot( rStrm.GetRoot() ), + meType( EXC_SBTYPE_UNKNOWN ), + mnSBTab( EXC_TAB_DELETED ) +{ + sal_uInt16 nSBTabCnt; + rStrm >> nSBTabCnt; + + if( rStrm.GetRecLeft() == 2 ) + { + switch( rStrm.ReaduInt16() ) + { + case EXC_SUPB_SELF: meType = EXC_SBTYPE_SELF; break; + case EXC_SUPB_ADDIN: meType = EXC_SBTYPE_ADDIN; break; + default: DBG_ERRORFILE( "XclImpSupbook::XclImpSupbook - unknown special SUPBOOK type" ); + } + return; + } + + String aEncUrl( rStrm.ReadUniString() ); + bool bSelf = false; + XclImpUrlHelper::DecodeUrl( maXclUrl, bSelf, GetRoot(), aEncUrl ); + + if( maXclUrl.EqualsIgnoreCaseAscii( "\010EUROTOOL.XLA" ) ) + { + meType = EXC_SBTYPE_EUROTOOL; + maSupbTabList.Append( new XclImpSupbookTab( maXclUrl ) ); + } + else if( nSBTabCnt ) + { + meType = EXC_SBTYPE_EXTERN; + for( sal_uInt16 nSBTab = 0; nSBTab < nSBTabCnt; ++nSBTab ) + { + String aTabName( rStrm.ReadUniString() ); + maSupbTabList.Append( new XclImpSupbookTab( aTabName ) ); + } + } + else + { + meType = EXC_SBTYPE_SPECIAL; + // create dummy list entry + maSupbTabList.Append( new XclImpSupbookTab( maXclUrl ) ); + } +} + +void XclImpSupbook::ReadXct( XclImpStream& rStrm ) +{ + rStrm.Ignore( 2 ); + rStrm >> mnSBTab; +} + +void XclImpSupbook::ReadCrn( XclImpStream& rStrm ) +{ + if( XclImpSupbookTab* pSBTab = maSupbTabList.GetObject( mnSBTab ) ) + { + sal_uInt8 nXclColLast, nXclColFirst; + sal_uInt16 nXclRow; + rStrm >> nXclColLast >> nXclColFirst >> nXclRow; + + for( sal_uInt8 nXclCol = nXclColFirst; (nXclCol <= nXclColLast) && (rStrm.GetRecLeft() > 1); ++nXclCol ) + pSBTab->ReadCrn( rStrm, XclAddress( nXclCol, nXclRow ) ); + } +} + +void XclImpSupbook::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv ) +{ + maExtNameList.Append( new XclImpExtName( *this, rStrm, meType, pFormulaConv ) ); +} + +const XclImpExtName* XclImpSupbook::GetExternName( sal_uInt16 nXclIndex ) const +{ + DBG_ASSERT( nXclIndex > 0, "XclImpSupbook::GetExternName - index must be >0" ); + return (meType == EXC_SBTYPE_SELF) ? 0 : maExtNameList.GetObject( nXclIndex - 1 ); +} + +bool XclImpSupbook::GetLinkData( String& rApplic, String& rTopic ) const +{ + return (meType == EXC_SBTYPE_SPECIAL) && XclImpUrlHelper::DecodeLink( rApplic, rTopic, maXclUrl ); +} + +const String& XclImpSupbook::GetMacroName( sal_uInt16 nXclNameIdx ) const +{ + DBG_ASSERT( nXclNameIdx > 0, "XclImpSupbook::GetMacroName - index must be >0" ); + const XclImpName* pName = (meType == EXC_SBTYPE_SELF) ? GetNameManager().GetName( nXclNameIdx ) : 0; + return (pName && pName->IsVBName()) ? pName->GetScName() : EMPTY_STRING; +} + +const String& XclImpSupbook::GetTabName( sal_uInt16 nXtiTab ) const +{ + if (maSupbTabList.Empty()) + return EMPTY_STRING; + + sal_uInt16 i = 0; + for (XclImpSupbookTab* p = maSupbTabList.First(); p; p = maSupbTabList.Next(), ++i) + { + if (i == nXtiTab) + return p->GetTabName(); + } + + return EMPTY_STRING; +} + +sal_uInt16 XclImpSupbook::GetTabCount() const +{ + return ulimit_cast<sal_uInt16>(maSupbTabList.Count()); +} + +void XclImpSupbook::LoadCachedValues() +{ + if (meType != EXC_SBTYPE_EXTERN || GetExtDocOptions().GetDocSettings().mnLinkCnt > 0 || !GetDocShell()) + return; + + String aAbsUrl( ScGlobal::GetAbsDocName(maXclUrl, GetDocShell()) ); + + ScExternalRefManager* pRefMgr = GetRoot().GetDoc().GetExternalRefManager(); + sal_uInt16 nFileId = pRefMgr->getExternalFileId(aAbsUrl); + + sal_uInt16 nCount = static_cast< sal_uInt16 >( maSupbTabList.Count() ); + for (sal_uInt16 i = 0; i < nCount; ++i) + { + XclImpSupbookTab* pTab = maSupbTabList.GetObject(i); + if (!pTab) + return; + + const String& rTabName = pTab->GetTabName(); + ScExternalRefCache::TableTypeRef pCacheTable = pRefMgr->getCacheTable(nFileId, rTabName, true); + pTab->LoadCachedValues(pCacheTable); + pCacheTable->setWholeTableCached(); + } +} + +// Import link manager ======================================================== + +XclImpLinkManagerImpl::XclImpLinkManagerImpl( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + mbCreated( false ) +{ +} + +void XclImpLinkManagerImpl::ReadExternsheet( XclImpStream& rStrm ) +{ + sal_uInt16 nXtiCount; + rStrm >> nXtiCount; + DBG_ASSERT( static_cast< sal_Size >( nXtiCount * 6 ) == rStrm.GetRecLeft(), "XclImpLinkManagerImpl::ReadExternsheet - invalid count" ); + nXtiCount = static_cast< sal_uInt16 >( ::std::min< sal_Size >( nXtiCount, rStrm.GetRecLeft() / 6 ) ); + + /* #i104057# A weird external XLS generator writes multiple EXTERNSHEET + records instead of only one as expected. Surprisingly, Excel seems to + insert the entries of the second record before the entries of the first + record. */ + XclImpXtiVector aNewEntries( nXtiCount ); + for( XclImpXtiVector::iterator aIt = aNewEntries.begin(), aEnd = aNewEntries.end(); rStrm.IsValid() && (aIt != aEnd); ++aIt ) + rStrm >> *aIt; + maXtiList.insert( maXtiList.begin(), aNewEntries.begin(), aNewEntries.end() ); + + LoadCachedValues(); +} + +void XclImpLinkManagerImpl::ReadSupbook( XclImpStream& rStrm ) +{ + maSupbookList.Append( new XclImpSupbook( rStrm ) ); +} + +void XclImpLinkManagerImpl::ReadXct( XclImpStream& rStrm ) +{ + if( XclImpSupbook* pSupbook = maSupbookList.Last() ) + pSupbook->ReadXct( rStrm ); +} + +void XclImpLinkManagerImpl::ReadCrn( XclImpStream& rStrm ) +{ + if( XclImpSupbook* pSupbook = maSupbookList.Last() ) + pSupbook->ReadCrn( rStrm ); +} + +void XclImpLinkManagerImpl::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv ) +{ + if( XclImpSupbook* pSupbook = maSupbookList.Last() ) + pSupbook->ReadExternname( rStrm, pFormulaConv ); +} + +bool XclImpLinkManagerImpl::IsSelfRef( sal_uInt16 nXtiIndex ) const +{ + const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex ); + return pSupbook && (pSupbook->GetType() == EXC_SBTYPE_SELF); +} + +bool XclImpLinkManagerImpl::GetScTabRange( + SCTAB& rnFirstScTab, SCTAB& rnLastScTab, sal_uInt16 nXtiIndex ) const +{ + if( const XclImpXti* pXti = GetXti( nXtiIndex ) ) + { + if (maSupbookList.GetObject(pXti->mnSupbook)) + { + rnFirstScTab = pXti->mnSBTabFirst; + rnLastScTab = pXti->mnSBTabLast; + return true; + } + } + return false; +} + +const XclImpExtName* XclImpLinkManagerImpl::GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const +{ + const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex ); + return pSupbook ? pSupbook->GetExternName( nExtName ) : 0; +} + +const String* XclImpLinkManagerImpl::GetSupbookUrl( sal_uInt16 nXtiIndex ) const +{ + const XclImpSupbook* p = GetSupbook( nXtiIndex ); + if (!p) + return NULL; + return &p->GetXclUrl(); +} + +const String& XclImpLinkManagerImpl::GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const +{ + const XclImpSupbook* p = GetSupbook(nXti); + return p ? p->GetTabName(nXtiTab) : EMPTY_STRING; +} + +bool XclImpLinkManagerImpl::GetLinkData( String& rApplic, String& rTopic, sal_uInt16 nXtiIndex ) const +{ + const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex ); + return pSupbook && pSupbook->GetLinkData( rApplic, rTopic ); +} + +const String& XclImpLinkManagerImpl::GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const +{ + const XclImpSupbook* pSupbook = GetSupbook( nExtSheet ); + return pSupbook ? pSupbook->GetMacroName( nExtName ) : EMPTY_STRING; +} + +const XclImpXti* XclImpLinkManagerImpl::GetXti( sal_uInt16 nXtiIndex ) const +{ + return (nXtiIndex < maXtiList.size()) ? &maXtiList[ nXtiIndex ] : 0; +} + +const XclImpSupbook* XclImpLinkManagerImpl::GetSupbook( sal_uInt16 nXtiIndex ) const +{ + const XclImpXti* pXti = GetXti( nXtiIndex ); + return pXti ? maSupbookList.GetObject( pXti->mnSupbook ) : 0; +} + +//UNUSED2009-05 const XclImpSupbook* XclImpLinkManagerImpl::GetSupbook( const String& rUrl ) const +//UNUSED2009-05 { +//UNUSED2009-05 for( const XclImpSupbook* pSupbook = maSupbookList.First(); pSupbook; pSupbook = maSupbookList.Next() ) +//UNUSED2009-05 if( pSupbook->GetXclUrl() == rUrl ) +//UNUSED2009-05 return pSupbook; +//UNUSED2009-05 return 0; +//UNUSED2009-05 } + +void XclImpLinkManagerImpl::LoadCachedValues() +{ + // Read all CRN records which can be accessed via XclImpSupbook, and store + // the cached values to the external reference manager. + + sal_uInt32 nCount = maSupbookList.Count(); + for (sal_uInt16 nSupbook = 0; nSupbook < nCount; ++nSupbook) + { + XclImpSupbook* pSupbook = maSupbookList.GetObject(nSupbook); + pSupbook->LoadCachedValues(); + } +} + +//UNUSED2009-05 bool XclImpLinkManagerImpl::FindNextTabRange( +//UNUSED2009-05 sal_uInt16& rnSBTabFirst, sal_uInt16& rnSBTabLast, +//UNUSED2009-05 sal_uInt16 nSupbook, sal_uInt16 nSBTabStart ) const +//UNUSED2009-05 { +//UNUSED2009-05 rnSBTabFirst = rnSBTabLast = EXC_NOTAB; +//UNUSED2009-05 for( const XclImpXti* pXti = maXtiList.First(); pXti; pXti = maXtiList.Next() ) +//UNUSED2009-05 { +//UNUSED2009-05 if( (nSupbook == pXti->mnSupbook) && (nSBTabStart <= pXti->mnSBTabLast) && (pXti->mnSBTabFirst < rnSBTabFirst) ) +//UNUSED2009-05 { +//UNUSED2009-05 rnSBTabFirst = ::std::max( nSBTabStart, pXti->mnSBTabFirst ); +//UNUSED2009-05 rnSBTabLast = pXti->mnSBTabLast; +//UNUSED2009-05 } +//UNUSED2009-05 } +//UNUSED2009-05 return rnSBTabFirst != EXC_NOTAB; +//UNUSED2009-05 } + +// ============================================================================ + +XclImpLinkManager::XclImpLinkManager( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + mxImpl( new XclImpLinkManagerImpl( rRoot ) ) +{ +} + +XclImpLinkManager::~XclImpLinkManager() +{ +} + +void XclImpLinkManager::ReadExternsheet( XclImpStream& rStrm ) +{ + mxImpl->ReadExternsheet( rStrm ); +} + +void XclImpLinkManager::ReadSupbook( XclImpStream& rStrm ) +{ + mxImpl->ReadSupbook( rStrm ); +} + +void XclImpLinkManager::ReadXct( XclImpStream& rStrm ) +{ + mxImpl->ReadXct( rStrm ); +} + +void XclImpLinkManager::ReadCrn( XclImpStream& rStrm ) +{ + mxImpl->ReadCrn( rStrm ); +} + +void XclImpLinkManager::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv ) +{ + mxImpl->ReadExternname( rStrm, pFormulaConv ); +} + +bool XclImpLinkManager::IsSelfRef( sal_uInt16 nXtiIndex ) const +{ + return mxImpl->IsSelfRef( nXtiIndex ); +} + +bool XclImpLinkManager::GetScTabRange( + SCTAB& rnFirstScTab, SCTAB& rnLastScTab, sal_uInt16 nXtiIndex ) const +{ + return mxImpl->GetScTabRange( rnFirstScTab, rnLastScTab, nXtiIndex ); +} + +const XclImpExtName* XclImpLinkManager::GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const +{ + return mxImpl->GetExternName( nXtiIndex, nExtName ); +} + +const String* XclImpLinkManager::GetSupbookUrl( sal_uInt16 nXtiIndex ) const +{ + return mxImpl->GetSupbookUrl(nXtiIndex); +} + +const String& XclImpLinkManager::GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const +{ + return mxImpl->GetSupbookTabName(nXti, nXtiTab); +} + +bool XclImpLinkManager::GetLinkData( String& rApplic, String& rTopic, sal_uInt16 nXtiIndex ) const +{ + return mxImpl->GetLinkData( rApplic, rTopic, nXtiIndex ); +} + +const String& XclImpLinkManager::GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const +{ + return mxImpl->GetMacroName( nExtSheet, nExtName ); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xiname.cxx b/sc/source/filter/excel/xiname.cxx new file mode 100644 index 000000000000..3d373f6c36ba --- /dev/null +++ b/sc/source/filter/excel/xiname.cxx @@ -0,0 +1,264 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xiname.hxx" +#include "rangenam.hxx" +#include "xistream.hxx" + +// for formula compiler +#include "excform.hxx" +// for filter manager +#include "excimp8.hxx" + +// ============================================================================ +// *** Implementation *** +// ============================================================================ + +XclImpName::XclImpName( XclImpStream& rStrm, sal_uInt16 nXclNameIdx ) : + XclImpRoot( rStrm.GetRoot() ), + mpScData( 0 ), + mcBuiltIn( EXC_BUILTIN_UNKNOWN ), + mnScTab( SCTAB_MAX ), + mbFunction( false ), + mbVBName( false ) +{ + ExcelToSc& rFmlaConv = GetOldFmlaConverter(); + ScRangeName& rRangeNames = GetNamedRanges(); + + // 1) *** read data from stream *** --------------------------------------- + + sal_uInt16 nFlags = 0, nFmlaSize = 0, nExtSheet = EXC_NAME_GLOBAL, nXclTab = EXC_NAME_GLOBAL; + sal_uInt8 nNameLen = 0, nShortCut; + + switch( GetBiff() ) + { + case EXC_BIFF2: + { + sal_uInt8 nFlagsBiff2; + rStrm >> nFlagsBiff2; + rStrm.Ignore( 1 ); + rStrm >> nShortCut >> nNameLen; + nFmlaSize = rStrm.ReaduInt8(); + ::set_flag( nFlags, EXC_NAME_FUNC, ::get_flag( nFlagsBiff2, EXC_NAME2_FUNC ) ); + } + break; + + case EXC_BIFF3: + case EXC_BIFF4: + { + rStrm >> nFlags >> nShortCut >> nNameLen >> nFmlaSize; + } + break; + + case EXC_BIFF5: + case EXC_BIFF8: + { + rStrm >> nFlags >> nShortCut >> nNameLen >> nFmlaSize >> nExtSheet >> nXclTab; + rStrm.Ignore( 4 ); + } + break; + + default: DBG_ERROR_BIFF(); + } + + if( GetBiff() <= EXC_BIFF5 ) + maXclName = rStrm.ReadRawByteString( nNameLen ); + else + maXclName = rStrm.ReadUniString( nNameLen ); + + // 2) *** convert sheet index and name *** -------------------------------- + + // functions and VBA + mbFunction = ::get_flag( nFlags, EXC_NAME_FUNC ); + mbVBName = ::get_flag( nFlags, EXC_NAME_VB ); + + // get built-in name, or convert characters invalid in Calc + bool bBuiltIn = ::get_flag( nFlags, EXC_NAME_BUILTIN ); + + // special case for BIFF5 filter range - name appears as plain text without built-in flag + if( (GetBiff() == EXC_BIFF5) && (maXclName == XclTools::GetXclBuiltInDefName( EXC_BUILTIN_FILTERDATABASE )) ) + { + bBuiltIn = true; + maXclName.Assign( EXC_BUILTIN_FILTERDATABASE ); + } + + // convert Excel name to Calc name + if( mbVBName ) + { + // VB macro name + maScName = maXclName; + } + else if( bBuiltIn ) + { + // built-in name + if( maXclName.Len() ) + mcBuiltIn = maXclName.GetChar( 0 ); + if( mcBuiltIn == '?' ) // NUL character is imported as '?' + mcBuiltIn = '\0'; + maScName = XclTools::GetBuiltInDefName( mcBuiltIn ); + } + else + { + // any other name + maScName = maXclName; + ScfTools::ConvertToScDefinedName( maScName ); + } + + // add index for local names + if( nXclTab != EXC_NAME_GLOBAL ) + { + sal_uInt16 nUsedTab = (GetBiff() == EXC_BIFF8) ? nXclTab : nExtSheet; + // #163146# do not rename sheet-local names by default, this breaks VBA scripts +// maScName.Append( '_' ).Append( String::CreateFromInt32( nUsedTab ) ); + // TODO: may not work for BIFF5, handle skipped sheets (all BIFF) + mnScTab = static_cast< SCTAB >( nUsedTab - 1 ); + } + + // find an unused name + String aOrigName( maScName ); + sal_Int32 nCounter = 0; + USHORT nDummy; + while( rRangeNames.SearchName( maScName, nDummy ) ) + maScName.Assign( aOrigName ).Append( ' ' ).Append( String::CreateFromInt32( ++nCounter ) ); + + // 3) *** convert the name definition formula *** ------------------------- + + rFmlaConv.Reset(); + const ScTokenArray* pTokArr = 0; // pointer to token array, owned by rFmlaConv + RangeType nNameType = RT_NAME; + + if( ::get_flag( nFlags, EXC_NAME_BIG ) ) + { + // special, unsupported name + rFmlaConv.GetDummy( pTokArr ); + } + else if( bBuiltIn ) + { + // --- print ranges or title ranges --- + rStrm.PushPosition(); + switch( mcBuiltIn ) + { + case EXC_BUILTIN_PRINTAREA: + if( rFmlaConv.Convert( GetPrintAreaBuffer(), rStrm, nFmlaSize, FT_RangeName ) == ConvOK ) + nNameType |= RT_PRINTAREA; + break; + case EXC_BUILTIN_PRINTTITLES: + if( rFmlaConv.Convert( GetTitleAreaBuffer(), rStrm, nFmlaSize, FT_RangeName ) == ConvOK ) + nNameType |= RT_COLHEADER | RT_ROWHEADER; + break; + } + rStrm.PopPosition(); + + // --- name formula --- + // JEG : double check this. It is clearly false for normal names + // but some of the builtins (sheettitle?) might be able to handle arrays + rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize, false, FT_RangeName ); + + // --- auto or advanced filter --- + if( (GetBiff() == EXC_BIFF8) && pTokArr && bBuiltIn ) + { + ScRange aRange; + if( pTokArr->IsReference( aRange ) ) + { + switch( mcBuiltIn ) + { + case EXC_BUILTIN_FILTERDATABASE: + GetFilterManager().Insert( &GetOldRoot(), aRange, maScName ); + break; + case EXC_BUILTIN_CRITERIA: + GetFilterManager().AddAdvancedRange( aRange ); + nNameType |= RT_CRITERIA; + break; + case EXC_BUILTIN_EXTRACT: + if( pTokArr->IsValidReference( aRange ) ) + GetFilterManager().AddExtractPos( aRange ); + break; + } + } + } + } + else if( nFmlaSize > 0 ) + { + // regular defined name + rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize, true, FT_RangeName ); + } + + // 4) *** create a defined name in the Calc document *** ------------------ + + // #163146# do not ignore hidden names (may be regular names created by VBA scripts) + if( pTokArr /*&& (bBuiltIn || !::get_flag( nFlags, EXC_NAME_HIDDEN ))*/ && !mbFunction && !mbVBName ) + { + // create the Calc name data + ScRangeData* pData = new ScRangeData( GetDocPtr(), maScName, *pTokArr, ScAddress(), nNameType ); + pData->GuessPosition(); // calculate base position for relative refs + pData->SetIndex( nXclNameIdx ); // used as unique identifier in formulas + rRangeNames.Insert( pData ); // takes ownership of pData + mpScData = pData; // cache for later use + } +} + +// ---------------------------------------------------------------------------- + +XclImpNameManager::XclImpNameManager( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +void XclImpNameManager::ReadName( XclImpStream& rStrm ) +{ + ULONG nCount = maNameList.Count(); + if( nCount < 0xFFFF ) + maNameList.Append( new XclImpName( rStrm, static_cast< sal_uInt16 >( nCount + 1 ) ) ); +} + +const XclImpName* XclImpNameManager::FindName( const String& rXclName, SCTAB nScTab ) const +{ + const XclImpName* pGlobalName = 0; // a found global name + const XclImpName* pLocalName = 0; // a found local name + for( const XclImpName* pName = maNameList.First(); pName && !pLocalName; pName = maNameList.Next() ) + { + if( pName->GetXclName() == rXclName ) + { + if( pName->GetScTab() == nScTab ) + pLocalName = pName; + else if( pName->IsGlobal() ) + pGlobalName = pName; + } + } + return pLocalName ? pLocalName : pGlobalName; +} + +const XclImpName* XclImpNameManager::GetName( sal_uInt16 nXclNameIdx ) const +{ + DBG_ASSERT( nXclNameIdx > 0, "XclImpNameManager::GetName - index must be >0" ); + return maNameList.GetObject( nXclNameIdx - 1 ); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xipage.cxx b/sc/source/filter/excel/xipage.cxx new file mode 100644 index 000000000000..38cebc2e6ca8 --- /dev/null +++ b/sc/source/filter/excel/xipage.cxx @@ -0,0 +1,390 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xipage.hxx" +#include <svl/itemset.hxx> +#include <vcl/graph.hxx> +#include "scitems.hxx" +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <svx/pageitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/brshitem.hxx> +#include "document.hxx" +#include "stlsheet.hxx" +#include "attrib.hxx" +#include "xistream.hxx" +#include "xihelper.hxx" +#include "xiescher.hxx" + +// Page settings ============================================================== + +XclImpPageSettings::XclImpPageSettings( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ + Initialize(); +} + +void XclImpPageSettings::Initialize() +{ + maData.SetDefaults(); + mbValidPaper = false; +} + +void XclImpPageSettings::ReadSetup( XclImpStream& rStrm ) +{ + DBG_ASSERT_BIFF( GetBiff() >= EXC_BIFF4 ); + if( GetBiff() < EXC_BIFF4 ) + return; + + // BIFF4 - BIFF8 + sal_uInt16 nFlags; + rStrm >> maData.mnPaperSize >> maData.mnScaling >> maData.mnStartPage + >> maData.mnFitToWidth >> maData.mnFitToHeight >> nFlags; + + mbValidPaper = maData.mbValid = !::get_flag( nFlags, EXC_SETUP_INVALID ); + maData.mbPrintInRows = ::get_flag( nFlags, EXC_SETUP_INROWS ); + maData.mbPortrait = ::get_flag( nFlags, EXC_SETUP_PORTRAIT ); + maData.mbBlackWhite = ::get_flag( nFlags, EXC_SETUP_BLACKWHITE ); + maData.mbManualStart = true; + + // new in BIFF5 - BIFF8 + if( GetBiff() >= EXC_BIFF5 ) + { + rStrm >> maData.mnHorPrintRes >> maData.mnVerPrintRes + >> maData.mfHeaderMargin >> maData.mfFooterMargin >> maData.mnCopies; + + maData.mbDraftQuality = ::get_flag( nFlags, EXC_SETUP_DRAFT ); + maData.mbPrintNotes = ::get_flag( nFlags, EXC_SETUP_PRINTNOTES ); + maData.mbManualStart = ::get_flag( nFlags, EXC_SETUP_STARTPAGE ); + } +} + +void XclImpPageSettings::ReadMargin( XclImpStream& rStrm ) +{ + switch( rStrm.GetRecId() ) + { + case EXC_ID_LEFTMARGIN: rStrm >> maData.mfLeftMargin; break; + case EXC_ID_RIGHTMARGIN: rStrm >> maData.mfRightMargin; break; + case EXC_ID_TOPMARGIN: rStrm >> maData.mfTopMargin; break; + case EXC_ID_BOTTOMMARGIN: rStrm >> maData.mfBottomMargin; break; + default: DBG_ERRORFILE( "XclImpPageSettings::ReadMargin - unknown record" ); + } +} + +void XclImpPageSettings::ReadCenter( XclImpStream& rStrm ) +{ + DBG_ASSERT_BIFF( GetBiff() >= EXC_BIFF3 ); // read it anyway + bool bCenter = (rStrm.ReaduInt16() != 0); + switch( rStrm.GetRecId() ) + { + case EXC_ID_HCENTER: maData.mbHorCenter = bCenter; break; + case EXC_ID_VCENTER: maData.mbVerCenter = bCenter; break; + default: DBG_ERRORFILE( "XclImpPageSettings::ReadCenter - unknown record" ); + } +} + +void XclImpPageSettings::ReadHeaderFooter( XclImpStream& rStrm ) +{ + String aString; + if( rStrm.GetRecLeft() ) + aString = (GetBiff() <= EXC_BIFF5) ? rStrm.ReadByteString( false ) : rStrm.ReadUniString(); + + switch( rStrm.GetRecId() ) + { + case EXC_ID_HEADER: maData.maHeader = aString; break; + case EXC_ID_FOOTER: maData.maFooter = aString; break; + default: DBG_ERRORFILE( "XclImpPageSettings::ReadHeaderFooter - unknown record" ); + } +} + +void XclImpPageSettings::ReadPageBreaks( XclImpStream& rStrm ) +{ + ScfUInt16Vec* pVec = 0; + switch( rStrm.GetRecId() ) + { + case EXC_ID_HORPAGEBREAKS: pVec = &maData.maHorPageBreaks; break; + case EXC_ID_VERPAGEBREAKS: pVec = &maData.maVerPageBreaks; break; + default: DBG_ERRORFILE( "XclImpPageSettings::ReadPageBreaks - unknown record" ); + } + + if( pVec ) + { + bool bIgnore = GetBiff() == EXC_BIFF8; // ignore start/end columns or rows in BIFF8 + + sal_uInt16 nCount, nBreak; + rStrm >> nCount; + pVec->clear(); + pVec->reserve( nCount ); + + while( nCount-- ) + { + rStrm >> nBreak; + if( nBreak ) + pVec->push_back( nBreak ); + if( bIgnore ) + rStrm.Ignore( 4 ); + } + } +} + +void XclImpPageSettings::ReadPrintHeaders( XclImpStream& rStrm ) +{ + maData.mbPrintHeadings = (rStrm.ReaduInt16() != 0); +} + +void XclImpPageSettings::ReadPrintGridLines( XclImpStream& rStrm ) +{ + maData.mbPrintGrid = (rStrm.ReaduInt16() != 0); +} + +void XclImpPageSettings::ReadImgData( XclImpStream& rStrm ) +{ + Graphic aGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm ); + if( aGraphic.GetType() != GRAPHIC_NONE ) + maData.mxBrushItem.reset( new SvxBrushItem( aGraphic, GPOS_TILED, ATTR_BACKGROUND ) ); +} + +void XclImpPageSettings::SetPaperSize( sal_uInt16 nXclPaperSize, bool bPortrait ) +{ + maData.mnPaperSize = nXclPaperSize; + maData.mbPortrait = bPortrait; + mbValidPaper = true; +} + +// ---------------------------------------------------------------------------- + +namespace { + +void lclPutMarginItem( SfxItemSet& rItemSet, sal_uInt16 nRecId, double fMarginInch ) +{ + sal_uInt16 nMarginTwips = XclTools::GetTwipsFromInch( fMarginInch ); + switch( nRecId ) + { + case EXC_ID_TOPMARGIN: + case EXC_ID_BOTTOMMARGIN: + { + SvxULSpaceItem aItem( GETITEM( rItemSet, SvxULSpaceItem, ATTR_ULSPACE ) ); + if( nRecId == EXC_ID_TOPMARGIN ) + aItem.SetUpperValue( nMarginTwips ); + else + aItem.SetLowerValue( nMarginTwips ); + rItemSet.Put( aItem ); + } + break; + case EXC_ID_LEFTMARGIN: + case EXC_ID_RIGHTMARGIN: + { + SvxLRSpaceItem aItem( GETITEM( rItemSet, SvxLRSpaceItem, ATTR_LRSPACE ) ); + if( nRecId == EXC_ID_LEFTMARGIN ) + aItem.SetLeftValue( nMarginTwips ); + else + aItem.SetRightValue( nMarginTwips ); + rItemSet.Put( aItem ); + } + break; + default: + DBG_ERRORFILE( "XclImpPageSettings::SetMarginItem - unknown record id" ); + } +} + +} // namespace + +void XclImpPageSettings::Finalize() +{ + ScDocument& rDoc = GetDoc(); + SCTAB nScTab = GetCurrScTab(); + + // *** create page style sheet *** + + String aStyleName( RTL_CONSTASCII_USTRINGPARAM( "PageStyle_" ) ); + String aTableName; + if( GetDoc().GetName( nScTab, aTableName ) ) + aStyleName.Append( aTableName ); + else + aStyleName.Append( String::CreateFromInt32( nScTab + 1 ) ); + + ScStyleSheet& rStyleSheet = ScfTools::MakePageStyleSheet( GetStyleSheetPool(), aStyleName, false ); + SfxItemSet& rItemSet = rStyleSheet.GetItemSet(); + + // *** page settings *** + + ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_TOPDOWN, !maData.mbPrintInRows ), true ); + ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_HORCENTER, maData.mbHorCenter ), true ); + ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_VERCENTER, maData.mbVerCenter ), true ); + ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_HEADERS, maData.mbPrintHeadings ), true ); + ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_GRID, maData.mbPrintGrid ), true ); + ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_NOTES, maData.mbPrintNotes ), true ); + + sal_uInt16 nStartPage = maData.mbManualStart ? maData.mnStartPage : 0; + ScfTools::PutItem( rItemSet, SfxUInt16Item( ATTR_PAGE_FIRSTPAGENO, nStartPage ), true ); + + if( maData.mxBrushItem.get() ) + rItemSet.Put( *maData.mxBrushItem ); + + if( mbValidPaper ) + { + SvxPageItem aPageItem( GETITEM( rItemSet, SvxPageItem, ATTR_PAGE ) ); + aPageItem.SetLandscape( !maData.mbPortrait ); + rItemSet.Put( aPageItem ); + ScfTools::PutItem( rItemSet, SvxSizeItem( ATTR_PAGE_SIZE, maData.GetScPaperSize() ), true ); + } + + if( maData.mbFitToPages ) + rItemSet.Put( ScPageScaleToItem( maData.mnFitToWidth, maData.mnFitToHeight ) ); + else if( maData.mbValid ) + rItemSet.Put( SfxUInt16Item( ATTR_PAGE_SCALE, maData.mnScaling ) ); + + // *** margin preparations *** + + double fLeftMargin = maData.mfLeftMargin; + double fRightMargin = maData.mfRightMargin; + double fTopMargin = maData.mfTopMargin; + double fBottomMargin = maData.mfBottomMargin; + // distances between header/footer and page area + double fHeaderHeight = 0.0; + double fHeaderDist = 0.0; + double fFooterHeight = 0.0; + double fFooterDist = 0.0; + // in Calc, "header/footer left/right margin" is X distance between header/footer and page margin + double fHdrLeftMargin = maData.mfHdrLeftMargin - maData.mfLeftMargin; + double fHdrRightMargin = maData.mfHdrRightMargin - maData.mfRightMargin; + double fFtrLeftMargin = maData.mfFtrLeftMargin - maData.mfLeftMargin; + double fFtrRightMargin = maData.mfFtrRightMargin - maData.mfRightMargin; + + // *** header and footer *** + + XclImpHFConverter aHFConv( GetRoot() ); + + // header + bool bHasHeader = (maData.maHeader.Len() != 0); + SvxSetItem aHdrSetItem( GETITEM( rItemSet, SvxSetItem, ATTR_PAGE_HEADERSET ) ); + SfxItemSet& rHdrItemSet = aHdrSetItem.GetItemSet(); + rHdrItemSet.Put( SfxBoolItem( ATTR_PAGE_ON, bHasHeader ) ); + if( bHasHeader ) + { + aHFConv.ParseString( maData.maHeader ); + aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_HEADERLEFT ); + aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_HEADERRIGHT ); + // #i23296# In Calc, "top margin" is distance to header + fTopMargin = maData.mfHeaderMargin; + // Calc uses distance between header and sheet data area + fHeaderHeight = XclTools::GetInchFromTwips( aHFConv.GetTotalHeight() ); + fHeaderDist = maData.mfTopMargin - maData.mfHeaderMargin - fHeaderHeight; + } + if( fHeaderDist < 0.0 ) + { + /* #i23296# Header overlays sheet data: + -> set fixed header height to get correct sheet data position. */ + ScfTools::PutItem( rHdrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, false ), true ); + // shrink header height + long nHdrHeight = XclTools::GetTwipsFromInch( fHeaderHeight + fHeaderDist ); + ScfTools::PutItem( rHdrItemSet, SvxSizeItem( ATTR_PAGE_SIZE, Size( 0, nHdrHeight ) ), true ); + lclPutMarginItem( rHdrItemSet, EXC_ID_BOTTOMMARGIN, 0.0 ); + } + else + { + // use dynamic header height + ScfTools::PutItem( rHdrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, true ), true ); + lclPutMarginItem( rHdrItemSet, EXC_ID_BOTTOMMARGIN, fHeaderDist ); + } + lclPutMarginItem( rHdrItemSet, EXC_ID_LEFTMARGIN, fHdrLeftMargin ); + lclPutMarginItem( rHdrItemSet, EXC_ID_RIGHTMARGIN, fHdrRightMargin ); + rItemSet.Put( aHdrSetItem ); + + // footer + bool bHasFooter = (maData.maFooter.Len() != 0); + SvxSetItem aFtrSetItem( GETITEM( rItemSet, SvxSetItem, ATTR_PAGE_FOOTERSET ) ); + SfxItemSet& rFtrItemSet = aFtrSetItem.GetItemSet(); + rFtrItemSet.Put( SfxBoolItem( ATTR_PAGE_ON, bHasFooter ) ); + if( bHasFooter ) + { + aHFConv.ParseString( maData.maFooter ); + aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_FOOTERLEFT ); + aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_FOOTERRIGHT ); + // #i23296# In Calc, "bottom margin" is distance to footer + fBottomMargin = maData.mfFooterMargin; + // Calc uses distance between footer and sheet data area + fFooterHeight = XclTools::GetInchFromTwips( aHFConv.GetTotalHeight() ); + fFooterDist = maData.mfBottomMargin - maData.mfFooterMargin - fFooterHeight; + } + if( fFooterDist < 0.0 ) + { + /* #i23296# Footer overlays sheet data: + -> set fixed footer height to get correct sheet data end position. */ + ScfTools::PutItem( rFtrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, false ), true ); + // shrink footer height + long nFtrHeight = XclTools::GetTwipsFromInch( fFooterHeight + fFooterDist ); + ScfTools::PutItem( rFtrItemSet, SvxSizeItem( ATTR_PAGE_SIZE, Size( 0, nFtrHeight ) ), true ); + lclPutMarginItem( rFtrItemSet, EXC_ID_TOPMARGIN, 0.0 ); + } + else + { + // use dynamic footer height + ScfTools::PutItem( rFtrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, true ), true ); + lclPutMarginItem( rFtrItemSet, EXC_ID_TOPMARGIN, fFooterDist ); + } + lclPutMarginItem( rFtrItemSet, EXC_ID_LEFTMARGIN, fFtrLeftMargin ); + lclPutMarginItem( rFtrItemSet, EXC_ID_RIGHTMARGIN, fFtrRightMargin ); + rItemSet.Put( aFtrSetItem ); + + // *** set final margins *** + + lclPutMarginItem( rItemSet, EXC_ID_LEFTMARGIN, fLeftMargin ); + lclPutMarginItem( rItemSet, EXC_ID_RIGHTMARGIN, fRightMargin ); + lclPutMarginItem( rItemSet, EXC_ID_TOPMARGIN, fTopMargin ); + lclPutMarginItem( rItemSet, EXC_ID_BOTTOMMARGIN, fBottomMargin ); + + // *** put style sheet into document *** + + rDoc.SetPageStyle( nScTab, rStyleSheet.GetName() ); + + // *** page breaks *** + + ScfUInt16Vec::const_iterator aIt, aEnd; + + for( aIt = maData.maHorPageBreaks.begin(), aEnd = maData.maHorPageBreaks.end(); aIt != aEnd; ++aIt ) + { + SCROW nScRow = static_cast< SCROW >( *aIt ); + if( nScRow <= MAXROW ) + rDoc.SetRowBreak(nScRow, nScTab, false, true); + } + + for( aIt = maData.maVerPageBreaks.begin(), aEnd = maData.maVerPageBreaks.end(); aIt != aEnd; ++aIt ) + { + SCCOL nScCol = static_cast< SCCOL >( *aIt ); + if( nScCol <= MAXCOL ) + rDoc.SetColBreak(nScCol, nScTab, false, true); + } +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xipivot.cxx b/sc/source/filter/excel/xipivot.cxx new file mode 100644 index 000000000000..29e27a698a42 --- /dev/null +++ b/sc/source/filter/excel/xipivot.cxx @@ -0,0 +1,1638 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "xipivot.hxx" + +#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp> +#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp> +#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp> +#include <com/sun/star/sheet/DataPilotFieldReference.hpp> + +#include <tools/datetime.hxx> +#include <svl/zformat.hxx> +#include <svl/intitem.hxx> + +#include "document.hxx" +#include "cell.hxx" +#include "dpsave.hxx" +#include "dpdimsave.hxx" +#include "dpobject.hxx" +#include "dpshttab.hxx" +#include "dpoutputgeometry.hxx" +#include "scitems.hxx" +#include "attrib.hxx" + +#include "xltracer.hxx" +#include "xistream.hxx" +#include "xihelper.hxx" +#include "xilink.hxx" +#include "xiescher.hxx" + +//! TODO ExcelToSc usage +#include "excform.hxx" +#include "xltable.hxx" + +#include <vector> + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::sheet::DataPilotFieldOrientation; +using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA; +using ::com::sun::star::sheet::DataPilotFieldSortInfo; +using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo; +using ::com::sun::star::sheet::DataPilotFieldLayoutInfo; +using ::com::sun::star::sheet::DataPilotFieldReference; +using ::std::vector; + +// ============================================================================ +// Pivot cache +// ============================================================================ + +XclImpPCItem::XclImpPCItem( XclImpStream& rStrm ) +{ + switch( rStrm.GetRecId() ) + { + case EXC_ID_SXDOUBLE: ReadSxdouble( rStrm ); break; + case EXC_ID_SXBOOLEAN: ReadSxboolean( rStrm ); break; + case EXC_ID_SXERROR: ReadSxerror( rStrm ); break; + case EXC_ID_SXINTEGER: ReadSxinteger( rStrm ); break; + case EXC_ID_SXSTRING: ReadSxstring( rStrm ); break; + case EXC_ID_SXDATETIME: ReadSxdatetime( rStrm ); break; + case EXC_ID_SXEMPTY: ReadSxempty( rStrm ); break; + default: DBG_ERRORFILE( "XclImpPCItem::XclImpPCItem - unknown record id" ); + } +} + +namespace { + +void lclSetValue( const XclImpRoot& rRoot, const ScAddress& rScPos, double fValue, short nFormatType ) +{ + ScDocument& rDoc = rRoot.GetDoc(); + rDoc.SetValue( rScPos.Col(), rScPos.Row(), rScPos.Tab(), fValue ); + sal_uInt32 nScNumFmt = rRoot.GetFormatter().GetStandardFormat( nFormatType, rRoot.GetDocLanguage() ); + rDoc.ApplyAttr( rScPos.Col(), rScPos.Row(), rScPos.Tab(), SfxUInt32Item( ATTR_VALUE_FORMAT, nScNumFmt ) ); +} + +} // namespace + +void XclImpPCItem::WriteToSource( const XclImpRoot& rRoot, const ScAddress& rScPos ) const +{ + ScDocument& rDoc = rRoot.GetDoc(); + if( const String* pText = GetText() ) + rDoc.SetString( rScPos.Col(), rScPos.Row(), rScPos.Tab(), *pText ); + else if( const double* pfValue = GetDouble() ) + rDoc.SetValue( rScPos.Col(), rScPos.Row(), rScPos.Tab(), *pfValue ); + else if( const sal_Int16* pnValue = GetInteger() ) + rDoc.SetValue( rScPos.Col(), rScPos.Row(), rScPos.Tab(), *pnValue ); + else if( const bool* pbValue = GetBool() ) + lclSetValue( rRoot, rScPos, *pbValue ? 1.0 : 0.0, NUMBERFORMAT_LOGICAL ); + else if( const DateTime* pDateTime = GetDateTime() ) + { + // set number format date, time, or date/time, depending on the value + double fValue = rRoot.GetDoubleFromDateTime( *pDateTime ); + double fInt = 0.0; + double fFrac = modf( fValue, &fInt ); + short nFormatType = ((fFrac == 0.0) && (fInt != 0.0)) ? NUMBERFORMAT_DATE : + ((fInt == 0.0) ? NUMBERFORMAT_TIME : NUMBERFORMAT_DATETIME); + lclSetValue( rRoot, rScPos, fValue, nFormatType ); + } + else if( const sal_uInt16* pnError = GetError() ) + { + double fValue; + sal_uInt8 nErrCode = static_cast< sal_uInt8 >( *pnError ); + const ScTokenArray* pScTokArr = rRoot.GetOldFmlaConverter().GetBoolErr( + XclTools::ErrorToEnum( fValue, EXC_BOOLERR_ERROR, nErrCode ) ); + ScFormulaCell* pCell = new ScFormulaCell( &rDoc, rScPos, pScTokArr ); + pCell->SetHybridDouble( fValue ); + rDoc.PutCell( rScPos, pCell ); + } +} + +void XclImpPCItem::ReadSxdouble( XclImpStream& rStrm ) +{ + DBG_ASSERT( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdouble - wrong record size" ); + SetDouble( rStrm.ReadDouble() ); +} + +void XclImpPCItem::ReadSxboolean( XclImpStream& rStrm ) +{ + DBG_ASSERT( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxboolean - wrong record size" ); + SetBool( rStrm.ReaduInt16() != 0 ); +} + +void XclImpPCItem::ReadSxerror( XclImpStream& rStrm ) +{ + DBG_ASSERT( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxerror - wrong record size" ); + SetError( rStrm.ReaduInt16() ); +} + +void XclImpPCItem::ReadSxinteger( XclImpStream& rStrm ) +{ + DBG_ASSERT( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxinteger - wrong record size" ); + SetInteger( rStrm.ReadInt16() ); +} + +void XclImpPCItem::ReadSxstring( XclImpStream& rStrm ) +{ + DBG_ASSERT( rStrm.GetRecSize() >= 3, "XclImpPCItem::ReadSxstring - wrong record size" ); + SetText( rStrm.ReadUniString() ); +} + +void XclImpPCItem::ReadSxdatetime( XclImpStream& rStrm ) +{ + DBG_ASSERT( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdatetime - wrong record size" ); + sal_uInt16 nYear, nMonth; + sal_uInt8 nDay, nHour, nMin, nSec; + rStrm >> nYear >> nMonth >> nDay >> nHour >> nMin >> nSec; + SetDateTime( DateTime( Date( nDay, nMonth, nYear ), Time( nHour, nMin, nSec ) ) ); +} + +void XclImpPCItem::ReadSxempty( XclImpStream& rStrm ) +{ + (void)rStrm; // avoid compiler warning + DBG_ASSERT( rStrm.GetRecSize() == 0, "XclImpPCItem::ReadSxempty - wrong record size" ); + SetEmpty(); +} + +// ============================================================================ + +XclImpPCField::XclImpPCField( const XclImpRoot& rRoot, XclImpPivotCache& rPCache, sal_uInt16 nFieldIdx ) : + XclPCField( EXC_PCFIELD_UNKNOWN, nFieldIdx ), + XclImpRoot( rRoot ), + mrPCache( rPCache ), + mnSourceScCol( -1 ), + mbNumGroupInfoRead( false ) +{ +} + +XclImpPCField::~XclImpPCField() +{ +} + +// general field/item access -------------------------------------------------- + +const String& XclImpPCField::GetFieldName( const ScfStringVec& rVisNames ) const +{ + if( IsGroupChildField() && (mnFieldIdx < rVisNames.size()) ) + { + const String& rVisName = rVisNames[ mnFieldIdx ]; + if( rVisName.Len() > 0 ) + return rVisName; + } + return maFieldInfo.maName; +} + +const XclImpPCField* XclImpPCField::GetGroupBaseField() const +{ + DBG_ASSERT( IsGroupChildField(), "XclImpPCField::GetGroupBaseField - this field type does not have a base field" ); + return IsGroupChildField() ? mrPCache.GetField( maFieldInfo.mnGroupBase ) : 0; +} + +sal_uInt16 XclImpPCField::GetItemCount() const +{ + return static_cast< sal_uInt16 >( maItems.size() ); +} + +const XclImpPCItem* XclImpPCField::GetItem( sal_uInt16 nItemIdx ) const +{ + return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : 0; +} + +const XclImpPCItem* XclImpPCField::GetLimitItem( sal_uInt16 nItemIdx ) const +{ + DBG_ASSERT( nItemIdx < 3, "XclImpPCField::GetLimitItem - invalid item index" ); + DBG_ASSERT( nItemIdx < maNumGroupItems.size(), "XclImpPCField::GetLimitItem - no item found" ); + return (nItemIdx < maNumGroupItems.size()) ? maNumGroupItems[ nItemIdx ].get() : 0; +} + +void XclImpPCField::WriteFieldNameToSource( SCCOL nScCol, SCTAB nScTab ) const +{ + DBG_ASSERT( HasOrigItems(), "XclImpPCField::WriteFieldNameToSource - only for standard fields" ); + GetDoc().SetString( nScCol, 0, nScTab, maFieldInfo.maName ); + mnSourceScCol = nScCol; +} + +void XclImpPCField::WriteOrigItemToSource( SCROW nScRow, SCTAB nScTab, sal_uInt16 nItemIdx ) const +{ + if( nItemIdx < maOrigItems.size() ) + maOrigItems[ nItemIdx ]->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) ); +} + +void XclImpPCField::WriteLastOrigItemToSource( SCROW nScRow, SCTAB nScTab ) const +{ + if( !maOrigItems.empty() ) + maOrigItems.back()->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) ); +} + +// records -------------------------------------------------------------------- + +void XclImpPCField::ReadSxfield( XclImpStream& rStrm ) +{ + rStrm >> maFieldInfo; + + /* Detect the type of this field. This is done very restrictive to detect + any unexpected state. */ + meFieldType = EXC_PCFIELD_UNKNOWN; + + bool bItems = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS ); + bool bPostp = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_POSTPONE ); + bool bCalced = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_CALCED ); + bool bChild = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ); + bool bNum = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP ); + + sal_uInt16 nVisC = maFieldInfo.mnVisItems; + sal_uInt16 nGroupC = maFieldInfo.mnGroupItems; + sal_uInt16 nBaseC = maFieldInfo.mnBaseItems; + sal_uInt16 nOrigC = maFieldInfo.mnOrigItems; + DBG_ASSERT( nVisC > 0, "XclImpPCField::ReadSxfield - field without visible items" ); + + sal_uInt16 nType = maFieldInfo.mnFlags & EXC_SXFIELD_DATA_MASK; + bool bType = + (nType == EXC_SXFIELD_DATA_STR) || + (nType == EXC_SXFIELD_DATA_INT) || + (nType == EXC_SXFIELD_DATA_DBL) || + (nType == EXC_SXFIELD_DATA_STR_INT) || + (nType == EXC_SXFIELD_DATA_STR_DBL) || + (nType == EXC_SXFIELD_DATA_DATE) || + (nType == EXC_SXFIELD_DATA_DATE_EMP) || + (nType == EXC_SXFIELD_DATA_DATE_NUM) || + (nType == EXC_SXFIELD_DATA_DATE_STR); + bool bTypeNone = + (nType == EXC_SXFIELD_DATA_NONE); + // for now, ignore data type of calculated fields + DBG_ASSERT( bCalced || bType || bTypeNone, "XclImpPCField::ReadSxfield - unknown item data type" ); + + if( nVisC > 0 || bPostp ) + { + if( bItems && !bPostp ) + { + if( !bCalced ) + { + // 1) standard fields and standard grouping fields + if( !bNum ) + { + // 1a) standard field without grouping + if( bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == nVisC) ) + meFieldType = EXC_PCFIELD_STANDARD; + + // 1b) standard grouping field + else if( bTypeNone && (nGroupC == nVisC) && (nBaseC > 0) && (nOrigC == 0) ) + meFieldType = EXC_PCFIELD_STDGROUP; + } + // 2) numerical grouping fields + else if( (nGroupC == nVisC) && (nBaseC == 0) ) + { + // 2a) single num/date grouping field without child grouping field + if( !bChild && bType && (nOrigC > 0) ) + { + switch( nType ) + { + case EXC_SXFIELD_DATA_INT: + case EXC_SXFIELD_DATA_DBL: meFieldType = EXC_PCFIELD_NUMGROUP; break; + case EXC_SXFIELD_DATA_DATE: meFieldType = EXC_PCFIELD_DATEGROUP; break; + default: DBG_ERRORFILE( "XclImpPCField::ReadSxfield - numeric group with wrong data type" ); + } + } + + // 2b) first date grouping field with child grouping field + else if( bChild && (nType == EXC_SXFIELD_DATA_DATE) && (nOrigC > 0) ) + meFieldType = EXC_PCFIELD_DATEGROUP; + + // 2c) additional date grouping field + else if( bTypeNone && (nOrigC == 0) ) + meFieldType = EXC_PCFIELD_DATECHILD; + } + DBG_ASSERT( meFieldType != EXC_PCFIELD_UNKNOWN, "XclImpPCField::ReadSxfield - invalid standard or grouped field" ); + } + + // 3) calculated field + else + { + if( !bChild && !bNum && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) ) + meFieldType = EXC_PCFIELD_CALCED; + DBG_ASSERT( meFieldType == EXC_PCFIELD_CALCED, "XclImpPCField::ReadSxfield - invalid calculated field" ); + } + } + + else if( !bItems && bPostp ) + { + // 4) standard field with postponed items + if( !bCalced && !bChild && !bNum && bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) ) + meFieldType = EXC_PCFIELD_STANDARD; + DBG_ASSERT( meFieldType == EXC_PCFIELD_STANDARD, "XclImpPCField::ReadSxfield - invalid postponed field" ); + } + } +} + +void XclImpPCField::ReadItem( XclImpStream& rStrm ) +{ + DBG_ASSERT( HasInlineItems() || HasPostponedItems(), "XclImpPCField::ReadItem - field does not expect items" ); + + // read the item + XclImpPCItemRef xItem( new XclImpPCItem( rStrm ) ); + + // try to insert into an item list + if( mbNumGroupInfoRead ) + { + // there are 3 items after SXNUMGROUP that contain grouping limits and step count + if( maNumGroupItems.size() < 3 ) + maNumGroupItems.push_back( xItem ); + else + maOrigItems.push_back( xItem ); + } + else if( HasInlineItems() || HasPostponedItems() ) + { + maItems.push_back( xItem ); + // visible item is original item in standard fields + if( IsStandardField() ) + maOrigItems.push_back( xItem ); + } +} + +void XclImpPCField::ReadSxnumgroup( XclImpStream& rStrm ) +{ + DBG_ASSERT( IsNumGroupField() || IsDateGroupField(), "XclImpPCField::ReadSxnumgroup - SXNUMGROUP outside numeric grouping field" ); + DBG_ASSERT( !mbNumGroupInfoRead, "XclImpPCField::ReadSxnumgroup - multiple SXNUMGROUP records" ); + DBG_ASSERT( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxnumgroup - SXNUMGROUP out of record order" ); + rStrm >> maNumGroupInfo; + mbNumGroupInfoRead = IsNumGroupField() || IsDateGroupField(); +} + +void XclImpPCField::ReadSxgroupinfo( XclImpStream& rStrm ) +{ + DBG_ASSERT( IsStdGroupField(), "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO outside grouping field" ); + DBG_ASSERT( maGroupOrder.empty(), "XclImpPCField::ReadSxgroupinfo - multiple SXGROUPINFO records" ); + DBG_ASSERT( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO out of record order" ); + DBG_ASSERT( (rStrm.GetRecLeft() / 2) == maFieldInfo.mnBaseItems, "XclImpPCField::ReadSxgroupinfo - wrong SXGROUPINFO size" ); + maGroupOrder.clear(); + size_t nSize = rStrm.GetRecLeft() / 2; + maGroupOrder.resize( nSize, 0 ); + for( size_t nIdx = 0; nIdx < nSize; ++nIdx ) + rStrm >> maGroupOrder[ nIdx ]; +} + +// grouping ------------------------------------------------------------------- + +void XclImpPCField::ConvertGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const +{ + if( GetFieldName( rVisNames ).Len() > 0 ) + { + if( IsStdGroupField() ) + ConvertStdGroupField( rSaveData, rVisNames ); + else if( IsNumGroupField() ) + ConvertNumGroupField( rSaveData, rVisNames ); + else if( IsDateGroupField() ) + ConvertDateGroupField( rSaveData, rVisNames ); + } +} + +// private -------------------------------------------------------------------- + +void XclImpPCField::ConvertStdGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const +{ + if( const XclImpPCField* pBaseField = GetGroupBaseField() ) + { + const String& rBaseFieldName = pBaseField->GetFieldName( rVisNames ); + if( rBaseFieldName.Len() > 0 ) + { + // *** create a ScDPSaveGroupItem for each own item, they collect base item names *** + typedef ::std::vector< ScDPSaveGroupItem > ScDPSaveGroupItemVec; + ScDPSaveGroupItemVec aGroupItems; + aGroupItems.reserve( maItems.size() ); + // initialize with own item names + for( XclImpPCItemVec::const_iterator aIt = maItems.begin(), aEnd = maItems.end(); aIt != aEnd; ++aIt ) + aGroupItems.push_back( ScDPSaveGroupItem( (*aIt)->ConvertToText() ) ); + + // *** iterate over all base items, set their names at corresponding own items *** + for( sal_uInt16 nItemIdx = 0, nItemCount = static_cast< sal_uInt16 >( maGroupOrder.size() ); nItemIdx < nItemCount; ++nItemIdx ) + if( maGroupOrder[ nItemIdx ] < aGroupItems.size() ) + if( const XclImpPCItem* pBaseItem = pBaseField->GetItem( nItemIdx ) ) + if( const XclImpPCItem* pGroupItem = GetItem( maGroupOrder[ nItemIdx ] ) ) + if( *pBaseItem != *pGroupItem ) + aGroupItems[ maGroupOrder[ nItemIdx ] ].AddElement( pBaseItem->ConvertToText() ); + + // *** create the ScDPSaveGroupDimension object, fill with grouping info *** + ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) ); + for( ScDPSaveGroupItemVec::const_iterator aIt = aGroupItems.begin(), aEnd = aGroupItems.end(); aIt != aEnd; ++aIt ) + if( !aIt->IsEmpty() ) + aGroupDim.AddGroupItem( *aIt ); + rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim ); + } + } +} + +void XclImpPCField::ConvertNumGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const +{ + ScDPNumGroupInfo aNumInfo( GetScNumGroupInfo() ); + ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aNumInfo ); + rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim ); +} + +void XclImpPCField::ConvertDateGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const +{ + ScDPNumGroupInfo aDateInfo( GetScDateGroupInfo() ); + sal_Int32 nScDateType = maNumGroupInfo.GetScDateType(); + + switch( meFieldType ) + { + case EXC_PCFIELD_DATEGROUP: + { + if( aDateInfo.DateValues ) + { + // special case for days only with step value - create numeric grouping + ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aDateInfo ); + rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim ); + } + else + { + ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), ScDPNumGroupInfo() ); + aNumGroupDim.SetDateInfo( aDateInfo, nScDateType ); + rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim ); + } + } + break; + + case EXC_PCFIELD_DATECHILD: + { + if( const XclImpPCField* pBaseField = GetGroupBaseField() ) + { + const String& rBaseFieldName = pBaseField->GetFieldName( rVisNames ); + if( rBaseFieldName.Len() > 0 ) + { + ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) ); + aGroupDim.SetDateInfo( aDateInfo, nScDateType ); + rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim ); + } + } + } + break; + + default: + DBG_ERRORFILE( "XclImpPCField::ConvertDateGroupField - unknown date field type" ); + } +} + +ScDPNumGroupInfo XclImpPCField::GetScNumGroupInfo() const +{ + ScDPNumGroupInfo aNumInfo; + aNumInfo.Enable = sal_True; + aNumInfo.DateValues = sal_False; + aNumInfo.AutoStart = sal_True; + aNumInfo.AutoEnd = sal_True; + + if( const double* pfMinValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MIN ) ) + { + aNumInfo.Start = *pfMinValue; + aNumInfo.AutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN ); + } + if( const double* pfMaxValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MAX ) ) + { + aNumInfo.End = *pfMaxValue; + aNumInfo.AutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX ); + } + if( const double* pfStepValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_STEP ) ) + aNumInfo.Step = *pfStepValue; + + return aNumInfo; +} + +ScDPNumGroupInfo XclImpPCField::GetScDateGroupInfo() const +{ + ScDPNumGroupInfo aDateInfo; + aDateInfo.Enable = sal_True; + aDateInfo.DateValues = sal_False; + aDateInfo.AutoStart = sal_True; + aDateInfo.AutoEnd = sal_True; + + if( const DateTime* pMinDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MIN ) ) + { + aDateInfo.Start = GetDoubleFromDateTime( *pMinDate ); + aDateInfo.AutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN ); + } + if( const DateTime* pMaxDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MAX ) ) + { + aDateInfo.End = GetDoubleFromDateTime( *pMaxDate ); + aDateInfo.AutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX ); + } + // GetDateGroupStep() returns a value for date type "day" in single date groups only + if( const sal_Int16* pnStepValue = GetDateGroupStep() ) + { + aDateInfo.Step = *pnStepValue; + aDateInfo.DateValues = sal_True; + } + + return aDateInfo; +} + +const double* XclImpPCField::GetNumGroupLimit( sal_uInt16 nLimitIdx ) const +{ + DBG_ASSERT( IsNumGroupField(), "XclImpPCField::GetNumGroupLimit - only for numeric grouping fields" ); + if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) ) + { + DBG_ASSERT( pItem->GetDouble(), "XclImpPCField::GetNumGroupLimit - SXDOUBLE item expected" ); + return pItem->GetDouble(); + } + return 0; +} + +const DateTime* XclImpPCField::GetDateGroupLimit( sal_uInt16 nLimitIdx ) const +{ + DBG_ASSERT( IsDateGroupField(), "XclImpPCField::GetDateGroupLimit - only for date grouping fields" ); + if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) ) + { + DBG_ASSERT( pItem->GetDateTime(), "XclImpPCField::GetDateGroupLimit - SXDATETIME item expected" ); + return pItem->GetDateTime(); + } + return 0; +} + +const sal_Int16* XclImpPCField::GetDateGroupStep() const +{ + // only for single date grouping fields, not for grouping chains + if( !IsGroupBaseField() && !IsGroupChildField() ) + { + // only days may have a step value, return 0 for all other date types + if( maNumGroupInfo.GetXclDataType() == EXC_SXNUMGROUP_TYPE_DAY ) + { + if( const XclImpPCItem* pItem = GetLimitItem( EXC_SXFIELD_INDEX_STEP ) ) + { + DBG_ASSERT( pItem->GetInteger(), "XclImpPCField::GetDateGroupStep - SXINTEGER item expected" ); + if( const sal_Int16* pnStep = pItem->GetInteger() ) + { + DBG_ASSERT( *pnStep > 0, "XclImpPCField::GetDateGroupStep - invalid step count" ); + // return nothing for step count 1 - this is also a standard date group in Excel + return (*pnStep > 1) ? pnStep : 0; + } + } + } + } + return 0; +} + +// ============================================================================ + +XclImpPivotCache::XclImpPivotCache( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + maSrcRange( ScAddress::INITIALIZE_INVALID ), + mnStrmId( 0 ), + mnSrcType( EXC_SXVS_UNKNOWN ), + mbSelfRef( false ) +{ +} + +XclImpPivotCache::~XclImpPivotCache() +{ +} + +// data access ---------------------------------------------------------------- + +sal_uInt16 XclImpPivotCache::GetFieldCount() const +{ + return static_cast< sal_uInt16 >( maFields.size() ); +} + +const XclImpPCField* XclImpPivotCache::GetField( sal_uInt16 nFieldIdx ) const +{ + return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : 0; +} + +// records -------------------------------------------------------------------- + +void XclImpPivotCache::ReadSxidstm( XclImpStream& rStrm ) +{ + rStrm >> mnStrmId; +} + +void XclImpPivotCache::ReadSxvs( XclImpStream& rStrm ) +{ + rStrm >> mnSrcType; + GetTracer().TracePivotDataSource( mnSrcType != EXC_SXVS_SHEET ); +} + +void XclImpPivotCache::ReadDconref( XclImpStream& rStrm ) +{ + /* Read DCONREF only once (by checking maTabName), there may be other + DCONREF records in another context. Read reference only if a leading + SXVS record is present (by checking mnSrcType). */ + if( (maTabName.Len() > 0) || (mnSrcType != EXC_SXVS_SHEET) ) + return; + + XclRange aXclRange( ScAddress::UNINITIALIZED ); + aXclRange.Read( rStrm, false ); + String aEncUrl = rStrm.ReadUniString(); + + XclImpUrlHelper::DecodeUrl( maUrl, maTabName, mbSelfRef, GetRoot(), aEncUrl ); + + /* Do not convert maTabName to Calc sheet name -> original name is used to + find the sheet in the document. Sheet index of source range will be + found later in XclImpPivotCache::ReadPivotCacheStream(), because sheet + may not exist yet. */ + if( mbSelfRef ) + GetAddressConverter().ConvertRange( maSrcRange, aXclRange, 0, 0, true ); +} + +void XclImpPivotCache::ReadPivotCacheStream( XclImpStream& rStrm ) +{ + if( (mnSrcType != EXC_SXVS_SHEET) && (mnSrcType != EXC_SXVS_EXTERN) ) + return; + + ScDocument& rDoc = GetDoc(); + SCCOL nFieldScCol = 0; // column index of source data for next field + SCROW nItemScRow = 0; // row index of source data for current items + SCTAB nScTab = 0; // sheet index of source data + bool bGenerateSource = false; // true = write source data from cache to dummy table + + if( mbSelfRef ) + { + // try to find internal sheet containing the source data + nScTab = GetTabInfo().GetScTabFromXclName( maTabName ); + if( rDoc.HasTable( nScTab ) ) + { + // set sheet index to source range + maSrcRange.aStart.SetTab( nScTab ); + maSrcRange.aEnd.SetTab( nScTab ); + } + else + { + // create dummy sheet for deleted internal sheet + bGenerateSource = true; + } + } + else + { + // create dummy sheet for external sheet + bGenerateSource = true; + } + + // create dummy sheet for source data from external or deleted sheet + if( bGenerateSource ) + { + if( rDoc.GetTableCount() >= MAXTABCOUNT ) + // cannot create more sheets -> exit + return; + + nScTab = rDoc.GetTableCount(); + rDoc.MakeTable( nScTab ); + String aDummyName = CREATE_STRING( "DPCache" ); + if( maTabName.Len() > 0 ) + aDummyName.Append( '_' ).Append( maTabName ); + rDoc.CreateValidTabName( aDummyName ); + rDoc.RenameTab( nScTab, aDummyName ); + // set sheet index to source range + maSrcRange.aStart.SetTab( nScTab ); + maSrcRange.aEnd.SetTab( nScTab ); + } + + // open pivot cache storage stream + SotStorageRef xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE ); + SotStorageStreamRef xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( mnStrmId ) ); + if( !xSvStrm.Is() ) + return; + + // create Excel record stream object + XclImpStream aPCStrm( *xSvStrm, GetRoot() ); + aPCStrm.CopyDecrypterFrom( rStrm ); // pivot cache streams are encrypted + + XclImpPCFieldRef xCurrField; // current field for new items + XclImpPCFieldVec aOrigFields; // all standard fields with inline original items + XclImpPCFieldVec aPostpFields; // all standard fields with postponed original items + size_t nPostpIdx = 0; // index to current field with postponed items + bool bLoop = true; // true = continue loop + + while( bLoop && aPCStrm.StartNextRecord() ) + { + switch( aPCStrm.GetRecId() ) + { + case EXC_ID_EOF: + bLoop = false; + break; + + case EXC_ID_SXDB: + aPCStrm >> maPCInfo; + break; + + case EXC_ID_SXFIELD: + { + xCurrField.reset(); + sal_uInt16 nNewFieldIdx = GetFieldCount(); + if( nNewFieldIdx < EXC_PC_MAXFIELDCOUNT ) + { + xCurrField.reset( new XclImpPCField( GetRoot(), *this, nNewFieldIdx ) ); + maFields.push_back( xCurrField ); + xCurrField->ReadSxfield( aPCStrm ); + if( xCurrField->HasOrigItems() ) + { + if( xCurrField->HasPostponedItems() ) + aPostpFields.push_back( xCurrField ); + else + aOrigFields.push_back( xCurrField ); + // insert field name into generated source data, field remembers its column index + if( bGenerateSource && (nFieldScCol <= MAXCOL) ) + xCurrField->WriteFieldNameToSource( nFieldScCol++, nScTab ); + } + // do not read items into invalid/postponed fields + if( !xCurrField->HasInlineItems() ) + xCurrField.reset(); + } + } + break; + + case EXC_ID_SXINDEXLIST: + // read index list and insert all items into generated source data + if( bGenerateSource && (nItemScRow <= MAXROW) && (++nItemScRow <= MAXROW) ) + { + for( XclImpPCFieldVec::const_iterator aIt = aOrigFields.begin(), aEnd = aOrigFields.end(); aIt != aEnd; ++aIt ) + { + sal_uInt16 nItemIdx = (*aIt)->Has16BitIndexes() ? aPCStrm.ReaduInt16() : aPCStrm.ReaduInt8(); + (*aIt)->WriteOrigItemToSource( nItemScRow, nScTab, nItemIdx ); + } + } + xCurrField.reset(); + break; + + case EXC_ID_SXDOUBLE: + case EXC_ID_SXBOOLEAN: + case EXC_ID_SXERROR: + case EXC_ID_SXINTEGER: + case EXC_ID_SXSTRING: + case EXC_ID_SXDATETIME: + case EXC_ID_SXEMPTY: + if( xCurrField.is() ) // inline items + { + xCurrField->ReadItem( aPCStrm ); + } + else if( !aPostpFields.empty() ) // postponed items + { + // read postponed item + aPostpFields[ nPostpIdx ]->ReadItem( aPCStrm ); + // write item to source + if( bGenerateSource && (nItemScRow <= MAXROW) ) + { + // start new row, if there are only postponed fields + if( aOrigFields.empty() && (nPostpIdx == 0) ) + ++nItemScRow; + if( nItemScRow <= MAXROW ) + aPostpFields[ nPostpIdx ]->WriteLastOrigItemToSource( nItemScRow, nScTab ); + } + // get index of next postponed field + ++nPostpIdx; + if( nPostpIdx >= aPostpFields.size() ) + nPostpIdx = 0; + } + break; + + case EXC_ID_SXNUMGROUP: + if( xCurrField.is() ) + xCurrField->ReadSxnumgroup( aPCStrm ); + break; + + case EXC_ID_SXGROUPINFO: + if( xCurrField.is() ) + xCurrField->ReadSxgroupinfo( aPCStrm ); + break; + + // known but ignored records + case EXC_ID_SXRULE: + case EXC_ID_SXFILT: + case EXC_ID_00F5: + case EXC_ID_SXNAME: + case EXC_ID_SXPAIR: + case EXC_ID_SXFMLA: + case EXC_ID_SXFORMULA: + case EXC_ID_SXDBEX: + case EXC_ID_SXFDBTYPE: + break; + + default: + DBG_ERROR1( "XclImpPivotCache::ReadPivotCacheStream - unknown record 0x%04hX", aPCStrm.GetRecId() ); + } + } + + DBG_ASSERT( maPCInfo.mnTotalFields == maFields.size(), + "XclImpPivotCache::ReadPivotCacheStream - field count mismatch" ); + + // set source range for external source data + if( bGenerateSource && (nFieldScCol > 0) ) + { + maSrcRange.aStart.SetCol( 0 ); + maSrcRange.aStart.SetRow( 0 ); + // nFieldScCol points to first unused column + maSrcRange.aEnd.SetCol( nFieldScCol - 1 ); + // nItemScRow points to last used row + maSrcRange.aEnd.SetRow( nItemScRow ); + } +} + +bool XclImpPivotCache::IsRefreshOnLoad() const +{ + return static_cast<bool>(maPCInfo.mnFlags & 0x0004); +} + +// ============================================================================ +// Pivot table +// ============================================================================ + +XclImpPTItem::XclImpPTItem( const XclImpPCField* pCacheField ) : + mpCacheField( pCacheField ) +{ +} + +const String* XclImpPTItem::GetItemName() const +{ + if( mpCacheField ) + if( const XclImpPCItem* pCacheItem = mpCacheField->GetItem( maItemInfo.mnCacheIdx ) ) + //! TODO: use XclImpPCItem::ConvertToText(), if all conversions are available + return pCacheItem->IsEmpty() ? &String::EmptyString() : pCacheItem->GetText(); + return 0; +} + +const String* XclImpPTItem::GetVisItemName() const +{ + return maItemInfo.HasVisName() ? maItemInfo.GetVisName() : GetItemName(); +} + +void XclImpPTItem::ReadSxvi( XclImpStream& rStrm ) +{ + rStrm >> maItemInfo; +} + +void XclImpPTItem::ConvertItem( ScDPSaveDimension& rSaveDim ) const +{ + if( const String* pItemName = GetItemName() ) + { + ScDPSaveMember& rMember = *rSaveDim.GetMemberByName( *pItemName ); + rMember.SetIsVisible( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN ) ); + rMember.SetShowDetails( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL ) ); + if (maItemInfo.HasVisName()) + rMember.SetLayoutName(*maItemInfo.GetVisName()); + } +} + +// ============================================================================ + +XclImpPTField::XclImpPTField( const XclImpPivotTable& rPTable, sal_uInt16 nCacheIdx ) : + mrPTable( rPTable ) +{ + maFieldInfo.mnCacheIdx = nCacheIdx; +} + +// general field/item access -------------------------------------------------- + +const XclImpPCField* XclImpPTField::GetCacheField() const +{ + XclImpPivotCacheRef xPCache = mrPTable.GetPivotCache(); + return xPCache.is() ? xPCache->GetField( maFieldInfo.mnCacheIdx ) : 0; +} + +const String& XclImpPTField::GetFieldName() const +{ + const XclImpPCField* pField = GetCacheField(); + return pField ? pField->GetFieldName( mrPTable.GetVisFieldNames() ) : String::EmptyString(); +} + +const String& XclImpPTField::GetVisFieldName() const +{ + const String* pVisName = maFieldInfo.GetVisName(); + return pVisName ? *pVisName : String::EmptyString(); +} + +const XclImpPTItem* XclImpPTField::GetItem( sal_uInt16 nItemIdx ) const +{ + return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : 0; +} + +const String* XclImpPTField::GetItemName( sal_uInt16 nItemIdx ) const +{ + const XclImpPTItem* pItem = GetItem( nItemIdx ); + return pItem ? pItem->GetItemName() : 0; +} + +const String* XclImpPTField::GetVisItemName( sal_uInt16 nItemIdx ) const +{ + const XclImpPTItem* pItem = GetItem( nItemIdx ); + return pItem ? pItem->GetVisItemName() : 0; +} + +// records -------------------------------------------------------------------- + +void XclImpPTField::ReadSxvd( XclImpStream& rStrm ) +{ + rStrm >> maFieldInfo; +} + +void XclImpPTField::ReadSxvdex( XclImpStream& rStrm ) +{ + rStrm >> maFieldExtInfo; +} + +void XclImpPTField::ReadSxvi( XclImpStream& rStrm ) +{ + XclImpPTItemRef xItem( new XclImpPTItem( GetCacheField() ) ); + maItems.push_back( xItem ); + xItem->ReadSxvi( rStrm ); +} + +// row/column fields ---------------------------------------------------------- + +void XclImpPTField::ConvertRowColField( ScDPSaveData& rSaveData ) const +{ + DBG_ASSERT( maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOL, "XclImpPTField::ConvertRowColField - no row/column field" ); + // special data orientation field? + if( maFieldInfo.mnCacheIdx == EXC_SXIVD_DATA ) + rSaveData.GetDataLayoutDimension()->SetOrientation( static_cast< USHORT >( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOL ) ) ); + else + ConvertRCPField( rSaveData ); +} + +// page fields ---------------------------------------------------------------- + +void XclImpPTField::SetPageFieldInfo( const XclPTPageFieldInfo& rPageInfo ) +{ + maPageInfo = rPageInfo; +} + +void XclImpPTField::ConvertPageField( ScDPSaveData& rSaveData ) const +{ + DBG_ASSERT( maFieldInfo.mnAxes & EXC_SXVD_AXIS_PAGE, "XclImpPTField::ConvertPageField - no page field" ); + if( ScDPSaveDimension* pSaveDim = ConvertRCPField( rSaveData ) ) + pSaveDim->SetCurrentPage( GetItemName( maPageInfo.mnSelItem ) ); +} + +// hidden fields -------------------------------------------------------------- + +void XclImpPTField::ConvertHiddenField( ScDPSaveData& rSaveData ) const +{ + DBG_ASSERT( (maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOLPAGE) == 0, "XclImpPTField::ConvertHiddenField - field not hidden" ); + ConvertRCPField( rSaveData ); +} + +// data fields ---------------------------------------------------------------- + +bool XclImpPTField::HasDataFieldInfo() const +{ + return !maDataInfoList.empty(); +} + +void XclImpPTField::AddDataFieldInfo( const XclPTDataFieldInfo& rDataInfo ) +{ + DBG_ASSERT( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::AddDataFieldInfo - no data field" ); + maDataInfoList.push_back( rDataInfo ); +} + +void XclImpPTField::ConvertDataField( ScDPSaveData& rSaveData ) const +{ + DBG_ASSERT( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::ConvertDataField - no data field" ); + DBG_ASSERT( !maDataInfoList.empty(), "XclImpPTField::ConvertDataField - no data field info" ); + if( !maDataInfoList.empty() ) + { + const String& rFieldName = GetFieldName(); + if( rFieldName.Len() > 0 ) + { + XclPTDataFieldInfoList::const_iterator aIt = maDataInfoList.begin(), aEnd = maDataInfoList.end(); + + ScDPSaveDimension& rSaveDim = *rSaveData.GetNewDimensionByName( rFieldName ); + ConvertDataField( rSaveDim, *aIt ); + + // multiple data fields -> clone dimension + for( ++aIt; aIt != aEnd; ++aIt ) + { + ScDPSaveDimension& rDupDim = rSaveData.DuplicateDimension( rSaveDim ); + ConvertDataFieldInfo( rDupDim, *aIt ); + } + } + } +} + +// private -------------------------------------------------------------------- + +/** + * Convert Excel-encoded subtotal name to a Calc-encoded one. + */ +static OUString lcl_convertExcelSubtotalName(const OUString& rName) +{ + OUStringBuffer aBuf; + const sal_Unicode* p = rName.getStr(); + sal_Int32 n = rName.getLength(); + for (sal_Int32 i = 0; i < n; ++i) + { + const sal_Unicode c = p[i]; + if (c == sal_Unicode('\\')) + { + aBuf.append(c); + aBuf.append(c); + } + else + aBuf.append(c); + } + return aBuf.makeStringAndClear(); +} + +ScDPSaveDimension* XclImpPTField::ConvertRCPField( ScDPSaveData& rSaveData ) const +{ + const String& rFieldName = GetFieldName(); + if( rFieldName.Len() == 0 ) + return 0; + + const XclImpPCField* pCacheField = GetCacheField(); + if( !pCacheField || !pCacheField->IsSupportedField() ) + return 0; + + ScDPSaveDimension& rSaveDim = *rSaveData.GetNewDimensionByName( rFieldName ); + + // orientation + rSaveDim.SetOrientation( static_cast< USHORT >( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOLPAGE ) ) ); + + // general field info + ConvertFieldInfo( rSaveDim ); + + // visible name + if( const String* pVisName = maFieldInfo.GetVisName() ) + if( pVisName->Len() > 0 ) + rSaveDim.SetLayoutName( *pVisName ); + + // subtotal function(s) + XclPTSubtotalVec aSubtotalVec; + maFieldInfo.GetSubtotals( aSubtotalVec ); + if( !aSubtotalVec.empty() ) + rSaveDim.SetSubTotals( static_cast< long >( aSubtotalVec.size() ), &aSubtotalVec[ 0 ] ); + + // sorting + DataPilotFieldSortInfo aSortInfo; + aSortInfo.Field = mrPTable.GetDataFieldName( maFieldExtInfo.mnSortField ); + aSortInfo.IsAscending = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC ); + aSortInfo.Mode = maFieldExtInfo.GetApiSortMode(); + rSaveDim.SetSortInfo( &aSortInfo ); + + // auto show + DataPilotFieldAutoShowInfo aShowInfo; + aShowInfo.IsEnabled = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW ); + aShowInfo.ShowItemsMode = maFieldExtInfo.GetApiAutoShowMode(); + aShowInfo.ItemCount = maFieldExtInfo.GetApiAutoShowCount(); + aShowInfo.DataField = mrPTable.GetDataFieldName( maFieldExtInfo.mnShowField ); + rSaveDim.SetAutoShowInfo( &aShowInfo ); + + // layout + DataPilotFieldLayoutInfo aLayoutInfo; + aLayoutInfo.LayoutMode = maFieldExtInfo.GetApiLayoutMode(); + aLayoutInfo.AddEmptyLines = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK ); + rSaveDim.SetLayoutInfo( &aLayoutInfo ); + + // grouping info + pCacheField->ConvertGroupField( rSaveData, mrPTable.GetVisFieldNames() ); + + // custom subtotal name + if (maFieldExtInfo.mpFieldTotalName.get()) + { + OUString aSubName = lcl_convertExcelSubtotalName(*maFieldExtInfo.mpFieldTotalName); + rSaveDim.SetSubtotalName(aSubName); + } + + return &rSaveDim; +} + +void XclImpPTField::ConvertFieldInfo( ScDPSaveDimension& rSaveDim ) const +{ + rSaveDim.SetShowEmpty( ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL ) ); + ConvertItems( rSaveDim ); +} + +void XclImpPTField::ConvertDataField( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const +{ + // orientation + rSaveDim.SetOrientation( DataPilotFieldOrientation_DATA ); + // general field info + ConvertFieldInfo( rSaveDim ); + // extended data field info + ConvertDataFieldInfo( rSaveDim, rDataInfo ); +} + +void XclImpPTField::ConvertDataFieldInfo( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const +{ + // visible name + if( const String* pVisName = rDataInfo.GetVisName() ) + if( pVisName->Len() > 0 ) + rSaveDim.SetLayoutName( *pVisName ); + + // aggregation function + rSaveDim.SetFunction( static_cast< USHORT >( rDataInfo.GetApiAggFunc() ) ); + + // result field reference + sal_Int32 nRefType = rDataInfo.GetApiRefType(); + if( nRefType != ::com::sun::star::sheet::DataPilotFieldReferenceType::NONE ) + { + DataPilotFieldReference aFieldRef; + aFieldRef.ReferenceType = nRefType; + + if( const XclImpPTField* pRefField = mrPTable.GetField( rDataInfo.mnRefField ) ) + { + aFieldRef.ReferenceField = pRefField->GetFieldName(); + aFieldRef.ReferenceItemType = rDataInfo.GetApiRefItemType(); + if( aFieldRef.ReferenceItemType == ::com::sun::star::sheet::DataPilotFieldReferenceItemType::NAMED ) + if( const String* pRefItemName = pRefField->GetItemName( rDataInfo.mnRefItem ) ) + aFieldRef.ReferenceItemName = *pRefItemName; + } + + rSaveDim.SetReferenceValue( &aFieldRef ); + } +} + +void XclImpPTField::ConvertItems( ScDPSaveDimension& rSaveDim ) const +{ + for( XclImpPTItemVec::const_iterator aIt = maItems.begin(), aEnd = maItems.end(); aIt != aEnd; ++aIt ) + (*aIt)->ConvertItem( rSaveDim ); +} + +// ============================================================================ + +XclImpPivotTable::XclImpPivotTable( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + maDataOrientField( *this, EXC_SXIVD_DATA ), + mpDPObj(NULL) +{ +} + +XclImpPivotTable::~XclImpPivotTable() +{ +} + +// cache/field access, misc. -------------------------------------------------- + +sal_uInt16 XclImpPivotTable::GetFieldCount() const +{ + return static_cast< sal_uInt16 >( maFields.size() ); +} + +const XclImpPTField* XclImpPivotTable::GetField( sal_uInt16 nFieldIdx ) const +{ + return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : + ((nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : 0); +} + +XclImpPTField* XclImpPivotTable::GetFieldAcc( sal_uInt16 nFieldIdx ) +{ + // do not return maDataOrientField + return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : 0; +} + +const String& XclImpPivotTable::GetFieldName( sal_uInt16 nFieldIdx ) const +{ + if( const XclImpPTField* pField = GetField( nFieldIdx ) ) + return pField->GetFieldName(); + return EMPTY_STRING; +} + +const XclImpPTField* XclImpPivotTable::GetDataField( sal_uInt16 nDataFieldIdx ) const +{ + if( nDataFieldIdx < maOrigDataFields.size() ) + return GetField( maOrigDataFields[ nDataFieldIdx ] ); + return 0; +} + +const String& XclImpPivotTable::GetDataFieldName( sal_uInt16 nDataFieldIdx ) const +{ + if( const XclImpPTField* pField = GetDataField( nDataFieldIdx ) ) + return pField->GetFieldName(); + return EMPTY_STRING; +} + +// records -------------------------------------------------------------------- + +void XclImpPivotTable::ReadSxview( XclImpStream& rStrm ) +{ + rStrm >> maPTInfo; + + GetAddressConverter().ConvertRange( + maOutScRange, maPTInfo.maOutXclRange, GetCurrScTab(), GetCurrScTab(), true ); + + mxPCache = GetPivotTableManager().GetPivotCache( maPTInfo.mnCacheIdx ); + mxCurrField.reset(); +} + +void XclImpPivotTable::ReadSxvd( XclImpStream& rStrm ) +{ + sal_uInt16 nFieldCount = GetFieldCount(); + if( nFieldCount < EXC_PT_MAXFIELDCOUNT ) + { + // cache index for the field is equal to the SXVD record index + mxCurrField.reset( new XclImpPTField( *this, nFieldCount ) ); + maFields.push_back( mxCurrField ); + mxCurrField->ReadSxvd( rStrm ); + // add visible name of new field to list of visible names + maVisFieldNames.push_back( mxCurrField->GetVisFieldName() ); + DBG_ASSERT( maFields.size() == maVisFieldNames.size(), + "XclImpPivotTable::ReadSxvd - wrong size of visible name array" ); + } + else + mxCurrField.reset(); +} + +void XclImpPivotTable::ReadSxvi( XclImpStream& rStrm ) +{ + if( mxCurrField.is() ) + mxCurrField->ReadSxvi( rStrm ); +} + +void XclImpPivotTable::ReadSxvdex( XclImpStream& rStrm ) +{ + if( mxCurrField.is() ) + mxCurrField->ReadSxvdex( rStrm ); +} + +void XclImpPivotTable::ReadSxivd( XclImpStream& rStrm ) +{ + mxCurrField.reset(); + + // find the index vector to fill (row SXIVD doesn't exist without row fields) + ScfUInt16Vec* pFieldVec = 0; + if( maRowFields.empty() && (maPTInfo.mnRowFields > 0) ) + pFieldVec = &maRowFields; + else if( maColFields.empty() && (maPTInfo.mnColFields > 0) ) + pFieldVec = &maColFields; + + // fill the vector from record data + if( pFieldVec ) + { + sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 2, EXC_PT_MAXROWCOLCOUNT ); + pFieldVec->reserve( nSize ); + for( sal_uInt16 nIdx = 0; nIdx < nSize; ++nIdx ) + { + sal_uInt16 nFieldIdx; + rStrm >> nFieldIdx; + pFieldVec->push_back( nFieldIdx ); + + // set orientation at special data orientation field + if( nFieldIdx == EXC_SXIVD_DATA ) + { + sal_uInt16 nAxis = (pFieldVec == &maRowFields) ? EXC_SXVD_AXIS_ROW : EXC_SXVD_AXIS_COL; + maDataOrientField.SetAxes( nAxis ); + } + } + } +} + +void XclImpPivotTable::ReadSxpi( XclImpStream& rStrm ) +{ + mxCurrField.reset(); + + sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 6 ); + for( sal_uInt16 nEntry = 0; nEntry < nSize; ++nEntry ) + { + XclPTPageFieldInfo aPageInfo; + rStrm >> aPageInfo; + if( XclImpPTField* pField = GetFieldAcc( aPageInfo.mnField ) ) + { + maPageFields.push_back( aPageInfo.mnField ); + pField->SetPageFieldInfo( aPageInfo ); + } + GetCurrSheetDrawing().SetSkipObj( aPageInfo.mnObjId ); + } +} + +void XclImpPivotTable::ReadSxdi( XclImpStream& rStrm ) +{ + mxCurrField.reset(); + + XclPTDataFieldInfo aDataInfo; + rStrm >> aDataInfo; + if( XclImpPTField* pField = GetFieldAcc( aDataInfo.mnField ) ) + { + maOrigDataFields.push_back( aDataInfo.mnField ); + // DataPilot does not support double data fields -> add first appearence to index list only + if( !pField->HasDataFieldInfo() ) + maFiltDataFields.push_back( aDataInfo.mnField ); + pField->AddDataFieldInfo( aDataInfo ); + } +} + +void XclImpPivotTable::ReadSxex( XclImpStream& rStrm ) +{ + rStrm >> maPTExtInfo; +} + +void XclImpPivotTable::ReadSxViewEx9( XclImpStream& rStrm ) +{ + rStrm >> maPTViewEx9Info; +} + +// ---------------------------------------------------------------------------- + +void XclImpPivotTable::Convert() +{ + if( !mxPCache || !mxPCache->GetSourceRange().IsValid() ) + return; + + ScDPSaveData aSaveData; + + // *** global settings *** + + aSaveData.SetRowGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND ) ); + aSaveData.SetColumnGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND ) ); + aSaveData.SetFilterButton( FALSE ); + aSaveData.SetDrillDown( ::get_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN ) ); + + // *** fields *** + + ScfUInt16Vec::const_iterator aIt, aEnd; + + // row fields + for( aIt = maRowFields.begin(), aEnd = maRowFields.end(); aIt != aEnd; ++aIt ) + if( const XclImpPTField* pField = GetField( *aIt ) ) + pField->ConvertRowColField( aSaveData ); + + // column fields + for( aIt = maColFields.begin(), aEnd = maColFields.end(); aIt != aEnd; ++aIt ) + if( const XclImpPTField* pField = GetField( *aIt ) ) + pField->ConvertRowColField( aSaveData ); + + // page fields + for( aIt = maPageFields.begin(), aEnd = maPageFields.end(); aIt != aEnd; ++aIt ) + if( const XclImpPTField* pField = GetField( *aIt ) ) + pField->ConvertPageField( aSaveData ); + + // We need to import hidden fields because hidden fields may contain + // special settings for subtotals (aggregation function, filters, custom + // name etc.) and members (hidden, custom name etc.). + + // hidden fields + for( sal_uInt16 nField = 0, nCount = GetFieldCount(); nField < nCount; ++nField ) + if( const XclImpPTField* pField = GetField( nField ) ) + if( (pField->GetAxes() & EXC_SXVD_AXIS_ROWCOLPAGE) == 0 ) + pField->ConvertHiddenField( aSaveData ); + + // data fields + for( aIt = maFiltDataFields.begin(), aEnd = maFiltDataFields.end(); aIt != aEnd; ++aIt ) + if( const XclImpPTField* pField = GetField( *aIt ) ) + pField->ConvertDataField( aSaveData ); + + // *** insert into Calc document *** + + // create source descriptor + ScSheetSourceDesc aDesc; + aDesc.aSourceRange = mxPCache->GetSourceRange(); + + // adjust output range to include the page fields + ScRange aOutRange( maOutScRange ); + if( !maPageFields.empty() ) + { + SCsROW nDecRows = ::std::min< SCsROW >( aOutRange.aStart.Row(), maPageFields.size() + 1 ); + aOutRange.aStart.IncRow( -nDecRows ); + } + + // create the DataPilot + ScDPObject* pDPObj = new ScDPObject( GetDocPtr() ); + pDPObj->SetName( maPTInfo.maTableName ); + if (maPTInfo.maDataName.Len() > 0) + aSaveData.GetDataLayoutDimension()->SetLayoutName(maPTInfo.maDataName); + + if (maPTViewEx9Info.maGrandTotalName.Len() > 0) + aSaveData.SetGrandTotalName(maPTViewEx9Info.maGrandTotalName); + + pDPObj->SetSaveData( aSaveData ); + pDPObj->SetSheetDesc( aDesc ); + pDPObj->SetOutRange( aOutRange ); + pDPObj->SetAlive( TRUE ); + pDPObj->SetHeaderLayout( maPTViewEx9Info.mnGridLayout == 0 ); + + GetDoc().GetDPCollection()->InsertNewTable(pDPObj); + mpDPObj = pDPObj; + + ApplyMergeFlags(aOutRange, aSaveData); +} + +void XclImpPivotTable::MaybeRefresh() +{ + if (mpDPObj && mxPCache->IsRefreshOnLoad()) + { + // 'refresh table on load' flag is set. Refresh the table now. Some + // Excel files contain partial table output when this flag is set. + ScRange aOutRange = mpDPObj->GetOutRange(); + mpDPObj->Output(aOutRange.aStart); + } +} + +void XclImpPivotTable::ApplyMergeFlags(const ScRange& rOutRange, const ScDPSaveData& rSaveData) +{ + // Apply merge flags for varoius datapilot controls. + + ScDPOutputGeometry aGeometry(rOutRange, false, ScDPOutputGeometry::XLS); + aGeometry.setColumnFieldCount(maPTInfo.mnColFields); + aGeometry.setPageFieldCount(maPTInfo.mnPageFields); + aGeometry.setDataFieldCount(maPTInfo.mnDataFields); + + // Excel includes data layout field in the row field count. We need to + // subtract it. + bool bDataLayout = maPTInfo.mnDataFields > 1; + aGeometry.setRowFieldCount(maPTInfo.mnRowFields - static_cast<sal_uInt32>(bDataLayout)); + + ScDocument& rDoc = GetDoc(); + + vector<ScAddress> aPageBtns; + aGeometry.getPageFieldPositions(aPageBtns); + vector<ScAddress>::const_iterator itr = aPageBtns.begin(), itrEnd = aPageBtns.end(); + for (; itr != itrEnd; ++itr) + { + sal_uInt16 nMFlag = SC_MF_BUTTON; + String aName; + rDoc.GetString(itr->Col(), itr->Row(), itr->Tab(), aName); + if (rSaveData.HasInvisibleMember(aName)) + nMFlag |= SC_MF_HIDDEN_MEMBER; + + rDoc.ApplyFlagsTab(itr->Col(), itr->Row(), itr->Col(), itr->Row(), itr->Tab(), nMFlag); + rDoc.ApplyFlagsTab(itr->Col()+1, itr->Row(), itr->Col()+1, itr->Row(), itr->Tab(), SC_MF_AUTO); + } + + vector<ScAddress> aColBtns; + aGeometry.getColumnFieldPositions(aColBtns); + itr = aColBtns.begin(); + itrEnd = aColBtns.end(); + for (; itr != itrEnd; ++itr) + { + sal_Int16 nMFlag = SC_MF_BUTTON | SC_MF_BUTTON_POPUP; + String aName; + rDoc.GetString(itr->Col(), itr->Row(), itr->Tab(), aName); + if (rSaveData.HasInvisibleMember(aName)) + nMFlag |= SC_MF_HIDDEN_MEMBER; + rDoc.ApplyFlagsTab(itr->Col(), itr->Row(), itr->Col(), itr->Row(), itr->Tab(), nMFlag); + } + + vector<ScAddress> aRowBtns; + aGeometry.getRowFieldPositions(aRowBtns); + if (aRowBtns.empty()) + { + if (bDataLayout) + { + // No row fields, but the data layout button exists. + SCROW nRow = aGeometry.getRowFieldHeaderRow(); + SCCOL nCol = rOutRange.aStart.Col(); + SCTAB nTab = rOutRange.aStart.Tab(); + rDoc.ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, SC_MF_BUTTON); + } + } + else + { + itr = aRowBtns.begin(); + itrEnd = aRowBtns.end(); + for (; itr != itrEnd; ++itr) + { + sal_Int16 nMFlag = SC_MF_BUTTON | SC_MF_BUTTON_POPUP; + String aName; + rDoc.GetString(itr->Col(), itr->Row(), itr->Tab(), aName); + if (rSaveData.HasInvisibleMember(aName)) + nMFlag |= SC_MF_HIDDEN_MEMBER; + rDoc.ApplyFlagsTab(itr->Col(), itr->Row(), itr->Col(), itr->Row(), itr->Tab(), nMFlag); + } + if (bDataLayout) + { + --itr; // move back to the last row field position. + rDoc.ApplyFlagsTab(itr->Col(), itr->Row(), itr->Col(), itr->Row(), itr->Tab(), SC_MF_BUTTON); + } + } +} + +// ============================================================================ +// ============================================================================ + +XclImpPivotTableManager::XclImpPivotTableManager( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +XclImpPivotTableManager::~XclImpPivotTableManager() +{ +} + +// pivot cache records -------------------------------------------------------- + +XclImpPivotCacheRef XclImpPivotTableManager::GetPivotCache( sal_uInt16 nCacheIdx ) +{ + XclImpPivotCacheRef xPCache; + if( nCacheIdx < maPCaches.size() ) + xPCache = maPCaches[ nCacheIdx ]; + return xPCache; +} + +void XclImpPivotTableManager::ReadSxidstm( XclImpStream& rStrm ) +{ + XclImpPivotCacheRef xPCache( new XclImpPivotCache( GetRoot() ) ); + maPCaches.push_back( xPCache ); + xPCache->ReadSxidstm( rStrm ); +} + +void XclImpPivotTableManager::ReadSxvs( XclImpStream& rStrm ) +{ + if( !maPCaches.empty() ) + maPCaches.back()->ReadSxvs( rStrm ); +} + +void XclImpPivotTableManager::ReadDconref( XclImpStream& rStrm ) +{ + if( !maPCaches.empty() ) + maPCaches.back()->ReadDconref( rStrm ); +} + +// pivot table records -------------------------------------------------------- + +void XclImpPivotTableManager::ReadSxview( XclImpStream& rStrm ) +{ + XclImpPivotTableRef xPTable( new XclImpPivotTable( GetRoot() ) ); + maPTables.push_back( xPTable ); + xPTable->ReadSxview( rStrm ); +} + +void XclImpPivotTableManager::ReadSxvd( XclImpStream& rStrm ) +{ + if( !maPTables.empty() ) + maPTables.back()->ReadSxvd( rStrm ); +} + +void XclImpPivotTableManager::ReadSxvdex( XclImpStream& rStrm ) +{ + if( !maPTables.empty() ) + maPTables.back()->ReadSxvdex( rStrm ); +} + +void XclImpPivotTableManager::ReadSxivd( XclImpStream& rStrm ) +{ + if( !maPTables.empty() ) + maPTables.back()->ReadSxivd( rStrm ); +} + +void XclImpPivotTableManager::ReadSxpi( XclImpStream& rStrm ) +{ + if( !maPTables.empty() ) + maPTables.back()->ReadSxpi( rStrm ); +} + +void XclImpPivotTableManager::ReadSxdi( XclImpStream& rStrm ) +{ + if( !maPTables.empty() ) + maPTables.back()->ReadSxdi( rStrm ); +} + +void XclImpPivotTableManager::ReadSxvi( XclImpStream& rStrm ) +{ + if( !maPTables.empty() ) + maPTables.back()->ReadSxvi( rStrm ); +} + +void XclImpPivotTableManager::ReadSxex( XclImpStream& rStrm ) +{ + if( !maPTables.empty() ) + maPTables.back()->ReadSxex( rStrm ); +} + +void XclImpPivotTableManager::ReadSxViewEx9( XclImpStream& rStrm ) +{ + if( !maPTables.empty() ) + maPTables.back()->ReadSxViewEx9( rStrm ); +} + +// ---------------------------------------------------------------------------- + +void XclImpPivotTableManager::ReadPivotCaches( XclImpStream& rStrm ) +{ + for( XclImpPivotCacheVec::iterator aIt = maPCaches.begin(), aEnd = maPCaches.end(); aIt != aEnd; ++aIt ) + (*aIt)->ReadPivotCacheStream( rStrm ); +} + +void XclImpPivotTableManager::ConvertPivotTables() +{ + for( XclImpPivotTableVec::iterator aIt = maPTables.begin(), aEnd = maPTables.end(); aIt != aEnd; ++aIt ) + (*aIt)->Convert(); +} + +void XclImpPivotTableManager::MaybeRefreshPivotTables() +{ + for( XclImpPivotTableVec::iterator aIt = maPTables.begin(), aEnd = maPTables.end(); aIt != aEnd; ++aIt ) + (*aIt)->MaybeRefresh(); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xiroot.cxx b/sc/source/filter/excel/xiroot.cxx new file mode 100644 index 000000000000..5f8bcbd481e5 --- /dev/null +++ b/sc/source/filter/excel/xiroot.cxx @@ -0,0 +1,299 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xiroot.hxx" +#include "addincol.hxx" +#include "document.hxx" +#include "scextopt.hxx" +#include "xltracer.hxx" +#include "xihelper.hxx" +#include "xiformula.hxx" +#include "xilink.hxx" +#include "xiname.hxx" +#include "xistyle.hxx" +#include "xicontent.hxx" +#include "xiescher.hxx" +#include "xipivot.hxx" +#include "xipage.hxx" +#include "xiview.hxx" + +#include "root.hxx" +#include "excimp8.hxx" + +// Global data ================================================================ + +XclImpRootData::XclImpRootData( XclBiff eBiff, SfxMedium& rMedium, + SotStorageRef xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc ) : + XclRootData( eBiff, rMedium, xRootStrg, rDoc, eTextEnc, false ), + mbHasCodePage( false ), + mbHasBasic( false ) +{ +} + +XclImpRootData::~XclImpRootData() +{ +} + +// ---------------------------------------------------------------------------- + +XclImpRoot::XclImpRoot( XclImpRootData& rImpRootData ) : + XclRoot( rImpRootData ), + mrImpData( rImpRootData ) +{ + mrImpData.mxAddrConv.reset( new XclImpAddressConverter( GetRoot() ) ); + mrImpData.mxFmlaComp.reset( new XclImpFormulaCompiler( GetRoot() ) ); + mrImpData.mxPalette.reset( new XclImpPalette( GetRoot() ) ); + mrImpData.mxFontBfr.reset( new XclImpFontBuffer( GetRoot() ) ); + mrImpData.mxNumFmtBfr.reset( new XclImpNumFmtBuffer( GetRoot() ) ); + mrImpData.mpXFBfr.reset( new XclImpXFBuffer( GetRoot() ) ); + mrImpData.mxXFRangeBfr.reset( new XclImpXFRangeBuffer( GetRoot() ) ); + mrImpData.mxTabInfo.reset( new XclImpTabInfo ); + mrImpData.mxNameMgr.reset( new XclImpNameManager( GetRoot() ) ); + mrImpData.mxObjMgr.reset( new XclImpObjectManager( GetRoot() ) ); + + if( GetBiff() == EXC_BIFF8 ) + { + mrImpData.mxLinkMgr.reset( new XclImpLinkManager( GetRoot() ) ); + mrImpData.mxSst.reset( new XclImpSst( GetRoot() ) ); + mrImpData.mxCondFmtMgr.reset( new XclImpCondFormatManager( GetRoot() ) ); + // TODO still in old RootData (deleted by RootData) + GetOldRoot().pAutoFilterBuffer = new XclImpAutoFilterBuffer; + mrImpData.mxWebQueryBfr.reset( new XclImpWebQueryBuffer( GetRoot() ) ); + mrImpData.mxPTableMgr.reset( new XclImpPivotTableManager( GetRoot() ) ); + mrImpData.mxTabProtect.reset( new XclImpSheetProtectBuffer( GetRoot() ) ); + mrImpData.mxDocProtect.reset( new XclImpDocProtectBuffer( GetRoot() ) ); + } + + mrImpData.mxPageSett.reset( new XclImpPageSettings( GetRoot() ) ); + mrImpData.mxDocViewSett.reset( new XclImpDocViewSettings( GetRoot() ) ); + mrImpData.mxTabViewSett.reset( new XclImpTabViewSettings( GetRoot() ) ); +} + +void XclImpRoot::SetCodePage( sal_uInt16 nCodePage ) +{ + SetTextEncoding( XclTools::GetTextEncoding( nCodePage ) ); + mrImpData.mbHasCodePage = true; +} + +void XclImpRoot::SetAppFontEncoding( rtl_TextEncoding eAppFontEnc ) +{ + if( !mrImpData.mbHasCodePage ) + SetTextEncoding( eAppFontEnc ); +} + +void XclImpRoot::InitializeTable( SCTAB /*nScTab*/ ) +{ + if( GetBiff() <= EXC_BIFF4 ) + { + GetPalette().Initialize(); + GetFontBuffer().Initialize(); + GetNumFmtBuffer().Initialize(); + GetXFBuffer().Initialize(); + } + GetXFRangeBuffer().Initialize(); + GetPageSettings().Initialize(); + GetTabViewSettings().Initialize(); +} + +void XclImpRoot::FinalizeTable() +{ + GetXFRangeBuffer().Finalize(); + GetOldRoot().pColRowBuff->Convert( GetCurrScTab() ); + GetPageSettings().Finalize(); + GetTabViewSettings().Finalize(); +} + +XclImpAddressConverter& XclImpRoot::GetAddressConverter() const +{ + return *mrImpData.mxAddrConv; +} + +XclImpFormulaCompiler& XclImpRoot::GetFormulaCompiler() const +{ + return *mrImpData.mxFmlaComp; +} + +ExcelToSc& XclImpRoot::GetOldFmlaConverter() const +{ + // TODO still in old RootData + return *GetOldRoot().pFmlaConverter; +} + +XclImpSst& XclImpRoot::GetSst() const +{ + DBG_ASSERT( mrImpData.mxSst.is(), "XclImpRoot::GetSst - invalid call, wrong BIFF" ); + return *mrImpData.mxSst; +} + +XclImpPalette& XclImpRoot::GetPalette() const +{ + return *mrImpData.mxPalette; +} + +XclImpFontBuffer& XclImpRoot::GetFontBuffer() const +{ + return *mrImpData.mxFontBfr; +} + +XclImpNumFmtBuffer& XclImpRoot::GetNumFmtBuffer() const +{ + return *mrImpData.mxNumFmtBfr; +} + +XclImpXFBuffer& XclImpRoot::GetXFBuffer() const +{ + return *mrImpData.mpXFBfr; +} + +XclImpXFRangeBuffer& XclImpRoot::GetXFRangeBuffer() const +{ + return *mrImpData.mxXFRangeBfr; +} + +_ScRangeListTabs& XclImpRoot::GetPrintAreaBuffer() const +{ + // TODO still in old RootData + return *GetOldRoot().pPrintRanges; +} + +_ScRangeListTabs& XclImpRoot::GetTitleAreaBuffer() const +{ + // TODO still in old RootData + return *GetOldRoot().pPrintTitles; +} + +XclImpTabInfo& XclImpRoot::GetTabInfo() const +{ + return *mrImpData.mxTabInfo; +} + +XclImpNameManager& XclImpRoot::GetNameManager() const +{ + return *mrImpData.mxNameMgr; +} + +XclImpLinkManager& XclImpRoot::GetLinkManager() const +{ + DBG_ASSERT( mrImpData.mxLinkMgr.is(), "XclImpRoot::GetLinkManager - invalid call, wrong BIFF" ); + return *mrImpData.mxLinkMgr; +} + +XclImpObjectManager& XclImpRoot::GetObjectManager() const +{ + return *mrImpData.mxObjMgr; +} + +XclImpSheetDrawing& XclImpRoot::GetCurrSheetDrawing() const +{ + DBG_ASSERT( !IsInGlobals(), "XclImpRoot::GetCurrSheetDrawing - must not be called from workbook globals" ); + return mrImpData.mxObjMgr->GetSheetDrawing( GetCurrScTab() ); +} + +XclImpCondFormatManager& XclImpRoot::GetCondFormatManager() const +{ + DBG_ASSERT( mrImpData.mxCondFmtMgr.is(), "XclImpRoot::GetCondFormatManager - invalid call, wrong BIFF" ); + return *mrImpData.mxCondFmtMgr; +} + +XclImpAutoFilterBuffer& XclImpRoot::GetFilterManager() const +{ + // TODO still in old RootData + DBG_ASSERT( GetOldRoot().pAutoFilterBuffer, "XclImpRoot::GetFilterManager - invalid call, wrong BIFF" ); + return *GetOldRoot().pAutoFilterBuffer; +} + +XclImpWebQueryBuffer& XclImpRoot::GetWebQueryBuffer() const +{ + DBG_ASSERT( mrImpData.mxWebQueryBfr.is(), "XclImpRoot::GetWebQueryBuffer - invalid call, wrong BIFF" ); + return *mrImpData.mxWebQueryBfr; +} + +XclImpPivotTableManager& XclImpRoot::GetPivotTableManager() const +{ + DBG_ASSERT( mrImpData.mxPTableMgr.is(), "XclImpRoot::GetPivotTableManager - invalid call, wrong BIFF" ); + return *mrImpData.mxPTableMgr; +} + +XclImpSheetProtectBuffer& XclImpRoot::GetSheetProtectBuffer() const +{ + DBG_ASSERT( mrImpData.mxTabProtect.is(), "XclImpRoot::GetSheetProtectBuffer - invalid call, wrong BIFF" ); + return *mrImpData.mxTabProtect; +} + +XclImpDocProtectBuffer& XclImpRoot::GetDocProtectBuffer() const +{ + DBG_ASSERT( mrImpData.mxDocProtect.is(), "XclImpRoot::GetDocProtectBuffer - invalid call, wrong BIFF" ); + return *mrImpData.mxDocProtect; +} + +XclImpPageSettings& XclImpRoot::GetPageSettings() const +{ + return *mrImpData.mxPageSett; +} + +XclImpDocViewSettings& XclImpRoot::GetDocViewSettings() const +{ + return *mrImpData.mxDocViewSett; +} + +XclImpTabViewSettings& XclImpRoot::GetTabViewSettings() const +{ + return *mrImpData.mxTabViewSett; +} + +String XclImpRoot::GetScAddInName( const String& rXclName ) const +{ + String aScName; + if( ScGlobal::GetAddInCollection()->GetCalcName( rXclName, aScName ) ) + return aScName; + return rXclName; +} + +void XclImpRoot::ReadCodeName( XclImpStream& rStrm, bool bGlobals ) +{ + if( mrImpData.mbHasBasic && (GetBiff() == EXC_BIFF8) ) + { + String aName = rStrm.ReadUniString(); + if( aName.Len() > 0 ) + { + if( bGlobals ) + { + GetExtDocOptions().GetDocSettings().maGlobCodeName = aName; + GetDoc().SetCodeName( aName ); + } + else + { + GetExtDocOptions().SetCodeName( GetCurrScTab(), aName ); + GetDoc().SetCodeName( GetCurrScTab(), aName ); + } + } + } +} + +// ============================================================================ diff --git a/sc/source/filter/excel/xistream.cxx b/sc/source/filter/excel/xistream.cxx new file mode 100644 index 000000000000..98db9dcb4471 --- /dev/null +++ b/sc/source/filter/excel/xistream.cxx @@ -0,0 +1,1102 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "xistream.hxx" +#include "xlstring.hxx" +#include "xiroot.hxx" + +#include <vector> + +using ::rtl::OString; +using ::rtl::OUString; +using ::rtl::OUStringToOString; + +// ============================================================================ +// Decryption +// ============================================================================ + +XclImpDecrypter::XclImpDecrypter() : + mnError( EXC_ENCR_ERROR_UNSUPP_CRYPT ), + mnOldPos( STREAM_SEEK_TO_END ), + mnRecSize( 0 ) +{ +} + +XclImpDecrypter::XclImpDecrypter( const XclImpDecrypter& rSrc ) : + ::comphelper::IDocPasswordVerifier(), + mnError( rSrc.mnError ), + mnOldPos( STREAM_SEEK_TO_END ), + mnRecSize( 0 ) +{ +} + +XclImpDecrypter::~XclImpDecrypter() +{ +} + +XclImpDecrypterRef XclImpDecrypter::Clone() const +{ + XclImpDecrypterRef xNewDecr; + if( IsValid() ) + xNewDecr.reset( OnClone() ); + return xNewDecr; +} + +::comphelper::DocPasswordVerifierResult XclImpDecrypter::verifyPassword( const OUString& rPassword ) +{ + bool bValid = OnVerify( rPassword ); + mnError = bValid ? ERRCODE_NONE : ERRCODE_ABORT; + return bValid ? ::comphelper::DocPasswordVerifierResult_OK : ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD; +} + +void XclImpDecrypter::Update( SvStream& rStrm, sal_uInt16 nRecSize ) +{ + if( IsValid() ) + { + sal_Size nNewPos = rStrm.Tell(); + if( (mnOldPos != nNewPos) || (mnRecSize != nRecSize) ) + { + OnUpdate( mnOldPos, nNewPos, nRecSize ); + mnOldPos = nNewPos; + mnRecSize = nRecSize; + } + } +} + +sal_uInt16 XclImpDecrypter::Read( SvStream& rStrm, void* pData, sal_uInt16 nBytes ) +{ + sal_uInt16 nRet = 0; + if( pData && nBytes ) + { + if( IsValid() ) + { + Update( rStrm, mnRecSize ); + nRet = OnRead( rStrm, reinterpret_cast< sal_uInt8* >( pData ), nBytes ); + mnOldPos = rStrm.Tell(); + } + else + nRet = static_cast< sal_uInt16 >( rStrm.Read( pData, nBytes ) ); + } + return nRet; +} + +// ---------------------------------------------------------------------------- + +XclImpBiff5Decrypter::XclImpBiff5Decrypter( sal_uInt16 nKey, sal_uInt16 nHash ) : + maPassword( 16 ), + mnKey( nKey ), + mnHash( nHash ) +{ +} + +XclImpBiff5Decrypter::XclImpBiff5Decrypter( const XclImpBiff5Decrypter& rSrc ) : + XclImpDecrypter( rSrc ), + maPassword( rSrc.maPassword ), + mnKey( rSrc.mnKey ), + mnHash( rSrc.mnHash ) +{ + if( IsValid() ) + maCodec.InitKey( &maPassword.front() ); +} + +XclImpBiff5Decrypter* XclImpBiff5Decrypter::OnClone() const +{ + return new XclImpBiff5Decrypter( *this ); +} + +bool XclImpBiff5Decrypter::OnVerify( const OUString& rPassword ) +{ + /* Convert password to a byte string. TODO: this needs some finetuning + according to the spec... */ + OString aBytePassword = OUStringToOString( rPassword, osl_getThreadTextEncoding() ); + sal_Int32 nLen = aBytePassword.getLength(); + if( (0 < nLen) && (nLen < 16) ) + { + // copy byte string to sal_uInt8 array + maPassword.clear(); + maPassword.resize( 16, 0 ); + memcpy( &maPassword.front(), aBytePassword.getStr(), static_cast< size_t >( nLen ) ); + + // init codec + maCodec.InitKey( &maPassword.front() ); + return maCodec.VerifyKey( mnKey, mnHash ); + } + return false; +} + +void XclImpBiff5Decrypter::OnUpdate( sal_Size /*nOldStrmPos*/, sal_Size nNewStrmPos, sal_uInt16 nRecSize ) +{ + maCodec.InitCipher(); + maCodec.Skip( (nNewStrmPos + nRecSize) & 0x0F ); +} + +sal_uInt16 XclImpBiff5Decrypter::OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes ) +{ + sal_uInt16 nRet = static_cast< sal_uInt16 >( rStrm.Read( pnData, nBytes ) ); + maCodec.Decode( pnData, nRet ); + return nRet; +} + +// ---------------------------------------------------------------------------- + +XclImpBiff8Decrypter::XclImpBiff8Decrypter( sal_uInt8 pnSalt[ 16 ], + sal_uInt8 pnVerifier[ 16 ], sal_uInt8 pnVerifierHash[ 16 ] ) : + maPassword( 16, 0 ), + maSalt( pnSalt, pnSalt + 16 ), + maVerifier( pnVerifier, pnVerifier + 16 ), + maVerifierHash( pnVerifierHash, pnVerifierHash + 16 ) +{ +} + +XclImpBiff8Decrypter::XclImpBiff8Decrypter( const XclImpBiff8Decrypter& rSrc ) : + XclImpDecrypter( rSrc ), + maPassword( rSrc.maPassword ), + maSalt( rSrc.maSalt ), + maVerifier( rSrc.maVerifier ), + maVerifierHash( rSrc.maVerifierHash ) +{ + if( IsValid() ) + maCodec.InitKey( &maPassword.front(), &maSalt.front() ); +} + +XclImpBiff8Decrypter* XclImpBiff8Decrypter::OnClone() const +{ + return new XclImpBiff8Decrypter( *this ); +} + +bool XclImpBiff8Decrypter::OnVerify( const OUString& rPassword ) +{ + sal_Int32 nLen = rPassword.getLength(); + if( (0 < nLen) && (nLen < 16) ) + { + // copy string to sal_uInt16 array + maPassword.clear(); + maPassword.resize( 16, 0 ); + const sal_Unicode* pcChar = rPassword.getStr(); + const sal_Unicode* pcCharEnd = pcChar + nLen; + ::std::vector< sal_uInt16 >::iterator aIt = maPassword.begin(); + for( ; pcChar < pcCharEnd; ++pcChar, ++aIt ) + *aIt = static_cast< sal_uInt16 >( *pcChar ); + + // init codec + maCodec.InitKey( &maPassword.front(), &maSalt.front() ); + return maCodec.VerifyKey( &maVerifier.front(), &maVerifierHash.front() ); + } + return false; +} + +void XclImpBiff8Decrypter::OnUpdate( sal_Size nOldStrmPos, sal_Size nNewStrmPos, sal_uInt16 /*nRecSize*/ ) +{ + if( nNewStrmPos != nOldStrmPos ) + { + sal_uInt32 nOldBlock = GetBlock( nOldStrmPos ); + sal_uInt16 nOldOffset = GetOffset( nOldStrmPos ); + + sal_uInt32 nNewBlock = GetBlock( nNewStrmPos ); + sal_uInt16 nNewOffset = GetOffset( nNewStrmPos ); + + /* Rekey cipher, if block changed or if previous offset in same block. */ + if( (nNewBlock != nOldBlock) || (nNewOffset < nOldOffset) ) + { + maCodec.InitCipher( nNewBlock ); + nOldOffset = 0; // reset nOldOffset for next if() statement + } + + /* Seek to correct offset. */ + if( nNewOffset > nOldOffset ) + maCodec.Skip( nNewOffset - nOldOffset ); + } +} + +sal_uInt16 XclImpBiff8Decrypter::OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes ) +{ + sal_uInt16 nRet = 0; + + sal_uInt8* pnCurrData = pnData; + sal_uInt16 nBytesLeft = nBytes; + while( nBytesLeft ) + { + sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - GetOffset( rStrm.Tell() ); + sal_uInt16 nDecBytes = ::std::min< sal_uInt16 >( nBytesLeft, nBlockLeft ); + + // read the block from stream + nRet = nRet + static_cast< sal_uInt16 >( rStrm.Read( pnCurrData, nDecBytes ) ); + // decode the block inplace + maCodec.Decode( pnCurrData, nDecBytes, pnCurrData, nDecBytes ); + if( GetOffset( rStrm.Tell() ) == 0 ) + maCodec.InitCipher( GetBlock( rStrm.Tell() ) ); + + pnCurrData += nDecBytes; + nBytesLeft = nBytesLeft - nDecBytes; + } + + return nRet; +} + +sal_uInt32 XclImpBiff8Decrypter::GetBlock( sal_Size nStrmPos ) const +{ + return static_cast< sal_uInt32 >( nStrmPos / EXC_ENCR_BLOCKSIZE ); +} + +sal_uInt16 XclImpBiff8Decrypter::GetOffset( sal_Size nStrmPos ) const +{ + return static_cast< sal_uInt16 >( nStrmPos % EXC_ENCR_BLOCKSIZE ); +} + +// ============================================================================ +// Stream +// ============================================================================ + +XclImpStreamPos::XclImpStreamPos() : + mnPos( STREAM_SEEK_TO_BEGIN ), + mnNextPos( STREAM_SEEK_TO_BEGIN ), + mnCurrSize( 0 ), + mnRawRecId( EXC_ID_UNKNOWN ), + mnRawRecSize( 0 ), + mnRawRecLeft( 0 ), + mbValid( false ) +{ +} + +void XclImpStreamPos::Set( + const SvStream& rStrm, sal_Size nNextPos, sal_Size nCurrSize, + sal_uInt16 nRawRecId, sal_uInt16 nRawRecSize, sal_uInt16 nRawRecLeft, + bool bValid ) +{ + mnPos = rStrm.Tell(); + mnNextPos = nNextPos; + mnCurrSize = nCurrSize; + mnRawRecId = nRawRecId; + mnRawRecSize = nRawRecSize; + mnRawRecLeft = nRawRecLeft; + mbValid = bValid; +} + +void XclImpStreamPos::Get( + SvStream& rStrm, sal_Size& rnNextPos, sal_Size& rnCurrSize, + sal_uInt16& rnRawRecId, sal_uInt16& rnRawRecSize, sal_uInt16& rnRawRecLeft, + bool& rbValid ) const +{ + rStrm.Seek( mnPos ); + rnNextPos = mnNextPos; + rnCurrSize = mnCurrSize; + rnRawRecId = mnRawRecId; + rnRawRecSize = mnRawRecSize; + rnRawRecLeft = mnRawRecLeft; + rbValid = mbValid; +} + +// ============================================================================ + +XclBiff XclImpStream::DetectBiffVersion( SvStream& rStrm ) +{ + XclBiff eBiff = EXC_BIFF_UNKNOWN; + + rStrm.Seek( STREAM_SEEK_TO_BEGIN ); + sal_uInt16 nBofId, nBofSize; + rStrm >> nBofId >> nBofSize; + + if( (4 <= nBofSize) && (nBofSize <= 16) ) switch( nBofId ) + { + case EXC_ID2_BOF: + eBiff = EXC_BIFF2; + break; + case EXC_ID3_BOF: + eBiff = EXC_BIFF3; + break; + case EXC_ID4_BOF: + eBiff = EXC_BIFF4; + break; + case EXC_ID5_BOF: + { + sal_uInt16 nVersion; + rStrm >> nVersion; + // #i23425# #i44031# #i62752# there are some *really* broken documents out there... + switch( nVersion & 0xFF00 ) + { + case 0: eBiff = EXC_BIFF5; break; // #i44031# #i62752# + case EXC_BOF_BIFF2: eBiff = EXC_BIFF2; break; + case EXC_BOF_BIFF3: eBiff = EXC_BIFF3; break; + case EXC_BOF_BIFF4: eBiff = EXC_BIFF4; break; + case EXC_BOF_BIFF5: eBiff = EXC_BIFF5; break; + case EXC_BOF_BIFF8: eBiff = EXC_BIFF8; break; + default: DBG_ERROR1( "XclImpStream::DetectBiffVersion - unknown BIFF version: 0x%04hX", nVersion ); + } + } + break; + } + return eBiff; +} + +XclImpStream::XclImpStream( SvStream& rInStrm, const XclImpRoot& rRoot, bool bContLookup ) : + mrStrm( rInStrm ), + mrRoot( rRoot ), + mnGlobRecId( EXC_ID_UNKNOWN ), + mbGlobValidRec( false ), + mbHasGlobPos( false ), + mnNextRecPos( STREAM_SEEK_TO_BEGIN ), + mnCurrRecSize( 0 ), + mnComplRecSize( 0 ), + mbHasComplRec( false ), + mnRecId( EXC_ID_UNKNOWN ), + mnAltContId( EXC_ID_UNKNOWN ), + mnRawRecId( EXC_ID_UNKNOWN ), + mnRawRecSize( 0 ), + mnRawRecLeft( 0 ), + mcNulSubst( '?' ), + mbCont( bContLookup ), + mbUseDecr( false ), + mbValidRec( false ), + mbValid( false ) +{ + mrStrm.Seek( STREAM_SEEK_TO_END ); + mnStreamSize = mrStrm.Tell(); + mrStrm.Seek( STREAM_SEEK_TO_BEGIN ); + DBG_ASSERT( mnStreamSize < STREAM_SEEK_TO_END, "XclImpStream::XclImpStream - stream error" ); +} + +XclImpStream::~XclImpStream() +{ +} + +bool XclImpStream::StartNextRecord() +{ + maPosStack.clear(); + + /* #i4266# Counter to ignore zero records (id==len==0) (i.e. the application + "Crystal Report" writes zero records between other records) */ + sal_Size nZeroRecCount = 5; + bool bIsZeroRec = false; + + do + { + mbValidRec = ReadNextRawRecHeader(); + bIsZeroRec = (mnRawRecId == 0) && (mnRawRecSize == 0); + if( bIsZeroRec ) --nZeroRecCount; + mnNextRecPos = mrStrm.Tell() + mnRawRecSize; + } + while( mbValidRec && ((mbCont && IsContinueId( mnRawRecId )) || (bIsZeroRec && nZeroRecCount)) ); + + mbValidRec = mbValidRec && !bIsZeroRec; + mbValid = mbValidRec; + SetupRecord(); + + return mbValidRec; +} + +bool XclImpStream::StartNextRecord( sal_Size nNextRecPos ) +{ + mnNextRecPos = nNextRecPos; + return StartNextRecord(); +} + +void XclImpStream::ResetRecord( bool bContLookup, sal_uInt16 nAltContId ) +{ + if( mbValidRec ) + { + maPosStack.clear(); + RestorePosition( maFirstRec ); + mnCurrRecSize = mnComplRecSize = mnRawRecSize; + mbHasComplRec = !bContLookup; + mbCont = bContLookup; + mnAltContId = nAltContId; + EnableDecryption(); + } +} + +void XclImpStream::SetDecrypter( XclImpDecrypterRef xDecrypter ) +{ + mxDecrypter = xDecrypter; + EnableDecryption(); + SetupDecrypter(); +} + +void XclImpStream::CopyDecrypterFrom( const XclImpStream& rStrm ) +{ + XclImpDecrypterRef xNewDecr; + if( rStrm.mxDecrypter.is() ) + xNewDecr = rStrm.mxDecrypter->Clone(); + SetDecrypter( xNewDecr ); +} + +bool XclImpStream::HasValidDecrypter() const +{ + return mxDecrypter.is() && mxDecrypter->IsValid(); +} + +void XclImpStream::EnableDecryption( bool bEnable ) +{ + mbUseDecr = bEnable && HasValidDecrypter(); +} + +// ---------------------------------------------------------------------------- + +void XclImpStream::PushPosition() +{ + maPosStack.push_back( XclImpStreamPos() ); + StorePosition( maPosStack.back() ); +} + +void XclImpStream::PopPosition() +{ + DBG_ASSERT( !maPosStack.empty(), "XclImpStream::PopPosition - stack empty" ); + if( !maPosStack.empty() ) + { + RestorePosition( maPosStack.back() ); + maPosStack.pop_back(); + } +} + +//UNUSED2008-05 void XclImpStream::RejectPosition() +//UNUSED2008-05 { +//UNUSED2008-05 DBG_ASSERT( !maPosStack.empty(), "XclImpStream::RejectPosition - stack empty" ); +//UNUSED2008-05 if( !maPosStack.empty() ) +//UNUSED2008-05 maPosStack.pop_back(); +//UNUSED2008-05 } + +void XclImpStream::StoreGlobalPosition() +{ + StorePosition( maGlobPos ); + mnGlobRecId = mnRecId; + mbGlobValidRec = mbValidRec; + mbHasGlobPos = true; +} + +void XclImpStream::SeekGlobalPosition() +{ + DBG_ASSERT( mbHasGlobPos, "XclImpStream::SeekGlobalPosition - no position stored" ); + if( mbHasGlobPos ) + { + RestorePosition( maGlobPos ); + mnRecId = mnGlobRecId; + mnComplRecSize = mnCurrRecSize; + mbHasComplRec = !mbCont; + mbValidRec = mbGlobValidRec; + } +} + +sal_Size XclImpStream::GetRecPos() const +{ + return mbValid ? (mnCurrRecSize - mnRawRecLeft) : EXC_REC_SEEK_TO_END; +} + +sal_Size XclImpStream::GetRecSize() +{ + if( !mbHasComplRec ) + { + PushPosition(); + while( JumpToNextContinue() ) ; // JumpToNextContinue() adds up mnCurrRecSize + mnComplRecSize = mnCurrRecSize; + mbHasComplRec = true; + PopPosition(); + } + return mnComplRecSize; +} + +sal_Size XclImpStream::GetRecLeft() +{ + return mbValid ? (GetRecSize() - GetRecPos()) : 0; +} + +sal_uInt16 XclImpStream::GetNextRecId() +{ + sal_uInt16 nRecId = EXC_ID_UNKNOWN; + if( mbValidRec ) + { + PushPosition(); + while( JumpToNextContinue() ) ; // skip following CONTINUE records + if( mnNextRecPos < mnStreamSize ) + { + mrStrm.Seek( mnNextRecPos ); + mrStrm >> nRecId; + } + PopPosition(); + } + return nRecId; +} + +// ---------------------------------------------------------------------------- + +XclImpStream& XclImpStream::operator>>( sal_Int8& rnValue ) +{ + if( EnsureRawReadSize( 1 ) ) + { + if( mbUseDecr ) + mxDecrypter->Read( mrStrm, &rnValue, 1 ); + else + mrStrm >> rnValue; + --mnRawRecLeft; + } + return *this; +} + +XclImpStream& XclImpStream::operator>>( sal_uInt8& rnValue ) +{ + if( EnsureRawReadSize( 1 ) ) + { + if( mbUseDecr ) + mxDecrypter->Read( mrStrm, &rnValue, 1 ); + else + mrStrm >> rnValue; + --mnRawRecLeft; + } + return *this; +} + +XclImpStream& XclImpStream::operator>>( sal_Int16& rnValue ) +{ + if( EnsureRawReadSize( 2 ) ) + { + if( mbUseDecr ) + { + SVBT16 pnBuffer; + mxDecrypter->Read( mrStrm, pnBuffer, 2 ); + rnValue = static_cast< sal_Int16 >( SVBT16ToShort( pnBuffer ) ); + } + else + mrStrm >> rnValue; + mnRawRecLeft -= 2; + } + return *this; +} + +XclImpStream& XclImpStream::operator>>( sal_uInt16& rnValue ) +{ + if( EnsureRawReadSize( 2 ) ) + { + if( mbUseDecr ) + { + SVBT16 pnBuffer; + mxDecrypter->Read( mrStrm, pnBuffer, 2 ); + rnValue = SVBT16ToShort( pnBuffer ); + } + else + mrStrm >> rnValue; + mnRawRecLeft -= 2; + } + return *this; +} + +XclImpStream& XclImpStream::operator>>( sal_Int32& rnValue ) +{ + if( EnsureRawReadSize( 4 ) ) + { + if( mbUseDecr ) + { + SVBT32 pnBuffer; + mxDecrypter->Read( mrStrm, pnBuffer, 4 ); + rnValue = static_cast< sal_Int32 >( SVBT32ToUInt32( pnBuffer ) ); + } + else + mrStrm >> rnValue; + mnRawRecLeft -= 4; + } + return *this; +} + +XclImpStream& XclImpStream::operator>>( sal_uInt32& rnValue ) +{ + if( EnsureRawReadSize( 4 ) ) + { + if( mbUseDecr ) + { + SVBT32 pnBuffer; + mxDecrypter->Read( mrStrm, pnBuffer, 4 ); + rnValue = SVBT32ToUInt32( pnBuffer ); + } + else + mrStrm >> rnValue; + mnRawRecLeft -= 4; + } + return *this; +} + +XclImpStream& XclImpStream::operator>>( float& rfValue ) +{ + if( EnsureRawReadSize( 4 ) ) + { + if( mbUseDecr ) + { + SVBT32 pnBuffer; + mxDecrypter->Read( mrStrm, pnBuffer, 4 ); + sal_uInt32 nValue = SVBT32ToUInt32( pnBuffer ); + memcpy( &rfValue, &nValue, 4 ); + } + else + mrStrm >> rfValue; + mnRawRecLeft -= 4; + } + return *this; +} + +XclImpStream& XclImpStream::operator>>( double& rfValue ) +{ + if( EnsureRawReadSize( 8 ) ) + { + if( mbUseDecr ) + { + SVBT64 pnBuffer; + mxDecrypter->Read( mrStrm, pnBuffer, 8 ); + rfValue = SVBT64ToDouble( pnBuffer ); + } + else + mrStrm >> rfValue; + mnRawRecLeft -= 8; + } + return *this; +} + +sal_Int8 XclImpStream::ReadInt8() +{ + sal_Int8 nValue(0); + operator>>( nValue ); + return nValue; +} + +sal_uInt8 XclImpStream::ReaduInt8() +{ + sal_uInt8 nValue(0); + operator>>( nValue ); + return nValue; +} + +sal_Int16 XclImpStream::ReadInt16() +{ + sal_Int16 nValue(0); + operator>>( nValue ); + return nValue; +} + +sal_uInt16 XclImpStream::ReaduInt16() +{ + sal_uInt16 nValue(0); + operator>>( nValue ); + return nValue; +} + +sal_Int32 XclImpStream::ReadInt32() +{ + sal_Int32 nValue(0); + operator>>( nValue ); + return nValue; +} + +sal_uInt32 XclImpStream::ReaduInt32() +{ + sal_uInt32 nValue(0); + operator>>( nValue ); + return nValue; +} + +float XclImpStream::ReadFloat() +{ + float fValue(0.0); + operator>>( fValue ); + return fValue; +} + +double XclImpStream::ReadDouble() +{ + double fValue(0.0); + operator>>( fValue ); + return fValue; +} + +sal_Size XclImpStream::Read( void* pData, sal_Size nBytes ) +{ + sal_Size nRet = 0; + if( mbValid && pData && (nBytes > 0) ) + { + sal_uInt8* pnBuffer = reinterpret_cast< sal_uInt8* >( pData ); + sal_Size nBytesLeft = nBytes; + + while( mbValid && (nBytesLeft > 0) ) + { + sal_uInt16 nReadSize = GetMaxRawReadSize( nBytesLeft ); + sal_uInt16 nReadRet = ReadRawData( pnBuffer, nReadSize ); + nRet += nReadRet; + mbValid = (nReadSize == nReadRet); + DBG_ASSERT( mbValid, "XclImpStream::Read - stream read error" ); + pnBuffer += nReadRet; + nBytesLeft -= nReadRet; + if( mbValid && (nBytesLeft > 0) ) + JumpToNextContinue(); + DBG_ASSERT( mbValid, "XclImpStream::Read - record overread" ); + } + } + return nRet; +} + +sal_Size XclImpStream::CopyToStream( SvStream& rOutStrm, sal_Size nBytes ) +{ + sal_Size nRet = 0; + if( mbValid && (nBytes > 0) ) + { + const sal_Size nMaxBuffer = 4096; + sal_uInt8* pnBuffer = new sal_uInt8[ ::std::min( nBytes, nMaxBuffer ) ]; + sal_Size nBytesLeft = nBytes; + + while( mbValid && (nBytesLeft > 0) ) + { + sal_Size nReadSize = ::std::min( nBytesLeft, nMaxBuffer ); + nRet += Read( pnBuffer, nReadSize ); + rOutStrm.Write( pnBuffer, nReadSize ); + nBytesLeft -= nReadSize; + } + + delete[] pnBuffer; + } + return nRet; +} + +sal_Size XclImpStream::CopyRecordToStream( SvStream& rOutStrm ) +{ + sal_Size nRet = 0; + if( mbValidRec ) + { + PushPosition(); + RestorePosition( maFirstRec ); + nRet = CopyToStream( rOutStrm, GetRecSize() ); + PopPosition(); + } + return nRet; +} + +void XclImpStream::Seek( sal_Size nPos ) +{ + if( mbValidRec ) + { + sal_Size nCurrPos = GetRecPos(); + if( !mbValid || (nPos < nCurrPos) ) // from invalid state or backward + { + RestorePosition( maFirstRec ); + Ignore( nPos ); + } + else if( nPos > nCurrPos ) // forward + { + Ignore( nPos - nCurrPos ); + } + } +} + +void XclImpStream::Ignore( sal_Size nBytes ) +{ + // implementation similar to Read(), but without really reading anything + sal_Size nBytesLeft = nBytes; + while( mbValid && (nBytesLeft > 0) ) + { + sal_uInt16 nReadSize = GetMaxRawReadSize( nBytesLeft ); + mrStrm.SeekRel( nReadSize ); + mnRawRecLeft = mnRawRecLeft - nReadSize; + nBytesLeft -= nReadSize; + if( nBytesLeft > 0 ) + JumpToNextContinue(); + DBG_ASSERT( mbValid, "XclImpStream::Ignore - record overread" ); + } +} + +// ---------------------------------------------------------------------------- + +sal_Size XclImpStream::ReadUniStringExtHeader( + bool& rb16Bit, bool& rbRich, bool& rbFareast, + sal_uInt16& rnFormatRuns, sal_uInt32& rnExtInf, sal_uInt8 nFlags ) +{ + DBG_ASSERT( !::get_flag( nFlags, EXC_STRF_UNKNOWN ), "XclImpStream::ReadUniStringExt - unknown flags" ); + rb16Bit = ::get_flag( nFlags, EXC_STRF_16BIT ); + rbRich = ::get_flag( nFlags, EXC_STRF_RICH ); + rbFareast = ::get_flag( nFlags, EXC_STRF_FAREAST ); + rnFormatRuns = rbRich ? ReaduInt16() : 0; + rnExtInf = rbFareast ? ReaduInt32() : 0; + return rnExtInf + 4 * rnFormatRuns; +} + +sal_Size XclImpStream::ReadUniStringExtHeader( bool& rb16Bit, sal_uInt8 nFlags ) +{ + bool bRich, bFareast; + sal_uInt16 nCrun; + sal_uInt32 nExtInf; + return ReadUniStringExtHeader( rb16Bit, bRich, bFareast, nCrun, nExtInf, nFlags ); +} + +// ---------------------------------------------------------------------------- + +String XclImpStream::ReadRawUniString( sal_uInt16 nChars, bool b16Bit ) +{ + String aRet; + sal_uInt16 nCharsLeft = nChars; + sal_uInt16 nReadSize; + + sal_Unicode* pcBuffer = new sal_Unicode[ nCharsLeft + 1 ]; + + while( IsValid() && (nCharsLeft > 0) ) + { + if( b16Bit ) + { + nReadSize = ::std::min< sal_uInt16 >( nCharsLeft, mnRawRecLeft / 2 ); + DBG_ASSERT( (nReadSize <= nCharsLeft) || !(mnRawRecLeft & 0x1), + "XclImpStream::ReadRawUniString - missing a byte" ); + } + else + nReadSize = GetMaxRawReadSize( nCharsLeft ); + + sal_Unicode* pcUniChar = pcBuffer; + sal_Unicode* pcEndChar = pcBuffer + nReadSize; + + if( b16Bit ) + { + sal_uInt16 nReadChar; + for( ; IsValid() && (pcUniChar < pcEndChar); ++pcUniChar ) + { + operator>>( nReadChar ); + (*pcUniChar) = (nReadChar == EXC_NUL) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar ); + } + } + else + { + sal_uInt8 nReadChar; + for( ; IsValid() && (pcUniChar < pcEndChar); ++pcUniChar ) + { + operator>>( nReadChar ); + (*pcUniChar) = (nReadChar == EXC_NUL_C) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar ); + } + } + + *pcEndChar = '\0'; + aRet.Append( pcBuffer ); + + nCharsLeft = nCharsLeft - nReadSize; + if( nCharsLeft > 0 ) + JumpToNextStringContinue( b16Bit ); + } + + delete[] pcBuffer; + return aRet; +} + +String XclImpStream::ReadUniString( sal_uInt16 nChars, sal_uInt8 nFlags ) +{ + bool b16Bit; + sal_Size nExtSize = ReadUniStringExtHeader( b16Bit, nFlags ); + String aRet( ReadRawUniString( nChars, b16Bit ) ); + Ignore( nExtSize ); + return aRet; +} + +String XclImpStream::ReadUniString( sal_uInt16 nChars ) +{ + return ReadUniString( nChars, ReaduInt8() ); +} + +String XclImpStream::ReadUniString() +{ + return ReadUniString( ReaduInt16() ); +} + +void XclImpStream::IgnoreRawUniString( sal_uInt16 nChars, bool b16Bit ) +{ + sal_uInt16 nCharsLeft = nChars; + sal_uInt16 nReadSize; + + while( IsValid() && (nCharsLeft > 0) ) + { + if( b16Bit ) + { + nReadSize = ::std::min< sal_uInt16 >( nCharsLeft, mnRawRecLeft / 2 ); + DBG_ASSERT( (nReadSize <= nCharsLeft) || !(mnRawRecLeft & 0x1), + "XclImpStream::IgnoreRawUniString - missing a byte" ); + Ignore( nReadSize * 2 ); + } + else + { + nReadSize = GetMaxRawReadSize( nCharsLeft ); + Ignore( nReadSize ); + } + + nCharsLeft = nCharsLeft - nReadSize; + if( nCharsLeft > 0 ) + JumpToNextStringContinue( b16Bit ); + } +} + +void XclImpStream::IgnoreUniString( sal_uInt16 nChars, sal_uInt8 nFlags ) +{ + bool b16Bit; + sal_Size nExtSize = ReadUniStringExtHeader( b16Bit, nFlags ); + IgnoreRawUniString( nChars, b16Bit ); + Ignore( nExtSize ); +} + +void XclImpStream::IgnoreUniString( sal_uInt16 nChars ) +{ + IgnoreUniString( nChars, ReaduInt8() ); +} + +void XclImpStream::IgnoreUniString() +{ + IgnoreUniString( ReaduInt16() ); +} + +// ---------------------------------------------------------------------------- + +String XclImpStream::ReadRawByteString( sal_uInt16 nChars ) +{ + sal_Char* pcBuffer = new sal_Char[ nChars + 1 ]; + sal_uInt16 nCharsRead = ReadRawData( pcBuffer, nChars ); + pcBuffer[ nCharsRead ] = '\0'; + String aRet( pcBuffer, mrRoot.GetTextEncoding() ); + delete[] pcBuffer; + return aRet; +} + +String XclImpStream::ReadByteString( bool b16BitLen ) +{ + return ReadRawByteString( ReadByteStrLen( b16BitLen ) ); +} + +// private -------------------------------------------------------------------- + +void XclImpStream::StorePosition( XclImpStreamPos& rPos ) +{ + rPos.Set( mrStrm, mnNextRecPos, mnCurrRecSize, mnRawRecId, mnRawRecSize, mnRawRecLeft, mbValid ); +} + +void XclImpStream::RestorePosition( const XclImpStreamPos& rPos ) +{ + rPos.Get( mrStrm, mnNextRecPos, mnCurrRecSize, mnRawRecId, mnRawRecSize, mnRawRecLeft, mbValid ); + SetupDecrypter(); +} + +bool XclImpStream::ReadNextRawRecHeader() +{ + mrStrm.Seek( mnNextRecPos ); + bool bRet = mnNextRecPos + 4 <= mnStreamSize; + if( bRet ) + mrStrm >> mnRawRecId >> mnRawRecSize; + return bRet; +} + +void XclImpStream::SetupDecrypter() +{ + if( mxDecrypter.is() ) + mxDecrypter->Update( mrStrm, mnRawRecSize ); +} + +void XclImpStream::SetupRawRecord() +{ + // pre: mnRawRecSize contains current raw record size + // pre: mrStrm points to start of raw record data + mnNextRecPos = mrStrm.Tell() + mnRawRecSize; + mnRawRecLeft = mnRawRecSize; + mnCurrRecSize += mnRawRecSize; + SetupDecrypter(); // decrypter works on raw record level +} + +void XclImpStream::SetupRecord() +{ + mnRecId = mnRawRecId; + mnAltContId = EXC_ID_UNKNOWN; + mnCurrRecSize = 0; + mnComplRecSize = mnRawRecSize; + mbHasComplRec = !mbCont; + SetupRawRecord(); + SetNulSubstChar(); + EnableDecryption(); + StorePosition( maFirstRec ); +} + +bool XclImpStream::IsContinueId( sal_uInt16 nRecId ) const +{ + return (nRecId == EXC_ID_CONT) || (nRecId == mnAltContId); +} + +bool XclImpStream::JumpToNextContinue() +{ + mbValid = mbValid && mbCont && ReadNextRawRecHeader() && IsContinueId( mnRawRecId ); + if( mbValid ) // do not setup a following non-CONTINUE record + SetupRawRecord(); + return mbValid; +} + +bool XclImpStream::JumpToNextStringContinue( bool& rb16Bit ) +{ + DBG_ASSERT( mnRawRecLeft == 0, "XclImpStream::JumpToNextStringContinue - unexpected garbage" ); + + if( mbCont && (GetRecLeft() > 0) ) + { + JumpToNextContinue(); + } + else if( mnRecId == EXC_ID_CONT ) + { + // CONTINUE handling is off, but we have started reading in a CONTINUE record + // -> start next CONTINUE for TXO import + mbValidRec = ReadNextRawRecHeader() && ((mnRawRecId != 0) || (mnRawRecSize > 0)); + mbValid = mbValidRec && (mnRawRecId == EXC_ID_CONT); + // we really start a new record here - no chance to return to string origin + if( mbValid ) + SetupRecord(); + } + else + mbValid = false; + + if( mbValid ) + rb16Bit = ::get_flag( ReaduInt8(), EXC_STRF_16BIT ); + return mbValid; +} + +bool XclImpStream::EnsureRawReadSize( sal_uInt16 nBytes ) +{ + if( mbValid && nBytes ) + { + while( mbValid && !mnRawRecLeft ) JumpToNextContinue(); + mbValid = mbValid && (nBytes <= mnRawRecLeft); + DBG_ASSERT( mbValid, "XclImpStream::EnsureRawReadSize - record overread" ); + } + return mbValid; +} + +sal_uInt16 XclImpStream::GetMaxRawReadSize( sal_Size nBytes ) const +{ + return static_cast< sal_uInt16 >( ::std::min< sal_Size >( nBytes, mnRawRecLeft ) ); +} + +sal_uInt16 XclImpStream::ReadRawData( void* pData, sal_uInt16 nBytes ) +{ + DBG_ASSERT( (nBytes <= mnRawRecLeft), "XclImpStream::ReadRawData - record overread" ); + sal_uInt16 nRet = 0; + if( mbUseDecr ) + nRet = mxDecrypter->Read( mrStrm, pData, nBytes ); + else + nRet = static_cast< sal_uInt16 >( mrStrm.Read( pData, nBytes ) ); + mnRawRecLeft = mnRawRecLeft - nRet; + return nRet; +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xistring.cxx b/sc/source/filter/excel/xistring.cxx new file mode 100644 index 000000000000..cb2645bec538 --- /dev/null +++ b/sc/source/filter/excel/xistring.cxx @@ -0,0 +1,213 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_sc.hxx" +#include "xistring.hxx" +#include "xlstyle.hxx" +#include "xistream.hxx" +#include "xiroot.hxx" + +// Byte/Unicode strings ======================================================= + +/** All allowed flags for import. */ +const XclStrFlags nAllowedFlags = EXC_STR_8BITLENGTH | EXC_STR_SMARTFLAGS | EXC_STR_SEPARATEFORMATS; + +// ---------------------------------------------------------------------------- + +XclImpString::XclImpString() +{ +} + +XclImpString::XclImpString( const String& rString ) : + maString( rString ) +{ +} + +XclImpString::~XclImpString() +{ +} + +void XclImpString::Read( XclImpStream& rStrm, XclStrFlags nFlags ) +{ + if( !::get_flag( nFlags, EXC_STR_SEPARATEFORMATS ) ) + maFormats.clear(); + + DBG_ASSERT( (nFlags & ~nAllowedFlags) == 0, "XclImpString::Read - unknown flag" ); + bool b16BitLen = !::get_flag( nFlags, EXC_STR_8BITLENGTH ); + + switch( rStrm.GetRoot().GetBiff() ) + { + case EXC_BIFF2: + case EXC_BIFF3: + case EXC_BIFF4: + case EXC_BIFF5: + // no integrated formatting in BIFF2-BIFF7 + maString = rStrm.ReadByteString( b16BitLen ); + break; + + case EXC_BIFF8: + { + // --- string header --- + sal_uInt16 nChars = b16BitLen ? rStrm.ReaduInt16() : rStrm.ReaduInt8(); + sal_uInt8 nFlagField = 0; + if( nChars || !::get_flag( nFlags, EXC_STR_SMARTFLAGS ) ) + rStrm >> nFlagField; + + bool b16Bit, bRich, bFarEast; + sal_uInt16 nRunCount; + sal_uInt32 nExtInf; + rStrm.ReadUniStringExtHeader( b16Bit, bRich, bFarEast, nRunCount, nExtInf, nFlagField ); + // #122185# ignore the flags, they may be wrong + + // --- character array --- + maString = rStrm.ReadRawUniString( nChars, b16Bit ); + + // --- formatting --- + if( nRunCount > 0 ) + ReadFormats( rStrm, nRunCount ); + + // --- extended (FarEast) information --- + rStrm.Ignore( nExtInf ); + } + break; + + default: + DBG_ERROR_BIFF(); + } +} + +void XclImpString::AppendFormat( XclFormatRunVec& rFormats, sal_uInt16 nChar, sal_uInt16 nFontIdx ) +{ + // #i33341# real life -- same character index may occur several times + DBG_ASSERT( rFormats.empty() || (rFormats.back().mnChar <= nChar), "XclImpString::AppendFormat - wrong char order" ); + if( rFormats.empty() || (rFormats.back().mnChar < nChar) ) + rFormats.push_back( XclFormatRun( nChar, nFontIdx ) ); + else + rFormats.back().mnFontIdx = nFontIdx; +} + +void XclImpString::ReadFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats ) +{ + bool bBiff8 = rStrm.GetRoot().GetBiff() == EXC_BIFF8; + sal_uInt16 nRunCount = bBiff8 ? rStrm.ReaduInt16() : rStrm.ReaduInt8(); + ReadFormats( rStrm, rFormats, nRunCount ); +} + +void XclImpString::ReadFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats, sal_uInt16 nRunCount ) +{ + rFormats.clear(); + rFormats.reserve( nRunCount ); + /* #i33341# real life -- same character index may occur several times + -> use AppendFormat() to validate formats */ + if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ) + { + for( sal_uInt16 nIdx = 0; nIdx < nRunCount; ++nIdx ) + { + sal_uInt16 nChar, nFontIdx; + rStrm >> nChar >> nFontIdx; + AppendFormat( rFormats, nChar, nFontIdx ); + } + } + else + { + for( sal_uInt16 nIdx = 0; nIdx < nRunCount; ++nIdx ) + { + sal_uInt8 nChar, nFontIdx; + rStrm >> nChar >> nFontIdx; + AppendFormat( rFormats, nChar, nFontIdx ); + } + } +} + +void XclImpString::ReadObjFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats, sal_uInt16 nFormatSize ) +{ + // number of formatting runs, each takes 8 bytes + sal_uInt16 nRunCount = nFormatSize / 8; + rFormats.clear(); + rFormats.reserve( nRunCount ); + for( sal_uInt16 nIdx = 0; nIdx < nRunCount; ++nIdx ) + { + sal_uInt16 nChar, nFontIdx; + rStrm >> nChar >> nFontIdx; + rStrm.Ignore( 4 ); + AppendFormat( rFormats, nChar, nFontIdx ); + } +} + +// String iterator ============================================================ + +XclImpStringIterator::XclImpStringIterator( const XclImpString& rString ) : + mrText( rString.GetText() ), + mrFormats( rString.GetFormats() ), + mnPortion( 0 ), + mnTextBeg( 0 ), + mnTextEnd( 0 ), + mnFormatsBeg( 0 ), + mnFormatsEnd( 0 ) +{ + // first portion is formatted, adjust vector index to next portion + if( !mrFormats.empty() && (mrFormats.front().mnChar == 0) ) + ++mnFormatsEnd; + // find end position of the first portion + mnTextEnd = static_cast< xub_StrLen >( (mnFormatsEnd < mrFormats.size()) ? + mrFormats[ mnFormatsEnd ].mnChar : mrText.Len() ); +} + +String XclImpStringIterator::GetPortionText() const +{ + return String( mrText, mnTextBeg, mnTextEnd - mnTextBeg ); +} + +sal_uInt16 XclImpStringIterator::GetPortionFont() const +{ + return (mnFormatsBeg < mnFormatsEnd) ? mrFormats[ mnFormatsBeg ].mnFontIdx : EXC_FONT_NOTFOUND; +} + +XclImpStringIterator& XclImpStringIterator::operator++() +{ + if( Is() ) + { + ++mnPortion; + do + { + // indexes into vector of formatting runs + if( mnFormatsBeg < mnFormatsEnd ) + ++mnFormatsBeg; + if( mnFormatsEnd < mrFormats.size() ) + ++mnFormatsEnd; + // character positions of next portion + mnTextBeg = mnTextEnd; + mnTextEnd = static_cast< xub_StrLen >( (mnFormatsEnd < mrFormats.size()) ? + mrFormats[ mnFormatsEnd ].mnChar : mrText.Len() ); + } + while( Is() && (mnTextBeg == mnTextEnd) ); + } + return *this; +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xistyle.cxx b/sc/source/filter/excel/xistyle.cxx new file mode 100644 index 000000000000..1559ef5530f5 --- /dev/null +++ b/sc/source/filter/excel/xistyle.cxx @@ -0,0 +1,1823 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xistyle.hxx" +#include <sfx2/printer.hxx> +#include <sfx2/objsh.hxx> +#include <svtools/ctrltool.hxx> +#include <editeng/editobj.hxx> +#include "scitems.hxx" +#include <editeng/fontitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/crsditem.hxx> +#include <editeng/cntritem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/escpitem.hxx> +#include <svx/algitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/bolnitem.hxx> +#include <svx/rotmodit.hxx> +#include <editeng/colritem.hxx> +#include <editeng/brshitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/flstitem.hxx> +#include "document.hxx" +#include "docpool.hxx" +#include "attrib.hxx" +#include "stlpool.hxx" +#include "stlsheet.hxx" +#include "cell.hxx" +#include "globstr.hrc" +#include "xltracer.hxx" +#include "xistream.hxx" +#include "xicontent.hxx" + +#include "root.hxx" +#include "colrowst.hxx" + +// PALETTE record - color information ========================================= + +XclImpPalette::XclImpPalette( const XclImpRoot& rRoot ) : + XclDefaultPalette( rRoot ) +{ +} + +void XclImpPalette::Initialize() +{ + maColorTable.clear(); +} + +ColorData XclImpPalette::GetColorData( sal_uInt16 nXclIndex ) const +{ + if( nXclIndex >= EXC_COLOR_USEROFFSET ) + { + sal_uInt32 nIx = nXclIndex - EXC_COLOR_USEROFFSET; + if( nIx < maColorTable.size() ) + return maColorTable[ nIx ]; + } + return GetDefColorData( nXclIndex ); +} + +void XclImpPalette::ReadPalette( XclImpStream& rStrm ) +{ + sal_uInt16 nCount; + rStrm >> nCount; + DBG_ASSERT( rStrm.GetRecLeft() == static_cast< sal_Size >( 4 * nCount ), + "XclImpPalette::ReadPalette - size mismatch" ); + + maColorTable.resize( nCount ); + Color aColor; + for( sal_uInt16 nIndex = 0; nIndex < nCount; ++nIndex ) + { + rStrm >> aColor; + maColorTable[ nIndex ] = aColor.GetColor(); + } +} + +// FONT record - font information ============================================= + +XclImpFont::XclImpFont( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + mbHasCharSet( false ), + mbHasWstrn( true ), + mbHasAsian( false ), + mbHasCmplx( false ) +{ + SetAllUsedFlags( false ); +} + +XclImpFont::XclImpFont( const XclImpRoot& rRoot, const XclFontData& rFontData ) : + XclImpRoot( rRoot ) +{ + SetFontData( rFontData, false ); +} + +void XclImpFont::SetAllUsedFlags( bool bUsed ) +{ + mbFontNameUsed = mbHeightUsed = mbColorUsed = mbWeightUsed = mbEscapemUsed = + mbUnderlUsed = mbItalicUsed = mbStrikeUsed = mbOutlineUsed = mbShadowUsed = bUsed; +} + +void XclImpFont::SetFontData( const XclFontData& rFontData, bool bHasCharSet ) +{ + maData = rFontData; + mbHasCharSet = bHasCharSet; + if( maData.maStyle.Len() ) + { + if( SfxObjectShell* pDocShell = GetDocShell() ) + { + if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >( + pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) ) + { + if( const FontList* pFontList = pInfoItem->GetFontList() ) + { + FontInfo aFontInfo( pFontList->Get( maData.maName, maData.maStyle ) ); + maData.SetScWeight( aFontInfo.GetWeight() ); + maData.SetScPosture( aFontInfo.GetItalic() ); + } + } + } + maData.maStyle.Erase(); + } + GuessScriptType(); + SetAllUsedFlags( true ); +} + +rtl_TextEncoding XclImpFont::GetFontEncoding() const +{ + // #i63105# use text encoding from FONT record + // #i67768# BIFF2-BIFF4 FONT records do not contain character set + rtl_TextEncoding eFontEnc = mbHasCharSet ? maData.GetFontEncoding() : GetTextEncoding(); + return (eFontEnc == RTL_TEXTENCODING_DONTKNOW) ? GetTextEncoding() : eFontEnc; +} + +void XclImpFont::ReadFont( XclImpStream& rStrm ) +{ + switch( GetBiff() ) + { + case EXC_BIFF2: + ReadFontData2( rStrm ); + ReadFontName2( rStrm ); + break; + case EXC_BIFF3: + case EXC_BIFF4: + ReadFontData2( rStrm ); + ReadFontColor( rStrm ); + ReadFontName2( rStrm ); + break; + case EXC_BIFF5: + ReadFontData5( rStrm ); + ReadFontName2( rStrm ); + break; + case EXC_BIFF8: + ReadFontData5( rStrm ); + ReadFontName8( rStrm ); + break; + default: + DBG_ERROR_BIFF(); + return; + } + GuessScriptType(); + SetAllUsedFlags( true ); +} + +void XclImpFont::ReadEfont( XclImpStream& rStrm ) +{ + ReadFontColor( rStrm ); +} + +void XclImpFont::ReadCFFontBlock( XclImpStream& rStrm ) +{ + DBG_ASSERT_BIFF( GetBiff() == EXC_BIFF8 ); + if( GetBiff() != EXC_BIFF8 ) + return; + + sal_uInt32 nHeight, nStyle, nColor, nFontFlags1, nFontFlags2, nFontFlags3; + sal_uInt16 nWeight, nEscapem; + sal_uInt8 nUnderl; + + rStrm.Ignore( 64 ); + rStrm >> nHeight >> nStyle >> nWeight >> nEscapem >> nUnderl; + rStrm.Ignore( 3 ); + rStrm >> nColor; + rStrm.Ignore( 4 ); + rStrm >> nFontFlags1 >> nFontFlags2 >> nFontFlags3; + rStrm.Ignore( 18 ); + + if( (mbHeightUsed = (nHeight <= 0x7FFF)) == true ) + maData.mnHeight = static_cast< sal_uInt16 >( nHeight ); + if( (mbWeightUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STYLE ) && (nWeight < 0x7FFF)) == true ) + maData.mnWeight = static_cast< sal_uInt16 >( nWeight ); + if( (mbItalicUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STYLE )) == true ) + maData.mbItalic = ::get_flag( nStyle, EXC_CF_FONT_STYLE ); + if( (mbUnderlUsed = !::get_flag( nFontFlags3, EXC_CF_FONT_UNDERL ) && (nUnderl <= 0x7F)) == true ) + maData.mnUnderline = nUnderl; + if( (mbColorUsed = (nColor <= 0x7FFF)) == true ) + maData.maColor = GetPalette().GetColor( static_cast< sal_uInt16 >( nColor ) ); + if( (mbStrikeUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT )) == true ) + maData.mbStrikeout = ::get_flag( nStyle, EXC_CF_FONT_STRIKEOUT ); +} + +void XclImpFont::FillToItemSet( SfxItemSet& rItemSet, XclFontItemType eType, bool bSkipPoolDefs ) const +{ + // true = edit engine Which-IDs (EE_CHAR_*); false = Calc Which-IDs (ATTR_*) + bool bEE = eType != EXC_FONTITEM_CELL; + +// item = the item to put into the item set +// sc_which = the Calc Which-ID of the item +// ee_which = the edit engine Which-ID of the item +#define PUTITEM( item, sc_which, ee_which ) \ + ScfTools::PutItem( rItemSet, item, (bEE ? (ee_which) : (sc_which)), bSkipPoolDefs ) + +// Font item + // #i36997# do not set default Tahoma font from notes + bool bDefNoteFont = (eType == EXC_FONTITEM_NOTE) && (maData.maName.EqualsIgnoreCaseAscii( "Tahoma" )); + if( mbFontNameUsed && !bDefNoteFont ) + { + rtl_TextEncoding eFontEnc = maData.GetFontEncoding(); + rtl_TextEncoding eTempTextEnc = (bEE && (eFontEnc == GetTextEncoding())) ? + ScfTools::GetSystemTextEncoding() : eFontEnc; + + SvxFontItem aFontItem( maData.GetScFamily( GetTextEncoding() ), maData.maName, EMPTY_STRING, + PITCH_DONTKNOW, eTempTextEnc, ATTR_FONT ); + // #91658# set only for valid script types + if( mbHasWstrn ) + PUTITEM( aFontItem, ATTR_FONT, EE_CHAR_FONTINFO ); + if( mbHasAsian ) + PUTITEM( aFontItem, ATTR_CJK_FONT, EE_CHAR_FONTINFO_CJK ); + if( mbHasCmplx ) + PUTITEM( aFontItem, ATTR_CTL_FONT, EE_CHAR_FONTINFO_CTL ); + } + +// Font height (for all script types) + if( mbHeightUsed ) + { + sal_Int32 nHeight = maData.mnHeight; + if( bEE && (eType != EXC_FONTITEM_HF) ) // do not convert header/footer height + nHeight = (nHeight * 127 + 36) / EXC_POINTS_PER_INCH; // #98527# 1 in == 72 pt + + SvxFontHeightItem aHeightItem( nHeight, 100, ATTR_FONT_HEIGHT ); + PUTITEM( aHeightItem, ATTR_FONT_HEIGHT, EE_CHAR_FONTHEIGHT ); + PUTITEM( aHeightItem, ATTR_CJK_FONT_HEIGHT, EE_CHAR_FONTHEIGHT_CJK ); + PUTITEM( aHeightItem, ATTR_CTL_FONT_HEIGHT, EE_CHAR_FONTHEIGHT_CTL ); + } + +// Font color - pass AUTO_COL to item + if( mbColorUsed ) + PUTITEM( SvxColorItem( maData.maColor, ATTR_FONT_COLOR ), ATTR_FONT_COLOR, EE_CHAR_COLOR ); + +// Font weight (for all script types) + if( mbWeightUsed ) + { + SvxWeightItem aWeightItem( maData.GetScWeight(), ATTR_FONT_WEIGHT ); + PUTITEM( aWeightItem, ATTR_FONT_WEIGHT, EE_CHAR_WEIGHT ); + PUTITEM( aWeightItem, ATTR_CJK_FONT_WEIGHT, EE_CHAR_WEIGHT_CJK ); + PUTITEM( aWeightItem, ATTR_CTL_FONT_WEIGHT, EE_CHAR_WEIGHT_CTL ); + } + +// Font underline + if( mbUnderlUsed ) + { + SvxUnderlineItem aUnderlItem( maData.GetScUnderline(), ATTR_FONT_UNDERLINE ); + PUTITEM( aUnderlItem, ATTR_FONT_UNDERLINE, EE_CHAR_UNDERLINE ); + } + +// Font posture (for all script types) + if( mbItalicUsed ) + { + SvxPostureItem aPostItem( maData.GetScPosture(), ATTR_FONT_POSTURE ); + PUTITEM( aPostItem, ATTR_FONT_POSTURE, EE_CHAR_ITALIC ); + PUTITEM( aPostItem, ATTR_CJK_FONT_POSTURE, EE_CHAR_ITALIC_CJK ); + PUTITEM( aPostItem, ATTR_CTL_FONT_POSTURE, EE_CHAR_ITALIC_CTL ); + } + +// Boolean attributes crossed out, contoured, shadowed + if( mbStrikeUsed ) + PUTITEM( SvxCrossedOutItem( maData.GetScStrikeout(), ATTR_FONT_CROSSEDOUT ), ATTR_FONT_CROSSEDOUT, EE_CHAR_STRIKEOUT ); + if( mbOutlineUsed ) + PUTITEM( SvxContourItem( maData.mbOutline, ATTR_FONT_CONTOUR ), ATTR_FONT_CONTOUR, EE_CHAR_OUTLINE ); + if( mbShadowUsed ) + PUTITEM( SvxShadowedItem( maData.mbShadow, ATTR_FONT_SHADOWED ), ATTR_FONT_SHADOWED, EE_CHAR_SHADOW ); + +// Super-/subscript: only on edit engine objects + if( mbEscapemUsed && bEE ) + rItemSet.Put( SvxEscapementItem( maData.GetScEscapement(), EE_CHAR_ESCAPEMENT ) ); + +#undef PUTITEM +} + +void XclImpFont::WriteFontProperties( ScfPropertySet& rPropSet, + XclFontPropSetType eType, const Color* pFontColor ) const +{ + GetFontPropSetHelper().WriteFontProperties( + rPropSet, eType, maData, mbHasWstrn, mbHasAsian, mbHasCmplx, pFontColor ); +} + +void XclImpFont::ReadFontData2( XclImpStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm >> maData.mnHeight >> nFlags; + + maData.mnWeight = ::get_flagvalue( nFlags, EXC_FONTATTR_BOLD, EXC_FONTWGHT_BOLD, EXC_FONTWGHT_NORMAL ); + maData.mnUnderline = ::get_flagvalue( nFlags, EXC_FONTATTR_UNDERLINE, EXC_FONTUNDERL_SINGLE, EXC_FONTUNDERL_NONE ); + maData.mbItalic = ::get_flag( nFlags, EXC_FONTATTR_ITALIC ); + maData.mbStrikeout = ::get_flag( nFlags, EXC_FONTATTR_STRIKEOUT ); + maData.mbOutline = ::get_flag( nFlags, EXC_FONTATTR_OUTLINE ); + maData.mbShadow = ::get_flag( nFlags, EXC_FONTATTR_SHADOW ); + mbHasCharSet = false; +} + +void XclImpFont::ReadFontData5( XclImpStream& rStrm ) +{ + sal_uInt16 nFlags; + + rStrm >> maData.mnHeight >> nFlags; + ReadFontColor( rStrm ); + rStrm >> maData.mnWeight >> maData.mnEscapem >> maData.mnUnderline >> maData.mnFamily >> maData.mnCharSet; + rStrm.Ignore( 1 ); + + maData.mbItalic = ::get_flag( nFlags, EXC_FONTATTR_ITALIC ); + maData.mbStrikeout = ::get_flag( nFlags, EXC_FONTATTR_STRIKEOUT ); + maData.mbOutline = ::get_flag( nFlags, EXC_FONTATTR_OUTLINE ); + maData.mbShadow = ::get_flag( nFlags, EXC_FONTATTR_SHADOW ); + mbHasCharSet = true; +} + +void XclImpFont::ReadFontColor( XclImpStream& rStrm ) +{ + maData.maColor = GetPalette().GetColor( rStrm.ReaduInt16() ); +} + +void XclImpFont::ReadFontName2( XclImpStream& rStrm ) +{ + maData.maName = rStrm.ReadByteString( false ); +} + +void XclImpFont::ReadFontName8( XclImpStream& rStrm ) +{ + maData.maName = rStrm.ReadUniString( rStrm.ReaduInt8() ); +} + +void XclImpFont::GuessScriptType() +{ + mbHasWstrn = true; + mbHasAsian = mbHasCmplx = false; + + // #91658# #113783# find the script types for which the font contains characters + if( OutputDevice* pPrinter = GetPrinter() ) + { + Font aFont( maData.maName, Size( 0, 10 ) ); + FontCharMap aCharMap; + + pPrinter->SetFont( aFont ); + if( pPrinter->GetFontCharMap( aCharMap ) ) + { + // #91658# CJK fonts + mbHasAsian = + aCharMap.HasChar( 0x3041 ) || // 3040-309F: Hiragana + aCharMap.HasChar( 0x30A1 ) || // 30A0-30FF: Katakana + aCharMap.HasChar( 0x3111 ) || // 3100-312F: Bopomofo + aCharMap.HasChar( 0x3131 ) || // 3130-318F: Hangul Compatibility Jamo + aCharMap.HasChar( 0x3301 ) || // 3300-33FF: CJK Compatibility + aCharMap.HasChar( 0x3401 ) || // 3400-4DBF: CJK Unified Ideographs Extension A + aCharMap.HasChar( 0x4E01 ) || // 4E00-9FAF: CJK Unified Ideographs + aCharMap.HasChar( 0x7E01 ) || // 4E00-9FAF: CJK unified ideographs + aCharMap.HasChar( 0xA001 ) || // A001-A48F: Yi Syllables + aCharMap.HasChar( 0xAC01 ) || // AC00-D7AF: Hangul Syllables + aCharMap.HasChar( 0xCC01 ) || // AC00-D7AF: Hangul Syllables + aCharMap.HasChar( 0xF901 ) || // F900-FAFF: CJK Compatibility Ideographs + aCharMap.HasChar( 0xFF71 ); // FF00-FFEF: Halfwidth/Fullwidth Forms + // #113783# CTL fonts + mbHasCmplx = + aCharMap.HasChar( 0x05D1 ) || // 0590-05FF: Hebrew + aCharMap.HasChar( 0x0631 ) || // 0600-06FF: Arabic + aCharMap.HasChar( 0x0721 ) || // 0700-074F: Syriac + aCharMap.HasChar( 0x0911 ) || // 0900-0DFF: Indic scripts + aCharMap.HasChar( 0x0E01 ) || // 0E00-0E7F: Thai + aCharMap.HasChar( 0xFB21 ) || // FB1D-FB4F: Hebrew Presentation Forms + aCharMap.HasChar( 0xFB51 ) || // FB50-FDFF: Arabic Presentation Forms-A + aCharMap.HasChar( 0xFE71 ); // FE70-FEFF: Arabic Presentation Forms-B + // Western fonts + mbHasWstrn = (!mbHasAsian && !mbHasCmplx) || aCharMap.HasChar( 'A' ); + } + } +} + +// ---------------------------------------------------------------------------- + +XclImpFontBuffer::XclImpFontBuffer( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + maFont4( rRoot ), + maCtrlFont( rRoot ) +{ + Initialize(); + + // default font for form controls without own font information + XclFontData aCtrlFontData; + switch( GetBiff() ) + { + case EXC_BIFF2: + case EXC_BIFF3: + case EXC_BIFF4: + case EXC_BIFF5: + aCtrlFontData.maName.AssignAscii( "Helv" ); + aCtrlFontData.mnHeight = 160; + aCtrlFontData.mnWeight = EXC_FONTWGHT_BOLD; + break; + case EXC_BIFF8: + aCtrlFontData.maName.AssignAscii( "Tahoma" ); + aCtrlFontData.mnHeight = 160; + aCtrlFontData.mnWeight = EXC_FONTWGHT_NORMAL; + break; + default: + DBG_ERROR_BIFF(); + } + maCtrlFont.SetFontData( aCtrlFontData, false ); +} + +void XclImpFontBuffer::Initialize() +{ + maFontList.Clear(); + + // application font for column width calculation, later filled with first font from font list + XclFontData aAppFontData; + aAppFontData.maName.AssignAscii( "Arial" ); + aAppFontData.mnHeight = 200; + aAppFontData.mnWeight = EXC_FONTWGHT_NORMAL; + UpdateAppFont( aAppFontData, false ); +} + +const XclImpFont* XclImpFontBuffer::GetFont( sal_uInt16 nFontIndex ) const +{ + /* Font with index 4 is not stored in an Excel file, but used e.g. by + BIFF5 form pushbutton objects. It is the bold default font. */ + return (nFontIndex == 4) ? &maFont4 : + maFontList.GetObject( (nFontIndex < 4) ? nFontIndex : (nFontIndex - 1) ); +} + +void XclImpFontBuffer::ReadFont( XclImpStream& rStrm ) +{ + XclImpFont* pFont = new XclImpFont( GetRoot() ); + pFont->ReadFont( rStrm ); + maFontList.Append( pFont ); + + if( maFontList.Count() == 1 ) + { + UpdateAppFont( pFont->GetFontData(), pFont->HasCharSet() ); + // #i71033# set text encoding from application font, if CODEPAGE is missing + SetAppFontEncoding( pFont->GetFontEncoding() ); + } +} + +void XclImpFontBuffer::ReadEfont( XclImpStream& rStrm ) +{ + if( XclImpFont* pFont = maFontList.Last() ) + pFont->ReadEfont( rStrm ); +} + +void XclImpFontBuffer::FillToItemSet( + SfxItemSet& rItemSet, XclFontItemType eType, + sal_uInt16 nFontIdx, bool bSkipPoolDefs ) const +{ + if( const XclImpFont* pFont = GetFont( nFontIdx ) ) + pFont->FillToItemSet( rItemSet, eType, bSkipPoolDefs ); +} + +void XclImpFontBuffer::WriteFontProperties( ScfPropertySet& rPropSet, + XclFontPropSetType eType, sal_uInt16 nFontIdx, const Color* pFontColor ) const +{ + if( const XclImpFont* pFont = GetFont( nFontIdx ) ) + pFont->WriteFontProperties( rPropSet, eType, pFontColor ); +} + +void XclImpFontBuffer::WriteDefaultCtrlFontProperties( ScfPropertySet& rPropSet ) const +{ + maCtrlFont.WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL ); +} + +void XclImpFontBuffer::UpdateAppFont( const XclFontData& rFontData, bool bHasCharSet ) +{ + maAppFont = rFontData; + // #i3006# Calculate the width of '0' from first font and current printer. + SetCharWidth( maAppFont ); + + // font 4 is bold font 0 + XclFontData aFont4Data( maAppFont ); + aFont4Data.mnWeight = EXC_FONTWGHT_BOLD; + maFont4.SetFontData( aFont4Data, bHasCharSet ); +} + +// FORMAT record - number formats ============================================= + +XclImpNumFmtBuffer::XclImpNumFmtBuffer( const XclImpRoot& rRoot ) : + XclNumFmtBuffer( rRoot ), + XclImpRoot( rRoot ), + mnNextXclIdx( 0 ) +{ +} + +void XclImpNumFmtBuffer::Initialize() +{ + maIndexMap.clear(); + mnNextXclIdx = 0; + InitializeImport(); // base class +} + +void XclImpNumFmtBuffer::ReadFormat( XclImpStream& rStrm ) +{ + String aFormat; + switch( GetBiff() ) + { + case EXC_BIFF2: + case EXC_BIFF3: + aFormat = rStrm.ReadByteString( false ); + break; + + case EXC_BIFF4: + rStrm.Ignore( 2 ); // in BIFF4 the index field exists, but is undefined + aFormat = rStrm.ReadByteString( false ); + break; + + case EXC_BIFF5: + rStrm >> mnNextXclIdx; + aFormat = rStrm.ReadByteString( false ); + break; + + case EXC_BIFF8: + rStrm >> mnNextXclIdx; + aFormat = rStrm.ReadUniString(); + break; + + default: + DBG_ERROR_BIFF(); + return; + } + + if( mnNextXclIdx < 0xFFFF ) + { + InsertFormat( mnNextXclIdx, aFormat ); + ++mnNextXclIdx; + } +} + +void XclImpNumFmtBuffer::CreateScFormats() +{ + DBG_ASSERT( maIndexMap.empty(), "XclImpNumFmtBuffer::CreateScFormats - already created" ); + + SvNumberFormatter& rFormatter = GetFormatter(); + for( XclNumFmtMap::const_iterator aIt = GetFormatMap().begin(), aEnd = GetFormatMap().end(); aIt != aEnd; ++aIt ) + { + const XclNumFmt& rNumFmt = aIt->second; + + // insert/convert the Excel number format + xub_StrLen nCheckPos; + short nType = NUMBERFORMAT_DEFINED; + sal_uInt32 nKey; + if( rNumFmt.maFormat.Len() ) + { + String aFormat( rNumFmt.maFormat ); + rFormatter.PutandConvertEntry( aFormat, nCheckPos, + nType, nKey, LANGUAGE_ENGLISH_US, rNumFmt.meLanguage ); + } + else + nKey = rFormatter.GetFormatIndex( rNumFmt.meOffset, rNumFmt.meLanguage ); + + // insert the resulting format key into the Excel->Calc index map + maIndexMap[ aIt->first ] = nKey; + } +} + +ULONG XclImpNumFmtBuffer::GetScFormat( sal_uInt16 nXclNumFmt ) const +{ + XclImpIndexMap::const_iterator aIt = maIndexMap.find( nXclNumFmt ); + return (aIt != maIndexMap.end()) ? aIt->second : NUMBERFORMAT_ENTRY_NOT_FOUND; +} + +void XclImpNumFmtBuffer::FillToItemSet( SfxItemSet& rItemSet, sal_uInt16 nXclNumFmt, bool bSkipPoolDefs ) const +{ + ULONG nScNumFmt = GetScFormat( nXclNumFmt ); + if( nScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND ) + nScNumFmt = GetStdScNumFmt(); + FillScFmtToItemSet( rItemSet, nScNumFmt, bSkipPoolDefs ); +} + +void XclImpNumFmtBuffer::FillScFmtToItemSet( SfxItemSet& rItemSet, ULONG nScNumFmt, bool bSkipPoolDefs ) const +{ + DBG_ASSERT( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND, "XclImpNumFmtBuffer::FillScFmtToItemSet - invalid number format" ); + ScfTools::PutItem( rItemSet, SfxUInt32Item( ATTR_VALUE_FORMAT, nScNumFmt ), bSkipPoolDefs ); + if( rItemSet.GetItemState( ATTR_VALUE_FORMAT, FALSE ) == SFX_ITEM_SET ) + ScGlobal::AddLanguage( rItemSet, GetFormatter() ); +} + +// XF, STYLE record - Cell formatting ========================================= + +void XclImpCellProt::FillFromXF2( sal_uInt8 nNumFmt ) +{ + mbLocked = ::get_flag( nNumFmt, EXC_XF2_LOCKED ); + mbHidden = ::get_flag( nNumFmt, EXC_XF2_HIDDEN ); +} + +void XclImpCellProt::FillFromXF3( sal_uInt16 nProt ) +{ + mbLocked = ::get_flag( nProt, EXC_XF_LOCKED ); + mbHidden = ::get_flag( nProt, EXC_XF_HIDDEN ); +} + +void XclImpCellProt::FillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const +{ + ScfTools::PutItem( rItemSet, ScProtectionAttr( mbLocked, mbHidden ), bSkipPoolDefs ); +} + + +// ---------------------------------------------------------------------------- + +void XclImpCellAlign::FillFromXF2( sal_uInt8 nFlags ) +{ + mnHorAlign = ::extract_value< sal_uInt8 >( nFlags, 0, 3 ); +} + +void XclImpCellAlign::FillFromXF3( sal_uInt16 nAlign ) +{ + mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 ); + mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK ); // new in BIFF3 +} + +void XclImpCellAlign::FillFromXF4( sal_uInt16 nAlign ) +{ + FillFromXF3( nAlign ); + mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 2 ); // new in BIFF4 + mnOrient = ::extract_value< sal_uInt8 >( nAlign, 6, 2 ); // new in BIFF4 +} + +void XclImpCellAlign::FillFromXF5( sal_uInt16 nAlign ) +{ + mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 ); + mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 ); + mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK ); + mnOrient = ::extract_value< sal_uInt8 >( nAlign, 8, 2 ); +} + +void XclImpCellAlign::FillFromXF8( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib ) +{ + mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 ); + mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 ); + mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK ); + mnRotation = ::extract_value< sal_uInt8 >( nAlign, 8, 8 ); // new in BIFF8 + mnIndent = ::extract_value< sal_uInt8 >( nMiscAttrib, 0, 4 ); // new in BIFF8 + mbShrink = ::get_flag( nMiscAttrib, EXC_XF8_SHRINK ); // new in BIFF8 + mnTextDir = ::extract_value< sal_uInt8 >( nMiscAttrib, 6, 2 ); // new in BIFF8 +} + +void XclImpCellAlign::FillToItemSet( SfxItemSet& rItemSet, const XclImpFont* pFont, bool bSkipPoolDefs ) const +{ + // horizontal alignment + ScfTools::PutItem( rItemSet, SvxHorJustifyItem( GetScHorAlign(), ATTR_HOR_JUSTIFY ), bSkipPoolDefs ); + + // text wrap (#i74508# always if vertical alignment is justified or distributed) + bool bLineBreak = mbLineBreak || (mnVerAlign == EXC_XF_VER_JUSTIFY) || (mnVerAlign == EXC_XF_VER_DISTRIB); + ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_LINEBREAK, bLineBreak ), bSkipPoolDefs ); + + // vertical alignment + ScfTools::PutItem( rItemSet, SvxVerJustifyItem( GetScVerAlign(), ATTR_VER_JUSTIFY ), bSkipPoolDefs ); + + // indent + sal_uInt16 nScIndent = mnIndent * 200; // 1 Excel unit == 10 pt == 200 twips + ScfTools::PutItem( rItemSet, SfxUInt16Item( ATTR_INDENT, nScIndent ), bSkipPoolDefs ); + + // shrink to fit + ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_SHRINKTOFIT, mbShrink ), bSkipPoolDefs ); + + // text orientation/rotation (BIFF2-BIFF7 sets mnOrient) + sal_uInt8 nXclRot = (mnOrient == EXC_ORIENT_NONE) ? mnRotation : XclTools::GetXclRotFromOrient( mnOrient ); + bool bStacked = (nXclRot == EXC_ROT_STACKED); + ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_STACKED, bStacked ), bSkipPoolDefs ); + // set an angle in the range from -90 to 90 degrees + sal_Int32 nAngle = XclTools::GetScRotation( nXclRot, 0 ); + ScfTools::PutItem( rItemSet, SfxInt32Item( ATTR_ROTATE_VALUE, nAngle ), bSkipPoolDefs ); + // #105933# set "Use asian vertical layout", if cell is stacked and font contains CKJ characters + bool bAsianVert = bStacked && pFont && pFont->HasAsianChars(); + ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_VERTICAL_ASIAN, bAsianVert ), bSkipPoolDefs ); + + // CTL text direction + ScfTools::PutItem( rItemSet, SvxFrameDirectionItem( GetScFrameDir(), ATTR_WRITINGDIR ), bSkipPoolDefs ); +} + +// ---------------------------------------------------------------------------- + +XclImpCellBorder::XclImpCellBorder() +{ + SetUsedFlags( false, false ); +} + +void XclImpCellBorder::SetUsedFlags( bool bOuterUsed, bool bDiagUsed ) +{ + mbLeftUsed = mbRightUsed = mbTopUsed = mbBottomUsed = bOuterUsed; + mbDiagUsed = bDiagUsed; +} + +void XclImpCellBorder::FillFromXF2( sal_uInt8 nFlags ) +{ + mnLeftLine = ::get_flagvalue( nFlags, EXC_XF2_LEFTLINE, EXC_LINE_THIN, EXC_LINE_NONE ); + mnRightLine = ::get_flagvalue( nFlags, EXC_XF2_RIGHTLINE, EXC_LINE_THIN, EXC_LINE_NONE ); + mnTopLine = ::get_flagvalue( nFlags, EXC_XF2_TOPLINE, EXC_LINE_THIN, EXC_LINE_NONE ); + mnBottomLine = ::get_flagvalue( nFlags, EXC_XF2_BOTTOMLINE, EXC_LINE_THIN, EXC_LINE_NONE ); + mnLeftColor = mnRightColor = mnTopColor = mnBottomColor = EXC_COLOR_BIFF2_BLACK; + SetUsedFlags( true, false ); +} + +void XclImpCellBorder::FillFromXF3( sal_uInt32 nBorder ) +{ + mnTopLine = ::extract_value< sal_uInt8 >( nBorder, 0, 3 ); + mnLeftLine = ::extract_value< sal_uInt8 >( nBorder, 8, 3 ); + mnBottomLine = ::extract_value< sal_uInt8 >( nBorder, 16, 3 ); + mnRightLine = ::extract_value< sal_uInt8 >( nBorder, 24, 3 ); + mnTopColor = ::extract_value< sal_uInt16 >( nBorder, 3, 5 ); + mnLeftColor = ::extract_value< sal_uInt16 >( nBorder, 11, 5 ); + mnBottomColor = ::extract_value< sal_uInt16 >( nBorder, 19, 5 ); + mnRightColor = ::extract_value< sal_uInt16 >( nBorder, 27, 5 ); + SetUsedFlags( true, false ); +} + +void XclImpCellBorder::FillFromXF5( sal_uInt32 nBorder, sal_uInt32 nArea ) +{ + mnTopLine = ::extract_value< sal_uInt8 >( nBorder, 0, 3 ); + mnLeftLine = ::extract_value< sal_uInt8 >( nBorder, 3, 3 ); + mnBottomLine = ::extract_value< sal_uInt8 >( nArea, 22, 3 ); + mnRightLine = ::extract_value< sal_uInt8 >( nBorder, 6, 3 ); + mnTopColor = ::extract_value< sal_uInt16 >( nBorder, 9, 7 ); + mnLeftColor = ::extract_value< sal_uInt16 >( nBorder, 16, 7 ); + mnBottomColor = ::extract_value< sal_uInt16 >( nArea, 25, 7 ); + mnRightColor = ::extract_value< sal_uInt16 >( nBorder, 23, 7 ); + SetUsedFlags( true, false ); +} + +void XclImpCellBorder::FillFromXF8( sal_uInt32 nBorder1, sal_uInt32 nBorder2 ) +{ + mnLeftLine = ::extract_value< sal_uInt8 >( nBorder1, 0, 4 ); + mnRightLine = ::extract_value< sal_uInt8 >( nBorder1, 4, 4 ); + mnTopLine = ::extract_value< sal_uInt8 >( nBorder1, 8, 4 ); + mnBottomLine = ::extract_value< sal_uInt8 >( nBorder1, 12, 4 ); + mnLeftColor = ::extract_value< sal_uInt16 >( nBorder1, 16, 7 ); + mnRightColor = ::extract_value< sal_uInt16 >( nBorder1, 23, 7 ); + mnTopColor = ::extract_value< sal_uInt16 >( nBorder2, 0, 7 ); + mnBottomColor = ::extract_value< sal_uInt16 >( nBorder2, 7, 7 ); + mbDiagTLtoBR = ::get_flag( nBorder1, EXC_XF_DIAGONAL_TL_TO_BR ); + mbDiagBLtoTR = ::get_flag( nBorder1, EXC_XF_DIAGONAL_BL_TO_TR ); + if( mbDiagTLtoBR || mbDiagBLtoTR ) + { + mnDiagLine = ::extract_value< sal_uInt8 >( nBorder2, 21, 4 ); + mnDiagColor = ::extract_value< sal_uInt16 >( nBorder2, 14, 7 ); + } + SetUsedFlags( true, true ); +} + +void XclImpCellBorder::FillFromCF8( sal_uInt16 nLineStyle, sal_uInt32 nLineColor, sal_uInt32 nFlags ) +{ + mnLeftLine = ::extract_value< sal_uInt8 >( nLineStyle, 0, 4 ); + mnRightLine = ::extract_value< sal_uInt8 >( nLineStyle, 4, 4 ); + mnTopLine = ::extract_value< sal_uInt8 >( nLineStyle, 8, 4 ); + mnBottomLine = ::extract_value< sal_uInt8 >( nLineStyle, 12, 4 ); + mnLeftColor = ::extract_value< sal_uInt16 >( nLineColor, 0, 7 ); + mnRightColor = ::extract_value< sal_uInt16 >( nLineColor, 7, 7 ); + mnTopColor = ::extract_value< sal_uInt16 >( nLineColor, 16, 7 ); + mnBottomColor = ::extract_value< sal_uInt16 >( nLineColor, 23, 7 ); + mbLeftUsed = !::get_flag( nFlags, EXC_CF_BORDER_LEFT ); + mbRightUsed = !::get_flag( nFlags, EXC_CF_BORDER_RIGHT ); + mbTopUsed = !::get_flag( nFlags, EXC_CF_BORDER_TOP ); + mbBottomUsed = !::get_flag( nFlags, EXC_CF_BORDER_BOTTOM ); + mbDiagUsed = false; +} + +bool XclImpCellBorder::HasAnyOuterBorder() const +{ + return + (mbLeftUsed && (mnLeftLine != EXC_LINE_NONE)) || + (mbRightUsed && (mnRightLine != EXC_LINE_NONE)) || + (mbTopUsed && (mnTopLine != EXC_LINE_NONE)) || + (mbBottomUsed && (mnBottomLine != EXC_LINE_NONE)); +} + +namespace { + +/** Converts the passed line style to a SvxBorderLine, or returns false, if style is "no line". */ +bool lclConvertBorderLine( SvxBorderLine& rLine, const XclImpPalette& rPalette, sal_uInt8 nXclLine, sal_uInt16 nXclColor ) +{ + static const sal_uInt16 ppnLineParam[][ 3 ] = + { + // outer width, inner width, distance + { 0, 0, 0 }, // 0 = none + { DEF_LINE_WIDTH_1, 0, 0 }, // 1 = thin + { DEF_LINE_WIDTH_2, 0, 0 }, // 2 = medium + { DEF_LINE_WIDTH_1, 0, 0 }, // 3 = dashed + { DEF_LINE_WIDTH_0, 0, 0 }, // 4 = dotted + { DEF_LINE_WIDTH_3, 0, 0 }, // 5 = thick + { DEF_LINE_WIDTH_1, DEF_LINE_WIDTH_1, DEF_LINE_WIDTH_1 }, // 6 = double + { DEF_LINE_WIDTH_0, 0, 0 }, // 7 = hair + { DEF_LINE_WIDTH_2, 0, 0 }, // 8 = med dash + { DEF_LINE_WIDTH_1, 0, 0 }, // 9 = thin dashdot + { DEF_LINE_WIDTH_2, 0, 0 }, // A = med dashdot + { DEF_LINE_WIDTH_1, 0, 0 }, // B = thin dashdotdot + { DEF_LINE_WIDTH_2, 0, 0 }, // C = med dashdotdot + { DEF_LINE_WIDTH_2, 0, 0 } // D = med slant dashdot + }; + + if( nXclLine == EXC_LINE_NONE ) + return false; + if( nXclLine >= STATIC_TABLE_SIZE( ppnLineParam ) ) + nXclLine = EXC_LINE_THIN; + + rLine.SetColor( rPalette.GetColor( nXclColor ) ); + rLine.SetOutWidth( ppnLineParam[ nXclLine ][ 0 ] ); + rLine.SetInWidth( ppnLineParam[ nXclLine ][ 1 ] ); + rLine.SetDistance( ppnLineParam[ nXclLine ][ 2 ] ); + return true; +} + +} // namespace + +void XclImpCellBorder::FillToItemSet( SfxItemSet& rItemSet, const XclImpPalette& rPalette, bool bSkipPoolDefs ) const +{ + if( mbLeftUsed || mbRightUsed || mbTopUsed || mbBottomUsed ) + { + SvxBoxItem aBoxItem( ATTR_BORDER ); + SvxBorderLine aLine; + if( mbLeftUsed && lclConvertBorderLine( aLine, rPalette, mnLeftLine, mnLeftColor ) ) + aBoxItem.SetLine( &aLine, BOX_LINE_LEFT ); + if( mbRightUsed && lclConvertBorderLine( aLine, rPalette, mnRightLine, mnRightColor ) ) + aBoxItem.SetLine( &aLine, BOX_LINE_RIGHT ); + if( mbTopUsed && lclConvertBorderLine( aLine, rPalette, mnTopLine, mnTopColor ) ) + aBoxItem.SetLine( &aLine, BOX_LINE_TOP ); + if( mbBottomUsed && lclConvertBorderLine( aLine, rPalette, mnBottomLine, mnBottomColor ) ) + aBoxItem.SetLine( &aLine, BOX_LINE_BOTTOM ); + ScfTools::PutItem( rItemSet, aBoxItem, bSkipPoolDefs ); + } + if( mbDiagUsed ) + { + SvxLineItem aTLBRItem( ATTR_BORDER_TLBR ); + SvxLineItem aBLTRItem( ATTR_BORDER_BLTR ); + SvxBorderLine aLine; + if( lclConvertBorderLine( aLine, rPalette, mnDiagLine, mnDiagColor ) ) + { + if( mbDiagTLtoBR ) + aTLBRItem.SetLine( &aLine ); + if( mbDiagBLtoTR ) + aBLTRItem.SetLine( &aLine ); + } + ScfTools::PutItem( rItemSet, aTLBRItem, bSkipPoolDefs ); + ScfTools::PutItem( rItemSet, aBLTRItem, bSkipPoolDefs ); + } +} + +// ---------------------------------------------------------------------------- + +XclImpCellArea::XclImpCellArea() +{ + SetUsedFlags( false ); +} + +void XclImpCellArea::SetUsedFlags( bool bUsed ) +{ + mbForeUsed = mbBackUsed = mbPattUsed = bUsed; +} + +void XclImpCellArea::FillFromXF2( sal_uInt8 nFlags ) +{ + mnPattern = ::get_flagvalue( nFlags, EXC_XF2_BACKGROUND, EXC_PATT_12_5_PERC, EXC_PATT_NONE ); + mnForeColor = EXC_COLOR_BIFF2_BLACK; + mnBackColor = EXC_COLOR_BIFF2_WHITE; + SetUsedFlags( true ); +} + +void XclImpCellArea::FillFromXF3( sal_uInt16 nArea ) +{ + mnPattern = ::extract_value< sal_uInt8 >( nArea, 0, 6 ); + mnForeColor = ::extract_value< sal_uInt16 >( nArea, 6, 5 ); + mnBackColor = ::extract_value< sal_uInt16 >( nArea, 11, 5 ); + SetUsedFlags( true ); +} + +void XclImpCellArea::FillFromXF5( sal_uInt32 nArea ) +{ + mnPattern = ::extract_value< sal_uInt8 >( nArea, 16, 6 ); + mnForeColor = ::extract_value< sal_uInt16 >( nArea, 0, 7 ); + mnBackColor = ::extract_value< sal_uInt16 >( nArea, 7, 7 ); + SetUsedFlags( true ); +} + +void XclImpCellArea::FillFromXF8( sal_uInt32 nBorder2, sal_uInt16 nArea ) +{ + mnPattern = ::extract_value< sal_uInt8 >( nBorder2, 26, 6 ); + mnForeColor = ::extract_value< sal_uInt16 >( nArea, 0, 7 ); + mnBackColor = ::extract_value< sal_uInt16 >( nArea, 7, 7 ); + SetUsedFlags( true ); +} + +void XclImpCellArea::FillFromCF8( sal_uInt16 nPattern, sal_uInt16 nColor, sal_uInt32 nFlags ) +{ + mnForeColor = ::extract_value< sal_uInt16 >( nColor, 0, 7 ); + mnBackColor = ::extract_value< sal_uInt16 >( nColor, 7, 7 ); + mnPattern = ::extract_value< sal_uInt8 >( nPattern, 10, 6 ); + mbForeUsed = !::get_flag( nFlags, EXC_CF_AREA_FGCOLOR ); + mbBackUsed = !::get_flag( nFlags, EXC_CF_AREA_BGCOLOR ); + mbPattUsed = !::get_flag( nFlags, EXC_CF_AREA_PATTERN ); + + if( mbBackUsed && (!mbPattUsed || (mnPattern == EXC_PATT_SOLID)) ) + { + mnForeColor = mnBackColor; + mnPattern = EXC_PATT_SOLID; + mbForeUsed = mbPattUsed = true; + } + else if( !mbBackUsed && mbPattUsed && (mnPattern == EXC_PATT_SOLID) ) + { + mbPattUsed = false; + } +} + +void XclImpCellArea::FillToItemSet( SfxItemSet& rItemSet, const XclImpPalette& rPalette, bool bSkipPoolDefs ) const +{ + if( mbPattUsed ) // colors may be both unused in cond. formats + { + SvxBrushItem aBrushItem( ATTR_BACKGROUND ); + + // #108935# do not use IsTransparent() - old Calc filter writes tranparency with different color indexes + if( mnPattern == EXC_PATT_NONE ) + { + aBrushItem.SetColor( Color( COL_TRANSPARENT ) ); + } + else + { + Color aFore( rPalette.GetColor( mbForeUsed ? mnForeColor : EXC_COLOR_WINDOWTEXT ) ); + Color aBack( rPalette.GetColor( mbBackUsed ? mnBackColor : EXC_COLOR_WINDOWBACK ) ); + aBrushItem.SetColor( XclTools::GetPatternColor( aFore, aBack, mnPattern ) ); + } + + ScfTools::PutItem( rItemSet, aBrushItem, bSkipPoolDefs ); + } +} + + +// ---------------------------------------------------------------------------- + +XclImpXF::XclImpXF( const XclImpRoot& rRoot ) : + XclXFBase( true ), // default is cell XF + XclImpRoot( rRoot ), + mpStyleSheet( 0 ), + mnXclNumFmt( 0 ), + mnXclFont( 0 ) +{ +} + +XclImpXF::~XclImpXF() +{ +} + +void XclImpXF::ReadXF2( XclImpStream& rStrm ) +{ + sal_uInt8 nReadFont, nReadNumFmt, nFlags; + rStrm >> nReadFont; + rStrm.Ignore( 1 ); + rStrm >> nReadNumFmt >> nFlags; + + // XF type always cell, no parent, used flags always true + SetAllUsedFlags( true ); + + // attributes + maProtection.FillFromXF2( nReadNumFmt ); + mnXclFont = nReadFont; + mnXclNumFmt = nReadNumFmt & EXC_XF2_VALFMT_MASK; + maAlignment.FillFromXF2( nFlags ); + maBorder.FillFromXF2( nFlags ); + maArea.FillFromXF2( nFlags ); +} + +void XclImpXF::ReadXF3( XclImpStream& rStrm ) +{ + sal_uInt32 nBorder; + sal_uInt16 nTypeProt, nAlign, nArea; + sal_uInt8 nReadFont, nReadNumFmt; + rStrm >> nReadFont >> nReadNumFmt >> nTypeProt >> nAlign >> nArea >> nBorder; + + // XF type/parent, attribute used flags + mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE ); // new in BIFF3 + mnParent = ::extract_value< sal_uInt16 >( nAlign, 4, 12 ); // new in BIFF3 + SetUsedFlags( ::extract_value< sal_uInt8 >( nTypeProt, 10, 6 ) ); + + // attributes + maProtection.FillFromXF3( nTypeProt ); + mnXclFont = nReadFont; + mnXclNumFmt = nReadNumFmt; + maAlignment.FillFromXF3( nAlign ); + maBorder.FillFromXF3( nBorder ); + maArea.FillFromXF3( nArea ); // new in BIFF3 +} + +void XclImpXF::ReadXF4( XclImpStream& rStrm ) +{ + sal_uInt32 nBorder; + sal_uInt16 nTypeProt, nAlign, nArea; + sal_uInt8 nReadFont, nReadNumFmt; + rStrm >> nReadFont >> nReadNumFmt >> nTypeProt >> nAlign >> nArea >> nBorder; + + // XF type/parent, attribute used flags + mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE ); + mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 ); + SetUsedFlags( ::extract_value< sal_uInt8 >( nAlign, 10, 6 ) ); + + // attributes + maProtection.FillFromXF3( nTypeProt ); + mnXclFont = nReadFont; + mnXclNumFmt = nReadNumFmt; + maAlignment.FillFromXF4( nAlign ); + maBorder.FillFromXF3( nBorder ); + maArea.FillFromXF3( nArea ); +} + +void XclImpXF::ReadXF5( XclImpStream& rStrm ) +{ + sal_uInt32 nArea, nBorder; + sal_uInt16 nTypeProt, nAlign; + rStrm >> mnXclFont >> mnXclNumFmt >> nTypeProt >> nAlign >> nArea >> nBorder; + + // XF type/parent, attribute used flags + mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE ); + mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 ); + SetUsedFlags( ::extract_value< sal_uInt8 >( nAlign, 10, 6 ) ); + + // attributes + maProtection.FillFromXF3( nTypeProt ); + maAlignment.FillFromXF5( nAlign ); + maBorder.FillFromXF5( nBorder, nArea ); + maArea.FillFromXF5( nArea ); +} + +void XclImpXF::ReadXF8( XclImpStream& rStrm ) +{ + sal_uInt32 nBorder1, nBorder2; + sal_uInt16 nTypeProt, nAlign, nMiscAttrib, nArea; + rStrm >> mnXclFont >> mnXclNumFmt >> nTypeProt >> nAlign >> nMiscAttrib >> nBorder1 >> nBorder2 >> nArea; + + // XF type/parent, attribute used flags + mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE ); + mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 ); + SetUsedFlags( ::extract_value< sal_uInt8 >( nMiscAttrib, 10, 6 ) ); + + // attributes + maProtection.FillFromXF3( nTypeProt ); + maAlignment.FillFromXF8( nAlign, nMiscAttrib ); + maBorder.FillFromXF8( nBorder1, nBorder2 ); + maArea.FillFromXF8( nBorder2, nArea ); +} + +void XclImpXF::ReadXF( XclImpStream& rStrm ) +{ + switch( GetBiff() ) + { + case EXC_BIFF2: ReadXF2( rStrm ); break; + case EXC_BIFF3: ReadXF3( rStrm ); break; + case EXC_BIFF4: ReadXF4( rStrm ); break; + case EXC_BIFF5: ReadXF5( rStrm ); break; + case EXC_BIFF8: ReadXF8( rStrm ); break; + default: DBG_ERROR_BIFF(); + } +} + +const ScPatternAttr& XclImpXF::CreatePattern( bool bSkipPoolDefs ) +{ + if( mpPattern.get() ) + return *mpPattern; + + // create new pattern attribute set + mpPattern.reset( new ScPatternAttr( GetDoc().GetPool() ) ); + SfxItemSet& rItemSet = mpPattern->GetItemSet(); + XclImpXF* pParentXF = IsCellXF() ? GetXFBuffer().GetXF( mnParent ) : 0; + + // parent cell style + if( IsCellXF() && !mpStyleSheet ) + { + mpStyleSheet = GetXFBuffer().CreateStyleSheet( mnParent ); + + /* Enables mb***Used flags, if the formatting attributes differ from + the passed XF record. In cell XFs Excel uses the cell attributes, + if they differ from the parent style XF. + #109899# ...or if the respective flag is not set in parent style XF. */ + if( pParentXF ) + { + if( !mbProtUsed ) + mbProtUsed = !pParentXF->mbProtUsed || !(maProtection == pParentXF->maProtection); + if( !mbFontUsed ) + mbFontUsed = !pParentXF->mbFontUsed || (mnXclFont != pParentXF->mnXclFont); + if( !mbFmtUsed ) + mbFmtUsed = !pParentXF->mbFmtUsed || (mnXclNumFmt != pParentXF->mnXclNumFmt); + if( !mbAlignUsed ) + mbAlignUsed = !pParentXF->mbAlignUsed || !(maAlignment == pParentXF->maAlignment); + if( !mbBorderUsed ) + mbBorderUsed = !pParentXF->mbBorderUsed || !(maBorder == pParentXF->maBorder); + if( !mbAreaUsed ) + mbAreaUsed = !pParentXF->mbAreaUsed || !(maArea == pParentXF->maArea); + } + } + + // cell protection + if( mbProtUsed ) + maProtection.FillToItemSet( rItemSet, bSkipPoolDefs ); + + // font + if( mbFontUsed ) + GetFontBuffer().FillToItemSet( rItemSet, EXC_FONTITEM_CELL, mnXclFont, bSkipPoolDefs ); + + // value format + if( mbFmtUsed ) + { + GetNumFmtBuffer().FillToItemSet( rItemSet, mnXclNumFmt, bSkipPoolDefs ); + // Trace occurrences of Windows date formats + GetTracer().TraceDates( mnXclNumFmt ); + } + + // alignment + if( mbAlignUsed ) + maAlignment.FillToItemSet( rItemSet, GetFontBuffer().GetFont( mnXclFont ), bSkipPoolDefs ); + + // border + if( mbBorderUsed ) + { + maBorder.FillToItemSet( rItemSet, GetPalette(), bSkipPoolDefs ); + GetTracer().TraceBorderLineStyle(maBorder.mnLeftLine > EXC_LINE_HAIR || + maBorder.mnRightLine > EXC_LINE_HAIR || maBorder.mnTopLine > EXC_LINE_HAIR || + maBorder.mnBottomLine > EXC_LINE_HAIR ); + } + + // area + if( mbAreaUsed ) + { + maArea.FillToItemSet( rItemSet, GetPalette(), bSkipPoolDefs ); + GetTracer().TraceFillPattern(maArea.mnPattern != EXC_PATT_NONE && + maArea.mnPattern != EXC_PATT_SOLID); + } + + /* #i38709# Decide which rotation reference mode to use. If any outer + border line of the cell is set (either explicitly or via cell style), + and the cell contents are rotated, set rotation reference to bottom of + cell. This causes the borders to be painted rotated with the text. */ + if( mbAlignUsed || mbBorderUsed ) + { + SvxRotateMode eRotateMode = SVX_ROTATE_MODE_STANDARD; + const XclImpCellAlign* pAlign = mbAlignUsed ? &maAlignment : (pParentXF ? &pParentXF->maAlignment : 0); + const XclImpCellBorder* pBorder = mbBorderUsed ? &maBorder : (pParentXF ? &pParentXF->maBorder : 0); + if( pAlign && pBorder && (0 < pAlign->mnRotation) && (pAlign->mnRotation <= 180) && pBorder->HasAnyOuterBorder() ) + eRotateMode = SVX_ROTATE_MODE_BOTTOM; + ScfTools::PutItem( rItemSet, SvxRotateModeItem( eRotateMode, ATTR_ROTATE_MODE ), bSkipPoolDefs ); + } + + return *mpPattern; +} + +void XclImpXF::ApplyPattern( + SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2, + SCTAB nScTab, ULONG nForceScNumFmt ) +{ + // force creation of cell style and hard formatting, do it here to have mpStyleSheet + const ScPatternAttr& rPattern = CreatePattern(); + + // insert into document + ScDocument& rDoc = GetDoc(); + if( IsCellXF() && mpStyleSheet ) + rDoc.ApplyStyleAreaTab( nScCol1, nScRow1, nScCol2, nScRow2, nScTab, *mpStyleSheet ); + if( HasUsedFlags() ) + rDoc.ApplyPatternAreaTab( nScCol1, nScRow1, nScCol2, nScRow2, nScTab, rPattern ); + + // #108770# apply special number format + if( nForceScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND ) + { + ScPatternAttr aPattern( GetDoc().GetPool() ); + GetNumFmtBuffer().FillScFmtToItemSet( aPattern.GetItemSet(), nForceScNumFmt ); + rDoc.ApplyPatternAreaTab( nScCol1, nScRow1, nScCol2, nScRow2, nScTab, aPattern ); + } +} + +void XclImpXF::SetUsedFlags( sal_uInt8 nUsedFlags ) +{ + /* Notes about finding the mb***Used flags: + - In cell XFs a *set* bit means a used attribute. + - In style XFs a *cleared* bit means a used attribute. + The mb***Used members always store true, if the attribute is used. + The "mbCellXF == ::get_flag(...)" construct evaluates to true in + both mentioned cases: cell XF and set bit; or style XF and cleared bit. + */ + mbProtUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_PROT )); + mbFontUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_FONT )); + mbFmtUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_VALFMT )); + mbAlignUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_ALIGN )); + mbBorderUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_BORDER )); + mbAreaUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_AREA )); +} + +// ---------------------------------------------------------------------------- + +XclImpStyle::XclImpStyle( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + mnXfId( EXC_XF_NOTFOUND ), + mnBuiltinId( EXC_STYLE_USERDEF ), + mnLevel( EXC_STYLE_NOLEVEL ), + mbBuiltin( false ), + mbCustom( false ), + mbHidden( false ), + mpStyleSheet( 0 ) +{ +} + +void XclImpStyle::ReadStyle( XclImpStream& rStrm ) +{ + DBG_ASSERT_BIFF( GetBiff() >= EXC_BIFF3 ); + + sal_uInt16 nXFIndex; + rStrm >> nXFIndex; + mnXfId = nXFIndex & EXC_STYLE_XFMASK; + mbBuiltin = ::get_flag( nXFIndex, EXC_STYLE_BUILTIN ); + + if( mbBuiltin ) + { + rStrm >> mnBuiltinId >> mnLevel; + } + else + { + maName = (GetBiff() <= EXC_BIFF5) ? rStrm.ReadByteString( false ) : rStrm.ReadUniString(); + // #i103281# check if this is a new built-in style introduced in XL2007 + if( (GetBiff() == EXC_BIFF8) && (rStrm.GetNextRecId() == EXC_ID_STYLEEXT) && rStrm.StartNextRecord() ) + { + sal_uInt8 nExtFlags; + rStrm.Ignore( 12 ); + rStrm >> nExtFlags; + mbBuiltin = ::get_flag( nExtFlags, EXC_STYLEEXT_BUILTIN ); + mbCustom = ::get_flag( nExtFlags, EXC_STYLEEXT_CUSTOM ); + mbHidden = ::get_flag( nExtFlags, EXC_STYLEEXT_HIDDEN ); + if( mbBuiltin ) + { + rStrm.Ignore( 1 ); // category + rStrm >> mnBuiltinId >> mnLevel; + } + } + } +} + +ScStyleSheet* XclImpStyle::CreateStyleSheet() +{ + // #i1624# #i1768# ignore unnamed user styles + if( !mpStyleSheet && (maFinalName.Len() > 0) ) + { + bool bCreatePattern = false; + XclImpXF* pXF = GetXFBuffer().GetXF( mnXfId ); + + bool bDefStyle = mbBuiltin && (mnBuiltinId == EXC_STYLE_NORMAL); + if( bDefStyle ) + { + // set all flags to true to get all items in XclImpXF::CreatePattern() + if( pXF ) pXF->SetAllUsedFlags( true ); + // use existing "Default" style sheet + mpStyleSheet = static_cast< ScStyleSheet* >( GetStyleSheetPool().Find( + ScGlobal::GetRscString( STR_STYLENAME_STANDARD ), SFX_STYLE_FAMILY_PARA ) ); + DBG_ASSERT( mpStyleSheet, "XclImpStyle::CreateStyleSheet - Default style not found" ); + bCreatePattern = true; + } + else + { + /* #i103281# do not create another style sheet of the same name, + if it exists already. This is needed to prevent that styles + pasted from clipboard get duplicated over and over. */ + mpStyleSheet = static_cast< ScStyleSheet* >( GetStyleSheetPool().Find( maFinalName, SFX_STYLE_FAMILY_PARA ) ); + if( !mpStyleSheet ) + { + mpStyleSheet = &static_cast< ScStyleSheet& >( GetStyleSheetPool().Make( maFinalName, SFX_STYLE_FAMILY_PARA, SFXSTYLEBIT_USERDEF ) ); + bCreatePattern = true; + } + } + + // bDefStyle==true omits default pool items in CreatePattern() + if( bCreatePattern && mpStyleSheet && pXF ) + mpStyleSheet->GetItemSet().Put( pXF->CreatePattern( bDefStyle ).GetItemSet() ); + } + return mpStyleSheet; +} + +void XclImpStyle::CreateUserStyle( const String& rFinalName ) +{ + maFinalName = rFinalName; + if( !IsBuiltin() || mbCustom ) + CreateStyleSheet(); +} + +// ---------------------------------------------------------------------------- + +XclImpXFBuffer::XclImpXFBuffer( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +void XclImpXFBuffer::Initialize() +{ + maXFList.Clear(); + maBuiltinStyles.Clear(); + maUserStyles.Clear(); + maStylesByXf.clear(); +} + +void XclImpXFBuffer::ReadXF( XclImpStream& rStrm ) +{ + XclImpXF* pXF = new XclImpXF( GetRoot() ); + pXF->ReadXF( rStrm ); + maXFList.Append( pXF ); +} + +void XclImpXFBuffer::ReadStyle( XclImpStream& rStrm ) +{ + XclImpStyle* pStyle = new XclImpStyle( GetRoot() ); + pStyle->ReadStyle( rStrm ); + (pStyle->IsBuiltin() ? maBuiltinStyles : maUserStyles).Append( pStyle ); + DBG_ASSERT( maStylesByXf.count( pStyle->GetXfId() ) == 0, "XclImpXFBuffer::ReadStyle - multiple styles with equal XF identifier" ); + maStylesByXf[ pStyle->GetXfId() ] = pStyle; +} + +sal_uInt16 XclImpXFBuffer::GetFontIndex( sal_uInt16 nXFIndex ) const +{ + const XclImpXF* pXF = GetXF( nXFIndex ); + return pXF ? pXF->GetFontIndex() : EXC_FONT_NOTFOUND; +} + +const XclImpFont* XclImpXFBuffer::GetFont( sal_uInt16 nXFIndex ) const +{ + return GetFontBuffer().GetFont( GetFontIndex( nXFIndex ) ); +} + +namespace { + +/** Functor for case-insensitive string comparison, usable in maps etc. */ +struct IgnoreCaseCompare +{ + inline bool operator()( const String& rName1, const String& rName2 ) const + { return rName1.CompareIgnoreCaseToAscii( rName2 ) == COMPARE_LESS; } +}; + +} // namespace + +void XclImpXFBuffer::CreateUserStyles() +{ + // calculate final names of all styles + typedef ::std::map< String, XclImpStyle*, IgnoreCaseCompare > CellStyleNameMap; + typedef ::std::vector< XclImpStyle* > XclImpStyleVector; + + CellStyleNameMap aCellStyles; + XclImpStyleVector aConflictNameStyles; + + /* First, reserve style names that are built-in in Calc. This causes that + imported cell styles get different unused names and thus do not try to + overwrite these built-in styles. For BIFF4 workbooks (which contain a + separate list of cell styles per sheet), reserve all existing styles if + current sheet is not the first sheet (this styles buffer will be + initialized again for every new sheet). This will create unique names + for styles in different sheets with the same name. Assuming that the + BIFF4W import filter is never used to import from clipboard... */ + bool bReserveAll = (GetBiff() == EXC_BIFF4) && (GetCurrScTab() > 0); + SfxStyleSheetIterator aStyleIter( GetDoc().GetStyleSheetPool(), SFX_STYLE_FAMILY_PARA ); + String aStandardName = ScGlobal::GetRscString( STR_STYLENAME_STANDARD ); + for( SfxStyleSheetBase* pStyleSheet = aStyleIter.First(); pStyleSheet; pStyleSheet = aStyleIter.Next() ) + if( (pStyleSheet->GetName() != aStandardName) && (bReserveAll || !pStyleSheet->IsUserDefined()) ) + if( aCellStyles.count( pStyleSheet->GetName() ) == 0 ) + aCellStyles[ pStyleSheet->GetName() ] = 0; + + /* Calculate names of built-in styles. Store styles with reserved names + in the aConflictNameStyles list. */ + for( XclImpStyle* pStyle = maBuiltinStyles.First(); pStyle; pStyle = maBuiltinStyles.Next() ) + { + String aStyleName = XclTools::GetBuiltInStyleName( pStyle->GetBuiltinId(), pStyle->GetName(), pStyle->GetLevel() ); + DBG_ASSERT( bReserveAll || (aCellStyles.count( aStyleName ) == 0), + "XclImpXFBuffer::CreateUserStyles - multiple styles with equal built-in identifier" ); + if( aCellStyles.count( aStyleName ) > 0 ) + aConflictNameStyles.push_back( pStyle ); + else + aCellStyles[ aStyleName ] = pStyle; + } + + /* Calculate names of user defined styles. Store styles with reserved + names in the aConflictNameStyles list. */ + for( XclImpStyle* pStyle = maUserStyles.First(); pStyle; pStyle = maUserStyles.Next() ) + { + // #i1624# #i1768# ignore unnamed user styles + if( pStyle->GetName().Len() > 0 ) + { + if( aCellStyles.count( pStyle->GetName() ) > 0 ) + aConflictNameStyles.push_back( pStyle ); + else + aCellStyles[ pStyle->GetName() ] = pStyle; + } + } + + // find unused names for all styles with conflicting names + for( XclImpStyleVector::iterator aIt = aConflictNameStyles.begin(), aEnd = aConflictNameStyles.end(); aIt != aEnd; ++aIt ) + { + XclImpStyle* pStyle = *aIt; + String aUnusedName; + sal_Int32 nIndex = 0; + do + { + aUnusedName.Assign( pStyle->GetName() ).Append( ' ' ).Append( String::CreateFromInt32( ++nIndex ) ); + } + while( aCellStyles.count( aUnusedName ) > 0 ); + aCellStyles[ aUnusedName ] = pStyle; + } + + // set final names and create user-defined and modified built-in cell styles + for( CellStyleNameMap::iterator aIt = aCellStyles.begin(), aEnd = aCellStyles.end(); aIt != aEnd; ++aIt ) + if( aIt->second ) + aIt->second->CreateUserStyle( aIt->first ); +} + +ScStyleSheet* XclImpXFBuffer::CreateStyleSheet( sal_uInt16 nXFIndex ) +{ + XclImpStyleMap::iterator aIt = maStylesByXf.find( nXFIndex ); + return (aIt == maStylesByXf.end()) ? 0 : aIt->second->CreateStyleSheet(); +} + +void XclImpXFBuffer::ApplyPattern( + SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2, + SCTAB nScTab, const XclImpXFIndex& rXFIndex ) +{ + if( XclImpXF* pXF = GetXF( rXFIndex.GetXFIndex() ) ) + { + // #108770# set 'Standard' number format for all Boolean cells + ULONG nForceScNumFmt = rXFIndex.IsBoolCell() ? GetNumFmtBuffer().GetStdScNumFmt() : NUMBERFORMAT_ENTRY_NOT_FOUND; + pXF->ApplyPattern( nScCol1, nScRow1, nScCol2, nScRow2, nScTab, nForceScNumFmt ); + } +} + +// Buffer for XF indexes in cells ============================================= + +IMPL_FIXEDMEMPOOL_NEWDEL( XclImpXFRange, 100, 500 ) + +bool XclImpXFRange::Expand( SCROW nScRow, const XclImpXFIndex& rXFIndex ) +{ + if( maXFIndex != rXFIndex ) + return false; + + if( mnScRow2 + 1 == nScRow ) + { + ++mnScRow2; + return true; + } + if( mnScRow1 > 0 && (mnScRow1 - 1 == nScRow) ) + { + --mnScRow1; + return true; + } + + return false; +} + +bool XclImpXFRange::Expand( const XclImpXFRange& rNextRange ) +{ + DBG_ASSERT( mnScRow2 < rNextRange.mnScRow1, "XclImpXFRange::Expand - rows out of order" ); + if( (maXFIndex == rNextRange.maXFIndex) && (mnScRow2 + 1 == rNextRange.mnScRow1) ) + { + mnScRow2 = rNextRange.mnScRow2; + return true; + } + return false; +} + +// ---------------------------------------------------------------------------- + +void XclImpXFRangeColumn::SetDefaultXF( const XclImpXFIndex& rXFIndex ) +{ + // List should be empty when inserting the default column format. + // Later explicit SetXF() calls will break up this range. + DBG_ASSERT( maIndexList.Empty(), "XclImpXFRangeColumn::SetDefaultXF - Setting Default Column XF is not empty" ); + + // insert a complete row range with one insert. + maIndexList.Append( new XclImpXFRange( 0, MAXROW, rXFIndex ) ); +} + +// ---------------------------------------------------------------------------- + +void XclImpXFRangeColumn::SetXF( SCROW nScRow, const XclImpXFIndex& rXFIndex ) +{ + XclImpXFRange* pPrevRange; + XclImpXFRange* pNextRange; + ULONG nNextIndex; + + Find( pPrevRange, pNextRange, nNextIndex, nScRow ); + + // previous range: + // try to overwrite XF (if row is contained in) or try to expand range + if( pPrevRange ) + { + if( pPrevRange->Contains( nScRow ) ) // overwrite old XF + { + if( rXFIndex == pPrevRange->maXFIndex ) + return; + + SCROW nFirstScRow = pPrevRange->mnScRow1; + SCROW nLastScRow = pPrevRange->mnScRow2; + ULONG nIndex = nNextIndex - 1; + XclImpXFRange* pThisRange = pPrevRange; + pPrevRange = nIndex ? maIndexList.GetObject( nIndex - 1 ) : 0; + + if( nFirstScRow == nLastScRow ) // replace solely XF + { + pThisRange->maXFIndex = rXFIndex; + TryConcatPrev( nNextIndex ); // try to concat. next with this + TryConcatPrev( nIndex ); // try to concat. this with previous + } + else if( nFirstScRow == nScRow ) // replace first XF + { + ++(pThisRange->mnScRow1); + // try to concatenate with previous of this + if( !pPrevRange || !pPrevRange->Expand( nScRow, rXFIndex ) ) + maIndexList.Insert( new XclImpXFRange( nScRow, rXFIndex ), nIndex ); + } + else if( nLastScRow == nScRow ) // replace last XF + { + --(pThisRange->mnScRow2); + if( !pNextRange || !pNextRange->Expand( nScRow, rXFIndex ) ) + maIndexList.Insert( new XclImpXFRange( nScRow, rXFIndex ), nNextIndex ); + } + else // insert in the middle of the range + { + pThisRange->mnScRow1 = nScRow + 1; + // List::Insert() moves entries towards end of list, so insert twice at nIndex + maIndexList.Insert( new XclImpXFRange( nScRow, rXFIndex ), nIndex ); + maIndexList.Insert( new XclImpXFRange( nFirstScRow, nScRow - 1, pThisRange->maXFIndex ), nIndex ); + } + return; + } + else if( pPrevRange->Expand( nScRow, rXFIndex ) ) // try to expand + { + TryConcatPrev( nNextIndex ); // try to concatenate next with expanded + return; + } + } + + // try to expand next range + if( pNextRange && pNextRange->Expand( nScRow, rXFIndex ) ) + return; + + // create new range + maIndexList.Insert( new XclImpXFRange( nScRow, rXFIndex ), nNextIndex ); +} + +void XclImpXFRangeColumn::Find( + XclImpXFRange*& rpPrevRange, XclImpXFRange*& rpNextRange, + ULONG& rnNextIndex, SCROW nScRow ) const +{ + + // test whether list is empty + if( maIndexList.Empty() ) + { + rpPrevRange = rpNextRange = 0; + rnNextIndex = 0; + return; + } + + rpPrevRange = maIndexList.GetObject( 0 ); + rpNextRange = maIndexList.GetObject( maIndexList.Count() - 1 ); + + // test whether row is at end of list (contained in or behind last range) + // rpPrevRange will contain a possible existing row + if( rpNextRange->mnScRow1 <= nScRow ) + { + rpPrevRange = rpNextRange; + rpNextRange = 0; + rnNextIndex = maIndexList.Count(); + return; + } + + // test whether row is at beginning of list (really before first range) + if( nScRow < rpPrevRange->mnScRow1 ) + { + rpNextRange = rpPrevRange; + rpPrevRange = 0; + rnNextIndex = 0; + return; + } + + // loop: find range entries before and after new row + // break the loop if there is no more range between first and last -or- + // if rpPrevRange contains nScRow (rpNextRange will never contain nScRow) + ULONG nPrevIndex = 0; + ULONG nMidIndex; + rnNextIndex = maIndexList.Count() - 1; + XclImpXFRange* pMidRange; + while( ((rnNextIndex - nPrevIndex) > 1) && (rpPrevRange->mnScRow2 < nScRow) ) + { + nMidIndex = (nPrevIndex + rnNextIndex) / 2; + pMidRange = maIndexList.GetObject( nMidIndex ); + DBG_ASSERT( pMidRange, "XclImpXFRangeColumn::Find - missing XF index range" ); + if( nScRow < pMidRange->mnScRow1 ) // row is really before pMidRange + { + rpNextRange = pMidRange; + rnNextIndex = nMidIndex; + } + else // row is in or after pMidRange + { + rpPrevRange = pMidRange; + nPrevIndex = nMidIndex; + } + } + + // find next rpNextRange if rpPrevRange contains nScRow + if( nScRow <= rpPrevRange->mnScRow2 ) + { + rnNextIndex = nPrevIndex + 1; + rpNextRange = maIndexList.GetObject( rnNextIndex ); + } +} + +void XclImpXFRangeColumn::TryConcatPrev( ULONG nIndex ) +{ + if( !nIndex ) + return; + + XclImpXFRange* pPrevRange = maIndexList.GetObject( nIndex - 1 ); + XclImpXFRange* pNextRange = maIndexList.GetObject( nIndex ); + if( !pPrevRange || !pNextRange ) + return; + + if( pPrevRange->Expand( *pNextRange ) ) + maIndexList.Delete( nIndex ); +} + +// ---------------------------------------------------------------------------- + +XclImpXFRangeBuffer::XclImpXFRangeBuffer( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +XclImpXFRangeBuffer::~XclImpXFRangeBuffer() +{ +} + +void XclImpXFRangeBuffer::Initialize() +{ + maColumns.clear(); + maHyperlinks.clear(); + maMergeList.RemoveAll(); +} + +void XclImpXFRangeBuffer::SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex, XclImpXFInsertMode eMode ) +{ + SCCOL nScCol = rScPos.Col(); + SCROW nScRow = rScPos.Row(); + + // set cell XF's + size_t nIndex = static_cast< size_t >( nScCol ); + if( maColumns.size() <= nIndex ) + maColumns.resize( nIndex + 1 ); + if( !maColumns[ nIndex ] ) + maColumns[ nIndex ].reset( new XclImpXFRangeColumn ); + // #108770# remember all Boolean cells, they will get 'Standard' number format + maColumns[ nIndex ]->SetXF( nScRow, XclImpXFIndex( nXFIndex, eMode == xlXFModeBoolCell ) ); + + // set "center across selection" and "fill" attribute for all following empty cells + // #97130# ignore it on row default XFs + if( eMode != xlXFModeRow ) + { + const XclImpXF* pXF = GetXFBuffer().GetXF( nXFIndex ); + if( pXF && ((pXF->GetHorAlign() == EXC_XF_HOR_CENTER_AS) || (pXF->GetHorAlign() == EXC_XF_HOR_FILL)) ) + { + // expand last merged range if this attribute is set repeatedly + ScRange* pRange = maMergeList.Last(); + if( pRange && (pRange->aEnd.Row() == nScRow) && (pRange->aEnd.Col() + 1 == nScCol) + && (eMode == xlXFModeBlank) ) + pRange->aEnd.IncCol(); + else if( eMode != xlXFModeBlank ) // #108781# do not merge empty cells + SetMerge( nScCol, nScRow ); + } + } +} + +void XclImpXFRangeBuffer::SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex ) +{ + SetXF( rScPos, nXFIndex, xlXFModeCell ); +} + +void XclImpXFRangeBuffer::SetBlankXF( const ScAddress& rScPos, sal_uInt16 nXFIndex ) +{ + SetXF( rScPos, nXFIndex, xlXFModeBlank ); +} + +void XclImpXFRangeBuffer::SetBoolXF( const ScAddress& rScPos, sal_uInt16 nXFIndex ) +{ + SetXF( rScPos, nXFIndex, xlXFModeBoolCell ); +} + +void XclImpXFRangeBuffer::SetRowDefXF( SCROW nScRow, sal_uInt16 nXFIndex ) +{ + for( SCCOL nScCol = 0; nScCol <= MAXCOL; ++nScCol ) + SetXF( ScAddress( nScCol, nScRow, 0 ), nXFIndex, xlXFModeRow ); +} + +void XclImpXFRangeBuffer::SetColumnDefXF( SCCOL nScCol, sal_uInt16 nXFIndex ) +{ + // our array should not have values when creating the default column format. + size_t nIndex = static_cast< size_t >( nScCol ); + if( maColumns.size() <= nIndex ) + maColumns.resize( nIndex + 1 ); + DBG_ASSERT( !maColumns[ nIndex ], "XclImpXFRangeBuffer::SetColumnDefXF - default column of XFs already has values" ); + maColumns[ nIndex ].reset( new XclImpXFRangeColumn ); + maColumns[ nIndex ]->SetDefaultXF( XclImpXFIndex( nXFIndex ) ); +} + +void XclImpXFRangeBuffer::SetBorderLine( const ScRange& rRange, SCTAB nScTab, USHORT nLine ) +{ + SCCOL nFromScCol = (nLine == BOX_LINE_RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col(); + SCROW nFromScRow = (nLine == BOX_LINE_BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row(); + ScDocument& rDoc = GetDoc(); + + const SvxBoxItem* pFromItem = static_cast< const SvxBoxItem* >( + rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER ) ); + const SvxBoxItem* pToItem = static_cast< const SvxBoxItem* >( + rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER ) ); + + SvxBoxItem aNewItem( *pToItem ); + aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine ); + rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem ); +} + +void XclImpXFRangeBuffer::SetHyperlink( const XclRange& rXclRange, const String& rUrl ) +{ + maHyperlinks.push_back( XclImpHyperlinkRange( rXclRange, rUrl ) ); +} + +void XclImpXFRangeBuffer::SetMerge( SCCOL nScCol, SCROW nScRow ) +{ + maMergeList.Append( ScRange( nScCol, nScRow, 0 ) ); +} + +void XclImpXFRangeBuffer::SetMerge( SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2 ) +{ + if( (nScCol1 < nScCol2) || (nScRow1 < nScRow2) ) + maMergeList.Append( ScRange( nScCol1, nScRow1, 0, nScCol2, nScRow2, 0 ) ); +} + +void XclImpXFRangeBuffer::Finalize() +{ + ScDocument& rDoc = GetDoc(); + SCTAB nScTab = GetCurrScTab(); + + // apply patterns + XclImpXFBuffer& rXFBuffer = GetXFBuffer(); + for( XclImpXFRangeColumnVec::const_iterator aVBeg = maColumns.begin(), aVEnd = maColumns.end(), aVIt = aVBeg; aVIt != aVEnd; ++aVIt ) + { + // apply all cell styles of an existing column + if( aVIt->is() ) + { + XclImpXFRangeColumn& rColumn = **aVIt; + SCCOL nScCol = static_cast< SCCOL >( aVIt - aVBeg ); + for( XclImpXFRange* pStyle = rColumn.First(); pStyle; pStyle = rColumn.Next() ) + rXFBuffer.ApplyPattern( nScCol, pStyle->mnScRow1, nScCol, pStyle->mnScRow2, nScTab, pStyle->maXFIndex ); + } + } + + // insert hyperlink cells + for( XclImpHyperlinkList::const_iterator aLIt = maHyperlinks.begin(), aLEnd = maHyperlinks.end(); aLIt != aLEnd; ++aLIt ) + XclImpHyperlink::InsertUrl( GetRoot(), aLIt->first, aLIt->second ); + + // apply cell merging + for( const ScRange* pRange = maMergeList.First(); pRange; pRange = maMergeList.Next() ) + { + const ScAddress& rStart = pRange->aStart; + const ScAddress& rEnd = pRange->aEnd; + bool bMultiCol = rStart.Col() != rEnd.Col(); + bool bMultiRow = rStart.Row() != rEnd.Row(); + // set correct right border + if( bMultiCol ) + SetBorderLine( *pRange, nScTab, BOX_LINE_RIGHT ); + // set correct lower border + if( bMultiRow ) + SetBorderLine( *pRange, nScTab, BOX_LINE_BOTTOM ); + // do merge + if( bMultiCol || bMultiRow ) + rDoc.DoMerge( nScTab, rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row() ); + // #i93609# merged range in a single row: test if manual row height is needed + if( !bMultiRow ) + { + bool bTextWrap = static_cast< const SfxBoolItem* >( rDoc.GetAttr( rStart.Col(), rStart.Row(), rStart.Tab(), ATTR_LINEBREAK ) )->GetValue(); + if( !bTextWrap && (rDoc.GetCellType( rStart ) == CELLTYPE_EDIT) ) + if( const EditTextObject* pEditObj = static_cast< const ScEditCell* >( rDoc.GetCell( rStart ) )->GetData() ) + bTextWrap = pEditObj->GetParagraphCount() > 1; + if( bTextWrap ) + GetOldRoot().pColRowBuff->SetManualRowHeight( rStart.Row() ); + } + } +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xiview.cxx b/sc/source/filter/excel/xiview.cxx new file mode 100644 index 000000000000..f75032ff461e --- /dev/null +++ b/sc/source/filter/excel/xiview.cxx @@ -0,0 +1,304 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xiview.hxx" +#include "document.hxx" +#include "scextopt.hxx" +#include "viewopti.hxx" +#include "xistream.hxx" +#include "xihelper.hxx" +#include "xistyle.hxx" + +// Document view settings ===================================================== + +XclImpDocViewSettings::XclImpDocViewSettings( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +void XclImpDocViewSettings::ReadWindow1( XclImpStream& rStrm ) +{ + rStrm >> maData.mnWinX + >> maData.mnWinY + >> maData.mnWinWidth + >> maData.mnWinHeight + >> maData.mnFlags; + if( GetBiff() >= EXC_BIFF5 ) + { + rStrm >> maData.mnDisplXclTab + >> maData.mnFirstVisXclTab + >> maData.mnXclSelectCnt + >> maData.mnTabBarWidth; + } +} + +SCTAB XclImpDocViewSettings::GetDisplScTab() const +{ + /* Simply cast Excel index to Calc index. + TODO: This may fail if the document contains scenarios. */ + sal_uInt16 nMaxXclTab = static_cast< sal_uInt16 >( GetMaxPos().Tab() ); + return static_cast< SCTAB >( (maData.mnDisplXclTab <= nMaxXclTab) ? maData.mnDisplXclTab : 0 ); +} + +void XclImpDocViewSettings::Finalize() +{ + ScViewOptions aViewOpt( GetDoc().GetViewOptions() ); + aViewOpt.SetOption( VOPT_HSCROLL, ::get_flag( maData.mnFlags, EXC_WIN1_HOR_SCROLLBAR ) ); + aViewOpt.SetOption( VOPT_VSCROLL, ::get_flag( maData.mnFlags, EXC_WIN1_VER_SCROLLBAR ) ); + aViewOpt.SetOption( VOPT_TABCONTROLS, ::get_flag( maData.mnFlags, EXC_WIN1_TABBAR ) ); + GetDoc().SetViewOptions( aViewOpt ); + + // displayed sheet + GetExtDocOptions().GetDocSettings().mnDisplTab = GetDisplScTab(); + + // width of the tabbar with sheet names + if( maData.mnTabBarWidth <= 1000 ) + GetExtDocOptions().GetDocSettings().mfTabBarWidth = static_cast< double >( maData.mnTabBarWidth ) / 1000.0; +} + +// Sheet view settings ======================================================== + +namespace { + +long lclGetScZoom( sal_uInt16 nXclZoom, sal_uInt16 nDefZoom ) +{ + return static_cast< long >( nXclZoom ? nXclZoom : nDefZoom ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclImpTabViewSettings::XclImpTabViewSettings( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ + Initialize(); +} + +void XclImpTabViewSettings::Initialize() +{ + maData.SetDefaults(); +} + +void XclImpTabViewSettings::ReadTabBgColor( XclImpStream& rStrm, XclImpPalette& rPal ) +{ + DBG_ASSERT_BIFF( GetBiff() >= EXC_BIFF8 ); + if( GetBiff() < EXC_BIFF8 ) + return; + + sal_uInt8 ColorIndex; + Color TabBgColor; + + rStrm.Ignore( 16 ); + ColorIndex = rStrm.ReaduInt8() & EXC_SHEETEXT_TABCOLOR; //0x7F + if ( ColorIndex >= 8 && ColorIndex <= 63 ) //only accept valid index values + { + TabBgColor = rPal.GetColor( ColorIndex ); + maData.maTabBgColor = TabBgColor; + } +} + +void XclImpTabViewSettings::ReadWindow2( XclImpStream& rStrm, bool bChart ) +{ + if( GetBiff() == EXC_BIFF2 ) + { + maData.mbShowFormulas = rStrm.ReaduInt8() != 0; + maData.mbShowGrid = rStrm.ReaduInt8() != 0; + maData.mbShowHeadings = rStrm.ReaduInt8() != 0; + maData.mbFrozenPanes = rStrm.ReaduInt8() != 0; + maData.mbShowZeros = rStrm.ReaduInt8() != 0; + rStrm >> maData.maFirstXclPos; + maData.mbDefGridColor = rStrm.ReaduInt8() != 0; + rStrm >> maData.maGridColor; + } + else + { + sal_uInt16 nFlags; + rStrm >> nFlags >> maData.maFirstXclPos; + + // #i59590# #158194# real life: Excel ignores some view settings in chart sheets + maData.mbSelected = ::get_flag( nFlags, EXC_WIN2_SELECTED ); + maData.mbDisplayed = ::get_flag( nFlags, EXC_WIN2_DISPLAYED ); + maData.mbMirrored = !bChart && ::get_flag( nFlags, EXC_WIN2_MIRRORED ); + maData.mbFrozenPanes = !bChart && ::get_flag( nFlags, EXC_WIN2_FROZEN ); + maData.mbPageMode = !bChart && ::get_flag( nFlags, EXC_WIN2_PAGEBREAKMODE ); + maData.mbDefGridColor = bChart || ::get_flag( nFlags, EXC_WIN2_DEFGRIDCOLOR ); + maData.mbShowFormulas = !bChart && ::get_flag( nFlags, EXC_WIN2_SHOWFORMULAS ); + maData.mbShowGrid = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWGRID ); + maData.mbShowHeadings = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWHEADINGS ); + maData.mbShowZeros = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWZEROS ); + maData.mbShowOutline = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWOUTLINE ); + + switch( GetBiff() ) + { + case EXC_BIFF3: + case EXC_BIFF4: + case EXC_BIFF5: + rStrm >> maData.maGridColor; + break; + case EXC_BIFF8: + { + sal_uInt16 nGridColorIdx; + rStrm >> nGridColorIdx; + // zoom data not included in chart sheets + if( rStrm.GetRecLeft() >= 6 ) + { + rStrm.Ignore( 2 ); + rStrm >> maData.mnPageZoom >> maData.mnNormalZoom; + } + + if( !maData.mbDefGridColor ) + maData.maGridColor = GetPalette().GetColor( nGridColorIdx ); + } + break; + default: DBG_ERROR_BIFF(); + } + } + + // do not scroll chart sheets + if( bChart ) + maData.maFirstXclPos.Set( 0, 0 ); +} + +void XclImpTabViewSettings::ReadScl( XclImpStream& rStrm ) +{ + sal_uInt16 nNum, nDenom; + rStrm >> nNum >> nDenom; + DBG_ASSERT( nDenom > 0, "XclImpPageSettings::ReadScl - invalid denominator" ); + if( nDenom > 0 ) + maData.mnCurrentZoom = limit_cast< sal_uInt16 >( (nNum * 100) / nDenom ); +} + +void XclImpTabViewSettings::ReadPane( XclImpStream& rStrm ) +{ + rStrm >> maData.mnSplitX + >> maData.mnSplitY + >> maData.maSecondXclPos + >> maData.mnActivePane; +} + +void XclImpTabViewSettings::ReadSelection( XclImpStream& rStrm ) +{ + // pane of this selection + sal_uInt8 nPane; + rStrm >> nPane; + XclSelectionData& rSelData = maData.CreateSelectionData( nPane ); + // cursor position and selection + rStrm >> rSelData.maXclCursor >> rSelData.mnCursorIdx; + rSelData.maXclSelection.Read( rStrm, false ); +} + +void XclImpTabViewSettings::Finalize() +{ + SCTAB nScTab = GetCurrScTab(); + ScDocument& rDoc = GetDoc(); + XclImpAddressConverter& rAddrConv = GetAddressConverter(); + ScExtTabSettings& rTabSett = GetExtDocOptions().GetOrCreateTabSettings( nScTab ); + bool bDisplayed = GetDocViewSettings().GetDisplScTab() == nScTab; + + // *** sheet options: cursor, selection, splits, zoom *** + + // sheet flags + if( maData.mbMirrored ) + // do not call this function with FALSE, it would mirror away all drawing objects + rDoc.SetLayoutRTL( nScTab, TRUE ); + rTabSett.mbSelected = maData.mbSelected || bDisplayed; + + // first visible cell in top-left pane and in additional pane(s) + rTabSett.maFirstVis = rAddrConv.CreateValidAddress( maData.maFirstXclPos, nScTab, false ); + rTabSett.maSecondVis = rAddrConv.CreateValidAddress( maData.maSecondXclPos, nScTab, false ); + + // cursor position and selection + if( const XclSelectionData* pSelData = maData.GetSelectionData( maData.mnActivePane ) ) + { + rTabSett.maCursor = rAddrConv.CreateValidAddress( pSelData->maXclCursor, nScTab, false ); + rAddrConv.ConvertRangeList( rTabSett.maSelection, pSelData->maXclSelection, nScTab, false ); + } + + // active pane + switch( maData.mnActivePane ) + { + case EXC_PANE_TOPLEFT: rTabSett.meActivePane = SCEXT_PANE_TOPLEFT; break; + case EXC_PANE_TOPRIGHT: rTabSett.meActivePane = SCEXT_PANE_TOPRIGHT; break; + case EXC_PANE_BOTTOMLEFT: rTabSett.meActivePane = SCEXT_PANE_BOTTOMLEFT; break; + case EXC_PANE_BOTTOMRIGHT: rTabSett.meActivePane = SCEXT_PANE_BOTTOMRIGHT; break; + } + + // freeze/split position + rTabSett.mbFrozenPanes = maData.mbFrozenPanes; + if( maData.mbFrozenPanes ) + { + /* Frozen panes: handle split position as row/column positions. + #i35812# Excel uses number of visible rows/columns, Calc uses position of freeze. */ + if( (maData.mnSplitX > 0) && (maData.maFirstXclPos.mnCol + maData.mnSplitX <= GetScMaxPos().Col()) ) + rTabSett.maFreezePos.SetCol( static_cast< SCCOL >( maData.maFirstXclPos.mnCol + maData.mnSplitX ) ); + if( (maData.mnSplitY > 0) && (maData.maFirstXclPos.mnRow + maData.mnSplitY <= GetScMaxPos().Row()) ) + rTabSett.maFreezePos.SetRow( static_cast< SCROW >( maData.maFirstXclPos.mnRow + maData.mnSplitY ) ); + } + else + { + // split window: position is in twips + rTabSett.maSplitPos.X() = static_cast< long >( maData.mnSplitX ); + rTabSett.maSplitPos.Y() = static_cast< long >( maData.mnSplitY ); + } + + // grid color + if( maData.mbDefGridColor ) + rTabSett.maGridColor.SetColor( COL_AUTO ); + else + rTabSett.maGridColor = maData.maGridColor; + + // view mode and zoom + if( maData.mnCurrentZoom != 0 ) + (maData.mbPageMode ? maData.mnPageZoom : maData.mnNormalZoom) = maData.mnCurrentZoom; + rTabSett.mbPageMode = maData.mbPageMode; + rTabSett.mnNormalZoom = lclGetScZoom( maData.mnNormalZoom, EXC_WIN2_NORMALZOOM_DEF ); + rTabSett.mnPageZoom = lclGetScZoom( maData.mnPageZoom, EXC_WIN2_PAGEZOOM_DEF ); + + // *** additional handling for displayed sheet *** + + if( bDisplayed ) + { + // set Excel sheet settings globally at Calc document, take settings from displayed sheet + ScViewOptions aViewOpt( rDoc.GetViewOptions() ); + aViewOpt.SetOption( VOPT_FORMULAS, maData.mbShowFormulas ); + aViewOpt.SetOption( VOPT_GRID, maData.mbShowGrid ); + aViewOpt.SetOption( VOPT_HEADER, maData.mbShowHeadings ); + aViewOpt.SetOption( VOPT_NULLVALS, maData.mbShowZeros ); + aViewOpt.SetOption( VOPT_OUTLINER, maData.mbShowOutline ); + rDoc.SetViewOptions( aViewOpt ); + } + + // *** set tab bg color + if ( !maData.IsDefaultTabBgColor() ) + rDoc.SetTabBgColor(nScTab, maData.maTabBgColor); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xladdress.cxx b/sc/source/filter/excel/xladdress.cxx new file mode 100644 index 000000000000..d0c1a925f8ab --- /dev/null +++ b/sc/source/filter/excel/xladdress.cxx @@ -0,0 +1,161 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xladdress.hxx" +#include "xestream.hxx" +#include "xltracer.hxx" +#include "xistream.hxx" + +// ============================================================================ + +void XclAddress::Read( XclImpStream& rStrm, bool bCol16Bit ) +{ + rStrm >> mnRow; + if( bCol16Bit ) + rStrm >> mnCol; + else + mnCol = rStrm.ReaduInt8(); +} + +void XclAddress::Write( XclExpStream& rStrm, bool bCol16Bit ) const +{ + rStrm << mnRow; + if( bCol16Bit ) + rStrm << mnCol; + else + rStrm << static_cast< sal_uInt8 >( mnCol ); +} + +// ---------------------------------------------------------------------------- + +bool XclRange::Contains( const XclAddress& rPos ) const +{ + return (maFirst.mnCol <= rPos.mnCol) && (rPos.mnCol <= maLast.mnCol) && + (maFirst.mnRow <= rPos.mnRow) && (rPos.mnRow <= maLast.mnRow); +} + +void XclRange::Read( XclImpStream& rStrm, bool bCol16Bit ) +{ + rStrm >> maFirst.mnRow >> maLast.mnRow; + if( bCol16Bit ) + rStrm >> maFirst.mnCol >> maLast.mnCol; + else + { + maFirst.mnCol = rStrm.ReaduInt8(); + maLast.mnCol = rStrm.ReaduInt8(); + } +} + +void XclRange::Write( XclExpStream& rStrm, bool bCol16Bit ) const +{ + rStrm << maFirst.mnRow << maLast.mnRow; + if( bCol16Bit ) + rStrm << maFirst.mnCol << maLast.mnCol; + else + rStrm << static_cast< sal_uInt8 >( maFirst.mnCol ) << static_cast< sal_uInt8 >( maLast.mnCol ); +} + +// ---------------------------------------------------------------------------- + +XclRange XclRangeList::GetEnclosingRange() const +{ + XclRange aXclRange; + if( !empty() ) + { + const_iterator aIt = begin(), aEnd = end(); + aXclRange = *aIt; + for( ++aIt; aIt != aEnd; ++aIt ) + { + aXclRange.maFirst.mnCol = ::std::min( aXclRange.maFirst.mnCol, aIt->maFirst.mnCol ); + aXclRange.maFirst.mnRow = ::std::min( aXclRange.maFirst.mnRow, aIt->maFirst.mnRow ); + aXclRange.maLast.mnCol = ::std::max( aXclRange.maLast.mnCol, aIt->maLast.mnCol ); + aXclRange.maLast.mnRow = ::std::max( aXclRange.maLast.mnRow, aIt->maLast.mnRow ); + } + } + return aXclRange; +} + +void XclRangeList::Read( XclImpStream& rStrm, bool bCol16Bit ) +{ + sal_uInt16 nCount; + rStrm >> nCount; + size_t nOldSize = size(); + resize( nOldSize + nCount ); + for( iterator aIt = begin() + nOldSize; rStrm.IsValid() && (nCount > 0); --nCount, ++aIt ) + aIt->Read( rStrm, bCol16Bit ); +} + +void XclRangeList::Write( XclExpStream& rStrm, bool bCol16Bit ) const +{ + WriteSubList( rStrm, 0, size(), bCol16Bit ); +} + +void XclRangeList::WriteSubList( XclExpStream& rStrm, size_t nBegin, size_t nCount, bool bCol16Bit ) const +{ + DBG_ASSERT( nBegin <= size(), "XclRangeList::WriteSubList - invalid start position" ); + size_t nEnd = ::std::min< size_t >( nBegin + nCount, size() ); + sal_uInt16 nXclCount = ulimit_cast< sal_uInt16 >( nEnd - nBegin ); + rStrm << nXclCount; + rStrm.SetSliceSize( bCol16Bit ? 8 : 6 ); + for( const_iterator aIt = begin() + nBegin, aEnd = begin() + nEnd; aIt != aEnd; ++aIt ) + aIt->Write( rStrm, bCol16Bit ); +} + +// ============================================================================ + +XclAddressConverterBase::XclAddressConverterBase( XclTracer& rTracer, const ScAddress& rMaxPos ) : + mrTracer( rTracer ), + maMaxPos( rMaxPos ), + mnMaxCol( static_cast< sal_uInt16 >( rMaxPos.Col() ) ), + mnMaxRow( static_cast< sal_uInt16 >( rMaxPos.Row() ) ), + mbColTrunc( false ), + mbRowTrunc( false ), + mbTabTrunc( false ) +{ + DBG_ASSERT( static_cast< size_t >( rMaxPos.Col() ) <= SAL_MAX_UINT16, "XclAddressConverterBase::XclAddressConverterBase - invalid max column" ); + DBG_ASSERT( static_cast< size_t >( rMaxPos.Row() ) <= SAL_MAX_UINT16, "XclAddressConverterBase::XclAddressConverterBase - invalid max row" ); +} + +XclAddressConverterBase::~XclAddressConverterBase() +{ +} + +bool XclAddressConverterBase::CheckScTab( SCTAB nScTab, bool bWarn ) +{ + bool bValid = (0 <= nScTab) && (nScTab <= maMaxPos.Tab()); + if( !bValid && bWarn ) + { + mbTabTrunc |= (nScTab > maMaxPos.Tab()); // do not warn for deleted refs + mrTracer.TraceInvalidTab( nScTab, maMaxPos.Tab() ); + } + return bValid; +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xlchart.cxx b/sc/source/filter/excel/xlchart.cxx new file mode 100755 index 000000000000..10a0657c7899 --- /dev/null +++ b/sc/source/filter/excel/xlchart.cxx @@ -0,0 +1,1328 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "xlchart.hxx" + +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/awt/Gradient.hpp> +#include <com/sun/star/drawing/Hatch.hpp> +#include <com/sun/star/drawing/LineDash.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/BitmapMode.hpp> +#include <com/sun/star/chart/DataLabelPlacement.hpp> +#include <com/sun/star/chart/XAxisXSupplier.hpp> +#include <com/sun/star/chart/XAxisYSupplier.hpp> +#include <com/sun/star/chart/XAxisZSupplier.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp> +#include <com/sun/star/chart2/Symbol.hpp> + +#include <rtl/math.hxx> +#include <svl/itemset.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xfltrit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/unomid.hxx> +#include <filter/msfilter/escherex.hxx> +#include <editeng/memberids.hrc> +#include "global.hxx" +#include "xlroot.hxx" +#include "xlstyle.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::chart2::XChartDocument; +using ::com::sun::star::drawing::XShape; + +namespace cssc = ::com::sun::star::chart; + +// Common ===================================================================== + +XclChRectangle::XclChRectangle() : + mnX( 0 ), + mnY( 0 ), + mnWidth( 0 ), + mnHeight( 0 ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChDataPointPos::XclChDataPointPos( sal_uInt16 nSeriesIdx, sal_uInt16 nPointIdx ) : + mnSeriesIdx( nSeriesIdx ), + mnPointIdx( nPointIdx ) +{ +} + +bool operator<( const XclChDataPointPos& rL, const XclChDataPointPos& rR ) +{ + return (rL.mnSeriesIdx < rR.mnSeriesIdx) || + ((rL.mnSeriesIdx == rR.mnSeriesIdx) && (rL.mnPointIdx < rR.mnPointIdx)); +} + +// ---------------------------------------------------------------------------- + +XclChFrBlock::XclChFrBlock( sal_uInt16 nType ) : + mnType( nType ), + mnContext( 0 ), + mnValue1( 0 ), + mnValue2( 0 ) +{ +} + +// Frame formatting =========================================================== + +XclChFramePos::XclChFramePos() : + mnTLMode( EXC_CHFRAMEPOS_PARENT ), + mnBRMode( EXC_CHFRAMEPOS_PARENT ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChLineFormat::XclChLineFormat() : + maColor( COL_BLACK ), + mnPattern( EXC_CHLINEFORMAT_SOLID ), + mnWeight( EXC_CHLINEFORMAT_SINGLE ), + mnFlags( EXC_CHLINEFORMAT_AUTO ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChAreaFormat::XclChAreaFormat() : + maPattColor( COL_WHITE ), + maBackColor( COL_BLACK ), + mnPattern( EXC_PATT_SOLID ), + mnFlags( EXC_CHAREAFORMAT_AUTO ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChEscherFormat::XclChEscherFormat() +{ +} + +XclChEscherFormat::~XclChEscherFormat() +{ +} + +// ---------------------------------------------------------------------------- + +XclChPicFormat::XclChPicFormat() : + mnBmpMode( EXC_CHPICFORMAT_NONE ), + mnFormat( EXC_CHPICFORMAT_DEFAULT ), + mnFlags( EXC_CHPICFORMAT_DEFAULTFLAGS ), + mfScale( 0.5 ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChFrame::XclChFrame() : + mnFormat( EXC_CHFRAME_STANDARD ), + mnFlags( EXC_CHFRAME_AUTOSIZE | EXC_CHFRAME_AUTOPOS ) +{ +} + +// Source links =============================================================== + +XclChSourceLink::XclChSourceLink() : + mnDestType( EXC_CHSRCLINK_TITLE ), + mnLinkType( EXC_CHSRCLINK_DEFAULT ), + mnFlags( 0 ), + mnNumFmtIdx( 0 ) +{ +} + +// Text ======================================================================= + +XclChObjectLink::XclChObjectLink() : + mnTarget( EXC_CHOBJLINK_NONE ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChFrLabelProps::XclChFrLabelProps() : + mnFlags( 0 ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChText::XclChText() : + maTextColor( COL_BLACK ), + mnHAlign( EXC_CHTEXT_ALIGN_CENTER ), + mnVAlign( EXC_CHTEXT_ALIGN_CENTER ), + mnBackMode( EXC_CHTEXT_TRANSPARENT ), + mnFlags( EXC_CHTEXT_AUTOCOLOR | EXC_CHTEXT_AUTOFILL ), + mnFlags2( EXC_CHTEXT_POS_DEFAULT ), + mnRotation( EXC_ROT_NONE ) +{ +} + +// Data series ================================================================ + +XclChMarkerFormat::XclChMarkerFormat() : + maLineColor( COL_BLACK ), + maFillColor( COL_WHITE ), + mnMarkerSize( EXC_CHMARKERFORMAT_SINGLESIZE ), + mnMarkerType( EXC_CHMARKERFORMAT_NOSYMBOL ), + mnFlags( EXC_CHMARKERFORMAT_AUTO ) +{ +}; + +// ---------------------------------------------------------------------------- + +XclCh3dDataFormat::XclCh3dDataFormat() : + mnBase( EXC_CH3DDATAFORMAT_RECT ), + mnTop( EXC_CH3DDATAFORMAT_STRAIGHT ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChDataFormat::XclChDataFormat() : + mnFormatIdx( EXC_CHDATAFORMAT_DEFAULT ), + mnFlags( 0 ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChSerTrendLine::XclChSerTrendLine() : + mfForecastFor( 0.0 ), + mfForecastBack( 0.0 ), + mnLineType( EXC_CHSERTREND_POLYNOMIAL ), + mnOrder( 1 ), + mnShowEquation( 0 ), + mnShowRSquared( 0 ) +{ + /* Set all bits in mfIntercept to 1 (that is -1.#NAN) to indicate that + there is no interception point. Cannot use ::rtl::math::setNan() here + cause it misses the sign bit. */ + sal_math_Double* pDouble = reinterpret_cast< sal_math_Double* >( &mfIntercept ); + pDouble->w32_parts.msw = pDouble->w32_parts.lsw = 0xFFFFFFFF; +} + +// ---------------------------------------------------------------------------- + +XclChSerErrorBar::XclChSerErrorBar() : + mfValue( 0.0 ), + mnValueCount( 1 ), + mnBarType( EXC_CHSERERR_NONE ), + mnSourceType( EXC_CHSERERR_FIXED ), + mnLineEnd( EXC_CHSERERR_END_TSHAPE ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChSeries::XclChSeries() : + mnCategType( EXC_CHSERIES_NUMERIC ), + mnValueType( EXC_CHSERIES_NUMERIC ), + mnBubbleType( EXC_CHSERIES_NUMERIC ), + mnCategCount( 0 ), + mnValueCount( 0 ), + mnBubbleCount( 0 ) +{ +} + +// Chart type groups ========================================================== + +XclChType::XclChType() : + mnOverlap( 0 ), + mnGap( 150 ), + mnRotation( 0 ), + mnPieHole( 0 ), + mnBubbleSize( 100 ), + mnBubbleType( EXC_CHSCATTER_AREA ), + mnFlags( 0 ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChChart3d::XclChChart3d() : + mnRotation( 20 ), + mnElevation( 15 ), + mnEyeDist( 30 ), + mnRelHeight( 100 ), + mnRelDepth( 100 ), + mnDepthGap( 150 ), + mnFlags( EXC_CHCHART3D_AUTOHEIGHT ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChLegend::XclChLegend() : + mnDockMode( EXC_CHLEGEND_RIGHT ), + mnSpacing( EXC_CHLEGEND_MEDIUM ), + mnFlags( EXC_CHLEGEND_DOCKED | EXC_CHLEGEND_AUTOSERIES | + EXC_CHLEGEND_AUTOPOSX | EXC_CHLEGEND_AUTOPOSY | EXC_CHLEGEND_STACKED ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChTypeGroup::XclChTypeGroup() : + mnFlags( 0 ), + mnGroupIdx( EXC_CHSERGROUP_NONE ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChProperties::XclChProperties() : + mnFlags( 0 ), + mnEmptyMode( EXC_CHPROPS_EMPTY_SKIP ) +{ +} + +// Axes ======================================================================= + +XclChLabelRange::XclChLabelRange() : + mnCross( 1 ), + mnLabelFreq( 1 ), + mnTickFreq( 1 ), + mnFlags( 0 ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChValueRange::XclChValueRange() : + mfMin( 0.0 ), + mfMax( 0.0 ), + mfMajorStep( 0.0 ), + mfMinorStep( 0.0 ), + mfCross( 0.0 ), + mnFlags( EXC_CHVALUERANGE_AUTOMIN | EXC_CHVALUERANGE_AUTOMAX | + EXC_CHVALUERANGE_AUTOMAJOR | EXC_CHVALUERANGE_AUTOMINOR | EXC_CHVALUERANGE_AUTOCROSS | EXC_CHVALUERANGE_BIT8 ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChTick::XclChTick() : + maTextColor( COL_BLACK ), + mnMajor( EXC_CHTICK_INSIDE | EXC_CHTICK_OUTSIDE ), + mnMinor( 0 ), + mnLabelPos( EXC_CHTICK_NEXT ), + mnBackMode( EXC_CHTICK_TRANSPARENT ), + mnFlags( EXC_CHTICK_AUTOCOLOR | EXC_CHTICK_AUTOROT ), + mnRotation( EXC_ROT_NONE ) +{ +} + +// ---------------------------------------------------------------------------- + +XclChAxis::XclChAxis() : + mnType( EXC_CHAXIS_NONE ) +{ +} + +sal_Int32 XclChAxis::GetApiAxisDimension() const +{ + sal_Int32 nApiAxisDim = EXC_CHART_AXIS_NONE; + switch( mnType ) + { + case EXC_CHAXIS_X: nApiAxisDim = EXC_CHART_AXIS_X; break; + case EXC_CHAXIS_Y: nApiAxisDim = EXC_CHART_AXIS_Y; break; + case EXC_CHAXIS_Z: nApiAxisDim = EXC_CHART_AXIS_Z; break; + } + return nApiAxisDim; +} + +// ---------------------------------------------------------------------------- + +XclChAxesSet::XclChAxesSet() : + mnAxesSetId( EXC_CHAXESSET_PRIMARY ) +{ +} + +sal_Int32 XclChAxesSet::GetApiAxesSetIndex() const +{ + sal_Int32 nApiAxesSetIdx = EXC_CHART_AXESSET_NONE; + switch( mnAxesSetId ) + { + case EXC_CHAXESSET_PRIMARY: nApiAxesSetIdx = EXC_CHART_AXESSET_PRIMARY; break; + case EXC_CHAXESSET_SECONDARY: nApiAxesSetIdx = EXC_CHART_AXESSET_SECONDARY; break; + } + return nApiAxesSetIdx; +} + +// Static helper functions ==================================================== + +sal_uInt16 XclChartHelper::GetSeriesLineAutoColorIdx( sal_uInt16 nFormatIdx ) +{ + static const sal_uInt16 spnLineColors[] = + { + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 63 + }; + return spnLineColors[ nFormatIdx % STATIC_TABLE_SIZE( spnLineColors ) ]; +} + +sal_uInt16 XclChartHelper::GetSeriesFillAutoColorIdx( sal_uInt16 nFormatIdx ) +{ + static const sal_uInt16 spnFillColors[] = + { + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23 + }; + return spnFillColors[ nFormatIdx % STATIC_TABLE_SIZE( spnFillColors ) ]; +} + +sal_uInt8 XclChartHelper::GetSeriesFillAutoTransp( sal_uInt16 nFormatIdx ) +{ + static const sal_uInt8 spnTrans[] = { 0x00, 0x40, 0x20, 0x60, 0x70 }; + return spnTrans[ (nFormatIdx / 56) % STATIC_TABLE_SIZE( spnTrans ) ]; +} + +sal_uInt16 XclChartHelper::GetAutoMarkerType( sal_uInt16 nFormatIdx ) +{ + static const sal_uInt16 spnSymbols[] = { + EXC_CHMARKERFORMAT_DIAMOND, EXC_CHMARKERFORMAT_SQUARE, EXC_CHMARKERFORMAT_TRIANGLE, + EXC_CHMARKERFORMAT_CROSS, EXC_CHMARKERFORMAT_STAR, EXC_CHMARKERFORMAT_CIRCLE, + EXC_CHMARKERFORMAT_PLUS, EXC_CHMARKERFORMAT_DOWJ, EXC_CHMARKERFORMAT_STDDEV }; + return spnSymbols[ nFormatIdx % STATIC_TABLE_SIZE( spnSymbols ) ]; +} + +bool XclChartHelper::HasMarkerFillColor( sal_uInt16 nMarkerType ) +{ + static const bool spbFilled[] = { + false, true, true, true, false, false, false, false, true, false }; + return (nMarkerType < STATIC_TABLE_SIZE( spbFilled )) && spbFilled[ nMarkerType ]; +} + +OUString XclChartHelper::GetErrorBarValuesRole( sal_uInt8 nBarType ) +{ + switch( nBarType ) + { + case EXC_CHSERERR_XPLUS: return EXC_CHPROP_ROLE_ERRORBARS_POSX; + case EXC_CHSERERR_XMINUS: return EXC_CHPROP_ROLE_ERRORBARS_NEGX; + case EXC_CHSERERR_YPLUS: return EXC_CHPROP_ROLE_ERRORBARS_POSY; + case EXC_CHSERERR_YMINUS: return EXC_CHPROP_ROLE_ERRORBARS_NEGY; + default: DBG_ERRORFILE( "XclChartHelper::GetErrorBarValuesRole - unknown bar type" ); + } + return OUString(); +} + +// Chart formatting info provider ============================================= + +namespace { + +static const XclChFormatInfo spFmtInfos[] = +{ + // object type property mode auto line color auto line weight auto pattern color missing frame type create delete isframe + { EXC_CHOBJTYPE_BACKGROUND, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, true, true, true }, + { EXC_CHOBJTYPE_PLOTFRAME, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, true, true, true }, + { EXC_CHOBJTYPE_WALL3D, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, true, false, true }, + { EXC_CHOBJTYPE_FLOOR3D, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, 23, EXC_CHFRAMETYPE_AUTO, true, false, true }, + { EXC_CHOBJTYPE_TEXT, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, true, true }, + { EXC_CHOBJTYPE_LEGEND, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, true, true, true }, + { EXC_CHOBJTYPE_LINEARSERIES, EXC_CHPROPMODE_LINEARSERIES, 0xFFFF, EXC_CHLINEFORMAT_SINGLE, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, false, false, false }, + { EXC_CHOBJTYPE_FILLEDSERIES, EXC_CHPROPMODE_FILLEDSERIES, EXC_COLOR_CHBORDERAUTO, EXC_CHLINEFORMAT_SINGLE, 0xFFFF, EXC_CHFRAMETYPE_AUTO, false, false, true }, + { EXC_CHOBJTYPE_AXISLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, false, false, false }, + { EXC_CHOBJTYPE_GRIDLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, true, false }, + { EXC_CHOBJTYPE_TRENDLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_DOUBLE, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false }, + { EXC_CHOBJTYPE_ERRORBAR, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_SINGLE, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false }, + { EXC_CHOBJTYPE_CONNECTLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false }, + { EXC_CHOBJTYPE_HILOLINE, EXC_CHPROPMODE_LINEARSERIES, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false }, + { EXC_CHOBJTYPE_WHITEDROPBAR, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, true }, + { EXC_CHOBJTYPE_BLACKDROPBAR, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWTEXT, EXC_CHFRAMETYPE_INVISIBLE, false, false, true } +}; + +} + +// ---------------------------------------------------------------------------- + +XclChFormatInfoProvider::XclChFormatInfoProvider() +{ + const XclChFormatInfo* pEnd = STATIC_TABLE_END( spFmtInfos ); + for( const XclChFormatInfo* pIt = spFmtInfos; pIt != pEnd; ++pIt ) + maInfoMap[ pIt->meObjType ] = pIt; +} + +const XclChFormatInfo& XclChFormatInfoProvider::GetFormatInfo( XclChObjectType eObjType ) const +{ + XclFmtInfoMap::const_iterator aIt = maInfoMap.find( eObjType ); + DBG_ASSERT( aIt != maInfoMap.end(), "XclChFormatInfoProvider::GetFormatInfo - unknown object type" ); + return (aIt == maInfoMap.end()) ? *spFmtInfos : *aIt->second; +} + +// Chart type info provider =================================================== + +namespace { + +// chart type service names +const sal_Char SERVICE_CHART2_AREA[] = "com.sun.star.chart2.AreaChartType"; +const sal_Char SERVICE_CHART2_CANDLE[] = "com.sun.star.chart2.CandleStickChartType"; +const sal_Char SERVICE_CHART2_COLUMN[] = "com.sun.star.chart2.ColumnChartType"; +const sal_Char SERVICE_CHART2_LINE[] = "com.sun.star.chart2.LineChartType"; +const sal_Char SERVICE_CHART2_NET[] = "com.sun.star.chart2.NetChartType"; +const sal_Char SERVICE_CHART2_FILLEDNET[] = "com.sun.star.chart2.FilledNetChartType"; +const sal_Char SERVICE_CHART2_PIE[] = "com.sun.star.chart2.PieChartType"; +const sal_Char SERVICE_CHART2_SCATTER[] = "com.sun.star.chart2.ScatterChartType"; +const sal_Char SERVICE_CHART2_BUBBLE[] = "com.sun.star.chart2.BubbleChartType"; +const sal_Char SERVICE_CHART2_SURFACE[] = "com.sun.star.chart2.ColumnChartType"; // Todo + +namespace csscd = cssc::DataLabelPlacement; + +static const XclChTypeInfo spTypeInfos[] = +{ + // chart type chart type category record id service varied point color def label pos comb2d 3d polar area2d area3d 1stvis xcateg swap stack revers betw + { EXC_CHTYPEID_BAR, EXC_CHTYPECATEG_BAR, EXC_ID_CHBAR, SERVICE_CHART2_COLUMN, EXC_CHVARPOINT_SINGLE, csscd::OUTSIDE, true, true, false, true, true, false, true, false, true, false, true }, + { EXC_CHTYPEID_HORBAR, EXC_CHTYPECATEG_BAR, EXC_ID_CHBAR, SERVICE_CHART2_COLUMN, EXC_CHVARPOINT_SINGLE, csscd::OUTSIDE, false, true, false, true, true, false, true, true, true, false, true }, + { EXC_CHTYPEID_LINE, EXC_CHTYPECATEG_LINE, EXC_ID_CHLINE, SERVICE_CHART2_LINE, EXC_CHVARPOINT_SINGLE, csscd::RIGHT, true, true, false, false, true, false, true, false, true, false, false }, + { EXC_CHTYPEID_AREA, EXC_CHTYPECATEG_LINE, EXC_ID_CHAREA, SERVICE_CHART2_AREA, EXC_CHVARPOINT_NONE, csscd::CENTER, true, true, false, true, true, false, true, false, true, true, false }, + { EXC_CHTYPEID_STOCK, EXC_CHTYPECATEG_LINE, EXC_ID_CHLINE, SERVICE_CHART2_CANDLE, EXC_CHVARPOINT_NONE, csscd::RIGHT, true, false, false, false, false, false, true, false, true, false, false }, + { EXC_CHTYPEID_RADARLINE, EXC_CHTYPECATEG_RADAR, EXC_ID_CHRADARLINE, SERVICE_CHART2_NET, EXC_CHVARPOINT_SINGLE, csscd::TOP, false, false, true, false, true, false, true, false, false, false, false }, + { EXC_CHTYPEID_RADARAREA, EXC_CHTYPECATEG_RADAR, EXC_ID_CHRADARAREA, SERVICE_CHART2_FILLEDNET, EXC_CHVARPOINT_NONE, csscd::TOP, false, false, true, true, true, false, true, false, false, true, false }, + { EXC_CHTYPEID_PIE, EXC_CHTYPECATEG_PIE, EXC_ID_CHPIE, SERVICE_CHART2_PIE, EXC_CHVARPOINT_MULTI, csscd::AVOID_OVERLAP, false, true, true, true, true, true, true, false, false, false, false }, + { EXC_CHTYPEID_DONUT, EXC_CHTYPECATEG_PIE, EXC_ID_CHPIE, SERVICE_CHART2_PIE, EXC_CHVARPOINT_MULTI, csscd::AVOID_OVERLAP, false, true, true, true, true, false, true, false, false, true, false }, + { EXC_CHTYPEID_PIEEXT, EXC_CHTYPECATEG_PIE, EXC_ID_CHPIEEXT, SERVICE_CHART2_PIE, EXC_CHVARPOINT_MULTI, csscd::AVOID_OVERLAP, false, false, true, true, true, true, true, false, false, false, false }, + { EXC_CHTYPEID_SCATTER, EXC_CHTYPECATEG_SCATTER, EXC_ID_CHSCATTER, SERVICE_CHART2_SCATTER, EXC_CHVARPOINT_SINGLE, csscd::RIGHT, true, false, false, false, true, false, false, false, false, false, false }, + { EXC_CHTYPEID_BUBBLES, EXC_CHTYPECATEG_SCATTER, EXC_ID_CHSCATTER, SERVICE_CHART2_BUBBLE, EXC_CHVARPOINT_SINGLE, csscd::RIGHT, false, false, false, true, true, false, false, false, false, false, false }, + { EXC_CHTYPEID_SURFACE, EXC_CHTYPECATEG_SURFACE, EXC_ID_CHSURFACE, SERVICE_CHART2_SURFACE, EXC_CHVARPOINT_NONE, csscd::RIGHT, false, true, false, true, true, false, true, false, false, false, false }, + { EXC_CHTYPEID_UNKNOWN, EXC_CHTYPECATEG_BAR, EXC_ID_CHBAR, SERVICE_CHART2_COLUMN, EXC_CHVARPOINT_SINGLE, csscd::OUTSIDE, true, true, false, true, true, false, true, false, true, false, true } +}; + +} // namespace + +XclChExtTypeInfo::XclChExtTypeInfo( const XclChTypeInfo& rTypeInfo ) : + XclChTypeInfo( rTypeInfo ), + mb3dChart( false ), + mbSpline( false ) +{ +} + +void XclChExtTypeInfo::Set( const XclChTypeInfo& rTypeInfo, bool b3dChart, bool bSpline ) +{ + static_cast< XclChTypeInfo& >( *this ) = rTypeInfo; + mb3dChart = mbSupports3d && b3dChart; + mbSpline = bSpline; +} + +// ---------------------------------------------------------------------------- + +XclChTypeInfoProvider::XclChTypeInfoProvider() +{ + const XclChTypeInfo* pEnd = STATIC_TABLE_END( spTypeInfos ); + for( const XclChTypeInfo* pIt = spTypeInfos; pIt != pEnd; ++pIt ) + maInfoMap[ pIt->meTypeId ] = pIt; +} + +const XclChTypeInfo& XclChTypeInfoProvider::GetTypeInfo( XclChTypeId eTypeId ) const +{ + XclChTypeInfoMap::const_iterator aIt = maInfoMap.find( eTypeId ); + DBG_ASSERT( aIt != maInfoMap.end(), "XclChTypeInfoProvider::GetTypeInfo - unknown chart type" ); + return (aIt == maInfoMap.end()) ? *maInfoMap.rbegin()->second : *aIt->second; +} + +const XclChTypeInfo& XclChTypeInfoProvider::GetTypeInfoFromRecId( sal_uInt16 nRecId ) const +{ + const XclChTypeInfo* pEnd = STATIC_TABLE_END( spTypeInfos ); + for( const XclChTypeInfo* pIt = spTypeInfos; pIt != pEnd; ++pIt ) + if( pIt->mnRecId == nRecId ) + return *pIt; + DBG_ERRORFILE( "XclChTypeInfoProvider::GetTypeInfoFromRecId - unknown record id" ); + return GetTypeInfo( EXC_CHTYPEID_UNKNOWN ); +} + +const XclChTypeInfo& XclChTypeInfoProvider::GetTypeInfoFromService( const OUString& rServiceName ) const +{ + const XclChTypeInfo* pEnd = STATIC_TABLE_END( spTypeInfos ); + for( const XclChTypeInfo* pIt = spTypeInfos; pIt != pEnd; ++pIt ) + if( rServiceName.equalsAscii( pIt->mpcServiceName ) ) + return *pIt; + DBG_ERRORFILE( "XclChTypeInfoProvider::GetTypeInfoFromService - unknown service name" ); + return GetTypeInfo( EXC_CHTYPEID_UNKNOWN ); +} + +// Property helpers =========================================================== + +XclChObjectTable::XclChObjectTable( Reference< XMultiServiceFactory > xFactory, + const OUString& rServiceName, const OUString& rObjNameBase ) : + mxFactory( xFactory ), + maServiceName( rServiceName ), + maObjNameBase( rObjNameBase ), + mnIndex( 0 ) +{ +} + +Any XclChObjectTable::GetObject( const OUString& rObjName ) +{ + // get object table + if( !mxContainer.is() ) + mxContainer.set( ScfApiHelper::CreateInstance( mxFactory, maServiceName ), UNO_QUERY ); + DBG_ASSERT( mxContainer.is(), "XclChObjectTable::GetObject - container not found" ); + + Any aObj; + if( mxContainer.is() ) + { + // get object from container + try + { + aObj = mxContainer->getByName( rObjName ); + } + catch( Exception& ) + { + DBG_ERRORFILE( "XclChObjectTable::GetObject - object not found" ); + } + } + return aObj; +} + +OUString XclChObjectTable::InsertObject( const Any& rObj ) +{ + + // create object table + if( !mxContainer.is() ) + mxContainer.set( ScfApiHelper::CreateInstance( mxFactory, maServiceName ), UNO_QUERY ); + DBG_ASSERT( mxContainer.is(), "XclChObjectTable::InsertObject - container not found" ); + + OUString aObjName; + if( mxContainer.is() ) + { + // create new unused identifier + do + { + aObjName = maObjNameBase + OUString::valueOf( ++mnIndex ); + } + while( mxContainer->hasByName( aObjName ) ); + + // insert object + try + { + mxContainer->insertByName( aObjName, rObj ); + } + catch( Exception& ) + { + DBG_ERRORFILE( "XclChObjectTable::InsertObject - cannot insert object" ); + aObjName = OUString(); + } + } + return aObjName; +} + +// Property names ------------------------------------------------------------- + +namespace { + +/** Property names for line style in common objects. */ +const sal_Char* const sppcLineNamesCommon[] = + { "LineStyle", "LineWidth", "LineColor", "LineTransparence", "LineDashName", 0 }; +/** Property names for line style in linear series objects. */ +const sal_Char* const sppcLineNamesLinear[] = + { "LineStyle", "LineWidth", "Color", "Transparency", "LineDashName", 0 }; +/** Property names for line style in filled series objects. */ +const sal_Char* const sppcLineNamesFilled[] = + { "BorderStyle", "BorderWidth", "BorderColor", "BorderTransparency", "BorderDashName", 0 }; + +/** Property names for solid area style in common objects. */ +const sal_Char* const sppcAreaNamesCommon[] = { "FillStyle", "FillColor", "FillTransparence", 0 }; +/** Property names for solid area style in filled series objects. */ +const sal_Char* const sppcAreaNamesFilled[] = { "FillStyle", "Color", "Transparency", 0 }; +/** Property names for gradient area style in common objects. */ +const sal_Char* const sppcGradNamesCommon[] = { "FillStyle", "FillGradientName", 0 }; +/** Property names for gradient area style in filled series objects. */ +const sal_Char* const sppcGradNamesFilled[] = { "FillStyle", "GradientName", 0 }; +/** Property names for hatch area style in common objects. */ +const sal_Char* const sppcHatchNamesCommon[] = { "FillStyle", "FillHatchName", "FillColor", "FillBackground", 0 }; +/** Property names for hatch area style in filled series objects. */ +const sal_Char* const sppcHatchNamesFilled[] = { "FillStyle", "HatchName", "Color", "FillBackground", 0 }; +/** Property names for bitmap area style. */ +const sal_Char* const sppcBitmapNames[] = { "FillStyle", "FillBitmapName", "FillBitmapMode", 0 }; + +} // namespace + +// ---------------------------------------------------------------------------- + +XclChPropSetHelper::XclChPropSetHelper() : + maLineHlpCommon( sppcLineNamesCommon ), + maLineHlpLinear( sppcLineNamesLinear ), + maLineHlpFilled( sppcLineNamesFilled ), + maAreaHlpCommon( sppcAreaNamesCommon ), + maAreaHlpFilled( sppcAreaNamesFilled ), + maGradHlpCommon( sppcGradNamesCommon ), + maGradHlpFilled( sppcGradNamesFilled ), + maHatchHlpCommon( sppcHatchNamesCommon ), + maHatchHlpFilled( sppcHatchNamesFilled ), + maBitmapHlp( sppcBitmapNames ) +{ +} + +// read properties ------------------------------------------------------------ + +void XclChPropSetHelper::ReadLineProperties( + XclChLineFormat& rLineFmt, XclChObjectTable& rDashTable, + const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) +{ + namespace cssd = ::com::sun::star::drawing; + + // read properties from property set + cssd::LineStyle eApiStyle = cssd::LineStyle_NONE; + sal_Int32 nApiWidth = 0; + sal_Int16 nApiTrans = 0; + Any aDashNameAny; + + ScfPropSetHelper& rLineHlp = GetLineHelper( ePropMode ); + rLineHlp.ReadFromPropertySet( rPropSet ); + rLineHlp >> eApiStyle >> nApiWidth >> rLineFmt.maColor >> nApiTrans >> aDashNameAny; + + // clear automatic flag + ::set_flag( rLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, false ); + + // line width + if( nApiWidth <= 0 ) rLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR; + else if( nApiWidth <= 35 ) rLineFmt.mnWeight = EXC_CHLINEFORMAT_SINGLE; + else if( nApiWidth <= 70 ) rLineFmt.mnWeight = EXC_CHLINEFORMAT_DOUBLE; + else rLineFmt.mnWeight = EXC_CHLINEFORMAT_TRIPLE; + + // line style + switch( eApiStyle ) + { + case cssd::LineStyle_NONE: + rLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE; + break; + case cssd::LineStyle_SOLID: + { + if( nApiTrans < 13 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID; + else if( nApiTrans < 38 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_DARKTRANS; + else if( nApiTrans < 63 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_MEDTRANS; + else if( nApiTrans < 100 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_LIGHTTRANS; + else rLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE; + } + break; + case cssd::LineStyle_DASH: + { + rLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID; + OUString aDashName; + cssd::LineDash aApiDash; + if( (aDashNameAny >>= aDashName) && (rDashTable.GetObject( aDashName ) >>= aApiDash) ) + { + // reorder dashes that are shorter than dots + if( (aApiDash.Dashes == 0) || (aApiDash.DashLen < aApiDash.DotLen) ) + { + ::std::swap( aApiDash.Dashes, aApiDash.Dots ); + ::std::swap( aApiDash.DashLen, aApiDash.DotLen ); + } + // ignore dots that are nearly equal to dashes + if( aApiDash.DotLen * 3 > aApiDash.DashLen * 2 ) + aApiDash.Dots = 0; + + // convert line dash to predefined Excel dash types + if( (aApiDash.Dashes == 1) && (aApiDash.Dots >= 1) ) + // one dash and one or more dots + rLineFmt.mnPattern = (aApiDash.Dots == 1) ? + EXC_CHLINEFORMAT_DASHDOT : EXC_CHLINEFORMAT_DASHDOTDOT; + else if( aApiDash.Dashes >= 1 ) + // one or more dashes and no dots (also: dash-dash-dot) + rLineFmt.mnPattern = (aApiDash.DashLen < 250) ? + EXC_CHLINEFORMAT_DOT : EXC_CHLINEFORMAT_DASH; + } + } + break; + default: + DBG_ERRORFILE( "XclChPropSetHelper::ReadLineProperties - unknown line style" ); + rLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID; + } +} + +bool XclChPropSetHelper::ReadAreaProperties( XclChAreaFormat& rAreaFmt, + const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) +{ + namespace cssd = ::com::sun::star::drawing; + + // read properties from property set + cssd::FillStyle eApiStyle = cssd::FillStyle_NONE; + sal_Int16 nTransparency = 0; + + ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode ); + rAreaHlp.ReadFromPropertySet( rPropSet ); + rAreaHlp >> eApiStyle >> rAreaFmt.maPattColor >> nTransparency; + + // clear automatic flag + ::set_flag( rAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, false ); + + // set fill style transparent or solid (set solid for anything but transparent) + rAreaFmt.mnPattern = (eApiStyle == cssd::FillStyle_NONE) ? EXC_PATT_NONE : EXC_PATT_SOLID; + + // return true to indicate complex fill (gradient, bitmap, solid transparency) + return (eApiStyle != cssd::FillStyle_NONE) && ((eApiStyle != cssd::FillStyle_SOLID) || (nTransparency > 0)); +} + +void XclChPropSetHelper::ReadEscherProperties( + XclChEscherFormat& rEscherFmt, XclChPicFormat& rPicFmt, + XclChObjectTable& rGradientTable, XclChObjectTable& rHatchTable, XclChObjectTable& rBitmapTable, + const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) +{ + namespace cssd = ::com::sun::star::drawing; + namespace cssa = ::com::sun::star::awt; + + // read style and transparency properties from property set + cssd::FillStyle eApiStyle = cssd::FillStyle_NONE; + Color aColor; + sal_Int16 nTransparency = 0; + + ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode ); + rAreaHlp.ReadFromPropertySet( rPropSet ); + rAreaHlp >> eApiStyle >> aColor >> nTransparency; + + switch( eApiStyle ) + { + case cssd::FillStyle_SOLID: + { + DBG_ASSERT( nTransparency > 0, "XclChPropSetHelper::ReadEscherProperties - unexpected solid area without transparency" ); + if( (0 < nTransparency) && (nTransparency <= 100) ) + { + // convert to Escher properties + sal_uInt32 nEscherColor = 0x02000000; + ::insert_value( nEscherColor, aColor.GetBlue(), 16, 8 ); + ::insert_value( nEscherColor, aColor.GetGreen(), 8, 8 ); + ::insert_value( nEscherColor, aColor.GetRed(), 0, 8 ); + sal_uInt32 nEscherOpacity = static_cast< sal_uInt32 >( (100 - nTransparency) * 655.36 ); + rEscherFmt.mxEscherSet.reset( new EscherPropertyContainer ); + rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillType, ESCHER_FillSolid ); + rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillColor, nEscherColor ); + rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillOpacity, nEscherOpacity ); + rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillBackColor, 0x02FFFFFF ); + rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillBackOpacity, 0x00010000 ); + rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fNoFillHitTest, 0x001F001C ); + } + } + break; + case cssd::FillStyle_GRADIENT: + { + // extract gradient from global gradient table + OUString aGradientName; + ScfPropSetHelper& rGradHlp = GetGradientHelper( ePropMode ); + rGradHlp.ReadFromPropertySet( rPropSet ); + rGradHlp >> eApiStyle >> aGradientName; + cssa::Gradient aGradient; + if( rGradientTable.GetObject( aGradientName ) >>= aGradient ) + { + // convert to Escher properties + rEscherFmt.mxEscherSet.reset( new EscherPropertyContainer ); + rEscherFmt.mxEscherSet->CreateGradientProperties( aGradient ); + } + } + break; + case cssd::FillStyle_HATCH: + { + // extract hatch from global hatch table + OUString aHatchName; + bool bFillBackground; + ScfPropSetHelper& rHatchHlp = GetHatchHelper( ePropMode ); + rHatchHlp.ReadFromPropertySet( rPropSet ); + rHatchHlp >> eApiStyle >> aHatchName >> aColor >> bFillBackground; + cssd::Hatch aHatch; + if( rHatchTable.GetObject( aHatchName ) >>= aHatch ) + { + // convert to Escher properties + rEscherFmt.mxEscherSet.reset( new EscherPropertyContainer ); + rEscherFmt.mxEscherSet->CreateEmbeddedHatchProperties( aHatch, aColor, bFillBackground ); + rPicFmt.mnBmpMode = EXC_CHPICFORMAT_STACK; + } + } + break; + case cssd::FillStyle_BITMAP: + { + // extract bitmap URL from global bitmap table + OUString aBitmapName; + cssd::BitmapMode eApiBmpMode; + maBitmapHlp.ReadFromPropertySet( rPropSet ); + maBitmapHlp >> eApiStyle >> aBitmapName >> eApiBmpMode; + OUString aBitmapUrl; + if( rBitmapTable.GetObject( aBitmapName ) >>= aBitmapUrl ) + { + // convert to Escher properties + rEscherFmt.mxEscherSet.reset( new EscherPropertyContainer ); + rEscherFmt.mxEscherSet->CreateEmbeddedBitmapProperties( aBitmapUrl, eApiBmpMode ); + rPicFmt.mnBmpMode = (eApiBmpMode == cssd::BitmapMode_REPEAT) ? + EXC_CHPICFORMAT_STACK : EXC_CHPICFORMAT_STRETCH; + } + } + break; + default: + DBG_ERRORFILE( "XclChPropSetHelper::ReadEscherProperties - unknown fill style" ); + } +} + +void XclChPropSetHelper::ReadMarkerProperties( + XclChMarkerFormat& rMarkerFmt, const ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) +{ + namespace cssc = ::com::sun::star::chart2; + namespace cssa = ::com::sun::star::awt; + cssc::Symbol aApiSymbol; + if( rPropSet.GetProperty( aApiSymbol, EXC_CHPROP_SYMBOL ) ) + { + // clear automatic flag + ::set_flag( rMarkerFmt.mnFlags, EXC_CHMARKERFORMAT_AUTO, false ); + + // symbol style + switch( aApiSymbol.Style ) + { + case cssc::SymbolStyle_NONE: + rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_NOSYMBOL; + break; + case cssc::SymbolStyle_STANDARD: + switch( aApiSymbol.StandardSymbol ) + { + case 0: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_SQUARE; break; // square + case 1: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_DIAMOND; break; // diamond + case 2: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STDDEV; break; // arrow down + case 3: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_TRIANGLE; break; // arrow up + case 4: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_CIRCLE; break; // arrow right + case 5: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_PLUS; break; // arrow left + case 6: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_CROSS; break; // bow tie + case 7: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STAR; break; // sand glass + default: rMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx ); + } + break; + default: + rMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx ); + } + bool bHasFillColor = XclChartHelper::HasMarkerFillColor( rMarkerFmt.mnMarkerType ); + ::set_flag( rMarkerFmt.mnFlags, EXC_CHMARKERFORMAT_NOFILL, !bHasFillColor ); + + // symbol size + sal_Int32 nApiSize = (aApiSymbol.Size.Width + aApiSymbol.Size.Height + 1) / 2; + rMarkerFmt.mnMarkerSize = XclTools::GetTwipsFromHmm( nApiSize ); + + // symbol colors + rMarkerFmt.maLineColor = ScfApiHelper::ConvertFromApiColor( aApiSymbol.BorderColor ); + rMarkerFmt.maFillColor = ScfApiHelper::ConvertFromApiColor( aApiSymbol.FillColor ); + } +} + +sal_uInt16 XclChPropSetHelper::ReadRotationProperties( const ScfPropertySet& rPropSet, bool bSupportsStacked ) +{ + // chart2 handles rotation as double in the range [0,360) + double fAngle = 0.0; + rPropSet.GetProperty( fAngle, EXC_CHPROP_TEXTROTATION ); + bool bStacked = bSupportsStacked && rPropSet.GetBoolProperty( EXC_CHPROP_STACKCHARACTERS ); + return bStacked ? EXC_ROT_STACKED : + XclTools::GetXclRotation( static_cast< sal_Int32 >( fAngle * 100.0 + 0.5 ) ); +} + +// write properties ----------------------------------------------------------- + +void XclChPropSetHelper::WriteLineProperties( + ScfPropertySet& rPropSet, XclChObjectTable& rDashTable, + const XclChLineFormat& rLineFmt, XclChPropertyMode ePropMode ) +{ + namespace cssd = ::com::sun::star::drawing; + + // line width + sal_Int32 nApiWidth = 0; // 0 is the width of a hair line + switch( rLineFmt.mnWeight ) + { + case EXC_CHLINEFORMAT_SINGLE: nApiWidth = 35; break; + case EXC_CHLINEFORMAT_DOUBLE: nApiWidth = 70; break; + case EXC_CHLINEFORMAT_TRIPLE: nApiWidth = 105; break; + } + + // line style + cssd::LineStyle eApiStyle = cssd::LineStyle_NONE; + sal_Int16 nApiTrans = 0; + sal_Int32 nDotLen = ::std::min< sal_Int32 >( rLineFmt.mnWeight + 105, 210 ); + cssd::LineDash aApiDash( cssd::DashStyle_RECT, 0, nDotLen, 0, 4 * nDotLen, nDotLen ); + + switch( rLineFmt.mnPattern ) + { + case EXC_CHLINEFORMAT_SOLID: + eApiStyle = cssd::LineStyle_SOLID; + break; + case EXC_CHLINEFORMAT_DARKTRANS: + eApiStyle = cssd::LineStyle_SOLID; nApiTrans = 25; + break; + case EXC_CHLINEFORMAT_MEDTRANS: + eApiStyle = cssd::LineStyle_SOLID; nApiTrans = 50; + break; + case EXC_CHLINEFORMAT_LIGHTTRANS: + eApiStyle = cssd::LineStyle_SOLID; nApiTrans = 75; + break; + case EXC_CHLINEFORMAT_DASH: + eApiStyle = cssd::LineStyle_DASH; aApiDash.Dashes = 1; + break; + case EXC_CHLINEFORMAT_DOT: + eApiStyle = cssd::LineStyle_DASH; aApiDash.Dots = 1; + break; + case EXC_CHLINEFORMAT_DASHDOT: + eApiStyle = cssd::LineStyle_DASH; aApiDash.Dashes = aApiDash.Dots = 1; + break; + case EXC_CHLINEFORMAT_DASHDOTDOT: + eApiStyle = cssd::LineStyle_DASH; aApiDash.Dashes = 1; aApiDash.Dots = 2; + break; + } + + // line color + sal_Int32 nApiColor = ScfApiHelper::ConvertToApiColor( rLineFmt.maColor ); + + // try to insert the dash style and receive its name + Any aDashNameAny; + if( eApiStyle == cssd::LineStyle_DASH ) + { + OUString aDashName = rDashTable.InsertObject( ::com::sun::star::uno::makeAny( aApiDash ) ); + if( aDashName.getLength() ) + aDashNameAny <<= aDashName; + } + + // write the properties + ScfPropSetHelper& rLineHlp = GetLineHelper( ePropMode ); + rLineHlp.InitializeWrite(); + rLineHlp << eApiStyle << nApiWidth << nApiColor << nApiTrans << aDashNameAny; + rLineHlp.WriteToPropertySet( rPropSet ); +} + +void XclChPropSetHelper::WriteAreaProperties( ScfPropertySet& rPropSet, + const XclChAreaFormat& rAreaFmt, XclChPropertyMode ePropMode ) +{ + namespace cssd = ::com::sun::star::drawing; + cssd::FillStyle eFillStyle = cssd::FillStyle_NONE; + Color aColor; + sal_Int16 nTransparency = 0; + + // fill color + if( rAreaFmt.mnPattern != EXC_PATT_NONE ) + { + eFillStyle = cssd::FillStyle_SOLID; + aColor = XclTools::GetPatternColor( rAreaFmt.maPattColor, rAreaFmt.maBackColor, rAreaFmt.mnPattern ); + } + + // write the properties + ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode ); + rAreaHlp.InitializeWrite(); + rAreaHlp << eFillStyle << aColor << nTransparency; + rAreaHlp.WriteToPropertySet( rPropSet ); +} + +void XclChPropSetHelper::WriteEscherProperties( ScfPropertySet& rPropSet, + XclChObjectTable& rGradientTable, XclChObjectTable& /*rHatchTable*/, XclChObjectTable& rBitmapTable, + const XclChEscherFormat& rEscherFmt, const XclChPicFormat& rPicFmt, + XclChPropertyMode ePropMode ) +{ + if( rEscherFmt.mxItemSet.is() ) + { + if( const XFillStyleItem* pStyleItem = static_cast< const XFillStyleItem* >( rEscherFmt.mxItemSet->GetItem( XATTR_FILLSTYLE, FALSE ) ) ) + { + switch( pStyleItem->GetValue() ) + { + case XFILL_SOLID: + // #i84812# Excel 2007 writes Escher properties for solid fill + if( const XFillColorItem* pColorItem = static_cast< const XFillColorItem* >( rEscherFmt.mxItemSet->GetItem( XATTR_FILLCOLOR, FALSE ) ) ) + { + namespace cssd = ::com::sun::star::drawing; + // get solid transparence too + const XFillTransparenceItem* pTranspItem = static_cast< const XFillTransparenceItem* >( rEscherFmt.mxItemSet->GetItem( XATTR_FILLTRANSPARENCE, FALSE ) ); + sal_uInt16 nTransp = pTranspItem ? pTranspItem->GetValue() : 0; + ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode ); + rAreaHlp.InitializeWrite(); + rAreaHlp << cssd::FillStyle_SOLID << pColorItem->GetColorValue() << static_cast< sal_Int16 >( nTransp ); + rAreaHlp.WriteToPropertySet( rPropSet ); + } + break; + case XFILL_GRADIENT: + if( const XFillGradientItem* pGradItem = static_cast< const XFillGradientItem* >( rEscherFmt.mxItemSet->GetItem( XATTR_FILLGRADIENT, FALSE ) ) ) + { + Any aGradientAny; + if( pGradItem->QueryValue( aGradientAny, MID_FILLGRADIENT ) ) + { + OUString aGradName = rGradientTable.InsertObject( aGradientAny ); + if( aGradName.getLength() ) + { + namespace cssd = ::com::sun::star::drawing; + ScfPropSetHelper& rGradHlp = GetGradientHelper( ePropMode ); + rGradHlp.InitializeWrite(); + rGradHlp << cssd::FillStyle_GRADIENT << aGradName; + rGradHlp.WriteToPropertySet( rPropSet ); + } + } + } + break; + case XFILL_BITMAP: + if( const XFillBitmapItem* pBmpItem = static_cast< const XFillBitmapItem* >( rEscherFmt.mxItemSet->GetItem( XATTR_FILLBITMAP, FALSE ) ) ) + { + Any aBitmapAny; + if( pBmpItem->QueryValue( aBitmapAny, MID_GRAFURL ) ) + { + OUString aBmpName = rBitmapTable.InsertObject( aBitmapAny ); + if( aBmpName.getLength() ) + { + namespace cssd = ::com::sun::star::drawing; + cssd::BitmapMode eApiBmpMode = (rPicFmt.mnBmpMode == EXC_CHPICFORMAT_STRETCH) ? + cssd::BitmapMode_STRETCH : cssd::BitmapMode_REPEAT; + maBitmapHlp.InitializeWrite(); + maBitmapHlp << cssd::FillStyle_BITMAP << aBmpName << eApiBmpMode; + maBitmapHlp.WriteToPropertySet( rPropSet ); + } + } + } + break; + default: + DBG_ERRORFILE( "XclChPropSetHelper::WriteEscherProperties - unknown fill mode" ); + } + } + } +} + +void XclChPropSetHelper::WriteMarkerProperties( + ScfPropertySet& rPropSet, const XclChMarkerFormat& rMarkerFmt ) +{ + namespace cssc = ::com::sun::star::chart2; + namespace cssa = ::com::sun::star::awt; + + // symbol style + cssc::Symbol aApiSymbol; + aApiSymbol.Style = cssc::SymbolStyle_STANDARD; + switch( rMarkerFmt.mnMarkerType ) + { + case EXC_CHMARKERFORMAT_NOSYMBOL: aApiSymbol.Style = cssc::SymbolStyle_NONE; break; + case EXC_CHMARKERFORMAT_SQUARE: aApiSymbol.StandardSymbol = 0; break; // square + case EXC_CHMARKERFORMAT_DIAMOND: aApiSymbol.StandardSymbol = 1; break; // diamond + case EXC_CHMARKERFORMAT_TRIANGLE: aApiSymbol.StandardSymbol = 3; break; // arrow up + case EXC_CHMARKERFORMAT_CROSS: aApiSymbol.StandardSymbol = 6; break; // bow tie + case EXC_CHMARKERFORMAT_STAR: aApiSymbol.StandardSymbol = 7; break; // sand glass + case EXC_CHMARKERFORMAT_DOWJ: aApiSymbol.StandardSymbol = 4; break; // arrow right + case EXC_CHMARKERFORMAT_STDDEV: aApiSymbol.StandardSymbol = 2; break; // arrow down + case EXC_CHMARKERFORMAT_CIRCLE: aApiSymbol.StandardSymbol = 4; break; // arrow right + case EXC_CHMARKERFORMAT_PLUS: aApiSymbol.StandardSymbol = 5; break; // arrow left + } + + // symbol size + sal_Int32 nApiSize = XclTools::GetHmmFromTwips( rMarkerFmt.mnMarkerSize ); + aApiSymbol.Size = cssa::Size( nApiSize, nApiSize ); + + // symbol colors + aApiSymbol.FillColor = ScfApiHelper::ConvertToApiColor( rMarkerFmt.maFillColor ); + aApiSymbol.BorderColor = ::get_flag( rMarkerFmt.mnFlags, EXC_CHMARKERFORMAT_NOLINE ) ? + aApiSymbol.FillColor : ScfApiHelper::ConvertToApiColor( rMarkerFmt.maLineColor ); + + // set the property + rPropSet.SetProperty( EXC_CHPROP_SYMBOL, aApiSymbol ); +} + +void XclChPropSetHelper::WriteRotationProperties( + ScfPropertySet& rPropSet, sal_uInt16 nRotation, bool bSupportsStacked ) +{ + if( nRotation != EXC_CHART_AUTOROTATION ) + { + // chart2 handles rotation as double in the range [0,360) + double fAngle = XclTools::GetScRotation( nRotation, 0 ) / 100.0; + rPropSet.SetProperty( EXC_CHPROP_TEXTROTATION, fAngle ); + if( bSupportsStacked ) + rPropSet.SetProperty( EXC_CHPROP_STACKCHARACTERS, nRotation == EXC_ROT_STACKED ); + } +} + +// private -------------------------------------------------------------------- + +ScfPropSetHelper& XclChPropSetHelper::GetLineHelper( XclChPropertyMode ePropMode ) +{ + switch( ePropMode ) + { + case EXC_CHPROPMODE_COMMON: return maLineHlpCommon; + case EXC_CHPROPMODE_LINEARSERIES: return maLineHlpLinear; + case EXC_CHPROPMODE_FILLEDSERIES: return maLineHlpFilled; + default: DBG_ERRORFILE( "XclChPropSetHelper::GetLineHelper - unknown property mode" ); + } + return maLineHlpCommon; +} + +ScfPropSetHelper& XclChPropSetHelper::GetAreaHelper( XclChPropertyMode ePropMode ) +{ + switch( ePropMode ) + { + case EXC_CHPROPMODE_COMMON: return maAreaHlpCommon; + case EXC_CHPROPMODE_FILLEDSERIES: return maAreaHlpFilled; + default: DBG_ERRORFILE( "XclChPropSetHelper::GetAreaHelper - unknown property mode" ); + } + return maAreaHlpCommon; +} + +ScfPropSetHelper& XclChPropSetHelper::GetGradientHelper( XclChPropertyMode ePropMode ) +{ + switch( ePropMode ) + { + case EXC_CHPROPMODE_COMMON: return maGradHlpCommon; + case EXC_CHPROPMODE_FILLEDSERIES: return maGradHlpFilled; + default: DBG_ERRORFILE( "XclChPropSetHelper::GetGradientHelper - unknown property mode" ); + } + return maGradHlpCommon; +} + +ScfPropSetHelper& XclChPropSetHelper::GetHatchHelper( XclChPropertyMode ePropMode ) +{ + switch( ePropMode ) + { + case EXC_CHPROPMODE_COMMON: return maHatchHlpCommon; + case EXC_CHPROPMODE_FILLEDSERIES: return maHatchHlpFilled; + default: DBG_ERRORFILE( "XclChPropSetHelper::GetHatchHelper - unknown property mode" ); + } + return maHatchHlpCommon; +} + +// ============================================================================ + +namespace { + +/* The following local functions implement getting the XShape interface of all + supported title objects (chart and axes). This needs some effort due to the + design of the old Chart1 API used to access these objects. */ + +/** A code fragment that returns a shape object from the passed shape supplier + using the specified interface function. Checks a boolean property first. */ +#define EXC_FRAGMENT_GETTITLESHAPE( shape_supplier, supplier_func, property_name ) \ + ScfPropertySet aPropSet( shape_supplier ); \ + if( shape_supplier.is() && aPropSet.GetBoolProperty( CREATE_OUSTRING( #property_name ) ) ) \ + return shape_supplier->supplier_func(); \ + return Reference< XShape >(); \ + +/** Implements a function returning the drawing shape of an axis title, if + existing, using the specified API interface and its function. */ +#define EXC_DEFINEFUNC_GETAXISTITLESHAPE( func_name, interface_type, supplier_func, property_name ) \ +Reference< XShape > func_name( const Reference< cssc::XChartDocument >& rxChart1Doc ) \ +{ \ + Reference< cssc::interface_type > xAxisSupp( rxChart1Doc->getDiagram(), UNO_QUERY ); \ + EXC_FRAGMENT_GETTITLESHAPE( xAxisSupp, supplier_func, property_name ) \ +} + +/** Returns the drawing shape of the main title, if existing. */ +Reference< XShape > lclGetMainTitleShape( const Reference< cssc::XChartDocument >& rxChart1Doc ) +{ + EXC_FRAGMENT_GETTITLESHAPE( rxChart1Doc, getTitle, HasMainTitle ) +} + +EXC_DEFINEFUNC_GETAXISTITLESHAPE( lclGetXAxisTitleShape, XAxisXSupplier, getXAxisTitle, HasXAxisTitle ) +EXC_DEFINEFUNC_GETAXISTITLESHAPE( lclGetYAxisTitleShape, XAxisYSupplier, getYAxisTitle, HasYAxisTitle ) +EXC_DEFINEFUNC_GETAXISTITLESHAPE( lclGetZAxisTitleShape, XAxisZSupplier, getZAxisTitle, HasZAxisTitle ) +EXC_DEFINEFUNC_GETAXISTITLESHAPE( lclGetSecXAxisTitleShape, XSecondAxisTitleSupplier, getSecondXAxisTitle, HasSecondaryXAxisTitle ) +EXC_DEFINEFUNC_GETAXISTITLESHAPE( lclGetSecYAxisTitleShape, XSecondAxisTitleSupplier, getSecondYAxisTitle, HasSecondaryYAxisTitle ) + +#undef EXC_DEFINEFUNC_GETAXISTITLESHAPE +#undef EXC_IMPLEMENT_GETTITLESHAPE + +} // namespace + +// ---------------------------------------------------------------------------- + +XclChRootData::XclChRootData() : + mxTypeInfoProv( new XclChTypeInfoProvider ), + mxFmtInfoProv( new XclChFormatInfoProvider ), + mnBorderGapX( 0 ), + mnBorderGapY( 0 ) +{ + // remember some title shape getter functions + maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_TITLE ) ] = lclGetMainTitleShape; + maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_PRIMARY, EXC_CHAXIS_X ) ] = lclGetXAxisTitleShape; + maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_PRIMARY, EXC_CHAXIS_Y ) ] = lclGetYAxisTitleShape; + maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_PRIMARY, EXC_CHAXIS_Z ) ] = lclGetZAxisTitleShape; + maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_SECONDARY, EXC_CHAXIS_X ) ] = lclGetSecXAxisTitleShape; + maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_SECONDARY, EXC_CHAXIS_Y ) ] = lclGetSecYAxisTitleShape; +} + +XclChRootData::~XclChRootData() +{ +} + +void XclChRootData::InitConversion( const XclRoot& rRoot, const Reference< XChartDocument >& rxChartDoc, const Rectangle& rChartRect ) +{ + // remember chart document reference and chart shape position/size + DBG_ASSERT( rxChartDoc.is(), "XclChRootData::InitConversion - missing chart document" ); + mxChartDoc = rxChartDoc; + maChartRect = rChartRect; + + // Excel excludes a border of 5 pixels in each direction from chart area + mnBorderGapX = rRoot.GetHmmFromPixelX( 5.0 ); + mnBorderGapY = rRoot.GetHmmFromPixelY( 5.0 ); + + // size of a chart unit in 1/100 mm + mfUnitSizeX = ::std::max< double >( maChartRect.GetWidth() - 2 * mnBorderGapX, mnBorderGapX ) / EXC_CHART_TOTALUNITS; + mfUnitSizeY = ::std::max< double >( maChartRect.GetHeight() - 2 * mnBorderGapY, mnBorderGapY ) / EXC_CHART_TOTALUNITS; + + // create object tables + Reference< XMultiServiceFactory > xFactory( mxChartDoc, UNO_QUERY ); + mxLineDashTable.reset( new XclChObjectTable( + xFactory, SERVICE_DRAWING_DASHTABLE, CREATE_OUSTRING( "Excel line dash " ) ) ); + mxGradientTable.reset( new XclChObjectTable( + xFactory, SERVICE_DRAWING_GRADIENTTABLE, CREATE_OUSTRING( "Excel gradient " ) ) ); + mxHatchTable.reset( new XclChObjectTable( + xFactory, SERVICE_DRAWING_HATCHTABLE, CREATE_OUSTRING( "Excel hatch " ) ) ); + mxBitmapTable.reset( new XclChObjectTable( + xFactory, SERVICE_DRAWING_BITMAPTABLE, CREATE_OUSTRING( "Excel bitmap " ) ) ); +} + +void XclChRootData::FinishConversion() +{ + // forget formatting object tables + mxBitmapTable.reset(); + mxHatchTable.reset(); + mxGradientTable.reset(); + mxLineDashTable.reset(); + // forget chart document reference + mxChartDoc.clear(); +} + +Reference< XShape > XclChRootData::GetTitleShape( const XclChTextKey& rTitleKey ) const +{ + XclChGetShapeFuncMap::const_iterator aIt = maGetShapeFuncs.find( rTitleKey ); + OSL_ENSURE( aIt != maGetShapeFuncs.end(), "XclChRootData::GetTitleShape - invalid title key" ); + Reference< cssc::XChartDocument > xChart1Doc( mxChartDoc, UNO_QUERY ); + Reference< XShape > xTitleShape; + if( xChart1Doc.is() && (aIt != maGetShapeFuncs.end()) ) + xTitleShape = (aIt->second)( xChart1Doc ); + return xTitleShape; +} + +// ============================================================================ diff --git a/sc/source/filter/excel/xlescher.cxx b/sc/source/filter/excel/xlescher.cxx new file mode 100644 index 000000000000..153a5d5ed3cc --- /dev/null +++ b/sc/source/filter/excel/xlescher.cxx @@ -0,0 +1,379 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "xlescher.hxx" + +#include <com/sun/star/drawing/XControlShape.hpp> +#include <com/sun/star/script/ScriptEventDescriptor.hpp> +#include <svx/unoapi.hxx> +#include "document.hxx" +#include "xestream.hxx" +#include "xistream.hxx" +#include "xlroot.hxx" +#include "xltools.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::drawing::XControlShape; +using ::com::sun::star::awt::XControlModel; +using ::com::sun::star::script::ScriptEventDescriptor; + +// Structs and classes ======================================================== + +XclObjId::XclObjId() : + mnScTab( SCTAB_INVALID ), + mnObjId( EXC_OBJ_INVALID_ID ) +{ +} + +XclObjId::XclObjId( SCTAB nScTab, sal_uInt16 nObjId ) : + mnScTab( nScTab ), + mnObjId( nObjId ) +{ +} + +bool operator==( const XclObjId& rL, const XclObjId& rR ) +{ + return (rL.mnScTab == rR.mnScTab) && (rL.mnObjId == rR.mnObjId); +} + +bool operator<( const XclObjId& rL, const XclObjId& rR ) +{ + return (rL.mnScTab < rR.mnScTab) || ((rL.mnScTab == rR.mnScTab) && (rL.mnObjId < rR.mnObjId)); +} + +// ---------------------------------------------------------------------------- + +namespace { + +/** Returns the scaling factor to calculate coordinates from twips. */ +double lclGetTwipsScale( MapUnit eMapUnit ) +{ + /* #111027# We cannot use OutputDevice::LogicToLogic() or the XclTools + conversion functions to calculate drawing layer coordinates due to + Calc's strange definition of a point (1 inch == 72.27 points, instead + of 72 points). */ + double fScale = 1.0; + switch( eMapUnit ) + { + case MAP_TWIP: fScale = 72 / POINTS_PER_INCH; break; // Calc twips <-> real twips + case MAP_100TH_MM: fScale = HMM_PER_TWIPS; break; // Calc twips <-> 1/100mm + default: DBG_ERRORFILE( "lclGetTwipsScale - map unit not implemented" ); + } + return fScale; +} + +/** Calculates a drawing layer X position (in twips) from an object column position. */ +long lclGetXFromCol( ScDocument& rDoc, SCTAB nScTab, sal_uInt16 nXclCol, sal_uInt16 nOffset, double fScale ) +{ + SCCOL nScCol = static_cast< SCCOL >( nXclCol ); + return static_cast< long >( fScale * (rDoc.GetColOffset( nScCol, nScTab ) + + ::std::min( nOffset / 1024.0, 1.0 ) * rDoc.GetColWidth( nScCol, nScTab )) + 0.5 ); +} + +/** Calculates a drawing layer Y position (in twips) from an object row position. */ +long lclGetYFromRow( ScDocument& rDoc, SCTAB nScTab, sal_uInt16 nXclRow, sal_uInt16 nOffset, double fScale ) +{ + SCROW nScRow = static_cast< SCROW >( nXclRow ); + return static_cast< long >( fScale * (rDoc.GetRowOffset( nScRow, nScTab ) + + ::std::min( nOffset / 256.0, 1.0 ) * rDoc.GetRowHeight( nScRow, nScTab )) + 0.5 ); +} + +/** Calculates an object column position from a drawing layer X position (in twips). */ +void lclGetColFromX( + ScDocument& rDoc, SCTAB nScTab, sal_uInt16& rnXclCol, + sal_uInt16& rnOffset, sal_uInt16 nXclStartCol, sal_uInt16 nXclMaxCol, + long& rnStartW, long nX, double fScale ) +{ + // rnStartW in conjunction with nXclStartCol is used as buffer for previously calculated width + long nTwipsX = static_cast< long >( nX / fScale + 0.5 ); + long nColW = 0; + for( rnXclCol = nXclStartCol; rnXclCol <= nXclMaxCol; ++rnXclCol ) + { + nColW = rDoc.GetColWidth( static_cast< SCCOL >( rnXclCol ), nScTab ); + if( rnStartW + nColW > nTwipsX ) + break; + rnStartW += nColW; + } + rnOffset = nColW ? static_cast< sal_uInt16 >( (nTwipsX - rnStartW) * 1024.0 / nColW + 0.5 ) : 0; +} + +/** Calculates an object row position from a drawing layer Y position (in twips). */ +void lclGetRowFromY( + ScDocument& rDoc, SCTAB nScTab, sal_uInt16& rnXclRow, + sal_uInt16& rnOffset, sal_uInt16 nXclStartRow, sal_uInt16 nXclMaxRow, + long& rnStartH, long nY, double fScale ) +{ + // rnStartH in conjunction with nXclStartRow is used as buffer for previously calculated height + long nTwipsY = static_cast< long >( nY / fScale + 0.5 ); + long nRowH = 0; + bool bFound = false; + for( SCROW nRow = static_cast< SCROW >( nXclStartRow ); nRow <= nXclMaxRow; ++nRow ) + { + nRowH = rDoc.GetRowHeight( nRow, nScTab ); + if( rnStartH + nRowH > nTwipsY ) + { + rnXclRow = static_cast< sal_uInt16 >( nRow ); + bFound = true; + break; + } + rnStartH += nRowH; + } + if( !bFound ) + rnXclRow = nXclMaxRow; + rnOffset = static_cast< sal_uInt16 >( nRowH ? ((nTwipsY - rnStartH) * 256.0 / nRowH + 0.5) : 0 ); +} + +/** Mirrors a rectangle (from LTR to RTL layout or vice versa). */ +void lclMirrorRectangle( Rectangle& rRect ) +{ + long nLeft = rRect.Left(); + rRect.Left() = -rRect.Right(); + rRect.Right() = -nLeft; +} + +sal_uInt16 lclGetEmbeddedScale( long nPageSize, sal_Int32 nPageScale, long nPos, double fPosScale ) +{ + return static_cast< sal_uInt16 >( nPos * fPosScale / nPageSize * nPageScale + 0.5 ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclObjAnchor::XclObjAnchor() : + mnLX( 0 ), + mnTY( 0 ), + mnRX( 0 ), + mnBY( 0 ) +{ +} + +Rectangle XclObjAnchor::GetRect( const XclRoot& rRoot, SCTAB nScTab, MapUnit eMapUnit ) const +{ + ScDocument& rDoc = rRoot.GetDoc(); + double fScale = lclGetTwipsScale( eMapUnit ); + Rectangle aRect( + lclGetXFromCol( rDoc, nScTab, maFirst.mnCol, mnLX, fScale ), + lclGetYFromRow( rDoc, nScTab, maFirst.mnRow, mnTY, fScale ), + lclGetXFromCol( rDoc, nScTab, maLast.mnCol, mnRX + 1, fScale ), + lclGetYFromRow( rDoc, nScTab, maLast.mnRow, mnBY, fScale ) ); + + // #106948# adjust coordinates in mirrored sheets + if( rDoc.IsLayoutRTL( nScTab ) ) + lclMirrorRectangle( aRect ); + return aRect; +} + +void XclObjAnchor::SetRect( const XclRoot& rRoot, SCTAB nScTab, const Rectangle& rRect, MapUnit eMapUnit ) +{ + ScDocument& rDoc = rRoot.GetDoc(); + sal_uInt16 nXclMaxCol = rRoot.GetXclMaxPos().Col(); + sal_uInt16 nXclMaxRow = static_cast<sal_uInt16>( rRoot.GetXclMaxPos().Row()); + + // #106948# adjust coordinates in mirrored sheets + Rectangle aRect( rRect ); + if( rDoc.IsLayoutRTL( nScTab ) ) + lclMirrorRectangle( aRect ); + + double fScale = lclGetTwipsScale( eMapUnit ); + long nDummy = 0; + lclGetColFromX( rDoc, nScTab, maFirst.mnCol, mnLX, 0, nXclMaxCol, nDummy, aRect.Left(), fScale ); + lclGetColFromX( rDoc, nScTab, maLast.mnCol, mnRX, maFirst.mnCol, nXclMaxCol, nDummy, aRect.Right(), fScale ); + nDummy = 0; + lclGetRowFromY( rDoc, nScTab, maFirst.mnRow, mnTY, 0, nXclMaxRow, nDummy, aRect.Top(), fScale ); + lclGetRowFromY( rDoc, nScTab, maLast.mnRow, mnBY, maFirst.mnRow, nXclMaxRow, nDummy, aRect.Bottom(), fScale ); +} + +void XclObjAnchor::SetRect( const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY, + const Rectangle& rRect, MapUnit eMapUnit, bool bDffAnchor ) +{ + double fScale = 1.0; + switch( eMapUnit ) + { + case MAP_TWIP: fScale = HMM_PER_TWIPS; break; // Calc twips -> 1/100mm + case MAP_100TH_MM: fScale = 1.0; break; // Calc 1/100mm -> 1/100mm + default: DBG_ERRORFILE( "XclObjAnchor::SetRect - map unit not implemented" ); + } + + /* In objects with DFF client anchor, the position of the shape is stored + in the cell address components of the client anchor. In old BIFF3-BIFF5 + objects, the position is stored in the offset components of the anchor. */ + (bDffAnchor ? maFirst.mnCol : mnLX) = lclGetEmbeddedScale( rPageSize.Width(), nScaleX, rRect.Left(), fScale ); + (bDffAnchor ? maFirst.mnRow : mnTY) = lclGetEmbeddedScale( rPageSize.Height(), nScaleY, rRect.Top(), fScale ); + (bDffAnchor ? maLast.mnCol : mnRX) = lclGetEmbeddedScale( rPageSize.Width(), nScaleX, rRect.Right(), fScale ); + (bDffAnchor ? maLast.mnRow : mnBY) = lclGetEmbeddedScale( rPageSize.Height(), nScaleY, rRect.Bottom(), fScale ); + + // for safety, clear the other members + if( bDffAnchor ) + mnLX = mnTY = mnRX = mnBY = 0; + else + Set( 0, 0, 0, 0 ); +} + +// ---------------------------------------------------------------------------- + +XclObjLineData::XclObjLineData() : + mnColorIdx( EXC_OBJ_LINE_AUTOCOLOR ), + mnStyle( EXC_OBJ_LINE_SOLID ), + mnWidth( EXC_OBJ_LINE_HAIR ), + mnAuto( EXC_OBJ_LINE_AUTO ) +{ +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclObjLineData& rLineData ) +{ + return rStrm + >> rLineData.mnColorIdx + >> rLineData.mnStyle + >> rLineData.mnWidth + >> rLineData.mnAuto; +} + +// ---------------------------------------------------------------------------- + +XclObjFillData::XclObjFillData() : + mnBackColorIdx( EXC_OBJ_LINE_AUTOCOLOR ), + mnPattColorIdx( EXC_OBJ_FILL_AUTOCOLOR ), + mnPattern( EXC_PATT_SOLID ), + mnAuto( EXC_OBJ_FILL_AUTO ) +{ +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclObjFillData& rFillData ) +{ + return rStrm + >> rFillData.mnBackColorIdx + >> rFillData.mnPattColorIdx + >> rFillData.mnPattern + >> rFillData.mnAuto; +} + +// ---------------------------------------------------------------------------- + +XclObjTextData::XclObjTextData() : + mnTextLen( 0 ), + mnFormatSize( 0 ), + mnLinkSize( 0 ), + mnDefFontIdx( EXC_FONT_APP ), + mnFlags( 0 ), + mnOrient( EXC_OBJ_ORIENT_NONE ), + mnButtonFlags( 0 ), + mnShortcut( 0 ), + mnShortcutEA( 0 ) +{ +} + +void XclObjTextData::ReadObj3( XclImpStream& rStrm ) +{ + rStrm >> mnTextLen; + rStrm.Ignore( 2 ); + rStrm >> mnFormatSize >> mnDefFontIdx; + rStrm.Ignore( 2 ); + rStrm >> mnFlags >> mnOrient; + rStrm.Ignore( 8 ); +} + +void XclObjTextData::ReadObj5( XclImpStream& rStrm ) +{ + rStrm >> mnTextLen; + rStrm.Ignore( 2 ); + rStrm >> mnFormatSize >> mnDefFontIdx; + rStrm.Ignore( 2 ); + rStrm >> mnFlags >> mnOrient; + rStrm.Ignore( 2 ); + rStrm >> mnLinkSize; + rStrm.Ignore( 2 ); + rStrm >> mnButtonFlags >> mnShortcut >> mnShortcutEA; +} + +void XclObjTextData::ReadTxo8( XclImpStream& rStrm ) +{ + rStrm >> mnFlags >> mnOrient >> mnButtonFlags >> mnShortcut >> mnShortcutEA >> mnTextLen >> mnFormatSize; +} + +// ============================================================================ + +Reference< XControlModel > XclControlHelper::GetControlModel( Reference< XShape > xShape ) +{ + Reference< XControlModel > xCtrlModel; + Reference< XControlShape > xCtrlShape( xShape, UNO_QUERY ); + if( xCtrlShape.is() ) + xCtrlModel = xCtrlShape->getControl(); + return xCtrlModel; +} + +namespace { + +static const struct +{ + const sal_Char* mpcListenerType; + const sal_Char* mpcEventMethod; +} +spTbxListenerData[] = +{ + // Attention: MUST be in order of the XclTbxEventType enum! + /*EXC_TBX_EVENT_ACTION*/ { "XActionListener", "actionPerformed" }, + /*EXC_TBX_EVENT_MOUSE*/ { "XMouseListener", "mouseReleased" }, + /*EXC_TBX_EVENT_TEXT*/ { "XTextListener", "textChanged" }, + /*EXC_TBX_EVENT_VALUE*/ { "XAdjustmentListener", "adjustmentValueChanged" }, + /*EXC_TBX_EVENT_CHANGE*/ { "XChangeListener", "changed" } +}; + +} // namespace + +bool XclControlHelper::FillMacroDescriptor( ScriptEventDescriptor& rDescriptor, + XclTbxEventType eEventType, const String& rXclMacroName, SfxObjectShell* pDocShell ) +{ + if( rXclMacroName.Len() > 0 ) + { + rDescriptor.ListenerType = OUString::createFromAscii( spTbxListenerData[ eEventType ].mpcListenerType ); + rDescriptor.EventMethod = OUString::createFromAscii( spTbxListenerData[ eEventType ].mpcEventMethod ); + rDescriptor.ScriptType = CREATE_OUSTRING( "Script" ); + rDescriptor.ScriptCode = XclTools::GetSbMacroUrl( rXclMacroName, pDocShell ); + return true; + } + return false; +} + +String XclControlHelper::ExtractFromMacroDescriptor( + const ScriptEventDescriptor& rDescriptor, XclTbxEventType eEventType ) +{ + if( (rDescriptor.ScriptCode.getLength() > 0) && + rDescriptor.ScriptType.equalsIgnoreAsciiCaseAscii( "Script" ) && + rDescriptor.ListenerType.equalsAscii( spTbxListenerData[ eEventType ].mpcListenerType ) && + rDescriptor.EventMethod.equalsAscii( spTbxListenerData[ eEventType ].mpcEventMethod ) ) + return XclTools::GetXclMacroName( rDescriptor.ScriptCode ); + return String::EmptyString(); +} + +// ============================================================================ diff --git a/sc/source/filter/excel/xlformula.cxx b/sc/source/filter/excel/xlformula.cxx new file mode 100644 index 000000000000..d613f1279242 --- /dev/null +++ b/sc/source/filter/excel/xlformula.cxx @@ -0,0 +1,786 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xlformula.hxx" + +#include "compiler.hxx" +#include "rangenam.hxx" +#include "token.hxx" +#include "tokenarray.hxx" +#include "xestream.hxx" +#include "xistream.hxx" +#include "xlroot.hxx" + +using namespace ::formula; + +// Function data ============================================================== + +String XclFunctionInfo::GetMacroFuncName() const +{ + if( IsMacroFunc() ) + return String( mpcMacroName, RTL_TEXTENCODING_UTF8 ); + return EMPTY_STRING; +} + +// abbreviations for function return token class +const sal_uInt8 R = EXC_TOKCLASS_REF; +const sal_uInt8 V = EXC_TOKCLASS_VAL; +const sal_uInt8 A = EXC_TOKCLASS_ARR; + +// abbreviations for parameter infos +#define RO { EXC_PARAM_REGULAR, EXC_PARAMCONV_ORG, false } +#define RV { EXC_PARAM_REGULAR, EXC_PARAMCONV_VAL, false } +#define RA { EXC_PARAM_REGULAR, EXC_PARAMCONV_ARR, false } +#define RR { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPT, false } +#define RX { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPX, false } +#define VO { EXC_PARAM_REGULAR, EXC_PARAMCONV_ORG, true } +#define VV { EXC_PARAM_REGULAR, EXC_PARAMCONV_VAL, true } +#define VA { EXC_PARAM_REGULAR, EXC_PARAMCONV_ARR, true } +#define VR { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPT, true } +#define VX { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPX, true } +#define RO_E { EXC_PARAM_EXCELONLY, EXC_PARAMCONV_ORG, false } +#define VR_E { EXC_PARAM_EXCELONLY, EXC_PARAMCONV_RPT, true } +#define C { EXC_PARAM_CALCONLY, EXC_PARAMCONV_ORG, false } + +const sal_uInt16 NOID = SAL_MAX_UINT16; /// No BIFF/OOBIN function identifier available. +const sal_uInt8 MX = 30; /// Maximum parameter count. + +#define EXC_FUNCNAME( ascii ) "_xlfn." ascii +#define EXC_FUNCNAME_ODF( ascii ) "_xlfnodf." ascii + +/** Functions new in BIFF2. */ +static const XclFunctionInfo saFuncTable_2[] = +{ + { ocCount, 0, 0, MX, V, { RX }, 0, 0 }, + { ocIf, 1, 2, 3, R, { VO, RO }, 0, 0 }, + { ocIsNA, 2, 1, 1, V, { VR }, 0, 0 }, + { ocIsError, 3, 1, 1, V, { VR }, 0, 0 }, + { ocSum, 4, 0, MX, V, { RX }, 0, 0 }, + { ocAverage, 5, 1, MX, V, { RX }, 0, 0 }, + { ocMin, 6, 1, MX, V, { RX }, 0, 0 }, + { ocMax, 7, 1, MX, V, { RX }, 0, 0 }, + { ocRow, 8, 0, 1, V, { RO }, 0, 0 }, + { ocColumn, 9, 0, 1, V, { RO }, 0, 0 }, + { ocNotAvail, 10, 0, 0, V, {}, 0, 0 }, + { ocNPV, 11, 2, MX, V, { VR, RX }, 0, 0 }, + { ocStDev, 12, 1, MX, V, { RX }, 0, 0 }, + { ocCurrency, 13, 1, 2, V, { VR }, 0, 0 }, + { ocFixed, 14, 1, 2, V, { VR, VR, C }, 0, 0 }, + { ocSin, 15, 1, 1, V, { VR }, 0, 0 }, + { ocCos, 16, 1, 1, V, { VR }, 0, 0 }, + { ocTan, 17, 1, 1, V, { VR }, 0, 0 }, + { ocCot, 17, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, 0 }, + { ocArcTan, 18, 1, 1, V, { VR }, 0, 0 }, + { ocArcCot, 18, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, 0 }, + { ocPi, 19, 0, 0, V, {}, 0, 0 }, + { ocSqrt, 20, 1, 1, V, { VR }, 0, 0 }, + { ocExp, 21, 1, 1, V, { VR }, 0, 0 }, + { ocLn, 22, 1, 1, V, { VR }, 0, 0 }, + { ocLog10, 23, 1, 1, V, { VR }, 0, 0 }, + { ocAbs, 24, 1, 1, V, { VR }, 0, 0 }, + { ocInt, 25, 1, 1, V, { VR }, 0, 0 }, + { ocPlusMinus, 26, 1, 1, V, { VR }, 0, 0 }, + { ocRound, 27, 2, 2, V, { VR }, 0, 0 }, + { ocLookup, 28, 2, 3, V, { VR, RA }, 0, 0 }, + { ocIndex, 29, 2, 4, R, { RA, VV }, 0, 0 }, + { ocRept, 30, 2, 2, V, { VR }, 0, 0 }, + { ocMid, 31, 3, 3, V, { VR }, 0, 0 }, + { ocLen, 32, 1, 1, V, { VR }, 0, 0 }, + { ocValue, 33, 1, 1, V, { VR }, 0, 0 }, + { ocTrue, 34, 0, 0, V, {}, 0, 0 }, + { ocFalse, 35, 0, 0, V, {}, 0, 0 }, + { ocAnd, 36, 1, MX, V, { RX }, 0, 0 }, + { ocOr, 37, 1, MX, V, { RX }, 0, 0 }, + { ocNot, 38, 1, 1, V, { VR }, 0, 0 }, + { ocMod, 39, 2, 2, V, { VR }, 0, 0 }, + { ocDBCount, 40, 3, 3, V, { RO, RR }, 0, 0 }, + { ocDBSum, 41, 3, 3, V, { RO, RR }, 0, 0 }, + { ocDBAverage, 42, 3, 3, V, { RO, RR }, 0, 0 }, + { ocDBMin, 43, 3, 3, V, { RO, RR }, 0, 0 }, + { ocDBMax, 44, 3, 3, V, { RO, RR }, 0, 0 }, + { ocDBStdDev, 45, 3, 3, V, { RO, RR }, 0, 0 }, + { ocVar, 46, 1, MX, V, { RX }, 0, 0 }, + { ocDBVar, 47, 3, 3, V, { RO, RR }, 0, 0 }, + { ocText, 48, 2, 2, V, { VR }, 0, 0 }, + { ocRGP, 49, 1, 2, A, { RA, RA, C, C }, 0, 0 }, + { ocTrend, 50, 1, 3, A, { RA, RA, RA, C }, 0, 0 }, + { ocRKP, 51, 1, 2, A, { RA, RA, C, C }, 0, 0 }, + { ocGrowth, 52, 1, 3, A, { RA, RA, RA, C }, 0, 0 }, + { ocBW, 56, 3, 5, V, { VR }, 0, 0 }, + { ocZW, 57, 3, 5, V, { VR }, 0, 0 }, + { ocZZR, 58, 3, 5, V, { VR }, 0, 0 }, + { ocRMZ, 59, 3, 5, V, { VR }, 0, 0 }, + { ocZins, 60, 3, 6, V, { VR }, 0, 0 }, + { ocMIRR, 61, 3, 3, V, { RA, VR }, 0, 0 }, + { ocIRR, 62, 1, 2, V, { RA, VR }, 0, 0 }, + { ocRandom, 63, 0, 0, V, {}, EXC_FUNCFLAG_VOLATILE, 0 }, + { ocMatch, 64, 2, 3, V, { VR, RX, RR }, 0, 0 }, + { ocGetDate, 65, 3, 3, V, { VR }, 0, 0 }, + { ocGetTime, 66, 3, 3, V, { VR }, 0, 0 }, + { ocGetDay, 67, 1, 1, V, { VR }, 0, 0 }, + { ocGetMonth, 68, 1, 1, V, { VR }, 0, 0 }, + { ocGetYear, 69, 1, 1, V, { VR }, 0, 0 }, + { ocGetDayOfWeek, 70, 1, 1, V, { VR, C }, 0, 0 }, + { ocGetHour, 71, 1, 1, V, { VR }, 0, 0 }, + { ocGetMin, 72, 1, 1, V, { VR }, 0, 0 }, + { ocGetSec, 73, 1, 1, V, { VR }, 0, 0 }, + { ocGetActTime, 74, 0, 0, V, {}, EXC_FUNCFLAG_VOLATILE, 0 }, + { ocAreas, 75, 1, 1, V, { RO }, 0, 0 }, + { ocRows, 76, 1, 1, V, { RO }, 0, 0 }, + { ocColumns, 77, 1, 1, V, { RO }, 0, 0 }, + { ocOffset, 78, 3, 5, R, { RO, VR }, EXC_FUNCFLAG_VOLATILE, 0 }, + { ocSearch, 82, 2, 3, V, { VR }, 0, 0 }, + { ocMatTrans, 83, 1, 1, A, { VO }, 0, 0 }, + { ocType, 86, 1, 1, V, { VX }, 0, 0 }, + { ocArcTan2, 97, 2, 2, V, { VR }, 0, 0 }, + { ocArcSin, 98, 1, 1, V, { VR }, 0, 0 }, + { ocArcCos, 99, 1, 1, V, { VR }, 0, 0 }, + { ocChose, 100, 2, MX, R, { VO, RO }, 0, 0 }, + { ocHLookup, 101, 3, 3, V, { VV, RO, RO, C }, 0, 0 }, + { ocVLookup, 102, 3, 3, V, { VV, RO, RO, C }, 0, 0 }, + { ocIsRef, 105, 1, 1, V, { RX }, 0, 0 }, + { ocLog, 109, 1, 2, V, { VR }, 0, 0 }, + { ocChar, 111, 1, 1, V, { VR }, 0, 0 }, + { ocLower, 112, 1, 1, V, { VR }, 0, 0 }, + { ocUpper, 113, 1, 1, V, { VR }, 0, 0 }, + { ocPropper, 114, 1, 1, V, { VR }, 0, 0 }, + { ocLeft, 115, 1, 2, V, { VR }, 0, 0 }, + { ocRight, 116, 1, 2, V, { VR }, 0, 0 }, + { ocExact, 117, 2, 2, V, { VR }, 0, 0 }, + { ocTrim, 118, 1, 1, V, { VR }, 0, 0 }, + { ocReplace, 119, 4, 4, V, { VR }, 0, 0 }, + { ocSubstitute, 120, 3, 4, V, { VR }, 0, 0 }, + { ocCode, 121, 1, 1, V, { VR }, 0, 0 }, + { ocFind, 124, 2, 3, V, { VR }, 0, 0 }, + { ocCell, 125, 1, 2, V, { VV, RO }, EXC_FUNCFLAG_VOLATILE, 0 }, + { ocIsErr, 126, 1, 1, V, { VR }, 0, 0 }, + { ocIsString, 127, 1, 1, V, { VR }, 0, 0 }, + { ocIsValue, 128, 1, 1, V, { VR }, 0, 0 }, + { ocIsEmpty, 129, 1, 1, V, { VR }, 0, 0 }, + { ocT, 130, 1, 1, V, { RO }, 0, 0 }, + { ocN, 131, 1, 1, V, { RO }, 0, 0 }, + { ocGetDateValue, 140, 1, 1, V, { VR }, 0, 0 }, + { ocGetTimeValue, 141, 1, 1, V, { VR }, 0, 0 }, + { ocLIA, 142, 3, 3, V, { VR }, 0, 0 }, + { ocDIA, 143, 4, 4, V, { VR }, 0, 0 }, + { ocGDA, 144, 4, 5, V, { VR }, 0, 0 }, + { ocIndirect, 148, 1, 2, R, { VR }, EXC_FUNCFLAG_VOLATILE, 0 }, + { ocClean, 162, 1, 1, V, { VR }, 0, 0 }, + { ocMatDet, 163, 1, 1, V, { VA }, 0, 0 }, + { ocMatInv, 164, 1, 1, A, { VA }, 0, 0 }, + { ocMatMult, 165, 2, 2, A, { VA }, 0, 0 }, + { ocZinsZ, 167, 4, 6, V, { VR }, 0, 0 }, + { ocKapz, 168, 4, 6, V, { VR }, 0, 0 }, + { ocCount2, 169, 0, MX, V, { RX }, 0, 0 }, + { ocProduct, 183, 0, MX, V, { RX }, 0, 0 }, + { ocFact, 184, 1, 1, V, { VR }, 0, 0 }, + { ocDBProduct, 189, 3, 3, V, { RO, RR }, 0, 0 }, + { ocIsNonString, 190, 1, 1, V, { VR }, 0, 0 }, + { ocStDevP, 193, 1, MX, V, { RX }, 0, 0 }, + { ocVarP, 194, 1, MX, V, { RX }, 0, 0 }, + { ocDBStdDevP, 195, 3, 3, V, { RO, RR }, 0, 0 }, + { ocDBVarP, 196, 3, 3, V, { RO, RR }, 0, 0 }, + { ocTrunc, 197, 1, 1, V, { VR, C }, 0, 0 }, + { ocIsLogical, 198, 1, 1, V, { VR }, 0, 0 }, + { ocDBCount2, 199, 3, 3, V, { RO, RR }, 0, 0 }, + { ocCurrency, 204, 1, 2, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, 0 }, + { ocRoundUp, 212, 2, 2, V, { VR }, 0, 0 }, + { ocRoundDown, 213, 2, 2, V, { VR }, 0, 0 }, + { ocExternal, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_IMPORTONLY, 0 } +}; + +/** Functions new in BIFF3. */ +static const XclFunctionInfo saFuncTable_3[] = +{ + { ocRGP, 49, 1, 4, A, { RA, RA, VV }, 0, 0 }, // BIFF2: 1-2, BIFF3: 1-4 + { ocTrend, 50, 1, 4, A, { RA, RA, RA, VV }, 0, 0 }, // BIFF2: 1-3, BIFF3: 1-4 + { ocRKP, 51, 1, 4, A, { RA, RA, VV }, 0, 0 }, // BIFF2: 1-2, BIFF3: 1-4 + { ocGrowth, 52, 1, 4, A, { RA, RA, RA, VV }, 0, 0 }, // BIFF2: 1-3, BIFF3: 1-4 + { ocTrunc, 197, 1, 2, V, { VR }, 0, 0 }, // BIFF2: 1, BIFF3: 1-2 + { ocAddress, 219, 2, 5, V, { VR }, 0, 0 }, + { ocGetDiffDate360, 220, 2, 2, V, { VR, VR, C }, 0, 0 }, + { ocGetActDate, 221, 0, 0, V, {}, EXC_FUNCFLAG_VOLATILE, 0 }, + { ocVBD, 222, 5, 7, V, { VR }, 0, 0 }, + { ocMedian, 227, 1, MX, V, { RX }, 0, 0 }, + { ocSumProduct, 228, 1, MX, V, { VA }, 0, 0 }, + { ocSinHyp, 229, 1, 1, V, { VR }, 0, 0 }, + { ocCosHyp, 230, 1, 1, V, { VR }, 0, 0 }, + { ocTanHyp, 231, 1, 1, V, { VR }, 0, 0 }, + { ocCotHyp, 231, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, 0 }, + { ocArcSinHyp, 232, 1, 1, V, { VR }, 0, 0 }, + { ocArcCosHyp, 233, 1, 1, V, { VR }, 0, 0 }, + { ocArcTanHyp, 234, 1, 1, V, { VR }, 0, 0 }, + { ocArcCotHyp, 234, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, 0 }, + { ocDBGet, 235, 3, 3, V, { RO, RR }, 0, 0 }, + { ocInfo, 244, 1, 1, V, { VR }, EXC_FUNCFLAG_VOLATILE, 0 } +}; + +/** Functions new in BIFF4. */ +static const XclFunctionInfo saFuncTable_4[] = +{ + { ocFixed, 14, 1, 3, V, { VR }, 0, 0 }, // BIFF2-3: 1-2, BIFF4: 1-3 + { ocAsc, 214, 1, 1, V, { VR }, 0, 0 }, + { ocJis, 215, 1, 1, V, { VR }, 0, 0 }, + { ocRank, 216, 2, 3, V, { VR, RO, VR }, 0, 0 }, + { ocGDA2, 247, 4, 5, V, { VR }, 0, 0 }, + { ocFrequency, 252, 2, 2, A, { RA }, 0, 0 }, + { ocErrorType, 261, 1, 1, V, { VR }, 0, 0 }, + { ocAveDev, 269, 1, MX, V, { RX }, 0, 0 }, + { ocBetaDist, 270, 3, 5, V, { VR }, 0, 0 }, + { ocGammaLn, 271, 1, 1, V, { VR }, 0, 0 }, + { ocBetaInv, 272, 3, 5, V, { VR }, 0, 0 }, + { ocBinomDist, 273, 4, 4, V, { VR }, 0, 0 }, + { ocChiDist, 274, 2, 2, V, { VR }, 0, 0 }, + { ocChiInv, 275, 2, 2, V, { VR }, 0, 0 }, + { ocKombin, 276, 2, 2, V, { VR }, 0, 0 }, + { ocConfidence, 277, 3, 3, V, { VR }, 0, 0 }, + { ocKritBinom, 278, 3, 3, V, { VR }, 0, 0 }, + { ocEven, 279, 1, 1, V, { VR }, 0, 0 }, + { ocExpDist, 280, 3, 3, V, { VR }, 0, 0 }, + { ocFDist, 281, 3, 3, V, { VR }, 0, 0 }, + { ocFInv, 282, 3, 3, V, { VR }, 0, 0 }, + { ocFisher, 283, 1, 1, V, { VR }, 0, 0 }, + { ocFisherInv, 284, 1, 1, V, { VR }, 0, 0 }, + { ocFloor, 285, 2, 2, V, { VR, VR, C }, 0, 0 }, + { ocGammaDist, 286, 4, 4, V, { VR }, 0, 0 }, + { ocGammaInv, 287, 3, 3, V, { VR }, 0, 0 }, + { ocCeil, 288, 2, 2, V, { VR, VR, C }, 0, 0 }, + { ocHypGeomDist, 289, 4, 4, V, { VR }, 0, 0 }, + { ocLogNormDist, 290, 3, 3, V, { VR }, 0, 0 }, + { ocLogInv, 291, 3, 3, V, { VR }, 0, 0 }, + { ocNegBinomVert, 292, 3, 3, V, { VR }, 0, 0 }, + { ocNormDist, 293, 4, 4, V, { VR }, 0, 0 }, + { ocStdNormDist, 294, 1, 1, V, { VR }, 0, 0 }, + { ocNormInv, 295, 3, 3, V, { VR }, 0, 0 }, + { ocSNormInv, 296, 1, 1, V, { VR }, 0, 0 }, + { ocStandard, 297, 3, 3, V, { VR }, 0, 0 }, + { ocOdd, 298, 1, 1, V, { VR }, 0, 0 }, + { ocVariationen, 299, 2, 2, V, { VR }, 0, 0 }, + { ocPoissonDist, 300, 3, 3, V, { VR }, 0, 0 }, + { ocTDist, 301, 3, 3, V, { VR }, 0, 0 }, + { ocWeibull, 302, 4, 4, V, { VR }, 0, 0 }, + { ocSumXMY2, 303, 2, 2, V, { VA }, 0, 0 }, + { ocSumX2MY2, 304, 2, 2, V, { VA }, 0, 0 }, + { ocSumX2DY2, 305, 2, 2, V, { VA }, 0, 0 }, + { ocChiTest, 306, 2, 2, V, { VA }, 0, 0 }, + { ocCorrel, 307, 2, 2, V, { VA }, 0, 0 }, + { ocCovar, 308, 2, 2, V, { VA }, 0, 0 }, + { ocForecast, 309, 3, 3, V, { VR, VA }, 0, 0 }, + { ocFTest, 310, 2, 2, V, { VA }, 0, 0 }, + { ocIntercept, 311, 2, 2, V, { VA }, 0, 0 }, + { ocPearson, 312, 2, 2, V, { VA }, 0, 0 }, + { ocRSQ, 313, 2, 2, V, { VA }, 0, 0 }, + { ocSTEYX, 314, 2, 2, V, { VA }, 0, 0 }, + { ocSlope, 315, 2, 2, V, { VA }, 0, 0 }, + { ocTTest, 316, 4, 4, V, { VA, VA, VR }, 0, 0 }, + { ocProb, 317, 3, 4, V, { VA, VA, VR }, 0, 0 }, + { ocDevSq, 318, 1, MX, V, { RX }, 0, 0 }, + { ocGeoMean, 319, 1, MX, V, { RX }, 0, 0 }, + { ocHarMean, 320, 1, MX, V, { RX }, 0, 0 }, + { ocSumSQ, 321, 0, MX, V, { RX }, 0, 0 }, + { ocKurt, 322, 1, MX, V, { RX }, 0, 0 }, + { ocSchiefe, 323, 1, MX, V, { RX }, 0, 0 }, + { ocZTest, 324, 2, 3, V, { RX, VR }, 0, 0 }, + { ocLarge, 325, 2, 2, V, { RX, VR }, 0, 0 }, + { ocSmall, 326, 2, 2, V, { RX, VR }, 0, 0 }, + { ocQuartile, 327, 2, 2, V, { RX, VR }, 0, 0 }, + { ocPercentile, 328, 2, 2, V, { RX, VR }, 0, 0 }, + { ocPercentrank, 329, 2, 3, V, { RX, VR, VR_E }, 0, 0 }, + { ocModalValue, 330, 1, MX, V, { VA }, 0, 0 }, + { ocTrimMean, 331, 2, 2, V, { RX, VR }, 0, 0 }, + { ocTInv, 332, 2, 2, V, { VR }, 0, 0 } +}; + +/** Functions new in BIFF5/BIFF7. Unsupported functions: DATEDIF, DATESTRING, NUMBERSTRING. */ +static const XclFunctionInfo saFuncTable_5[] = +{ + { ocGetDayOfWeek, 70, 1, 2, V, { VR }, 0, 0 }, // BIFF2-4: 1, BIFF5: 1-2 + { ocHLookup, 101, 3, 4, V, { VV, RO, RO, VV }, 0, 0 }, // BIFF2-4: 3, BIFF5: 3-4 + { ocVLookup, 102, 3, 4, V, { VV, RO, RO, VV }, 0, 0 }, // BIFF2-4: 3, BIFF5: 3-4 + { ocGetDiffDate360, 220, 2, 3, V, { VR }, 0, 0 }, // BIFF3-4: 2, BIFF5: 2-3 + { ocMacro, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, 0 }, + { ocExternal, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, 0 }, + { ocConcat, 336, 0, MX, V, { VR }, 0, 0 }, + { ocPower, 337, 2, 2, V, { VR }, 0, 0 }, + { ocRad, 342, 1, 1, V, { VR }, 0, 0 }, + { ocDeg, 343, 1, 1, V, { VR }, 0, 0 }, + { ocSubTotal, 344, 2, MX, V, { VR, RO }, 0, 0 }, + { ocSumIf, 345, 2, 3, V, { RO, VR, RO }, 0, 0 }, + { ocCountIf, 346, 2, 2, V, { RO, VR }, 0, 0 }, + { ocCountEmptyCells, 347, 1, 1, V, { RO }, 0, 0 }, + { ocISPMT, 350, 4, 4, V, { VR }, 0, 0 }, + { ocNoName, 351, 3, 3, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, 0 }, // DATEDIF + { ocNoName, 352, 1, 1, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, 0 }, // DATESTRING + { ocNoName, 353, 2, 2, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, 0 }, // NUMBERSTRING + { ocRoman, 354, 1, 2, V, { VR }, 0, 0 } +}; + +/** Functions new in BIFF8. Unsupported functions: PHONETIC. */ +static const XclFunctionInfo saFuncTable_8[] = +{ + { ocGetPivotData, 358, 2, MX, V, { RR, RR, VR }, 0, 0 }, + { ocHyperLink, 359, 1, 2, V, { VV, VO }, 0, 0 }, + { ocNoName, 360, 1, 1, V, { RO }, EXC_FUNCFLAG_IMPORTONLY, 0 }, // PHONETIC + { ocAverageA, 361, 1, MX, V, { RX }, 0, 0 }, + { ocMaxA, 362, 1, MX, V, { RX }, 0, 0 }, + { ocMinA, 363, 1, MX, V, { RX }, 0, 0 }, + { ocStDevPA, 364, 1, MX, V, { RX }, 0, 0 }, + { ocVarPA, 365, 1, MX, V, { RX }, 0, 0 }, + { ocStDevA, 366, 1, MX, V, { RX }, 0, 0 }, + { ocVarA, 367, 1, MX, V, { RX }, 0, 0 }, + { ocBahtText, 368, 1, 1, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "BAHTTEXT" ) }, + { ocBahtText, 255, 2, 2, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "BAHTTEXT" ) }, + { ocEuroConvert, 255, 4, 6, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, "EUROCONVERT" } +}; + +#define EXC_FUNCENTRY_ODF( opcode, minparam, maxparam, flags, asciiname ) \ + { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME_ODF( asciiname ) }, \ + { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME_ODF( asciiname ) } + +/** Functions defined by OpenFormula, but not supported by Calc (ocNoName) or by Excel (defined op-code). */ +static const XclFunctionInfo saFuncTable_Odf[] = +{ + EXC_FUNCENTRY_ODF( ocArabic, 1, 1, 0, "ARABIC" ), + EXC_FUNCENTRY_ODF( ocB, 3, 4, 0, "B" ), + EXC_FUNCENTRY_ODF( ocBase, 2, 3, 0, "BASE" ), + EXC_FUNCENTRY_ODF( ocNoName, 2, 2, 0, "BITAND" ), + EXC_FUNCENTRY_ODF( ocNoName, 2, 2, 0, "BITLSHIFT" ), + EXC_FUNCENTRY_ODF( ocNoName, 2, 2, 0, "BITOR" ), + EXC_FUNCENTRY_ODF( ocNoName, 2, 2, 0, "BITRSHIFT" ), + EXC_FUNCENTRY_ODF( ocNoName, 2, 2, 0, "BITXOR" ), + EXC_FUNCENTRY_ODF( ocChiSqDist, 2, 3, 0, "CHISQDIST" ), + EXC_FUNCENTRY_ODF( ocChiSqInv, 2, 2, 0, "CHISQINV" ), + EXC_FUNCENTRY_ODF( ocKombin2, 2, 2, 0, "COMBINA" ), + EXC_FUNCENTRY_ODF( ocGetDiffDate, 2, 2, 0, "DAYS" ), + EXC_FUNCENTRY_ODF( ocDecimal, 2, 2, 0, "DECIMAL" ), + EXC_FUNCENTRY_ODF( ocFDist, 3, 4, 0, "FDIST" ), + EXC_FUNCENTRY_ODF( ocFInv, 3, 3, 0, "FINV" ), + EXC_FUNCENTRY_ODF( ocFormula, 1, 1, 0, "FORMULA" ), + EXC_FUNCENTRY_ODF( ocGamma, 1, 1, 0, "GAMMA" ), + EXC_FUNCENTRY_ODF( ocGauss, 1, 1, 0, "GAUSS" ), + EXC_FUNCENTRY_ODF( ocNoName, 2, 2, 0, "IFNA" ), + EXC_FUNCENTRY_ODF( ocIsFormula, 1, 1, 0, "ISFORMULA" ), + EXC_FUNCENTRY_ODF( ocWeek, 1, 2, 0, "ISOWEEKNUM" ), + EXC_FUNCENTRY_ODF( ocMatrixUnit, 1, 1, 0, "MUNIT" ), + EXC_FUNCENTRY_ODF( ocNumberValue, 2, 2, 0, "NUMBERVALUE" ), + EXC_FUNCENTRY_ODF( ocLaufz, 3, 3, 0, "PDURATION" ), + EXC_FUNCENTRY_ODF( ocVariationen2, 2, 2, 0, "PERMUTATIONA" ), + EXC_FUNCENTRY_ODF( ocPhi, 1, 1, 0, "PHI" ), + EXC_FUNCENTRY_ODF( ocZGZ, 3, 3, 0, "RRI" ), + EXC_FUNCENTRY_ODF( ocTable, 1, 1, 0, "SHEET" ), + EXC_FUNCENTRY_ODF( ocTables, 0, 1, 0, "SHEETS" ), + EXC_FUNCENTRY_ODF( ocNoName, 1, MX, 0, "SKEWP" ), + EXC_FUNCENTRY_ODF( ocUnichar, 1, 1, 0, "UNICHAR" ), + EXC_FUNCENTRY_ODF( ocUnicode, 1, 1, 0, "UNICODE" ), + EXC_FUNCENTRY_ODF( ocNoName, 1, MX, 0, "XOR" ) +}; + +#undef EXC_FUNCENTRY_ODF + +// ---------------------------------------------------------------------------- + +XclFunctionProvider::XclFunctionProvider( const XclRoot& rRoot ) +{ + void (XclFunctionProvider::*pFillFunc)( const XclFunctionInfo*, const XclFunctionInfo* ) = + rRoot.IsImport() ? &XclFunctionProvider::FillXclFuncMap : &XclFunctionProvider::FillScFuncMap; + + /* Only read/write functions supported in the current BIFF version. + Function tables from later BIFF versions may overwrite single functions + from earlier tables. */ + XclBiff eBiff = rRoot.GetBiff(); + if( eBiff >= EXC_BIFF2 ) + (this->*pFillFunc)( saFuncTable_2, STATIC_TABLE_END( saFuncTable_2 ) ); + if( eBiff >= EXC_BIFF3 ) + (this->*pFillFunc)( saFuncTable_3, STATIC_TABLE_END( saFuncTable_3 ) ); + if( eBiff >= EXC_BIFF4 ) + (this->*pFillFunc)( saFuncTable_4, STATIC_TABLE_END( saFuncTable_4 ) ); + if( eBiff >= EXC_BIFF5 ) + (this->*pFillFunc)( saFuncTable_5, STATIC_TABLE_END( saFuncTable_5 ) ); + if( eBiff >= EXC_BIFF8 ) + (this->*pFillFunc)( saFuncTable_8, STATIC_TABLE_END( saFuncTable_8 ) ); + (this->*pFillFunc)( saFuncTable_Odf, STATIC_TABLE_END( saFuncTable_Odf ) ); +} + +const XclFunctionInfo* XclFunctionProvider::GetFuncInfoFromXclFunc( sal_uInt16 nXclFunc ) const +{ + // only in import filter allowed + DBG_ASSERT( !maXclFuncMap.empty(), "XclFunctionProvider::GetFuncInfoFromXclFunc - wrong filter" ); + XclFuncMap::const_iterator aIt = maXclFuncMap.find( nXclFunc ); + return (aIt == maXclFuncMap.end()) ? 0 : aIt->second; +} + +const XclFunctionInfo* XclFunctionProvider::GetFuncInfoFromXclMacroName( const String& rXclMacroName ) const +{ + // only in import filter allowed, but do not test maXclMacroNameMap, it may be empty for old BIFF versions + DBG_ASSERT( !maXclFuncMap.empty(), "XclFunctionProvider::GetFuncInfoFromXclMacroName - wrong filter" ); + XclMacroNameMap::const_iterator aIt = maXclMacroNameMap.find( rXclMacroName ); + return (aIt == maXclMacroNameMap.end()) ? 0 : aIt->second; +} + +const XclFunctionInfo* XclFunctionProvider::GetFuncInfoFromOpCode( OpCode eOpCode ) const +{ + // only in export filter allowed + DBG_ASSERT( !maScFuncMap.empty(), "XclFunctionProvider::GetFuncInfoFromOpCode - wrong filter" ); + ScFuncMap::const_iterator aIt = maScFuncMap.find( eOpCode ); + return (aIt == maScFuncMap.end()) ? 0 : aIt->second; +} + +void XclFunctionProvider::FillXclFuncMap( const XclFunctionInfo* pBeg, const XclFunctionInfo* pEnd ) +{ + for( const XclFunctionInfo* pIt = pBeg; pIt != pEnd; ++pIt ) + { + if( !::get_flag( pIt->mnFlags, EXC_FUNCFLAG_EXPORTONLY ) ) + { + maXclFuncMap[ pIt->mnXclFunc ] = pIt; + if( pIt->IsMacroFunc() ) + maXclMacroNameMap[ pIt->GetMacroFuncName() ] = pIt; + } + } +} + +void XclFunctionProvider::FillScFuncMap( const XclFunctionInfo* pBeg, const XclFunctionInfo* pEnd ) +{ + for( const XclFunctionInfo* pIt = pBeg; pIt != pEnd; ++pIt ) + if( !::get_flag( pIt->mnFlags, EXC_FUNCFLAG_IMPORTONLY ) ) + maScFuncMap[ pIt->meOpCode ] = pIt; +} + +// Token array ================================================================ + +XclTokenArray::XclTokenArray( bool bVolatile ) : + mbVolatile( bVolatile ) +{ +} + +XclTokenArray::XclTokenArray( ScfUInt8Vec& rTokVec, bool bVolatile ) : + mbVolatile( bVolatile ) +{ + maTokVec.swap( rTokVec ); +} + +XclTokenArray::XclTokenArray( ScfUInt8Vec& rTokVec, ScfUInt8Vec& rExtDataVec, bool bVolatile ) : + mbVolatile( bVolatile ) +{ + maTokVec.swap( rTokVec ); + maExtDataVec.swap( rExtDataVec ); +} + +sal_uInt16 XclTokenArray::GetSize() const +{ + DBG_ASSERT( maTokVec.size() <= 0xFFFF, "XclTokenArray::GetSize - array too long" ); + return limit_cast< sal_uInt16 >( maTokVec.size() ); +} + +void XclTokenArray::ReadSize( XclImpStream& rStrm ) +{ + sal_uInt16 nSize; + rStrm >> nSize; + maTokVec.resize( nSize ); +} + +void XclTokenArray::ReadArray( XclImpStream& rStrm ) +{ + if( !maTokVec.empty() ) + rStrm.Read( &maTokVec.front(), GetSize() ); +} + +void XclTokenArray::Read( XclImpStream& rStrm ) +{ + ReadSize( rStrm ); + ReadArray( rStrm ); +} + +void XclTokenArray::WriteSize( XclExpStream& rStrm ) const +{ + rStrm << GetSize(); +} + +void XclTokenArray::WriteArray( XclExpStream& rStrm ) const +{ + if( !maTokVec.empty() ) + rStrm.Write( &maTokVec.front(), GetSize() ); + if( !maExtDataVec.empty() ) + rStrm.Write( &maExtDataVec.front(), maExtDataVec.size() ); +} + +void XclTokenArray::Write( XclExpStream& rStrm ) const +{ + WriteSize( rStrm ); + WriteArray( rStrm ); +} + +bool XclTokenArray::operator==( const XclTokenArray& rTokArr ) const +{ + return (mbVolatile == rTokArr.mbVolatile) && (maTokVec == rTokArr.maTokVec) && (maExtDataVec == rTokArr.maExtDataVec); +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclTokenArray& rTokArr ) +{ + rTokArr.Read( rStrm ); + return rStrm; +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclTokenArrayRef& rxTokArr ) +{ + if( !rxTokArr ) + rxTokArr.reset( new XclTokenArray ); + rxTokArr->Read( rStrm ); + return rStrm; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclTokenArray& rTokArr ) +{ + rTokArr.Write( rStrm ); + return rStrm; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclTokenArrayRef& rxTokArr ) +{ + if( rxTokArr.is() ) + rxTokArr->Write( rStrm ); + else + rStrm << sal_uInt16( 0 ); + return rStrm; +} + +// ---------------------------------------------------------------------------- + +XclTokenArrayIterator::XclTokenArrayIterator() : + mppScTokenBeg( 0 ), + mppScTokenEnd( 0 ), + mppScToken( 0 ), + mbSkipSpaces( false ) +{ +} + +XclTokenArrayIterator::XclTokenArrayIterator( const ScTokenArray& rScTokArr, bool bSkipSpaces ) +{ + Init( rScTokArr, bSkipSpaces ); +} + +XclTokenArrayIterator::XclTokenArrayIterator( const XclTokenArrayIterator& rTokArrIt, bool bSkipSpaces ) : + mppScTokenBeg( rTokArrIt.mppScTokenBeg ), + mppScTokenEnd( rTokArrIt.mppScTokenEnd ), + mppScToken( rTokArrIt.mppScToken ), + mbSkipSpaces( bSkipSpaces ) +{ + SkipSpaces(); +} + +void XclTokenArrayIterator::Init() +{ + mppScTokenBeg = mppScTokenEnd = mppScToken = 0; +} + +void XclTokenArrayIterator::Init( const ScTokenArray& rScTokArr, bool bSkipSpaces ) +{ + USHORT nTokArrLen = rScTokArr.GetLen(); + mppScTokenBeg = static_cast< const FormulaToken* const* >( nTokArrLen ? rScTokArr.GetArray() : 0 ); + mppScTokenEnd = mppScTokenBeg ? (mppScTokenBeg + nTokArrLen) : 0; + mppScToken = (mppScTokenBeg != mppScTokenEnd) ? mppScTokenBeg : 0; + mbSkipSpaces = bSkipSpaces; + SkipSpaces(); +} + +XclTokenArrayIterator& XclTokenArrayIterator::operator++() +{ + NextRawToken(); + SkipSpaces(); + return *this; +} + +void XclTokenArrayIterator::NextRawToken() +{ + if( mppScToken ) + if( (++mppScToken == mppScTokenEnd) || !*mppScToken ) + mppScToken = 0; +} + +void XclTokenArrayIterator::SkipSpaces() +{ + if( mbSkipSpaces ) + while( Is() && ((*this)->GetOpCode() == ocSpaces) ) + NextRawToken(); +} + +// strings and string lists --------------------------------------------------- + +bool XclTokenArrayHelper::GetTokenString( String& rString, const FormulaToken& rScToken ) +{ + bool bIsStr = (rScToken.GetType() == svString) && (rScToken.GetOpCode() == ocPush); + if( bIsStr ) rString = rScToken.GetString(); + return bIsStr; +} + +bool XclTokenArrayHelper::GetString( String& rString, const ScTokenArray& rScTokArr ) +{ + XclTokenArrayIterator aIt( rScTokArr, true ); + // something is following the string token -> error + return aIt.Is() && GetTokenString( rString, *aIt ) && !++aIt; +} + +bool XclTokenArrayHelper::GetStringList( String& rStringList, const ScTokenArray& rScTokArr, sal_Unicode cSep ) +{ + bool bRet = true; + String aString; + XclTokenArrayIterator aIt( rScTokArr, true ); + enum { STATE_START, STATE_STR, STATE_SEP, STATE_END } eState = STATE_START; + while( eState != STATE_END ) switch( eState ) + { + case STATE_START: + eState = aIt.Is() ? STATE_STR : STATE_END; + break; + case STATE_STR: + bRet = GetTokenString( aString, *aIt ); + if( bRet ) rStringList.Append( aString ); + eState = (bRet && (++aIt).Is()) ? STATE_SEP : STATE_END; + break; + case STATE_SEP: + bRet = aIt->GetOpCode() == ocSep; + if( bRet ) rStringList.Append( cSep ); + eState = (bRet && (++aIt).Is()) ? STATE_STR : STATE_END; + break; + default:; + } + return bRet; +} + +void XclTokenArrayHelper::ConvertStringToList( ScTokenArray& rScTokArr, sal_Unicode cStringSep, bool bTrimLeadingSpaces ) +{ + String aString; + if( GetString( aString, rScTokArr ) ) + { + rScTokArr.Clear(); + xub_StrLen nTokenCnt = aString.GetTokenCount( cStringSep ); + xub_StrLen nStringIx = 0; + for( xub_StrLen nToken = 0; nToken < nTokenCnt; ++nToken ) + { + String aToken( aString.GetToken( 0, cStringSep, nStringIx ) ); + if( bTrimLeadingSpaces ) + aToken.EraseLeadingChars( ' ' ); + if( nToken > 0 ) + rScTokArr.AddOpCode( ocSep ); + rScTokArr.AddString( aToken ); + } + } +} + +// shared formulas ------------------------------------------------------------ + +const ScTokenArray* XclTokenArrayHelper::GetSharedFormula( const XclRoot& rRoot, const ScTokenArray& rScTokArr ) +{ + if( rScTokArr.GetLen() == 1 ) + if( const FormulaToken* pScToken = rScTokArr.GetArray()[ 0 ] ) + if( pScToken->GetOpCode() == ocName ) + if( ScRangeData* pData = rRoot.GetNamedRanges().FindIndex( pScToken->GetIndex() ) ) + if( pData->HasType( RT_SHARED ) ) + return pData->GetCode(); + return 0; +} + +// multiple operations -------------------------------------------------------- + +namespace { + +inline bool lclGetAddress( ScAddress& rAddress, const FormulaToken& rToken ) +{ + OpCode eOpCode = rToken.GetOpCode(); + bool bIsSingleRef = (eOpCode == ocPush) && (rToken.GetType() == svSingleRef); + if( bIsSingleRef ) + { + const ScSingleRefData& rRef = static_cast<const ScToken&>(rToken).GetSingleRef(); + rAddress.Set( rRef.nCol, rRef.nRow, rRef.nTab ); + bIsSingleRef = !rRef.IsDeleted(); + } + return bIsSingleRef; +} + +} // namespace + +bool XclTokenArrayHelper::GetMultipleOpRefs( XclMultipleOpRefs& rRefs, const ScTokenArray& rScTokArr ) +{ + rRefs.mbDblRefMode = false; + enum + { + stBegin, stTableOp, stOpen, stFormula, stFormulaSep, + stColFirst, stColFirstSep, stColRel, stColRelSep, + stRowFirst, stRowFirstSep, stRowRel, stClose, stError + } eState = stBegin; // last read token + for( XclTokenArrayIterator aIt( rScTokArr, true ); aIt.Is() && (eState != stError); ++aIt ) + { + OpCode eOpCode = aIt->GetOpCode(); + bool bIsSep = eOpCode == ocSep; + switch( eState ) + { + case stBegin: + eState = (eOpCode == ocTableOp) ? stTableOp : stError; + break; + case stTableOp: + eState = (eOpCode == ocOpen) ? stOpen : stError; + break; + case stOpen: + eState = lclGetAddress( rRefs.maFmlaScPos, *aIt ) ? stFormula : stError; + break; + case stFormula: + eState = bIsSep ? stFormulaSep : stError; + break; + case stFormulaSep: + eState = lclGetAddress( rRefs.maColFirstScPos, *aIt ) ? stColFirst : stError; + break; + case stColFirst: + eState = bIsSep ? stColFirstSep : stError; + break; + case stColFirstSep: + eState = lclGetAddress( rRefs.maColRelScPos, *aIt ) ? stColRel : stError; + break; + case stColRel: + eState = bIsSep ? stColRelSep : ((eOpCode == ocClose) ? stClose : stError); + break; + case stColRelSep: + eState = lclGetAddress( rRefs.maRowFirstScPos, *aIt ) ? stRowFirst : stError; + rRefs.mbDblRefMode = true; + break; + case stRowFirst: + eState = bIsSep ? stRowFirstSep : stError; + break; + case stRowFirstSep: + eState = lclGetAddress( rRefs.maRowRelScPos, *aIt ) ? stRowRel : stError; + break; + case stRowRel: + eState = (eOpCode == ocClose) ? stClose : stError; + break; + default: + eState = stError; + } + } + return eState == stClose; +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xlpage.cxx b/sc/source/filter/excel/xlpage.cxx new file mode 100644 index 000000000000..a59c6f761667 --- /dev/null +++ b/sc/source/filter/excel/xlpage.cxx @@ -0,0 +1,248 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xlpage.hxx" +#include <sfx2/printer.hxx> +#include <editeng/svxenum.hxx> +#include <editeng/paperinf.hxx> +#include <vcl/svapp.hxx> +#include "scitems.hxx" +#include <editeng/brshitem.hxx> +#include "global.hxx" +#include "xlconst.hxx" + +// Paper size ================================================================= + +struct XclPaperSize +{ + Paper mePaper; /// SVX paper size identifier. + long mnWidth; /// Paper width in twips. + long mnHeight; /// Paper height in twips. +}; + +#define IN2TWIPS( v ) ((long)((v) * EXC_TWIPS_PER_INCH + 0.5)) +#define MM2TWIPS( v ) ((long)((v) * EXC_TWIPS_PER_INCH / CM_PER_INCH / 10.0 + 0.5)) + +static const XclPaperSize pPaperSizeTable[] = +{ +/* 0*/ { PAPER_USER, 0, 0 }, // undefined + { PAPER_LETTER, IN2TWIPS( 8.5 ), IN2TWIPS( 11 ) }, // Letter + { PAPER_USER, IN2TWIPS( 8.5 ), IN2TWIPS( 11 ) }, // Letter Small + { PAPER_TABLOID, IN2TWIPS( 11 ), IN2TWIPS( 17 ) }, // Tabloid + { PAPER_LEDGER, IN2TWIPS( 17 ), IN2TWIPS( 11 ) }, // Ledger +/* 5*/ { PAPER_LEGAL, IN2TWIPS( 8.5 ), IN2TWIPS( 14 ) }, // Legal + { PAPER_STATEMENT, IN2TWIPS( 5.5 ), IN2TWIPS( 8.5 ) }, // Statement + { PAPER_EXECUTIVE, IN2TWIPS( 7.25 ), IN2TWIPS( 10.5 ) }, // Executive + { PAPER_A3, MM2TWIPS( 297 ), MM2TWIPS( 420 ) }, // A3 + { PAPER_A4, MM2TWIPS( 210 ), MM2TWIPS( 297 ) }, // A4 +/* 10*/ { PAPER_USER, MM2TWIPS( 210 ), MM2TWIPS( 297 ) }, // A4 Small + { PAPER_A5, MM2TWIPS( 148 ), MM2TWIPS( 210 ) }, // A5 + //See: http://wiki.services.openoffice.org/wiki/DefaultPaperSize comments + //near DMPAPER_B4 in vcl + //i.e. + //http://msdn.microsoft.com/en-us/library/bb241398.aspx makes the claim: + //xlPaperB4 12 B4 (250 mm x 354 mm) + //xlPaperB5 13 A5 (148 mm x 210 mm) + //but, a paper enum called B5 is surely not actually "A5", and furthermore + //the XlPaperSize enumeration otherwise follows the DMPAPER values + //http://msdn.microsoft.com/en-us/library/ms776398(VS.85).aspx + //which has + //DMPAPER_B4 12 B4 (JIS) 250 x 354 + //DMPAPER_B5 13 B5 (JIS) 182 x 257 mm + //which claim them to be the JIS sizes. Though that document then gives + //"B4 (JIS)" an *ISO* B4 size in the text, but + //http://partners.adobe.com/public/developer/en/ps/5003.PPD_Spec_v4.3.pdf + //claims that the MS DMPAPER_B4 and DMPAPER_B5 truly are the JIS sizes + //which at least makes some sort of sense. (cmc) + { PAPER_B4_JIS, MM2TWIPS( 257 ), MM2TWIPS( 364 ) }, // B4 (JIS) + { PAPER_B5_JIS, MM2TWIPS( 182 ), MM2TWIPS( 257 ) }, // B5 (JIS) + { PAPER_USER, IN2TWIPS( 8.5 ), IN2TWIPS( 13 ) }, // Folio +/* 15*/ { PAPER_QUARTO, MM2TWIPS( 215 ), MM2TWIPS( 275 ) }, // Quarto + { PAPER_10x14, IN2TWIPS( 10 ), IN2TWIPS( 14 ) }, // 10x14 + { PAPER_USER, IN2TWIPS( 11 ), IN2TWIPS( 17 ) }, // 11x17 + { PAPER_USER, IN2TWIPS( 8.5 ), IN2TWIPS( 11 ) }, // Note + { PAPER_ENV_9, IN2TWIPS( 3.875 ), IN2TWIPS( 8.875 ) }, // Envelope #9 +/* 20*/ { PAPER_ENV_10, IN2TWIPS( 4.125 ), IN2TWIPS( 9.5 ) }, // Envelope #10 + { PAPER_ENV_11, IN2TWIPS( 4.5 ), IN2TWIPS( 10.375 ) }, // Envelope #11 + { PAPER_ENV_12, IN2TWIPS( 4.75 ), IN2TWIPS( 11 ) }, // Envelope #12 + { PAPER_ENV_14, IN2TWIPS( 5 ), IN2TWIPS( 11.5 ) }, // Envelope #14 + { PAPER_C, IN2TWIPS( 17 ), IN2TWIPS( 22 ) }, // ANSI-C +/* 25*/ { PAPER_D, IN2TWIPS( 22 ), IN2TWIPS( 34 ) }, // ANSI-D + { PAPER_E, IN2TWIPS( 34 ), IN2TWIPS( 44 ) }, // ANSI-E + { PAPER_ENV_DL, MM2TWIPS( 110 ), MM2TWIPS( 220 ) }, // Envelope DL + { PAPER_ENV_C5, MM2TWIPS( 162 ), MM2TWIPS( 229 ) }, // Envelope C5 + { PAPER_ENV_C3, MM2TWIPS( 324 ), MM2TWIPS( 458 ) }, // Envelope C3 +/* 30*/ { PAPER_ENV_C4, MM2TWIPS( 229 ), MM2TWIPS( 324 ) }, // Envelope C4 + { PAPER_ENV_C6, MM2TWIPS( 114 ), MM2TWIPS( 162 ) }, // Envelope C6 + { PAPER_ENV_C65, MM2TWIPS( 114 ), MM2TWIPS( 229 ) }, // Envelope C65 + { PAPER_B4_ISO, MM2TWIPS( 250 ), MM2TWIPS( 353 ) }, // B4 (ISO) + { PAPER_B5_ISO, MM2TWIPS( 176 ), MM2TWIPS( 250 ) }, // B5 (ISO) +/* 35*/ { PAPER_B6_ISO, MM2TWIPS( 125 ), MM2TWIPS( 176 ) }, // B6 (ISO) + { PAPER_ENV_ITALY, MM2TWIPS( 110 ), MM2TWIPS( 230 ) }, // Envelope Italy + { PAPER_ENV_MONARCH, IN2TWIPS( 3.875 ), IN2TWIPS( 7.5 ) }, // Envelope Monarch + { PAPER_ENV_PERSONAL, IN2TWIPS( 3.625 ), IN2TWIPS( 6.5 ) }, // 6 3/4 Envelope + { PAPER_FANFOLD_US, IN2TWIPS( 14.875 ), IN2TWIPS( 11 ) }, // US Std Fanfold +/* 40*/ { PAPER_FANFOLD_DE, IN2TWIPS( 8.5 ), IN2TWIPS( 12 ) }, // German Std Fanfold + { PAPER_FANFOLD_LEGAL_DE, IN2TWIPS( 8.5 ), IN2TWIPS( 13 ) }, // German Legal Fanfold + { PAPER_B4_ISO, MM2TWIPS( 250 ), MM2TWIPS( 353 ) }, // B4 (ISO) + { PAPER_POSTCARD_JP,MM2TWIPS( 100 ), MM2TWIPS( 148 ) }, // Japanese Postcard + { PAPER_9x11, IN2TWIPS( 9 ), IN2TWIPS( 11 ) }, // 9x11 +/* 45*/ { PAPER_10x11, IN2TWIPS( 10 ), IN2TWIPS( 11 ) }, // 10x11 + { PAPER_15x11, IN2TWIPS( 15 ), IN2TWIPS( 11 ) }, // 15x11 + { PAPER_ENV_INVITE, MM2TWIPS( 220 ), MM2TWIPS( 220 ) }, // Envelope Invite + { PAPER_USER, 0, 0 }, // undefined + { PAPER_USER, 0, 0 }, // undefined +/* 50*/ { PAPER_USER, IN2TWIPS( 9.5 ), IN2TWIPS( 12 ) }, // Letter Extra + { PAPER_USER, IN2TWIPS( 9.5 ), IN2TWIPS( 15 ) }, // Legal Extra + { PAPER_USER, IN2TWIPS( 11.69 ), IN2TWIPS( 18 ) }, // Tabloid Extra + { PAPER_USER, MM2TWIPS( 235 ), MM2TWIPS( 322 ) }, // A4 Extra + { PAPER_USER, IN2TWIPS( 8.5 ), IN2TWIPS( 11 ) }, // Letter Transverse +/* 55*/ { PAPER_USER, MM2TWIPS( 210 ), MM2TWIPS( 297 ) }, // A4 Transverse + { PAPER_USER, IN2TWIPS( 9.5 ), IN2TWIPS( 12 ) }, // Letter Extra Transverse + { PAPER_A_PLUS, MM2TWIPS( 227 ), MM2TWIPS( 356 ) }, // Super A/A4 + { PAPER_B_PLUS, MM2TWIPS( 305 ), MM2TWIPS( 487 ) }, // Super B/A3 + { PAPER_LETTER_PLUS,IN2TWIPS( 8.5 ), IN2TWIPS( 12.69 ) }, // Letter Plus +/* 60*/ { PAPER_A4_PLUS, MM2TWIPS( 210 ), MM2TWIPS( 330 ) }, // A4 Plus + { PAPER_USER, MM2TWIPS( 148 ), MM2TWIPS( 210 ) }, // A5 Transverse + { PAPER_USER, MM2TWIPS( 182 ), MM2TWIPS( 257 ) }, // B5 (JIS) Transverse + { PAPER_USER, MM2TWIPS( 322 ), MM2TWIPS( 445 ) }, // A3 Extra + { PAPER_USER, MM2TWIPS( 174 ), MM2TWIPS( 235 ) }, // A5 Extra +/* 65*/ { PAPER_USER, MM2TWIPS( 201 ), MM2TWIPS( 276 ) }, // B5 (ISO) Extra + { PAPER_A2, MM2TWIPS( 420 ), MM2TWIPS( 594 ) }, // A2 + { PAPER_USER, MM2TWIPS( 297 ), MM2TWIPS( 420 ) }, // A3 Transverse + { PAPER_USER, MM2TWIPS( 322 ), MM2TWIPS( 445 ) }, // A3 Extra Transverse + { PAPER_DOUBLEPOSTCARD_JP, MM2TWIPS( 200 ), MM2TWIPS( 148 ) }, // Double Japanese Postcard +/* 70*/ { PAPER_A6, MM2TWIPS( 105 ), MM2TWIPS( 148 ) }, // A6 + { PAPER_USER, 0, 0 }, // undefined + { PAPER_USER, 0, 0 }, // undefined + { PAPER_USER, 0, 0 }, // undefined + { PAPER_USER, 0, 0 }, // undefined +/* 75*/ { PAPER_USER, IN2TWIPS( 11 ), IN2TWIPS( 8.5 ) }, // Letter Rotated + { PAPER_USER, MM2TWIPS( 420 ), MM2TWIPS( 297 ) }, // A3 Rotated + { PAPER_USER, MM2TWIPS( 297 ), MM2TWIPS( 210 ) }, // A4 Rotated + { PAPER_USER, MM2TWIPS( 210 ), MM2TWIPS( 148 ) }, // A5 Rotated + { PAPER_USER, MM2TWIPS( 364 ), MM2TWIPS( 257 ) }, // B4 (JIS) Rotated +/* 80*/ { PAPER_USER, MM2TWIPS( 257 ), MM2TWIPS( 182 ) }, // B5 (JIS) Rotated + { PAPER_USER, MM2TWIPS( 148 ), MM2TWIPS( 100 ) }, // Japanese Postcard Rotated + { PAPER_USER, MM2TWIPS( 148 ), MM2TWIPS( 200 ) }, // Double Japanese Postcard Rotated + { PAPER_USER, MM2TWIPS( 148 ), MM2TWIPS( 105 ) }, // A6 Rotated + { PAPER_USER, 0, 0 }, // undefined +/* 85*/ { PAPER_USER, 0, 0 }, // undefined + { PAPER_USER, 0, 0 }, // undefined + { PAPER_USER, 0, 0 }, // undefined + { PAPER_B6_JIS, MM2TWIPS( 128 ), MM2TWIPS( 182 ) }, // B6 (JIS) + { PAPER_USER, MM2TWIPS( 182 ), MM2TWIPS( 128 ) }, // B6 (JIS) Rotated +/* 90*/ { PAPER_12x11, IN2TWIPS( 12 ), IN2TWIPS( 11 ) } // 12x11 +}; + +#undef IN2TWIPS +#undef MM2TWIPS + +// Page settings ============================================================== + +XclPageData::XclPageData() +{ + SetDefaults(); +} + +XclPageData::~XclPageData() +{ + // SvxBrushItem incomplete in header +} + +void XclPageData::SetDefaults() +{ + maHorPageBreaks.clear(); + maVerPageBreaks.clear(); + mxBrushItem.reset(); + maHeader.Erase(); + maFooter.Erase(); + mfLeftMargin = mfRightMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_LR ); + mfTopMargin = mfBottomMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_TB ); + mfHeaderMargin = mfFooterMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_HF ); + mfHdrLeftMargin = mfHdrRightMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_HLR ); + mfFtrLeftMargin = mfFtrRightMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_FLR ); + mnPaperSize = EXC_PAPERSIZE_DEFAULT; + mnCopies = 1; + mnStartPage = 0; + mnScaling = 100; + mnFitToWidth = mnFitToHeight = 1; + mnHorPrintRes = mnVerPrintRes = 300; + mbValid = false; + mbPortrait = true; + mbPrintInRows = mbBlackWhite = mbDraftQuality = mbPrintNotes = mbManualStart = mbFitToPages = false; + mbHorCenter = mbVerCenter = mbPrintHeadings = mbPrintGrid = false; +} + +Size XclPageData::GetScPaperSize() const +{ + const XclPaperSize* pEntry = pPaperSizeTable; + if( mnPaperSize < STATIC_TABLE_SIZE( pPaperSizeTable ) ) + pEntry += mnPaperSize; + + Size aSize; + if( pEntry->mePaper == PAPER_USER ) + aSize = Size( pEntry->mnWidth, pEntry->mnHeight ); + else + aSize = SvxPaperInfo::GetPaperSize( pEntry->mePaper ); + + // invalid size -> back to default + if( !aSize.Width() || !aSize.Height() ) + aSize = SvxPaperInfo::GetDefaultPaperSize(); + + if( !mbPortrait ) + ::std::swap( aSize.Width(), aSize.Height() ); + + return aSize; +} + +void XclPageData::SetScPaperSize( const Size& rSize, bool bPortrait ) +{ + mbPortrait = bPortrait; + mnPaperSize = 0; + long nWidth = bPortrait ? rSize.Width() : rSize.Height(); + long nHeight = bPortrait ? rSize.Height() : rSize.Width(); + long nMaxWDiff = 80; + long nMaxHDiff = 50; + for( const XclPaperSize* pEntry = pPaperSizeTable; pEntry != STATIC_TABLE_END( pPaperSizeTable ); ++pEntry ) + { + long nWDiff = Abs( pEntry->mnWidth - nWidth ); + long nHDiff = Abs( pEntry->mnHeight - nHeight ); + if( ((nWDiff <= nMaxWDiff) && (nHDiff < nMaxHDiff)) || + ((nWDiff < nMaxWDiff) && (nHDiff <= nMaxHDiff)) ) + { + mnPaperSize = static_cast< sal_uInt16 >( pEntry - pPaperSizeTable ); + nMaxWDiff = nWDiff; + nMaxHDiff = nHDiff; + } + } +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xlpivot.cxx b/sc/source/filter/excel/xlpivot.cxx new file mode 100644 index 000000000000..ed7aadd0a410 --- /dev/null +++ b/sc/source/filter/excel/xlpivot.cxx @@ -0,0 +1,1031 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "dpgroup.hxx" +#include "dpsave.hxx" +#include "xestream.hxx" +#include "xistream.hxx" +#include "xestring.hxx" +#include "xlpivot.hxx" +#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp> + +using ::com::sun::star::sheet::GeneralFunction; +using ::com::sun::star::sheet::DataPilotFieldOrientation; + +namespace ScDPSortMode = ::com::sun::star::sheet::DataPilotFieldSortMode; +namespace ScDPShowItemsMode = ::com::sun::star::sheet::DataPilotFieldShowItemsMode; +namespace ScDPLayoutMode = ::com::sun::star::sheet::DataPilotFieldLayoutMode; +namespace ScDPRefItemType = ::com::sun::star::sheet::DataPilotFieldReferenceItemType; +namespace ScDPGroupBy = ::com::sun::star::sheet::DataPilotFieldGroupBy; + +// ============================================================================ +// Pivot cache +// ============================================================================ + +XclPCItem::XclPCItem() : + meType( EXC_PCITEM_INVALID ) +{ +} + +XclPCItem::~XclPCItem() +{ +} + +void XclPCItem::SetEmpty() +{ + meType = EXC_PCITEM_EMPTY; + maText.Erase(); +} + +void XclPCItem::SetText( const String& rText ) +{ + meType = EXC_PCITEM_TEXT; + maText = rText; +} + +void XclPCItem::SetDouble( double fValue ) +{ + meType = EXC_PCITEM_DOUBLE; + //! TODO convert double to string + maText.Erase(); + mfValue = fValue; +} + +void XclPCItem::SetDateTime( const DateTime& rDateTime ) +{ + meType = EXC_PCITEM_DATETIME; + //! TODO convert date to string + maText.Erase(); + maDateTime = rDateTime; +} + +void XclPCItem::SetInteger( sal_Int16 nValue ) +{ + meType = EXC_PCITEM_INTEGER; + maText = String::CreateFromInt32( nValue ); + mnValue = nValue; +} + +void XclPCItem::SetError( sal_uInt16 nError ) +{ + meType = EXC_PCITEM_ERROR; + //! TODO convert error to string + maText.Erase(); + mnError = nError; +} + +void XclPCItem::SetBool( bool bValue ) +{ + meType = EXC_PCITEM_BOOL; + //! TODO convert boolean to string + maText.Erase(); + mbValue = bValue; +} + +// ---------------------------------------------------------------------------- + +bool XclPCItem::IsEqual( const XclPCItem& rItem ) const +{ + if( meType == rItem.meType ) switch( meType ) + { + case EXC_PCITEM_INVALID: return true; + case EXC_PCITEM_EMPTY: return true; + case EXC_PCITEM_TEXT: return maText == rItem.maText; + case EXC_PCITEM_DOUBLE: return mfValue == rItem.mfValue; + case EXC_PCITEM_DATETIME: return maDateTime == rItem.maDateTime; + case EXC_PCITEM_INTEGER: return mnValue == rItem.mnValue; + case EXC_PCITEM_BOOL: return mbValue == rItem.mbValue; + case EXC_PCITEM_ERROR: return mnError == rItem.mnError; + default: DBG_ERRORFILE( "XclPCItem::IsEqual - unknown pivot cache item type" ); + } + return false; +} + +bool XclPCItem::IsEmpty() const +{ + return meType == EXC_PCITEM_EMPTY; +} + +const String* XclPCItem::GetText() const +{ + return (meType == EXC_PCITEM_TEXT) ? &maText : 0; +} + +const double* XclPCItem::GetDouble() const +{ + return (meType == EXC_PCITEM_DOUBLE) ? &mfValue : 0; +} + +const DateTime* XclPCItem::GetDateTime() const +{ + return (meType == EXC_PCITEM_DATETIME) ? &maDateTime : 0; +} + +const sal_Int16* XclPCItem::GetInteger() const +{ + return (meType == EXC_PCITEM_INTEGER) ? &mnValue : 0; +} + +const sal_uInt16* XclPCItem::GetError() const +{ + return (meType == EXC_PCITEM_ERROR) ? &mnError : 0; +} + +const bool* XclPCItem::GetBool() const +{ + return (meType == EXC_PCITEM_BOOL) ? &mbValue : 0; +} + +// Field settings ============================================================= + +XclPCFieldInfo::XclPCFieldInfo() : + mnFlags( 0 ), + mnGroupChild( 0 ), + mnGroupBase( 0 ), + mnVisItems( 0 ), + mnGroupItems( 0 ), + mnBaseItems( 0 ), + mnOrigItems( 0 ) +{ +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclPCFieldInfo& rInfo ) +{ + rStrm >> rInfo.mnFlags + >> rInfo.mnGroupChild + >> rInfo.mnGroupBase + >> rInfo.mnVisItems + >> rInfo.mnGroupItems + >> rInfo.mnBaseItems + >> rInfo.mnOrigItems; + if( rStrm.GetRecLeft() >= 3 ) + rInfo.maName = rStrm.ReadUniString(); + else + rInfo.maName.Erase(); + return rStrm; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclPCFieldInfo& rInfo ) +{ + return rStrm + << rInfo.mnFlags + << rInfo.mnGroupChild + << rInfo.mnGroupBase + << rInfo.mnVisItems + << rInfo.mnGroupItems + << rInfo.mnBaseItems + << rInfo.mnOrigItems + << XclExpString( rInfo.maName ); +} + +// Numeric grouping field settings ============================================ + +XclPCNumGroupInfo::XclPCNumGroupInfo() : + mnFlags( EXC_SXNUMGROUP_AUTOMIN | EXC_SXNUMGROUP_AUTOMAX ) +{ + SetNumType(); +} + +void XclPCNumGroupInfo::SetNumType() +{ + SetXclDataType( EXC_SXNUMGROUP_TYPE_NUM ); +} + +sal_Int32 XclPCNumGroupInfo::GetScDateType() const +{ + sal_Int32 nScType = 0; + switch( GetXclDataType() ) + { + case EXC_SXNUMGROUP_TYPE_SEC: nScType = ScDPGroupBy::SECONDS; break; + case EXC_SXNUMGROUP_TYPE_MIN: nScType = ScDPGroupBy::MINUTES; break; + case EXC_SXNUMGROUP_TYPE_HOUR: nScType = ScDPGroupBy::HOURS; break; + case EXC_SXNUMGROUP_TYPE_DAY: nScType = ScDPGroupBy::DAYS; break; + case EXC_SXNUMGROUP_TYPE_MONTH: nScType = ScDPGroupBy::MONTHS; break; + case EXC_SXNUMGROUP_TYPE_QUART: nScType = ScDPGroupBy::QUARTERS; break; + case EXC_SXNUMGROUP_TYPE_YEAR: nScType = ScDPGroupBy::YEARS; break; + default: DBG_ERROR1( "XclPCNumGroupInfo::GetScDateType - unexpected date type %d", GetXclDataType() ); + } + return nScType; +} + +void XclPCNumGroupInfo::SetScDateType( sal_Int32 nScType ) +{ + sal_uInt16 nXclType = EXC_SXNUMGROUP_TYPE_NUM; + switch( nScType ) + { + case ScDPGroupBy::SECONDS: nXclType = EXC_SXNUMGROUP_TYPE_SEC; break; + case ScDPGroupBy::MINUTES: nXclType = EXC_SXNUMGROUP_TYPE_MIN; break; + case ScDPGroupBy::HOURS: nXclType = EXC_SXNUMGROUP_TYPE_HOUR; break; + case ScDPGroupBy::DAYS: nXclType = EXC_SXNUMGROUP_TYPE_DAY; break; + case ScDPGroupBy::MONTHS: nXclType = EXC_SXNUMGROUP_TYPE_MONTH; break; + case ScDPGroupBy::QUARTERS: nXclType = EXC_SXNUMGROUP_TYPE_QUART; break; + case ScDPGroupBy::YEARS: nXclType = EXC_SXNUMGROUP_TYPE_YEAR; break; + default: DBG_ERROR1( "XclPCNumGroupInfo::SetScDateType - unexpected date type %d", nScType ); + } + SetXclDataType( nXclType ); +} + +sal_uInt16 XclPCNumGroupInfo::GetXclDataType() const +{ + return ::extract_value< sal_uInt16 >( mnFlags, 2, 4 ); +} + +void XclPCNumGroupInfo::SetXclDataType( sal_uInt16 nXclType ) +{ + ::insert_value( mnFlags, nXclType, 2, 4 ); +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclPCNumGroupInfo& rInfo ) +{ + return rStrm >> rInfo.mnFlags; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclPCNumGroupInfo& rInfo ) +{ + return rStrm << rInfo.mnFlags; +} + +// Base class for pivot cache fields ========================================== + +XclPCField::XclPCField( XclPCFieldType eFieldType, sal_uInt16 nFieldIdx ) : + meFieldType( eFieldType ), + mnFieldIdx( nFieldIdx ) +{ +} + +XclPCField::~XclPCField() +{ +} + +bool XclPCField::IsSupportedField() const +{ + return (meFieldType != EXC_PCFIELD_CALCED) && (meFieldType != EXC_PCFIELD_UNKNOWN); +} + +bool XclPCField::IsStandardField() const +{ + return meFieldType == EXC_PCFIELD_STANDARD; +} + +//UNUSED2008-05 bool XclPCField::IsCalculatedField() const +//UNUSED2008-05 { +//UNUSED2008-05 return meFieldType == EXC_PCFIELD_CALCED; +//UNUSED2008-05 } + +bool XclPCField::IsStdGroupField() const +{ + return meFieldType == EXC_PCFIELD_STDGROUP; +} + +bool XclPCField::IsNumGroupField() const +{ + return meFieldType == EXC_PCFIELD_NUMGROUP; +} + +bool XclPCField::IsDateGroupField() const +{ + return (meFieldType == EXC_PCFIELD_DATEGROUP) || (meFieldType == EXC_PCFIELD_DATECHILD); +} + +bool XclPCField::IsGroupField() const +{ + return IsStdGroupField() || IsNumGroupField() || IsDateGroupField(); +} + +bool XclPCField::IsGroupBaseField() const +{ + return ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ); +} + +bool XclPCField::IsGroupChildField() const +{ + return (meFieldType == EXC_PCFIELD_STDGROUP) || (meFieldType == EXC_PCFIELD_DATECHILD); +} + +sal_uInt16 XclPCField::GetBaseFieldIndex() const +{ + return IsGroupChildField() ? maFieldInfo.mnGroupBase : mnFieldIdx; +} + +bool XclPCField::HasOrigItems() const +{ + return IsSupportedField() && ((maFieldInfo.mnOrigItems > 0) || HasPostponedItems()); +} + +bool XclPCField::HasInlineItems() const +{ + return (IsStandardField() || IsGroupField()) && ((maFieldInfo.mnGroupItems > 0) || (maFieldInfo.mnOrigItems > 0)); +} + +bool XclPCField::HasPostponedItems() const +{ + return IsStandardField() && ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_POSTPONE ); +} + +bool XclPCField::Has16BitIndexes() const +{ + return IsStandardField() && ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT ); +} + +// Pivot cache settings ======================================================= + +/** Contains data for a pivot cache (SXDB record). */ +XclPCInfo::XclPCInfo() : + mnSrcRecs( 0 ), + mnStrmId( 0xFFFF ), + mnFlags( EXC_SXDB_DEFAULTFLAGS ), + mnBlockRecs( EXC_SXDB_BLOCKRECS ), + mnStdFields( 0 ), + mnTotalFields( 0 ), + mnSrcType( EXC_SXDB_SRC_SHEET ) +{ +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclPCInfo& rInfo ) +{ + rStrm >> rInfo.mnSrcRecs + >> rInfo.mnStrmId + >> rInfo.mnFlags + >> rInfo.mnBlockRecs + >> rInfo.mnStdFields + >> rInfo.mnTotalFields; + rStrm.Ignore( 2 ); + rStrm >> rInfo.mnSrcType; + rInfo.maUserName = rStrm.ReadUniString(); + return rStrm; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclPCInfo& rInfo ) +{ + return rStrm + << rInfo.mnSrcRecs + << rInfo.mnStrmId + << rInfo.mnFlags + << rInfo.mnBlockRecs + << rInfo.mnStdFields + << rInfo.mnTotalFields + << sal_uInt16( 0 ) + << rInfo.mnSrcType + << XclExpString( rInfo.maUserName ); +} + +// ============================================================================ +// Pivot table +// ============================================================================ + +// cached name ================================================================ + +XclImpStream& operator>>( XclImpStream& rStrm, XclPTCachedName& rCachedName ) +{ + sal_uInt16 nStrLen; + rStrm >> nStrLen; + rCachedName.mbUseCache = nStrLen == EXC_PT_NOSTRING; + if( rCachedName.mbUseCache ) + rCachedName.maName.Erase(); + else + rCachedName.maName = rStrm.ReadUniString( nStrLen ); + return rStrm; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclPTCachedName& rCachedName ) +{ + if( rCachedName.mbUseCache ) + rStrm << EXC_PT_NOSTRING; + else + rStrm << XclExpString( rCachedName.maName, EXC_STR_DEFAULT, EXC_PT_MAXSTRLEN ); + return rStrm; +} + +// ---------------------------------------------------------------------------- + +const String* XclPTVisNameInfo::GetVisName() const +{ + return HasVisName() ? &maVisName.maName : 0; +} + +void XclPTVisNameInfo::SetVisName( const String& rName ) +{ + maVisName.maName = rName; + maVisName.mbUseCache = rName.Len() == 0; +} + +// Field item settings ======================================================== + +XclPTItemInfo::XclPTItemInfo() : + mnType( EXC_SXVI_TYPE_DATA ), + mnFlags( EXC_SXVI_DEFAULTFLAGS ), + mnCacheIdx( EXC_SXVI_DEFAULT_CACHE ) +{ +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclPTItemInfo& rInfo ) +{ + return rStrm + >> rInfo.mnType + >> rInfo.mnFlags + >> rInfo.mnCacheIdx + >> rInfo.maVisName; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclPTItemInfo& rInfo ) +{ + return rStrm + << rInfo.mnType + << rInfo.mnFlags + << rInfo.mnCacheIdx + << rInfo.maVisName; +} + +// General field settings ===================================================== + +XclPTFieldInfo::XclPTFieldInfo() : + mnAxes( EXC_SXVD_AXIS_NONE ), + mnSubtCount( 1 ), + mnSubtotals( EXC_SXVD_SUBT_DEFAULT ), + mnItemCount( 0 ), + mnCacheIdx( EXC_SXVD_DEFAULT_CACHE ) +{ +} + +DataPilotFieldOrientation XclPTFieldInfo::GetApiOrient( sal_uInt16 nMask ) const +{ + using namespace ::com::sun::star::sheet; + DataPilotFieldOrientation eOrient = DataPilotFieldOrientation_HIDDEN; + sal_uInt16 nUsedAxes = mnAxes & nMask; + if( nUsedAxes & EXC_SXVD_AXIS_ROW ) + eOrient = DataPilotFieldOrientation_ROW; + else if( nUsedAxes & EXC_SXVD_AXIS_COL ) + eOrient = DataPilotFieldOrientation_COLUMN; + else if( nUsedAxes & EXC_SXVD_AXIS_PAGE ) + eOrient = DataPilotFieldOrientation_PAGE; + else if( nUsedAxes & EXC_SXVD_AXIS_DATA ) + eOrient = DataPilotFieldOrientation_DATA; + return eOrient; +} + +void XclPTFieldInfo::AddApiOrient( DataPilotFieldOrientation eOrient ) +{ + using namespace ::com::sun::star::sheet; + switch( eOrient ) + { + case DataPilotFieldOrientation_ROW: mnAxes |= EXC_SXVD_AXIS_ROW; break; + case DataPilotFieldOrientation_COLUMN: mnAxes |= EXC_SXVD_AXIS_COL; break; + case DataPilotFieldOrientation_PAGE: mnAxes |= EXC_SXVD_AXIS_PAGE; break; + case DataPilotFieldOrientation_DATA: mnAxes |= EXC_SXVD_AXIS_DATA; break; + default:; + } +} + +//! TODO: should be a Sequence<GeneralFunction> in ScDPSaveData +void XclPTFieldInfo::GetSubtotals( XclPTSubtotalVec& rSubtotals ) const +{ + rSubtotals.clear(); + rSubtotals.reserve( 16 ); + + using namespace ::com::sun::star::sheet; + if( mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) rSubtotals.push_back( GeneralFunction_AUTO ); + if( mnSubtotals & EXC_SXVD_SUBT_SUM ) rSubtotals.push_back( GeneralFunction_SUM ); + if( mnSubtotals & EXC_SXVD_SUBT_COUNT ) rSubtotals.push_back( GeneralFunction_COUNT ); + if( mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) rSubtotals.push_back( GeneralFunction_AVERAGE ); + if( mnSubtotals & EXC_SXVD_SUBT_MAX ) rSubtotals.push_back( GeneralFunction_MAX ); + if( mnSubtotals & EXC_SXVD_SUBT_MIN ) rSubtotals.push_back( GeneralFunction_MIN ); + if( mnSubtotals & EXC_SXVD_SUBT_PROD ) rSubtotals.push_back( GeneralFunction_PRODUCT ); + if( mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) rSubtotals.push_back( GeneralFunction_COUNTNUMS ); + if( mnSubtotals & EXC_SXVD_SUBT_STDDEV ) rSubtotals.push_back( GeneralFunction_STDEV ); + if( mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) rSubtotals.push_back( GeneralFunction_STDEVP ); + if( mnSubtotals & EXC_SXVD_SUBT_VAR ) rSubtotals.push_back( GeneralFunction_VAR ); + if( mnSubtotals & EXC_SXVD_SUBT_VARP ) rSubtotals.push_back( GeneralFunction_VARP ); +} + +void XclPTFieldInfo::SetSubtotals( const XclPTSubtotalVec& rSubtotals ) +{ + mnSubtotals = EXC_SXVD_SUBT_NONE; + using namespace ::com::sun::star::sheet; + for( XclPTSubtotalVec::const_iterator aIt = rSubtotals.begin(), aEnd = rSubtotals.end(); aIt != aEnd; ++aIt ) + { + switch( *aIt ) + { + case GeneralFunction_AUTO: mnSubtotals |= EXC_SXVD_SUBT_DEFAULT; break; + case GeneralFunction_SUM: mnSubtotals |= EXC_SXVD_SUBT_SUM; break; + case GeneralFunction_COUNT: mnSubtotals |= EXC_SXVD_SUBT_COUNT; break; + case GeneralFunction_AVERAGE: mnSubtotals |= EXC_SXVD_SUBT_AVERAGE; break; + case GeneralFunction_MAX: mnSubtotals |= EXC_SXVD_SUBT_MAX; break; + case GeneralFunction_MIN: mnSubtotals |= EXC_SXVD_SUBT_MIN; break; + case GeneralFunction_PRODUCT: mnSubtotals |= EXC_SXVD_SUBT_PROD; break; + case GeneralFunction_COUNTNUMS: mnSubtotals |= EXC_SXVD_SUBT_COUNTNUM; break; + case GeneralFunction_STDEV: mnSubtotals |= EXC_SXVD_SUBT_STDDEV; break; + case GeneralFunction_STDEVP: mnSubtotals |= EXC_SXVD_SUBT_STDDEVP; break; + case GeneralFunction_VAR: mnSubtotals |= EXC_SXVD_SUBT_VAR; break; + case GeneralFunction_VARP: mnSubtotals |= EXC_SXVD_SUBT_VARP; break; + } + } + + mnSubtCount = 0; + for( sal_uInt16 nMask = 0x8000; nMask; nMask >>= 1 ) + if( mnSubtotals & nMask ) + ++mnSubtCount; +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldInfo& rInfo ) +{ + // rInfo.mnCacheIdx is not part of the SXVD record + return rStrm + >> rInfo.mnAxes + >> rInfo.mnSubtCount + >> rInfo.mnSubtotals + >> rInfo.mnItemCount + >> rInfo.maVisName; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldInfo& rInfo ) +{ + // rInfo.mnCacheIdx is not part of the SXVD record + return rStrm + << rInfo.mnAxes + << rInfo.mnSubtCount + << rInfo.mnSubtotals + << rInfo.mnItemCount + << rInfo.maVisName; +} + +// Extended field settings ==================================================== + +XclPTFieldExtInfo::XclPTFieldExtInfo() : + mnFlags( EXC_SXVDEX_DEFAULTFLAGS ), + mnSortField( EXC_SXVDEX_SORT_OWN ), + mnShowField( EXC_SXVDEX_SHOW_NONE ), + mnNumFmt(0), + mpFieldTotalName(NULL) +{ +} + +sal_Int32 XclPTFieldExtInfo::GetApiSortMode() const +{ + sal_Int32 nSortMode = ScDPSortMode::MANUAL; + if( ::get_flag( mnFlags, EXC_SXVDEX_SORT ) ) + nSortMode = (mnSortField == EXC_SXVDEX_SORT_OWN) ? ScDPSortMode::NAME : ScDPSortMode::DATA; + return nSortMode; +} + +void XclPTFieldExtInfo::SetApiSortMode( sal_Int32 nSortMode ) +{ + bool bSort = (nSortMode == ScDPSortMode::NAME) || (nSortMode == ScDPSortMode::DATA); + ::set_flag( mnFlags, EXC_SXVDEX_SORT, bSort ); + if( nSortMode == ScDPSortMode::NAME ) + mnSortField = EXC_SXVDEX_SORT_OWN; // otherwise sort field has to be set by caller +} + +sal_Int32 XclPTFieldExtInfo::GetApiAutoShowMode() const +{ + return ::get_flagvalue( mnFlags, EXC_SXVDEX_AUTOSHOW_ASC, + ScDPShowItemsMode::FROM_TOP, ScDPShowItemsMode::FROM_BOTTOM ); +} + +void XclPTFieldExtInfo::SetApiAutoShowMode( sal_Int32 nShowMode ) +{ + ::set_flag( mnFlags, EXC_SXVDEX_AUTOSHOW_ASC, nShowMode == ScDPShowItemsMode::FROM_TOP ); +} + +sal_Int32 XclPTFieldExtInfo::GetApiAutoShowCount() const +{ + return ::extract_value< sal_Int32 >( mnFlags, 24, 8 ); +} + +void XclPTFieldExtInfo::SetApiAutoShowCount( sal_Int32 nShowCount ) +{ + ::insert_value( mnFlags, limit_cast< sal_uInt8 >( nShowCount ), 24, 8 ); +} + +sal_Int32 XclPTFieldExtInfo::GetApiLayoutMode() const +{ + sal_Int32 nLayoutMode = ScDPLayoutMode::TABULAR_LAYOUT; + if( ::get_flag( mnFlags, EXC_SXVDEX_LAYOUT_REPORT ) ) + nLayoutMode = ::get_flag( mnFlags, EXC_SXVDEX_LAYOUT_TOP ) ? + ScDPLayoutMode::OUTLINE_SUBTOTALS_TOP : ScDPLayoutMode::OUTLINE_SUBTOTALS_BOTTOM; + return nLayoutMode; +} + +void XclPTFieldExtInfo::SetApiLayoutMode( sal_Int32 nLayoutMode ) +{ + ::set_flag( mnFlags, EXC_SXVDEX_LAYOUT_REPORT, nLayoutMode != ScDPLayoutMode::TABULAR_LAYOUT ); + ::set_flag( mnFlags, EXC_SXVDEX_LAYOUT_TOP, nLayoutMode == ScDPLayoutMode::OUTLINE_SUBTOTALS_TOP ); +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldExtInfo& rInfo ) +{ + sal_uInt8 nNameLen = 0; + rStrm >> rInfo.mnFlags + >> rInfo.mnSortField + >> rInfo.mnShowField + >> rInfo.mnNumFmt + >> nNameLen; + + rStrm.Ignore(10); + if (nNameLen != 0xFF) + // Custom field total name is used. Pick it up. + rInfo.mpFieldTotalName.reset(new rtl::OUString(rStrm.ReadUniString(nNameLen, 0))); + + return rStrm; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldExtInfo& rInfo ) +{ + rStrm << rInfo.mnFlags + << rInfo.mnSortField + << rInfo.mnShowField + << EXC_SXVDEX_FORMAT_NONE; + + if (rInfo.mpFieldTotalName.get() && rInfo.mpFieldTotalName->getLength() > 0) + { + rtl::OUString aFinalName = *rInfo.mpFieldTotalName; + if (aFinalName.getLength() >= 254) + aFinalName = aFinalName.copy(0, 254); + sal_uInt8 nNameLen = static_cast<sal_uInt8>(aFinalName.getLength()); + rStrm << nNameLen; + rStrm.WriteZeroBytes(10); + rStrm << XclExpString(aFinalName, EXC_STR_NOHEADER); + } + else + { + rStrm << sal_uInt16(0xFFFF); + rStrm.WriteZeroBytes(8); + } + return rStrm; +} + +// Page field settings ======================================================== + +XclPTPageFieldInfo::XclPTPageFieldInfo() : + mnField( 0 ), + mnSelItem( EXC_SXPI_ALLITEMS ), + mnObjId( 0xFFFF ) +{ +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclPTPageFieldInfo& rInfo ) +{ + return rStrm + >> rInfo.mnField + >> rInfo.mnSelItem + >> rInfo.mnObjId; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclPTPageFieldInfo& rInfo ) +{ + return rStrm + << rInfo.mnField + << rInfo.mnSelItem + << rInfo.mnObjId; +} + +// Data field settings ======================================================== + +XclPTDataFieldInfo::XclPTDataFieldInfo() : + mnField( 0 ), + mnAggFunc( EXC_SXDI_FUNC_SUM ), + mnRefType( EXC_SXDI_REF_NORMAL ), + mnRefField( 0 ), + mnRefItem( 0 ), + mnNumFmt( 0 ) +{ +} + +GeneralFunction XclPTDataFieldInfo::GetApiAggFunc() const +{ + using namespace ::com::sun::star::sheet; + GeneralFunction eAggFunc; + switch( mnAggFunc ) + { + case EXC_SXDI_FUNC_SUM: eAggFunc = GeneralFunction_SUM; break; + case EXC_SXDI_FUNC_COUNT: eAggFunc = GeneralFunction_COUNT; break; + case EXC_SXDI_FUNC_AVERAGE: eAggFunc = GeneralFunction_AVERAGE; break; + case EXC_SXDI_FUNC_MAX: eAggFunc = GeneralFunction_MAX; break; + case EXC_SXDI_FUNC_MIN: eAggFunc = GeneralFunction_MIN; break; + case EXC_SXDI_FUNC_PRODUCT: eAggFunc = GeneralFunction_PRODUCT; break; + case EXC_SXDI_FUNC_COUNTNUM: eAggFunc = GeneralFunction_COUNTNUMS; break; + case EXC_SXDI_FUNC_STDDEV: eAggFunc = GeneralFunction_STDEV; break; + case EXC_SXDI_FUNC_STDDEVP: eAggFunc = GeneralFunction_STDEVP; break; + case EXC_SXDI_FUNC_VAR: eAggFunc = GeneralFunction_VAR; break; + case EXC_SXDI_FUNC_VARP: eAggFunc = GeneralFunction_VARP; break; + default: eAggFunc = GeneralFunction_SUM; + } + return eAggFunc; +} + +void XclPTDataFieldInfo::SetApiAggFunc( GeneralFunction eAggFunc ) +{ + using namespace ::com::sun::star::sheet; + switch( eAggFunc ) + { + case GeneralFunction_SUM: mnAggFunc = EXC_SXDI_FUNC_SUM; break; + case GeneralFunction_COUNT: mnAggFunc = EXC_SXDI_FUNC_COUNT; break; + case GeneralFunction_AVERAGE: mnAggFunc = EXC_SXDI_FUNC_AVERAGE; break; + case GeneralFunction_MAX: mnAggFunc = EXC_SXDI_FUNC_MAX; break; + case GeneralFunction_MIN: mnAggFunc = EXC_SXDI_FUNC_MIN; break; + case GeneralFunction_PRODUCT: mnAggFunc = EXC_SXDI_FUNC_PRODUCT; break; + case GeneralFunction_COUNTNUMS: mnAggFunc = EXC_SXDI_FUNC_COUNTNUM; break; + case GeneralFunction_STDEV: mnAggFunc = EXC_SXDI_FUNC_STDDEV; break; + case GeneralFunction_STDEVP: mnAggFunc = EXC_SXDI_FUNC_STDDEVP; break; + case GeneralFunction_VAR: mnAggFunc = EXC_SXDI_FUNC_VAR; break; + case GeneralFunction_VARP: mnAggFunc = EXC_SXDI_FUNC_VARP; break; + default: mnAggFunc = EXC_SXDI_FUNC_SUM; + } +} + +sal_Int32 XclPTDataFieldInfo::GetApiRefType() const +{ + namespace ScDPRefType = ::com::sun::star::sheet::DataPilotFieldReferenceType; + sal_Int32 nRefType; + switch( mnRefType ) + { + case EXC_SXDI_REF_DIFF: nRefType = ScDPRefType::ITEM_DIFFERENCE; break; + case EXC_SXDI_REF_PERC: nRefType = ScDPRefType::ITEM_PERCENTAGE; break; + case EXC_SXDI_REF_PERC_DIFF: nRefType = ScDPRefType::ITEM_PERCENTAGE_DIFFERENCE; break; + case EXC_SXDI_REF_RUN_TOTAL: nRefType = ScDPRefType::RUNNING_TOTAL; break; + case EXC_SXDI_REF_PERC_ROW: nRefType = ScDPRefType::ROW_PERCENTAGE; break; + case EXC_SXDI_REF_PERC_COL: nRefType = ScDPRefType::COLUMN_PERCENTAGE; break; + case EXC_SXDI_REF_PERC_TOTAL: nRefType = ScDPRefType::TOTAL_PERCENTAGE; break; + case EXC_SXDI_REF_INDEX: nRefType = ScDPRefType::INDEX; break; + default: nRefType = ScDPRefType::NONE; + } + return nRefType; +} + +void XclPTDataFieldInfo::SetApiRefType( sal_Int32 nRefType ) +{ + namespace ScDPRefType = ::com::sun::star::sheet::DataPilotFieldReferenceType; + switch( nRefType ) + { + case ScDPRefType::ITEM_DIFFERENCE: mnRefType = EXC_SXDI_REF_DIFF; break; + case ScDPRefType::ITEM_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC; break; + case ScDPRefType::ITEM_PERCENTAGE_DIFFERENCE: mnRefType = EXC_SXDI_REF_PERC_DIFF; break; + case ScDPRefType::RUNNING_TOTAL: mnRefType = EXC_SXDI_REF_RUN_TOTAL; break; + case ScDPRefType::ROW_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC_ROW; break; + case ScDPRefType::COLUMN_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC_COL; break; + case ScDPRefType::TOTAL_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC_TOTAL;break; + case ScDPRefType::INDEX: mnRefType = EXC_SXDI_REF_INDEX; break; + default: mnRefType = EXC_SXDI_REF_NORMAL; + } +} + +sal_Int32 XclPTDataFieldInfo::GetApiRefItemType() const +{ + sal_Int32 nRefItemType; + switch( mnRefItem ) + { + case EXC_SXDI_PREVITEM: nRefItemType = ScDPRefItemType::PREVIOUS; break; + case EXC_SXDI_NEXTITEM: nRefItemType = ScDPRefItemType::NEXT; break; + default: nRefItemType = ScDPRefItemType::NAMED; + } + return nRefItemType; +} + +void XclPTDataFieldInfo::SetApiRefItemType( sal_Int32 nRefItemType ) +{ + switch( nRefItemType ) + { + case ScDPRefItemType::PREVIOUS: mnRefItem = EXC_SXDI_PREVITEM; break; + case ScDPRefItemType::NEXT: mnRefItem = EXC_SXDI_NEXTITEM; break; + // nothing for named item reference + } +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclPTDataFieldInfo& rInfo ) +{ + return rStrm + >> rInfo.mnField + >> rInfo.mnAggFunc + >> rInfo.mnRefType + >> rInfo.mnRefField + >> rInfo.mnRefItem + >> rInfo.mnNumFmt + >> rInfo.maVisName; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclPTDataFieldInfo& rInfo ) +{ + return rStrm + << rInfo.mnField + << rInfo.mnAggFunc + << rInfo.mnRefType + << rInfo.mnRefField + << rInfo.mnRefItem + << rInfo.mnNumFmt + << rInfo.maVisName; +} + +// Pivot table settings ======================================================= + +XclPTInfo::XclPTInfo() : + mnFirstHeadRow( 0 ), + mnCacheIdx( 0xFFFF ), + mnDataAxis( EXC_SXVD_AXIS_NONE ), + mnDataPos( EXC_SXVIEW_DATALAST ), + mnFields( 0 ), + mnRowFields( 0 ), + mnColFields( 0 ), + mnPageFields( 0 ), + mnDataFields( 0 ), + mnDataRows( 0 ), + mnDataCols( 0 ), + mnFlags( EXC_SXVIEW_DEFAULTFLAGS ), + mnAutoFmtIdx( EXC_SXVIEW_AUTOFMT ) +{ +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclPTInfo& rInfo ) +{ + sal_uInt16 nTabLen, nDataLen; + + rStrm >> rInfo.maOutXclRange + >> rInfo.mnFirstHeadRow + >> rInfo.maDataXclPos + >> rInfo.mnCacheIdx; + rStrm.Ignore( 2 ); + rStrm >> rInfo.mnDataAxis >> rInfo.mnDataPos + >> rInfo.mnFields + >> rInfo.mnRowFields >> rInfo.mnColFields + >> rInfo.mnPageFields >> rInfo.mnDataFields + >> rInfo.mnDataRows >> rInfo.mnDataCols + >> rInfo.mnFlags + >> rInfo.mnAutoFmtIdx + >> nTabLen >> nDataLen; + rInfo.maTableName = rStrm.ReadUniString( nTabLen ); + rInfo.maDataName = rStrm.ReadUniString( nDataLen ); + return rStrm; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclPTInfo& rInfo ) +{ + XclExpString aXclTableName( rInfo.maTableName ); + XclExpString aXclDataName( rInfo.maDataName ); + + rStrm << rInfo.maOutXclRange + << rInfo.mnFirstHeadRow + << rInfo.maDataXclPos + << rInfo.mnCacheIdx + << sal_uInt16( 0 ) + << rInfo.mnDataAxis << rInfo.mnDataPos + << rInfo.mnFields + << rInfo.mnRowFields << rInfo.mnColFields + << rInfo.mnPageFields << rInfo.mnDataFields + << rInfo.mnDataRows << rInfo.mnDataCols + << rInfo.mnFlags + << rInfo.mnAutoFmtIdx + << aXclTableName.Len() << aXclDataName.Len(); + aXclTableName.WriteFlagField( rStrm ); + aXclTableName.WriteBuffer( rStrm ); + aXclDataName.WriteFlagField( rStrm ); + aXclDataName.WriteBuffer( rStrm ); + return rStrm; +} + +// Extended pivot table settings ============================================== + +XclPTExtInfo::XclPTExtInfo() : + mnSxformulaRecs( 0 ), + mnSxselectRecs( 0 ), + mnPagePerRow( 0 ), + mnPagePerCol( 0 ), + mnFlags( EXC_SXEX_DEFAULTFLAGS ) +{ +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclPTExtInfo& rInfo ) +{ + rStrm >> rInfo.mnSxformulaRecs; + rStrm.Ignore( 6 ); + return rStrm + >> rInfo.mnSxselectRecs + >> rInfo.mnPagePerRow + >> rInfo.mnPagePerCol + >> rInfo.mnFlags; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclPTExtInfo& rInfo ) +{ + return rStrm + << rInfo.mnSxformulaRecs + << EXC_PT_NOSTRING // length of alt. error text + << EXC_PT_NOSTRING // length of alt. empty text + << EXC_PT_NOSTRING // length of tag + << rInfo.mnSxselectRecs + << rInfo.mnPagePerRow + << rInfo.mnPagePerCol + << rInfo.mnFlags + << EXC_PT_NOSTRING // length of page field style name + << EXC_PT_NOSTRING // length of table style name + << EXC_PT_NOSTRING; // length of vacate style name +} + +// ============================================================================ + +// Pivot table autoformat settings ============================================ + +/** +classic : 10 08 00 00 00 00 00 00 20 00 00 00 01 00 00 00 00 +default : 10 08 00 00 00 00 00 00 20 00 00 00 01 00 00 00 00 +report01 : 10 08 02 00 00 00 00 00 20 00 00 00 00 10 00 00 00 +report02 : 10 08 02 00 00 00 00 00 20 00 00 00 01 10 00 00 00 +report03 : 10 08 02 00 00 00 00 00 20 00 00 00 02 10 00 00 00 +report04 : 10 08 02 00 00 00 00 00 20 00 00 00 03 10 00 00 00 +report05 : 10 08 02 00 00 00 00 00 20 00 00 00 04 10 00 00 00 +report06 : 10 08 02 00 00 00 00 00 20 00 00 00 05 10 00 00 00 +report07 : 10 08 02 00 00 00 00 00 20 00 00 00 06 10 00 00 00 +report08 : 10 08 02 00 00 00 00 00 20 00 00 00 07 10 00 00 00 +report09 : 10 08 02 00 00 00 00 00 20 00 00 00 08 10 00 00 00 +report10 : 10 08 02 00 00 00 00 00 20 00 00 00 09 10 00 00 00 +table01 : 10 08 00 00 00 00 00 00 20 00 00 00 0a 10 00 00 00 +table02 : 10 08 00 00 00 00 00 00 20 00 00 00 0b 10 00 00 00 +table03 : 10 08 00 00 00 00 00 00 20 00 00 00 0c 10 00 00 00 +table04 : 10 08 00 00 00 00 00 00 20 00 00 00 0d 10 00 00 00 +table05 : 10 08 00 00 00 00 00 00 20 00 00 00 0e 10 00 00 00 +table06 : 10 08 00 00 00 00 00 00 20 00 00 00 0f 10 00 00 00 +table07 : 10 08 00 00 00 00 00 00 20 00 00 00 10 10 00 00 00 +table08 : 10 08 00 00 00 00 00 00 20 00 00 00 11 10 00 00 00 +table09 : 10 08 00 00 00 00 00 00 20 00 00 00 12 10 00 00 00 +table10 : 10 08 00 00 00 00 00 00 20 00 00 00 13 10 00 00 00 +none : 10 08 00 00 00 00 00 00 20 00 00 00 15 10 00 00 00 +**/ + +XclPTViewEx9Info::XclPTViewEx9Info() : + mbReport( 0 ), + mnAutoFormat( 0 ), + mnGridLayout( 0x10 ) +{ +} + +void XclPTViewEx9Info::Init( const ScDPObject& rDPObj ) +{ + if( rDPObj.GetHeaderLayout() ) + { + mbReport = 0; + mnAutoFormat = 1; + mnGridLayout = 0; + } + else + { + // Report1 for now + // TODO : sync with autoformat indicies + mbReport = 2; + mnAutoFormat = 1; + mnGridLayout = 0x10; + } + + const ScDPSaveData* pData = rDPObj.GetSaveData(); + if (pData) + { + const rtl::OUString* pGrandTotal = pData->GetGrandTotalName(); + if (pGrandTotal) + maGrandTotalName = *pGrandTotal; + } +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclPTViewEx9Info& rInfo ) +{ + rStrm.Ignore( 2 ); + rStrm >> rInfo.mbReport; /// 2 for report* fmts ? + rStrm.Ignore( 6 ); + rStrm >> rInfo.mnAutoFormat >> rInfo.mnGridLayout; + rInfo.maGrandTotalName = rStrm.ReadUniString(); + return rStrm; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclPTViewEx9Info& rInfo ) +{ + return rStrm + << EXC_PT_AUTOFMT_HEADER + << rInfo.mbReport + << EXC_PT_AUTOFMT_ZERO + << EXC_PT_AUTOFMT_FLAGS + << rInfo.mnAutoFormat + << rInfo.mnGridLayout + << XclExpString(rInfo.maGrandTotalName, EXC_STR_DEFAULT, EXC_PT_MAXSTRLEN); +} + diff --git a/sc/source/filter/excel/xlroot.cxx b/sc/source/filter/excel/xlroot.cxx new file mode 100644 index 000000000000..2b2180db5e6a --- /dev/null +++ b/sc/source/filter/excel/xlroot.cxx @@ -0,0 +1,419 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xlroot.hxx" +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XFramesSupplier.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <comphelper/processfactory.hxx> +#include <vcl/svapp.hxx> +#include <svl/stritem.hxx> +#include <svl/languageoptions.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/docfile.hxx> +#include <vcl/font.hxx> +#include <editeng/editstat.hxx> +#include "scitems.hxx" +#include <editeng/eeitem.hxx> +#include "document.hxx" +#include "docpool.hxx" +#include "docuno.hxx" +#include "editutil.hxx" +#include "drwlayer.hxx" +#include "scextopt.hxx" +#include "patattr.hxx" +#include "fapihelper.hxx" +#include "xlconst.hxx" +#include "xlstyle.hxx" +#include "xlchart.hxx" +#include "xltracer.hxx" +#include <unotools/useroptions.hxx> +#include "root.hxx" + +namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + +using ::rtl::OUString; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::UNO_SET_THROW; +using ::com::sun::star::awt::XDevice; +using ::com::sun::star::awt::DeviceInfo; +using ::com::sun::star::frame::XFrame; +using ::com::sun::star::frame::XFramesSupplier; +using ::com::sun::star::lang::XMultiServiceFactory; + +// Global data ================================================================ + +#ifdef DBG_UTIL +XclDebugObjCounter::~XclDebugObjCounter() +{ + DBG_ASSERT( mnObjCnt == 0, "XclDebugObjCounter::~XclDebugObjCounter - wrong root object count" ); +} +#endif + +// ---------------------------------------------------------------------------- + +XclRootData::XclRootData( XclBiff eBiff, SfxMedium& rMedium, + SotStorageRef xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc, bool bExport ) : + meBiff( eBiff ), + meOutput( EXC_OUTPUT_BINARY ), + mrMedium( rMedium ), + mxRootStrg( xRootStrg ), + mrDoc( rDoc ), + maDefPassword( CREATE_STRING( "VelvetSweatshop" ) ), + meTextEnc( eTextEnc ), + meSysLang( Application::GetSettings().GetLanguage() ), + meDocLang( Application::GetSettings().GetLanguage() ), + meUILang( Application::GetSettings().GetUILanguage() ), + mnDefApiScript( ApiScriptType::LATIN ), + maScMaxPos( MAXCOL, MAXROW, MAXTAB ), + maXclMaxPos( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ), + maMaxPos( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ), + mxFontPropSetHlp( new XclFontPropSetHelper ), + mxChPropSetHlp( new XclChPropSetHelper ), + mxRD( new RootData ),//! + mfScreenPixelX( 50.0 ), + mfScreenPixelY( 50.0 ), + mnCharWidth( 110 ), + mnScTab( 0 ), + mbExport( bExport ) +{ + maUserName = SvtUserOptions().GetLastName(); + if( maUserName.Len() == 0 ) + maUserName = CREATE_STRING( "Calc" ); + + switch( ScGlobal::GetDefaultScriptType() ) + { + case SCRIPTTYPE_LATIN: mnDefApiScript = ApiScriptType::LATIN; break; + case SCRIPTTYPE_ASIAN: mnDefApiScript = ApiScriptType::ASIAN; break; + case SCRIPTTYPE_COMPLEX: mnDefApiScript = ApiScriptType::COMPLEX; break; + default: DBG_ERRORFILE( "XclRootData::XclRootData - unknown script type" ); + } + + // maximum cell position + switch( meBiff ) + { + case EXC_BIFF2: maXclMaxPos.Set( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ); break; + case EXC_BIFF3: maXclMaxPos.Set( EXC_MAXCOL3, EXC_MAXROW3, EXC_MAXTAB3 ); break; + case EXC_BIFF4: maXclMaxPos.Set( EXC_MAXCOL4, EXC_MAXROW4, EXC_MAXTAB4 ); break; + case EXC_BIFF5: maXclMaxPos.Set( EXC_MAXCOL5, EXC_MAXROW5, EXC_MAXTAB5 ); break; + case EXC_BIFF8: maXclMaxPos.Set( EXC_MAXCOL8, EXC_MAXROW8, EXC_MAXTAB8 ); break; + default: DBG_ERROR_BIFF(); + } + maMaxPos.SetCol( ::std::min( maScMaxPos.Col(), maXclMaxPos.Col() ) ); + maMaxPos.SetRow( ::std::min( maScMaxPos.Row(), maXclMaxPos.Row() ) ); + maMaxPos.SetTab( ::std::min( maScMaxPos.Tab(), maXclMaxPos.Tab() ) ); + + // document URL and path + if( const SfxItemSet* pItemSet = mrMedium.GetItemSet() ) + if( const SfxStringItem* pItem = static_cast< const SfxStringItem* >( pItemSet->GetItem( SID_FILE_NAME ) ) ) + maDocUrl = pItem->GetValue(); + maBasePath = maDocUrl.Copy( 0, maDocUrl.SearchBackward( '/' ) + 1 ); + + // extended document options - always own object, try to copy existing data from document + if( const ScExtDocOptions* pOldDocOpt = mrDoc.GetExtDocOptions() ) + mxExtDocOpt.reset( new ScExtDocOptions( *pOldDocOpt ) ); + else + mxExtDocOpt.reset( new ScExtDocOptions ); + + // screen pixel size + try + { + Reference< XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory(), UNO_SET_THROW ); + Reference< XFramesSupplier > xFramesSupp( xFactory->createInstance( CREATE_OUSTRING( "com.sun.star.frame.Desktop" ) ), UNO_QUERY_THROW ); + Reference< XFrame > xFrame( xFramesSupp->getActiveFrame(), UNO_SET_THROW ); + Reference< XDevice > xDevice( xFrame->getContainerWindow(), UNO_QUERY_THROW ); + DeviceInfo aDeviceInfo = xDevice->getInfo(); + mfScreenPixelX = (aDeviceInfo.PixelPerMeterX > 0) ? (100000.0 / aDeviceInfo.PixelPerMeterX) : 50.0; + mfScreenPixelY = (aDeviceInfo.PixelPerMeterY > 0) ? (100000.0 / aDeviceInfo.PixelPerMeterY) : 50.0; + } + catch( Exception& ) + { + OSL_ENSURE( false, "XclRootData::XclRootData - cannot get output device info" ); + } +} + +XclRootData::~XclRootData() +{ +} + +// ---------------------------------------------------------------------------- + +XclRoot::XclRoot( XclRootData& rRootData ) : + mrData( rRootData ) +{ +#ifdef DBG_UTIL + ++mrData.mnObjCnt; +#endif + + // filter tracer + // do not use CREATE_OUSTRING for conditional expression + mrData.mxTracer.reset( new XclTracer( GetDocUrl(), OUString::createFromAscii( + IsExport() ? "Office.Tracing/Export/Excel" : "Office.Tracing/Import/Excel" ) ) ); +} + +XclRoot::XclRoot( const XclRoot& rRoot ) : + mrData( rRoot.mrData ) +{ +#ifdef DBG_UTIL + ++mrData.mnObjCnt; +#endif +} + +XclRoot::~XclRoot() +{ +#ifdef DBG_UTIL + --mrData.mnObjCnt; +#endif +} + +XclRoot& XclRoot::operator=( const XclRoot& rRoot ) +{ + (void)rRoot; // avoid compiler warning + // allowed for assignment in derived classes - but test if the same root data is used + DBG_ASSERT( &mrData == &rRoot.mrData, "XclRoot::operator= - incompatible root data" ); + return *this; +} + +void XclRoot::SetTextEncoding( rtl_TextEncoding eTextEnc ) +{ + if( eTextEnc != RTL_TEXTENCODING_DONTKNOW ) + mrData.meTextEnc = eTextEnc; +} + +void XclRoot::SetCharWidth( const XclFontData& rFontData ) +{ + mrData.mnCharWidth = 0; + if( OutputDevice* pPrinter = GetPrinter() ) + { + Font aFont( rFontData.maName, Size( 0, rFontData.mnHeight ) ); + aFont.SetFamily( rFontData.GetScFamily( GetTextEncoding() ) ); + aFont.SetCharSet( rFontData.GetFontEncoding() ); + aFont.SetWeight( rFontData.GetScWeight() ); + pPrinter->SetFont( aFont ); + mrData.mnCharWidth = pPrinter->GetTextWidth( String( '0' ) ); + } + if( mrData.mnCharWidth <= 0 ) + { + // #i48717# Win98 with HP LaserJet returns 0 + DBG_ERRORFILE( "XclRoot::SetCharWidth - invalid character width (no printer?)" ); + mrData.mnCharWidth = 11 * rFontData.mnHeight / 20; + } +} + +sal_Int32 XclRoot::GetHmmFromPixelX( double fPixelX ) const +{ + return static_cast< sal_Int32 >( fPixelX * mrData.mfScreenPixelX + 0.5 ); +} + +sal_Int32 XclRoot::GetHmmFromPixelY( double fPixelY ) const +{ + return static_cast< sal_Int32 >( fPixelY * mrData.mfScreenPixelY + 0.5 ); +} + +String XclRoot::RequestPassword( ::comphelper::IDocPasswordVerifier& rVerifier ) const +{ + ::std::vector< OUString > aDefaultPasswords; + aDefaultPasswords.push_back( mrData.maDefPassword ); + return ScfApiHelper::QueryPasswordForMedium( mrData.mrMedium, rVerifier, &aDefaultPasswords ); +} + +bool XclRoot::HasVbaStorage() const +{ + SotStorageRef xRootStrg = GetRootStorage(); + return xRootStrg.Is() && xRootStrg->IsContained( EXC_STORAGE_VBA_PROJECT ); +} + +SotStorageRef XclRoot::OpenStorage( SotStorageRef xStrg, const String& rStrgName ) const +{ + return mrData.mbExport ? + ScfTools::OpenStorageWrite( xStrg, rStrgName ) : + ScfTools::OpenStorageRead( xStrg, rStrgName ); +} + +SotStorageRef XclRoot::OpenStorage( const String& rStrgName ) const +{ + return OpenStorage( GetRootStorage(), rStrgName ); +} + +SotStorageStreamRef XclRoot::OpenStream( SotStorageRef xStrg, const String& rStrmName ) const +{ + return mrData.mbExport ? + ScfTools::OpenStorageStreamWrite( xStrg, rStrmName ) : + ScfTools::OpenStorageStreamRead( xStrg, rStrmName ); +} + +SotStorageStreamRef XclRoot::OpenStream( const String& rStrmName ) const +{ + return OpenStream( GetRootStorage(), rStrmName ); +} + +SfxObjectShell* XclRoot::GetDocShell() const +{ + return GetDoc().GetDocumentShell(); +} + +ScModelObj* XclRoot::GetDocModelObj() const +{ + SfxObjectShell* pDocShell = GetDocShell(); + return pDocShell ? ScModelObj::getImplementation( pDocShell->GetModel() ) : 0; +} + +OutputDevice* XclRoot::GetPrinter() const +{ + return GetDoc().GetRefDevice(); +} + +ScStyleSheetPool& XclRoot::GetStyleSheetPool() const +{ + return *GetDoc().GetStyleSheetPool(); +} + +ScRangeName& XclRoot::GetNamedRanges() const +{ + return *GetDoc().GetRangeName(); +} + +ScDBCollection& XclRoot::GetDatabaseRanges() const +{ + return *GetDoc().GetDBCollection(); +} + +SdrPage* XclRoot::GetSdrPage( SCTAB nScTab ) const +{ + return ((nScTab >= 0) && GetDoc().GetDrawLayer()) ? + GetDoc().GetDrawLayer()->GetPage( static_cast< sal_uInt16 >( nScTab ) ) : 0; +} + +SvNumberFormatter& XclRoot::GetFormatter() const +{ + return *GetDoc().GetFormatTable(); +} + +DateTime XclRoot::GetNullDate() const +{ + return *GetFormatter().GetNullDate(); +} + +double XclRoot::GetDoubleFromDateTime( const DateTime& rDateTime ) const +{ + double fValue = rDateTime - GetNullDate(); + // adjust dates before 1900-03-01 to get correct time values in the range [0.0,1.0) + if( rDateTime < DateTime( Date( 1, 3, 1900 ) ) ) + fValue -= 1.0; + return fValue; +} + +DateTime XclRoot::GetDateTimeFromDouble( double fValue ) const +{ + DateTime aDateTime = GetNullDate() + fValue; + // adjust dates before 1900-03-01 to get correct time values + if( aDateTime < DateTime( Date( 1, 3, 1900 ) ) ) + aDateTime += 1L; + return aDateTime; +} + +ScEditEngineDefaulter& XclRoot::GetEditEngine() const +{ + if( !mrData.mxEditEngine.get() ) + { + mrData.mxEditEngine.reset( new ScEditEngineDefaulter( GetDoc().GetEnginePool() ) ); + ScEditEngineDefaulter& rEE = *mrData.mxEditEngine; + rEE.SetRefMapMode( MAP_100TH_MM ); + rEE.SetEditTextObjectPool( GetDoc().GetEditPool() ); + rEE.SetUpdateMode( FALSE ); + rEE.EnableUndo( FALSE ); + rEE.SetControlWord( rEE.GetControlWord() & ~EE_CNTRL_ALLOWBIGOBJS ); + } + return *mrData.mxEditEngine; +} + +ScHeaderEditEngine& XclRoot::GetHFEditEngine() const +{ + if( !mrData.mxHFEditEngine.get() ) + { + mrData.mxHFEditEngine.reset( new ScHeaderEditEngine( EditEngine::CreatePool(), TRUE ) ); + ScHeaderEditEngine& rEE = *mrData.mxHFEditEngine; + rEE.SetRefMapMode( MAP_TWIP ); // headers/footers use twips as default metric + rEE.SetUpdateMode( FALSE ); + rEE.EnableUndo( FALSE ); + rEE.SetControlWord( rEE.GetControlWord() & ~EE_CNTRL_ALLOWBIGOBJS ); + + // set Calc header/footer defaults + SfxItemSet* pEditSet = new SfxItemSet( rEE.GetEmptyItemSet() ); + SfxItemSet aItemSet( *GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END ); + ScPatternAttr::FillToEditItemSet( *pEditSet, aItemSet ); + // FillToEditItemSet() adjusts font height to 1/100th mm, we need twips + pEditSet->Put( aItemSet.Get( ATTR_FONT_HEIGHT ), EE_CHAR_FONTHEIGHT ); + pEditSet->Put( aItemSet.Get( ATTR_CJK_FONT_HEIGHT ), EE_CHAR_FONTHEIGHT_CJK ); + pEditSet->Put( aItemSet.Get( ATTR_CTL_FONT_HEIGHT ), EE_CHAR_FONTHEIGHT_CTL ); + rEE.SetDefaults( pEditSet ); // takes ownership + } + return *mrData.mxHFEditEngine; +} + +EditEngine& XclRoot::GetDrawEditEngine() const +{ + if( !mrData.mxDrawEditEng.get() ) + { + mrData.mxDrawEditEng.reset( new EditEngine( &GetDoc().GetDrawLayer()->GetItemPool() ) ); + EditEngine& rEE = *mrData.mxDrawEditEng; + rEE.SetRefMapMode( MAP_100TH_MM ); + rEE.SetUpdateMode( FALSE ); + rEE.EnableUndo( FALSE ); + rEE.SetControlWord( rEE.GetControlWord() & ~EE_CNTRL_ALLOWBIGOBJS ); + } + return *mrData.mxDrawEditEng; +} + +XclFontPropSetHelper& XclRoot::GetFontPropSetHelper() const +{ + return *mrData.mxFontPropSetHlp; +} + +XclChPropSetHelper& XclRoot::GetChartPropSetHelper() const +{ + return *mrData.mxChPropSetHlp; +} + +ScExtDocOptions& XclRoot::GetExtDocOptions() const +{ + return *mrData.mxExtDocOpt; +} + +XclTracer& XclRoot::GetTracer() const +{ + return *mrData.mxTracer; +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xlstyle.cxx b/sc/source/filter/excel/xlstyle.cxx new file mode 100644 index 000000000000..4a48584da7fa --- /dev/null +++ b/sc/source/filter/excel/xlstyle.cxx @@ -0,0 +1,1771 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "xlstyle.hxx" +#include <com/sun/star/awt/FontFamily.hpp> +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <vcl/svapp.hxx> +#include <vcl/font.hxx> +#include <rtl/tencinfo.h> +#include <toolkit/unohlp.hxx> +#include <editeng/svxfont.hxx> +#include "global.hxx" +#include "xlroot.hxx" + +// Color data ================================================================= + +/** Standard EGA colors, bright. */ +#define EXC_PALETTE_EGA_COLORS_LIGHT \ + 0x000000, 0xFFFFFF, 0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF +/** Standard EGA colors, dark. */ +#define EXC_PALETTE_EGA_COLORS_DARK \ + 0x800000, 0x008000, 0x000080, 0x808000, 0x800080, 0x008080, 0xC0C0C0, 0x808080 + +/** Default color table for BIFF2. */ +static const ColorData spnDefColorTable2[] = +{ +/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT +}; + +/** Default color table for BIFF3/BIFF4. */ +static const ColorData spnDefColorTable3[] = +{ +/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT, +/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT, +/* 16 */ EXC_PALETTE_EGA_COLORS_DARK +}; + +/** Default color table for BIFF5/BIFF7. */ +static const ColorData spnDefColorTable5[] = +{ +/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT, +/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT, +/* 16 */ EXC_PALETTE_EGA_COLORS_DARK, +/* 24 */ 0x8080FF, 0x802060, 0xFFFFC0, 0xA0E0E0, 0x600080, 0xFF8080, 0x0080C0, 0xC0C0FF, +/* 32 */ 0x000080, 0xFF00FF, 0xFFFF00, 0x00FFFF, 0x800080, 0x800000, 0x008080, 0x0000FF, +/* 40 */ 0x00CFFF, 0x69FFFF, 0xE0FFE0, 0xFFFF80, 0xA6CAF0, 0xDD9CB3, 0xB38FEE, 0xE3E3E3, +/* 48 */ 0x2A6FF9, 0x3FB8CD, 0x488436, 0x958C41, 0x8E5E42, 0xA0627A, 0x624FAC, 0x969696, +/* 56 */ 0x1D2FBE, 0x286676, 0x004500, 0x453E01, 0x6A2813, 0x85396A, 0x4A3285, 0x424242 +}; + +/** Default color table for BIFF8. */ +static const ColorData spnDefColorTable8[] = +{ +/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT, +/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT, +/* 16 */ EXC_PALETTE_EGA_COLORS_DARK, +/* 24 */ 0x9999FF, 0x993366, 0xFFFFCC, 0xCCFFFF, 0x660066, 0xFF8080, 0x0066CC, 0xCCCCFF, +/* 32 */ 0x000080, 0xFF00FF, 0xFFFF00, 0x00FFFF, 0x800080, 0x800000, 0x008080, 0x0000FF, +/* 40 */ 0x00CCFF, 0xCCFFFF, 0xCCFFCC, 0xFFFF99, 0x99CCFF, 0xFF99CC, 0xCC99FF, 0xFFCC99, +/* 48 */ 0x3366FF, 0x33CCCC, 0x99CC00, 0xFFCC00, 0xFF9900, 0xFF6600, 0x666699, 0x969696, +/* 56 */ 0x003366, 0x339966, 0x003300, 0x333300, 0x993300, 0x993366, 0x333399, 0x333333 +}; + +#undef EXC_PALETTE_EGA_COLORS_LIGHT +#undef EXC_PALETTE_EGA_COLORS_DARK + +// ---------------------------------------------------------------------------- + +XclDefaultPalette::XclDefaultPalette( const XclRoot& rRoot ) : + mpnColorTable( 0 ), + mnTableSize( 0 ) +{ + const StyleSettings& rSett = Application::GetSettings().GetStyleSettings(); + mnWindowText = rSett.GetWindowTextColor().GetColor(); + mnWindowBack = rSett.GetWindowColor().GetColor(); + mnFaceColor = rSett.GetFaceColor().GetColor(); + mnNoteText = rSett.GetHelpTextColor().GetColor(); + mnNoteBack = rSett.GetHelpColor().GetColor(); + + // default colors + switch( rRoot.GetBiff() ) + { + case EXC_BIFF2: + mpnColorTable = spnDefColorTable2; + mnTableSize = STATIC_TABLE_SIZE( spnDefColorTable2 ); + break; + case EXC_BIFF3: + case EXC_BIFF4: + mpnColorTable = spnDefColorTable3; + mnTableSize = STATIC_TABLE_SIZE( spnDefColorTable3 ); + break; + case EXC_BIFF5: + mpnColorTable = spnDefColorTable5; + mnTableSize = STATIC_TABLE_SIZE( spnDefColorTable5 ); + break; + case EXC_BIFF8: + mpnColorTable = spnDefColorTable8; + mnTableSize = STATIC_TABLE_SIZE( spnDefColorTable8 ); + break; + default: + DBG_ERROR_BIFF(); + } +} + +ColorData XclDefaultPalette::GetDefColorData( sal_uInt16 nXclIndex ) const +{ + ColorData nColor; + if( nXclIndex < mnTableSize ) + nColor = mpnColorTable[ nXclIndex ]; + else switch( nXclIndex ) + { + case EXC_COLOR_WINDOWTEXT3: + case EXC_COLOR_WINDOWTEXT: + case EXC_COLOR_CHWINDOWTEXT: nColor = mnWindowText; break; + case EXC_COLOR_WINDOWBACK3: + case EXC_COLOR_WINDOWBACK: + case EXC_COLOR_CHWINDOWBACK: nColor = mnWindowBack; break; + case EXC_COLOR_BUTTONBACK: nColor = mnFaceColor; break; + case EXC_COLOR_CHBORDERAUTO: nColor = COL_BLACK; break; // TODO: really always black? + case EXC_COLOR_NOTEBACK: nColor = mnNoteBack; break; + case EXC_COLOR_NOTETEXT: nColor = mnNoteText; break; + case EXC_COLOR_FONTAUTO: nColor = COL_AUTO; break; + default: + DBG_ERROR1( "XclDefaultPalette::GetDefColorData - unknown default color index: %d", nXclIndex ); + nColor = COL_AUTO; + } + return nColor; +} + +// Font Data ================================================================== + +namespace Awt = ::com::sun::star::awt; +namespace AwtFontFamily = Awt::FontFamily; +namespace AwtFontUnderline = Awt::FontUnderline; +namespace AwtFontStrikeout = Awt::FontStrikeout; + +// ---------------------------------------------------------------------------- + +XclFontData::XclFontData() +{ + Clear(); +} + +XclFontData::XclFontData( const Font& rFont ) +{ + Clear(); + FillFromVclFont( rFont ); +} + +XclFontData::XclFontData( const SvxFont& rFont ) +{ + FillFromSvxFont( rFont ); +} + +void XclFontData::Clear() +{ + maName.Erase(); + maStyle.Erase(); + maColor.SetColor( COL_AUTO ); + mnHeight = 0; + mnWeight = EXC_FONTWGHT_DONTKNOW; + mnEscapem = EXC_FONTESC_NONE; + mnFamily = EXC_FONTFAM_SYSTEM; + mnCharSet = EXC_FONTCSET_ANSI_LATIN; + mnUnderline = EXC_FONTUNDERL_NONE; + mbItalic = mbStrikeout = mbOutline = mbShadow = false; +} + +void XclFontData::FillFromVclFont( const Font& rFont ) +{ + maName = XclTools::GetXclFontName( rFont.GetName() ); // #106246# substitute with MS fonts + maStyle.Erase(); + maColor = rFont.GetColor(); + SetScUnderline( rFont.GetUnderline() ); + mnEscapem = EXC_FONTESC_NONE; + SetScHeight( rFont.GetSize().Height() ); + SetScWeight( rFont.GetWeight() ); + SetScFamily( rFont.GetFamily() ); + SetFontEncoding( rFont.GetCharSet() ); + SetScPosture( rFont.GetItalic() ); + SetScStrikeout( rFont.GetStrikeout() ); + mbOutline = rFont.IsOutline(); + mbShadow = rFont.IsShadow(); +} + +void XclFontData::FillFromSvxFont( const SvxFont& rFont ) +{ + FillFromVclFont( rFont ); + SetScEscapement( rFont.GetEscapement() ); +} + +// *** conversion of VCL/SVX constants *** ------------------------------------ + +FontFamily XclFontData::GetScFamily( rtl_TextEncoding eDefTextEnc ) const +{ + FontFamily eScFamily; + // ! format differs from Windows documentation: family is in lower nibble, pitch unknown + switch( mnFamily & 0x0F ) + { + case EXC_FONTFAM_ROMAN: eScFamily = FAMILY_ROMAN; break; + case EXC_FONTFAM_SWISS: eScFamily = FAMILY_SWISS; break; + case EXC_FONTFAM_MODERN: eScFamily = FAMILY_MODERN; break; + case EXC_FONTFAM_SCRIPT: eScFamily = FAMILY_SCRIPT; break; + case EXC_FONTFAM_DECORATIVE: eScFamily = FAMILY_DECORATIVE; break; + default: + eScFamily = + ((eDefTextEnc == RTL_TEXTENCODING_APPLE_ROMAN) && + (maName.EqualsIgnoreCaseAscii( "Geneva" ) || maName.EqualsIgnoreCaseAscii( "Chicago" ))) ? + FAMILY_SWISS : FAMILY_DONTKNOW; + } + return eScFamily; +} + +rtl_TextEncoding XclFontData::GetFontEncoding() const +{ + // convert Windows character set to text encoding identifier + return rtl_getTextEncodingFromWindowsCharset( mnCharSet ); +} + +FontItalic XclFontData::GetScPosture() const +{ + return mbItalic ? ITALIC_NORMAL : ITALIC_NONE; +} + +FontWeight XclFontData::GetScWeight() const +{ + FontWeight eScWeight; + + if( !mnWeight ) eScWeight = WEIGHT_DONTKNOW; + else if( mnWeight < 150 ) eScWeight = WEIGHT_THIN; + else if( mnWeight < 250 ) eScWeight = WEIGHT_ULTRALIGHT; + else if( mnWeight < 325 ) eScWeight = WEIGHT_LIGHT; + else if( mnWeight < 375 ) eScWeight = WEIGHT_SEMILIGHT; + else if( mnWeight < 450 ) eScWeight = WEIGHT_NORMAL; + else if( mnWeight < 550 ) eScWeight = WEIGHT_MEDIUM; + else if( mnWeight < 650 ) eScWeight = WEIGHT_SEMIBOLD; + else if( mnWeight < 750 ) eScWeight = WEIGHT_BOLD; + else if( mnWeight < 850 ) eScWeight = WEIGHT_ULTRABOLD; + else eScWeight = WEIGHT_BLACK; + + return eScWeight; +} + +FontUnderline XclFontData::GetScUnderline() const +{ + FontUnderline eScUnderl = UNDERLINE_NONE; + switch( mnUnderline ) + { + case EXC_FONTUNDERL_SINGLE: + case EXC_FONTUNDERL_SINGLE_ACC: eScUnderl = UNDERLINE_SINGLE; break; + case EXC_FONTUNDERL_DOUBLE: + case EXC_FONTUNDERL_DOUBLE_ACC: eScUnderl = UNDERLINE_DOUBLE; break; + } + return eScUnderl; +} + +SvxEscapement XclFontData::GetScEscapement() const +{ + SvxEscapement eScEscapem = SVX_ESCAPEMENT_OFF; + switch( mnEscapem ) + { + case EXC_FONTESC_SUPER: eScEscapem = SVX_ESCAPEMENT_SUPERSCRIPT; break; + case EXC_FONTESC_SUB: eScEscapem = SVX_ESCAPEMENT_SUBSCRIPT; break; + } + return eScEscapem; +} + +FontStrikeout XclFontData::GetScStrikeout() const +{ + return mbStrikeout ? STRIKEOUT_SINGLE : STRIKEOUT_NONE; +} + +void XclFontData::SetScHeight( sal_Int32 nTwips ) +{ + mnHeight = static_cast< sal_uInt16 >( ::std::min( nTwips, static_cast<sal_Int32>(0x7FFFL) ) ); +} + +void XclFontData::SetScFamily( FontFamily eScFamily ) +{ + switch( eScFamily ) + { + case FAMILY_DONTKNOW: mnFamily = EXC_FONTFAM_DONTKNOW; break; + case FAMILY_DECORATIVE: mnFamily = EXC_FONTFAM_DECORATIVE; break; + case FAMILY_MODERN: mnFamily = EXC_FONTFAM_MODERN; break; + case FAMILY_ROMAN: mnFamily = EXC_FONTFAM_ROMAN; break; + case FAMILY_SCRIPT: mnFamily = EXC_FONTFAM_SCRIPT; break; + case FAMILY_SWISS: mnFamily = EXC_FONTFAM_SWISS; break; + case FAMILY_SYSTEM: mnFamily = EXC_FONTFAM_SYSTEM; break; + default: + DBG_ERRORFILE( "XclFontData::SetScFamily - unknown font family" ); + mnFamily = EXC_FONTFAM_DONTKNOW; + } +} + +void XclFontData::SetFontEncoding( rtl_TextEncoding eFontEnc ) +{ + // convert text encoding identifier to Windows character set + mnCharSet = rtl_getBestWindowsCharsetFromTextEncoding( eFontEnc ); +} + + +void XclFontData::SetScPosture( FontItalic eScPosture ) +{ + mbItalic = (eScPosture == ITALIC_OBLIQUE) || (eScPosture == ITALIC_NORMAL); +} + +void XclFontData::SetScWeight( FontWeight eScWeight ) +{ + switch( eScWeight ) + { + case WEIGHT_DONTKNOW: mnWeight = EXC_FONTWGHT_DONTKNOW; break; + case WEIGHT_THIN: mnWeight = EXC_FONTWGHT_THIN; break; + case WEIGHT_ULTRALIGHT: mnWeight = EXC_FONTWGHT_ULTRALIGHT; break; + case WEIGHT_LIGHT: mnWeight = EXC_FONTWGHT_LIGHT; break; + case WEIGHT_SEMILIGHT: mnWeight = EXC_FONTWGHT_SEMILIGHT; break; + case WEIGHT_NORMAL: mnWeight = EXC_FONTWGHT_NORMAL; break; + case WEIGHT_MEDIUM: mnWeight = EXC_FONTWGHT_MEDIUM; break; + case WEIGHT_SEMIBOLD: mnWeight = EXC_FONTWGHT_SEMIBOLD; break; + case WEIGHT_BOLD: mnWeight = EXC_FONTWGHT_BOLD; break; + case WEIGHT_ULTRABOLD: mnWeight = EXC_FONTWGHT_ULTRABOLD; break; + case WEIGHT_BLACK: mnWeight = EXC_FONTWGHT_BLACK; break; + default: mnWeight = EXC_FONTWGHT_NORMAL; + } +} + +void XclFontData::SetScUnderline( FontUnderline eScUnderl ) +{ + switch( eScUnderl ) + { + case UNDERLINE_NONE: + case UNDERLINE_DONTKNOW: mnUnderline = EXC_FONTUNDERL_NONE; break; + case UNDERLINE_DOUBLE: + case UNDERLINE_DOUBLEWAVE: mnUnderline = EXC_FONTUNDERL_DOUBLE; break; + default: mnUnderline = EXC_FONTUNDERL_SINGLE; + } +} + +void XclFontData::SetScEscapement( short nScEscapem ) +{ + if( nScEscapem > 0 ) + mnEscapem = EXC_FONTESC_SUPER; + else if( nScEscapem < 0 ) + mnEscapem = EXC_FONTESC_SUB; + else + mnEscapem = EXC_FONTESC_NONE; +} + +void XclFontData::SetScStrikeout( FontStrikeout eScStrikeout ) +{ + mbStrikeout = + (eScStrikeout == STRIKEOUT_SINGLE) || (eScStrikeout == STRIKEOUT_DOUBLE) || + (eScStrikeout == STRIKEOUT_BOLD) || (eScStrikeout == STRIKEOUT_SLASH) || + (eScStrikeout == STRIKEOUT_X); +} + +// *** conversion of API constants *** ---------------------------------------- + +float XclFontData::GetApiHeight() const +{ + return static_cast< float >( mnHeight / TWIPS_PER_POINT ); +} + +sal_Int16 XclFontData::GetApiFamily() const +{ + sal_Int16 nApiFamily = AwtFontFamily::DONTKNOW; + switch( mnFamily ) + { + case FAMILY_DECORATIVE: nApiFamily = AwtFontFamily::DECORATIVE; break; + case FAMILY_MODERN: nApiFamily = AwtFontFamily::MODERN; break; + case FAMILY_ROMAN: nApiFamily = AwtFontFamily::ROMAN; break; + case FAMILY_SCRIPT: nApiFamily = AwtFontFamily::SCRIPT; break; + case FAMILY_SWISS: nApiFamily = AwtFontFamily::SWISS; break; + case FAMILY_SYSTEM: nApiFamily = AwtFontFamily::SYSTEM; break; + } + return nApiFamily; +} + +sal_Int16 XclFontData::GetApiFontEncoding() const +{ + // API constants are equal to rtl_TextEncoding constants + return static_cast< sal_Int16 >( GetFontEncoding() ); +} + +Awt::FontSlant XclFontData::GetApiPosture() const +{ + return mbItalic ? Awt::FontSlant_ITALIC : Awt::FontSlant_NONE; +} + +float XclFontData::GetApiWeight() const +{ + return VCLUnoHelper::ConvertFontWeight( GetScWeight() ); +} + +sal_Int16 XclFontData::GetApiUnderline() const +{ + sal_Int16 nApiUnderl = AwtFontUnderline::NONE; + switch( mnUnderline ) + { + case EXC_FONTUNDERL_SINGLE: + case EXC_FONTUNDERL_SINGLE_ACC: nApiUnderl = AwtFontUnderline::SINGLE; break; + case EXC_FONTUNDERL_DOUBLE: + case EXC_FONTUNDERL_DOUBLE_ACC: nApiUnderl = AwtFontUnderline::DOUBLE; break; + } + return nApiUnderl; +} + +sal_Int16 XclFontData::GetApiEscapement() const +{ + sal_Int16 nApiEscapem = 0; + switch( mnEscapem ) + { + case EXC_FONTESC_SUPER: nApiEscapem = 33; break; + case EXC_FONTESC_SUB: nApiEscapem = -33; break; + } + return nApiEscapem; +} + +sal_Int16 XclFontData::GetApiStrikeout() const +{ + return mbStrikeout ? AwtFontStrikeout::SINGLE : AwtFontStrikeout::NONE; +} + +void XclFontData::SetApiHeight( float fPoint ) +{ + mnHeight = static_cast< sal_uInt16 >( ::std::min( fPoint * TWIPS_PER_POINT + 0.5, 32767.0 ) ); +} + +void XclFontData::SetApiFamily( sal_Int16 nApiFamily ) +{ + switch( nApiFamily ) + { + case AwtFontFamily::DECORATIVE: mnFamily = FAMILY_DECORATIVE; break; + case AwtFontFamily::MODERN: mnFamily = FAMILY_MODERN; break; + case AwtFontFamily::ROMAN: mnFamily = FAMILY_ROMAN; break; + case AwtFontFamily::SCRIPT: mnFamily = FAMILY_SCRIPT; break; + case AwtFontFamily::SWISS: mnFamily = FAMILY_SWISS; break; + case AwtFontFamily::SYSTEM: mnFamily = FAMILY_SYSTEM; break; + default: mnFamily = FAMILY_DONTKNOW; + } +} + +//UNUSED2009-05 void XclFontData::SetApiFontEncoding( sal_Int16 nApiFontEnc ) +//UNUSED2009-05 { +//UNUSED2009-05 // API constants are equal to rtl_TextEncoding constants +//UNUSED2009-05 SetFontEncoding( static_cast< rtl_TextEncoding >( nApiFontEnc ) ); +//UNUSED2009-05 } + +void XclFontData::SetApiPosture( Awt::FontSlant eApiPosture ) +{ + mbItalic = + (eApiPosture == Awt::FontSlant_OBLIQUE) || + (eApiPosture == Awt::FontSlant_ITALIC) || + (eApiPosture == Awt::FontSlant_REVERSE_OBLIQUE) || + (eApiPosture == Awt::FontSlant_REVERSE_ITALIC); +} + +void XclFontData::SetApiWeight( float fApiWeight ) +{ + SetScWeight( VCLUnoHelper::ConvertFontWeight( fApiWeight ) ); +} + +void XclFontData::SetApiUnderline( sal_Int16 nApiUnderl ) +{ + switch( nApiUnderl ) + { + case AwtFontUnderline::NONE: + case AwtFontUnderline::DONTKNOW: mnUnderline = EXC_FONTUNDERL_NONE; break; + case AwtFontUnderline::DOUBLE: + case AwtFontUnderline::DOUBLEWAVE: mnUnderline = EXC_FONTUNDERL_DOUBLE; break; + default: mnUnderline = EXC_FONTUNDERL_SINGLE; + } +} + +void XclFontData::SetApiEscapement( sal_Int16 nApiEscapem ) +{ + if( nApiEscapem > 0 ) + mnEscapem = EXC_FONTESC_SUPER; + else if( nApiEscapem < 0 ) + mnEscapem = EXC_FONTESC_SUB; + else + mnEscapem = EXC_FONTESC_NONE; +} + +void XclFontData::SetApiStrikeout( sal_Int16 nApiStrikeout ) +{ + mbStrikeout = + (nApiStrikeout != AwtFontStrikeout::NONE) && + (nApiStrikeout != AwtFontStrikeout::DONTKNOW); +} + +// ---------------------------------------------------------------------------- + +bool operator==( const XclFontData& rLeft, const XclFontData& rRight ) +{ + return + (rLeft.mnHeight == rRight.mnHeight) && + (rLeft.mnWeight == rRight.mnWeight) && + (rLeft.mnUnderline == rRight.mnUnderline) && + (rLeft.maColor == rRight.maColor) && + (rLeft.mnEscapem == rRight.mnEscapem) && + (rLeft.mnFamily == rRight.mnFamily) && + (rLeft.mnCharSet == rRight.mnCharSet) && + (rLeft.mbItalic == rRight.mbItalic) && + (rLeft.mbStrikeout == rRight.mbStrikeout) && + (rLeft.mbOutline == rRight.mbOutline) && + (rLeft.mbShadow == rRight.mbShadow) && + (rLeft.maName == rRight.maName); +} + +// ---------------------------------------------------------------------------- + +namespace { + +/** Property names for common font settings. */ +const sal_Char *const sppcPropNamesChCommon[] = +{ + "CharUnderline", "CharStrikeout", "CharColor", "CharContoured", "CharShadowed", 0 +}; +/** Property names for Western font settings. */ +const sal_Char *const sppcPropNamesChWstrn[] = +{ + "CharFontName", "CharHeight", "CharPosture", "CharWeight", 0 +}; +/** Property names for Asian font settings. */ +const sal_Char *const sppcPropNamesChAsian[] = +{ + "CharFontNameAsian", "CharHeightAsian", "CharPostureAsian", "CharWeightAsian", 0 +}; +/** Property names for Complex font settings. */ +const sal_Char *const sppcPropNamesChCmplx[] = +{ + "CharFontNameComplex", "CharHeightComplex", "CharPostureComplex", "CharWeightComplex", 0 +}; +/** Property names for escapement. */ +const sal_Char *const sppcPropNamesChEscapement[] = +{ + "CharEscapement", "CharEscapementHeight", 0 +}; +const sal_Int8 EXC_API_ESC_HEIGHT = 58; /// Default escapement font height. + +/** Property names for Western font settings without font name. */ +const sal_Char *const *const sppcPropNamesChWstrnNoName = sppcPropNamesChWstrn + 1; +/** Property names for Asian font settings without font name. */ +const sal_Char *const *const sppcPropNamesChAsianNoName = sppcPropNamesChAsian + 1; +/** Property names for Complex font settings without font name. */ +const sal_Char *const *const sppcPropNamesChCmplxNoName = sppcPropNamesChCmplx + 1; + +/** Property names for font settings in form controls. */ +const sal_Char *const sppcPropNamesControl[] = +{ + "FontName", "FontFamily", "FontCharset", "FontHeight", "FontSlant", + "FontWeight", "FontUnderline", "FontStrikeout", "TextColor", 0 +}; + +/** Inserts all passed API font settings into the font data object. */ +void lclSetApiFontSettings( XclFontData& rFontData, + const String& rApiFontName, float fApiHeight, float fApiWeight, + Awt::FontSlant eApiPosture, sal_Int16 nApiUnderl, sal_Int16 nApiStrikeout ) +{ + rFontData.maName = XclTools::GetXclFontName( rApiFontName ); + rFontData.SetApiHeight( fApiHeight ); + rFontData.SetApiWeight( fApiWeight ); + rFontData.SetApiPosture( eApiPosture ); + rFontData.SetApiUnderline( nApiUnderl ); + rFontData.SetApiStrikeout( nApiStrikeout ); +} + +/** Writes script dependent properties to a font property set helper. */ +void lclWriteChartFont( ScfPropertySet& rPropSet, + ScfPropSetHelper& rHlpName, ScfPropSetHelper& rHlpNoName, + const XclFontData& rFontData, bool bHasFontName ) +{ + // select the font helper + ScfPropSetHelper& rPropSetHlp = bHasFontName ? rHlpName : rHlpNoName; + // initialize the font helper (must be called before writing any properties) + rPropSetHlp.InitializeWrite(); + // write font name + if( bHasFontName ) + rPropSetHlp << rFontData.maName; + // write remaining properties + rPropSetHlp << rFontData.GetApiHeight() << rFontData.GetApiPosture() << rFontData.GetApiWeight(); + // write properties to property set + rPropSetHlp.WriteToPropertySet( rPropSet ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclFontPropSetHelper::XclFontPropSetHelper() : + maHlpChCommon( sppcPropNamesChCommon ), + maHlpChWstrn( sppcPropNamesChWstrn ), + maHlpChAsian( sppcPropNamesChAsian ), + maHlpChCmplx( sppcPropNamesChCmplx ), + maHlpChWstrnNoName( sppcPropNamesChWstrnNoName ), + maHlpChAsianNoName( sppcPropNamesChAsianNoName ), + maHlpChCmplxNoName( sppcPropNamesChCmplxNoName ), + maHlpChEscapement( sppcPropNamesChEscapement ), + maHlpControl( sppcPropNamesControl ) +{ +} + +void XclFontPropSetHelper::ReadFontProperties( XclFontData& rFontData, + const ScfPropertySet& rPropSet, XclFontPropSetType eType, sal_Int16 nScript ) +{ + switch( eType ) + { + case EXC_FONTPROPSET_CHART: + { + String aApiFontName; + float fApiHeight, fApiWeight; + sal_Int16 nApiUnderl = 0, nApiStrikeout = 0; + Awt::FontSlant eApiPosture; + + // read script type dependent properties + ScfPropSetHelper& rPropSetHlp = GetChartHelper( nScript ); + rPropSetHlp.ReadFromPropertySet( rPropSet ); + rPropSetHlp >> aApiFontName >> fApiHeight >> eApiPosture >> fApiWeight; + // read common properties + maHlpChCommon.ReadFromPropertySet( rPropSet ); + maHlpChCommon >> nApiUnderl + >> nApiStrikeout + >> rFontData.maColor + >> rFontData.mbOutline + >> rFontData.mbShadow; + + // convert API property values to Excel settings + lclSetApiFontSettings( rFontData, aApiFontName, + fApiHeight, fApiWeight, eApiPosture, nApiUnderl, nApiStrikeout ); + + // font escapement + sal_Int16 nApiEscapement = 0; + sal_Int8 nApiEscHeight = 0; + maHlpChEscapement.ReadFromPropertySet( rPropSet ); + maHlpChEscapement.ReadFromPropertySet( rPropSet ); + maHlpChEscapement.ReadFromPropertySet( rPropSet ); + maHlpChEscapement >> nApiEscapement >> nApiEscHeight; + rFontData.SetApiEscapement( nApiEscapement ); + } + break; + + case EXC_FONTPROPSET_CONTROL: + { + String aApiFontName; + float fApiHeight, fApiWeight; + sal_Int16 nApiFamily, nApiCharSet, nApiPosture, nApiUnderl, nApiStrikeout; + + // read font properties + maHlpControl.ReadFromPropertySet( rPropSet ); + maHlpControl >> aApiFontName + >> nApiFamily + >> nApiCharSet + >> fApiHeight + >> nApiPosture + >> fApiWeight + >> nApiUnderl + >> nApiStrikeout + >> rFontData.maColor; + + // convert API property values to Excel settings + Awt::FontSlant eApiPosture = static_cast< Awt::FontSlant >( nApiPosture ); + lclSetApiFontSettings( rFontData, aApiFontName, + fApiHeight, fApiWeight, eApiPosture, nApiUnderl, nApiStrikeout ); + rFontData.SetApiFamily( nApiFamily ); + rFontData.SetFontEncoding( nApiCharSet ); + } + break; + } +} + +void XclFontPropSetHelper::WriteFontProperties( + ScfPropertySet& rPropSet, XclFontPropSetType eType, + const XclFontData& rFontData, bool bHasWstrn, bool bHasAsian, bool bHasCmplx, + const Color* pFontColor ) +{ + switch( eType ) + { + case EXC_FONTPROPSET_CHART: + { + // write common properties + maHlpChCommon.InitializeWrite(); + const Color& rColor = pFontColor ? *pFontColor : rFontData.maColor; + maHlpChCommon << rFontData.GetApiUnderline() + << rFontData.GetApiStrikeout() + << rColor + << rFontData.mbOutline + << rFontData.mbShadow; + maHlpChCommon.WriteToPropertySet( rPropSet ); + + // write script type dependent properties + lclWriteChartFont( rPropSet, maHlpChWstrn, maHlpChWstrnNoName, rFontData, bHasWstrn ); + lclWriteChartFont( rPropSet, maHlpChAsian, maHlpChAsianNoName, rFontData, bHasAsian ); + lclWriteChartFont( rPropSet, maHlpChCmplx, maHlpChCmplxNoName, rFontData, bHasCmplx ); + + // font escapement + if( rFontData.GetScEscapement() != SVX_ESCAPEMENT_OFF ) + { + maHlpChEscapement.InitializeWrite(); + maHlpChEscapement << rFontData.GetApiEscapement() << EXC_API_ESC_HEIGHT; + maHlpChEscapement.WriteToPropertySet( rPropSet ); + } + } + break; + + case EXC_FONTPROPSET_CONTROL: + { + maHlpControl.InitializeWrite(); + maHlpControl << rFontData.maName + << rFontData.GetApiFamily() + << rFontData.GetApiFontEncoding() + << static_cast< sal_Int16 >( rFontData.GetApiHeight() + 0.5 ) + << rFontData.GetApiPosture() + << rFontData.GetApiWeight() + << rFontData.GetApiUnderline() + << rFontData.GetApiStrikeout() + << rFontData.maColor; + maHlpControl.WriteToPropertySet( rPropSet ); + } + break; + } +} + +ScfPropSetHelper& XclFontPropSetHelper::GetChartHelper( sal_Int16 nScript ) +{ + namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + switch( nScript ) + { + case ApiScriptType::LATIN: return maHlpChWstrn; + case ApiScriptType::ASIAN: return maHlpChAsian; + case ApiScriptType::COMPLEX: return maHlpChCmplx; + default: DBG_ERRORFILE( "XclFontPropSetHelper::GetChartHelper - unknown script type" ); + } + return maHlpChWstrn; +} + +// Number formats ============================================================= + +namespace { + +// ---------------------------------------------------------------------------- + +/** Special number format index describing a reused format. */ +const NfIndexTableOffset PRV_NF_INDEX_REUSE = NF_INDEX_TABLE_ENTRIES; + +/** German primary language not defined, LANGUAGE_GERMAN belongs to Germany. */ +const LanguageType PRV_LANGUAGE_GERMAN_PRIM = LANGUAGE_GERMAN & LANGUAGE_MASK_PRIMARY; +/** French primary language not defined, LANGUAGE_FRENCH belongs to France. */ +const LanguageType PRV_LANGUAGE_FRENCH_PRIM = LANGUAGE_FRENCH & LANGUAGE_MASK_PRIMARY; +/** Parent language identifier for Asian languages (LANGUAGE_CHINESE is a primary only ID). */ +const LanguageType PRV_LANGUAGE_ASIAN_PRIM = LANGUAGE_CHINESE; + +// ---------------------------------------------------------------------------- + +/** Stores the number format used in Calc for an Excel built-in number format. */ +struct XclBuiltInFormat +{ + sal_uInt16 mnXclNumFmt; /// Excel built-in index. + const sal_Char* mpFormat; /// Format string, may be 0 (meOffset used then). + NfIndexTableOffset meOffset; /// SvNumberFormatter format index, if mpFormat==0. + sal_uInt16 mnXclReuseFmt; /// Use this Excel format, if meOffset==PRV_NF_INDEX_REUSE. +}; + +// ---------------------------------------------------------------------------- + +/** Defines a literal Excel built-in number format. */ +#define EXC_NUMFMT_STRING( nXclNumFmt, pcUtf8 ) \ + { nXclNumFmt, pcUtf8, NF_NUMBER_STANDARD, 0 } + +/** Defines an Excel built-in number format that maps to an own built-in format. */ +#define EXC_NUMFMT_OFFSET( nXclNumFmt, eOffset ) \ + { nXclNumFmt, 0, eOffset, 0 } + +/** Defines an Excel built-in number format that is the same as the specified. */ +#define EXC_NUMFMT_REUSE( nXclNumFmt, nXclReuse ) \ + { nXclNumFmt, 0, PRV_NF_INDEX_REUSE, nXclReuse } + +/** Terminates an Excel built-in number format table. */ +#define EXC_NUMFMT_ENDTABLE() \ + { EXC_FORMAT_NOTFOUND, 0, NF_NUMBER_STANDARD, 0 } + +// ---------------------------------------------------------------------------- + +// Currency unit characters +#define UTF8_BAHT "\340\270\277" +#define UTF8_EURO "\342\202\254" +#define UTF8_POUND_UK "\302\243" +#define UTF8_SHEQEL "\342\202\252" +#define UTF8_WON "\357\277\246" +#define UTF8_YEN_CS "\357\277\245" +#define UTF8_YEN_JP "\302\245" + +// Japanese/Chinese date/time characters +#define UTF8_CJ_YEAR "\345\271\264" +#define UTF8_CJ_MON "\346\234\210" +#define UTF8_CJ_DAY "\346\227\245" +#define UTF8_CJ_HOUR "\346\231\202" +#define UTF8_CJ_MIN "\345\210\206" +#define UTF8_CJ_SEC "\347\247\222" + +// Chinese Simplified date/time characters +#define UTF8_CS_HOUR "\346\227\266" + +// Korean date/time characters +#define UTF8_KO_YEAR "\353\205\204" +#define UTF8_KO_MON "\354\233\224" +#define UTF8_KO_DAY "\354\235\274" +#define UTF8_KO_HOUR "\354\213\234" +#define UTF8_KO_MIN "\353\266\204" +#define UTF8_KO_SEC "\354\264\210" + +// ---------------------------------------------------------------------------- + +/** Default number format table. Last parent of all other tables, used for unknown languages. */ +static const XclBuiltInFormat spBuiltInFormats_DONTKNOW[] = +{ + EXC_NUMFMT_OFFSET( 0, NF_NUMBER_STANDARD ), // General + EXC_NUMFMT_OFFSET( 1, NF_NUMBER_INT ), // 0 + EXC_NUMFMT_OFFSET( 2, NF_NUMBER_DEC2 ), // 0.00 + EXC_NUMFMT_OFFSET( 3, NF_NUMBER_1000INT ), // #,##0 + EXC_NUMFMT_OFFSET( 4, NF_NUMBER_1000DEC2 ), // #,##0.00 + // 5...8 contained in file + EXC_NUMFMT_OFFSET( 9, NF_PERCENT_INT ), // 0% + EXC_NUMFMT_OFFSET( 10, NF_PERCENT_DEC2 ), // 0.00% + EXC_NUMFMT_OFFSET( 11, NF_SCIENTIFIC_000E00 ), // 0.00E+00 + EXC_NUMFMT_OFFSET( 12, NF_FRACTION_1 ), // # ?/? + EXC_NUMFMT_OFFSET( 13, NF_FRACTION_2 ), // # ??/?? + + // 14...22 date and time formats + EXC_NUMFMT_OFFSET( 14, NF_DATE_SYS_DDMMYYYY ), + EXC_NUMFMT_OFFSET( 15, NF_DATE_SYS_DMMMYY ), + EXC_NUMFMT_OFFSET( 16, NF_DATE_SYS_DDMMM ), + EXC_NUMFMT_OFFSET( 17, NF_DATE_SYS_MMYY ), + EXC_NUMFMT_OFFSET( 18, NF_TIME_HHMMAMPM ), + EXC_NUMFMT_OFFSET( 19, NF_TIME_HHMMSSAMPM ), + EXC_NUMFMT_OFFSET( 20, NF_TIME_HHMM ), + EXC_NUMFMT_OFFSET( 21, NF_TIME_HHMMSS ), + EXC_NUMFMT_OFFSET( 22, NF_DATETIME_SYSTEM_SHORT_HHMM ), + + // 23...36 international formats + EXC_NUMFMT_REUSE( 23, 0 ), + EXC_NUMFMT_REUSE( 24, 0 ), + EXC_NUMFMT_REUSE( 25, 0 ), + EXC_NUMFMT_REUSE( 26, 0 ), + EXC_NUMFMT_REUSE( 27, 14 ), + EXC_NUMFMT_REUSE( 28, 14 ), + EXC_NUMFMT_REUSE( 29, 14 ), + EXC_NUMFMT_REUSE( 30, 14 ), + EXC_NUMFMT_REUSE( 31, 14 ), + EXC_NUMFMT_REUSE( 32, 21 ), + EXC_NUMFMT_REUSE( 33, 21 ), + EXC_NUMFMT_REUSE( 34, 21 ), + EXC_NUMFMT_REUSE( 35, 21 ), + EXC_NUMFMT_REUSE( 36, 14 ), + + // 37...44 accounting formats + // 41...44 contained in file + EXC_NUMFMT_STRING( 37, "#,##0;-#,##0" ), + EXC_NUMFMT_STRING( 38, "#,##0;[RED]-#,##0" ), + EXC_NUMFMT_STRING( 39, "#,##0.00;-#,##0.00" ), + EXC_NUMFMT_STRING( 40, "#,##0.00;[RED]-#,##0.00" ), + + // 45...49 more special formats + EXC_NUMFMT_STRING( 45, "mm:ss" ), + EXC_NUMFMT_STRING( 46, "[h]:mm:ss" ), + EXC_NUMFMT_STRING( 47, "mm:ss.0" ), + EXC_NUMFMT_STRING( 48, "##0.0E+0" ), + EXC_NUMFMT_OFFSET( 49, NF_TEXT ), + + // 50...81 international formats + EXC_NUMFMT_REUSE( 50, 14 ), + EXC_NUMFMT_REUSE( 51, 14 ), + EXC_NUMFMT_REUSE( 52, 14 ), + EXC_NUMFMT_REUSE( 53, 14 ), + EXC_NUMFMT_REUSE( 54, 14 ), + EXC_NUMFMT_REUSE( 55, 14 ), + EXC_NUMFMT_REUSE( 56, 14 ), + EXC_NUMFMT_REUSE( 57, 14 ), + EXC_NUMFMT_REUSE( 58, 14 ), + EXC_NUMFMT_REUSE( 59, 1 ), + EXC_NUMFMT_REUSE( 60, 2 ), + EXC_NUMFMT_REUSE( 61, 3 ), + EXC_NUMFMT_REUSE( 62, 4 ), + EXC_NUMFMT_REUSE( 67, 9 ), + EXC_NUMFMT_REUSE( 68, 10 ), + EXC_NUMFMT_REUSE( 69, 12 ), + EXC_NUMFMT_REUSE( 70, 13 ), + EXC_NUMFMT_REUSE( 71, 14 ), + EXC_NUMFMT_REUSE( 72, 14 ), + EXC_NUMFMT_REUSE( 73, 15 ), + EXC_NUMFMT_REUSE( 74, 16 ), + EXC_NUMFMT_REUSE( 75, 17 ), + EXC_NUMFMT_REUSE( 76, 20 ), + EXC_NUMFMT_REUSE( 77, 21 ), + EXC_NUMFMT_REUSE( 78, 22 ), + EXC_NUMFMT_REUSE( 79, 45 ), + EXC_NUMFMT_REUSE( 80, 46 ), + EXC_NUMFMT_REUSE( 81, 47 ), + + // 82...163 not used, must not occur in a file (Excel may crash) + + EXC_NUMFMT_ENDTABLE() +}; + +// ENGLISH -------------------------------------------------------------------- + +/** Base table for English locales. */ +static const XclBuiltInFormat spBuiltInFormats_ENGLISH[] = +{ + EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ), + EXC_NUMFMT_STRING( 16, "DD-MMM" ), + EXC_NUMFMT_STRING( 17, "MMM-YY" ), + EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ), + EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ), + EXC_NUMFMT_STRING( 22, "DD/MM/YYYY hh:mm" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_ENGLISH_UK[] = +{ + EXC_NUMFMT_STRING( 63, UTF8_POUND_UK "#,##0;-" UTF8_POUND_UK "#,##0" ), + EXC_NUMFMT_STRING( 64, UTF8_POUND_UK "#,##0;[RED]-" UTF8_POUND_UK "#,##0" ), + EXC_NUMFMT_STRING( 65, UTF8_POUND_UK "#,##0.00;-" UTF8_POUND_UK "#,##0.00" ), + EXC_NUMFMT_STRING( 66, UTF8_POUND_UK "#,##0.00;[RED]-" UTF8_POUND_UK "#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_ENGLISH_EIRE[] = +{ + EXC_NUMFMT_STRING( 63, UTF8_EURO "#,##0;-" UTF8_EURO "#,##0" ), + EXC_NUMFMT_STRING( 64, UTF8_EURO "#,##0;[RED]-" UTF8_EURO "#,##0" ), + EXC_NUMFMT_STRING( 65, UTF8_EURO "#,##0.00;-" UTF8_EURO "#,##0.00" ), + EXC_NUMFMT_STRING( 66, UTF8_EURO "#,##0.00;[RED]-" UTF8_EURO "#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_ENGLISH_US[] = +{ + EXC_NUMFMT_STRING( 14, "M/D/YYYY" ), + EXC_NUMFMT_STRING( 15, "D-MMM-YY" ), + EXC_NUMFMT_STRING( 16, "D-MMM" ), + EXC_NUMFMT_STRING( 20, "h:mm" ), + EXC_NUMFMT_STRING( 21, "h:mm:ss" ), + EXC_NUMFMT_STRING( 22, "M/D/YYYY h:mm" ), + EXC_NUMFMT_STRING( 37, "#,##0_);(#,##0)" ), + EXC_NUMFMT_STRING( 38, "#,##0_);[RED](#,##0)" ), + EXC_NUMFMT_STRING( 39, "#,##0.00_);(#,##0.00)" ), + EXC_NUMFMT_STRING( 40, "#,##0.00_);[RED](#,##0.00)" ), + EXC_NUMFMT_STRING( 63, "$#,##0_);($#,##0)" ), + EXC_NUMFMT_STRING( 64, "$#,##0_);[RED]($#,##0)" ), + EXC_NUMFMT_STRING( 65, "$#,##0.00_);($#,##0.00)" ), + EXC_NUMFMT_STRING( 66, "$#,##0.00_);[RED]($#,##0.00)" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_ENGLISH_CAN[] = +{ + EXC_NUMFMT_STRING( 20, "h:mm" ), + EXC_NUMFMT_STRING( 21, "h:mm:ss" ), + EXC_NUMFMT_STRING( 22, "DD/MM/YYYY h:mm" ), + EXC_NUMFMT_STRING( 63, "$#,##0;-$#,##0" ), + EXC_NUMFMT_STRING( 64, "$#,##0;[RED]-$#,##0" ), + EXC_NUMFMT_STRING( 65, "$#,##0.00;-$#,##0.00" ), + EXC_NUMFMT_STRING( 66, "$#,##0.00;[RED]-$#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_ENGLISH_AUS[] = +{ + EXC_NUMFMT_STRING( 14, "D/MM/YYYY" ), + EXC_NUMFMT_STRING( 15, "D-MMM-YY" ), + EXC_NUMFMT_STRING( 16, "D-MMM" ), + EXC_NUMFMT_STRING( 20, "h:mm" ), + EXC_NUMFMT_STRING( 21, "h:mm:ss" ), + EXC_NUMFMT_STRING( 22, "D/MM/YYYY h:mm" ), + EXC_NUMFMT_STRING( 63, "$#,##0;-$#,##0" ), + EXC_NUMFMT_STRING( 64, "$#,##0;[RED]-$#,##0" ), + EXC_NUMFMT_STRING( 65, "$#,##0.00;-$#,##0.00" ), + EXC_NUMFMT_STRING( 66, "$#,##0.00;[RED]-$#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_ENGLISH_SAFRICA[] = +{ + EXC_NUMFMT_STRING( 14, "YYYY/MM/DD" ), + EXC_NUMFMT_OFFSET( 18, NF_TIME_HHMMAMPM ), + EXC_NUMFMT_OFFSET( 19, NF_TIME_HHMMSSAMPM ), + EXC_NUMFMT_STRING( 22, "YYYY/MM/DD hh:mm" ), + EXC_NUMFMT_STRING( 63, "\\R #,##0;\\R -#,##0" ), + EXC_NUMFMT_STRING( 64, "\\R #,##0;[RED]\\R -#,##0" ), + EXC_NUMFMT_STRING( 65, "\\R #,##0.00;\\R -#,##0.00" ), + EXC_NUMFMT_STRING( 66, "\\R #,##0.00;[RED]\\R -#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +// FRENCH --------------------------------------------------------------------- + +/** Base table for French locales. */ +static const XclBuiltInFormat spBuiltInFormats_FRENCH[] = +{ + EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ), + EXC_NUMFMT_STRING( 16, "DD-MMM" ), + EXC_NUMFMT_STRING( 17, "MMM-YY" ), + EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ), + EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_FRENCH_FRANCE[] = +{ + EXC_NUMFMT_STRING( 22, "DD/MM/YYYY hh:mm" ), + EXC_NUMFMT_STRING( 37, "#,##0\\ _" UTF8_EURO ";-#,##0\\ _" UTF8_EURO ), + EXC_NUMFMT_STRING( 38, "#,##0\\ _" UTF8_EURO ";[RED]-#,##0\\ _" UTF8_EURO ), + EXC_NUMFMT_STRING( 39, "#,##0.00\\ _" UTF8_EURO ";-#,##0.00\\ _" UTF8_EURO ), + EXC_NUMFMT_STRING( 40, "#,##0.00\\ _" UTF8_EURO ";[RED]-#,##0.00\\ _" UTF8_EURO ), + EXC_NUMFMT_STRING( 63, "#,##0\\ " UTF8_EURO ";-#,##0\\ " UTF8_EURO ), + EXC_NUMFMT_STRING( 64, "#,##0\\ " UTF8_EURO ";[RED]-#,##0\\ " UTF8_EURO ), + EXC_NUMFMT_STRING( 65, "#,##0.00\\ " UTF8_EURO ";-#,##0.00\\ " UTF8_EURO ), + EXC_NUMFMT_STRING( 66, "#,##0.00\\ " UTF8_EURO ";[RED]-#,##0.00\\ " UTF8_EURO ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_FRENCH_CANADIAN[] = +{ + EXC_NUMFMT_STRING( 22, "YYYY-MM-DD hh:mm" ), + EXC_NUMFMT_STRING( 37, "#,##0\\ _$_-;#,##0\\ _$-" ), + EXC_NUMFMT_STRING( 38, "#,##0\\ _$_-;[RED]#,##0\\ _$-" ), + EXC_NUMFMT_STRING( 39, "#,##0.00\\ _$_-;#,##0.00\\ _$-" ), + EXC_NUMFMT_STRING( 40, "#,##0.00\\ _$_-;[RED]#,##0.00\\ _$-" ), + EXC_NUMFMT_STRING( 63, "#,##0\\ $_-;#,##0\\ $-" ), + EXC_NUMFMT_STRING( 64, "#,##0\\ $_-;[RED]#,##0\\ $-" ), + EXC_NUMFMT_STRING( 65, "#,##0.00\\ $_-;#,##0.00\\ $-" ), + EXC_NUMFMT_STRING( 66, "#,##0.00\\ $_-;[RED]#,##0.00\\ $-" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_FRENCH_SWISS[] = +{ + EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ), + EXC_NUMFMT_STRING( 16, "DD.MMM" ), + EXC_NUMFMT_STRING( 17, "MMM.YY" ), + EXC_NUMFMT_STRING( 22, "DD.MM.YYYY hh:mm" ), + EXC_NUMFMT_STRING( 63, "\"SFr. \"#,##0;\"SFr. \"-#,##0" ), + EXC_NUMFMT_STRING( 64, "\"SFr. \"#,##0;[RED]\"SFr. \"-#,##0" ), + EXC_NUMFMT_STRING( 65, "\"SFr. \"#,##0.00;\"SFr. \"-#,##0.00" ), + EXC_NUMFMT_STRING( 66, "\"SFr. \"#,##0.00;[RED]\"SFr. \"-#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_FRENCH_BELGIAN[] = +{ + EXC_NUMFMT_STRING( 14, "D/MM/YYYY" ), + EXC_NUMFMT_STRING( 15, "D-MMM-YY" ), + EXC_NUMFMT_STRING( 16, "D-MMM" ), + EXC_NUMFMT_STRING( 20, "h:mm" ), + EXC_NUMFMT_STRING( 21, "h:mm:ss" ), + EXC_NUMFMT_STRING( 22, "D/MM/YYYY h:mm" ), + EXC_NUMFMT_ENDTABLE() +}; + +// GERMAN --------------------------------------------------------------------- + +/** Base table for German locales. */ +static const XclBuiltInFormat spBuiltInFormats_GERMAN[] = +{ + EXC_NUMFMT_STRING( 15, "DD. MMM YY" ), + EXC_NUMFMT_STRING( 16, "DD. MMM" ), + EXC_NUMFMT_STRING( 17, "MMM YY" ), + EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ), + EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ), + EXC_NUMFMT_STRING( 22, "DD.MM.YYYY hh:mm" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_GERMAN_GERMANY[] = +{ + EXC_NUMFMT_STRING( 37, "#,##0 _" UTF8_EURO ";-#,##0 _" UTF8_EURO ), + EXC_NUMFMT_STRING( 38, "#,##0 _" UTF8_EURO ";[RED]-#,##0 _" UTF8_EURO ), + EXC_NUMFMT_STRING( 39, "#,##0.00 _" UTF8_EURO ";-#,##0.00 _" UTF8_EURO ), + EXC_NUMFMT_STRING( 40, "#,##0.00 _" UTF8_EURO ";[RED]-#,##0.00 _" UTF8_EURO ), + EXC_NUMFMT_STRING( 63, "#,##0 " UTF8_EURO ";-#,##0 " UTF8_EURO ), + EXC_NUMFMT_STRING( 64, "#,##0 " UTF8_EURO ";[RED]-#,##0 " UTF8_EURO ), + EXC_NUMFMT_STRING( 65, "#,##0.00 " UTF8_EURO ";-#,##0.00 " UTF8_EURO ), + EXC_NUMFMT_STRING( 66, "#,##0.00 " UTF8_EURO ";[RED]-#,##0.00 " UTF8_EURO ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_GERMAN_AUSTRIAN[] = +{ + EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ), + EXC_NUMFMT_STRING( 16, "DD.MMM" ), + EXC_NUMFMT_STRING( 17, "MMM.YY" ), + EXC_NUMFMT_STRING( 63, UTF8_EURO " #,##0;-" UTF8_EURO " #,##0" ), + EXC_NUMFMT_STRING( 64, UTF8_EURO " #,##0;[RED]-" UTF8_EURO " #,##0" ), + EXC_NUMFMT_STRING( 65, UTF8_EURO " #,##0.00;-" UTF8_EURO " #,##0.00" ), + EXC_NUMFMT_STRING( 66, UTF8_EURO " #,##0.00;[RED]-" UTF8_EURO " #,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_GERMAN_SWISS[] = +{ + EXC_NUMFMT_STRING( 63, "\"SFr. \"#,##0;\"SFr. \"-#,##0" ), + EXC_NUMFMT_STRING( 64, "\"SFr. \"#,##0;[RED]\"SFr. \"-#,##0" ), + EXC_NUMFMT_STRING( 65, "\"SFr. \"#,##0.00;\"SFr. \"-#,##0.00" ), + EXC_NUMFMT_STRING( 66, "\"SFr. \"#,##0.00;[RED]\"SFr. \"-#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_GERMAN_LUXEMBOURG[] = +{ + EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ), + EXC_NUMFMT_STRING( 16, "DD.MMM" ), + EXC_NUMFMT_STRING( 17, "MMM.YY" ), + EXC_NUMFMT_STRING( 37, "#,##0 _" UTF8_EURO ";-#,##0 _" UTF8_EURO ), + EXC_NUMFMT_STRING( 38, "#,##0 _" UTF8_EURO ";[RED]-#,##0 _" UTF8_EURO ), + EXC_NUMFMT_STRING( 39, "#,##0.00 _" UTF8_EURO ";-#,##0.00 _" UTF8_EURO ), + EXC_NUMFMT_STRING( 40, "#,##0.00 _" UTF8_EURO ";[RED]-#,##0.00 _" UTF8_EURO ), + EXC_NUMFMT_STRING( 63, "#,##0 " UTF8_EURO ";-#,##0 " UTF8_EURO ), + EXC_NUMFMT_STRING( 64, "#,##0 " UTF8_EURO ";[RED]-#,##0 " UTF8_EURO ), + EXC_NUMFMT_STRING( 65, "#,##0.00 " UTF8_EURO ";-#,##0.00 " UTF8_EURO ), + EXC_NUMFMT_STRING( 66, "#,##0.00 " UTF8_EURO ";[RED]-#,##0.00 " UTF8_EURO ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_GERMAN_LIECHTENSTEIN[] = +{ + EXC_NUMFMT_STRING( 63, "\"CHF \"#,##0;\"CHF \"-#,##0" ), + EXC_NUMFMT_STRING( 64, "\"CHF \"#,##0;[RED]\"CHF \"-#,##0" ), + EXC_NUMFMT_STRING( 65, "\"CHF \"#,##0.00;\"CHF \"-#,##0.00" ), + EXC_NUMFMT_STRING( 66, "\"CHF \"#,##0.00;[RED]\"CHF \"-#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +// ITALIAN -------------------------------------------------------------------- + +static const XclBuiltInFormat spBuiltInFormats_ITALIAN_ITALY[] = +{ + EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ), + EXC_NUMFMT_STRING( 16, "DD-MMM" ), + EXC_NUMFMT_STRING( 17, "MMM-YY" ), + EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ), + EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ), + EXC_NUMFMT_STRING( 20, "h:mm" ), + EXC_NUMFMT_STRING( 21, "h:mm:ss" ), + EXC_NUMFMT_STRING( 22, "DD/MM/YYYY h:mm" ), + EXC_NUMFMT_STRING( 63, UTF8_EURO " #,##0;-" UTF8_EURO " #,##0" ), + EXC_NUMFMT_STRING( 64, UTF8_EURO " #,##0;[RED]-" UTF8_EURO " #,##0" ), + EXC_NUMFMT_STRING( 65, UTF8_EURO " #,##0.00;-" UTF8_EURO " #,##0.00" ), + EXC_NUMFMT_STRING( 66, UTF8_EURO " #,##0.00;[RED]-" UTF8_EURO " #,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_ITALIAN_SWISS[] = +{ + EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ), + EXC_NUMFMT_STRING( 16, "DD.MMM" ), + EXC_NUMFMT_STRING( 17, "MMM.YY" ), + EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ), + EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ), + EXC_NUMFMT_STRING( 22, "DD.MM.YYYY hh:mm" ), + EXC_NUMFMT_STRING( 63, "\"SFr. \"#,##0;\"SFr. \"-#,##0" ), + EXC_NUMFMT_STRING( 64, "\"SFr. \"#,##0;[RED]\"SFr. \"-#,##0" ), + EXC_NUMFMT_STRING( 65, "\"SFr. \"#,##0.00;\"SFr. \"-#,##0.00" ), + EXC_NUMFMT_STRING( 66, "\"SFr. \"#,##0.00;[RED]\"SFr. \"-#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +// SWEDISH -------------------------------------------------------------------- + +static const XclBuiltInFormat spBuiltInFormats_SWEDISH_SWEDEN[] = +{ + EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ), + EXC_NUMFMT_STRING( 16, "DD-MMM" ), + EXC_NUMFMT_STRING( 17, "MMM-YY" ), + EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ), + EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ), + EXC_NUMFMT_STRING( 22, "YYYY-MM-DD hh:mm" ), + EXC_NUMFMT_STRING( 37, "#,##0 _k_r;-#,##0 _k_r" ), + EXC_NUMFMT_STRING( 38, "#,##0 _k_r;[RED]-#,##0 _k_r" ), + EXC_NUMFMT_STRING( 39, "#,##0.00 _k_r;-#,##0.00 _k_r" ), + EXC_NUMFMT_STRING( 40, "#,##0.00 _k_r;[RED]-#,##0.00 _k_r" ), + EXC_NUMFMT_STRING( 63, "#,##0 \"kr\";-#,##0 \"kr\"" ), + EXC_NUMFMT_STRING( 64, "#,##0 \"kr\";[RED]-#,##0 \"kr\"" ), + EXC_NUMFMT_STRING( 65, "#,##0.00 \"kr\";-#,##0.00 \"kr\"" ), + EXC_NUMFMT_STRING( 66, "#,##0.00 \"kr\";[RED]-#,##0.00 \"kr\"" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_SWEDISH_FINLAND[] = +{ + EXC_NUMFMT_STRING( 9, "0 %" ), + EXC_NUMFMT_STRING( 10, "0.00 %" ), + EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ), + EXC_NUMFMT_STRING( 16, "DD.MMM" ), + EXC_NUMFMT_STRING( 17, "MMM.YY" ), + EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ), + EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ), + EXC_NUMFMT_STRING( 22, "D.M.YYYY hh:mm" ), + EXC_NUMFMT_STRING( 37, "#,##0 _" UTF8_EURO ";-#,##0 _" UTF8_EURO ), + EXC_NUMFMT_STRING( 38, "#,##0 _" UTF8_EURO ";[RED]-#,##0 _" UTF8_EURO ), + EXC_NUMFMT_STRING( 39, "#,##0.00 _" UTF8_EURO ";-#,##0.00 _" UTF8_EURO ), + EXC_NUMFMT_STRING( 40, "#,##0.00 _" UTF8_EURO ";[RED]-#,##0.00 _" UTF8_EURO ), + EXC_NUMFMT_STRING( 63, "#,##0 " UTF8_EURO ";-#,##0 " UTF8_EURO ), + EXC_NUMFMT_STRING( 64, "#,##0 " UTF8_EURO ";[RED]-#,##0 " UTF8_EURO ), + EXC_NUMFMT_STRING( 65, "#,##0.00 " UTF8_EURO ";-#,##0.00 " UTF8_EURO ), + EXC_NUMFMT_STRING( 66, "#,##0.00 " UTF8_EURO ";[RED]-#,##0.00 " UTF8_EURO ), + EXC_NUMFMT_ENDTABLE() +}; + +// ASIAN ---------------------------------------------------------------------- + +/** Base table for Asian locales. */ +static const XclBuiltInFormat spBuiltInFormats_ASIAN[] = +{ + EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ), + EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ), + EXC_NUMFMT_STRING( 20, "h:mm" ), + EXC_NUMFMT_STRING( 21, "h:mm:ss" ), + EXC_NUMFMT_STRING( 23, "$#,##0_);($#,##0)" ), + EXC_NUMFMT_STRING( 24, "$#,##0_);[RED]($#,##0)" ), + EXC_NUMFMT_STRING( 25, "$#,##0.00_);($#,##0.00)" ), + EXC_NUMFMT_STRING( 26, "$#,##0.00_);[RED]($#,##0.00)" ), + EXC_NUMFMT_REUSE( 29, 28 ), + EXC_NUMFMT_REUSE( 36, 27 ), + EXC_NUMFMT_REUSE( 50, 27 ), + EXC_NUMFMT_REUSE( 51, 28 ), + EXC_NUMFMT_REUSE( 52, 34 ), + EXC_NUMFMT_REUSE( 53, 35 ), + EXC_NUMFMT_REUSE( 54, 28 ), + EXC_NUMFMT_REUSE( 55, 34 ), + EXC_NUMFMT_REUSE( 56, 35 ), + EXC_NUMFMT_REUSE( 57, 27 ), + EXC_NUMFMT_REUSE( 58, 28 ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_JAPANESE[] = +{ + EXC_NUMFMT_STRING( 14, "YYYY/M/D" ), + EXC_NUMFMT_STRING( 15, "D-MMM-YY" ), + EXC_NUMFMT_STRING( 16, "D-MMM" ), + EXC_NUMFMT_STRING( 17, "MMM-YY" ), + EXC_NUMFMT_STRING( 22, "YYYY/M/D h:mm" ), + EXC_NUMFMT_STRING( 27, "[$-0411]GE.M.D" ), + EXC_NUMFMT_STRING( 28, "[$-0411]GGGE" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ), + EXC_NUMFMT_STRING( 30, "[$-0411]M/D/YY" ), + EXC_NUMFMT_STRING( 31, "[$-0411]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ), + EXC_NUMFMT_STRING( 32, "[$-0411]h" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN ), + EXC_NUMFMT_STRING( 33, "[$-0411]h" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ), + EXC_NUMFMT_STRING( 34, "[$-0411]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON ), + EXC_NUMFMT_STRING( 35, "[$-0411]M" UTF8_CJ_MON "D" UTF8_CJ_DAY ), + EXC_NUMFMT_STRING( 63, UTF8_YEN_JP "#,##0;-" UTF8_YEN_JP "#,##0" ), + EXC_NUMFMT_STRING( 64, UTF8_YEN_JP "#,##0;[RED]-" UTF8_YEN_JP "#,##0" ), + EXC_NUMFMT_STRING( 65, UTF8_YEN_JP "#,##0.00;-" UTF8_YEN_JP "#,##0.00" ), + EXC_NUMFMT_STRING( 66, UTF8_YEN_JP "#,##0.00;[RED]-" UTF8_YEN_JP "#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_KOREAN[] = +{ + EXC_NUMFMT_STRING( 14, "YYYY-MM-DD" ), + EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ), + EXC_NUMFMT_STRING( 16, "DD-MMM" ), + EXC_NUMFMT_STRING( 17, "MMM-YY" ), + EXC_NUMFMT_STRING( 22, "YYYY-MM-DD h:mm" ), + EXC_NUMFMT_STRING( 27, "[$-0412]YYYY" UTF8_CJ_YEAR " MM" UTF8_CJ_MON " DD" UTF8_CJ_DAY ), + EXC_NUMFMT_STRING( 28, "[$-0412]MM-DD" ), + EXC_NUMFMT_STRING( 30, "[$-0412]MM-DD-YY" ), + EXC_NUMFMT_STRING( 31, "[$-0412]YYYY" UTF8_KO_YEAR " MM" UTF8_KO_MON " DD" UTF8_KO_DAY ), + EXC_NUMFMT_STRING( 32, "[$-0412]h" UTF8_KO_HOUR " mm" UTF8_KO_MIN ), + EXC_NUMFMT_STRING( 33, "[$-0412]h" UTF8_KO_HOUR " mm" UTF8_KO_MIN " ss" UTF8_KO_SEC ), + EXC_NUMFMT_STRING( 34, "[$-0412]YYYY\"/\"MM\"/\"DD" ), + EXC_NUMFMT_STRING( 35, "[$-0412]YYYY-MM-DD" ), + EXC_NUMFMT_STRING( 63, UTF8_WON "#,##0;-" UTF8_WON "#,##0" ), + EXC_NUMFMT_STRING( 64, UTF8_WON "#,##0;[RED]-" UTF8_WON "#,##0" ), + EXC_NUMFMT_STRING( 65, UTF8_WON "#,##0.00;-" UTF8_WON "#,##0.00" ), + EXC_NUMFMT_STRING( 66, UTF8_WON "#,##0.00;[RED]-" UTF8_WON "#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_CHINESE_SIMPLIFIED[] = +{ + EXC_NUMFMT_STRING( 14, "YYYY-M-D" ), + EXC_NUMFMT_STRING( 15, "D-MMM-YY" ), + EXC_NUMFMT_STRING( 16, "D-MMM" ), + EXC_NUMFMT_STRING( 17, "MMM-YY" ), + EXC_NUMFMT_STRING( 22, "YYYY-M-D h:mm" ), + EXC_NUMFMT_STRING( 27, "[$-0804]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON ), + EXC_NUMFMT_STRING( 28, "[$-0804]M" UTF8_CJ_MON "D" UTF8_CJ_DAY ), + EXC_NUMFMT_STRING( 30, "[$-0804]M-D-YY" ), + EXC_NUMFMT_STRING( 31, "[$-0804]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ), + EXC_NUMFMT_STRING( 32, "[$-0804]h" UTF8_CS_HOUR "mm" UTF8_CJ_MIN ), + EXC_NUMFMT_STRING( 33, "[$-0804]h" UTF8_CS_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ), + EXC_NUMFMT_STRING( 34, "[$-0804]AM/PMh" UTF8_CS_HOUR "mm" UTF8_CJ_MIN ), + EXC_NUMFMT_STRING( 35, "[$-0804]AM/PMh" UTF8_CS_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ), + EXC_NUMFMT_REUSE( 52, 27 ), + EXC_NUMFMT_REUSE( 53, 28 ), + EXC_NUMFMT_STRING( 63, UTF8_YEN_CS "#,##0;-" UTF8_YEN_CS "#,##0" ), + EXC_NUMFMT_STRING( 64, UTF8_YEN_CS "#,##0;[RED]-" UTF8_YEN_CS "#,##0" ), + EXC_NUMFMT_STRING( 65, UTF8_YEN_CS "#,##0.00;-" UTF8_YEN_CS "#,##0.00" ), + EXC_NUMFMT_STRING( 66, UTF8_YEN_CS "#,##0.00;[RED]-" UTF8_YEN_CS "#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_CHINESE_TRADITIONAL[] = +{ + EXC_NUMFMT_STRING( 15, "D-MMM-YY" ), + EXC_NUMFMT_STRING( 16, "D-MMM" ), + EXC_NUMFMT_STRING( 17, "MMM-YY" ), + EXC_NUMFMT_STRING( 18, "hh:mm AM/PM" ), + EXC_NUMFMT_STRING( 19, "hh:mm:ss AM/PM" ), + EXC_NUMFMT_OFFSET( 20, NF_TIME_HHMM ), + EXC_NUMFMT_OFFSET( 21, NF_TIME_HHMMSS ), + EXC_NUMFMT_STRING( 22, "YYYY/M/D hh:mm" ), + EXC_NUMFMT_STRING( 23, "US$#,##0_);(US$#,##0)" ), + EXC_NUMFMT_STRING( 24, "US$#,##0_);[RED](US$#,##0)" ), + EXC_NUMFMT_STRING( 25, "US$#,##0.00_);(US$#,##0.00)" ), + EXC_NUMFMT_STRING( 26, "US$#,##0.00_);[RED](US$#,##0.00)" ), + EXC_NUMFMT_STRING( 27, "[$-0404]E/M/D" ), + EXC_NUMFMT_STRING( 28, "[$-0404]E" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ), + EXC_NUMFMT_STRING( 30, "[$-0404]M/D/YY" ), + EXC_NUMFMT_STRING( 31, "[$-0404]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ), + EXC_NUMFMT_STRING( 32, "[$-0404]hh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN ), + EXC_NUMFMT_STRING( 33, "[$-0404]hh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ), + EXC_NUMFMT_STRING( 34, "[$-0404]AM/PMhh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN ), + EXC_NUMFMT_STRING( 35, "[$-0404]AM/PMhh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ), + EXC_NUMFMT_STRING( 63, "$#,##0;-$#,##0" ), + EXC_NUMFMT_STRING( 64, "$#,##0;[RED]-$#,##0" ), + EXC_NUMFMT_STRING( 65, "$#,##0.00;-$#,##0.00" ), + EXC_NUMFMT_STRING( 66, "$#,##0.00;[RED]-$#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +// OTHER ---------------------------------------------------------------------- + +static const XclBuiltInFormat spBuiltInFormats_HEBREW[] = +{ + EXC_NUMFMT_STRING( 15, "DD-MMMM-YY" ), + EXC_NUMFMT_STRING( 16, "DD-MMMM" ), + EXC_NUMFMT_STRING( 17, "MMMM-YY" ), + EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ), + EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ), + EXC_NUMFMT_STRING( 63, UTF8_SHEQEL " #,##0;" UTF8_SHEQEL " -#,##0" ), + EXC_NUMFMT_STRING( 64, UTF8_SHEQEL " #,##0;[RED]" UTF8_SHEQEL " -#,##0" ), + EXC_NUMFMT_STRING( 65, UTF8_SHEQEL " #,##0.00;" UTF8_SHEQEL " -#,##0.00" ), + EXC_NUMFMT_STRING( 66, UTF8_SHEQEL " #,##0.00;[RED]" UTF8_SHEQEL " -#,##0.00" ), + EXC_NUMFMT_ENDTABLE() +}; + +static const XclBuiltInFormat spBuiltInFormats_THAI[] = +{ + EXC_NUMFMT_STRING( 14, "D/M/YYYY" ), + EXC_NUMFMT_STRING( 15, "D-MMM-YY" ), + EXC_NUMFMT_STRING( 16, "D-MMM" ), + EXC_NUMFMT_STRING( 17, "MMM-YY" ), + EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ), + EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ), + EXC_NUMFMT_STRING( 22, "D/M/YYYY h:mm" ), + EXC_NUMFMT_STRING( 59, "t0" ), + EXC_NUMFMT_STRING( 60, "t0.00" ), + EXC_NUMFMT_STRING( 61, "t#,##0" ), + EXC_NUMFMT_STRING( 62, "t#,##0.00" ), + EXC_NUMFMT_STRING( 63, "t" UTF8_BAHT "#,##0_);t(" UTF8_BAHT "#,##0)" ), + EXC_NUMFMT_STRING( 64, "t" UTF8_BAHT "#,##0_);[RED]t(" UTF8_BAHT "#,##0)" ), + EXC_NUMFMT_STRING( 65, "t" UTF8_BAHT "#,##0.00_);t(" UTF8_BAHT "#,##0.00)" ), + EXC_NUMFMT_STRING( 66, "t" UTF8_BAHT "#,##0.00_);[RED]t(" UTF8_BAHT "#,##0.00)" ), + EXC_NUMFMT_STRING( 67, "t0%" ), + EXC_NUMFMT_STRING( 68, "t0.00%" ), + EXC_NUMFMT_STRING( 69, "t# ?/?" ), + EXC_NUMFMT_STRING( 70, "t# ?\?/?\?" ), + EXC_NUMFMT_STRING( 71, "tD/M/EE" ), + EXC_NUMFMT_STRING( 72, "tD-MMM-E" ), + EXC_NUMFMT_STRING( 73, "tD-MMM" ), + EXC_NUMFMT_STRING( 74, "tMMM-E" ), + EXC_NUMFMT_STRING( 75, "th:mm" ), + EXC_NUMFMT_STRING( 76, "th:mm:ss" ), + EXC_NUMFMT_STRING( 77, "tD/M/EE h:mm" ), + EXC_NUMFMT_STRING( 78, "tmm:ss" ), + EXC_NUMFMT_STRING( 79, "t[h]:mm:ss" ), + EXC_NUMFMT_STRING( 80, "tmm:ss.0" ), + EXC_NUMFMT_STRING( 81, "D/M/E" ), + EXC_NUMFMT_ENDTABLE() +}; + +// ---------------------------------------------------------------------------- + +#undef EXC_NUMFMT_ENDTABLE +#undef EXC_NUMFMT_REUSE +#undef EXC_NUMFMT_OFFSET +#undef EXC_NUMFMT_STRING + +// ---------------------------------------------------------------------------- + +/** Specifies a number format table for a specific langauge. */ +struct XclBuiltInFormatTable +{ + LanguageType meLanguage; /// The language of this table. + LanguageType meParentLang; /// The language of the parent table. + const XclBuiltInFormat* mpFormats; /// The number format table. +}; + +static const XclBuiltInFormatTable spBuiltInFormatTables[] = +{ // language parent language format table + { LANGUAGE_DONTKNOW, LANGUAGE_NONE, spBuiltInFormats_DONTKNOW }, + + { LANGUAGE_ENGLISH, LANGUAGE_DONTKNOW, spBuiltInFormats_ENGLISH }, + { LANGUAGE_ENGLISH_UK, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_UK }, + { LANGUAGE_ENGLISH_EIRE, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_EIRE }, + { LANGUAGE_ENGLISH_US, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_US }, + { LANGUAGE_ENGLISH_CAN, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_CAN }, + { LANGUAGE_ENGLISH_AUS, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_AUS }, + { LANGUAGE_ENGLISH_SAFRICA, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_SAFRICA }, + { LANGUAGE_ENGLISH_NZ, LANGUAGE_ENGLISH_AUS, 0 }, + + { PRV_LANGUAGE_FRENCH_PRIM, LANGUAGE_DONTKNOW, spBuiltInFormats_FRENCH }, + { LANGUAGE_FRENCH, PRV_LANGUAGE_FRENCH_PRIM, spBuiltInFormats_FRENCH_FRANCE }, + { LANGUAGE_FRENCH_CANADIAN, PRV_LANGUAGE_FRENCH_PRIM, spBuiltInFormats_FRENCH_CANADIAN }, + { LANGUAGE_FRENCH_SWISS, PRV_LANGUAGE_FRENCH_PRIM, spBuiltInFormats_FRENCH_SWISS }, + { LANGUAGE_FRENCH_BELGIAN, LANGUAGE_FRENCH, spBuiltInFormats_FRENCH_BELGIAN }, + { LANGUAGE_FRENCH_LUXEMBOURG, LANGUAGE_FRENCH, 0 }, + { LANGUAGE_FRENCH_MONACO, LANGUAGE_FRENCH, 0 }, + + { PRV_LANGUAGE_GERMAN_PRIM, LANGUAGE_DONTKNOW, spBuiltInFormats_GERMAN }, + { LANGUAGE_GERMAN, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_GERMANY }, + { LANGUAGE_GERMAN_AUSTRIAN, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_AUSTRIAN }, + { LANGUAGE_GERMAN_SWISS, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_SWISS }, + { LANGUAGE_GERMAN_LUXEMBOURG, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_LUXEMBOURG }, + { LANGUAGE_GERMAN_LIECHTENSTEIN, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_LIECHTENSTEIN }, + + { LANGUAGE_ITALIAN, LANGUAGE_DONTKNOW, spBuiltInFormats_ITALIAN_ITALY }, + { LANGUAGE_ITALIAN_SWISS, LANGUAGE_DONTKNOW, spBuiltInFormats_ITALIAN_SWISS }, + + { LANGUAGE_SWEDISH, LANGUAGE_DONTKNOW, spBuiltInFormats_SWEDISH_SWEDEN }, + { LANGUAGE_SWEDISH_FINLAND, LANGUAGE_DONTKNOW, spBuiltInFormats_SWEDISH_FINLAND }, + + { PRV_LANGUAGE_ASIAN_PRIM, LANGUAGE_DONTKNOW, spBuiltInFormats_ASIAN }, + { LANGUAGE_JAPANESE, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_JAPANESE }, + { LANGUAGE_KOREAN, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_KOREAN }, + { LANGUAGE_CHINESE_SIMPLIFIED, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_CHINESE_SIMPLIFIED }, + { LANGUAGE_CHINESE_TRADITIONAL, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_CHINESE_TRADITIONAL }, + + { LANGUAGE_HEBREW, LANGUAGE_DONTKNOW, spBuiltInFormats_HEBREW }, + { LANGUAGE_THAI, LANGUAGE_DONTKNOW, spBuiltInFormats_THAI } +}; + +// ---------------------------------------------------------------------------- + +} // namespace + +// ============================================================================ + +XclNumFmtBuffer::XclNumFmtBuffer( const XclRoot& rRoot ) : + meSysLang( rRoot.GetSysLanguage() ), + mnStdScNumFmt( rRoot.GetFormatter().GetStandardFormat( ScGlobal::eLnge ) ) +{ + // *** insert default formats (BIFF5+ only)*** + + if( rRoot.GetBiff() >= EXC_BIFF5 ) + InsertBuiltinFormats(); +} + +void XclNumFmtBuffer::InitializeImport() +{ + maFmtMap.clear(); +} + +//UNUSED2008-05 const XclNumFmt* XclNumFmtBuffer::GetFormat( sal_uInt16 nXclNumFmt ) const +//UNUSED2008-05 { +//UNUSED2008-05 XclNumFmtMap::const_iterator aIt = maFmtMap.find( nXclNumFmt ); +//UNUSED2008-05 return (aIt != maFmtMap.end()) ? &aIt->second : 0; +//UNUSED2008-05 } + +void XclNumFmtBuffer::InsertFormat( sal_uInt16 nXclNumFmt, const String& rFormat ) +{ + XclNumFmt& rNumFmt = maFmtMap[ nXclNumFmt ]; + rNumFmt.maFormat = rFormat; + // #i62053# rFormat may be an empty string, meOffset must be initialized + rNumFmt.meOffset = NF_NUMBER_STANDARD; + rNumFmt.meLanguage = LANGUAGE_SYSTEM; +} + +void XclNumFmtBuffer::InsertBuiltinFormats() +{ + // build a map containing tables for all languages + typedef ::std::map< LanguageType, const XclBuiltInFormatTable* > XclBuiltInMap; + XclBuiltInMap aBuiltInMap; + for( const XclBuiltInFormatTable* pTable = spBuiltInFormatTables; + pTable != STATIC_TABLE_END( spBuiltInFormatTables ); ++pTable ) + aBuiltInMap[ pTable->meLanguage ] = pTable; + + // build a list of table pointers for the current language, with all parent tables + typedef ::std::vector< const XclBuiltInFormatTable* > XclBuiltInVec; + XclBuiltInVec aBuiltInVec; + for( XclBuiltInMap::const_iterator aMIt = aBuiltInMap.find( meSysLang ), aMEnd = aBuiltInMap.end(); + aMIt != aMEnd; aMIt = aBuiltInMap.find( aMIt->second->meParentLang ) ) + aBuiltInVec.push_back( aMIt->second ); + // language not supported + if( aBuiltInVec.empty() ) + { + DBG_ERROR1( "XclNumFmtBuffer::InsertBuiltinFormats - language 0x%04hX not supported (#i29949#)", meSysLang ); + XclBuiltInMap::const_iterator aMIt = aBuiltInMap.find( LANGUAGE_DONTKNOW ); + DBG_ASSERT( aMIt != aBuiltInMap.end(), "XclNumFmtBuffer::InsertBuiltinFormats - default map not found" ); + if( aMIt != aBuiltInMap.end() ) + aBuiltInVec.push_back( aMIt->second ); + } + + // insert the default formats in the format map, from root parent to system language + typedef ::std::map< sal_uInt16, sal_uInt16 > XclReuseMap; + XclReuseMap aReuseMap; + for( XclBuiltInVec::reverse_iterator aVIt = aBuiltInVec.rbegin(), aVEnd = aBuiltInVec.rend(); aVIt != aVEnd; ++aVIt ) + { + // put LANGUAGE_SYSTEM for all entries in default table + LanguageType eLang = ((*aVIt)->meLanguage == LANGUAGE_DONTKNOW) ? LANGUAGE_SYSTEM : meSysLang; + for( const XclBuiltInFormat* pBuiltIn = (*aVIt)->mpFormats; pBuiltIn && (pBuiltIn->mnXclNumFmt != EXC_FORMAT_NOTFOUND); ++pBuiltIn ) + { + XclNumFmt& rNumFmt = maFmtMap[ pBuiltIn->mnXclNumFmt ]; + + rNumFmt.meOffset = pBuiltIn->meOffset; + rNumFmt.meLanguage = eLang; + + if( pBuiltIn->mpFormat ) + rNumFmt.maFormat = String( pBuiltIn->mpFormat, RTL_TEXTENCODING_UTF8 ); + else + rNumFmt.maFormat = EMPTY_STRING; + + if( pBuiltIn->meOffset == PRV_NF_INDEX_REUSE ) + aReuseMap[ pBuiltIn->mnXclNumFmt ] = pBuiltIn->mnXclReuseFmt; + else + aReuseMap.erase( pBuiltIn->mnXclNumFmt ); + } + } + + // copy reused number formats + for( XclReuseMap::const_iterator aRIt = aReuseMap.begin(), aREnd = aReuseMap.end(); aRIt != aREnd; ++aRIt ) + maFmtMap[ aRIt->first ] = maFmtMap[ aRIt->second ]; +} + +// Cell formatting data (XF) ================================================== + +XclCellProt::XclCellProt() : + mbLocked( true ), // default in Excel and Calc + mbHidden( false ) +{ +} + +bool operator==( const XclCellProt& rLeft, const XclCellProt& rRight ) +{ + return (rLeft.mbLocked == rRight.mbLocked) && (rLeft.mbHidden == rRight.mbHidden); +} + +// ---------------------------------------------------------------------------- + +XclCellAlign::XclCellAlign() : + mnHorAlign( EXC_XF_HOR_GENERAL ), + mnVerAlign( EXC_XF_VER_BOTTOM ), + mnOrient( EXC_ORIENT_NONE ), + mnTextDir( EXC_XF_TEXTDIR_CONTEXT ), + mnRotation( EXC_ROT_NONE ), + mnIndent( 0 ), + mbLineBreak( false ), + mbShrink( false ) +{ +} + +SvxCellHorJustify XclCellAlign::GetScHorAlign() const +{ + SvxCellHorJustify eHorJust = SVX_HOR_JUSTIFY_STANDARD; + switch( mnHorAlign ) + { + case EXC_XF_HOR_GENERAL: eHorJust = SVX_HOR_JUSTIFY_STANDARD; break; + case EXC_XF_HOR_LEFT: eHorJust = SVX_HOR_JUSTIFY_LEFT; break; + case EXC_XF_HOR_CENTER_AS: + case EXC_XF_HOR_CENTER: eHorJust = SVX_HOR_JUSTIFY_CENTER; break; + case EXC_XF_HOR_RIGHT: eHorJust = SVX_HOR_JUSTIFY_RIGHT; break; + case EXC_XF_HOR_FILL: eHorJust = SVX_HOR_JUSTIFY_REPEAT; break; + case EXC_XF_HOR_JUSTIFY: + case EXC_XF_HOR_DISTRIB: eHorJust = SVX_HOR_JUSTIFY_BLOCK; break; + default: DBG_ERRORFILE( "XclCellAlign::GetScHorAlign - unknown horizontal alignment" ); + } + return eHorJust; +} + +SvxCellVerJustify XclCellAlign::GetScVerAlign() const +{ + SvxCellVerJustify eVerJust = SVX_VER_JUSTIFY_STANDARD; + switch( mnVerAlign ) + { + case EXC_XF_VER_TOP: eVerJust = SVX_VER_JUSTIFY_TOP; break; + case EXC_XF_VER_CENTER: eVerJust = SVX_VER_JUSTIFY_CENTER; break; + case EXC_XF_VER_BOTTOM: eVerJust = SVX_VER_JUSTIFY_STANDARD; break; + case EXC_XF_VER_JUSTIFY: + case EXC_XF_VER_DISTRIB: eVerJust = SVX_VER_JUSTIFY_TOP; break; + default: DBG_ERRORFILE( "XclCellAlign::GetScVerAlign - unknown vertical alignment" ); + } + return eVerJust; +} + +SvxFrameDirection XclCellAlign::GetScFrameDir() const +{ + SvxFrameDirection eFrameDir = FRMDIR_ENVIRONMENT; + switch( mnTextDir ) + { + case EXC_XF_TEXTDIR_CONTEXT: eFrameDir = FRMDIR_ENVIRONMENT; break; + case EXC_XF_TEXTDIR_LTR: eFrameDir = FRMDIR_HORI_LEFT_TOP; break; + case EXC_XF_TEXTDIR_RTL: eFrameDir = FRMDIR_HORI_RIGHT_TOP; break; + default: DBG_ERRORFILE( "XclCellAlign::GetScFrameDir - unknown CTL text direction" ); + } + return eFrameDir; +} + +void XclCellAlign::SetScHorAlign( SvxCellHorJustify eHorJust ) +{ + switch( eHorJust ) + { + case SVX_HOR_JUSTIFY_STANDARD: mnHorAlign = EXC_XF_HOR_GENERAL; break; + case SVX_HOR_JUSTIFY_LEFT: mnHorAlign = EXC_XF_HOR_LEFT; break; + case SVX_HOR_JUSTIFY_CENTER: mnHorAlign = EXC_XF_HOR_CENTER; break; + case SVX_HOR_JUSTIFY_RIGHT: mnHorAlign = EXC_XF_HOR_RIGHT; break; + case SVX_HOR_JUSTIFY_BLOCK: mnHorAlign = EXC_XF_HOR_JUSTIFY; break; + case SVX_HOR_JUSTIFY_REPEAT: mnHorAlign = EXC_XF_HOR_FILL; break; + default: mnHorAlign = EXC_XF_HOR_GENERAL; + DBG_ERROR( "XclCellAlign::SetScHorAlign - unknown horizontal alignment" ); + } +} + +void XclCellAlign::SetScVerAlign( SvxCellVerJustify eVerJust ) +{ + switch( eVerJust ) + { + case SVX_VER_JUSTIFY_STANDARD: mnVerAlign = EXC_XF_VER_BOTTOM; break; + case SVX_VER_JUSTIFY_TOP: mnVerAlign = EXC_XF_VER_TOP; break; + case SVX_VER_JUSTIFY_CENTER: mnVerAlign = EXC_XF_VER_CENTER; break; + case SVX_VER_JUSTIFY_BOTTOM: mnVerAlign = EXC_XF_VER_BOTTOM; break; + default: mnVerAlign = EXC_XF_VER_BOTTOM; + DBG_ERROR( "XclCellAlign::SetScVerAlign - unknown vertical alignment" ); + } +} + +void XclCellAlign::SetScFrameDir( SvxFrameDirection eFrameDir ) +{ + switch( eFrameDir ) + { + case FRMDIR_ENVIRONMENT: mnTextDir = EXC_XF_TEXTDIR_CONTEXT; break; + case FRMDIR_HORI_LEFT_TOP: mnTextDir = EXC_XF_TEXTDIR_LTR; break; + case FRMDIR_HORI_RIGHT_TOP: mnTextDir = EXC_XF_TEXTDIR_RTL; break; + default: mnTextDir = EXC_XF_TEXTDIR_CONTEXT; + DBG_ERRORFILE( "XclCellAlign::SetScFrameDir - unknown CTL text direction" ); + } +} + +bool operator==( const XclCellAlign& rLeft, const XclCellAlign& rRight ) +{ + return + (rLeft.mnHorAlign == rRight.mnHorAlign) && (rLeft.mnVerAlign == rRight.mnVerAlign) && + (rLeft.mnTextDir == rRight.mnTextDir) && (rLeft.mnOrient == rRight.mnOrient) && + (rLeft.mnRotation == rRight.mnRotation) && (rLeft.mnIndent == rRight.mnIndent) && + (rLeft.mbLineBreak == rRight.mbLineBreak) && (rLeft.mbShrink == rRight.mbShrink); +} + +// ---------------------------------------------------------------------------- + +XclCellBorder::XclCellBorder() : + mnLeftColor( 0 ), + mnRightColor( 0 ), + mnTopColor( 0 ), + mnBottomColor( 0 ), + mnDiagColor( 0 ), + mnLeftLine( EXC_LINE_NONE ), + mnRightLine( EXC_LINE_NONE ), + mnTopLine( EXC_LINE_NONE ), + mnBottomLine( EXC_LINE_NONE ), + mnDiagLine( EXC_LINE_NONE ), + mbDiagTLtoBR( false ), + mbDiagBLtoTR( false ) +{ +} + +bool operator==( const XclCellBorder& rLeft, const XclCellBorder& rRight ) +{ + return + (rLeft.mnLeftColor == rRight.mnLeftColor) && (rLeft.mnRightColor == rRight.mnRightColor) && + (rLeft.mnTopColor == rRight.mnTopColor) && (rLeft.mnBottomColor == rRight.mnBottomColor) && + (rLeft.mnLeftLine == rRight.mnLeftLine) && (rLeft.mnRightLine == rRight.mnRightLine) && + (rLeft.mnTopLine == rRight.mnTopLine) && (rLeft.mnBottomLine == rRight.mnBottomLine) && + (rLeft.mnDiagColor == rRight.mnDiagColor) && (rLeft.mnDiagLine == rRight.mnDiagLine) && + (rLeft.mbDiagTLtoBR == rRight.mbDiagTLtoBR) && (rLeft.mbDiagBLtoTR == rRight.mbDiagBLtoTR); +} + +// ---------------------------------------------------------------------------- + +XclCellArea::XclCellArea() : + mnForeColor( EXC_COLOR_WINDOWTEXT ), + mnBackColor( EXC_COLOR_WINDOWBACK ), + mnPattern( EXC_PATT_NONE ) +{ +} + +bool XclCellArea::IsTransparent() const +{ + return (mnPattern == EXC_PATT_NONE) && (mnBackColor == EXC_COLOR_WINDOWBACK); +} + +bool operator==( const XclCellArea& rLeft, const XclCellArea& rRight ) +{ + return + (rLeft.mnForeColor == rRight.mnForeColor) && (rLeft.mnBackColor == rRight.mnBackColor) && + (rLeft.mnPattern == rRight.mnPattern); +} + +// ---------------------------------------------------------------------------- + +XclXFBase::XclXFBase( bool bCellXF ) : + mnParent( bCellXF ? EXC_XF_DEFAULTSTYLE : EXC_XF_STYLEPARENT ), + mbCellXF( bCellXF ) +{ + SetAllUsedFlags( false ); +} + +XclXFBase::~XclXFBase() +{ +} + +void XclXFBase::SetAllUsedFlags( bool bUsed ) +{ + mbProtUsed = mbFontUsed = mbFmtUsed = mbAlignUsed = mbBorderUsed = mbAreaUsed = bUsed; +} + +bool XclXFBase::HasUsedFlags() const +{ + return mbProtUsed || mbFontUsed || mbFmtUsed || mbAlignUsed || mbBorderUsed || mbAreaUsed; +} + +bool XclXFBase::Equals( const XclXFBase& rCmp ) const +{ + return + (mbCellXF == rCmp.mbCellXF) && (mnParent == rCmp.mnParent) && + (mbProtUsed == rCmp.mbProtUsed) && (mbFontUsed == rCmp.mbFontUsed) && + (mbFmtUsed == rCmp.mbFmtUsed) && (mbAlignUsed == rCmp.mbAlignUsed) && + (mbBorderUsed == rCmp.mbBorderUsed) && (mbAreaUsed == rCmp.mbAreaUsed); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xltools.cxx b/sc/source/filter/excel/xltools.cxx new file mode 100644 index 000000000000..0dd988d67586 --- /dev/null +++ b/sc/source/filter/excel/xltools.cxx @@ -0,0 +1,736 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include <algorithm> +#include <math.h> +#include <sal/mathconf.h> +#include <unotools/fontcvt.hxx> +#include <sfx2/objsh.hxx> +#include <editeng/editstat.hxx> +#include <filter/msfilter/msvbahelper.hxx> +#include "xestream.hxx" +#include "document.hxx" +#include "docuno.hxx" +#include "editutil.hxx" +#include "formula/errorcodes.hxx" +#include "globstr.hrc" +#include "xlstyle.hxx" +#include "xlname.hxx" +#include "xistream.hxx" +#include "xiroot.hxx" +#include "xltools.hxx" + +using ::rtl::OUString; + +// GUID import/export ========================================================= + +XclGuid::XclGuid() +{ + ::std::fill( mpnData, STATIC_TABLE_END( mpnData ), 0 ); +} + +XclGuid::XclGuid( + sal_uInt32 nData1, sal_uInt16 nData2, sal_uInt16 nData3, + sal_uInt8 nData41, sal_uInt8 nData42, sal_uInt8 nData43, sal_uInt8 nData44, + sal_uInt8 nData45, sal_uInt8 nData46, sal_uInt8 nData47, sal_uInt8 nData48 ) +{ + // convert to little endian -> makes streaming easy + UInt32ToSVBT32( nData1, mpnData ); + ShortToSVBT16( nData2, mpnData + 4 ); + ShortToSVBT16( nData3, mpnData + 6 ); + mpnData[ 8 ] = nData41; + mpnData[ 9 ] = nData42; + mpnData[ 10 ] = nData43; + mpnData[ 11 ] = nData44; + mpnData[ 12 ] = nData45; + mpnData[ 13 ] = nData46; + mpnData[ 14 ] = nData47; + mpnData[ 15 ] = nData48; +} + +bool operator==( const XclGuid& rCmp1, const XclGuid& rCmp2 ) +{ + return ::std::equal( rCmp1.mpnData, STATIC_TABLE_END( rCmp1.mpnData ), rCmp2.mpnData ); +} + +bool operator<( const XclGuid& rCmp1, const XclGuid& rCmp2 ) +{ + return ::std::lexicographical_compare( + rCmp1.mpnData, STATIC_TABLE_END( rCmp1.mpnData ), + rCmp2.mpnData, STATIC_TABLE_END( rCmp2.mpnData ) ); +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclGuid& rGuid ) +{ + rStrm.Read( rGuid.mpnData, 16 ); // mpnData always in little endian + return rStrm; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const XclGuid& rGuid ) +{ + rStrm.Write( rGuid.mpnData, 16 ); // mpnData already in little endian + return rStrm; +} + +// Excel Tools ================================================================ + +// GUID's --------------------------------------------------------------------- + +const XclGuid XclTools::maGuidStdLink( + 0x79EAC9D0, 0xBAF9, 0x11CE, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B ); + +const XclGuid XclTools::maGuidUrlMoniker( + 0x79EAC9E0, 0xBAF9, 0x11CE, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B ); + +const XclGuid XclTools::maGuidFileMoniker( + 0x00000303, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 ); + +// numeric conversion --------------------------------------------------------- + +double XclTools::GetDoubleFromRK( sal_Int32 nRKValue ) +{ + union + { + double fVal; + sal_math_Double smD; + }; + fVal = 0.0; + + if( ::get_flag( nRKValue, EXC_RK_INTFLAG ) ) + { + sal_Int32 nTemp = nRKValue >> 2; + ::set_flag< sal_Int32 >( nTemp, 0xE0000000, nRKValue < 0 ); + fVal = nTemp; + } + else + { + smD.w32_parts.msw = nRKValue & EXC_RK_VALUEMASK; + } + + if( ::get_flag( nRKValue, EXC_RK_100FLAG ) ) + fVal /= 100.0; + + return fVal; +} + +bool XclTools::GetRKFromDouble( sal_Int32& rnRKValue, double fValue ) +{ + double fFrac, fInt; + + // integer + fFrac = modf( fValue, &fInt ); + if( (fFrac == 0.0) && (fInt >= -536870912.0) && (fInt <= 536870911.0) ) // 2^29 + { + rnRKValue = static_cast< sal_Int32 >( fInt ); + rnRKValue <<= 2; + rnRKValue |= EXC_RK_INT; + return true; + } + + // integer/100 + fFrac = modf( fValue * 100.0, &fInt ); + if( (fFrac == 0.0) && (fInt >= -536870912.0) && (fInt <= 536870911.0) ) + { + rnRKValue = static_cast< sal_Int32 >( fInt ); + rnRKValue <<= 2; + rnRKValue |= EXC_RK_INT100; + return true; + } + + // double + return false; +} + +sal_Int32 XclTools::GetScRotation( sal_uInt16 nXclRot, sal_Int32 nRotForStacked ) +{ + if( nXclRot == EXC_ROT_STACKED ) + return nRotForStacked; + DBG_ASSERT( nXclRot <= 180, "XclTools::GetScRotation - illegal rotation angle" ); + return static_cast< sal_Int32 >( (nXclRot <= 180) ? (100 * ((nXclRot > 90) ? (450 - nXclRot) : nXclRot)) : 0 ); +} + +sal_uInt8 XclTools::GetXclRotation( sal_Int32 nScRot ) +{ + sal_Int32 nXclRot = nScRot / 100; + if( (0 <= nXclRot) && (nXclRot <= 90) ) + return static_cast< sal_uInt8 >( nXclRot ); + if( nXclRot < 180 ) + return static_cast< sal_uInt8 >( 270 - nXclRot ); + if( nXclRot < 270 ) + return static_cast< sal_uInt8 >( nXclRot - 180 ); + if( nXclRot < 360 ) + return static_cast< sal_uInt8 >( 450 - nXclRot ); + return 0; +} + +sal_uInt8 XclTools::GetXclRotFromOrient( sal_uInt8 nXclOrient ) +{ + switch( nXclOrient ) + { + case EXC_ORIENT_NONE: return EXC_ROT_NONE; + case EXC_ORIENT_STACKED: return EXC_ROT_STACKED; + case EXC_ORIENT_90CCW: return EXC_ROT_90CCW; + case EXC_ORIENT_90CW: return EXC_ROT_90CW; + default: DBG_ERRORFILE( "XclTools::GetXclRotFromOrient - unknown text orientation" ); + } + return EXC_ROT_NONE; +} + +sal_uInt8 XclTools::GetXclOrientFromRot( sal_uInt16 nXclRot ) +{ + if( nXclRot == EXC_ROT_STACKED ) + return EXC_ORIENT_STACKED; + DBG_ASSERT( nXclRot <= 180, "XclTools::GetXclOrientFromRot - unknown text rotation" ); + if( (45 < nXclRot) && (nXclRot <= 90) ) + return EXC_ORIENT_90CCW; + if( (135 < nXclRot) && (nXclRot <= 180) ) + return EXC_ORIENT_90CW; + return EXC_ORIENT_NONE; +} + +sal_uInt8 XclTools::GetXclErrorCode( USHORT nScError ) +{ + using namespace ScErrorCodes; + switch( nScError ) + { + case errIllegalArgument: return EXC_ERR_VALUE; + case errIllegalFPOperation: return EXC_ERR_NUM; // maybe DIV/0 or NUM... + case errDivisionByZero: return EXC_ERR_DIV0; + case errIllegalParameter: return EXC_ERR_VALUE; + case errPairExpected: return EXC_ERR_VALUE; + case errOperatorExpected: return EXC_ERR_VALUE; + case errVariableExpected: return EXC_ERR_VALUE; + case errParameterExpected: return EXC_ERR_VALUE; + case errNoValue: return EXC_ERR_VALUE; + case errCircularReference: return EXC_ERR_VALUE; + case errNoCode: return EXC_ERR_NULL; + case errNoRef: return EXC_ERR_REF; + case errNoName: return EXC_ERR_NAME; + case errNoAddin: return EXC_ERR_NAME; + case errNoMacro: return EXC_ERR_NAME; + case NOTAVAILABLE: return EXC_ERR_NA; + } + return EXC_ERR_NA; +} + +USHORT XclTools::GetScErrorCode( sal_uInt8 nXclError ) +{ + using namespace ScErrorCodes; + switch( nXclError ) + { + case EXC_ERR_NULL: return errNoCode; + case EXC_ERR_DIV0: return errDivisionByZero; + case EXC_ERR_VALUE: return errNoValue; + case EXC_ERR_REF: return errNoRef; + case EXC_ERR_NAME: return errNoName; + case EXC_ERR_NUM: return errIllegalFPOperation; + case EXC_ERR_NA: return NOTAVAILABLE; + default: DBG_ERRORFILE( "XclTools::GetScErrorCode - unknown error code" ); + } + return NOTAVAILABLE; +} + +double XclTools::ErrorToDouble( sal_uInt8 nXclError ) +{ + union + { + double fVal; + sal_math_Double smD; + }; + ::rtl::math::setNan( &fVal ); + smD.nan_parts.fraction_lo = GetScErrorCode( nXclError ); + return fVal; +} + +XclBoolError XclTools::ErrorToEnum( double& rfDblValue, sal_uInt8 bErrOrBool, sal_uInt8 nValue ) +{ + XclBoolError eType; + if( bErrOrBool ) + { + // error value + switch( nValue ) + { + case EXC_ERR_NULL: eType = xlErrNull; break; + case EXC_ERR_DIV0: eType = xlErrDiv0; break; + case EXC_ERR_VALUE: eType = xlErrValue; break; + case EXC_ERR_REF: eType = xlErrRef; break; + case EXC_ERR_NAME: eType = xlErrName; break; + case EXC_ERR_NUM: eType = xlErrNum; break; + case EXC_ERR_NA: eType = xlErrNA; break; + default: eType = xlErrUnknown; + } + rfDblValue = 0.0; + } + else + { + // Boolean value + eType = nValue ? xlErrTrue : xlErrFalse; + rfDblValue = nValue ? 1.0 : 0.0; + } + return eType; +} + +sal_uInt16 XclTools::GetTwipsFromInch( double fInches ) +{ + return static_cast< sal_uInt16 >( + ::std::min( ::std::max( (fInches * EXC_TWIPS_PER_INCH + 0.5), 0.0 ), 65535.0 ) ); +} + +sal_uInt16 XclTools::GetTwipsFromHmm( sal_Int32 nHmm ) +{ + return GetTwipsFromInch( static_cast< double >( nHmm ) / 1000.0 / CM_PER_INCH ); +} + +double XclTools::GetInchFromTwips( sal_Int32 nTwips ) +{ + return static_cast< double >( nTwips ) / EXC_TWIPS_PER_INCH; +} + +double XclTools::GetInchFromHmm( sal_Int32 nHmm ) +{ + return GetInchFromTwips( GetTwipsFromHmm( nHmm ) ); +} + +sal_Int32 XclTools::GetHmmFromInch( double fInches ) +{ + return static_cast< sal_Int32 >( fInches * CM_PER_INCH * 1000 ); +} + +sal_Int32 XclTools::GetHmmFromTwips( sal_Int32 nTwips ) +{ + return GetHmmFromInch( GetInchFromTwips( nTwips ) ); +} + +USHORT XclTools::GetScColumnWidth( sal_uInt16 nXclWidth, long nScCharWidth ) +{ + double fScWidth = static_cast< double >( nXclWidth ) / 256.0 * nScCharWidth + 0.5; + return limit_cast< USHORT >( fScWidth ); +} + +sal_uInt16 XclTools::GetXclColumnWidth( USHORT nScWidth, long nScCharWidth ) +{ + double fXclWidth = static_cast< double >( nScWidth ) * 256.0 / nScCharWidth + 0.5; + return limit_cast< sal_uInt16 >( fXclWidth ); +} + +double XclTools::GetXclDefColWidthCorrection( long nXclDefFontHeight ) +{ + return 40960.0 / ::std::max( nXclDefFontHeight - 15L, 60L ) + 50.0; +} + +// formatting ----------------------------------------------------------------- + +Color XclTools::GetPatternColor( const Color& rPattColor, const Color& rBackColor, sal_uInt16 nXclPattern ) +{ + // 0x00 == 0% transparence (full rPattColor) + // 0x80 == 100% transparence (full rBackColor) + static const sal_uInt8 pnRatioTable[] = + { + 0x80, 0x00, 0x40, 0x20, 0x60, 0x40, 0x40, 0x40, // 00 - 07 + 0x40, 0x40, 0x20, 0x60, 0x60, 0x60, 0x60, 0x48, // 08 - 15 + 0x50, 0x70, 0x78 // 16 - 18 + }; + return (nXclPattern < STATIC_TABLE_SIZE( pnRatioTable )) ? + ScfTools::GetMixedColor( rPattColor, rBackColor, pnRatioTable[ nXclPattern ] ) : rPattColor; +} + +// text encoding -------------------------------------------------------------- + +namespace { + +const struct XclCodePageEntry +{ + sal_uInt16 mnCodePage; + rtl_TextEncoding meTextEnc; +} +pCodePageTable[] = +{ + { 437, RTL_TEXTENCODING_IBM_437 }, // OEM US +// { 720, RTL_TEXTENCODING_IBM_720 }, // OEM Arabic + { 737, RTL_TEXTENCODING_IBM_737 }, // OEM Greek + { 775, RTL_TEXTENCODING_IBM_775 }, // OEM Baltic + { 850, RTL_TEXTENCODING_IBM_850 }, // OEM Latin I + { 852, RTL_TEXTENCODING_IBM_852 }, // OEM Latin II (Central European) + { 855, RTL_TEXTENCODING_IBM_855 }, // OEM Cyrillic + { 857, RTL_TEXTENCODING_IBM_857 }, // OEM Turkish +// { 858, RTL_TEXTENCODING_IBM_858 }, // OEM Multilingual Latin I with Euro + { 860, RTL_TEXTENCODING_IBM_860 }, // OEM Portugese + { 861, RTL_TEXTENCODING_IBM_861 }, // OEM Icelandic + { 862, RTL_TEXTENCODING_IBM_862 }, // OEM Hebrew + { 863, RTL_TEXTENCODING_IBM_863 }, // OEM Canadian (French) + { 864, RTL_TEXTENCODING_IBM_864 }, // OEM Arabic + { 865, RTL_TEXTENCODING_IBM_865 }, // OEM Nordic + { 866, RTL_TEXTENCODING_IBM_866 }, // OEM Cyrillic (Russian) + { 869, RTL_TEXTENCODING_IBM_869 }, // OEM Greek (Modern) + { 874, RTL_TEXTENCODING_MS_874 }, // MS Windows Thai + { 932, RTL_TEXTENCODING_MS_932 }, // MS Windows Japanese Shift-JIS + { 936, RTL_TEXTENCODING_MS_936 }, // MS Windows Chinese Simplified GBK + { 949, RTL_TEXTENCODING_MS_949 }, // MS Windows Korean (Wansung) + { 950, RTL_TEXTENCODING_MS_950 }, // MS Windows Chinese Traditional BIG5 + { 1200, RTL_TEXTENCODING_DONTKNOW }, // Unicode (BIFF8) - return *_DONTKNOW to preserve old code page + { 1250, RTL_TEXTENCODING_MS_1250 }, // MS Windows Latin II (Central European) + { 1251, RTL_TEXTENCODING_MS_1251 }, // MS Windows Cyrillic + { 1252, RTL_TEXTENCODING_MS_1252 }, // MS Windows Latin I (BIFF4-BIFF8) + { 1253, RTL_TEXTENCODING_MS_1253 }, // MS Windows Greek + { 1254, RTL_TEXTENCODING_MS_1254 }, // MS Windows Turkish + { 1255, RTL_TEXTENCODING_MS_1255 }, // MS Windows Hebrew + { 1256, RTL_TEXTENCODING_MS_1256 }, // MS Windows Arabic + { 1257, RTL_TEXTENCODING_MS_1257 }, // MS Windows Baltic + { 1258, RTL_TEXTENCODING_MS_1258 }, // MS Windows Vietnamese + { 1361, RTL_TEXTENCODING_MS_1361 }, // MS Windows Korean (Johab) + { 10000, RTL_TEXTENCODING_APPLE_ROMAN }, // Apple Roman + { 32768, RTL_TEXTENCODING_APPLE_ROMAN }, // Apple Roman + { 32769, RTL_TEXTENCODING_MS_1252 } // MS Windows Latin I (BIFF2-BIFF3) +}; +const XclCodePageEntry* const pCodePageTableEnd = STATIC_TABLE_END( pCodePageTable ); + +struct XclCodePageEntry_CPPred +{ + inline explicit XclCodePageEntry_CPPred( sal_uInt16 nCodePage ) : mnCodePage( nCodePage ) {} + inline bool operator()( const XclCodePageEntry& rEntry ) const { return rEntry.mnCodePage == mnCodePage; } + sal_uInt16 mnCodePage; +}; + +struct XclCodePageEntry_TEPred +{ + inline explicit XclCodePageEntry_TEPred( rtl_TextEncoding eTextEnc ) : meTextEnc( eTextEnc ) {} + inline bool operator()( const XclCodePageEntry& rEntry ) const { return rEntry.meTextEnc == meTextEnc; } + rtl_TextEncoding meTextEnc; +}; + +} // namespace + +rtl_TextEncoding XclTools::GetTextEncoding( sal_uInt16 nCodePage ) +{ + const XclCodePageEntry* pEntry = ::std::find_if( pCodePageTable, pCodePageTableEnd, XclCodePageEntry_CPPred( nCodePage ) ); + if( pEntry == pCodePageTableEnd ) + { + DBG_ERROR2( "XclTools::GetTextEncoding - unknown code page: 0x%04hX (%d)", nCodePage, nCodePage ); + return RTL_TEXTENCODING_DONTKNOW; + } + return pEntry->meTextEnc; +} + +sal_uInt16 XclTools::GetXclCodePage( rtl_TextEncoding eTextEnc ) +{ + if( eTextEnc == RTL_TEXTENCODING_UNICODE ) + return 1200; // for BIFF8 + + const XclCodePageEntry* pEntry = ::std::find_if( pCodePageTable, pCodePageTableEnd, XclCodePageEntry_TEPred( eTextEnc ) ); + if( pEntry == pCodePageTableEnd ) + { + DBG_ERROR1( "XclTools::GetXclCodePage - unsupported text encoding: %d", eTextEnc ); + return 1252; + } + return pEntry->mnCodePage; +} + +// font names ----------------------------------------------------------------- + +String XclTools::GetXclFontName( const String& rFontName ) +{ + // #106246# substitute with MS fonts + String aNewName( GetSubsFontName( rFontName, SUBSFONT_ONLYONE | SUBSFONT_MS ) ); + if( aNewName.Len() ) + return aNewName; + return rFontName; +} + +// built-in defined names ----------------------------------------------------- + +const String XclTools::maDefNamePrefix( RTL_CONSTASCII_USTRINGPARAM( "Excel_BuiltIn_" ) ); + +static const sal_Char* const ppcDefNames[] = +{ + "Consolidate_Area", + "Auto_Open", + "Auto_Close", + "Extract", + "Database", + "Criteria", + "Print_Area", + "Print_Titles", + "Recorder", + "Data_Form", + "Auto_Activate", + "Auto_Deactivate", + "Sheet_Title", + "_FilterDatabase" +}; + +String XclTools::GetXclBuiltInDefName( sal_Unicode cBuiltIn ) +{ + DBG_ASSERT( STATIC_TABLE_SIZE( ppcDefNames ) == EXC_BUILTIN_UNKNOWN, + "XclTools::GetXclBuiltInDefName - built-in defined name list modified" ); + String aDefName; + if( cBuiltIn < STATIC_TABLE_SIZE( ppcDefNames ) ) + aDefName.AssignAscii( ppcDefNames[ cBuiltIn ] ); + else + aDefName = String::CreateFromInt32( cBuiltIn ); + return aDefName; +} + +String XclTools::GetBuiltInDefName( sal_Unicode cBuiltIn ) +{ + return String( maDefNamePrefix ).Append( GetXclBuiltInDefName( cBuiltIn ) ); +} + +sal_Unicode XclTools::GetBuiltInDefNameIndex( const String& rDefName ) +{ + xub_StrLen nPrefixLen = maDefNamePrefix.Len(); + if( rDefName.EqualsIgnoreCaseAscii( maDefNamePrefix, 0, nPrefixLen ) ) + { + for( sal_Unicode cBuiltIn = 0; cBuiltIn < EXC_BUILTIN_UNKNOWN; ++cBuiltIn ) + { + String aBuiltInName( GetXclBuiltInDefName( cBuiltIn ) ); + xub_StrLen nBuiltInLen = aBuiltInName.Len(); + if( rDefName.EqualsIgnoreCaseAscii( aBuiltInName, nPrefixLen, nBuiltInLen ) ) + { + // name can be followed by underline or space character + xub_StrLen nNextCharPos = nPrefixLen + nBuiltInLen; + sal_Unicode cNextChar = (rDefName.Len() > nNextCharPos) ? rDefName.GetChar( nNextCharPos ) : '\0'; + if( (cNextChar == '\0') || (cNextChar == ' ') || (cNextChar == '_') ) + return cBuiltIn; + } + } + } + return EXC_BUILTIN_UNKNOWN; +} + +// built-in style names ------------------------------------------------------- + +const String XclTools::maStyleNamePrefix1( RTL_CONSTASCII_USTRINGPARAM( "Excel_BuiltIn_" ) ); +const String XclTools::maStyleNamePrefix2( RTL_CONSTASCII_USTRINGPARAM( "Excel Built-in " ) ); + +static const sal_Char* const ppcStyleNames[] = +{ + "", // "Normal" not used directly, but localized "Default" + "RowLevel_", // outline level will be appended + "ColumnLevel_", // outline level will be appended + "Comma", + "Currency", + "Percent", + "Comma_0", + "Currency_0", + "Hyperlink", + "Followed_Hyperlink" +}; + +String XclTools::GetBuiltInStyleName( sal_uInt8 nStyleId, const String& rName, sal_uInt8 nLevel ) +{ + String aStyleName; + + if( nStyleId == EXC_STYLE_NORMAL ) // "Normal" becomes "Default" style + { + aStyleName = ScGlobal::GetRscString( STR_STYLENAME_STANDARD ); + } + else + { + aStyleName = maStyleNamePrefix1; + if( nStyleId < STATIC_TABLE_SIZE( ppcStyleNames ) ) + aStyleName.AppendAscii( ppcStyleNames[ nStyleId ] ); + else if( rName.Len() > 0 ) + aStyleName.Append( rName ); + else + aStyleName.Append( String::CreateFromInt32( nStyleId ) ); + if( (nStyleId == EXC_STYLE_ROWLEVEL) || (nStyleId == EXC_STYLE_COLLEVEL) ) + aStyleName.Append( String::CreateFromInt32( nLevel + 1 ) ); + } + + return aStyleName; +} + +String XclTools::GetBuiltInStyleName( const String& rStyleName ) +{ + return String( maStyleNamePrefix1 ).Append( rStyleName ); +} + +bool XclTools::IsBuiltInStyleName( const String& rStyleName, sal_uInt8* pnStyleId, xub_StrLen* pnNextChar ) +{ + // "Default" becomes "Normal" + if( rStyleName == ScGlobal::GetRscString( STR_STYLENAME_STANDARD ) ) + { + if( pnStyleId ) *pnStyleId = EXC_STYLE_NORMAL; + if( pnNextChar ) *pnNextChar = rStyleName.Len(); + return true; + } + + // try the other built-in styles + sal_uInt8 nFoundId = 0; + xub_StrLen nNextChar = 0; + + xub_StrLen nPrefixLen = 0; + if( rStyleName.EqualsIgnoreCaseAscii( maStyleNamePrefix1, 0, maStyleNamePrefix1.Len() ) ) + nPrefixLen = maStyleNamePrefix1.Len(); + else if( rStyleName.EqualsIgnoreCaseAscii( maStyleNamePrefix2, 0, maStyleNamePrefix2.Len() ) ) + nPrefixLen = maStyleNamePrefix2.Len(); + if( nPrefixLen > 0 ) + { + String aShortName; + for( sal_uInt8 nId = 0; nId < STATIC_TABLE_SIZE( ppcStyleNames ); ++nId ) + { + if( nId != EXC_STYLE_NORMAL ) + { + aShortName.AssignAscii( ppcStyleNames[ nId ] ); + if( rStyleName.EqualsIgnoreCaseAscii( aShortName, nPrefixLen, aShortName.Len() ) && + (nNextChar < nPrefixLen + aShortName.Len()) ) + { + nFoundId = nId; + nNextChar = nPrefixLen + aShortName.Len(); + } + } + } + } + + if( nNextChar > 0 ) + { + if( pnStyleId ) *pnStyleId = nFoundId; + if( pnNextChar ) *pnNextChar = nNextChar; + return true; + } + + if( pnStyleId ) *pnStyleId = EXC_STYLE_USERDEF; + if( pnNextChar ) *pnNextChar = 0; + return nPrefixLen > 0; // also return true for unknown built-in styles +} + +bool XclTools::GetBuiltInStyleId( sal_uInt8& rnStyleId, sal_uInt8& rnLevel, const String& rStyleName ) +{ + sal_uInt8 nStyleId; + xub_StrLen nNextChar; + if( IsBuiltInStyleName( rStyleName, &nStyleId, &nNextChar ) && (nStyleId != EXC_STYLE_USERDEF) ) + { + if( (nStyleId == EXC_STYLE_ROWLEVEL) || (nStyleId == EXC_STYLE_COLLEVEL) ) + { + String aLevel( rStyleName, nNextChar, STRING_LEN ); + sal_Int32 nLevel = aLevel.ToInt32(); + if( (String::CreateFromInt32( nLevel ) == aLevel) && (nLevel > 0) && (nLevel <= EXC_STYLE_LEVELCOUNT) ) + { + rnStyleId = nStyleId; + rnLevel = static_cast< sal_uInt8 >( nLevel - 1 ); + return true; + } + } + else if( rStyleName.Len() == nNextChar ) + { + rnStyleId = nStyleId; + rnLevel = EXC_STYLE_NOLEVEL; + return true; + } + } + rnStyleId = EXC_STYLE_USERDEF; + rnLevel = EXC_STYLE_NOLEVEL; + return false; +} + +// conditional formatting style names ----------------------------------------- + +const String XclTools::maCFStyleNamePrefix1( RTL_CONSTASCII_USTRINGPARAM( "Excel_CondFormat_" ) ); +const String XclTools::maCFStyleNamePrefix2( RTL_CONSTASCII_USTRINGPARAM( "ConditionalStyle_" ) ); + +String XclTools::GetCondFormatStyleName( SCTAB nScTab, sal_Int32 nFormat, sal_uInt16 nCondition ) +{ + return String( maCFStyleNamePrefix1 ).Append( String::CreateFromInt32( nScTab + 1 ) ). + Append( '_' ).Append( String::CreateFromInt32( nFormat + 1 ) ). + Append( '_' ).Append( String::CreateFromInt32( nCondition + 1 ) ); +} + +bool XclTools::IsCondFormatStyleName( const String& rStyleName, xub_StrLen* pnNextChar ) +{ + xub_StrLen nPrefixLen = 0; + if( rStyleName.EqualsIgnoreCaseAscii( maCFStyleNamePrefix1, 0, maCFStyleNamePrefix1.Len() ) ) + nPrefixLen = maCFStyleNamePrefix1.Len(); + else if( rStyleName.EqualsIgnoreCaseAscii( maCFStyleNamePrefix2, 0, maCFStyleNamePrefix2.Len() ) ) + nPrefixLen = maCFStyleNamePrefix2.Len(); + if( pnNextChar ) *pnNextChar = nPrefixLen; + return nPrefixLen > 0; +} + +// stream handling ------------------------------------------------------------ + +void XclTools::SkipSubStream( XclImpStream& rStrm ) +{ + bool bLoop = true; + while( bLoop && rStrm.StartNextRecord() ) + { + sal_uInt16 nRecId = rStrm.GetRecId(); + bLoop = nRecId != EXC_ID_EOF; + if( (nRecId == EXC_ID2_BOF) || (nRecId == EXC_ID3_BOF) || (nRecId == EXC_ID4_BOF) || (nRecId == EXC_ID5_BOF) ) + SkipSubStream( rStrm ); + } +} + +// Basic macro names ---------------------------------------------------------- + +const OUString XclTools::maSbMacroPrefix( RTL_CONSTASCII_USTRINGPARAM( "vnd.sun.star.script:Standard." ) ); +const OUString XclTools::maSbMacroSuffix( RTL_CONSTASCII_USTRINGPARAM( "?language=Basic&location=document" ) ); + +OUString XclTools::GetSbMacroUrl( const String& rMacroName, SfxObjectShell* pDocShell ) +{ + OSL_ENSURE( rMacroName.Len() > 0, "XclTools::GetSbMacroUrl - macro name is empty" ); + ::ooo::vba::VBAMacroResolvedInfo aMacroInfo = ::ooo::vba::resolveVBAMacro( pDocShell, rMacroName, false ); + if( aMacroInfo.IsResolved() ) + return ::ooo::vba::makeMacroURL( aMacroInfo.ResolvedMacro() ); + return OUString(); +} + +OUString XclTools::GetSbMacroUrl( const String& rModuleName, const String& rMacroName, SfxObjectShell* pDocShell ) +{ + OSL_ENSURE( rModuleName.Len() > 0, "XclTools::GetSbMacroUrl - module name is empty" ); + OSL_ENSURE( rMacroName.Len() > 0, "XclTools::GetSbMacroUrl - macro name is empty" ); + return GetSbMacroUrl( rModuleName + OUString( sal_Unicode( '.' ) ) + rMacroName, pDocShell ); +} + +String XclTools::GetXclMacroName( const OUString& rSbMacroUrl ) +{ + sal_Int32 nSbMacroUrlLen = rSbMacroUrl.getLength(); + sal_Int32 nMacroNameLen = nSbMacroUrlLen - maSbMacroPrefix.getLength() - maSbMacroSuffix.getLength(); + if( (nMacroNameLen > 0) && rSbMacroUrl.matchIgnoreAsciiCase( maSbMacroPrefix, 0 ) && + rSbMacroUrl.matchIgnoreAsciiCase( maSbMacroSuffix, nSbMacroUrlLen - maSbMacroSuffix.getLength() ) ) + return rSbMacroUrl.copy( maSbMacroPrefix.getLength(), nMacroNameLen ); + return String::EmptyString(); +} + +// read/write colors ---------------------------------------------------------- + +XclImpStream& operator>>( XclImpStream& rStrm, Color& rColor ) +{ + sal_uInt8 nR, nG, nB, nD; + rStrm >> nR >> nG >> nB >> nD; + rColor.SetColor( RGB_COLORDATA( nR, nG, nB ) ); + return rStrm; +} + +XclExpStream& operator<<( XclExpStream& rStrm, const Color& rColor ) +{ + return rStrm << rColor.GetRed() << rColor.GetGreen() << rColor.GetBlue() << sal_uInt8( 0 ); +} + +// ============================================================================ diff --git a/sc/source/filter/excel/xltracer.cxx b/sc/source/filter/excel/xltracer.cxx new file mode 100644 index 000000000000..7dadbde8198a --- /dev/null +++ b/sc/source/filter/excel/xltracer.cxx @@ -0,0 +1,270 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +// ============================================================================ +#include "xltracer.hxx" +#include <filter/msfilter/msfiltertracer.hxx> +#include "address.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::beans::PropertyValue; + +// ============================================================================ + +// Trace Table details are grouped by functionality using the context entry. +// Each separate context starts at the next 1000 interval. New entries should +// be added to their appropriate context. New contexts should be added onto +// the end. Any gaps in the 1000 sequence or within the 1000 are the result +// of trace events no longer present. +static const XclTracerDetails pTracerDetails[] = +{ + { eUnKnown, 0, "UNKNOWN", "UNKNOWN", "Unknown trace property." }, + { eRowLimitExceeded, 1000, "Limits", "Sheet", "Row limit exceeded." }, + { eTabLimitExceeded, 1001, "Limits", "Sheet", "Sheet limit exceeded." }, + { ePassword, 2000, "Protection", "Password", "Document is password protected." }, + { ePrintRange, 3000, "Print", "Print Range", "Print Range present." }, + { eShortDate, 4000, "CellFormatting", "Short Dates", "Possible Date format issue." }, + { eBorderLineStyle, 4004, "CellFormatting", "Border", "Line style not supported.", }, + { eFillPattern, 4005, "CellFormatting", "Pattern", "Fill Pattern not supported.", }, + { eInvisibleGrid, 5000, "Properties", "Grid Invisible", "Grid invisible on first sheet." }, + { eFormattedNote, 6000, "Notes", "Formatting", "Text may be formatted." }, + { eFormulaExtName, 7000, "Formula", "External Name", "External names not supported." }, + { eFormulaMissingArg, 7001, "Formula", "Missing Argument","Parameter missing." }, + { ePivotDataSource, 8000, "Chart", "Pivot", "External data source not supported."}, + { ePivotChartExists, 8001, "Chart", "Pivot", "Pivot Chart not supported."}, + { eChartUnKnownType, 8002, "Chart", "Type", "Chart Type not supported."}, + { eChartTrendLines, 8003, "Chart", "Type", "Chart trendlines not supported."}, + { eChartOnlySheet, 8004, "Chart", "Type", "Chart only sheet not supported."}, + { eChartRange, 8005, "Chart", "Source Data", "Chart source ranges too complex."}, + { eChartDSName, 8006, "Chart", "Source Data", "Series titles not linked to cells."}, + { eChartDataTable, 8007, "Chart", "Legend", "Data table not supported."}, + { eChartLegendPosition, 8008, "Chart", "Legend", "Position not guaranteed."}, + { eChartTextFormatting, 8009, "Chart", "Formatting", "Text formatting present."}, + { eChartEmbeddedObj, 8010, "Chart", "Area", "Object inside chart."}, + { eChartAxisAuto, 8012, "Chart", "Axis", "Axis interval is automatic."}, + { eChartInvalidXY, 8013, "Chart", "Scatter", "Unsupported or invalid data range for XY chart."}, + { eChartErrorBars, 8014, "Chart", "Type", "Chart error bars not supported."}, + { eChartAxisManual, 8015, "Chart", "Axis", "Manual axis crossing point adjusted."}, + { eUnsupportedObject, 9000, "Object", "Type", "Limited Object support."}, + { eObjectNotPrintable, 9001, "Object", "Print", "Object not printable."}, + { eDVType, 10000, "DataValidation", "Type", "Custom type present."} +}; + +XclTracer::XclTracer( const String& rDocUrl, const OUString& rConfigPath ) : + maFirstTimes(eTraceLength,true) +{ + Sequence< PropertyValue > aConfigData( 1 ); + aConfigData[ 0 ].Name = CREATE_OUSTRING( "DocumentURL" ); + aConfigData[ 0 ].Value <<= OUString( rDocUrl ); + mpTracer.reset( new MSFilterTracer( rConfigPath, &aConfigData ) ); + mpTracer->StartTracing(); + mbEnabled = mpTracer->IsEnabled(); +} + +XclTracer::~XclTracer() +{ + mpTracer->EndTracing(); +} + +void XclTracer::AddAttribute( const OUString& rName, const OUString& rValue ) +{ + if( mbEnabled ) + mpTracer->AddAttribute( rName, rValue ); +} + +void XclTracer::Trace( const OUString& rElementID, const OUString& rMessage ) +{ + if( mbEnabled ) + { + mpTracer->Trace( rElementID, rMessage ); + mpTracer->ClearAttributes(); + } +} + +void XclTracer::TraceLog( XclTracerId eProblem, sal_Int32 nValue ) +{ + if( mbEnabled ) + { + OUString sID( CREATE_STRING( "SC" ) ); + sID += OUString::valueOf( static_cast< sal_Int32 >( pTracerDetails[ eProblem ].mnID ) ); + OUString sProblem = OUString::createFromAscii( pTracerDetails[ eProblem ].mpProblem ); + + switch (eProblem) + { + case eRowLimitExceeded: + Context(eProblem,static_cast<SCTAB>(nValue)); + break; + case eTabLimitExceeded: + Context(eProblem,static_cast<SCTAB>(nValue)); + break; + default: + Context(eProblem); + break; + } + Trace(sID, sProblem); + } +} + +void XclTracer::Context( XclTracerId eProblem, SCTAB nTab ) +{ + OUString sContext = OUString::createFromAscii( pTracerDetails[ eProblem ].mpContext ); + OUString sDetail = OUString::createFromAscii( pTracerDetails[ eProblem ].mpDetail ); + + switch (eProblem) + { + case eRowLimitExceeded: + case eTabLimitExceeded: + sDetail += OUString::valueOf( static_cast< sal_Int32 >( nTab + 1 ) ); + break; + default: + break; + } + AddAttribute(sContext, sDetail); +} + +void XclTracer::ProcessTraceOnce(XclTracerId eProblem, SCTAB nTab) +{ + if( mbEnabled && maFirstTimes[eProblem]) + { + TraceLog(pTracerDetails[eProblem].meProblemId, nTab); + maFirstTimes[eProblem] = false; + } +} + +void XclTracer::TraceInvalidAddress( const ScAddress& rPos, const ScAddress& rMaxPos ) +{ + TraceInvalidRow(rPos.Tab(), rPos.Row(), rMaxPos.Row()); + TraceInvalidTab(rPos.Tab(), rMaxPos.Tab()); +} + +void XclTracer::TraceInvalidRow( SCTAB nTab, sal_uInt32 nRow, sal_uInt32 nMaxRow ) +{ + if(nRow > nMaxRow) + ProcessTraceOnce(eRowLimitExceeded, nTab); +} + +void XclTracer::TraceInvalidTab( SCTAB nTab, SCTAB nMaxTab ) +{ + if(nTab > nMaxTab) + ProcessTraceOnce(eTabLimitExceeded, nTab); +} + +void XclTracer::TracePrintRange() +{ + ProcessTraceOnce( ePrintRange); +} + +void XclTracer::TraceDates( sal_uInt16 nNumFmt) +{ + // Short Date = 14 and Short Date+Time = 22 + if(nNumFmt == 14 || nNumFmt == 22) + ProcessTraceOnce(eShortDate); +} + +void XclTracer::TraceBorderLineStyle( bool bBorderLineStyle) +{ + if(bBorderLineStyle) + ProcessTraceOnce(eBorderLineStyle); +} + +void XclTracer::TraceFillPattern( bool bFillPattern) +{ + if(bFillPattern) + ProcessTraceOnce(eFillPattern); +} + +void XclTracer::TraceFormulaMissingArg() +{ + // missing parameter in Formula record + ProcessTraceOnce(eFormulaMissingArg); +} + +void XclTracer::TracePivotDataSource( bool bExternal) +{ + if(bExternal) + ProcessTraceOnce(ePivotDataSource); +} + +void XclTracer::TracePivotChartExists() +{ + // Pivot Charts not currently displayed. + ProcessTraceOnce(ePivotChartExists); +} + +void XclTracer::TraceChartUnKnownType() +{ + ProcessTraceOnce(eChartUnKnownType); +} + +void XclTracer::TraceChartOnlySheet() +{ + ProcessTraceOnce(eChartOnlySheet); +} + +void XclTracer::TraceChartDataTable() +{ + // Data table is not supported. + ProcessTraceOnce(eChartDataTable); +} + +void XclTracer::TraceChartLegendPosition() +{ + // If position is set to "not docked or inside the plot area" then + // we cannot guarantee the legend position. + ProcessTraceOnce(eChartLegendPosition); +} + +void XclTracer::TraceChartEmbeddedObj() +{ + // drawing objects e.g. text boxes etc not supported inside charts + ProcessTraceOnce(eChartEmbeddedObj); +} + +void XclTracer::TraceUnsupportedObjects() +{ + // Called from Excel 5.0 - limited Graphical object support. + ProcessTraceOnce(eUnsupportedObject); +} + +void XclTracer::TraceObjectNotPrintable() +{ + ProcessTraceOnce(eObjectNotPrintable); +} + +void XclTracer::TraceDVType( bool bType) +{ + // Custom types work if 'Data->validity dialog' is not OKed. + if(bType) + ProcessTraceOnce(eDVType); +} + +// ============================================================================ + diff --git a/sc/source/filter/excel/xlview.cxx b/sc/source/filter/excel/xlview.cxx new file mode 100644 index 000000000000..8e9df7e0ac1f --- /dev/null +++ b/sc/source/filter/excel/xlview.cxx @@ -0,0 +1,115 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include "xlview.hxx" +#include "ftools.hxx" + +// Structs ==================================================================== + +XclDocViewData::XclDocViewData() : + mnWinX( 0 ), + mnWinY( 0 ), + mnWinWidth( 0 ), + mnWinHeight( 0 ), + mnFlags( EXC_WIN1_HOR_SCROLLBAR | EXC_WIN1_VER_SCROLLBAR | EXC_WIN1_TABBAR ), + mnDisplXclTab( 0 ), + mnFirstVisXclTab( 0 ), + mnXclSelectCnt( 1 ), + mnTabBarWidth( 600 ) +{ +} + +// ---------------------------------------------------------------------------- + +XclTabViewData::XclTabViewData() : + maFirstXclPos( ScAddress::UNINITIALIZED ), + maSecondXclPos( ScAddress::UNINITIALIZED ) +{ + SetDefaults(); +} + +XclTabViewData::~XclTabViewData() +{ +} + +void XclTabViewData::SetDefaults() +{ + maSelMap.clear(); + maGridColor.SetColor( COL_AUTO ); + maFirstXclPos.Set( 0, 0 ); + maSecondXclPos.Set( 0, 0 ); + mnSplitX = mnSplitY = 0; + mnNormalZoom = EXC_WIN2_NORMALZOOM_DEF; + mnPageZoom = EXC_WIN2_PAGEZOOM_DEF; + mnCurrentZoom = 0; // default to mnNormalZoom or mnPageZoom + mnActivePane = EXC_PANE_TOPLEFT; + mbSelected = mbDisplayed = false; + mbMirrored = false; + mbFrozenPanes = false; + mbPageMode = false; + mbDefGridColor = true; + mbShowFormulas = false; + mbShowGrid = mbShowHeadings = mbShowZeros = mbShowOutline = true; + maTabBgColor.SetColor( COL_AUTO ); +} + +bool XclTabViewData::IsSplit() const +{ + return (mnSplitX > 0) || (mnSplitY > 0); +} + +bool XclTabViewData::HasPane( sal_uInt8 nPaneId ) const +{ + switch( nPaneId ) + { + case EXC_PANE_BOTTOMRIGHT: return (mnSplitX > 0) && (mnSplitY > 0); + case EXC_PANE_TOPRIGHT: return mnSplitX > 0; + case EXC_PANE_BOTTOMLEFT: return mnSplitY > 0; + case EXC_PANE_TOPLEFT: return true; + } + DBG_ERRORFILE( "XclExpPane::HasPane - wrong pane ID" ); + return false; +} + +const XclSelectionData* XclTabViewData::GetSelectionData( sal_uInt8 nPane ) const +{ + XclSelectionMap::const_iterator aIt = maSelMap.find( nPane ); + return (aIt == maSelMap.end()) ? 0 : aIt->second.get(); +} + +XclSelectionData& XclTabViewData::CreateSelectionData( sal_uInt8 nPane ) +{ + XclSelectionDataRef& rxSelData = maSelMap[ nPane ]; + if( !rxSelData ) + rxSelData.reset( new XclSelectionData ); + return *rxSelData; +} + +// ============================================================================ + |