diff options
Diffstat (limited to 'sc/source/core/tool')
67 files changed, 60735 insertions, 0 deletions
diff --git a/sc/source/core/tool/addincfg.cxx b/sc/source/core/tool/addincfg.cxx new file mode 100644 index 000000000000..c76b60eae077 --- /dev/null +++ b/sc/source/core/tool/addincfg.cxx @@ -0,0 +1,72 @@ +/************************************************************************* + * + * 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 <tools/debug.hxx> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include "global.hxx" +#include "addincol.hxx" +#include "addincfg.hxx" +#include "scmod.hxx" +#include "sc.hrc" + +using namespace com::sun::star; + +//================================================================== + +#define CFGPATH_ADDINS "Office.CalcAddIns/AddInInfo" + +ScAddInCfg::ScAddInCfg() : + ConfigItem( rtl::OUString::createFromAscii( CFGPATH_ADDINS ) ) +{ + uno::Sequence<rtl::OUString> aNames(1); // one entry: empty string + EnableNotification( aNames ); +} + +void ScAddInCfg::Commit() +{ + DBG_ERROR("ScAddInCfg shouldn't be modified"); +} + +void ScAddInCfg::Notify( const uno::Sequence<rtl::OUString>& ) +{ + // forget all add-in information, re-initialize when needed next time + ScGlobal::GetAddInCollection()->Clear(); + + // function list must also be rebuilt, but can't be modified while function + // autopilot is open (function list for autopilot is then still old) + if ( SC_MOD()->GetCurRefDlgId() != SID_OPENDLG_FUNCTION ) + ScGlobal::ResetFunctionList(); +} + + diff --git a/sc/source/core/tool/addincol.cxx b/sc/source/core/tool/addincol.cxx new file mode 100644 index 000000000000..07b79c11b373 --- /dev/null +++ b/sc/source/core/tool/addincol.cxx @@ -0,0 +1,1802 @@ +/************************************************************************* + * + * 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 <comphelper/processfactory.hxx> +#include <tools/debug.hxx> +#include <i18npool/mslangid.hxx> +#include <vcl/svapp.hxx> +#include <vos/xception.hxx> +#include <sfx2/objsh.hxx> +#include <unotools/charclass.hxx> + +#include <com/sun/star/container/XContentEnumerationAccess.hpp> +#include <com/sun/star/lang/XServiceName.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/reflection/XIdlClass.hpp> +#include <com/sun/star/reflection/XIdlClassProvider.hpp> +#include <com/sun/star/beans/XIntrospectionAccess.hpp> +#include <com/sun/star/beans/XIntrospection.hpp> +#include <com/sun/star/beans/MethodConcept.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/sheet/XCompatibilityNames.hpp> +#include <com/sun/star/sheet/NoConvergenceException.hpp> + +#include "addincol.hxx" +#include "addinhelpid.hxx" +#include "compiler.hxx" +#include "scmatrix.hxx" +#include "addinlis.hxx" +#include "formula/errorcodes.hxx" +#include "scfuncs.hrc" +#include "optutil.hxx" +#include "addincfg.hxx" +#include "scmod.hxx" +#include "rangeseq.hxx" +#include "funcdesc.hxx" + +using namespace com::sun::star; + +//------------------------------------------------------------------------ + +#define SC_CALLERPOS_NONE (-1) + +#define SCADDINSUPPLIER_SERVICE "com.sun.star.sheet.AddIn" + +//------------------------------------------------------------------------ + + + + +//------------------------------------------------------------------------ + +ScUnoAddInFuncData::ScUnoAddInFuncData( const String& rNam, const String& rLoc, + const String& rDesc, + USHORT nCat, USHORT nHelp, + const uno::Reference<reflection::XIdlMethod>& rFunc, + const uno::Any& rO, + long nAC, const ScAddInArgDesc* pAD, + long nCP ) : + aOriginalName( rNam ), + aLocalName( rLoc ), + aUpperName( rNam ), + aUpperLocal( rLoc ), + aDescription( rDesc ), + xFunction( rFunc ), + aObject( rO ), + nArgCount( nAC ), + nCallerPos( nCP ), + nCategory( nCat ), + nHelpId( nHelp ), + bCompInitialized( FALSE ) +{ + if ( nArgCount ) + { + pArgDescs = new ScAddInArgDesc[nArgCount]; + for (long i=0; i<nArgCount; i++) + pArgDescs[i] = pAD[i]; + } + else + pArgDescs = NULL; + + ScGlobal::pCharClass->toUpper(aUpperName); + ScGlobal::pCharClass->toUpper(aUpperLocal); +} + +ScUnoAddInFuncData::~ScUnoAddInFuncData() +{ + delete[] pArgDescs; +} + +const uno::Sequence<sheet::LocalizedName>& ScUnoAddInFuncData::GetCompNames() const +{ + if ( !bCompInitialized ) + { + // read sequence of compatibility names on demand + + uno::Reference<sheet::XAddIn> xAddIn; + if ( aObject >>= xAddIn ) + { + uno::Reference<sheet::XCompatibilityNames> xComp( xAddIn, uno::UNO_QUERY ); + if ( xComp.is() && xFunction.is() ) + { + rtl::OUString aMethodName = xFunction->getName(); + aCompNames = xComp->getCompatibilityNames( aMethodName ); + + // change all locale entries to default case + // (language in lower case, country in upper case) + // for easier searching + + long nSeqLen = aCompNames.getLength(); + if ( nSeqLen ) + { + sheet::LocalizedName* pArray = aCompNames.getArray(); + for (long i=0; i<nSeqLen; i++) + { + lang::Locale& rLocale = pArray[i].Locale; + rLocale.Language = rLocale.Language.toAsciiLowerCase(); + rLocale.Country = rLocale.Country.toAsciiUpperCase(); + } + } + } + } + + bCompInitialized = TRUE; // also if not successful + } + return aCompNames; +} + +void ScUnoAddInFuncData::SetCompNames( const uno::Sequence< sheet::LocalizedName>& rNew ) +{ + DBG_ASSERT( !bCompInitialized, "SetCompNames after initializing" ); + + aCompNames = rNew; + + // change all locale entries to default case + // (language in lower case, country in upper case) + // for easier searching + + long nSeqLen = aCompNames.getLength(); + if ( nSeqLen ) + { + sheet::LocalizedName* pArray = aCompNames.getArray(); + for (long i=0; i<nSeqLen; i++) + { + lang::Locale& rLocale = pArray[i].Locale; + rLocale.Language = rLocale.Language.toAsciiLowerCase(); + rLocale.Country = rLocale.Country.toAsciiUpperCase(); + } + } + + bCompInitialized = TRUE; +} + +BOOL ScUnoAddInFuncData::GetExcelName( LanguageType eDestLang, String& rRetExcelName ) const +{ + const uno::Sequence<sheet::LocalizedName>& rSequence = GetCompNames(); + long nSeqLen = rSequence.getLength(); + if ( nSeqLen ) + { + const sheet::LocalizedName* pArray = rSequence.getConstArray(); + long i; + + rtl::OUString aLangStr, aCountryStr; + MsLangId::convertLanguageToIsoNames( eDestLang, aLangStr, aCountryStr ); + rtl::OUString aUserLang = aLangStr.toAsciiLowerCase(); + rtl::OUString aUserCountry = aCountryStr.toAsciiUpperCase(); + + // first check for match of both language and country + + for ( i=0; i<nSeqLen; i++) + if ( pArray[i].Locale.Language == aUserLang && + pArray[i].Locale.Country == aUserCountry ) + { + rRetExcelName = pArray[i].Name; + return TRUE; + } + + // second: check only language + + for ( i=0; i<nSeqLen; i++) + if ( pArray[i].Locale.Language == aUserLang ) + { + rRetExcelName = pArray[i].Name; + return TRUE; + } + + // third: #i57772# fall-back to en-US + + if ( eDestLang != LANGUAGE_ENGLISH_US ) + return GetExcelName( LANGUAGE_ENGLISH_US, rRetExcelName ); + + // forth: use first (default) entry + + rRetExcelName = pArray[0].Name; + return TRUE; + } + return FALSE; +} + +void ScUnoAddInFuncData::SetFunction( const uno::Reference< reflection::XIdlMethod>& rNewFunc, const uno::Any& rNewObj ) +{ + xFunction = rNewFunc; + aObject = rNewObj; +} + +void ScUnoAddInFuncData::SetArguments( long nNewCount, const ScAddInArgDesc* pNewDescs ) +{ + delete[] pArgDescs; + + nArgCount = nNewCount; + if ( nArgCount ) + { + pArgDescs = new ScAddInArgDesc[nArgCount]; + for (long i=0; i<nArgCount; i++) + pArgDescs[i] = pNewDescs[i]; + } + else + pArgDescs = NULL; +} + +void ScUnoAddInFuncData::SetCallerPos( long nNewPos ) +{ + nCallerPos = nNewPos; +} + +//------------------------------------------------------------------------ + +ScUnoAddInCollection::ScUnoAddInCollection() : + nFuncCount( 0 ), + ppFuncData( NULL ), + pExactHashMap( NULL ), + pNameHashMap( NULL ), + pLocalHashMap( NULL ), + bInitialized( FALSE ) +{ +} + +ScUnoAddInCollection::~ScUnoAddInCollection() +{ + Clear(); +} + +void ScUnoAddInCollection::Clear() +{ + DELETEZ( pExactHashMap ); + DELETEZ( pNameHashMap ); + DELETEZ( pLocalHashMap ); + if ( ppFuncData ) + { + for ( long i=0; i<nFuncCount; i++ ) + delete ppFuncData[i]; + delete[] ppFuncData; + } + ppFuncData = NULL; + nFuncCount = 0; + + bInitialized = FALSE; +} + +uno::Reference<uno::XComponentContext> getContext(uno::Reference<lang::XMultiServiceFactory> xMSF) +{ + uno::Reference<uno::XComponentContext> xCtx; + try { + uno::Reference<beans::XPropertySet> xPropset(xMSF, uno::UNO_QUERY); + xPropset->getPropertyValue( + ::rtl::OUString::createFromAscii("DefaultContext")) >>= xCtx; + } + catch ( uno::Exception & ) { + } + return xCtx; +} + +void ScUnoAddInCollection::Initialize() +{ + DBG_ASSERT( !bInitialized, "Initialize twice?" ); + + uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory(); + uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY ); + if ( xEnAc.is() ) + { + uno::Reference<container::XEnumeration> xEnum = + xEnAc->createContentEnumeration( + rtl::OUString::createFromAscii(SCADDINSUPPLIER_SERVICE) ); + if ( xEnum.is() ) + { + // loop through all AddIns + while ( xEnum->hasMoreElements() ) + { + uno::Any aAddInAny = xEnum->nextElement(); +//? if ( aAddInAny.getReflection()->getTypeClass() == uno::TypeClass_INTERFACE ) + { + uno::Reference<uno::XInterface> xIntFac; + aAddInAny >>= xIntFac; + if ( xIntFac.is() ) + { + // #i59984# try XSingleComponentFactory in addition to (old) XSingleServiceFactory, + // passing the context to the component + + uno::Reference<uno::XInterface> xInterface; + uno::Reference<uno::XComponentContext> xCtx = getContext(xManager); + uno::Reference<lang::XSingleComponentFactory> xCFac( xIntFac, uno::UNO_QUERY ); + if (xCtx.is() && xCFac.is()) + { + xInterface = xCFac->createInstanceWithContext(xCtx); + if (xInterface.is()) + ReadFromAddIn( xInterface ); + } + + if (!xInterface.is()) + { + uno::Reference<lang::XSingleServiceFactory> xFac( xIntFac, uno::UNO_QUERY ); + if ( xFac.is() ) + { + xInterface = xFac->createInstance(); + if (xInterface.is()) + ReadFromAddIn( xInterface ); + } + } + } + } + } + } + } + + // ReadConfiguration is called after looking at the AddIn implementations. + // Duplicated are skipped (by using the service information, they don't have to be updated again + // when argument information is needed). + ReadConfiguration(); + + bInitialized = TRUE; // with or without functions +} +// ----------------------------------------------------------------------------- + +USHORT lcl_GetCategory( const String& rName ) +{ + static const sal_Char* aFuncNames[SC_FUNCGROUP_COUNT] = + { + // array index = ID - 1 (ID starts at 1) + // all upper case + "Database", // ID_FUNCTION_GRP_DATABASE + "Date&Time", // ID_FUNCTION_GRP_DATETIME + "Financial", // ID_FUNCTION_GRP_FINANZ + "Information", // ID_FUNCTION_GRP_INFO + "Logical", // ID_FUNCTION_GRP_LOGIC + "Mathematical", // ID_FUNCTION_GRP_MATH + "Matrix", // ID_FUNCTION_GRP_MATRIX + "Statistical", // ID_FUNCTION_GRP_STATISTIC + "Spreadsheet", // ID_FUNCTION_GRP_TABLE + "Text", // ID_FUNCTION_GRP_TEXT + "Add-In" // ID_FUNCTION_GRP_ADDINS + }; + for (USHORT i=0; i<SC_FUNCGROUP_COUNT; i++) + if ( rName.EqualsAscii( aFuncNames[i] ) ) + return i+1; // IDs start at 1 + + return ID_FUNCTION_GRP_ADDINS; // if not found, use Add-In group +} + + +#define CFGPATH_ADDINS "Office.CalcAddIns/AddInInfo" +#define CFGSTR_ADDINFUNCTIONS "AddInFunctions" + +#define CFG_FUNCPROP_DISPLAYNAME 0 +#define CFG_FUNCPROP_DESCRIPTION 1 +#define CFG_FUNCPROP_CATEGORY 2 +#define CFG_FUNCPROP_COUNT 3 +#define CFGSTR_DISPLAYNAME "DisplayName" +#define CFGSTR_DESCRIPTION "Description" +#define CFGSTR_CATEGORY "Category" +// CategoryDisplayName is ignored for now + +#define CFGSTR_COMPATIBILITYNAME "CompatibilityName" +#define CFGSTR_PARAMETERS "Parameters" + + +void ScUnoAddInCollection::ReadConfiguration() +{ + // called only from Initialize + + ScAddInCfg& rAddInConfig = SC_MOD()->GetAddInCfg(); + + // additional, temporary config item for the compatibility names + ScLinkConfigItem aAllLocalesConfig( rtl::OUString::createFromAscii( CFGPATH_ADDINS ), CONFIG_MODE_ALL_LOCALES ); + // CommitLink is not used (only reading values) + + const rtl::OUString sSlash('/'); + + // get the list of add-ins (services) + rtl::OUString aEmptyString; + uno::Sequence<rtl::OUString> aServiceNames = rAddInConfig.GetNodeNames( aEmptyString ); + + sal_Int32 nServiceCount = aServiceNames.getLength(); + for ( sal_Int32 nService = 0; nService < nServiceCount; nService++ ) + { + rtl::OUString aServiceName = aServiceNames[nService]; + ScUnoAddInHelpIdGenerator aHelpIdGenerator( aServiceName ); + + rtl::OUString aFunctionsPath = aServiceName; + aFunctionsPath += sSlash; + aFunctionsPath += rtl::OUString::createFromAscii( CFGSTR_ADDINFUNCTIONS ); + + uno::Sequence<rtl::OUString> aFunctionNames = rAddInConfig.GetNodeNames( aFunctionsPath ); + sal_Int32 nNewCount = aFunctionNames.getLength(); + + // allocate pointers + + long nOld = nFuncCount; + nFuncCount = nNewCount+nOld; + if ( nOld ) + { + ScUnoAddInFuncData** ppNew = new ScUnoAddInFuncData*[nFuncCount]; + for (long i=0; i<nOld; i++) + ppNew[i] = ppFuncData[i]; + delete[] ppFuncData; + ppFuncData = ppNew; + } + else + ppFuncData = new ScUnoAddInFuncData*[nFuncCount]; + + //! TODO: adjust bucket count? + if ( !pExactHashMap ) + pExactHashMap = new ScAddInHashMap; + if ( !pNameHashMap ) + pNameHashMap = new ScAddInHashMap; + if ( !pLocalHashMap ) + pLocalHashMap = new ScAddInHashMap; + + //! get the function information in a single call for all functions? + + const rtl::OUString* pFuncNameArray = aFunctionNames.getConstArray(); + for ( sal_Int32 nFuncPos = 0; nFuncPos < nNewCount; nFuncPos++ ) + { + ppFuncData[nFuncPos+nOld] = NULL; + + // stored function name: (service name).(function) + String aFuncName( aServiceName ); + aFuncName += '.'; + aFuncName += String( pFuncNameArray[nFuncPos] ); + + // skip the function if already known (read from old AddIn service) + + if ( pExactHashMap->find( aFuncName ) == pExactHashMap->end() ) + { + rtl::OUString aLocalName; + rtl::OUString aDescription; + USHORT nCategory = ID_FUNCTION_GRP_ADDINS; + + // get direct information on the function + + rtl::OUString aFuncPropPath = aFunctionsPath; + aFuncPropPath += sSlash; + aFuncPropPath += pFuncNameArray[nFuncPos]; + aFuncPropPath += sSlash; + + uno::Sequence<rtl::OUString> aFuncPropNames(CFG_FUNCPROP_COUNT); + rtl::OUString* pNameArray = aFuncPropNames.getArray(); + pNameArray[CFG_FUNCPROP_DISPLAYNAME] = aFuncPropPath; + pNameArray[CFG_FUNCPROP_DISPLAYNAME] += rtl::OUString::createFromAscii( CFGSTR_DISPLAYNAME ); + pNameArray[CFG_FUNCPROP_DESCRIPTION] = aFuncPropPath; + pNameArray[CFG_FUNCPROP_DESCRIPTION] += rtl::OUString::createFromAscii( CFGSTR_DESCRIPTION ); + pNameArray[CFG_FUNCPROP_CATEGORY] = aFuncPropPath; + pNameArray[CFG_FUNCPROP_CATEGORY] += rtl::OUString::createFromAscii( CFGSTR_CATEGORY ); + + uno::Sequence<uno::Any> aFuncProperties = rAddInConfig.GetProperties( aFuncPropNames ); + if ( aFuncProperties.getLength() == CFG_FUNCPROP_COUNT ) + { + aFuncProperties[CFG_FUNCPROP_DISPLAYNAME] >>= aLocalName; + aFuncProperties[CFG_FUNCPROP_DESCRIPTION] >>= aDescription; + + rtl::OUString aCategoryName; + aFuncProperties[CFG_FUNCPROP_CATEGORY] >>= aCategoryName; + nCategory = lcl_GetCategory( aCategoryName ); + } + + // get compatibility names + + uno::Sequence<sheet::LocalizedName> aCompNames; + + rtl::OUString aCompPath = aFuncPropPath; + aCompPath += rtl::OUString::createFromAscii( CFGSTR_COMPATIBILITYNAME ); + uno::Sequence<rtl::OUString> aCompPropNames( &aCompPath, 1 ); + + uno::Sequence<uno::Any> aCompProperties = aAllLocalesConfig.GetProperties( aCompPropNames ); + if ( aCompProperties.getLength() == 1 ) + { + uno::Sequence<beans::PropertyValue> aLocalEntries; + if ( aCompProperties[0] >>= aLocalEntries ) + { + sal_Int32 nLocaleCount = aLocalEntries.getLength(); + aCompNames.realloc( nLocaleCount ); + const beans::PropertyValue* pConfigArray = aLocalEntries.getConstArray(); + sheet::LocalizedName* pCompArray = aCompNames.getArray(); + + for ( sal_Int32 nLocale = 0; nLocale < nLocaleCount; nLocale++ ) + { + const sal_Unicode cLocaleSep = '-'; // separator in configuration locale strings + + // PropertyValue name is the locale (convert from string to Locale struct) + + const rtl::OUString& rLocaleStr = pConfigArray[nLocale].Name; + lang::Locale& rLocale = pCompArray[nLocale].Locale; + sal_Int32 nSepPos = rLocaleStr.indexOf( cLocaleSep ); + if ( nSepPos >= 0 ) + { + rLocale.Language = rLocaleStr.copy( 0, nSepPos ); + rLocale.Country = rLocaleStr.copy( nSepPos+1 ); + } + else + rLocale.Language = rLocaleStr; // leave country empty (default ctor from sequence) + + // PropertyValue value is the localized value (string in this case) + + pConfigArray[nLocale].Value >>= pCompArray[nLocale].Name; + } + } + } + + // get argument info + + ScAddInArgDesc* pVisibleArgs = NULL; + long nVisibleCount = 0; + long nCallerPos = SC_CALLERPOS_NONE; + + rtl::OUString aArgumentsPath = aFuncPropPath; + aArgumentsPath += rtl::OUString::createFromAscii( CFGSTR_PARAMETERS ); + + uno::Sequence<rtl::OUString> aArgumentNames = rAddInConfig.GetNodeNames( aArgumentsPath ); + sal_Int32 nArgumentCount = aArgumentNames.getLength(); + if ( nArgumentCount ) + { + // get DisplayName and Description for each argument + uno::Sequence<rtl::OUString> aArgPropNames( nArgumentCount * 2 ); + rtl::OUString* pPropNameArray = aArgPropNames.getArray(); + + sal_Int32 nArgument; + sal_Int32 nIndex = 0; + const rtl::OUString* pArgNameArray = aArgumentNames.getConstArray(); + for ( nArgument = 0; nArgument < nArgumentCount; nArgument++ ) + { + rtl::OUString aOneArgPath = aArgumentsPath; + aOneArgPath += sSlash; + aOneArgPath += pArgNameArray[nArgument]; + aOneArgPath += sSlash; + + pPropNameArray[nIndex] = aOneArgPath; + pPropNameArray[nIndex++] += rtl::OUString::createFromAscii( CFGSTR_DISPLAYNAME ); + pPropNameArray[nIndex] = aOneArgPath; + pPropNameArray[nIndex++] += rtl::OUString::createFromAscii( CFGSTR_DESCRIPTION ); + } + + uno::Sequence<uno::Any> aArgProperties = rAddInConfig.GetProperties( aArgPropNames ); + if ( aArgProperties.getLength() == aArgPropNames.getLength() ) + { + const uno::Any* pPropArray = aArgProperties.getConstArray(); + rtl::OUString sDisplayName; + rtl::OUString sDescription; + + ScAddInArgDesc aDesc; + aDesc.eType = SC_ADDINARG_NONE; // arg type is not in configuration + aDesc.bOptional = FALSE; + + nVisibleCount = nArgumentCount; + pVisibleArgs = new ScAddInArgDesc[nVisibleCount]; + + nIndex = 0; + for ( nArgument = 0; nArgument < nArgumentCount; nArgument++ ) + { + pPropArray[nIndex++] >>= sDisplayName; + pPropArray[nIndex++] >>= sDescription; + + aDesc.aInternalName = pArgNameArray[nArgument]; + aDesc.aName = sDisplayName; + aDesc.aDescription = sDescription; + + pVisibleArgs[nArgument] = aDesc; + } + } + } + + USHORT nHelpId = aHelpIdGenerator.GetHelpId( pFuncNameArray[nFuncPos] ); + + uno::Reference<reflection::XIdlMethod> xFunc; // remains empty + uno::Any aObject; // also empty + + // create and insert into the array + + ScUnoAddInFuncData* pData = new ScUnoAddInFuncData( + aFuncName, aLocalName, aDescription, + nCategory, nHelpId, + xFunc, aObject, + nVisibleCount, pVisibleArgs, nCallerPos ); + + pData->SetCompNames( aCompNames ); + + ppFuncData[nFuncPos+nOld] = pData; + + pExactHashMap->insert( + ScAddInHashMap::value_type( + pData->GetOriginalName(), + pData ) ); + pNameHashMap->insert( + ScAddInHashMap::value_type( + pData->GetUpperName(), + pData ) ); + pLocalHashMap->insert( + ScAddInHashMap::value_type( + pData->GetUpperLocal(), + pData ) ); + + delete[] pVisibleArgs; + } + } + } +} + +void ScUnoAddInCollection::LoadComponent( const ScUnoAddInFuncData& rFuncData ) +{ + String aFullName = rFuncData.GetOriginalName(); + xub_StrLen nPos = aFullName.SearchBackward( (sal_Unicode) '.' ); + if ( nPos != STRING_NOTFOUND && nPos > 0 ) + { + String aServiceName = aFullName.Copy( 0, nPos ); + + uno::Reference<lang::XMultiServiceFactory> xServiceFactory = comphelper::getProcessServiceFactory(); + uno::Reference<uno::XInterface> xInterface( xServiceFactory->createInstance( aServiceName ) ); + + if (xInterface.is()) + UpdateFromAddIn( xInterface, aServiceName ); + } +} + +BOOL ScUnoAddInCollection::GetExcelName( const String& rCalcName, + LanguageType eDestLang, String& rRetExcelName ) +{ + const ScUnoAddInFuncData* pFuncData = GetFuncData( rCalcName ); + if ( pFuncData ) + return pFuncData->GetExcelName( eDestLang, rRetExcelName); + return FALSE; +} + +BOOL ScUnoAddInCollection::GetCalcName( const String& rExcelName, String& rRetCalcName ) +{ + if (!bInitialized) + Initialize(); + + String aUpperCmp = rExcelName; + ScGlobal::pCharClass->toUpper(aUpperCmp); + + for (long i=0; i<nFuncCount; i++) + { + ScUnoAddInFuncData* pFuncData = ppFuncData[i]; + if ( pFuncData ) + { + const uno::Sequence<sheet::LocalizedName>& rSequence = pFuncData->GetCompNames(); + long nSeqLen = rSequence.getLength(); + if ( nSeqLen ) + { + const sheet::LocalizedName* pArray = rSequence.getConstArray(); + for ( long nName=0; nName<nSeqLen; nName++) + if ( ScGlobal::pCharClass->upper( pArray[nName].Name ) == aUpperCmp ) + { + //! store upper case for comparing? + + // use the first function that has this name for any language + rRetCalcName = pFuncData->GetOriginalName(); + return TRUE; + } + } + } + } + return FALSE; +} + +inline BOOL IsTypeName( const rtl::OUString& rName, const uno::Type& rType ) +{ + return rName == rType.getTypeName(); +} + +BOOL lcl_ValidReturnType( const uno::Reference<reflection::XIdlClass>& xClass ) +{ + // this must match with ScUnoAddInCall::SetResult + + if ( !xClass.is() ) return FALSE; + + switch (xClass->getTypeClass()) + { + // case uno::TypeClass_VOID: + // ??? + + case uno::TypeClass_ANY: // variable type + case uno::TypeClass_ENUM: //! ??? + case uno::TypeClass_BOOLEAN: + case uno::TypeClass_CHAR: + case uno::TypeClass_BYTE: + case uno::TypeClass_SHORT: + case uno::TypeClass_UNSIGNED_SHORT: + case uno::TypeClass_LONG: + case uno::TypeClass_UNSIGNED_LONG: + case uno::TypeClass_FLOAT: + case uno::TypeClass_DOUBLE: + case uno::TypeClass_STRING: + return TRUE; // values or string + + case uno::TypeClass_INTERFACE: + { + // return type XInterface may contain a XVolatileResult + //! XIdlClass needs getType() method! + + rtl::OUString sName = xClass->getName(); + return ( + IsTypeName( sName, getCppuType((uno::Reference<sheet::XVolatileResult>*)0) ) || + IsTypeName( sName, getCppuType((uno::Reference<uno::XInterface>*)0) ) ); + } + + default: + { + // nested sequences for arrays + //! XIdlClass needs getType() method! + + rtl::OUString sName = xClass->getName(); + return ( + IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<INT32> >*)0) ) || + IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<double> >*)0) ) || + IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<rtl::OUString> >*)0) ) || + IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<uno::Any> >*)0) ) ); + } + } + return FALSE; +} + +ScAddInArgumentType lcl_GetArgType( const uno::Reference<reflection::XIdlClass>& xClass ) +{ + if (!xClass.is()) + return SC_ADDINARG_NONE; + + uno::TypeClass eType = xClass->getTypeClass(); + + if ( eType == uno::TypeClass_LONG ) //! other integer types? + return SC_ADDINARG_INTEGER; + + if ( eType == uno::TypeClass_DOUBLE ) + return SC_ADDINARG_DOUBLE; + + if ( eType == uno::TypeClass_STRING ) + return SC_ADDINARG_STRING; + + //! XIdlClass needs getType() method! + rtl::OUString sName = xClass->getName(); + + if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<INT32> >*)0) )) + return SC_ADDINARG_INTEGER_ARRAY; + + if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<double> >*)0) )) + return SC_ADDINARG_DOUBLE_ARRAY; + + if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<rtl::OUString> >*)0) )) + return SC_ADDINARG_STRING_ARRAY; + + if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<uno::Any> >*)0) )) + return SC_ADDINARG_MIXED_ARRAY; + + if (IsTypeName( sName, getCppuType((uno::Any*)0) )) + return SC_ADDINARG_VALUE_OR_ARRAY; + + if (IsTypeName( sName, getCppuType((uno::Reference<table::XCellRange>*)0) )) + return SC_ADDINARG_CELLRANGE; + + if (IsTypeName( sName, getCppuType((uno::Reference<beans::XPropertySet>*)0) )) + return SC_ADDINARG_CALLER; + + if (IsTypeName( sName, getCppuType((uno::Sequence<uno::Any>*)0) )) + return SC_ADDINARG_VARARGS; + + return SC_ADDINARG_NONE; +} + +void ScUnoAddInCollection::ReadFromAddIn( const uno::Reference<uno::XInterface>& xInterface ) +{ + uno::Reference<sheet::XAddIn> xAddIn( xInterface, uno::UNO_QUERY ); + uno::Reference<lang::XServiceName> xName( xInterface, uno::UNO_QUERY ); + if ( xAddIn.is() && xName.is() ) + { + // AddIns must use the language for which the office is installed + LanguageType eOfficeLang = Application::GetSettings().GetUILanguage(); + + lang::Locale aLocale( MsLangId::convertLanguageToLocale( eOfficeLang )); + xAddIn->setLocale( aLocale ); + + String aServiceName = String( xName->getServiceName() ); + ScUnoAddInHelpIdGenerator aHelpIdGenerator( xName->getServiceName() ); + + //! pass XIntrospection to ReadFromAddIn + + uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory(); + if ( xManager.is() ) + { + uno::Reference<beans::XIntrospection> xIntro( + xManager->createInstance(rtl::OUString::createFromAscii( + "com.sun.star.beans.Introspection" )), + uno::UNO_QUERY ); + if ( xIntro.is() ) + { + uno::Any aObject; + aObject <<= xAddIn; + uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject); + if (xAcc.is()) + { + uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods = + xAcc->getMethods( beans::MethodConcept::ALL ); + long nNewCount = aMethods.getLength(); + if ( nNewCount ) + { + long nOld = nFuncCount; + nFuncCount = nNewCount+nOld; + if ( nOld ) + { + ScUnoAddInFuncData** ppNew = new ScUnoAddInFuncData*[nFuncCount]; + for (long i=0; i<nOld; i++) + ppNew[i] = ppFuncData[i]; + delete[] ppFuncData; + ppFuncData = ppNew; + } + else + ppFuncData = new ScUnoAddInFuncData*[nFuncCount]; + + //! TODO: adjust bucket count? + if ( !pExactHashMap ) + pExactHashMap = new ScAddInHashMap; + if ( !pNameHashMap ) + pNameHashMap = new ScAddInHashMap; + if ( !pLocalHashMap ) + pLocalHashMap = new ScAddInHashMap; + + const uno::Reference<reflection::XIdlMethod>* pArray = aMethods.getConstArray(); + for (long nFuncPos=0; nFuncPos<nNewCount; nFuncPos++) + { + ppFuncData[nFuncPos+nOld] = NULL; + + uno::Reference<reflection::XIdlMethod> xFunc = pArray[nFuncPos]; + if (xFunc.is()) + { + // leave out internal functions + uno::Reference<reflection::XIdlClass> xClass = + xFunc->getDeclaringClass(); + BOOL bSkip = TRUE; + if ( xClass.is() ) + { + //! XIdlClass needs getType() method! + rtl::OUString sName = xClass->getName(); + bSkip = ( + IsTypeName( sName, + getCppuType((uno::Reference<uno::XInterface>*)0) ) || + IsTypeName( sName, + getCppuType((uno::Reference<reflection::XIdlClassProvider>*)0) ) || + IsTypeName( sName, + getCppuType((uno::Reference<lang::XServiceName>*)0) ) || + IsTypeName( sName, + getCppuType((uno::Reference<lang::XServiceInfo>*)0) ) || + IsTypeName( sName, + getCppuType((uno::Reference<sheet::XAddIn>*)0) ) ); + } + if (!bSkip) + { + uno::Reference<reflection::XIdlClass> xReturn = + xFunc->getReturnType(); + if ( !lcl_ValidReturnType( xReturn ) ) + bSkip = TRUE; + } + if (!bSkip) + { + rtl::OUString aFuncU = xFunc->getName(); + + // stored function name: (service name).(function) + String aFuncName = aServiceName; + aFuncName += '.'; + aFuncName += String( aFuncU ); + + BOOL bValid = TRUE; + long nVisibleCount = 0; + long nCallerPos = SC_CALLERPOS_NONE; + + uno::Sequence<reflection::ParamInfo> aParams = + xFunc->getParameterInfos(); + long nParamCount = aParams.getLength(); + const reflection::ParamInfo* pParArr = aParams.getConstArray(); + long nParamPos; + for (nParamPos=0; nParamPos<nParamCount; nParamPos++) + { + if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN ) + bValid = FALSE; + uno::Reference<reflection::XIdlClass> xParClass = + pParArr[nParamPos].aType; + ScAddInArgumentType eArgType = lcl_GetArgType( xParClass ); + if ( eArgType == SC_ADDINARG_NONE ) + bValid = FALSE; + else if ( eArgType == SC_ADDINARG_CALLER ) + nCallerPos = nParamPos; + else + ++nVisibleCount; + } + if (bValid) + { + USHORT nCategory = lcl_GetCategory( + String( + xAddIn->getProgrammaticCategoryName( + aFuncU ) ) ); + + USHORT nHelpId = aHelpIdGenerator.GetHelpId( aFuncU ); + + rtl::OUString aLocalU; + try + { + aLocalU = xAddIn-> + getDisplayFunctionName( aFuncU ); + } + catch(uno::Exception&) + { + aLocalU = rtl::OUString::createFromAscii( "###" ); + } + String aLocalName = String( aLocalU ); + + rtl::OUString aDescU; + try + { + aDescU = xAddIn-> + getFunctionDescription( aFuncU ); + } + catch(uno::Exception&) + { + aDescU = rtl::OUString::createFromAscii( "###" ); + } + String aDescription = String( aDescU ); + + ScAddInArgDesc* pVisibleArgs = NULL; + if ( nVisibleCount > 0 ) + { + ScAddInArgDesc aDesc; + pVisibleArgs = new ScAddInArgDesc[nVisibleCount]; + long nDestPos = 0; + for (nParamPos=0; nParamPos<nParamCount; nParamPos++) + { + uno::Reference<reflection::XIdlClass> xParClass = + pParArr[nParamPos].aType; + ScAddInArgumentType eArgType = lcl_GetArgType( xParClass ); + if ( eArgType != SC_ADDINARG_CALLER ) + { + rtl::OUString aArgName; + try + { + aArgName = xAddIn-> + getDisplayArgumentName( aFuncU, nParamPos ); + } + catch(uno::Exception&) + { + aArgName = rtl::OUString::createFromAscii( "###" ); + } + rtl::OUString aArgDesc; + try + { + aArgDesc = xAddIn-> + getArgumentDescription( aFuncU, nParamPos ); + } + catch(uno::Exception&) + { + aArgName = rtl::OUString::createFromAscii( "###" ); + } + + BOOL bOptional = + ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY || + eArgType == SC_ADDINARG_VARARGS ); + + aDesc.eType = eArgType; + aDesc.aName = String( aArgName ); + aDesc.aDescription = String( aArgDesc ); + aDesc.bOptional = bOptional; + //! initialize aInternalName only from config? + aDesc.aInternalName = pParArr[nParamPos].aName; + + pVisibleArgs[nDestPos++] = aDesc; + } + } + DBG_ASSERT( nDestPos==nVisibleCount, "wrong count" ); + } + + ppFuncData[nFuncPos+nOld] = new ScUnoAddInFuncData( + aFuncName, aLocalName, aDescription, + nCategory, nHelpId, + xFunc, aObject, + nVisibleCount, pVisibleArgs, nCallerPos ); + + const ScUnoAddInFuncData* pData = + ppFuncData[nFuncPos+nOld]; + pExactHashMap->insert( + ScAddInHashMap::value_type( + pData->GetOriginalName(), + pData ) ); + pNameHashMap->insert( + ScAddInHashMap::value_type( + pData->GetUpperName(), + pData ) ); + pLocalHashMap->insert( + ScAddInHashMap::value_type( + pData->GetUpperLocal(), + pData ) ); + + delete[] pVisibleArgs; + } + } + } + } + } + } + } + } + } +} + +void lcl_UpdateFunctionList( ScFunctionList& rFunctionList, const ScUnoAddInFuncData& rFuncData ) +{ + String aCompare = rFuncData.GetUpperLocal(); // as used in FillFunctionDescFromData + + ULONG nCount = rFunctionList.GetCount(); + for (ULONG nPos=0; nPos<nCount; nPos++) + { + const ScFuncDesc* pDesc = rFunctionList.GetFunction( nPos ); + if ( pDesc && pDesc->pFuncName && *pDesc->pFuncName == aCompare ) + { + ScUnoAddInCollection::FillFunctionDescFromData( rFuncData, *const_cast<ScFuncDesc*>(pDesc) ); + break; + } + } +} + +const ScAddInArgDesc* lcl_FindArgDesc( const ScUnoAddInFuncData& rFuncData, const String& rArgIntName ) +{ + long nArgCount = rFuncData.GetArgumentCount(); + const ScAddInArgDesc* pArguments = rFuncData.GetArguments(); + for (long nPos=0; nPos<nArgCount; nPos++) + { + if ( pArguments[nPos].aInternalName == rArgIntName ) + return &pArguments[nPos]; + } + return NULL; +} + +void ScUnoAddInCollection::UpdateFromAddIn( const uno::Reference<uno::XInterface>& xInterface, + const String& rServiceName ) +{ + uno::Reference<lang::XLocalizable> xLoc( xInterface, uno::UNO_QUERY ); + if ( xLoc.is() ) // optional in new add-ins + { + LanguageType eOfficeLang = Application::GetSettings().GetUILanguage(); + lang::Locale aLocale( MsLangId::convertLanguageToLocale( eOfficeLang )); + xLoc->setLocale( aLocale ); + } + + // if function list was already initialized, it must be updated + + ScFunctionList* pFunctionList = NULL; + if ( ScGlobal::HasStarCalcFunctionList() ) + pFunctionList = ScGlobal::GetStarCalcFunctionList(); + + // only get the function information from Introspection + + uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory(); + if ( xManager.is() ) + { + uno::Reference<beans::XIntrospection> xIntro( + xManager->createInstance(rtl::OUString::createFromAscii( + "com.sun.star.beans.Introspection" )), + uno::UNO_QUERY ); + if ( xIntro.is() ) + { + uno::Any aObject; + aObject <<= xInterface; + uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject); + if (xAcc.is()) + { + uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods = + xAcc->getMethods( beans::MethodConcept::ALL ); + long nMethodCount = aMethods.getLength(); + const uno::Reference<reflection::XIdlMethod>* pArray = aMethods.getConstArray(); + for (long nFuncPos=0; nFuncPos<nMethodCount; nFuncPos++) + { + uno::Reference<reflection::XIdlMethod> xFunc = pArray[nFuncPos]; + if (xFunc.is()) + { + rtl::OUString aFuncU = xFunc->getName(); + + // stored function name: (service name).(function) + String aFuncName = rServiceName; + aFuncName += '.'; + aFuncName += String( aFuncU ); + + // internal names are skipped because no FuncData exists + ScUnoAddInFuncData* pOldData = const_cast<ScUnoAddInFuncData*>( GetFuncData( aFuncName ) ); + if ( pOldData ) + { + // Create new (complete) argument info. + // As in ReadFromAddIn, the reflection information is authoritative. + // Local names and descriptions from pOldData are looked up using the + // internal argument name. + + BOOL bValid = TRUE; + long nVisibleCount = 0; + long nCallerPos = SC_CALLERPOS_NONE; + + uno::Sequence<reflection::ParamInfo> aParams = + xFunc->getParameterInfos(); + long nParamCount = aParams.getLength(); + const reflection::ParamInfo* pParArr = aParams.getConstArray(); + long nParamPos; + for (nParamPos=0; nParamPos<nParamCount; nParamPos++) + { + if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN ) + bValid = FALSE; + uno::Reference<reflection::XIdlClass> xParClass = + pParArr[nParamPos].aType; + ScAddInArgumentType eArgType = lcl_GetArgType( xParClass ); + if ( eArgType == SC_ADDINARG_NONE ) + bValid = FALSE; + else if ( eArgType == SC_ADDINARG_CALLER ) + nCallerPos = nParamPos; + else + ++nVisibleCount; + } + if (bValid) + { + ScAddInArgDesc* pVisibleArgs = NULL; + if ( nVisibleCount > 0 ) + { + ScAddInArgDesc aDesc; + pVisibleArgs = new ScAddInArgDesc[nVisibleCount]; + long nDestPos = 0; + for (nParamPos=0; nParamPos<nParamCount; nParamPos++) + { + uno::Reference<reflection::XIdlClass> xParClass = + pParArr[nParamPos].aType; + ScAddInArgumentType eArgType = lcl_GetArgType( xParClass ); + if ( eArgType != SC_ADDINARG_CALLER ) + { + const ScAddInArgDesc* pOldArgDesc = + lcl_FindArgDesc( *pOldData, pParArr[nParamPos].aName ); + if ( pOldArgDesc ) + { + aDesc.aName = pOldArgDesc->aName; + aDesc.aDescription = pOldArgDesc->aDescription; + } + else + aDesc.aName = aDesc.aDescription = String::CreateFromAscii( "###" ); + + BOOL bOptional = + ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY || + eArgType == SC_ADDINARG_VARARGS ); + + aDesc.eType = eArgType; + aDesc.bOptional = bOptional; + //! initialize aInternalName only from config? + aDesc.aInternalName = pParArr[nParamPos].aName; + + pVisibleArgs[nDestPos++] = aDesc; + } + } + DBG_ASSERT( nDestPos==nVisibleCount, "wrong count" ); + } + + pOldData->SetFunction( xFunc, aObject ); + pOldData->SetArguments( nVisibleCount, pVisibleArgs ); + pOldData->SetCallerPos( nCallerPos ); + + if ( pFunctionList ) + lcl_UpdateFunctionList( *pFunctionList, *pOldData ); + + delete[] pVisibleArgs; + } + } + } + } + } + } + } +} + +String ScUnoAddInCollection::FindFunction( const String& rUpperName, BOOL bLocalFirst ) +{ + if (!bInitialized) + Initialize(); + + if (nFuncCount == 0) + return EMPTY_STRING; + + if ( bLocalFirst ) + { + // first scan all local names (used for entering formulas) + + ScAddInHashMap::const_iterator iLook( pLocalHashMap->find( rUpperName ) ); + if ( iLook != pLocalHashMap->end() ) + return iLook->second->GetOriginalName(); + +#if 0 + // after that, scan international names (really?) + + iLook = pNameHashMap->find( rUpperName ); + if ( iLook != pNameHashMap->end() ) + return iLook->second->GetOriginalName(); +#endif + } + else + { + // first scan international names (used when calling a function) + //! before that, check for exact match??? + + ScAddInHashMap::const_iterator iLook( pNameHashMap->find( rUpperName ) ); + if ( iLook != pNameHashMap->end() ) + return iLook->second->GetOriginalName(); + + // after that, scan all local names (to allow replacing old AddIns with Uno) + + iLook = pLocalHashMap->find( rUpperName ); + if ( iLook != pLocalHashMap->end() ) + return iLook->second->GetOriginalName(); + } + + return EMPTY_STRING; +} + +const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( const String& rName, bool bComplete ) +{ + if (!bInitialized) + Initialize(); + + // rName must be the exact internal name + + ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) ); + if ( iLook != pExactHashMap->end() ) + { + const ScUnoAddInFuncData* pFuncData = iLook->second; + + if ( bComplete && !pFuncData->GetFunction().is() ) //! extra flag? + LoadComponent( *pFuncData ); + + return pFuncData; + } + + return NULL; +} + +const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( long nIndex ) +{ + if (!bInitialized) + Initialize(); + + if (nIndex < nFuncCount) + return ppFuncData[nIndex]; + return NULL; +} + +void ScUnoAddInCollection::LocalizeString( String& rName ) +{ + if (!bInitialized) + Initialize(); + + // modify rName - input: exact name + + ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) ); + if ( iLook != pExactHashMap->end() ) + rName = iLook->second->GetUpperLocal(); //! upper? +} + + +long ScUnoAddInCollection::GetFuncCount() +{ + if (!bInitialized) + Initialize(); + + return nFuncCount; +} + +BOOL ScUnoAddInCollection::FillFunctionDesc( long nFunc, ScFuncDesc& rDesc ) +{ + if (!bInitialized) + Initialize(); + + if (nFunc >= nFuncCount || !ppFuncData[nFunc]) + return FALSE; + + const ScUnoAddInFuncData& rFuncData = *ppFuncData[nFunc]; + + return FillFunctionDescFromData( rFuncData, rDesc ); +} + +// static +BOOL ScUnoAddInCollection::FillFunctionDescFromData( const ScUnoAddInFuncData& rFuncData, ScFuncDesc& rDesc ) +{ + rDesc.Clear(); + + BOOL bIncomplete = !rFuncData.GetFunction().is(); //! extra flag? + + long nArgCount = rFuncData.GetArgumentCount(); + if ( nArgCount > USHRT_MAX ) + return FALSE; + + if ( bIncomplete ) + nArgCount = 0; // if incomplete, fill without argument info (no wrong order) + + // nFIndex is set from outside + + rDesc.pFuncName = new String( rFuncData.GetUpperLocal() ); //! upper? + rDesc.nCategory = rFuncData.GetCategory(); + rDesc.nHelpId = rFuncData.GetHelpId(); + + String aDesc = rFuncData.GetDescription(); + if (!aDesc.Len()) + aDesc = rFuncData.GetLocalName(); // use name if no description is available + rDesc.pFuncDesc = new String( aDesc ); + + // AddInArgumentType_CALLER is already left out in FuncData + + rDesc.nArgCount = (USHORT)nArgCount; + if ( nArgCount ) + { + BOOL bMultiple = FALSE; + const ScAddInArgDesc* pArgs = rFuncData.GetArguments(); + + rDesc.ppDefArgNames = new String*[nArgCount]; + rDesc.ppDefArgDescs = new String*[nArgCount]; + rDesc.pDefArgFlags = new ScFuncDesc::ParameterFlags[nArgCount]; + for ( long nArg=0; nArg<nArgCount; nArg++ ) + { + rDesc.ppDefArgNames[nArg] = new String( pArgs[nArg].aName ); + rDesc.ppDefArgDescs[nArg] = new String( pArgs[nArg].aDescription ); + rDesc.pDefArgFlags[nArg].bOptional = pArgs[nArg].bOptional; + rDesc.pDefArgFlags[nArg].bSuppress = false; + + // no empty names... + if ( rDesc.ppDefArgNames[nArg]->Len() == 0 ) + { + String aDefName( RTL_CONSTASCII_USTRINGPARAM("arg") ); + aDefName += String::CreateFromInt32( nArg+1 ); + *rDesc.ppDefArgNames[nArg] = aDefName; + } + + // last argument repeated? + if ( nArg+1 == nArgCount && ( pArgs[nArg].eType == SC_ADDINARG_VARARGS ) ) + bMultiple = TRUE; + } + + if ( bMultiple ) + rDesc.nArgCount += VAR_ARGS - 1; // VAR_ARGS means just one repeated arg + } + + rDesc.bIncomplete = bIncomplete; + + return TRUE; +} + + +//------------------------------------------------------------------------ + +ScUnoAddInCall::ScUnoAddInCall( ScUnoAddInCollection& rColl, const String& rName, + long nParamCount ) : + bValidCount( FALSE ), + nErrCode( errNoCode ), // before function was called + bHasString( TRUE ), + fValue( 0.0 ), + xMatrix( NULL ) +{ + pFuncData = rColl.GetFuncData( rName, true ); // need fully initialized data + DBG_ASSERT( pFuncData, "Function Data missing" ); + if ( pFuncData ) + { + long nDescCount = pFuncData->GetArgumentCount(); + const ScAddInArgDesc* pArgs = pFuncData->GetArguments(); + + // is aVarArg sequence needed? + if ( nParamCount >= nDescCount && nDescCount > 0 && + pArgs[nDescCount-1].eType == SC_ADDINARG_VARARGS ) + { + long nVarCount = nParamCount - ( nDescCount - 1 ); // size of last argument + aVarArg.realloc( nVarCount ); + bValidCount = TRUE; + } + else if ( nParamCount <= nDescCount ) + { + // all args behind nParamCount must be optional + bValidCount = TRUE; + for (long i=nParamCount; i<nDescCount; i++) + if ( !pArgs[i].bOptional ) + bValidCount = FALSE; + } + // else invalid (too many arguments) + + if ( bValidCount ) + aArgs.realloc( nDescCount ); // sequence must always match function signature + } +} + +ScUnoAddInCall::~ScUnoAddInCall() +{ + // pFuncData is deleted with ScUnoAddInCollection +} + +BOOL ScUnoAddInCall::ValidParamCount() +{ + return bValidCount; +} + +ScAddInArgumentType ScUnoAddInCall::GetArgType( long nPos ) +{ + if ( pFuncData ) + { + long nCount = pFuncData->GetArgumentCount(); + const ScAddInArgDesc* pArgs = pFuncData->GetArguments(); + + // if last arg is sequence, use "any" type + if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS ) + return SC_ADDINARG_VALUE_OR_ARRAY; + + if ( nPos < nCount ) + return pArgs[nPos].eType; + } + return SC_ADDINARG_VALUE_OR_ARRAY; //! error code !!!! +} + +BOOL ScUnoAddInCall::NeedsCaller() const +{ + return pFuncData && pFuncData->GetCallerPos() != SC_CALLERPOS_NONE; +} + +void ScUnoAddInCall::SetCaller( const uno::Reference<uno::XInterface>& rInterface ) +{ + xCaller = rInterface; +} + +void ScUnoAddInCall::SetCallerFromObjectShell( SfxObjectShell* pObjSh ) +{ + if (pObjSh) + { + uno::Reference<uno::XInterface> xInt( pObjSh->GetBaseModel(), uno::UNO_QUERY ); + SetCaller( xInt ); + } +} + +void ScUnoAddInCall::SetParam( long nPos, const uno::Any& rValue ) +{ + if ( pFuncData ) + { + long nCount = pFuncData->GetArgumentCount(); + const ScAddInArgDesc* pArgs = pFuncData->GetArguments(); + if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS ) + { + long nVarPos = nPos-(nCount-1); + if ( nVarPos < aVarArg.getLength() ) + aVarArg.getArray()[nVarPos] = rValue; + else + { + DBG_ERROR("wrong argument number"); + } + } + else if ( nPos < aArgs.getLength() ) + aArgs.getArray()[nPos] = rValue; + else + { + DBG_ERROR("wrong argument number"); + } + } +} + +void ScUnoAddInCall::ExecuteCall() +{ + if ( !pFuncData ) + return; + + long nCount = pFuncData->GetArgumentCount(); + const ScAddInArgDesc* pArgs = pFuncData->GetArguments(); + if ( nCount > 0 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS ) + { + // insert aVarArg as last argument + //! after inserting caller (to prevent copying twice)? + + DBG_ASSERT( aArgs.getLength() == nCount, "wrong argument count" ); + aArgs.getArray()[nCount-1] <<= aVarArg; + } + + if ( pFuncData->GetCallerPos() != SC_CALLERPOS_NONE ) + { + uno::Any aCallerAny; + aCallerAny <<= xCaller; + + long nUserLen = aArgs.getLength(); + long nCallPos = pFuncData->GetCallerPos(); + if (nCallPos>nUserLen) // should not happen + { + DBG_ERROR("wrong CallPos"); + nCallPos = nUserLen; + } + + long nDestLen = nUserLen + 1; + uno::Sequence<uno::Any> aRealArgs( nDestLen ); + uno::Any* pDest = aRealArgs.getArray(); + + const uno::Any* pSource = aArgs.getConstArray(); + long nSrcPos = 0; + + for ( long nDestPos = 0; nDestPos < nDestLen; nDestPos++ ) + { + if ( nDestPos == nCallPos ) + pDest[nDestPos] = aCallerAny; + else + pDest[nDestPos] = pSource[nSrcPos++]; + } + + ExecuteCallWithArgs( aRealArgs ); + } + else + ExecuteCallWithArgs( aArgs ); +} + +void ScUnoAddInCall::ExecuteCallWithArgs(uno::Sequence<uno::Any>& rCallArgs) +{ + // rCallArgs may not match argument descriptions (because of caller) + + uno::Reference<reflection::XIdlMethod> xFunction; + uno::Any aObject; + if ( pFuncData ) + { + xFunction = pFuncData->GetFunction(); + aObject = pFuncData->GetObject(); + } + + if ( xFunction.is() ) + { + uno::Any aAny; + nErrCode = 0; + + try + { + aAny = xFunction->invoke( aObject, rCallArgs ); + } + catch(lang::IllegalArgumentException&) + { + nErrCode = errIllegalArgument; + } +#if 0 + catch(FloatingPointException&) + { + nErrCode = errIllegalFPOperation; + } +#endif + catch(reflection::InvocationTargetException& rWrapped) + { + if ( rWrapped.TargetException.getValueType().equals( + getCppuType( (lang::IllegalArgumentException*)0 ) ) ) + nErrCode = errIllegalArgument; + else if ( rWrapped.TargetException.getValueType().equals( + getCppuType( (sheet::NoConvergenceException*)0 ) ) ) + nErrCode = errNoConvergence; + else + nErrCode = errNoValue; + } + + catch(uno::Exception&) + { + nErrCode = errNoValue; + } + + if (!nErrCode) + SetResult( aAny ); // convert result to Calc types + } +} + +void ScUnoAddInCall::SetResult( const uno::Any& rNewRes ) +{ + nErrCode = 0; + xVarRes = NULL; + + // Reflection* pRefl = rNewRes.getReflection(); + + uno::TypeClass eClass = rNewRes.getValueTypeClass(); + uno::Type aType = rNewRes.getValueType(); + switch (eClass) + { + case uno::TypeClass_VOID: + nErrCode = NOTAVAILABLE; // #NA + break; + + case uno::TypeClass_ENUM: + case uno::TypeClass_BOOLEAN: + case uno::TypeClass_CHAR: + case uno::TypeClass_BYTE: + case uno::TypeClass_SHORT: + case uno::TypeClass_UNSIGNED_SHORT: + case uno::TypeClass_LONG: + case uno::TypeClass_UNSIGNED_LONG: + case uno::TypeClass_FLOAT: + case uno::TypeClass_DOUBLE: + { + uno::TypeClass eMyClass; + ScApiTypeConversion::ConvertAnyToDouble( fValue, eMyClass, rNewRes); + bHasString = FALSE; + } + break; + + case uno::TypeClass_STRING: + { + rtl::OUString aUStr; + rNewRes >>= aUStr; + aString = String( aUStr ); + bHasString = TRUE; + } + break; + + case uno::TypeClass_INTERFACE: + { + //! directly extract XVolatileResult from any? + uno::Reference<uno::XInterface> xInterface; + rNewRes >>= xInterface; + if ( xInterface.is() ) + xVarRes = uno::Reference<sheet::XVolatileResult>( xInterface, uno::UNO_QUERY ); + + if (!xVarRes.is()) + nErrCode = errNoValue; // unknown interface + } + break; + + default: + if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<INT32> > *)0 ) ) ) + { + const uno::Sequence< uno::Sequence<INT32> >* pRowSeq = NULL; + + //! use pointer from any! + uno::Sequence< uno::Sequence<INT32> > aSequence; + if ( rNewRes >>= aSequence ) + pRowSeq = &aSequence; + + if ( pRowSeq ) + { + long nRowCount = pRowSeq->getLength(); + const uno::Sequence<INT32>* pRowArr = pRowSeq->getConstArray(); + long nMaxColCount = 0; + long nCol, nRow; + for (nRow=0; nRow<nRowCount; nRow++) + { + long nTmp = pRowArr[nRow].getLength(); + if ( nTmp > nMaxColCount ) + nMaxColCount = nTmp; + } + if ( nMaxColCount && nRowCount ) + { + xMatrix = new ScMatrix( + static_cast<SCSIZE>(nMaxColCount), + static_cast<SCSIZE>(nRowCount) ); + ScMatrix* pMatrix = xMatrix; + for (nRow=0; nRow<nRowCount; nRow++) + { + long nColCount = pRowArr[nRow].getLength(); + const INT32* pColArr = pRowArr[nRow].getConstArray(); + for (nCol=0; nCol<nColCount; nCol++) + pMatrix->PutDouble( pColArr[nCol], + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + for (nCol=nColCount; nCol<nMaxColCount; nCol++) + pMatrix->PutDouble( 0.0, + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + } + } + } + } + else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<double> > *)0 ) ) ) + { + const uno::Sequence< uno::Sequence<double> >* pRowSeq = NULL; + + //! use pointer from any! + uno::Sequence< uno::Sequence<double> > aSequence; + if ( rNewRes >>= aSequence ) + pRowSeq = &aSequence; + + if ( pRowSeq ) + { + long nRowCount = pRowSeq->getLength(); + const uno::Sequence<double>* pRowArr = pRowSeq->getConstArray(); + long nMaxColCount = 0; + long nCol, nRow; + for (nRow=0; nRow<nRowCount; nRow++) + { + long nTmp = pRowArr[nRow].getLength(); + if ( nTmp > nMaxColCount ) + nMaxColCount = nTmp; + } + if ( nMaxColCount && nRowCount ) + { + xMatrix = new ScMatrix( + static_cast<SCSIZE>(nMaxColCount), + static_cast<SCSIZE>(nRowCount) ); + ScMatrix* pMatrix = xMatrix; + for (nRow=0; nRow<nRowCount; nRow++) + { + long nColCount = pRowArr[nRow].getLength(); + const double* pColArr = pRowArr[nRow].getConstArray(); + for (nCol=0; nCol<nColCount; nCol++) + pMatrix->PutDouble( pColArr[nCol], + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + for (nCol=nColCount; nCol<nMaxColCount; nCol++) + pMatrix->PutDouble( 0.0, + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + } + } + } + } + else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<rtl::OUString> > *)0 ) ) ) + { + const uno::Sequence< uno::Sequence<rtl::OUString> >* pRowSeq = NULL; + + //! use pointer from any! + uno::Sequence< uno::Sequence<rtl::OUString> > aSequence; + if ( rNewRes >>= aSequence ) + pRowSeq = &aSequence; + + if ( pRowSeq ) + { + long nRowCount = pRowSeq->getLength(); + const uno::Sequence<rtl::OUString>* pRowArr = pRowSeq->getConstArray(); + long nMaxColCount = 0; + long nCol, nRow; + for (nRow=0; nRow<nRowCount; nRow++) + { + long nTmp = pRowArr[nRow].getLength(); + if ( nTmp > nMaxColCount ) + nMaxColCount = nTmp; + } + if ( nMaxColCount && nRowCount ) + { + xMatrix = new ScMatrix( + static_cast<SCSIZE>(nMaxColCount), + static_cast<SCSIZE>(nRowCount) ); + ScMatrix* pMatrix = xMatrix; + for (nRow=0; nRow<nRowCount; nRow++) + { + long nColCount = pRowArr[nRow].getLength(); + const rtl::OUString* pColArr = pRowArr[nRow].getConstArray(); + for (nCol=0; nCol<nColCount; nCol++) + pMatrix->PutString( String( pColArr[nCol] ), + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + for (nCol=nColCount; nCol<nMaxColCount; nCol++) + pMatrix->PutString( EMPTY_STRING, + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + } + } + } + } + else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<uno::Any> > *)0 ) ) ) + { + xMatrix = ScSequenceToMatrix::CreateMixedMatrix( rNewRes ); + } + + if (!xMatrix) // no array found + nErrCode = errNoValue; //! code for error in return type??? + } +} + + + +//------------------------------------------------------------------------ + + + diff --git a/sc/source/core/tool/addinhelpid.cxx b/sc/source/core/tool/addinhelpid.cxx new file mode 100644 index 000000000000..a4de52e27b1b --- /dev/null +++ b/sc/source/core/tool/addinhelpid.cxx @@ -0,0 +1,217 @@ +/************************************************************************* + * + * 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 "addinhelpid.hxx" +#include "sc.hrc" + +// ============================================================================ + +// A struct containing the built-in function name and the built-in help ID. +struct ScUnoAddInHelpId +{ + const sal_Char* pFuncName; + sal_uInt16 nHelpId; +}; + + +// ---------------------------------------------------------------------------- + +// Help IDs for Analysis AddIn. MUST BE SORTED for binary search. +const ScUnoAddInHelpId pAnalysisHelpIds[] = +{ + { "getAccrint" , HID_AAI_FUNC_ACCRINT }, + { "getAccrintm" , HID_AAI_FUNC_ACCRINTM }, + { "getAmordegrc" , HID_AAI_FUNC_AMORDEGRC }, + { "getAmorlinc" , HID_AAI_FUNC_AMORLINC }, + { "getBesseli" , HID_AAI_FUNC_BESSELI }, + { "getBesselj" , HID_AAI_FUNC_BESSELJ }, + { "getBesselk" , HID_AAI_FUNC_BESSELK }, + { "getBessely" , HID_AAI_FUNC_BESSELY }, + { "getBin2Dec" , HID_AAI_FUNC_BIN2DEC }, + { "getBin2Hex" , HID_AAI_FUNC_BIN2HEX }, + { "getBin2Oct" , HID_AAI_FUNC_BIN2OCT }, + { "getComplex" , HID_AAI_FUNC_COMPLEX }, + { "getConvert" , HID_AAI_FUNC_CONVERT }, + { "getCoupdaybs" , HID_AAI_FUNC_COUPDAYBS }, + { "getCoupdays" , HID_AAI_FUNC_COUPDAYS }, + { "getCoupdaysnc" , HID_AAI_FUNC_COUPDAYSNC }, + { "getCoupncd" , HID_AAI_FUNC_COUPNCD }, + { "getCoupnum" , HID_AAI_FUNC_COUPNUM }, + { "getCouppcd" , HID_AAI_FUNC_COUPPCD }, + { "getCumipmt" , HID_AAI_FUNC_CUMIPMT }, + { "getCumprinc" , HID_AAI_FUNC_CUMPRINC }, + { "getDec2Bin" , HID_AAI_FUNC_DEC2BIN }, + { "getDec2Hex" , HID_AAI_FUNC_DEC2HEX }, + { "getDec2Oct" , HID_AAI_FUNC_DEC2OCT }, + { "getDelta" , HID_AAI_FUNC_DELTA }, + { "getDisc" , HID_AAI_FUNC_DISC }, + { "getDollarde" , HID_AAI_FUNC_DOLLARDE }, + { "getDollarfr" , HID_AAI_FUNC_DOLLARFR }, + { "getDuration" , HID_AAI_FUNC_DURATION }, + { "getEdate" , HID_AAI_FUNC_EDATE }, + { "getEffect" , HID_AAI_FUNC_EFFECT }, + { "getEomonth" , HID_AAI_FUNC_EOMONTH }, + { "getErf" , HID_AAI_FUNC_ERF }, + { "getErfc" , HID_AAI_FUNC_ERFC }, + { "getFactdouble" , HID_AAI_FUNC_FACTDOUBLE }, + { "getFvschedule" , HID_AAI_FUNC_FVSCHEDULE }, + { "getGcd" , HID_AAI_FUNC_GCD }, + { "getGestep" , HID_AAI_FUNC_GESTEP }, + { "getHex2Bin" , HID_AAI_FUNC_HEX2BIN }, + { "getHex2Dec" , HID_AAI_FUNC_HEX2DEC }, + { "getHex2Oct" , HID_AAI_FUNC_HEX2OCT }, + { "getImabs" , HID_AAI_FUNC_IMABS }, + { "getImaginary" , HID_AAI_FUNC_IMAGINARY }, + { "getImargument" , HID_AAI_FUNC_IMARGUMENT }, + { "getImconjugate" , HID_AAI_FUNC_IMCONJUGATE }, + { "getImcos" , HID_AAI_FUNC_IMCOS }, + { "getImdiv" , HID_AAI_FUNC_IMDIV }, + { "getImexp" , HID_AAI_FUNC_IMEXP }, + { "getImln" , HID_AAI_FUNC_IMLN }, + { "getImlog10" , HID_AAI_FUNC_IMLOG10 }, + { "getImlog2" , HID_AAI_FUNC_IMLOG2 }, + { "getImpower" , HID_AAI_FUNC_IMPOWER }, + { "getImproduct" , HID_AAI_FUNC_IMPRODUCT }, + { "getImreal" , HID_AAI_FUNC_IMREAL }, + { "getImsin" , HID_AAI_FUNC_IMSIN }, + { "getImsqrt" , HID_AAI_FUNC_IMSQRT }, + { "getImsub" , HID_AAI_FUNC_IMSUB }, + { "getImsum" , HID_AAI_FUNC_IMSUM }, + { "getIntrate" , HID_AAI_FUNC_INTRATE }, + { "getIseven" , HID_AAI_FUNC_ISEVEN }, + { "getIsodd" , HID_AAI_FUNC_ISODD }, + { "getLcm" , HID_AAI_FUNC_LCM }, + { "getMduration" , HID_AAI_FUNC_MDURATION }, + { "getMround" , HID_AAI_FUNC_MROUND }, + { "getMultinomial" , HID_AAI_FUNC_MULTINOMIAL }, + { "getNetworkdays" , HID_AAI_FUNC_NETWORKDAYS }, + { "getNominal" , HID_AAI_FUNC_NOMINAL }, + { "getOct2Bin" , HID_AAI_FUNC_OCT2BIN }, + { "getOct2Dec" , HID_AAI_FUNC_OCT2DEZ }, + { "getOct2Hex" , HID_AAI_FUNC_OCT2HEX }, + { "getOddfprice" , HID_AAI_FUNC_ODDFPRICE }, + { "getOddfyield" , HID_AAI_FUNC_ODDFYIELD }, + { "getOddlprice" , HID_AAI_FUNC_ODDLPRICE }, + { "getOddlyield" , HID_AAI_FUNC_ODDLYIELD }, + { "getPrice" , HID_AAI_FUNC_PRICE }, + { "getPricedisc" , HID_AAI_FUNC_PRICEDISC }, + { "getPricemat" , HID_AAI_FUNC_PRICEMAT }, + { "getQuotient" , HID_AAI_FUNC_QUOTIENT }, + { "getRandbetween" , HID_AAI_FUNC_RANDBETWEEN }, + { "getReceived" , HID_AAI_FUNC_RECEIVED }, + { "getSeriessum" , HID_AAI_FUNC_SERIESSUM }, + { "getSqrtpi" , HID_AAI_FUNC_SQRTPI }, + { "getTbilleq" , HID_AAI_FUNC_TBILLEQ }, + { "getTbillprice" , HID_AAI_FUNC_TBILLPRICE }, + { "getTbillyield" , HID_AAI_FUNC_TBILLYIELD }, + { "getWeeknum" , HID_AAI_FUNC_WEEKNUM }, + { "getWorkday" , HID_AAI_FUNC_WORKDAY }, + { "getXirr" , HID_AAI_FUNC_XIRR }, + { "getXnpv" , HID_AAI_FUNC_XNPV }, + { "getYearfrac" , HID_AAI_FUNC_YEARFRAC }, + { "getYield" , HID_AAI_FUNC_YIELD }, + { "getYielddisc" , HID_AAI_FUNC_YIELDDISC }, + { "getYieldmat" , HID_AAI_FUNC_YIELDMAT } +}; + + +// ---------------------------------------------------------------------------- + +// Help IDs for DateFunc AddIn. MUST BE SORTED for binary search. +const ScUnoAddInHelpId pDateFuncHelpIds[] = +{ + { "getDaysInMonth" , HID_DAI_FUNC_DAYSINMONTH }, + { "getDaysInYear" , HID_DAI_FUNC_DAYSINYEAR }, + { "getDiffMonths" , HID_DAI_FUNC_DIFFMONTHS }, + { "getDiffWeeks" , HID_DAI_FUNC_DIFFWEEKS }, + { "getDiffYears" , HID_DAI_FUNC_DIFFYEARS }, + { "getRot13" , HID_DAI_FUNC_ROT13 }, + { "getWeeksInYear" , HID_DAI_FUNC_WEEKSINYEAR } +}; + + +// ============================================================================ + +//UNUSED2008-05 ScUnoAddInHelpIdGenerator::ScUnoAddInHelpIdGenerator() : +//UNUSED2008-05 pCurrHelpIds( NULL ), +//UNUSED2008-05 nArrayCount( 0 ) +//UNUSED2008-05 { +//UNUSED2008-05 } + +ScUnoAddInHelpIdGenerator::ScUnoAddInHelpIdGenerator( const ::rtl::OUString& rServiceName ) +{ + SetServiceName( rServiceName ); +} + +void ScUnoAddInHelpIdGenerator::SetServiceName( const ::rtl::OUString& rServiceName ) +{ + pCurrHelpIds = NULL; + sal_uInt32 nSize = 0; + + if( rServiceName.equalsAscii( "com.sun.star.sheet.addin.Analysis" ) ) + { + pCurrHelpIds = pAnalysisHelpIds; + nSize = sizeof( pAnalysisHelpIds ); + } + else if( rServiceName.equalsAscii( "com.sun.star.sheet.addin.DateFunctions" ) ) + { + pCurrHelpIds = pDateFuncHelpIds; + nSize = sizeof( pDateFuncHelpIds ); + } + + nArrayCount = nSize / sizeof( ScUnoAddInHelpId ); +} + +sal_uInt16 ScUnoAddInHelpIdGenerator::GetHelpId( const ::rtl::OUString& rFuncName ) const +{ + if( !pCurrHelpIds || !nArrayCount ) + return 0; + + const ScUnoAddInHelpId* pFirst = pCurrHelpIds; + const ScUnoAddInHelpId* pLast = pCurrHelpIds + nArrayCount - 1; + + while( pFirst <= pLast ) + { + const ScUnoAddInHelpId* pMiddle = pFirst + (pLast - pFirst) / 2; + sal_Int32 nResult = rFuncName.compareToAscii( pMiddle->pFuncName ); + if( !nResult ) + return pMiddle->nHelpId; + else if( nResult < 0 ) + pLast = pMiddle - 1; + else + pFirst = pMiddle + 1; + } + + return 0; +} + + +// ============================================================================ + diff --git a/sc/source/core/tool/addinlis.cxx b/sc/source/core/tool/addinlis.cxx new file mode 100644 index 000000000000..ad6b60073ccb --- /dev/null +++ b/sc/source/core/tool/addinlis.cxx @@ -0,0 +1,190 @@ +/************************************************************************* + * + * 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 <tools/debug.hxx> +#include <sfx2/objsh.hxx> + + +#include "addinlis.hxx" +#include "miscuno.hxx" // SC_IMPL_SERVICE_INFO +#include "document.hxx" +#include "brdcst.hxx" +#include "unoguard.hxx" +#include "sc.hrc" + +using namespace com::sun::star; + +//------------------------------------------------------------------------ + +//SMART_UNO_IMPLEMENTATION( ScAddInListener, UsrObject ); + +SC_SIMPLE_SERVICE_INFO( ScAddInListener, "ScAddInListener", "stardiv.one.sheet.AddInListener" ) + +//------------------------------------------------------------------------ + +List ScAddInListener::aAllListeners; + +//------------------------------------------------------------------------ + +// static +ScAddInListener* ScAddInListener::CreateListener( + uno::Reference<sheet::XVolatileResult> xVR, ScDocument* pDoc ) +{ + ScAddInListener* pNew = new ScAddInListener( xVR, pDoc ); + + pNew->acquire(); // for aAllListeners + aAllListeners.Insert( pNew, LIST_APPEND ); + + if ( xVR.is() ) + xVR->addResultListener( pNew ); // after at least 1 ref exists! + + return pNew; +} + +ScAddInListener::ScAddInListener( uno::Reference<sheet::XVolatileResult> xVR, ScDocument* pDoc ) : + xVolRes( xVR ) +{ + pDocs = new ScAddInDocs( 1, 1 ); + pDocs->Insert( pDoc ); +} + +ScAddInListener::~ScAddInListener() +{ + delete pDocs; +} + +// static +ScAddInListener* ScAddInListener::Get( uno::Reference<sheet::XVolatileResult> xVR ) +{ + sheet::XVolatileResult* pComp = xVR.get(); + + ULONG nCount = aAllListeners.Count(); + for (ULONG nPos=0; nPos<nCount; nPos++) + { + ScAddInListener* pLst = (ScAddInListener*)aAllListeners.GetObject(nPos); + if ( pComp == (sheet::XVolatileResult*)pLst->xVolRes.get() ) + return pLst; + } + return NULL; // not found +} + +//! move to some container object? +// static +void ScAddInListener::RemoveDocument( ScDocument* pDocumentP ) +{ + ULONG nPos = aAllListeners.Count(); + while (nPos) + { + // loop backwards because elements are removed + --nPos; + ScAddInListener* pLst = (ScAddInListener*)aAllListeners.GetObject(nPos); + ScAddInDocs* p = pLst->pDocs; + USHORT nFoundPos; + if ( p->Seek_Entry( pDocumentP, &nFoundPos ) ) + { + p->Remove( nFoundPos ); + if ( p->Count() == 0 ) + { + // this AddIn is no longer used + // dont delete, just remove the ref for the list + + aAllListeners.Remove( nPos ); + + if ( pLst->xVolRes.is() ) + pLst->xVolRes->removeResultListener( pLst ); + + pLst->release(); // Ref for aAllListeners - pLst may be deleted here + } + } + } +} + +//------------------------------------------------------------------------ + +// XResultListener + +void SAL_CALL ScAddInListener::modified( const ::com::sun::star::sheet::ResultEvent& aEvent ) + throw(::com::sun::star::uno::RuntimeException) +{ + ScUnoGuard aGuard; //! or generate a UserEvent + + aResult = aEvent.Value; // store result + + if ( !HasListeners() ) + { + //! remove from list and removeListener, as in RemoveDocument ??? + +#if 0 + //! this will crash if called before first StartListening !!! + aAllListeners.Remove( this ); + if ( xVolRes.is() ) + xVolRes->removeResultListener( this ); + release(); // Ref for aAllListeners - this may be deleted here + return; +#endif + } + + // notify document of changes + + Broadcast( ScHint( SC_HINT_DATACHANGED, ScAddress(), NULL ) ); + + const ScDocument** ppDoc = (const ScDocument**) pDocs->GetData(); + USHORT nCount = pDocs->Count(); + for ( USHORT j=0; j<nCount; j++, ppDoc++ ) + { + ScDocument* pDoc = (ScDocument*)*ppDoc; + pDoc->TrackFormulas(); + pDoc->GetDocumentShell()->Broadcast( SfxSimpleHint( FID_DATACHANGED ) ); + pDoc->ResetChanged( ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB) ); + } +} + +// XEventListener + +void SAL_CALL ScAddInListener::disposing( const ::com::sun::star::lang::EventObject& /* Source */ ) + throw(::com::sun::star::uno::RuntimeException) +{ + // hold a ref so this is not deleted at removeResultListener + uno::Reference<sheet::XResultListener> xRef( this ); + + if ( xVolRes.is() ) + { + xVolRes->removeResultListener( this ); + xVolRes = NULL; + } +} + + +//------------------------------------------------------------------------ + + + diff --git a/sc/source/core/tool/address.cxx b/sc/source/core/tool/address.cxx new file mode 100644 index 000000000000..ccbecd52d9f2 --- /dev/null +++ b/sc/source/core/tool/address.cxx @@ -0,0 +1,2029 @@ +/************************************************************************* + * + * 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 "address.hxx" +#include "global.hxx" +#include "compiler.hxx" +#include "document.hxx" +#include "externalrefmgr.hxx" + +#include "globstr.hrc" +#include <sal/alloca.h> + +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sheet/ExternalLinkInfo.hpp> +#include <com/sun/star/sheet/ExternalLinkType.hpp> +#include <sfx2/objsh.hxx> +#include <tools/urlobj.hxx> +using namespace ::com::sun::star; + +//////////////////////////////////////////////////////////////////////////// +const ScAddress::Details ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO, 0, 0 ); + +ScAddress::Details::Details ( const ScDocument* pDoc, + const ScAddress & rAddr ) : + eConv( pDoc->GetAddressConvention() ), + nRow( rAddr.Row() ), + nCol( rAddr.Col() ) +{ +} + +//UNUSED2009-05 void ScAddress::Details::SetPos ( const ScDocument* pDoc, +//UNUSED2009-05 const ScAddress & rAddr ) +//UNUSED2009-05 { +//UNUSED2009-05 nRow = rAddr.Row(); +//UNUSED2009-05 nCol = rAddr.Col(); +//UNUSED2009-05 eConv = pDoc->GetAddressConvention(); +//UNUSED2009-05 } + +//////////////////////////////////////////////////////////////////////////// + +#include <iostream> + +/** + * Parse from the opening single quote to the closing single quote. Inside + * the quotes, a single quote character is encoded by double single-quote + * characters. + * + * @param p pointer to the first character to begin parsing. + * @param rName (reference) parsed name within the quotes. If the name is + * empty, either the parsing failed or it's an empty quote. + * + * @return pointer to the character immediately after the closing single + * quote. + */ +static const sal_Unicode* lcl_ParseQuotedName( const sal_Unicode* p, String& rName ) +{ + rName.Erase(); + if (*p != '\'') + return p; + + const sal_Unicode* pStart = p; + sal_Unicode cPrev = 0; + for (++p; *p; ++p) + { + if (*p == '\'') + { + if (cPrev == '\'') + { + // double single-quote equals one single quote. + rName += *p; + cPrev = 0; + continue; + } + } + else if (cPrev == '\'') + // We are past the closing quote. We're done! + return p; + else + rName += *p; + cPrev = *p; + } + rName.Erase(); + return pStart; +} + +static long int +sal_Unicode_strtol ( const sal_Unicode* p, + const sal_Unicode** pEnd ) +{ + long int accum = 0, prev = 0; + bool is_neg = false; + + if( *p == '-' ) + { + is_neg = true; + p++; + } + else if( *p == '+' ) + p++; + + while (CharClass::isAsciiDigit( *p )) + { + accum = accum * 10 + *p - '0'; + if( accum < prev ) + { + *pEnd = NULL; + return 0; + } + prev = accum; + p++; + } + + *pEnd = p; + return is_neg ? -accum : accum; +} + +const sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p ) +{ + if ( p ) + { + while( *p == ' ' ) + ++p; + } + return p; +} + +/** Determines the number of sheets an external reference spans and sets + rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding + bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in + cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName + is set to rEndTabName. + @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not + result in the identical file ID. Else <TRUE/>. + */ +static bool lcl_ScRange_External_TabSpan( + ScRange & rRange, + USHORT & rFlags, + ScAddress::ExternalInfo* pExtInfo, + const String & rExternDocName, + const String & rStartTabName, + const String & rEndTabName, + ScDocument* pDoc ) +{ + if (!rExternDocName.Len()) + return !pExtInfo || !pExtInfo->mbExternal; + + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + if (pRefMgr->isOwnDocument( rExternDocName)) + return !pExtInfo || !pExtInfo->mbExternal; + + sal_uInt16 nFileId = pRefMgr->getExternalFileId( rExternDocName); + + if (pExtInfo) + { + if (pExtInfo->mbExternal) + { + if (pExtInfo->mnFileId != nFileId) + return false; + } + else + { + pExtInfo->mbExternal = true; + pExtInfo->maTabName = rStartTabName; + pExtInfo->mnFileId = nFileId; + } + } + + if (!rEndTabName.Len() || rStartTabName == rEndTabName) + { + rRange.aEnd.SetTab( rRange.aStart.Tab()); + return true; + } + + SCsTAB nSpan = pRefMgr->getCachedTabSpan( nFileId, rStartTabName, rEndTabName); + if (nSpan == -1) + rFlags &= ~(SCA_VALID_TAB | SCA_VALID_TAB2); + else if (nSpan == 0) + rFlags &= ~SCA_VALID_TAB2; + else if (nSpan >= 1) + rRange.aEnd.SetTab( rRange.aStart.Tab() + nSpan - 1); + else // (nSpan < -1) + { + rRange.aEnd.SetTab( rRange.aStart.Tab() - nSpan - 1); + if (pExtInfo) + pExtInfo->maTabName = rEndTabName; + } + return true; +} + +/** Returns NULL if the string should be a sheet name, but is invalid. + Returns a pointer to the first character after the sheet name, if there was + any, else pointer to start. + @param pMsoxlQuoteStop + Starting _within_ a quoted name, but still may be 3D; quoted name stops + at pMsoxlQuoteStop + */ +static const sal_Unicode * +lcl_XL_ParseSheetRef( const sal_Unicode* start, + String& rExternTabName, + bool allow_3d, + const sal_Unicode* pMsoxlQuoteStop ) +{ + String aTabName; + const sal_Unicode *p = start; + + // XL only seems to use single quotes for sheet names. + if (pMsoxlQuoteStop) + { + const sal_Unicode* pCurrentStart = p; + while (p < pMsoxlQuoteStop) + { + if (*p == '\'') + { + // We pre-analyzed the quoting, no checks needed here. + if (*++p == '\'') + { + aTabName.Append( pCurrentStart, + sal::static_int_cast<xub_StrLen>( p - pCurrentStart)); + pCurrentStart = ++p; + } + } + else if (*p == ':') + { + break; // while + } + else + ++p; + } + if (pCurrentStart < p) + aTabName.Append( pCurrentStart, sal::static_int_cast<xub_StrLen>( p - pCurrentStart)); + if (!aTabName.Len()) + return NULL; + if (p == pMsoxlQuoteStop) + ++p; // position on ! of ...'!... + if( *p != '!' && ( !allow_3d || *p != ':' ) ) + return (!allow_3d && *p == ':') ? p : start; + } + else if( *p == '\'') + { + p = lcl_ParseQuotedName(p, aTabName); + if (!aTabName.Len()) + return NULL; + } + else + { + bool only_digits = TRUE; + + /* + * Valid: Normal!a1 + * Valid: x.y!a1 + * Invalid: .y!a1 + * + * Some names starting with digits are actually valid, but + * unparse quoted. Things are quite tricky: most sheet names + * starting with a digit are ok, but not those starting with + * "[0-9]*\." or "[0-9]+[eE]". + * + * Valid: 42!a1 + * Valid: 4x!a1 + * Invalid: 1.!a1 + * Invalid: 1e!a1 + */ + while( 1 ) + { + const sal_Unicode uc = *p; + if( CharClass::isAsciiAlpha( uc ) || uc == '_' ) + { + if( only_digits && p != start && + (uc == 'e' || uc == 'E' ) ) + { + p = start; + break; + } + only_digits = FALSE; + p++; + } + else if( CharClass::isAsciiDigit( uc )) + { + p++; + } + else if( uc == '.' ) + { + if( only_digits ) // Valid, except after only digits. + { + p = start; + break; + } + p++; + } + else if (uc > 127) + { + // non ASCII character is allowed. + ++p; + } + else + break; + } + + if( *p != '!' && ( !allow_3d || *p != ':' ) ) + return (!allow_3d && *p == ':') ? p : start; + + aTabName.Append( start, sal::static_int_cast<xub_StrLen>( p - start ) ); + } + + rExternTabName = aTabName; + return p; +} + + +const sal_Unicode* ScRange::Parse_XL_Header( + const sal_Unicode* p, + const ScDocument* pDoc, + String& rExternDocName, + String& rStartTabName, + String& rEndTabName, + USHORT& nFlags, + bool bOnlyAcceptSingle, + const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks ) +{ + const sal_Unicode* startTabs, *start = p; + USHORT nSaveFlags = nFlags; + + // Is this an external reference ? + rStartTabName.Erase(); + rEndTabName.Erase(); + rExternDocName.Erase(); + const sal_Unicode* pMsoxlQuoteStop = NULL; + if (*p == '[') + { + ++p; + // Only single quotes are correct, and a double single quote escapes a + // single quote text inside the quoted text. + if (*p == '\'') + { + p = lcl_ParseQuotedName(p, rExternDocName); + if (!*p || *p != ']' || !rExternDocName.Len()) + { + rExternDocName.Erase(); + return start; + } + } + else + { + // non-quoted file name. + p = ScGlobal::UnicodeStrChr( start+1, ']' ); + if( p == NULL ) + return start; + rExternDocName.Append( start+1, sal::static_int_cast<xub_StrLen>( p-(start+1) ) ); + } + ++p; + + // 1-based, sequence starts with an empty element. + if (pExternalLinks && pExternalLinks->getLength() > 1) + { + // A numeric "document name" is an index into the sequence. + if (CharClass::isAsciiNumeric( rExternDocName)) + { + sal_Int32 i = rExternDocName.ToInt32(); + if (i <= 0 || i >= pExternalLinks->getLength()) + return start; + const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i]; + switch (rInfo.Type) + { + case sheet::ExternalLinkType::DOCUMENT : + { + rtl::OUString aStr; + if (!(rInfo.Data >>= aStr)) + { + DBG_ERROR1( "ScRange::Parse_XL_Header: Data type mismatch for ExternalLinkInfo %d", i); + return NULL; + } + rExternDocName = aStr; + } + break; + default: + DBG_ERROR2( "ScRange::Parse_XL_Header: unhandled ExternalLinkType %d for index %d", + rInfo.Type, i); + return NULL; + } + } + } + rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, pDoc->GetDocumentShell()); + } + else if (*p == '\'') + { + // Sickness in Excel's ODF msoxl namespace: + // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7 or + // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11 + // But, 'Sheet1'!B3 would also be a valid! + // Excel does not allow [ and ] characters in sheet names though. + p = lcl_ParseQuotedName(p, rExternDocName); + if (!*p || *p != '!') + { + rExternDocName.Erase(); + return start; + } + if (rExternDocName.Len()) + { + xub_StrLen nOpen = rExternDocName.Search( '['); + if (nOpen == STRING_NOTFOUND) + rExternDocName.Erase(); + else + { + xub_StrLen nClose = rExternDocName.Search( ']', nOpen+1); + if (nClose == STRING_NOTFOUND) + rExternDocName.Erase(); + else + { + rExternDocName.Erase( nClose); + rExternDocName.Erase( nOpen, 1); + pMsoxlQuoteStop = p - 1; // the ' quote char + // There may be embedded escaped quotes, just matching the + // doc name's length may not work. + for (p = start; *p != '['; ++p) + ; + for ( ; *p != ']'; ++p) + ; + ++p; + } + } + } + if (!rExternDocName.Len()) + p = start; + } + + startTabs = p; + p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop); + if( NULL == p ) + return start; // invalid tab + if (bOnlyAcceptSingle && *p == ':') + return NULL; // 3D + if( p != startTabs ) + { + nFlags |= SCA_VALID_TAB | SCA_TAB_3D | SCA_TAB_ABSOLUTE; + if( *p == ':' ) // 3d ref + { + p = lcl_XL_ParseSheetRef( p+1, rEndTabName, false, pMsoxlQuoteStop); + if( p == NULL ) + { + nFlags = nSaveFlags; + return start; // invalid tab + } + nFlags |= SCA_VALID_TAB2 | SCA_TAB2_3D | SCA_TAB2_ABSOLUTE; + } + else + { + // If only one sheet is given, the full reference is still valid, + // only the second 3D flag is not set. + nFlags |= SCA_VALID_TAB2 | SCA_TAB2_ABSOLUTE; + aEnd.SetTab( aStart.Tab() ); + } + + if( *p++ != '!' ) + { + nFlags = nSaveFlags; + return start; // syntax error + } + else + p = lcl_eatWhiteSpace( p ); + } + else + { + nFlags |= SCA_VALID_TAB | SCA_VALID_TAB2; + // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. ); + } + + if (rExternDocName.Len()) + { + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + pRefMgr->convertToAbsName( rExternDocName); + } + else + { + // Internal reference. + if (!rStartTabName.Len()) + { + nFlags = nSaveFlags; + return start; + } + + SCTAB nTab; + if (!pDoc->GetTable(rStartTabName, nTab)) + { + // invalid table name. + nFlags &= ~SCA_VALID_TAB; + nTab = -1; + } + + aStart.SetTab(nTab); + aEnd.SetTab(nTab); + + if (rEndTabName.Len()) + { + if (!pDoc->GetTable(rEndTabName, nTab)) + { + // invalid table name. + nFlags &= ~SCA_VALID_TAB2; + nTab = -1; + } + + aEnd.SetTab(nTab); + } + } + return p; +} + + +static const sal_Unicode* +lcl_r1c1_get_col( const sal_Unicode* p, + const ScAddress::Details& rDetails, + ScAddress* pAddr, USHORT* nFlags ) +{ + const sal_Unicode *pEnd; + long int n; + bool isRelative; + + if( p[0] == '\0' ) + return NULL; + + p++; + if( (isRelative = (*p == '[') ) != false ) + p++; + n = sal_Unicode_strtol( p, &pEnd ); + if( NULL == pEnd ) + return NULL; + + if( p == pEnd ) // C is a relative ref with offset 0 + { + if( isRelative ) + return NULL; + n = rDetails.nCol; + } + else if( isRelative ) + { + if( *pEnd != ']' ) + return NULL; + n += rDetails.nCol; + pEnd++; + } + else + { + *nFlags |= SCA_COL_ABSOLUTE; + n--; + } + + if( n < 0 || n >= MAXCOLCOUNT ) + return NULL; + pAddr->SetCol( static_cast<SCCOL>( n ) ); + *nFlags |= SCA_VALID_COL; + + return pEnd; +} +static inline const sal_Unicode* +lcl_r1c1_get_row( const sal_Unicode* p, + const ScAddress::Details& rDetails, + ScAddress* pAddr, USHORT* nFlags ) +{ + const sal_Unicode *pEnd; + long int n; + bool isRelative; + + if( p[0] == '\0' ) + return NULL; + + p++; + if( (isRelative = (*p == '[') ) != false ) + p++; + n = sal_Unicode_strtol( p, &pEnd ); + if( NULL == pEnd ) + return NULL; + + if( p == pEnd ) // R is a relative ref with offset 0 + { + if( isRelative ) + return NULL; + n = rDetails.nRow; + } + else if( isRelative ) + { + if( *pEnd != ']' ) + return NULL; + n += rDetails.nRow; + pEnd++; + } + else + { + *nFlags |= SCA_ROW_ABSOLUTE; + n--; + } + + if( n < 0 || n >= MAXROWCOUNT ) + return NULL; + pAddr->SetRow( static_cast<SCROW>( n ) ); + *nFlags |= SCA_VALID_ROW; + + return pEnd; +} + +static USHORT +lcl_ScRange_Parse_XL_R1C1( ScRange& r, + const sal_Unicode* p, + ScDocument* pDoc, + const ScAddress::Details& rDetails, + bool bOnlyAcceptSingle, + ScAddress::ExternalInfo* pExtInfo ) +{ + const sal_Unicode* pTmp = NULL; + String aExternDocName, aStartTabName, aEndTabName; + USHORT nFlags = SCA_VALID | SCA_VALID_TAB; + // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged. + USHORT nFlags2 = SCA_VALID_TAB; + +#if 0 + { + ByteString aStr(p, RTL_TEXTENCODING_UTF8); + aStr.Append(static_cast< char >(0)); + std::cerr << "parse::XL::R1C1 \'" << aStr.GetBuffer() << '\'' << std::endl; + } +#endif + p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName, + aEndTabName, nFlags, bOnlyAcceptSingle, NULL ); + + if (aExternDocName.Len() > 0) + lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName, + aStartTabName, aEndTabName, pDoc); + + if( NULL == p ) + return 0; + + if( *p == 'R' || *p == 'r' ) + { + if( NULL == (p = lcl_r1c1_get_row( p, rDetails, &r.aStart, &nFlags )) ) + goto failed; + + if( *p != 'C' && *p != 'c' ) // full row R# + { + if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) || + NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 ))) + { + // Only the initial row number is given, or the second row + // number is invalid. Fallback to just the initial R + nFlags |= (nFlags << 4); + r.aEnd.SetRow( r.aStart.Row() ); + } + else + { + // Full row range successfully parsed. + nFlags |= (nFlags2 << 4); + p = pTmp; + } + + if (p && p[0] != 0) + { + // any trailing invalid character must invalidate the whole address. + nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | + SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); + return nFlags; + } + + nFlags |= + SCA_VALID_COL | SCA_VALID_COL2 | + SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE; + r.aStart.SetCol( 0 ); + r.aEnd.SetCol( MAXCOL ); + + return bOnlyAcceptSingle ? 0 : nFlags; + } + else if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags ))) + goto failed; + + if( p[0] != ':' || + (p[1] != 'R' && p[1] != 'r') || + NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )) || + (*pTmp != 'C' && *pTmp != 'c') || + NULL == (pTmp = lcl_r1c1_get_col( pTmp, rDetails, &r.aEnd, &nFlags2 ))) + { + // single cell reference + + if (p && p[0] != 0) + { + // any trailing invalid character must invalidate the whole address. + nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB); + return nFlags; + } + + return bOnlyAcceptSingle ? nFlags : 0; + } + p = pTmp; + + // double reference + + if (p && p[0] != 0) + { + // any trailing invalid character must invalidate the whole range. + nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | + SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); + return nFlags; + } + + nFlags |= (nFlags2 << 4); + return bOnlyAcceptSingle ? 0 : nFlags; + } + else if( *p == 'C' || *p == 'c' ) // full col C# + { + if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags ))) + goto failed; + + if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') || + NULL == (pTmp = lcl_r1c1_get_col( p+1, rDetails, &r.aEnd, &nFlags2 ))) + { // Fallback to just the initial C + nFlags |= (nFlags << 4); + r.aEnd.SetCol( r.aStart.Col() ); + } + else + { + nFlags |= (nFlags2 << 4); + p = pTmp; + } + + if (p && p[0] != 0) + { + // any trailing invalid character must invalidate the whole address. + nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | + SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); + return nFlags; + } + + nFlags |= + SCA_VALID_ROW | SCA_VALID_ROW2 | + SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE; + r.aStart.SetRow( 0 ); + r.aEnd.SetRow( MAXROW ); + + return bOnlyAcceptSingle ? 0 : nFlags; + } + +failed : + return 0; +} + +static inline const sal_Unicode* +lcl_a1_get_col( const sal_Unicode* p, ScAddress* pAddr, USHORT* nFlags ) +{ + SCCOL nCol; + + if( *p == '$' ) + *nFlags |= SCA_COL_ABSOLUTE, p++; + + if( !CharClass::isAsciiAlpha( *p ) ) + return NULL; + + nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' ); + while (nCol <= MAXCOL && CharClass::isAsciiAlpha(*p)) + nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' ); + if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) ) + return NULL; + + *nFlags |= SCA_VALID_COL; + pAddr->SetCol( nCol ); + + return p; +} + +static inline const sal_Unicode* +lcl_a1_get_row( const sal_Unicode* p, ScAddress* pAddr, USHORT* nFlags ) +{ + const sal_Unicode *pEnd; + long int n; + + if( *p == '$' ) + *nFlags |= SCA_ROW_ABSOLUTE, p++; + + n = sal_Unicode_strtol( p, &pEnd ) - 1; + if( NULL == pEnd || p == pEnd || n < 0 || n > MAXROW ) + return NULL; + + *nFlags |= SCA_VALID_ROW; + pAddr->SetRow( static_cast<SCROW>(n) ); + + return pEnd; +} + +static USHORT +lcl_ScRange_Parse_XL_A1( ScRange& r, + const sal_Unicode* p, + ScDocument* pDoc, + bool bOnlyAcceptSingle, + ScAddress::ExternalInfo* pExtInfo, + const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks ) +{ + const sal_Unicode* tmp1, *tmp2; + String aExternDocName, aStartTabName, aEndTabName; // for external link table + USHORT nFlags = SCA_VALID | SCA_VALID_TAB, nFlags2 = SCA_VALID_TAB; + +#if 0 + { + ByteString aStr(p, RTL_TEXTENCODING_UTF8); + aStr.Append(static_cast< char >(0)); + std::cerr << "parse::XL::A1 \'" << aStr.GetBuffer() << '\'' << std::endl; + } +#endif + p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName, + aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks ); + + if (aExternDocName.Len() > 0) + lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName, + aStartTabName, aEndTabName, pDoc); + + if( NULL == p ) + return 0; + + tmp1 = lcl_a1_get_col( p, &r.aStart, &nFlags ); + if( tmp1 == NULL ) // Is it a row only reference 3:5 + { + if( bOnlyAcceptSingle ) // by definition full row refs are ranges + return 0; + + tmp1 = lcl_a1_get_row( p, &r.aStart, &nFlags ); + + tmp1 = lcl_eatWhiteSpace( tmp1 ); + if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2) + return 0; + + tmp1 = lcl_eatWhiteSpace( tmp1 ); + tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 ); + if( !tmp2 ) + return 0; + + r.aStart.SetCol( 0 ); r.aEnd.SetCol( MAXCOL ); + nFlags |= + SCA_VALID_COL | SCA_VALID_COL2 | + SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE; + nFlags |= (nFlags2 << 4); + return nFlags; + } + + tmp2 = lcl_a1_get_row( tmp1, &r.aStart, &nFlags ); + if( tmp2 == NULL ) // check for col only reference F:H + { + if( bOnlyAcceptSingle ) // by definition full col refs are ranges + return 0; + + tmp1 = lcl_eatWhiteSpace( tmp1 ); + if( *tmp1++ != ':' ) // Even a singleton requires ':' (eg F:F) + return 0; + + tmp1 = lcl_eatWhiteSpace( tmp1 ); + tmp2 = lcl_a1_get_col( tmp1, &r.aEnd, &nFlags2 ); + if( !tmp2 ) + return 0; + + r.aStart.SetRow( 0 ); r.aEnd.SetRow( MAXROW ); + nFlags |= + SCA_VALID_ROW | SCA_VALID_ROW2 | + SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE; + nFlags |= (nFlags2 << 4); + return nFlags; + } + + // prepare as if it's a singleton, in case we want to fall back */ + r.aEnd.SetCol( r.aStart.Col() ); + r.aEnd.SetRow( r.aStart.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header() + + if ( bOnlyAcceptSingle ) + { + if ( *tmp2 == 0 ) + return nFlags; + else + { + // any trailing invalid character must invalidate the address. + nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB); + return nFlags; + } + } + + tmp2 = lcl_eatWhiteSpace( tmp2 ); + if( *tmp2 != ':' ) + { + // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is + // not. Any trailing invalid character invalidates the range. + if (*tmp2 == 0 && (nFlags & SCA_TAB2_3D)) + { + if (nFlags & SCA_COL_ABSOLUTE) + nFlags |= SCA_COL2_ABSOLUTE; + if (nFlags & SCA_ROW_ABSOLUTE) + nFlags |= SCA_ROW2_ABSOLUTE; + } + else + nFlags &= ~(SCA_VALID | + SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | + SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); + return nFlags; + } + + p = tmp2; + p = lcl_eatWhiteSpace( p+1 ); + tmp1 = lcl_a1_get_col( p, &r.aEnd, &nFlags2 ); + if( !tmp1 ) // strange, but valid singleton + return nFlags; + + tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 ); + if( !tmp2 ) // strange, but valid singleton + return nFlags; + + if ( *tmp2 != 0 ) + { + // any trailing invalid character must invalidate the range. + nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | + SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); + return nFlags; + } + + nFlags |= (nFlags2 << 4); + return nFlags; +} + +/** + @param pRange pointer to range where rAddr effectively is *pRange->aEnd, + used in conjunction with pExtInfo to determine the tab span + of a 3D reference. + */ +static USHORT +lcl_ScAddress_Parse_OOo( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr, + ScAddress::ExternalInfo* pExtInfo = NULL, ScRange* pRange = NULL ) +{ + USHORT nRes = 0; + String aDocName; // der pure Dokumentenname + String aTab; + bool bExtDoc = false; + bool bExtDocInherited = false; + const ScAddress aCurPos(rAddr); + + // Lets see if this is a reference to something in an external file. A + // document name is always quoted and has a trailing #. + if (*p == '\'') + { + const sal_Unicode* pStart = p; + p = lcl_ParseQuotedName(p, aDocName); + if (*p++ == SC_COMPILER_FILE_TAB_SEP) + bExtDoc = true; + else + // This is not a document name. Perhaps a quoted relative table + // name. + p = pStart; + } + else if (pExtInfo && pExtInfo->mbExternal) + { + // This is an external reference. + bExtDoc = bExtDocInherited = true; + } + + SCCOL nCol = 0; + SCROW nRow = 0; + SCTAB nTab = 0; + USHORT nBits = SCA_VALID_TAB; + const sal_Unicode* q; + if ( ScGlobal::FindUnquoted( p, '.') ) + { + nRes |= SCA_TAB_3D; + if ( bExtDoc ) + nRes |= SCA_TAB_ABSOLUTE; + if (*p == '$') + nRes |= SCA_TAB_ABSOLUTE, p++; + + if (*p == '\'') + { + // Tokens that start at ' can have anything in them until a final + // ' but '' marks an escaped '. We've earlier guaranteed that a + // string containing '' will be surrounded by '. + p = lcl_ParseQuotedName(p, aTab); + } + else + { + while (*p) + { + if( *p == '.') + break; + + if( *p == '\'' ) + { + p++; break; + } + aTab += *p++; + } + } + if( *p++ != '.' ) + nBits = 0; + + if (!bExtDoc && (!pDoc || !pDoc->GetTable( aTab, nTab ))) + nBits = 0; + } + else + { + if (bExtDoc && !bExtDocInherited) + return nRes; // After a document a sheet must follow. + nTab = rAddr.Tab(); + } + nRes |= nBits; + + q = p; + if (*p) + { + nBits = SCA_VALID_COL; + if (*p == '$') + nBits |= SCA_COL_ABSOLUTE, p++; + + if (CharClass::isAsciiAlpha( *p )) + { + nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' ); + while (nCol < MAXCOL && CharClass::isAsciiAlpha(*p)) + nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' ); + } + else + nBits = 0; + + if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) ) + nBits = 0; + nRes |= nBits; + if( !nBits ) + p = q; + } + + q = p; + if (*p) + { + nBits = SCA_VALID_ROW; + if (*p == '$') + nBits |= SCA_ROW_ABSOLUTE, p++; + if( !CharClass::isAsciiDigit( *p ) ) + { + nBits = 0; + nRow = SCROW(-1); + } + else + { + String aTmp( p ); + long n = aTmp.ToInt32() - 1; + while (CharClass::isAsciiDigit( *p )) + p++; + if( n < 0 || n > MAXROW ) + nBits = 0; + nRow = static_cast<SCROW>(n); + } + nRes |= nBits; + if( !nBits ) + p = q; + } + + rAddr.Set( nCol, nRow, nTab ); + + if (!*p && bExtDoc) + { + if (!pDoc) + nRes = 0; + else + { + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + + // Need document name if inherited. + if (bExtDocInherited) + { + const String* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId); + if (pFileName) + aDocName = *pFileName; + else + nRes = 0; + } + pRefMgr->convertToAbsName(aDocName); + + if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName)) + { + if (!pDoc->GetTable( aTab, nTab )) + nRes = 0; + else + { + rAddr.SetTab( nTab); + nRes |= SCA_VALID_TAB; + } + } + else + { + if (!pExtInfo) + nRes = 0; + else + { + if (!pExtInfo->mbExternal) + { + sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName); + + pExtInfo->mbExternal = true; + pExtInfo->maTabName = aTab; + pExtInfo->mnFileId = nFileId; + + if (pRefMgr->getSingleRefToken(nFileId, aTab, + ScAddress(nCol, nRow, 0), NULL, + &nTab).get()) + { + rAddr.SetTab( nTab); + nRes |= SCA_VALID_TAB; + } + else + nRes = 0; + } + else + { + // This is a call for the second part of the reference, + // we must have the range to adapt tab span. + if (!pRange) + nRes = 0; + else + { + USHORT nFlags = nRes | SCA_VALID_TAB2; + if (!lcl_ScRange_External_TabSpan( *pRange, nFlags, + pExtInfo, aDocName, + pExtInfo->maTabName, aTab, pDoc)) + nRes &= ~SCA_VALID_TAB; + else + { + if (nFlags & SCA_VALID_TAB2) + { + rAddr.SetTab( pRange->aEnd.Tab()); + nRes |= SCA_VALID_TAB; + } + else + nRes &= ~SCA_VALID_TAB; + } + } + } + } + } + } + } + + if ( !(nRes & SCA_VALID_ROW) && (nRes & SCA_VALID_COL) + && !( (nRes & SCA_TAB_3D) && (nRes & SCA_VALID_TAB)) ) + { // no Row, no Tab, but Col => DM (...), B (...) et al + nRes = 0; + } + if( !*p ) + { + USHORT nMask = nRes & ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ); + if( nMask == ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ) ) + nRes |= SCA_VALID; + } + else + nRes = 0; + return nRes; +} + +static USHORT +lcl_ScAddress_Parse ( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr, + const ScAddress::Details& rDetails, + ScAddress::ExternalInfo* pExtInfo = NULL, + const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks = NULL ) +{ + if( !*p ) + return 0; + + switch (rDetails.eConv) + { + default : + case formula::FormulaGrammar::CONV_OOO: + { + return lcl_ScAddress_Parse_OOo( p, pDoc, rAddr, pExtInfo, NULL ); + } + + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + { + ScRange r = rAddr; + USHORT nFlags = lcl_ScRange_Parse_XL_A1( r, p, pDoc, true, pExtInfo, + (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) ); + rAddr = r.aStart; + return nFlags; + } + case formula::FormulaGrammar::CONV_XL_R1C1: + { + ScRange r = rAddr; + USHORT nFlags = lcl_ScRange_Parse_XL_R1C1( r, p, pDoc, rDetails, true, pExtInfo ); + rAddr = r.aStart; + return nFlags; + } + } +} + + +bool ConvertSingleRef( ScDocument* pDoc, const String& rRefString, + SCTAB nDefTab, ScRefAddress& rRefAddress, + const ScAddress::Details& rDetails, + ScAddress::ExternalInfo* pExtInfo /* = NULL */ ) +{ + bool bRet = false; + if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND)) + { + ScAddress aAddr( 0, 0, nDefTab ); + USHORT nRes = aAddr.Parse( rRefString, pDoc, rDetails, pExtInfo); + if ( nRes & SCA_VALID ) + { + rRefAddress.Set( aAddr, + ((nRes & SCA_COL_ABSOLUTE) == 0), + ((nRes & SCA_ROW_ABSOLUTE) == 0), + ((nRes & SCA_TAB_ABSOLUTE) == 0)); + bRet = true; + } + } + return bRet; +} + + +bool ConvertDoubleRef( ScDocument* pDoc, const String& rRefString, SCTAB nDefTab, + ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress, + const ScAddress::Details& rDetails, + ScAddress::ExternalInfo* pExtInfo /* = NULL */ ) +{ + bool bRet = false; + if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND)) + { + ScRange aRange( ScAddress( 0, 0, nDefTab)); + USHORT nRes = aRange.Parse( rRefString, pDoc, rDetails, pExtInfo); + if ( nRes & SCA_VALID ) + { + rStartRefAddress.Set( aRange.aStart, + ((nRes & SCA_COL_ABSOLUTE) == 0), + ((nRes & SCA_ROW_ABSOLUTE) == 0), + ((nRes & SCA_TAB_ABSOLUTE) == 0)); + rEndRefAddress.Set( aRange.aEnd, + ((nRes & SCA_COL2_ABSOLUTE) == 0), + ((nRes & SCA_ROW2_ABSOLUTE) == 0), + ((nRes & SCA_TAB2_ABSOLUTE) == 0)); + bRet = true; + } + } + return bRet; +} + + +USHORT ScAddress::Parse( const String& r, ScDocument* pDoc, + const Details& rDetails, + ExternalInfo* pExtInfo, + const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks ) +{ + return lcl_ScAddress_Parse( r.GetBuffer(), pDoc, *this, rDetails, pExtInfo, pExternalLinks ); +} + + +bool ScRange::Intersects( const ScRange& r ) const +{ + return !( + Min( aEnd.Col(), r.aEnd.Col() ) < Max( aStart.Col(), r.aStart.Col() ) + || Min( aEnd.Row(), r.aEnd.Row() ) < Max( aStart.Row(), r.aStart.Row() ) + || Min( aEnd.Tab(), r.aEnd.Tab() ) < Max( aStart.Tab(), r.aStart.Tab() ) + ); +} + + +void ScRange::Justify() +{ + SCCOL nTempCol; + if ( aEnd.Col() < (nTempCol = aStart.Col()) ) + { + aStart.SetCol(aEnd.Col()); aEnd.SetCol(nTempCol); + } + SCROW nTempRow; + if ( aEnd.Row() < (nTempRow = aStart.Row()) ) + { + aStart.SetRow(aEnd.Row()); aEnd.SetRow(nTempRow); + } + SCTAB nTempTab; + if ( aEnd.Tab() < (nTempTab = aStart.Tab()) ) + { + aStart.SetTab(aEnd.Tab()); aEnd.SetTab(nTempTab); + } +} + +void ScRange::ExtendTo( const ScRange& rRange ) +{ + DBG_ASSERT( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" ); + if( IsValid() ) + { + aStart.SetCol( ::std::min( aStart.Col(), rRange.aStart.Col() ) ); + aStart.SetRow( ::std::min( aStart.Row(), rRange.aStart.Row() ) ); + aStart.SetTab( ::std::min( aStart.Tab(), rRange.aStart.Tab() ) ); + aEnd.SetCol( ::std::max( aEnd.Col(), rRange.aEnd.Col() ) ); + aEnd.SetRow( ::std::max( aEnd.Row(), rRange.aEnd.Row() ) ); + aEnd.SetTab( ::std::max( aEnd.Tab(), rRange.aEnd.Tab() ) ); + } + else + *this = rRange; +} + +static USHORT +lcl_ScRange_Parse_OOo( ScRange &aRange, const String& r, ScDocument* pDoc, ScAddress::ExternalInfo* pExtInfo = NULL ) +{ + USHORT nRes1 = 0, nRes2 = 0; + xub_StrLen nPos = ScGlobal::FindUnquoted( r, ':'); + if (nPos != STRING_NOTFOUND) + { + String aTmp( r ); + sal_Unicode* p = aTmp.GetBufferAccess(); + p[ nPos ] = 0; + if( (nRes1 = lcl_ScAddress_Parse_OOo( p, pDoc, aRange.aStart, pExtInfo, NULL ) ) != 0 ) + { + aRange.aEnd = aRange.aStart; // sheet must be initialized identical to first sheet + if ( (nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, pDoc, aRange.aEnd, pExtInfo, &aRange ) ) != 0 ) + { + // PutInOrder / Justify + USHORT nMask, nBits1, nBits2; + SCCOL nTempCol; + if ( aRange.aEnd.Col() < (nTempCol = aRange.aStart.Col()) ) + { + aRange.aStart.SetCol(aRange.aEnd.Col()); aRange.aEnd.SetCol(nTempCol); + nMask = (SCA_VALID_COL | SCA_COL_ABSOLUTE); + nBits1 = nRes1 & nMask; + nBits2 = nRes2 & nMask; + nRes1 = (nRes1 & ~nMask) | nBits2; + nRes2 = (nRes2 & ~nMask) | nBits1; + } + SCROW nTempRow; + if ( aRange.aEnd.Row() < (nTempRow = aRange.aStart.Row()) ) + { + aRange.aStart.SetRow(aRange.aEnd.Row()); aRange.aEnd.SetRow(nTempRow); + nMask = (SCA_VALID_ROW | SCA_ROW_ABSOLUTE); + nBits1 = nRes1 & nMask; + nBits2 = nRes2 & nMask; + nRes1 = (nRes1 & ~nMask) | nBits2; + nRes2 = (nRes2 & ~nMask) | nBits1; + } + SCTAB nTempTab; + if ( aRange.aEnd.Tab() < (nTempTab = aRange.aStart.Tab()) ) + { + aRange.aStart.SetTab(aRange.aEnd.Tab()); aRange.aEnd.SetTab(nTempTab); + nMask = (SCA_VALID_TAB | SCA_TAB_ABSOLUTE | SCA_TAB_3D); + nBits1 = nRes1 & nMask; + nBits2 = nRes2 & nMask; + nRes1 = (nRes1 & ~nMask) | nBits2; + nRes2 = (nRes2 & ~nMask) | nBits1; + } + if ( ((nRes1 & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D )) + == ( SCA_TAB_ABSOLUTE | SCA_TAB_3D )) + && !(nRes2 & SCA_TAB_3D) ) + nRes2 |= SCA_TAB_ABSOLUTE; + } + else + nRes1 = 0; // #38840# keine Tokens aus halben Sachen + } + } + nRes1 = ( ( nRes1 | nRes2 ) & SCA_VALID ) + | nRes1 + | ( ( nRes2 & 0x070F ) << 4 ); + return nRes1; +} + +USHORT ScRange::Parse( const String& r, ScDocument* pDoc, + const ScAddress::Details& rDetails, + ScAddress::ExternalInfo* pExtInfo, + const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks ) +{ + if ( r.Len() <= 0 ) + return 0; + + switch (rDetails.eConv) + { + default : + case formula::FormulaGrammar::CONV_OOO: + return lcl_ScRange_Parse_OOo( *this, r, pDoc, pExtInfo ); + + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + return lcl_ScRange_Parse_XL_A1( *this, r.GetBuffer(), pDoc, false, pExtInfo, + (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) ); + + case formula::FormulaGrammar::CONV_XL_R1C1: + return lcl_ScRange_Parse_XL_R1C1( *this, r.GetBuffer(), pDoc, rDetails, false, pExtInfo ); + } +} + + +// Accept a full range, or an address +USHORT ScRange::ParseAny( const String& r, ScDocument* pDoc, + const ScAddress::Details& rDetails ) +{ + USHORT nRet = Parse( r, pDoc, rDetails ); + const USHORT nValid = SCA_VALID | SCA_VALID_COL2 | SCA_VALID_ROW2 | + SCA_VALID_TAB2; + + if ( (nRet & nValid) != nValid ) + { + ScAddress aAdr; + nRet = aAdr.Parse( r, pDoc, rDetails ); + if ( nRet & SCA_VALID ) + aStart = aEnd = aAdr; + } + return nRet; +} + +// Parse only full row references +USHORT ScRange::ParseCols( const String& rStr, ScDocument* pDoc, + const ScAddress::Details& rDetails ) +{ + const sal_Unicode* p = rStr.GetBuffer(); + USHORT nRes = 0, ignored = 0; + + if( NULL == p ) + return 0; + + pDoc = NULL; // make compiler shutup we may need this later + + switch (rDetails.eConv) + { + default : + case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + if (NULL != (p = lcl_a1_get_col( p, &aStart, &ignored ) ) ) + { + if( p[0] == ':') + { + if( NULL != (p = lcl_a1_get_col( p+1, &aEnd, &ignored ))) + { + nRes = SCA_VALID_COL; + } + } + else + { + aEnd = aStart; + nRes = SCA_VALID_COL; + } + } + break; + + case formula::FormulaGrammar::CONV_XL_R1C1: + if ((p[0] == 'C' || p[0] != 'c') && + NULL != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored ))) + { + if( p[0] == ':') + { + if( (p[1] == 'C' || p[1] == 'c') && + NULL != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored ))) + { + nRes = SCA_VALID_COL; + } + } + else + { + aEnd = aStart; + nRes = SCA_VALID_COL; + } + } + break; + } + + return (p != NULL && *p == '\0') ? nRes : 0; +} + +// Parse only full row references +USHORT ScRange::ParseRows( const String& rStr, ScDocument* pDoc, + const ScAddress::Details& rDetails ) +{ + const sal_Unicode* p = rStr.GetBuffer(); + USHORT nRes = 0, ignored = 0; + + if( NULL == p ) + return 0; + + pDoc = NULL; // make compiler shutup we may need this later + + switch (rDetails.eConv) + { + default : + case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + if (NULL != (p = lcl_a1_get_row( p, &aStart, &ignored ) ) ) + { + if( p[0] == ':') + { + if( NULL != (p = lcl_a1_get_row( p+1, &aEnd, &ignored ))) + { + nRes = SCA_VALID_COL; + } + } + else + { + aEnd = aStart; + nRes = SCA_VALID_COL; + } + } + break; + + case formula::FormulaGrammar::CONV_XL_R1C1: + if ((p[0] == 'R' || p[0] != 'r') && + NULL != (p = lcl_r1c1_get_row( p, rDetails, &aStart, &ignored ))) + { + if( p[0] == ':') + { + if( (p[1] == 'R' || p[1] == 'r') && + NULL != (p = lcl_r1c1_get_row( p+1, rDetails, &aEnd, &ignored ))) + { + nRes = SCA_VALID_COL; + } + } + else + { + aEnd = aStart; + nRes = SCA_VALID_COL; + } + } + break; + } + + return (p != NULL && *p == '\0') ? nRes : 0; +} + +static inline void +lcl_a1_append_c ( String &r, int nCol, bool bIsAbs ) +{ + if( bIsAbs ) + r += '$'; + ScColToAlpha( r, sal::static_int_cast<SCCOL>(nCol) ); +} + +static inline void +lcl_a1_append_r ( String &r, int nRow, bool bIsAbs ) +{ + if ( bIsAbs ) + r += '$'; + r += String::CreateFromInt32( nRow+1 ); +} + +static inline void +lcl_r1c1_append_c ( String &r, int nCol, bool bIsAbs, + const ScAddress::Details& rDetails ) +{ + r += 'C'; + if (bIsAbs) + { + r += String::CreateFromInt32( nCol + 1 ); + } + else + { + nCol -= rDetails.nCol; + if (nCol != 0) { + r += '['; + r += String::CreateFromInt32( nCol ); + r += ']'; + } + } +} +static inline void +lcl_r1c1_append_r ( String &r, int nRow, bool bIsAbs, + const ScAddress::Details& rDetails ) +{ + r += 'R'; + if (bIsAbs) + { + r += String::CreateFromInt32( nRow + 1 ); + } + else + { + nRow -= rDetails.nRow; + if (nRow != 0) { + r += '['; + r += String::CreateFromInt32( nRow ); + r += ']'; + } + } +} + +static String +getFileNameFromDoc( const ScDocument* pDoc ) +{ + // TODO : er points at ScGlobal::GetAbsDocName() + // as a better template. Look into it + String sFileName; + SfxObjectShell* pShell; + + if( NULL != pDoc && + NULL != (pShell = pDoc->GetDocumentShell() ) ) + { + uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_QUERY ); + if( xModel.is() ) + { + if( xModel->getURL().getLength() ) + { + INetURLObject aURL( xModel->getURL() ); + sFileName = aURL.GetLastName(); + } + else + sFileName = pShell->GetTitle(); + } + } +#if 0 + { + ByteString aStr( sFileName, RTL_TEXTENCODING_UTF8 ); + aStr.Append(static_cast< char >(0)); + std::cerr << "docname \'" << aStr.GetBuffer() << '\'' << std::endl; + } +#endif + return sFileName; +} + +void ScAddress::Format( String& r, USHORT nFlags, ScDocument* pDoc, + const Details& rDetails) const +{ + r.Erase(); + if( nFlags & SCA_VALID ) + nFlags |= ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ); + if( pDoc && (nFlags & SCA_VALID_TAB ) ) + { + if ( nTab >= pDoc->GetTableCount() ) + { + r = ScGlobal::GetRscString( STR_NOREF_STR ); + return; + } +// if( nFlags & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ) ) + if( nFlags & SCA_TAB_3D ) + { + String aTabName, aDocName; + pDoc->GetName( nTab, aTabName ); + // External Reference, same as in ScCompiler::MakeTabStr() + if( aTabName.GetChar(0) == '\'' ) + { // "'Doc'#Tab" + xub_StrLen nPos = ScGlobal::FindUnquoted( aTabName, SC_COMPILER_FILE_TAB_SEP); + if (nPos != STRING_NOTFOUND && nPos > 0 && aTabName.GetChar(nPos-1) == '\'') + { + aDocName = aTabName.Copy( 0, nPos + 1 ); + aTabName.Erase( 0, nPos + 1 ); + } + } + else if( nFlags & SCA_FORCE_DOC ) + { + // VBA has an 'external' flag that forces the addition of the + // tab name _and_ the doc name. The VBA code would be + // needlessly complicated if it constructed an actual external + // reference so we add this somewhat cheesy kludge to force the + // addition of the document name even for non-external references + aDocName = getFileNameFromDoc( pDoc ); + } + ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv); + + switch( rDetails.eConv ) + { + default : + case formula::FormulaGrammar::CONV_OOO: + r += aDocName; + if( nFlags & SCA_TAB_ABSOLUTE ) + r += '$'; + r += aTabName; + r += '.'; + break; + + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_R1C1: + case formula::FormulaGrammar::CONV_XL_OOX: + if (aDocName.Len() > 0) + { + r += '['; + r += aDocName; + r += ']'; + } + r += aTabName; + r += '!'; + break; + } + } + } + switch( rDetails.eConv ) + { + default : + case formula::FormulaGrammar::CONV_OOO: + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + if( nFlags & SCA_VALID_COL ) + lcl_a1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE ); + if( nFlags & SCA_VALID_ROW ) + lcl_a1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE ); + break; + + case formula::FormulaGrammar::CONV_XL_R1C1: + if( nFlags & SCA_VALID_ROW ) + lcl_r1c1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE, rDetails ); + if( nFlags & SCA_VALID_COL ) + lcl_r1c1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE, rDetails ); + break; + } +} + +static void +lcl_Split_DocTab( const ScDocument* pDoc, SCTAB nTab, + const ScAddress::Details& rDetails, + USHORT nFlags, + String& rTabName, String& rDocName ) +{ + pDoc->GetName( nTab, rTabName ); + rDocName.Erase(); +#if 0 + { + ByteString aStr(rTabName, RTL_TEXTENCODING_UTF8); + aStr.Append(static_cast< char >(0)); + std::cerr << "tabname \'" << aStr.GetBuffer() << '\'' << std::endl; + } +#endif + // External reference, same as in ScCompiler::MakeTabStr() + if ( rTabName.GetChar(0) == '\'' ) + { // "'Doc'#Tab" + xub_StrLen nPos = ScGlobal::FindUnquoted( rTabName, SC_COMPILER_FILE_TAB_SEP); + if (nPos != STRING_NOTFOUND && nPos > 0 && rTabName.GetChar(nPos-1) == '\'') + { + rDocName = rTabName.Copy( 0, nPos + 1 ); + rTabName.Erase( 0, nPos + 1 ); + } + } + else if( nFlags & SCA_FORCE_DOC ) + { + // VBA has an 'external' flag that forces the addition of the + // tab name _and_ the doc name. The VBA code would be + // needlessly complicated if it constructed an actual external + // reference so we add this somewhat cheesy kludge to force the + // addition of the document name even for non-external references + rDocName = getFileNameFromDoc( pDoc ); + } + ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv); +} + +static void +lcl_ScRange_Format_XL_Header( String& r, const ScRange& rRange, + USHORT nFlags, ScDocument* pDoc, + const ScAddress::Details& rDetails ) +{ + if( nFlags & SCA_TAB_3D ) + { + String aTabName, aDocName; + lcl_Split_DocTab( pDoc, rRange.aStart.Tab(), rDetails, nFlags, + aTabName, aDocName ); + if( aDocName.Len() > 0 ) + { + r += '['; + r += aDocName; + r += ']'; + } + r += aTabName; + + if( nFlags & SCA_TAB2_3D ) + { + lcl_Split_DocTab( pDoc, rRange.aEnd.Tab(), rDetails, nFlags, + aTabName, aDocName ); + r += ':'; + r += aTabName; + } + r += '!'; + } +} + +void ScRange::Format( String& r, USHORT nFlags, ScDocument* pDoc, + const ScAddress::Details& rDetails ) const +{ + r.Erase(); + if( !( nFlags & SCA_VALID ) ) + { + r = ScGlobal::GetRscString( STR_NOREF_STR ); + return; + } + +#define absrel_differ(nFlags, mask) (((nFlags) & (mask)) ^ (((nFlags) >> 4) & (mask))) + switch( rDetails.eConv ) { + default : + case formula::FormulaGrammar::CONV_OOO: { + BOOL bOneTab = (aStart.Tab() == aEnd.Tab()); + if ( !bOneTab ) + nFlags |= SCA_TAB_3D; + aStart.Format( r, nFlags, pDoc, rDetails ); + if( aStart != aEnd || + absrel_differ( nFlags, SCA_COL_ABSOLUTE ) || + absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) + { + String aName; + nFlags = ( nFlags & SCA_VALID ) | ( ( nFlags >> 4 ) & 0x070F ); + if ( bOneTab ) + pDoc = NULL; + else + nFlags |= SCA_TAB_3D; + aEnd.Format( aName, nFlags, pDoc, rDetails ); + r += ':'; + r += aName; + } + } + break; + + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails ); + if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL ) + { + // Full col refs always require 2 rows (2:2) + lcl_a1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE ); + r += ':'; + lcl_a1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE ); + } + else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW ) + { + // Full row refs always require 2 cols (A:A) + lcl_a1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE ); + r += ':'; + lcl_a1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE ); + } + else + { + lcl_a1_append_c ( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE ); + lcl_a1_append_r ( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE ); + if( aStart.Col() != aEnd.Col() || + absrel_differ( nFlags, SCA_COL_ABSOLUTE ) || + aStart.Row() != aEnd.Row() || + absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) { + r += ':'; + lcl_a1_append_c ( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE ); + lcl_a1_append_r ( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE ); + } + } + break; + + case formula::FormulaGrammar::CONV_XL_R1C1: + lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails ); + if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL ) + { + lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails ); + if( aStart.Row() != aEnd.Row() || + absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) { + r += ':'; + lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails ); + } + } + else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW ) + { + lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails ); + if( aStart.Col() != aEnd.Col() || + absrel_differ( nFlags, SCA_COL_ABSOLUTE )) { + r += ':'; + lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails ); + } + } + else + { + lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails ); + lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails ); + if( aStart.Col() != aEnd.Col() || + absrel_differ( nFlags, SCA_COL_ABSOLUTE ) || + aStart.Row() != aEnd.Row() || + absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) { + r += ':'; + lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails ); + lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails ); + } + } + } +#undef absrel_differ +} + +bool ScAddress::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc ) +{ + SCsTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB+1; + dx = Col() + dx; + dy = Row() + dy; + dz = Tab() + dz; + BOOL bValid = TRUE; + if( dx < 0 ) + dx = 0, bValid = FALSE; + else if( dx > MAXCOL ) + dx = MAXCOL, bValid =FALSE; + if( dy < 0 ) + dy = 0, bValid = FALSE; + else if( dy > MAXROW ) + dy = MAXROW, bValid =FALSE; + if( dz < 0 ) + dz = 0, bValid = FALSE; + else if( dz >= nMaxTab ) + dz = nMaxTab-1, bValid =FALSE; + Set( dx, dy, dz ); + return bValid; +} + + +bool ScRange::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc ) +{ + // Einfahces &, damit beides ausgefuehrt wird!! + return aStart.Move( dx, dy, dz, pDoc ) & aEnd.Move( dx, dy, dz, pDoc ); +} + + +String ScAddress::GetColRowString( bool bAbsolute, + const Details& rDetails ) const +{ + String aString; + + switch( rDetails.eConv ) + { + default : + case formula::FormulaGrammar::CONV_OOO: + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_OOX: + if (bAbsolute) + aString.Append( '$' ); + + ScColToAlpha( aString, nCol); + + if ( bAbsolute ) + aString.Append( '$' ); + + aString += String::CreateFromInt32(nRow+1); + break; + + case formula::FormulaGrammar::CONV_XL_R1C1: + lcl_r1c1_append_r ( aString, nRow, bAbsolute, rDetails ); + lcl_r1c1_append_c ( aString, nCol, bAbsolute, rDetails ); + break; + } + + return aString; +} + + +String ScRefAddress::GetRefString( ScDocument* pDoc, SCTAB nActTab, + const ScAddress::Details& rDetails ) const +{ + if ( !pDoc ) + return EMPTY_STRING; + if ( Tab()+1 > pDoc->GetTableCount() ) + return ScGlobal::GetRscString( STR_NOREF_STR ); + + String aString; + USHORT nFlags = SCA_VALID; + if ( nActTab != Tab() ) + { + nFlags |= SCA_TAB_3D; + if ( !bRelTab ) + nFlags |= SCA_TAB_ABSOLUTE; + } + if ( !bRelCol ) + nFlags |= SCA_COL_ABSOLUTE; + if ( !bRelRow ) + nFlags |= SCA_ROW_ABSOLUTE; + + aAdr.Format( aString, nFlags, pDoc, rDetails ); + + return aString; +} + +//------------------------------------------------------------------------ + +void ScColToAlpha( rtl::OUStringBuffer& rBuf, SCCOL nCol ) +{ + if (nCol < 26*26) + { + if (nCol < 26) + rBuf.append( static_cast<sal_Unicode>( 'A' + + static_cast<sal_uInt16>(nCol))); + else + { + rBuf.append( static_cast<sal_Unicode>( 'A' + + (static_cast<sal_uInt16>(nCol) / 26) - 1)); + rBuf.append( static_cast<sal_Unicode>( 'A' + + (static_cast<sal_uInt16>(nCol) % 26))); + } + } + else + { + String aStr; + while (nCol >= 26) + { + SCCOL nC = nCol % 26; + aStr += static_cast<sal_Unicode>( 'A' + + static_cast<sal_uInt16>(nC)); + nCol = sal::static_int_cast<SCCOL>( nCol - nC ); + nCol = nCol / 26 - 1; + } + aStr += static_cast<sal_Unicode>( 'A' + + static_cast<sal_uInt16>(nCol)); + aStr.Reverse(); + rBuf.append( aStr); + } +} + + +bool AlphaToCol( SCCOL& rCol, const String& rStr) +{ + SCCOL nResult = 0; + xub_StrLen nStop = rStr.Len(); + xub_StrLen nPos = 0; + sal_Unicode c; + while (nResult <= MAXCOL && nPos < nStop && (c = rStr.GetChar( nPos)) != 0 && + CharClass::isAsciiAlpha(c)) + { + if (nPos > 0) + nResult = (nResult + 1) * 26; + nResult += ScGlobal::ToUpperAlpha(c) - 'A'; + ++nPos; + } + bool bOk = (ValidCol(nResult) && nPos > 0); + if (bOk) + rCol = nResult; + return bOk; +} diff --git a/sc/source/core/tool/adiasync.cxx b/sc/source/core/tool/adiasync.cxx new file mode 100644 index 000000000000..4f19a043a34d --- /dev/null +++ b/sc/source/core/tool/adiasync.cxx @@ -0,0 +1,187 @@ +/************************************************************************* + * + * 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/objsh.hxx> + +#include "adiasync.hxx" +#include "brdcst.hxx" +#include "global.hxx" +#include "document.hxx" +#include "sc.hrc" // FID_DATACHANGED +#include <osl/thread.h> + + +//------------------------------------------------------------------------ + +ScAddInAsyncs theAddInAsyncTbl; +static ScAddInAsync aSeekObj; + + +SV_IMPL_OP_PTRARR_SORT( ScAddInAsyncs, ScAddInAsyncPtr ); + +SV_IMPL_PTRARR_SORT( ScAddInDocs, ScAddInDocPtr ); + +extern "C" { +void CALLTYPE ScAddInAsyncCallBack( double& nHandle, void* pData ) +{ + ScAddInAsync::CallBack( ULONG( nHandle ), pData ); +} +} + + + +ScAddInAsync::ScAddInAsync() : + SvtBroadcaster(), + nHandle( 0 ) +{ // nur fuer aSeekObj ! +} + + + +ScAddInAsync::ScAddInAsync( ULONG nHandleP, USHORT nIndex, ScDocument* pDoc ) : + SvtBroadcaster(), + pStr( NULL ), + nHandle( nHandleP ), + bValid( FALSE ) +{ + pDocs = new ScAddInDocs( 1, 1 ); + pDocs->Insert( pDoc ); + pFuncData = (FuncData*)ScGlobal::GetFuncCollection()->At(nIndex); + eType = pFuncData->GetAsyncType(); + theAddInAsyncTbl.Insert( this ); +} + + + +ScAddInAsync::~ScAddInAsync() +{ + // aSeekObj hat das alles nicht, Handle 0 gibt es sonst nicht + if ( nHandle ) + { + // im dTor wg. theAddInAsyncTbl.DeleteAndDestroy in ScGlobal::Clear + pFuncData->Unadvice( (double)nHandle ); + if ( eType == PTR_STRING && pStr ) // mit Typvergleich wg. Union! + delete pStr; + delete pDocs; + } +} + + + +ScAddInAsync* ScAddInAsync::Get( ULONG nHandleP ) +{ + USHORT nPos; + ScAddInAsync* pRet = 0; + aSeekObj.nHandle = nHandleP; + if ( theAddInAsyncTbl.Seek_Entry( &aSeekObj, &nPos ) ) + pRet = theAddInAsyncTbl[ nPos ]; + aSeekObj.nHandle = 0; + return pRet; +} + + + +void ScAddInAsync::CallBack( ULONG nHandleP, void* pData ) +{ + ScAddInAsync* p; + if ( (p = Get( nHandleP )) == NULL ) + return; + // keiner mehr dran? Unadvice und weg damit + if ( !p->HasListeners() ) + { + // nicht im dTor wg. theAddInAsyncTbl.DeleteAndDestroy in ScGlobal::Clear + theAddInAsyncTbl.Remove( p ); + delete p; + return ; + } + switch ( p->eType ) + { + case PTR_DOUBLE : + p->nVal = *(double*)pData; + break; + case PTR_STRING : + if ( p->pStr ) + *p->pStr = String( (sal_Char*)pData, osl_getThreadTextEncoding() ); + else + p->pStr = new String( (sal_Char*)pData, osl_getThreadTextEncoding() ); + break; + default : + DBG_ERROR( "unbekannter AsyncType" ); + return; + } + p->bValid = TRUE; + p->Broadcast( ScHint( SC_HINT_DATACHANGED, ScAddress(), NULL ) ); + + const ScDocument** ppDoc = (const ScDocument**) p->pDocs->GetData(); + USHORT nCount = p->pDocs->Count(); + for ( USHORT j=0; j<nCount; j++, ppDoc++ ) + { + ScDocument* pDoc = (ScDocument*)*ppDoc; + pDoc->TrackFormulas(); + pDoc->GetDocumentShell()->Broadcast( SfxSimpleHint( FID_DATACHANGED ) ); + pDoc->ResetChanged( ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB) ); + } +} + + + +void ScAddInAsync::RemoveDocument( ScDocument* pDocumentP ) +{ + USHORT nPos = theAddInAsyncTbl.Count(); + if ( nPos ) + { + const ScAddInAsync** ppAsync = + (const ScAddInAsync**) theAddInAsyncTbl.GetData() + nPos - 1; + for ( ; nPos-- >0; ppAsync-- ) + { // rueckwaerts wg. Pointer-Aufrueckerei im Array + ScAddInDocs* p = ((ScAddInAsync*)*ppAsync)->pDocs; + USHORT nFoundPos; + if ( p->Seek_Entry( pDocumentP, &nFoundPos ) ) + { + p->Remove( nFoundPos ); + if ( p->Count() == 0 ) + { // dieses AddIn wird nicht mehr benutzt + ScAddInAsync* pAsync = (ScAddInAsync*)*ppAsync; + theAddInAsyncTbl.Remove( nPos ); + delete pAsync; + ppAsync = (const ScAddInAsync**) theAddInAsyncTbl.GetData() + + nPos; + } + } + } + } +} + + + diff --git a/sc/source/core/tool/appoptio.cxx b/sc/source/core/tool/appoptio.cxx new file mode 100644 index 000000000000..857efd972886 --- /dev/null +++ b/sc/source/core/tool/appoptio.cxx @@ -0,0 +1,745 @@ +/************************************************************************* + * + * 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 <vcl/svapp.hxx> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include "cfgids.hxx" +#include "appoptio.hxx" +#include "rechead.hxx" +#include "scresid.hxx" +#include "global.hxx" +#include "userlist.hxx" +#include "sc.hrc" +#include <formula/compiler.hrc> +#include "miscuno.hxx" + +using namespace utl; +using namespace rtl; +using namespace com::sun::star::uno; + +// STATIC DATA ----------------------------------------------------------- + +#define SC_VERSION ((USHORT)304) + +//======================================================================== +// ScAppOptions - Applikations-Optionen +//======================================================================== + +ScAppOptions::ScAppOptions() : pLRUList( NULL ) +{ + SetDefaults(); +} + +//------------------------------------------------------------------------ + +ScAppOptions::ScAppOptions( const ScAppOptions& rCpy ) : pLRUList( NULL ) +{ + *this = rCpy; +} + +//------------------------------------------------------------------------ + +ScAppOptions::~ScAppOptions() +{ + delete [] pLRUList; +} + +//------------------------------------------------------------------------ + +void ScAppOptions::SetDefaults() +{ + if ( ScOptionsUtil::IsMetricSystem() ) + eMetric = FUNIT_CM; // default for countries with metric system + else + eMetric = FUNIT_INCH; // default for others + + nZoom = 100; + eZoomType = SVX_ZOOM_PERCENT; + bSynchronizeZoom = TRUE; + nStatusFunc = SUBTOTAL_FUNC_SUM; + bAutoComplete = TRUE; + bDetectiveAuto = TRUE; + + delete [] pLRUList; + pLRUList = new USHORT[5]; // sinnvoll vorbelegen + pLRUList[0] = SC_OPCODE_SUM; + pLRUList[1] = SC_OPCODE_AVERAGE; + pLRUList[2] = SC_OPCODE_MIN; + pLRUList[3] = SC_OPCODE_MAX; + pLRUList[4] = SC_OPCODE_IF; + nLRUFuncCount = 5; + + nTrackContentColor = COL_TRANSPARENT; + nTrackInsertColor = COL_TRANSPARENT; + nTrackDeleteColor = COL_TRANSPARENT; + nTrackMoveColor = COL_TRANSPARENT; + eLinkMode = LM_ON_DEMAND; + + nDefaultObjectSizeWidth = 8000; + nDefaultObjectSizeHeight = 5000; + + mbShowSharedDocumentWarning = true; +} + +//------------------------------------------------------------------------ + +const ScAppOptions& ScAppOptions::operator=( const ScAppOptions& rCpy ) +{ + eMetric = rCpy.eMetric; + eZoomType = rCpy.eZoomType; + bSynchronizeZoom = rCpy.bSynchronizeZoom; + nZoom = rCpy.nZoom; + SetLRUFuncList( rCpy.pLRUList, rCpy.nLRUFuncCount ); + nStatusFunc = rCpy.nStatusFunc; + bAutoComplete = rCpy.bAutoComplete; + bDetectiveAuto = rCpy.bDetectiveAuto; + nTrackContentColor = rCpy.nTrackContentColor; + nTrackInsertColor = rCpy.nTrackInsertColor; + nTrackDeleteColor = rCpy.nTrackDeleteColor; + nTrackMoveColor = rCpy.nTrackMoveColor; + eLinkMode = rCpy.eLinkMode; + nDefaultObjectSizeWidth = rCpy.nDefaultObjectSizeWidth; + nDefaultObjectSizeHeight = rCpy.nDefaultObjectSizeHeight; + mbShowSharedDocumentWarning = rCpy.mbShowSharedDocumentWarning; + return *this; +} + +//------------------------------------------------------------------------ + +void ScAppOptions::SetLRUFuncList( const USHORT* pList, const USHORT nCount ) +{ + delete [] pLRUList; + + nLRUFuncCount = nCount; + + if ( nLRUFuncCount > 0 ) + { + pLRUList = new USHORT[nLRUFuncCount]; + + for ( USHORT i=0; i<nLRUFuncCount; i++ ) + pLRUList[i] = pList[i]; + } + else + pLRUList = NULL; +} + +//================================================================== +// Config Item containing app options +//================================================================== + +void lcl_SetLastFunctions( ScAppOptions& rOpt, const Any& rValue ) +{ + Sequence<sal_Int32> aSeq; + if ( rValue >>= aSeq ) + { + long nCount = aSeq.getLength(); + if ( nCount < USHRT_MAX ) + { + const sal_Int32* pArray = aSeq.getConstArray(); + USHORT* pUShorts = new USHORT[nCount]; + for (long i=0; i<nCount; i++) + pUShorts[i] = (USHORT) pArray[i]; + + rOpt.SetLRUFuncList( pUShorts, sal::static_int_cast<USHORT>(nCount) ); + + delete[] pUShorts; + } + } +} + +void lcl_GetLastFunctions( Any& rDest, const ScAppOptions& rOpt ) +{ + long nCount = rOpt.GetLRUFuncListCount(); + USHORT* pUShorts = rOpt.GetLRUFuncList(); + if ( nCount && pUShorts ) + { + Sequence<sal_Int32> aSeq( nCount ); + sal_Int32* pArray = aSeq.getArray(); + for (long i=0; i<nCount; i++) + pArray[i] = pUShorts[i]; + rDest <<= aSeq; + } + else + rDest <<= Sequence<sal_Int32>(0); // empty +} + +void lcl_SetSortList( const Any& rValue ) +{ + Sequence<OUString> aSeq; + if ( rValue >>= aSeq ) + { + long nCount = aSeq.getLength(); + const OUString* pArray = aSeq.getConstArray(); + ScUserList aList; + + // if setting is "default", keep default values from ScUserList ctor + //! mark "default" in a safe way + BOOL bDefault = ( nCount == 1 && + pArray[0].equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "NULL" ) ) ); + + if (!bDefault) + { + aList.FreeAll(); + + for (long i=0; i<nCount; i++) + { + ScUserListData* pNew = new ScUserListData( pArray[i] ); + if ( !aList.Insert(pNew) ) + delete pNew; + } + } + + ScGlobal::SetUserList( &aList ); + } +} + +void lcl_GetSortList( Any& rDest ) +{ + const ScUserList* pUserList = ScGlobal::GetUserList(); + if (pUserList) + { + long nCount = pUserList->GetCount(); + Sequence<OUString> aSeq( nCount ); + OUString* pArray = aSeq.getArray(); + for (long i=0; i<nCount; i++) + pArray[i] = (*pUserList)[sal::static_int_cast<USHORT>(i)]->GetString(); + rDest <<= aSeq; + } + else + rDest <<= Sequence<OUString>(0); // empty +} + +//------------------------------------------------------------------ + +#define CFGPATH_LAYOUT "Office.Calc/Layout" + +#define SCLAYOUTOPT_MEASURE 0 +#define SCLAYOUTOPT_STATUSBAR 1 +#define SCLAYOUTOPT_ZOOMVAL 2 +#define SCLAYOUTOPT_ZOOMTYPE 3 +#define SCLAYOUTOPT_SYNCZOOM 4 +#define SCLAYOUTOPT_COUNT 5 + +#define CFGPATH_INPUT "Office.Calc/Input" + +#define SCINPUTOPT_LASTFUNCS 0 +#define SCINPUTOPT_AUTOINPUT 1 +#define SCINPUTOPT_DET_AUTO 2 +#define SCINPUTOPT_COUNT 3 + +#define CFGPATH_REVISION "Office.Calc/Revision/Color" + +#define SCREVISOPT_CHANGE 0 +#define SCREVISOPT_INSERTION 1 +#define SCREVISOPT_DELETION 2 +#define SCREVISOPT_MOVEDENTRY 3 +#define SCREVISOPT_COUNT 4 + +#define CFGPATH_CONTENT "Office.Calc/Content/Update" + +#define SCCONTENTOPT_LINK 0 +#define SCCONTENTOPT_COUNT 1 + +#define CFGPATH_SORTLIST "Office.Calc/SortList" + +#define SCSORTLISTOPT_LIST 0 +#define SCSORTLISTOPT_COUNT 1 + +#define CFGPATH_MISC "Office.Calc/Misc" + +#define SCMISCOPT_DEFOBJWIDTH 0 +#define SCMISCOPT_DEFOBJHEIGHT 1 +#define SCMISCOPT_SHOWSHAREDDOCWARN 2 +#define SCMISCOPT_COUNT 3 + + +Sequence<OUString> ScAppCfg::GetLayoutPropertyNames() +{ + static const char* aPropNames[] = + { + "Other/MeasureUnit/NonMetric", // SCLAYOUTOPT_MEASURE + "Other/StatusbarFunction", // SCLAYOUTOPT_STATUSBAR + "Zoom/Value", // SCLAYOUTOPT_ZOOMVAL + "Zoom/Type", // SCLAYOUTOPT_ZOOMTYPE + "Zoom/Synchronize" // SCLAYOUTOPT_SYNCZOOM + }; + Sequence<OUString> aNames(SCLAYOUTOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCLAYOUTOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + // adjust for metric system + if (ScOptionsUtil::IsMetricSystem()) + pNames[SCLAYOUTOPT_MEASURE] = OUString::createFromAscii( "Other/MeasureUnit/Metric" ); + + return aNames; +} + +Sequence<OUString> ScAppCfg::GetInputPropertyNames() +{ + static const char* aPropNames[] = + { + "LastFunctions", // SCINPUTOPT_LASTFUNCS + "AutoInput", // SCINPUTOPT_AUTOINPUT + "DetectiveAuto" // SCINPUTOPT_DET_AUTO + }; + Sequence<OUString> aNames(SCINPUTOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCINPUTOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + return aNames; +} + +Sequence<OUString> ScAppCfg::GetRevisionPropertyNames() +{ + static const char* aPropNames[] = + { + "Change", // SCREVISOPT_CHANGE + "Insertion", // SCREVISOPT_INSERTION + "Deletion", // SCREVISOPT_DELETION + "MovedEntry" // SCREVISOPT_MOVEDENTRY + }; + Sequence<OUString> aNames(SCREVISOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCREVISOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + return aNames; +} + +Sequence<OUString> ScAppCfg::GetContentPropertyNames() +{ + static const char* aPropNames[] = + { + "Link" // SCCONTENTOPT_LINK + }; + Sequence<OUString> aNames(SCCONTENTOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCCONTENTOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + return aNames; +} + +Sequence<OUString> ScAppCfg::GetSortListPropertyNames() +{ + static const char* aPropNames[] = + { + "List" // SCSORTLISTOPT_LIST + }; + Sequence<OUString> aNames(SCSORTLISTOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCSORTLISTOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + return aNames; +} + +Sequence<OUString> ScAppCfg::GetMiscPropertyNames() +{ + static const char* aPropNames[] = + { + "DefaultObjectSize/Width", // SCMISCOPT_DEFOBJWIDTH + "DefaultObjectSize/Height", // SCMISCOPT_DEFOBJHEIGHT + "SharedDocument/ShowWarning" // SCMISCOPT_SHOWSHAREDDOCWARN + }; + Sequence<OUString> aNames(SCMISCOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCMISCOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + return aNames; +} + + +ScAppCfg::ScAppCfg() : + aLayoutItem( OUString::createFromAscii( CFGPATH_LAYOUT ) ), + aInputItem( OUString::createFromAscii( CFGPATH_INPUT ) ), + aRevisionItem( OUString::createFromAscii( CFGPATH_REVISION ) ), + aContentItem( OUString::createFromAscii( CFGPATH_CONTENT ) ), + aSortListItem( OUString::createFromAscii( CFGPATH_SORTLIST ) ), + aMiscItem( OUString::createFromAscii( CFGPATH_MISC ) ) +{ + sal_Int32 nIntVal = 0; + + Sequence<OUString> aNames; + Sequence<Any> aValues; + const Any* pValues = NULL; + + aNames = GetLayoutPropertyNames(); + aValues = aLayoutItem.GetProperties(aNames); + aLayoutItem.EnableNotification(aNames); + pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCLAYOUTOPT_MEASURE: + if (pValues[nProp] >>= nIntVal) SetAppMetric( (FieldUnit) nIntVal ); + break; + case SCLAYOUTOPT_STATUSBAR: + if (pValues[nProp] >>= nIntVal) SetStatusFunc( (USHORT) nIntVal ); + break; + case SCLAYOUTOPT_ZOOMVAL: + if (pValues[nProp] >>= nIntVal) SetZoom( (USHORT) nIntVal ); + break; + case SCLAYOUTOPT_ZOOMTYPE: + if (pValues[nProp] >>= nIntVal) SetZoomType( (SvxZoomType) nIntVal ); + break; + case SCLAYOUTOPT_SYNCZOOM: + SetSynchronizeZoom( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + } + } + } + } + aLayoutItem.SetCommitLink( LINK( this, ScAppCfg, LayoutCommitHdl ) ); + + aNames = GetInputPropertyNames(); + aValues = aInputItem.GetProperties(aNames); + aInputItem.EnableNotification(aNames); + pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCINPUTOPT_LASTFUNCS: + lcl_SetLastFunctions( *this, pValues[nProp] ); + break; + case SCINPUTOPT_AUTOINPUT: + SetAutoComplete( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCINPUTOPT_DET_AUTO: + SetDetectiveAuto( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + } + } + } + } + aInputItem.SetCommitLink( LINK( this, ScAppCfg, InputCommitHdl ) ); + + aNames = GetRevisionPropertyNames(); + aValues = aRevisionItem.GetProperties(aNames); + aRevisionItem.EnableNotification(aNames); + pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCREVISOPT_CHANGE: + if (pValues[nProp] >>= nIntVal) SetTrackContentColor( (sal_uInt32) nIntVal ); + break; + case SCREVISOPT_INSERTION: + if (pValues[nProp] >>= nIntVal) SetTrackInsertColor( (sal_uInt32) nIntVal ); + break; + case SCREVISOPT_DELETION: + if (pValues[nProp] >>= nIntVal) SetTrackDeleteColor( (sal_uInt32) nIntVal ); + break; + case SCREVISOPT_MOVEDENTRY: + if (pValues[nProp] >>= nIntVal) SetTrackMoveColor( (sal_uInt32) nIntVal ); + break; + } + } + } + } + aRevisionItem.SetCommitLink( LINK( this, ScAppCfg, RevisionCommitHdl ) ); + + aNames = GetContentPropertyNames(); + aValues = aContentItem.GetProperties(aNames); + aContentItem.EnableNotification(aNames); + pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCCONTENTOPT_LINK: + if (pValues[nProp] >>= nIntVal) SetLinkMode( (ScLkUpdMode) nIntVal ); + break; + } + } + } + } + aContentItem.SetCommitLink( LINK( this, ScAppCfg, ContentCommitHdl ) ); + + aNames = GetSortListPropertyNames(); + aValues = aSortListItem.GetProperties(aNames); + aSortListItem.EnableNotification(aNames); + pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCSORTLISTOPT_LIST: + lcl_SetSortList( pValues[nProp] ); + break; + } + } + } + } + aSortListItem.SetCommitLink( LINK( this, ScAppCfg, SortListCommitHdl ) ); + + aNames = GetMiscPropertyNames(); + aValues = aMiscItem.GetProperties(aNames); + aMiscItem.EnableNotification(aNames); + pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCMISCOPT_DEFOBJWIDTH: + if (pValues[nProp] >>= nIntVal) SetDefaultObjectSizeWidth( nIntVal ); + break; + case SCMISCOPT_DEFOBJHEIGHT: + if (pValues[nProp] >>= nIntVal) SetDefaultObjectSizeHeight( nIntVal ); + break; + case SCMISCOPT_SHOWSHAREDDOCWARN: + SetShowSharedDocumentWarning( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + } + } + } + } + aMiscItem.SetCommitLink( LINK( this, ScAppCfg, MiscCommitHdl ) ); +} + +IMPL_LINK( ScAppCfg, LayoutCommitHdl, void *, EMPTYARG ) +{ + Sequence<OUString> aNames = GetLayoutPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCLAYOUTOPT_MEASURE: + pValues[nProp] <<= (sal_Int32) GetAppMetric(); + break; + case SCLAYOUTOPT_STATUSBAR: + pValues[nProp] <<= (sal_Int32) GetStatusFunc(); + break; + case SCLAYOUTOPT_ZOOMVAL: + pValues[nProp] <<= (sal_Int32) GetZoom(); + break; + case SCLAYOUTOPT_ZOOMTYPE: + pValues[nProp] <<= (sal_Int32) GetZoomType(); + break; + case SCLAYOUTOPT_SYNCZOOM: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetSynchronizeZoom() ); + break; + } + } + aLayoutItem.PutProperties(aNames, aValues); + + return 0; +} + +IMPL_LINK( ScAppCfg, InputCommitHdl, void *, EMPTYARG ) +{ + Sequence<OUString> aNames = GetInputPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCINPUTOPT_LASTFUNCS: + lcl_GetLastFunctions( pValues[nProp], *this ); + break; + case SCINPUTOPT_AUTOINPUT: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetAutoComplete() ); + break; + case SCINPUTOPT_DET_AUTO: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetDetectiveAuto() ); + break; + } + } + aInputItem.PutProperties(aNames, aValues); + + return 0; +} + +IMPL_LINK( ScAppCfg, RevisionCommitHdl, void *, EMPTYARG ) +{ + Sequence<OUString> aNames = GetRevisionPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCREVISOPT_CHANGE: + pValues[nProp] <<= (sal_Int32) GetTrackContentColor(); + break; + case SCREVISOPT_INSERTION: + pValues[nProp] <<= (sal_Int32) GetTrackInsertColor(); + break; + case SCREVISOPT_DELETION: + pValues[nProp] <<= (sal_Int32) GetTrackDeleteColor(); + break; + case SCREVISOPT_MOVEDENTRY: + pValues[nProp] <<= (sal_Int32) GetTrackMoveColor(); + break; + } + } + aRevisionItem.PutProperties(aNames, aValues); + + return 0; +} + +IMPL_LINK( ScAppCfg, ContentCommitHdl, void *, EMPTYARG ) +{ + Sequence<OUString> aNames = GetContentPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCCONTENTOPT_LINK: + pValues[nProp] <<= (sal_Int32) GetLinkMode(); + break; + } + } + aContentItem.PutProperties(aNames, aValues); + + return 0; +} + +IMPL_LINK( ScAppCfg, SortListCommitHdl, void *, EMPTYARG ) +{ + Sequence<OUString> aNames = GetSortListPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCSORTLISTOPT_LIST: + lcl_GetSortList( pValues[nProp] ); + break; + } + } + aSortListItem.PutProperties(aNames, aValues); + + return 0; +} + +IMPL_LINK( ScAppCfg, MiscCommitHdl, void *, EMPTYARG ) +{ + Sequence<OUString> aNames = GetMiscPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCMISCOPT_DEFOBJWIDTH: + pValues[nProp] <<= (sal_Int32) GetDefaultObjectSizeWidth(); + break; + case SCMISCOPT_DEFOBJHEIGHT: + pValues[nProp] <<= (sal_Int32) GetDefaultObjectSizeHeight(); + break; + case SCMISCOPT_SHOWSHAREDDOCWARN: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetShowSharedDocumentWarning() ); + break; + } + } + aMiscItem.PutProperties(aNames, aValues); + + return 0; +} + +void ScAppCfg::SetOptions( const ScAppOptions& rNew ) +{ + *(ScAppOptions*)this = rNew; + OptionsChanged(); +} + +void ScAppCfg::OptionsChanged() +{ + aLayoutItem.SetModified(); + aInputItem.SetModified(); + aRevisionItem.SetModified(); + aContentItem.SetModified(); + aSortListItem.SetModified(); + aMiscItem.SetModified(); +} + + diff --git a/sc/source/core/tool/autoform.cxx b/sc/source/core/tool/autoform.cxx new file mode 100644 index 000000000000..5a7f117e75d9 --- /dev/null +++ b/sc/source/core/tool/autoform.cxx @@ -0,0 +1,1200 @@ +/************************************************************************* + * + * 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" + + + +#define READ_OLDVERS + +#include "autoform.hxx" + +#include <sfx2/app.hxx> +#include <sfx2/docfile.hxx> +#include <unotools/pathoptions.hxx> +#include <svl/itemset.hxx> +#include <tools/shl.hxx> +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> +#include <svx/dialmgr.hxx> +#include <svx/dialogs.hrc> +#include <editeng/langitem.hxx> +#include <tools/urlobj.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <tools/tenccvt.hxx> + +#include "globstr.hrc" +#include "document.hxx" + +//------------------------------------------------------------------------ + +const sal_Char *linker_dummy = ""; + +// Standard-Name ist jetzt STR_STYLENAME_STANDARD (wie Vorlagen) +//static const sal_Char __FAR_DATA cStandardName[] = "Standard"; + +static const sal_Char __FAR_DATA sAutoTblFmtName[] = "autotbl.fmt"; + +// bis SO5PF +const USHORT AUTOFORMAT_ID_X = 9501; +const USHORT AUTOFORMAT_ID_358 = 9601; +const USHORT AUTOFORMAT_DATA_ID_X = 9502; + +// ab SO5 +//! in nachfolgenden Versionen muss der Betrag dieser IDs groesser sein +const USHORT AUTOFORMAT_ID_504 = 9801; +const USHORT AUTOFORMAT_DATA_ID_504 = 9802; + +const USHORT AUTOFORMAT_ID_552 = 9901; +const USHORT AUTOFORMAT_DATA_ID_552 = 9902; + +// --- from 641 on: CJK and CTL font settings +const USHORT AUTOFORMAT_ID_641 = 10001; +const USHORT AUTOFORMAT_DATA_ID_641 = 10002; + +// --- from 680/dr14 on: diagonal frame lines +const USHORT AUTOFORMAT_ID_680DR14 = 10011; +const USHORT AUTOFORMAT_DATA_ID_680DR14 = 10012; + +// --- from 680/dr25 on: #21549# store strings as UTF-8 +const USHORT AUTOFORMAT_ID_680DR25 = 10021; +const USHORT AUTOFORMAT_DATA_ID_680DR25 = 10022; + +// --- from DEV300/overline2 on: #5991# overline support +const USHORT AUTOFORMAT_ID_300OVRLN = 10031; +const USHORT AUTOFORMAT_DATA_ID_300OVRLN = 10032; + +// aktuelle Version +const USHORT AUTOFORMAT_ID = AUTOFORMAT_ID_300OVRLN; +const USHORT AUTOFORMAT_DATA_ID = AUTOFORMAT_DATA_ID_300OVRLN; + + +#ifdef READ_OLDVERS +const USHORT AUTOFORMAT_OLD_ID_OLD = 4201; +const USHORT AUTOFORMAT_OLD_DATA_ID = 4202; +const USHORT AUTOFORMAT_OLD_ID_NEW = 4203; +#endif + + +// Struct mit Versionsnummern der Items + +struct ScAfVersions +{ +public: + USHORT nFontVersion; + USHORT nFontHeightVersion; + USHORT nWeightVersion; + USHORT nPostureVersion; + USHORT nUnderlineVersion; + USHORT nOverlineVersion; + USHORT nCrossedOutVersion; + USHORT nContourVersion; + USHORT nShadowedVersion; + USHORT nColorVersion; + USHORT nBoxVersion; + USHORT nLineVersion; + USHORT nBrushVersion; + + USHORT nAdjustVersion; + + USHORT nHorJustifyVersion; + USHORT nVerJustifyVersion; + USHORT nOrientationVersion; + USHORT nMarginVersion; + USHORT nBoolVersion; + USHORT nInt32Version; + USHORT nRotateModeVersion; + + USHORT nNumFmtVersion; + + ScAfVersions(); + void Load( SvStream& rStream, USHORT nVer ); + static void Write(SvStream& rStream); +}; + +ScAfVersions::ScAfVersions() : + nFontVersion(0), + nFontHeightVersion(0), + nWeightVersion(0), + nPostureVersion(0), + nUnderlineVersion(0), + nOverlineVersion(0), + nCrossedOutVersion(0), + nContourVersion(0), + nShadowedVersion(0), + nColorVersion(0), + nBoxVersion(0), + nLineVersion(0), + nBrushVersion(0), + nAdjustVersion(0), + nHorJustifyVersion(0), + nVerJustifyVersion(0), + nOrientationVersion(0), + nMarginVersion(0), + nBoolVersion(0), + nInt32Version(0), + nRotateModeVersion(0), + nNumFmtVersion(0) +{ +} + +void ScAfVersions::Load( SvStream& rStream, USHORT nVer ) +{ + rStream >> nFontVersion; + rStream >> nFontHeightVersion; + rStream >> nWeightVersion; + rStream >> nPostureVersion; + rStream >> nUnderlineVersion; + if ( nVer >= AUTOFORMAT_ID_300OVRLN ) + rStream >> nOverlineVersion; + rStream >> nCrossedOutVersion; + rStream >> nContourVersion; + rStream >> nShadowedVersion; + rStream >> nColorVersion; + rStream >> nBoxVersion; + if ( nVer >= AUTOFORMAT_ID_680DR14 ) + rStream >> nLineVersion; + rStream >> nBrushVersion; + rStream >> nAdjustVersion; + rStream >> nHorJustifyVersion; + rStream >> nVerJustifyVersion; + rStream >> nOrientationVersion; + rStream >> nMarginVersion; + rStream >> nBoolVersion; + if ( nVer >= AUTOFORMAT_ID_504 ) + { + rStream >> nInt32Version; + rStream >> nRotateModeVersion; + } + rStream >> nNumFmtVersion; +} + +void ScAfVersions::Write(SvStream& rStream) +{ + rStream << SvxFontItem(ATTR_FONT).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxFontHeightItem(240, 100, ATTR_FONT_HEIGHT).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxWeightItem(WEIGHT_NORMAL, ATTR_FONT_WEIGHT).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxPostureItem(ITALIC_NONE, ATTR_FONT_POSTURE).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxUnderlineItem(UNDERLINE_NONE, ATTR_FONT_UNDERLINE).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxOverlineItem(UNDERLINE_NONE, ATTR_FONT_OVERLINE).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxCrossedOutItem(STRIKEOUT_NONE, ATTR_FONT_CROSSEDOUT).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxContourItem(sal_False, ATTR_FONT_CONTOUR).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxShadowedItem(sal_False, ATTR_FONT_SHADOWED).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxColorItem(ATTR_FONT_COLOR).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxBoxItem(ATTR_BORDER).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxLineItem(SID_FRAME_LINESTYLE).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxBrushItem(ATTR_BACKGROUND).GetVersion(SOFFICE_FILEFORMAT_40); + + rStream << SvxAdjustItem(SVX_ADJUST_LEFT, 0).GetVersion(SOFFICE_FILEFORMAT_40); + + rStream << SvxHorJustifyItem(SVX_HOR_JUSTIFY_STANDARD, ATTR_HOR_JUSTIFY).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxVerJustifyItem(SVX_VER_JUSTIFY_STANDARD, ATTR_VER_JUSTIFY).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxOrientationItem(SVX_ORIENTATION_STANDARD, 0).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxMarginItem(ATTR_MARGIN).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SfxBoolItem(ATTR_LINEBREAK).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SfxInt32Item(ATTR_ROTATE_VALUE).GetVersion(SOFFICE_FILEFORMAT_40); + rStream << SvxRotateModeItem(SVX_ROTATE_MODE_STANDARD,0).GetVersion(SOFFICE_FILEFORMAT_40); + + rStream << (USHORT)0; // Num-Format +} + +// --------------------------------------------------------------------------- + +ScAutoFormatDataField::ScAutoFormatDataField() : + aFont( ATTR_FONT ), + aHeight( 240, 100, ATTR_FONT_HEIGHT ), + aWeight( WEIGHT_NORMAL, ATTR_FONT_WEIGHT ), + aPosture( ITALIC_NONE, ATTR_FONT_POSTURE ), + + aCJKFont( ATTR_CJK_FONT ), + aCJKHeight( 240, 100, ATTR_CJK_FONT_HEIGHT ), + aCJKWeight( WEIGHT_NORMAL, ATTR_CJK_FONT_WEIGHT ), + aCJKPosture( ITALIC_NONE, ATTR_CJK_FONT_POSTURE ), + + aCTLFont( ATTR_CTL_FONT ), + aCTLHeight( 240, 100, ATTR_CTL_FONT_HEIGHT ), + aCTLWeight( WEIGHT_NORMAL, ATTR_CTL_FONT_WEIGHT ), + aCTLPosture( ITALIC_NONE, ATTR_CTL_FONT_POSTURE ), + + aUnderline( UNDERLINE_NONE,ATTR_FONT_UNDERLINE ), + aOverline( UNDERLINE_NONE,ATTR_FONT_OVERLINE ), + aCrossedOut( STRIKEOUT_NONE, ATTR_FONT_CROSSEDOUT ), + aContour( sal_False, ATTR_FONT_CONTOUR ), + aShadowed( sal_False, ATTR_FONT_SHADOWED ), + aColor( ATTR_FONT_COLOR ), + aBox( ATTR_BORDER ), + aTLBR( ATTR_BORDER_TLBR ), + aBLTR( ATTR_BORDER_BLTR ), + aBackground( ATTR_BACKGROUND ), + aAdjust( SVX_ADJUST_LEFT, 0 ), + aHorJustify( SVX_HOR_JUSTIFY_STANDARD, ATTR_HOR_JUSTIFY ), + aVerJustify( SVX_VER_JUSTIFY_STANDARD, ATTR_VER_JUSTIFY ), + aMargin( ATTR_MARGIN ), + aLinebreak( ATTR_LINEBREAK ), + aRotateAngle( ATTR_ROTATE_VALUE ), + aRotateMode( SVX_ROTATE_MODE_STANDARD, ATTR_ROTATE_MODE ) +{ +} + +ScAutoFormatDataField::ScAutoFormatDataField( const ScAutoFormatDataField& rCopy ) : + aFont( rCopy.aFont ), + aHeight( rCopy.aHeight ), + aWeight( rCopy.aWeight ), + aPosture( rCopy.aPosture ), + aCJKFont( rCopy.aCJKFont ), + aCJKHeight( rCopy.aCJKHeight ), + aCJKWeight( rCopy.aCJKWeight ), + aCJKPosture( rCopy.aCJKPosture ), + aCTLFont( rCopy.aCTLFont ), + aCTLHeight( rCopy.aCTLHeight ), + aCTLWeight( rCopy.aCTLWeight ), + aCTLPosture( rCopy.aCTLPosture ), + aUnderline( rCopy.aUnderline ), + aOverline( rCopy.aOverline ), + aCrossedOut( rCopy.aCrossedOut ), + aContour( rCopy.aContour ), + aShadowed( rCopy.aShadowed ), + aColor( rCopy.aColor ), + aBox( rCopy.aBox ), + aTLBR( rCopy.aTLBR ), + aBLTR( rCopy.aBLTR ), + aBackground( rCopy.aBackground ), + aAdjust( rCopy.aAdjust ), + aHorJustify( rCopy.aHorJustify ), + aVerJustify( rCopy.aVerJustify ), + aStacked( rCopy.aStacked ), + aMargin( rCopy.aMargin ), + aLinebreak( rCopy.aLinebreak ), + aRotateAngle( rCopy.aRotateAngle ), + aRotateMode( rCopy.aRotateMode ), + aNumFormat( rCopy.aNumFormat ) +{ +} + +ScAutoFormatDataField::~ScAutoFormatDataField() +{ +} + +void ScAutoFormatDataField::SetAdjust( const SvxAdjustItem& rAdjust ) +{ + aAdjust.SetAdjust( rAdjust.GetAdjust() ); + aAdjust.SetOneWord( rAdjust.GetOneWord() ); + aAdjust.SetLastBlock( rAdjust.GetLastBlock() ); +} + +#define READ( aItem, ItemType, nVers ) \ + pNew = aItem.Create( rStream, nVers ); \ + aItem = *(ItemType*)pNew; \ + delete pNew; + +BOOL ScAutoFormatDataField::Load( SvStream& rStream, const ScAfVersions& rVersions, USHORT nVer ) +{ + SfxPoolItem* pNew; + SvxOrientationItem aOrientation( SVX_ORIENTATION_STANDARD, 0 ); + + READ( aFont, SvxFontItem, rVersions.nFontVersion) + READ( aHeight, SvxFontHeightItem, rVersions.nFontHeightVersion) + READ( aWeight, SvxWeightItem, rVersions.nWeightVersion) + READ( aPosture, SvxPostureItem, rVersions.nPostureVersion) + // --- from 641 on: CJK and CTL font settings + if( AUTOFORMAT_DATA_ID_641 <= nVer ) + { + READ( aCJKFont, SvxFontItem, rVersions.nFontVersion) + READ( aCJKHeight, SvxFontHeightItem, rVersions.nFontHeightVersion) + READ( aCJKWeight, SvxWeightItem, rVersions.nWeightVersion) + READ( aCJKPosture, SvxPostureItem, rVersions.nPostureVersion) + READ( aCTLFont, SvxFontItem, rVersions.nFontVersion) + READ( aCTLHeight, SvxFontHeightItem, rVersions.nFontHeightVersion) + READ( aCTLWeight, SvxWeightItem, rVersions.nWeightVersion) + READ( aCTLPosture, SvxPostureItem, rVersions.nPostureVersion) + } + READ( aUnderline, SvxUnderlineItem, rVersions.nUnderlineVersion) + if ( nVer >= AUTOFORMAT_DATA_ID_300OVRLN ) + { + READ( aOverline, SvxOverlineItem, rVersions.nOverlineVersion) + } + READ( aCrossedOut, SvxCrossedOutItem, rVersions.nCrossedOutVersion) + READ( aContour, SvxContourItem, rVersions.nContourVersion) + READ( aShadowed, SvxShadowedItem, rVersions.nShadowedVersion) + READ( aColor, SvxColorItem, rVersions.nColorVersion) + READ( aBox, SvxBoxItem, rVersions.nBoxVersion) + + // --- from 680/dr14 on: diagonal frame lines + if( AUTOFORMAT_DATA_ID_680DR14 <= nVer ) + { + READ( aTLBR, SvxLineItem, rVersions.nLineVersion) + READ( aBLTR, SvxLineItem, rVersions.nLineVersion) + } + + READ( aBackground, SvxBrushItem, rVersions.nBrushVersion) + + pNew = aAdjust.Create( rStream, rVersions.nAdjustVersion ); + SetAdjust( *(SvxAdjustItem*)pNew ); + delete pNew; + + READ( aHorJustify, SvxHorJustifyItem, rVersions.nHorJustifyVersion) + READ( aVerJustify, SvxVerJustifyItem, rVersions.nVerJustifyVersion) + READ( aOrientation, SvxOrientationItem, rVersions.nOrientationVersion) + READ( aMargin, SvxMarginItem, rVersions.nMarginVersion) + + pNew = aLinebreak.Create( rStream, rVersions.nBoolVersion ); + SetLinebreak( *(SfxBoolItem*)pNew ); + delete pNew; + + if ( nVer >= AUTOFORMAT_DATA_ID_504 ) + { + pNew = aRotateAngle.Create( rStream, rVersions.nInt32Version ); + SetRotateAngle( *(SfxInt32Item*)pNew ); + delete pNew; + pNew = aRotateMode.Create( rStream, rVersions.nRotateModeVersion ); + SetRotateMode( *(SvxRotateModeItem*)pNew ); + delete pNew; + } + + if( 0 == rVersions.nNumFmtVersion ) + { + // --- from 680/dr25 on: #21549# store strings as UTF-8 + CharSet eCharSet = (nVer >= AUTOFORMAT_ID_680DR25) ? RTL_TEXTENCODING_UTF8 : rStream.GetStreamCharSet(); + aNumFormat.Load( rStream, eCharSet ); + } + + // adjust charset in font + CharSet eSysSet = gsl_getSystemTextEncoding(); + CharSet eSrcSet = rStream.GetStreamCharSet(); + if( eSrcSet != eSysSet && aFont.GetCharSet() == eSrcSet ) + aFont.GetCharSet() = eSysSet; + + aStacked.SetValue( aOrientation.IsStacked() ); + aRotateAngle.SetValue( aOrientation.GetRotation( aRotateAngle.GetValue() ) ); + + return (rStream.GetError() == 0); +} + +#ifdef READ_OLDVERS +BOOL ScAutoFormatDataField::LoadOld( SvStream& rStream, const ScAfVersions& rVersions ) +{ + SfxPoolItem* pNew; + SvxOrientationItem aOrientation( SVX_ORIENTATION_STANDARD, 0 ); + + aNumFormat.Load(rStream, rStream.GetStreamCharSet()); + + READ( aFont, SvxFontItem, rVersions.nFontVersion) + READ( aHeight, SvxFontHeightItem, rVersions.nFontHeightVersion) + READ( aWeight, SvxWeightItem, rVersions.nWeightVersion) + READ( aPosture, SvxPostureItem, rVersions.nPostureVersion) + READ( aUnderline, SvxUnderlineItem, rVersions.nUnderlineVersion) + READ( aCrossedOut, SvxCrossedOutItem, rVersions.nCrossedOutVersion) + READ( aContour, SvxContourItem, rVersions.nContourVersion) + READ( aShadowed, SvxShadowedItem, rVersions.nShadowedVersion) + READ( aColor, SvxColorItem, rVersions.nColorVersion) + READ( aHorJustify, SvxHorJustifyItem, rVersions.nHorJustifyVersion) + READ( aVerJustify, SvxVerJustifyItem, rVersions.nVerJustifyVersion) + READ( aOrientation, SvxOrientationItem, rVersions.nOrientationVersion) + pNew = aLinebreak.Create( rStream, rVersions.nBoolVersion ); + SetLinebreak( *(SfxBoolItem*)pNew ); + delete pNew; + READ( aMargin, SvxMarginItem, rVersions.nMarginVersion) + READ( aBox, SvxBoxItem, rVersions.nBoxVersion) + READ( aBackground, SvxBrushItem, rVersions.nBrushVersion) + + aStacked.SetValue( aOrientation.IsStacked() ); + aRotateAngle.SetValue( aOrientation.GetRotation( aRotateAngle.GetValue() ) ); + + return (rStream.GetError() == 0); +} +#endif + +BOOL ScAutoFormatDataField::Save( SvStream& rStream ) +{ + SvxOrientationItem aOrientation( aRotateAngle.GetValue(), aStacked.GetValue(), 0 ); + + aFont.Store ( rStream, aFont.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aHeight.Store ( rStream, aHeight.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aWeight.Store ( rStream, aWeight.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aPosture.Store ( rStream, aPosture.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + // --- from 641 on: CJK and CTL font settings + aCJKFont.Store ( rStream, aCJKFont.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aCJKHeight.Store ( rStream, aCJKHeight.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aCJKWeight.Store ( rStream, aCJKWeight.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aCJKPosture.Store ( rStream, aCJKPosture.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aCTLFont.Store ( rStream, aCTLFont.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aCTLHeight.Store ( rStream, aCTLHeight.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aCTLWeight.Store ( rStream, aCTLWeight.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aCTLPosture.Store ( rStream, aCTLPosture.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + + aUnderline.Store ( rStream, aUnderline.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + // --- from DEV300/overline2 on: overline support + aOverline.Store ( rStream, aOverline.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aCrossedOut.Store ( rStream, aCrossedOut.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aContour.Store ( rStream, aContour.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aShadowed.Store ( rStream, aShadowed.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aColor.Store ( rStream, aColor.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aBox.Store ( rStream, aBox.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + + // --- from 680/dr14 on: diagonal frame lines + aTLBR.Store ( rStream, aTLBR.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aBLTR.Store ( rStream, aBLTR.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + + aBackground.Store ( rStream, aBackground.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + + aAdjust.Store ( rStream, aAdjust.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + + aHorJustify.Store ( rStream, aHorJustify.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aVerJustify.Store ( rStream, aVerJustify.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aOrientation.Store ( rStream, aOrientation.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aMargin.Store ( rStream, aMargin.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aLinebreak.Store ( rStream, aLinebreak.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + // Rotation ab SO5 + aRotateAngle.Store ( rStream, aRotateAngle.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + aRotateMode.Store ( rStream, aRotateMode.GetVersion( SOFFICE_FILEFORMAT_40 ) ); + + // --- from 680/dr25 on: #21549# store strings as UTF-8 + aNumFormat.Save( rStream, RTL_TEXTENCODING_UTF8 ); + + return (rStream.GetError() == 0); +} + + +// --------------------------------------------------------------------------- + +ScAutoFormatData::ScAutoFormatData() +{ + nStrResId = USHRT_MAX; + + bIncludeValueFormat = + bIncludeFont = + bIncludeJustify = + bIncludeFrame = + bIncludeBackground = + bIncludeWidthHeight = TRUE; + + ppDataField = new ScAutoFormatDataField*[ 16 ]; + for( USHORT nIndex = 0; nIndex < 16; ++nIndex ) + ppDataField[ nIndex ] = new ScAutoFormatDataField; +} + +ScAutoFormatData::ScAutoFormatData( const ScAutoFormatData& rData ) : + ScDataObject(), + aName( rData.aName ), + nStrResId( rData.nStrResId ), + bIncludeFont( rData.bIncludeFont ), + bIncludeJustify( rData.bIncludeJustify ), + bIncludeFrame( rData.bIncludeFrame ), + bIncludeBackground( rData.bIncludeBackground ), + bIncludeValueFormat( rData.bIncludeValueFormat ), + bIncludeWidthHeight( rData.bIncludeWidthHeight ) +{ + ppDataField = new ScAutoFormatDataField*[ 16 ]; + for( USHORT nIndex = 0; nIndex < 16; ++nIndex ) + ppDataField[ nIndex ] = new ScAutoFormatDataField( rData.GetField( nIndex ) ); +} + +ScAutoFormatData::~ScAutoFormatData() +{ + for( USHORT nIndex = 0; nIndex < 16; ++nIndex ) + delete ppDataField[ nIndex ]; + delete[] ppDataField; +} + +ScAutoFormatDataField& ScAutoFormatData::GetField( USHORT nIndex ) +{ + DBG_ASSERT( nIndex < 16, "ScAutoFormatData::GetField - illegal index" ); + DBG_ASSERT( ppDataField && ppDataField[ nIndex ], "ScAutoFormatData::GetField - no data" ); + return *ppDataField[ nIndex ]; +} + +const ScAutoFormatDataField& ScAutoFormatData::GetField( USHORT nIndex ) const +{ + DBG_ASSERT( nIndex < 16, "ScAutoFormatData::GetField - illegal index" ); + DBG_ASSERT( ppDataField && ppDataField[ nIndex ], "ScAutoFormatData::GetField - no data" ); + return *ppDataField[ nIndex ]; +} + +const SfxPoolItem* ScAutoFormatData::GetItem( USHORT nIndex, USHORT nWhich ) const +{ + const ScAutoFormatDataField& rField = GetField( nIndex ); + switch( nWhich ) + { + case ATTR_FONT: return &rField.GetFont(); + case ATTR_FONT_HEIGHT: return &rField.GetHeight(); + case ATTR_FONT_WEIGHT: return &rField.GetWeight(); + case ATTR_FONT_POSTURE: return &rField.GetPosture(); + case ATTR_CJK_FONT: return &rField.GetCJKFont(); + case ATTR_CJK_FONT_HEIGHT: return &rField.GetCJKHeight(); + case ATTR_CJK_FONT_WEIGHT: return &rField.GetCJKWeight(); + case ATTR_CJK_FONT_POSTURE: return &rField.GetCJKPosture(); + case ATTR_CTL_FONT: return &rField.GetCTLFont(); + case ATTR_CTL_FONT_HEIGHT: return &rField.GetCTLHeight(); + case ATTR_CTL_FONT_WEIGHT: return &rField.GetCTLWeight(); + case ATTR_CTL_FONT_POSTURE: return &rField.GetCTLPosture(); + case ATTR_FONT_UNDERLINE: return &rField.GetUnderline(); + case ATTR_FONT_OVERLINE: return &rField.GetOverline(); + case ATTR_FONT_CROSSEDOUT: return &rField.GetCrossedOut(); + case ATTR_FONT_CONTOUR: return &rField.GetContour(); + case ATTR_FONT_SHADOWED: return &rField.GetShadowed(); + case ATTR_FONT_COLOR: return &rField.GetColor(); + case ATTR_BORDER: return &rField.GetBox(); + case ATTR_BORDER_TLBR: return &rField.GetTLBR(); + case ATTR_BORDER_BLTR: return &rField.GetBLTR(); + case ATTR_BACKGROUND: return &rField.GetBackground(); + case ATTR_HOR_JUSTIFY: return &rField.GetHorJustify(); + case ATTR_VER_JUSTIFY: return &rField.GetVerJustify(); + case ATTR_STACKED: return &rField.GetStacked(); + case ATTR_MARGIN: return &rField.GetMargin(); + case ATTR_LINEBREAK: return &rField.GetLinebreak(); + case ATTR_ROTATE_VALUE: return &rField.GetRotateAngle(); + case ATTR_ROTATE_MODE: return &rField.GetRotateMode(); + } + return NULL; +} + +void ScAutoFormatData::PutItem( USHORT nIndex, const SfxPoolItem& rItem ) +{ + ScAutoFormatDataField& rField = GetField( nIndex ); + switch( rItem.Which() ) + { + case ATTR_FONT: rField.SetFont( (const SvxFontItem&)rItem ); break; + case ATTR_FONT_HEIGHT: rField.SetHeight( (const SvxFontHeightItem&)rItem ); break; + case ATTR_FONT_WEIGHT: rField.SetWeight( (const SvxWeightItem&)rItem ); break; + case ATTR_FONT_POSTURE: rField.SetPosture( (const SvxPostureItem&)rItem ); break; + case ATTR_CJK_FONT: rField.SetCJKFont( (const SvxFontItem&)rItem ); break; + case ATTR_CJK_FONT_HEIGHT: rField.SetCJKHeight( (const SvxFontHeightItem&)rItem ); break; + case ATTR_CJK_FONT_WEIGHT: rField.SetCJKWeight( (const SvxWeightItem&)rItem ); break; + case ATTR_CJK_FONT_POSTURE: rField.SetCJKPosture( (const SvxPostureItem&)rItem ); break; + case ATTR_CTL_FONT: rField.SetCTLFont( (const SvxFontItem&)rItem ); break; + case ATTR_CTL_FONT_HEIGHT: rField.SetCTLHeight( (const SvxFontHeightItem&)rItem ); break; + case ATTR_CTL_FONT_WEIGHT: rField.SetCTLWeight( (const SvxWeightItem&)rItem ); break; + case ATTR_CTL_FONT_POSTURE: rField.SetCTLPosture( (const SvxPostureItem&)rItem ); break; + case ATTR_FONT_UNDERLINE: rField.SetUnderline( (const SvxUnderlineItem&)rItem ); break; + case ATTR_FONT_OVERLINE: rField.SetOverline( (const SvxOverlineItem&)rItem ); break; + case ATTR_FONT_CROSSEDOUT: rField.SetCrossedOut( (const SvxCrossedOutItem&)rItem ); break; + case ATTR_FONT_CONTOUR: rField.SetContour( (const SvxContourItem&)rItem ); break; + case ATTR_FONT_SHADOWED: rField.SetShadowed( (const SvxShadowedItem&)rItem ); break; + case ATTR_FONT_COLOR: rField.SetColor( (const SvxColorItem&)rItem ); break; + case ATTR_BORDER: rField.SetBox( (const SvxBoxItem&)rItem ); break; + case ATTR_BORDER_TLBR: rField.SetTLBR( (const SvxLineItem&)rItem ); break; + case ATTR_BORDER_BLTR: rField.SetBLTR( (const SvxLineItem&)rItem ); break; + case ATTR_BACKGROUND: rField.SetBackground( (const SvxBrushItem&)rItem ); break; + case ATTR_HOR_JUSTIFY: rField.SetHorJustify( (const SvxHorJustifyItem&)rItem ); break; + case ATTR_VER_JUSTIFY: rField.SetVerJustify( (const SvxVerJustifyItem&)rItem ); break; + case ATTR_STACKED: rField.SetStacked( (const SfxBoolItem&)rItem ); break; + case ATTR_MARGIN: rField.SetMargin( (const SvxMarginItem&)rItem ); break; + case ATTR_LINEBREAK: rField.SetLinebreak( (const SfxBoolItem&)rItem ); break; + case ATTR_ROTATE_VALUE: rField.SetRotateAngle( (const SfxInt32Item&)rItem ); break; + case ATTR_ROTATE_MODE: rField.SetRotateMode( (const SvxRotateModeItem&)rItem ); break; + } +} + +void ScAutoFormatData::CopyItem( USHORT nToIndex, USHORT nFromIndex, USHORT nWhich ) +{ + const SfxPoolItem* pItem = GetItem( nFromIndex, nWhich ); + if( pItem ) + PutItem( nToIndex, *pItem ); +} + +const ScNumFormatAbbrev& ScAutoFormatData::GetNumFormat( USHORT nIndex ) const +{ + return GetField( nIndex ).GetNumFormat(); +} + +BOOL ScAutoFormatData::IsEqualData( USHORT nIndex1, USHORT nIndex2 ) const +{ + BOOL bEqual = TRUE; + const ScAutoFormatDataField& rField1 = GetField( nIndex1 ); + const ScAutoFormatDataField& rField2 = GetField( nIndex2 ); + + if( bIncludeValueFormat ) + { + bEqual = bEqual + && (rField1.GetNumFormat() == rField2.GetNumFormat()); + } + if( bIncludeFont ) + { + bEqual = bEqual + && (rField1.GetFont() == rField2.GetFont()) + && (rField1.GetHeight() == rField2.GetHeight()) + && (rField1.GetWeight() == rField2.GetWeight()) + && (rField1.GetPosture() == rField2.GetPosture()) + && (rField1.GetCJKFont() == rField2.GetCJKFont()) + && (rField1.GetCJKHeight() == rField2.GetCJKHeight()) + && (rField1.GetCJKWeight() == rField2.GetCJKWeight()) + && (rField1.GetCJKPosture() == rField2.GetCJKPosture()) + && (rField1.GetCTLFont() == rField2.GetCTLFont()) + && (rField1.GetCTLHeight() == rField2.GetCTLHeight()) + && (rField1.GetCTLWeight() == rField2.GetCTLWeight()) + && (rField1.GetCTLPosture() == rField2.GetCTLPosture()) + && (rField1.GetUnderline() == rField2.GetUnderline()) + && (rField1.GetOverline() == rField2.GetOverline()) + && (rField1.GetCrossedOut() == rField2.GetCrossedOut()) + && (rField1.GetContour() == rField2.GetContour()) + && (rField1.GetShadowed() == rField2.GetShadowed()) + && (rField1.GetColor() == rField2.GetColor()); + } + if( bIncludeJustify ) + { + bEqual = bEqual + && (rField1.GetHorJustify() == rField2.GetHorJustify()) + && (rField1.GetVerJustify() == rField2.GetVerJustify()) + && (rField1.GetStacked() == rField2.GetStacked()) + && (rField1.GetLinebreak() == rField2.GetLinebreak()) + && (rField1.GetMargin() == rField2.GetMargin()) + && (rField1.GetRotateAngle() == rField2.GetRotateAngle()) + && (rField1.GetRotateMode() == rField2.GetRotateMode()); + } + if( bIncludeFrame ) + { + bEqual = bEqual + && (rField1.GetBox() == rField2.GetBox()) + && (rField1.GetTLBR() == rField2.GetTLBR()) + && (rField1.GetBLTR() == rField2.GetBLTR()); + } + if( bIncludeBackground ) + { + bEqual = bEqual + && (rField1.GetBackground() == rField2.GetBackground()); + } + return bEqual; +} + +void ScAutoFormatData::FillToItemSet( USHORT nIndex, SfxItemSet& rItemSet, ScDocument& rDoc ) const +{ + const ScAutoFormatDataField& rField = GetField( nIndex ); + + if( bIncludeValueFormat ) + { + ScNumFormatAbbrev& rNumFormat = (ScNumFormatAbbrev&)rField.GetNumFormat(); + SfxUInt32Item aValueFormat( ATTR_VALUE_FORMAT, 0 ); + aValueFormat.SetValue( rNumFormat.GetFormatIndex( *rDoc.GetFormatTable() ) ); + rItemSet.Put( aValueFormat ); + rItemSet.Put( SvxLanguageItem( rNumFormat.GetLanguage(), ATTR_LANGUAGE_FORMAT ) ); + } + if( bIncludeFont ) + { + rItemSet.Put( rField.GetFont() ); + rItemSet.Put( rField.GetHeight() ); + rItemSet.Put( rField.GetWeight() ); + rItemSet.Put( rField.GetPosture() ); + // #103065# do not insert empty CJK font + const SvxFontItem& rCJKFont = rField.GetCJKFont(); + if( rCJKFont.GetStyleName().Len() ) + { + rItemSet.Put( rCJKFont ); + rItemSet.Put( rField.GetCJKHeight() ); + rItemSet.Put( rField.GetCJKWeight() ); + rItemSet.Put( rField.GetCJKPosture() ); + } + else + { + rItemSet.Put( rField.GetHeight(), ATTR_CJK_FONT_HEIGHT ); + rItemSet.Put( rField.GetWeight(), ATTR_CJK_FONT_WEIGHT ); + rItemSet.Put( rField.GetPosture(), ATTR_CJK_FONT_POSTURE ); + } + // #103065# do not insert empty CTL font + const SvxFontItem& rCTLFont = rField.GetCTLFont(); + if( rCTLFont.GetStyleName().Len() ) + { + rItemSet.Put( rCTLFont ); + rItemSet.Put( rField.GetCTLHeight() ); + rItemSet.Put( rField.GetCTLWeight() ); + rItemSet.Put( rField.GetCTLPosture() ); + } + else + { + rItemSet.Put( rField.GetHeight(), ATTR_CTL_FONT_HEIGHT ); + rItemSet.Put( rField.GetWeight(), ATTR_CTL_FONT_WEIGHT ); + rItemSet.Put( rField.GetPosture(), ATTR_CTL_FONT_POSTURE ); + } + rItemSet.Put( rField.GetUnderline() ); + rItemSet.Put( rField.GetOverline() ); + rItemSet.Put( rField.GetCrossedOut() ); + rItemSet.Put( rField.GetContour() ); + rItemSet.Put( rField.GetShadowed() ); + rItemSet.Put( rField.GetColor() ); + } + if( bIncludeJustify ) + { + rItemSet.Put( rField.GetHorJustify() ); + rItemSet.Put( rField.GetVerJustify() ); + rItemSet.Put( rField.GetStacked() ); + rItemSet.Put( rField.GetLinebreak() ); + rItemSet.Put( rField.GetMargin() ); + rItemSet.Put( rField.GetRotateAngle() ); + rItemSet.Put( rField.GetRotateMode() ); + } + if( bIncludeFrame ) + { + rItemSet.Put( rField.GetBox() ); + rItemSet.Put( rField.GetTLBR() ); + rItemSet.Put( rField.GetBLTR() ); + } + if( bIncludeBackground ) + rItemSet.Put( rField.GetBackground() ); +} + +void ScAutoFormatData::GetFromItemSet( USHORT nIndex, const SfxItemSet& rItemSet, const ScNumFormatAbbrev& rNumFormat ) +{ + ScAutoFormatDataField& rField = GetField( nIndex ); + + rField.SetNumFormat ( rNumFormat); + rField.SetFont ( (const SvxFontItem&) rItemSet.Get( ATTR_FONT ) ); + rField.SetHeight ( (const SvxFontHeightItem&) rItemSet.Get( ATTR_FONT_HEIGHT ) ); + rField.SetWeight ( (const SvxWeightItem&) rItemSet.Get( ATTR_FONT_WEIGHT ) ); + rField.SetPosture ( (const SvxPostureItem&) rItemSet.Get( ATTR_FONT_POSTURE ) ); + rField.SetCJKFont ( (const SvxFontItem&) rItemSet.Get( ATTR_CJK_FONT ) ); + rField.SetCJKHeight ( (const SvxFontHeightItem&) rItemSet.Get( ATTR_CJK_FONT_HEIGHT ) ); + rField.SetCJKWeight ( (const SvxWeightItem&) rItemSet.Get( ATTR_CJK_FONT_WEIGHT ) ); + rField.SetCJKPosture ( (const SvxPostureItem&) rItemSet.Get( ATTR_CJK_FONT_POSTURE ) ); + rField.SetCTLFont ( (const SvxFontItem&) rItemSet.Get( ATTR_CTL_FONT ) ); + rField.SetCTLHeight ( (const SvxFontHeightItem&) rItemSet.Get( ATTR_CTL_FONT_HEIGHT ) ); + rField.SetCTLWeight ( (const SvxWeightItem&) rItemSet.Get( ATTR_CTL_FONT_WEIGHT ) ); + rField.SetCTLPosture ( (const SvxPostureItem&) rItemSet.Get( ATTR_CTL_FONT_POSTURE ) ); + rField.SetUnderline ( (const SvxUnderlineItem&) rItemSet.Get( ATTR_FONT_UNDERLINE ) ); + rField.SetOverline ( (const SvxOverlineItem&) rItemSet.Get( ATTR_FONT_OVERLINE ) ); + rField.SetCrossedOut ( (const SvxCrossedOutItem&) rItemSet.Get( ATTR_FONT_CROSSEDOUT ) ); + rField.SetContour ( (const SvxContourItem&) rItemSet.Get( ATTR_FONT_CONTOUR ) ); + rField.SetShadowed ( (const SvxShadowedItem&) rItemSet.Get( ATTR_FONT_SHADOWED ) ); + rField.SetColor ( (const SvxColorItem&) rItemSet.Get( ATTR_FONT_COLOR ) ); + rField.SetTLBR ( (const SvxLineItem&) rItemSet.Get( ATTR_BORDER_TLBR ) ); + rField.SetBLTR ( (const SvxLineItem&) rItemSet.Get( ATTR_BORDER_BLTR ) ); + rField.SetHorJustify ( (const SvxHorJustifyItem&) rItemSet.Get( ATTR_HOR_JUSTIFY ) ); + rField.SetVerJustify ( (const SvxVerJustifyItem&) rItemSet.Get( ATTR_VER_JUSTIFY ) ); + rField.SetStacked ( (const SfxBoolItem&) rItemSet.Get( ATTR_STACKED ) ); + rField.SetLinebreak ( (const SfxBoolItem&) rItemSet.Get( ATTR_LINEBREAK ) ); + rField.SetMargin ( (const SvxMarginItem&) rItemSet.Get( ATTR_MARGIN ) ); + rField.SetBackground ( (const SvxBrushItem&) rItemSet.Get( ATTR_BACKGROUND ) ); + rField.SetRotateAngle ( (const SfxInt32Item&) rItemSet.Get( ATTR_ROTATE_VALUE ) ); + rField.SetRotateMode ( (const SvxRotateModeItem&) rItemSet.Get( ATTR_ROTATE_MODE ) ); +} + +BOOL ScAutoFormatData::Load( SvStream& rStream, const ScAfVersions& rVersions ) +{ + BOOL bRet = TRUE; + USHORT nVer = 0; + rStream >> nVer; + bRet = 0 == rStream.GetError(); + if( bRet && (nVer == AUTOFORMAT_DATA_ID_X || + (AUTOFORMAT_DATA_ID_504 <= nVer && nVer <= AUTOFORMAT_DATA_ID)) ) + { + // --- from 680/dr25 on: #21549# store strings as UTF-8 + CharSet eCharSet = (nVer >= AUTOFORMAT_ID_680DR25) ? RTL_TEXTENCODING_UTF8 : rStream.GetStreamCharSet(); + rStream.ReadByteString( aName, eCharSet ); + if( AUTOFORMAT_DATA_ID_552 <= nVer ) + { + rStream >> nStrResId; + USHORT nId = RID_SVXSTR_TBLAFMT_BEGIN + nStrResId; + if( RID_SVXSTR_TBLAFMT_BEGIN <= nId && + nId < RID_SVXSTR_TBLAFMT_END ) + { + aName = SVX_RESSTR( nId ); + } + else + nStrResId = USHRT_MAX; + } + + BOOL b; + rStream >> b; bIncludeFont = b; + rStream >> b; bIncludeJustify = b; + rStream >> b; bIncludeFrame = b; + rStream >> b; bIncludeBackground = b; + rStream >> b; bIncludeValueFormat = b; + rStream >> b; bIncludeWidthHeight = b; + + bRet = 0 == rStream.GetError(); + for( USHORT i = 0; bRet && i < 16; ++i ) + bRet = GetField( i ).Load( rStream, rVersions, nVer ); + } + else + bRet = FALSE; + return bRet; +} + +#ifdef READ_OLDVERS +BOOL ScAutoFormatData::LoadOld( SvStream& rStream, const ScAfVersions& rVersions ) +{ + BOOL bRet = TRUE; + USHORT nVal = 0; + rStream >> nVal; + bRet = (rStream.GetError() == 0); + if (bRet && (nVal == AUTOFORMAT_OLD_DATA_ID)) + { + rStream.ReadByteString( aName, rStream.GetStreamCharSet() ); + BOOL b; + rStream >> b; bIncludeFont = b; + rStream >> b; bIncludeJustify = b; + rStream >> b; bIncludeFrame = b; + rStream >> b; bIncludeBackground = b; + rStream >> b; bIncludeValueFormat = b; + rStream >> b; bIncludeWidthHeight = b; + + bRet = 0 == rStream.GetError(); + for (USHORT i=0; bRet && i < 16; i++) + bRet = GetField( i ).LoadOld( rStream, rVersions ); + } + else + bRet = FALSE; + return bRet; +} +#endif + +BOOL ScAutoFormatData::Save(SvStream& rStream) +{ + USHORT nVal = AUTOFORMAT_DATA_ID; + BOOL b; + rStream << nVal; + // --- from 680/dr25 on: #21549# store strings as UTF-8 + rStream.WriteByteString( aName, RTL_TEXTENCODING_UTF8 ); + +#if 0 + // This was an internal flag to allow creating AutoFormats with localized names + + if ( USHRT_MAX == nStrResId ) + { + String aIniVal( SFX_APP()->GetIniManager()->Get( + SFX_GROUP_WORKINGSET_IMPL, + String( RTL_CONSTASCII_USTRINGPARAM( "SaveTableAutoFmtNameId" )))); + if( 0 != aIniVal.ToInt32() ) + { + // check Name for ResId + for( USHORT nId = RID_SVXSTR_TBLAFMT_BEGIN; + RID_SVXSTR_TBLAFMT_END > nId; ++nId ) + { + String s( SVX_RES( nId ) ); + if( s == aName ) + { + nStrResId = nId - RID_SVXSTR_TBLAFMT_BEGIN; + break; + } + } + } + } +#endif + + rStream << nStrResId; + rStream << ( b = bIncludeFont ); + rStream << ( b = bIncludeJustify ); + rStream << ( b = bIncludeFrame ); + rStream << ( b = bIncludeBackground ); + rStream << ( b = bIncludeValueFormat ); + rStream << ( b = bIncludeWidthHeight ); + + BOOL bRet = 0 == rStream.GetError(); + for (USHORT i = 0; bRet && (i < 16); i++) + bRet = GetField( i ).Save( rStream ); + + return bRet; +} + +//--------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------- + +ScAutoFormat::ScAutoFormat(USHORT nLim, USHORT nDel, BOOL bDup): + ScSortedCollection (nLim, nDel, bDup), + bSaveLater (FALSE) +{ + // create default autoformat + ScAutoFormatData* pData = new ScAutoFormatData; + String aName(ScGlobal::GetRscString(STR_STYLENAME_STANDARD)); + pData->SetName(aName); + + // default font, default height + Font aStdFont = OutputDevice::GetDefaultFont( + DEFAULTFONT_LATIN_SPREADSHEET, LANGUAGE_ENGLISH_US, DEFAULTFONT_FLAGS_ONLYONE ); + SvxFontItem aFontItem( + aStdFont.GetFamily(), aStdFont.GetName(), aStdFont.GetStyleName(), + aStdFont.GetPitch(), aStdFont.GetCharSet(), ATTR_FONT ); + + aStdFont = OutputDevice::GetDefaultFont( + DEFAULTFONT_CJK_SPREADSHEET, LANGUAGE_ENGLISH_US, DEFAULTFONT_FLAGS_ONLYONE ); + SvxFontItem aCJKFontItem( + aStdFont.GetFamily(), aStdFont.GetName(), aStdFont.GetStyleName(), + aStdFont.GetPitch(), aStdFont.GetCharSet(), ATTR_CJK_FONT ); + + aStdFont = OutputDevice::GetDefaultFont( + DEFAULTFONT_CTL_SPREADSHEET, LANGUAGE_ENGLISH_US, DEFAULTFONT_FLAGS_ONLYONE ); + SvxFontItem aCTLFontItem( + aStdFont.GetFamily(), aStdFont.GetName(), aStdFont.GetStyleName(), + aStdFont.GetPitch(), aStdFont.GetCharSet(), ATTR_CTL_FONT ); + + SvxFontHeightItem aHeight( 200, 100, ATTR_FONT_HEIGHT ); // 10 pt; + + // black thin border + Color aBlack( COL_BLACK ); + SvxBorderLine aLine( &aBlack, DEF_LINE_WIDTH_0 ); + SvxBoxItem aBox( ATTR_BORDER ); + aBox.SetLine(&aLine, BOX_LINE_LEFT); + aBox.SetLine(&aLine, BOX_LINE_TOP); + aBox.SetLine(&aLine, BOX_LINE_RIGHT); + aBox.SetLine(&aLine, BOX_LINE_BOTTOM); + + Color aWhite(COL_WHITE); + Color aBlue(COL_BLUE); + SvxColorItem aWhiteText( aWhite, ATTR_FONT_COLOR ); + SvxColorItem aBlackText( aBlack, ATTR_FONT_COLOR ); + SvxBrushItem aBlueBack( aBlue, ATTR_BACKGROUND ); + SvxBrushItem aWhiteBack( aWhite, ATTR_BACKGROUND ); + SvxBrushItem aGray70Back( Color(0x4d, 0x4d, 0x4d), ATTR_BACKGROUND ); + SvxBrushItem aGray20Back( Color(0xcc, 0xcc, 0xcc), ATTR_BACKGROUND ); + + for (USHORT i=0; i<16; i++) + { + pData->PutItem( i, aBox ); + pData->PutItem( i, aFontItem ); + pData->PutItem( i, aCJKFontItem ); + pData->PutItem( i, aCTLFontItem ); + aHeight.SetWhich( ATTR_FONT_HEIGHT ); + pData->PutItem( i, aHeight ); + aHeight.SetWhich( ATTR_CJK_FONT_HEIGHT ); + pData->PutItem( i, aHeight ); + aHeight.SetWhich( ATTR_CTL_FONT_HEIGHT ); + pData->PutItem( i, aHeight ); + if (i<4) // top: white on blue + { + pData->PutItem( i, aWhiteText ); + pData->PutItem( i, aBlueBack ); + } + else if ( i%4 == 0 ) // left: white on gray70 + { + pData->PutItem( i, aWhiteText ); + pData->PutItem( i, aGray70Back ); + } + else if ( i%4 == 3 || i >= 12 ) // right and bottom: black on gray20 + { + pData->PutItem( i, aBlackText ); + pData->PutItem( i, aGray20Back ); + } + else // center: black on white + { + pData->PutItem( i, aBlackText ); + pData->PutItem( i, aWhiteBack ); + } + } + + Insert(pData); +} + +ScAutoFormat::ScAutoFormat(const ScAutoFormat& rAutoFormat) : + ScSortedCollection (rAutoFormat), + bSaveLater (FALSE) +{} + +ScAutoFormat::~ScAutoFormat() +{ + // Bei Aenderungen per StarOne wird nicht sofort gespeichert, sondern zuerst nur + // das SaveLater Flag gesetzt. Wenn das Flag noch gesetzt ist, jetzt speichern. + + if (bSaveLater) + Save(); +} + +void ScAutoFormat::SetSaveLater( BOOL bSet ) +{ + bSaveLater = bSet; +} + +short ScAutoFormat::Compare(ScDataObject* pKey1, ScDataObject* pKey2) const +{ + String aStr1; + String aStr2; + ((ScAutoFormatData*)pKey1)->GetName(aStr1); + ((ScAutoFormatData*)pKey2)->GetName(aStr2); + String aStrStandard = ScGlobal::GetRscString(STR_STYLENAME_STANDARD); + if ( ScGlobal::GetpTransliteration()->isEqual( aStr1, aStrStandard ) ) + return -1; + if ( ScGlobal::GetpTransliteration()->isEqual( aStr2, aStrStandard ) ) + return 1; + return (short) ScGlobal::GetpTransliteration()->compareString( aStr1, aStr2 ); +} + +BOOL ScAutoFormat::Load() +{ + BOOL bRet = TRUE; + + INetURLObject aURL; + SvtPathOptions aPathOpt; + aURL.SetSmartURL( aPathOpt.GetUserConfigPath() ); + aURL.setFinalSlash(); + aURL.Append( String( RTL_CONSTASCII_USTRINGPARAM( sAutoTblFmtName ) ) ); + + SfxMedium aMedium( aURL.GetMainURL(INetURLObject::NO_DECODE), STREAM_READ, TRUE ); + SvStream* pStream = aMedium.GetInStream(); + bRet = (pStream && pStream->GetError() == 0); + if (bRet) + { + SvStream& rStream = *pStream; + // Achtung hier muss ein allgemeiner Header gelesen werden + USHORT nVal = 0; + rStream >> nVal; + bRet = 0 == rStream.GetError(); + + ScAfVersions aVersions; + + if (bRet) + { + if( nVal == AUTOFORMAT_ID_358 || + (AUTOFORMAT_ID_504 <= nVal && nVal <= AUTOFORMAT_ID) ) + { + UINT16 nFileVers = SOFFICE_FILEFORMAT_40; + BYTE nChrSet, nCnt; + long nPos = rStream.Tell(); + rStream >> nCnt >> nChrSet; +// if( 4 <= nCnt ) +// rStream >> nFileVers; + if( rStream.Tell() != ULONG(nPos + nCnt) ) + { + DBG_ERRORFILE( "Der Header enthaelt mehr/neuere Daten" ); + rStream.Seek( nPos + nCnt ); + } + rStream.SetStreamCharSet( GetSOLoadTextEncoding( nChrSet, nFileVers ) ); + rStream.SetVersion( nFileVers ); + } + + if( nVal == AUTOFORMAT_ID_358 || nVal == AUTOFORMAT_ID_X || + (AUTOFORMAT_ID_504 <= nVal && nVal <= AUTOFORMAT_ID) ) + { + aVersions.Load( rStream, nVal ); // Item-Versionen + + ScAutoFormatData* pData; + USHORT nAnz = 0; + rStream >> nAnz; + bRet = (rStream.GetError() == 0); + for (USHORT i=0; bRet && (i < nAnz); i++) + { + pData = new ScAutoFormatData(); + bRet = pData->Load(rStream, aVersions); + Insert(pData); + } + } +#ifdef READ_OLDVERS + else + { + if( AUTOFORMAT_OLD_ID_NEW == nVal ) + { + // alte Version der Versions laden + rStream >> aVersions.nFontVersion; + rStream >> aVersions.nFontHeightVersion; + rStream >> aVersions.nWeightVersion; + rStream >> aVersions.nPostureVersion; + rStream >> aVersions.nUnderlineVersion; + rStream >> aVersions.nCrossedOutVersion; + rStream >> aVersions.nContourVersion; + rStream >> aVersions.nShadowedVersion; + rStream >> aVersions.nColorVersion; + rStream >> aVersions.nHorJustifyVersion; + rStream >> aVersions.nVerJustifyVersion; + rStream >> aVersions.nOrientationVersion; + rStream >> aVersions.nBoolVersion; + rStream >> aVersions.nMarginVersion; + rStream >> aVersions.nBoxVersion; + rStream >> aVersions.nBrushVersion; + } + if( AUTOFORMAT_OLD_ID_OLD == nVal || + AUTOFORMAT_OLD_ID_NEW == nVal ) + { + ScAutoFormatData* pData; + USHORT nAnz = 0; + rStream >> nAnz; + bRet = 0 == rStream.GetError(); + for( USHORT i=0; bRet && (i < nAnz); ++i ) + { + pData = new ScAutoFormatData(); + bRet = pData->LoadOld( rStream, aVersions ); + Insert( pData ); + } + } + else + bRet = FALSE; + } +#endif + } + } + bSaveLater = FALSE; + return bRet; +} + +BOOL ScAutoFormat::Save() +{ + BOOL bRet = TRUE; + + INetURLObject aURL; + SvtPathOptions aPathOpt; + aURL.SetSmartURL( aPathOpt.GetUserConfigPath() ); + aURL.setFinalSlash(); + aURL.Append( String( RTL_CONSTASCII_USTRINGPARAM( sAutoTblFmtName ) ) ); + + SfxMedium aMedium( aURL.GetMainURL(INetURLObject::NO_DECODE), STREAM_WRITE, TRUE ); + SvStream* pStream = aMedium.GetOutStream(); + bRet = (pStream && pStream->GetError() == 0); + if (bRet) + { + SvStream& rStream = *pStream; + rStream.SetVersion( SOFFICE_FILEFORMAT_40 ); + + // Achtung hier muss ein allgemeiner Header gespeichert werden + USHORT nVal = AUTOFORMAT_ID; + rStream << nVal + << (BYTE)2 // Anzahl von Zeichen des Headers incl. diesem + << (BYTE)::GetSOStoreTextEncoding( + gsl_getSystemTextEncoding(), sal::static_int_cast<USHORT>(rStream.GetVersion()) ); +// << (BYTE)4 // Anzahl von Zeichen des Headers incl. diesem +// << (BYTE)::GetStoreCharSet(::GetSystemCharSet()) +// << (UNIT16)SOFFICE_FILEFORMAT_NOW; + ScAfVersions::Write(rStream); // Item-Versionen + + bRet = (rStream.GetError() == 0); + //----------------------------------------------------------- + rStream << (USHORT)(nCount - 1); + bRet = (rStream.GetError() == 0); + for (USHORT i=1; bRet && (i < nCount); i++) + bRet = ((ScAutoFormatData*)pItems[i])->Save(rStream); + rStream.Flush(); + + aMedium.Commit(); + } + bSaveLater = FALSE; + return bRet; +} + +USHORT ScAutoFormat::FindIndexPerName( const String& rName ) const +{ + String aName; + + for( USHORT i=0; i<nCount ; i++ ) + { + ScAutoFormatData* pItem = (ScAutoFormatData*)pItems[i]; + pItem->GetName( aName ); + + if( aName == rName ) + return i; + } + + return 0; +} + + + + diff --git a/sc/source/core/tool/callform.cxx b/sc/source/core/tool/callform.cxx new file mode 100644 index 000000000000..e8e2b28f3f8e --- /dev/null +++ b/sc/source/core/tool/callform.cxx @@ -0,0 +1,469 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- +#include <vcl/svapp.hxx> +#include <osl/module.hxx> +#include <osl/file.hxx> +#include <unotools/transliterationwrapper.hxx> + +#include "callform.hxx" +#include "global.hxx" +#include "adiasync.hxx" + +//------------------------------------------------------------------------ + +extern "C" { + +typedef void (CALLTYPE* ExFuncPtr1)(void*); +typedef void (CALLTYPE* ExFuncPtr2)(void*, void*); +typedef void (CALLTYPE* ExFuncPtr3)(void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr4)(void*, void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr5)(void*, void*, void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr6)(void*, void*, void*, void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr7)(void*, void*, void*, void*, void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr8)(void*, void*, void*, void*, void*, void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr9)(void*, void*, void*, void*, void*, void*, void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr10)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr11)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr12)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr13)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr14)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr15)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*); +typedef void (CALLTYPE* ExFuncPtr16)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*); + +typedef void (CALLTYPE* GetFuncCountPtr)(USHORT& nCount); +typedef void (CALLTYPE* GetFuncDataPtr) + (USHORT& nNo, sal_Char* pFuncName, USHORT& nParamCount, ParamType* peType, sal_Char* pInternalName); + +typedef void (CALLTYPE* SetLanguagePtr)( USHORT& nLanguage ); +typedef void (CALLTYPE* GetParamDesc) + (USHORT& nNo, USHORT& nParam, sal_Char* pName, sal_Char* pDesc ); + +typedef void (CALLTYPE* IsAsync) ( USHORT& nNo, + ParamType* peType ); +typedef void (CALLTYPE* Advice) ( USHORT& nNo, + AdvData& pfCallback ); +typedef void (CALLTYPE* Unadvice)( double& nHandle ); + +typedef void (CALLTYPE* FARPROC) ( void ); + +} + +#if defined(OS2) && defined(BLC) +#define GETFUNCTIONCOUNT "_GetFunctionCount" +#define GETFUNCTIONDATA "_GetFunctionData" +#define SETLANGUAGE "_SetLanguage" +#define GETPARAMDESC "_GetParameterDescription" +#define ISASYNC "_IsAsync" +#define ADVICE "_Advice" +#define UNADVICE "_Unadvice" +#else // Pascal oder extern "C" +#define GETFUNCTIONCOUNT "GetFunctionCount" +#define GETFUNCTIONDATA "GetFunctionData" +#define SETLANGUAGE "SetLanguage" +#define GETPARAMDESC "GetParameterDescription" +#define ISASYNC "IsAsync" +#define ADVICE "Advice" +#define UNADVICE "Unadvice" +#endif + +#define LIBFUNCNAME( name ) \ + (String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( name ) )) + +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +FuncData::FuncData(const String& rIName) : + pModuleData (NULL), + aInternalName (rIName), +// aFuncName (""), + nNumber (0), + nParamCount (0), + eAsyncType (NONE) +{ + for (USHORT i = 0; i < MAXFUNCPARAM; i++) + eParamType[i] = PTR_DOUBLE; +} + +//------------------------------------------------------------------------ + +FuncData::FuncData(const ModuleData*pModule, + const String& rIName, + const String& rFName, + USHORT nNo, + USHORT nCount, + const ParamType* peType, + ParamType eType) : + pModuleData (pModule), + aInternalName (rIName), + aFuncName (rFName), + nNumber (nNo), + nParamCount (nCount), + eAsyncType (eType) +{ + for (USHORT i = 0; i < MAXFUNCPARAM; i++) + eParamType[i] = peType[i]; +} + +//------------------------------------------------------------------------ + +FuncData::FuncData(const FuncData& rData) : + ScDataObject(), + pModuleData (rData.pModuleData), + aInternalName (rData.aInternalName), + aFuncName (rData.aFuncName), + nNumber (rData.nNumber), + nParamCount (rData.nParamCount), + eAsyncType (rData.eAsyncType) +{ + for (USHORT i = 0; i < MAXFUNCPARAM; i++) + eParamType[i] = rData.eParamType[i]; +} + +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +short FuncCollection::Compare(ScDataObject* pKey1, ScDataObject* pKey2) const +{ + return (short) ScGlobal::GetpTransliteration()->compareString( + ((FuncData*)pKey1)->aInternalName, ((FuncData*)pKey2)->aInternalName ); +} + +//------------------------------------------------------------------------ + +BOOL FuncCollection::SearchFunc( const String& rName, USHORT& rIndex ) const +{ + FuncData aDataObj(rName); + return Search( &aDataObj, rIndex ); +} + +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +class ModuleData : public ScDataObject +{ +friend class ModuleCollection; + String aName; + osl::Module* pInstance; +public: + ModuleData(const String& rStr, osl::Module* pInst) : aName (rStr), pInstance (pInst) {} + ModuleData(const ModuleData& rData) : ScDataObject(), aName (rData.aName) {pInstance = new osl::Module(aName);} + ~ModuleData() { delete pInstance; } + virtual ScDataObject* Clone() const { return new ModuleData(*this); } + + const String& GetName() const { return aName; } + osl::Module* GetInstance() const { return pInstance; } + void FreeInstance() { delete pInstance; pInstance = 0; } +}; + +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +class ModuleCollection : public ScSortedCollection +{ +public: + ModuleCollection(USHORT nLim = 4, USHORT nDel = 4, BOOL bDup = FALSE) : ScSortedCollection ( nLim, nDel, bDup ) {} + ModuleCollection(const ModuleCollection& rModuleCollection) : ScSortedCollection ( rModuleCollection ) {} + + virtual ScDataObject* Clone() const { return new ModuleCollection(*this); } + ModuleData* operator[]( const USHORT nIndex) const {return (ModuleData*)At(nIndex);} + virtual short Compare(ScDataObject* pKey1, ScDataObject* pKey2) const; + BOOL SearchModule( const String& rName, + const ModuleData*& rpModule ) const; +}; + +static ModuleCollection aModuleCollection; + +//------------------------------------------------------------------------ + +short ModuleCollection::Compare(ScDataObject* pKey1, ScDataObject* pKey2) const +{ + return (short) ScGlobal::GetpTransliteration()->compareString( + ((ModuleData*)pKey1)->aName, ((ModuleData*)pKey2)->aName ); +} + +//------------------------------------------------------------------------ + +BOOL ModuleCollection::SearchModule( const String& rName, + const ModuleData*& rpModule ) const +{ + USHORT nIndex; + ModuleData aSearchModule(rName, 0); + BOOL bFound = Search( &aSearchModule, nIndex ); + if (bFound) + rpModule = (ModuleData*)At(nIndex); + else + rpModule = 0; + return bFound; +} + +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +BOOL InitExternalFunc(const rtl::OUString& rModuleName) +{ + String aModuleName( rModuleName ); + + // Module schon geladen? + const ModuleData* pTemp; + if (aModuleCollection.SearchModule(aModuleName, pTemp)) + return FALSE; + + rtl::OUString aNP; + aNP = rModuleName; + + BOOL bRet = FALSE; + osl::Module* pLib = new osl::Module( aNP ); + if (pLib->is()) + { + FARPROC fpGetCount = (FARPROC)pLib->getFunctionSymbol(LIBFUNCNAME(GETFUNCTIONCOUNT)); + FARPROC fpGetData = (FARPROC)pLib->getFunctionSymbol(LIBFUNCNAME(GETFUNCTIONDATA)); + if ((fpGetCount != NULL) && (fpGetData != NULL)) + { + FARPROC fpIsAsync = (FARPROC)pLib->getFunctionSymbol(LIBFUNCNAME(ISASYNC)); + FARPROC fpAdvice = (FARPROC)pLib->getFunctionSymbol(LIBFUNCNAME(ADVICE)); + FARPROC fpSetLanguage = (FARPROC)pLib->getFunctionSymbol(LIBFUNCNAME(SETLANGUAGE)); + if ( fpSetLanguage ) + { + LanguageType eLanguage = Application::GetSettings().GetUILanguage(); + USHORT nLanguage = (USHORT) eLanguage; + (*((SetLanguagePtr)fpSetLanguage))( nLanguage ); + } + + // Module in die Collection aufnehmen + ModuleData* pModuleData = new ModuleData(aModuleName, pLib); + aModuleCollection.Insert(pModuleData); + + // Schnittstelle initialisieren + AdvData pfCallBack = &ScAddInAsyncCallBack; + FuncData* pFuncData; + FuncCollection* pFuncCol = ScGlobal::GetFuncCollection(); + USHORT nCount; + (*((GetFuncCountPtr)fpGetCount))(nCount); + for (USHORT i=0; i < nCount; i++) + { + sal_Char cFuncName[256]; + sal_Char cInternalName[256]; + USHORT nParamCount; + ParamType eParamType[MAXFUNCPARAM]; + ParamType eAsyncType = NONE; + // #62113# alles initialisieren, falls das AddIn sich schlecht verhaelt + cFuncName[0] = 0; + cInternalName[0] = 0; + nParamCount = 0; + for ( USHORT j=0; j<MAXFUNCPARAM; j++ ) + { + eParamType[j] = NONE; + } + (*((GetFuncDataPtr)fpGetData))(i, cFuncName, nParamCount, + eParamType, cInternalName); + if( fpIsAsync ) + { + (*((IsAsync)fpIsAsync))(i, &eAsyncType); + if ( fpAdvice && eAsyncType != NONE ) + (*((Advice)fpAdvice))( i, pfCallBack ); + } + String aInternalName( cInternalName, osl_getThreadTextEncoding() ); + String aFuncName( cFuncName, osl_getThreadTextEncoding() ); + pFuncData = new FuncData( pModuleData, + aInternalName, + aFuncName, + i, + nParamCount, + eParamType, + eAsyncType ); + pFuncCol->Insert(pFuncData); + } + bRet = TRUE; + } + else + delete pLib; + } + else + delete pLib; + return bRet; +} + +//------------------------------------------------------------------------ + +void ExitExternalFunc() +{ + USHORT nCount = aModuleCollection.GetCount(); + for (USHORT i=0; i<nCount; i++) + { + ModuleData* pData = aModuleCollection[i]; + pData->FreeInstance(); + } +} + +//------------------------------------------------------------------------ + +BOOL FuncData::Call(void** ppParam) +{ + BOOL bRet = FALSE; + osl::Module* pLib = pModuleData->GetInstance(); + FARPROC fProc = (FARPROC)pLib->getFunctionSymbol(aFuncName); + if (fProc != NULL) + { + switch (nParamCount) + { + case 1 : + (*((ExFuncPtr1)fProc))(ppParam[0]); + bRet = TRUE; + break; + case 2 : + (*((ExFuncPtr2)fProc))(ppParam[0], ppParam[1]); + bRet = TRUE; + break; + case 3 : + (*((ExFuncPtr3)fProc))(ppParam[0], ppParam[1], ppParam[2]); + bRet = TRUE; + break; + case 4 : + (*((ExFuncPtr4)fProc))(ppParam[0], ppParam[1], ppParam[2], ppParam[3]); + bRet = TRUE; + break; + case 5 : + (*((ExFuncPtr5)fProc))(ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4]); + bRet = TRUE; + break; + case 6 : + (*((ExFuncPtr6)fProc))(ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5]); + bRet = TRUE; + break; + case 7 : + (*((ExFuncPtr7)fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5], + ppParam[6]); + bRet = TRUE; + break; + case 8 : + (*((ExFuncPtr8)fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5], + ppParam[6], ppParam[7]); + bRet = TRUE; + break; + case 9 : + (*((ExFuncPtr9)fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5], + ppParam[6], ppParam[7], ppParam[8]); + bRet = TRUE; + break; + case 10 : + (*((ExFuncPtr10)fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5], + ppParam[6], ppParam[7], ppParam[8], ppParam[9]); + bRet = TRUE; + break; + case 11 : + (*((ExFuncPtr11)fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5], + ppParam[6], ppParam[7], ppParam[8], ppParam[9], ppParam[10]); + bRet = TRUE; + break; + case 12: + (*((ExFuncPtr12)fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5], + ppParam[6], ppParam[7], ppParam[8], ppParam[9], ppParam[10], ppParam[11]); + bRet = TRUE; + break; + case 13: + (*((ExFuncPtr13)fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5], + ppParam[6], ppParam[7], ppParam[8], ppParam[9], ppParam[10], ppParam[11], + ppParam[12]); + bRet = TRUE; + break; + case 14 : + (*((ExFuncPtr14)fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5], + ppParam[6], ppParam[7], ppParam[8], ppParam[9], ppParam[10], ppParam[11], + ppParam[12], ppParam[13]); + bRet = TRUE; + break; + case 15 : + (*((ExFuncPtr15)fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5], + ppParam[6], ppParam[7], ppParam[8], ppParam[9], ppParam[10], ppParam[11], + ppParam[12], ppParam[13], ppParam[14]); + bRet = TRUE; + break; + case 16 : + (*((ExFuncPtr16)fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5], + ppParam[6], ppParam[7], ppParam[8], ppParam[9], ppParam[10], ppParam[11], + ppParam[12], ppParam[13], ppParam[14], ppParam[15]); + bRet = TRUE; + break; + default : break; + } + } + return bRet; +} + +//------------------------------------------------------------------------ + +BOOL FuncData::Unadvice( double nHandle ) +{ + BOOL bRet = FALSE; + osl::Module* pLib = pModuleData->GetInstance(); + FARPROC fProc = (FARPROC)pLib->getFunctionSymbol(LIBFUNCNAME(UNADVICE)); + if (fProc != NULL) + { + ((::Unadvice)fProc)(nHandle); + bRet = TRUE; + } + return bRet; +} + +//------------------------------------------------------------------------ + +const String& FuncData::GetModuleName() const +{ + // DBG_ASSERT( pModuleData, "Keine Arme, keine Kekse" ): + return pModuleData->GetName(); +} + +//------------------------------------------------------------------------ + +BOOL FuncData::GetParamDesc( String& aName, String& aDesc, USHORT nParam ) +{ + BOOL bRet = FALSE; + if ( nParam <= nParamCount ) + { + osl::Module* pLib = pModuleData->GetInstance(); + FARPROC fProc = (FARPROC) pLib->getFunctionSymbol( LIBFUNCNAME(GETPARAMDESC) ); + if ( fProc != NULL ) + { + sal_Char pcName[256]; + sal_Char pcDesc[256]; + *pcName = *pcDesc = 0; + USHORT nFuncNo = nNumber; // nicht per Reference versauen lassen.. + ((::GetParamDesc)fProc)( nFuncNo, nParam, pcName, pcDesc ); + aName = String( pcName, osl_getThreadTextEncoding() ); + aDesc = String( pcDesc, osl_getThreadTextEncoding() ); + bRet = TRUE; + } + } + if ( !bRet ) + { + aName.Erase(); + aDesc.Erase(); + } + return bRet; +} + + diff --git a/sc/source/core/tool/cellform.cxx b/sc/source/core/tool/cellform.cxx new file mode 100644 index 000000000000..45d2c4a890bb --- /dev/null +++ b/sc/source/core/tool/cellform.cxx @@ -0,0 +1,216 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <sfx2/objsh.hxx> +#include <svl/smplhint.hxx> +#include <svl/zforlist.hxx> + +#include "cellform.hxx" +#include "cell.hxx" +#include "document.hxx" +#include "formula/errorcodes.hxx" +#include "sc.hrc" + +// STATIC DATA ----------------------------------------------------------- + +// Err527 Workaround +const ScFormulaCell* pLastFormulaTreeTop = 0; + +// ----------------------------------------------------------------------- + +void ScCellFormat::GetString( ScBaseCell* pCell, ULONG nFormat, String& rString, + Color** ppColor, SvNumberFormatter& rFormatter, + BOOL bNullVals, + BOOL bFormula, + ScForceTextFmt eForceTextFmt ) +{ + *ppColor = NULL; + if (&rFormatter==NULL) + { + rString.Erase(); + return; + } + + CellType eType = pCell->GetCellType(); + switch(eType) + { + case CELLTYPE_STRING: + { + String aCellString; + ((ScStringCell*)pCell)->GetString( aCellString ); + rFormatter.GetOutputString( aCellString, nFormat, rString, ppColor ); + } + break; + case CELLTYPE_EDIT: + { + String aCellString; + ((ScEditCell*)pCell)->GetString( aCellString ); + rFormatter.GetOutputString( aCellString, nFormat, rString, ppColor ); + } + break; + case CELLTYPE_VALUE: + { + double nValue = ((ScValueCell*)pCell)->GetValue(); + if ( !bNullVals && nValue == 0.0 ) + rString.Erase(); + else + { + if( eForceTextFmt == ftCheck ) + { + if( nFormat && rFormatter.IsTextFormat( nFormat ) ) + eForceTextFmt = ftForce; + } + if( eForceTextFmt == ftForce ) + { + String aTemp; + rFormatter.GetOutputString( nValue, 0, aTemp, ppColor ); + rFormatter.GetOutputString( aTemp, nFormat, rString, ppColor ); + } + else + rFormatter.GetOutputString( nValue, nFormat, rString, ppColor ); + } + } + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + if ( bFormula ) + pFCell->GetFormula( rString ); + else + { + // #62160# Ein via Interpreter gestartetes Makro, das hart + // auf Formelzellen zugreift, bekommt einen CellText, auch + // wenn dadurch ein weiterer Interpreter gestartet wird, + // aber nicht wenn diese Zelle gerade interpretiert wird. + // IdleCalc startet generell keine weiteren Interpreter, + // um keine Err522 (zirkulaer) zu bekommen. + if ( pFCell->GetDocument()->IsInInterpreter() && + (!pFCell->GetDocument()->GetMacroInterpretLevel() + || pFCell->IsRunning()) ) + { + rString.AssignAscii( RTL_CONSTASCII_STRINGPARAM("...") ); + } + else + { + USHORT nErrCode = pFCell->GetErrCode(); + + // erst nach dem Interpretieren (GetErrCode) das Zahlformat holen: + if ( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) + nFormat = pFCell->GetStandardFormat( rFormatter, + nFormat ); + + if (nErrCode != 0) + rString = ScGlobal::GetErrorString(nErrCode); + else if ( pFCell->IsEmptyDisplayedAsString() ) + rString.Erase(); + else if ( pFCell->IsValue() ) + { + double fValue = pFCell->GetValue(); + if ( !bNullVals && fValue == 0.0 ) + rString.Erase(); + else + rFormatter.GetOutputString( fValue, nFormat, rString, ppColor ); + } + else + { + String aCellString; + pFCell->GetString( aCellString ); + rFormatter.GetOutputString( aCellString, nFormat, rString, ppColor ); + } + } + } + } + break; + default: + rString.Erase(); + break; + } +} + +void ScCellFormat::GetInputString( ScBaseCell* pCell, ULONG nFormat, String& rString, + SvNumberFormatter& rFormatter ) +{ + if (&rFormatter==NULL) + { + rString.Erase(); + return; + } + + CellType eType = pCell->GetCellType(); + switch(eType) + { + case CELLTYPE_STRING: + { + ((ScStringCell*)pCell)->GetString( rString ); + } + break; + case CELLTYPE_EDIT: + { + ((ScEditCell*)pCell)->GetString( rString ); + } + break; + case CELLTYPE_VALUE: + { + double nValue = ((ScValueCell*)pCell)->GetValue(); + rFormatter.GetInputLineString( nValue, nFormat, rString ); + } + break; + case CELLTYPE_FORMULA: + { + if (((ScFormulaCell*)pCell)->IsEmptyDisplayedAsString()) + { + rString.Erase(); + } + else if (((ScFormulaCell*)pCell)->IsValue()) + { + double nValue = ((ScFormulaCell*)pCell)->GetValue(); + rFormatter.GetInputLineString( nValue, nFormat, rString ); + } + else + { + ((ScFormulaCell*)pCell)->GetString( rString ); + } + + USHORT nErrCode = ((ScFormulaCell*)pCell)->GetErrCode(); + if (nErrCode != 0) + { + rString.Erase(); + } + } + break; + default: + rString.Erase(); + break; + } +} + + + diff --git a/sc/source/core/tool/cellkeytranslator.cxx b/sc/source/core/tool/cellkeytranslator.cxx new file mode 100644 index 000000000000..4db8c22d19d8 --- /dev/null +++ b/sc/source/core/tool/cellkeytranslator.cxx @@ -0,0 +1,232 @@ +/************************************************************************* + * + * 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 "cellkeytranslator.hxx" +#include "comphelper/processfactory.hxx" +#include "i18npool/mslangid.hxx" +#include "i18npool/lang.h" +#include "rtl/ustring.hxx" + +#include <com/sun/star/i18n/TransliterationModules.hpp> + +using ::com::sun::star::lang::Locale; +using ::com::sun::star::uno::Sequence; +using ::std::list; +using ::std::hash_map; +using ::rtl::OUString; + +using namespace ::com::sun::star; + +enum LocaleMatch +{ + LOCALE_MATCH_NONE = 0, + LOCALE_MATCH_LANG, + LOCALE_MATCH_LANG_COUNTRY, + LOCALE_MATCH_ALL +}; + +static LocaleMatch lclLocaleCompare(const Locale& rLocale1, const Locale& rLocale2) +{ + LocaleMatch eMatchLevel = LOCALE_MATCH_NONE; + if ( !rLocale1.Language.compareTo(rLocale1.Language) ) + eMatchLevel = LOCALE_MATCH_LANG; + else + return eMatchLevel; + + if ( !rLocale1.Country.compareTo(rLocale2.Country) ) + eMatchLevel = LOCALE_MATCH_LANG_COUNTRY; + else + return eMatchLevel; + + if ( !rLocale1.Variant.compareTo(rLocale2.Variant) ) + eMatchLevel = LOCALE_MATCH_ALL; + + return eMatchLevel; +} + +ScCellKeyword::ScCellKeyword(const sal_Char* pName, OpCode eOpCode, const Locale& rLocale) : + mpName(pName), + meOpCode(eOpCode), + mrLocale(rLocale) +{ +} + +::std::auto_ptr<ScCellKeywordTranslator> ScCellKeywordTranslator::spInstance(NULL); + +static void lclMatchKeyword(String& rName, const ScCellKeywordHashMap& aMap, + OpCode eOpCode = ocNone, const Locale* pLocale = NULL) +{ + ScCellKeywordHashMap::const_iterator itrEnd = aMap.end(); + ScCellKeywordHashMap::const_iterator itr = aMap.find(rName); + + if ( itr == itrEnd || itr->second.empty() ) + // No candidate strings exist. Bail out. + return; + + if ( eOpCode == ocNone && !pLocale ) + { + // Since no locale nor opcode matching is needed, simply return + // the first item on the list. + rName = String::CreateFromAscii( itr->second.front().mpName ); + return; + } + + const sal_Char* aBestMatchName = itr->second.front().mpName; + LocaleMatch eLocaleMatchLevel = LOCALE_MATCH_NONE; + bool bOpCodeMatched = false; + + list<ScCellKeyword>::const_iterator itrListEnd = itr->second.end(); + list<ScCellKeyword>::const_iterator itrList = itr->second.begin(); + for ( ; itrList != itrListEnd; ++itrList ) + { + if ( eOpCode != ocNone && pLocale ) + { + if ( itrList->meOpCode == eOpCode ) + { + LocaleMatch eLevel = lclLocaleCompare(itrList->mrLocale, *pLocale); + if ( eLevel == LOCALE_MATCH_ALL ) + { + // Name with matching opcode and locale found. + rName = String::CreateFromAscii( itrList->mpName ); + return; + } + else if ( eLevel > eLocaleMatchLevel ) + { + // Name with a better matching locale. + eLocaleMatchLevel = eLevel; + aBestMatchName = itrList->mpName; + } + else if ( !bOpCodeMatched ) + // At least the opcode matches. + aBestMatchName = itrList->mpName; + + bOpCodeMatched = true; + } + } + else if ( eOpCode != ocNone && !pLocale ) + { + if ( itrList->meOpCode == eOpCode ) + { + // Name with a matching opcode preferred. + rName = String::CreateFromAscii( itrList->mpName ); + return; + } + } + else if ( !eOpCode && pLocale ) + { + LocaleMatch eLevel = lclLocaleCompare(itrList->mrLocale, *pLocale); + if ( eLevel == LOCALE_MATCH_ALL ) + { + // Name with matching locale preferred. + rName = String::CreateFromAscii( itrList->mpName ); + return; + } + else if ( eLevel > eLocaleMatchLevel ) + { + // Name with a better matching locale. + eLocaleMatchLevel = eLevel; + aBestMatchName = itrList->mpName; + } + } + } + + // No preferred strings found. Return the best matching name. + rName = String::CreateFromAscii(aBestMatchName); +} + +void ScCellKeywordTranslator::transKeyword(String& rName, const Locale* pLocale, OpCode eOpCode) +{ + if ( !spInstance.get() ) + spInstance.reset( new ScCellKeywordTranslator ); + + LanguageType eLang = pLocale ? MsLangId::convertLocaleToLanguageWithFallback(*pLocale) : LANGUAGE_SYSTEM; + Sequence<sal_Int32> aOffsets; + rName = spInstance->maTransWrapper.transliterate(rName, eLang, 0, rName.Len(), &aOffsets); + lclMatchKeyword(rName, spInstance->maStringNameMap, eOpCode, pLocale); +} + +ScCellKeywordTranslator::ScCellKeywordTranslator() : + maTransWrapper( ::comphelper::getProcessServiceFactory(), + i18n::TransliterationModules_LOWERCASE_UPPERCASE ) +{ + init(); +} + +ScCellKeywordTranslator::~ScCellKeywordTranslator() +{ +} + +struct TransItem +{ + const sal_Unicode* from; + const sal_Char* to; + OpCode func; +}; + +void ScCellKeywordTranslator::init() +{ + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + + // The file below has been autogenerated by sc/workben/celltrans/parse.py. + // To add new locale keywords, edit sc/workben/celltrans/keywords_utf16.txt + // and re-run the parse.py script. + // + // All keywords must be uppercase, and the mapping must be from the + // localized keyword to the English keyword. + // + // Make sure that the original keyword file (keywords_utf16.txt) is + // encoded in UCS-2/UTF-16! + + #include "cellkeywords.inl" +} + +void ScCellKeywordTranslator::addToMap(const String& rKey, const sal_Char* pName, const Locale& rLocale, OpCode eOpCode) +{ + ScCellKeyword aKeyItem( pName, eOpCode, rLocale ); + + ScCellKeywordHashMap::iterator itrEnd = maStringNameMap.end(); + ScCellKeywordHashMap::iterator itr = maStringNameMap.find(rKey); + + if ( itr == itrEnd ) + { + // New keyword. + list<ScCellKeyword> aList; + aList.push_back(aKeyItem); + maStringNameMap.insert( ScCellKeywordHashMap::value_type(rKey, aList) ); + } + else + itr->second.push_back(aKeyItem); +} + +void ScCellKeywordTranslator::addToMap(const TransItem* pItems, const Locale& rLocale) +{ + for (sal_uInt16 i = 0; pItems[i].from != NULL; ++i) + addToMap(String(pItems[i].from), pItems[i].to, rLocale, pItems[i].func); +} diff --git a/sc/source/core/tool/cellkeywords.inl b/sc/source/core/tool/cellkeywords.inl new file mode 100644 index 000000000000..9fb58c02797b --- /dev/null +++ b/sc/source/core/tool/cellkeywords.inl @@ -0,0 +1,181 @@ +// This file has been automatically generated. Do not hand-edit this! + +// --------------------------------------------------------------------------- +// French language locale (automatically generated) +// --------------------------------------------------------------------------- +static const Locale aFr(OUString::createFromAscii("fr"), OUString(), OUString()); + +// pre instantiations of localized function names +static const sal_Unicode cell_address_fr[] = { + 0x0041, 0x0044, 0x0052, 0x0045, 0x0053, 0x0053, 0x0045, 0x0000}; +static const sal_Unicode cell_col_fr[] = { + 0x0043, 0x004F, 0x004C, 0x004F, 0x004E, 0x004E, 0x0045, 0x0000}; +static const sal_Unicode cell_contents_fr[] = { + 0x0043, 0x004F, 0x004E, 0x0054, 0x0045, 0x004E, 0x0055, 0x0000}; +static const sal_Unicode cell_color_fr[] = { + 0x0043, 0x004F, 0x0055, 0x004C, 0x0045, 0x0055, 0x0052, 0x0000}; +static const sal_Unicode cell_width_fr[] = { + 0x004C, 0x0041, 0x0052, 0x0047, 0x0045, 0x0055, 0x0052, 0x0000}; +static const sal_Unicode cell_row_fr[] = { + 0x004C, 0x0049, 0x0047, 0x004E, 0x0045, 0x0000}; +static const sal_Unicode cell_filename_fr[] = { + 0x004E, 0x004F, 0x004D, 0x0046, 0x0049, 0x0043, 0x0048, 0x0049, 0x0045, 0x0052, 0x0000}; +static const sal_Unicode cell_prefix_fr[] = { + 0x0050, 0x0052, 0x0045, 0x0046, 0x0049, 0x0058, 0x0045, 0x0000}; +static const sal_Unicode cell_protect_fr[] = { + 0x0050, 0x0052, 0x004F, 0x0054, 0x0045, 0x0047, 0x0045, 0x0000}; +static const sal_Unicode info_numfile_fr[] = { + 0x004E, 0x0042, 0x0046, 0x0049, 0x0043, 0x0048, 0x0000}; +static const sal_Unicode info_recalc_fr[] = { + 0x0052, 0x0045, 0x0043, 0x0041, 0x004C, 0x0043, 0x0055, 0x004C, 0x0000}; +static const sal_Unicode info_system_fr[] = { + 0x0053, 0x0059, 0x0053, 0x0054, 0x0045, 0x0058, 0x0050, 0x004C, 0x0000}; +static const sal_Unicode info_release_fr[] = { + 0x0056, 0x0045, 0x0052, 0x0053, 0x0049, 0x004F, 0x004E, 0x0000}; +static const sal_Unicode info_osversion_fr[] = { + 0x0056, 0x0045, 0x0052, 0x0053, 0x0049, 0x004F, 0x004E, 0x0053, 0x0045, 0x0000}; + +static const TransItem pFr[] = { + {cell_address_fr, "ADDRESS", ocCell}, + {cell_col_fr, "COL", ocCell}, + {cell_contents_fr, "CONTENTS", ocCell}, + {cell_color_fr, "COLOR", ocCell}, + {cell_width_fr, "WIDTH", ocCell}, + {cell_row_fr, "ROW", ocCell}, + {cell_filename_fr, "FILENAME", ocCell}, + {cell_prefix_fr, "PREFIX", ocCell}, + {cell_protect_fr, "PROTECT", ocCell}, + {info_numfile_fr, "NUMFILE", ocInfo}, + {info_recalc_fr, "RECALC", ocInfo}, + {info_system_fr, "SYSTEM", ocInfo}, + {info_release_fr, "RELEASE", ocInfo}, + {info_osversion_fr, "OSVERSION", ocInfo}, + {NULL, NULL, ocNone} +}; + +addToMap(pFr, aFr); + +// --------------------------------------------------------------------------- +// Hungarian language locale (automatically generated) +// --------------------------------------------------------------------------- +static const Locale aHu(OUString::createFromAscii("hu"), OUString(), OUString()); + +// pre instantiations of localized function names +static const sal_Unicode cell_address_hu[] = { + 0x0043, 0x00CD, 0x004D, 0x0000}; +static const sal_Unicode cell_col_hu[] = { + 0x004F, 0x0053, 0x005A, 0x004C, 0x004F, 0x0050, 0x0000}; +static const sal_Unicode cell_color_hu[] = { + 0x0053, 0x005A, 0x00CD, 0x004E, 0x0000}; +static const sal_Unicode cell_contents_hu[] = { + 0x0054, 0x0041, 0x0052, 0x0054, 0x0041, 0x004C, 0x004F, 0x004D, 0x0000}; +static const sal_Unicode cell_width_hu[] = { + 0x0053, 0x005A, 0x00C9, 0x004C, 0x0045, 0x0053, 0x0000}; +static const sal_Unicode cell_row_hu[] = { + 0x0053, 0x004F, 0x0052, 0x0000}; +static const sal_Unicode cell_filename_hu[] = { + 0x0046, 0x0049, 0x004C, 0x0045, 0x004E, 0x00C9, 0x0056, 0x0000}; +static const sal_Unicode cell_prefix_hu[] = { + 0x0050, 0x0052, 0x0045, 0x0046, 0x0049, 0x0058, 0x0000}; +static const sal_Unicode cell_protect_hu[] = { + 0x0056, 0x00C9, 0x0044, 0x0045, 0x0054, 0x0054, 0x0000}; +static const sal_Unicode cell_coord_hu[] = { + 0x004B, 0x004F, 0x004F, 0x0052, 0x0044, 0x0000}; +static const sal_Unicode cell_format_hu[] = { + 0x0046, 0x004F, 0x0052, 0x004D, 0x0041, 0x0000}; +static const sal_Unicode cell_parentheses_hu[] = { + 0x005A, 0x00C1, 0x0052, 0x00D3, 0x004A, 0x0045, 0x004C, 0x0045, 0x004B, 0x0000}; +static const sal_Unicode cell_sheet_hu[] = { + 0x004C, 0x0041, 0x0050, 0x0000}; +static const sal_Unicode cell_type_hu[] = { + 0x0054, 0x00CD, 0x0050, 0x0055, 0x0053, 0x0000}; +static const sal_Unicode info_numfile_hu[] = { + 0x0046, 0x0049, 0x004C, 0x0045, 0x0053, 0x005A, 0x00C1, 0x004D, 0x0000}; +static const sal_Unicode info_recalc_hu[] = { + 0x0053, 0x005A, 0x00C1, 0x004D, 0x004F, 0x004C, 0x00C1, 0x0053, 0x0000}; +static const sal_Unicode info_system_hu[] = { + 0x0052, 0x0045, 0x004E, 0x0044, 0x0053, 0x005A, 0x0045, 0x0052, 0x0000}; +static const sal_Unicode info_release_hu[] = { + 0x0056, 0x0045, 0x0052, 0x005A, 0x0049, 0x00D3, 0x0000}; +static const sal_Unicode info_osversion_hu[] = { + 0x004F, 0x0050, 0x0052, 0x0045, 0x004E, 0x0044, 0x0053, 0x005A, 0x0045, 0x0052, 0x0000}; + +static const TransItem pHu[] = { + {cell_address_hu, "ADDRESS", ocCell}, + {cell_col_hu, "COL", ocCell}, + {cell_color_hu, "COLOR", ocCell}, + {cell_contents_hu, "CONTENTS", ocCell}, + {cell_width_hu, "WIDTH", ocCell}, + {cell_row_hu, "ROW", ocCell}, + {cell_filename_hu, "FILENAME", ocCell}, + {cell_prefix_hu, "PREFIX", ocCell}, + {cell_protect_hu, "PROTECT", ocCell}, + {cell_coord_hu, "COORD", ocCell}, + {cell_format_hu, "FORMAT", ocCell}, + {cell_parentheses_hu, "PARENTHESES", ocCell}, + {cell_sheet_hu, "SHEET", ocCell}, + {cell_type_hu, "TYPE", ocCell}, + {info_numfile_hu, "NUMFILE", ocInfo}, + {info_recalc_hu, "RECALC", ocInfo}, + {info_system_hu, "SYSTEM", ocInfo}, + {info_release_hu, "RELEASE", ocInfo}, + {info_osversion_hu, "OSVERSION", ocInfo}, + {NULL, NULL, ocNone} +}; + +addToMap(pHu, aHu); + +// --------------------------------------------------------------------------- +// German language locale (automatically generated) +// --------------------------------------------------------------------------- +static const Locale aDe(OUString::createFromAscii("de"), OUString(), OUString()); + +// pre instantiations of localized function names +static const sal_Unicode cell_row_de[] = { + 0x005A, 0x0045, 0x0049, 0x004C, 0x0045, 0x0000}; +static const sal_Unicode cell_col_de[] = { + 0x0053, 0x0050, 0x0041, 0x004C, 0x0054, 0x0045, 0x0000}; +static const sal_Unicode cell_width_de[] = { + 0x0042, 0x0052, 0x0045, 0x0049, 0x0054, 0x0045, 0x0000}; +static const sal_Unicode cell_address_de[] = { + 0x0041, 0x0044, 0x0052, 0x0045, 0x0053, 0x0053, 0x0045, 0x0000}; +static const sal_Unicode cell_filename_de[] = { + 0x0044, 0x0041, 0x0054, 0x0045, 0x0049, 0x004E, 0x0041, 0x004D, 0x0045, 0x0000}; +static const sal_Unicode cell_color_de[] = { + 0x0046, 0x0041, 0x0052, 0x0042, 0x0045, 0x0000}; +static const sal_Unicode cell_format_de[] = { + 0x0046, 0x004F, 0x0052, 0x004D, 0x0041, 0x0054, 0x0000}; +static const sal_Unicode cell_contents_de[] = { + 0x0049, 0x004E, 0x0048, 0x0041, 0x004C, 0x0054, 0x0000}; +static const sal_Unicode cell_parentheses_de[] = { + 0x004B, 0x004C, 0x0041, 0x004D, 0x004D, 0x0045, 0x0052, 0x004E, 0x0000}; +static const sal_Unicode cell_protect_de[] = { + 0x0053, 0x0043, 0x0048, 0x0055, 0x0054, 0x005A, 0x0000}; +static const sal_Unicode cell_type_de[] = { + 0x0054, 0x0059, 0x0050, 0x0000}; +static const sal_Unicode cell_prefix_de[] = { + 0x0050, 0x0052, 0x00C4, 0x0046, 0x0049, 0x0058, 0x0000}; +static const sal_Unicode cell_sheet_de[] = { + 0x0042, 0x004C, 0x0041, 0x0054, 0x0054, 0x0000}; +static const sal_Unicode cell_coord_de[] = { + 0x004B, 0x004F, 0x004F, 0x0052, 0x0044, 0x0000}; + +static const TransItem pDe[] = { + {cell_row_de, "ROW", ocCell}, + {cell_col_de, "COL", ocCell}, + {cell_width_de, "WIDTH", ocCell}, + {cell_address_de, "ADDRESS", ocCell}, + {cell_filename_de, "FILENAME", ocCell}, + {cell_color_de, "COLOR", ocCell}, + {cell_format_de, "FORMAT", ocCell}, + {cell_contents_de, "CONTENTS", ocCell}, + {cell_parentheses_de, "PARENTHESES", ocCell}, + {cell_protect_de, "PROTECT", ocCell}, + {cell_type_de, "TYPE", ocCell}, + {cell_prefix_de, "PREFIX", ocCell}, + {cell_sheet_de, "SHEET", ocCell}, + {cell_coord_de, "COORD", ocCell}, + {NULL, NULL, ocNone} +}; + +addToMap(pDe, aDe); diff --git a/sc/source/core/tool/chartarr.cxx b/sc/source/core/tool/chartarr.cxx new file mode 100644 index 000000000000..573763141b18 --- /dev/null +++ b/sc/source/core/tool/chartarr.cxx @@ -0,0 +1,615 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include "scitems.hxx" +#include <svl/intitem.hxx> +#include <svl/zforlist.hxx> +#include <float.h> // DBL_MIN + +#include "chartarr.hxx" +#include "document.hxx" +#include "rechead.hxx" +#include "globstr.hrc" +#include "cell.hxx" +#include "docoptio.hxx" + +#include <vector> + +using ::std::vector; + +// ----------------------------------------------------------------------- + +ScMemChart::ScMemChart(short nCols, short nRows) +{ + nRowCnt = nRows; + nColCnt = nCols; + pData = new double[nColCnt * nRowCnt]; + + if (pData) + { + double *pFill = pData; + + for (short i = 0; i < nColCnt; i++) + for (short j = 0; j < nRowCnt; j++) + *(pFill ++) = 0.0; + } + + pColText = new String[nColCnt]; + pRowText = new String[nRowCnt]; +} + +ScMemChart::~ScMemChart() +{ + delete[] pRowText; + delete[] pColText; + delete[] pData; +} + +// ----------------------------------------------------------------------- + +ScChartArray::ScChartArray( ScDocument* pDoc, SCTAB nTab, + SCCOL nStartColP, SCROW nStartRowP, SCCOL nEndColP, SCROW nEndRowP, + const String& rChartName ) : + aName( rChartName ), + pDocument( pDoc ), + aPositioner(pDoc, nTab, nStartColP, nStartRowP, nEndColP, nEndRowP), + bValid( TRUE ) +{ +} + +ScChartArray::ScChartArray( ScDocument* pDoc, const ScRangeListRef& rRangeList, + const String& rChartName ) : + aName( rChartName ), + pDocument( pDoc ), + aPositioner(pDoc, rRangeList), + bValid( TRUE ) +{ +} + +ScChartArray::ScChartArray( const ScChartArray& rArr ) : + ScDataObject(), + aName(rArr.aName), + pDocument(rArr.pDocument), + aPositioner(rArr.aPositioner), + bValid(rArr.bValid) +{ +} + +ScChartArray::~ScChartArray() +{ +} + +ScDataObject* ScChartArray::Clone() const +{ + return new ScChartArray(*this); +} + +BOOL ScChartArray::operator==(const ScChartArray& rCmp) const +{ + return aPositioner == rCmp.aPositioner + && aName == rCmp.aName; +} + +#ifdef _MSC_VER +#pragma optimize("",off) +#endif + +ScMemChart* ScChartArray::CreateMemChart() +{ + ScRangeListRef aRangeListRef(GetRangeList()); + ULONG nCount = aRangeListRef->Count(); + if ( nCount > 1 ) + return CreateMemChartMulti(); + else if ( nCount == 1 ) + { + ScRange* pR = aRangeListRef->First(); + if ( pR->aStart.Tab() != pR->aEnd.Tab() ) + return CreateMemChartMulti(); + else + return CreateMemChartSingle(); + } + else + return CreateMemChartMulti(); // kann 0 Range besser ab als Single +} + +ScMemChart* ScChartArray::CreateMemChartSingle() +{ + SCSIZE nCol; + SCSIZE nRow; + + // + // wirkliche Groesse (ohne versteckte Zeilen/Spalten) + // + + SCCOL nColAdd = HasRowHeaders() ? 1 : 0; + SCROW nRowAdd = HasColHeaders() ? 1 : 0; + + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + ScRangeListRef aRangeListRef(GetRangeList()); + aRangeListRef->First()->GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + + SCCOL nStrCol = nCol1; // fuer Beschriftung merken + SCROW nStrRow = nRow1; + // Skip hidden columns. + // TODO: make use of last column value once implemented. + SCCOL nLastCol = -1; + while (pDocument->ColHidden(nCol1, nTab1, nLastCol)) + ++nCol1; + + // Skip hidden rows. + SCROW nLastRow = -1; + if (pDocument->RowHidden(nRow1, nTab1, nLastRow)) + nRow1 = nLastRow + 1; + + // falls alles hidden ist, bleibt die Beschriftung am Anfang + if ( nCol1 <= nCol2 ) + { + nStrCol = nCol1; + nCol1 = sal::static_int_cast<SCCOL>( nCol1 + nColAdd ); + } + if ( nRow1 <= nRow2 ) + { + nStrRow = nRow1; + nRow1 = sal::static_int_cast<SCROW>( nRow1 + nRowAdd ); + } + + SCSIZE nTotalCols = ( nCol1 <= nCol2 ? nCol2 - nCol1 + 1 : 0 ); + vector<SCCOL> aCols; + aCols.reserve(nTotalCols); + for (SCSIZE i=0; i<nTotalCols; i++) + { + SCCOL nThisCol = sal::static_int_cast<SCCOL>(nCol1+i); + if (!pDocument->ColHidden(nThisCol, nTab1, nLastCol)) + aCols.push_back(nThisCol); + } + SCSIZE nColCount = aCols.size(); + + SCSIZE nTotalRows = ( nRow1 <= nRow2 ? nRow2 - nRow1 + 1 : 0 ); + vector<SCROW> aRows; + aRows.reserve(nTotalRows); + if (nRow1 <= nRow2) + { + // Get all visible rows between nRow1 and nRow2. + SCROW nThisRow = nRow1; + while (nThisRow <= nRow2) + { + if (pDocument->RowHidden(nThisRow, nTab1, nLastRow)) + nThisRow = nLastRow; + else + aRows.push_back(nThisRow); + ++nThisRow; + } + } + SCSIZE nRowCount = aRows.size(); + + // May happen at least with more than 32k rows. + if (nColCount > SHRT_MAX || nRowCount > SHRT_MAX) + { + nColCount = 0; + nRowCount = 0; + } + + BOOL bValidData = TRUE; + if ( !nColCount ) + { + bValidData = FALSE; + nColCount = 1; + aCols.push_back(nStrCol); + } + if ( !nRowCount ) + { + bValidData = FALSE; + nRowCount = 1; + aRows.push_back(nStrRow); + } + + // + // Daten + // + + ScMemChart* pMemChart = new ScMemChart( + static_cast<short>(nColCount), static_cast<short>(nRowCount) ); + if (pMemChart) + { +// SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); +// pMemChart->SetNumberFormatter( pFormatter ); + if ( bValidData ) + { + BOOL bCalcAsShown = pDocument->GetDocOptions().IsCalcAsShown(); + ScBaseCell* pCell; + for (nCol=0; nCol<nColCount; nCol++) + { + for (nRow=0; nRow<nRowCount; nRow++) + { + double nVal = DBL_MIN; // Hack fuer Chart, um leere Zellen zu erkennen + + pDocument->GetCell( aCols[nCol], aRows[nRow], nTab1, pCell ); + if (pCell) + { + CellType eType = pCell->GetCellType(); + if (eType == CELLTYPE_VALUE) + { + nVal = ((ScValueCell*)pCell)->GetValue(); + if ( bCalcAsShown && nVal != 0.0 ) + { + sal_uInt32 nFormat; + pDocument->GetNumberFormat( aCols[nCol], + aRows[nRow], nTab1, nFormat ); + nVal = pDocument->RoundValueAsShown( nVal, nFormat ); + } + } + else if (eType == CELLTYPE_FORMULA) + { + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + if ( (pFCell->GetErrCode() == 0) && pFCell->IsValue() ) + nVal = pFCell->GetValue(); + } + } + pMemChart->SetData(static_cast<short>(nCol), static_cast<short>(nRow), nVal); + } + } + } + else + { + //! Flag, dass Daten ungueltig ?? + + for (nCol=0; nCol<nColCount; nCol++) + for (nRow=0; nRow<nRowCount; nRow++) + pMemChart->SetData( static_cast<short>(nCol), static_cast<short>(nRow), DBL_MIN ); + } + + // + // Spalten-Header + // + + for (nCol=0; nCol<nColCount; nCol++) + { + String aString, aColStr; + if (HasColHeaders()) + pDocument->GetString( aCols[nCol], nStrRow, nTab1, aString ); + if ( !aString.Len() ) + { + aString = ScGlobal::GetRscString(STR_COLUMN); + aString += ' '; +// aString += String::CreateFromInt32( pCols[nCol]+1 ); + ScAddress aPos( aCols[ nCol ], 0, 0 ); + aPos.Format( aColStr, SCA_VALID_COL, NULL ); + aString += aColStr; + } + pMemChart->SetColText( static_cast<short>(nCol), aString); + +// ULONG nNumberAttr = (nTotalRows ? pDocument->GetNumberFormat( +// ScAddress( pCols[nCol], nRow1, nTab1)) : 0); +// pMemChart->SetNumFormatIdCol( static_cast<long>(nCol), nNumberAttr ); + } + + // + // Zeilen-Header + // + + for (nRow=0; nRow<nRowCount; nRow++) + { + String aString; + if (HasRowHeaders()) + { + ScAddress aAddr( nStrCol, aRows[nRow], nTab1 ); + pDocument->GetString( nStrCol, aRows[nRow], nTab1, aString ); + } + if ( !aString.Len() ) + { + aString = ScGlobal::GetRscString(STR_ROW); + aString += ' '; + aString += String::CreateFromInt32( aRows[nRow]+1 ); + } + pMemChart->SetRowText( static_cast<short>(nRow), aString); + +// ULONG nNumberAttr = (nTotalCols ? pDocument->GetNumberFormat( +// ScAddress( nCol1, pRows[nRow], nTab1)) : 0); +// pMemChart->SetNumFormatIdRow( static_cast<long>(nRow), nNumberAttr ); + } + + // + // Titel + // + +// pMemChart->SetMainTitle(ScGlobal::GetRscString(STR_CHART_MAINTITLE)); +// pMemChart->SetSubTitle(ScGlobal::GetRscString(STR_CHART_SUBTITLE)); +// pMemChart->SetXAxisTitle(ScGlobal::GetRscString(STR_CHART_XTITLE)); +// pMemChart->SetYAxisTitle(ScGlobal::GetRscString(STR_CHART_YTITLE)); +// pMemChart->SetZAxisTitle(ScGlobal::GetRscString(STR_CHART_ZTITLE)); + + // + // Zahlen-Typ + // + +// ULONG nNumberAttr = (nTotalCols && nTotalRows ? +// pDocument->GetNumberFormat( ScAddress( nCol1, nRow1, nTab1)) : +// 0); +// if (pFormatter) +// pMemChart->SetDataType(pFormatter->GetType( nNumberAttr )); + + // + // Parameter-Strings + // + +// SetExtraStrings( *pMemChart ); + } + + return pMemChart; +} + +ScMemChart* ScChartArray::CreateMemChartMulti() +{ + SCSIZE nColCount = GetPositionMap()->GetColCount(); + SCSIZE nRowCount = GetPositionMap()->GetRowCount(); + + SCSIZE nCol = 0; + SCSIZE nRow = 0; + + // May happen at least with more than 32k rows. + if (nColCount > SHRT_MAX || nRowCount > SHRT_MAX) + { + nColCount = 0; + nRowCount = 0; + } + + BOOL bValidData = TRUE; + if ( !nColCount ) + { + bValidData = FALSE; + nColCount = 1; + } + if ( !nRowCount ) + { + bValidData = FALSE; + nRowCount = 1; + } + + // + // Daten + // + + ScMemChart* pMemChart = new ScMemChart( + static_cast<short>(nColCount), static_cast<short>(nRowCount) ); + if (pMemChart) + { +// pMemChart->SetNumberFormatter( pDocument->GetFormatTable() ); + BOOL bCalcAsShown = pDocument->GetDocOptions().IsCalcAsShown(); + ULONG nIndex = 0; + if (bValidData) + { + for ( nCol = 0; nCol < nColCount; nCol++ ) + { + for ( nRow = 0; nRow < nRowCount; nRow++, nIndex++ ) + { + double nVal = DBL_MIN; // Hack fuer Chart, um leere Zellen zu erkennen + const ScAddress* pPos = GetPositionMap()->GetPosition( nIndex ); + if ( pPos ) + { // sonst: Luecke + ScBaseCell* pCell = pDocument->GetCell( *pPos ); + if (pCell) + { + CellType eType = pCell->GetCellType(); + if (eType == CELLTYPE_VALUE) + { + nVal = ((ScValueCell*)pCell)->GetValue(); + if ( bCalcAsShown && nVal != 0.0 ) + { + ULONG nFormat = pDocument->GetNumberFormat( *pPos ); + nVal = pDocument->RoundValueAsShown( nVal, nFormat ); + } + } + else if (eType == CELLTYPE_FORMULA) + { + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + if ( (pFCell->GetErrCode() == 0) && pFCell->IsValue() ) + nVal = pFCell->GetValue(); + } + } + } + pMemChart->SetData(static_cast<short>(nCol), static_cast<short>(nRow), nVal); + } + } + } + else + { + for ( nRow = 0; nRow < nRowCount; nRow++, nIndex++ ) + { + double nVal = DBL_MIN; // Hack fuer Chart, um leere Zellen zu erkennen + const ScAddress* pPos = GetPositionMap()->GetPosition( nIndex ); + if ( pPos ) + { // sonst: Luecke + ScBaseCell* pCell = pDocument->GetCell( *pPos ); + if (pCell) + { + CellType eType = pCell->GetCellType(); + if (eType == CELLTYPE_VALUE) + { + nVal = ((ScValueCell*)pCell)->GetValue(); + if ( bCalcAsShown && nVal != 0.0 ) + { + ULONG nFormat = pDocument->GetNumberFormat( *pPos ); + nVal = pDocument->RoundValueAsShown( nVal, nFormat ); + } + } + else if (eType == CELLTYPE_FORMULA) + { + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + if ( (pFCell->GetErrCode() == 0) && pFCell->IsValue() ) + nVal = pFCell->GetValue(); + } + } + } + pMemChart->SetData(static_cast<short>(nCol), static_cast<short>(nRow), nVal); + } + } + +//2do: Beschriftung bei Luecken + + // + // Spalten-Header + // + + SCCOL nPosCol = 0; + for ( nCol = 0; nCol < nColCount; nCol++ ) + { + String aString, aColStr; + const ScAddress* pPos = GetPositionMap()->GetColHeaderPosition( static_cast<SCCOL>(nCol) ); + if ( HasColHeaders() && pPos ) + pDocument->GetString( + pPos->Col(), pPos->Row(), pPos->Tab(), aString ); + if ( !aString.Len() ) + { + aString = ScGlobal::GetRscString(STR_COLUMN); + aString += ' '; + if ( pPos ) + nPosCol = pPos->Col() + 1; + else + nPosCol++; + ScAddress aPos( nPosCol - 1, 0, 0 ); + aPos.Format( aColStr, SCA_VALID_COL, NULL ); +// aString += String::CreateFromInt32( nPosCol ); + aString += aColStr; + } + pMemChart->SetColText( static_cast<short>(nCol), aString); + +// ULONG nNumberAttr = 0; +// pPos = GetPositionMap()->GetPosition( nCol, 0 ); +// if ( pPos ) +// nNumberAttr = pDocument->GetNumberFormat( *pPos ); +// pMemChart->SetNumFormatIdCol( static_cast<long>(nCol), nNumberAttr ); + } + + // + // Zeilen-Header + // + + SCROW nPosRow = 0; + for ( nRow = 0; nRow < nRowCount; nRow++ ) + { + String aString; + const ScAddress* pPos = GetPositionMap()->GetRowHeaderPosition( nRow ); + if ( HasRowHeaders() && pPos ) + { + pDocument->GetString( + pPos->Col(), pPos->Row(), pPos->Tab(), aString ); + } + if ( !aString.Len() ) + { + aString = ScGlobal::GetRscString(STR_ROW); + aString += ' '; + if ( pPos ) + nPosRow = pPos->Row() + 1; + else + nPosRow++; + aString += String::CreateFromInt32( nPosRow ); + } + pMemChart->SetRowText( static_cast<short>(nRow), aString); + +// ULONG nNumberAttr = 0; +// pPos = GetPositionMap()->GetPosition( 0, nRow ); +// if ( pPos ) +// nNumberAttr = pDocument->GetNumberFormat( *pPos ); +// pMemChart->SetNumFormatIdRow( static_cast<long>(nRow), nNumberAttr ); + } + + // + // Titel + // + +// pMemChart->SetMainTitle(ScGlobal::GetRscString(STR_CHART_MAINTITLE)); +// pMemChart->SetSubTitle(ScGlobal::GetRscString(STR_CHART_SUBTITLE)); +// pMemChart->SetXAxisTitle(ScGlobal::GetRscString(STR_CHART_XTITLE)); +// pMemChart->SetYAxisTitle(ScGlobal::GetRscString(STR_CHART_YTITLE)); +// pMemChart->SetZAxisTitle(ScGlobal::GetRscString(STR_CHART_ZTITLE)); + + // + // Zahlen-Typ + // + +// SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); +// if (pFormatter) +// { +// ULONG nIndex = 0; +// ULONG nCount = GetPositionMap()->GetCount(); +// const ScAddress* pPos; +// do +// { +// pPos = GetPositionMap()->GetPosition( nIndex ); +// } while ( !pPos && ++nIndex < nCount ); +// ULONG nFormat = ( pPos ? pDocument->GetNumberFormat( *pPos ) : 0 ); +// pMemChart->SetDataType( pFormatter->GetType( nFormat ) ); +// } + + // + // Parameter-Strings + // + +// SetExtraStrings( *pMemChart ); + } + + return pMemChart; +} + +#ifdef _MSC_VER +#pragma optimize("",on) +#endif + + +// +// Collection +// + +ScDataObject* ScChartCollection::Clone() const +{ + return new ScChartCollection(*this); +} + +BOOL ScChartCollection::operator==(const ScChartCollection& rCmp) const +{ + if (nCount != rCmp.nCount) + return FALSE; + + for (USHORT i=0; i<nCount; i++) + if (!((*(const ScChartArray*)pItems[i]) == (*(const ScChartArray*)rCmp.pItems[i]))) + return FALSE; + + return TRUE; +} + diff --git a/sc/source/core/tool/charthelper.cxx b/sc/source/core/tool/charthelper.cxx new file mode 100644 index 000000000000..1b2cde3d4a6d --- /dev/null +++ b/sc/source/core/tool/charthelper.cxx @@ -0,0 +1,298 @@ +/************************************************************************* + * + * 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 "charthelper.hxx" +#include "document.hxx" +#include "drwlayer.hxx" +#include "rangelst.hxx" +#include "chartlis.hxx" + +//#include <vcl/svapp.hxx> +#include <svx/svditer.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdpage.hxx> + +#include <com/sun/star/chart2/data/XDataReceiver.hpp> + +using namespace com::sun::star; +using ::com::sun::star::uno::Reference; + + +// ==================================================================== + +namespace +{ + + +USHORT lcl_DoUpdateCharts( const ScAddress& rPos, ScDocument* pDoc, BOOL bAllCharts ) +{ + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + if (!pModel) + return 0; + + USHORT nFound = 0; + + USHORT nPageCount = pModel->GetPageCount(); + for (USHORT nPageNo=0; nPageNo<nPageCount; nPageNo++) + { + SdrPage* pPage = pModel->GetPage(nPageNo); + DBG_ASSERT(pPage,"Page ?"); + + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == OBJ_OLE2 && pDoc->IsChart( pObject ) ) + { + String aName = ((SdrOle2Obj*)pObject)->GetPersistName(); + BOOL bHit = TRUE; + if ( !bAllCharts ) + { + ScRangeList aRanges; + BOOL bColHeaders = FALSE; + BOOL bRowHeaders = FALSE; + pDoc->GetOldChartParameters( aName, aRanges, bColHeaders, bRowHeaders ); + bHit = aRanges.In( rPos ); + } + if ( bHit ) + { + pDoc->UpdateChart( aName ); + ++nFound; + } + } + pObject = aIter.Next(); + } + } + return nFound; +} + +BOOL lcl_AdjustRanges( ScRangeList& rRanges, SCTAB nSourceTab, SCTAB nDestTab, SCTAB nTabCount ) +{ + //! if multiple sheets are copied, update references into the other copied sheets? + + BOOL bChanged = FALSE; + + ULONG nCount = rRanges.Count(); + for (ULONG i=0; i<nCount; i++) + { + ScRange* pRange = rRanges.GetObject(i); + if ( pRange->aStart.Tab() == nSourceTab && pRange->aEnd.Tab() == nSourceTab ) + { + pRange->aStart.SetTab( nDestTab ); + pRange->aEnd.SetTab( nDestTab ); + bChanged = TRUE; + } + if ( pRange->aStart.Tab() >= nTabCount ) + { + pRange->aStart.SetTab( nTabCount > 0 ? ( nTabCount - 1 ) : 0 ); + bChanged = TRUE; + } + if ( pRange->aEnd.Tab() >= nTabCount ) + { + pRange->aEnd.SetTab( nTabCount > 0 ? ( nTabCount - 1 ) : 0 ); + bChanged = TRUE; + } + } + + return bChanged; +} + +}//end anonymous namespace + +// === ScChartHelper ====================================== + +//static +USHORT ScChartHelper::DoUpdateCharts( const ScAddress& rPos, ScDocument* pDoc ) +{ + return lcl_DoUpdateCharts( rPos, pDoc, FALSE ); +} + +//static +USHORT ScChartHelper::DoUpdateAllCharts( ScDocument* pDoc ) +{ + return lcl_DoUpdateCharts( ScAddress(), pDoc, TRUE ); +} + +//static +void ScChartHelper::AdjustRangesOfChartsOnDestinationPage( ScDocument* pSrcDoc, ScDocument* pDestDoc, const SCTAB nSrcTab, const SCTAB nDestTab ) +{ + if( !pSrcDoc || !pDestDoc ) + return; + ScDrawLayer* pDrawLayer = pDestDoc->GetDrawLayer(); + if( !pDrawLayer ) + return; + + SdrPage* pDestPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nDestTab)); + if( pDestPage ) + { + SdrObjListIter aIter( *pDestPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while( pObject ) + { + if( pObject->GetObjIdentifier() == OBJ_OLE2 && ((SdrOle2Obj*)pObject)->IsChart() ) + { + String aChartName = ((SdrOle2Obj*)pObject)->GetPersistName(); + + Reference< chart2::XChartDocument > xChartDoc( pDestDoc->GetChartByName( aChartName ) ); + Reference< chart2::data::XDataReceiver > xReceiver( xChartDoc, uno::UNO_QUERY ); + if( xChartDoc.is() && xReceiver.is() && !xChartDoc->hasInternalDataProvider() ) + { + ::std::vector< ScRangeList > aRangesVector; + pDestDoc->GetChartRanges( aChartName, aRangesVector, pSrcDoc ); + + ::std::vector< ScRangeList >::iterator aIt( aRangesVector.begin() ); + for( ; aIt!=aRangesVector.end(); aIt++ ) + { + ScRangeList& rScRangeList( *aIt ); + lcl_AdjustRanges( rScRangeList, nSrcTab, nDestTab, pDestDoc->GetTableCount() ); + } + pDestDoc->SetChartRanges( aChartName, aRangesVector ); + } + } + pObject = aIter.Next(); + } + } +} + +//static +uno::Reference< chart2::XChartDocument > ScChartHelper::GetChartFromSdrObject( SdrObject* pObject ) +{ + uno::Reference< chart2::XChartDocument > xReturn; + if( pObject ) + { + if( pObject->GetObjIdentifier() == OBJ_OLE2 && ((SdrOle2Obj*)pObject)->IsChart() ) + { + uno::Reference< embed::XEmbeddedObject > xIPObj = ((SdrOle2Obj*)pObject)->GetObjRef(); + if( xIPObj.is() ) + { + svt::EmbeddedObjectRef::TryRunningState( xIPObj ); + uno::Reference< util::XCloseable > xComponent = xIPObj->getComponent(); + xReturn.set( uno::Reference< chart2::XChartDocument >( xComponent, uno::UNO_QUERY ) ); + } + } + } + return xReturn; +} + +void ScChartHelper::GetChartRanges( const uno::Reference< chart2::XChartDocument >& xChartDoc, + uno::Sequence< rtl::OUString >& rRanges ) +{ + rRanges.realloc(0); + uno::Reference< chart2::data::XDataSource > xDataSource( xChartDoc, uno::UNO_QUERY ); + if( !xDataSource.is() ) + return; + //uno::Reference< chart2::data::XDataProvider > xProvider = xChartDoc->getDataProvider(); + + uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLabeledDataSequences( xDataSource->getDataSequences() ); + rRanges.realloc(2*aLabeledDataSequences.getLength()); + sal_Int32 nRealCount=0; + for( sal_Int32 nN=0;nN<aLabeledDataSequences.getLength();nN++) + { + uno::Reference< chart2::data::XLabeledDataSequence > xLabeledSequence( aLabeledDataSequences[nN] ); + if(!xLabeledSequence.is()) + continue; + uno::Reference< chart2::data::XDataSequence > xLabel( xLabeledSequence->getLabel()); + uno::Reference< chart2::data::XDataSequence > xValues( xLabeledSequence->getValues()); + + if( xLabel.is()) + rRanges[nRealCount++] = xLabel->getSourceRangeRepresentation(); + if( xValues.is()) + rRanges[nRealCount++] = xValues->getSourceRangeRepresentation(); + } + rRanges.realloc(nRealCount); +} + +void ScChartHelper::SetChartRanges( const uno::Reference< chart2::XChartDocument >& xChartDoc, + const uno::Sequence< rtl::OUString >& rRanges ) +{ + uno::Reference< chart2::data::XDataSource > xDataSource( xChartDoc, uno::UNO_QUERY ); + if( !xDataSource.is() ) + return; + uno::Reference< chart2::data::XDataProvider > xDataProvider = xChartDoc->getDataProvider(); + if( !xDataProvider.is() ) + return; + + uno::Reference< frame::XModel > xModel( xChartDoc, uno::UNO_QUERY ); + if( xModel.is() ) + xModel->lockControllers(); + + try + { + rtl::OUString aPropertyNameRole( ::rtl::OUString::createFromAscii("Role") ); + + uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLabeledDataSequences( xDataSource->getDataSequences() ); + sal_Int32 nRange=0; + for( sal_Int32 nN=0; (nN<aLabeledDataSequences.getLength()) && (nRange<rRanges.getLength()); nN++ ) + { + uno::Reference< chart2::data::XLabeledDataSequence > xLabeledSequence( aLabeledDataSequences[nN] ); + if(!xLabeledSequence.is()) + continue; + uno::Reference< beans::XPropertySet > xLabel( xLabeledSequence->getLabel(), uno::UNO_QUERY ); + uno::Reference< beans::XPropertySet > xValues( xLabeledSequence->getValues(), uno::UNO_QUERY ); + + if( xLabel.is()) + { + // the range string must be in Calc A1 format. + uno::Reference< chart2::data::XDataSequence > xNewSeq( + xDataProvider->createDataSequenceByRangeRepresentation( rRanges[nRange++] )); + + uno::Reference< beans::XPropertySet > xNewProps( xNewSeq, uno::UNO_QUERY ); + if( xNewProps.is() ) + xNewProps->setPropertyValue( aPropertyNameRole, xLabel->getPropertyValue( aPropertyNameRole ) ); + + xLabeledSequence->setLabel( xNewSeq ); + } + + if( !(nRange<rRanges.getLength()) ) + break; + + if( xValues.is()) + { + // the range string must be in Calc A1 format. + uno::Reference< chart2::data::XDataSequence > xNewSeq( + xDataProvider->createDataSequenceByRangeRepresentation( rRanges[nRange++] )); + + uno::Reference< beans::XPropertySet > xNewProps( xNewSeq, uno::UNO_QUERY ); + if( xNewProps.is() ) + xNewProps->setPropertyValue( aPropertyNameRole, xValues->getPropertyValue( aPropertyNameRole ) ); + + xLabeledSequence->setValues( xNewSeq ); + } + } + } + catch ( uno::Exception& ex ) + { + (void)ex; + DBG_ERROR("Exception in ScChartHelper::SetChartRanges - invalid range string?"); + } + + if( xModel.is() ) + xModel->unlockControllers(); +} diff --git a/sc/source/core/tool/chartlis.cxx b/sc/source/core/tool/chartlis.cxx new file mode 100644 index 000000000000..b729ce0b30b5 --- /dev/null +++ b/sc/source/core/tool/chartlis.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 <vcl/svapp.hxx> + +#include "chartlis.hxx" +#include "brdcst.hxx" +#include "document.hxx" +#include "reftokenhelper.hxx" + +using namespace com::sun::star; +using ::std::vector; +using ::std::list; +using ::std::hash_set; +using ::std::auto_ptr; +using ::std::unary_function; +using ::std::for_each; + +//2do: DocOption TimeOut? +//#define SC_CHARTTIMEOUT 1000 // eine Sekunde keine Aenderung/KeyEvent + +// Update chart listeners quickly, to get a similar behavior to loaded charts +// which register UNO listeners. +#define SC_CHARTTIMEOUT 10 + + +// ==================================================================== + +class ScChartUnoData +{ + uno::Reference< chart::XChartDataChangeEventListener > xListener; + uno::Reference< chart::XChartData > xSource; + +public: + ScChartUnoData( const uno::Reference< chart::XChartDataChangeEventListener >& rL, + const uno::Reference< chart::XChartData >& rS ) : + xListener( rL ), xSource( rS ) {} + ~ScChartUnoData() {} + + const uno::Reference< chart::XChartDataChangeEventListener >& GetListener() const { return xListener; } + const uno::Reference< chart::XChartData >& GetSource() const { return xSource; } +}; + + +// === ScChartListener ================================================ + +ScChartListener::ExternalRefListener::ExternalRefListener(ScChartListener& rParent, ScDocument* pDoc) : + mrParent(rParent), mpDoc(pDoc) +{ +} + +ScChartListener::ExternalRefListener::~ExternalRefListener() +{ + if (!mpDoc || mpDoc->IsInDtorClear()) + // The document is being destroyed. Do nothing. + return; + + // Make sure to remove all pointers to this object. + mpDoc->GetExternalRefManager()->removeLinkListener(this); +} + +void ScChartListener::ExternalRefListener::notify(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) +{ + switch (eType) + { + case ScExternalRefManager::LINK_MODIFIED: + { + if (maFileIds.count(nFileId)) + // We are listening to this external document. Send an update + // requst to the chart. + mrParent.SetUpdateQueue(); + } + break; + case ScExternalRefManager::LINK_BROKEN: + removeFileId(nFileId); + break; + } +} + +void ScChartListener::ExternalRefListener::addFileId(sal_uInt16 nFileId) +{ + maFileIds.insert(nFileId); +} + +void ScChartListener::ExternalRefListener::removeFileId(sal_uInt16 nFileId) +{ + maFileIds.erase(nFileId); +} + +hash_set<sal_uInt16>& ScChartListener::ExternalRefListener::getAllFileIds() +{ + return maFileIds; +} + +// ---------------------------------------------------------------------------- + +ScChartListener::ScChartListener( const String& rName, ScDocument* pDocP, + const ScRange& rRange ) : + StrData( rName ), + SvtListener(), + mpExtRefListener(NULL), + mpTokens(new vector<ScSharedTokenRef>), + pUnoData( NULL ), + pDoc( pDocP ), + bUsed( FALSE ), + bDirty( FALSE ), + bSeriesRangesScheduled( FALSE ) +{ + SetRangeList( rRange ); +} + +ScChartListener::ScChartListener( const String& rName, ScDocument* pDocP, + const ScRangeListRef& rRangeList ) : + StrData( rName ), + SvtListener(), + mpExtRefListener(NULL), + mpTokens(new vector<ScSharedTokenRef>), + pUnoData( NULL ), + pDoc( pDocP ), + bUsed( FALSE ), + bDirty( FALSE ), + bSeriesRangesScheduled( FALSE ) +{ + ScRefTokenHelper::getTokensFromRangeList(*mpTokens, *rRangeList); +} + +ScChartListener::ScChartListener( const String& rName, ScDocument* pDocP, vector<ScSharedTokenRef>* pTokens ) : + StrData( rName ), + SvtListener(), + mpExtRefListener(NULL), + mpTokens(pTokens), + pUnoData( NULL ), + pDoc( pDocP ), + bUsed( FALSE ), + bDirty( FALSE ), + bSeriesRangesScheduled( FALSE ) +{ +} + +ScChartListener::ScChartListener( const ScChartListener& r ) : + StrData( r ), + SvtListener(), + mpExtRefListener(NULL), + mpTokens(new vector<ScSharedTokenRef>(*r.mpTokens)), + pUnoData( NULL ), + pDoc( r.pDoc ), + bUsed( FALSE ), + bDirty( r.bDirty ), + bSeriesRangesScheduled( r.bSeriesRangesScheduled ) +{ + if ( r.pUnoData ) + pUnoData = new ScChartUnoData( *r.pUnoData ); + + if (r.mpExtRefListener.get()) + { + // Re-register this new listener for the files that the old listener + // was listening to. + + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + const hash_set<sal_uInt16>& rFileIds = r.mpExtRefListener->getAllFileIds(); + mpExtRefListener.reset(new ExternalRefListener(*this, pDoc)); + hash_set<sal_uInt16>::const_iterator itr = rFileIds.begin(), itrEnd = rFileIds.end(); + for (; itr != itrEnd; ++itr) + { + pRefMgr->addLinkListener(*itr, mpExtRefListener.get()); + mpExtRefListener->addFileId(*itr); + } + } +} + +ScChartListener::~ScChartListener() +{ + if ( HasBroadcaster() ) + EndListeningTo(); + delete pUnoData; + + if (mpExtRefListener.get()) + { + // Stop listening to all external files. + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + const hash_set<sal_uInt16>& rFileIds = mpExtRefListener->getAllFileIds(); + hash_set<sal_uInt16>::const_iterator itr = rFileIds.begin(), itrEnd = rFileIds.end(); + for (; itr != itrEnd; ++itr) + pRefMgr->removeLinkListener(*itr, mpExtRefListener.get()); + } +} + +ScDataObject* ScChartListener::Clone() const +{ + return new ScChartListener( *this ); +} + +void ScChartListener::SetUno( + const uno::Reference< chart::XChartDataChangeEventListener >& rListener, + const uno::Reference< chart::XChartData >& rSource ) +{ +// DBG_ASSERT( rListener.is() && rSource.is(), "Nullpointer bei SetUno" ); + delete pUnoData; + pUnoData = new ScChartUnoData( rListener, rSource ); +} + +uno::Reference< chart::XChartDataChangeEventListener > ScChartListener::GetUnoListener() const +{ + if ( pUnoData ) + return pUnoData->GetListener(); + return uno::Reference< chart::XChartDataChangeEventListener >(); +} + +uno::Reference< chart::XChartData > ScChartListener::GetUnoSource() const +{ + if ( pUnoData ) + return pUnoData->GetSource(); + return uno::Reference< chart::XChartData >(); +} + +void ScChartListener::Notify( SvtBroadcaster&, const SfxHint& rHint ) +{ + const ScHint* p = dynamic_cast<const ScHint*>(&rHint); + if (p && (p->GetId() & (SC_HINT_DATACHANGED | SC_HINT_DYING))) + SetUpdateQueue(); +} + +void ScChartListener::Update() +{ + if ( pDoc->IsInInterpreter() ) + { // #73482# If interpreting do nothing and restart timer so we don't + // interfere with interpreter and don't produce an Err522 or similar. + // This may happen if we are rescheduled via Basic function. + pDoc->GetChartListenerCollection()->StartTimer(); + return ; + } + if ( pUnoData ) + { + bDirty = FALSE; + //! irgendwann mal erkennen, was sich innerhalb des Charts geaendert hat + chart::ChartDataChangeEvent aEvent( pUnoData->GetSource(), + chart::ChartDataChangeType_ALL, + 0, 0, 0, 0 ); + pUnoData->GetListener()->chartDataChanged( aEvent ); + } + else if ( pDoc->GetAutoCalc() ) + { + bDirty = FALSE; + pDoc->UpdateChart( GetString()); + } +} + +ScRangeListRef ScChartListener::GetRangeList() const +{ + ScRangeListRef aRLRef(new ScRangeList); + ScRefTokenHelper::getRangeListFromTokens(*aRLRef, *mpTokens); + return aRLRef; +} + +void ScChartListener::SetRangeList( const ScRangeListRef& rNew ) +{ + vector<ScSharedTokenRef> aTokens; + ScRefTokenHelper::getTokensFromRangeList(aTokens, *rNew); + mpTokens->swap(aTokens); +} + +void ScChartListener::SetRangeList( const ScRange& rRange ) +{ + ScSharedTokenRef pToken; + ScRefTokenHelper::getTokenFromRange(pToken, rRange); + mpTokens->push_back(pToken); +} + +namespace { + +class StartEndListening : public unary_function<ScSharedTokenRef, void> +{ +public: + StartEndListening(ScDocument* pDoc, ScChartListener& rParent, bool bStart) : + mpDoc(pDoc), mrParent(rParent), mbStart(bStart) {} + + void operator() (const ScSharedTokenRef& pToken) + { + if (!ScRefTokenHelper::isRef(pToken)) + return; + + bool bExternal = ScRefTokenHelper::isExternalRef(pToken); + if (bExternal) + { + sal_uInt16 nFileId = pToken->GetIndex(); + ScExternalRefManager* pRefMgr = mpDoc->GetExternalRefManager(); + ScChartListener::ExternalRefListener* pExtRefListener = mrParent.GetExtRefListener(); + if (mbStart) + { + pRefMgr->addLinkListener(nFileId, pExtRefListener); + pExtRefListener->addFileId(nFileId); + } + else + { + pRefMgr->removeLinkListener(nFileId, pExtRefListener); + pExtRefListener->removeFileId(nFileId); + } + } + else + { + ScRange aRange; + ScRefTokenHelper::getRangeFromToken(aRange, pToken, bExternal); + if (mbStart) + startListening(aRange); + else + endListening(aRange); + } + } + +private: + void startListening(const ScRange& rRange) + { + if (rRange.aStart == rRange.aEnd) + mpDoc->StartListeningCell(rRange.aStart, &mrParent); + else + mpDoc->StartListeningArea(rRange, &mrParent); + } + + void endListening(const ScRange& rRange) + { + if (rRange.aStart == rRange.aEnd) + mpDoc->EndListeningCell(rRange.aStart, &mrParent); + else + mpDoc->EndListeningArea(rRange, &mrParent); + } + +private: + ScDocument* mpDoc; + ScChartListener& mrParent; + bool mbStart; +}; + +} + +void ScChartListener::StartListeningTo() +{ + if (!mpTokens.get() || mpTokens->empty()) + // no references to listen to. + return; + + for_each(mpTokens->begin(), mpTokens->end(), StartEndListening(pDoc, *this, true)); +} + +void ScChartListener::EndListeningTo() +{ + if (!mpTokens.get() || mpTokens->empty()) + // no references to listen to. + return; + + for_each(mpTokens->begin(), mpTokens->end(), StartEndListening(pDoc, *this, false)); +} + + +void ScChartListener::ChangeListening( const ScRangeListRef& rRangeListRef, + BOOL bDirtyP ) +{ + EndListeningTo(); + SetRangeList( rRangeListRef ); + StartListeningTo(); + if ( bDirtyP ) + SetDirty( TRUE ); +} + + +void ScChartListener::UpdateScheduledSeriesRanges() +{ + if ( bSeriesRangesScheduled ) + { + bSeriesRangesScheduled = FALSE; + UpdateSeriesRanges(); + } +} + + +void ScChartListener::UpdateChartIntersecting( const ScRange& rRange ) +{ + ScSharedTokenRef pToken; + ScRefTokenHelper::getTokenFromRange(pToken, rRange); + + if (ScRefTokenHelper::intersects(*mpTokens, pToken)) + { + // force update (chart has to be loaded), don't use ScChartListener::Update + pDoc->UpdateChart( GetString()); + } +} + + +void ScChartListener::UpdateSeriesRanges() +{ + ScRangeListRef pRangeList(new ScRangeList); + ScRefTokenHelper::getRangeListFromTokens(*pRangeList, *mpTokens); + pDoc->SetChartRangeList(GetString(), pRangeList); +} + +ScChartListener::ExternalRefListener* ScChartListener::GetExtRefListener() +{ + if (!mpExtRefListener.get()) + mpExtRefListener.reset(new ExternalRefListener(*this, pDoc)); + + return mpExtRefListener.get(); +} + +void ScChartListener::SetUpdateQueue() +{ + bDirty = true; + pDoc->GetChartListenerCollection()->StartTimer(); +} + +BOOL ScChartListener::operator==( const ScChartListener& r ) +{ + bool b1 = (mpTokens.get() && !mpTokens->empty()); + bool b2 = (r.mpTokens.get() && !r.mpTokens->empty()); + + if (pDoc != r.pDoc || bUsed != r.bUsed || bDirty != r.bDirty || + bSeriesRangesScheduled != r.bSeriesRangesScheduled || + GetString() != r.GetString() || b1 != b2) + return false; + + if (!b1 && !b2) + // both token list instances are empty. + return true; + + return *mpTokens == *r.mpTokens; +} + +// ============================================================================ + +ScChartHiddenRangeListener::ScChartHiddenRangeListener() +{ +} + +ScChartHiddenRangeListener::~ScChartHiddenRangeListener() +{ + // empty d'tor +} + +// === ScChartListenerCollection ====================================== + +ScChartListenerCollection::RangeListenerItem::RangeListenerItem(const ScRange& rRange, ScChartHiddenRangeListener* p) : + maRange(rRange), mpListener(p) +{ +} + +ScChartListenerCollection::ScChartListenerCollection( ScDocument* pDocP ) : + ScStrCollection( 4, 4, FALSE ), + pDoc( pDocP ) +{ + aTimer.SetTimeoutHdl( LINK( this, ScChartListenerCollection, TimerHdl ) ); +} + +ScChartListenerCollection::ScChartListenerCollection( + const ScChartListenerCollection& rColl ) : + ScStrCollection( rColl ), + pDoc( rColl.pDoc ) +{ + aTimer.SetTimeoutHdl( LINK( this, ScChartListenerCollection, TimerHdl ) ); +} + +ScChartListenerCollection::~ScChartListenerCollection() +{ + // #96783# remove ChartListener objects before aTimer dtor is called, because + // ScChartListener::EndListeningTo may cause ScChartListenerCollection::StartTimer + // to be called if an empty ScNoteCell is deleted + + if (GetCount()) + FreeAll(); +} + +ScDataObject* ScChartListenerCollection::Clone() const +{ + return new ScChartListenerCollection( *this ); +} + +void ScChartListenerCollection::StartAllListeners() +{ + for ( USHORT nIndex = 0; nIndex < nCount; nIndex++ ) + { + ((ScChartListener*) pItems[ nIndex ])->StartListeningTo(); + } +} + +void ScChartListenerCollection::ChangeListening( const String& rName, + const ScRangeListRef& rRangeListRef, BOOL bDirty ) +{ + ScChartListener aCLSearcher( rName, pDoc, rRangeListRef ); + ScChartListener* pCL; + USHORT nIndex; + if ( Search( &aCLSearcher, nIndex ) ) + { + pCL = (ScChartListener*) pItems[ nIndex ]; + pCL->EndListeningTo(); + pCL->SetRangeList( rRangeListRef ); + } + else + { + pCL = new ScChartListener( aCLSearcher ); + Insert( pCL ); + } + pCL->StartListeningTo(); + if ( bDirty ) + pCL->SetDirty( TRUE ); +} + +void ScChartListenerCollection::FreeUnused() +{ + // rueckwaerts wg. Pointer-Aufrueckerei im Array + for ( USHORT nIndex = nCount; nIndex-- >0; ) + { + ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; + // Uno-Charts nicht rauskicken + // (werden per FreeUno von aussen geloescht) + if ( !pCL->IsUno() ) + { + if ( pCL->IsUsed() ) + pCL->SetUsed( FALSE ); + else + Free( pCL ); + } + } +} + +void ScChartListenerCollection::FreeUno( const uno::Reference< chart::XChartDataChangeEventListener >& rListener, + const uno::Reference< chart::XChartData >& rSource ) +{ + // rueckwaerts wg. Pointer-Aufrueckerei im Array + for ( USHORT nIndex = nCount; nIndex-- >0; ) + { + ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; + if ( pCL->IsUno() && + pCL->GetUnoListener() == rListener && + pCL->GetUnoSource() == rSource ) + { + Free( pCL ); + } + //! sollte nur einmal vorkommen? + } +} + +void ScChartListenerCollection::StartTimer() +{ + aTimer.SetTimeout( SC_CHARTTIMEOUT ); + aTimer.Start(); +} + +IMPL_LINK( ScChartListenerCollection, TimerHdl, Timer*, EMPTYARG ) +{ + if ( Application::AnyInput( INPUT_KEYBOARD ) ) + { + aTimer.Start(); + return 0; + } + UpdateDirtyCharts(); + return 0; +} + +void ScChartListenerCollection::UpdateDirtyCharts() +{ + for ( USHORT nIndex = 0; nIndex < nCount; nIndex++ ) + { + ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; + if ( pCL->IsDirty() ) + pCL->Update(); + if ( aTimer.IsActive() && !pDoc->IsImportingXML()) + break; // da kam einer dazwischen + } +} + + +void ScChartListenerCollection::SetDirty() +{ + for ( USHORT nIndex = 0; nIndex < nCount; nIndex++ ) + { + ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; + pCL->SetDirty( TRUE ); + } + StartTimer(); +} + + +void ScChartListenerCollection::SetDiffDirty( + const ScChartListenerCollection& rCmp, BOOL bSetChartRangeLists ) +{ + BOOL bDirty = FALSE; + for ( USHORT nIndex = 0; nIndex < nCount; nIndex++ ) + { + ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; + USHORT nFound; + BOOL bFound = rCmp.Search( pCL, nFound ); + if ( !bFound || (*pCL != *((const ScChartListener*) rCmp.pItems[ nFound ])) ) + { + if ( bSetChartRangeLists ) + { + if ( bFound ) + { + const ScRangeListRef& rList1 = pCL->GetRangeList(); + const ScRangeListRef& rList2 = + ((const ScChartListener*) rCmp.pItems[ nFound ])->GetRangeList(); + BOOL b1 = rList1.Is(); + BOOL b2 = rList2.Is(); + if ( b1 != b2 || (b1 && b2 && (*rList1 != *rList2)) ) + pDoc->SetChartRangeList( pCL->GetString(), rList1 ); + } + else + pDoc->SetChartRangeList( pCL->GetString(), pCL->GetRangeList() ); + } + bDirty = TRUE; + pCL->SetDirty( TRUE ); + } + } + if ( bDirty ) + StartTimer(); +} + + +void ScChartListenerCollection::SetRangeDirty( const ScRange& rRange ) +{ + BOOL bDirty = FALSE; + for ( USHORT nIndex = 0; nIndex < nCount; nIndex++ ) + { + ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; + const ScRangeListRef& rList = pCL->GetRangeList(); + if ( rList.Is() && rList->Intersects( rRange ) ) + { + bDirty = TRUE; + pCL->SetDirty( TRUE ); + } + } + if ( bDirty ) + StartTimer(); + + // New hidden range listener implementation + for (list<RangeListenerItem>::iterator itr = maHiddenListeners.begin(), itrEnd = maHiddenListeners.end(); + itr != itrEnd; ++itr) + { + if (itr->maRange.Intersects(rRange)) + itr->mpListener->notify(); + } +} + + +void ScChartListenerCollection::UpdateScheduledSeriesRanges() +{ + for ( USHORT nIndex = 0; nIndex < nCount; nIndex++ ) + { + ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; + pCL->UpdateScheduledSeriesRanges(); + } +} + + +void ScChartListenerCollection::UpdateChartsContainingTab( SCTAB nTab ) +{ + ScRange aRange( 0, 0, nTab, MAXCOL, MAXROW, nTab ); + for ( USHORT nIndex = 0; nIndex < nCount; nIndex++ ) + { + ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; + pCL->UpdateChartIntersecting( aRange ); + } +} + + +BOOL ScChartListenerCollection::operator==( const ScChartListenerCollection& r ) +{ + // hier nicht ScStrCollection::operator==() verwenden, der umstaendlich via + // IsEqual und Compare laeuft, stattdessen ScChartListener::operator==() + if ( pDoc != r.pDoc || nCount != r.nCount ) + return FALSE; + for ( USHORT nIndex = 0; nIndex < nCount; nIndex++ ) + { + if ( *((ScChartListener*) pItems[ nIndex ]) != + *((ScChartListener*) r.pItems[ nIndex ]) ) + return FALSE; + } + return TRUE; +} + +void ScChartListenerCollection::StartListeningHiddenRange( const ScRange& rRange, ScChartHiddenRangeListener* pListener ) +{ + RangeListenerItem aItem(rRange, pListener); + maHiddenListeners.push_back(aItem); +} + +namespace { + +struct MatchListener : public ::std::unary_function< + ScChartListenerCollection::RangeListenerItem, bool> +{ + MatchListener(const ScChartHiddenRangeListener* pMatch) : + mpMatch(pMatch) + { + } + + bool operator() (const ScChartListenerCollection::RangeListenerItem& rItem) const + { + return mpMatch == rItem.mpListener; + } + +private: + const ScChartHiddenRangeListener* mpMatch; +}; + +} +void ScChartListenerCollection::EndListeningHiddenRange( ScChartHiddenRangeListener* pListener ) +{ + maHiddenListeners.remove_if(MatchListener(pListener)); +} + diff --git a/sc/source/core/tool/chartlock.cxx b/sc/source/core/tool/chartlock.cxx new file mode 100644 index 000000000000..54a49fb4b4aa --- /dev/null +++ b/sc/source/core/tool/chartlock.cxx @@ -0,0 +1,195 @@ +/************************************************************************* + * + * 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 <vcl/svapp.hxx> +#include <svx/svditer.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdpage.hxx> + +#include "chartlock.hxx" +#include "document.hxx" +#include "drwlayer.hxx" + +using namespace com::sun::star; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::WeakReference; + +#define SC_CHARTLOCKTIMEOUT 660 + +// ==================================================================== + +namespace +{ + +std::vector< WeakReference< frame::XModel > > lcl_getAllLivingCharts( ScDocument* pDoc ) +{ + std::vector< WeakReference< frame::XModel > > aRet; + if( !pDoc ) + return aRet; + ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer(); + if (!pDrawLayer) + return aRet; + + for (SCTAB nTab=0; nTab<=pDoc->GetMaxTableNumber(); nTab++) + { + if (pDoc->HasTable(nTab)) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if( pDoc->IsChart( pObject ) ) + { + uno::Reference< embed::XEmbeddedObject > xIPObj = ((SdrOle2Obj*)pObject)->GetObjRef(); + uno::Reference< embed::XComponentSupplier > xCompSupp( xIPObj, uno::UNO_QUERY ); + if( xCompSupp.is()) + { + Reference< frame::XModel > xModel( xCompSupp->getComponent(), uno::UNO_QUERY ); + if( xModel.is() ) + aRet.push_back( xModel ); + } + } + pObject = aIter.Next(); + } + } + } + return aRet; +} + +}//end anonymous namespace + +// === ScChartLockGuard ====================================== + +ScChartLockGuard::ScChartLockGuard( ScDocument* pDoc ) : + maChartModels( lcl_getAllLivingCharts( pDoc ) ) +{ + std::vector< WeakReference< frame::XModel > >::const_iterator aIter = maChartModels.begin(); + const std::vector< WeakReference< frame::XModel > >::const_iterator aEnd = maChartModels.end(); + for( ; aIter != aEnd; ++aIter ) + { + try + { + Reference< frame::XModel > xModel( *aIter ); + if( xModel.is()) + xModel->lockControllers(); + } + catch ( uno::Exception& ) + { + DBG_ERROR("Unexpected exception in ScChartLockGuard"); + } + } +} + +ScChartLockGuard::~ScChartLockGuard() +{ + std::vector< WeakReference< frame::XModel > >::const_iterator aIter = maChartModels.begin(); + const std::vector< WeakReference< frame::XModel > >::const_iterator aEnd = maChartModels.end(); + for( ; aIter != aEnd; ++aIter ) + { + try + { + Reference< frame::XModel > xModel( *aIter ); + if( xModel.is()) + xModel->unlockControllers(); + } + catch ( uno::Exception& ) + { + DBG_ERROR("Unexpected exception in ScChartLockGuard"); + } + } +} + +void ScChartLockGuard::AlsoLockThisChart( const Reference< frame::XModel >& xModel ) +{ + if(!xModel.is()) + return; + + WeakReference< frame::XModel > xWeakModel(xModel); + + std::vector< WeakReference< frame::XModel > >::iterator aFindIter( + ::std::find( maChartModels.begin(), maChartModels.end(), xWeakModel ) ); + + if( aFindIter == maChartModels.end() ) + { + try + { + xModel->lockControllers(); + maChartModels.push_back( xModel ); + } + catch ( uno::Exception& ) + { + DBG_ERROR("Unexpected exception in ScChartLockGuard"); + } + } +} + +// === ScTemporaryChartLock ====================================== + +ScTemporaryChartLock::ScTemporaryChartLock( ScDocument* pDocP ) : + mpDoc( pDocP ) +{ + maTimer.SetTimeout( SC_CHARTLOCKTIMEOUT ); + maTimer.SetTimeoutHdl( LINK( this, ScTemporaryChartLock, TimeoutHdl ) ); +} + + +ScTemporaryChartLock::~ScTemporaryChartLock() +{ + mpDoc = 0; + StopLocking(); +} + +void ScTemporaryChartLock::StartOrContinueLocking() +{ + if(!mapScChartLockGuard.get()) + mapScChartLockGuard = std::auto_ptr< ScChartLockGuard >( new ScChartLockGuard(mpDoc) ); + maTimer.Start(); +} + +void ScTemporaryChartLock::StopLocking() +{ + maTimer.Stop(); + mapScChartLockGuard.reset(); +} + +void ScTemporaryChartLock::AlsoLockThisChart( const Reference< frame::XModel >& xModel ) +{ + if(mapScChartLockGuard.get()) + mapScChartLockGuard->AlsoLockThisChart( xModel ); +} + +IMPL_LINK( ScTemporaryChartLock, TimeoutHdl, Timer*, EMPTYARG ) +{ + mapScChartLockGuard.reset(); + return 0; +} diff --git a/sc/source/core/tool/chartpos.cxx b/sc/source/core/tool/chartpos.cxx new file mode 100644 index 000000000000..58ee1b06f7f7 --- /dev/null +++ b/sc/source/core/tool/chartpos.cxx @@ -0,0 +1,623 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <tools/table.hxx> + +#include "chartpos.hxx" +#include "document.hxx" +#include "rechead.hxx" + + +ScChartPositioner::ScChartPositioner( ScDocument* pDoc, SCTAB nTab, + SCCOL nStartColP, SCROW nStartRowP, SCCOL nEndColP, SCROW nEndRowP) : + pDocument( pDoc ), + pPositionMap( NULL ), + eGlue( SC_CHARTGLUE_NA ), + nStartCol(0), + nStartRow(0), + bColHeaders( FALSE ), + bRowHeaders( FALSE ), + bDummyUpperLeft( FALSE ) +{ + SetRangeList( ScRange( nStartColP, nStartRowP, nTab, nEndColP, nEndRowP, nTab ) ); + CheckColRowHeaders(); +} + +ScChartPositioner::ScChartPositioner( ScDocument* pDoc, const ScRangeListRef& rRangeList ) : + aRangeListRef( rRangeList ), + pDocument( pDoc ), + pPositionMap( NULL ), + eGlue( SC_CHARTGLUE_NA ), + nStartCol(0), + nStartRow(0), + bColHeaders( FALSE ), + bRowHeaders( FALSE ), + bDummyUpperLeft( FALSE ) +{ + if ( aRangeListRef.Is() ) + CheckColRowHeaders(); +} + +ScChartPositioner::ScChartPositioner( const ScChartPositioner& rPositioner ) : + aRangeListRef( rPositioner.aRangeListRef ), + pDocument(rPositioner.pDocument), + pPositionMap( NULL ), + eGlue(rPositioner.eGlue), + nStartCol(rPositioner.nStartCol), + nStartRow(rPositioner.nStartRow), + bColHeaders(rPositioner.bColHeaders), + bRowHeaders(rPositioner.bRowHeaders), + bDummyUpperLeft( rPositioner.bDummyUpperLeft ) +{ +} + +ScChartPositioner::~ScChartPositioner() +{ + delete pPositionMap; +} + +BOOL ScChartPositioner::operator==(const ScChartPositioner& rCmp) const +{ + return bColHeaders == rCmp.bColHeaders + && bRowHeaders == rCmp.bRowHeaders + && *aRangeListRef == *rCmp.aRangeListRef; +} + +void ScChartPositioner::SetRangeList( const ScRange& rRange ) +{ + aRangeListRef = new ScRangeList; + aRangeListRef->Append( rRange ); + InvalidateGlue(); +} + +void ScChartPositioner::GlueState() +{ + if ( eGlue != SC_CHARTGLUE_NA ) + return; + bDummyUpperLeft = FALSE; + ScRangePtr pR; + if ( aRangeListRef->Count() <= 1 ) + { + if ( (pR = aRangeListRef->First())!=NULL ) + { + if ( pR->aStart.Tab() == pR->aEnd.Tab() ) + eGlue = SC_CHARTGLUE_NONE; + else + eGlue = SC_CHARTGLUE_COLS; // mehrere Tabellen spaltenweise + nStartCol = pR->aStart.Col(); + nStartRow = pR->aStart.Row(); + } + else + { + InvalidateGlue(); + nStartCol = 0; + nStartRow = 0; + } + return; + } +// ULONG nOldPos = aRangeListRef->GetCurPos(); + + pR = aRangeListRef->First(); + nStartCol = pR->aStart.Col(); + nStartRow = pR->aStart.Row(); + SCCOL nMaxCols, nEndCol; + SCROW nMaxRows, nEndRow; + nMaxCols = nEndCol = 0; + nMaxRows = nEndRow = 0; + do + { // umspannenden Bereich etc. feststellen + SCCOLROW nTmp, n1, n2; + if ( (n1 = pR->aStart.Col()) < nStartCol ) + nStartCol = static_cast<SCCOL>(n1); + if ( (n2 = pR->aEnd.Col()) > nEndCol ) + nEndCol = static_cast<SCCOL>(n2); + if ( (nTmp = n2 - n1 + 1) > nMaxCols ) + nMaxCols = static_cast<SCCOL>(nTmp); + if ( (n1 = pR->aStart.Row()) < nStartRow ) + nStartRow = static_cast<SCROW>(n1); + if ( (n2 = pR->aEnd.Row()) > nEndRow ) + nEndRow = static_cast<SCROW>(n2); + if ( (nTmp = n2 - n1 + 1) > nMaxRows ) + nMaxRows = static_cast<SCROW>(nTmp); + } while ( (pR = aRangeListRef->Next())!=NULL ); + SCCOL nC = nEndCol - nStartCol + 1; + if ( nC == 1 ) + { + eGlue = SC_CHARTGLUE_ROWS; + return; + } + SCROW nR = nEndRow - nStartRow + 1; + if ( nR == 1 ) + { + eGlue = SC_CHARTGLUE_COLS; + return; + } + ULONG nCR = (ULONG)nC * nR; +//2do: +/* + Erstmal simpel ohne Bitmaskiererei, maximal koennten so 8MB alloziert + werden (256 Cols mal 32000 Rows), das liesse sich mit 2 Bit je Eintrag + auf 2MB reduzieren, andererseits ist es so schneller. + Weitere Platz-Optimierung waere, in dem Array nur die wirklich benutzten + Zeilen/Spalten abzulegen, wuerde aber ein weiteres durchlaufen der + RangeList und indirekten Zugriff auf das Array bedeuten. + */ + const BYTE nHole = 0; + const BYTE nOccu = 1; + const BYTE nFree = 2; + const BYTE nGlue = 3; + BYTE* p; + BYTE* pA = new BYTE[ nCR ]; + memset( pA, 0, nCR * sizeof(BYTE) ); + + SCCOL nCol, nCol1, nCol2; + SCROW nRow, nRow1, nRow2; + for ( pR = aRangeListRef->First(); pR; pR = aRangeListRef->Next() ) + { // Selektionen 2D als belegt markieren + nCol1 = pR->aStart.Col() - nStartCol; + nCol2 = pR->aEnd.Col() - nStartCol; + nRow1 = pR->aStart.Row() - nStartRow; + nRow2 = pR->aEnd.Row() - nStartRow; + for ( nCol = nCol1; nCol <= nCol2; nCol++ ) + { + p = pA + (ULONG)nCol * nR + nRow1; + for ( nRow = nRow1; nRow <= nRow2; nRow++, p++ ) + *p = nOccu; + } + } + BOOL bGlue = TRUE; + + BOOL bGlueCols = FALSE; + for ( nCol = 0; bGlue && nCol < nC; nCol++ ) + { // Spalten probieren durchzugehen und als frei markieren + p = pA + (ULONG)nCol * nR; + for ( nRow = 0; bGlue && nRow < nR; nRow++, p++ ) + { + if ( *p == nOccu ) + { // Wenn einer mittendrin liegt ist keine Zusammenfassung + // moeglich. Am Rand koennte ok sein, wenn in dieser Spalte + // in jeder belegten Zeile einer belegt ist. + if ( nRow > 0 && nCol > 0 ) + bGlue = FALSE; // nCol==0 kann DummyUpperLeft sein + else + nRow = nR; + } + else + *p = nFree; + } + if ( bGlue && *(p = (pA + ((((ULONG)nCol+1) * nR) - 1))) == nFree ) + { // Spalte als komplett frei markieren + *p = nGlue; + bGlueCols = TRUE; // mindestens eine freie Spalte + } + } + + BOOL bGlueRows = FALSE; + for ( nRow = 0; bGlue && nRow < nR; nRow++ ) + { // Zeilen probieren durchzugehen und als frei markieren + p = pA + nRow; + for ( nCol = 0; bGlue && nCol < nC; nCol++, p+=nR ) + { + if ( *p == nOccu ) + { + if ( nCol > 0 && nRow > 0 ) + bGlue = FALSE; // nRow==0 kann DummyUpperLeft sein + else + nCol = nC; + } + else + *p = nFree; + } + if ( bGlue && *(p = (pA + ((((ULONG)nC-1) * nR) + nRow))) == nFree ) + { // Zeile als komplett frei markieren + *p = nGlue; + bGlueRows = TRUE; // mindestens eine freie Zeile + } + } + + // n=1: die linke obere Ecke koennte bei Beschriftung automagisch + // hinzugezogen werden + p = pA + 1; + for ( ULONG n = 1; bGlue && n < nCR; n++, p++ ) + { // ein unberuehrtes Feld heisst, dass es weder spaltenweise noch + // zeilenweise zu erreichen war, also nichts zusamenzufassen + if ( *p == nHole ) + bGlue = FALSE; + } + if ( bGlue ) + { + if ( bGlueCols && bGlueRows ) + eGlue = SC_CHARTGLUE_BOTH; + else if ( bGlueRows ) + eGlue = SC_CHARTGLUE_ROWS; + else + eGlue = SC_CHARTGLUE_COLS; + if ( *pA != nOccu ) + bDummyUpperLeft = TRUE; + } + else + { + eGlue = SC_CHARTGLUE_NONE; + } + + delete [] pA; +} + +void ScChartPositioner::CheckColRowHeaders() +{ + SCCOL nCol1, nCol2, iCol; + SCROW nRow1, nRow2, iRow; + SCTAB nTab1, nTab2; + + BOOL bColStrings = TRUE; + BOOL bRowStrings = TRUE; + GlueState(); + if ( aRangeListRef->Count() == 1 ) + { + aRangeListRef->First()->GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if ( nCol1 > nCol2 || nRow1 > nRow2 ) + bColStrings = bRowStrings = FALSE; + else + { + for (iCol=nCol1; iCol<=nCol2 && bColStrings; iCol++) + { + if (pDocument->HasValueData( iCol, nRow1, nTab1 )) + bColStrings = FALSE; + } + for (iRow=nRow1; iRow<=nRow2 && bRowStrings; iRow++) + { + if (pDocument->HasValueData( nCol1, iRow, nTab1 )) + bRowStrings = FALSE; + } + } + } + else + { + BOOL bVert = (eGlue == SC_CHARTGLUE_NONE || eGlue == SC_CHARTGLUE_ROWS); + for ( ScRangePtr pR = aRangeListRef->First(); + pR && (bColStrings || bRowStrings); + pR = aRangeListRef->Next() ) + { + pR->GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + BOOL bTopRow = (nRow1 == nStartRow); + if ( bRowStrings && (bVert || nCol1 == nStartCol) ) + { // NONE oder ROWS: RowStrings in jeder Selektion moeglich + // COLS oder BOTH: nur aus der ersten Spalte + if ( nCol1 <= nCol2 ) + for (iRow=nRow1; iRow<=nRow2 && bRowStrings; iRow++) + { + if (pDocument->HasValueData( nCol1, iRow, nTab1 )) + bRowStrings = FALSE; + } + } + if ( bColStrings && bTopRow ) + { // ColStrings nur aus der ersten Zeile + if ( nRow1 <= nRow2 ) + for (iCol=nCol1; iCol<=nCol2 && bColStrings; iCol++) + { + if (pDocument->HasValueData( iCol, nRow1, nTab1 )) + bColStrings = FALSE; + } + } + } + } + bColHeaders = bColStrings; + bRowHeaders = bRowStrings; +} + +const ScChartPositionMap* ScChartPositioner::GetPositionMap() +{ + CreatePositionMap(); + return pPositionMap; +} + + +void ScChartPositioner::CreatePositionMap() +{ + if ( eGlue == SC_CHARTGLUE_NA && pPositionMap ) + { + delete pPositionMap; + pPositionMap = NULL; + } + + if ( pPositionMap ) + return ; + + SCSIZE nColAdd = bRowHeaders ? 1 : 0; + SCSIZE nRowAdd = bColHeaders ? 1 : 0; + + SCCOL nCol, nCol1, nCol2; + SCROW nRow, nRow1, nRow2; + SCTAB nTab, nTab1, nTab2; + + // + // wirkliche Groesse (ohne versteckte Zeilen/Spalten) + // + + SCSIZE nColCount = 0; + SCSIZE nRowCount = 0; + + GlueState(); + + BOOL bNoGlue = (eGlue == SC_CHARTGLUE_NONE); + Table* pCols = new Table; + Table* pNewRowTable = new Table; + ScAddress* pNewAddress = new ScAddress; + ScRangePtr pR; + Table* pCol; + ScAddress* pPos; + SCROW nNoGlueRow = 0; + for ( pR = aRangeListRef->First(); pR; pR = aRangeListRef->Next() ) + { + pR->GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + for ( nTab = nTab1; nTab <= nTab2; nTab++ ) + { + // nTab im ColKey, um gleiche Col/Row in anderer Table haben zu koennen + ULONG nInsCol = (static_cast<ULONG>(nTab) << 16) | (bNoGlue ? 0 : + static_cast<ULONG>(nCol1)); + for ( nCol = nCol1; nCol <= nCol2; ++nCol, ++nInsCol ) + { + if ( bNoGlue || eGlue == SC_CHARTGLUE_ROWS ) + { // meistens gleiche Cols + if ( (pCol = (Table*) pCols->Get( nInsCol ))==NULL ) + { + pCols->Insert( nInsCol, pNewRowTable ); + pCol = pNewRowTable; + pNewRowTable = new Table; + } + } + else + { // meistens neue Cols + if ( pCols->Insert( nInsCol, pNewRowTable ) ) + { + pCol = pNewRowTable; + pNewRowTable = new Table; + } + else + pCol = (Table*) pCols->Get( nInsCol ); + } + // bei anderer Tabelle wurde bereits neuer ColKey erzeugt, + // die Zeilen muessen fuer's Dummy fuellen gleich sein! + ULONG nInsRow = (bNoGlue ? nNoGlueRow : nRow1); + for ( nRow = nRow1; nRow <= nRow2; nRow++, nInsRow++ ) + { + if ( pCol->Insert( nInsRow, pNewAddress ) ) + { + pNewAddress->Set( nCol, nRow, nTab ); + pNewAddress = new ScAddress; + } + } + } + } + // bei NoGlue werden zusammengehoerige Tabellen als ColGlue dargestellt + nNoGlueRow += nRow2 - nRow1 + 1; + } + delete pNewAddress; + delete pNewRowTable; + + // Anzahl der Daten + nColCount = static_cast< SCSIZE >( pCols->Count()); + if ( (pCol = (Table*) pCols->First())!=NULL ) + { + if ( bDummyUpperLeft ) + pCol->Insert( 0, (void*)0 ); // Dummy fuer Beschriftung + nRowCount = static_cast< SCSIZE >( pCol->Count()); + } + else + nRowCount = 0; + if ( nColCount > 0 ) + nColCount -= nColAdd; + if ( nRowCount > 0 ) + nRowCount -= nRowAdd; + + if ( nColCount==0 || nRowCount==0 ) + { // einen Eintrag ohne Daten erzeugen + pR = aRangeListRef->First(); + if ( pCols->Count() > 0 ) + pCol = (Table*) pCols->First(); + else + { + pCol = new Table; + pCols->Insert( 0, pCol ); + } + nColCount = 1; + if ( pCol->Count() > 0 ) + { // kann ja eigentlich nicht sein, wenn nColCount==0 || nRowCount==0 + pPos = (ScAddress*) pCol->First(); + if ( pPos ) + { + delete pPos; + pCol->Replace( pCol->GetCurKey(), (void*)0 ); + } + } + else + pCol->Insert( 0, (void*)0 ); + nRowCount = 1; + nColAdd = 0; + nRowAdd = 0; + } + else + { + if ( bNoGlue ) + { // Luecken mit Dummies fuellen, erste Spalte ist Master + Table* pFirstCol = (Table*) pCols->First(); + ULONG nCount = pFirstCol->Count(); + pFirstCol->First(); + for ( ULONG n = 0; n < nCount; n++, pFirstCol->Next() ) + { + ULONG nKey = pFirstCol->GetCurKey(); + pCols->First(); + while ( (pCol = (Table*) pCols->Next())!=NULL ) + pCol->Insert( nKey, (void*)0 ); // keine Daten + } + } + } + + pPositionMap = new ScChartPositionMap( static_cast<SCCOL>(nColCount), static_cast<SCROW>(nRowCount), + static_cast<SCCOL>(nColAdd), static_cast<SCROW>(nRowAdd), *pCols ); + + // Aufraeumen + for ( pCol = (Table*) pCols->First(); pCol; pCol = (Table*) pCols->Next() ) + { //! nur Tables loeschen, nicht die ScAddress* + delete pCol; + } + delete pCols; +} + + +ScChartPositionMap::ScChartPositionMap( SCCOL nChartCols, SCROW nChartRows, + SCCOL nColAdd, SCROW nRowAdd, Table& rCols ) : + ppData( new ScAddress* [ nChartCols * nChartRows ] ), + ppColHeader( new ScAddress* [ nChartCols ] ), + ppRowHeader( new ScAddress* [ nChartRows ] ), + nCount( (ULONG) nChartCols * nChartRows ), + nColCount( nChartCols ), + nRowCount( nChartRows ) +{ + DBG_ASSERT( nColCount && nRowCount, "ScChartPositionMap without dimension" ); + + ScAddress* pPos; + SCCOL nCol; + SCROW nRow; + + Table* pCol = (Table*) rCols.First(); + + // Zeilen-Header + pPos = (ScAddress*) pCol->First(); + if ( nRowAdd ) + pPos = (ScAddress*) pCol->Next(); + if ( nColAdd ) + { // eigenstaendig + for ( nRow = 0; nRow < nRowCount; nRow++ ) + { + ppRowHeader[ nRow ] = pPos; + pPos = (ScAddress*) pCol->Next(); + } + } + else + { // Kopie + for ( nRow = 0; nRow < nRowCount; nRow++ ) + { + ppRowHeader[ nRow ] = ( pPos ? new ScAddress( *pPos ) : NULL ); + pPos = (ScAddress*) pCol->Next(); + } + } + if ( nColAdd ) + pCol = (Table*) rCols.Next(); + + // Daten spaltenweise und Spalten-Header + ULONG nIndex = 0; + for ( nCol = 0; nCol < nColCount; nCol++ ) + { + if ( pCol ) + { + pPos = (ScAddress*) pCol->First(); + if ( nRowAdd ) + { + ppColHeader[ nCol ] = pPos; // eigenstaendig + pPos = (ScAddress*) pCol->Next(); + } + else + ppColHeader[ nCol ] = ( pPos ? new ScAddress( *pPos ) : NULL ); + for ( nRow = 0; nRow < nRowCount; nRow++, nIndex++ ) + { + ppData[ nIndex ] = pPos; + pPos = (ScAddress*) pCol->Next(); + } + } + else + { + ppColHeader[ nCol ] = NULL; + for ( nRow = 0; nRow < nRowCount; nRow++, nIndex++ ) + { + ppData[ nIndex ] = NULL; + } + } + pCol = (Table*) rCols.Next(); + } +} + + +ScChartPositionMap::~ScChartPositionMap() +{ + for ( ULONG nIndex=0; nIndex < nCount; nIndex++ ) + { + delete ppData[nIndex]; + } + delete [] ppData; + + SCCOL j; + for ( j=0; j < nColCount; j++ ) + { + delete ppColHeader[j]; + } + delete [] ppColHeader; + SCROW i; + for ( i=0; i < nRowCount; i++ ) + { + delete ppRowHeader[i]; + } + delete [] ppRowHeader; +} + + +//UNUSED2009-05 ScRangeListRef ScChartPositionMap::GetColRanges( SCCOL nChartCol ) const +//UNUSED2009-05 { +//UNUSED2009-05 ScRangeListRef xRangeList = new ScRangeList; +//UNUSED2009-05 if ( nChartCol < nColCount ) +//UNUSED2009-05 { +//UNUSED2009-05 ULONG nStop = GetIndex( nChartCol, nRowCount ); +//UNUSED2009-05 for ( ULONG nIndex = GetIndex( nChartCol, 0 ); nIndex < nStop; nIndex++ ) +//UNUSED2009-05 { +//UNUSED2009-05 if ( ppData[ nIndex ] ) +//UNUSED2009-05 xRangeList->Join( *ppData[ nIndex ] ); +//UNUSED2009-05 } +//UNUSED2009-05 } +//UNUSED2009-05 return xRangeList; +//UNUSED2009-05 } + + +//UNUSED2009-05 ScRangeListRef ScChartPositionMap::GetRowRanges( SCROW nChartRow ) const +//UNUSED2009-05 { +//UNUSED2009-05 ScRangeListRef xRangeList = new ScRangeList; +//UNUSED2009-05 if ( nChartRow < nRowCount ) +//UNUSED2009-05 { +//UNUSED2009-05 ULONG nStop = GetIndex( nColCount, nChartRow ); +//UNUSED2009-05 for ( ULONG nIndex = GetIndex( 0, nChartRow ); nIndex < nStop; +//UNUSED2009-05 nIndex += nRowCount ) +//UNUSED2009-05 { +//UNUSED2009-05 if ( ppData[ nIndex ] ) +//UNUSED2009-05 xRangeList->Join( *ppData[ nIndex ] ); +//UNUSED2009-05 } +//UNUSED2009-05 } +//UNUSED2009-05 return xRangeList; +//UNUSED2009-05 } diff --git a/sc/source/core/tool/chgtrack.cxx b/sc/source/core/tool/chgtrack.cxx new file mode 100644 index 000000000000..ba3f3e47bfb7 --- /dev/null +++ b/sc/source/core/tool/chgtrack.cxx @@ -0,0 +1,4869 @@ +/************************************************************************* + * + * 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 <tools/debug.hxx> +#include <tools/shl.hxx> // SHL_CALC +#include <tools/stack.hxx> +#include <tools/rtti.hxx> +#include <svl/zforlist.hxx> +#include <svl/itemset.hxx> +#include <svl/isethint.hxx> +#include <svl/itempool.hxx> +#include <sfx2/app.hxx> +#include <unotools/useroptions.hxx> +#include <sfx2/sfxsids.hrc> + +#include "cell.hxx" +#include "document.hxx" +#include "dociter.hxx" +#include "global.hxx" +#include "rechead.hxx" +#include "scerrors.hxx" +#include "scmod.hxx" // SC_MOD +#include "inputopt.hxx" // GetExpandRefs +#include "patattr.hxx" +#include "hints.hxx" + +#include "globstr.hrc" + +#include <stack> + +#define SC_CHGTRACK_CXX +#include "chgtrack.hxx" + +DECLARE_STACK( ScChangeActionStack, ScChangeAction* ) + +const USHORT nMemPoolChangeActionCellListEntry = (0x2000 - 64) / sizeof(ScChangeActionCellListEntry); +IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionCellListEntry, nMemPoolChangeActionCellListEntry, nMemPoolChangeActionCellListEntry ) + +const USHORT nMemPoolChangeActionLinkEntry = (0x8000 - 64) / sizeof(ScChangeActionLinkEntry); +IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionLinkEntry, nMemPoolChangeActionLinkEntry, nMemPoolChangeActionLinkEntry ) + +// loaded MSB > eigenes => inkompatibel +#define SC_CHGTRACK_FILEFORMAT_FIRST 0x0001 +#define SC_CHGTRACK_FILEFORMAT 0x0001 + +// --- ScChangeActionLinkEntry --------------------------------------------- + +#if DEBUG_CHANGETRACK +String ScChangeActionLinkEntry::ToString() const +{ + String aReturn; + if ( pAction ) + { + aReturn = String::CreateFromInt64( static_cast< sal_Int64 >( pAction->GetActionNumber() ) ); + } + else if ( pLink && pLink->pAction ) + { + aReturn = String::CreateFromAscii( "*" ); + aReturn += String::CreateFromInt64( static_cast< sal_Int64 >( pLink->pAction->GetActionNumber() ) ); + } + else + { + aReturn = String::CreateFromAscii( "-" ); + } + + return aReturn; +} +#endif // DEBUG_CHANGETRACK + +// --- ScChangeAction ------------------------------------------------------ + +ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScRange& rRange ) + : + aBigRange( rRange ), + pNext( NULL ), + pPrev( NULL ), + pLinkAny( NULL ), + pLinkDeletedIn( NULL ), + pLinkDeleted( NULL ), + pLinkDependent( NULL ), + nAction( 0 ), + nRejectAction( 0 ), + eType( eTypeP ), + eState( SC_CAS_VIRGIN ) +{ + aDateTime.ConvertToUTC(); +} + +ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScBigRange& rRange, + const ULONG nTempAction, const ULONG nTempRejectAction, + const ScChangeActionState eTempState, const DateTime& aTempDateTime, + const String& aTempUser, const String& aTempComment) + : + aBigRange( rRange ), + aDateTime( aTempDateTime ), + aUser( aTempUser ), + aComment( aTempComment ), + pNext( NULL ), + pPrev( NULL ), + pLinkAny( NULL ), + pLinkDeletedIn( NULL ), + pLinkDeleted( NULL ), + pLinkDependent( NULL ), + nAction( nTempAction ), + nRejectAction( nTempRejectAction ), + eType( eTypeP ), + eState( eTempState ) +{ +} + +ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScBigRange& rRange, + const ULONG nTempAction) + : + aBigRange( rRange ), + pNext( NULL ), + pPrev( NULL ), + pLinkAny( NULL ), + pLinkDeletedIn( NULL ), + pLinkDeleted( NULL ), + pLinkDependent( NULL ), + nAction( nTempAction ), + nRejectAction( 0 ), + eType( eTypeP ), + eState( SC_CAS_VIRGIN ) +{ + aDateTime.ConvertToUTC(); +} + + +ScChangeAction::~ScChangeAction() +{ + RemoveAllLinks(); +} + + +BOOL ScChangeAction::IsVisible() const +{ + //! sequence order of execution is significant + if ( IsRejected() || GetType() == SC_CAT_DELETE_TABS || IsDeletedIn() ) + return FALSE; + if ( GetType() == SC_CAT_CONTENT ) + return ((ScChangeActionContent*)this)->IsTopContent(); + return TRUE; +} + + +BOOL ScChangeAction::IsTouchable() const +{ + //! sequence order of execution is significant + if ( IsRejected() || GetType() == SC_CAT_REJECT || IsDeletedIn() ) + return FALSE; + // content may reject and be touchable if on top + if ( GetType() == SC_CAT_CONTENT ) + return ((ScChangeActionContent*)this)->IsTopContent(); + if ( IsRejecting() ) + return FALSE; + return TRUE; +} + + +BOOL ScChangeAction::IsClickable() const +{ + //! sequence order of execution is significant + if ( !IsVirgin() ) + return FALSE; + if ( IsDeletedIn() ) + return FALSE; + if ( GetType() == SC_CAT_CONTENT ) + { + ScChangeActionContentCellType eCCT = + ScChangeActionContent::GetContentCellType( + ((ScChangeActionContent*)this)->GetNewCell() ); + if ( eCCT == SC_CACCT_MATREF ) + return FALSE; + if ( eCCT == SC_CACCT_MATORG ) + { // no Accept-Select if one of the references is in a deleted col/row + const ScChangeActionLinkEntry* pL = + ((ScChangeActionContent*)this)->GetFirstDependentEntry(); + while ( pL ) + { + ScChangeAction* p = (ScChangeAction*) pL->GetAction(); + if ( p && p->IsDeletedIn() ) + return FALSE; + pL = pL->GetNext(); + } + } + return TRUE; // for Select() a content doesn't have to be touchable + } + return IsTouchable(); // Accept()/Reject() only on touchables +} + + +BOOL ScChangeAction::IsRejectable() const +{ + //! sequence order of execution is significant + if ( !IsClickable() ) + return FALSE; + if ( GetType() == SC_CAT_CONTENT ) + { + if ( ((ScChangeActionContent*)this)->IsOldMatrixReference() ) + return FALSE; + ScChangeActionContent* pNextContent = + ((ScChangeActionContent*)this)->GetNextContent(); + if ( pNextContent == NULL ) + return TRUE; // *this is TopContent + return pNextContent->IsRejected(); // *this is next rejectable + } + return IsTouchable(); +} + + +BOOL ScChangeAction::IsInternalRejectable() const +{ + //! sequence order of execution is significant + if ( !IsVirgin() ) + return FALSE; + if ( IsDeletedIn() ) + return FALSE; + if ( GetType() == SC_CAT_CONTENT ) + { + ScChangeActionContent* pNextContent = + ((ScChangeActionContent*)this)->GetNextContent(); + if ( pNextContent == NULL ) + return TRUE; // *this is TopContent + return pNextContent->IsRejected(); // *this is next rejectable + } + return IsTouchable(); +} + + +BOOL ScChangeAction::IsDialogRoot() const +{ + return IsInternalRejectable(); // only rejectables in root +} + + +BOOL ScChangeAction::IsDialogParent() const +{ + //! sequence order of execution is significant + if ( GetType() == SC_CAT_CONTENT ) + { + if ( !IsDialogRoot() ) + return FALSE; + if ( ((ScChangeActionContent*)this)->IsMatrixOrigin() && HasDependent() ) + return TRUE; + ScChangeActionContent* pPrevContent = + ((ScChangeActionContent*)this)->GetPrevContent(); + return pPrevContent && pPrevContent->IsVirgin(); + } + if ( HasDependent() ) + return IsDeleteType() ? TRUE : !IsDeletedIn(); + if ( HasDeleted() ) + { + if ( IsDeleteType() ) + { + if ( IsDialogRoot() ) + return TRUE; + ScChangeActionLinkEntry* pL = pLinkDeleted; + while ( pL ) + { + ScChangeAction* p = pL->GetAction(); + if ( p && p->GetType() != eType ) + return TRUE; + pL = pL->GetNext(); + } + } + else + return TRUE; + } + return FALSE; +} + + +BOOL ScChangeAction::IsMasterDelete() const +{ + if ( !IsDeleteType() ) + return FALSE; + ScChangeActionDel* pDel = (ScChangeActionDel*) this; + return pDel->IsMultiDelete() && (pDel->IsTopDelete() || pDel->IsRejectable()); +} + + +void ScChangeAction::RemoveAllLinks() +{ + RemoveAllAnyLinks(); + RemoveAllDeletedIn(); + RemoveAllDeleted(); + RemoveAllDependent(); +} + + +void ScChangeAction::RemoveAllAnyLinks() +{ + while ( pLinkAny ) + delete pLinkAny; // rueckt sich selbst hoch +} + + +BOOL ScChangeAction::RemoveDeletedIn( const ScChangeAction* p ) +{ + BOOL bRemoved = FALSE; + ScChangeActionLinkEntry* pL = GetDeletedIn(); + while ( pL ) + { + ScChangeActionLinkEntry* pNextLink = pL->GetNext(); + if ( pL->GetAction() == p ) + { + delete pL; + bRemoved = TRUE; + } + pL = pNextLink; + } + return bRemoved; +} + + +BOOL ScChangeAction::IsDeletedIn( const ScChangeAction* p ) const +{ + ScChangeActionLinkEntry* pL = GetDeletedIn(); + while ( pL ) + { + if ( pL->GetAction() == p ) + return TRUE; + pL = pL->GetNext(); + } + return FALSE; +} + + +void ScChangeAction::RemoveAllDeletedIn() +{ + //! nicht vom evtl. TopContent sondern wirklich dieser + while ( pLinkDeletedIn ) + delete pLinkDeletedIn; // rueckt sich selbst hoch +} + + +BOOL ScChangeAction::IsDeletedInDelType( ScChangeActionType eDelType ) const +{ + ScChangeAction* p; + ScChangeActionLinkEntry* pL = GetDeletedIn(); + if ( pL ) + { + // InsertType fuer MergePrepare/MergeOwn + ScChangeActionType eInsType; + switch ( eDelType ) + { + case SC_CAT_DELETE_COLS : + eInsType = SC_CAT_INSERT_COLS; + break; + case SC_CAT_DELETE_ROWS : + eInsType = SC_CAT_INSERT_ROWS; + break; + case SC_CAT_DELETE_TABS : + eInsType = SC_CAT_INSERT_TABS; + break; + default: + eInsType = SC_CAT_NONE; + } + while ( pL ) + { + if ( (p = pL->GetAction()) != NULL && + (p->GetType() == eDelType || p->GetType() == eInsType) ) + return TRUE; + pL = pL->GetNext(); + } + } + return FALSE; +} + + +void ScChangeAction::SetDeletedIn( ScChangeAction* p ) +{ + ScChangeActionLinkEntry* pLink1 = AddDeletedIn( p ); + ScChangeActionLinkEntry* pLink2; + if ( GetType() == SC_CAT_CONTENT ) + pLink2 = p->AddDeleted( ((ScChangeActionContent*)this)->GetTopContent() ); + else + pLink2 = p->AddDeleted( this ); + pLink1->SetLink( pLink2 ); +} + + +void ScChangeAction::RemoveAllDeleted() +{ + while ( pLinkDeleted ) + delete pLinkDeleted; // rueckt sich selbst hoch +} + + +void ScChangeAction::RemoveAllDependent() +{ + while ( pLinkDependent ) + delete pLinkDependent; // rueckt sich selbst hoch +} + + +DateTime ScChangeAction::GetDateTime() const +{ + DateTime aDT( aDateTime ); + aDT.ConvertToLocalTime(); + return aDT; +} + + +void ScChangeAction::UpdateReference( const ScChangeTrack* /* pTrack */, + UpdateRefMode eMode, const ScBigRange& rRange, + INT32 nDx, INT32 nDy, INT32 nDz ) +{ + ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() ); +} + + +void ScChangeAction::GetDescription( String& rStr, ScDocument* /* pDoc */, + BOOL /* bSplitRange */, bool bWarning ) const +{ + if ( IsRejecting() && bWarning ) + { + // #112261# Add comment if rejection may have resulted in references + // not properly restored in formulas. See specification at + // http://specs.openoffice.org/calc/ease-of-use/redlining_comment.sxw + if (GetType() == SC_CAT_MOVE) + { + rStr += ScGlobal::GetRscString( + STR_CHANGED_MOVE_REJECTION_WARNING); + rStr += ' '; + } + else if (IsInsertType()) + { + rStr += ScGlobal::GetRscString( + STR_CHANGED_DELETE_REJECTION_WARNING); + rStr += ' '; + } + else + { + const ScChangeTrack* pCT = GetChangeTrack(); + if (pCT) + { + ScChangeAction* pReject = pCT->GetActionOrGenerated( + GetRejectAction()); + if (pReject) + { + if (pReject->GetType() == SC_CAT_MOVE) + { + rStr += ScGlobal::GetRscString( + STR_CHANGED_MOVE_REJECTION_WARNING); + rStr += ' '; + } + else if (pReject->IsDeleteType()) + { + rStr += ScGlobal::GetRscString( + STR_CHANGED_DELETE_REJECTION_WARNING); + rStr += ' '; + } + else if (pReject->HasDependent()) + { + ScChangeActionTable aTable; + pCT->GetDependents( pReject, aTable, FALSE, TRUE ); + for ( const ScChangeAction* p = aTable.First(); p; + p = aTable.Next() ) + { + if (p->GetType() == SC_CAT_MOVE) + { + rStr += ScGlobal::GetRscString( + STR_CHANGED_MOVE_REJECTION_WARNING); + rStr += ' '; + break; // for + } + else if (pReject->IsDeleteType()) + { + rStr += ScGlobal::GetRscString( + STR_CHANGED_DELETE_REJECTION_WARNING); + rStr += ' '; + break; // for + } + } + } + } + } + } + } +} + + +String ScChangeAction::GetRefString( const ScBigRange& rRange, + ScDocument* pDoc, BOOL bFlag3D ) const +{ + String aStr; + USHORT nFlags = ( rRange.IsValid( pDoc ) ? SCA_VALID : 0 ); + if ( !nFlags ) + aStr = ScGlobal::GetRscString( STR_NOREF_STR ); + else + { + ScRange aTmpRange( rRange.MakeRange() ); + switch ( GetType() ) + { + case SC_CAT_INSERT_COLS : + case SC_CAT_DELETE_COLS : + if ( bFlag3D ) + { + pDoc->GetName( aTmpRange.aStart.Tab(), aStr ); + aStr += '.'; + } + aStr += ::ScColToAlpha( aTmpRange.aStart.Col() ); + aStr += ':'; + aStr += ::ScColToAlpha( aTmpRange.aEnd.Col() ); + break; + case SC_CAT_INSERT_ROWS : + case SC_CAT_DELETE_ROWS : + if ( bFlag3D ) + { + pDoc->GetName( aTmpRange.aStart.Tab(), aStr ); + aStr += '.'; + } + aStr += String::CreateFromInt32( aTmpRange.aStart.Row() + 1 ); + aStr += ':'; + aStr += String::CreateFromInt32( aTmpRange.aEnd.Row() + 1 ); + break; + default: + if ( bFlag3D || GetType() == SC_CAT_INSERT_TABS ) + nFlags |= SCA_TAB_3D; + aTmpRange.Format( aStr, nFlags, pDoc, pDoc->GetAddressConvention() ); + } + if ( (bFlag3D && IsDeleteType()) || IsDeletedIn() ) + { + aStr.Insert( '(', 0 ); + aStr += ')'; + } + } + return aStr; +} + + +void ScChangeAction::GetRefString( String& rStr, ScDocument* pDoc, + BOOL bFlag3D ) const +{ + rStr = GetRefString( GetBigRange(), pDoc, bFlag3D ); +} + + +void ScChangeAction::Accept() +{ + if ( IsVirgin() ) + { + SetState( SC_CAS_ACCEPTED ); + DeleteCellEntries(); + } +} + + +void ScChangeAction::SetRejected() +{ + if ( IsVirgin() ) + { + SetState( SC_CAS_REJECTED ); + RemoveAllLinks(); + DeleteCellEntries(); + } +} + + +void ScChangeAction::RejectRestoreContents( ScChangeTrack* pTrack, + SCsCOL nDx, SCsROW nDy ) +{ + // Liste der Contents aufbauen + ScChangeActionCellListEntry* pListContents = NULL; + for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() ) + { + ScChangeAction* p = pL->GetAction(); + if ( p && p->GetType() == SC_CAT_CONTENT ) + { + ScChangeActionCellListEntry* pE = new ScChangeActionCellListEntry( + (ScChangeActionContent*) p, pListContents ); + pListContents = pE; + } + } + SetState( SC_CAS_REJECTED ); // vor UpdateReference fuer Move + pTrack->UpdateReference( this, TRUE ); // LinkDeleted freigeben + DBG_ASSERT( !pLinkDeleted, "ScChangeAction::RejectRestoreContents: pLinkDeleted != NULL" ); + // Liste der Contents abarbeiten und loeschen + ScDocument* pDoc = pTrack->GetDocument(); + ScChangeActionCellListEntry* pE = pListContents; + while ( pE ) + { + if ( !pE->pContent->IsDeletedIn() && + pE->pContent->GetBigRange().aStart.IsValid( pDoc ) ) + pE->pContent->PutNewValueToDoc( pDoc, nDx, nDy ); + ScChangeActionCellListEntry* pNextEntry; + pNextEntry = pE->pNext; + delete pE; + pE = pNextEntry; + } + DeleteCellEntries(); // weg mit den generierten +} + + +void ScChangeAction::SetDeletedInThis( ULONG nActionNumber, + const ScChangeTrack* pTrack ) +{ + if ( nActionNumber ) + { + ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber ); + DBG_ASSERT( pAct, "ScChangeAction::SetDeletedInThis: missing Action" ); + if ( pAct ) + pAct->SetDeletedIn( this ); + } +} + + +void ScChangeAction::AddDependent( ULONG nActionNumber, + const ScChangeTrack* pTrack ) +{ + if ( nActionNumber ) + { + ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber ); + DBG_ASSERT( pAct, "ScChangeAction::AddDependent: missing Action" ); + if ( pAct ) + { + ScChangeActionLinkEntry* pLink = AddDependent( pAct ); + pAct->AddLink( this, pLink ); + } + } +} + + +#if DEBUG_CHANGETRACK +String ScChangeAction::ToString( ScDocument* pDoc ) const +{ + String aReturn; + + String aNumber = String::CreateFromInt64( static_cast< sal_Int64 >( GetActionNumber() ) ); + + String aActionState; + ScChangeActionState eActionState = GetState(); + switch ( eActionState ) + { + case SC_CAS_VIRGIN: + { + aActionState = String::CreateFromAscii( " " ); + } + break; + case SC_CAS_ACCEPTED: + { + aActionState = String::CreateFromAscii( "+" ); + } + break; + case SC_CAS_REJECTED: + { + aActionState = String::CreateFromAscii( "-" ); + } + break; + } + + String aRejectAction; + if ( IsRejecting() ) + { + aRejectAction += 'r'; + aRejectAction += String::CreateFromInt64( static_cast< sal_Int64 >( GetRejectAction() ) ); + } + + String aReference; + GetRefString( aReference, pDoc, TRUE ); + + String aAuthor = GetUser(); + + DateTime aDT = GetDateTime(); + String aDate = ScGlobal::pLocaleData->getDate( aDT ); + aDate += ' '; + aDate += ScGlobal::pLocaleData->getTime( aDT, FALSE, FALSE ); + + String aDescription; + GetDescription( aDescription, pDoc ); + + String aLinkAny; + const ScChangeActionLinkEntry* pLinkA = pLinkAny; + while ( pLinkA ) + { + if ( !aLinkAny.Len() ) + { + aLinkAny = String::CreateFromAscii( "(Any:" ); + } + aLinkAny += String::CreateFromAscii( " ->" ); + aLinkAny += pLinkA->ToString(); + pLinkA = pLinkA->GetNext(); + } + if ( aLinkAny.Len() ) + { + aLinkAny += ')'; + } + + String aLinkDeletedIn; + const ScChangeActionLinkEntry* pLinkDI = pLinkDeletedIn; + while ( pLinkDI ) + { + if ( !aLinkDeletedIn.Len() ) + { + aLinkDeletedIn = String::CreateFromAscii( "(DeletedIn:" ); + } + aLinkDeletedIn += String::CreateFromAscii( " ->" ); + aLinkDeletedIn += pLinkDI->ToString(); + pLinkDI = pLinkDI->GetNext(); + } + if ( aLinkDeletedIn.Len() ) + { + aLinkDeletedIn += ')'; + } + + String aLinkDeleted; + const ScChangeActionLinkEntry* pLinkD = pLinkDeleted; + while ( pLinkD ) + { + if ( !aLinkDeleted.Len() ) + { + aLinkDeleted = String::CreateFromAscii( "(Deleted:" ); + } + aLinkDeleted += String::CreateFromAscii( " ->" ); + aLinkDeleted += pLinkD->ToString(); + pLinkD = pLinkD->GetNext(); + } + if ( aLinkDeleted.Len() ) + { + aLinkDeleted += ')'; + } + + String aLinkDependent; + const ScChangeActionLinkEntry* pLinkDp = pLinkDependent; + while ( pLinkDp ) + { + if ( !aLinkDependent.Len() ) + { + aLinkDependent = String::CreateFromAscii( "(Dependent:" ); + } + aLinkDependent += String::CreateFromAscii( " ->" ); + aLinkDependent += pLinkDp->ToString(); + pLinkDp = pLinkDp->GetNext(); + } + if ( aLinkDependent.Len() ) + { + aLinkDependent += ')'; + } + + aReturn += aNumber; + aReturn += aActionState; + aReturn += aRejectAction; + aReturn += String::CreateFromAscii( ": " ); + aReturn += aReference; + aReturn += ' '; + aReturn += aAuthor; + aReturn += ' '; + aReturn += aDate; + aReturn += ' '; + aReturn += aDescription; + aReturn += ' '; + aReturn += aLinkAny; + aReturn += ' '; + aReturn += aLinkDeletedIn; + aReturn += ' '; + aReturn += aLinkDeleted; + aReturn += ' '; + aReturn += aLinkDependent; + + return aReturn; +} +#endif // DEBUG_CHANGETRACK + + +// --- ScChangeActionIns --------------------------------------------------- + +ScChangeActionIns::ScChangeActionIns( const ScRange& rRange ) + : ScChangeAction( SC_CAT_NONE, rRange ) +{ + if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == MAXCOL ) + { + aBigRange.aStart.SetCol( nInt32Min ); + aBigRange.aEnd.SetCol( nInt32Max ); + if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW ) + { + SetType( SC_CAT_INSERT_TABS ); + aBigRange.aStart.SetRow( nInt32Min ); + aBigRange.aEnd.SetRow( nInt32Max ); + } + else + SetType( SC_CAT_INSERT_ROWS ); + } + else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW ) + { + SetType( SC_CAT_INSERT_COLS ); + aBigRange.aStart.SetRow( nInt32Min ); + aBigRange.aEnd.SetRow( nInt32Max ); + } + else + { + DBG_ERROR( "ScChangeActionIns: Block not supported!" ); + } +} + + +ScChangeActionIns::ScChangeActionIns(const ULONG nActionNumber, const ScChangeActionState eStateP, const ULONG nRejectingNumber, + const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String& sComment, + const ScChangeActionType eTypeP) + : + ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment) +{ +} + +ScChangeActionIns::~ScChangeActionIns() +{ +} + + +void ScChangeActionIns::GetDescription( String& rStr, ScDocument* pDoc, + BOOL bSplitRange, bool bWarning ) const +{ + ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning ); + + USHORT nWhatId; + switch ( GetType() ) + { + case SC_CAT_INSERT_COLS : + nWhatId = STR_COLUMN; + break; + case SC_CAT_INSERT_ROWS : + nWhatId = STR_ROW; + break; + default: + nWhatId = STR_AREA; + } + + String aRsc( ScGlobal::GetRscString( STR_CHANGED_INSERT ) ); + xub_StrLen nPos = aRsc.SearchAscii( "#1" ); + rStr += aRsc.Copy( 0, nPos ); + rStr += ScGlobal::GetRscString( nWhatId ); + rStr += ' '; + rStr += GetRefString( GetBigRange(), pDoc ); + rStr += aRsc.Copy( nPos+2 ); +} + + +BOOL ScChangeActionIns::Reject( ScDocument* pDoc ) +{ + if ( !aBigRange.IsValid( pDoc ) ) + return FALSE; + + ScRange aRange( aBigRange.MakeRange() ); + if ( !pDoc->IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(), + aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) ) + return FALSE; + + switch ( GetType() ) + { + case SC_CAT_INSERT_COLS : + pDoc->DeleteCol( aRange ); + break; + case SC_CAT_INSERT_ROWS : + pDoc->DeleteRow( aRange ); + break; + case SC_CAT_INSERT_TABS : + pDoc->DeleteTab( aRange.aStart.Tab() ); + break; + default: + { + // added to avoid warnings + } + } + SetState( SC_CAS_REJECTED ); + RemoveAllLinks(); + return TRUE; +} + + +// --- ScChangeActionDel --------------------------------------------------- + +ScChangeActionDel::ScChangeActionDel( const ScRange& rRange, + SCsCOL nDxP, SCsROW nDyP, ScChangeTrack* pTrackP ) + : + ScChangeAction( SC_CAT_NONE, rRange ), + pTrack( pTrackP ), + pFirstCell( NULL ), + pCutOff( NULL ), + nCutOff( 0 ), + pLinkMove( NULL ), + nDx( nDxP ), + nDy( nDyP ) +{ + if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == MAXCOL ) + { + aBigRange.aStart.SetCol( nInt32Min ); + aBigRange.aEnd.SetCol( nInt32Max ); + if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW ) + { + SetType( SC_CAT_DELETE_TABS ); + aBigRange.aStart.SetRow( nInt32Min ); + aBigRange.aEnd.SetRow( nInt32Max ); + } + else + SetType( SC_CAT_DELETE_ROWS ); + } + else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW ) + { + SetType( SC_CAT_DELETE_COLS ); + aBigRange.aStart.SetRow( nInt32Min ); + aBigRange.aEnd.SetRow( nInt32Max ); + } + else + { + DBG_ERROR( "ScChangeActionDel: Block not supported!" ); + } +} + + +ScChangeActionDel::ScChangeActionDel(const ULONG nActionNumber, const ScChangeActionState eStateP, const ULONG nRejectingNumber, + const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String &sComment, + const ScChangeActionType eTypeP, const SCsCOLROW nD, ScChangeTrack* pTrackP) // wich of nDx and nDy is set is depend on the type + : + ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment), + pTrack( pTrackP ), + pFirstCell( NULL ), + pCutOff( NULL ), + nCutOff( 0 ), + pLinkMove( NULL ), + nDx( 0 ), + nDy( 0 ) +{ + if (eType == SC_CAT_DELETE_COLS) + nDx = static_cast<SCsCOL>(nD); + else if (eType == SC_CAT_DELETE_ROWS) + nDy = static_cast<SCsROW>(nD); +} + +ScChangeActionDel::~ScChangeActionDel() +{ + DeleteCellEntries(); + while ( pLinkMove ) + delete pLinkMove; +} + +void ScChangeActionDel::AddContent( ScChangeActionContent* pContent ) +{ + ScChangeActionCellListEntry* pE = new ScChangeActionCellListEntry( + pContent, pFirstCell ); + pFirstCell = pE; +} + + +void ScChangeActionDel::DeleteCellEntries() +{ + pTrack->DeleteCellEntries( pFirstCell, this ); +} + + +BOOL ScChangeActionDel::IsBaseDelete() const +{ + return !GetDx() && !GetDy(); +} + + +BOOL ScChangeActionDel::IsTopDelete() const +{ + const ScChangeAction* p = GetNext(); + if ( !p || p->GetType() != GetType() ) + return TRUE; + return ((ScChangeActionDel*)p)->IsBaseDelete(); +} + + +BOOL ScChangeActionDel::IsMultiDelete() const +{ + if ( GetDx() || GetDy() ) + return TRUE; + const ScChangeAction* p = GetNext(); + if ( !p || p->GetType() != GetType() ) + return FALSE; + const ScChangeActionDel* pDel = (const ScChangeActionDel*) p; + if ( (pDel->GetDx() > GetDx() || pDel->GetDy() > GetDy()) && + pDel->GetBigRange() == aBigRange ) + return TRUE; + return FALSE; +} + + +BOOL ScChangeActionDel::IsTabDeleteCol() const +{ + if ( GetType() != SC_CAT_DELETE_COLS ) + return FALSE; + const ScChangeAction* p = this; + while ( p && p->GetType() == SC_CAT_DELETE_COLS && + !((const ScChangeActionDel*)p)->IsTopDelete() ) + p = p->GetNext(); + return p && p->GetType() == SC_CAT_DELETE_TABS; +} + + +void ScChangeActionDel::UpdateReference( const ScChangeTrack* /* pTrack */, + UpdateRefMode eMode, const ScBigRange& rRange, + INT32 nDxP, INT32 nDyP, INT32 nDz ) +{ + ScRefUpdate::Update( eMode, rRange, nDxP, nDyP, nDz, GetBigRange() ); + if ( !IsDeletedIn() ) + return ; + // evtl. in "druntergerutschten" anpassen + for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() ) + { + ScChangeAction* p = pL->GetAction(); + if ( p && p->GetType() == SC_CAT_CONTENT && + !GetBigRange().In( p->GetBigRange() ) ) + { + switch ( GetType() ) + { + case SC_CAT_DELETE_COLS : + p->GetBigRange().aStart.SetCol( GetBigRange().aStart.Col() ); + p->GetBigRange().aEnd.SetCol( GetBigRange().aStart.Col() ); + break; + case SC_CAT_DELETE_ROWS : + p->GetBigRange().aStart.SetRow( GetBigRange().aStart.Row() ); + p->GetBigRange().aEnd.SetRow( GetBigRange().aStart.Row() ); + break; + case SC_CAT_DELETE_TABS : + p->GetBigRange().aStart.SetTab( GetBigRange().aStart.Tab() ); + p->GetBigRange().aEnd.SetTab( GetBigRange().aStart.Tab() ); + break; + default: + { + // added to avoid warnings + } + } + } + } +} + + +ScBigRange ScChangeActionDel::GetOverAllRange() const +{ + ScBigRange aTmpRange( GetBigRange() ); + aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() ); + aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() ); + return aTmpRange; +} + + +void ScChangeActionDel::GetDescription( String& rStr, ScDocument* pDoc, + BOOL bSplitRange, bool bWarning ) const +{ + ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning ); + + USHORT nWhatId; + switch ( GetType() ) + { + case SC_CAT_DELETE_COLS : + nWhatId = STR_COLUMN; + break; + case SC_CAT_DELETE_ROWS : + nWhatId = STR_ROW; + break; + default: + nWhatId = STR_AREA; + } + + ScBigRange aTmpRange( GetBigRange() ); + if ( !IsRejected() ) + { + if ( bSplitRange ) + { + aTmpRange.aStart.SetCol( aTmpRange.aStart.Col() + GetDx() ); + aTmpRange.aStart.SetRow( aTmpRange.aStart.Row() + GetDy() ); + } + aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() ); + aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() ); + } + + String aRsc( ScGlobal::GetRscString( STR_CHANGED_DELETE ) ); + xub_StrLen nPos = aRsc.SearchAscii( "#1" ); + rStr += aRsc.Copy( 0, nPos ); + rStr += ScGlobal::GetRscString( nWhatId ); + rStr += ' '; + rStr += GetRefString( aTmpRange, pDoc ); + rStr += aRsc.Copy( nPos+2 ); +} + + +BOOL ScChangeActionDel::Reject( ScDocument* pDoc ) +{ + if ( !aBigRange.IsValid( pDoc ) && GetType() != SC_CAT_DELETE_TABS ) + return FALSE; + + BOOL bOk = TRUE; + + if ( IsTopDelete() ) + { // den kompletten Bereich in einem Rutsch restaurieren + ScBigRange aTmpRange( GetOverAllRange() ); + if ( !aTmpRange.IsValid( pDoc ) ) + { + if ( GetType() == SC_CAT_DELETE_TABS ) + { // wird Tab angehaengt? + if ( aTmpRange.aStart.Tab() > pDoc->GetMaxTableNumber() ) + bOk = FALSE; + } + else + bOk = FALSE; + } + if ( bOk ) + { + ScRange aRange( aTmpRange.MakeRange() ); + // InDelete... fuer Formel UpdateReference in Document + pTrack->SetInDeleteRange( aRange ); + pTrack->SetInDeleteTop( TRUE ); + pTrack->SetInDeleteUndo( TRUE ); + pTrack->SetInDelete( TRUE ); + switch ( GetType() ) + { + case SC_CAT_DELETE_COLS : + if ( !(aRange.aStart.Col() == 0 && aRange.aEnd.Col() == MAXCOL) ) + { // nur wenn nicht TabDelete + if ( ( bOk = pDoc->CanInsertCol( aRange ) ) != FALSE ) + bOk = pDoc->InsertCol( aRange ); + } + break; + case SC_CAT_DELETE_ROWS : + if ( ( bOk = pDoc->CanInsertRow( aRange ) ) != FALSE ) + bOk = pDoc->InsertRow( aRange ); + break; + case SC_CAT_DELETE_TABS : + { +//2do: Tabellennamen merken? + String aName; + pDoc->CreateValidTabName( aName ); + if ( ( bOk = pDoc->ValidNewTabName( aName ) ) != FALSE ) + bOk = pDoc->InsertTab( aRange.aStart.Tab(), aName ); + } + break; + default: + { + // added to avoid warnings + } + } + pTrack->SetInDelete( FALSE ); + pTrack->SetInDeleteUndo( FALSE ); + } + if ( !bOk ) + { + pTrack->SetInDeleteTop( FALSE ); + return FALSE; + } + // InDeleteTop fuer UpdateReference-Undo behalten + } + + // setzt rejected und ruft UpdateReference-Undo und DeleteCellEntries + RejectRestoreContents( pTrack, GetDx(), GetDy() ); + + pTrack->SetInDeleteTop( FALSE ); + RemoveAllLinks(); + return TRUE; +} + + +void ScChangeActionDel::UndoCutOffMoves() +{ // abgeschnittene Moves wiederherstellen, Entries/Links deleten + while ( pLinkMove ) + { + ScChangeActionMove* pMove = pLinkMove->GetMove(); + short nFrom = pLinkMove->GetCutOffFrom(); + short nTo = pLinkMove->GetCutOffTo(); + switch ( GetType() ) + { + case SC_CAT_DELETE_COLS : + if ( nFrom > 0 ) + pMove->GetFromRange().aStart.IncCol( -nFrom ); + else if ( nFrom < 0 ) + pMove->GetFromRange().aEnd.IncCol( -nFrom ); + if ( nTo > 0 ) + pMove->GetBigRange().aStart.IncCol( -nTo ); + else if ( nTo < 0 ) + pMove->GetBigRange().aEnd.IncCol( -nTo ); + break; + case SC_CAT_DELETE_ROWS : + if ( nFrom > 0 ) + pMove->GetFromRange().aStart.IncRow( -nFrom ); + else if ( nFrom < 0 ) + pMove->GetFromRange().aEnd.IncRow( -nFrom ); + if ( nTo > 0 ) + pMove->GetBigRange().aStart.IncRow( -nTo ); + else if ( nTo < 0 ) + pMove->GetBigRange().aEnd.IncRow( -nTo ); + break; + case SC_CAT_DELETE_TABS : + if ( nFrom > 0 ) + pMove->GetFromRange().aStart.IncTab( -nFrom ); + else if ( nFrom < 0 ) + pMove->GetFromRange().aEnd.IncTab( -nFrom ); + if ( nTo > 0 ) + pMove->GetBigRange().aStart.IncTab( -nTo ); + else if ( nTo < 0 ) + pMove->GetBigRange().aEnd.IncTab( -nTo ); + break; + default: + { + // added to avoid warnings + } + } + delete pLinkMove; // rueckt sich selbst hoch + } +} + +void ScChangeActionDel::UndoCutOffInsert() +{ // abgeschnittenes Insert wiederherstellen + if ( pCutOff ) + { + switch ( pCutOff->GetType() ) + { + case SC_CAT_INSERT_COLS : + if ( nCutOff < 0 ) + pCutOff->GetBigRange().aEnd.IncCol( -nCutOff ); + else + pCutOff->GetBigRange().aStart.IncCol( -nCutOff ); + break; + case SC_CAT_INSERT_ROWS : + if ( nCutOff < 0 ) + pCutOff->GetBigRange().aEnd.IncRow( -nCutOff ); + else + pCutOff->GetBigRange().aStart.IncRow( -nCutOff ); + break; + case SC_CAT_INSERT_TABS : + if ( nCutOff < 0 ) + pCutOff->GetBigRange().aEnd.IncTab( -nCutOff ); + else + pCutOff->GetBigRange().aStart.IncTab( -nCutOff ); + break; + default: + { + // added to avoid warnings + } + } + SetCutOffInsert( NULL, 0 ); + } +} + + +// --- ScChangeActionMove -------------------------------------------------- + +ScChangeActionMove::ScChangeActionMove(const ULONG nActionNumber, const ScChangeActionState eStateP, const ULONG nRejectingNumber, + const ScBigRange& aToBigRange, const String& aUserP, const DateTime& aDateTimeP, const String &sComment, + const ScBigRange& aFromBigRange, ScChangeTrack* pTrackP) // wich of nDx and nDy is set is depend on the type + : + ScChangeAction(SC_CAT_MOVE, aToBigRange, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment), + aFromRange(aFromBigRange), + pTrack( pTrackP ), + pFirstCell( NULL ), + nStartLastCut(0), + nEndLastCut(0) +{ +} + +ScChangeActionMove::~ScChangeActionMove() +{ + DeleteCellEntries(); +} + + +void ScChangeActionMove::AddContent( ScChangeActionContent* pContent ) +{ + ScChangeActionCellListEntry* pE = new ScChangeActionCellListEntry( + pContent, pFirstCell ); + pFirstCell = pE; +} + + +void ScChangeActionMove::DeleteCellEntries() +{ + pTrack->DeleteCellEntries( pFirstCell, this ); +} + + +void ScChangeActionMove::UpdateReference( const ScChangeTrack* /* pTrack */, + UpdateRefMode eMode, const ScBigRange& rRange, + INT32 nDx, INT32 nDy, INT32 nDz ) +{ + ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aFromRange ); + ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() ); +} + + +void ScChangeActionMove::GetDelta( INT32& nDx, INT32& nDy, INT32& nDz ) const +{ + const ScBigAddress& rToPos = GetBigRange().aStart; + const ScBigAddress& rFromPos = GetFromRange().aStart; + nDx = rToPos.Col() - rFromPos.Col(); + nDy = rToPos.Row() - rFromPos.Row(); + nDz = rToPos.Tab() - rFromPos.Tab(); +} + + +void ScChangeActionMove::GetDescription( String& rStr, ScDocument* pDoc, + BOOL bSplitRange, bool bWarning ) const +{ + ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning ); + + BOOL bFlag3D = ( GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab() ); + + String aRsc( ScGlobal::GetRscString( STR_CHANGED_MOVE ) ); + + xub_StrLen nPos = 0; + String aTmpStr = ScChangeAction::GetRefString( GetFromRange(), pDoc, bFlag3D ); + nPos = aRsc.SearchAscii( "#1", nPos ); + aRsc.Erase( nPos, 2 ); + aRsc.Insert( aTmpStr, nPos ); + nPos = sal::static_int_cast<xub_StrLen>( nPos + aTmpStr.Len() ); + + aTmpStr = ScChangeAction::GetRefString( GetBigRange(), pDoc, bFlag3D ); + nPos = aRsc.SearchAscii( "#2", nPos ); + aRsc.Erase( nPos, 2 ); + aRsc.Insert( aTmpStr, nPos ); + nPos = sal::static_int_cast<xub_StrLen>( nPos + aTmpStr.Len() ); + + rStr += aRsc; +} + + +void ScChangeActionMove::GetRefString( String& rStr, ScDocument* pDoc, + BOOL bFlag3D ) const +{ + if ( !bFlag3D ) + bFlag3D = ( GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab() ); + rStr = ScChangeAction::GetRefString( GetFromRange(), pDoc, bFlag3D ); + rStr += ','; + rStr += ' '; + rStr += ScChangeAction::GetRefString( GetBigRange(), pDoc, bFlag3D ); +} + + +BOOL ScChangeActionMove::Reject( ScDocument* pDoc ) +{ + if ( !(aBigRange.IsValid( pDoc ) && aFromRange.IsValid( pDoc )) ) + return FALSE; + + ScRange aToRange( aBigRange.MakeRange() ); + ScRange aFrmRange( aFromRange.MakeRange() ); + + BOOL bOk = pDoc->IsBlockEditable( aToRange.aStart.Tab(), + aToRange.aStart.Col(), aToRange.aStart.Row(), + aToRange.aEnd.Col(), aToRange.aEnd.Row() ); + if ( bOk ) + bOk = pDoc->IsBlockEditable( aFrmRange.aStart.Tab(), + aFrmRange.aStart.Col(), aFrmRange.aStart.Row(), + aFrmRange.aEnd.Col(), aFrmRange.aEnd.Row() ); + if ( !bOk ) + return FALSE; + + pTrack->LookUpContents( aToRange, pDoc, 0, 0, 0 ); // zu movende Contents + + pDoc->DeleteAreaTab( aToRange, IDF_ALL ); + pDoc->DeleteAreaTab( aFrmRange, IDF_ALL ); + // Formeln im Dokument anpassen + pDoc->UpdateReference( URM_MOVE, + aFrmRange.aStart.Col(), aFrmRange.aStart.Row(), aFrmRange.aStart.Tab(), + aFrmRange.aEnd.Col(), aFrmRange.aEnd.Row(), aFrmRange.aEnd.Tab(), + (SCsCOL) aFrmRange.aStart.Col() - aToRange.aStart.Col(), + (SCsROW) aFrmRange.aStart.Row() - aToRange.aStart.Row(), + (SCsTAB) aFrmRange.aStart.Tab() - aToRange.aStart.Tab(), NULL ); + + // LinkDependent freigeben, nachfolgendes UpdateReference-Undo setzt + // ToRange->FromRange Dependents + RemoveAllDependent(); + + // setzt rejected und ruft UpdateReference-Undo und DeleteCellEntries + RejectRestoreContents( pTrack, 0, 0 ); + + while ( pLinkDependent ) + { + ScChangeAction* p = pLinkDependent->GetAction(); + if ( p && p->GetType() == SC_CAT_CONTENT ) + { + ScChangeActionContent* pContent = (ScChangeActionContent*) p; + if ( !pContent->IsDeletedIn() && + pContent->GetBigRange().aStart.IsValid( pDoc ) ) + pContent->PutNewValueToDoc( pDoc, 0, 0 ); + // in LookUpContents generierte loeschen + if ( pTrack->IsGenerated( pContent->GetActionNumber() ) && + !pContent->IsDeletedIn() ) + { + pLinkDependent->UnLink(); //! sonst wird der mitgeloescht + pTrack->DeleteGeneratedDelContent( pContent ); + } + } + delete pLinkDependent; + } + + RemoveAllLinks(); + return TRUE; +} + + +// --- ScChangeActionContent ----------------------------------------------- + +const USHORT nMemPoolChangeActionContent = (0x8000 - 64) / sizeof(ScChangeActionContent); +IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionContent, nMemPoolChangeActionContent, nMemPoolChangeActionContent ) + +ScChangeActionContent::ScChangeActionContent( const ULONG nActionNumber, + const ScChangeActionState eStateP, const ULONG nRejectingNumber, + const ScBigRange& aBigRangeP, const String& aUserP, + const DateTime& aDateTimeP, const String& sComment, + ScBaseCell* pTempOldCell, ScDocument* pDoc, const String& sOldValue ) + : + ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment), + aOldValue(sOldValue), + pOldCell(pTempOldCell), + pNewCell(NULL), + pNextContent(NULL), + pPrevContent(NULL), + pNextInSlot(NULL), + ppPrevInSlot(NULL) + +{ + if (pOldCell) + ScChangeActionContent::SetCell( aOldValue, pOldCell, 0, pDoc ); + if ( sOldValue.Len() ) // #i40704# don't overwrite SetCell result with empty string + aOldValue = sOldValue; // set again, because SetCell removes it +} + +ScChangeActionContent::ScChangeActionContent( const ULONG nActionNumber, + ScBaseCell* pTempNewCell, const ScBigRange& aBigRangeP, + ScDocument* pDoc, const String& sNewValue ) + : + ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber), + aNewValue(sNewValue), + pOldCell(NULL), + pNewCell(pTempNewCell), + pNextContent(NULL), + pPrevContent(NULL), + pNextInSlot(NULL), + ppPrevInSlot(NULL) +{ + if (pNewCell) + ScChangeActionContent::SetCell( aNewValue, pNewCell, 0, pDoc ); + if ( sNewValue.Len() ) // #i40704# don't overwrite SetCell result with empty string + aNewValue = sNewValue; // set again, because SetCell removes it +} + +ScChangeActionContent::~ScChangeActionContent() +{ + ClearTrack(); +} + + +void ScChangeActionContent::ClearTrack() +{ + RemoveFromSlot(); + if ( pPrevContent ) + pPrevContent->pNextContent = pNextContent; + if ( pNextContent ) + pNextContent->pPrevContent = pPrevContent; +} + + +ScChangeActionContent* ScChangeActionContent::GetTopContent() const +{ + if ( pNextContent ) + { + ScChangeActionContent* pContent = pNextContent; + while ( pContent->pNextContent && pContent != pContent->pNextContent ) + pContent = pContent->pNextContent; + return pContent; + } + return (ScChangeActionContent*) this; +} + + +ScChangeActionLinkEntry* ScChangeActionContent::GetDeletedIn() const +{ + if ( pNextContent ) + return GetTopContent()->pLinkDeletedIn; + return pLinkDeletedIn; +} + + +ScChangeActionLinkEntry** ScChangeActionContent::GetDeletedInAddress() +{ + if ( pNextContent ) + return GetTopContent()->GetDeletedInAddress(); + return &pLinkDeletedIn; +} + + +void ScChangeActionContent::SetOldValue( const ScBaseCell* pCell, + const ScDocument* pFromDoc, ScDocument* pToDoc, ULONG nFormat ) +{ + ScChangeActionContent::SetValue( aOldValue, pOldCell, + nFormat, pCell, pFromDoc, pToDoc ); +} + + +void ScChangeActionContent::SetOldValue( const ScBaseCell* pCell, + const ScDocument* pFromDoc, ScDocument* pToDoc ) +{ + ScChangeActionContent::SetValue( aOldValue, pOldCell, + aBigRange.aStart.MakeAddress(), pCell, pFromDoc, pToDoc ); +} + + +void ScChangeActionContent::SetNewValue( const ScBaseCell* pCell, + ScDocument* pDoc ) +{ + ScChangeActionContent::SetValue( aNewValue, pNewCell, + aBigRange.aStart.MakeAddress(), pCell, pDoc, pDoc ); +} + + +void ScChangeActionContent::SetOldNewCells( ScBaseCell* pOldCellP, + ULONG nOldFormat, ScBaseCell* pNewCellP, + ULONG nNewFormat, ScDocument* pDoc ) +{ + pOldCell = pOldCellP; + pNewCell = pNewCellP; + ScChangeActionContent::SetCell( aOldValue, pOldCell, nOldFormat, pDoc ); + ScChangeActionContent::SetCell( aNewValue, pNewCell, nNewFormat, pDoc ); +} + +void ScChangeActionContent::SetNewCell( ScBaseCell* pCell, ScDocument* pDoc, const String& rFormatted ) +{ + DBG_ASSERT( !pNewCell, "ScChangeActionContent::SetNewCell: overwriting existing cell" ); + pNewCell = pCell; + ScChangeActionContent::SetCell( aNewValue, pNewCell, 0, pDoc ); + + // #i40704# allow to set formatted text here - don't call SetNewValue with String from XML filter + if ( rFormatted.Len() ) + aNewValue = rFormatted; +} + +void ScChangeActionContent::SetValueString( String& rValue, ScBaseCell*& pCell, + const String& rStr, ScDocument* pDoc ) +{ + if ( pCell ) + { + pCell->Delete(); + pCell = NULL; + } + if ( rStr.Len() > 1 && rStr.GetChar(0) == '=' ) + { + rValue.Erase(); + pCell = new ScFormulaCell( + pDoc, aBigRange.aStart.MakeAddress(), rStr, formula::FormulaGrammar::GRAM_DEFAULT, formula::FormulaGrammar::CONV_OOO ); + ((ScFormulaCell*)pCell)->SetInChangeTrack( TRUE ); + } + else + rValue = rStr; +} + + +void ScChangeActionContent::SetOldValue( const String& rOld, ScDocument* pDoc ) +{ + SetValueString( aOldValue, pOldCell, rOld, pDoc ); +} + + +void ScChangeActionContent::SetNewValue( const String& rNew, ScDocument* pDoc ) +{ + SetValueString( aNewValue, pNewCell, rNew, pDoc ); +} + + +void ScChangeActionContent::GetOldString( String& rStr ) const +{ + GetValueString( rStr, aOldValue, pOldCell ); +} + + +void ScChangeActionContent::GetNewString( String& rStr ) const +{ + GetValueString( rStr, aNewValue, pNewCell ); +} + + +void ScChangeActionContent::GetDescription( String& rStr, ScDocument* pDoc, + BOOL bSplitRange, bool bWarning ) const +{ + ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning ); + + String aRsc( ScGlobal::GetRscString( STR_CHANGED_CELL ) ); + + String aTmpStr; + GetRefString( aTmpStr, pDoc ); + + xub_StrLen nPos = 0; + nPos = aRsc.SearchAscii( "#1", nPos ); + aRsc.Erase( nPos, 2 ); + aRsc.Insert( aTmpStr, nPos ); + nPos = sal::static_int_cast<xub_StrLen>( nPos + aTmpStr.Len() ); + + GetOldString( aTmpStr ); + if ( !aTmpStr.Len() ) + aTmpStr = ScGlobal::GetRscString( STR_CHANGED_BLANK ); + nPos = aRsc.SearchAscii( "#2", nPos ); + aRsc.Erase( nPos, 2 ); + aRsc.Insert( aTmpStr, nPos ); + nPos = sal::static_int_cast<xub_StrLen>( nPos + aTmpStr.Len() ); + + GetNewString( aTmpStr ); + if ( !aTmpStr.Len() ) + aTmpStr = ScGlobal::GetRscString( STR_CHANGED_BLANK ); + nPos = aRsc.SearchAscii( "#3", nPos ); + aRsc.Erase( nPos, 2 ); + aRsc.Insert( aTmpStr, nPos ); + + rStr += aRsc; +} + + +void ScChangeActionContent::GetRefString( String& rStr, ScDocument* pDoc, + BOOL bFlag3D ) const +{ + USHORT nFlags = ( GetBigRange().IsValid( pDoc ) ? SCA_VALID : 0 ); + if ( nFlags ) + { + const ScBaseCell* pCell = GetNewCell(); + if ( ScChangeActionContent::GetContentCellType( pCell ) == SC_CACCT_MATORG ) + { + ScBigRange aLocalBigRange( GetBigRange() ); + SCCOL nC; + SCROW nR; + ((const ScFormulaCell*)pCell)->GetMatColsRows( nC, nR ); + aLocalBigRange.aEnd.IncCol( nC-1 ); + aLocalBigRange.aEnd.IncRow( nR-1 ); + rStr = ScChangeAction::GetRefString( aLocalBigRange, pDoc, bFlag3D ); + + return ; + } + + ScAddress aTmpAddress( GetBigRange().aStart.MakeAddress() ); + if ( bFlag3D ) + nFlags |= SCA_TAB_3D; + aTmpAddress.Format( rStr, nFlags, pDoc, pDoc->GetAddressConvention() ); + if ( IsDeletedIn() ) + { + rStr.Insert( '(', 0 ); + rStr += ')'; + } + } + else + rStr = ScGlobal::GetRscString( STR_NOREF_STR ); +} + + +BOOL ScChangeActionContent::Reject( ScDocument* pDoc ) +{ + if ( !aBigRange.IsValid( pDoc ) ) + return FALSE; + + PutOldValueToDoc( pDoc, 0, 0 ); + + SetState( SC_CAS_REJECTED ); + RemoveAllLinks(); + + return TRUE; +} + + +BOOL ScChangeActionContent::Select( ScDocument* pDoc, ScChangeTrack* pTrack, + BOOL bOldest, Stack* pRejectActions ) +{ + if ( !aBigRange.IsValid( pDoc ) ) + return FALSE; + + ScChangeActionContent* pContent = this; + // accept previous contents + while ( ( pContent = pContent->pPrevContent ) != NULL ) + { + if ( pContent->IsVirgin() ) + pContent->SetState( SC_CAS_ACCEPTED ); + } + ScChangeActionContent* pEnd = pContent = this; + // reject subsequent contents + while ( ( pContent = pContent->pNextContent ) != NULL ) + { + // MatrixOrigin may have dependents, no dependency recursion needed + const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry(); + while ( pL ) + { + ScChangeAction* p = (ScChangeAction*) pL->GetAction(); + if ( p ) + p->SetRejected(); + pL = pL->GetNext(); + } + pContent->SetRejected(); + pEnd = pContent; + } + + if ( bOldest || pEnd != this ) + { // wenn nicht aeltester: ist es ueberhaupt ein anderer als der letzte? + ScRange aRange( aBigRange.aStart.MakeAddress() ); + const ScAddress& rPos = aRange.aStart; + + ScChangeActionContent* pNew = new ScChangeActionContent( aRange ); + pNew->SetOldValue( pDoc->GetCell( rPos ), pDoc, pDoc ); + + if ( bOldest ) + PutOldValueToDoc( pDoc, 0, 0 ); + else + PutNewValueToDoc( pDoc, 0, 0 ); + + pNew->SetRejectAction( bOldest ? GetActionNumber() : pEnd->GetActionNumber() ); + pNew->SetState( SC_CAS_ACCEPTED ); + if ( pRejectActions ) + pRejectActions->Push( pNew ); + else + { + pNew->SetNewValue( pDoc->GetCell( rPos ), pDoc ); + pTrack->Append( pNew ); + } + } + + if ( bOldest ) + SetRejected(); + else + SetState( SC_CAS_ACCEPTED ); + + return TRUE; +} + + +// static +void ScChangeActionContent::GetStringOfCell( String& rStr, + const ScBaseCell* pCell, const ScDocument* pDoc, const ScAddress& rPos ) +{ + if ( pCell ) + { + if ( ScChangeActionContent::NeedsNumberFormat( pCell ) ) + GetStringOfCell( rStr, pCell, pDoc, pDoc->GetNumberFormat( rPos ) ); + else + GetStringOfCell( rStr, pCell, pDoc, 0 ); + } + else + rStr.Erase(); +} + + +// static +void ScChangeActionContent::GetStringOfCell( String& rStr, + const ScBaseCell* pCell, const ScDocument* pDoc, ULONG nFormat ) +{ + if ( ScChangeActionContent::GetContentCellType( pCell ) ) + { + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : + { + double nValue = ((ScValueCell*)pCell)->GetValue(); + pDoc->GetFormatTable()->GetInputLineString( nValue, nFormat, + rStr ); + } + break; + case CELLTYPE_STRING : + ((ScStringCell*)pCell)->GetString( rStr ); + break; + case CELLTYPE_EDIT : + ((ScEditCell*)pCell)->GetString( rStr ); + break; + case CELLTYPE_FORMULA : + ((ScFormulaCell*)pCell)->GetFormula( rStr ); + break; + default: + rStr.Erase(); + } + } + else + rStr.Erase(); +} + + +// static +ScChangeActionContentCellType ScChangeActionContent::GetContentCellType( const ScBaseCell* pCell ) +{ + if ( pCell ) + { + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : + case CELLTYPE_STRING : + case CELLTYPE_EDIT : + return SC_CACCT_NORMAL; + //break; + case CELLTYPE_FORMULA : + switch ( ((const ScFormulaCell*)pCell)->GetMatrixFlag() ) + { + case MM_NONE : + return SC_CACCT_NORMAL; + //break; + case MM_FORMULA : + case MM_FAKE : + return SC_CACCT_MATORG; + //break; + case MM_REFERENCE : + return SC_CACCT_MATREF; + //break; + } + return SC_CACCT_NORMAL; + //break; + default: + return SC_CACCT_NONE; + } + } + return SC_CACCT_NONE; +} + + +// static +BOOL ScChangeActionContent::NeedsNumberFormat( const ScBaseCell* pCell ) +{ + return pCell && pCell->GetCellType() == CELLTYPE_VALUE; +} + + +// static +void ScChangeActionContent::SetValue( String& rStr, ScBaseCell*& pCell, + const ScAddress& rPos, const ScBaseCell* pOrgCell, + const ScDocument* pFromDoc, ScDocument* pToDoc ) +{ + ULONG nFormat = NeedsNumberFormat( pOrgCell ) ? pFromDoc->GetNumberFormat( rPos ) : 0; + SetValue( rStr, pCell, nFormat, pOrgCell, pFromDoc, pToDoc ); +} + + +// static +void ScChangeActionContent::SetValue( String& rStr, ScBaseCell*& pCell, + ULONG nFormat, const ScBaseCell* pOrgCell, + const ScDocument* pFromDoc, ScDocument* pToDoc ) +{ + rStr.Erase(); + if ( pCell ) + pCell->Delete(); + if ( ScChangeActionContent::GetContentCellType( pOrgCell ) ) + { + pCell = pOrgCell->CloneWithoutNote( *pToDoc ); + switch ( pOrgCell->GetCellType() ) + { + case CELLTYPE_VALUE : + { // z.B. Datum auch als solches merken + double nValue = ((ScValueCell*)pOrgCell)->GetValue(); + pFromDoc->GetFormatTable()->GetInputLineString( nValue, + nFormat, rStr ); + } + break; + case CELLTYPE_FORMULA : + ((ScFormulaCell*)pCell)->SetInChangeTrack( TRUE ); + break; + default: + { + // added to avoid warnings + } + } + } + else + pCell = NULL; +} + + +// static +void ScChangeActionContent::SetCell( String& rStr, ScBaseCell* pCell, + ULONG nFormat, const ScDocument* pDoc ) +{ + rStr.Erase(); + if ( pCell ) + { + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : + { // e.g. remember date as date string + double nValue = ((ScValueCell*)pCell)->GetValue(); + pDoc->GetFormatTable()->GetInputLineString( nValue, + nFormat, rStr ); + } + break; + case CELLTYPE_FORMULA : + ((ScFormulaCell*)pCell)->SetInChangeTrack( TRUE ); + break; + default: + { + // added to avoid warnings + } + } + } +} + + +void ScChangeActionContent::GetValueString( String& rStr, + const String& rValue, const ScBaseCell* pCell ) const +{ + if ( !rValue.Len() ) + { + if ( pCell ) + { + switch ( pCell->GetCellType() ) + { + case CELLTYPE_STRING : + ((ScStringCell*)pCell)->GetString( rStr ); + break; + case CELLTYPE_EDIT : + ((ScEditCell*)pCell)->GetString( rStr ); + break; + case CELLTYPE_VALUE : // ist immer in rValue + rStr = rValue; + break; + case CELLTYPE_FORMULA : + GetFormulaString( rStr, (ScFormulaCell*) pCell ); + break; + default: + { + // added to avoid warnings + } + } + } + else + rStr.Erase(); + } + else + rStr = rValue; +} + + +void ScChangeActionContent::GetFormulaString( String& rStr, + const ScFormulaCell* pCell ) const +{ + ScAddress aPos( aBigRange.aStart.MakeAddress() ); + if ( aPos == pCell->aPos || IsDeletedIn() ) + pCell->GetFormula( rStr ); + else + { + DBG_ERROR( "ScChangeActionContent::GetFormulaString: aPos != pCell->aPos" ); + ScFormulaCell* pNew = new ScFormulaCell( *pCell, *pCell->GetDocument(), aPos ); + pNew->GetFormula( rStr ); + delete pNew; + } +} + + +void ScChangeActionContent::PutOldValueToDoc( ScDocument* pDoc, + SCsCOL nDx, SCsROW nDy ) const +{ + PutValueToDoc( pOldCell, aOldValue, pDoc, nDx, nDy ); +} + + +void ScChangeActionContent::PutNewValueToDoc( ScDocument* pDoc, + SCsCOL nDx, SCsROW nDy ) const +{ + PutValueToDoc( pNewCell, aNewValue, pDoc, nDx, nDy ); +} + + +void ScChangeActionContent::PutValueToDoc( ScBaseCell* pCell, + const String& rValue, ScDocument* pDoc, SCsCOL nDx, SCsROW nDy ) const +{ + ScAddress aPos( aBigRange.aStart.MakeAddress() ); + if ( nDx ) + aPos.IncCol( nDx ); + if ( nDy ) + aPos.IncRow( nDy ); + if ( !rValue.Len() ) + { + if ( pCell ) + { + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : // ist immer in rValue + pDoc->SetString( aPos.Col(), aPos.Row(), aPos.Tab(), rValue ); + break; + default: + switch ( ScChangeActionContent::GetContentCellType( pCell ) ) + { + case SC_CACCT_MATORG : + { + SCCOL nC; + SCROW nR; + ((const ScFormulaCell*)pCell)->GetMatColsRows( nC, nR ); + DBG_ASSERT( nC>0 && nR>0, "ScChangeActionContent::PutValueToDoc: MatColsRows?" ); + ScRange aRange( aPos ); + if ( nC > 1 ) + aRange.aEnd.IncCol( nC-1 ); + if ( nR > 1 ) + aRange.aEnd.IncRow( nR-1 ); + ScMarkData aDestMark; + aDestMark.SelectOneTable( aPos.Tab() ); + aDestMark.SetMarkArea( aRange ); + pDoc->InsertMatrixFormula( aPos.Col(), aPos.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row(), + aDestMark, EMPTY_STRING, + ((const ScFormulaCell*)pCell)->GetCode() ); + } + break; + case SC_CACCT_MATREF : + // nothing + break; + default: + pDoc->PutCell( aPos, pCell->CloneWithoutNote( *pDoc ) ); + } + } + } + else + pDoc->PutCell( aPos, NULL ); + } + else + pDoc->SetString( aPos.Col(), aPos.Row(), aPos.Tab(), rValue ); +} + + +void lcl_InvalidateReference( ScToken& rTok, const ScBigAddress& rPos ) +{ + ScSingleRefData& rRef1 = rTok.GetSingleRef(); + if ( rPos.Col() < 0 || MAXCOL < rPos.Col() ) + { + rRef1.nCol = SCCOL_MAX; + rRef1.nRelCol = SCCOL_MAX; + rRef1.SetColDeleted( TRUE ); + } + if ( rPos.Row() < 0 || MAXROW < rPos.Row() ) + { + rRef1.nRow = SCROW_MAX; + rRef1.nRelRow = SCROW_MAX; + rRef1.SetRowDeleted( TRUE ); + } + if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() ) + { + rRef1.nTab = SCTAB_MAX; + rRef1.nRelTab = SCTAB_MAX; + rRef1.SetTabDeleted( TRUE ); + } + if ( rTok.GetType() == formula::svDoubleRef ) + { + ScSingleRefData& rRef2 = rTok.GetDoubleRef().Ref2; + if ( rPos.Col() < 0 || MAXCOL < rPos.Col() ) + { + rRef2.nCol = SCCOL_MAX; + rRef2.nRelCol = SCCOL_MAX; + rRef2.SetColDeleted( TRUE ); + } + if ( rPos.Row() < 0 || MAXROW < rPos.Row() ) + { + rRef2.nRow = SCROW_MAX; + rRef2.nRelRow = SCROW_MAX; + rRef2.SetRowDeleted( TRUE ); + } + if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() ) + { + rRef2.nTab = SCTAB_MAX; + rRef2.nRelTab = SCTAB_MAX; + rRef2.SetTabDeleted( TRUE ); + } + } +} + + +void ScChangeActionContent::UpdateReference( const ScChangeTrack* pTrack, + UpdateRefMode eMode, const ScBigRange& rRange, + INT32 nDx, INT32 nDy, INT32 nDz ) +{ + SCSIZE nOldSlot = ScChangeTrack::ComputeContentSlot( aBigRange.aStart.Row() ); + ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aBigRange ); + SCSIZE nNewSlot = ScChangeTrack::ComputeContentSlot( aBigRange.aStart.Row() ); + if ( nNewSlot != nOldSlot ) + { + RemoveFromSlot(); + InsertInSlot( &(pTrack->GetContentSlots()[nNewSlot]) ); + } + + if ( pTrack->IsInDelete() && !pTrack->IsInDeleteTop() ) + return ; // Formeln nur kompletten Bereich updaten + + BOOL bOldFormula = ( pOldCell && pOldCell->GetCellType() == CELLTYPE_FORMULA ); + BOOL bNewFormula = ( pNewCell && pNewCell->GetCellType() == CELLTYPE_FORMULA ); + if ( bOldFormula || bNewFormula ) + { // via ScFormulaCell UpdateReference anpassen (dort) + if ( pTrack->IsInDelete() ) + { + const ScRange& rDelRange = pTrack->GetInDeleteRange(); + if ( nDx > 0 ) + nDx = rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1; + else if ( nDx < 0 ) + nDx = -(rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1); + if ( nDy > 0 ) + nDy = rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1; + else if ( nDy < 0 ) + nDy = -(rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1); + if ( nDz > 0 ) + nDz = rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1; + else if ( nDz < 0 ) + nDz = -(rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1); + } + ScBigRange aTmpRange( rRange ); + switch ( eMode ) + { + case URM_INSDEL : + if ( nDx < 0 || nDy < 0 || nDz < 0 ) + { // Delete startet dort hinter geloeschtem Bereich, + // Position wird dort angepasst. + if ( nDx ) + aTmpRange.aStart.IncCol( -nDx ); + if ( nDy ) + aTmpRange.aStart.IncRow( -nDy ); + if ( nDz ) + aTmpRange.aStart.IncTab( -nDz ); + } + break; + case URM_MOVE : + // Move ist hier Quelle, dort Ziel, + // Position muss vorher angepasst sein. + if ( bOldFormula ) + ((ScFormulaCell*)pOldCell)->aPos = aBigRange.aStart.MakeAddress(); + if ( bNewFormula ) + ((ScFormulaCell*)pNewCell)->aPos = aBigRange.aStart.MakeAddress(); + if ( nDx ) + { + aTmpRange.aStart.IncCol( nDx ); + aTmpRange.aEnd.IncCol( nDx ); + } + if ( nDy ) + { + aTmpRange.aStart.IncRow( nDy ); + aTmpRange.aEnd.IncRow( nDy ); + } + if ( nDz ) + { + aTmpRange.aStart.IncTab( nDz ); + aTmpRange.aEnd.IncTab( nDz ); + } + break; + default: + { + // added to avoid warnings + } + } + ScRange aRange( aTmpRange.MakeRange() ); + if ( bOldFormula ) + ((ScFormulaCell*)pOldCell)->UpdateReference( eMode, aRange, + (SCsCOL) nDx, (SCsROW) nDy, (SCsTAB) nDz, NULL ); + if ( bNewFormula ) + ((ScFormulaCell*)pNewCell)->UpdateReference( eMode, aRange, + (SCsCOL) nDx, (SCsROW) nDy, (SCsTAB) nDz, NULL ); + if ( !aBigRange.aStart.IsValid( pTrack->GetDocument() ) ) + { //! HACK! + //! UpdateReference kann nicht mit Positionen ausserhalb des + //! Dokuments umgehen, deswegen alles auf #REF! setzen +//2do: make it possible! das bedeutet grossen Umbau von ScAddress etc.! + const ScBigAddress& rPos = aBigRange.aStart; + if ( bOldFormula ) + { + ScToken* t; + ScTokenArray* pArr = ((ScFormulaCell*)pOldCell)->GetCode(); + pArr->Reset(); + while ( ( t = static_cast<ScToken*>(pArr->GetNextReference()) ) != NULL ) + lcl_InvalidateReference( *t, rPos ); + pArr->Reset(); + while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL ) + lcl_InvalidateReference( *t, rPos ); + } + if ( bNewFormula ) + { + ScToken* t; + ScTokenArray* pArr = ((ScFormulaCell*)pNewCell)->GetCode(); + pArr->Reset(); + while ( ( t = static_cast<ScToken*>(pArr->GetNextReference()) ) != NULL ) + lcl_InvalidateReference( *t, rPos ); + pArr->Reset(); + while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL ) + lcl_InvalidateReference( *t, rPos ); + } + } + } +} + + +// --- ScChangeActionReject ------------------------------------------------ + +ScChangeActionReject::ScChangeActionReject(const ULONG nActionNumber, const ScChangeActionState eStateP, const ULONG nRejectingNumber, + const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String& sComment) + : + ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment) +{ +} + + +// --- ScChangeTrack ------------------------------------------------------- + +IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeTrackMsgInfo, 16, 16 ) + +const SCROW ScChangeTrack::nContentRowsPerSlot = InitContentRowsPerSlot(); +const SCSIZE ScChangeTrack::nContentSlots = + (MAXROWCOUNT) / InitContentRowsPerSlot() + 2; + +// static +SCROW ScChangeTrack::InitContentRowsPerSlot() +{ + const SCSIZE nMaxSlots = 0xffe0 / sizeof( ScChangeActionContent* ) - 2; + SCROW nRowsPerSlot = (MAXROWCOUNT) / nMaxSlots; + if ( nRowsPerSlot * nMaxSlots < sal::static_int_cast<SCSIZE>(MAXROWCOUNT) ) + ++nRowsPerSlot; + return nRowsPerSlot; +} + + +ScChangeTrack::ScChangeTrack( ScDocument* pDocP ) : + pDoc( pDocP ) +{ + Init(); + SC_MOD()->GetUserOptions().AddListener(this); + + ppContentSlots = new ScChangeActionContent* [ nContentSlots ]; + memset( ppContentSlots, 0, nContentSlots * sizeof( ScChangeActionContent* ) ); +} + +ScChangeTrack::ScChangeTrack( ScDocument* pDocP, const ScStrCollection& aTempUserCollection) : + aUserCollection(aTempUserCollection), + pDoc( pDocP ) +{ + Init(); + SC_MOD()->GetUserOptions().AddListener(this); + ppContentSlots = new ScChangeActionContent* [ nContentSlots ]; + memset( ppContentSlots, 0, nContentSlots * sizeof( ScChangeActionContent* ) ); +} + +ScChangeTrack::~ScChangeTrack() +{ + SC_MOD()->GetUserOptions().RemoveListener(this); + DtorClear(); + delete [] ppContentSlots; +} + + +void ScChangeTrack::Init() +{ + pFirst = NULL; + pLast = NULL; + pFirstGeneratedDelContent = NULL; + pLastCutMove = NULL; + pLinkInsertCol = NULL; + pLinkInsertRow = NULL; + pLinkInsertTab = NULL; + pLinkMove = NULL; + pBlockModifyMsg = NULL; + nActionMax = 0; + nGeneratedMin = SC_CHGTRACK_GENERATED_START; + nMarkLastSaved = 0; + nStartLastCut = 0; + nEndLastCut = 0; + nLastMerge = 0; + eMergeState = SC_CTMS_NONE; + nLoadedFileFormatVersion = SC_CHGTRACK_FILEFORMAT; + bLoadSave = FALSE; + bInDelete = FALSE; + bInDeleteTop = FALSE; + bInDeleteUndo = FALSE; + bInPasteCut = FALSE; + bUseFixDateTime = FALSE; + bTime100thSeconds = TRUE; + + const SvtUserOptions& rUserOpt = SC_MOD()->GetUserOptions(); + aUser = rUserOpt.GetFirstName(); + aUser += ' '; + aUser += (String)rUserOpt.GetLastName(); + aUserCollection.Insert( new StrData( aUser ) ); +} + + +void ScChangeTrack::DtorClear() +{ + ScChangeAction* p; + ScChangeAction* pNext; + for ( p = GetFirst(); p; p = pNext ) + { + pNext = p->GetNext(); + delete p; + } + for ( p = pFirstGeneratedDelContent; p; p = pNext ) + { + pNext = p->GetNext(); + delete p; + } + for ( p = aPasteCutTable.First(); p; p = aPasteCutTable.Next() ) + { + delete p; + } + delete pLastCutMove; + ClearMsgQueue(); +} + + +void ScChangeTrack::ClearMsgQueue() +{ + if ( pBlockModifyMsg ) + { + delete pBlockModifyMsg; + pBlockModifyMsg = NULL; + } + ScChangeTrackMsgInfo* pMsgInfo; + while ( ( pMsgInfo = aMsgStackTmp.Pop() ) != NULL ) + delete pMsgInfo; + while ( ( pMsgInfo = aMsgStackFinal.Pop() ) != NULL ) + delete pMsgInfo; + while ( ( pMsgInfo = aMsgQueue.Get() ) != NULL ) + delete pMsgInfo; +} + + +void ScChangeTrack::Clear() +{ + DtorClear(); + aTable.Clear(); + aGeneratedTable.Clear(); + aPasteCutTable.Clear(); + aUserCollection.FreeAll(); + aUser.Erase(); + Init(); +} + + +void __EXPORT ScChangeTrack::ConfigurationChanged( utl::ConfigurationBroadcaster*, sal_uInt32 ) +{ + if ( !pDoc->IsInDtorClear() ) + { + const SvtUserOptions& rUserOptions = SC_MOD()->GetUserOptions(); + USHORT nOldCount = aUserCollection.GetCount(); + + String aStr( rUserOptions.GetFirstName() ); + aStr += ' '; + aStr += (String)rUserOptions.GetLastName(); + SetUser( aStr ); + + if ( aUserCollection.GetCount() != nOldCount ) + { + // New user in collection -> have to repaint because + // colors may be different now (#106697#). + // (Has to be done in the Notify handler, to be sure + // the user collection has already been updated) + + SfxObjectShell* pDocSh = pDoc->GetDocumentShell(); + if (pDocSh) + pDocSh->Broadcast( ScPaintHint( ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB), PAINT_GRID ) ); + } + } +} + + +void ScChangeTrack::SetUser( const String& rUser ) +{ + if ( IsLoadSave() ) + return ; // nicht die Collection zerschiessen + + aUser = rUser; + StrData* pStrData = new StrData( aUser ); + if ( !aUserCollection.Insert( pStrData ) ) + delete pStrData; +} + + +void ScChangeTrack::StartBlockModify( ScChangeTrackMsgType eMsgType, + ULONG nStartAction ) +{ + if ( aModifiedLink.IsSet() ) + { + if ( pBlockModifyMsg ) + aMsgStackTmp.Push( pBlockModifyMsg ); // Block im Block + pBlockModifyMsg = new ScChangeTrackMsgInfo; + pBlockModifyMsg->eMsgType = eMsgType; + pBlockModifyMsg->nStartAction = nStartAction; + } +} + + +void ScChangeTrack::EndBlockModify( ULONG nEndAction ) +{ + if ( aModifiedLink.IsSet() ) + { + if ( pBlockModifyMsg ) + { + if ( pBlockModifyMsg->nStartAction <= nEndAction ) + { + pBlockModifyMsg->nEndAction = nEndAction; + // Blocks in Blocks aufgeloest + aMsgStackFinal.Push( pBlockModifyMsg ); + } + else + delete pBlockModifyMsg; + pBlockModifyMsg = aMsgStackTmp.Pop(); // evtl. Block im Block + } + if ( !pBlockModifyMsg ) + { + BOOL bNew = FALSE; + ScChangeTrackMsgInfo* pMsg; + while ( ( pMsg = aMsgStackFinal.Pop() ) != NULL ) + { + aMsgQueue.Put( pMsg ); + bNew = TRUE; + } + if ( bNew ) + aModifiedLink.Call( this ); + } + } +} + + +void ScChangeTrack::NotifyModified( ScChangeTrackMsgType eMsgType, + ULONG nStartAction, ULONG nEndAction ) +{ + if ( aModifiedLink.IsSet() ) + { + if ( !pBlockModifyMsg || pBlockModifyMsg->eMsgType != eMsgType || + (IsGenerated( nStartAction ) && + (eMsgType == SC_CTM_APPEND || eMsgType == SC_CTM_REMOVE)) ) + { // Append innerhalb von Append z.B. nicht + StartBlockModify( eMsgType, nStartAction ); + EndBlockModify( nEndAction ); + } + } +} + + +void ScChangeTrack::MasterLinks( ScChangeAction* pAppend ) +{ + ScChangeActionType eType = pAppend->GetType(); + + if ( eType == SC_CAT_CONTENT ) + { + if ( !IsGenerated( pAppend->GetActionNumber() ) ) + { + SCSIZE nSlot = ComputeContentSlot( + pAppend->GetBigRange().aStart.Row() ); + ((ScChangeActionContent*)pAppend)->InsertInSlot( + &ppContentSlots[nSlot] ); + } + return ; + } + + if ( pAppend->IsRejecting() ) + return ; // Rejects haben keine Abhaengigkeiten + + switch ( eType ) + { + case SC_CAT_INSERT_COLS : + { + ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry( + &pLinkInsertCol, pAppend ); + pAppend->AddLink( NULL, pLink ); + } + break; + case SC_CAT_INSERT_ROWS : + { + ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry( + &pLinkInsertRow, pAppend ); + pAppend->AddLink( NULL, pLink ); + } + break; + case SC_CAT_INSERT_TABS : + { + ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry( + &pLinkInsertTab, pAppend ); + pAppend->AddLink( NULL, pLink ); + } + break; + case SC_CAT_MOVE : + { + ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry( + &pLinkMove, pAppend ); + pAppend->AddLink( NULL, pLink ); + } + break; + default: + { + // added to avoid warnings + } + } +} + + +void ScChangeTrack::AppendLoaded( ScChangeAction* pAppend ) +{ + aTable.Insert( pAppend->GetActionNumber(), pAppend ); + if ( !pLast ) + pFirst = pLast = pAppend; + else + { + pLast->pNext = pAppend; + pAppend->pPrev = pLast; + pLast = pAppend; + } + MasterLinks( pAppend ); +} + + +void ScChangeTrack::Append( ScChangeAction* pAppend, ULONG nAction ) +{ + if ( nActionMax < nAction ) + nActionMax = nAction; + pAppend->SetUser( aUser ); + if ( bUseFixDateTime ) + pAppend->SetDateTimeUTC( aFixDateTime ); + pAppend->SetActionNumber( nAction ); + aTable.Insert( nAction, pAppend ); + // UpdateReference Inserts vor Dependencies. + // Delete rejectendes Insert hatte UpdateReference mit Delete-Undo. + // UpdateReference auch wenn pLast==NULL, weil pAppend ein Delete sein + // kann, dass DelContents generiert haben kann + if ( pAppend->IsInsertType() && !pAppend->IsRejecting() ) + UpdateReference( pAppend, FALSE ); + if ( !pLast ) + pFirst = pLast = pAppend; + else + { + pLast->pNext = pAppend; + pAppend->pPrev = pLast; + pLast = pAppend; + Dependencies( pAppend ); + } + // UpdateReference Inserts nicht nach Dependencies. + // Move rejectendes Move hatte UpdateReference mit Move-Undo, Inhalt in + // ToRange nicht deleten. + if ( !pAppend->IsInsertType() && + !(pAppend->GetType() == SC_CAT_MOVE && pAppend->IsRejecting()) ) + UpdateReference( pAppend, FALSE ); + MasterLinks( pAppend ); + + if ( aModifiedLink.IsSet() ) + { + NotifyModified( SC_CTM_APPEND, nAction, nAction ); + if ( pAppend->GetType() == SC_CAT_CONTENT ) + { + ScChangeActionContent* pContent = (ScChangeActionContent*) pAppend; + if ( ( pContent = pContent->GetPrevContent() ) != NULL ) + { + ULONG nMod = pContent->GetActionNumber(); + NotifyModified( SC_CTM_CHANGE, nMod, nMod ); + } + } + else + NotifyModified( SC_CTM_CHANGE, pFirst->GetActionNumber(), + pLast->GetActionNumber() ); + } +} + + +void ScChangeTrack::Append( ScChangeAction* pAppend ) +{ + Append( pAppend, ++nActionMax ); +} + + +void ScChangeTrack::AppendDeleteRange( const ScRange& rRange, + ScDocument* pRefDoc, ULONG& nStartAction, ULONG& nEndAction, SCsTAB nDz ) +{ + nStartAction = GetActionMax() + 1; + AppendDeleteRange( rRange, pRefDoc, nDz, 0 ); + nEndAction = GetActionMax(); +} + + +void ScChangeTrack::AppendDeleteRange( const ScRange& rRange, + ScDocument* pRefDoc, SCsTAB nDz, ULONG nRejectingInsert ) +{ + SetInDeleteRange( rRange ); + StartBlockModify( SC_CTM_APPEND, GetActionMax() + 1 ); + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ ) + { + if ( !pRefDoc || nTab < pRefDoc->GetTableCount() ) + { + if ( nCol1 == 0 && nCol2 == MAXCOL ) + { // ganze Zeilen und/oder Tabellen + if ( nRow1 == 0 && nRow2 == MAXROW ) + { // ganze Tabellen +//2do: geht nicht auch komplette Tabelle als ganzes? + ScRange aRange( 0, 0, nTab, 0, MAXROW, nTab ); + for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ ) + { // spaltenweise ist weniger als zeilenweise + aRange.aStart.SetCol( nCol ); + aRange.aEnd.SetCol( nCol ); + if ( nCol == nCol2 ) + SetInDeleteTop( TRUE ); + AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0, + nTab-nTab1 + nDz, nRejectingInsert ); + } + //! immer noch InDeleteTop + AppendOneDeleteRange( rRange, pRefDoc, 0, 0, + nTab-nTab1 + nDz, nRejectingInsert ); + } + else + { // ganze Zeilen + ScRange aRange( 0, 0, nTab, MAXCOL, 0, nTab ); + for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ ) + { + aRange.aStart.SetRow( nRow ); + aRange.aEnd.SetRow( nRow ); + if ( nRow == nRow2 ) + SetInDeleteTop( TRUE ); + AppendOneDeleteRange( aRange, pRefDoc, 0, nRow-nRow1, + 0, nRejectingInsert ); + } + } + } + else if ( nRow1 == 0 && nRow2 == MAXROW ) + { // ganze Spalten + ScRange aRange( 0, 0, nTab, 0, MAXROW, nTab ); + for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ ) + { + aRange.aStart.SetCol( nCol ); + aRange.aEnd.SetCol( nCol ); + if ( nCol == nCol2 ) + SetInDeleteTop( TRUE ); + AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0, + 0, nRejectingInsert ); + } + } + else + { + DBG_ERROR( "ScChangeTrack::AppendDeleteRange: Block not supported!" ); + } + SetInDeleteTop( FALSE ); + } + } + EndBlockModify( GetActionMax() ); +} + + +void ScChangeTrack::AppendOneDeleteRange( const ScRange& rOrgRange, + ScDocument* pRefDoc, SCsCOL nDx, SCsROW nDy, SCsTAB nDz, + ULONG nRejectingInsert ) +{ + ScRange aTrackRange( rOrgRange ); + if ( nDx ) + { + aTrackRange.aStart.IncCol( -nDx ); + aTrackRange.aEnd.IncCol( -nDx ); + } + if ( nDy ) + { + aTrackRange.aStart.IncRow( -nDy ); + aTrackRange.aEnd.IncRow( -nDy ); + } + if ( nDz ) + { + aTrackRange.aStart.IncTab( -nDz ); + aTrackRange.aEnd.IncTab( -nDz ); + } + ScChangeActionDel* pAct = new ScChangeActionDel( aTrackRange, nDx, nDy, + this ); + // TabDelete keine Contents, sind in einzelnen Spalten + if ( !(rOrgRange.aStart.Col() == 0 && rOrgRange.aStart.Row() == 0 && + rOrgRange.aEnd.Col() == MAXCOL && rOrgRange.aEnd.Row() == MAXROW) ) + LookUpContents( rOrgRange, pRefDoc, -nDx, -nDy, -nDz ); + if ( nRejectingInsert ) + { + pAct->SetRejectAction( nRejectingInsert ); + pAct->SetState( SC_CAS_ACCEPTED ); + } + Append( pAct ); +} + + +void ScChangeTrack::LookUpContents( const ScRange& rOrgRange, + ScDocument* pRefDoc, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + if ( pRefDoc ) + { + ScAddress aPos; + ScBigAddress aBigPos; + ScCellIterator aIter( pRefDoc, rOrgRange ); + ScBaseCell* pCell = aIter.GetFirst(); + while ( pCell ) + { + if ( ScChangeActionContent::GetContentCellType( pCell ) ) + { + aBigPos.Set( aIter.GetCol() + nDx, aIter.GetRow() + nDy, + aIter.GetTab() + nDz ); + ScChangeActionContent* pContent = SearchContentAt( aBigPos, NULL ); + if ( !pContent ) + { // nicht getrackte Contents + aPos.Set( aIter.GetCol() + nDx, aIter.GetRow() + nDy, + aIter.GetTab() + nDz ); + GenerateDelContent( aPos, pCell, pRefDoc ); + //! der Content wird hier _nicht_ per AddContent hinzugefuegt, + //! sondern in UpdateReference, um z.B. auch kreuzende Deletes + //! korrekt zu erfassen + } + } + pCell = aIter.GetNext(); + } + } +} + + +void ScChangeTrack::AppendMove( const ScRange& rFromRange, + const ScRange& rToRange, ScDocument* pRefDoc ) +{ + ScChangeActionMove* pAct = new ScChangeActionMove( rFromRange, rToRange, this ); + LookUpContents( rToRange, pRefDoc, 0, 0, 0 ); // ueberschriebene Contents + Append( pAct ); +} + + +// static +BOOL ScChangeTrack::IsMatrixFormulaRangeDifferent( const ScBaseCell* pOldCell, + const ScBaseCell* pNewCell ) +{ + SCCOL nC1, nC2; + SCROW nR1, nR2; + nC1 = nC2 = 0; + nR1 = nR2 = 0; + if ( pOldCell && (pOldCell->GetCellType() == CELLTYPE_FORMULA) && + ((const ScFormulaCell*)pOldCell)->GetMatrixFlag() == MM_FORMULA ) + ((const ScFormulaCell*)pOldCell)->GetMatColsRows( nC1, nR1 ); + if ( pNewCell && (pNewCell->GetCellType() == CELLTYPE_FORMULA) && + ((const ScFormulaCell*)pNewCell)->GetMatrixFlag() == MM_FORMULA ) + ((const ScFormulaCell*)pNewCell)->GetMatColsRows( nC1, nR1 ); + return nC1 != nC2 || nR1 != nR2; +} + + +void ScChangeTrack::AppendContent( const ScAddress& rPos, + const String& rNewValue, ScBaseCell* pOldCell ) +{ + String aOldValue; + ScChangeActionContent::GetStringOfCell( aOldValue, pOldCell, pDoc, rPos ); + if ( aOldValue != rNewValue || + IsMatrixFormulaRangeDifferent( pOldCell, NULL ) ) + { // nur wirkliche Aenderung tracken + ScRange aRange( rPos ); + ScChangeActionContent* pAct = new ScChangeActionContent( aRange ); + pAct->SetOldValue( pOldCell, pDoc, pDoc ); + pAct->SetNewValue( rNewValue, pDoc ); + Append( pAct ); + } +} + + +void ScChangeTrack::AppendContent( const ScAddress& rPos, + const ScBaseCell* pOldCell, ULONG nOldFormat, ScDocument* pRefDoc ) +{ + if ( !pRefDoc ) + pRefDoc = pDoc; + String aOldValue; + ScChangeActionContent::GetStringOfCell( aOldValue, pOldCell, pRefDoc, nOldFormat ); + String aNewValue; + ScBaseCell* pNewCell = pDoc->GetCell( rPos ); + ScChangeActionContent::GetStringOfCell( aNewValue, pNewCell, pDoc, rPos ); + if ( aOldValue != aNewValue || + IsMatrixFormulaRangeDifferent( pOldCell, pNewCell ) ) + { // nur wirkliche Aenderung tracken + ScRange aRange( rPos ); + ScChangeActionContent* pAct = new ScChangeActionContent( aRange ); + pAct->SetOldValue( pOldCell, pRefDoc, pDoc, nOldFormat ); + pAct->SetNewValue( pNewCell, pDoc ); + Append( pAct ); + } +} + + +void ScChangeTrack::AppendContent( const ScAddress& rPos, + ScDocument* pRefDoc ) +{ + String aOldValue; + ScBaseCell* pOldCell = pRefDoc->GetCell( rPos ); + ScChangeActionContent::GetStringOfCell( aOldValue, pOldCell, pRefDoc, rPos ); + String aNewValue; + ScBaseCell* pNewCell = pDoc->GetCell( rPos ); + ScChangeActionContent::GetStringOfCell( aNewValue, pNewCell, pDoc, rPos ); + if ( aOldValue != aNewValue || + IsMatrixFormulaRangeDifferent( pOldCell, pNewCell ) ) + { // nur wirkliche Aenderung tracken + ScRange aRange( rPos ); + ScChangeActionContent* pAct = new ScChangeActionContent( aRange ); + pAct->SetOldValue( pOldCell, pRefDoc, pDoc ); + pAct->SetNewValue( pNewCell, pDoc ); + Append( pAct ); + } +} + + +void ScChangeTrack::AppendContent( const ScAddress& rPos, + const ScBaseCell* pOldCell ) +{ + if ( ScChangeActionContent::NeedsNumberFormat( pOldCell ) ) + AppendContent( rPos, pOldCell, pDoc->GetNumberFormat( rPos ), pDoc ); + else + AppendContent( rPos, pOldCell, 0, pDoc ); +} + + +void ScChangeTrack::SetLastCutMoveRange( const ScRange& rRange, + ScDocument* pRefDoc ) +{ + if ( pLastCutMove ) + { + // ToRange nicht mit Deletes linken und nicht in der Groesse aendern, + // eigentlich unnoetig, da ein Delete vorher in + // ScViewFunc::PasteFromClip ein ResetLastCut ausloest + ScBigRange& r = pLastCutMove->GetBigRange(); + r.aEnd.SetCol( -1 ); + r.aEnd.SetRow( -1 ); + r.aEnd.SetTab( -1 ); + r.aStart.SetCol( -1 - (rRange.aEnd.Col() - rRange.aStart.Col()) ); + r.aStart.SetRow( -1 - (rRange.aEnd.Row() - rRange.aStart.Row()) ); + r.aStart.SetTab( -1 - (rRange.aEnd.Tab() - rRange.aStart.Tab()) ); + // zu ueberschreibende Contents im FromRange + LookUpContents( rRange, pRefDoc, 0, 0, 0 ); + } +} + + +void ScChangeTrack::AppendContentRange( const ScRange& rRange, + ScDocument* pRefDoc, ULONG& nStartAction, ULONG& nEndAction, + ScChangeActionClipMode eClipMode ) +{ + if ( eClipMode == SC_CACM_CUT ) + { + ResetLastCut(); + pLastCutMove = new ScChangeActionMove( rRange, rRange, this ); + SetLastCutMoveRange( rRange, pRefDoc ); + } + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + BOOL bDoContents; + if ( eClipMode == SC_CACM_PASTE && HasLastCut() ) + { + bDoContents = FALSE; + SetInPasteCut( TRUE ); + // Paste und Cut abstimmen, Paste kann groesserer Range sein + ScRange aRange( rRange ); + ScBigRange& r = pLastCutMove->GetBigRange(); + SCCOL nTmpCol; + if ( (nTmpCol = (SCCOL) (r.aEnd.Col() - r.aStart.Col())) != (nCol2 - nCol1) ) + { + aRange.aEnd.SetCol( aRange.aStart.Col() + nTmpCol ); + nCol1 += nTmpCol + 1; + bDoContents = TRUE; + } + SCROW nTmpRow; + if ( (nTmpRow = (SCROW) (r.aEnd.Row() - r.aStart.Row())) != (nRow2 - nRow1) ) + { + aRange.aEnd.SetRow( aRange.aStart.Row() + nTmpRow ); + nRow1 += nTmpRow + 1; + bDoContents = TRUE; + } + SCTAB nTmpTab; + if ( (nTmpTab = (SCTAB) (r.aEnd.Tab() - r.aStart.Tab())) != (nTab2 - nTab1) ) + { + aRange.aEnd.SetTab( aRange.aStart.Tab() + nTmpTab ); + nTab1 += nTmpTab + 1; + bDoContents = TRUE; + } + r = aRange; + Undo( nStartLastCut, nEndLastCut ); // hier werden sich die Cuts gemerkt + //! StartAction erst nach Undo + nStartAction = GetActionMax() + 1; + StartBlockModify( SC_CTM_APPEND, nStartAction ); + // zu ueberschreibende Contents im ToRange + LookUpContents( aRange, pRefDoc, 0, 0, 0 ); + pLastCutMove->SetStartLastCut( nStartLastCut ); + pLastCutMove->SetEndLastCut( nEndLastCut ); + Append( pLastCutMove ); + pLastCutMove = NULL; + ResetLastCut(); + SetInPasteCut( FALSE ); + } + else + { + bDoContents = TRUE; + nStartAction = GetActionMax() + 1; + StartBlockModify( SC_CTM_APPEND, nStartAction ); + } + if ( bDoContents ) + { + ScAddress aPos; + for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ ) + { + aPos.SetTab( nTab ); + for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ ) + { + aPos.SetCol( nCol ); + for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ ) + { + aPos.SetRow( nRow ); + AppendContent( aPos, pRefDoc ); + } + } + } + } + nEndAction = GetActionMax(); + EndBlockModify( nEndAction ); + if ( eClipMode == SC_CACM_CUT ) + { + nStartLastCut = nStartAction; + nEndLastCut = nEndAction; + } +} + + +void ScChangeTrack::AppendContentsIfInRefDoc( ScDocument* pRefDoc, + ULONG& nStartAction, ULONG& nEndAction ) +{ + ScDocumentIterator aIter( pRefDoc, 0, MAXTAB ); + if ( aIter.GetFirst() ) + { + nStartAction = GetActionMax() + 1; + StartBlockModify( SC_CTM_APPEND, nStartAction ); + SvNumberFormatter* pFormatter = pRefDoc->GetFormatTable(); + do + { + SCCOL nCol; + SCROW nRow; + SCTAB nTab; + aIter.GetPos( nCol, nRow, nTab ); + ScAddress aPos( nCol, nRow, nTab ); + AppendContent( aPos, aIter.GetCell(), + aIter.GetPattern()->GetNumberFormat( pFormatter ), pRefDoc ); + } while ( aIter.GetNext() ); + nEndAction = GetActionMax(); + EndBlockModify( nEndAction ); + } + else + nStartAction = nEndAction = 0; +} + + +ScChangeActionContent* ScChangeTrack::AppendContentOnTheFly( + const ScAddress& rPos, ScBaseCell* pOldCell, ScBaseCell* pNewCell, + ULONG nOldFormat, ULONG nNewFormat ) +{ + ScRange aRange( rPos ); + ScChangeActionContent* pAct = new ScChangeActionContent( aRange ); + pAct->SetOldNewCells( pOldCell, nOldFormat, pNewCell, nNewFormat, pDoc ); + Append( pAct ); + return pAct; +} + + +void ScChangeTrack::AppendInsert( const ScRange& rRange ) +{ + ScChangeActionIns* pAct = new ScChangeActionIns( rRange ); + Append( pAct ); +} + + +void ScChangeTrack::DeleteCellEntries( ScChangeActionCellListEntry*& pCellList, + ScChangeAction* pDeletor ) +{ + ScChangeActionCellListEntry* pE = pCellList; + while ( pE ) + { + ScChangeActionCellListEntry* pNext = pE->pNext; + pE->pContent->RemoveDeletedIn( pDeletor ); + if ( IsGenerated( pE->pContent->GetActionNumber() ) && + !pE->pContent->IsDeletedIn() ) + DeleteGeneratedDelContent( pE->pContent ); + delete pE; + pE = pNext; + } + pCellList = NULL; +} + + +ScChangeActionContent* ScChangeTrack::GenerateDelContent( + const ScAddress& rPos, const ScBaseCell* pCell, + const ScDocument* pFromDoc ) +{ + ScChangeActionContent* pContent = new ScChangeActionContent( + ScRange( rPos ) ); + pContent->SetActionNumber( --nGeneratedMin ); + // nur NewValue + ScChangeActionContent::SetValue( pContent->aNewValue, pContent->pNewCell, + rPos, pCell, pFromDoc, pDoc ); + // pNextContent und pPrevContent werden nicht gesetzt + if ( pFirstGeneratedDelContent ) + { // vorne reinhaengen + pFirstGeneratedDelContent->pPrev = pContent; + pContent->pNext = pFirstGeneratedDelContent; + } + pFirstGeneratedDelContent = pContent; + aGeneratedTable.Insert( nGeneratedMin, pContent ); + NotifyModified( SC_CTM_APPEND, nGeneratedMin, nGeneratedMin ); + return pContent; +} + + +void ScChangeTrack::DeleteGeneratedDelContent( ScChangeActionContent* pContent ) +{ + ULONG nAct = pContent->GetActionNumber(); + aGeneratedTable.Remove( nAct ); + if ( pFirstGeneratedDelContent == pContent ) + pFirstGeneratedDelContent = (ScChangeActionContent*) pContent->pNext; + if ( pContent->pNext ) + pContent->pNext->pPrev = pContent->pPrev; + if ( pContent->pPrev ) + pContent->pPrev->pNext = pContent->pNext; + delete pContent; + NotifyModified( SC_CTM_REMOVE, nAct, nAct ); + if ( nAct == nGeneratedMin ) + ++nGeneratedMin; //! erst nach NotifyModified wg. IsGenerated +} + + +ScChangeActionContent* ScChangeTrack::SearchContentAt( + const ScBigAddress& rPos, ScChangeAction* pButNotThis ) const +{ + SCSIZE nSlot = ComputeContentSlot( rPos.Row() ); + for ( ScChangeActionContent* p = ppContentSlots[nSlot]; p; + p = p->GetNextInSlot() ) + { + if ( p != pButNotThis && !p->IsDeletedIn() && + p->GetBigRange().aStart == rPos ) + { + ScChangeActionContent* pContent = p->GetTopContent(); + if ( !pContent->IsDeletedIn() ) + return pContent; + } + } + return NULL; +} + + +void ScChangeTrack::AddDependentWithNotify( ScChangeAction* pParent, + ScChangeAction* pDependent ) +{ + ScChangeActionLinkEntry* pLink = pParent->AddDependent( pDependent ); + pDependent->AddLink( pParent, pLink ); + if ( aModifiedLink.IsSet() ) + { + ULONG nMod = pParent->GetActionNumber(); + NotifyModified( SC_CTM_PARENT, nMod, nMod ); + } +} + + +void ScChangeTrack::Dependencies( ScChangeAction* pAct ) +{ + // Finde die letzte Abhaengigkeit fuer jeweils Col/Row/Tab. + // Content an gleicher Position verketten. + // Move Abhaengigkeiten. + ScChangeActionType eActType = pAct->GetType(); + if ( eActType == SC_CAT_REJECT || + (eActType == SC_CAT_MOVE && pAct->IsRejecting()) ) + return ; // diese Rejects sind nicht abhaengig + + if ( eActType == SC_CAT_CONTENT ) + { + if ( !(((ScChangeActionContent*)pAct)->GetNextContent() || + ((ScChangeActionContent*)pAct)->GetPrevContent()) ) + { // Contents an gleicher Position verketten + ScChangeActionContent* pContent = SearchContentAt( + pAct->GetBigRange().aStart, pAct ); + if ( pContent ) + { + pContent->SetNextContent( (ScChangeActionContent*) pAct ); + ((ScChangeActionContent*)pAct)->SetPrevContent( pContent ); + } + } + const ScBaseCell* pCell = ((ScChangeActionContent*)pAct)->GetNewCell(); + if ( ScChangeActionContent::GetContentCellType( pCell ) == SC_CACCT_MATREF ) + { + ScAddress aOrg; + ((const ScFormulaCell*)pCell)->GetMatrixOrigin( aOrg ); + ScChangeActionContent* pContent = SearchContentAt( aOrg, pAct ); + if ( pContent && pContent->IsMatrixOrigin() ) + { + AddDependentWithNotify( pContent, pAct ); + } + else + { + DBG_ERRORFILE( "ScChangeTrack::Dependencies: MatOrg not found" ); + } + } + } + + if ( !(pLinkInsertCol || pLinkInsertRow || pLinkInsertTab || pLinkMove) ) + return ; // keine Dependencies + if ( pAct->IsRejecting() ) + return ; // ausser Content keine Dependencies + + // Insert in einem entsprechenden Insert haengt davon ab, sonst muesste + // der vorherige Insert gesplittet werden. + // Sich kreuzende Inserts und Deletes sind nicht abhaengig. + // Alles andere ist abhaengig. + + // Der zuletzt eingelinkte Insert steht am Anfang einer Kette, + // also genau richtig + + const ScBigRange& rRange = pAct->GetBigRange(); + BOOL bActNoInsert = !pAct->IsInsertType(); + BOOL bActColDel = ( eActType == SC_CAT_DELETE_COLS ); + BOOL bActRowDel = ( eActType == SC_CAT_DELETE_ROWS ); + BOOL bActTabDel = ( eActType == SC_CAT_DELETE_TABS ); + + if ( pLinkInsertCol && (eActType == SC_CAT_INSERT_COLS || + (bActNoInsert && !bActRowDel && !bActTabDel)) ) + { + for ( ScChangeActionLinkEntry* pL = pLinkInsertCol; pL; pL = pL->GetNext() ) + { + ScChangeActionIns* pTest = (ScChangeActionIns*) pL->GetAction(); + if ( !pTest->IsRejected() && + pTest->GetBigRange().Intersects( rRange ) ) + { + AddDependentWithNotify( pTest, pAct ); + break; // for + } + } + } + if ( pLinkInsertRow && (eActType == SC_CAT_INSERT_ROWS || + (bActNoInsert && !bActColDel && !bActTabDel)) ) + { + for ( ScChangeActionLinkEntry* pL = pLinkInsertRow; pL; pL = pL->GetNext() ) + { + ScChangeActionIns* pTest = (ScChangeActionIns*) pL->GetAction(); + if ( !pTest->IsRejected() && + pTest->GetBigRange().Intersects( rRange ) ) + { + AddDependentWithNotify( pTest, pAct ); + break; // for + } + } + } + if ( pLinkInsertTab && (eActType == SC_CAT_INSERT_TABS || + (bActNoInsert && !bActColDel && !bActRowDel)) ) + { + for ( ScChangeActionLinkEntry* pL = pLinkInsertTab; pL; pL = pL->GetNext() ) + { + ScChangeActionIns* pTest = (ScChangeActionIns*) pL->GetAction(); + if ( !pTest->IsRejected() && + pTest->GetBigRange().Intersects( rRange ) ) + { + AddDependentWithNotify( pTest, pAct ); + break; // for + } + } + } + + if ( pLinkMove ) + { + if ( eActType == SC_CAT_CONTENT ) + { // Content ist von FromRange abhaengig + const ScBigAddress& rPos = rRange.aStart; + for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() ) + { + ScChangeActionMove* pTest = (ScChangeActionMove*) pL->GetAction(); + if ( !pTest->IsRejected() && + pTest->GetFromRange().In( rPos ) ) + { + AddDependentWithNotify( pTest, pAct ); + } + } + } + else if ( eActType == SC_CAT_MOVE ) + { // Move FromRange ist von ToRange abhaengig + const ScBigRange& rFromRange = ((ScChangeActionMove*)pAct)->GetFromRange(); + for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() ) + { + ScChangeActionMove* pTest = (ScChangeActionMove*) pL->GetAction(); + if ( !pTest->IsRejected() && + pTest->GetBigRange().Intersects( rFromRange ) ) + { + AddDependentWithNotify( pTest, pAct ); + } + } + } + else + { // Inserts und Deletes sind abhaengig, sobald sie FromRange oder + // ToRange kreuzen + for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() ) + { + ScChangeActionMove* pTest = (ScChangeActionMove*) pL->GetAction(); + if ( !pTest->IsRejected() && + (pTest->GetFromRange().Intersects( rRange ) || + pTest->GetBigRange().Intersects( rRange )) ) + { + AddDependentWithNotify( pTest, pAct ); + } + } + } + } +} + + +void ScChangeTrack::Remove( ScChangeAction* pRemove ) +{ + // aus Track ausklinken + ULONG nAct = pRemove->GetActionNumber(); + aTable.Remove( nAct ); + if ( nAct == nActionMax ) + --nActionMax; + if ( pRemove == pLast ) + pLast = pRemove->pPrev; + if ( pRemove == pFirst ) + pFirst = pRemove->pNext; + if ( nAct == nMarkLastSaved ) + nMarkLastSaved = + ( pRemove->pPrev ? pRemove->pPrev->GetActionNumber() : 0 ); + + // aus der globalen Kette ausklinken + if ( pRemove->pNext ) + pRemove->pNext->pPrev = pRemove->pPrev; + if ( pRemove->pPrev ) + pRemove->pPrev->pNext = pRemove->pNext; + + // Dependencies nicht loeschen, passiert on delete automatisch durch + // LinkEntry, ohne Listen abzuklappern + + if ( aModifiedLink.IsSet() ) + { + NotifyModified( SC_CTM_REMOVE, nAct, nAct ); + if ( pRemove->GetType() == SC_CAT_CONTENT ) + { + ScChangeActionContent* pContent = (ScChangeActionContent*) pRemove; + if ( ( pContent = pContent->GetPrevContent() ) != NULL ) + { + ULONG nMod = pContent->GetActionNumber(); + NotifyModified( SC_CTM_CHANGE, nMod, nMod ); + } + } + else if ( pLast ) + NotifyModified( SC_CTM_CHANGE, pFirst->GetActionNumber(), + pLast->GetActionNumber() ); + } + + if ( IsInPasteCut() && pRemove->GetType() == SC_CAT_CONTENT ) + { //! Content wird wiederverwertet + ScChangeActionContent* pContent = (ScChangeActionContent*) pRemove; + pContent->RemoveAllLinks(); + pContent->ClearTrack(); + pContent->pNext = pContent->pPrev = NULL; + pContent->pNextContent = pContent->pPrevContent = NULL; + } +} + + +void ScChangeTrack::Undo( ULONG nStartAction, ULONG nEndAction, bool bMerge ) +{ + // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong + if ( bMerge ) + { + SetMergeState( SC_CTMS_UNDO ); + } + + if ( nStartAction == 0 ) + ++nStartAction; + if ( nEndAction > nActionMax ) + nEndAction = nActionMax; + if ( nEndAction && nStartAction <= nEndAction ) + { + if ( nStartAction == nStartLastCut && nEndAction == nEndLastCut && + !IsInPasteCut() ) + ResetLastCut(); + StartBlockModify( SC_CTM_REMOVE, nStartAction ); + for ( ULONG j = nEndAction; j >= nStartAction; --j ) + { // rueckwaerts um evtl. nActionMax zu recyclen und schnelleren + // Zugriff via pLast, Deletes in richtiger Reihenfolge + ScChangeAction* pAct = ( (j == nActionMax && pLast && + pLast->GetActionNumber() == j) ? pLast : GetAction( j ) ); + if ( pAct ) + { + if ( pAct->IsDeleteType() ) + { + if ( j == nEndAction || (pAct != pLast && + ((ScChangeActionDel*)pAct)->IsTopDelete()) ) + { + SetInDeleteTop( TRUE ); + SetInDeleteRange( ((ScChangeActionDel*)pAct)-> + GetOverAllRange().MakeRange() ); + } + } + UpdateReference( pAct, TRUE ); + SetInDeleteTop( FALSE ); + Remove( pAct ); + if ( IsInPasteCut() ) + aPasteCutTable.Insert( pAct->GetActionNumber(), pAct ); + else + { + if ( j == nStartAction && pAct->GetType() == SC_CAT_MOVE ) + { + ScChangeActionMove* pMove = (ScChangeActionMove*) pAct; + ULONG nStart = pMove->GetStartLastCut(); + ULONG nEnd = pMove->GetEndLastCut(); + if ( nStart && nStart <= nEnd ) + { // LastCut wiederherstellen + //! Links vor Cut-Append aufloesen + pMove->RemoveAllLinks(); + StartBlockModify( SC_CTM_APPEND, nStart ); + for ( ULONG nCut = nStart; nCut <= nEnd; nCut++ ) + { + ScChangeAction* pCut = aPasteCutTable.Remove( nCut ); + if ( pCut ) + { + DBG_ASSERT( !aTable.Get( nCut ), "ScChangeTrack::Undo: nCut dup" ); + Append( pCut, nCut ); + } + else + { + DBG_ERROR( "ScChangeTrack::Undo: nCut not found" ); + } + } + EndBlockModify( nEnd ); + ResetLastCut(); + nStartLastCut = nStart; + nEndLastCut = nEnd; + pLastCutMove = pMove; + SetLastCutMoveRange( + pMove->GetFromRange().MakeRange(), pDoc ); + } + else + delete pMove; + } + else + delete pAct; + } + } + } + EndBlockModify( nEndAction ); + } + + // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong + if ( bMerge ) + { + SetMergeState( SC_CTMS_OTHER ); + } +} + + +// static +BOOL ScChangeTrack::MergeIgnore( const ScChangeAction& rAction, ULONG nFirstMerge ) +{ + if ( rAction.IsRejected() ) + return TRUE; // da kommt noch eine passende Reject-Action + + if ( rAction.IsRejecting() && rAction.GetRejectAction() >= nFirstMerge ) + return TRUE; // da ist sie + + return FALSE; // alles andere +} + + +void ScChangeTrack::MergePrepare( ScChangeAction* pFirstMerge, bool bShared ) +{ + SetMergeState( SC_CTMS_PREPARE ); + ULONG nFirstMerge = pFirstMerge->GetActionNumber(); + ScChangeAction* pAct = GetLast(); + if ( pAct ) + { + SetLastMerge( pAct->GetActionNumber() ); + while ( pAct ) + { // rueckwaerts, Deletes in richtiger Reihenfolge + // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong + if ( bShared || !ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) ) + { + if ( pAct->IsDeleteType() ) + { + if ( ((ScChangeActionDel*)pAct)->IsTopDelete() ) + { + SetInDeleteTop( TRUE ); + SetInDeleteRange( ((ScChangeActionDel*)pAct)-> + GetOverAllRange().MakeRange() ); + } + } + UpdateReference( pAct, TRUE ); + SetInDeleteTop( FALSE ); + pAct->DeleteCellEntries(); // sonst GPF bei Track Clear() + } + pAct = ( pAct == pFirstMerge ? NULL : pAct->GetPrev() ); + } + } + SetMergeState( SC_CTMS_OTHER ); //! nachfolgende per default MergeOther +} + + +void ScChangeTrack::MergeOwn( ScChangeAction* pAct, ULONG nFirstMerge, bool bShared ) +{ + // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong + if ( bShared || !ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) ) + { + SetMergeState( SC_CTMS_OWN ); + if ( pAct->IsDeleteType() ) + { + if ( ((ScChangeActionDel*)pAct)->IsTopDelete() ) + { + SetInDeleteTop( TRUE ); + SetInDeleteRange( ((ScChangeActionDel*)pAct)-> + GetOverAllRange().MakeRange() ); + } + } + UpdateReference( pAct, FALSE ); + SetInDeleteTop( FALSE ); + SetMergeState( SC_CTMS_OTHER ); //! nachfolgende per default MergeOther + } +} + + +void ScChangeTrack::UpdateReference( ScChangeAction* pAct, BOOL bUndo ) +{ + ScChangeActionType eActType = pAct->GetType(); + if ( eActType == SC_CAT_CONTENT || eActType == SC_CAT_REJECT ) + return ; + + //! Formelzellen haengen nicht im Dokument + BOOL bOldAutoCalc = pDoc->GetAutoCalc(); + pDoc->SetAutoCalc( FALSE ); + BOOL bOldNoListening = pDoc->GetNoListening(); + pDoc->SetNoListening( TRUE ); + //! Formelzellen ExpandRefs synchronisiert zu denen im Dokument + BOOL bOldExpandRefs = pDoc->IsExpandRefs(); + if ( (!bUndo && pAct->IsInsertType()) || (bUndo && pAct->IsDeleteType()) ) + pDoc->SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() ); + + if ( pAct->IsDeleteType() ) + { + SetInDeleteUndo( bUndo ); + SetInDelete( TRUE ); + } + else if ( GetMergeState() == SC_CTMS_OWN ) + { + // Referenzen von Formelzellen wiederherstellen, + // vorheriges MergePrepare war bei einem Insert wie ein Delete + if ( pAct->IsInsertType() ) + SetInDeleteUndo( TRUE ); + } + + //! erst die generated, als waeren sie vorher getrackt worden + if ( pFirstGeneratedDelContent ) + UpdateReference( (ScChangeAction**)&pFirstGeneratedDelContent, pAct, + bUndo ); + UpdateReference( &pFirst, pAct, bUndo ); + + SetInDelete( FALSE ); + SetInDeleteUndo( FALSE ); + + pDoc->SetExpandRefs( bOldExpandRefs ); + pDoc->SetNoListening( bOldNoListening ); + pDoc->SetAutoCalc( bOldAutoCalc ); +} + + +void ScChangeTrack::UpdateReference( ScChangeAction** ppFirstAction, + ScChangeAction* pAct, BOOL bUndo ) +{ + ScChangeActionType eActType = pAct->GetType(); + BOOL bGeneratedDelContents = + ( ppFirstAction == (ScChangeAction**)&pFirstGeneratedDelContent ); + const ScBigRange& rOrgRange = pAct->GetBigRange(); + ScBigRange aRange( rOrgRange ); + ScBigRange aDelRange( rOrgRange ); + INT32 nDx, nDy, nDz; + nDx = nDy = nDz = 0; + UpdateRefMode eMode = URM_INSDEL; + BOOL bDel = FALSE; + switch ( eActType ) + { + case SC_CAT_INSERT_COLS : + aRange.aEnd.SetCol( nInt32Max ); + nDx = rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1; + break; + case SC_CAT_INSERT_ROWS : + aRange.aEnd.SetRow( nInt32Max ); + nDy = rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1; + break; + case SC_CAT_INSERT_TABS : + aRange.aEnd.SetTab( nInt32Max ); + nDz = rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1; + break; + case SC_CAT_DELETE_COLS : + aRange.aEnd.SetCol( nInt32Max ); + nDx = -(rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1); + aDelRange.aEnd.SetCol( aDelRange.aStart.Col() - nDx - 1 ); + bDel = TRUE; + break; + case SC_CAT_DELETE_ROWS : + aRange.aEnd.SetRow( nInt32Max ); + nDy = -(rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1); + aDelRange.aEnd.SetRow( aDelRange.aStart.Row() - nDy - 1 ); + bDel = TRUE; + break; + case SC_CAT_DELETE_TABS : + aRange.aEnd.SetTab( nInt32Max ); + nDz = -(rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1); + aDelRange.aEnd.SetTab( aDelRange.aStart.Tab() - nDz - 1 ); + bDel = TRUE; + break; + case SC_CAT_MOVE : + eMode = URM_MOVE; + ((ScChangeActionMove*)pAct)->GetDelta( nDx, nDy, nDz ); + break; + default: + DBG_ERROR( "ScChangeTrack::UpdateReference: unknown Type" ); + } + if ( bUndo ) + { + nDx = -nDx; + nDy = -nDy; + nDz = -nDz; + } + if ( bDel ) + { //! fuer diesen Mechanismus gilt: + //! es gibt nur ganze, einfache geloeschte Spalten/Zeilen + ScChangeActionDel* pActDel = (ScChangeActionDel*) pAct; + if ( !bUndo ) + { // Delete + ScChangeActionType eInsType = SC_CAT_NONE; // for Insert-Undo-"Deletes" + switch ( eActType ) + { + case SC_CAT_DELETE_COLS : + eInsType = SC_CAT_INSERT_COLS; + break; + case SC_CAT_DELETE_ROWS : + eInsType = SC_CAT_INSERT_ROWS; + break; + case SC_CAT_DELETE_TABS : + eInsType = SC_CAT_INSERT_TABS; + break; + default: + { + // added to avoid warnings + } + } + for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) + { + if ( p == pAct ) + continue; // for + BOOL bUpdate = TRUE; + if ( GetMergeState() == SC_CTMS_OTHER && + p->GetActionNumber() <= GetLastMerge() ) + { // Delete in mergendem Dokument, Action im zu mergenden + if ( p->IsInsertType() ) + { + // Bei Insert Referenzen nur anpassen, wenn das Delete + // das Insert nicht schneidet. + if ( !aDelRange.Intersects( p->GetBigRange() ) ) + p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); + bUpdate = FALSE; + } + else if ( p->GetType() == SC_CAT_CONTENT && + p->IsDeletedInDelType( eInsType ) ) + { // Content in Insert-Undo-"Delete" + // Nicht anpassen, wenn dieses Delete in dem + // Insert-"Delete" sein wuerde (ist nur verschoben). + if ( aDelRange.In( p->GetBigRange().aStart ) ) + bUpdate = FALSE; + else + { + const ScChangeActionLinkEntry* pLink = p->GetDeletedIn(); + while ( pLink && bUpdate ) + { + const ScChangeAction* pDel = pLink->GetAction(); + if ( pDel && pDel->GetType() == eInsType && + pDel->GetBigRange().In( aDelRange ) ) + bUpdate = FALSE; + pLink = pLink->GetNext(); + } + } + } + if ( !bUpdate ) + continue; // for + } + if ( aDelRange.In( p->GetBigRange() ) ) + { + // Innerhalb eines gerade geloeschten Bereiches nicht + // anpassen, stattdessen dem Bereich zuordnen. + // Mehrfache geloeschte Bereiche "stapeln". + // Kreuzende Deletes setzen mehrfach geloescht. + if ( !p->IsDeletedInDelType( eActType ) ) + { + p->SetDeletedIn( pActDel ); + // GeneratedDelContent in zu loeschende Liste aufnehmen + if ( bGeneratedDelContents ) + pActDel->AddContent( (ScChangeActionContent*) p ); + } + bUpdate = FALSE; + } + else + { + // Eingefuegte Bereiche abschneiden, wenn Start/End im + // Delete liegt, aber das Insert nicht komplett innerhalb + // des Delete liegt bzw. das Delete nicht komplett im + // Insert. Das Delete merkt sich, welchem Insert es was + // abgeschnitten hat, es kann auch nur ein einziges Insert + // sein (weil Delete einspaltig/einzeilig ist). + // Abgeschnittene Moves kann es viele geben. + //! Ein Delete ist immer einspaltig/einzeilig, deswegen 1 + //! ohne die Ueberlappung auszurechnen. + switch ( p->GetType() ) + { + case SC_CAT_INSERT_COLS : + if ( eActType == SC_CAT_DELETE_COLS ) + { + if ( aDelRange.In( p->GetBigRange().aStart ) ) + { + pActDel->SetCutOffInsert( + (ScChangeActionIns*) p, 1 ); + p->GetBigRange().aStart.IncCol( 1 ); + } + else if ( aDelRange.In( p->GetBigRange().aEnd ) ) + { + pActDel->SetCutOffInsert( + (ScChangeActionIns*) p, -1 ); + p->GetBigRange().aEnd.IncCol( -1 ); + } + } + break; + case SC_CAT_INSERT_ROWS : + if ( eActType == SC_CAT_DELETE_ROWS ) + { + if ( aDelRange.In( p->GetBigRange().aStart ) ) + { + pActDel->SetCutOffInsert( + (ScChangeActionIns*) p, 1 ); + p->GetBigRange().aStart.IncRow( 1 ); + } + else if ( aDelRange.In( p->GetBigRange().aEnd ) ) + { + pActDel->SetCutOffInsert( + (ScChangeActionIns*) p, -1 ); + p->GetBigRange().aEnd.IncRow( -1 ); + } + } + break; + case SC_CAT_INSERT_TABS : + if ( eActType == SC_CAT_DELETE_TABS ) + { + if ( aDelRange.In( p->GetBigRange().aStart ) ) + { + pActDel->SetCutOffInsert( + (ScChangeActionIns*) p, 1 ); + p->GetBigRange().aStart.IncTab( 1 ); + } + else if ( aDelRange.In( p->GetBigRange().aEnd ) ) + { + pActDel->SetCutOffInsert( + (ScChangeActionIns*) p, -1 ); + p->GetBigRange().aEnd.IncTab( -1 ); + } + } + break; + case SC_CAT_MOVE : + { + ScChangeActionMove* pMove = (ScChangeActionMove*) p; + short nFrom = 0; + short nTo = 0; + if ( aDelRange.In( pMove->GetBigRange().aStart ) ) + nTo = 1; + else if ( aDelRange.In( pMove->GetBigRange().aEnd ) ) + nTo = -1; + if ( aDelRange.In( pMove->GetFromRange().aStart ) ) + nFrom = 1; + else if ( aDelRange.In( pMove->GetFromRange().aEnd ) ) + nFrom = -1; + if ( nFrom ) + { + switch ( eActType ) + { + case SC_CAT_DELETE_COLS : + if ( nFrom > 0 ) + pMove->GetFromRange().aStart.IncCol( nFrom ); + else + pMove->GetFromRange().aEnd.IncCol( nFrom ); + break; + case SC_CAT_DELETE_ROWS : + if ( nFrom > 0 ) + pMove->GetFromRange().aStart.IncRow( nFrom ); + else + pMove->GetFromRange().aEnd.IncRow( nFrom ); + break; + case SC_CAT_DELETE_TABS : + if ( nFrom > 0 ) + pMove->GetFromRange().aStart.IncTab( nFrom ); + else + pMove->GetFromRange().aEnd.IncTab( nFrom ); + break; + default: + { + // added to avoid warnings + } + } + } + if ( nTo ) + { + switch ( eActType ) + { + case SC_CAT_DELETE_COLS : + if ( nTo > 0 ) + pMove->GetBigRange().aStart.IncCol( nTo ); + else + pMove->GetBigRange().aEnd.IncCol( nTo ); + break; + case SC_CAT_DELETE_ROWS : + if ( nTo > 0 ) + pMove->GetBigRange().aStart.IncRow( nTo ); + else + pMove->GetBigRange().aEnd.IncRow( nTo ); + break; + case SC_CAT_DELETE_TABS : + if ( nTo > 0 ) + pMove->GetBigRange().aStart.IncTab( nTo ); + else + pMove->GetBigRange().aEnd.IncTab( nTo ); + break; + default: + { + // added to avoid warnings + } + } + } + if ( nFrom || nTo ) + { + ScChangeActionDelMoveEntry* pLink = + pActDel->AddCutOffMove( pMove, nFrom, nTo ); + pMove->AddLink( pActDel, pLink ); + } + } + break; + default: + { + // added to avoid warnings + } + } + } + if ( bUpdate ) + { + p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); + if ( p->GetType() == eActType && !p->IsRejected() && + !pActDel->IsDeletedIn() && + p->GetBigRange().In( aDelRange ) ) + pActDel->SetDeletedIn( p ); // "druntergerutscht" + } + } + } + else + { // Undo Delete + for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) + { + if ( p == pAct ) + continue; // for + BOOL bUpdate = TRUE; + if ( aDelRange.In( p->GetBigRange() ) ) + { + // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong + if ( GetMergeState() == SC_CTMS_UNDO && !p->IsDeletedIn( pAct ) && pAct->IsDeleteType() && + ( p->GetType() == SC_CAT_CONTENT || + p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS || + p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) ) + { + p->SetDeletedIn( pAct ); + } + + if ( p->IsDeletedInDelType( eActType ) ) + { + if ( p->IsDeletedIn( pActDel ) ) + { + if ( p->GetType() != SC_CAT_CONTENT || + ((ScChangeActionContent*)p)->IsTopContent() ) + { // erst der TopContent wird wirklich entfernt + p->RemoveDeletedIn( pActDel ); + // GeneratedDelContent _nicht_ aus Liste loeschen, + // wir brauchen ihn evtl. noch fuer Reject, + // geloescht wird in DeleteCellEntries + } + } + bUpdate = FALSE; + } + else if ( eActType != SC_CAT_DELETE_TABS && + p->IsDeletedInDelType( SC_CAT_DELETE_TABS ) ) + { // in geloeschten Tabellen nicht updaten, + // ausser wenn Tabelle verschoben wird + bUpdate = FALSE; + } + if ( p->GetType() == eActType && pActDel->IsDeletedIn( p ) ) + { + pActDel->RemoveDeletedIn( p ); // "druntergerutscht" + bUpdate = TRUE; + } + } + if ( bUpdate ) + p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); + } + if ( !bGeneratedDelContents ) + { // die werden sonst noch fuer das echte Undo gebraucht + pActDel->UndoCutOffInsert(); + pActDel->UndoCutOffMoves(); + } + } + } + else if ( eActType == SC_CAT_MOVE ) + { + ScChangeActionMove* pActMove = (ScChangeActionMove*) pAct; + BOOL bLastCutMove = ( pActMove == pLastCutMove ); + const ScBigRange& rTo = pActMove->GetBigRange(); + const ScBigRange& rFrom = pActMove->GetFromRange(); + if ( !bUndo ) + { // Move + for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) + { + if ( p == pAct ) + continue; // for + if ( p->GetType() == SC_CAT_CONTENT ) + { + // Inhalt in Ziel deleten (Inhalt in Quelle moven) + if ( rTo.In( p->GetBigRange() ) ) + { + if ( !p->IsDeletedIn( pActMove ) ) + { + p->SetDeletedIn( pActMove ); + // GeneratedDelContent in zu loeschende Liste aufnehmen + if ( bGeneratedDelContents ) + pActMove->AddContent( (ScChangeActionContent*) p ); + } + } + else if ( bLastCutMove && + p->GetActionNumber() > nEndLastCut && + rFrom.In( p->GetBigRange() ) ) + { // Paste Cut: neuer Content nach Cut eingefuegt, bleibt. + // Aufsplitten der ContentChain + ScChangeActionContent *pHere, *pTmp; + pHere = (ScChangeActionContent*) p; + while ( (pTmp = pHere->GetPrevContent()) != NULL && + pTmp->GetActionNumber() > nEndLastCut ) + pHere = pTmp; + if ( pTmp ) + { // wird TopContent des Move + pTmp->SetNextContent( NULL ); + pHere->SetPrevContent( NULL ); + } + do + { // Abhaengigkeit vom FromRange herstellen + AddDependentWithNotify( pActMove, pHere ); + } while ( ( pHere = pHere->GetNextContent() ) != NULL ); + } + // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly + else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() ) + p->UpdateReference( this, eMode, rFrom, nDx, nDy, nDz ); + } + } + } + else + { // Undo Move + BOOL bActRejected = pActMove->IsRejected(); + for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) + { + if ( p == pAct ) + continue; // for + if ( p->GetType() == SC_CAT_CONTENT ) + { + // Inhalt in Ziel moven, wenn nicht deleted, sonst undelete + if ( p->IsDeletedIn( pActMove ) ) + { + if ( ((ScChangeActionContent*)p)->IsTopContent() ) + { // erst der TopContent wird wirklich entfernt + p->RemoveDeletedIn( pActMove ); + // GeneratedDelContent _nicht_ aus Liste loeschen, + // wir brauchen ihn evtl. noch fuer Reject, + // geloescht wird in DeleteCellEntries + } + } + // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly + else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() ) + p->UpdateReference( this, eMode, rTo, nDx, nDy, nDz ); + if ( bActRejected && + ((ScChangeActionContent*)p)->IsTopContent() && + rFrom.In( p->GetBigRange() ) ) + { // Abhaengigkeit herstellen, um Content zu schreiben + ScChangeActionLinkEntry* pLink = + pActMove->AddDependent( p ); + p->AddLink( pActMove, pLink ); + } + } + } + } + } + else + { // Insert / Undo Insert + switch ( GetMergeState() ) + { + case SC_CTMS_NONE : + case SC_CTMS_OTHER : + { + for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) + { + if ( p == pAct ) + continue; // for + p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); + } + } + break; + case SC_CTMS_PREPARE : + { + // in Insert-Undo "Deleten" + const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry(); + while ( pLink ) + { + ScChangeAction* p = (ScChangeAction*) pLink->GetAction(); + if ( p ) + p->SetDeletedIn( pAct ); + pLink = pLink->GetNext(); + } + + // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly + for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) + { + if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() && + // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong + ( p->GetType() == SC_CAT_CONTENT || + p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS || + p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) && + pAct->GetBigRange().Intersects( p->GetBigRange() ) ) + { + p->SetDeletedIn( pAct ); + } + } + + for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) + { + if ( p == pAct ) + continue; // for + if ( !p->IsDeletedIn( pAct ) + // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet + && p->GetActionNumber() <= pAct->GetActionNumber() ) + { + p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); + } + } + } + break; + case SC_CTMS_OWN : + { + for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) + { + if ( p == pAct ) + continue; // for + if ( !p->IsDeletedIn( pAct ) + // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet + && p->GetActionNumber() <= pAct->GetActionNumber() ) + { + p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); + } + } + // in Insert-Undo "Delete" rueckgaengig + const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry(); + while ( pLink ) + { + ScChangeAction* p = (ScChangeAction*) pLink->GetAction(); + if ( p ) + p->RemoveDeletedIn( pAct ); + pLink = pLink->GetNext(); + } + + // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly + for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) + { + if ( p->IsDeletedIn( pAct ) && pAct->IsInsertType() && + // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong + ( p->GetType() == SC_CAT_CONTENT || + p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS || + p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) && + pAct->GetBigRange().Intersects( p->GetBigRange() ) ) + { + p->RemoveDeletedIn( pAct ); + } + } + } + break; + // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong + case SC_CTMS_UNDO : + { + for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) + { + if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() && + ( p->GetType() == SC_CAT_CONTENT || + p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS || + p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) && + pAct->GetBigRange().Intersects( p->GetBigRange() ) ) + { + p->SetDeletedIn( pAct ); + } + } + + for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) + { + if ( p == pAct ) + { + continue; + } + if ( !p->IsDeletedIn( pAct ) && p->GetActionNumber() <= pAct->GetActionNumber() ) + { + p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); + } + } + } + break; + } + } +} + + +void ScChangeTrack::GetDependents( ScChangeAction* pAct, + ScChangeActionTable& rTable, BOOL bListMasterDelete, BOOL bAllFlat ) const +{ + //! bAllFlat==TRUE: intern aus Accept oder Reject gerufen, + //! => Generated werden nicht aufgenommen + + BOOL bIsDelete = pAct->IsDeleteType(); + BOOL bIsMasterDelete = ( bListMasterDelete && pAct->IsMasterDelete() ); + + const ScChangeAction* pCur = pAct; + ScChangeActionStack* pStack = new ScChangeActionStack; + do + { + if ( pCur->IsInsertType() ) + { + const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry(); + while ( pL ) + { + ScChangeAction* p = (ScChangeAction*) pL->GetAction(); + if ( p != pAct ) + { + if ( bAllFlat ) + { + ULONG n = p->GetActionNumber(); + if ( !IsGenerated( n ) && rTable.Insert( n, p ) ) + if ( p->HasDependent() ) + pStack->Push( p ); + } + else + { + if ( p->GetType() == SC_CAT_CONTENT ) + { + if ( ((ScChangeActionContent*)p)->IsTopContent() ) + rTable.Insert( p->GetActionNumber(), p ); + } + else + rTable.Insert( p->GetActionNumber(), p ); + } + } + pL = pL->GetNext(); + } + } + else if ( pCur->IsDeleteType() ) + { + if ( bIsDelete ) + { // Inhalte geloeschter Bereiche interessieren nur bei Delete + ScChangeActionDel* pDel = (ScChangeActionDel*) pCur; + if ( !bAllFlat && bIsMasterDelete && pCur == pAct ) + { + // zu diesem Delete gehoerende Deletes in gleiche Ebene, + // wenn dieses Delete das momentan oberste einer Reihe ist, + ScChangeActionType eType = pDel->GetType(); + ScChangeAction* p = pDel; + while ( (p = p->GetPrev()) != NULL && p->GetType() == eType && + !((ScChangeActionDel*)p)->IsTopDelete() ) + rTable.Insert( p->GetActionNumber(), p ); + // dieses Delete auch in Table! + rTable.Insert( pAct->GetActionNumber(), pAct ); + } + else + { + const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry(); + while ( pL ) + { + ScChangeAction* p = (ScChangeAction*) pL->GetAction(); + if ( p != pAct ) + { + if ( bAllFlat ) + { + // nur ein TopContent einer Kette ist in LinkDeleted + ULONG n = p->GetActionNumber(); + if ( !IsGenerated( n ) && rTable.Insert( n, p ) ) + if ( p->HasDeleted() || + p->GetType() == SC_CAT_CONTENT ) + pStack->Push( p ); + } + else + { + if ( p->IsDeleteType() ) + { // weiteres TopDelete in gleiche Ebene, + // es ist nicht rejectable + if ( ((ScChangeActionDel*)p)->IsTopDelete() ) + rTable.Insert( p->GetActionNumber(), p ); + } + else + rTable.Insert( p->GetActionNumber(), p ); + } + } + pL = pL->GetNext(); + } + } + } + } + else if ( pCur->GetType() == SC_CAT_MOVE ) + { + // geloeschte Contents im ToRange + const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry(); + while ( pL ) + { + ScChangeAction* p = (ScChangeAction*) pL->GetAction(); + if ( p != pAct && rTable.Insert( p->GetActionNumber(), p ) ) + { + // nur ein TopContent einer Kette ist in LinkDeleted + if ( bAllFlat && (p->HasDeleted() || + p->GetType() == SC_CAT_CONTENT) ) + pStack->Push( p ); + } + pL = pL->GetNext(); + } + // neue Contents im FromRange oder neuer FromRange im ToRange + // oder Inserts/Deletes in FromRange/ToRange + pL = pCur->GetFirstDependentEntry(); + while ( pL ) + { + ScChangeAction* p = (ScChangeAction*) pL->GetAction(); + if ( p != pAct ) + { + if ( bAllFlat ) + { + ULONG n = p->GetActionNumber(); + if ( !IsGenerated( n ) && rTable.Insert( n, p ) ) + if ( p->HasDependent() || p->HasDeleted() ) + pStack->Push( p ); + } + else + { + if ( p->GetType() == SC_CAT_CONTENT ) + { + if ( ((ScChangeActionContent*)p)->IsTopContent() ) + rTable.Insert( p->GetActionNumber(), p ); + } + else + rTable.Insert( p->GetActionNumber(), p ); + } + } + pL = pL->GetNext(); + } + } + else if ( pCur->GetType() == SC_CAT_CONTENT ) + { // alle Aenderungen an gleicher Position + ScChangeActionContent* pContent = (ScChangeActionContent*) pCur; + // alle vorherigen + while ( ( pContent = pContent->GetPrevContent() ) != NULL ) + { + if ( !pContent->IsRejected() ) + rTable.Insert( pContent->GetActionNumber(), pContent ); + } + pContent = (ScChangeActionContent*) pCur; + // alle nachfolgenden + while ( ( pContent = pContent->GetNextContent() ) != NULL ) + { + if ( !pContent->IsRejected() ) + rTable.Insert( pContent->GetActionNumber(), pContent ); + } + // all MatrixReferences of a MatrixOrigin + const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry(); + while ( pL ) + { + ScChangeAction* p = (ScChangeAction*) pL->GetAction(); + if ( p != pAct ) + { + if ( bAllFlat ) + { + ULONG n = p->GetActionNumber(); + if ( !IsGenerated( n ) && rTable.Insert( n, p ) ) + if ( p->HasDependent() ) + pStack->Push( p ); + } + else + rTable.Insert( p->GetActionNumber(), p ); + } + pL = pL->GetNext(); + } + } + else if ( pCur->GetType() == SC_CAT_REJECT ) + { + if ( bAllFlat ) + { + ScChangeAction* p = GetAction( + ((ScChangeActionReject*)pCur)->GetRejectAction() ); + if ( p != pAct && !rTable.Get( p->GetActionNumber() ) ) + pStack->Push( p ); + } + } + } while ( ( pCur = pStack->Pop() ) != NULL ); + delete pStack; +} + + +BOOL ScChangeTrack::SelectContent( ScChangeAction* pAct, BOOL bOldest ) +{ + if ( pAct->GetType() != SC_CAT_CONTENT ) + return FALSE; + + ScChangeActionContent* pContent = (ScChangeActionContent*) pAct; + if ( bOldest ) + { + pContent = pContent->GetTopContent(); + ScChangeActionContent* pPrevContent; + while ( (pPrevContent = pContent->GetPrevContent()) != NULL && + pPrevContent->IsVirgin() ) + pContent = pPrevContent; + } + + if ( !pContent->IsClickable() ) + return FALSE; + + ScBigRange aBigRange( pContent->GetBigRange() ); + const ScBaseCell* pCell = (bOldest ? pContent->GetOldCell() : + pContent->GetNewCell()); + if ( ScChangeActionContent::GetContentCellType( pCell ) == SC_CACCT_MATORG ) + { + SCCOL nC; + SCROW nR; + ((const ScFormulaCell*)pCell)->GetMatColsRows( nC, nR ); + aBigRange.aEnd.IncCol( nC-1 ); + aBigRange.aEnd.IncRow( nR-1 ); + } + + if ( !aBigRange.IsValid( pDoc ) ) + return FALSE; + + ScRange aRange( aBigRange.MakeRange() ); + if ( !pDoc->IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(), + aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) ) + return FALSE; + + if ( pContent->HasDependent() ) + { + BOOL bOk = TRUE; + Stack aRejectActions; + const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry(); + while ( pL ) + { + ScChangeAction* p = (ScChangeAction*) pL->GetAction(); + if ( p != pContent ) + { + if ( p->GetType() == SC_CAT_CONTENT ) + { + // we don't need no recursion here, do we? + bOk &= ((ScChangeActionContent*)p)->Select( pDoc, this, + bOldest, &aRejectActions ); + } + else + { + DBG_ERRORFILE( "ScChangeTrack::SelectContent: content dependent no content" ); + } + } + pL = pL->GetNext(); + } + + bOk &= pContent->Select( pDoc, this, bOldest, NULL ); + // now the matrix is inserted and new content values are ready + + ScChangeActionContent* pNew; + while ( ( pNew = (ScChangeActionContent*) aRejectActions.Pop() ) != NULL ) + { + ScAddress aPos( pNew->GetBigRange().aStart.MakeAddress() ); + pNew->SetNewValue( pDoc->GetCell( aPos ), pDoc ); + Append( pNew ); + } + return bOk; + } + else + return pContent->Select( pDoc, this, bOldest, NULL ); +} + + +void ScChangeTrack::AcceptAll() +{ + for ( ScChangeAction* p = GetFirst(); p; p = p->GetNext() ) + { + p->Accept(); + } +} + + +BOOL ScChangeTrack::Accept( ScChangeAction* pAct ) +{ + if ( !pAct->IsClickable() ) + return FALSE; + + if ( pAct->IsDeleteType() || pAct->GetType() == SC_CAT_CONTENT ) + { + ScChangeActionTable aActionTable; + GetDependents( pAct, aActionTable, FALSE, TRUE ); + for ( ScChangeAction* p = aActionTable.First(); p; p = aActionTable.Next() ) + { + p->Accept(); + } + } + pAct->Accept(); + return TRUE; +} + + +BOOL ScChangeTrack::RejectAll() +{ + BOOL bOk = TRUE; + for ( ScChangeAction* p = GetLast(); p && bOk; p = p->GetPrev() ) + { //! rueckwaerts, weil abhaengige hinten und RejectActions angehaengt + if ( p->IsInternalRejectable() ) + bOk = Reject( p ); + } + return bOk; +} + + +BOOL ScChangeTrack::Reject( ScChangeAction* pAct, bool bShared ) +{ + // #i100895# When collaboration changes are reversed, it must be possible + // to reject a deleted row above another deleted row. + if ( bShared && pAct->IsDeletedIn() ) + pAct->RemoveAllDeletedIn(); + + if ( !pAct->IsRejectable() ) + return FALSE; + + ScChangeActionTable* pTable = NULL; + if ( pAct->HasDependent() ) + { + pTable = new ScChangeActionTable; + GetDependents( pAct, *pTable, FALSE, TRUE ); + } + BOOL bRejected = Reject( pAct, pTable, FALSE ); + if ( pTable ) + delete pTable; + return bRejected; +} + + +BOOL ScChangeTrack::Reject( ScChangeAction* pAct, ScChangeActionTable* pTable, + BOOL bRecursion ) +{ + if ( !pAct->IsInternalRejectable() ) + return FALSE; + + BOOL bOk = TRUE; + BOOL bRejected = FALSE; + if ( pAct->IsInsertType() ) + { + if ( pAct->HasDependent() && !bRecursion ) + { + DBG_ASSERT( pTable, "ScChangeTrack::Reject: Insert ohne Table" ); + for ( ScChangeAction* p = pTable->Last(); p && bOk; p = pTable->Prev() ) + { + // keine Contents restoren, die eh geloescht werden wuerden + if ( p->GetType() == SC_CAT_CONTENT ) + p->SetRejected(); + else if ( p->IsDeleteType() ) + p->Accept(); // geloeschtes ins Nirvana + else + bOk = Reject( p, NULL, TRUE ); //! rekursiv + } + } + if ( bOk && (bRejected = pAct->Reject( pDoc )) != FALSE ) + { + // pRefDoc NULL := geloeschte Zellen nicht speichern + AppendDeleteRange( pAct->GetBigRange().MakeRange(), NULL, (short) 0, + pAct->GetActionNumber() ); + } + } + else if ( pAct->IsDeleteType() ) + { + DBG_ASSERT( !pTable, "ScChangeTrack::Reject: Delete mit Table" ); + ScBigRange aDelRange; + ULONG nRejectAction = pAct->GetActionNumber(); + BOOL bTabDel, bTabDelOk; + if ( pAct->GetType() == SC_CAT_DELETE_TABS ) + { + bTabDel = TRUE; + aDelRange = pAct->GetBigRange(); + bOk = bTabDelOk = pAct->Reject( pDoc ); + if ( bOk ) + { + pAct = pAct->GetPrev(); + bOk = ( pAct && pAct->GetType() == SC_CAT_DELETE_COLS ); + } + } + else + bTabDel = bTabDelOk = FALSE; + ScChangeActionDel* pDel = (ScChangeActionDel*) pAct; + if ( bOk ) + { + aDelRange = pDel->GetOverAllRange(); + bOk = aDelRange.IsValid( pDoc ); + } + BOOL bOneOk = FALSE; + if ( bOk ) + { + ScChangeActionType eActType = pAct->GetType(); + switch ( eActType ) + { + case SC_CAT_DELETE_COLS : + aDelRange.aStart.SetCol( aDelRange.aEnd.Col() ); + break; + case SC_CAT_DELETE_ROWS : + aDelRange.aStart.SetRow( aDelRange.aEnd.Row() ); + break; + case SC_CAT_DELETE_TABS : + aDelRange.aStart.SetTab( aDelRange.aEnd.Tab() ); + break; + default: + { + // added to avoid warnings + } + } + ScChangeAction* p = pAct; + BOOL bLoop = TRUE; + do + { + pDel = (ScChangeActionDel*) p; + bOk = pDel->Reject( pDoc ); + if ( bOk ) + { + if ( bOneOk ) + { + switch ( pDel->GetType() ) + { + case SC_CAT_DELETE_COLS : + aDelRange.aStart.IncCol( -1 ); + break; + case SC_CAT_DELETE_ROWS : + aDelRange.aStart.IncRow( -1 ); + break; + case SC_CAT_DELETE_TABS : + aDelRange.aStart.IncTab( -1 ); + break; + default: + { + // added to avoid warnings + } + } + } + else + bOneOk = TRUE; + } + if ( pDel->IsBaseDelete() ) + bLoop = FALSE; + else + p = p->GetPrev(); + } while ( bOk && bLoop && p && p->GetType() == eActType && + !((ScChangeActionDel*)p)->IsTopDelete() ); + } + bRejected = bOk; + if ( bOneOk || (bTabDel && bTabDelOk) ) + { + // Delete-Reject machte UpdateReference Undo + ScChangeActionIns* pReject = new ScChangeActionIns( + aDelRange.MakeRange() ); + pReject->SetRejectAction( nRejectAction ); + pReject->SetState( SC_CAS_ACCEPTED ); + Append( pReject ); + } + } + else if ( pAct->GetType() == SC_CAT_MOVE ) + { + if ( pAct->HasDependent() && !bRecursion ) + { + DBG_ASSERT( pTable, "ScChangeTrack::Reject: Move ohne Table" ); + for ( ScChangeAction* p = pTable->Last(); p && bOk; p = pTable->Prev() ) + { + bOk = Reject( p, NULL, TRUE ); //! rekursiv + } + } + if ( bOk && (bRejected = pAct->Reject( pDoc )) != FALSE ) + { + ScChangeActionMove* pReject = new ScChangeActionMove( + pAct->GetBigRange().MakeRange(), + ((ScChangeActionMove*)pAct)->GetFromRange().MakeRange(), this ); + pReject->SetRejectAction( pAct->GetActionNumber() ); + pReject->SetState( SC_CAS_ACCEPTED ); + Append( pReject ); + } + } + else if ( pAct->GetType() == SC_CAT_CONTENT ) + { + ScRange aRange; + ScChangeActionContent* pReject; + if ( bRecursion ) + pReject = NULL; + else + { + aRange = pAct->GetBigRange().aStart.MakeAddress(); + pReject = new ScChangeActionContent( aRange ); + pReject->SetOldValue( pDoc->GetCell( aRange.aStart ), pDoc, pDoc ); + } + if ( (bRejected = pAct->Reject( pDoc )) != FALSE && !bRecursion ) + { + pReject->SetNewValue( pDoc->GetCell( aRange.aStart ), pDoc ); + pReject->SetRejectAction( pAct->GetActionNumber() ); + pReject->SetState( SC_CAS_ACCEPTED ); + Append( pReject ); + } + else if ( pReject ) + delete pReject; + } + else + { + DBG_ERROR( "ScChangeTrack::Reject: say what?" ); + } + + return bRejected; +} + + +ULONG ScChangeTrack::AddLoadedGenerated(ScBaseCell* pNewCell, const ScBigRange& aBigRange, const String& sNewValue ) +{ + ScChangeActionContent* pAct = new ScChangeActionContent( --nGeneratedMin, pNewCell, aBigRange, pDoc, sNewValue ); + if ( pAct ) + { + if ( pFirstGeneratedDelContent ) + pFirstGeneratedDelContent->pPrev = pAct; + pAct->pNext = pFirstGeneratedDelContent; + pFirstGeneratedDelContent = pAct; + aGeneratedTable.Insert( pAct->GetActionNumber(), pAct ); + return pAct->GetActionNumber(); + } + return 0; +} + +void ScChangeTrack::AppendCloned( ScChangeAction* pAppend ) +{ + aTable.Insert( pAppend->GetActionNumber(), pAppend ); + if ( !pLast ) + pFirst = pLast = pAppend; + else + { + pLast->pNext = pAppend; + pAppend->pPrev = pLast; + pLast = pAppend; + } +} + +ScChangeTrack* ScChangeTrack::Clone( ScDocument* pDocument ) const +{ + if ( !pDocument ) + { + return NULL; + } + + ScChangeTrack* pClonedTrack = new ScChangeTrack( pDocument ); + pClonedTrack->SetTime100thSeconds( IsTime100thSeconds() ); + + // clone generated actions + ::std::stack< const ScChangeAction* > aGeneratedStack; + const ScChangeAction* pGenerated = GetFirstGenerated(); + while ( pGenerated ) + { + aGeneratedStack.push( pGenerated ); + pGenerated = pGenerated->GetNext(); + } + while ( !aGeneratedStack.empty() ) + { + pGenerated = aGeneratedStack.top(); + aGeneratedStack.pop(); + const ScChangeActionContent* pContent = dynamic_cast< const ScChangeActionContent* >( pGenerated ); + DBG_ASSERT( pContent, "ScChangeTrack::Clone: pContent is null!" ); + const ScBaseCell* pNewCell = pContent->GetNewCell(); + if ( pNewCell ) + { + ScBaseCell* pClonedNewCell = pNewCell->CloneWithoutNote( *pDocument ); + String aNewValue; + pContent->GetNewString( aNewValue ); + pClonedTrack->nGeneratedMin = pGenerated->GetActionNumber() + 1; + pClonedTrack->AddLoadedGenerated( pClonedNewCell, pGenerated->GetBigRange(), aNewValue ); + } + } + + // clone actions + const ScChangeAction* pAction = GetFirst(); + while ( pAction ) + { + ScChangeAction* pClonedAction = NULL; + + switch ( pAction->GetType() ) + { + case SC_CAT_INSERT_COLS: + case SC_CAT_INSERT_ROWS: + case SC_CAT_INSERT_TABS: + { + pClonedAction = new ScChangeActionIns( + pAction->GetActionNumber(), + pAction->GetState(), + pAction->GetRejectAction(), + pAction->GetBigRange(), + pAction->GetUser(), + pAction->GetDateTimeUTC(), + pAction->GetComment(), + pAction->GetType() ); + } + break; + case SC_CAT_DELETE_COLS: + case SC_CAT_DELETE_ROWS: + case SC_CAT_DELETE_TABS: + { + const ScChangeActionDel* pDelete = dynamic_cast< const ScChangeActionDel* >( pAction ); + DBG_ASSERT( pDelete, "ScChangeTrack::Clone: pDelete is null!" ); + + SCsCOLROW nD = 0; + ScChangeActionType eType = pAction->GetType(); + if ( eType == SC_CAT_DELETE_COLS ) + { + nD = static_cast< SCsCOLROW >( pDelete->GetDx() ); + } + else if ( eType == SC_CAT_DELETE_ROWS ) + { + nD = static_cast< SCsCOLROW >( pDelete->GetDy() ); + } + + pClonedAction = new ScChangeActionDel( + pAction->GetActionNumber(), + pAction->GetState(), + pAction->GetRejectAction(), + pAction->GetBigRange(), + pAction->GetUser(), + pAction->GetDateTimeUTC(), + pAction->GetComment(), + eType, + nD, + pClonedTrack ); + } + break; + case SC_CAT_MOVE: + { + const ScChangeActionMove* pMove = dynamic_cast< const ScChangeActionMove* >( pAction ); + DBG_ASSERT( pMove, "ScChangeTrack::Clone: pMove is null!" ); + + pClonedAction = new ScChangeActionMove( + pAction->GetActionNumber(), + pAction->GetState(), + pAction->GetRejectAction(), + pAction->GetBigRange(), + pAction->GetUser(), + pAction->GetDateTimeUTC(), + pAction->GetComment(), + pMove->GetFromRange(), + pClonedTrack ); + } + break; + case SC_CAT_CONTENT: + { + const ScChangeActionContent* pContent = dynamic_cast< const ScChangeActionContent* >( pAction ); + DBG_ASSERT( pContent, "ScChangeTrack::Clone: pContent is null!" ); + const ScBaseCell* pOldCell = pContent->GetOldCell(); + ScBaseCell* pClonedOldCell = pOldCell ? pOldCell->CloneWithoutNote( *pDocument ) : 0; + String aOldValue; + pContent->GetOldString( aOldValue ); + + ScChangeActionContent* pClonedContent = new ScChangeActionContent( + pAction->GetActionNumber(), + pAction->GetState(), + pAction->GetRejectAction(), + pAction->GetBigRange(), + pAction->GetUser(), + pAction->GetDateTimeUTC(), + pAction->GetComment(), + pClonedOldCell, + pDocument, + aOldValue ); + + const ScBaseCell* pNewCell = pContent->GetNewCell(); + if ( pNewCell ) + { + ScBaseCell* pClonedNewCell = pNewCell->CloneWithoutNote( *pDocument ); + pClonedContent->SetNewValue( pClonedNewCell, pDocument ); + } + + pClonedAction = pClonedContent; + } + break; + case SC_CAT_REJECT: + { + pClonedAction = new ScChangeActionReject( + pAction->GetActionNumber(), + pAction->GetState(), + pAction->GetRejectAction(), + pAction->GetBigRange(), + pAction->GetUser(), + pAction->GetDateTimeUTC(), + pAction->GetComment() ); + } + break; + default: + { + } + break; + } + + if ( pClonedAction ) + { + pClonedTrack->AppendCloned( pClonedAction ); + } + + pAction = pAction->GetNext(); + } + + if ( pClonedTrack->GetLast() ) + { + pClonedTrack->SetActionMax( pClonedTrack->GetLast()->GetActionNumber() ); + } + + // set dependencies for Deleted/DeletedIn + pAction = GetFirst(); + while ( pAction ) + { + if ( pAction->HasDeleted() ) + { + ::std::stack< ULONG > aStack; + const ScChangeActionLinkEntry* pL = pAction->GetFirstDeletedEntry(); + while ( pL ) + { + const ScChangeAction* pDeleted = pL->GetAction(); + if ( pDeleted ) + { + aStack.push( pDeleted->GetActionNumber() ); + } + pL = pL->GetNext(); + } + ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() ); + if ( pClonedAction ) + { + while ( !aStack.empty() ) + { + ScChangeAction* pClonedDeleted = pClonedTrack->GetActionOrGenerated( aStack.top() ); + aStack.pop(); + if ( pClonedDeleted ) + { + pClonedDeleted->SetDeletedIn( pClonedAction ); + } + } + } + } + pAction = pAction->GetNext(); + } + + // set dependencies for Dependent/Any + pAction = GetLast(); + while ( pAction ) + { + if ( pAction->HasDependent() ) + { + ::std::stack< ULONG > aStack; + const ScChangeActionLinkEntry* pL = pAction->GetFirstDependentEntry(); + while ( pL ) + { + const ScChangeAction* pDependent = pL->GetAction(); + if ( pDependent ) + { + aStack.push( pDependent->GetActionNumber() ); + } + pL = pL->GetNext(); + } + ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() ); + if ( pClonedAction ) + { + while ( !aStack.empty() ) + { + ScChangeAction* pClonedDependent = pClonedTrack->GetActionOrGenerated( aStack.top() ); + aStack.pop(); + if ( pClonedDependent ) + { + ScChangeActionLinkEntry* pLink = pClonedAction->AddDependent( pClonedDependent ); + pClonedDependent->AddLink( pClonedAction, pLink ); + } + } + } + } + pAction = pAction->GetPrev(); + } + + // masterlinks + ScChangeAction* pClonedAction = pClonedTrack->GetFirst(); + while ( pClonedAction ) + { + pClonedTrack->MasterLinks( pClonedAction ); + pClonedAction = pClonedAction->GetNext(); + } + + if ( IsProtected() ) + { + pClonedTrack->SetProtection( GetProtection() ); + } + + if ( pClonedTrack->GetLast() ) + { + pClonedTrack->SetLastSavedActionNumber( pClonedTrack->GetLast()->GetActionNumber() ); + } + + pDocument->SetChangeTrack( pClonedTrack ); + + return pClonedTrack; +} + +void ScChangeTrack::MergeActionState( ScChangeAction* pAct, const ScChangeAction* pOtherAct ) +{ + if ( pAct->IsVirgin() ) + { + if ( pOtherAct->IsAccepted() ) + { + pAct->Accept(); + if ( pOtherAct->IsRejecting() ) + { + pAct->SetRejectAction( pOtherAct->GetRejectAction() ); + } + } + else if ( pOtherAct->IsRejected() ) + { + pAct->SetRejected(); + } + } +} + +#if DEBUG_CHANGETRACK +String ScChangeTrack::ToString() const +{ + String aReturn; + + aReturn += String::CreateFromAscii( "============================================================\n" ); + + const ScChangeAction* pGenerated = GetFirstGenerated(); + while ( pGenerated ) + { + aReturn += pGenerated->ToString( pDoc ); + aReturn += '\n'; + pGenerated = pGenerated->GetNext(); + } + + aReturn += String::CreateFromAscii( "------------------------------------------------------------\n" ); + + const ScChangeAction* pAction = GetFirst(); + while ( pAction ) + { + aReturn += pAction->ToString( pDoc ); + aReturn += '\n'; + pAction = pAction->GetNext(); + } + aReturn += String::CreateFromAscii( "============================================================\n" ); + + return aReturn; +} +#endif // DEBUG_CHANGETRACK diff --git a/sc/source/core/tool/chgviset.cxx b/sc/source/core/tool/chgviset.cxx new file mode 100644 index 000000000000..ac587db2bd12 --- /dev/null +++ b/sc/source/core/tool/chgviset.cxx @@ -0,0 +1,178 @@ +/************************************************************************* + * + * 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/textsearch.hxx> + +#include "chgviset.hxx" +#include "rechead.hxx" +#include "chgtrack.hxx" + +// ----------------------------------------------------------------------- +ScChangeViewSettings::~ScChangeViewSettings() +{ + if(pCommentSearcher!=NULL) + delete pCommentSearcher; +} + +ScChangeViewSettings::ScChangeViewSettings( const ScChangeViewSettings& r ) +{ + SetTheComment(r.aComment); + + aFirstDateTime =r.aFirstDateTime; + aLastDateTime =r.aLastDateTime; + aAuthorToShow =r.aAuthorToShow; + aRangeList =r.aRangeList; + eDateMode =r.eDateMode; + bShowIt =r.bShowIt; + bIsDate =r.bIsDate; + bIsAuthor =r.bIsAuthor; + bIsComment =r.bIsComment; + bIsRange =r.bIsRange; + bEveryoneButMe =r.bEveryoneButMe; + bShowAccepted =r.bShowAccepted; + bShowRejected =r.bShowRejected; + mbIsActionRange = r.mbIsActionRange; + mnFirstAction = r.mnFirstAction; + mnLastAction = r.mnLastAction; + +} + +ScChangeViewSettings& ScChangeViewSettings::operator=( const ScChangeViewSettings& r ) +{ + SetTheComment(r.aComment); + + aFirstDateTime =r.aFirstDateTime; + aLastDateTime =r.aLastDateTime; + aAuthorToShow =r.aAuthorToShow; + aRangeList =r.aRangeList; + eDateMode =r.eDateMode; + bShowIt =r.bShowIt; + bIsDate =r.bIsDate; + bIsAuthor =r.bIsAuthor; + bIsComment =r.bIsComment; + bIsRange =r.bIsRange; + bEveryoneButMe =r.bEveryoneButMe; + bShowAccepted =r.bShowAccepted; + bShowRejected =r.bShowRejected; + mbIsActionRange = r.mbIsActionRange; + mnFirstAction = r.mnFirstAction; + mnLastAction = r.mnLastAction; + + return *this; +} + +BOOL ScChangeViewSettings::IsValidComment(const String* pCommentStr) const +{ + BOOL nTheFlag=TRUE; + + if(pCommentSearcher!=NULL) + { + xub_StrLen nStartPos = 0; + xub_StrLen nEndPos = pCommentStr->Len(); + + nTheFlag=sal::static_int_cast<BOOL>(pCommentSearcher->SearchFrwrd( *pCommentStr, &nStartPos, &nEndPos)); + } + return nTheFlag; +} + +void ScChangeViewSettings::SetTheComment(const String& rString) +{ + aComment=rString; + if(pCommentSearcher!=NULL) + { + delete pCommentSearcher; + pCommentSearcher=NULL; + } + + if(rString.Len()>0) + { + utl::SearchParam aSearchParam( rString, + utl::SearchParam::SRCH_REGEXP,FALSE,FALSE,FALSE ); + + pCommentSearcher = new utl::TextSearch( aSearchParam, *ScGlobal::pCharClass ); + } +} + +void ScChangeViewSettings::AdjustDateMode( const ScDocument& rDoc ) +{ + switch ( eDateMode ) + { // corresponds with ScViewUtil::IsActionShown + case SCDM_DATE_EQUAL : + case SCDM_DATE_NOTEQUAL : + aFirstDateTime.SetTime( 0 ); + aLastDateTime = aFirstDateTime; + aLastDateTime.SetTime( 23595999 ); + break; + case SCDM_DATE_SAVE: + { + const ScChangeAction* pLast = 0; + ScChangeTrack* pTrack = rDoc.GetChangeTrack(); + if ( pTrack ) + { + pLast = pTrack->GetLastSaved(); + if ( pLast ) + { + aFirstDateTime = pLast->GetDateTime(); +#if 0 +// This would be the proper handling. But since the SvxTPFilter dialog uses +// DateField/TimeField, and the filter dialog is used in ScAcceptChgDlg as the +// controlling instance, and the TimeFields are used there without seconds or +// 100ths, we'd display some extra entries between the floor of the minute and +// the start of the next minute. + // add one 100th second to point past last saved + aFirstDateTime += Time( 0, 0, 0, 1 ); +#else + // Set the next minute as the start time and assume that + // the document isn't saved, reloaded, edited and filter set + // all together during the gap between those two times. + aFirstDateTime += Time( 0, 1 ); + aFirstDateTime.SetSec(0); + aFirstDateTime.Set100Sec(0); +#endif + } + } + if ( !pLast ) + { + aFirstDateTime.SetDate( 18990101 ); + aFirstDateTime.SetTime( 0 ); + } + aLastDateTime = Date(); + aLastDateTime.SetYear( aLastDateTime.GetYear() + 100 ); + } + break; + default: + { + // added to avoid warnings + } + } +} + diff --git a/sc/source/core/tool/collect.cxx b/sc/source/core/tool/collect.cxx new file mode 100644 index 000000000000..c7aa72343fbb --- /dev/null +++ b/sc/source/core/tool/collect.cxx @@ -0,0 +1,522 @@ +/************************************************************************* + * + * 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 <string.h> +#include <tools/stream.hxx> +#include <unotools/transliterationwrapper.hxx> + +#include "rechead.hxx" +#include "collect.hxx" +#include "document.hxx" // fuer TypedStrData Konstruktor + +// ----------------------------------------------------------------------- + +ScDataObject::~ScDataObject() +{ +} + +//------------------------------------------------------------------------ +// Collection +//------------------------------------------------------------------------ + +void lcl_DeleteScDataObjects( ScDataObject** p, USHORT nCount ) +{ + if ( p ) + { + for (USHORT i = 0; i < nCount; i++) delete p[i]; + delete[] p; + p = NULL; + } +} + +ScCollection::ScCollection(USHORT nLim, USHORT nDel) : + nCount ( 0 ), + nLimit ( nLim ), + nDelta ( nDel ), + pItems ( NULL ) +{ + if (nDelta > MAXDELTA) + nDelta = MAXDELTA; + else if (nDelta == 0) + nDelta = 1; + if (nLimit > MAXCOLLECTIONSIZE) + nLimit = MAXCOLLECTIONSIZE; + else if (nLimit < nDelta) + nLimit = nDelta; + pItems = new ScDataObject*[nLimit]; +} + +ScCollection::ScCollection(const ScCollection& rCollection) + : ScDataObject(), + nCount ( 0 ), + nLimit ( 0 ), + nDelta ( 0 ), + pItems ( NULL ) +{ + *this = rCollection; +} + +//------------------------------------------------------------------------ + +ScCollection::~ScCollection() +{ + lcl_DeleteScDataObjects( pItems, nCount ); +} + +//------------------------------------------------------------------------ +USHORT ScCollection::GetCount() const { return nCount; } +void ScCollection::AtFree(USHORT nIndex) +{ + if ((pItems) && (nIndex < nCount)) + { + delete pItems[nIndex]; + --nCount; // before memmove + memmove ( &pItems[nIndex], &pItems[nIndex + 1], (nCount - nIndex) * sizeof(ScDataObject*)); + pItems[nCount] = NULL; + } +} + +//------------------------------------------------------------------------ + +void ScCollection::Free(ScDataObject* pScDataObject) +{ + AtFree(IndexOf(pScDataObject)); +} + +//------------------------------------------------------------------------ + +void ScCollection::FreeAll() +{ + lcl_DeleteScDataObjects( pItems, nCount ); + nCount = 0; + pItems = new ScDataObject*[nLimit]; +} + +//------------------------------------------------------------------------ + +BOOL ScCollection::AtInsert(USHORT nIndex, ScDataObject* pScDataObject) +{ + if ((nCount < MAXCOLLECTIONSIZE) && (nIndex <= nCount) && pItems) + { + if (nCount == nLimit) + { + ScDataObject** pNewItems = new ScDataObject*[nLimit + nDelta]; + if (!pNewItems) + return FALSE; + nLimit = sal::static_int_cast<USHORT>( nLimit + nDelta ); + memmove(pNewItems, pItems, nCount * sizeof(ScDataObject*)); + delete[] pItems; + pItems = pNewItems; + } + if (nCount > nIndex) + memmove(&pItems[nIndex + 1], &pItems[nIndex], (nCount - nIndex) * sizeof(ScDataObject*)); + pItems[nIndex] = pScDataObject; + nCount++; + return TRUE; + } + return FALSE; +} + +//------------------------------------------------------------------------ + +BOOL ScCollection::Insert(ScDataObject* pScDataObject) +{ + return AtInsert(nCount, pScDataObject); +} + +//------------------------------------------------------------------------ + +ScDataObject* ScCollection::At(USHORT nIndex) const +{ + if (nIndex < nCount) + return pItems[nIndex]; + else + return NULL; +} + +//------------------------------------------------------------------------ + +USHORT ScCollection::IndexOf(ScDataObject* pScDataObject) const +{ + USHORT nIndex = 0xffff; + for (USHORT i = 0; ((i < nCount) && (nIndex == 0xffff)); i++) + { + if (pItems[i] == pScDataObject) nIndex = i; + } + return nIndex; +} + +//------------------------------------------------------------------------ + +ScCollection& ScCollection::operator=( const ScCollection& r ) +{ + lcl_DeleteScDataObjects( pItems, nCount ); + + nCount = r.nCount; + nLimit = r.nLimit; + nDelta = r.nDelta; + pItems = new ScDataObject*[nLimit]; + for ( USHORT i=0; i<nCount; i++ ) + pItems[i] = r.pItems[i]->Clone(); + + return *this; +} + +//------------------------------------------------------------------------ + +ScDataObject* ScCollection::Clone() const +{ + return new ScCollection(*this); +} + +//------------------------------------------------------------------------ +// ScSortedCollection +//------------------------------------------------------------------------ + +ScSortedCollection::ScSortedCollection(USHORT nLim, USHORT nDel, BOOL bDup) : + ScCollection (nLim, nDel), + bDuplicates ( bDup) +{ +} + +//------------------------------------------------------------------------ + +USHORT ScSortedCollection::IndexOf(ScDataObject* pScDataObject) const +{ + USHORT nIndex; + if (Search(pScDataObject, nIndex)) + return nIndex; + else + return 0xffff; +} + +//------------------------------------------------------------------------ + +BOOL ScSortedCollection::Search(ScDataObject* pScDataObject, USHORT& rIndex) const +{ + rIndex = nCount; + BOOL bFound = FALSE; + short nLo = 0; + short nHi = nCount - 1; + short nIndex; + short nCompare; + while (nLo <= nHi) + { + nIndex = (nLo + nHi) / 2; + nCompare = Compare(pItems[nIndex], pScDataObject); + if (nCompare < 0) + nLo = nIndex + 1; + else + { + nHi = nIndex - 1; + if (nCompare == 0) + { + bFound = TRUE; + nLo = nIndex; + } + } + } + rIndex = nLo; + return bFound; +} + +//------------------------------------------------------------------------ + +BOOL ScSortedCollection::Insert(ScDataObject* pScDataObject) +{ + USHORT nIndex; + BOOL bFound = Search(pScDataObject, nIndex); + if (bFound) + { + if (bDuplicates) + return AtInsert(nIndex, pScDataObject); + else + return FALSE; + } + else + return AtInsert(nIndex, pScDataObject); +} + +//------------------------------------------------------------------------ + +BOOL ScSortedCollection::InsertPos(ScDataObject* pScDataObject, USHORT& nIndex) +{ + BOOL bFound = Search(pScDataObject, nIndex); + if (bFound) + { + if (bDuplicates) + return AtInsert(nIndex, pScDataObject); + else + return FALSE; + } + else + return AtInsert(nIndex, pScDataObject); +} + +//------------------------------------------------------------------------ + +BOOL ScSortedCollection::operator==(const ScSortedCollection& rCmp) const +{ + if ( nCount != rCmp.nCount ) + return FALSE; + for (USHORT i=0; i<nCount; i++) + if ( !IsEqual(pItems[i],rCmp.pItems[i]) ) + return FALSE; + return TRUE; +} + +//------------------------------------------------------------------------ + +// IsEqual - komplette Inhalte vergleichen + +BOOL ScSortedCollection::IsEqual(ScDataObject* pKey1, ScDataObject* pKey2) const +{ + return ( Compare(pKey1, pKey2) == 0 ); // Default: nur Index vergleichen +} + +//------------------------------------------------------------------------ + +ScDataObject* StrData::Clone() const +{ + return new StrData(*this); +} + +//------------------------------------------------------------------------ + +short ScStrCollection::Compare(ScDataObject* pKey1, ScDataObject* pKey2) const +{ + StringCompare eComp = ((StrData*)pKey1)->aStr.CompareTo(((StrData*)pKey2)->aStr); + if (eComp == COMPARE_EQUAL) + return 0; + else if (eComp == COMPARE_LESS) + return -1; + else + return 1; +} + +//------------------------------------------------------------------------ + +ScDataObject* ScStrCollection::Clone() const +{ + return new ScStrCollection(*this); +} + +//------------------------------------------------------------------------ +// TypedScStrCollection +//------------------------------------------------------------------------ + +//UNUSED2008-05 TypedStrData::TypedStrData( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, +//UNUSED2008-05 BOOL bAllStrings ) +//UNUSED2008-05 { +//UNUSED2008-05 if ( pDoc->HasValueData( nCol, nRow, nTab ) ) +//UNUSED2008-05 { +//UNUSED2008-05 pDoc->GetValue( nCol, nRow, nTab, nValue ); +//UNUSED2008-05 if (bAllStrings) +//UNUSED2008-05 pDoc->GetString( nCol, nRow, nTab, aStrValue ); +//UNUSED2008-05 nStrType = 0; +//UNUSED2008-05 } +//UNUSED2008-05 else +//UNUSED2008-05 { +//UNUSED2008-05 pDoc->GetString( nCol, nRow, nTab, aStrValue ); +//UNUSED2008-05 nValue = 0.0; +//UNUSED2008-05 nStrType = 1; //! Typ uebergeben ? +//UNUSED2008-05 } +//UNUSED2008-05 } + + +ScDataObject* TypedStrData::Clone() const +{ + return new TypedStrData(*this); +} + +TypedScStrCollection::TypedScStrCollection( USHORT nLim , USHORT nDel , BOOL bDup ) + : ScSortedCollection( nLim, nDel, bDup ) +{ + bCaseSensitive = FALSE; +} + +TypedScStrCollection::~TypedScStrCollection() +{} +ScDataObject* TypedScStrCollection::Clone() const +{ + return new TypedScStrCollection(*this); +} + +TypedStrData* TypedScStrCollection::operator[]( const USHORT nIndex) const +{ + return (TypedStrData*)At(nIndex); +} + +void TypedScStrCollection::SetCaseSensitive( BOOL bSet ) +{ + bCaseSensitive = bSet; +} + +short TypedScStrCollection::Compare( ScDataObject* pKey1, ScDataObject* pKey2 ) const +{ + short nResult = 0; + + if ( pKey1 && pKey2 ) + { + TypedStrData& rData1 = (TypedStrData&)*pKey1; + TypedStrData& rData2 = (TypedStrData&)*pKey2; + + if ( rData1.nStrType > rData2.nStrType ) + nResult = 1; + else if ( rData1.nStrType < rData2.nStrType ) + nResult = -1; + else if ( !rData1.nStrType /* && !rData2.nStrType */ ) + { + //-------------------- + // Zahlen vergleichen: + //-------------------- + if ( rData1.nValue == rData2.nValue ) + nResult = 0; + else if ( rData1.nValue < rData2.nValue ) + nResult = -1; + else + nResult = 1; + } + else /* if ( rData1.nStrType && rData2.nStrType ) */ + { + //--------------------- + // Strings vergleichen: + //--------------------- + if ( bCaseSensitive ) + nResult = (short) ScGlobal::GetCaseTransliteration()->compareString( + rData1.aStrValue, rData2.aStrValue ); + else + nResult = (short) ScGlobal::GetpTransliteration()->compareString( + rData1.aStrValue, rData2.aStrValue ); + } + } + + return nResult; +} + +BOOL TypedScStrCollection::FindText( const String& rStart, String& rResult, + USHORT& rPos, BOOL bBack ) const +{ + // Die Collection ist nach String-Vergleichen sortiert, darum muss hier + // alles durchsucht werden + + BOOL bFound = FALSE; + + String aOldResult; + if ( rPos != SCPOS_INVALID && rPos < nCount ) + { + TypedStrData* pData = (TypedStrData*) pItems[rPos]; + if (pData->nStrType) + aOldResult = pData->aStrValue; + } + + if ( bBack ) // rueckwaerts + { + USHORT nStartPos = nCount; + if ( rPos != SCPOS_INVALID ) + nStartPos = rPos; // weitersuchen... + + for ( USHORT i=nStartPos; i>0; ) + { + --i; + TypedStrData* pData = (TypedStrData*) pItems[i]; + if (pData->nStrType) + { + if ( ScGlobal::GetpTransliteration()->isMatch( rStart, pData->aStrValue ) ) + { + // If the collection is case sensitive, it may contain several entries + // that are equal when compared case-insensitive. They are skipped here. + if ( !bCaseSensitive || !aOldResult.Len() || + !ScGlobal::GetpTransliteration()->isEqual( + pData->aStrValue, aOldResult ) ) + { + rResult = pData->aStrValue; + rPos = i; + bFound = TRUE; + break; + } + } + } + } + } + else // vorwaerts + { + USHORT nStartPos = 0; + if ( rPos != SCPOS_INVALID ) + nStartPos = rPos + 1; // weitersuchen... + + for ( USHORT i=nStartPos; i<nCount; i++ ) + { + TypedStrData* pData = (TypedStrData*) pItems[i]; + if (pData->nStrType) + { + if ( ScGlobal::GetpTransliteration()->isMatch( rStart, pData->aStrValue ) ) + { + // If the collection is case sensitive, it may contain several entries + // that are equal when compared case-insensitive. They are skipped here. + if ( !bCaseSensitive || !aOldResult.Len() || + !ScGlobal::GetpTransliteration()->isEqual( + pData->aStrValue, aOldResult ) ) + { + rResult = pData->aStrValue; + rPos = i; + bFound = TRUE; + break; + } + } + } + } + } + + return bFound; +} + + // Gross-/Kleinschreibung anpassen + +BOOL TypedScStrCollection::GetExactMatch( String& rString ) const +{ + for (USHORT i=0; i<nCount; i++) + { + TypedStrData* pData = (TypedStrData*) pItems[i]; + if ( pData->nStrType && ScGlobal::GetpTransliteration()->isEqual( + pData->aStrValue, rString ) ) + { + rString = pData->aStrValue; // String anpassen + return TRUE; + } + } + + return FALSE; +} + + + diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx new file mode 100644 index 000000000000..828c9ae64c7d --- /dev/null +++ b/sc/source/core/tool/compiler.cxx @@ -0,0 +1,5484 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <sfx2/app.hxx> +#include <sfx2/objsh.hxx> +#include <basic/sbmeth.hxx> +#include <basic/sbstar.hxx> +#include <svl/zforlist.hxx> +#include <tools/rcid.h> +#include <tools/rc.hxx> +#include <tools/solar.h> +#include <unotools/charclass.hxx> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp> +#include <com/sun/star/sheet/FormulaLanguage.hpp> +#include <com/sun/star/sheet/FormulaMapGroup.hpp> +#include <comphelper/processfactory.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <tools/urlobj.hxx> +#include <rtl/math.hxx> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include "compiler.hxx" +#include "rangenam.hxx" +#include "dbcolect.hxx" +#include "document.hxx" +#include "callform.hxx" +#include "addincol.hxx" +#include "refupdat.hxx" +#include "scresid.hxx" +#include "sc.hrc" +#include "globstr.hrc" +#include "cell.hxx" +#include "dociter.hxx" +#include "docoptio.hxx" +#include <formula/errorcodes.hxx> +#include "parclass.hxx" +#include "autonamecache.hxx" +#include "externalrefmgr.hxx" +#include "rangeutl.hxx" +#include "convuno.hxx" +#include "tokenuno.hxx" +#include "formulaparserpool.hxx" + +using namespace formula; +using namespace ::com::sun::star; +using rtl::OUString; +using ::std::vector; + +#if OSL_DEBUG_LEVEL > 1 +// For some unknown reason the identical dbg_dump utilities in +// tools/source/string/debugprint.cxx tend to crash when called from within +// gdb. Having them here also comes handy as libtl*.so doesn't have to be +// replaced. +const char* dbg_sc_dump( const ByteString & rStr ) +{ + static ByteString aStr; + aStr = rStr; + aStr.Append(static_cast<char>(0)); + return aStr.GetBuffer(); +} +const char* dbg_sc_dump( const UniString & rStr ) +{ + return dbg_sc_dump(ByteString(rStr, RTL_TEXTENCODING_UTF8)); +} +const char* dbg_sc_dump( const sal_Unicode * pBuf ) +{ + return dbg_sc_dump( UniString( pBuf)); +} +const char* dbg_sc_dump( const sal_Unicode c ) +{ + return dbg_sc_dump( UniString( c)); +} +#endif + +CharClass* ScCompiler::pCharClassEnglish = NULL; +const ScCompiler::Convention* ScCompiler::pConventions[ ] = { NULL, NULL, NULL, NULL, NULL, NULL }; + +enum ScanState +{ + ssGetChar, + ssGetBool, + ssGetValue, + ssGetString, + ssSkipString, + ssGetIdent, + ssGetReference, + ssSkipReference, + ssStop +}; + +static const sal_Char* pInternal[ 1 ] = { "TTT" }; + +using namespace ::com::sun::star::i18n; + +///////////////////////////////////////////////////////////////////////// + + + +class ScCompilerRecursionGuard +{ +private: + short& rRecursion; +public: + ScCompilerRecursionGuard( short& rRec ) + : rRecursion( rRec ) { ++rRecursion; } + ~ScCompilerRecursionGuard() { --rRecursion; } +}; + + +void ScCompiler::fillFromAddInMap( NonConstOpCodeMapPtr xMap,FormulaGrammar::Grammar _eGrammar ) const +{ + size_t nSymbolOffset; + switch( _eGrammar ) + { + case FormulaGrammar::GRAM_PODF: + nSymbolOffset = offsetof( AddInMap, pUpper); + break; + default: + case FormulaGrammar::GRAM_ODFF: + nSymbolOffset = offsetof( AddInMap, pODFF); + break; + case FormulaGrammar::GRAM_ENGLISH: + nSymbolOffset = offsetof( AddInMap, pEnglish); + break; + } + const AddInMap* pMap = GetAddInMap(); + const AddInMap* const pStop = pMap + GetAddInMapCount(); + for ( ; pMap < pStop; ++pMap) + { + char const * const * ppSymbol = + reinterpret_cast< char const * const * >( + reinterpret_cast< char const * >(pMap) + nSymbolOffset); + xMap->putExternal( String::CreateFromAscii( *ppSymbol), + String::CreateFromAscii( pMap->pOriginal)); + } +} + +void ScCompiler::fillFromAddInCollectionUpperName( NonConstOpCodeMapPtr xMap ) const +{ + ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection(); + long nCount = pColl->GetFuncCount(); + for (long i=0; i < nCount; ++i) + { + const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i); + if (pFuncData) + xMap->putExternalSoftly( pFuncData->GetUpperName(), + pFuncData->GetOriginalName()); + } +} + +void ScCompiler::fillFromAddInCollectionEnglishName( NonConstOpCodeMapPtr xMap ) const +{ + ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection(); + long nCount = pColl->GetFuncCount(); + for (long i=0; i < nCount; ++i) + { + const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i); + if (pFuncData) + { + String aName; + if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName)) + xMap->putExternalSoftly( aName, pFuncData->GetOriginalName()); + else + xMap->putExternalSoftly( pFuncData->GetUpperName(), + pFuncData->GetOriginalName()); + } + } +} + + +#ifdef erGENERATEMAPPING +// Run in en-US UI by calling from within gdb, edit pODFF entries afterwards. +void dbg_call_generateMappingODFF() +{ + // static ScCompiler members + fprintf( stdout, "%s", "static struct AddInMap\n{\n const char* pODFF;\n const char* pEnglish;\n bool bMapDupToInternal;\n const char* pOriginal;\n const char* pUpper;\n} maAddInMap[];\n"); + fprintf( stdout, "%s", "static const AddInMap* GetAddInMap();\n"); + fprintf( stdout, "%s", "static size_t GetAddInMapCount();\n"); + fprintf( stdout, "addinfuncdata___:%s", "ScCompiler::AddInMap ScCompiler::maAddInMap[] =\n{\n"); + ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection(); + long nCount = pColl->GetFuncCount(); + for (long i=0; i < nCount; ++i) + { + const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i); + if (pFuncData) + { +#define out(rStr) (ByteString( rStr, RTL_TEXTENCODING_UTF8).GetBuffer()) + String aL = pFuncData->GetUpperLocal(); + String aP = pFuncData->GetOriginalName(); + String aU = pFuncData->GetUpperName(); + fprintf( stdout, "addinfuncdata%3ld: { \"%s\", \"%s\", false, \"%s\", \"%s\" },\n", + i, out(aL), out(aL), out(aP), out(aU)); +#undef out + } + } + fprintf( stdout, "addinfuncdata___:%s", "};\n"); + fprintf( stdout, "%s", "\n// static\nconst ScCompiler::AddInMap* ScCompiler::GetAddInMap()\n{\n return maAddInMap;\n}\n"); + fprintf( stdout, "%s", "\n// static\nsize_t ScCompiler::GetAddInMapCount()\n{\n return sizeof(maAddInMap)/sizeof(maAddInMap[0]);\n}\n"); + fflush( stdout); +} +#endif // erGENERATEMAPPING + +#ifdef erGENERATEMAPPINGDIFF +// Run in en-US UI by calling from within gdb. +void dbg_call_generateMappingDiff() +{ + using namespace ::com::sun::star::sheet; + ScCompiler::OpCodeMapPtr xPODF = ScCompiler::GetOpCodeMap( + FormulaLanguage::ODF_11); + ScCompiler::OpCodeMapPtr xODFF = ScCompiler::GetOpCodeMap( + FormulaLanguage::ODFF); + ScCompiler::OpCodeMapPtr xENUS = ScCompiler::GetOpCodeMap( + FormulaLanguage::ENGLISH); + USHORT nPODF = xPODF->getSymbolCount(); + USHORT nODFF = xODFF->getSymbolCount(); + USHORT nENUS = xENUS->getSymbolCount(); + printf( "%s\n", "This is a semicolon separated file, you may import it as such to Calc."); + printf( "%s\n", "Spreadsheet functions name differences between PODF (ODF < 1.2) and ODFF (ODF >= 1.2), plus English UI names."); + printf( "\nInternal OpCodes; PODF: %d; ODFF: %d; ENUS: %d\n", + (int)nPODF, (int)nODFF, (int)nENUS); + USHORT nMax = ::std::max( ::std::max( nPODF, nODFF), nENUS); +#define out(rStr) (ByteString( rStr, RTL_TEXTENCODING_UTF8).GetBuffer()) + for (USHORT i=0; i < nMax; ++i) + { + const String& rPODF = xPODF->getSymbol(static_cast<OpCode>(i)); + const String& rODFF = xODFF->getSymbol(static_cast<OpCode>(i)); + const String& rENUS = xENUS->getSymbol(static_cast<OpCode>(i)); + if (rPODF != rODFF) + printf( "%d;%s;%s;%s\n", (int)i, out(rPODF), out(rODFF), out(rENUS)); + } + // Actually they should all differ, so we could simply list them all, but + // this is correct and we would find odd things, if any. + const ExternalHashMap* pPODF = xPODF->getReverseExternalHashMap(); + const ExternalHashMap* pODFF = xODFF->getReverseExternalHashMap(); + const ExternalHashMap* pENUS = xENUS->getReverseExternalHashMap(); + printf( "\n%s\n", "Add-In mapping"); + for (ExternalHashMap::const_iterator it = pPODF->begin(); it != pPODF->end(); ++it) + { + ExternalHashMap::const_iterator iLookODFF = pODFF->find( (*it).first); + ExternalHashMap::const_iterator iLookENUS = pENUS->find( (*it).first); + String aNative( iLookENUS == pENUS->end() ? + String::CreateFromAscii( "ENGLISH_SYMBOL_NOT_FOUND") : + (*iLookENUS).second); + if (iLookODFF == pODFF->end()) + printf( "NOT FOUND;%s;;%s\n", out((*it).first), out(aNative)); + else if((*it).second == (*iLookODFF).second) // upper equal + printf( "EQUAL;%s;%s;%s\n", out((*it).first), out((*iLookODFF).second), out(aNative)); + else + printf( ";%s;%s;%s\n", out((*it).first), out((*iLookODFF).second), out(aNative)); + } +#undef out + fflush( stdout); +} +#endif // erGENERATEMAPPINGDIFF + +// static +void ScCompiler::DeInit() +{ + if (pCharClassEnglish) + { + delete pCharClassEnglish; + pCharClassEnglish = NULL; + } +} + +bool ScCompiler::IsEnglishSymbol( const String& rName ) +{ + // function names are always case-insensitive + String aUpper( ScGlobal::pCharClass->upper( rName ) ); + + // 1. built-in function name + OpCode eOp = ScCompiler::GetEnglishOpCode( aUpper ); + if ( eOp != ocNone ) + { + return true; + } + // 2. old add in functions + USHORT nIndex; + if ( ScGlobal::GetFuncCollection()->SearchFunc( aUpper, nIndex ) ) + { + return true; + } + + // 3. new (uno) add in functions + String aIntName(ScGlobal::GetAddInCollection()->FindFunction( aUpper, FALSE )); + if (aIntName.Len()) + { + return true; + } + return false; // no valid function name +} + +// static +void ScCompiler::InitCharClassEnglish() +{ + ::com::sun::star::lang::Locale aLocale( + OUString( RTL_CONSTASCII_USTRINGPARAM( "en")), + OUString( RTL_CONSTASCII_USTRINGPARAM( "US")), + OUString()); + pCharClassEnglish = new CharClass( + ::comphelper::getProcessServiceFactory(), aLocale); +} + + +void ScCompiler::SetGrammar( const FormulaGrammar::Grammar eGrammar ) +{ + DBG_ASSERT( eGrammar != FormulaGrammar::GRAM_UNSPECIFIED, "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED"); + if (eGrammar == GetGrammar()) + return; // nothing to be done + + if( eGrammar == FormulaGrammar::GRAM_EXTERNAL ) + { + meGrammar = eGrammar; + mxSymbols = GetOpCodeMap( ::com::sun::star::sheet::FormulaLanguage::NATIVE); + } + else + { + FormulaGrammar::Grammar eMyGrammar = eGrammar; + const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( eMyGrammar); + OpCodeMapPtr xMap = GetOpCodeMap( nFormulaLanguage); + DBG_ASSERT( xMap, "ScCompiler::SetGrammar: unknown formula language"); + if (!xMap) + { + xMap = GetOpCodeMap( ::com::sun::star::sheet::FormulaLanguage::NATIVE); + eMyGrammar = xMap->getGrammar(); + } + + // Save old grammar for call to SetGrammarAndRefConvention(). + FormulaGrammar::Grammar eOldGrammar = GetGrammar(); + // This also sets the grammar associated with the map! + SetFormulaLanguage( xMap); + + // Override if necessary. + if (eMyGrammar != GetGrammar()) + SetGrammarAndRefConvention( eMyGrammar, eOldGrammar); + } +} + +void ScCompiler::SetEncodeUrlMode( EncodeUrlMode eMode ) +{ + meEncodeUrlMode = eMode; +} + +ScCompiler::EncodeUrlMode ScCompiler::GetEncodeUrlMode() const +{ + return meEncodeUrlMode; +} + +void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr & xMap ) +{ + if (xMap.get()) + { + mxSymbols = xMap; + if (mxSymbols->isEnglish()) + { + if (!pCharClassEnglish) + InitCharClassEnglish(); + pCharClass = pCharClassEnglish; + } + else + pCharClass = ScGlobal::pCharClass; + SetGrammarAndRefConvention( mxSymbols->getGrammar(), GetGrammar()); + } +} + + +void ScCompiler::SetGrammarAndRefConvention( + const FormulaGrammar::Grammar eNewGrammar, const FormulaGrammar::Grammar eOldGrammar ) +{ + meGrammar = eNewGrammar; //! SetRefConvention needs the new grammar set! + FormulaGrammar::AddressConvention eConv = FormulaGrammar::extractRefConvention( meGrammar); + if (eConv == FormulaGrammar::CONV_UNSPECIFIED && eOldGrammar == FormulaGrammar::GRAM_UNSPECIFIED) + { + if (pDoc) + SetRefConvention( pDoc->GetAddressConvention()); + else + SetRefConvention( pConvOOO_A1); + } + else + SetRefConvention( eConv ); +} + +String ScCompiler::FindAddInFunction( const String& rUpperName, BOOL bLocalFirst ) const +{ + return ScGlobal::GetAddInCollection()->FindFunction(rUpperName, bLocalFirst); // bLocalFirst=FALSE for english +} + + +#ifdef erDEBUG +void dbg_call_testcreatemapping() +{ + using namespace ::com::sun::star::sheet; + ScCompiler::OpCodeMapPtr xMap = ScCompiler::GetOpCodeMap( FormulaLanguage::ODFF); + xMap->createSequenceOfAvailableMappings( FormulaMapGroup::FUNCTIONS); +} +#endif + +//----------------------------------------------------------------------------- + +ScCompiler::Convention::~Convention() +{ + delete [] mpCharTable; + mpCharTable = NULL; +} + +ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv ) + : + meConv( eConv ) +{ + int i; + ULONG *t= new ULONG [128]; + + ScCompiler::pConventions[ meConv ] = this; + mpCharTable = t; + + for (i = 0; i < 128; i++) + t[i] = SC_COMPILER_C_ILLEGAL; + +/* */ t[32] = SC_COMPILER_C_CHAR_DONTCARE | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; +/* ! */ t[33] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; + if (FormulaGrammar::CONV_ODF == meConv) +/* ! */ t[33] |= SC_COMPILER_C_ODF_LABEL_OP; +/* " */ t[34] = SC_COMPILER_C_CHAR_STRING | SC_COMPILER_C_STRING_SEP; +/* # */ t[35] = SC_COMPILER_C_WORD_SEP; +/* $ */ t[36] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT; + if (FormulaGrammar::CONV_ODF == meConv) +/* $ */ t[36] |= SC_COMPILER_C_ODF_NAME_MARKER; +/* % */ t[37] = SC_COMPILER_C_VALUE; +/* & */ t[38] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; +/* ' */ t[39] = SC_COMPILER_C_NAME_SEP; +/* ( */ t[40] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; +/* ) */ t[41] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; +/* * */ t[42] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; +/* + */ t[43] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_SIGN; +/* , */ t[44] = SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_VALUE; +/* - */ t[45] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_SIGN; +/* . */ t[46] = SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_VALUE | SC_COMPILER_C_IDENT | SC_COMPILER_C_NAME; +/* / */ t[47] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; + + for (i = 48; i < 58; i++) +/* 0-9 */ t[i] = SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_WORD | SC_COMPILER_C_VALUE | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_VALUE | SC_COMPILER_C_IDENT | SC_COMPILER_C_NAME; + +/* : */ t[58] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD; +/* ; */ t[59] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; +/* < */ t[60] = SC_COMPILER_C_CHAR_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; +/* = */ t[61] = SC_COMPILER_C_CHAR | SC_COMPILER_C_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; +/* > */ t[62] = SC_COMPILER_C_CHAR_BOOL | SC_COMPILER_C_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; +/* ? */ t[63] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_NAME; +/* @ */ // FREE + + for (i = 65; i < 91; i++) +/* A-Z */ t[i] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT | SC_COMPILER_C_CHAR_NAME | SC_COMPILER_C_NAME; + + if (FormulaGrammar::CONV_ODF == meConv) + { +/* [ */ t[91] = SC_COMPILER_C_ODF_LBRACKET; +/* \ */ // FREE +/* ] */ t[93] = SC_COMPILER_C_ODF_RBRACKET; + } + else + { +/* [ */ // FREE +/* \ */ // FREE +/* ] */ // FREE + } +/* ^ */ t[94] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; +/* _ */ t[95] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT | SC_COMPILER_C_CHAR_NAME | SC_COMPILER_C_NAME; +/* ` */ // FREE + + for (i = 97; i < 123; i++) +/* a-z */ t[i] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT | SC_COMPILER_C_CHAR_NAME | SC_COMPILER_C_NAME; + +/* { */ t[123] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array open +/* | */ t[124] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array row sep (Should be OOo specific) +/* } */ t[125] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array close +/* ~ */ t[126] = SC_COMPILER_C_CHAR; // OOo specific +/* 127 */ // FREE + + if( FormulaGrammar::CONV_XL_A1 == meConv || FormulaGrammar::CONV_XL_R1C1 == meConv || FormulaGrammar::CONV_XL_OOX == meConv ) + { +/* */ t[32] |= SC_COMPILER_C_WORD; +/* ! */ t[33] |= SC_COMPILER_C_IDENT | SC_COMPILER_C_WORD; +/* " */ t[34] |= SC_COMPILER_C_WORD; +/* # */ t[35] &= (~SC_COMPILER_C_WORD_SEP); +/* # */ t[35] |= SC_COMPILER_C_WORD; +/* % */ t[37] |= SC_COMPILER_C_WORD; +/* ' */ t[39] |= SC_COMPILER_C_WORD; + +/* % */ t[37] |= SC_COMPILER_C_WORD; +/* & */ t[38] |= SC_COMPILER_C_WORD; +/* ' */ t[39] |= SC_COMPILER_C_WORD; +/* ( */ t[40] |= SC_COMPILER_C_WORD; +/* ) */ t[41] |= SC_COMPILER_C_WORD; +/* * */ t[42] |= SC_COMPILER_C_WORD; +/* + */ t[43] |= SC_COMPILER_C_WORD; +#if 0 /* this really needs to be locale specific. */ +/* , */ t[44] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; +#else +/* , */ t[44] |= SC_COMPILER_C_WORD; +#endif +/* - */ t[45] |= SC_COMPILER_C_WORD; + +/* ; */ t[59] |= SC_COMPILER_C_WORD; +/* < */ t[60] |= SC_COMPILER_C_WORD; +/* = */ t[61] |= SC_COMPILER_C_WORD; +/* > */ t[62] |= SC_COMPILER_C_WORD; +/* ? */ // question really is not permitted in sheet name +/* @ */ t[64] |= SC_COMPILER_C_WORD; +/* [ */ t[91] |= SC_COMPILER_C_WORD; +/* ] */ t[93] |= SC_COMPILER_C_WORD; +/* { */ t[123]|= SC_COMPILER_C_WORD; +/* | */ t[124]|= SC_COMPILER_C_WORD; +/* } */ t[125]|= SC_COMPILER_C_WORD; +/* ~ */ t[126]|= SC_COMPILER_C_WORD; + + if( FormulaGrammar::CONV_XL_R1C1 == meConv ) + { +/* - */ t[45] |= SC_COMPILER_C_IDENT; +/* [ */ t[91] |= SC_COMPILER_C_IDENT; +/* ] */ t[93] |= SC_COMPILER_C_IDENT; + } + if( FormulaGrammar::CONV_XL_OOX == meConv ) + { +/* [ */ t[91] |= SC_COMPILER_C_CHAR_IDENT; +/* ] */ t[93] |= SC_COMPILER_C_IDENT; + } + } +} + +//----------------------------------------------------------------------------- + +static bool lcl_isValidQuotedText( const String& rFormula, xub_StrLen nSrcPos, ParseResult& rRes ) +{ + // Tokens that start at ' can have anything in them until a final ' + // but '' marks an escaped ' + // We've earlier guaranteed that a string containing '' will be + // surrounded by ' + if (rFormula.GetChar(nSrcPos) == '\'') + { + xub_StrLen nPos = nSrcPos+1; + while (nPos < rFormula.Len()) + { + if (rFormula.GetChar(nPos) == '\'') + { + if ( (nPos+1 == rFormula.Len()) || (rFormula.GetChar(nPos+1) != '\'') ) + { + rRes.TokenType = KParseType::SINGLE_QUOTE_NAME; + rRes.EndPos = nPos+1; + return true; + } + ++nPos; + } + ++nPos; + } + } + + return false; +} + +static bool lcl_parseExternalName( + const String& rSymbol, + String& rFile, + String& rName, + const sal_Unicode cSep, + const ScDocument* pDoc = NULL, + const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks = NULL ) +{ + /* TODO: future versions will have to support sheet-local names too, thus + * return a possible sheet name as well. */ + const sal_Unicode* const pStart = rSymbol.GetBuffer(); + const sal_Unicode* p = pStart; + xub_StrLen nLen = rSymbol.Len(); + sal_Unicode cPrev = 0; + String aTmpFile, aTmpName; + xub_StrLen i = 0; + bool bInName = false; + if (cSep == '!') + { + // For XL use existing parser that resolves bracketed and quoted and + // indexed external document names. + ScRange aRange; + String aStartTabName, aEndTabName; + USHORT nFlags = 0; + p = aRange.Parse_XL_Header( p, pDoc, aTmpFile, aStartTabName, + aEndTabName, nFlags, true, pExternalLinks ); + if (!p || p == pStart) + return false; + i = xub_StrLen(p - pStart); + cPrev = *(p-1); + } + for ( ; i < nLen; ++i, ++p) + { + sal_Unicode c = *p; + if (i == 0) + { + if (c == '.' || c == cSep) + return false; + + if (c == '\'') + { + // Move to the next chart and loop until the second single + // quote. + cPrev = c; + ++i; ++p; + for (xub_StrLen j = i; j < nLen; ++j, ++p) + { + c = *p; + if (c == '\'') + { + if (j == i) + { + // empty quote e.g. (=''!Name) + return false; + } + + if (cPrev == '\'') + { + // two consecutive quotes equals a single + // quote in the file name. + aTmpFile.Append(c); + cPrev = 'a'; + } + else + cPrev = c; + + continue; + } + + if (cPrev == '\'' && j != i) + { + // this is not a quote but the previous one + // is. This ends the parsing of the quoted + // segment. + + i = j; + bInName = true; + break; + } + aTmpFile.Append(c); + cPrev = c; + } + + if (!bInName) + { + // premature ending of the quoted segment. + return false; + } + + if (c != cSep) + { + // only the separator is allowed after the closing quote. + return false; + } + + cPrev = c; + continue; + } + } + + if (bInName) + { + if (c == cSep) + { + // A second separator ? Not a valid external name. + return false; + } + aTmpName.Append(c); + } + else + { + if (c == cSep) + { + bInName = true; + } + else + { + do + { + if (CharClass::isAsciiAlphaNumeric(c)) + // allowed. + break; + + if (c > 128) + // non-ASCII character is allowed. + break; + + bool bValid = false; + switch (c) + { + case '_': + case '-': + case '.': + // these special characters are allowed. + bValid = true; + break; + } + if (bValid) + break; + + return false; + } + while (false); + aTmpFile.Append(c); + } + } + cPrev = c; + } + + if (!bInName) + { + // No name found - most likely the symbol has no '!'s. + return false; + } + + rFile = aTmpFile; + rName = aTmpName; + return true; +} + +static String lcl_makeExternalNameStr( const String& rFile, const String& rName, + const sal_Unicode cSep, bool bODF ) +{ + String aFile( rFile), aName( rName), aEscQuote( RTL_CONSTASCII_USTRINGPARAM("''")); + aFile.SearchAndReplaceAllAscii( "'", aEscQuote); + if (bODF) + aName.SearchAndReplaceAllAscii( "'", aEscQuote); + rtl::OUStringBuffer aBuf( aFile.Len() + aName.Len() + 9); + if (bODF) + aBuf.append( sal_Unicode( '[')); + aBuf.append( sal_Unicode( '\'')); + aBuf.append( aFile); + aBuf.append( sal_Unicode( '\'')); + aBuf.append( cSep); + if (bODF) + aBuf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "$$'")); + aBuf.append( aName); + if (bODF) + aBuf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "']")); + return String( aBuf.makeStringAndClear()); +} + +static bool lcl_getLastTabName( String& rTabName2, const String& rTabName1, + const vector<String>& rTabNames, const ScComplexRefData& rRef ) +{ + SCsTAB nTabSpan = rRef.Ref2.nTab - rRef.Ref1.nTab; + if (nTabSpan > 0) + { + size_t nCount = rTabNames.size(); + vector<String>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end(); + vector<String>::const_iterator itr = ::std::find(itrBeg, itrEnd, rTabName1); + if (itr == rTabNames.end()) + { + rTabName2 = ScGlobal::GetRscString(STR_NO_REF_TABLE); + return false; + } + + size_t nDist = ::std::distance(itrBeg, itr); + if (nDist + static_cast<size_t>(nTabSpan) >= nCount) + { + rTabName2 = ScGlobal::GetRscString(STR_NO_REF_TABLE); + return false; + } + + rTabName2 = rTabNames[nDist+nTabSpan]; + } + else + rTabName2 = rTabName1; + + return true; +} + +struct Convention_A1 : public ScCompiler::Convention +{ + Convention_A1( FormulaGrammar::AddressConvention eConv ) : ScCompiler::Convention( eConv ) { } + static void MakeColStr( rtl::OUStringBuffer& rBuffer, SCCOL nCol ); + static void MakeRowStr( rtl::OUStringBuffer& rBuffer, SCROW nRow ); + + ParseResult parseAnyToken( const String& rFormula, + xub_StrLen nSrcPos, + const CharClass* pCharClass) const + { + ParseResult aRet; + if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) ) + return aRet; + + static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | + KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR; + static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT; + // '?' allowed in range names because of Xcl :-/ + static const String aAddAllowed(String::CreateFromAscii("?#")); + return pCharClass->parseAnyToken( rFormula, + nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed ); + } +}; + +void Convention_A1::MakeColStr( rtl::OUStringBuffer& rBuffer, SCCOL nCol ) +{ + if ( !ValidCol( nCol) ) + rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + else + ::ScColToAlpha( rBuffer, nCol); +} + +void Convention_A1::MakeRowStr( rtl::OUStringBuffer& rBuffer, SCROW nRow ) +{ + if ( !ValidRow(nRow) ) + rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + else + rBuffer.append(sal_Int32(nRow + 1)); +} + +//----------------------------------------------------------------------------- + +struct ConventionOOO_A1 : public Convention_A1 +{ + ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO) { } + ConventionOOO_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1 (eConv) { } + static String MakeTabStr( const ScCompiler& rComp, SCTAB nTab, String& aDoc ) + { + String aString; + if (!rComp.GetDoc()->GetName( nTab, aString )) + aString = ScGlobal::GetRscString(STR_NO_REF_TABLE); + else + { + if ( aString.GetChar(0) == '\'' ) + { // "'Doc'#Tab" + xub_StrLen nPos = ScGlobal::FindUnquoted( aString, SC_COMPILER_FILE_TAB_SEP); + if (nPos != STRING_NOTFOUND && nPos > 0 && aString.GetChar(nPos-1) == '\'') + { + aDoc = aString.Copy( 0, nPos + 1 ); + aString.Erase( 0, nPos + 1 ); + aDoc = INetURLObject::decode( aDoc, INET_HEX_ESCAPE, + INetURLObject::DECODE_UNAMBIGUOUS ); + } + else + aDoc.Erase(); + } + else + aDoc.Erase(); + ScCompiler::CheckTabQuotes( aString, FormulaGrammar::CONV_OOO ); + } + aString += '.'; + return aString; + } + + void MakeRefStrImpl( rtl::OUStringBuffer& rBuffer, + const ScCompiler& rComp, + const ScComplexRefData& rRef, + bool bSingleRef, + bool bODF ) const + { + if (bODF) + rBuffer.append(sal_Unicode('[')); + ScComplexRefData aRef( rRef ); + // In case absolute/relative positions weren't separately available: + // transform relative to absolute! + // AdjustReference( aRef.Ref1 ); + // if( !bSingleRef ) + // AdjustReference( aRef.Ref2 ); + aRef.Ref1.CalcAbsIfRel( rComp.GetPos() ); + if( !bSingleRef ) + aRef.Ref2.CalcAbsIfRel( rComp.GetPos() ); + if( aRef.Ref1.IsFlag3D() ) + { + if (aRef.Ref1.IsTabDeleted()) + { + if (!aRef.Ref1.IsTabRel()) + rBuffer.append(sal_Unicode('$')); + rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + rBuffer.append(sal_Unicode('.')); + } + else + { + String aDoc; + String aRefStr( MakeTabStr( rComp, aRef.Ref1.nTab, aDoc ) ); + rBuffer.append(aDoc); + if (!aRef.Ref1.IsTabRel()) rBuffer.append(sal_Unicode('$')); + rBuffer.append(aRefStr); + } + } + else if (bODF) + rBuffer.append(sal_Unicode('.')); + if (!aRef.Ref1.IsColRel()) + rBuffer.append(sal_Unicode('$')); + if ( aRef.Ref1.IsColDeleted() ) + rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + else + MakeColStr(rBuffer, aRef.Ref1.nCol ); + if (!aRef.Ref1.IsRowRel()) + rBuffer.append(sal_Unicode('$')); + if ( aRef.Ref1.IsRowDeleted() ) + rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + else + MakeRowStr( rBuffer, aRef.Ref1.nRow ); + if (!bSingleRef) + { + rBuffer.append(sal_Unicode(':')); + if (aRef.Ref2.IsFlag3D() || aRef.Ref2.nTab != aRef.Ref1.nTab) + { + if (aRef.Ref2.IsTabDeleted()) + { + if (!aRef.Ref2.IsTabRel()) + rBuffer.append(sal_Unicode('$')); + rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + rBuffer.append(sal_Unicode('.')); + } + else + { + String aDoc; + String aRefStr( MakeTabStr( rComp, aRef.Ref2.nTab, aDoc ) ); + rBuffer.append(aDoc); + if (!aRef.Ref2.IsTabRel()) rBuffer.append(sal_Unicode('$')); + rBuffer.append(aRefStr); + } + } + else if (bODF) + rBuffer.append(sal_Unicode('.')); + if (!aRef.Ref2.IsColRel()) + rBuffer.append(sal_Unicode('$')); + if ( aRef.Ref2.IsColDeleted() ) + rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + else + MakeColStr( rBuffer, aRef.Ref2.nCol ); + if (!aRef.Ref2.IsRowRel()) + rBuffer.append(sal_Unicode('$')); + if ( aRef.Ref2.IsRowDeleted() ) + rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + else + MakeRowStr( rBuffer, aRef.Ref2.nRow ); + } + if (bODF) + rBuffer.append(sal_Unicode(']')); + } + + void MakeRefStr( rtl::OUStringBuffer& rBuffer, + const ScCompiler& rComp, + const ScComplexRefData& rRef, + BOOL bSingleRef ) const + { + MakeRefStrImpl( rBuffer, rComp, rRef, bSingleRef, false); + } + + virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const + { + switch (eSymType) + { + case ScCompiler::Convention::ABS_SHEET_PREFIX: + return '$'; + case ScCompiler::Convention::SHEET_SEPARATOR: + return '.'; + } + + return sal_Unicode(0); + } + + virtual bool parseExternalName( const String& rSymbol, String& rFile, String& rName, + const ScDocument* pDoc, + const ::com::sun::star::uno::Sequence< + const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const + { + return lcl_parseExternalName(rSymbol, rFile, rName, sal_Unicode('#'), pDoc, pExternalLinks); + } + + virtual String makeExternalNameStr( const String& rFile, const String& rName ) const + { + return lcl_makeExternalNameStr( rFile, rName, sal_Unicode('#'), false); + } + + bool makeExternalSingleRefStr( ::rtl::OUStringBuffer& rBuffer, sal_uInt16 nFileId, + const String& rTabName, const ScSingleRefData& rRef, + ScExternalRefManager* pRefMgr, bool bDisplayTabName, bool bEncodeUrl ) const + { + if (bDisplayTabName) + { + String aFile; + const String* p = pRefMgr->getExternalFileName(nFileId); + if (p) + { + if (bEncodeUrl) + aFile = *p; + else + aFile = INetURLObject::decode(*p, INET_HEX_ESCAPE, INetURLObject::DECODE_UNAMBIGUOUS); + } + aFile.SearchAndReplaceAllAscii("'", String::CreateFromAscii("''")); + + rBuffer.append(sal_Unicode('\'')); + rBuffer.append(aFile); + rBuffer.append(sal_Unicode('\'')); + rBuffer.append(sal_Unicode('#')); + + if (!rRef.IsTabRel()) + rBuffer.append(sal_Unicode('$')); + ScRangeStringConverter::AppendTableName(rBuffer, rTabName); + + rBuffer.append(sal_Unicode('.')); + } + + if (!rRef.IsColRel()) + rBuffer.append(sal_Unicode('$')); + MakeColStr( rBuffer, rRef.nCol); + if (!rRef.IsRowRel()) + rBuffer.append(sal_Unicode('$')); + MakeRowStr( rBuffer, rRef.nRow); + + return true; + } + + void makeExternalRefStrImpl( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler, + sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef, + ScExternalRefManager* pRefMgr, bool bODF ) const + { + ScSingleRefData aRef(rRef); + aRef.CalcAbsIfRel(rCompiler.GetPos()); + + if (bODF) + rBuffer.append( sal_Unicode('[')); + + bool bEncodeUrl = true; + switch (rCompiler.GetEncodeUrlMode()) + { + case ScCompiler::ENCODE_BY_GRAMMAR: + bEncodeUrl = bODF; + break; + case ScCompiler::ENCODE_ALWAYS: + bEncodeUrl = true; + break; + case ScCompiler::ENCODE_NEVER: + bEncodeUrl = false; + break; + default: + ; + } + makeExternalSingleRefStr(rBuffer, nFileId, rTabName, aRef, pRefMgr, true, bEncodeUrl); + if (bODF) + rBuffer.append( sal_Unicode(']')); + } + + virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler, + sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef, + ScExternalRefManager* pRefMgr ) const + { + makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, false); + } + + void makeExternalRefStrImpl( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler, + sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef, + ScExternalRefManager* pRefMgr, bool bODF ) const + { + ScComplexRefData aRef(rRef); + aRef.CalcAbsIfRel(rCompiler.GetPos()); + + if (bODF) + rBuffer.append( sal_Unicode('[')); + // Ensure that there's always a closing bracket, no premature returns. + bool bEncodeUrl = true; + switch (rCompiler.GetEncodeUrlMode()) + { + case ScCompiler::ENCODE_BY_GRAMMAR: + bEncodeUrl = bODF; + break; + case ScCompiler::ENCODE_ALWAYS: + bEncodeUrl = true; + break; + case ScCompiler::ENCODE_NEVER: + bEncodeUrl = false; + break; + default: + ; + } + + do + { + if (!makeExternalSingleRefStr(rBuffer, nFileId, rTabName, aRef.Ref1, pRefMgr, true, bEncodeUrl)) + break; + + rBuffer.append(sal_Unicode(':')); + + String aLastTabName; + bool bDisplayTabName = (aRef.Ref1.nTab != aRef.Ref2.nTab); + if (bDisplayTabName) + { + // Get the name of the last table. + vector<String> aTabNames; + pRefMgr->getAllCachedTableNames(nFileId, aTabNames); + if (aTabNames.empty()) + { + DBG_ERROR1( "ConventionOOO_A1::makeExternalRefStrImpl: no sheet names for document ID %s", nFileId); + } + + if (!lcl_getLastTabName(aLastTabName, rTabName, aTabNames, aRef)) + { + DBG_ERROR( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found"); + // aLastTabName contains #REF!, proceed. + } + } + else if (bODF) + rBuffer.append( sal_Unicode('.')); // need at least the sheet separator in ODF + makeExternalSingleRefStr( rBuffer, nFileId, aLastTabName, + aRef.Ref2, pRefMgr, bDisplayTabName, bEncodeUrl); + } while (0); + if (bODF) + rBuffer.append( sal_Unicode(']')); + } + + virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler, + sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef, + ScExternalRefManager* pRefMgr ) const + { + makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, false); + } +}; + + +static const ConventionOOO_A1 ConvOOO_A1; +const ScCompiler::Convention * const ScCompiler::pConvOOO_A1 = &ConvOOO_A1; + +//----------------------------------------------------------------------------- + +struct ConventionOOO_A1_ODF : public ConventionOOO_A1 +{ + ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF) { } + void MakeRefStr( rtl::OUStringBuffer& rBuffer, + const ScCompiler& rComp, + const ScComplexRefData& rRef, + BOOL bSingleRef ) const + { + MakeRefStrImpl( rBuffer, rComp, rRef, bSingleRef, true); + } + + virtual String makeExternalNameStr( const String& rFile, const String& rName ) const + { + return lcl_makeExternalNameStr( rFile, rName, sal_Unicode('#'), true); + } + + virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler, + sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef, + ScExternalRefManager* pRefMgr ) const + { + makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, true); + } + + virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler, + sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef, + ScExternalRefManager* pRefMgr ) const + { + makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, true); + } +}; + +static const ConventionOOO_A1_ODF ConvOOO_A1_ODF; +const ScCompiler::Convention * const ScCompiler::pConvOOO_A1_ODF = &ConvOOO_A1_ODF; + +//----------------------------------------------------------------------------- + +struct ConventionXL +{ + static bool GetDocAndTab( const ScCompiler& rComp, + const ScSingleRefData& rRef, + String& rDocName, + String& rTabName ) + { + bool bHasDoc = false; + + rDocName.Erase(); + if (rRef.IsTabDeleted() || + !rComp.GetDoc()->GetName( rRef.nTab, rTabName )) + { + rTabName = ScGlobal::GetRscString( STR_NO_REF_TABLE ); + return false; + } + + // Cheesy hack to unparse the OOO style "'Doc'#Tab" + if ( rTabName.GetChar(0) == '\'' ) + { + xub_StrLen nPos = ScGlobal::FindUnquoted( rTabName, SC_COMPILER_FILE_TAB_SEP); + if (nPos != STRING_NOTFOUND && nPos > 0 && rTabName.GetChar(nPos-1) == '\'') + { + rDocName = rTabName.Copy( 0, nPos ); + // TODO : More research into how XL escapes the doc path + rDocName = INetURLObject::decode( rDocName, INET_HEX_ESCAPE, + INetURLObject::DECODE_UNAMBIGUOUS ); + rTabName.Erase( 0, nPos + 1 ); + bHasDoc = true; + } + } + + // XL uses the same sheet name quoting conventions in both modes + // it is safe to use A1 here + ScCompiler::CheckTabQuotes( rTabName, FormulaGrammar::CONV_XL_A1 ); + return bHasDoc; + } + + static void MakeDocStr( rtl::OUStringBuffer& rBuf, + const ScCompiler& rComp, + const ScComplexRefData& rRef, + bool bSingleRef ) + { + if( rRef.Ref1.IsFlag3D() ) + { + String aStartTabName, aStartDocName, aEndTabName, aEndDocName; + bool bStartHasDoc = false, bEndHasDoc = false; + + bStartHasDoc = GetDocAndTab( rComp, rRef.Ref1, + aStartDocName, aStartTabName); + + if( !bSingleRef && rRef.Ref2.IsFlag3D() ) + { + bEndHasDoc = GetDocAndTab( rComp, rRef.Ref2, + aEndDocName, aEndTabName); + } + else + bEndHasDoc = bStartHasDoc; + + if( bStartHasDoc ) + { + // A ref across multipled workbooks ? + if( !bEndHasDoc ) + return; + + rBuf.append( sal_Unicode( '[' ) ); + rBuf.append( aStartDocName ); + rBuf.append( sal_Unicode( ']' ) ); + } + + rBuf.append( aStartTabName ); + if( !bSingleRef && rRef.Ref2.IsFlag3D() && aStartTabName != aEndTabName ) + { + rBuf.append( sal_Unicode( ':' ) ); + rBuf.append( aEndTabName ); + } + + rBuf.append( sal_Unicode( '!' ) ); + } + } + + static sal_Unicode getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType ) + { + switch (eSymType) + { + case ScCompiler::Convention::ABS_SHEET_PREFIX: + return sal_Unicode(0); + case ScCompiler::Convention::SHEET_SEPARATOR: + return '!'; + } + return sal_Unicode(0); + } + + static bool parseExternalName( const String& rSymbol, String& rFile, String& rName, + const ScDocument* pDoc, + const ::com::sun::star::uno::Sequence< + const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) + { + return lcl_parseExternalName( rSymbol, rFile, rName, sal_Unicode('!'), pDoc, pExternalLinks); + } + + static String makeExternalNameStr( const String& rFile, const String& rName ) + { + return lcl_makeExternalNameStr( rFile, rName, sal_Unicode('!'), false); + } + + static void makeExternalDocStr( ::rtl::OUStringBuffer& rBuffer, const String& rFullName, bool bEncodeUrl ) + { + // Format that is easier to deal with inside OOo, because we use file + // URL, and all characetrs are allowed. Check if it makes sense to do + // it the way Gnumeric does it. Gnumeric doesn't use the URL form + // and allows relative file path. + // + // ['file:///path/to/source/filename.xls'] + + rBuffer.append(sal_Unicode('[')); + rBuffer.append(sal_Unicode('\'')); + String aFullName; + if (bEncodeUrl) + aFullName = rFullName; + else + aFullName = INetURLObject::decode(rFullName, INET_HEX_ESCAPE, INetURLObject::DECODE_UNAMBIGUOUS); + + const sal_Unicode* pBuf = aFullName.GetBuffer(); + xub_StrLen nLen = aFullName.Len(); + for (xub_StrLen i = 0; i < nLen; ++i) + { + const sal_Unicode c = pBuf[i]; + if (c == sal_Unicode('\'')) + rBuffer.append(c); + rBuffer.append(c); + } + rBuffer.append(sal_Unicode('\'')); + rBuffer.append(sal_Unicode(']')); + } + + static void makeExternalTabNameRange( ::rtl::OUStringBuffer& rBuf, const String& rTabName, + const vector<String>& rTabNames, + const ScComplexRefData& rRef ) + { + String aLastTabName; + if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, rRef)) + { + ScRangeStringConverter::AppendTableName(rBuf, aLastTabName); + return; + } + + ScRangeStringConverter::AppendTableName(rBuf, rTabName); + if (rTabName != aLastTabName) + { + rBuf.append(sal_Unicode(':')); + ScRangeStringConverter::AppendTableName(rBuf, rTabName); + } + } + + static void parseExternalDocName( const String& rFormula, xub_StrLen& rSrcPos ) + { + xub_StrLen nLen = rFormula.Len(); + const sal_Unicode* p = rFormula.GetBuffer(); + sal_Unicode cPrev = 0; + for (xub_StrLen i = rSrcPos; i < nLen; ++i) + { + sal_Unicode c = p[i]; + if (i == rSrcPos) + { + // first character must be '['. + if (c != '[') + return; + } + else if (i == rSrcPos + 1) + { + // second character must be a single quote. + if (c != '\'') + return; + } + else if (c == '\'') + { + if (cPrev == '\'') + // two successive single quote is treated as a single + // valid character. + c = 'a'; + } + else if (c == ']') + { + if (cPrev == '\'') + { + // valid source document path found. Increment the + // current position to skip the source path. + rSrcPos = i + 1; + if (rSrcPos >= nLen) + rSrcPos = nLen - 1; + return; + } + else + return; + } + else + { + // any other character + if (i > rSrcPos + 2 && cPrev == '\'') + // unless it's the 3rd character, a normal character + // following immediately a single quote is invalid. + return; + } + cPrev = c; + } + } +}; + +struct ConventionXL_A1 : public Convention_A1, public ConventionXL +{ + ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1 ) { } + ConventionXL_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1( eConv ) { } + + void makeSingleCellStr( ::rtl::OUStringBuffer& rBuf, const ScSingleRefData& rRef ) const + { + if (!rRef.IsColRel()) + rBuf.append(sal_Unicode('$')); + MakeColStr(rBuf, rRef.nCol); + if (!rRef.IsRowRel()) + rBuf.append(sal_Unicode('$')); + MakeRowStr(rBuf, rRef.nRow); + } + + void MakeRefStr( rtl::OUStringBuffer& rBuf, + const ScCompiler& rComp, + const ScComplexRefData& rRef, + BOOL bSingleRef ) const + { + ScComplexRefData aRef( rRef ); + + // Play fast and loose with invalid refs. There is not much point in producing + // Foo!A1:#REF! versus #REF! at this point + aRef.Ref1.CalcAbsIfRel( rComp.GetPos() ); + + MakeDocStr( rBuf, rComp, aRef, bSingleRef ); + + if( aRef.Ref1.IsColDeleted() || aRef.Ref1.IsRowDeleted() ) + { + rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + return; + } + + if( !bSingleRef ) + { + aRef.Ref2.CalcAbsIfRel( rComp.GetPos() ); + if( aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted() ) + { + rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + return; + } + + if( aRef.Ref1.nCol == 0 && aRef.Ref2.nCol >= MAXCOL ) + { + if (!aRef.Ref1.IsRowRel()) + rBuf.append(sal_Unicode( '$' )); + MakeRowStr( rBuf, aRef.Ref1.nRow ); + rBuf.append(sal_Unicode( ':' )); + if (!aRef.Ref2.IsRowRel()) + rBuf.append(sal_Unicode( '$' )); + MakeRowStr( rBuf, aRef.Ref2.nRow ); + return; + } + + if( aRef.Ref1.nRow == 0 && aRef.Ref2.nRow >= MAXROW ) + { + if (!aRef.Ref1.IsColRel()) + rBuf.append(sal_Unicode( '$' )); + MakeColStr(rBuf, aRef.Ref1.nCol ); + rBuf.append(sal_Unicode( ':' )); + if (!aRef.Ref2.IsColRel()) + rBuf.append(sal_Unicode( '$' )); + MakeColStr(rBuf, aRef.Ref2.nCol ); + return; + } + } + + makeSingleCellStr(rBuf, aRef.Ref1); + if (!bSingleRef) + { + rBuf.append(sal_Unicode( ':' )); + makeSingleCellStr(rBuf, aRef.Ref2); + } + } + + virtual ParseResult parseAnyToken( const String& rFormula, + xub_StrLen nSrcPos, + const CharClass* pCharClass) const + { + ParseResult aRet; + if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) ) + return aRet; + + static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | + KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR; + static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT; + // '?' allowed in range names + static const String aAddAllowed = String::CreateFromAscii("?!"); + return pCharClass->parseAnyToken( rFormula, + nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed ); + } + + virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const + { + return ConventionXL::getSpecialSymbol(eSymType); + } + + virtual bool parseExternalName( const String& rSymbol, String& rFile, String& rName, + const ScDocument* pDoc, + const ::com::sun::star::uno::Sequence< + const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const + { + return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks); + } + + virtual String makeExternalNameStr( const String& rFile, const String& rName ) const + { + return ConventionXL::makeExternalNameStr(rFile, rName); + } + + virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler, + sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef, + ScExternalRefManager* pRefMgr ) const + { + // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1 + // This is a little different from the format Excel uses, as Excel + // puts [] only around the file name. But we need to enclose the + // whole file path with [] because the file name can contain any + // characters. + + const String* pFullName = pRefMgr->getExternalFileName(nFileId); + if (!pFullName) + return; + + ScSingleRefData aRef(rRef); + aRef.CalcAbsIfRel(rCompiler.GetPos()); + + ConventionXL::makeExternalDocStr( + rBuffer, *pFullName, rCompiler.GetEncodeUrlMode() == ScCompiler::ENCODE_ALWAYS); + ScRangeStringConverter::AppendTableName(rBuffer, rTabName); + rBuffer.append(sal_Unicode('!')); + + makeSingleCellStr(rBuffer, aRef); + } + + virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler, + sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef, + ScExternalRefManager* pRefMgr ) const + { + const String* pFullName = pRefMgr->getExternalFileName(nFileId); + if (!pFullName) + return; + + vector<String> aTabNames; + pRefMgr->getAllCachedTableNames(nFileId, aTabNames); + if (aTabNames.empty()) + return; + + ScComplexRefData aRef(rRef); + aRef.CalcAbsIfRel(rCompiler.GetPos()); + + ConventionXL::makeExternalDocStr( + rBuffer, *pFullName, rCompiler.GetEncodeUrlMode() == ScCompiler::ENCODE_ALWAYS); + ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, aTabNames, aRef); + rBuffer.append(sal_Unicode('!')); + + makeSingleCellStr(rBuffer, aRef.Ref1); + if (aRef.Ref1 != aRef.Ref2) + { + rBuffer.append(sal_Unicode(':')); + makeSingleCellStr(rBuffer, aRef.Ref2); + } + } +}; + +static const ConventionXL_A1 ConvXL_A1; +const ScCompiler::Convention * const ScCompiler::pConvXL_A1 = &ConvXL_A1; + + +struct ConventionXL_OOX : public ConventionXL_A1 +{ + ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { } +}; + +static const ConventionXL_OOX ConvXL_OOX; +const ScCompiler::Convention * const ScCompiler::pConvXL_OOX = &ConvXL_OOX; + + +//----------------------------------------------------------------------------- + +static void +r1c1_add_col( rtl::OUStringBuffer &rBuf, const ScSingleRefData& rRef ) +{ + rBuf.append( sal_Unicode( 'C' ) ); + if( rRef.IsColRel() ) + { + if (rRef.nRelCol != 0) + { + rBuf.append( sal_Unicode( '[' ) ); + rBuf.append( String::CreateFromInt32( rRef.nRelCol ) ); + rBuf.append( sal_Unicode( ']' ) ); + } + } + else + rBuf.append( String::CreateFromInt32( rRef.nCol + 1 ) ); +} +static void +r1c1_add_row( rtl::OUStringBuffer &rBuf, const ScSingleRefData& rRef ) +{ + rBuf.append( sal_Unicode( 'R' ) ); + if( rRef.IsRowRel() ) + { + if (rRef.nRelRow != 0) + { + rBuf.append( sal_Unicode( '[' ) ); + rBuf.append( String::CreateFromInt32( rRef.nRelRow ) ); + rBuf.append( sal_Unicode( ']' ) ); + } + } + else + rBuf.append( String::CreateFromInt32( rRef.nRow + 1 ) ); +} + +struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL +{ + ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1 ) { } + void MakeRefStr( rtl::OUStringBuffer& rBuf, + const ScCompiler& rComp, + const ScComplexRefData& rRef, + BOOL bSingleRef ) const + { + ScComplexRefData aRef( rRef ); + + MakeDocStr( rBuf, rComp, aRef, bSingleRef ); + + // Play fast and loose with invalid refs. There is not much point in producing + // Foo!A1:#REF! versus #REF! at this point + aRef.Ref1.CalcAbsIfRel( rComp.GetPos() ); + if( aRef.Ref1.IsColDeleted() || aRef.Ref1.IsRowDeleted() ) + { + rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + return; + } + + if( !bSingleRef ) + { + aRef.Ref2.CalcAbsIfRel( rComp.GetPos() ); + if( aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted() ) + { + rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + return; + } + + if( aRef.Ref1.nCol == 0 && aRef.Ref2.nCol >= MAXCOL ) + { + r1c1_add_row( rBuf, rRef.Ref1 ); + if( rRef.Ref1.nRow != rRef.Ref2.nRow || + rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() ) { + rBuf.append (sal_Unicode ( ':' ) ); + r1c1_add_row( rBuf, rRef.Ref2 ); + } + return; + + } + + if( aRef.Ref1.nRow == 0 && aRef.Ref2.nRow >= MAXROW ) + { + r1c1_add_col( rBuf, rRef.Ref1 ); + if( rRef.Ref1.nCol != rRef.Ref2.nCol || + rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel() ) + { + rBuf.append (sal_Unicode ( ':' ) ); + r1c1_add_col( rBuf, rRef.Ref2 ); + } + return; + } + } + + r1c1_add_row( rBuf, rRef.Ref1 ); + r1c1_add_col( rBuf, rRef.Ref1 ); + if (!bSingleRef) + { + rBuf.append (sal_Unicode ( ':' ) ); + r1c1_add_row( rBuf, rRef.Ref2 ); + r1c1_add_col( rBuf, rRef.Ref2 ); + } + } + + ParseResult parseAnyToken( const String& rFormula, + xub_StrLen nSrcPos, + const CharClass* pCharClass) const + { + ConventionXL::parseExternalDocName(rFormula, nSrcPos); + + ParseResult aRet; + if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) ) + return aRet; + + static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | + KParseTokens::ASC_UNDERSCORE ; + static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT; + // '?' allowed in range names + static const String aAddAllowed = String::CreateFromAscii( "?-[]!" ); + + return pCharClass->parseAnyToken( rFormula, + nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed ); + } + + virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const + { + return ConventionXL::getSpecialSymbol(eSymType); + } + + virtual bool parseExternalName( const String& rSymbol, String& rFile, String& rName, + const ScDocument* pDoc, + const ::com::sun::star::uno::Sequence< + const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const + { + return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks); + } + + virtual String makeExternalNameStr( const String& rFile, const String& rName ) const + { + return ConventionXL::makeExternalNameStr(rFile, rName); + } + + virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler, + sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef, + ScExternalRefManager* pRefMgr ) const + { + // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1 + // This is a little different from the format Excel uses, as Excel + // puts [] only around the file name. But we need to enclose the + // whole file path with [] because the file name can contain any + // characters. + + const String* pFullName = pRefMgr->getExternalFileName(nFileId); + if (!pFullName) + return; + + ScSingleRefData aRef(rRef); + aRef.CalcAbsIfRel(rCompiler.GetPos()); + + ConventionXL::makeExternalDocStr( + rBuffer, *pFullName, rCompiler.GetEncodeUrlMode() == ScCompiler::ENCODE_ALWAYS); + ScRangeStringConverter::AppendTableName(rBuffer, rTabName); + rBuffer.append(sal_Unicode('!')); + + r1c1_add_row(rBuffer, aRef); + r1c1_add_col(rBuffer, aRef); + } + + virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler, + sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef, + ScExternalRefManager* pRefMgr ) const + { + const String* pFullName = pRefMgr->getExternalFileName(nFileId); + if (!pFullName) + return; + + vector<String> aTabNames; + pRefMgr->getAllCachedTableNames(nFileId, aTabNames); + if (aTabNames.empty()) + return; + + ScComplexRefData aRef(rRef); + aRef.CalcAbsIfRel(rCompiler.GetPos()); + + ConventionXL::makeExternalDocStr( + rBuffer, *pFullName, rCompiler.GetEncodeUrlMode() == ScCompiler::ENCODE_ALWAYS); + ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, aTabNames, aRef); + rBuffer.append(sal_Unicode('!')); + + if (aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted()) + { + rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE)); + return; + } + + if (aRef.Ref1.nCol == 0 && aRef.Ref2.nCol >= MAXCOL) + { + r1c1_add_row(rBuffer, rRef.Ref1); + if (rRef.Ref1.nRow != rRef.Ref2.nRow || rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel()) + { + rBuffer.append (sal_Unicode(':')); + r1c1_add_row(rBuffer, rRef.Ref2); + } + return; + } + + if (aRef.Ref1.nRow == 0 && aRef.Ref2.nRow >= MAXROW) + { + r1c1_add_col(rBuffer, aRef.Ref1); + if (aRef.Ref1.nCol != aRef.Ref2.nCol || aRef.Ref1.IsColRel() != aRef.Ref2.IsColRel()) + { + rBuffer.append (sal_Unicode(':')); + r1c1_add_col(rBuffer, aRef.Ref2); + } + return; + } + + r1c1_add_row(rBuffer, aRef.Ref1); + r1c1_add_col(rBuffer, aRef.Ref1); + rBuffer.append (sal_Unicode (':')); + r1c1_add_row(rBuffer, aRef.Ref2); + r1c1_add_col(rBuffer, aRef.Ref2); + } +}; + +static const ConventionXL_R1C1 ConvXL_R1C1; +const ScCompiler::Convention * const ScCompiler::pConvXL_R1C1 = &ConvXL_R1C1; + +//----------------------------------------------------------------------------- +ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos,ScTokenArray& rArr) + : FormulaCompiler(rArr), + pDoc( pDocument ), + aPos( rPos ), + pCharClass( ScGlobal::pCharClass ), + mnPredetectedReference(0), + mnRangeOpPosInSymbol(-1), + pConv( pConvOOO_A1 ), + meEncodeUrlMode( ENCODE_BY_GRAMMAR ), + mbCloseBrackets( true ), + mbExtendedErrorDetection( false ), + mbRewind( false ) +{ + nMaxTab = pDoc ? pDoc->GetTableCount() - 1 : 0; +} + +ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos) + : + pDoc( pDocument ), + aPos( rPos ), + pCharClass( ScGlobal::pCharClass ), + mnPredetectedReference(0), + mnRangeOpPosInSymbol(-1), + pConv( pConvOOO_A1 ), + meEncodeUrlMode( ENCODE_BY_GRAMMAR ), + mbCloseBrackets( true ), + mbExtendedErrorDetection( false ), + mbRewind( false ) +{ + nMaxTab = pDoc ? pDoc->GetTableCount() - 1 : 0; +} + +void ScCompiler::CheckTabQuotes( String& rString, + const FormulaGrammar::AddressConvention eConv ) +{ + using namespace ::com::sun::star::i18n; + sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | KParseTokens::ASC_UNDERSCORE; + sal_Int32 nContFlags = nStartFlags; + ParseResult aRes = ScGlobal::pCharClass->parsePredefinedToken( + KParseType::IDENTNAME, rString, 0, nStartFlags, EMPTY_STRING, nContFlags, EMPTY_STRING); + bool bNeedsQuote = !((aRes.TokenType & KParseType::IDENTNAME) && aRes.EndPos == rString.Len()); + + switch ( eConv ) + { + default : + case FormulaGrammar::CONV_UNSPECIFIED : + break; + case FormulaGrammar::CONV_OOO : + case FormulaGrammar::CONV_XL_A1 : + case FormulaGrammar::CONV_XL_R1C1 : + case FormulaGrammar::CONV_XL_OOX : + if( bNeedsQuote ) + { + static const String one_quote = static_cast<sal_Unicode>( '\'' ); + static const String two_quote = String::CreateFromAscii( "''" ); + // escape embedded quotes + rString.SearchAndReplaceAll( one_quote, two_quote ); + } + break; + } + + if ( !bNeedsQuote && CharClass::isAsciiNumeric( rString ) ) + { + // Prevent any possible confusion resulting from pure numeric sheet names. + bNeedsQuote = true; + } + + if( bNeedsQuote ) + { + rString.Insert( '\'', 0 ); + rString += '\''; + } +} + +//--------------------------------------------------------------------------- + +void ScCompiler::SetRefConvention( FormulaGrammar::AddressConvention eConv ) +{ + switch ( eConv ) { + case FormulaGrammar::CONV_UNSPECIFIED : + break; + default : + case FormulaGrammar::CONV_OOO : SetRefConvention( pConvOOO_A1 ); break; + case FormulaGrammar::CONV_ODF : SetRefConvention( pConvOOO_A1_ODF ); break; + case FormulaGrammar::CONV_XL_A1 : SetRefConvention( pConvXL_A1 ); break; + case FormulaGrammar::CONV_XL_R1C1 : SetRefConvention( pConvXL_R1C1 ); break; + case FormulaGrammar::CONV_XL_OOX : SetRefConvention( pConvXL_OOX ); break; + } +} + +void ScCompiler::SetRefConvention( const ScCompiler::Convention *pConvP ) +{ + pConv = pConvP; + meGrammar = FormulaGrammar::mergeToGrammar( meGrammar, pConv->meConv); + DBG_ASSERT( FormulaGrammar::isSupported( meGrammar), + "ScCompiler::SetRefConvention: unsupported grammar resulting"); +} + +void ScCompiler::SetError(USHORT nError) +{ + if( !pArr->GetCodeError() ) + pArr->SetCodeError( nError); +} + + +sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pSrc, xub_StrLen nMax ) +{ + const sal_Unicode* const pStop = pDst + nMax; + while ( *pSrc && pDst < pStop ) + { + *pDst++ = *pSrc++; + } + *pDst = 0; + return pDst; +} + + +//--------------------------------------------------------------------------- +// NextSymbol +//--------------------------------------------------------------------------- +// Zerlegt die Formel in einzelne Symbole fuer die weitere +// Verarbeitung (Turing-Maschine). +//--------------------------------------------------------------------------- +// Ausgangs Zustand = GetChar +//---------------+-------------------+-----------------------+--------------- +// Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand +//---------------+-------------------+-----------------------+--------------- +// GetChar | ;()+-*/^=& | Symbol=Zeichen | Stop +// | <> | Symbol=Zeichen | GetBool +// | $ Buchstabe | Symbol=Zeichen | GetWord +// | Ziffer | Symbol=Zeichen | GetValue +// | " | Keine | GetString +// | Sonst | Keine | GetChar +//---------------+-------------------+-----------------------+--------------- +// GetBool | => | Symbol=Symbol+Zeichen | Stop +// | Sonst | Dec(CharPos) | Stop +//---------------+-------------------+-----------------------+--------------- +// GetWord | SepSymbol | Dec(CharPos) | Stop +// | ()+-*/^=<>&~ | | +// | Leerzeichen | Dec(CharPos) | Stop +// | $_:. | | +// | Buchstabe,Ziffer | Symbol=Symbol+Zeichen | GetWord +// | Sonst | Fehler | Stop +//---------------|-------------------+-----------------------+--------------- +// GetValue | ;()*/^=<>& | | +// | Leerzeichen | Dec(CharPos) | Stop +// | Ziffer E+-%,. | Symbol=Symbol+Zeichen | GetValue +// | Sonst | Fehler | Stop +//---------------+-------------------+-----------------------+--------------- +// GetString | " | Keine | Stop +// | Sonst | Symbol=Symbol+Zeichen | GetString +//---------------+-------------------+-----------------------+--------------- + +xub_StrLen ScCompiler::NextSymbol(bool bInArray) +{ + cSymbol[MAXSTRLEN-1] = 0; // Stopper + sal_Unicode* pSym = cSymbol; + const sal_Unicode* const pStart = aFormula.GetBuffer(); + const sal_Unicode* pSrc = pStart + nSrcPos; + bool bi18n = false; + sal_Unicode c = *pSrc; + sal_Unicode cLast = 0; + bool bQuote = false; + mnRangeOpPosInSymbol = -1; + ScanState eState = ssGetChar; + xub_StrLen nSpaces = 0; + sal_Unicode cSep = mxSymbols->getSymbol( ocSep).GetChar(0); + sal_Unicode cArrayColSep = mxSymbols->getSymbol( ocArrayColSep).GetChar(0); + sal_Unicode cArrayRowSep = mxSymbols->getSymbol( ocArrayRowSep).GetChar(0); + sal_Unicode cDecSep = (mxSymbols->isEnglish() ? '.' : + ScGlobal::pLocaleData->getNumDecimalSep().GetChar(0)); + + // special symbols specific to address convention used + sal_Unicode cSheetPrefix = pConv->getSpecialSymbol(ScCompiler::Convention::ABS_SHEET_PREFIX); + sal_Unicode cSheetSep = pConv->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR); + + int nDecSeps = 0; + bool bAutoIntersection = false; + int nRefInName = 0; + mnPredetectedReference = 0; + // try to parse simple tokens before calling i18n parser + while ((c != 0) && (eState != ssStop) ) + { + pSrc++; + ULONG nMask = GetCharTableFlags( c ); + // The parameter separator and the array column and row separators end + // things unconditionally if not in string or reference. + if (c == cSep || (bInArray && (c == cArrayColSep || c == cArrayRowSep))) + { + switch (eState) + { + // these are to be continued + case ssGetString: + case ssSkipString: + case ssGetReference: + case ssSkipReference: + break; + default: + if (eState == ssGetChar) + *pSym++ = c; + else + pSrc--; + eState = ssStop; + } + } +Label_MaskStateMachine: + switch (eState) + { + case ssGetChar : + { + // Order is important! + if( nMask & SC_COMPILER_C_ODF_LABEL_OP ) + { + // '!!' automatic intersection + if (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_ODF_LABEL_OP) + { + /* TODO: For now the UI "space operator" is used, this + * could be enhanced using a specialized OpCode to get + * rid of the space ambiguity, which would need some + * places to be adapted though. And we would still need + * to support the ambiguous space operator for UI + * purposes anyway. However, we then could check for + * invalid usage of '!!', which currently isn't + * possible. */ + if (!bAutoIntersection) + { + ++pSrc; + nSpaces += 2; // must match the character count + bAutoIntersection = true; + } + else + { + pSrc--; + eState = ssStop; + } + } + else + { + nMask &= ~SC_COMPILER_C_ODF_LABEL_OP; + goto Label_MaskStateMachine; + } + } + else if( nMask & SC_COMPILER_C_ODF_NAME_MARKER ) + { + // '$$' defined name marker + if (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_ODF_NAME_MARKER) + { + // both eaten, not added to pSym + ++pSrc; + } + else + { + nMask &= ~SC_COMPILER_C_ODF_NAME_MARKER; + goto Label_MaskStateMachine; + } + } + else if( nMask & SC_COMPILER_C_CHAR ) + { + *pSym++ = c; + eState = ssStop; + } + else if( nMask & SC_COMPILER_C_ODF_LBRACKET ) + { + // eaten, not added to pSym + eState = ssGetReference; + mnPredetectedReference = 1; + } + else if( nMask & SC_COMPILER_C_CHAR_BOOL ) + { + *pSym++ = c; + eState = ssGetBool; + } + else if( nMask & SC_COMPILER_C_CHAR_VALUE ) + { + *pSym++ = c; + eState = ssGetValue; + } + else if( nMask & SC_COMPILER_C_CHAR_STRING ) + { + *pSym++ = c; + eState = ssGetString; + } + else if( nMask & SC_COMPILER_C_CHAR_DONTCARE ) + { + nSpaces++; + } + else if( nMask & SC_COMPILER_C_CHAR_IDENT ) + { // try to get a simple ASCII identifier before calling + // i18n, to gain performance during import + *pSym++ = c; + eState = ssGetIdent; + } + else + { + bi18n = true; + eState = ssStop; + } + } + break; + case ssGetIdent: + { + if ( nMask & SC_COMPILER_C_IDENT ) + { // This catches also $Sheet1.A$1, for example. + if( pSym == &cSymbol[ MAXSTRLEN-1 ] ) + { + SetError(errStringOverflow); + eState = ssStop; + } + else + *pSym++ = c; + } + else if (c == ':' && mnRangeOpPosInSymbol < 0) + { + // One range operator may form Sheet1.A:A, which we need to + // pass as one entity to IsReference(). + mnRangeOpPosInSymbol = pSym - &cSymbol[0]; + if( pSym == &cSymbol[ MAXSTRLEN-1 ] ) + { + SetError(errStringOverflow); + eState = ssStop; + } + else + *pSym++ = c; + } + else if ( 128 <= c || '\'' == c ) + { // High values need reparsing with i18n, + // single quoted $'sheet' names too (otherwise we'd had to + // implement everything twice). + bi18n = true; + eState = ssStop; + } + else + { + pSrc--; + eState = ssStop; + } + } + break; + case ssGetBool : + { + if( nMask & SC_COMPILER_C_BOOL ) + { + *pSym++ = c; + eState = ssStop; + } + else + { + pSrc--; + eState = ssStop; + } + } + break; + case ssGetValue : + { + if( pSym == &cSymbol[ MAXSTRLEN-1 ] ) + { + SetError(errStringOverflow); + eState = ssStop; + } + else if (c == cDecSep) + { + if (++nDecSeps > 1) + { + // reparse with i18n, may be numeric sheet name as well + bi18n = true; + eState = ssStop; + } + else + *pSym++ = c; + } + else if( nMask & SC_COMPILER_C_VALUE ) + *pSym++ = c; + else if( nMask & SC_COMPILER_C_VALUE_SEP ) + { + pSrc--; + eState = ssStop; + } + else if (c == 'E' || c == 'e') + { + if (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_VALUE_EXP) + *pSym++ = c; + else + { + // reparse with i18n + bi18n = true; + eState = ssStop; + } + } + else if( nMask & SC_COMPILER_C_VALUE_SIGN ) + { + if (((cLast == 'E') || (cLast == 'e')) && + (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_VALUE_VALUE)) + { + *pSym++ = c; + } + else + { + pSrc--; + eState = ssStop; + } + } + else + { + // reparse with i18n + bi18n = true; + eState = ssStop; + } + } + break; + case ssGetString : + { + if( nMask & SC_COMPILER_C_STRING_SEP ) + { + if ( !bQuote ) + { + if ( *pSrc == '"' ) + bQuote = true; // "" => literal " + else + eState = ssStop; + } + else + bQuote = false; + } + if ( !bQuote ) + { + if( pSym == &cSymbol[ MAXSTRLEN-1 ] ) + { + SetError(errStringOverflow); + eState = ssSkipString; + } + else + *pSym++ = c; + } + } + break; + case ssSkipString: + if( nMask & SC_COMPILER_C_STRING_SEP ) + eState = ssStop; + break; + case ssGetReference: + if( pSym == &cSymbol[ MAXSTRLEN-1 ] ) + { + SetError( errStringOverflow); + eState = ssSkipReference; + } + // fall through and follow logic + case ssSkipReference: + // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being + // mandatory also if no sheet name. 'External'# is optional, + // sheet name is optional, quotes around sheet name are + // optional if no quote contained. + // 2nd usage: ['Sheet'.$$'DefinedName'] + // 3rd usage: ['External'#$$'DefinedName'] + // 4th usage: ['External'#$'Sheet'.$$'DefinedName'] + // Also for all these names quotes are optional if no quote + // contained. + { + + // nRefInName: 0 := not in sheet name yet. 'External' + // is parsed as if it was a sheet name and nRefInName + // is reset when # is encountered immediately after closing + // quote. Same with 'DefinedName', nRefInName is cleared + // when : is encountered. + + // Encountered leading $ before sheet name. + static const int kDollar = (1 << 1); + // Encountered ' opening quote, which may be after $ or + // not. + static const int kOpen = (1 << 2); + // Somewhere in name. + static const int kName = (1 << 3); + // Encountered ' in name, will be cleared if double or + // transformed to kClose if not, in which case kOpen is + // cleared. + static const int kQuote = (1 << 4); + // Past ' closing quote. + static const int kClose = (1 << 5); + // Encountered # file/sheet separator. + static const int kFileSep = (1 << 6); + // Past . sheet name separator. + static const int kPast = (1 << 7); + // Marked name $$ follows sheet name separator, detected + // while we're still on the separator. Will be cleared when + // entering the name. + static const int kMarkAhead = (1 << 8); + // In marked defined name. + static const int kDefName = (1 << 9); + + bool bAddToSymbol = true; + if ((nMask & SC_COMPILER_C_ODF_RBRACKET) && !(nRefInName & kOpen)) + { + DBG_ASSERT( nRefInName & (kPast | kDefName), + "ScCompiler::NextSymbol: reference: " + "closing bracket ']' without prior sheet name separator '.' violates ODF spec"); + // eaten, not added to pSym + bAddToSymbol = false; + eState = ssStop; + } + else if (cSheetSep == c && nRefInName == 0) + { + // eat it, no sheet name [.A1] + bAddToSymbol = false; + nRefInName |= kPast; + if ('$' == pSrc[0] && '$' == pSrc[1]) + nRefInName |= kMarkAhead; + } + else if (!(nRefInName & kPast) || (nRefInName & (kMarkAhead | kDefName))) + { + // Not in col/row yet. + + if (SC_COMPILER_FILE_TAB_SEP == c && (nRefInName & kFileSep)) + nRefInName = 0; + else if ('$' == c && '$' == pSrc[0] && !(nRefInName & kOpen)) + { + nRefInName &= ~kMarkAhead; + if (!(nRefInName & kDefName)) + { + // eaten, not added to pSym (2 chars) + bAddToSymbol = false; + ++pSrc; + nRefInName &= kPast; + nRefInName |= kDefName; + } + else + { + // ScAddress::Parse() will recognize this as + // invalid later. + if (eState != ssSkipReference) + { + *pSym++ = c; + *pSym++ = *pSrc++; + } + bAddToSymbol = false; + } + } + else if (cSheetPrefix == c && nRefInName == 0) + nRefInName |= kDollar; + else if ('\'' == c) + { + // TODO: The conventions' parseExternalName() + // should handle quoted names, but as long as they + // don't remove non-embedded quotes here. + if (!(nRefInName & kName)) + { + nRefInName |= (kOpen | kName); + bAddToSymbol = !(nRefInName & kDefName); + } + else if (!(nRefInName & kOpen)) + { + DBG_ERRORFILE("ScCompiler::NextSymbol: reference: " + "a ''' without the name being enclosed in '...' violates ODF spec"); + } + else if (nRefInName & kQuote) + { + // escaped embedded quote + nRefInName &= ~kQuote; + } + else + { + switch (pSrc[0]) + { + case '\'': + // escapes embedded quote + nRefInName |= kQuote; + break; + case SC_COMPILER_FILE_TAB_SEP: + // sheet name should follow + nRefInName |= kFileSep; + // fallthru + default: + // quote not followed by quote => close + nRefInName |= kClose; + nRefInName &= ~kOpen; + } + bAddToSymbol = !(nRefInName & kDefName); + } + } + else if (cSheetSep == c && !(nRefInName & kOpen)) + { + // unquoted sheet name separator + nRefInName |= kPast; + if ('$' == pSrc[0] && '$' == pSrc[1]) + nRefInName |= kMarkAhead; + } + else if (':' == c && !(nRefInName & kOpen)) + { + DBG_ERRORFILE("ScCompiler::NextSymbol: reference: " + "range operator ':' without prior sheet name separator '.' violates ODF spec"); + nRefInName = 0; + ++mnPredetectedReference; + } + else if (!(nRefInName & kName)) + { + // start unquoted name + nRefInName |= kName; + } + } + else if (':' == c) + { + // range operator + nRefInName = 0; + ++mnPredetectedReference; + } + if (bAddToSymbol && eState != ssSkipReference) + *pSym++ = c; // everything is part of reference + } + break; + case ssStop: + ; // nothing, prevent warning + break; + } + cLast = c; + c = *pSrc; + } + if ( bi18n ) + { + nSrcPos = sal::static_int_cast<xub_StrLen>( nSrcPos + nSpaces ); + String aSymbol; + mnRangeOpPosInSymbol = -1; + USHORT nErr = 0; + do + { + bi18n = false; + // special case (e.g. $'sheetname' in OOO A1) + if ( pStart[nSrcPos] == cSheetPrefix && pStart[nSrcPos+1] == '\'' ) + aSymbol += pStart[nSrcPos++]; + + ParseResult aRes = pConv->parseAnyToken( aFormula, nSrcPos, pCharClass ); + + if ( !aRes.TokenType ) + SetError( nErr = errIllegalChar ); // parsed chars as string + if ( aRes.EndPos <= nSrcPos ) + { // ?!? + SetError( nErr = errIllegalChar ); + nSrcPos = aFormula.Len(); + aSymbol.Erase(); + } + else + { + aSymbol.Append( pStart + nSrcPos, (xub_StrLen)aRes.EndPos - nSrcPos ); + nSrcPos = (xub_StrLen) aRes.EndPos; + c = pStart[nSrcPos]; + if ( aRes.TokenType & KParseType::SINGLE_QUOTE_NAME ) + { // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1) + bi18n = (c == cSheetSep || c == SC_COMPILER_FILE_TAB_SEP); + } + // One range operator restarts parsing for second reference. + if (c == ':' && mnRangeOpPosInSymbol < 0) + { + mnRangeOpPosInSymbol = aSymbol.Len(); + bi18n = true; + } + if ( bi18n ) + aSymbol += pStart[nSrcPos++]; + } + } while ( bi18n && !nErr ); + xub_StrLen nLen = aSymbol.Len(); + if ( nLen >= MAXSTRLEN ) + { + SetError( errStringOverflow ); + nLen = MAXSTRLEN-1; + } + lcl_UnicodeStrNCpy( cSymbol, aSymbol.GetBuffer(), nLen ); + } + else + { + nSrcPos = sal::static_int_cast<xub_StrLen>( pSrc - pStart ); + *pSym = 0; + } + if (mnRangeOpPosInSymbol >= 0 && mnRangeOpPosInSymbol == (pSym-1) - &cSymbol[0]) + { + // This is a trailing range operator, which is nonsense. Will be caught + // in next round. + mnRangeOpPosInSymbol = -1; + *--pSym = 0; + --nSrcPos; + } + if ( bAutoCorrect ) + aCorrectedSymbol = cSymbol; + if (bAutoIntersection && nSpaces > 1) + --nSpaces; // replace '!!' with only one space + return nSpaces; +} + +//--------------------------------------------------------------------------- +// Convert symbol to token +//--------------------------------------------------------------------------- + +BOOL ScCompiler::IsOpCode( const String& rName, bool bInArray ) +{ + OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName)); + BOOL bFound = (iLook != mxSymbols->getHashMap()->end()); + if (bFound) + { + ScRawToken aToken; + OpCode eOp = iLook->second; + if (bInArray) + { + if (rName.Equals(mxSymbols->getSymbol(ocArrayColSep))) + eOp = ocArrayColSep; + else if (rName.Equals(mxSymbols->getSymbol(ocArrayRowSep))) + eOp = ocArrayRowSep; + } + aToken.SetOpCode(eOp); + pRawToken = aToken.Clone(); + } + else if (mxSymbols->isODFF()) + { + // ODFF names that are not written in the current mapping but to be + // recognized. New names will be written in a future relase, then + // exchange (!) with the names in + // formula/source/core/resource/core_resource.src to be able to still + // read the old names as well. + struct FunctionName + { + const sal_Char* pName; + OpCode eOp; + }; + static const FunctionName aOdffAliases[] = { + // Renamed old names: + // XXX none yet. + // Renamed new names: + { "BINOM.DIST.RANGE", ocB }, // B -> BINOM.DIST.RANGE + { "LEGACY.TDIST", ocTDist }, // TDIST -> LEGACY.TDIST + { "ORG.OPENOFFICE.EASTERSUNDAY", ocEasterSunday } // EASTERSUNDAY -> ORG.OPENOFFICE.EASTERSUNDAY + }; + static const size_t nOdffAliases = sizeof(aOdffAliases) / sizeof(aOdffAliases[0]); + for (size_t i=0; i<nOdffAliases; ++i) + { + if (rName.EqualsIgnoreCaseAscii( aOdffAliases[i].pName)) + { + ScRawToken aToken; + aToken.SetOpCode( aOdffAliases[i].eOp); + pRawToken = aToken.Clone(); + bFound = TRUE; + break; // for + } + } + } + if (!bFound) + { + String aIntName; + if (mxSymbols->hasExternals()) + { + // If symbols are set by filters get mapping to exact name. + ExternalHashMap::const_iterator iExt( + mxSymbols->getExternalHashMap()->find( rName)); + if (iExt != mxSymbols->getExternalHashMap()->end()) + { + if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt).second)) + aIntName = (*iExt).second; + } + if (!aIntName.Len()) + { + // If that isn't found we might continue with rName lookup as a + // last resort by just falling through to FindFunction(), but + // it shouldn't happen if the map was setup correctly. Don't + // waste time and bail out. + return FALSE; + } + } + if (!aIntName.Len()) + { + // Old (deprecated) addins first for legacy. + USHORT nIndex; + bFound = ScGlobal::GetFuncCollection()->SearchFunc( cSymbol, nIndex); + if (bFound) + { + ScRawToken aToken; + aToken.SetExternal( cSymbol ); + pRawToken = aToken.Clone(); + } + else + // bLocalFirst=FALSE for (English) upper full original name + // (service.function) + aIntName = ScGlobal::GetAddInCollection()->FindFunction( + rName, !mxSymbols->isEnglish()); + } + if (aIntName.Len()) + { + ScRawToken aToken; + aToken.SetExternal( aIntName.GetBuffer() ); // international name + pRawToken = aToken.Clone(); + bFound = TRUE; + } + } + OpCode eOp; + if (bFound && ((eOp = pRawToken->GetOpCode()) == ocSub || eOp == ocNegSub)) + { + bool bShouldBeNegSub = + (eLastOp == ocOpen || eLastOp == ocSep || eLastOp == ocNegSub || + (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_BIN_OP) || + eLastOp == ocArrayOpen || + eLastOp == ocArrayColSep || eLastOp == ocArrayRowSep); + if (bShouldBeNegSub && eOp == ocSub) + pRawToken->NewOpCode( ocNegSub ); + //! if ocNegSub had ForceArray we'd have to set it here + else if (!bShouldBeNegSub && eOp == ocNegSub) + pRawToken->NewOpCode( ocSub ); + } + return bFound; +} + +BOOL ScCompiler::IsOpCode2( const String& rName ) +{ + BOOL bFound = FALSE; + USHORT i; + + for( i = ocInternalBegin; i <= ocInternalEnd && !bFound; i++ ) + bFound = rName.EqualsAscii( pInternal[ i-ocInternalBegin ] ); + + if (bFound) + { + ScRawToken aToken; + aToken.SetOpCode( (OpCode) --i ); + pRawToken = aToken.Clone(); + } + return bFound; +} + +BOOL ScCompiler::IsValue( const String& rSym ) +{ + double fVal; + sal_uInt32 nIndex = ( mxSymbols->isEnglish() ? + pDoc->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US ) : 0 ); +// ULONG nIndex = 0; +//// ULONG nIndex = pDoc->GetFormatTable()->GetStandardIndex(ScGlobal::eLnge); + if (pDoc->GetFormatTable()->IsNumberFormat( rSym, nIndex, fVal ) ) + { + USHORT nType = pDoc->GetFormatTable()->GetType(nIndex); + + // Don't accept 3:3 as time, it is a reference to entire row 3 instead. + // Dates should never be entered directly and automatically converted + // to serial, because the serial would be wrong if null-date changed. + // Usually it wouldn't be accepted anyway because the date separator + // clashed with other separators or operators. + if (nType & (NUMBERFORMAT_TIME | NUMBERFORMAT_DATE)) + return FALSE; + + if (nType == NUMBERFORMAT_LOGICAL) + { + const sal_Unicode* p = aFormula.GetBuffer() + nSrcPos; + while( *p == ' ' ) + p++; + if (*p == '(') + return FALSE; // Boolean function instead. + } + + if( aFormula.GetChar(nSrcPos) == '.' ) + // numerical sheet name? + return FALSE; + + if( nType == NUMBERFORMAT_TEXT ) + // HACK: number too big! + SetError( errIllegalArgument ); + ScRawToken aToken; + aToken.SetDouble( fVal ); + pRawToken = aToken.Clone(); + return TRUE; + } + else + return FALSE; +} + +BOOL ScCompiler::IsString() +{ + register const sal_Unicode* p = cSymbol; + while ( *p ) + p++; + xub_StrLen nLen = sal::static_int_cast<xub_StrLen>( p - cSymbol - 1 ); + BOOL bQuote = ((cSymbol[0] == '"') && (cSymbol[nLen] == '"')); + if ((bQuote ? nLen-2 : nLen) > MAXSTRLEN-1) + { + SetError(errStringOverflow); + return FALSE; + } + if ( bQuote ) + { + cSymbol[nLen] = '\0'; + ScRawToken aToken; + aToken.SetString( cSymbol+1 ); + pRawToken = aToken.Clone(); + return TRUE; + } + return FALSE; +} + + +BOOL ScCompiler::IsPredetectedReference( const String& rName ) +{ + // Speedup documents with lots of broken references, e.g. sheet deleted. + xub_StrLen nPos = rName.SearchAscii( "#REF!"); + if (nPos != STRING_NOTFOUND) + { + /* TODO: this may be enhanced by reusing scan information from + * NextSymbol(), the positions of quotes and special characters found + * there for $'sheet'.A1:... could be stored in a vector. We don't + * fully rescan here whether found positions are within single quotes + * for performance reasons. This code does not check for possible + * occurrences of insane "valid" sheet names like + * 'haha.#REF!1fooledyou' and will generate an error on such. */ + if (nPos == 0) + return false; // #REF!.AB42 or #REF!42 or #REF!#REF! + sal_Unicode c = rName.GetChar(nPos-1); // before #REF! + if ('$' == c) + { + if (nPos == 1) + return false; // $#REF!.AB42 or $#REF!42 or $#REF!#REF! + c = rName.GetChar(nPos-2); // before $#REF! + } + sal_Unicode c2 = rName.GetChar(nPos+5); // after #REF! + switch (c) + { + case '.': + if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9')) + return false; // sheet.#REF!42 or sheet.#REF!#REF! + break; + case ':': + if (mnPredetectedReference > 1 && + ('.' == c2 || '$' == c2 || '#' == c2 || + ('0' <= c2 && c2 <= '9'))) + return false; // :#REF!.AB42 or :#REF!42 or :#REF!#REF! + break; + default: + if ((('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) && + ((mnPredetectedReference > 1 && ':' == c2) || 0 == c2)) + return false; // AB#REF!: or AB#REF! + } + } + switch (mnPredetectedReference) + { + case 1: + return IsSingleReference( rName); + case 2: + return IsDoubleReference( rName); + } + return false; +} + + +BOOL ScCompiler::IsDoubleReference( const String& rName ) +{ + ScRange aRange( aPos, aPos ); + const ScAddress::Details aDetails( pConv->meConv, aPos ); + ScAddress::ExternalInfo aExtInfo; + USHORT nFlags = aRange.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks ); + if( nFlags & SCA_VALID ) + { + ScRawToken aToken; + ScComplexRefData aRef; + aRef.InitRange( aRange ); + aRef.Ref1.SetColRel( (nFlags & SCA_COL_ABSOLUTE) == 0 ); + aRef.Ref1.SetRowRel( (nFlags & SCA_ROW_ABSOLUTE) == 0 ); + aRef.Ref1.SetTabRel( (nFlags & SCA_TAB_ABSOLUTE) == 0 ); + if ( !(nFlags & SCA_VALID_TAB) ) + aRef.Ref1.SetTabDeleted( TRUE ); // #REF! + aRef.Ref1.SetFlag3D( ( nFlags & SCA_TAB_3D ) != 0 ); + aRef.Ref2.SetColRel( (nFlags & SCA_COL2_ABSOLUTE) == 0 ); + aRef.Ref2.SetRowRel( (nFlags & SCA_ROW2_ABSOLUTE) == 0 ); + aRef.Ref2.SetTabRel( (nFlags & SCA_TAB2_ABSOLUTE) == 0 ); + if ( !(nFlags & SCA_VALID_TAB2) ) + aRef.Ref2.SetTabDeleted( TRUE ); // #REF! + aRef.Ref2.SetFlag3D( ( nFlags & SCA_TAB2_3D ) != 0 ); + aRef.CalcRelFromAbs( aPos ); + if (aExtInfo.mbExternal) + { + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + const String* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName); + aToken.SetExternalDoubleRef( + aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef); + } + else + { + aToken.SetDoubleReference(aRef); + } + pRawToken = aToken.Clone(); + } + + return ( nFlags & SCA_VALID ) != 0; +} + + +BOOL ScCompiler::IsSingleReference( const String& rName ) +{ + ScAddress aAddr( aPos ); + const ScAddress::Details aDetails( pConv->meConv, aPos ); + ScAddress::ExternalInfo aExtInfo; + USHORT nFlags = aAddr.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks ); + // Something must be valid in order to recognize Sheet1.blah or blah.a1 + // as a (wrong) reference. + if( nFlags & ( SCA_VALID_COL|SCA_VALID_ROW|SCA_VALID_TAB ) ) + { + ScRawToken aToken; + ScSingleRefData aRef; + aRef.InitAddress( aAddr ); + aRef.SetColRel( (nFlags & SCA_COL_ABSOLUTE) == 0 ); + aRef.SetRowRel( (nFlags & SCA_ROW_ABSOLUTE) == 0 ); + aRef.SetTabRel( (nFlags & SCA_TAB_ABSOLUTE) == 0 ); + aRef.SetFlag3D( ( nFlags & SCA_TAB_3D ) != 0 ); + // the reference is really invalid + if( !( nFlags & SCA_VALID ) ) + { + if( !( nFlags & SCA_VALID_COL ) ) + aRef.nCol = MAXCOL+1; + if( !( nFlags & SCA_VALID_ROW ) ) + aRef.nRow = MAXROW+1; + if( !( nFlags & SCA_VALID_TAB ) ) + aRef.nTab = MAXTAB+3; + nFlags |= SCA_VALID; + } + aRef.CalcRelFromAbs( aPos ); + + if (aExtInfo.mbExternal) + { + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + const String* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName); + aToken.SetExternalSingleRef( + aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef); + } + else + aToken.SetSingleReference(aRef); + pRawToken = aToken.Clone(); + } + + return ( nFlags & SCA_VALID ) != 0; +} + + +BOOL ScCompiler::IsReference( const String& rName ) +{ + // Has to be called before IsValue + sal_Unicode ch1 = rName.GetChar(0); + sal_Unicode cDecSep = ( mxSymbols->isEnglish() ? '.' : + ScGlobal::pLocaleData->getNumDecimalSep().GetChar(0) ); + if ( ch1 == cDecSep ) + return FALSE; + // Who was that imbecile introducing '.' as the sheet name separator!?! + if ( CharClass::isAsciiNumeric( ch1 ) ) + { + // Numerical sheet name is valid. + // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01 + // Don't create a #REF! of values. But also do not bail out on + // something like 3:3, meaning entire row 3. + do + { + const xub_StrLen nPos = ScGlobal::FindUnquoted( rName, '.'); + if ( nPos == STRING_NOTFOUND ) + { + if (ScGlobal::FindUnquoted( rName, ':') != STRING_NOTFOUND) + break; // may be 3:3, continue as usual + return FALSE; + } + sal_Unicode const * const pTabSep = rName.GetBuffer() + nPos; + sal_Unicode ch2 = pTabSep[1]; // maybe a column identifier + if ( !(ch2 == '$' || CharClass::isAsciiAlpha( ch2 )) ) + return FALSE; + if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e') // E + - digit + && (GetCharTableFlags( pTabSep[2] ) & SC_COMPILER_C_VALUE_EXP) ) + { // #91053# + // If it is an 1.E2 expression check if "1" is an existent sheet + // name. If so, a desired value 1.E2 would have to be entered as + // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to + // require numerical sheet names always being entered quoted, which + // is not desirable (too many 1999, 2000, 2001 sheets in use). + // Furthermore, XML files created with versions prior to SRC640e + // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes() + // and would produce wrong formulas if the conditions here are met. + // If you can live with these restrictions you may remove the + // check and return an unconditional FALSE. + String aTabName( rName.Copy( 0, nPos ) ); + SCTAB nTab; + if ( !pDoc->GetTable( aTabName, nTab ) ) + return FALSE; + // If sheet "1" exists and the expression is 1.E+2 continue as + // usual, the ScRange/ScAddress parser will take care of it. + } + } while(0); + } + + if (IsSingleReference( rName)) + return true; + + // Though the range operator is handled explicitly, when encountering + // something like Sheet1.A:A we will have to treat it as one entity if it + // doesn't pass as single cell reference. + if (mnRangeOpPosInSymbol > 0) // ":foo" would be nonsense + { + if (IsDoubleReference( rName)) + return true; + // Now try with a symbol up to the range operator, rewind source + // position. + sal_Int32 nLen = mnRangeOpPosInSymbol; + while (cSymbol[++nLen]) + ; + cSymbol[mnRangeOpPosInSymbol] = 0; + nSrcPos -= static_cast<xub_StrLen>(nLen - mnRangeOpPosInSymbol); + mnRangeOpPosInSymbol = -1; + mbRewind = true; + return true; // end all checks + } + else + { + // Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel sickness, + // mnRangeOpPosInSymbol did not catch the range operator as it is + // within a quoted name. + switch (pConv->meConv) + { + case FormulaGrammar::CONV_XL_A1: + case FormulaGrammar::CONV_XL_R1C1: + case FormulaGrammar::CONV_XL_OOX: + if (rName.GetChar(0) == '\'' && IsDoubleReference( rName)) + return true; + break; + default: + ; // nothing + } + } + return false; +} + +BOOL ScCompiler::IsMacro( const String& rName ) +{ + String aName( rName); + StarBASIC* pObj = 0; + SfxObjectShell* pDocSh = pDoc->GetDocumentShell(); + + SfxApplication* pSfxApp = SFX_APP(); + pSfxApp->EnterBasicCall(); // initialize document's BASIC + + if( pDocSh )//XXX + pObj = pDocSh->GetBasic(); + else + pObj = pSfxApp->GetBasic(); + + // ODFF recommends to store user-defined functions prefixed with "USER.", + // use only unprefixed name if encountered. BASIC doesn't allow '.' in a + // function name so a function "USER.FOO" could not exist, and macro check + // is assigned the lowest priority in function name check. + if (FormulaGrammar::isODFF( GetGrammar()) && aName.EqualsIgnoreCaseAscii( "USER.", 0, 5)) + aName.Erase( 0, 5); + + SbxMethod* pMeth = (SbxMethod*) pObj->Find( aName, SbxCLASS_METHOD ); + if( !pMeth ) + { + pSfxApp->LeaveBasicCall(); + return FALSE; + } + // It really should be a BASIC function! + if( pMeth->GetType() == SbxVOID + || ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY ) + || !pMeth->ISA(SbMethod) ) + { + pSfxApp->LeaveBasicCall(); + return FALSE; + } + ScRawToken aToken; + aToken.SetExternal( aName.GetBuffer() ); + aToken.eOp = ocMacro; + pRawToken = aToken.Clone(); + pSfxApp->LeaveBasicCall(); + return TRUE; +} + +BOOL ScCompiler::IsNamedRange( const String& rUpperName ) +{ + // IsNamedRange is called only from NextNewToken, with an upper-case string + + USHORT n; + ScRangeName* pRangeName = pDoc->GetRangeName(); + if (pRangeName->SearchNameUpper( rUpperName, n ) ) + { + ScRangeData* pData = (*pRangeName)[n]; + ScRawToken aToken; + aToken.SetName( pData->GetIndex() ); + pRawToken = aToken.Clone(); + return TRUE; + } + else + return FALSE; +} + +bool ScCompiler::IsExternalNamedRange( const String& rSymbol ) +{ + /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc) + * correctly parses external named references in OOo, as required per RFE + * #i3740#, just that we can't store them in ODF yet. We will need an OASIS + * spec first. Until then don't pretend to support external names that + * wouldn't survive a save and reload cycle, return false instead. */ + +#if 0 + if (!pConv) + return false; + + String aFile, aName; + if (!pConv->parseExternalName( rSymbol, aFile, aName, pDoc, &maExternalLinks)) + return false; + + ScRawToken aToken; + if (aFile.Len() > MAXSTRLEN || aName.Len() > MAXSTRLEN) + return false; + + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + pRefMgr->convertToAbsName(aFile); + sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFile); + if (!pRefMgr->getRangeNameTokens(nFileId, aName).get()) + // range name doesn't exist in the source document. + return false; + + const String* pRealName = pRefMgr->getRealRangeName(nFileId, aName); + aToken.SetExternalName(nFileId, pRealName ? *pRealName : aName); + pRawToken = aToken.Clone(); + return true; +#else + (void)rSymbol; + return false; +#endif +} + +BOOL ScCompiler::IsDBRange( const String& rName ) +{ + USHORT n; + ScDBCollection* pDBColl = pDoc->GetDBCollection(); + if (pDBColl->SearchName( rName, n ) ) + { + ScDBData* pData = (*pDBColl)[n]; + ScRawToken aToken; + aToken.SetName( pData->GetIndex() ); + aToken.eOp = ocDBArea; + pRawToken = aToken.Clone(); + return TRUE; + } + else + return FALSE; +} + +BOOL ScCompiler::IsColRowName( const String& rName ) +{ + BOOL bInList = FALSE; + BOOL bFound = FALSE; + ScSingleRefData aRef; + String aName( rName ); + DeQuote( aName ); + SCTAB nThisTab = aPos.Tab(); + for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- ) + { // #50300# first check ranges on this sheet, in case of duplicated names + for ( short jRow=0; jRow<2 && !bInList; jRow++ ) + { + ScRangePairList* pRL; + if ( !jRow ) + pRL = pDoc->GetColNameRanges(); + else + pRL = pDoc->GetRowNameRanges(); + for ( ScRangePair* pR = pRL->First(); pR && !bInList; pR = pRL->Next() ) + { + const ScRange& rNameRange = pR->GetRange(0); + if ( jThisTab && !(rNameRange.aStart.Tab() <= nThisTab && + nThisTab <= rNameRange.aEnd.Tab()) ) + continue; // for + ScCellIterator aIter( pDoc, rNameRange ); + for ( ScBaseCell* pCell = aIter.GetFirst(); pCell && !bInList; + pCell = aIter.GetNext() ) + { + // Don't crash if cell (via CompileNameFormula) encounters + // a formula cell without code and + // HasStringData/Interpret/Compile is executed and all that + // recursive.. + // Furthermore, *this* cell won't be touched, since no RPN exists yet. + CellType eType = pCell->GetCellType(); + BOOL bOk = sal::static_int_cast<BOOL>( (eType == CELLTYPE_FORMULA ? + ((ScFormulaCell*)pCell)->GetCode()->GetCodeLen() > 0 + && ((ScFormulaCell*)pCell)->aPos != aPos // noIter + : TRUE ) ); + if ( bOk && pCell->HasStringData() ) + { + String aStr; + switch ( eType ) + { + case CELLTYPE_STRING: + ((ScStringCell*)pCell)->GetString( aStr ); + break; + case CELLTYPE_FORMULA: + ((ScFormulaCell*)pCell)->GetString( aStr ); + break; + case CELLTYPE_EDIT: + ((ScEditCell*)pCell)->GetString( aStr ); + break; + case CELLTYPE_NONE: + case CELLTYPE_VALUE: + case CELLTYPE_NOTE: + case CELLTYPE_SYMBOLS: +#if DBG_UTIL + case CELLTYPE_DESTROYED: +#endif + ; // nothing, prevent compiler warning + break; + } + if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) ) + { + aRef.InitFlags(); + aRef.nCol = aIter.GetCol(); + aRef.nRow = aIter.GetRow(); + aRef.nTab = aIter.GetTab(); + if ( !jRow ) + aRef.SetColRel( TRUE ); // ColName + else + aRef.SetRowRel( TRUE ); // RowName + aRef.CalcRelFromAbs( aPos ); + bInList = bFound = TRUE; + } + } + } + } + } + } + if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() ) + { // search in current sheet + long nDistance = 0, nMax = 0; + long nMyCol = (long) aPos.Col(); + long nMyRow = (long) aPos.Row(); + BOOL bTwo = FALSE; + ScAddress aOne( 0, 0, aPos.Tab() ); + ScAddress aTwo( MAXCOL, MAXROW, aPos.Tab() ); + + ScAutoNameCache* pNameCache = pDoc->GetAutoNameCache(); + if ( pNameCache ) + { + // #b6355215# use GetNameOccurences to collect all positions of aName on the sheet + // (only once), similar to the outer part of the loop in the "else" branch. + + const ScAutoNameAddresses& rAddresses = pNameCache->GetNameOccurences( aName, aPos.Tab() ); + + // Loop through the found positions, similar to the inner part of the loop in the "else" branch. + // The order of addresses in the vector is the same as from ScCellIterator. + + ScAutoNameAddresses::const_iterator aEnd(rAddresses.end()); + for ( ScAutoNameAddresses::const_iterator aAdrIter(rAddresses.begin()); aAdrIter != aEnd; ++aAdrIter ) + { + ScAddress aAddress( *aAdrIter ); // cell address with an equal string + + if ( bFound ) + { // stop if everything else is further away + if ( nMax < (long)aAddress.Col() ) + break; // aIter + } + if ( aAddress != aPos ) + { + // same treatment as in isEqual case below + + SCCOL nCol = aAddress.Col(); + SCROW nRow = aAddress.Row(); + long nC = nMyCol - nCol; + long nR = nMyRow - nRow; + if ( bFound ) + { + long nD = nC * nC + nR * nR; + if ( nD < nDistance ) + { + if ( nC < 0 || nR < 0 ) + { // right or below + bTwo = TRUE; + aTwo.Set( nCol, nRow, aAddress.Tab() ); + nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) ); + nDistance = nD; + } + else if ( !(nRow < aOne.Row() && nMyRow >= (long)aOne.Row()) ) + { + // upper left, only if not further up than the + // current entry and nMyRow is below (CellIter + // runs column-wise) + bTwo = FALSE; + aOne.Set( nCol, nRow, aAddress.Tab() ); + nMax = Max( nMyCol + nC, nMyRow + nR ); + nDistance = nD; + } + } + } + else + { + aOne.Set( nCol, nRow, aAddress.Tab() ); + nDistance = nC * nC + nR * nR; + nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) ); + } + bFound = TRUE; + } + } + } + else + { + ScCellIterator aIter( pDoc, ScRange( aOne, aTwo ) ); + for ( ScBaseCell* pCell = aIter.GetFirst(); pCell; pCell = aIter.GetNext() ) + { + if ( bFound ) + { // stop if everything else is further away + if ( nMax < (long)aIter.GetCol() ) + break; // aIter + } + CellType eType = pCell->GetCellType(); + BOOL bOk = sal::static_int_cast<BOOL>( (eType == CELLTYPE_FORMULA ? + ((ScFormulaCell*)pCell)->GetCode()->GetCodeLen() > 0 + && ((ScFormulaCell*)pCell)->aPos != aPos // noIter + : TRUE ) ); + if ( bOk && pCell->HasStringData() ) + { + String aStr; + switch ( eType ) + { + case CELLTYPE_STRING: + ((ScStringCell*)pCell)->GetString( aStr ); + break; + case CELLTYPE_FORMULA: + ((ScFormulaCell*)pCell)->GetString( aStr ); + break; + case CELLTYPE_EDIT: + ((ScEditCell*)pCell)->GetString( aStr ); + break; + case CELLTYPE_NONE: + case CELLTYPE_VALUE: + case CELLTYPE_NOTE: + case CELLTYPE_SYMBOLS: +#if DBG_UTIL + case CELLTYPE_DESTROYED: +#endif + ; // nothing, prevent compiler warning + break; + } + if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) ) + { + SCCOL nCol = aIter.GetCol(); + SCROW nRow = aIter.GetRow(); + long nC = nMyCol - nCol; + long nR = nMyRow - nRow; + if ( bFound ) + { + long nD = nC * nC + nR * nR; + if ( nD < nDistance ) + { + if ( nC < 0 || nR < 0 ) + { // right or below + bTwo = TRUE; + aTwo.Set( nCol, nRow, aIter.GetTab() ); + nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) ); + nDistance = nD; + } + else if ( !(nRow < aOne.Row() && nMyRow >= (long)aOne.Row()) ) + { + // upper left, only if not further up than the + // current entry and nMyRow is below (CellIter + // runs column-wise) + bTwo = FALSE; + aOne.Set( nCol, nRow, aIter.GetTab() ); + nMax = Max( nMyCol + nC, nMyRow + nR ); + nDistance = nD; + } + } + } + else + { + aOne.Set( nCol, nRow, aIter.GetTab() ); + nDistance = nC * nC + nR * nR; + nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) ); + } + bFound = TRUE; + } + } + } + } + + if ( bFound ) + { + ScAddress aAdr; + if ( bTwo ) + { + if ( nMyCol >= (long)aOne.Col() && nMyRow >= (long)aOne.Row() ) + aAdr = aOne; // upper left takes precedence + else + { + if ( nMyCol < (long)aOne.Col() ) + { // two to the right + if ( nMyRow >= (long)aTwo.Row() ) + aAdr = aTwo; // directly right + else + aAdr = aOne; + } + else + { // two below or below and right, take the nearest + long nC1 = nMyCol - aOne.Col(); + long nR1 = nMyRow - aOne.Row(); + long nC2 = nMyCol - aTwo.Col(); + long nR2 = nMyRow - aTwo.Row(); + if ( nC1 * nC1 + nR1 * nR1 <= nC2 * nC2 + nR2 * nR2 ) + aAdr = aOne; + else + aAdr = aTwo; + } + } + } + else + aAdr = aOne; + aRef.InitAddress( aAdr ); + if ( (aRef.nRow != MAXROW && pDoc->HasStringData( + aRef.nCol, aRef.nRow + 1, aRef.nTab )) + || (aRef.nRow != 0 && pDoc->HasStringData( + aRef.nCol, aRef.nRow - 1, aRef.nTab )) ) + aRef.SetRowRel( TRUE ); // RowName + else + aRef.SetColRel( TRUE ); // ColName + aRef.CalcRelFromAbs( aPos ); + } + } + if ( bFound ) + { + ScRawToken aToken; + aToken.SetSingleReference( aRef ); + aToken.eOp = ocColRowName; + pRawToken = aToken.Clone(); + return TRUE; + } + else + return FALSE; +} + +BOOL ScCompiler::IsBoolean( const String& rName ) +{ + OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName ) ); + if( iLook != mxSymbols->getHashMap()->end() && + ((*iLook).second == ocTrue || + (*iLook).second == ocFalse) ) + { + ScRawToken aToken; + aToken.SetOpCode( (*iLook).second ); + pRawToken = aToken.Clone(); + return TRUE; + } + else + return FALSE; +} + +//--------------------------------------------------------------------------- + +void ScCompiler::AutoCorrectParsedSymbol() +{ + xub_StrLen nPos = aCorrectedSymbol.Len(); + if ( nPos ) + { + nPos--; + const sal_Unicode cQuote = '\"'; + const sal_Unicode cx = 'x'; + const sal_Unicode cX = 'X'; + sal_Unicode c1 = aCorrectedSymbol.GetChar( 0 ); + sal_Unicode c2 = aCorrectedSymbol.GetChar( nPos ); + if ( c1 == cQuote && c2 != cQuote ) + { // "... + // What's not a word doesn't belong to it. + // Don't be pedantic: c < 128 should be sufficient here. + while ( nPos && ((aCorrectedSymbol.GetChar(nPos) < 128) && + ((GetCharTableFlags( aCorrectedSymbol.GetChar(nPos) ) & + (SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_DONTCARE)) == 0)) ) + nPos--; + if ( nPos == MAXSTRLEN - 2 ) + aCorrectedSymbol.SetChar( nPos, cQuote ); // '"' the 255th character + else + aCorrectedSymbol.Insert( cQuote, nPos + 1 ); + bCorrected = TRUE; + } + else if ( c1 != cQuote && c2 == cQuote ) + { // ..." + aCorrectedSymbol.Insert( cQuote, 0 ); + bCorrected = TRUE; + } + else if ( nPos == 0 && (c1 == cx || c1 == cX) ) + { // x => * + aCorrectedSymbol = mxSymbols->getSymbol(ocMul); + bCorrected = TRUE; + } + else if ( (GetCharTableFlags( c1 ) & SC_COMPILER_C_CHAR_VALUE) + && (GetCharTableFlags( c2 ) & SC_COMPILER_C_CHAR_VALUE) ) + { + xub_StrLen nXcount; + if ( (nXcount = aCorrectedSymbol.GetTokenCount( cx )) > 1 ) + { // x => * + xub_StrLen nIndex = 0; + sal_Unicode c = mxSymbols->getSymbol(ocMul).GetChar(0); + while ( (nIndex = aCorrectedSymbol.SearchAndReplace( + cx, c, nIndex )) != STRING_NOTFOUND ) + nIndex++; + bCorrected = TRUE; + } + if ( (nXcount = aCorrectedSymbol.GetTokenCount( cX )) > 1 ) + { // X => * + xub_StrLen nIndex = 0; + sal_Unicode c = mxSymbols->getSymbol(ocMul).GetChar(0); + while ( (nIndex = aCorrectedSymbol.SearchAndReplace( + cX, c, nIndex )) != STRING_NOTFOUND ) + nIndex++; + bCorrected = TRUE; + } + } + else + { + String aSymbol( aCorrectedSymbol ); + String aDoc; + xub_StrLen nPosition; + if ( aSymbol.GetChar(0) == '\'' + && ((nPosition = aSymbol.SearchAscii( "'#" )) != STRING_NOTFOUND) ) + { // Split off 'Doc'#, may be d:\... or whatever + aDoc = aSymbol.Copy( 0, nPosition + 2 ); + aSymbol.Erase( 0, nPosition + 2 ); + } + xub_StrLen nRefs = aSymbol.GetTokenCount( ':' ); + BOOL bColons; + if ( nRefs > 2 ) + { // duplicated or too many ':'? B:2::C10 => B2:C10 + bColons = TRUE; + xub_StrLen nIndex = 0; + String aTmp1( aSymbol.GetToken( 0, ':', nIndex ) ); + xub_StrLen nLen1 = aTmp1.Len(); + String aSym, aTmp2; + BOOL bLastAlp, bNextNum; + bLastAlp = bNextNum = TRUE; + xub_StrLen nStrip = 0; + xub_StrLen nCount = nRefs; + for ( xub_StrLen j=1; j<nCount; j++ ) + { + aTmp2 = aSymbol.GetToken( 0, ':', nIndex ); + xub_StrLen nLen2 = aTmp2.Len(); + if ( nLen1 || nLen2 ) + { + if ( nLen1 ) + { + aSym += aTmp1; + bLastAlp = CharClass::isAsciiAlpha( aTmp1 ); + } + if ( nLen2 ) + { + bNextNum = CharClass::isAsciiNumeric( aTmp2 ); + if ( bLastAlp == bNextNum && nStrip < 1 ) + { + // Must be alternating number/string, only + // strip within a reference. + nRefs--; + nStrip++; + } + else + { + xub_StrLen nSymLen = aSym.Len(); + if ( nSymLen + && (aSym.GetChar( nSymLen - 1 ) != ':') ) + aSym += ':'; + nStrip = 0; + } + bLastAlp = !bNextNum; + } + else + { // :: + nRefs--; + if ( nLen1 ) + { // B10::C10 ? append ':' on next round + if ( !bLastAlp && !CharClass::isAsciiNumeric( aTmp1 ) ) + nStrip++; + } + bNextNum = !bLastAlp; + } + aTmp1 = aTmp2; + nLen1 = nLen2; + } + else + nRefs--; + } + aSymbol = aSym; + aSymbol += aTmp1; + } + else + bColons = FALSE; + if ( nRefs && nRefs <= 2 ) + { // reference twisted? 4A => A4 etc. + String aTab[2], aRef[2]; + const ScAddress::Details aDetails( pConv->meConv, aPos ); + if ( nRefs == 2 ) + { + aRef[0] = aSymbol.GetToken( 0, ':' ); + aRef[1] = aSymbol.GetToken( 1, ':' ); + } + else + aRef[0] = aSymbol; + + BOOL bChanged = FALSE; + BOOL bOk = TRUE; + USHORT nMask = SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW; + for ( int j=0; j<nRefs; j++ ) + { + xub_StrLen nTmp = 0; + xub_StrLen nDotPos = STRING_NOTFOUND; + while ( (nTmp = aRef[j].Search( '.', nTmp )) != STRING_NOTFOUND ) + nDotPos = nTmp++; // the last one counts + if ( nDotPos != STRING_NOTFOUND ) + { + aTab[j] = aRef[j].Copy( 0, nDotPos + 1 ); // with '.' + aRef[j].Erase( 0, nDotPos + 1 ); + } + String aOld( aRef[j] ); + String aStr2; + const sal_Unicode* p = aRef[j].GetBuffer(); + while ( *p && CharClass::isAsciiNumeric( *p ) ) + aStr2 += *p++; + aRef[j] = String( p ); + aRef[j] += aStr2; + if ( bColons || aRef[j] != aOld ) + { + bChanged = TRUE; + ScAddress aAdr; + bOk &= ((aAdr.Parse( aRef[j], pDoc, aDetails ) & nMask) == nMask); + } + } + if ( bChanged && bOk ) + { + aCorrectedSymbol = aDoc; + aCorrectedSymbol += aTab[0]; + aCorrectedSymbol += aRef[0]; + if ( nRefs == 2 ) + { + aCorrectedSymbol += ':'; + aCorrectedSymbol += aTab[1]; + aCorrectedSymbol += aRef[1]; + } + bCorrected = TRUE; + } + } + } + } +} + +inline bool lcl_UpperAsciiOrI18n( String& rUpper, const String& rOrg, FormulaGrammar::Grammar eGrammar ) +{ + if (FormulaGrammar::isODFF( eGrammar )) + { + // ODFF has a defined set of English function names, avoid i18n + // overhead. + rUpper = rOrg; + rUpper.ToUpperAscii(); + return true; + } + else + { + rUpper = ScGlobal::pCharClass->upper( rOrg ); + return false; + } +} + +BOOL ScCompiler::NextNewToken( bool bInArray ) +{ + bool bAllowBooleans = bInArray; + xub_StrLen nSpaces = NextSymbol(bInArray); + +#if 0 + fprintf( stderr, "NextNewToken '%s' (spaces = %d)\n", + rtl::OUStringToOString( cSymbol, RTL_TEXTENCODING_UTF8 ).getStr(), nSpaces ); +#endif + + if (!cSymbol[0]) + return false; + + if( nSpaces ) + { + ScRawToken aToken; + aToken.SetOpCode( ocSpaces ); + aToken.sbyte.cByte = (BYTE) ( nSpaces > 255 ? 255 : nSpaces ); + if( !static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ) ) + { + SetError(errCodeOverflow); + return false; + } + } + + // Short cut for references when reading ODF to speedup things. + if (mnPredetectedReference) + { + String aStr( cSymbol); + if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr)) + { + /* TODO: it would be nice to generate a #REF! error here, which + * would need an ocBad token with additional error value. + * FormulaErrorToken wouldn't do because we want to preserve the + * original string containing partial valid address + * information. */ + ScRawToken aToken; + aToken.SetString( aStr.GetBuffer() ); + aToken.NewOpCode( ocBad ); + pRawToken = aToken.Clone(); + } + return true; + } + + if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 && + !bAutoCorrect ) + { // #101100# special case to speed up broken [$]#REF documents + /* FIXME: ISERROR(#REF!) would be valid and TRUE and the formula to + * be processed as usual. That would need some special treatment, + * also in NextSymbol() because of possible combinations of + * #REF!.#REF!#REF! parts. In case of reading ODF that is all + * handled by IsPredetectedReference(), this case here remains for + * manual/API input. */ + String aBad( aFormula.Copy( nSrcPos-1 ) ); + eLastOp = pArr->AddBad( aBad )->GetOpCode(); + return false; + } + + if( IsString() ) + return true; + + bool bMayBeFuncName; + bool bAsciiNonAlnum; // operators, separators, ... + if ( cSymbol[0] < 128 ) + { + bMayBeFuncName = CharClass::isAsciiAlpha( cSymbol[0] ); + bAsciiNonAlnum = !bMayBeFuncName && !CharClass::isAsciiDigit( cSymbol[0] ); + } + else + { + String aTmpStr( cSymbol[0] ); + bMayBeFuncName = ScGlobal::pCharClass->isLetter( aTmpStr, 0 ); + bAsciiNonAlnum = false; + } + if ( bMayBeFuncName ) + { + // a function name must be followed by a parenthesis + const sal_Unicode* p = aFormula.GetBuffer() + nSrcPos; + while( *p == ' ' ) + p++; + bMayBeFuncName = ( *p == '(' ); + } + +#if 0 + fprintf( stderr, "Token '%s'\n", + rtl::OUStringToOString( aUpper, RTL_TEXTENCODING_UTF8 ).getStr() ); +#endif + + // #42016# Italian ARCTAN.2 resulted in #REF! => IsOpcode() before + // IsReference(). + + String aUpper; + + do + { + mbRewind = false; + const String aOrg( cSymbol ); + + if (bAsciiNonAlnum && IsOpCode( aOrg, bInArray )) + return true; + + aUpper.Erase(); + bool bAsciiUpper = false; + if (bMayBeFuncName) + { + bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar); + if (IsOpCode( aUpper, bInArray )) + return true; + } + + // Column 'DM' ("Deutsche Mark", German currency) couldn't be + // referred => IsReference() before IsValue(). + // Preserve case of file names in external references. + if (IsReference( aOrg )) + { + if (mbRewind) // Range operator, but no direct reference. + continue; // do; up to range operator. + return true; + } + + if (!aUpper.Len()) + bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar); + + // IsBoolean() before IsValue() to catch inline bools without the kludge + // for inline arrays. + if (bAllowBooleans && IsBoolean( aUpper )) + return true; + + if (IsValue( aUpper )) + return true; + + // User defined names and such do need i18n upper also in ODF. + if (bAsciiUpper) + aUpper = ScGlobal::pCharClass->upper( aOrg ); + + if (IsNamedRange( aUpper )) + return true; + // Preserve case of file names in external references. + if (IsExternalNamedRange( aOrg )) + return true; + if (IsDBRange( aUpper )) + return true; + if (IsColRowName( aUpper )) + return true; + if (bMayBeFuncName && IsMacro( aUpper )) + return true; + if (bMayBeFuncName && IsOpCode2( aUpper )) + return true; + + } while (mbRewind); + + if ( mbExtendedErrorDetection ) + { + // set an error and end compilation + SetError( errNoName ); + return false; + } + + // Provide single token information and continue. Do not set an error, that + // would prematurely end compilation. Simple unknown names are handled by + // the interpreter. + ScGlobal::pCharClass->toLower( aUpper ); + ScRawToken aToken; + aToken.SetString( aUpper.GetBuffer() ); + aToken.NewOpCode( ocBad ); + pRawToken = aToken.Clone(); + if ( bAutoCorrect ) + AutoCorrectParsedSymbol(); + return true; +} + +void ScCompiler::CreateStringFromXMLTokenArray( String& rFormula, String& rFormulaNmsp ) +{ + bool bExternal = GetGrammar() == FormulaGrammar::GRAM_EXTERNAL; + USHORT nExpectedCount = bExternal ? 2 : 1; + DBG_ASSERT( pArr->GetLen() == nExpectedCount, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" ); + if( pArr->GetLen() == nExpectedCount ) + { + FormulaToken** ppTokens = pArr->GetArray(); + // string tokens expected, GetString() will assert if token type is wrong + rFormula = ppTokens[ 0 ]->GetString(); + if( bExternal ) + rFormulaNmsp = ppTokens[ 1 ]->GetString(); + } +} + +ScTokenArray* ScCompiler::CompileString( const String& rFormula ) +{ +#if 0 + fprintf( stderr, "CompileString '%s'\n", + rtl::OUStringToOString( rFormula, RTL_TEXTENCODING_UTF8 ).getStr() ); +#endif + + OSL_ENSURE( meGrammar != FormulaGrammar::GRAM_EXTERNAL, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" ); + if( meGrammar == FormulaGrammar::GRAM_EXTERNAL ) + SetGrammar( FormulaGrammar::GRAM_PODF ); + + ScTokenArray aArr; + pArr = &aArr; + aFormula = rFormula; + + aFormula.EraseLeadingChars(); + aFormula.EraseTrailingChars(); + nSrcPos = 0; + bCorrected = FALSE; + if ( bAutoCorrect ) + { + aCorrectedFormula.Erase(); + aCorrectedSymbol.Erase(); + } + BYTE nForced = 0; // ==formula forces recalc even if cell is not visible + if( aFormula.GetChar(nSrcPos) == '=' ) + { + nSrcPos++; + nForced++; + if ( bAutoCorrect ) + aCorrectedFormula += '='; + } + if( aFormula.GetChar(nSrcPos) == '=' ) + { + nSrcPos++; + nForced++; + if ( bAutoCorrect ) + aCorrectedFormula += '='; + } + struct FunctionStack + { + OpCode eOp; + short nPar; + }; + // FunctionStack only used if PODF! + bool bPODF = FormulaGrammar::isPODF( meGrammar); + const size_t nAlloc = 512; + FunctionStack aFuncs[ nAlloc ]; + FunctionStack* pFunctionStack = (bPODF && rFormula.Len() > nAlloc ? + new FunctionStack[ rFormula.Len() ] : &aFuncs[0]); + pFunctionStack[0].eOp = ocNone; + pFunctionStack[0].nPar = 0; + size_t nFunction = 0; + short nBrackets = 0; + bool bInArray = false; + eLastOp = ocOpen; + while( NextNewToken( bInArray ) ) + { + const OpCode eOp = pRawToken->GetOpCode(); + switch (eOp) + { + case ocOpen: + { + ++nBrackets; + if (bPODF) + { + ++nFunction; + pFunctionStack[ nFunction ].eOp = eLastOp; + pFunctionStack[ nFunction ].nPar = 0; + } + } + break; + case ocClose: + { + if( !nBrackets ) + { + SetError( errPairExpected ); + if ( bAutoCorrect ) + { + bCorrected = TRUE; + aCorrectedSymbol.Erase(); + } + } + else + nBrackets--; + if (bPODF && nFunction) + --nFunction; + } + break; + case ocSep: + { + if (bPODF) + ++pFunctionStack[ nFunction ].nPar; + } + break; + case ocArrayOpen: + { + if( bInArray ) + SetError( errNestedArray ); + else + bInArray = true; + // Don't count following column separator as parameter separator. + if (bPODF) + { + ++nFunction; + pFunctionStack[ nFunction ].eOp = eOp; + pFunctionStack[ nFunction ].nPar = 0; + } + } + break; + case ocArrayClose: + { + if( bInArray ) + { + bInArray = false; + } + else + { + SetError( errPairExpected ); + if ( bAutoCorrect ) + { + bCorrected = TRUE; + aCorrectedSymbol.Erase(); + } + } + if (bPODF && nFunction) + --nFunction; + } + default: + break; + } + if( (eLastOp == ocSep || + eLastOp == ocArrayRowSep || + eLastOp == ocArrayColSep || + eLastOp == ocArrayOpen) && + (eOp == ocSep || + eOp == ocArrayRowSep || + eOp == ocArrayColSep || + eOp == ocArrayClose) ) + { + // FIXME: should we check for known functions with optional empty + // args so the correction dialog can do better? + if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaMissingToken ) ) + { + SetError(errCodeOverflow); break; + } + } + if (bPODF) + { + /* TODO: for now this is the only PODF adapter. If there were more, + * factor this out. */ + // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th. + if (eOp == ocSep && + pFunctionStack[ nFunction ].eOp == ocAddress && + pFunctionStack[ nFunction ].nPar == 3) + { + if (!static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep,ocSep)) || + !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0))) + { + SetError(errCodeOverflow); break; + } + ++pFunctionStack[ nFunction ].nPar; + } + } + FormulaToken* pNewToken = static_cast<ScTokenArray*>(pArr)->Add( pRawToken->CreateToken()); + if (!pNewToken) + { + SetError(errCodeOverflow); break; + } + else if (eLastOp == ocRange && pNewToken->GetOpCode() == ocPush && + pNewToken->GetType() == svSingleRef) + static_cast<ScTokenArray*>(pArr)->MergeRangeReference( aPos); + eLastOp = pRawToken->GetOpCode(); + if ( bAutoCorrect ) + aCorrectedFormula += aCorrectedSymbol; + } + if ( mbCloseBrackets ) + { + if( bInArray ) + { + FormulaByteToken aToken( ocArrayClose ); + if( !pArr->AddToken( aToken ) ) + { + SetError(errCodeOverflow); + } + else if ( bAutoCorrect ) + aCorrectedFormula += mxSymbols->getSymbol(ocArrayClose); + } + + FormulaByteToken aToken( ocClose ); + while( nBrackets-- ) + { + if( !pArr->AddToken( aToken ) ) + { + SetError(errCodeOverflow); break; + } + if ( bAutoCorrect ) + aCorrectedFormula += mxSymbols->getSymbol(ocClose); + } + } + if ( nForced >= 2 ) + pArr->SetRecalcModeForced(); + + if (pFunctionStack != &aFuncs[0]) + delete [] pFunctionStack; + + // remember pArr, in case a subsequent CompileTokenArray() is executed. + ScTokenArray* pNew = new ScTokenArray( aArr ); + pArr = pNew; + return pNew; +} + + +ScTokenArray* ScCompiler::CompileString( const String& rFormula, const String& rFormulaNmsp ) +{ + DBG_ASSERT( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL) || (rFormulaNmsp.Len() == 0), + "ScCompiler::CompileString - unexpected formula namespace for internal grammar" ); + if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL ) try + { + ScFormulaParserPool& rParserPool = pDoc->GetFormulaParserPool(); + uno::Reference< sheet::XFormulaParser > xParser( rParserPool.getFormulaParser( rFormulaNmsp ), uno::UNO_SET_THROW ); + table::CellAddress aReferencePos; + ScUnoConversion::FillApiAddress( aReferencePos, aPos ); + uno::Sequence< sheet::FormulaToken > aTokenSeq = xParser->parseFormula( rFormula, aReferencePos ); + ScTokenArray aTokenArray; + if( ScTokenConversion::ConvertToTokenArray( *pDoc, aTokenArray, aTokenSeq ) ) + { + // remember pArr, in case a subsequent CompileTokenArray() is executed. + ScTokenArray* pNew = new ScTokenArray( aTokenArray ); + pArr = pNew; + return pNew; + } + } + catch( uno::Exception& ) + { + } + // no success - fallback to some internal grammar and hope the best + return CompileString( rFormula ); +} + + +BOOL ScCompiler::HandleRange() +{ + ScRangeData* pRangeData = pDoc->GetRangeName()->FindIndex( pToken->GetIndex() ); + if (pRangeData) + { + USHORT nErr = pRangeData->GetErrCode(); + if( nErr ) + SetError( errNoName ); + else if ( !bCompileForFAP ) + { + ScTokenArray* pNew; + // #35168# put named formula into parentheses. + // #37680# But only if there aren't any yet, parenthetical + // ocSep doesn't work, e.g. SUM((...;...)) + // or if not directly between ocSep/parenthesis, + // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes, + // in short: if it isn't a self-contained expression. + FormulaToken* p1 = pArr->PeekPrevNoSpaces(); + FormulaToken* p2 = pArr->PeekNextNoSpaces(); + OpCode eOp1 = (p1 ? p1->GetOpCode() : static_cast<OpCode>( ocSep ) ); + OpCode eOp2 = (p2 ? p2->GetOpCode() : static_cast<OpCode>( ocSep ) ); + BOOL bBorder1 = (eOp1 == ocSep || eOp1 == ocOpen); + BOOL bBorder2 = (eOp2 == ocSep || eOp2 == ocClose); + BOOL bAddPair = !(bBorder1 && bBorder2); + if ( bAddPair ) + { + pNew = new ScTokenArray(); + pNew->AddOpCode( ocClose ); + PushTokenArray( pNew, TRUE ); + pNew->Reset(); + } + pNew = pRangeData->GetCode()->Clone(); + PushTokenArray( pNew, TRUE ); + if( pRangeData->HasReferences() ) + { + SetRelNameReference(); + MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); + } + pNew->Reset(); + if ( bAddPair ) + { + pNew = new ScTokenArray(); + pNew->AddOpCode( ocOpen ); + PushTokenArray( pNew, TRUE ); + pNew->Reset(); + } + return GetToken(); + } + } + else + SetError(errNoName); + return TRUE; +} +// ----------------------------------------------------------------------------- +BOOL ScCompiler::HandleExternalReference(const FormulaToken& _aToken) +{ + // Handle external range names. + switch (_aToken.GetType()) + { + case svExternalSingleRef: + case svExternalDoubleRef: + pArr->IncrementRefs(); + break; + case svExternalName: + { + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + const String* pFile = pRefMgr->getExternalFileName(_aToken.GetIndex()); + if (!pFile) + { + SetError(errNoName); + return true; + } + + const String& rName = _aToken.GetString(); + ScExternalRefCache::TokenArrayRef xNew = pRefMgr->getRangeNameTokens( + _aToken.GetIndex(), rName, &aPos); + + if (!xNew) + { + SetError(errNoName); + return true; + } + + ScTokenArray* pNew = xNew->Clone(); + PushTokenArray( pNew, true); + if (pNew->GetNextReference() != NULL) + { + SetRelNameReference(); + MoveRelWrap(MAXCOL, MAXROW); + } + pNew->Reset(); + return GetToken(); + } + default: + DBG_ERROR("Wrong type for external reference!"); + return FALSE; + } + return TRUE; +} + + +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// Append token to RPN code +//--------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// RPN creation by recursion +//--------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- + +BOOL ScCompiler::HasModifiedRange() +{ + pArr->Reset(); + for ( FormulaToken* t = pArr->Next(); t; t = pArr->Next() ) + { + OpCode eOpCode = t->GetOpCode(); + if ( eOpCode == ocName ) + { + ScRangeData* pRangeData = pDoc->GetRangeName()->FindIndex(t->GetIndex()); + + if (pRangeData && pRangeData->IsModified()) + return TRUE; + } + else if ( eOpCode == ocDBArea ) + { + ScDBData* pDBData = pDoc->GetDBCollection()->FindIndex(t->GetIndex()); + + if (pDBData && pDBData->IsModified()) + return TRUE; + } + } + return FALSE; +} + + +//--------------------------------------------------------------------------- + +template< typename T, typename S > +S lcl_adjval( S& n, T pos, T max, BOOL bRel ) +{ + max++; + if( bRel ) + n = sal::static_int_cast<S>( n + pos ); + if( n < 0 ) + n = sal::static_int_cast<S>( n + max ); + else if( n >= max ) + n = sal::static_int_cast<S>( n - max ); + if( bRel ) + n = sal::static_int_cast<S>( n - pos ); + return n; +} + +// reference of named range with relative references + +void ScCompiler::SetRelNameReference() +{ + pArr->Reset(); + for( ScToken* t = static_cast<ScToken*>(pArr->GetNextReference()); t; + t = static_cast<ScToken*>(pArr->GetNextReference()) ) + { + ScSingleRefData& rRef1 = t->GetSingleRef(); + if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() ) + rRef1.SetRelName( TRUE ); + if ( t->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; + if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() ) + rRef2.SetRelName( TRUE ); + } + } +} + +// Wrap-adjust relative references of a RangeName to current position, +// don't call for other token arrays! +void ScCompiler::MoveRelWrap( SCCOL nMaxCol, SCROW nMaxRow ) +{ + pArr->Reset(); + for( ScToken* t = static_cast<ScToken*>(pArr->GetNextReference()); t; + t = static_cast<ScToken*>(pArr->GetNextReference()) ) + { + if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef ) + ScRefUpdate::MoveRelWrap( pDoc, aPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( t->GetSingleRef() ).Ref() ); + else + ScRefUpdate::MoveRelWrap( pDoc, aPos, nMaxCol, nMaxRow, t->GetDoubleRef() ); + } +} + +// static +// Wrap-adjust relative references of a RangeName to current position, +// don't call for other token arrays! +void ScCompiler::MoveRelWrap( ScTokenArray& rArr, ScDocument* pDoc, const ScAddress& rPos, + SCCOL nMaxCol, SCROW nMaxRow ) +{ + rArr.Reset(); + for( ScToken* t = static_cast<ScToken*>(rArr.GetNextReference()); t; + t = static_cast<ScToken*>(rArr.GetNextReference()) ) + { + if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef ) + ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( t->GetSingleRef() ).Ref() ); + else + ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, t->GetDoubleRef() ); + } +} + +ScRangeData* ScCompiler::UpdateReference(UpdateRefMode eUpdateRefMode, + const ScAddress& rOldPos, const ScRange& r, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz, + BOOL& rChanged, BOOL& rRefSizeChanged ) +{ + rChanged = rRefSizeChanged = FALSE; + if ( eUpdateRefMode == URM_COPY ) + { // Normally nothing has to be done here since RelRefs are used, also + // SharedFormulas don't need any special handling, except if they + // wrapped around sheet borders. + // #67383# But ColRowName tokens pointing to a ColRow header which was + // copied along with this formula need to be updated to point to the + // copied header instead of the old position's new intersection. + ScToken* t; + pArr->Reset(); + while( (t = static_cast<ScToken*>(pArr->GetNextColRowName())) != NULL ) + { + ScSingleRefData& rRef = t->GetSingleRef(); + rRef.CalcAbsIfRel( rOldPos ); + ScAddress aNewRef( rRef.nCol + nDx, rRef.nRow + nDy, rRef.nTab + nDz ); + if ( r.In( aNewRef ) ) + { // yes, this is URM_MOVE + if ( ScRefUpdate::Update( pDoc, URM_MOVE, aPos, + r, nDx, nDy, nDz, + SingleDoubleRefModifier( rRef ).Ref() ) + != UR_NOTHING + ) + rChanged = TRUE; + } + } + // Check for SharedFormulas. + ScRangeData* pRangeData = NULL; + pArr->Reset(); + for( FormulaToken* j = pArr->GetNextName(); j && !pRangeData; + j = pArr->GetNextName() ) + { + if( j->GetOpCode() == ocName ) + { + ScRangeData* pName = pDoc->GetRangeName()->FindIndex( j->GetIndex() ); + if (pName && pName->HasType(RT_SHARED)) + pRangeData = pName; + } + } + // Check SharedFormulas for wraps. + if (pRangeData) + { + ScRangeData* pName = pRangeData; + pRangeData = NULL; + pArr->Reset(); + for( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()); t && !pRangeData; + t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) + { + BOOL bRelName = (t->GetType() == svSingleRef ? + t->GetSingleRef().IsRelName() : + (t->GetDoubleRef().Ref1.IsRelName() || + t->GetDoubleRef().Ref2.IsRelName())); + if (bRelName) + { + t->CalcAbsIfRel( rOldPos); + BOOL bValid = (t->GetType() == svSingleRef ? + t->GetSingleRef().Valid() : + t->GetDoubleRef().Valid()); + // If the reference isn't valid, copying the formula + // wrapped it. Replace SharedFormula. + if (!bValid) + { + pRangeData = pName; + rChanged = TRUE; + } + } + } + } + return pRangeData; + } + else + { +/* + * Set SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE to 1 if we wanted to preserve as + * many shared formulas as possible instead of replacing them with direct code. + * Note that this may produce shared formula usage Excel doesn't understand, + * which would have to be adapted for in the export filter. Advisable as a long + * term goal, since it could decrease memory footprint. + */ +#define SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE 0 + ScRangeData* pRangeData = NULL; + ScToken* t; + pArr->Reset(); + while( (t = static_cast<ScToken*>(pArr->GetNextReferenceOrName())) != NULL ) + { + if( t->GetOpCode() == ocName ) + { + ScRangeData* pName = pDoc->GetRangeName()->FindIndex( t->GetIndex() ); + if (pName && pName->HasType(RT_SHAREDMOD)) + { + pRangeData = pName; // maybe need a replacement of shared with own code +#if ! SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE + rChanged = TRUE; +#endif + } + } + else if( t->GetType() != svIndex ) // it may be a DB area!!! + { + t->CalcAbsIfRel( rOldPos ); + switch (t->GetType()) + { + case svExternalSingleRef: + case svExternalDoubleRef: + // External references never change their positioning + // nor point to parts that will be removed or expanded. + // In fact, calling ScRefUpdate::Update() for URM_MOVE + // may have negative side effects. Simply adapt + // relative references to the new position. + t->CalcRelFromAbs( aPos); + break; + case svSingleRef: + { + if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, + aPos, r, nDx, nDy, nDz, + SingleDoubleRefModifier( + t->GetSingleRef()).Ref()) + != UR_NOTHING) + rChanged = TRUE; + } + break; + default: + { + ScComplexRefData& rRef = t->GetDoubleRef(); + SCCOL nCols = rRef.Ref2.nCol - rRef.Ref1.nCol; + SCROW nRows = rRef.Ref2.nRow - rRef.Ref1.nRow; + SCTAB nTabs = rRef.Ref2.nTab - rRef.Ref1.nTab; + if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, + aPos, r, nDx, nDy, nDz, + t->GetDoubleRef()) != UR_NOTHING) + { + rChanged = TRUE; + if (rRef.Ref2.nCol - rRef.Ref1.nCol != nCols || + rRef.Ref2.nRow - rRef.Ref1.nRow != nRows || + rRef.Ref2.nTab - rRef.Ref1.nTab != nTabs) + rRefSizeChanged = TRUE; + } + } + } + } + } +#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE + BOOL bEasyShared, bPosInRange; + if ( !pRangeData ) + bEasyShared = bPosInRange = FALSE; + else + { + bEasyShared = TRUE; + bPosInRange = r.In( eUpdateRefMode == URM_MOVE ? aPos : rOldPos ); + } +#endif + pArr->Reset(); + while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL ) + { + if ( t->GetRef() != 1 ) + { +#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE + bEasyShared = FALSE; +#endif + } + else + { // if nRefCnt>1 it's already updated in token code + if ( t->GetType() == svSingleRef ) + { + ScSingleRefData& rRef = t->GetSingleRef(); + SingleDoubleRefModifier aMod( rRef ); + if ( rRef.IsRelName() ) + { + ScRefUpdate::MoveRelWrap( pDoc, aPos, MAXCOL, MAXROW, aMod.Ref() ); + rChanged = TRUE; + } + else + { + aMod.Ref().CalcAbsIfRel( rOldPos ); + if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos, + r, nDx, nDy, nDz, aMod.Ref() ) + != UR_NOTHING + ) + rChanged = TRUE; + } +#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE + if ( bEasyShared ) + { + const ScSingleRefData& rSRD = aMod.Ref().Ref1; + ScAddress aRef( rSRD.nCol, rSRD.nRow, rSRD.nTab ); + if ( r.In( aRef ) != bPosInRange ) + bEasyShared = FALSE; + } +#endif + } + else + { + ScComplexRefData& rRef = t->GetDoubleRef(); + SCCOL nCols = rRef.Ref2.nCol - rRef.Ref1.nCol; + SCROW nRows = rRef.Ref2.nRow - rRef.Ref1.nRow; + SCTAB nTabs = rRef.Ref2.nTab - rRef.Ref1.nTab; + if ( rRef.Ref1.IsRelName() || rRef.Ref2.IsRelName() ) + { + ScRefUpdate::MoveRelWrap( pDoc, aPos, MAXCOL, MAXROW, rRef ); + rChanged = TRUE; + } + else + { + if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos, + r, nDx, nDy, nDz, rRef ) + != UR_NOTHING + ) + { + rChanged = TRUE; + if (rRef.Ref2.nCol - rRef.Ref1.nCol != nCols || + rRef.Ref2.nRow - rRef.Ref1.nRow != nRows || + rRef.Ref2.nTab - rRef.Ref1.nTab != nTabs) + { + rRefSizeChanged = TRUE; +#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE + bEasyShared = FALSE; +#endif + } + } + } +#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE + if ( bEasyShared ) + { + ScRange aRef( rRef.Ref1.nCol, rRef.Ref1.nRow, + rRef.Ref1.nTab, rRef.Ref2.nCol, rRef.Ref2.nRow, + rRef.Ref2.nTab ); + if ( r.In( aRef ) != bPosInRange ) + bEasyShared = FALSE; + } +#endif + } + } + } +#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE + if ( pRangeData ) + { + if ( bEasyShared ) + pRangeData = 0; + else + rChanged = TRUE; + } +#endif +#undef SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE + return pRangeData; + } +} + +BOOL ScCompiler::UpdateNameReference(UpdateRefMode eUpdateRefMode, + const ScRange& r, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz, + BOOL& rChanged, BOOL bSharedFormula) +{ + BOOL bRelRef = FALSE; // set if relative reference + rChanged = FALSE; + pArr->Reset(); + ScToken* t; + while ( (t = static_cast<ScToken*>(pArr->GetNextReference())) != NULL ) + { + SingleDoubleRefModifier aMod( *t ); + ScComplexRefData& rRef = aMod.Ref(); + bRelRef = rRef.Ref1.IsColRel() || rRef.Ref1.IsRowRel() || + rRef.Ref1.IsTabRel(); + if (!bRelRef && t->GetType() == svDoubleRef) + bRelRef = rRef.Ref2.IsColRel() || rRef.Ref2.IsRowRel() || + rRef.Ref2.IsTabRel(); + bool bUpdate = !rRef.Ref1.IsColRel() || !rRef.Ref1.IsRowRel() || + !rRef.Ref1.IsTabRel(); + if (!bUpdate && t->GetType() == svDoubleRef) + bUpdate = !rRef.Ref2.IsColRel() || !rRef.Ref2.IsRowRel() || + !rRef.Ref2.IsTabRel(); + if (!bSharedFormula) + { + // We cannot update names with sheet-relative references, they may + // be used on other sheets as well and the resulting reference + // would be wrong. This is a dilemma if col/row would need to be + // updated for the current usage. + // TODO: seems the only way out of this would be to not allow + // relative sheet references and have sheet-local names that can be + // copied along with sheets. + bUpdate = bUpdate && !rRef.Ref1.IsTabRel() && !rRef.Ref2.IsTabRel(); + } + if (bUpdate) + { + rRef.CalcAbsIfRel( aPos); + if (ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos, r, + nDx, nDy, nDz, rRef, ScRefUpdate::ABSOLUTE) + != UR_NOTHING ) + rChanged = TRUE; + } + } + return bRelRef; +} + + +void ScCompiler::UpdateSharedFormulaReference( UpdateRefMode eUpdateRefMode, + const ScAddress& rOldPos, const ScRange& r, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + if ( eUpdateRefMode == URM_COPY ) + return ; + else + { + ScToken* t; + pArr->Reset(); + while ( (t = static_cast<ScToken*>(pArr->GetNextReference())) != NULL ) + { + if( t->GetType() != svIndex ) // it may be a DB area!!! + { + t->CalcAbsIfRel( rOldPos ); + // Absolute references have been already adjusted in the named + // shared formula itself prior to breaking the shared formula + // and calling this function. Don't readjust them again. + SingleDoubleRefModifier aMod( *t ); + ScComplexRefData& rRef = aMod.Ref(); + ScComplexRefData aBkp = rRef; + ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos, + r, nDx, nDy, nDz, rRef ); + // restore absolute parts + if ( !aBkp.Ref1.IsColRel() ) + { + rRef.Ref1.nCol = aBkp.Ref1.nCol; + rRef.Ref1.nRelCol = aBkp.Ref1.nRelCol; + rRef.Ref1.SetColDeleted( aBkp.Ref1.IsColDeleted() ); + } + if ( !aBkp.Ref1.IsRowRel() ) + { + rRef.Ref1.nRow = aBkp.Ref1.nRow; + rRef.Ref1.nRelRow = aBkp.Ref1.nRelRow; + rRef.Ref1.SetRowDeleted( aBkp.Ref1.IsRowDeleted() ); + } + if ( !aBkp.Ref1.IsTabRel() ) + { + rRef.Ref1.nTab = aBkp.Ref1.nTab; + rRef.Ref1.nRelTab = aBkp.Ref1.nRelTab; + rRef.Ref1.SetTabDeleted( aBkp.Ref1.IsTabDeleted() ); + } + if ( t->GetType() == svDoubleRef ) + { + if ( !aBkp.Ref2.IsColRel() ) + { + rRef.Ref2.nCol = aBkp.Ref2.nCol; + rRef.Ref2.nRelCol = aBkp.Ref2.nRelCol; + rRef.Ref2.SetColDeleted( aBkp.Ref2.IsColDeleted() ); + } + if ( !aBkp.Ref2.IsRowRel() ) + { + rRef.Ref2.nRow = aBkp.Ref2.nRow; + rRef.Ref2.nRelRow = aBkp.Ref2.nRelRow; + rRef.Ref2.SetRowDeleted( aBkp.Ref2.IsRowDeleted() ); + } + if ( !aBkp.Ref2.IsTabRel() ) + { + rRef.Ref2.nTab = aBkp.Ref2.nTab; + rRef.Ref2.nRelTab = aBkp.Ref2.nRelTab; + rRef.Ref2.SetTabDeleted( aBkp.Ref2.IsTabDeleted() ); + } + } + } + } + } +} + + +ScRangeData* ScCompiler::UpdateInsertTab( SCTAB nTable, BOOL bIsName ) +{ + ScRangeData* pRangeData = NULL; + SCTAB nPosTab = aPos.Tab(); // _after_ incremented! + SCTAB nOldPosTab = ((nPosTab > nTable) ? (nPosTab - 1) : nPosTab); + BOOL bIsRel = FALSE; + ScToken* t; + pArr->Reset(); + if (bIsName) + t = static_cast<ScToken*>(pArr->GetNextReference()); + else + t = static_cast<ScToken*>(pArr->GetNextReferenceOrName()); + while( t ) + { + if( t->GetOpCode() == ocName ) + { + if (!bIsName) + { + ScRangeData* pName = pDoc->GetRangeName()->FindIndex(t->GetIndex()); + if (pName && pName->HasType(RT_SHAREDMOD)) + pRangeData = pName; + } + } + else if( t->GetType() != svIndex ) // it may be a DB area!!! + { + if ( !(bIsName && t->GetSingleRef().IsTabRel()) ) + { // of names only adjust absolute references + ScSingleRefData& rRef = t->GetSingleRef(); + if ( rRef.IsTabRel() ) + { + rRef.nTab = rRef.nRelTab + nOldPosTab; + if ( rRef.nTab < 0 ) + rRef.nTab = sal::static_int_cast<SCsTAB>( rRef.nTab + pDoc->GetTableCount() ); // was a wrap + } + if (nTable <= rRef.nTab) + ++rRef.nTab; + rRef.nRelTab = rRef.nTab - nPosTab; + } + else + bIsRel = TRUE; + if ( t->GetType() == svDoubleRef ) + { + if ( !(bIsName && t->GetDoubleRef().Ref2.IsTabRel()) ) + { // of names only adjust absolute references + ScSingleRefData& rRef = t->GetDoubleRef().Ref2; + if ( rRef.IsTabRel() ) + { + rRef.nTab = rRef.nRelTab + nOldPosTab; + if ( rRef.nTab < 0 ) + rRef.nTab = sal::static_int_cast<SCsTAB>( rRef.nTab + pDoc->GetTableCount() ); // was a wrap + } + if (nTable <= rRef.nTab) + ++rRef.nTab; + rRef.nRelTab = rRef.nTab - nPosTab; + } + else + bIsRel = TRUE; + } + if ( bIsName && bIsRel ) + pRangeData = (ScRangeData*) this; // not dereferenced in rangenam + } + if (bIsName) + t = static_cast<ScToken*>(pArr->GetNextReference()); + else + t = static_cast<ScToken*>(pArr->GetNextReferenceOrName()); + } + if ( !bIsName ) + { + pArr->Reset(); + while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL ) + { + if ( t->GetRef() == 1 ) + { + ScSingleRefData& rRef1 = t->GetSingleRef(); + if ( !(rRef1.IsRelName() && rRef1.IsTabRel()) ) + { // of names only adjust absolute references + if ( rRef1.IsTabRel() ) + { + rRef1.nTab = rRef1.nRelTab + nOldPosTab; + if ( rRef1.nTab < 0 ) + rRef1.nTab = sal::static_int_cast<SCsTAB>( rRef1.nTab + pDoc->GetTableCount() ); // was a wrap + } + if (nTable <= rRef1.nTab) + ++rRef1.nTab; + rRef1.nRelTab = rRef1.nTab - nPosTab; + } + if ( t->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; + if ( !(rRef2.IsRelName() && rRef2.IsTabRel()) ) + { // of names only adjust absolute references + if ( rRef2.IsTabRel() ) + { + rRef2.nTab = rRef2.nRelTab + nOldPosTab; + if ( rRef2.nTab < 0 ) + rRef2.nTab = sal::static_int_cast<SCsTAB>( rRef2.nTab + pDoc->GetTableCount() ); // was a wrap + } + if (nTable <= rRef2.nTab) + ++rRef2.nTab; + rRef2.nRelTab = rRef2.nTab - nPosTab; + } + } + } + } + } + return pRangeData; +} + +ScRangeData* ScCompiler::UpdateDeleteTab(SCTAB nTable, BOOL /* bIsMove */, BOOL bIsName, + BOOL& rChanged) +{ + ScRangeData* pRangeData = NULL; + SCTAB nTab, nTab2; + SCTAB nPosTab = aPos.Tab(); // _after_ decremented! + SCTAB nOldPosTab = ((nPosTab >= nTable) ? (nPosTab + 1) : nPosTab); + rChanged = FALSE; + BOOL bIsRel = FALSE; + ScToken* t; + pArr->Reset(); + if (bIsName) + t = static_cast<ScToken*>(pArr->GetNextReference()); + else + t = static_cast<ScToken*>(pArr->GetNextReferenceOrName()); + while( t ) + { + if( t->GetOpCode() == ocName ) + { + if (!bIsName) + { + ScRangeData* pName = pDoc->GetRangeName()->FindIndex(t->GetIndex()); + if (pName && pName->HasType(RT_SHAREDMOD)) + pRangeData = pName; + } + rChanged = TRUE; + } + else if( t->GetType() != svIndex ) // it may be a DB area!!! + { + if ( !(bIsName && t->GetSingleRef().IsTabRel()) ) + { // of names only adjust absolute references + ScSingleRefData& rRef = t->GetSingleRef(); + if ( rRef.IsTabRel() ) + nTab = rRef.nTab = rRef.nRelTab + nOldPosTab; + else + nTab = rRef.nTab; + if ( nTable < nTab ) + { + rRef.nTab = nTab - 1; + rChanged = TRUE; + } + else if ( nTable == nTab ) + { + if ( t->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; + if ( rRef2.IsTabRel() ) + nTab2 = rRef2.nRelTab + nOldPosTab; + else + nTab2 = rRef2.nTab; + if ( nTab == nTab2 + || (nTab+1) >= pDoc->GetTableCount() ) + { + rRef.nTab = MAXTAB+1; + rRef.SetTabDeleted( TRUE ); + } + // else: nTab later points to what's nTable+1 now + // => area shrunk + } + else + { + rRef.nTab = MAXTAB+1; + rRef.SetTabDeleted( TRUE ); + } + rChanged = TRUE; + } + rRef.nRelTab = rRef.nTab - nPosTab; + } + else + bIsRel = TRUE; + if ( t->GetType() == svDoubleRef ) + { + if ( !(bIsName && t->GetDoubleRef().Ref2.IsTabRel()) ) + { // of names only adjust absolute references + ScSingleRefData& rRef = t->GetDoubleRef().Ref2; + if ( rRef.IsTabRel() ) + nTab = rRef.nTab = rRef.nRelTab + nOldPosTab; + else + nTab = rRef.nTab; + if ( nTable < nTab ) + { + rRef.nTab = nTab - 1; + rChanged = TRUE; + } + else if ( nTable == nTab ) + { + if ( !t->GetDoubleRef().Ref1.IsTabDeleted() ) + rRef.nTab = nTab - 1; // shrink area + else + { + rRef.nTab = MAXTAB+1; + rRef.SetTabDeleted( TRUE ); + } + rChanged = TRUE; + } + rRef.nRelTab = rRef.nTab - nPosTab; + } + else + bIsRel = TRUE; + } + if ( bIsName && bIsRel ) + pRangeData = (ScRangeData*) this; // not dereferenced in rangenam + } + if (bIsName) + t = static_cast<ScToken*>(pArr->GetNextReference()); + else + t = static_cast<ScToken*>(pArr->GetNextReferenceOrName()); + } + if ( !bIsName ) + { + pArr->Reset(); + while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL ) + { + if ( t->GetRef() == 1 ) + { + ScSingleRefData& rRef1 = t->GetSingleRef(); + if ( !(rRef1.IsRelName() && rRef1.IsTabRel()) ) + { // of names only adjust absolute references + if ( rRef1.IsTabRel() ) + nTab = rRef1.nTab = rRef1.nRelTab + nOldPosTab; + else + nTab = rRef1.nTab; + if ( nTable < nTab ) + { + rRef1.nTab = nTab - 1; + rChanged = TRUE; + } + else if ( nTable == nTab ) + { + if ( t->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; + if ( rRef2.IsTabRel() ) + nTab2 = rRef2.nRelTab + nOldPosTab; + else + nTab2 = rRef2.nTab; + if ( nTab == nTab2 + || (nTab+1) >= pDoc->GetTableCount() ) + { + rRef1.nTab = MAXTAB+1; + rRef1.SetTabDeleted( TRUE ); + } + // else: nTab later points to what's nTable+1 now + // => area shrunk + } + else + { + rRef1.nTab = MAXTAB+1; + rRef1.SetTabDeleted( TRUE ); + } + rChanged = TRUE; + } + rRef1.nRelTab = rRef1.nTab - nPosTab; + } + if ( t->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; + if ( !(rRef2.IsRelName() && rRef2.IsTabRel()) ) + { // of names only adjust absolute references + if ( rRef2.IsTabRel() ) + nTab = rRef2.nTab = rRef2.nRelTab + nOldPosTab; + else + nTab = rRef2.nTab; + if ( nTable < nTab ) + { + rRef2.nTab = nTab - 1; + rChanged = TRUE; + } + else if ( nTable == nTab ) + { + if ( !rRef1.IsTabDeleted() ) + rRef2.nTab = nTab - 1; // shrink area + else + { + rRef2.nTab = MAXTAB+1; + rRef2.SetTabDeleted( TRUE ); + } + rChanged = TRUE; + } + rRef2.nRelTab = rRef2.nTab - nPosTab; + } + } + } + } + } + return pRangeData; +} + +// aPos.Tab() must be already adjusted! +ScRangeData* ScCompiler::UpdateMoveTab( SCTAB nOldTab, SCTAB nNewTab, + BOOL bIsName ) +{ + ScRangeData* pRangeData = NULL; + SCsTAB nTab; + + SCTAB nStart, nEnd; + short nDir; // direction in which others move + if ( nOldTab < nNewTab ) + { + nDir = -1; + nStart = nOldTab; + nEnd = nNewTab; + } + else + { + nDir = 1; + nStart = nNewTab; + nEnd = nOldTab; + } + SCTAB nPosTab = aPos.Tab(); // current sheet + SCTAB nOldPosTab; // previously it was this one + if ( nPosTab == nNewTab ) + nOldPosTab = nOldTab; // look, it's me! + else if ( nPosTab < nStart || nEnd < nPosTab ) + nOldPosTab = nPosTab; // wasn't moved + else + nOldPosTab = nPosTab - nDir; // moved by one + + BOOL bIsRel = FALSE; + ScToken* t; + pArr->Reset(); + if (bIsName) + t = static_cast<ScToken*>(pArr->GetNextReference()); + else + t = static_cast<ScToken*>(pArr->GetNextReferenceOrName()); + while( t ) + { + if( t->GetOpCode() == ocName ) + { + if (!bIsName) + { + ScRangeData* pName = pDoc->GetRangeName()->FindIndex(t->GetIndex()); + if (pName && pName->HasType(RT_SHAREDMOD)) + pRangeData = pName; + } + } + else if( t->GetType() != svIndex ) // it may be a DB area!!! + { + ScSingleRefData& rRef1 = t->GetSingleRef(); + if ( !(bIsName && rRef1.IsTabRel()) ) + { // of names only adjust absolute references + if ( rRef1.IsTabRel() ) + nTab = rRef1.nRelTab + nOldPosTab; + else + nTab = rRef1.nTab; + if ( nTab == nOldTab ) + rRef1.nTab = nNewTab; + else if ( nStart <= nTab && nTab <= nEnd ) + rRef1.nTab = nTab + nDir; + rRef1.nRelTab = rRef1.nTab - nPosTab; + } + else + bIsRel = TRUE; + if ( t->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; + if ( !(bIsName && rRef2.IsTabRel()) ) + { // of names only adjust absolute references + if ( rRef2.IsTabRel() ) + nTab = rRef2.nRelTab + nOldPosTab; + else + nTab = rRef2.nTab; + if ( nTab == nOldTab ) + rRef2.nTab = nNewTab; + else if ( nStart <= nTab && nTab <= nEnd ) + rRef2.nTab = nTab + nDir; + rRef2.nRelTab = rRef2.nTab - nPosTab; + } + else + bIsRel = TRUE; + SCsTAB nTab1, nTab2; + if ( rRef1.IsTabRel() ) + nTab1 = rRef1.nRelTab + nPosTab; + else + nTab1 = rRef1.nTab; + if ( rRef2.IsTabRel() ) + nTab2 = rRef2.nRelTab + nPosTab; + else + nTab2 = rRef1.nTab; + if ( nTab2 < nTab1 ) + { // PutInOrder + rRef1.nTab = nTab2; + rRef2.nTab = nTab1; + rRef1.nRelTab = rRef1.nTab - nPosTab; + rRef2.nRelTab = rRef2.nTab - nPosTab; + } + } + if ( bIsName && bIsRel ) + pRangeData = (ScRangeData*) this; // not dereferenced in rangenam + } + if (bIsName) + t = static_cast<ScToken*>(pArr->GetNextReference()); + else + t = static_cast<ScToken*>(pArr->GetNextReferenceOrName()); + } + if ( !bIsName ) + { + SCsTAB nMaxTabMod = (SCsTAB) pDoc->GetTableCount(); + pArr->Reset(); + while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL ) + { + if ( t->GetRef() == 1 ) + { + ScSingleRefData& rRef1 = t->GetSingleRef(); + if ( rRef1.IsRelName() && rRef1.IsTabRel() ) + { // possibly wrap RelName, like lcl_MoveItWrap in refupdat.cxx + nTab = rRef1.nRelTab + nPosTab; + if ( nTab < 0 ) + nTab = sal::static_int_cast<SCsTAB>( nTab + nMaxTabMod ); + else if ( nTab > nMaxTab ) + nTab = sal::static_int_cast<SCsTAB>( nTab - nMaxTabMod ); + rRef1.nRelTab = nTab - nPosTab; + } + else + { + if ( rRef1.IsTabRel() ) + nTab = rRef1.nRelTab + nOldPosTab; + else + nTab = rRef1.nTab; + if ( nTab == nOldTab ) + rRef1.nTab = nNewTab; + else if ( nStart <= nTab && nTab <= nEnd ) + rRef1.nTab = nTab + nDir; + rRef1.nRelTab = rRef1.nTab - nPosTab; + } + if( t->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; + if ( rRef2.IsRelName() && rRef2.IsTabRel() ) + { // possibly wrap RelName, like lcl_MoveItWrap in refupdat.cxx + nTab = rRef2.nRelTab + nPosTab; + if ( nTab < 0 ) + nTab = sal::static_int_cast<SCsTAB>( nTab + nMaxTabMod ); + else if ( nTab > nMaxTab ) + nTab = sal::static_int_cast<SCsTAB>( nTab - nMaxTabMod ); + rRef2.nRelTab = nTab - nPosTab; + } + else + { + if ( rRef2.IsTabRel() ) + nTab = rRef2.nRelTab + nOldPosTab; + else + nTab = rRef2.nTab; + if ( nTab == nOldTab ) + rRef2.nTab = nNewTab; + else if ( nStart <= nTab && nTab <= nEnd ) + rRef2.nTab = nTab + nDir; + rRef2.nRelTab = rRef2.nTab - nPosTab; + } + SCsTAB nTab1, nTab2; + if ( rRef1.IsTabRel() ) + nTab1 = rRef1.nRelTab + nPosTab; + else + nTab1 = rRef1.nTab; + if ( rRef2.IsTabRel() ) + nTab2 = rRef2.nRelTab + nPosTab; + else + nTab2 = rRef1.nTab; + if ( nTab2 < nTab1 ) + { // PutInOrder + rRef1.nTab = nTab2; + rRef2.nTab = nTab1; + rRef1.nRelTab = rRef1.nTab - nPosTab; + rRef2.nRelTab = rRef2.nTab - nPosTab; + } + } + } + } + } + return pRangeData; +} + + +void ScCompiler::CreateStringFromExternal(rtl::OUStringBuffer& rBuffer, FormulaToken* pTokenP) +{ + FormulaToken* t = pTokenP; + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + switch (t->GetType()) + { + case svExternalName: + { + const String *pStr = pRefMgr->getExternalFileName(t->GetIndex()); + String aFileName = pStr ? *pStr : ScGlobal::GetRscString(STR_NO_NAME_REF); + rBuffer.append(pConv->makeExternalNameStr( aFileName, t->GetString())); + } + break; + case svExternalSingleRef: + pConv->makeExternalRefStr( + rBuffer, *this, t->GetIndex(), t->GetString(), static_cast<ScToken*>(t)->GetSingleRef(), pRefMgr); + break; + case svExternalDoubleRef: + pConv->makeExternalRefStr( + rBuffer, *this, t->GetIndex(), t->GetString(), static_cast<ScToken*>(t)->GetDoubleRef(), pRefMgr); + break; + default: + // warning, not error, otherwise we may end up with a never + // ending message box loop if this was the cursor cell to be redrawn. + DBG_WARNING("ScCompiler::CreateStringFromToken: unknown type of ocExternalRef"); + } +} + +void ScCompiler::CreateStringFromMatrix( rtl::OUStringBuffer& rBuffer, + FormulaToken* pTokenP) +{ + const ScMatrix* pMatrix = static_cast<ScToken*>(pTokenP)->GetMatrix(); + SCSIZE nC, nMaxC, nR, nMaxR; + + pMatrix->GetDimensions( nMaxC, nMaxR); + + rBuffer.append( mxSymbols->getSymbol(ocArrayOpen) ); + for( nR = 0 ; nR < nMaxR ; nR++) + { + if( nR > 0) + { + rBuffer.append( mxSymbols->getSymbol(ocArrayRowSep) ); + } + + for( nC = 0 ; nC < nMaxC ; nC++) + { + if( nC > 0) + { + rBuffer.append( mxSymbols->getSymbol(ocArrayColSep) ); + } + + if( pMatrix->IsValue( nC, nR ) ) + { + ScMatValType nType; + const ScMatrixValue* pVal = pMatrix->Get( nC, nR, nType); + + if( nType == SC_MATVAL_BOOLEAN ) + AppendBoolean( rBuffer, pVal->GetBoolean() ); + else + { + USHORT nErr = pVal->GetError(); + if( nErr ) + rBuffer.append( ScGlobal::GetErrorString( nErr ) ); + else + AppendDouble( rBuffer, pVal->fVal ); + } + } + else if( pMatrix->IsEmpty( nC, nR ) ) + ; + else if( pMatrix->IsString( nC, nR ) ) + AppendString( rBuffer, pMatrix->GetString( nC, nR ) ); + } + } + rBuffer.append( mxSymbols->getSymbol(ocArrayClose) ); +} + +void ScCompiler::CreateStringFromSingleRef(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP) +{ + const OpCode eOp = _pTokenP->GetOpCode(); + ScSingleRefData& rRef = static_cast<ScToken*>(_pTokenP)->GetSingleRef(); + ScComplexRefData aRef; + aRef.Ref1 = aRef.Ref2 = rRef; + if ( eOp == ocColRowName ) + { + rRef.CalcAbsIfRel( aPos ); + if ( pDoc->HasStringData( rRef.nCol, rRef.nRow, rRef.nTab ) ) + { + String aStr; + pDoc->GetString( rRef.nCol, rRef.nRow, rRef.nTab, aStr ); + EnQuote( aStr ); + rBuffer.append(aStr); + } + else + { + rBuffer.append(ScGlobal::GetRscString(STR_NO_NAME_REF)); + pConv->MakeRefStr (rBuffer, *this, aRef, TRUE ); + } + } + else + pConv->MakeRefStr( rBuffer, *this, aRef, TRUE ); +} +// ----------------------------------------------------------------------------- +void ScCompiler::CreateStringFromDoubleRef(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP) +{ + pConv->MakeRefStr( rBuffer, *this, static_cast<ScToken*>(_pTokenP)->GetDoubleRef(), FALSE ); +} +// ----------------------------------------------------------------------------- +void ScCompiler::CreateStringFromIndex(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP) +{ + const OpCode eOp = _pTokenP->GetOpCode(); + rtl::OUStringBuffer aBuffer; + switch ( eOp ) + { + case ocName: + { + ScRangeData* pData = pDoc->GetRangeName()->FindIndex(_pTokenP->GetIndex()); + if (pData) + { + if (pData->HasType(RT_SHARED)) + pData->UpdateSymbol( aBuffer, aPos, GetGrammar()); + else + aBuffer.append(pData->GetName()); + } + } + break; + case ocDBArea: + { + ScDBData* pDBData = pDoc->GetDBCollection()->FindIndex(_pTokenP->GetIndex()); + if (pDBData) + aBuffer.append(pDBData->GetName()); + } + break; + default: + ; // nothing + } + if ( aBuffer.getLength() ) + rBuffer.append(aBuffer); + else + rBuffer.append(ScGlobal::GetRscString(STR_NO_NAME_REF)); +} +// ----------------------------------------------------------------------------- +void ScCompiler::LocalizeString( String& rName ) +{ + ScGlobal::GetAddInCollection()->LocalizeString( rName ); +} +// ----------------------------------------------------------------------------- +BOOL ScCompiler::IsImportingXML() const +{ + return pDoc->IsImportingXML(); +} + +// Put quotes around string if non-alphanumeric characters are contained, +// quote characters contained within are escaped by '\\'. +BOOL ScCompiler::EnQuote( String& rStr ) +{ + sal_Int32 nType = ScGlobal::pCharClass->getStringType( rStr, 0, rStr.Len() ); + if ( !CharClass::isNumericType( nType ) + && CharClass::isAlphaNumericType( nType ) ) + return FALSE; + + xub_StrLen nPos = 0; + while ( (nPos = rStr.Search( '\'', nPos)) != STRING_NOTFOUND ) + { + rStr.Insert( '\\', nPos ); + nPos += 2; + } + rStr.Insert( '\'', 0 ); + rStr += '\''; + return TRUE; +} + +sal_Unicode ScCompiler::GetNativeAddressSymbol( Convention::SpecialSymbolType eType ) const +{ + return pConv->getSpecialSymbol(eType); +} + +void ScCompiler::fillAddInToken(::std::vector< ::com::sun::star::sheet::FormulaOpCodeMapEntry >& _rVec,bool _bIsEnglish) const +{ + // All known AddIn functions. + sheet::FormulaOpCodeMapEntry aEntry; + aEntry.Token.OpCode = ocExternal; + + ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection(); + const long nCount = pColl->GetFuncCount(); + for (long i=0; i < nCount; ++i) + { + const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i); + if (pFuncData) + { + if ( _bIsEnglish ) + { + String aName; + if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName)) + aEntry.Name = aName; + else + aEntry.Name = pFuncData->GetUpperName(); + } + else + aEntry.Name = pFuncData->GetUpperLocal(); + aEntry.Token.Data <<= ::rtl::OUString( pFuncData->GetOriginalName()); + _rVec.push_back( aEntry); + } + } + // FIXME: what about those old non-UNO AddIns? +} +// ----------------------------------------------------------------------------- +BOOL ScCompiler::HandleSingleRef() +{ + ScSingleRefData& rRef = static_cast<ScToken*>((FormulaToken*)pToken)->GetSingleRef(); + rRef.CalcAbsIfRel( aPos ); + if ( !rRef.Valid() ) + { + SetError( errNoRef ); + return TRUE; + } + SCCOL nCol = rRef.nCol; + SCROW nRow = rRef.nRow; + SCTAB nTab = rRef.nTab; + ScAddress aLook( nCol, nRow, nTab ); + BOOL bColName = rRef.IsColRel(); + SCCOL nMyCol = aPos.Col(); + SCROW nMyRow = aPos.Row(); + BOOL bInList = FALSE; + BOOL bValidName = FALSE; + ScRangePairList* pRL = (bColName ? + pDoc->GetColNameRanges() : pDoc->GetRowNameRanges()); + ScRange aRange; + for ( ScRangePair* pR = pRL->First(); pR; pR = pRL->Next() ) + { + if ( pR->GetRange(0).In( aLook ) ) + { + bInList = bValidName = TRUE; + aRange = pR->GetRange(1); + if ( bColName ) + { + aRange.aStart.SetCol( nCol ); + aRange.aEnd.SetCol( nCol ); + } + else + { + aRange.aStart.SetRow( nRow ); + aRange.aEnd.SetRow( nRow ); + } + break; // for + } + } + if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() ) + { // automagically or created by copying and NamePos isn't in list + BOOL bString = pDoc->HasStringData( nCol, nRow, nTab ); + if ( !bString && !pDoc->GetCell( aLook ) ) + bString = TRUE; // empty cell is ok + if ( bString ) + { //! coresponds with ScInterpreter::ScColRowNameAuto() + bValidName = TRUE; + if ( bColName ) + { // ColName + SCROW nStartRow = nRow + 1; + if ( nStartRow > MAXROW ) + nStartRow = MAXROW; + SCROW nMaxRow = MAXROW; + if ( nMyCol == nCol ) + { // formula cell in same column + if ( nMyRow == nStartRow ) + { // take remainder under name cell + nStartRow++; + if ( nStartRow > MAXROW ) + nStartRow = MAXROW; + } + else if ( nMyRow > nStartRow ) + { // from name cell down to formula cell + nMaxRow = nMyRow - 1; + } + } + for ( ScRangePair* pR = pRL->First(); pR; pR = pRL->Next() ) + { // next defined ColNameRange below limits row + const ScRange& rRange = pR->GetRange(1); + if ( rRange.aStart.Col() <= nCol && nCol <= rRange.aEnd.Col() ) + { // identical column range + SCROW nTmp = rRange.aStart.Row(); + if ( nStartRow < nTmp && nTmp <= nMaxRow ) + nMaxRow = nTmp - 1; + } + } + aRange.aStart.Set( nCol, nStartRow, nTab ); + aRange.aEnd.Set( nCol, nMaxRow, nTab ); + } + else + { // RowName + SCCOL nStartCol = nCol + 1; + if ( nStartCol > MAXCOL ) + nStartCol = MAXCOL; + SCCOL nMaxCol = MAXCOL; + if ( nMyRow == nRow ) + { // formula cell in same row + if ( nMyCol == nStartCol ) + { // take remainder right from name cell + nStartCol++; + if ( nStartCol > MAXCOL ) + nStartCol = MAXCOL; + } + else if ( nMyCol > nStartCol ) + { // from name cell right to formula cell + nMaxCol = nMyCol - 1; + } + } + for ( ScRangePair* pR = pRL->First(); pR; pR = pRL->Next() ) + { // next defined RowNameRange to the right limits column + const ScRange& rRange = pR->GetRange(1); + if ( rRange.aStart.Row() <= nRow && nRow <= rRange.aEnd.Row() ) + { // identical row range + SCCOL nTmp = rRange.aStart.Col(); + if ( nStartCol < nTmp && nTmp <= nMaxCol ) + nMaxCol = nTmp - 1; + } + } + aRange.aStart.Set( nStartCol, nRow, nTab ); + aRange.aEnd.Set( nMaxCol, nRow, nTab ); + } + } + } + if ( bValidName ) + { + // And now the magic to distinguish between a range and a single + // cell thereof, which is picked position-dependent of the formula + // cell. If a direct neighbor is a binary operator (ocAdd, ...) a + // SingleRef matching the column/row of the formula cell is + // generated. A ocColRowName or ocIntersect as a neighbor results + // in a range. Special case: if label is valid for a single cell, a + // position independent SingleRef is generated. + BOOL bSingle = (aRange.aStart == aRange.aEnd); + BOOL bFound; + if ( bSingle ) + bFound = TRUE; + else + { + FormulaToken* p1 = pArr->PeekPrevNoSpaces(); + FormulaToken* p2 = pArr->PeekNextNoSpaces(); + // begin/end of a formula => single + OpCode eOp1 = p1 ? p1->GetOpCode() : static_cast<OpCode>( ocAdd ); + OpCode eOp2 = p2 ? p2->GetOpCode() : static_cast<OpCode>( ocAdd ); + if ( eOp1 != ocColRowName && eOp1 != ocIntersect + && eOp2 != ocColRowName && eOp2 != ocIntersect ) + { + if ( (SC_OPCODE_START_BIN_OP <= eOp1 && eOp1 < SC_OPCODE_STOP_BIN_OP) || + (SC_OPCODE_START_BIN_OP <= eOp2 && eOp2 < SC_OPCODE_STOP_BIN_OP)) + bSingle = TRUE; + } + if ( bSingle ) + { // column and/or row must match range + if ( bColName ) + { + bFound = (aRange.aStart.Row() <= nMyRow + && nMyRow <= aRange.aEnd.Row()); + if ( bFound ) + aRange.aStart.SetRow( nMyRow ); + } + else + { + bFound = (aRange.aStart.Col() <= nMyCol + && nMyCol <= aRange.aEnd.Col()); + if ( bFound ) + aRange.aStart.SetCol( nMyCol ); + } + } + else + bFound = TRUE; + } + if ( !bFound ) + SetError(errNoRef); + else if ( !bCompileForFAP ) + { + ScTokenArray* pNew = new ScTokenArray(); + if ( bSingle ) + { + ScSingleRefData aRefData; + aRefData.InitAddress( aRange.aStart ); + if ( bColName ) + aRefData.SetColRel( TRUE ); + else + aRefData.SetRowRel( TRUE ); + aRefData.CalcRelFromAbs( aPos ); + pNew->AddSingleReference( aRefData ); + } + else + { + ScComplexRefData aRefData; + aRefData.InitRange( aRange ); + if ( bColName ) + { + aRefData.Ref1.SetColRel( TRUE ); + aRefData.Ref2.SetColRel( TRUE ); + } + else + { + aRefData.Ref1.SetRowRel( TRUE ); + aRefData.Ref2.SetRowRel( TRUE ); + } + aRefData.CalcRelFromAbs( aPos ); + if ( bInList ) + pNew->AddDoubleReference( aRefData ); + else + { // automagically + pNew->Add( new ScDoubleRefToken( aRefData, ocColRowNameAuto ) ); + } + } + PushTokenArray( pNew, TRUE ); + pNew->Reset(); + return GetToken(); + } + } + else + SetError(errNoName); + return TRUE; +} +// ----------------------------------------------------------------------------- +BOOL ScCompiler::HandleDbData() +{ + ScDBData* pDBData = pDoc->GetDBCollection()->FindIndex( pToken->GetIndex() ); + if ( !pDBData ) + SetError(errNoName); + else if ( !bCompileForFAP ) + { + ScComplexRefData aRefData; + aRefData.InitFlags(); + pDBData->GetArea( (SCTAB&) aRefData.Ref1.nTab, + (SCCOL&) aRefData.Ref1.nCol, + (SCROW&) aRefData.Ref1.nRow, + (SCCOL&) aRefData.Ref2.nCol, + (SCROW&) aRefData.Ref2.nRow); + aRefData.Ref2.nTab = aRefData.Ref1.nTab; + aRefData.CalcRelFromAbs( aPos ); + ScTokenArray* pNew = new ScTokenArray(); + pNew->AddDoubleReference( aRefData ); + PushTokenArray( pNew, TRUE ); + pNew->Reset(); + return GetToken(); + } + return TRUE; +} + +String GetScCompilerNativeSymbol( OpCode eOp ) +{ + return ScCompiler::GetNativeSymbol( eOp ); +} +// ----------------------------------------------------------------------------- +FormulaTokenRef ScCompiler::ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2, bool bReuseDoubleRef ) +{ + return ScToken::ExtendRangeReference( rTok1, rTok2, aPos,bReuseDoubleRef ); +} diff --git a/sc/source/core/tool/consoli.cxx b/sc/source/core/tool/consoli.cxx new file mode 100644 index 000000000000..addd92082cfd --- /dev/null +++ b/sc/source/core/tool/consoli.cxx @@ -0,0 +1,858 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <tools/debug.hxx> + +#include "consoli.hxx" +#include "document.hxx" +#include "olinetab.hxx" +#include "globstr.hrc" +#include "subtotal.hxx" +#include "formula/errorcodes.hxx" +#include "cell.hxx" + +#include <math.h> +#include <string.h> + +#define SC_CONS_NOTFOUND -1 + +// STATIC DATA ----------------------------------------------------------- + +/* Strings bei Gelegenheit ganz raus... +static USHORT nFuncRes[] = { // Reihenfolge wie bei enum ScSubTotalFunc + 0, // none + STR_PIVOTFUNC_AVG, + STR_PIVOTFUNC_COUNT, + STR_PIVOTFUNC_COUNT2, + STR_PIVOTFUNC_MAX, + STR_PIVOTFUNC_MIN, + STR_PIVOTFUNC_PROD, + STR_PIVOTFUNC_STDDEV, + STR_PIVOTFUNC_STDDEV2, + STR_PIVOTFUNC_SUM, + STR_PIVOTFUNC_VAR, + STR_PIVOTFUNC_VAR2 }; +*/ + +static OpCode eOpCodeTable[] = { // Reihenfolge wie bei enum ScSubTotalFunc + ocBad, // none + ocAverage, + ocCount, + ocCount2, + ocMax, + ocMin, + ocProduct, + ocStDev, + ocStDevP, + ocSum, + ocVar, + ocVarP }; + +// ----------------------------------------------------------------------- + +void ScReferenceList::AddEntry( SCCOL nCol, SCROW nRow, SCTAB nTab ) +{ + ScReferenceEntry* pOldData = pData; + pData = new ScReferenceEntry[ nFullSize+1 ]; + if (pOldData) + { + memmove( pData, pOldData, nCount * sizeof(ScReferenceEntry) ); + delete[] pOldData; + } + while (nCount < nFullSize) + { + pData[nCount].nCol = SC_CONS_NOTFOUND; + pData[nCount].nRow = SC_CONS_NOTFOUND; + pData[nCount].nTab = SC_CONS_NOTFOUND; + ++nCount; + } + pData[nCount].nCol = nCol; + pData[nCount].nRow = nRow; + pData[nCount].nTab = nTab; + ++nCount; + nFullSize = nCount; +} + +template< typename T > +void lcl_AddString( String**& pData, T& nCount, const String& rInsert ) +{ + String** pOldData = pData; + pData = new String*[ nCount+1 ]; + if (pOldData) + { + memmove( pData, pOldData, nCount * sizeof(String*) ); + delete[] pOldData; + } + pData[nCount] = new String(rInsert); + ++nCount; +} + +// ----------------------------------------------------------------------- + +ScConsData::ScConsData() : + eFunction(SUBTOTAL_FUNC_SUM), + bReference(FALSE), + bColByName(FALSE), + bRowByName(FALSE), + bSubTitles(FALSE), + nColCount(0), + nRowCount(0), + ppUsed(NULL), + ppSum(NULL), + ppCount(NULL), + ppSumSqr(NULL), + ppRefs(NULL), + ppColHeaders(NULL), + ppRowHeaders(NULL), + nDataCount(0), + nTitleCount(0), + ppTitles(NULL), + ppTitlePos(NULL), + bCornerUsed(FALSE) +{ +} + +ScConsData::~ScConsData() +{ + DeleteData(); +} + + +#define DELETEARR(ppArray,nCount) \ +{ \ + ULONG i; \ + if (ppArray) \ + for(i=0; i<nCount; i++) \ + delete[] ppArray[i]; \ + delete[] ppArray; \ + ppArray = NULL; \ +} + +#define DELETESTR(ppArray,nCount) \ +{ \ + ULONG i; \ + if (ppArray) \ + for(i=0; i<nCount; i++) \ + delete ppArray[i]; \ + delete[] ppArray; \ + ppArray = NULL; \ +} + +void ScConsData::DeleteData() +{ + if (ppRefs) + { + for (SCSIZE i=0; i<nColCount; i++) + { + for (SCSIZE j=0; j<nRowCount; j++) + if (ppUsed[i][j]) + ppRefs[i][j].Clear(); + delete[] ppRefs[i]; + } + delete[] ppRefs; + ppRefs = NULL; + } + +// DELETEARR( ppData1, nColCount ); +// DELETEARR( ppData2, nColCount ); + DELETEARR( ppCount, nColCount ); + DELETEARR( ppSum, nColCount ); + DELETEARR( ppSumSqr,nColCount ); + DELETEARR( ppUsed, nColCount ); // erst nach ppRefs !!! + DELETEARR( ppTitlePos, nRowCount ); + DELETESTR( ppColHeaders, nColCount ); + DELETESTR( ppRowHeaders, nRowCount ); + DELETESTR( ppTitles, nTitleCount ); + nTitleCount = 0; + nDataCount = 0; + + if (bColByName) nColCount = 0; // sonst stimmt ppColHeaders nicht + if (bRowByName) nRowCount = 0; + + bCornerUsed = FALSE; + aCornerText.Erase(); +} + +#undef DELETEARR +#undef DELETESTR + +void ScConsData::InitData( BOOL bDelete ) +{ + if (bDelete) + DeleteData(); + + if (bReference && nColCount && !ppRefs) + { + ppRefs = new ScReferenceList*[nColCount]; + for (SCSIZE i=0; i<nColCount; i++) + ppRefs[i] = new ScReferenceList[nRowCount]; + } + else if (nColCount && !ppCount) + { + ppCount = new double*[nColCount]; + ppSum = new double*[nColCount]; + ppSumSqr = new double*[nColCount]; + for (SCSIZE i=0; i<nColCount; i++) + { + ppCount[i] = new double[nRowCount]; + ppSum[i] = new double[nRowCount]; + ppSumSqr[i] = new double[nRowCount]; + } + } + + if (nColCount && !ppUsed) + { + ppUsed = new BOOL*[nColCount]; + for (SCSIZE i=0; i<nColCount; i++) + { + ppUsed[i] = new BOOL[nRowCount]; + memset( ppUsed[i], 0, nRowCount * sizeof(BOOL) ); + } + } + + if (nRowCount && nDataCount && !ppTitlePos) + { + ppTitlePos = new SCSIZE*[nRowCount]; + for (SCSIZE i=0; i<nRowCount; i++) + { + ppTitlePos[i] = new SCSIZE[nDataCount]; + memset( ppTitlePos[i], 0, nDataCount * sizeof(SCSIZE) ); //! unnoetig ? + } + } + + // CornerText: einzelner String +} + +void ScConsData::DoneFields() +{ + InitData(FALSE); +} + +void ScConsData::SetSize( SCCOL nCols, SCROW nRows ) +{ + DeleteData(); + nColCount = static_cast<SCSIZE>(nCols); + nRowCount = static_cast<SCSIZE>(nRows); +} + +void ScConsData::GetSize( SCCOL& rCols, SCROW& rRows ) const +{ + rCols = static_cast<SCCOL>(nColCount); + rRows = static_cast<SCROW>(nRowCount); +} + +void ScConsData::SetFlags( ScSubTotalFunc eFunc, BOOL bColName, BOOL bRowName, BOOL bRef ) +{ + DeleteData(); + bReference = bRef; + bColByName = bColName; + if (bColName) nColCount = 0; + bRowByName = bRowName; + if (bRowName) nRowCount = 0; + eFunction = eFunc; +} + +void ScConsData::AddFields( ScDocument* pSrcDoc, SCTAB nTab, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) +{ + ++nDataCount; + + String aTitle; + + SCCOL nStartCol = nCol1; + SCROW nStartRow = nRow1; + if (bColByName) ++nStartRow; + if (bRowByName) ++nStartCol; + + if (bColByName) + { + for (SCCOL nCol=nStartCol; nCol<=nCol2; nCol++) + { + pSrcDoc->GetString( nCol, nRow1, nTab, aTitle ); + if (aTitle.Len()) + { + BOOL bFound = FALSE; + for (SCSIZE i=0; i<nColCount && !bFound; i++) + if ( *ppColHeaders[i] == aTitle ) + bFound = TRUE; + if (!bFound) + lcl_AddString( ppColHeaders, nColCount, aTitle ); + } + } + } + + if (bRowByName) + { + for (SCROW nRow=nStartRow; nRow<=nRow2; nRow++) + { + pSrcDoc->GetString( nCol1, nRow, nTab, aTitle ); + if (aTitle.Len()) + { + BOOL bFound = FALSE; + for (SCSIZE i=0; i<nRowCount && !bFound; i++) + if ( *ppRowHeaders[i] == aTitle ) + bFound = TRUE; + if (!bFound) + lcl_AddString( ppRowHeaders, nRowCount, aTitle ); + } + } + } +} + +void ScConsData::AddName( const String& rName ) +{ + SCSIZE nArrX; + SCSIZE nArrY; + + if (bReference) + { + lcl_AddString( ppTitles, nTitleCount, rName ); + + for (nArrY=0; nArrY<nRowCount; nArrY++) + { + // Daten auf gleiche Laenge bringen + + SCSIZE nMax = 0; + for (nArrX=0; nArrX<nColCount; nArrX++) + if (ppUsed[nArrX][nArrY]) + nMax = Max( nMax, ppRefs[nArrX][nArrY].GetCount() ); + + for (nArrX=0; nArrX<nColCount; nArrX++) + { + if (!ppUsed[nArrX][nArrY]) + { + ppUsed[nArrX][nArrY] = TRUE; + ppRefs[nArrX][nArrY].Init(); + } + ppRefs[nArrX][nArrY].SetFullSize(nMax); + } + + // Positionen eintragen + + if (ppTitlePos) + if (nTitleCount < nDataCount) + ppTitlePos[nArrY][nTitleCount] = nMax; + } + } +} + + // rCount < 0 <=> Fehler aufgetreten + +void lcl_UpdateArray( ScSubTotalFunc eFunc, + double& rCount, double& rSum, double& rSumSqr, double nVal ) +{ + if (rCount < 0.0) + return; + switch (eFunc) + { + case SUBTOTAL_FUNC_SUM: + if (!SubTotal::SafePlus(rSum, nVal)) + rCount = -MAXDOUBLE; + break; + case SUBTOTAL_FUNC_PROD: + if (!SubTotal::SafeMult(rSum, nVal)) + rCount = -MAXDOUBLE; + break; + case SUBTOTAL_FUNC_CNT: + case SUBTOTAL_FUNC_CNT2: + rCount += 1.0; + break; + case SUBTOTAL_FUNC_AVE: + if (!SubTotal::SafePlus(rSum, nVal)) + rCount = -MAXDOUBLE; + else + rCount += 1.0; + break; + case SUBTOTAL_FUNC_MAX: + if (nVal > rSum) + rSum = nVal; + break; + case SUBTOTAL_FUNC_MIN: + if (nVal < rSum) + rSum = nVal; + break; + case SUBTOTAL_FUNC_STD: + case SUBTOTAL_FUNC_STDP: + case SUBTOTAL_FUNC_VAR: + case SUBTOTAL_FUNC_VARP: + { + BOOL bOk = SubTotal::SafePlus(rSum, nVal); + bOk = bOk && SubTotal::SafeMult(nVal, nVal); + bOk = bOk && SubTotal::SafePlus(rSumSqr, nVal); + if (!bOk) + rCount = -MAXDOUBLE; + else + rCount += 1.0; + break; + } + default: + { + // added to avoid warnings + } + } +} + +void lcl_InitArray( ScSubTotalFunc eFunc, + double& rCount, double& rSum, double& rSumSqr, double nVal ) +{ + rCount = 1.0; + switch (eFunc) + { + case SUBTOTAL_FUNC_SUM: + case SUBTOTAL_FUNC_MAX: + case SUBTOTAL_FUNC_MIN: + case SUBTOTAL_FUNC_PROD: + case SUBTOTAL_FUNC_AVE: + rSum = nVal; + break; + case SUBTOTAL_FUNC_STD: + case SUBTOTAL_FUNC_STDP: + case SUBTOTAL_FUNC_VAR: + case SUBTOTAL_FUNC_VARP: + { + rSum = nVal; + BOOL bOk = SubTotal::SafeMult(nVal, nVal); + if (bOk) + rSumSqr = nVal; + else + rCount = -MAXDOUBLE; + } + break; + default: + break; + } +} + +double lcl_CalcData( ScSubTotalFunc eFunc, + double fCount, double fSum, double fSumSqr) +{ + if (fCount < 0.0) + return 0.0; + double fVal = 0.0; + switch (eFunc) + { + case SUBTOTAL_FUNC_CNT: + case SUBTOTAL_FUNC_CNT2: + fVal = fCount; + break; + case SUBTOTAL_FUNC_SUM: + case SUBTOTAL_FUNC_MAX: + case SUBTOTAL_FUNC_MIN: + case SUBTOTAL_FUNC_PROD: + fVal = fSum; + break; + case SUBTOTAL_FUNC_AVE: + if (fCount > 0.0) + fVal = fSum / fCount; + else + fCount = -MAXDOUBLE; + break; + case SUBTOTAL_FUNC_STD: + { + if (fCount > 1 && SubTotal::SafeMult(fSum, fSum)) + fVal = sqrt((fSumSqr - fSum/fCount)/(fCount-1.0)); + else + fCount = -MAXDOUBLE; + } + break; + case SUBTOTAL_FUNC_STDP: + { + if (fCount > 0 && SubTotal::SafeMult(fSum, fSum)) + fVal = sqrt((fSumSqr - fSum/fCount)/fCount); + else + fCount = -MAXDOUBLE; + } + break; + case SUBTOTAL_FUNC_VAR: + { + if (fCount > 1 && SubTotal::SafeMult(fSum, fSum)) + fVal = (fSumSqr - fSum/fCount)/(fCount-1.0); + else + fCount = -MAXDOUBLE; + } + break; + case SUBTOTAL_FUNC_VARP: + { + if (fCount > 0 && SubTotal::SafeMult(fSum, fSum)) + fVal = (fSumSqr - fSum/fCount)/fCount; + else + fCount = -MAXDOUBLE; + } + break; + default: + { + DBG_ERROR("unbekannte Funktion bei Consoli::CalcData"); + fCount = -MAXDOUBLE; + } + break; + } + return fVal; +} + +void ScConsData::AddData( ScDocument* pSrcDoc, SCTAB nTab, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) +{ + PutInOrder(nCol1,nCol2); + PutInOrder(nRow1,nRow2); + if ( nCol2 >= sal::static_int_cast<SCCOL>(nCol1 + nColCount) && !bColByName ) + { + DBG_ASSERT(0,"Bereich zu gross"); + nCol2 = sal::static_int_cast<SCCOL>( nCol1 + nColCount - 1 ); + } + if ( nRow2 >= sal::static_int_cast<SCROW>(nRow1 + nRowCount) && !bRowByName ) + { + DBG_ASSERT(0,"Bereich zu gross"); + nRow2 = sal::static_int_cast<SCROW>( nRow1 + nRowCount - 1 ); + } + + SCCOL nCol; + SCROW nRow; + + // Ecke links oben + + if ( bColByName && bRowByName ) + { + String aThisCorner; + pSrcDoc->GetString(nCol1,nRow1,nTab,aThisCorner); + if (bCornerUsed) + { + if (aCornerText != aThisCorner) + aCornerText.Erase(); + } + else + { + aCornerText = aThisCorner; + bCornerUsed = TRUE; + } + } + + // Titel suchen + + SCCOL nStartCol = nCol1; + SCROW nStartRow = nRow1; + if (bColByName) ++nStartRow; + if (bRowByName) ++nStartCol; + String aTitle; + SCCOL* pDestCols = NULL; + SCROW* pDestRows = NULL; + if (bColByName) + { + pDestCols = new SCCOL[nCol2-nStartCol+1]; + for (nCol=nStartCol; nCol<=nCol2; nCol++) + { + pSrcDoc->GetString(nCol,nRow1,nTab,aTitle); + SCCOL nPos = SC_CONS_NOTFOUND; + if (aTitle.Len()) + { + BOOL bFound = FALSE; + for (SCSIZE i=0; i<nColCount && !bFound; i++) + if ( *ppColHeaders[i] == aTitle ) + { + nPos = static_cast<SCCOL>(i); + bFound = TRUE; + } + DBG_ASSERT(bFound, "Spalte nicht gefunden"); + } + pDestCols[nCol-nStartCol] = nPos; + } + } + if (bRowByName) + { + pDestRows = new SCROW[nRow2-nStartRow+1]; + for (nRow=nStartRow; nRow<=nRow2; nRow++) + { + pSrcDoc->GetString(nCol1,nRow,nTab,aTitle); + SCROW nPos = SC_CONS_NOTFOUND; + if (aTitle.Len()) + { + BOOL bFound = FALSE; + for (SCSIZE i=0; i<nRowCount && !bFound; i++) + if ( *ppRowHeaders[i] == aTitle ) + { + nPos = static_cast<SCROW>(i); + bFound = TRUE; + } + DBG_ASSERT(bFound, "Zeile nicht gefunden"); + } + pDestRows[nRow-nStartRow] = nPos; + } + } + nCol1 = nStartCol; + nRow1 = nStartRow; + + // Daten + + BOOL bAnyCell = ( eFunction == SUBTOTAL_FUNC_CNT2 ); + for (nCol=nCol1; nCol<=nCol2; nCol++) + { + SCCOL nArrX = nCol-nCol1; + if (bColByName) nArrX = pDestCols[nArrX]; + if (nArrX != SC_CONS_NOTFOUND) + { + for (nRow=nRow1; nRow<=nRow2; nRow++) + { + SCROW nArrY = nRow-nRow1; + if (bRowByName) nArrY = pDestRows[nArrY]; + if ( nArrY != SC_CONS_NOTFOUND && ( + bAnyCell ? pSrcDoc->HasData( nCol, nRow, nTab ) + : pSrcDoc->HasValueData( nCol, nRow, nTab ) ) ) + { + if (bReference) + { + if (ppUsed[nArrX][nArrY]) + ppRefs[nArrX][nArrY].AddEntry( nCol, nRow, nTab ); + else + { + ppUsed[nArrX][nArrY] = TRUE; + ppRefs[nArrX][nArrY].Init(); + ppRefs[nArrX][nArrY].AddEntry( nCol, nRow, nTab ); + } + } + else + { + double nVal; + pSrcDoc->GetValue( nCol, nRow, nTab, nVal ); + if (ppUsed[nArrX][nArrY]) + lcl_UpdateArray( eFunction, ppCount[nArrX][nArrY], + ppSum[nArrX][nArrY], ppSumSqr[nArrX][nArrY], + nVal); + else + { + ppUsed[nArrX][nArrY] = TRUE; + lcl_InitArray( eFunction, ppCount[nArrX][nArrY], + ppSum[nArrX][nArrY], + ppSumSqr[nArrX][nArrY], nVal ); + } + } + } + } + } + } + + delete[] pDestCols; + delete[] pDestRows; +} + +// vorher testen, wieviele Zeilen eingefuegt werden (fuer Undo) + +SCROW ScConsData::GetInsertCount() const +{ + SCROW nInsert = 0; + SCSIZE nArrX; + SCSIZE nArrY; + if ( ppRefs && ppUsed ) + { + for (nArrY=0; nArrY<nRowCount; nArrY++) + { + SCSIZE nNeeded = 0; + for (nArrX=0; nArrX<nColCount; nArrX++) + if (ppUsed[nArrX][nArrY]) + nNeeded = Max( nNeeded, ppRefs[nArrX][nArrY].GetCount() ); + + nInsert += nNeeded; + } + } + return nInsert; +} + +// fertige Daten ins Dokument schreiben +//! optimieren nach Spalten? + +void ScConsData::OutputToDocument( ScDocument* pDestDoc, SCCOL nCol, SCROW nRow, SCTAB nTab ) +{ + OpCode eOpCode = eOpCodeTable[eFunction]; + + SCSIZE nArrX; + SCSIZE nArrY; + + // Ecke links oben + + if ( bColByName && bRowByName && aCornerText.Len() ) + pDestDoc->SetString( nCol, nRow, nTab, aCornerText ); + + // Titel + + SCCOL nStartCol = nCol; + SCROW nStartRow = nRow; + if (bColByName) ++nStartRow; + if (bRowByName) ++nStartCol; + + if (bColByName) + for (SCSIZE i=0; i<nColCount; i++) + pDestDoc->SetString( sal::static_int_cast<SCCOL>(nStartCol+i), nRow, nTab, *ppColHeaders[i] ); + if (bRowByName) + for (SCSIZE j=0; j<nRowCount; j++) + pDestDoc->SetString( nCol, sal::static_int_cast<SCROW>(nStartRow+j), nTab, *ppRowHeaders[j] ); + + nCol = nStartCol; + nRow = nStartRow; + + // Daten + + if ( ppCount && ppUsed ) // Werte direkt einfuegen + { + for (nArrX=0; nArrX<nColCount; nArrX++) + for (nArrY=0; nArrY<nRowCount; nArrY++) + if (ppUsed[nArrX][nArrY]) + { + double fVal = lcl_CalcData( eFunction, ppCount[nArrX][nArrY], + ppSum[nArrX][nArrY], + ppSumSqr[nArrX][nArrY]); + if (ppCount[nArrX][nArrY] < 0.0) + pDestDoc->SetError( sal::static_int_cast<SCCOL>(nCol+nArrX), + sal::static_int_cast<SCROW>(nRow+nArrY), nTab, errNoValue ); + else + pDestDoc->SetValue( sal::static_int_cast<SCCOL>(nCol+nArrX), + sal::static_int_cast<SCROW>(nRow+nArrY), nTab, fVal ); + } + } + + if ( ppRefs && ppUsed ) // Referenzen einfuegen + { + //! unterscheiden, ob nach Kategorien aufgeteilt + String aString; + + ScSingleRefData aSRef; // Daten fuer Referenz-Formelzellen + aSRef.InitFlags(); + aSRef.SetFlag3D(TRUE); + + ScComplexRefData aCRef; // Daten fuer Summen-Zellen + aCRef.InitFlags(); + aCRef.Ref1.SetColRel(TRUE); aCRef.Ref1.SetRowRel(TRUE); aCRef.Ref1.SetTabRel(TRUE); + aCRef.Ref2.SetColRel(TRUE); aCRef.Ref2.SetRowRel(TRUE); aCRef.Ref2.SetTabRel(TRUE); + + for (nArrY=0; nArrY<nRowCount; nArrY++) + { + SCSIZE nNeeded = 0; + for (nArrX=0; nArrX<nColCount; nArrX++) + if (ppUsed[nArrX][nArrY]) + nNeeded = Max( nNeeded, ppRefs[nArrX][nArrY].GetCount() ); + + if (nNeeded) + { + pDestDoc->InsertRow( 0,nTab, MAXCOL,nTab, nRow+nArrY, nNeeded ); + + for (nArrX=0; nArrX<nColCount; nArrX++) + if (ppUsed[nArrX][nArrY]) + { + ScReferenceList& rList = ppRefs[nArrX][nArrY]; + SCSIZE nCount = rList.GetCount(); + if (nCount) + { + for (SCSIZE nPos=0; nPos<nCount; nPos++) + { + ScReferenceEntry aRef = rList.GetEntry(nPos); + if (aRef.nTab != SC_CONS_NOTFOUND) + { + // Referenz einfuegen (absolut, 3d) + + aSRef.nCol = aRef.nCol; + aSRef.nRow = aRef.nRow; + aSRef.nTab = aRef.nTab; + + ScTokenArray aRefArr; + aRefArr.AddSingleReference(aSRef); + aRefArr.AddOpCode(ocStop); + ScAddress aDest( sal::static_int_cast<SCCOL>(nCol+nArrX), + sal::static_int_cast<SCROW>(nRow+nArrY+nPos), nTab ); + ScBaseCell* pCell = new ScFormulaCell( pDestDoc, aDest, &aRefArr ); + pDestDoc->PutCell( aDest.Col(), aDest.Row(), aDest.Tab(), pCell ); + } + } + + // Summe einfuegen (relativ, nicht 3d) + + ScAddress aDest( sal::static_int_cast<SCCOL>(nCol+nArrX), + sal::static_int_cast<SCROW>(nRow+nArrY+nNeeded), nTab ); + + aCRef.Ref1.nTab = aCRef.Ref2.nTab = nTab; + aCRef.Ref1.nCol = aCRef.Ref2.nCol = sal::static_int_cast<SCsCOL>( nCol+nArrX ); + aCRef.Ref1.nRow = nRow+nArrY; + aCRef.Ref2.nRow = nRow+nArrY+nNeeded-1; + aCRef.CalcRelFromAbs( aDest ); + + ScTokenArray aArr; + aArr.AddOpCode(eOpCode); // ausgewaehlte Funktion + aArr.AddOpCode(ocOpen); + aArr.AddDoubleReference(aCRef); + aArr.AddOpCode(ocClose); + aArr.AddOpCode(ocStop); + ScBaseCell* pCell = new ScFormulaCell( pDestDoc, aDest, &aArr ); + pDestDoc->PutCell( aDest.Col(), aDest.Row(), aDest.Tab(), pCell ); + } + } + + // Gliederung einfuegen + + ScOutlineArray* pOutArr = pDestDoc->GetOutlineTable( nTab, TRUE )->GetRowArray(); + SCROW nOutStart = nRow+nArrY; + SCROW nOutEnd = nRow+nArrY+nNeeded-1; + BOOL bSize = FALSE; + pOutArr->Insert( nOutStart, nOutEnd, bSize ); + for (SCROW nOutRow=nOutStart; nOutRow<=nOutEnd; nOutRow++) + pDestDoc->ShowRow( nOutRow, nTab, FALSE ); + pDestDoc->UpdateOutlineRow( nOutStart, nOutEnd, nTab, FALSE ); + + // Zwischentitel + + if (ppTitlePos && ppTitles && ppRowHeaders) + { + String aDelim( RTL_CONSTASCII_USTRINGPARAM(" / ") ); + for (SCSIZE nPos=0; nPos<nDataCount; nPos++) + { + SCSIZE nTPos = ppTitlePos[nArrY][nPos]; + BOOL bDo = TRUE; + if (nPos+1<nDataCount) + if (ppTitlePos[nArrY][nPos+1] == nTPos) + bDo = FALSE; // leer + if ( bDo && nTPos < nNeeded ) + { + aString = *ppRowHeaders[nArrY]; + aString += aDelim; + aString += *ppTitles[nPos]; + pDestDoc->SetString( nCol-1, nRow+nArrY+nTPos, nTab, aString ); + } + } + } + + nRow += nNeeded; + } + } + } +} + + + + + diff --git a/sc/source/core/tool/dbcolect.cxx b/sc/source/core/tool/dbcolect.cxx new file mode 100644 index 000000000000..7f94cb64c827 --- /dev/null +++ b/sc/source/core/tool/dbcolect.cxx @@ -0,0 +1,891 @@ +/************************************************************************* + * + * 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 <tools/debug.hxx> +#include <unotools/transliterationwrapper.hxx> + +#include "dbcolect.hxx" +#include "global.hxx" +#include "refupdat.hxx" +#include "rechead.hxx" +#include "document.hxx" +#include "queryparam.hxx" +#include "globstr.hrc" + + +//--------------------------------------------------------------------------------------- + +ScDBData::ScDBData( const String& rName, + SCTAB nTab, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + BOOL bByR, BOOL bHasH) : + aName (rName), + nTable (nTab), + nStartCol (nCol1), + nStartRow (nRow1), + nEndCol (nCol2), + nEndRow (nRow2), + bByRow (bByR), + bHasHeader (bHasH), + bDoSize (FALSE), + bKeepFmt (FALSE), + bStripData (FALSE), + bIsAdvanced (FALSE), + bDBSelection(FALSE), + nIndex (0), + bAutoFilter (FALSE), + bModified (FALSE) +{ + USHORT i; + + ScSortParam aSortParam; + ScQueryParam aQueryParam; + ScSubTotalParam aSubTotalParam; + ScImportParam aImportParam; + + for (i=0; i<MAXQUERY; i++) + pQueryStr[i] = new String; + + for (i=0; i<MAXSUBTOTAL; i++) + { + nSubTotals[i] = 0; + pSubTotals[i] = NULL; + pFunctions[i] = NULL; + } + + SetSortParam( aSortParam ); + SetQueryParam( aQueryParam ); + SetSubTotalParam( aSubTotalParam ); + SetImportParam( aImportParam ); +} + +ScDBData::ScDBData( const ScDBData& rData ) : + ScDataObject(), + ScRefreshTimer ( rData ), + aName (rData.aName), + nTable (rData.nTable), + nStartCol (rData.nStartCol), + nStartRow (rData.nStartRow), + nEndCol (rData.nEndCol), + nEndRow (rData.nEndRow), + bByRow (rData.bByRow), + bHasHeader (rData.bHasHeader), + bDoSize (rData.bDoSize), + bKeepFmt (rData.bKeepFmt), + bStripData (rData.bStripData), + bSortCaseSens (rData.bSortCaseSens), + bIncludePattern (rData.bIncludePattern), + bSortInplace (rData.bSortInplace), + bSortUserDef (rData.bSortUserDef), + nSortUserIndex (rData.nSortUserIndex), + nSortDestTab (rData.nSortDestTab), + nSortDestCol (rData.nSortDestCol), + nSortDestRow (rData.nSortDestRow), + aSortLocale (rData.aSortLocale), + aSortAlgorithm (rData.aSortAlgorithm), + bQueryInplace (rData.bQueryInplace), + bQueryCaseSens (rData.bQueryCaseSens), + bQueryRegExp (rData.bQueryRegExp), + bQueryDuplicate (rData.bQueryDuplicate), + nQueryDestTab (rData.nQueryDestTab), + nQueryDestCol (rData.nQueryDestCol), + nQueryDestRow (rData.nQueryDestRow), + bIsAdvanced (rData.bIsAdvanced), + aAdvSource (rData.aAdvSource), + bSubRemoveOnly (rData.bSubRemoveOnly), + bSubReplace (rData.bSubReplace), + bSubPagebreak (rData.bSubPagebreak), + bSubCaseSens (rData.bSubCaseSens), + bSubDoSort (rData.bSubDoSort), + bSubAscending (rData.bSubAscending), + bSubIncludePattern (rData.bSubIncludePattern), + bSubUserDef (rData.bSubUserDef), + nSubUserIndex (rData.nSubUserIndex), + bDBImport (rData.bDBImport), + aDBName (rData.aDBName), + aDBStatement (rData.aDBStatement), + bDBNative (rData.bDBNative), + bDBSelection (rData.bDBSelection), + bDBSql (rData.bDBSql), + nDBType (rData.nDBType), + nIndex (rData.nIndex), + bAutoFilter (rData.bAutoFilter), + bModified (rData.bModified) +{ + USHORT i; + USHORT j; + + for (i=0; i<MAXSORT; i++) + { + bDoSort[i] = rData.bDoSort[i]; + nSortField[i] = rData.nSortField[i]; + bAscending[i] = rData.bAscending[i]; + } + for (i=0; i<MAXQUERY; i++) + { + bDoQuery[i] = rData.bDoQuery[i]; + nQueryField[i] = rData.nQueryField[i]; + eQueryOp[i] = rData.eQueryOp[i]; + bQueryByString[i] = rData.bQueryByString[i]; + bQueryByDate[i] = rData.bQueryByDate[i]; + pQueryStr[i] = new String( *(rData.pQueryStr[i]) ); + nQueryVal[i] = rData.nQueryVal[i]; + eQueryConnect[i] = rData.eQueryConnect[i]; + } + for (i=0; i<MAXSUBTOTAL; i++) + { + bDoSubTotal[i] = rData.bDoSubTotal[i]; + nSubField[i] = rData.nSubField[i]; + + SCCOL nCount = rData.nSubTotals[i]; + nSubTotals[i] = nCount; + pFunctions[i] = nCount > 0 ? new ScSubTotalFunc [nCount] : NULL; + pSubTotals[i] = nCount > 0 ? new SCCOL [nCount] : NULL; + + for (j=0; j<nCount; j++) + { + pSubTotals[i][j] = rData.pSubTotals[i][j]; + pFunctions[i][j] = rData.pFunctions[i][j]; + } + } +} + +ScDBData& ScDBData::operator= (const ScDBData& rData) +{ + USHORT i; + USHORT j; + + ScRefreshTimer::operator=( rData ); + aName = rData.aName; + nTable = rData.nTable; + nStartCol = rData.nStartCol; + nStartRow = rData.nStartRow; + nEndCol = rData.nEndCol; + nEndRow = rData.nEndRow; + bByRow = rData.bByRow; + bHasHeader = rData.bHasHeader; + bDoSize = rData.bDoSize; + bKeepFmt = rData.bKeepFmt; + bStripData = rData.bStripData; + bSortCaseSens = rData.bSortCaseSens; + bIncludePattern = rData.bIncludePattern; + bSortInplace = rData.bSortInplace; + nSortDestTab = rData.nSortDestTab; + nSortDestCol = rData.nSortDestCol; + nSortDestRow = rData.nSortDestRow; + bSortUserDef = rData.bSortUserDef; + nSortUserIndex = rData.nSortUserIndex; + aSortLocale = rData.aSortLocale; + aSortAlgorithm = rData.aSortAlgorithm; + bQueryInplace = rData.bQueryInplace; + bQueryCaseSens = rData.bQueryCaseSens; + bQueryRegExp = rData.bQueryRegExp; + bQueryDuplicate = rData.bQueryDuplicate; + nQueryDestTab = rData.nQueryDestTab; + nQueryDestCol = rData.nQueryDestCol; + nQueryDestRow = rData.nQueryDestRow; + bIsAdvanced = rData.bIsAdvanced; + aAdvSource = rData.aAdvSource; + bSubRemoveOnly = rData.bSubRemoveOnly; + bSubReplace = rData.bSubReplace; + bSubPagebreak = rData.bSubPagebreak; + bSubCaseSens = rData.bSubCaseSens; + bSubDoSort = rData.bSubDoSort; + bSubAscending = rData.bSubAscending; + bSubIncludePattern = rData.bSubIncludePattern; + bSubUserDef = rData.bSubUserDef; + nSubUserIndex = rData.nSubUserIndex; + bDBImport = rData.bDBImport; + aDBName = rData.aDBName; + aDBStatement = rData.aDBStatement; + bDBNative = rData.bDBNative; + bDBSelection = rData.bDBSelection; + bDBSql = rData.bDBSql; + nDBType = rData.nDBType; + nIndex = rData.nIndex; + bAutoFilter = rData.bAutoFilter; + + for (i=0; i<MAXSORT; i++) + { + bDoSort[i] = rData.bDoSort[i]; + nSortField[i] = rData.nSortField[i]; + bAscending[i] = rData.bAscending[i]; + } + for (i=0; i<MAXQUERY; i++) + { + bDoQuery[i] = rData.bDoQuery[i]; + nQueryField[i] = rData.nQueryField[i]; + eQueryOp[i] = rData.eQueryOp[i]; + bQueryByString[i] = rData.bQueryByString[i]; + bQueryByDate[i] = rData.bQueryByDate[i]; + *pQueryStr[i] = *rData.pQueryStr[i]; + nQueryVal[i] = rData.nQueryVal[i]; + eQueryConnect[i] = rData.eQueryConnect[i]; + } + for (i=0; i<MAXSUBTOTAL; i++) + { + bDoSubTotal[i] = rData.bDoSubTotal[i]; + nSubField[i] = rData.nSubField[i]; + SCCOL nCount = rData.nSubTotals[i]; + nSubTotals[i] = nCount; + + delete[] pSubTotals[i]; + delete[] pFunctions[i]; + + pSubTotals[i] = nCount > 0 ? new SCCOL [nCount] : NULL; + pFunctions[i] = nCount > 0 ? new ScSubTotalFunc [nCount] : NULL; + for (j=0; j<nCount; j++) + { + pSubTotals[i][j] = rData.pSubTotals[i][j]; + pFunctions[i][j] = rData.pFunctions[i][j]; + } + } + + return *this; +} + +BOOL ScDBData::operator== (const ScDBData& rData) const +{ + // Daten, die nicht in den Params sind + + if ( nTable != rData.nTable || + bDoSize != rData.bDoSize || + bKeepFmt != rData.bKeepFmt || + bIsAdvanced!= rData.bIsAdvanced|| + bStripData != rData.bStripData || +// SAB: I think this should be here, but I don't want to break something +// bAutoFilter!= rData.bAutoFilter|| + ScRefreshTimer::operator!=( rData ) + ) + return FALSE; + + if ( bIsAdvanced && aAdvSource != rData.aAdvSource ) + return FALSE; + + ScSortParam aSort1, aSort2; + GetSortParam(aSort1); + rData.GetSortParam(aSort2); + if (!(aSort1 == aSort2)) + return FALSE; + + ScQueryParam aQuery1, aQuery2; + GetQueryParam(aQuery1); + rData.GetQueryParam(aQuery2); + if (!(aQuery1 == aQuery2)) + return FALSE; + + ScSubTotalParam aSubTotal1, aSubTotal2; + GetSubTotalParam(aSubTotal1); + rData.GetSubTotalParam(aSubTotal2); + if (!(aSubTotal1 == aSubTotal2)) + return FALSE; + + ScImportParam aImport1, aImport2; + GetImportParam(aImport1); + rData.GetImportParam(aImport2); + if (!(aImport1 == aImport2)) + return FALSE; + + return TRUE; +} + +ScDBData::~ScDBData() +{ + StopRefreshTimer(); + USHORT i; + + for (i=0; i<MAXQUERY; i++) + delete pQueryStr[i]; + for (i=0; i<MAXSUBTOTAL; i++) + { + delete[] pSubTotals[i]; + delete[] pFunctions[i]; + } +} + +//UNUSED2008-05 BOOL ScDBData::IsBeyond(SCROW nMaxRow) const +//UNUSED2008-05 { +//UNUSED2008-05 return ( nStartRow > nMaxRow || +//UNUSED2008-05 nEndRow > nMaxRow || +//UNUSED2008-05 nQueryDestRow > nMaxRow ); +//UNUSED2008-05 } + +String ScDBData::GetSourceString() const +{ + String aVal; + if (bDBImport) + { + aVal = aDBName; + aVal += '/'; + aVal += aDBStatement; + } + return aVal; +} + +String ScDBData::GetOperations() const +{ + String aVal; + if (bDoQuery[0]) + aVal = ScGlobal::GetRscString(STR_OPERATION_FILTER); + + if (bDoSort[0]) + { + if (aVal.Len()) + aVal.AppendAscii( RTL_CONSTASCII_STRINGPARAM(", ") ); + aVal += ScGlobal::GetRscString(STR_OPERATION_SORT); + } + + if (bDoSubTotal[0] && !bSubRemoveOnly) + { + if (aVal.Len()) + aVal.AppendAscii( RTL_CONSTASCII_STRINGPARAM(", ") ); + aVal += ScGlobal::GetRscString(STR_OPERATION_SUBTOTAL); + } + + if (!aVal.Len()) + aVal = ScGlobal::GetRscString(STR_OPERATION_NONE); + + return aVal; +} + +void ScDBData::GetArea(SCTAB& rTab, SCCOL& rCol1, SCROW& rRow1, SCCOL& rCol2, SCROW& rRow2) const +{ + rTab = nTable; + rCol1 = nStartCol; + rRow1 = nStartRow; + rCol2 = nEndCol; + rRow2 = nEndRow; +} + +void ScDBData::GetArea(ScRange& rRange) const +{ + rRange = ScRange( nStartCol,nStartRow,nTable, nEndCol,nEndRow,nTable ); +} + +void ScDBData::SetArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) +{ + nTable = nTab; + nStartCol = nCol1; + nStartRow = nRow1; + nEndCol = nCol2; + nEndRow = nRow2; +} + +void ScDBData::MoveTo(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) +{ + USHORT i; + long nDifX = ((long) nCol1) - ((long) nStartCol); + long nDifY = ((long) nRow1) - ((long) nStartRow); + + long nSortDif = bByRow ? nDifX : nDifY; + long nSortEnd = bByRow ? static_cast<long>(nCol2) : static_cast<long>(nRow2); + + for (i=0; i<MAXSORT; i++) + { + nSortField[i] += nSortDif; + if (nSortField[i] > nSortEnd) + { + nSortField[i] = 0; + bDoSort[i] = FALSE; + } + } + for (i=0; i<MAXQUERY; i++) + { + nQueryField[i] += nDifX; + if (nQueryField[i] > nCol2) + { + nQueryField[i] = 0; + bDoQuery[i] = FALSE; + } + } + for (i=0; i<MAXSUBTOTAL; i++) + { + nSubField[i] = sal::static_int_cast<SCCOL>( nSubField[i] + nDifX ); + if (nSubField[i] > nCol2) + { + nSubField[i] = 0; + bDoSubTotal[i] = FALSE; + } + } + + SetArea( nTab, nCol1, nRow1, nCol2, nRow2 ); +} + +void ScDBData::GetSortParam( ScSortParam& rSortParam ) const +{ + rSortParam.nCol1 = nStartCol; + rSortParam.nRow1 = nStartRow; + rSortParam.nCol2 = nEndCol; + rSortParam.nRow2 = nEndRow; + rSortParam.bByRow = bByRow; + rSortParam.bHasHeader = bHasHeader; + rSortParam.bCaseSens = bSortCaseSens; + rSortParam.bInplace = bSortInplace; + rSortParam.nDestTab = nSortDestTab; + rSortParam.nDestCol = nSortDestCol; + rSortParam.nDestRow = nSortDestRow; + rSortParam.bIncludePattern = bIncludePattern; + rSortParam.bUserDef = bSortUserDef; + rSortParam.nUserIndex = nSortUserIndex; + for (USHORT i=0; i<MAXSORT; i++) + { + rSortParam.bDoSort[i] = bDoSort[i]; + rSortParam.nField[i] = nSortField[i]; + rSortParam.bAscending[i] = bAscending[i]; + } + rSortParam.aCollatorLocale = aSortLocale; + rSortParam.aCollatorAlgorithm = aSortAlgorithm; +} + +void ScDBData::SetSortParam( const ScSortParam& rSortParam ) +{ + bSortCaseSens = rSortParam.bCaseSens; + bIncludePattern = rSortParam.bIncludePattern; + bSortInplace = rSortParam.bInplace; + nSortDestTab = rSortParam.nDestTab; + nSortDestCol = rSortParam.nDestCol; + nSortDestRow = rSortParam.nDestRow; + bSortUserDef = rSortParam.bUserDef; + nSortUserIndex = rSortParam.nUserIndex; + for (USHORT i=0; i<MAXSORT; i++) + { + bDoSort[i] = rSortParam.bDoSort[i]; + nSortField[i] = rSortParam.nField[i]; + bAscending[i] = rSortParam.bAscending[i]; + } + aSortLocale = rSortParam.aCollatorLocale; + aSortAlgorithm = rSortParam.aCollatorAlgorithm; + + //#98317#; set the orientation + bByRow = rSortParam.bByRow; +} + +void ScDBData::GetQueryParam( ScQueryParam& rQueryParam ) const +{ + rQueryParam.nCol1 = nStartCol; + rQueryParam.nRow1 = nStartRow; + rQueryParam.nCol2 = nEndCol; + rQueryParam.nRow2 = nEndRow; + rQueryParam.nTab = nTable; + rQueryParam.bByRow = bByRow; + rQueryParam.bHasHeader = bHasHeader; + rQueryParam.bInplace = bQueryInplace; + rQueryParam.bCaseSens = bQueryCaseSens; + rQueryParam.bRegExp = bQueryRegExp; + rQueryParam.bDuplicate = bQueryDuplicate; + rQueryParam.nDestTab = nQueryDestTab; + rQueryParam.nDestCol = nQueryDestCol; + rQueryParam.nDestRow = nQueryDestRow; + + rQueryParam.Resize( MAXQUERY ); + for (SCSIZE i=0; i<MAXQUERY; i++) + { + ScQueryEntry& rEntry = rQueryParam.GetEntry(i); + + rEntry.bDoQuery = bDoQuery[i]; + rEntry.nField = nQueryField[i]; + rEntry.eOp = eQueryOp[i]; + rEntry.bQueryByString = bQueryByString[i]; + rEntry.bQueryByDate = bQueryByDate[i]; + *rEntry.pStr = *pQueryStr[i]; + rEntry.nVal = nQueryVal[i]; + rEntry.eConnect = eQueryConnect[i]; + } +} + +void ScDBData::SetQueryParam(const ScQueryParam& rQueryParam) +{ + DBG_ASSERT( rQueryParam.GetEntryCount() <= MAXQUERY || + !rQueryParam.GetEntry(MAXQUERY).bDoQuery, + "zuviele Eintraege bei ScDBData::SetQueryParam" ); + + // set bIsAdvanced to FALSE for everything that is not from the + // advanced filter dialog + bIsAdvanced = FALSE; + + bQueryInplace = rQueryParam.bInplace; + bQueryCaseSens = rQueryParam.bCaseSens; + bQueryRegExp = rQueryParam.bRegExp; + bQueryDuplicate = rQueryParam.bDuplicate; + nQueryDestTab = rQueryParam.nDestTab; + nQueryDestCol = rQueryParam.nDestCol; + nQueryDestRow = rQueryParam.nDestRow; + for (SCSIZE i=0; i<MAXQUERY; i++) + { + ScQueryEntry& rEntry = rQueryParam.GetEntry(i); + + bDoQuery[i] = rEntry.bDoQuery; + nQueryField[i] = rEntry.nField; + eQueryOp[i] = rEntry.eOp; + bQueryByString[i] = rEntry.bQueryByString; + bQueryByDate[i] = rEntry.bQueryByDate; + *pQueryStr[i] = *rEntry.pStr; + nQueryVal[i] = rEntry.nVal; + eQueryConnect[i] = rEntry.eConnect; + } +} + +void ScDBData::SetAdvancedQuerySource(const ScRange* pSource) +{ + if (pSource) + { + aAdvSource = *pSource; + bIsAdvanced = TRUE; + } + else + bIsAdvanced = FALSE; +} + +BOOL ScDBData::GetAdvancedQuerySource(ScRange& rSource) const +{ + rSource = aAdvSource; + return bIsAdvanced; +} + +void ScDBData::GetSubTotalParam(ScSubTotalParam& rSubTotalParam) const +{ + USHORT i; + USHORT j; + + rSubTotalParam.nCol1 = nStartCol; + rSubTotalParam.nRow1 = nStartRow; + rSubTotalParam.nCol2 = nEndCol; + rSubTotalParam.nRow2 = nEndRow; + + rSubTotalParam.bRemoveOnly = bSubRemoveOnly; + rSubTotalParam.bReplace = bSubReplace; + rSubTotalParam.bPagebreak = bSubPagebreak; + rSubTotalParam.bCaseSens = bSubCaseSens; + rSubTotalParam.bDoSort = bSubDoSort; + rSubTotalParam.bAscending = bSubAscending; + rSubTotalParam.bIncludePattern = bSubIncludePattern; + rSubTotalParam.bUserDef = bSubUserDef; + rSubTotalParam.nUserIndex = nSubUserIndex; + + for (i=0; i<MAXSUBTOTAL; i++) + { + rSubTotalParam.bGroupActive[i] = bDoSubTotal[i]; + rSubTotalParam.nField[i] = nSubField[i]; + SCCOL nCount = nSubTotals[i]; + + rSubTotalParam.nSubTotals[i] = nCount; + delete[] rSubTotalParam.pSubTotals[i]; + delete[] rSubTotalParam.pFunctions[i]; + rSubTotalParam.pSubTotals[i] = nCount > 0 ? new SCCOL[nCount] : NULL; + rSubTotalParam.pFunctions[i] = nCount > 0 ? new ScSubTotalFunc[nCount] + : NULL; + for (j=0; j<nCount; j++) + { + rSubTotalParam.pSubTotals[i][j] = pSubTotals[i][j]; + rSubTotalParam.pFunctions[i][j] = pFunctions[i][j]; + } + } +} + +void ScDBData::SetSubTotalParam(const ScSubTotalParam& rSubTotalParam) +{ + USHORT i; + USHORT j; + + bSubRemoveOnly = rSubTotalParam.bRemoveOnly; + bSubReplace = rSubTotalParam.bReplace; + bSubPagebreak = rSubTotalParam.bPagebreak; + bSubCaseSens = rSubTotalParam.bCaseSens; + bSubDoSort = rSubTotalParam.bDoSort; + bSubAscending = rSubTotalParam.bAscending; + bSubIncludePattern = rSubTotalParam.bIncludePattern; + bSubUserDef = rSubTotalParam.bUserDef; + nSubUserIndex = rSubTotalParam.nUserIndex; + + for (i=0; i<MAXSUBTOTAL; i++) + { + bDoSubTotal[i] = rSubTotalParam.bGroupActive[i]; + nSubField[i] = rSubTotalParam.nField[i]; + SCCOL nCount = rSubTotalParam.nSubTotals[i]; + + nSubTotals[i] = nCount; + delete[] pSubTotals[i]; + delete[] pFunctions[i]; + pSubTotals[i] = nCount > 0 ? new SCCOL [nCount] : NULL; + pFunctions[i] = nCount > 0 ? new ScSubTotalFunc [nCount] : NULL; + for (j=0; j<nCount; j++) + { + pSubTotals[i][j] = rSubTotalParam.pSubTotals[i][j]; + pFunctions[i][j] = rSubTotalParam.pFunctions[i][j]; + } + } +} + +void ScDBData::GetImportParam(ScImportParam& rImportParam) const +{ + rImportParam.nCol1 = nStartCol; + rImportParam.nRow1 = nStartRow; + rImportParam.nCol2 = nEndCol; + rImportParam.nRow2 = nEndRow; + + rImportParam.bImport = bDBImport; + rImportParam.aDBName = aDBName; + rImportParam.aStatement = aDBStatement; + rImportParam.bNative = bDBNative; + rImportParam.bSql = bDBSql; + rImportParam.nType = nDBType; +} + +void ScDBData::SetImportParam(const ScImportParam& rImportParam) +{ + bDBImport = rImportParam.bImport; + aDBName = rImportParam.aDBName; + aDBStatement = rImportParam.aStatement; + bDBNative = rImportParam.bNative; + bDBSql = rImportParam.bSql; + nDBType = rImportParam.nType; +} + +BOOL ScDBData::IsDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, BOOL bStartOnly) const +{ + if (nTab == nTable) + { + if ( bStartOnly ) + return ( nCol == nStartCol && nRow == nStartRow ); + else + return ( nCol >= nStartCol && nCol <= nEndCol && + nRow >= nStartRow && nRow <= nEndRow ); + } + + return FALSE; +} + +BOOL ScDBData::IsDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const +{ + return (BOOL)((nTab == nTable) + && (nCol1 == nStartCol) && (nRow1 == nStartRow) + && (nCol2 == nEndCol) && (nRow2 == nEndRow)); +} + +ScDataObject* ScDBData::Clone() const +{ + return new ScDBData(*this); +} + + +//--------------------------------------------------------------------------------------- +// Compare zum Sortieren + +short ScDBCollection::Compare(ScDataObject* pKey1, ScDataObject* pKey2) const +{ + const String& rStr1 = ((ScDBData*)pKey1)->GetName(); + const String& rStr2 = ((ScDBData*)pKey2)->GetName(); + return (short) ScGlobal::GetpTransliteration()->compareString( rStr1, rStr2 ); +} + +// IsEqual - alles gleich + +BOOL ScDBCollection::IsEqual(ScDataObject* pKey1, ScDataObject* pKey2) const +{ + return *(ScDBData*)pKey1 == *(ScDBData*)pKey2; +} + +ScDBData* ScDBCollection::GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, BOOL bStartOnly) const +{ + ScDBData* pNoNameData = NULL; + if (pItems) + { + const String& rNoName = ScGlobal::GetRscString( STR_DB_NONAME ); + + for (USHORT i = 0; i < nCount; i++) + if (((ScDBData*)pItems[i])->IsDBAtCursor(nCol, nRow, nTab, bStartOnly)) + { + ScDBData* pDB = (ScDBData*)pItems[i]; + if ( pDB->GetName() == rNoName ) + pNoNameData = pDB; + else + return pDB; + } + } + return pNoNameData; // "unbenannt" nur zurueck, wenn sonst nichts gefunden +} + +ScDBData* ScDBCollection::GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const +{ + ScDBData* pNoNameData = NULL; + if (pItems) + { + const String& rNoName = ScGlobal::GetRscString( STR_DB_NONAME ); + + for (USHORT i = 0; i < nCount; i++) + if (((ScDBData*)pItems[i])->IsDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2)) + { + ScDBData* pDB = (ScDBData*)pItems[i]; + if ( pDB->GetName() == rNoName ) + pNoNameData = pDB; + else + return pDB; + } + } + return pNoNameData; // "unbenannt" nur zurueck, wenn sonst nichts gefunden +} + +BOOL ScDBCollection::SearchName( const String& rName, USHORT& rIndex ) const +{ + ScDBData aDataObj( rName, 0,0,0,0,0 ); + return Search( &aDataObj, rIndex ); +} + +void ScDBCollection::DeleteOnTab( SCTAB nTab ) +{ + USHORT nPos = 0; + while ( nPos < nCount ) + { + // look for output positions on the deleted sheet + + SCCOL nEntryCol1, nEntryCol2; + SCROW nEntryRow1, nEntryRow2; + SCTAB nEntryTab; + static_cast<const ScDBData*>(At(nPos))->GetArea( nEntryTab, nEntryCol1, nEntryRow1, nEntryCol2, nEntryRow2 ); + if ( nEntryTab == nTab ) + AtFree(nPos); + else + ++nPos; + } +} + +void ScDBCollection::UpdateReference(UpdateRefMode eUpdateRefMode, + SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + for (USHORT i=0; i<nCount; i++) + { + SCCOL theCol1; + SCROW theRow1; + SCTAB theTab1; + SCCOL theCol2; + SCROW theRow2; + SCTAB theTab2; + ((ScDBData*)pItems[i])->GetArea( theTab1, theCol1, theRow1, theCol2, theRow2 ); + theTab2 = theTab1; + + BOOL bDoUpdate = ScRefUpdate::Update( pDoc, eUpdateRefMode, + nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz, + theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ) != UR_NOTHING; + if (bDoUpdate) + ((ScDBData*)pItems[i])->MoveTo( theTab1, theCol1, theRow1, theCol2, theRow2 ); + + ScRange aAdvSource; + if ( ((ScDBData*)pItems[i])->GetAdvancedQuerySource(aAdvSource) ) + { + aAdvSource.GetVars( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ); + if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, + nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz, + theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ) ) + { + aAdvSource.aStart.Set( theCol1,theRow1,theTab1 ); + aAdvSource.aEnd.Set( theCol2,theRow2,theTab2 ); + ((ScDBData*)pItems[i])->SetAdvancedQuerySource( &aAdvSource ); + + bDoUpdate = TRUE; // DBData is modified + } + } + + ((ScDBData*)pItems[i])->SetModified(bDoUpdate); + + //! Testen, ob mitten aus dem Bereich geloescht/eingefuegt wurde !!! + } +} + + +void ScDBCollection::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos ) +{ + // wenn nOldPos vor nNewPos liegt, ist nNewPos schon angepasst + + for (USHORT i=0; i<nCount; i++) + { + ScRange aRange; + ScDBData* pData = (ScDBData*)pItems[i]; + pData->GetArea( aRange ); + SCTAB nTab = aRange.aStart.Tab(); // hat nur eine Tabelle + + // anpassen wie die aktuelle Tabelle bei ScTablesHint (tabvwsh5.cxx) + + if ( nTab == nOldPos ) // verschobene Tabelle + nTab = nNewPos; + else if ( nOldPos < nNewPos ) // nach hinten verschoben + { + if ( nTab > nOldPos && nTab <= nNewPos ) // nachrueckender Bereich + --nTab; + } + else // nach vorne verschoben + { + if ( nTab >= nNewPos && nTab < nOldPos ) // nachrueckender Bereich + ++nTab; + } + + BOOL bChanged = ( nTab != aRange.aStart.Tab() ); + if (bChanged) + pData->SetArea( nTab, aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(),aRange.aEnd .Row() ); + + // MoveTo ist nicht noetig, wenn nur die Tabelle geaendert ist + + pData->SetModified(bChanged); + } +} + + +ScDBData* ScDBCollection::FindIndex(USHORT nIndex) +{ + USHORT i = 0; + while (i < nCount) + { + if ((*this)[i]->GetIndex() == nIndex) + return (*this)[i]; + i++; + } + return NULL; +} + +BOOL ScDBCollection::Insert(ScDataObject* pScDataObject) +{ + ScDBData* pData = (ScDBData*) pScDataObject; + if (!pData->GetIndex()) // schon gesetzt? + pData->SetIndex(nEntryIndex++); + BOOL bInserted = ScSortedCollection::Insert(pScDataObject); + if ( bInserted && pData->HasImportParam() && !pData->HasImportSelection() ) + { + pData->SetRefreshHandler( GetRefreshHandler() ); + pData->SetRefreshControl( pDoc->GetRefreshTimerControlAddress() ); + } + return bInserted; +} + + + + diff --git a/sc/source/core/tool/ddelink.cxx b/sc/source/core/tool/ddelink.cxx new file mode 100644 index 000000000000..977161760eb0 --- /dev/null +++ b/sc/source/core/tool/ddelink.cxx @@ -0,0 +1,279 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- +#include <tools/list.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/bindings.hxx> +#include <svl/zforlist.hxx> + +#include "ddelink.hxx" +#include "brdcst.hxx" +#include "document.hxx" +#include "scmatrix.hxx" +#include "patattr.hxx" +#include "rechead.hxx" +#include "rangeseq.hxx" +#include "sc.hrc" +#include "hints.hxx" + +TYPEINIT2(ScDdeLink,::sfx2::SvBaseLink,SfxBroadcaster); + +#define DDE_TXT_ENCODING gsl_getSystemTextEncoding() + +BOOL ScDdeLink::bIsInUpdate = FALSE; + +//------------------------------------------------------------------------ + +ScDdeLink::ScDdeLink( ScDocument* pD, const String& rA, const String& rT, const String& rI, + BYTE nM ) : + ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ALWAYS,FORMAT_STRING), + pDoc( pD ), + aAppl( rA ), + aTopic( rT ), + aItem( rI ), + nMode( nM ), + bNeedUpdate( FALSE ), + pResult( NULL ) +{ +} + +__EXPORT ScDdeLink::~ScDdeLink() +{ + // Verbindung aufheben + + // pResult is refcounted +} + +ScDdeLink::ScDdeLink( ScDocument* pD, const ScDdeLink& rOther ) : + ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ALWAYS,FORMAT_STRING), + pDoc ( pD ), + aAppl ( rOther.aAppl ), + aTopic ( rOther.aTopic ), + aItem ( rOther.aItem ), + nMode ( rOther.nMode ), + bNeedUpdate( FALSE ), + pResult ( NULL ) +{ + if (rOther.pResult) + pResult = rOther.pResult->Clone(); +} + +ScDdeLink::ScDdeLink( ScDocument* pD, SvStream& rStream, ScMultipleReadHeader& rHdr ) : + ::sfx2::SvBaseLink(sfx2::LINKUPDATE_ALWAYS,FORMAT_STRING), + pDoc( pD ), + bNeedUpdate( FALSE ), + pResult( NULL ) +{ + rHdr.StartEntry(); + + rtl_TextEncoding eCharSet = rStream.GetStreamCharSet(); + rStream.ReadByteString( aAppl, eCharSet ); + rStream.ReadByteString( aTopic, eCharSet ); + rStream.ReadByteString( aItem, eCharSet ); + + BOOL bHasValue; + rStream >> bHasValue; + if ( bHasValue ) + pResult = new ScMatrix( rStream ); + + if (rHdr.BytesLeft()) // neu in 388b und der 364w (RealTime-Client) Version + rStream >> nMode; + else + nMode = SC_DDE_DEFAULT; + + rHdr.EndEntry(); +} + +void ScDdeLink::Store( SvStream& rStream, ScMultipleWriteHeader& rHdr ) const +{ + rHdr.StartEntry(); + + rtl_TextEncoding eCharSet = rStream.GetStreamCharSet(); + rStream.WriteByteString( aAppl, eCharSet ); + rStream.WriteByteString( aTopic, eCharSet ); + rStream.WriteByteString( aItem, eCharSet ); + + BOOL bHasValue = ( pResult != NULL ); + rStream << bHasValue; + if (bHasValue) + pResult->Store( rStream ); + + if( rStream.GetVersion() > SOFFICE_FILEFORMAT_40 ) // nicht bei 4.0 Export + rStream << nMode; // seit 388b + + // Links mit Mode != SC_DDE_DEFAULT werden bei 4.0 Export komplett weggelassen + // (aus ScDocument::SaveDdeLinks) + + rHdr.EndEntry(); +} + +void __EXPORT ScDdeLink::DataChanged( const String& rMimeType, + const ::com::sun::star::uno::Any & rValue ) +{ + // wir koennen nur Strings... + if ( FORMAT_STRING != SotExchange::GetFormatIdFromMimeType( rMimeType )) + return; + + String aLinkStr; + ScByteSequenceToString::GetString( aLinkStr, rValue, DDE_TXT_ENCODING ); + aLinkStr.ConvertLineEnd(LINEEND_LF); + + // wenn String mit Zeilenende aufhoert, streichen: + + xub_StrLen nLen = aLinkStr.Len(); + if (nLen && aLinkStr.GetChar(nLen-1) == '\n') + aLinkStr.Erase(nLen-1); + + String aLine; + SCSIZE nCols = 1; // Leerstring -> eine leere Zelle + SCSIZE nRows = 1; + if (aLinkStr.Len()) + { + nRows = static_cast<SCSIZE>(aLinkStr.GetTokenCount( '\n' )); + aLine = aLinkStr.GetToken( 0, '\n' ); + if (aLine.Len()) + nCols = static_cast<SCSIZE>(aLine.GetTokenCount( '\t' )); + } + + if (!nRows || !nCols) // keine Daten + { + pResult.Clear(); + } + else // Daten aufteilen + { + // Matrix immer neu anlegen, damit bIsString nicht durcheinanderkommt + pResult = new ScMatrix( nCols, nRows ); + + SvNumberFormatter* pFormatter = pDoc->GetFormatTable(); + + // nMode bestimmt, wie der Text interpretiert wird (#44455#/#49783#): + // SC_DDE_DEFAULT - Zahlformat aus Zellvorlage "Standard" + // SC_DDE_ENGLISH - Standard-Zahlformat fuer English/US + // SC_DDE_TEXT - ohne NumberFormatter direkt als String + ULONG nStdFormat = 0; + if ( nMode == SC_DDE_DEFAULT ) + { + ScPatternAttr* pDefPattern = pDoc->GetDefPattern(); // enthaelt Standard-Vorlage + if ( pDefPattern ) + nStdFormat = pDefPattern->GetNumberFormat( pFormatter ); + } + else if ( nMode == SC_DDE_ENGLISH ) + nStdFormat = pFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US); + + String aEntry; + for (SCSIZE nR=0; nR<nRows; nR++) + { + aLine = aLinkStr.GetToken( (xub_StrLen) nR, '\n' ); + for (SCSIZE nC=0; nC<nCols; nC++) + { + aEntry = aLine.GetToken( (xub_StrLen) nC, '\t' ); + sal_uInt32 nIndex = nStdFormat; + double fVal; + if ( nMode != SC_DDE_TEXT && pFormatter->IsNumberFormat( aEntry, nIndex, fVal ) ) + pResult->PutDouble( fVal, nC, nR ); + else + pResult->PutString( aEntry, nC, nR ); + } + } + } + + // Es hat sich was getan... + + if (HasListeners()) + { + Broadcast( ScHint( SC_HINT_DATACHANGED, ScAddress(), NULL ) ); + pDoc->TrackFormulas(); // muss sofort passieren + pDoc->StartTrackTimer(); + + // StartTrackTimer ruft asynchron TrackFormulas, Broadcast(FID_DATACHANGED), + // ResetChanged, SetModified und Invalidate(SID_SAVEDOC/SID_DOC_MODIFIED) + // TrackFormulas zusaetzlich nochmal sofort, damit nicht z.B. durch IdleCalc + // eine Formel berechnet wird, die noch im FormulaTrack steht (#61676#) + + // notify Uno objects (for XRefreshListener) + // must be after TrackFormulas + //! do this asynchronously? + ScLinkRefreshedHint aHint; + aHint.SetDdeLink( aAppl, aTopic, aItem, nMode ); + pDoc->BroadcastUno( aHint ); + } +} + +void ScDdeLink::ResetValue() +{ + pResult.Clear(); + + // Es hat sich was getan... + // Tracking, FID_DATACHANGED etc. passiert von aussen + + if (HasListeners()) + Broadcast( ScHint( SC_HINT_DATACHANGED, ScAddress(), NULL ) ); +} + +void __EXPORT ScDdeLink::ListenersGone() +{ + BOOL bWas = bIsInUpdate; + bIsInUpdate = TRUE; // Remove() kann Reschedule ausloesen??!? + + ScDocument* pStackDoc = pDoc; // member pDoc can't be used after removing the link + + sfx2::LinkManager* pLinkMgr = pDoc->GetLinkManager(); + pLinkMgr->Remove( this); // deletes this + + if ( !pLinkMgr->GetLinks().Count() ) // letzten geloescht ? + { + SfxBindings* pBindings = pStackDoc->GetViewBindings(); // don't use member pDoc! + if (pBindings) + pBindings->Invalidate( SID_LINKS ); + } + + bIsInUpdate = bWas; +} + +void ScDdeLink::TryUpdate() +{ + if (bIsInUpdate) + bNeedUpdate = TRUE; // kann jetzt nicht ausgefuehrt werden + else + { + bIsInUpdate = TRUE; + //Application::Reschedule(); //! OS/2-Simulation + pDoc->IncInDdeLinkUpdate(); + Update(); + pDoc->DecInDdeLinkUpdate(); + bIsInUpdate = FALSE; + bNeedUpdate = FALSE; + } +} + + diff --git a/sc/source/core/tool/detdata.cxx b/sc/source/core/tool/detdata.cxx new file mode 100644 index 000000000000..a1add9ca6939 --- /dev/null +++ b/sc/source/core/tool/detdata.cxx @@ -0,0 +1,118 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <tools/debug.hxx> + +#include "detdata.hxx" +#include "refupdat.hxx" +#include "rechead.hxx" + +//------------------------------------------------------------------------ + +SV_IMPL_PTRARR( ScDetOpArr_Impl, ScDetOpDataPtr ); + +//------------------------------------------------------------------------ + +ScDetOpList::ScDetOpList(const ScDetOpList& rList) : + ScDetOpArr_Impl(), + bHasAddError( FALSE ) +{ + USHORT nCount = rList.Count(); + + for (USHORT i=0; i<nCount; i++) + Append( new ScDetOpData(*rList[i]) ); +} + +void ScDetOpList::DeleteOnTab( SCTAB nTab ) +{ + USHORT nPos = 0; + while ( nPos < Count() ) + { + // look for operations on the deleted sheet + + if ( (*this)[nPos]->GetPos().Tab() == nTab ) + Remove(nPos); + else + ++nPos; + } +} + +void ScDetOpList::UpdateReference( ScDocument* pDoc, UpdateRefMode eUpdateRefMode, + const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + USHORT nCount = Count(); + for (USHORT i=0; i<nCount; i++) + { + ScAddress aPos = (*this)[i]->GetPos(); + SCCOL nCol1 = aPos.Col(); + SCROW nRow1 = aPos.Row(); + SCTAB nTab1 = aPos.Tab(); + SCCOL nCol2 = nCol1; + SCROW nRow2 = nRow1; + SCTAB nTab2 = nTab1; + + ScRefUpdateRes eRes = + ScRefUpdate::Update( pDoc, eUpdateRefMode, + rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(), + rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz, + nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if ( eRes != UR_NOTHING ) + (*this)[i]->SetPos( ScAddress( nCol1, nRow1, nTab1 ) ); + } +} + +void ScDetOpList::Append( ScDetOpData* pDetOpData ) +{ + if ( pDetOpData->GetOperation() == SCDETOP_ADDERROR ) + bHasAddError = TRUE; + + Insert( pDetOpData, Count() ); +} + + +BOOL ScDetOpList::operator==( const ScDetOpList& r ) const +{ + // fuer Ref-Undo + + USHORT nCount = Count(); + BOOL bEqual = ( nCount == r.Count() ); + for (USHORT i=0; i<nCount && bEqual; i++) // Reihenfolge muss auch gleich sein + if ( !(*(*this)[i] == *r[i]) ) // Eintraege unterschiedlich ? + bEqual = FALSE; + + return bEqual; +} + + + diff --git a/sc/source/core/tool/detfunc.cxx b/sc/source/core/tool/detfunc.cxx new file mode 100644 index 000000000000..e86bb22646c8 --- /dev/null +++ b/sc/source/core/tool/detfunc.cxx @@ -0,0 +1,1712 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include "scitems.hxx" +#include <svtools/colorcfg.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/outlobj.hxx> +#include <svx/sdshitm.hxx> +#include <svx/sdsxyitm.hxx> +#include <svx/sdtditm.hxx> +#include <svx/svditer.hxx> +#include <svx/svdocapt.hxx> +#include <svx/svdocirc.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdorect.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdundo.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xlnedcit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xlnedwit.hxx> +#include <svx/xlnstcit.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnstwit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xtable.hxx> +#include <editeng/outliner.hxx> +#include <editeng/editobj.hxx> +#include <svx/sxcecitm.hxx> +#include <svl/whiter.hxx> +#include <editeng/writingmodeitem.hxx> + +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> + +#include "detfunc.hxx" +#include "document.hxx" +#include "dociter.hxx" +#include "drwlayer.hxx" +#include "userdat.hxx" +#include "validat.hxx" +#include "cell.hxx" +#include "docpool.hxx" +#include "patattr.hxx" +#include "attrib.hxx" +#include "scmod.hxx" +#include "postit.hxx" + +//------------------------------------------------------------------------ + +// #99319# line ends are now created with an empty name. +// The checkForUniqueItem method then finds a unique name for the item's value. +#define SC_LINEEND_NAME EMPTY_STRING + +//------------------------------------------------------------------------ + +enum DetInsertResult { // Return-Werte beim Einfuegen in einen Level + DET_INS_CONTINUE, + DET_INS_INSERTED, + DET_INS_EMPTY, + DET_INS_CIRCULAR }; + + +//------------------------------------------------------------------------ + +class ScDetectiveData +{ +private: + SfxItemSet aBoxSet; + SfxItemSet aArrowSet; + SfxItemSet aToTabSet; + SfxItemSet aFromTabSet; + SfxItemSet aCircleSet; //! einzeln ? + USHORT nMaxLevel; + +public: + ScDetectiveData( SdrModel* pModel ); + + SfxItemSet& GetBoxSet() { return aBoxSet; } + SfxItemSet& GetArrowSet() { return aArrowSet; } + SfxItemSet& GetToTabSet() { return aToTabSet; } + SfxItemSet& GetFromTabSet() { return aFromTabSet; } + SfxItemSet& GetCircleSet() { return aCircleSet; } + + void SetMaxLevel( USHORT nVal ) { nMaxLevel = nVal; } + USHORT GetMaxLevel() const { return nMaxLevel; } +}; + +class ScCommentData +{ +public: + ScCommentData( ScDocument& rDoc, SdrModel* pModel ); + + SfxItemSet& GetCaptionSet() { return aCaptionSet; } + void UpdateCaptionSet( const SfxItemSet& rItemSet ); + +private: + SfxItemSet aCaptionSet; +}; + +//------------------------------------------------------------------------ + +ColorData ScDetectiveFunc::nArrowColor = 0; +ColorData ScDetectiveFunc::nErrorColor = 0; +ColorData ScDetectiveFunc::nCommentColor = 0; +BOOL ScDetectiveFunc::bColorsInitialized = FALSE; + +//------------------------------------------------------------------------ + +BOOL lcl_HasThickLine( SdrObject& rObj ) +{ + // thin lines get width 0 -> everything greater 0 is a thick line + + return ( ((const XLineWidthItem&)rObj.GetMergedItem(XATTR_LINEWIDTH)).GetValue() > 0 ); +} + +//------------------------------------------------------------------------ + +ScDetectiveData::ScDetectiveData( SdrModel* pModel ) : + aBoxSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ), + aArrowSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ), + aToTabSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ), + aFromTabSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ), + aCircleSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ) +{ + nMaxLevel = 0; + + aBoxSet.Put( XLineColorItem( EMPTY_STRING, Color( ScDetectiveFunc::GetArrowColor() ) ) ); + aBoxSet.Put( XFillStyleItem( XFILL_NONE ) ); + + // #66479# Standard-Linienenden (wie aus XLineEndList::Create) selber zusammenbasteln, + // um von den konfigurierten Linienenden unabhaengig zu sein + + basegfx::B2DPolygon aTriangle; + aTriangle.append(basegfx::B2DPoint(10.0, 0.0)); + aTriangle.append(basegfx::B2DPoint(0.0, 30.0)); + aTriangle.append(basegfx::B2DPoint(20.0, 30.0)); + aTriangle.setClosed(true); + + basegfx::B2DPolygon aSquare; + aSquare.append(basegfx::B2DPoint(0.0, 0.0)); + aSquare.append(basegfx::B2DPoint(10.0, 0.0)); + aSquare.append(basegfx::B2DPoint(10.0, 10.0)); + aSquare.append(basegfx::B2DPoint(0.0, 10.0)); + aSquare.setClosed(true); + + basegfx::B2DPolygon aCircle(basegfx::tools::createPolygonFromEllipse(basegfx::B2DPoint(0.0, 0.0), 100.0, 100.0)); + aCircle.setClosed(true); + + String aName = SC_LINEEND_NAME; + + aArrowSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) ); + aArrowSet.Put( XLineStartWidthItem( 200 ) ); + aArrowSet.Put( XLineStartCenterItem( TRUE ) ); + aArrowSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) ); + aArrowSet.Put( XLineEndWidthItem( 200 ) ); + aArrowSet.Put( XLineEndCenterItem( FALSE ) ); + + aToTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) ); + aToTabSet.Put( XLineStartWidthItem( 200 ) ); + aToTabSet.Put( XLineStartCenterItem( TRUE ) ); + aToTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aSquare) ) ); + aToTabSet.Put( XLineEndWidthItem( 300 ) ); + aToTabSet.Put( XLineEndCenterItem( FALSE ) ); + + aFromTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aSquare) ) ); + aFromTabSet.Put( XLineStartWidthItem( 300 ) ); + aFromTabSet.Put( XLineStartCenterItem( TRUE ) ); + aFromTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) ); + aFromTabSet.Put( XLineEndWidthItem( 200 ) ); + aFromTabSet.Put( XLineEndCenterItem( FALSE ) ); + + aCircleSet.Put( XLineColorItem( String(), Color( ScDetectiveFunc::GetErrorColor() ) ) ); + aCircleSet.Put( XFillStyleItem( XFILL_NONE ) ); + USHORT nWidth = 55; // 54 = 1 Pixel + aCircleSet.Put( XLineWidthItem( nWidth ) ); +} + +ScCommentData::ScCommentData( ScDocument& rDoc, SdrModel* pModel ) : + aCaptionSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END, EE_ITEMS_START, EE_ITEMS_END, 0, 0 ) +{ + basegfx::B2DPolygon aTriangle; + aTriangle.append(basegfx::B2DPoint(10.0, 0.0)); + aTriangle.append(basegfx::B2DPoint(0.0, 30.0)); + aTriangle.append(basegfx::B2DPoint(20.0, 30.0)); + aTriangle.setClosed(true); + + String aName = SC_LINEEND_NAME; + + aCaptionSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) ); + aCaptionSet.Put( XLineStartWidthItem( 200 ) ); + aCaptionSet.Put( XLineStartCenterItem( FALSE ) ); + aCaptionSet.Put( XFillStyleItem( XFILL_SOLID ) ); + Color aYellow( ScDetectiveFunc::GetCommentColor() ); + aCaptionSet.Put( XFillColorItem( String(), aYellow ) ); + + // shadow + // SdrShadowItem has FALSE, instead the shadow is set for the rectangle + // only with SetSpecialTextBoxShadow when the object is created + // (item must be set to adjust objects from older files) + aCaptionSet.Put( SdrShadowItem( FALSE ) ); + aCaptionSet.Put( SdrShadowXDistItem( 100 ) ); + aCaptionSet.Put( SdrShadowYDistItem( 100 ) ); + + // text attributes + aCaptionSet.Put( SdrTextLeftDistItem( 100 ) ); + aCaptionSet.Put( SdrTextRightDistItem( 100 ) ); + aCaptionSet.Put( SdrTextUpperDistItem( 100 ) ); + aCaptionSet.Put( SdrTextLowerDistItem( 100 ) ); + + aCaptionSet.Put( SdrTextAutoGrowWidthItem( FALSE ) ); + aCaptionSet.Put( SdrTextAutoGrowHeightItem( TRUE ) ); + + // #78943# do use the default cell style, so the user has a chance to + // modify the font for the annotations + ((const ScPatternAttr&)rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN)). + FillEditItemSet( &aCaptionSet ); + + // support the best position for the tail connector now that + // that notes can be resized and repositioned. + aCaptionSet.Put( SdrCaptionEscDirItem( SDRCAPT_ESCBESTFIT) ); +} + +void ScCommentData::UpdateCaptionSet( const SfxItemSet& rItemSet ) +{ + SfxWhichIter aWhichIter( rItemSet ); + const SfxPoolItem* pPoolItem = 0; + + for( USHORT nWhich = aWhichIter.FirstWhich(); nWhich > 0; nWhich = aWhichIter.NextWhich() ) + { + if(rItemSet.GetItemState(nWhich, FALSE, &pPoolItem) == SFX_ITEM_SET) + { + switch(nWhich) + { + case SDRATTR_SHADOW: + // use existing Caption default - appears that setting this + // to true screws up the tail appearance. See also comment + // for default setting above. + break; + case SDRATTR_SHADOWXDIST: + // use existing Caption default - svx sets a value of 35 + // but default 100 gives a better appearance. + break; + case SDRATTR_SHADOWYDIST: + // use existing Caption default - svx sets a value of 35 + // but default 100 gives a better appearance. + break; + + default: + aCaptionSet.Put(*pPoolItem); + } + } + } +} + +//------------------------------------------------------------------------ + +void ScDetectiveFunc::Modified() +{ + if (pDoc->IsStreamValid(nTab)) + pDoc->SetStreamValid(nTab, FALSE); +} + +inline BOOL Intersect( SCCOL nStartCol1, SCROW nStartRow1, SCCOL nEndCol1, SCROW nEndRow1, + SCCOL nStartCol2, SCROW nStartRow2, SCCOL nEndCol2, SCROW nEndRow2 ) +{ + return nEndCol1 >= nStartCol2 && nEndCol2 >= nStartCol1 && + nEndRow1 >= nStartRow2 && nEndRow2 >= nStartRow1; +} + +BOOL ScDetectiveFunc::HasError( const ScRange& rRange, ScAddress& rErrPos ) +{ + rErrPos = rRange.aStart; + USHORT nError = 0; + + ScCellIterator aCellIter( pDoc, rRange); + ScBaseCell* pCell = aCellIter.GetFirst(); + while (pCell) + { + if (pCell->GetCellType() == CELLTYPE_FORMULA) + { + nError = ((ScFormulaCell*)pCell)->GetErrCode(); + if (nError) + rErrPos.Set( aCellIter.GetCol(), aCellIter.GetRow(), aCellIter.GetTab() ); + } + pCell = aCellIter.GetNext(); + } + + return (nError != 0); +} + +Point ScDetectiveFunc::GetDrawPos( SCCOL nCol, SCROW nRow, DrawPosMode eMode ) const +{ + DBG_ASSERT( ValidColRow( nCol, nRow ), "ScDetectiveFunc::GetDrawPos - invalid cell address" ); + SanitizeCol( nCol ); + SanitizeRow( nRow ); + + Point aPos; + + switch( eMode ) + { + case DRAWPOS_TOPLEFT: + break; + case DRAWPOS_BOTTOMRIGHT: + ++nCol; + ++nRow; + break; + case DRAWPOS_DETARROW: + aPos.X() += pDoc->GetColWidth( nCol, nTab ) / 4; + aPos.Y() += pDoc->GetRowHeight( nRow, nTab ) / 2; + break; + case DRAWPOS_CAPTIONLEFT: + aPos.X() += 6; + break; + case DRAWPOS_CAPTIONRIGHT: + { + // find right end of passed cell position + const ScMergeAttr* pMerge = static_cast< const ScMergeAttr* >( pDoc->GetAttr( nCol, nRow, nTab, ATTR_MERGE ) ); + if ( pMerge->GetColMerge() > 1 ) + nCol = nCol + pMerge->GetColMerge(); + else + ++nCol; + aPos.X() -= 6; + } + break; + } + + for ( SCCOL i = 0; i < nCol; ++i ) + aPos.X() += pDoc->GetColWidth( i, nTab ); + aPos.Y() += pDoc->GetRowHeight( 0, nRow - 1, nTab ); + + aPos.X() = static_cast< long >( aPos.X() * HMM_PER_TWIPS ); + aPos.Y() = static_cast< long >( aPos.Y() * HMM_PER_TWIPS ); + + if ( pDoc->IsNegativePage( nTab ) ) + aPos.X() *= -1; + + return aPos; +} + +Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const +{ + Rectangle aRect( + GetDrawPos( ::std::min( nCol1, nCol2 ), ::std::min( nRow1, nRow2 ), DRAWPOS_TOPLEFT ), + GetDrawPos( ::std::max( nCol1, nCol2 ), ::std::max( nRow1, nRow2 ), DRAWPOS_BOTTOMRIGHT ) ); + aRect.Justify(); // reorder left/right in RTL sheets + return aRect; +} + +Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol, SCROW nRow ) const +{ + return GetDrawRect( nCol, nRow, nCol, nRow ); +} + +BOOL lcl_IsOtherTab( const basegfx::B2DPolyPolygon& rPolyPolygon ) +{ + // test if rPolygon is the line end for "other table" (rectangle) + if(1L == rPolyPolygon.count()) + { + const basegfx::B2DPolygon aSubPoly(rPolyPolygon.getB2DPolygon(0L)); + + // #i73305# circle consists of 4 segments, too, distinguishable from square by + // the use of control points + if(4L == aSubPoly.count() && aSubPoly.isClosed() && !aSubPoly.areControlPointsUsed()) + { + return true; + } + } + + return false; +} + +BOOL ScDetectiveFunc::HasArrow( const ScAddress& rStart, + SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab ) +{ + BOOL bStartAlien = ( rStart.Tab() != nTab ); + BOOL bEndAlien = ( nEndTab != nTab ); + + if (bStartAlien && bEndAlien) + { + DBG_ERROR("bStartAlien && bEndAlien"); + return TRUE; + } + + Rectangle aStartRect; + Rectangle aEndRect; + if (!bStartAlien) + aStartRect = GetDrawRect( rStart.Col(), rStart.Row() ); + if (!bEndAlien) + aEndRect = GetDrawRect( nEndCol, nEndRow ); + + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + + BOOL bFound = FALSE; + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject && !bFound) + { + if ( pObject->GetLayer()==SC_LAYER_INTERN && + pObject->IsPolyObj() && pObject->GetPointCount()==2 ) + { + const SfxItemSet& rSet = pObject->GetMergedItemSet(); + + BOOL bObjStartAlien = + lcl_IsOtherTab( ((const XLineStartItem&)rSet.Get(XATTR_LINESTART)).GetLineStartValue() ); + BOOL bObjEndAlien = + lcl_IsOtherTab( ((const XLineEndItem&)rSet.Get(XATTR_LINEEND)).GetLineEndValue() ); + + BOOL bStartHit = bStartAlien ? bObjStartAlien : + ( !bObjStartAlien && aStartRect.IsInside(pObject->GetPoint(0)) ); + BOOL bEndHit = bEndAlien ? bObjEndAlien : + ( !bObjEndAlien && aEndRect.IsInside(pObject->GetPoint(1)) ); + + if ( bStartHit && bEndHit ) + bFound = TRUE; + } + pObject = aIter.Next(); + } + + return bFound; +} + +BOOL ScDetectiveFunc::IsNonAlienArrow( SdrObject* pObject ) // static +{ + if ( pObject->GetLayer()==SC_LAYER_INTERN && + pObject->IsPolyObj() && pObject->GetPointCount()==2 ) + { + const SfxItemSet& rSet = pObject->GetMergedItemSet(); + + BOOL bObjStartAlien = + lcl_IsOtherTab( ((const XLineStartItem&)rSet.Get(XATTR_LINESTART)).GetLineStartValue() ); + BOOL bObjEndAlien = + lcl_IsOtherTab( ((const XLineEndItem&)rSet.Get(XATTR_LINEEND)).GetLineEndValue() ); + + return !bObjStartAlien && !bObjEndAlien; + } + + return FALSE; +} + +//------------------------------------------------------------------------ + +// InsertXXX: called from DrawEntry/DrawAlienEntry and InsertObject + +BOOL ScDetectiveFunc::InsertArrow( SCCOL nCol, SCROW nRow, + SCCOL nRefStartCol, SCROW nRefStartRow, + SCCOL nRefEndCol, SCROW nRefEndRow, + BOOL bFromOtherTab, BOOL bRed, + ScDetectiveData& rData ) +{ + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab)); + + BOOL bArea = ( nRefStartCol != nRefEndCol || nRefStartRow != nRefEndRow ); + if (bArea && !bFromOtherTab) + { + // insert the rectangle before the arrow - this is relied on in FindFrameForObject + + Rectangle aRect = GetDrawRect( nRefStartCol, nRefStartRow, nRefEndCol, nRefEndRow ); + SdrRectObj* pBox = new SdrRectObj( aRect ); + + pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet()); + + ScDrawLayer::SetAnchor( pBox, SCA_CELL ); + pBox->SetLayer( SC_LAYER_INTERN ); + pPage->InsertObject( pBox ); + pModel->AddCalcUndo( new SdrUndoInsertObj( *pBox ) ); + + ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, TRUE ); + pData->maStart.Set( nRefStartCol, nRefStartRow, nTab); + pData->maEnd.Set( nRefEndCol, nRefEndRow, nTab); + } + + Point aStartPos = GetDrawPos( nRefStartCol, nRefStartRow, DRAWPOS_DETARROW ); + Point aEndPos = GetDrawPos( nCol, nRow, DRAWPOS_DETARROW ); + + if (bFromOtherTab) + { + BOOL bNegativePage = pDoc->IsNegativePage( nTab ); + long nPageSign = bNegativePage ? -1 : 1; + + aStartPos = Point( aEndPos.X() - 1000 * nPageSign, aEndPos.Y() - 1000 ); + if (aStartPos.X() * nPageSign < 0) + aStartPos.X() += 2000 * nPageSign; + if (aStartPos.Y() < 0) + aStartPos.Y() += 2000; + } + + SfxItemSet& rAttrSet = bFromOtherTab ? rData.GetFromTabSet() : rData.GetArrowSet(); + + if (bArea && !bFromOtherTab) + rAttrSet.Put( XLineWidthItem( 50 ) ); // Bereich + else + rAttrSet.Put( XLineWidthItem( 0 ) ); // einzelne Referenz + + ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() ); + rAttrSet.Put( XLineColorItem( String(), Color( nColorData ) ) ); + + basegfx::B2DPolygon aTempPoly; + aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y())); + aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y())); + SdrPathObj* pArrow = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly)); + pArrow->NbcSetLogicRect(Rectangle(aStartPos,aEndPos)); //! noetig ??? + pArrow->SetMergedItemSetAndBroadcast(rAttrSet); + + ScDrawLayer::SetAnchor( pArrow, SCA_CELL ); + pArrow->SetLayer( SC_LAYER_INTERN ); + pPage->InsertObject( pArrow ); + pModel->AddCalcUndo( new SdrUndoInsertObj( *pArrow ) ); + + ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow, TRUE ); + if (bFromOtherTab) + pData->maStart.SetInvalid(); + else + pData->maStart.Set( nRefStartCol, nRefStartRow, nTab); + + pData->maEnd.Set( nCol, nRow, nTab); + + Modified(); + return TRUE; +} + +BOOL ScDetectiveFunc::InsertToOtherTab( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, BOOL bRed, + ScDetectiveData& rData ) +{ + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab)); + + BOOL bArea = ( nStartCol != nEndCol || nStartRow != nEndRow ); + if (bArea) + { + Rectangle aRect = GetDrawRect( nStartCol, nStartRow, nEndCol, nEndRow ); + SdrRectObj* pBox = new SdrRectObj( aRect ); + + pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet()); + + ScDrawLayer::SetAnchor( pBox, SCA_CELL ); + pBox->SetLayer( SC_LAYER_INTERN ); + pPage->InsertObject( pBox ); + pModel->AddCalcUndo( new SdrUndoInsertObj( *pBox ) ); + + ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, TRUE ); + pData->maStart.Set( nStartCol, nStartRow, nTab); + pData->maEnd.Set( nEndCol, nEndRow, nTab); + } + + BOOL bNegativePage = pDoc->IsNegativePage( nTab ); + long nPageSign = bNegativePage ? -1 : 1; + + Point aStartPos = GetDrawPos( nStartCol, nStartRow, DRAWPOS_DETARROW ); + Point aEndPos = Point( aStartPos.X() + 1000 * nPageSign, aStartPos.Y() - 1000 ); + if (aEndPos.Y() < 0) + aEndPos.Y() += 2000; + + SfxItemSet& rAttrSet = rData.GetToTabSet(); + if (bArea) + rAttrSet.Put( XLineWidthItem( 50 ) ); // Bereich + else + rAttrSet.Put( XLineWidthItem( 0 ) ); // einzelne Referenz + + ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() ); + rAttrSet.Put( XLineColorItem( String(), Color( nColorData ) ) ); + + basegfx::B2DPolygon aTempPoly; + aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y())); + aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y())); + SdrPathObj* pArrow = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly)); + pArrow->NbcSetLogicRect(Rectangle(aStartPos,aEndPos)); //! noetig ??? + + pArrow->SetMergedItemSetAndBroadcast(rAttrSet); + + ScDrawLayer::SetAnchor( pArrow, SCA_CELL ); + pArrow->SetLayer( SC_LAYER_INTERN ); + pPage->InsertObject( pArrow ); + pModel->AddCalcUndo( new SdrUndoInsertObj( *pArrow ) ); + + ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow, TRUE ); + pData->maStart.Set( nStartCol, nStartRow, nTab); + pData->maEnd.SetInvalid(); + + Modified(); + return TRUE; +} + +//------------------------------------------------------------------------ + +// DrawEntry: Formel auf dieser Tabelle, +// Referenz auf dieser oder anderer +// DrawAlienEntry: Formel auf anderer Tabelle, +// Referenz auf dieser + +// return FALSE: da war schon ein Pfeil + +BOOL ScDetectiveFunc::DrawEntry( SCCOL nCol, SCROW nRow, + const ScRange& rRef, + ScDetectiveData& rData ) +{ + if ( HasArrow( rRef.aStart, nCol, nRow, nTab ) ) + return FALSE; + + ScAddress aErrorPos; + BOOL bError = HasError( rRef, aErrorPos ); + BOOL bAlien = ( rRef.aEnd.Tab() < nTab || rRef.aStart.Tab() > nTab ); + + return InsertArrow( nCol, nRow, + rRef.aStart.Col(), rRef.aStart.Row(), + rRef.aEnd.Col(), rRef.aEnd.Row(), + bAlien, bError, rData ); +} + +BOOL ScDetectiveFunc::DrawAlienEntry( const ScRange& rRef, + ScDetectiveData& rData ) +{ + if ( HasArrow( rRef.aStart, 0, 0, nTab+1 ) ) + return FALSE; + + ScAddress aErrorPos; + BOOL bError = HasError( rRef, aErrorPos ); + + return InsertToOtherTab( rRef.aStart.Col(), rRef.aStart.Row(), + rRef.aEnd.Col(), rRef.aEnd.Row(), + bError, rData ); +} + +void ScDetectiveFunc::DrawCircle( SCCOL nCol, SCROW nRow, ScDetectiveData& rData ) +{ + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab)); + + Rectangle aRect = GetDrawRect( nCol, nRow ); + aRect.Left() -= 250; + aRect.Right() += 250; + aRect.Top() -= 70; + aRect.Bottom() += 70; + + SdrCircObj* pCircle = new SdrCircObj( OBJ_CIRC, aRect ); + SfxItemSet& rAttrSet = rData.GetCircleSet(); + + pCircle->SetMergedItemSetAndBroadcast(rAttrSet); + + ScDrawLayer::SetAnchor( pCircle, SCA_CELL ); + pCircle->SetLayer( SC_LAYER_INTERN ); + pPage->InsertObject( pCircle ); + pModel->AddCalcUndo( new SdrUndoInsertObj( *pCircle ) ); + + ScDrawObjData* pData = ScDrawLayer::GetObjData( pCircle, TRUE ); + pData->maStart.Set( nCol, nRow, nTab); + pData->maEnd.SetInvalid(); + + Modified(); +} + +void ScDetectiveFunc::DeleteArrowsAt( SCCOL nCol, SCROW nRow, BOOL bDestPnt ) +{ + Rectangle aRect = GetDrawRect( nCol, nRow ); + + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + + pPage->RecalcObjOrdNums(); + + long nDelCount = 0; + ULONG nObjCount = pPage->GetObjCount(); + if (nObjCount) + { + SdrObject** ppObj = new SdrObject*[nObjCount]; + + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetLayer()==SC_LAYER_INTERN && + pObject->IsPolyObj() && pObject->GetPointCount()==2 ) + { + if (aRect.IsInside(pObject->GetPoint(bDestPnt))) // Start/Zielpunkt + ppObj[nDelCount++] = pObject; + } + + pObject = aIter.Next(); + } + + long i; + for (i=1; i<=nDelCount; i++) + pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) ); + + for (i=1; i<=nDelCount; i++) + pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() ); + + delete[] ppObj; + + Modified(); + } +} + + // Box um Referenz loeschen + +#define SC_DET_TOLERANCE 50 + +inline BOOL RectIsPoints( const Rectangle& rRect, const Point& rStart, const Point& rEnd ) +{ + return rRect.Left() >= rStart.X() - SC_DET_TOLERANCE + && rRect.Left() <= rStart.X() + SC_DET_TOLERANCE + && rRect.Right() >= rEnd.X() - SC_DET_TOLERANCE + && rRect.Right() <= rEnd.X() + SC_DET_TOLERANCE + && rRect.Top() >= rStart.Y() - SC_DET_TOLERANCE + && rRect.Top() <= rStart.Y() + SC_DET_TOLERANCE + && rRect.Bottom() >= rEnd.Y() - SC_DET_TOLERANCE + && rRect.Bottom() <= rEnd.Y() + SC_DET_TOLERANCE; +} + +#undef SC_DET_TOLERANCE + +void ScDetectiveFunc::DeleteBox( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) +{ +/* String aStr; + aStr += nCol1; + aStr += '/'; + aStr += nRow1; + aStr += '/'; + aStr += nCol2; + aStr += '/'; + aStr += nRow2; + InfoBox(0,aStr).Execute(); +*/ + + Rectangle aCornerRect = GetDrawRect( nCol1, nRow1, nCol2, nRow2 ); + Point aStartCorner = aCornerRect.TopLeft(); + Point aEndCorner = aCornerRect.BottomRight(); + Rectangle aObjRect; + + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + + pPage->RecalcObjOrdNums(); + + long nDelCount = 0; + ULONG nObjCount = pPage->GetObjCount(); + if (nObjCount) + { + SdrObject** ppObj = new SdrObject*[nObjCount]; + + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetLayer() == SC_LAYER_INTERN && + pObject->Type() == TYPE(SdrRectObj) ) + { + aObjRect = ((SdrRectObj*)pObject)->GetLogicRect(); + aObjRect.Justify(); + if ( RectIsPoints( aObjRect, aStartCorner, aEndCorner ) ) + ppObj[nDelCount++] = pObject; + } + + pObject = aIter.Next(); + } + + long i; + for (i=1; i<=nDelCount; i++) + pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) ); + + for (i=1; i<=nDelCount; i++) + pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() ); + + delete[] ppObj; + + Modified(); + } +} + +//------------------------------------------------------------------------ + +USHORT ScDetectiveFunc::InsertPredLevelArea( const ScRange& rRef, + ScDetectiveData& rData, USHORT nLevel ) +{ + USHORT nResult = DET_INS_EMPTY; + + ScCellIterator aCellIter( pDoc, rRef); + ScBaseCell* pCell = aCellIter.GetFirst(); + while (pCell) + { + if (pCell->GetCellType() == CELLTYPE_FORMULA) + switch( InsertPredLevel( aCellIter.GetCol(), aCellIter.GetRow(), rData, nLevel ) ) + { + case DET_INS_INSERTED: + nResult = DET_INS_INSERTED; + break; + case DET_INS_CONTINUE: + if (nResult != DET_INS_INSERTED) + nResult = DET_INS_CONTINUE; + break; + case DET_INS_CIRCULAR: + if (nResult == DET_INS_EMPTY) + nResult = DET_INS_CIRCULAR; + break; + } + + pCell = aCellIter.GetNext(); + } + + return nResult; +} + +USHORT ScDetectiveFunc::InsertPredLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData, + USHORT nLevel ) +{ + ScBaseCell* pCell; + pDoc->GetCell( nCol, nRow, nTab, pCell ); + if (!pCell) + return DET_INS_EMPTY; + if (pCell->GetCellType() != CELLTYPE_FORMULA) + return DET_INS_EMPTY; + + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + if (pFCell->IsRunning()) + return DET_INS_CIRCULAR; + + if (pFCell->GetDirty()) + pFCell->Interpret(); // nach SetRunning geht's nicht mehr! + pFCell->SetRunning(TRUE); + + USHORT nResult = DET_INS_EMPTY; + + ScDetectiveRefIter aIter( (ScFormulaCell*) pCell ); + ScRange aRef; + while ( aIter.GetNextRef( aRef ) ) + { + if (DrawEntry( nCol, nRow, aRef, rData )) + { + nResult = DET_INS_INSERTED; // neuer Pfeil eingetragen + } + else + { + // weiterverfolgen + + if ( nLevel < rData.GetMaxLevel() ) + { + USHORT nSubResult; + BOOL bArea = (aRef.aStart != aRef.aEnd); + if (bArea) + nSubResult = InsertPredLevelArea( aRef, rData, nLevel+1 ); + else + nSubResult = InsertPredLevel( aRef.aStart.Col(), aRef.aStart.Row(), + rData, nLevel+1 ); + + switch (nSubResult) + { + case DET_INS_INSERTED: + nResult = DET_INS_INSERTED; + break; + case DET_INS_CONTINUE: + if (nResult != DET_INS_INSERTED) + nResult = DET_INS_CONTINUE; + break; + case DET_INS_CIRCULAR: + if (nResult == DET_INS_EMPTY) + nResult = DET_INS_CIRCULAR; + break; + // DET_INS_EMPTY: unveraendert lassen + } + } + else // nMaxLevel erreicht + if (nResult != DET_INS_INSERTED) + nResult = DET_INS_CONTINUE; + } + } + + pFCell->SetRunning(FALSE); + + return nResult; +} + +USHORT ScDetectiveFunc::FindPredLevelArea( const ScRange& rRef, + USHORT nLevel, USHORT nDeleteLevel ) +{ + USHORT nResult = nLevel; + + ScCellIterator aCellIter( pDoc, rRef); + ScBaseCell* pCell = aCellIter.GetFirst(); + while (pCell) + { + if (pCell->GetCellType() == CELLTYPE_FORMULA) + { + USHORT nTemp = FindPredLevel( aCellIter.GetCol(), aCellIter.GetRow(), nLevel, nDeleteLevel ); + if (nTemp > nResult) + nResult = nTemp; + } + pCell = aCellIter.GetNext(); + } + + return nResult; +} + + // nDeleteLevel != 0 -> loeschen + +USHORT ScDetectiveFunc::FindPredLevel( SCCOL nCol, SCROW nRow, USHORT nLevel, USHORT nDeleteLevel ) +{ + DBG_ASSERT( nLevel<1000, "Level" ); + + ScBaseCell* pCell; + pDoc->GetCell( nCol, nRow, nTab, pCell ); + if (!pCell) + return nLevel; + if (pCell->GetCellType() != CELLTYPE_FORMULA) + return nLevel; + + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + if (pFCell->IsRunning()) + return nLevel; + + if (pFCell->GetDirty()) + pFCell->Interpret(); // nach SetRunning geht's nicht mehr! + pFCell->SetRunning(TRUE); + + USHORT nResult = nLevel; + BOOL bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 ); + + if ( bDelete ) + { + DeleteArrowsAt( nCol, nRow, TRUE ); // Pfeile, die hierher zeigen + } + + ScDetectiveRefIter aIter( (ScFormulaCell*) pCell ); + ScRange aRef; + while ( aIter.GetNextRef( aRef) ) + { + BOOL bArea = ( aRef.aStart != aRef.aEnd ); + + if ( bDelete ) // Rahmen loeschen ? + { + if (bArea) + { + DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(), aRef.aEnd.Col(), aRef.aEnd.Row() ); + } + } + else // weitersuchen + { + if ( HasArrow( aRef.aStart, nCol,nRow,nTab ) ) + { + USHORT nTemp; + if (bArea) + nTemp = FindPredLevelArea( aRef, nLevel+1, nDeleteLevel ); + else + nTemp = FindPredLevel( aRef.aStart.Col(),aRef.aStart.Row(), + nLevel+1, nDeleteLevel ); + if (nTemp > nResult) + nResult = nTemp; + } + } + } + + pFCell->SetRunning(FALSE); + + return nResult; +} + +//------------------------------------------------------------------------ + +USHORT ScDetectiveFunc::InsertErrorLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData, + USHORT nLevel ) +{ + ScBaseCell* pCell; + pDoc->GetCell( nCol, nRow, nTab, pCell ); + if (!pCell) + return DET_INS_EMPTY; + if (pCell->GetCellType() != CELLTYPE_FORMULA) + return DET_INS_EMPTY; + + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + if (pFCell->IsRunning()) + return DET_INS_CIRCULAR; + + if (pFCell->GetDirty()) + pFCell->Interpret(); // nach SetRunning geht's nicht mehr! + pFCell->SetRunning(TRUE); + + USHORT nResult = DET_INS_EMPTY; + + ScDetectiveRefIter aIter( (ScFormulaCell*) pCell ); + ScRange aRef; + ScAddress aErrorPos; + BOOL bHasError = FALSE; + while ( aIter.GetNextRef( aRef ) ) + { + if (HasError( aRef, aErrorPos )) + { + bHasError = TRUE; + if (DrawEntry( nCol, nRow, ScRange( aErrorPos), rData )) + nResult = DET_INS_INSERTED; + + // und weiterverfolgen + + if ( nLevel < rData.GetMaxLevel() ) // praktisch immer + { + if (InsertErrorLevel( aErrorPos.Col(), aErrorPos.Row(), + rData, nLevel+1 ) == DET_INS_INSERTED) + nResult = DET_INS_INSERTED; + } + } + } + + pFCell->SetRunning(FALSE); + + // Blaetter ? + if (!bHasError) + if (InsertPredLevel( nCol, nRow, rData, rData.GetMaxLevel() ) == DET_INS_INSERTED) + nResult = DET_INS_INSERTED; + + return nResult; +} + +//------------------------------------------------------------------------ + +USHORT ScDetectiveFunc::InsertSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + ScDetectiveData& rData, USHORT nLevel ) +{ + // ueber ganzes Dokument + + USHORT nResult = DET_INS_EMPTY; +// ScCellIterator aCellIter( pDoc, 0,0, nTab, MAXCOL,MAXROW, nTab ); + ScCellIterator aCellIter( pDoc, 0,0,0, MAXCOL,MAXROW,MAXTAB ); // alle Tabellen + ScBaseCell* pCell = aCellIter.GetFirst(); + while (pCell) + { + if (pCell->GetCellType() == CELLTYPE_FORMULA) + { + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + BOOL bRunning = pFCell->IsRunning(); + + if (pFCell->GetDirty()) + pFCell->Interpret(); // nach SetRunning geht's nicht mehr! + pFCell->SetRunning(TRUE); + + ScDetectiveRefIter aIter( (ScFormulaCell*) pCell ); + ScRange aRef; + while ( aIter.GetNextRef( aRef) ) + { + if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab) + { + if (Intersect( nCol1,nRow1,nCol2,nRow2, + aRef.aStart.Col(),aRef.aStart.Row(), + aRef.aEnd.Col(),aRef.aEnd.Row() )) + { + BOOL bAlien = ( aCellIter.GetTab() != nTab ); + BOOL bDrawRet; + if (bAlien) + bDrawRet = DrawAlienEntry( aRef, rData ); + else + bDrawRet = DrawEntry( aCellIter.GetCol(), aCellIter.GetRow(), + aRef, rData ); + if (bDrawRet) + { + nResult = DET_INS_INSERTED; // neuer Pfeil eingetragen + } + else + { + if (bRunning) + { + if (nResult == DET_INS_EMPTY) + nResult = DET_INS_CIRCULAR; + } + else + { + // weiterverfolgen + + if ( nLevel < rData.GetMaxLevel() ) + { + USHORT nSubResult = InsertSuccLevel( + aCellIter.GetCol(), aCellIter.GetRow(), + aCellIter.GetCol(), aCellIter.GetRow(), + rData, nLevel+1 ); + switch (nSubResult) + { + case DET_INS_INSERTED: + nResult = DET_INS_INSERTED; + break; + case DET_INS_CONTINUE: + if (nResult != DET_INS_INSERTED) + nResult = DET_INS_CONTINUE; + break; + case DET_INS_CIRCULAR: + if (nResult == DET_INS_EMPTY) + nResult = DET_INS_CIRCULAR; + break; + // DET_INS_EMPTY: unveraendert lassen + } + } + else // nMaxLevel erreicht + if (nResult != DET_INS_INSERTED) + nResult = DET_INS_CONTINUE; + } + } + } + } + } + pFCell->SetRunning(bRunning); + } + pCell = aCellIter.GetNext(); + } + + return nResult; +} + +USHORT ScDetectiveFunc::FindSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + USHORT nLevel, USHORT nDeleteLevel ) +{ + DBG_ASSERT( nLevel<1000, "Level" ); + + USHORT nResult = nLevel; + BOOL bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 ); + + ScCellIterator aCellIter( pDoc, 0,0, nTab, MAXCOL,MAXROW, nTab ); + ScBaseCell* pCell = aCellIter.GetFirst(); + while (pCell) + { + if (pCell->GetCellType() == CELLTYPE_FORMULA) + { + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + BOOL bRunning = pFCell->IsRunning(); + + if (pFCell->GetDirty()) + pFCell->Interpret(); // nach SetRunning geht's nicht mehr! + pFCell->SetRunning(TRUE); + + ScDetectiveRefIter aIter( (ScFormulaCell*) pCell ); + ScRange aRef; + while ( aIter.GetNextRef( aRef) ) + { + if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab) + { + if (Intersect( nCol1,nRow1,nCol2,nRow2, + aRef.aStart.Col(),aRef.aStart.Row(), + aRef.aEnd.Col(),aRef.aEnd.Row() )) + { + if ( bDelete ) // Pfeile, die hier anfangen + { + if (aRef.aStart != aRef.aEnd) + { + DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(), + aRef.aEnd.Col(), aRef.aEnd.Row() ); + } + DeleteArrowsAt( aRef.aStart.Col(), aRef.aStart.Row(), FALSE ); + } + else if ( !bRunning && + HasArrow( aRef.aStart, + aCellIter.GetCol(),aCellIter.GetRow(),aCellIter.GetTab() ) ) + { + USHORT nTemp = FindSuccLevel( aCellIter.GetCol(), aCellIter.GetRow(), + aCellIter.GetCol(), aCellIter.GetRow(), + nLevel+1, nDeleteLevel ); + if (nTemp > nResult) + nResult = nTemp; + } + } + } + } + + pFCell->SetRunning(bRunning); + } + pCell = aCellIter.GetNext(); + } + + return nResult; +} + + +// +// -------------------------------------------------------------------------------- +// + +BOOL ScDetectiveFunc::ShowPred( SCCOL nCol, SCROW nRow ) +{ + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + if (!pModel) + return FALSE; + + ScDetectiveData aData( pModel ); + + USHORT nMaxLevel = 0; + USHORT nResult = DET_INS_CONTINUE; + while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000) + { + aData.SetMaxLevel( nMaxLevel ); + nResult = InsertPredLevel( nCol, nRow, aData, 0 ); + ++nMaxLevel; + } + + return ( nResult == DET_INS_INSERTED ); +} + +BOOL ScDetectiveFunc::ShowSucc( SCCOL nCol, SCROW nRow ) +{ + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + if (!pModel) + return FALSE; + + ScDetectiveData aData( pModel ); + + USHORT nMaxLevel = 0; + USHORT nResult = DET_INS_CONTINUE; + while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000) + { + aData.SetMaxLevel( nMaxLevel ); + nResult = InsertSuccLevel( nCol, nRow, nCol, nRow, aData, 0 ); + ++nMaxLevel; + } + + return ( nResult == DET_INS_INSERTED ); +} + +BOOL ScDetectiveFunc::ShowError( SCCOL nCol, SCROW nRow ) +{ + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + if (!pModel) + return FALSE; + + ScRange aRange( nCol, nRow, nTab ); + ScAddress aErrPos; + if ( !HasError( aRange,aErrPos ) ) + return FALSE; + + ScDetectiveData aData( pModel ); + + aData.SetMaxLevel( 1000 ); + USHORT nResult = InsertErrorLevel( nCol, nRow, aData, 0 ); + + return ( nResult == DET_INS_INSERTED ); +} + +BOOL ScDetectiveFunc::DeleteSucc( SCCOL nCol, SCROW nRow ) +{ + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + if (!pModel) + return FALSE; + + USHORT nLevelCount = FindSuccLevel( nCol, nRow, nCol, nRow, 0, 0 ); + if ( nLevelCount ) + FindSuccLevel( nCol, nRow, nCol, nRow, 0, nLevelCount ); // loeschen + + return ( nLevelCount != 0 ); +} + +BOOL ScDetectiveFunc::DeletePred( SCCOL nCol, SCROW nRow ) +{ + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + if (!pModel) + return FALSE; + + USHORT nLevelCount = FindPredLevel( nCol, nRow, 0, 0 ); + if ( nLevelCount ) + FindPredLevel( nCol, nRow, 0, nLevelCount ); // loeschen + + return ( nLevelCount != 0 ); +} + +BOOL ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat ) +{ + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + if (!pModel) + return FALSE; + + SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + + pPage->RecalcObjOrdNums(); + + long nDelCount = 0; + ULONG nObjCount = pPage->GetObjCount(); + if (nObjCount) + { + SdrObject** ppObj = new SdrObject*[nObjCount]; + + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetLayer() == SC_LAYER_INTERN ) + { + BOOL bDoThis = TRUE; + if ( eWhat != SC_DET_ALL ) + { + BOOL bCircle = ( pObject->ISA(SdrCircObj) ); + BOOL bCaption = ScDrawLayer::IsNoteCaption( pObject ); + if ( eWhat == SC_DET_DETECTIVE ) // Detektiv, aus Menue + bDoThis = !bCaption; // auch Kreise + else if ( eWhat == SC_DET_CIRCLES ) // Kreise, wenn neue erzeugt werden + bDoThis = bCircle; + else if ( eWhat == SC_DET_ARROWS ) // DetectiveRefresh + bDoThis = !bCaption && !bCircle; // don't include circles + else + { + DBG_ERROR("wat?"); + } + } + if ( bDoThis ) + ppObj[nDelCount++] = pObject; + } + + pObject = aIter.Next(); + } + + long i; + for (i=1; i<=nDelCount; i++) + pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) ); + + for (i=1; i<=nDelCount; i++) + pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() ); + + delete[] ppObj; + + Modified(); + } + + return ( nDelCount != 0 ); +} + +BOOL ScDetectiveFunc::MarkInvalid(BOOL& rOverflow) +{ + rOverflow = FALSE; + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + if (!pModel) + return FALSE; + + BOOL bDeleted = DeleteAll( SC_DET_CIRCLES ); // nur die Kreise + + ScDetectiveData aData( pModel ); + long nInsCount = 0; + + // Stellen suchen, wo Gueltigkeit definiert ist + + ScDocAttrIterator aAttrIter( pDoc, nTab, 0,0,MAXCOL,MAXROW ); + SCCOL nCol; + SCROW nRow1; + SCROW nRow2; + const ScPatternAttr* pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 ); + while ( pPattern && nInsCount < SC_DET_MAXCIRCLE ) + { + ULONG nIndex = ((const SfxUInt32Item&)pPattern->GetItem(ATTR_VALIDDATA)).GetValue(); + if (nIndex) + { + const ScValidationData* pData = pDoc->GetValidationEntry( nIndex ); + if ( pData ) + { + // Zellen in dem Bereich durchgehen + + BOOL bMarkEmpty = !pData->IsIgnoreBlank(); + SCROW nNextRow = nRow1; + SCROW nRow; + ScCellIterator aCellIter( pDoc, nCol,nRow1,nTab, nCol,nRow2,nTab ); + ScBaseCell* pCell = aCellIter.GetFirst(); + while ( pCell && nInsCount < SC_DET_MAXCIRCLE ) + { + SCROW nCellRow = aCellIter.GetRow(); + if ( bMarkEmpty ) + for ( nRow = nNextRow; nRow < nCellRow && nInsCount < SC_DET_MAXCIRCLE; nRow++ ) + { + DrawCircle( nCol, nRow, aData ); + ++nInsCount; + } + if ( !pData->IsDataValid( pCell, ScAddress( nCol, nCellRow, nTab ) ) ) + { + DrawCircle( nCol, nCellRow, aData ); + ++nInsCount; + } + nNextRow = nCellRow + 1; + pCell = aCellIter.GetNext(); + } + if ( bMarkEmpty ) + for ( nRow = nNextRow; nRow <= nRow2 && nInsCount < SC_DET_MAXCIRCLE; nRow++ ) + { + DrawCircle( nCol, nRow, aData ); + ++nInsCount; + } + } + } + + pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 ); + } + + if ( nInsCount >= SC_DET_MAXCIRCLE ) + rOverflow = TRUE; + + return ( bDeleted || nInsCount != 0 ); +} + +void ScDetectiveFunc::UpdateAllComments( ScDocument& rDoc ) +{ + // for all caption objects, update attributes and SpecialTextBoxShadow flag + // (on all tables - nTab is ignored!) + + // no undo actions, this is refreshed after undo + + ScDrawLayer* pModel = rDoc.GetDrawLayer(); + if (!pModel) + return; + + for( SCTAB nObjTab = 0, nTabCount = rDoc.GetTableCount(); nObjTab < nTabCount; ++nObjTab ) + { + rDoc.InitializeNoteCaptions( nObjTab ); + SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) ); + DBG_ASSERT( pPage, "Page ?" ); + if( pPage ) + { + SdrObjListIter aIter( *pPage, IM_FLAT ); + for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() ) + { + if ( ScDrawObjData* pData = ScDrawLayer::GetNoteCaptionData( pObject, nObjTab ) ) + { + ScPostIt* pNote = rDoc.GetNote( pData->maStart ); + // caption should exist, we iterate over drawing objects... + DBG_ASSERT( pNote && (pNote->GetCaption() == pObject), "ScDetectiveFunc::UpdateAllComments - invalid cell note" ); + if( pNote ) + { + ScCommentData aData( rDoc, pModel ); + SfxItemSet aAttrColorSet = pObject->GetMergedItemSet(); + aAttrColorSet.Put( XFillColorItem( String(), GetCommentColor() ) ); + aData.UpdateCaptionSet( aAttrColorSet ); + pObject->SetMergedItemSetAndBroadcast( aData.GetCaptionSet() ); + if( SdrCaptionObj* pCaption = dynamic_cast< SdrCaptionObj* >( pObject ) ) + { + pCaption->SetSpecialTextBoxShadow(); + pCaption->SetFixedTail(); + } + } + } + } + } + } +} + +void ScDetectiveFunc::UpdateAllArrowColors() +{ + // no undo actions necessary + + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + if (!pModel) + return; + + for( SCTAB nObjTab = 0, nTabCount = pDoc->GetTableCount(); nObjTab < nTabCount; ++nObjTab ) + { + SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) ); + DBG_ASSERT( pPage, "Page ?" ); + if( pPage ) + { + SdrObjListIter aIter( *pPage, IM_FLAT ); + for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() ) + { + if ( pObject->GetLayer() == SC_LAYER_INTERN ) + { + BOOL bArrow = FALSE; + BOOL bError = FALSE; + + ScAddress aPos; + ScRange aSource; + BOOL bDummy; + ScDetectiveObjType eType = GetDetectiveObjectType( pObject, nObjTab, aPos, aSource, bDummy ); + if ( eType == SC_DETOBJ_ARROW || eType == SC_DETOBJ_TOOTHERTAB ) + { + // source is valid, determine error flag from source range + + ScAddress aErrPos; + if ( HasError( aSource, aErrPos ) ) + bError = TRUE; + else + bArrow = TRUE; + } + else if ( eType == SC_DETOBJ_FROMOTHERTAB ) + { + // source range is no longer known, take error flag from formula itself + // (this means, if the formula has an error, all references to other tables + // are marked red) + + ScAddress aErrPos; + if ( HasError( ScRange( aPos), aErrPos ) ) + bError = TRUE; + else + bArrow = TRUE; + } + else if ( eType == SC_DETOBJ_CIRCLE ) + { + // circles (error marks) are always red + + bError = TRUE; + } + else if ( eType == SC_DETOBJ_NONE ) + { + // frame for area reference has no ObjType, always gets arrow color + + if ( pObject->ISA( SdrRectObj ) && !pObject->ISA( SdrCaptionObj ) ) + { + bArrow = TRUE; + } + } + + if ( bArrow || bError ) + { + ColorData nColorData = ( bError ? GetErrorColor() : GetArrowColor() ); + //pObject->SendRepaintBroadcast(pObject->GetBoundRect()); + pObject->SetMergedItem( XLineColorItem( String(), Color( nColorData ) ) ); + + // repaint only + pObject->ActionChanged(); + // pObject->SendRepaintBroadcast(pObject->GetBoundRect()); + } + } + } + } + } +} + +BOOL ScDetectiveFunc::FindFrameForObject( SdrObject* pObject, ScRange& rRange ) +{ + // find the rectangle for an arrow (always the object directly before the arrow) + // rRange must be initialized to the source cell of the arrow (start of area) + + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + if (!pModel) return FALSE; + + SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + if (!pPage) return FALSE; + + // test if the object is a direct page member + if( pObject && pObject->GetPage() && (pObject->GetPage() == pObject->GetObjList()) ) + { + // Is there a previous object? + const sal_uInt32 nOrdNum(pObject->GetOrdNum()); + + if(nOrdNum > 0) + { + SdrObject* pPrevObj = pPage->GetObj(nOrdNum - 1); + + if ( pPrevObj && pPrevObj->GetLayer() == SC_LAYER_INTERN && pPrevObj->ISA(SdrRectObj) ) + { + ScDrawObjData* pPrevData = ScDrawLayer::GetObjDataTab( pPrevObj, rRange.aStart.Tab() ); + if ( pPrevData && pPrevData->maStart.IsValid() && pPrevData->maEnd.IsValid() && (pPrevData->maStart == rRange.aStart) ) + { + rRange.aEnd = pPrevData->maEnd; + return TRUE; + } + } + } + } + return FALSE; +} + +ScDetectiveObjType ScDetectiveFunc::GetDetectiveObjectType( SdrObject* pObject, SCTAB nObjTab, + ScAddress& rPosition, ScRange& rSource, BOOL& rRedLine ) +{ + rRedLine = FALSE; + ScDetectiveObjType eType = SC_DETOBJ_NONE; + + if ( pObject && pObject->GetLayer() == SC_LAYER_INTERN ) + { + if ( ScDrawObjData* pData = ScDrawLayer::GetObjDataTab( pObject, nObjTab ) ) + { + bool bValidStart = pData->maStart.IsValid(); + bool bValidEnd = pData->maEnd.IsValid(); + + if ( pObject->IsPolyObj() && pObject->GetPointCount() == 2 ) + { + // line object -> arrow + + if ( bValidStart ) + eType = bValidEnd ? SC_DETOBJ_ARROW : SC_DETOBJ_TOOTHERTAB; + else if ( bValidEnd ) + eType = SC_DETOBJ_FROMOTHERTAB; + + if ( bValidStart ) + rSource = pData->maStart; + if ( bValidEnd ) + rPosition = pData->maEnd; + + if ( bValidStart && lcl_HasThickLine( *pObject ) ) + { + // thick line -> look for frame before this object + + FindFrameForObject( pObject, rSource ); // modifies rSource + } + + ColorData nObjColor = ((const XLineColorItem&)pObject->GetMergedItem(XATTR_LINECOLOR)).GetColorValue().GetColor(); + if ( nObjColor == GetErrorColor() && nObjColor != GetArrowColor() ) + rRedLine = TRUE; + } + else if ( pObject->ISA(SdrCircObj) ) + { + if ( bValidStart ) + { + // cell position is returned in rPosition + + rPosition = pData->maStart; + eType = SC_DETOBJ_CIRCLE; + } + } + } + } + + return eType; +} + +void ScDetectiveFunc::InsertObject( ScDetectiveObjType eType, + const ScAddress& rPosition, const ScRange& rSource, + BOOL bRedLine ) +{ + ScDrawLayer* pModel = pDoc->GetDrawLayer(); + if (!pModel) return; + ScDetectiveData aData( pModel ); + + switch (eType) + { + case SC_DETOBJ_ARROW: + case SC_DETOBJ_FROMOTHERTAB: + InsertArrow( rPosition.Col(), rPosition.Row(), + rSource.aStart.Col(), rSource.aStart.Row(), + rSource.aEnd.Col(), rSource.aEnd.Row(), + (eType == SC_DETOBJ_FROMOTHERTAB), bRedLine, aData ); + break; + case SC_DETOBJ_TOOTHERTAB: + InsertToOtherTab( rSource.aStart.Col(), rSource.aStart.Row(), + rSource.aEnd.Col(), rSource.aEnd.Row(), + bRedLine, aData ); + break; + case SC_DETOBJ_CIRCLE: + DrawCircle( rPosition.Col(), rPosition.Row(), aData ); + break; + default: + { + // added to avoid warnings + } + } +} + +// static +ColorData ScDetectiveFunc::GetArrowColor() +{ + if (!bColorsInitialized) + InitializeColors(); + return nArrowColor; +} + +// static +ColorData ScDetectiveFunc::GetErrorColor() +{ + if (!bColorsInitialized) + InitializeColors(); + return nErrorColor; +} + +// static +ColorData ScDetectiveFunc::GetCommentColor() +{ + if (!bColorsInitialized) + InitializeColors(); + return nCommentColor; +} + +// static +void ScDetectiveFunc::InitializeColors() +{ + // may be called several times to update colors from configuration + + const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig(); + nArrowColor = rColorCfg.GetColorValue(svtools::CALCDETECTIVE).nColor; + nErrorColor = rColorCfg.GetColorValue(svtools::CALCDETECTIVEERROR).nColor; + nCommentColor = rColorCfg.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor; + + bColorsInitialized = TRUE; +} + +// static +BOOL ScDetectiveFunc::IsColorsInitialized() +{ + return bColorsInitialized; +} + diff --git a/sc/source/core/tool/docoptio.cxx b/sc/source/core/tool/docoptio.cxx new file mode 100644 index 000000000000..95ce357b3217 --- /dev/null +++ b/sc/source/core/tool/docoptio.cxx @@ -0,0 +1,442 @@ +/************************************************************************* + * + * 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 <vcl/svapp.hxx> +#include <svl/zforlist.hxx> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include "cfgids.hxx" +#include "docoptio.hxx" +#include "rechead.hxx" +#include "scresid.hxx" +#include "sc.hrc" +#include "miscuno.hxx" + +using namespace utl; +using namespace rtl; +using namespace com::sun::star::uno; + +//------------------------------------------------------------------------ + +#define SC_VERSION ((USHORT)251) + +TYPEINIT1(ScTpCalcItem, SfxPoolItem); + +//------------------------------------------------------------------------ + +//! these functions should be moved to some header file +inline long TwipsToHMM(long nTwips) { return (nTwips * 127 + 36) / 72; } +inline long HMMToTwips(long nHMM) { return (nHMM * 72 + 63) / 127; } + +inline long TwipsToEvenHMM(long nTwips) { return ( (nTwips * 127 + 72) / 144 ) * 2; } + +//------------------------------------------------------------------------ + +USHORT lcl_GetDefaultTabDist() +{ + if ( ScOptionsUtil::IsMetricSystem() ) + return 709; // 1,25 cm + else + return 720; // 1/2" +} + +//======================================================================== +// ScDocOptions - Dokument-Optionen +//======================================================================== + +ScDocOptions::ScDocOptions() +{ + ResetDocOptions(); +} + +//------------------------------------------------------------------------ + +ScDocOptions::ScDocOptions( const ScDocOptions& rCpy ) + : fIterEps( rCpy.fIterEps ), + nIterCount( rCpy.nIterCount ), + nPrecStandardFormat( rCpy.nPrecStandardFormat ), + nDay( rCpy.nDay ), + nMonth( rCpy.nMonth ), + nYear( rCpy.nYear ), + nYear2000( rCpy.nYear2000 ), + nTabDistance( rCpy.nTabDistance ), + bIsIgnoreCase( rCpy.bIsIgnoreCase ), + bIsIter( rCpy.bIsIter ), + bCalcAsShown( rCpy.bCalcAsShown ), + bMatchWholeCell( rCpy.bMatchWholeCell ), + bDoAutoSpell( rCpy.bDoAutoSpell ), + bLookUpColRowNames( rCpy.bLookUpColRowNames ), + bFormulaRegexEnabled( rCpy.bFormulaRegexEnabled ) +{ +} + +//------------------------------------------------------------------------ + +ScDocOptions::~ScDocOptions() +{ +} + +//------------------------------------------------------------------------ + +void ScDocOptions::ResetDocOptions() +{ + bIsIgnoreCase = FALSE; + bIsIter = FALSE; + nIterCount = 100; + fIterEps = 1.0E-3; + nPrecStandardFormat = SvNumberFormatter::UNLIMITED_PRECISION; + nDay = 30; + nMonth = 12; + nYear = 1899; + nYear2000 = SvNumberFormatter::GetYear2000Default(); + nTabDistance = lcl_GetDefaultTabDist(); + bCalcAsShown = FALSE; + bMatchWholeCell = TRUE; + bDoAutoSpell = FALSE; + bLookUpColRowNames = TRUE; + bFormulaRegexEnabled= TRUE; +} + +//======================================================================== +// ScTpCalcItem - Daten fuer die CalcOptions-TabPage +//======================================================================== + +//UNUSED2008-05 ScTpCalcItem::ScTpCalcItem( USHORT nWhichP ) : SfxPoolItem( nWhichP ) +//UNUSED2008-05 { +//UNUSED2008-05 } + +//------------------------------------------------------------------------ + +ScTpCalcItem::ScTpCalcItem( USHORT nWhichP, const ScDocOptions& rOpt ) + : SfxPoolItem ( nWhichP ), + theOptions ( rOpt ) +{ +} + +//------------------------------------------------------------------------ + +ScTpCalcItem::ScTpCalcItem( const ScTpCalcItem& rItem ) + : SfxPoolItem ( rItem ), + theOptions ( rItem.theOptions ) +{ +} + +//------------------------------------------------------------------------ + +__EXPORT ScTpCalcItem::~ScTpCalcItem() +{ +} + +//------------------------------------------------------------------------ + +String __EXPORT ScTpCalcItem::GetValueText() const +{ + return String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM("ScTpCalcItem") ); +} + +//------------------------------------------------------------------------ + +int __EXPORT ScTpCalcItem::operator==( const SfxPoolItem& rItem ) const +{ + DBG_ASSERT( SfxPoolItem::operator==( rItem ), "unequal Which or Type" ); + + const ScTpCalcItem& rPItem = (const ScTpCalcItem&)rItem; + + return ( theOptions == rPItem.theOptions ); +} + +//------------------------------------------------------------------------ + +SfxPoolItem* __EXPORT ScTpCalcItem::Clone( SfxItemPool * ) const +{ + return new ScTpCalcItem( *this ); +} + +//================================================================== +// Config Item containing document options +//================================================================== + +#define CFGPATH_CALC "Office.Calc/Calculate" + +#define SCCALCOPT_ITER_ITER 0 +#define SCCALCOPT_ITER_STEPS 1 +#define SCCALCOPT_ITER_MINCHG 2 +#define SCCALCOPT_DATE_DAY 3 +#define SCCALCOPT_DATE_MONTH 4 +#define SCCALCOPT_DATE_YEAR 5 +#define SCCALCOPT_DECIMALS 6 +#define SCCALCOPT_CASESENSITIVE 7 +#define SCCALCOPT_PRECISION 8 +#define SCCALCOPT_SEARCHCRIT 9 +#define SCCALCOPT_FINDLABEL 10 +#define SCCALCOPT_REGEX 11 +#define SCCALCOPT_COUNT 12 + +#define CFGPATH_DOCLAYOUT "Office.Calc/Layout/Other" + +#define SCDOCLAYOUTOPT_TABSTOP 0 +#define SCDOCLAYOUTOPT_COUNT 1 + + +Sequence<OUString> ScDocCfg::GetCalcPropertyNames() +{ + static const char* aPropNames[] = + { + "IterativeReference/Iteration", // SCCALCOPT_ITER_ITER + "IterativeReference/Steps", // SCCALCOPT_ITER_STEPS + "IterativeReference/MinimumChange", // SCCALCOPT_ITER_MINCHG + "Other/Date/DD", // SCCALCOPT_DATE_DAY + "Other/Date/MM", // SCCALCOPT_DATE_MONTH + "Other/Date/YY", // SCCALCOPT_DATE_YEAR + "Other/DecimalPlaces", // SCCALCOPT_DECIMALS + "Other/CaseSensitive", // SCCALCOPT_CASESENSITIVE + "Other/Precision", // SCCALCOPT_PRECISION + "Other/SearchCriteria", // SCCALCOPT_SEARCHCRIT + "Other/FindLabel", // SCCALCOPT_FINDLABEL + "Other/RegularExpressions" // SCCALCOPT_REGEX + }; + Sequence<OUString> aNames(SCCALCOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCCALCOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + return aNames; +} + +Sequence<OUString> ScDocCfg::GetLayoutPropertyNames() +{ + static const char* aPropNames[] = + { + "TabStop/NonMetric" // SCDOCLAYOUTOPT_TABSTOP + }; + Sequence<OUString> aNames(SCDOCLAYOUTOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCDOCLAYOUTOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + // adjust for metric system + if (ScOptionsUtil::IsMetricSystem()) + pNames[SCDOCLAYOUTOPT_TABSTOP] = OUString::createFromAscii( "TabStop/Metric" ); + + return aNames; +} + +ScDocCfg::ScDocCfg() : + aCalcItem( OUString::createFromAscii( CFGPATH_CALC ) ), + aLayoutItem( OUString::createFromAscii( CFGPATH_DOCLAYOUT ) ) +{ + sal_Int32 nIntVal = 0; + double fDoubleVal = 0; + + Sequence<OUString> aNames; + Sequence<Any> aValues; + const Any* pValues = NULL; + + USHORT nDateDay, nDateMonth, nDateYear; + GetDate( nDateDay, nDateMonth, nDateYear ); + + aNames = GetCalcPropertyNames(); + aValues = aCalcItem.GetProperties(aNames); + aCalcItem.EnableNotification(aNames); + pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCCALCOPT_ITER_ITER: + SetIter( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCCALCOPT_ITER_STEPS: + if (pValues[nProp] >>= nIntVal) SetIterCount( (USHORT) nIntVal ); + break; + case SCCALCOPT_ITER_MINCHG: + if (pValues[nProp] >>= fDoubleVal) SetIterEps( fDoubleVal ); + break; + case SCCALCOPT_DATE_DAY: + if (pValues[nProp] >>= nIntVal) nDateDay = (USHORT) nIntVal; + break; + case SCCALCOPT_DATE_MONTH: + if (pValues[nProp] >>= nIntVal) nDateMonth = (USHORT) nIntVal; + break; + case SCCALCOPT_DATE_YEAR: + if (pValues[nProp] >>= nIntVal) nDateYear = (USHORT) nIntVal; + break; + case SCCALCOPT_DECIMALS: + if (pValues[nProp] >>= nIntVal) SetStdPrecision( (USHORT) nIntVal ); + break; + case SCCALCOPT_CASESENSITIVE: + // content is reversed + SetIgnoreCase( !ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCCALCOPT_PRECISION: + SetCalcAsShown( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCCALCOPT_SEARCHCRIT: + SetMatchWholeCell( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCCALCOPT_FINDLABEL: + SetLookUpColRowNames( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCCALCOPT_REGEX : + SetFormulaRegexEnabled( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + } + } + } + } + aCalcItem.SetCommitLink( LINK( this, ScDocCfg, CalcCommitHdl ) ); + + SetDate( nDateDay, nDateMonth, nDateYear ); + + aNames = GetLayoutPropertyNames(); + aValues = aLayoutItem.GetProperties(aNames); + aLayoutItem.EnableNotification(aNames); + pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCDOCLAYOUTOPT_TABSTOP: + // TabDistance in ScDocOptions is in twips + if (pValues[nProp] >>= nIntVal) + SetTabDistance( (USHORT) HMMToTwips( nIntVal ) ); + break; + } + } + } + } + aLayoutItem.SetCommitLink( LINK( this, ScDocCfg, LayoutCommitHdl ) ); +} + +IMPL_LINK( ScDocCfg, CalcCommitHdl, void *, EMPTYARG ) +{ + Sequence<OUString> aNames = GetCalcPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + USHORT nDateDay, nDateMonth, nDateYear; + GetDate( nDateDay, nDateMonth, nDateYear ); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCCALCOPT_ITER_ITER: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], IsIter() ); + break; + case SCCALCOPT_ITER_STEPS: + pValues[nProp] <<= (sal_Int32) GetIterCount(); + break; + case SCCALCOPT_ITER_MINCHG: + pValues[nProp] <<= (double) GetIterEps(); + break; + case SCCALCOPT_DATE_DAY: + pValues[nProp] <<= (sal_Int32) nDateDay; + break; + case SCCALCOPT_DATE_MONTH: + pValues[nProp] <<= (sal_Int32) nDateMonth; + break; + case SCCALCOPT_DATE_YEAR: + pValues[nProp] <<= (sal_Int32) nDateYear; + break; + case SCCALCOPT_DECIMALS: + pValues[nProp] <<= (sal_Int32) GetStdPrecision(); + break; + case SCCALCOPT_CASESENSITIVE: + // content is reversed + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], !IsIgnoreCase() ); + break; + case SCCALCOPT_PRECISION: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], IsCalcAsShown() ); + break; + case SCCALCOPT_SEARCHCRIT: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], IsMatchWholeCell() ); + break; + case SCCALCOPT_FINDLABEL: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], IsLookUpColRowNames() ); + break; + case SCCALCOPT_REGEX : + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], IsFormulaRegexEnabled() ); + } + } + aCalcItem.PutProperties(aNames, aValues); + + return 0; +} + +IMPL_LINK( ScDocCfg, LayoutCommitHdl, void *, EMPTYARG ) +{ + Sequence<OUString> aNames = GetLayoutPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCDOCLAYOUTOPT_TABSTOP: + // TabDistance in ScDocOptions is in twips + // use only even numbers, so defaults don't get changed + // by modifying other settings in the same config item + pValues[nProp] <<= (sal_Int32) TwipsToEvenHMM( GetTabDistance() ); + break; + } + } + aLayoutItem.PutProperties(aNames, aValues); + + return 0; +} + + +void ScDocCfg::SetOptions( const ScDocOptions& rNew ) +{ + *(ScDocOptions*)this = rNew; + + aCalcItem.SetModified(); + aLayoutItem.SetModified(); +} + + diff --git a/sc/source/core/tool/doubleref.cxx b/sc/source/core/tool/doubleref.cxx new file mode 100644 index 000000000000..740413ea8533 --- /dev/null +++ b/sc/source/core/tool/doubleref.cxx @@ -0,0 +1,565 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: interpre.hxx,v $ + * $Revision: 1.35.44.2 $ + * + * 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 --------------------------------------------------------------- + +#include "doubleref.hxx" +#include "cell.hxx" +#include "global.hxx" +#include "document.hxx" +#include "queryparam.hxx" +#include "globstr.hrc" + +#include <memory> +#include <vector> + +using ::rtl::OUString; +using ::std::auto_ptr; +using ::std::vector; + +namespace { + +void lcl_toUpper(OUString& rStr) +{ + rStr = ScGlobal::pCharClass->toUpper(rStr.trim(), 0, static_cast<xub_StrLen>(rStr.getLength())); +} + +bool lcl_createStarQuery(ScQueryParamBase* pParam, const ScDBRangeBase* pDBRef, const ScDBRangeBase* pQueryRef) +{ + // A valid StarQuery must be at least 4 columns wide. To be precise it + // should be exactly 4 columns ... + // Additionally, if this wasn't checked, a formula pointing to a valid 1-3 + // column Excel style query range immediately left to itself would result + // in a circular reference when the field name or operator or value (first + // to third query range column) is obtained (#i58354#). Furthermore, if the + // range wasn't sufficiently specified data changes wouldn't flag formula + // cells for recalculation. + + if (pQueryRef->getColSize() < 4) + return false; + + BOOL bValid; + BOOL bFound; + OUString aCellStr; + SCSIZE nIndex = 0; + SCROW nRow = 0; + SCROW nRows = pDBRef->getRowSize(); + SCSIZE nNewEntries = static_cast<SCSIZE>(nRows); + pParam->Resize(nNewEntries); + + do + { + ScQueryEntry& rEntry = pParam->GetEntry(nIndex); + + bValid = FALSE; + + if (nIndex > 0) + { + // For all entries after the first one, check the and/or connector in the first column. + aCellStr = pQueryRef->getString(0, nRow); + lcl_toUpper(aCellStr); + if ( aCellStr.equals(ScGlobal::GetRscString(STR_TABLE_UND)) ) + { + rEntry.eConnect = SC_AND; + bValid = TRUE; + } + else if ( aCellStr.equals(ScGlobal::GetRscString(STR_TABLE_ODER)) ) + { + rEntry.eConnect = SC_OR; + bValid = TRUE; + } + } + + if ((nIndex < 1) || bValid) + { + // field name in the 2nd column. + bFound = FALSE; + aCellStr = pQueryRef->getString(1, nRow); + SCCOL nField = pDBRef->findFieldColumn(aCellStr); // TODO: must be case insensitive comparison. + if (ValidCol(nField)) + { + rEntry.nField = nField; + bValid = true; + } + else + bValid = false; + } + + if (bValid) + { + // equality, non-equality operator in the 3rd column. + bFound = FALSE; + aCellStr = pQueryRef->getString(2, nRow); + lcl_toUpper(aCellStr); + const sal_Unicode* p = aCellStr.getStr(); + if (p[0] == sal_Unicode('<')) + { + if (p[1] == sal_Unicode('>')) + rEntry.eOp = SC_NOT_EQUAL; + else if (p[1] == sal_Unicode('=')) + rEntry.eOp = SC_LESS_EQUAL; + else + rEntry.eOp = SC_LESS; + } + else if (p[0] == sal_Unicode('>')) + { + if (p[1] == sal_Unicode('=')) + rEntry.eOp = SC_GREATER_EQUAL; + else + rEntry.eOp = SC_GREATER; + } + else if (p[0] == sal_Unicode('=')) + rEntry.eOp = SC_EQUAL; + + } + + if (bValid) + { + // Finally, the right-hand-side value in the 4th column. + *rEntry.pStr = pQueryRef->getString(3, nRow); + rEntry.bDoQuery = TRUE; + } + nIndex++; + nRow++; + } + while (bValid && (nRow < nRows) /* && (nIndex < MAXQUERY) */ ); + return bValid; +} + +bool lcl_createExcelQuery( + ScQueryParamBase* pParam, const ScDBRangeBase* pDBRef, const ScDBRangeBase* pQueryRef) +{ + bool bValid = true; + SCCOL nCols = pQueryRef->getColSize(); + SCROW nRows = pQueryRef->getRowSize(); + vector<SCCOL> aFields(nCols); + SCCOL nCol = 0; + while (bValid && (nCol < nCols)) + { + OUString aQueryStr = pQueryRef->getString(nCol, 0); + SCCOL nField = pDBRef->findFieldColumn(aQueryStr); + if (ValidCol(nField)) + aFields[nCol] = nField; + else + bValid = false; + ++nCol; + } + + if (bValid) + { +// ULONG nVisible = 0; +// for ( nCol=nCol1; nCol<=nCol2; nCol++ ) +// nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 ); + + // Count the number of visible cells (excluding the header row). Each + // visible cell corresponds with a single query. + SCSIZE nVisible = pQueryRef->getVisibleDataCellCount(); + if ( nVisible > SCSIZE_MAX / sizeof(void*) ) + { + DBG_ERROR("zu viele Filterkritierien"); + nVisible = 0; + } + + SCSIZE nNewEntries = nVisible; + pParam->Resize( nNewEntries ); + + SCSIZE nIndex = 0; + SCROW nRow = 1; + String aCellStr; + while (nRow < nRows) + { + nCol = 0; + while (nCol < nCols) + { + aCellStr = pQueryRef->getString(nCol, nRow); + ScGlobal::pCharClass->toUpper( aCellStr ); + if (aCellStr.Len() > 0) + { + if (nIndex < nNewEntries) + { + pParam->GetEntry(nIndex).nField = aFields[nCol]; + pParam->FillInExcelSyntax(aCellStr, nIndex); + nIndex++; + if (nIndex < nNewEntries) + pParam->GetEntry(nIndex).eConnect = SC_AND; + } + else + bValid = FALSE; + } + nCol++; + } + nRow++; + if (nIndex < nNewEntries) + pParam->GetEntry(nIndex).eConnect = SC_OR; + } + } + return bValid; +} + +bool lcl_fillQueryEntries( + ScQueryParamBase* pParam, const ScDBRangeBase* pDBRef, const ScDBRangeBase* pQueryRef) +{ + SCSIZE nCount = pParam->GetEntryCount(); + for (SCSIZE i = 0; i < nCount; ++i) + pParam->GetEntry(i).Clear(); + + // Standard QueryTabelle + bool bValid = lcl_createStarQuery(pParam, pDBRef, pQueryRef); + // Excel QueryTabelle + if (!bValid) + bValid = lcl_createExcelQuery(pParam, pDBRef, pQueryRef); + + nCount = pParam->GetEntryCount(); + if (bValid) + { + // bQueryByString muss gesetzt sein + for (SCSIZE i = 0; i < nCount; ++i) + pParam->GetEntry(i).bQueryByString = true; + } + else + { + // nix + for (SCSIZE i = 0; i < nCount; ++i) + pParam->GetEntry(i).Clear(); + } + return bValid; +} + +} + +// ============================================================================ + +ScDBRangeBase::ScDBRangeBase(ScDocument* pDoc, RefType eType) : + mpDoc(pDoc), meType(eType) +{ +} + +ScDBRangeBase::~ScDBRangeBase() +{ +} + +bool ScDBRangeBase::fillQueryEntries(ScQueryParamBase* pParam, const ScDBRangeBase* pDBRef) const +{ + if (!pDBRef) + return false; + + return lcl_fillQueryEntries(pParam, pDBRef, this); +} + +void ScDBRangeBase::fillQueryOptions(ScQueryParamBase* pParam) +{ + pParam->bHasHeader = true; + pParam->bByRow = true; + pParam->bInplace = true; + pParam->bCaseSens = false; + pParam->bRegExp = false; + pParam->bDuplicate = true; + pParam->bMixedComparison = false; +} + +ScDocument* ScDBRangeBase::getDoc() const +{ + return mpDoc; +} + +// ============================================================================ + +ScDBInternalRange::ScDBInternalRange(ScDocument* pDoc, const ScRange& rRange) : + ScDBRangeBase(pDoc, INTERNAL), maRange(rRange) +{ +} + +ScDBInternalRange::~ScDBInternalRange() +{ +} + +const ScRange& ScDBInternalRange::getRange() const +{ + return maRange; +} + +SCCOL ScDBInternalRange::getColSize() const +{ + return maRange.aEnd.Col() - maRange.aStart.Col() + 1; +} + +SCROW ScDBInternalRange::getRowSize() const +{ + return maRange.aEnd.Row() - maRange.aStart.Row() + 1; +} + +SCSIZE ScDBInternalRange::getVisibleDataCellCount() const +{ + SCCOL nCols = getColSize(); + SCROW nRows = getRowSize(); + if (nRows <= 1) + return 0; + + return (nRows-1)*nCols; +} + +OUString ScDBInternalRange::getString(SCCOL nCol, SCROW nRow) const +{ + String aStr; + const ScAddress& s = maRange.aStart; + // #i109200# this is used in formula calculation, use GetInputString, not GetString + // (consistent with ScDBInternalRange::getCellString) + getDoc()->GetInputString(s.Col() + nCol, s.Row() + nRow, maRange.aStart.Tab(), aStr); + return aStr; +} + +SCCOL ScDBInternalRange::getFirstFieldColumn() const +{ + return getRange().aStart.Col(); +} + +SCCOL ScDBInternalRange::findFieldColumn(SCCOL nIndex) const +{ + const ScRange& rRange = getRange(); + const ScAddress& s = rRange.aStart; + const ScAddress& e = rRange.aEnd; + + SCCOL nDBCol1 = s.Col(); + SCCOL nDBCol2 = e.Col(); + + if ( nIndex <= 0 || nIndex > (nDBCol2 - nDBCol1 + 1) ) + return nDBCol1; + + return Min(nDBCol2, static_cast<SCCOL>(nDBCol1 + nIndex - 1)); +} + +sal_uInt16 ScDBInternalRange::getCellString(OUString& rStr, ScBaseCell* pCell) const +{ + sal_uInt16 nErr = 0; + String aStr; + if (pCell) + { + SvNumberFormatter* pFormatter = getDoc()->GetFormatTable(); + switch (pCell->GetCellType()) + { + case CELLTYPE_STRING: + ((ScStringCell*) pCell)->GetString(aStr); + break; + case CELLTYPE_EDIT: + ((ScEditCell*) pCell)->GetString(aStr); + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFCell = (ScFormulaCell*) pCell; + nErr = pFCell->GetErrCode(); + if (pFCell->IsValue()) + { + double fVal = pFCell->GetValue(); + ULONG nIndex = pFormatter->GetStandardFormat( + NUMBERFORMAT_NUMBER, + ScGlobal::eLnge); + pFormatter->GetInputLineString(fVal, nIndex, aStr); + } + else + pFCell->GetString(aStr); + } + break; + case CELLTYPE_VALUE: + { + double fVal = ((ScValueCell*) pCell)->GetValue(); + ULONG nIndex = pFormatter->GetStandardFormat( + NUMBERFORMAT_NUMBER, + ScGlobal::eLnge); + pFormatter->GetInputLineString(fVal, nIndex, aStr); + } + break; + default: + ; + } + } + rStr = aStr; + return nErr; +} + +SCCOL ScDBInternalRange::findFieldColumn(const OUString& rStr, sal_uInt16* pErr) const +{ + const ScAddress& s = maRange.aStart; + const ScAddress& e = maRange.aEnd; + OUString aUpper = rStr; + lcl_toUpper(aUpper); + + SCCOL nDBCol1 = s.Col(); + SCROW nDBRow1 = s.Row(); + SCTAB nDBTab1 = s.Tab(); + SCCOL nDBCol2 = e.Col(); + + SCCOL nField = nDBCol1; + BOOL bFound = TRUE; + + bFound = FALSE; + OUString aCellStr; + ScAddress aLook( nDBCol1, nDBRow1, nDBTab1 ); + while (!bFound && (aLook.Col() <= nDBCol2)) + { + ScBaseCell* pCell = getDoc()->GetCell( aLook ); + sal_uInt16 nErr = getCellString( aCellStr, pCell ); + if (pErr) + *pErr = nErr; + lcl_toUpper(aCellStr); + bFound = ScGlobal::GetpTransliteration()->isEqual(aCellStr, aUpper); + if (!bFound) + aLook.IncCol(); + } + nField = aLook.Col(); + + return bFound ? nField : -1; +} + +ScDBQueryParamBase* ScDBInternalRange::createQueryParam(const ScDBRangeBase* pQueryRef) const +{ + auto_ptr<ScDBQueryParamInternal> pParam(new ScDBQueryParamInternal); + + // Set the database range first. + const ScAddress& s = maRange.aStart; + const ScAddress& e = maRange.aEnd; + pParam->nCol1 = s.Col(); + pParam->nRow1 = s.Row(); + pParam->nCol2 = e.Col(); + pParam->nRow2 = e.Row(); + pParam->nTab = s.Tab(); + + fillQueryOptions(pParam.get()); + + // Now construct the query entries from the query range. + if (!pQueryRef->fillQueryEntries(pParam.get(), this)) + return NULL; + + return pParam.release(); +} + +bool ScDBInternalRange::isRangeEqual(const ScRange& rRange) const +{ + return maRange == rRange; +} + +// ============================================================================ + +ScDBExternalRange::ScDBExternalRange(ScDocument* pDoc, const ScMatrixRef& pMat) : + ScDBRangeBase(pDoc, EXTERNAL), mpMatrix(pMat) +{ + SCSIZE nC, nR; + mpMatrix->GetDimensions(nC, nR); + mnCols = static_cast<SCCOL>(nC); + mnRows = static_cast<SCROW>(nR); +} + +ScDBExternalRange::~ScDBExternalRange() +{ +} + +SCCOL ScDBExternalRange::getColSize() const +{ + return mnCols; +} + +SCROW ScDBExternalRange::getRowSize() const +{ + return mnRows; +} + +SCSIZE ScDBExternalRange::getVisibleDataCellCount() const +{ + SCCOL nCols = getColSize(); + SCROW nRows = getRowSize(); + if (nRows <= 1) + return 0; + + return (nRows-1)*nCols; +} + +OUString ScDBExternalRange::getString(SCCOL nCol, SCROW nRow) const +{ + if (nCol >= mnCols || nRow >= mnRows) + return OUString(); + + return mpMatrix->GetString(nCol, nRow); +} + +SCCOL ScDBExternalRange::getFirstFieldColumn() const +{ + return 0; +} + +SCCOL ScDBExternalRange::findFieldColumn(SCCOL nIndex) const +{ + if (nIndex < 1) + // 1st field + return 0; + + if (nIndex > mnCols) + // last field + return mnCols - 1; + + return nIndex - 1; +} + +SCCOL ScDBExternalRange::findFieldColumn(const OUString& rStr, sal_uInt16* pErr) const +{ + if (pErr) + pErr = 0; + + OUString aUpper = rStr; + lcl_toUpper(aUpper); + for (SCCOL i = 0; i < mnCols; ++i) + { + OUString aUpperVal = mpMatrix->GetString(i, 0); + lcl_toUpper(aUpperVal); + if (aUpper.equals(aUpperVal)) + return i; + } + return -1; +} + +ScDBQueryParamBase* ScDBExternalRange::createQueryParam(const ScDBRangeBase* pQueryRef) const +{ + auto_ptr<ScDBQueryParamMatrix> pParam(new ScDBQueryParamMatrix); + pParam->mpMatrix = mpMatrix; + fillQueryOptions(pParam.get()); + + // Now construct the query entries from the query range. + if (!pQueryRef->fillQueryEntries(pParam.get(), this)) + return NULL; + + return pParam.release(); +} + +bool ScDBExternalRange::isRangeEqual(const ScRange& /*rRange*/) const +{ + return false; +} + diff --git a/sc/source/core/tool/editutil.cxx b/sc/source/core/tool/editutil.cxx new file mode 100644 index 000000000000..483f0cea6b57 --- /dev/null +++ b/sc/source/core/tool/editutil.cxx @@ -0,0 +1,778 @@ +/************************************************************************* + * + * 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" + +// System - Includes ----------------------------------------------------- + + + +// INCLUDE --------------------------------------------------------------- + +#include "scitems.hxx" +#include <editeng/eeitem.hxx> + +#include <svx/algitem.hxx> +#include <svtools/colorcfg.hxx> +#include <editeng/editview.hxx> +#include <editeng/editstat.hxx> +#include <editeng/escpitem.hxx> +#include <editeng/flditem.hxx> +#include <editeng/numitem.hxx> +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> +#include <svl/inethist.hxx> +#include <unotools/syslocale.hxx> +#ifndef _SVSTDARR_USHORTS +#define _SVSTDARR_USHORTS +#include <svl/svstdarr.hxx> +#endif + +#include "editutil.hxx" +#include "global.hxx" +#include "attrib.hxx" +#include "document.hxx" +#include "docpool.hxx" +#include "patattr.hxx" +#include "scmod.hxx" +#include "inputopt.hxx" +#include "compiler.hxx" + +// STATIC DATA ----------------------------------------------------------- + +// Delimiters zusaetzlich zu EditEngine-Default: + +const sal_Char __FAR_DATA ScEditUtil::pCalcDelimiters[] = "=()+-*/^&<>"; + + +//------------------------------------------------------------------------ + +String ScEditUtil::ModifyDelimiters( const String& rOld ) +{ + String aRet = rOld; + aRet.EraseAllChars( '_' ); // underscore is used in function argument names + aRet.AppendAscii( RTL_CONSTASCII_STRINGPARAM( pCalcDelimiters ) ); + aRet.Append(ScCompiler::GetNativeSymbol(ocSep)); // argument separator is localized. + return aRet; +} + +static String lcl_GetDelimitedString( const EditEngine& rEngine, const sal_Char c ) +{ + String aRet; + USHORT nParCount = rEngine.GetParagraphCount(); + for (USHORT nPar=0; nPar<nParCount; nPar++) + { + if (nPar > 0) + aRet += c; + aRet += rEngine.GetText( nPar ); + } + return aRet; +} + +String ScEditUtil::GetSpaceDelimitedString( const EditEngine& rEngine ) +{ + return lcl_GetDelimitedString(rEngine, ' '); +} + +String ScEditUtil::GetMultilineString( const EditEngine& rEngine ) +{ + return lcl_GetDelimitedString(rEngine, '\n'); +} + +//------------------------------------------------------------------------ + +Rectangle ScEditUtil::GetEditArea( const ScPatternAttr* pPattern, BOOL bForceToTop ) +{ + // bForceToTop = always align to top, for editing + // (FALSE for querying URLs etc.) + + if (!pPattern) + pPattern = pDoc->GetPattern( nCol, nRow, nTab ); + + Point aStartPos = aScrPos; + + BOOL bLayoutRTL = pDoc->IsLayoutRTL( nTab ); + long nLayoutSign = bLayoutRTL ? -1 : 1; + + const ScMergeAttr* pMerge = (const ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE); + long nCellX = (long) ( pDoc->GetColWidth(nCol,nTab) * nPPTX ); + if ( pMerge->GetColMerge() > 1 ) + { + SCCOL nCountX = pMerge->GetColMerge(); + for (SCCOL i=1; i<nCountX; i++) + nCellX += (long) ( pDoc->GetColWidth(nCol+i,nTab) * nPPTX ); + } + long nCellY = (long) ( pDoc->GetRowHeight(nRow,nTab) * nPPTY ); + if ( pMerge->GetRowMerge() > 1 ) + { + SCROW nCountY = pMerge->GetRowMerge(); + nCellY += (long) pDoc->GetScaledRowHeight( nRow+1, nRow+nCountY-1, nTab, nPPTY); + } + + const SvxMarginItem* pMargin = (const SvxMarginItem*)&pPattern->GetItem(ATTR_MARGIN); + USHORT nIndent = 0; + if ( ((const SvxHorJustifyItem&)pPattern->GetItem(ATTR_HOR_JUSTIFY)).GetValue() == + SVX_HOR_JUSTIFY_LEFT ) + nIndent = ((const SfxUInt16Item&)pPattern->GetItem(ATTR_INDENT)).GetValue(); + long nPixDifX = (long) ( ( pMargin->GetLeftMargin() + nIndent ) * nPPTX ); + aStartPos.X() += nPixDifX * nLayoutSign; + nCellX -= nPixDifX + (long) ( pMargin->GetRightMargin() * nPPTX ); // wegen Umbruch etc. + + // vertikale Position auf die in der Tabelle anpassen + + long nPixDifY; + long nTopMargin = (long) ( pMargin->GetTopMargin() * nPPTY ); + SvxCellVerJustify eJust = (SvxCellVerJustify) ((const SvxVerJustifyItem&)pPattern-> + GetItem(ATTR_VER_JUSTIFY)).GetValue(); + + // asian vertical is always edited top-aligned + BOOL bAsianVertical = ((const SfxBoolItem&)pPattern->GetItem( ATTR_STACKED )).GetValue() && + ((const SfxBoolItem&)pPattern->GetItem( ATTR_VERTICAL_ASIAN )).GetValue(); + + if ( eJust == SVX_VER_JUSTIFY_TOP || + ( bForceToTop && ( SC_MOD()->GetInputOptions().GetTextWysiwyg() || bAsianVertical ) ) ) + nPixDifY = nTopMargin; + else + { + MapMode aMode = pDev->GetMapMode(); + pDev->SetMapMode( MAP_PIXEL ); + + long nTextHeight = pDoc->GetNeededSize( nCol, nRow, nTab, + pDev, nPPTX, nPPTY, aZoomX, aZoomY, FALSE ); + if (!nTextHeight) + { // leere Zelle + Font aFont; + // font color doesn't matter here + pPattern->GetFont( aFont, SC_AUTOCOL_BLACK, pDev, &aZoomY ); + pDev->SetFont(aFont); + nTextHeight = pDev->GetTextHeight() + nTopMargin + + (long) ( pMargin->GetBottomMargin() * nPPTY ); + } + + pDev->SetMapMode(aMode); + + if ( nTextHeight > nCellY + nTopMargin || bForceToTop ) + nPixDifY = 0; // zu gross -> oben anfangen + else + { + if ( eJust == SVX_VER_JUSTIFY_CENTER ) + nPixDifY = nTopMargin + ( nCellY - nTextHeight ) / 2; + else + nPixDifY = nCellY - nTextHeight + nTopMargin; // JUSTIFY_BOTTOM + } + } + + aStartPos.Y() += nPixDifY; + nCellY -= nPixDifY; + + if ( bLayoutRTL ) + aStartPos.X() -= nCellX - 2; // excluding grid on both sides + + // -1 -> Gitter nicht ueberschreiben + return Rectangle( aStartPos, Size(nCellX-1,nCellY-1) ); +} + +//------------------------------------------------------------------------ + +ScEditAttrTester::ScEditAttrTester( ScEditEngineDefaulter* pEng ) : + pEngine( pEng ), + pEditAttrs( NULL ), + bNeedsObject( FALSE ), + bNeedsCellAttr( FALSE ) +{ + if ( pEngine->GetParagraphCount() > 1 ) + { + bNeedsObject = TRUE; //! Zellatribute finden ? + } + else + { + const SfxPoolItem* pItem = NULL; + pEditAttrs = new SfxItemSet( pEngine->GetAttribs( + ESelection(0,0,0,pEngine->GetTextLen(0)), EditEngineAttribs_OnlyHard ) ); + const SfxItemSet& rEditDefaults = pEngine->GetDefaults(); + + for (USHORT nId = EE_CHAR_START; nId <= EE_CHAR_END && !bNeedsObject; nId++) + { + SfxItemState eState = pEditAttrs->GetItemState( nId, FALSE, &pItem ); + if (eState == SFX_ITEM_DONTCARE) + bNeedsObject = TRUE; + else if (eState == SFX_ITEM_SET) + { + if ( nId == EE_CHAR_ESCAPEMENT || nId == EE_CHAR_PAIRKERNING || + nId == EE_CHAR_KERNING || nId == EE_CHAR_XMLATTRIBS ) + { + // Escapement and kerning are kept in EditEngine because there are no + // corresponding cell format items. User defined attributes are kept in + // EditEngine because "user attributes applied to all the text" is different + // from "user attributes applied to the cell". + + if ( *pItem != rEditDefaults.Get(nId) ) + bNeedsObject = TRUE; + } + else + if (!bNeedsCellAttr) + if ( *pItem != rEditDefaults.Get(nId) ) + bNeedsCellAttr = TRUE; + // rEditDefaults contains the defaults from the cell format + } + } + + // Feldbefehle enthalten? + + SfxItemState eFieldState = pEditAttrs->GetItemState( EE_FEATURE_FIELD, FALSE ); + if ( eFieldState == SFX_ITEM_DONTCARE || eFieldState == SFX_ITEM_SET ) + bNeedsObject = TRUE; + + // not converted characters? + + SfxItemState eConvState = pEditAttrs->GetItemState( EE_FEATURE_NOTCONV, FALSE ); + if ( eConvState == SFX_ITEM_DONTCARE || eConvState == SFX_ITEM_SET ) + bNeedsObject = TRUE; + } +} + +ScEditAttrTester::~ScEditAttrTester() +{ + delete pEditAttrs; +} + + +//------------------------------------------------------------------------ + +ScEnginePoolHelper::ScEnginePoolHelper( SfxItemPool* pEnginePoolP, + BOOL bDeleteEnginePoolP ) + : + pEnginePool( pEnginePoolP ), + pDefaults( NULL ), + bDeleteEnginePool( bDeleteEnginePoolP ), + bDeleteDefaults( FALSE ) +{ +} + + +ScEnginePoolHelper::ScEnginePoolHelper( const ScEnginePoolHelper& rOrg ) + : + pEnginePool( rOrg.bDeleteEnginePool ? rOrg.pEnginePool->Clone() : rOrg.pEnginePool ), + pDefaults( NULL ), + bDeleteEnginePool( rOrg.bDeleteEnginePool ), + bDeleteDefaults( FALSE ) +{ +} + + +ScEnginePoolHelper::~ScEnginePoolHelper() +{ + if ( bDeleteDefaults ) + delete pDefaults; + if ( bDeleteEnginePool ) + SfxItemPool::Free(pEnginePool); +} + + +//------------------------------------------------------------------------ + +ScEditEngineDefaulter::ScEditEngineDefaulter( SfxItemPool* pEnginePoolP, + BOOL bDeleteEnginePoolP ) + : + ScEnginePoolHelper( pEnginePoolP, bDeleteEnginePoolP ), + EditEngine( pEnginePoolP ) +{ + // All EditEngines use ScGlobal::GetEditDefaultLanguage as DefaultLanguage. + // DefaultLanguage for InputHandler's EditEngine is updated later. + + SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() ); +} + + +ScEditEngineDefaulter::ScEditEngineDefaulter( const ScEditEngineDefaulter& rOrg ) + : + ScEnginePoolHelper( rOrg ), + EditEngine( pEnginePool ) +{ + SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() ); +} + + +ScEditEngineDefaulter::~ScEditEngineDefaulter() +{ +} + + +void ScEditEngineDefaulter::SetDefaults( const SfxItemSet& rSet, BOOL bRememberCopy ) +{ + if ( bRememberCopy ) + { + if ( bDeleteDefaults ) + delete pDefaults; + pDefaults = new SfxItemSet( rSet ); + bDeleteDefaults = TRUE; + } + const SfxItemSet& rNewSet = bRememberCopy ? *pDefaults : rSet; + BOOL bUndo = IsUndoEnabled(); + EnableUndo( FALSE ); + BOOL bUpdateMode = GetUpdateMode(); + if ( bUpdateMode ) + SetUpdateMode( FALSE ); + USHORT nPara = GetParagraphCount(); + for ( USHORT j=0; j<nPara; j++ ) + { + SetParaAttribs( j, rNewSet ); + } + if ( bUpdateMode ) + SetUpdateMode( TRUE ); + if ( bUndo ) + EnableUndo( TRUE ); +} + + +void ScEditEngineDefaulter::SetDefaults( SfxItemSet* pSet, BOOL bTakeOwnership ) +{ + if ( bDeleteDefaults ) + delete pDefaults; + pDefaults = pSet; + bDeleteDefaults = bTakeOwnership; + if ( pDefaults ) + SetDefaults( *pDefaults, FALSE ); +} + + +void ScEditEngineDefaulter::SetDefaultItem( const SfxPoolItem& rItem ) +{ + if ( !pDefaults ) + { + pDefaults = new SfxItemSet( GetEmptyItemSet() ); + bDeleteDefaults = TRUE; + } + pDefaults->Put( rItem ); + SetDefaults( *pDefaults, FALSE ); +} + +const SfxItemSet& ScEditEngineDefaulter::GetDefaults() +{ + if ( !pDefaults ) + { + pDefaults = new SfxItemSet( GetEmptyItemSet() ); + bDeleteDefaults = TRUE; + } + return *pDefaults; +} + +void ScEditEngineDefaulter::SetText( const EditTextObject& rTextObject ) +{ + BOOL bUpdateMode = GetUpdateMode(); + if ( bUpdateMode ) + SetUpdateMode( FALSE ); + EditEngine::SetText( rTextObject ); + if ( pDefaults ) + SetDefaults( *pDefaults, FALSE ); + if ( bUpdateMode ) + SetUpdateMode( TRUE ); +} + +void ScEditEngineDefaulter::SetTextNewDefaults( const EditTextObject& rTextObject, + const SfxItemSet& rSet, BOOL bRememberCopy ) +{ + BOOL bUpdateMode = GetUpdateMode(); + if ( bUpdateMode ) + SetUpdateMode( FALSE ); + EditEngine::SetText( rTextObject ); + SetDefaults( rSet, bRememberCopy ); + if ( bUpdateMode ) + SetUpdateMode( TRUE ); +} + +void ScEditEngineDefaulter::SetTextNewDefaults( const EditTextObject& rTextObject, + SfxItemSet* pSet, BOOL bTakeOwnership ) +{ + BOOL bUpdateMode = GetUpdateMode(); + if ( bUpdateMode ) + SetUpdateMode( FALSE ); + EditEngine::SetText( rTextObject ); + SetDefaults( pSet, bTakeOwnership ); + if ( bUpdateMode ) + SetUpdateMode( TRUE ); +} + + +void ScEditEngineDefaulter::SetText( const String& rText ) +{ + BOOL bUpdateMode = GetUpdateMode(); + if ( bUpdateMode ) + SetUpdateMode( FALSE ); + EditEngine::SetText( rText ); + if ( pDefaults ) + SetDefaults( *pDefaults, FALSE ); + if ( bUpdateMode ) + SetUpdateMode( TRUE ); +} + +void ScEditEngineDefaulter::SetTextNewDefaults( const String& rText, + const SfxItemSet& rSet, BOOL bRememberCopy ) +{ + BOOL bUpdateMode = GetUpdateMode(); + if ( bUpdateMode ) + SetUpdateMode( FALSE ); + EditEngine::SetText( rText ); + SetDefaults( rSet, bRememberCopy ); + if ( bUpdateMode ) + SetUpdateMode( TRUE ); +} + +void ScEditEngineDefaulter::SetTextNewDefaults( const String& rText, + SfxItemSet* pSet, BOOL bTakeOwnership ) +{ + BOOL bUpdateMode = GetUpdateMode(); + if ( bUpdateMode ) + SetUpdateMode( FALSE ); + EditEngine::SetText( rText ); + SetDefaults( pSet, bTakeOwnership ); + if ( bUpdateMode ) + SetUpdateMode( TRUE ); +} + +void ScEditEngineDefaulter::RepeatDefaults() +{ + if ( pDefaults ) + { + USHORT nPara = GetParagraphCount(); + for ( USHORT j=0; j<nPara; j++ ) + SetParaAttribs( j, *pDefaults ); + } +} + +void ScEditEngineDefaulter::RemoveParaAttribs() +{ + SfxItemSet* pCharItems = NULL; + BOOL bUpdateMode = GetUpdateMode(); + if ( bUpdateMode ) + SetUpdateMode( FALSE ); + USHORT nParCount = GetParagraphCount(); + for (USHORT nPar=0; nPar<nParCount; nPar++) + { + const SfxItemSet& rParaAttribs = GetParaAttribs( nPar ); + USHORT nWhich; + for (nWhich = EE_CHAR_START; nWhich <= EE_CHAR_END; nWhich ++) + { + const SfxPoolItem* pParaItem; + if ( rParaAttribs.GetItemState( nWhich, FALSE, &pParaItem ) == SFX_ITEM_SET ) + { + // if defaults are set, use only items that are different from default + if ( !pDefaults || *pParaItem != pDefaults->Get(nWhich) ) + { + if (!pCharItems) + pCharItems = new SfxItemSet( GetEmptyItemSet() ); + pCharItems->Put( *pParaItem ); + } + } + } + + if ( pCharItems ) + { + SvUShorts aPortions; + GetPortions( nPar, aPortions ); + + // loop through the portions of the paragraph, and set only those items + // that are not overridden by existing character attributes + + USHORT nPCount = aPortions.Count(); + USHORT nStart = 0; + for ( USHORT nPos=0; nPos<nPCount; nPos++ ) + { + USHORT nEnd = aPortions.GetObject( nPos ); + ESelection aSel( nPar, nStart, nPar, nEnd ); + SfxItemSet aOldCharAttrs = GetAttribs( aSel ); + SfxItemSet aNewCharAttrs = *pCharItems; + for (nWhich = EE_CHAR_START; nWhich <= EE_CHAR_END; nWhich ++) + { + // Clear those items that are different from existing character attributes. + // Where no character attributes are set, GetAttribs returns the paragraph attributes. + const SfxPoolItem* pItem; + if ( aNewCharAttrs.GetItemState( nWhich, FALSE, &pItem ) == SFX_ITEM_SET && + *pItem != aOldCharAttrs.Get(nWhich) ) + { + aNewCharAttrs.ClearItem(nWhich); + } + } + if ( aNewCharAttrs.Count() ) + QuickSetAttribs( aNewCharAttrs, aSel ); + + nStart = nEnd; + } + + DELETEZ( pCharItems ); + } + + if ( rParaAttribs.Count() ) + { + // clear all paragraph attributes (including defaults), + // so they are not contained in resulting EditTextObjects + + SetParaAttribs( nPar, SfxItemSet( *rParaAttribs.GetPool(), rParaAttribs.GetRanges() ) ); + } + } + if ( bUpdateMode ) + SetUpdateMode( TRUE ); +} + +//------------------------------------------------------------------------ + +ScTabEditEngine::ScTabEditEngine( ScDocument* pDoc ) + : ScEditEngineDefaulter( pDoc->GetEnginePool() ) +{ + SetEditTextObjectPool( pDoc->GetEditPool() ); + Init((const ScPatternAttr&)pDoc->GetPool()->GetDefaultItem(ATTR_PATTERN)); +} + +ScTabEditEngine::ScTabEditEngine( const ScPatternAttr& rPattern, + SfxItemPool* pEnginePoolP, SfxItemPool* pTextObjectPool ) + : ScEditEngineDefaulter( pEnginePoolP ) +{ + if ( pTextObjectPool ) + SetEditTextObjectPool( pTextObjectPool ); + Init( rPattern ); +} + +void ScTabEditEngine::Init( const ScPatternAttr& rPattern ) +{ + SetRefMapMode(MAP_100TH_MM); + SfxItemSet* pEditDefaults = new SfxItemSet( GetEmptyItemSet() ); + rPattern.FillEditItemSet( pEditDefaults ); + SetDefaults( pEditDefaults ); + // wir haben keine StyleSheets fuer Text + SetControlWord( GetControlWord() & ~EE_CNTRL_RTFSTYLESHEETS ); +} + +//------------------------------------------------------------------------ +// Feldbefehle fuer Kopf- und Fusszeilen +//------------------------------------------------------------------------ + +// +// Zahlen aus \sw\source\core\doc\numbers.cxx +// + +String lcl_GetCharStr( sal_Int32 nNo ) +{ + DBG_ASSERT( nNo, "0 ist eine ungueltige Nummer !!" ); + String aStr; + + const sal_Int32 coDiff = 'Z' - 'A' +1; + sal_Int32 nCalc; + + do { + nCalc = nNo % coDiff; + if( !nCalc ) + nCalc = coDiff; + aStr.Insert( (sal_Unicode)('a' - 1 + nCalc ), 0 ); + nNo = sal::static_int_cast<sal_Int32>( nNo - nCalc ); + if( nNo ) + nNo /= coDiff; + } while( nNo ); + return aStr; +} + +String lcl_GetNumStr( sal_Int32 nNo, SvxNumType eType ) +{ + String aTmpStr( '0' ); + if( nNo ) + { + switch( eType ) + { + case SVX_CHARS_UPPER_LETTER: + case SVX_CHARS_LOWER_LETTER: + aTmpStr = lcl_GetCharStr( nNo ); + break; + + case SVX_ROMAN_UPPER: + case SVX_ROMAN_LOWER: + if( nNo < 4000 ) + aTmpStr = SvxNumberFormat::CreateRomanString( nNo, ( eType == SVX_ROMAN_UPPER ) ); + else + aTmpStr.Erase(); + break; + + case SVX_NUMBER_NONE: + aTmpStr.Erase(); + break; + +// CHAR_SPECIAL: +// ???? + +// case ARABIC: ist jetzt default + default: + aTmpStr = String::CreateFromInt32( nNo ); + break; + } + + if( SVX_CHARS_UPPER_LETTER == eType ) + aTmpStr.ToUpperAscii(); + } + return aTmpStr; +} + +ScHeaderFieldData::ScHeaderFieldData() +{ + nPageNo = nTotalPages = 0; + eNumType = SVX_ARABIC; +} + +ScHeaderEditEngine::ScHeaderEditEngine( SfxItemPool* pEnginePoolP, BOOL bDeleteEnginePoolP ) + : ScEditEngineDefaulter( pEnginePoolP, bDeleteEnginePoolP ) +{ +} + +String __EXPORT ScHeaderEditEngine::CalcFieldValue( const SvxFieldItem& rField, + USHORT /* nPara */, USHORT /* nPos */, + Color*& /* rTxtColor */, Color*& /* rFldColor */ ) +{ + String aRet; + const SvxFieldData* pFieldData = rField.GetField(); + if ( pFieldData ) + { + TypeId aType = pFieldData->Type(); + if (aType == TYPE(SvxPageField)) + aRet = lcl_GetNumStr( aData.nPageNo,aData.eNumType ); + else if (aType == TYPE(SvxPagesField)) + aRet = lcl_GetNumStr( aData.nTotalPages,aData.eNumType ); + else if (aType == TYPE(SvxTimeField)) + aRet = ScGlobal::pLocaleData->getTime(aData.aTime); + else if (aType == TYPE(SvxFileField)) + aRet = aData.aTitle; + else if (aType == TYPE(SvxExtFileField)) + { + switch ( ((const SvxExtFileField*)pFieldData)->GetFormat() ) + { + case SVXFILEFORMAT_FULLPATH : + aRet = aData.aLongDocName; + break; + default: + aRet = aData.aShortDocName; + } + } + else if (aType == TYPE(SvxTableField)) + aRet = aData.aTabName; + else if (aType == TYPE(SvxDateField)) + aRet = ScGlobal::pLocaleData->getDate(aData.aDate); + else + { + //DBG_ERROR("unbekannter Feldbefehl"); + aRet = '?'; + } + } + else + { + DBG_ERROR("FieldData ist 0"); + aRet = '?'; + } + + return aRet; +} + +//------------------------------------------------------------------------ +// +// Feld-Daten +// +//------------------------------------------------------------------------ + +ScFieldEditEngine::ScFieldEditEngine( SfxItemPool* pEnginePoolP, + SfxItemPool* pTextObjectPool, BOOL bDeleteEnginePoolP ) + : + ScEditEngineDefaulter( pEnginePoolP, bDeleteEnginePoolP ), + bExecuteURL( TRUE ) +{ + if ( pTextObjectPool ) + SetEditTextObjectPool( pTextObjectPool ); + // EE_CNTRL_URLSFXEXECUTE nicht, weil die Edit-Engine den ViewFrame nicht kennt + // wir haben keine StyleSheets fuer Text + SetControlWord( (GetControlWord() | EE_CNTRL_MARKFIELDS) & ~EE_CNTRL_RTFSTYLESHEETS ); +} + +String __EXPORT ScFieldEditEngine::CalcFieldValue( const SvxFieldItem& rField, + USHORT /* nPara */, USHORT /* nPos */, + Color*& rTxtColor, Color*& /* rFldColor */ ) +{ + String aRet; + const SvxFieldData* pFieldData = rField.GetField(); + + if ( pFieldData ) + { + TypeId aType = pFieldData->Type(); + + if (aType == TYPE(SvxURLField)) + { + String aURL = ((const SvxURLField*)pFieldData)->GetURL(); + + switch ( ((const SvxURLField*)pFieldData)->GetFormat() ) + { + case SVXURLFORMAT_APPDEFAULT: //!!! einstellbar an App??? + case SVXURLFORMAT_REPR: + aRet = ((const SvxURLField*)pFieldData)->GetRepresentation(); + break; + + case SVXURLFORMAT_URL: + aRet = aURL; + break; + } + + svtools::ColorConfigEntry eEntry = + INetURLHistory::GetOrCreate()->QueryUrl( aURL ) ? svtools::LINKSVISITED : svtools::LINKS; + rTxtColor = new Color( SC_MOD()->GetColorConfig().GetColorValue(eEntry).nColor ); + } + else + { + //DBG_ERROR("unbekannter Feldbefehl"); + aRet = '?'; + } + } + + if (!aRet.Len()) // leer ist baeh + aRet = ' '; // Space ist Default der Editengine + + return aRet; +} + +void __EXPORT ScFieldEditEngine::FieldClicked( const SvxFieldItem& rField, USHORT, USHORT ) +{ + const SvxFieldData* pFld = rField.GetField(); + + if ( pFld && pFld->ISA( SvxURLField ) && bExecuteURL ) + { + const SvxURLField* pURLField = (const SvxURLField*) pFld; + ScGlobal::OpenURL( pURLField->GetURL(), pURLField->GetTargetFrame() ); + } +} + +//------------------------------------------------------------------------ + +ScNoteEditEngine::ScNoteEditEngine( SfxItemPool* pEnginePoolP, + SfxItemPool* pTextObjectPool, BOOL bDeleteEnginePoolP ) : + ScEditEngineDefaulter( pEnginePoolP, bDeleteEnginePoolP ) +{ + if ( pTextObjectPool ) + SetEditTextObjectPool( pTextObjectPool ); + SetControlWord( (GetControlWord() | EE_CNTRL_MARKFIELDS) & ~EE_CNTRL_RTFSTYLESHEETS ); +} diff --git a/sc/source/core/tool/filtopt.cxx b/sc/source/core/tool/filtopt.cxx new file mode 100644 index 000000000000..7ac3d710b420 --- /dev/null +++ b/sc/source/core/tool/filtopt.cxx @@ -0,0 +1,120 @@ +/************************************************************************* + * + * 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 <tools/debug.hxx> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include "filtopt.hxx" +#include "miscuno.hxx" + +using namespace utl; +using namespace rtl; +using namespace com::sun::star::uno; + +//------------------------------------------------------------------ + +#define CFGPATH_FILTER "Office.Calc/Filter/Import" + +#define SCFILTOPT_COLSCALE 0 +#define SCFILTOPT_ROWSCALE 1 +#define SCFILTOPT_WK3 2 +#define SCFILTOPT_COUNT 3 + +Sequence<OUString> ScFilterOptions::GetPropertyNames() +{ + static const char* aPropNames[] = + { + "MS_Excel/ColScale", // SCFILTOPT_COLSCALE + "MS_Excel/RowScale", // SCFILTOPT_ROWSCALE + "Lotus123/WK3" // SCFILTOPT_WK3 + }; + Sequence<OUString> aNames(SCFILTOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCFILTOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + return aNames; +} + +ScFilterOptions::ScFilterOptions() : + ConfigItem( OUString::createFromAscii( CFGPATH_FILTER ) ), + bWK3Flag( FALSE ), + fExcelColScale( 0 ), + fExcelRowScale( 0 ) +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues = GetProperties(aNames); +// EnableNotification(aNames); + const Any* pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCFILTOPT_COLSCALE: + pValues[nProp] >>= fExcelColScale; + break; + case SCFILTOPT_ROWSCALE: + pValues[nProp] >>= fExcelRowScale; + break; + case SCFILTOPT_WK3: + bWK3Flag = ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ); + break; + } + } + } + } +} + + +void ScFilterOptions::Commit() +{ + // options are never modified from office + + DBG_ERROR("trying to commit changed ScFilterOptions?"); +} + +void ScFilterOptions::Notify( const Sequence<rtl::OUString>& /* aPropertyNames */ ) +{ + DBG_ERROR("properties have been changed"); +} + + diff --git a/sc/source/core/tool/formulaparserpool.cxx b/sc/source/core/tool/formulaparserpool.cxx new file mode 100644 index 000000000000..75d1c874eba8 --- /dev/null +++ b/sc/source/core/tool/formulaparserpool.cxx @@ -0,0 +1,168 @@ +/************************************************************************* + * + * 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 "formulaparserpool.hxx" +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XContentEnumerationAccess.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sheet/XFilterFormulaParser.hpp> +#include <rtl/instance.hxx> +#include <comphelper/processfactory.hxx> +#include <sfx2/objsh.hxx> +#include "document.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringHash; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sheet; +using namespace ::com::sun::star::uno; + +// ============================================================================ + +namespace { + +class ScParserFactoryMap +{ +public: + explicit ScParserFactoryMap(); + + Reference< XFormulaParser > createFormulaParser( + const Reference< XComponent >& rxComponent, + const OUString& rNamespace ); + +private: + typedef ::std::hash_map< + OUString, + Reference< XSingleComponentFactory >, + OUStringHash, + ::std::equal_to< OUString > > FactoryMap; + + Reference< XComponentContext > mxContext; /// Default context of global process factory. + FactoryMap maFactories; /// All parser factories, mapped by formula namespace. +}; + +ScParserFactoryMap::ScParserFactoryMap() +{ + try + { + // get process factory and default component context + Reference< XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory(), UNO_SET_THROW ); + Reference< XPropertySet > xPropSet( xFactory, UNO_QUERY_THROW ); + mxContext.set( xPropSet->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ) ) ), UNO_QUERY_THROW ); + + // enumerate all implementations of the FormulaParser service + Reference< XContentEnumerationAccess > xFactoryEA( xFactory, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnum( xFactoryEA->createContentEnumeration( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.sheet.FilterFormulaParser" ) ) ), UNO_SET_THROW ); + while( xEnum->hasMoreElements() ) try // single try/catch for every element + { + // create an instance of the formula parser implementation + Reference< XSingleComponentFactory > xCompFactory( xEnum->nextElement(), UNO_QUERY_THROW ); + Reference< XFilterFormulaParser > xParser( xCompFactory->createInstanceWithContext( mxContext ), UNO_QUERY_THROW ); + + // store factory in the map + OUString aNamespace = xParser->getSupportedNamespace(); + if( aNamespace.getLength() > 0 ) + maFactories[ aNamespace ] = xCompFactory; + } + catch( Exception& ) + { + } + } + catch( Exception& ) + { + } +} + +Reference< XFormulaParser > ScParserFactoryMap::createFormulaParser( + const Reference< XComponent >& rxComponent, const OUString& rNamespace ) +{ + Reference< XFormulaParser > xParser; + FactoryMap::const_iterator aIt = maFactories.find( rNamespace ); + if( aIt != maFactories.end() ) try + { + Sequence< Any > aArgs( 1 ); + aArgs[ 0 ] <<= rxComponent; + xParser.set( aIt->second->createInstanceWithArgumentsAndContext( aArgs, mxContext ), UNO_QUERY_THROW ); + } + catch( Exception& ) + { + } + return xParser; +} + +struct ScParserFactorySingleton : public ::rtl::Static< ScParserFactoryMap, ScParserFactorySingleton > {}; + +} // namespace + +// ============================================================================ + +ScFormulaParserPool::ScFormulaParserPool( const ScDocument& rDoc ) : + mrDoc( rDoc ) +{ +} + +ScFormulaParserPool::~ScFormulaParserPool() +{ +} + +bool ScFormulaParserPool::hasFormulaParser( const OUString& rNamespace ) +{ + return getFormulaParser( rNamespace ).is(); +} + +Reference< XFormulaParser > ScFormulaParserPool::getFormulaParser( const OUString& rNamespace ) +{ + // try to find an existing parser entry + ParserMap::iterator aIt = maParsers.find( rNamespace ); + if( aIt != maParsers.end() ) + return aIt->second; + + // always create a new entry in the map (even if the following initialization fails) + Reference< XFormulaParser >& rxParser = maParsers[ rNamespace ]; + + // try to create a new parser object + if( SfxObjectShell* pDocShell = mrDoc.GetDocumentShell() ) try + { + Reference< XComponent > xComponent( pDocShell->GetModel(), UNO_QUERY_THROW ); + ScParserFactoryMap& rFactoryMap = ScParserFactorySingleton::get(); + rxParser = rFactoryMap.createFormulaParser( xComponent, rNamespace ); + } + catch( Exception& ) + { + } + return rxParser; +} + +// ============================================================================ + diff --git a/sc/source/core/tool/hints.cxx b/sc/source/core/tool/hints.cxx new file mode 100644 index 000000000000..b2a8266705c7 --- /dev/null +++ b/sc/source/core/tool/hints.cxx @@ -0,0 +1,162 @@ +/************************************************************************* + * + * 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 "hints.hxx" + +// ----------------------------------------------------------------------- + +TYPEINIT1(ScPaintHint, SfxHint); +TYPEINIT1(ScUpdateRefHint, SfxHint); +TYPEINIT1(ScPointerChangedHint, SfxHint); +TYPEINIT1(ScLinkRefreshedHint, SfxHint); +TYPEINIT1(ScAutoStyleHint, SfxHint); +TYPEINIT1(ScDBRangeRefreshedHint, SfxHint); +TYPEINIT1(ScDataPilotModifiedHint, SfxHint); + +// ----------------------------------------------------------------------- +// ScPaintHint - Angabe, was neu gezeichnet werden muss +// ----------------------------------------------------------------------- + +ScPaintHint::ScPaintHint( const ScRange& rRng, USHORT nPaint ) : + aRange( rRng ), + nParts( nPaint ), + bPrint( TRUE ) +{ +} + +ScPaintHint::~ScPaintHint() +{ +} + +// ----------------------------------------------------------------------- +// ScUpdateRefHint - Referenz-Updaterei +// ----------------------------------------------------------------------- + +ScUpdateRefHint::ScUpdateRefHint( UpdateRefMode eMode, const ScRange& rR, + SCsCOL nX, SCsROW nY, SCsTAB nZ ) : + eUpdateRefMode( eMode ), + aRange( rR ), + nDx( nX ), + nDy( nY ), + nDz( nZ ) +{ +} + +ScUpdateRefHint::~ScUpdateRefHint() +{ +} + +// ----------------------------------------------------------------------- +// ScPointerChangedHint - Pointer ist ungueltig geworden +// ----------------------------------------------------------------------- + +//UNUSED2008-05 ScPointerChangedHint::ScPointerChangedHint( USHORT nF ) : +//UNUSED2008-05 nFlags( nF ) +//UNUSED2008-05 { +//UNUSED2008-05 } + +ScPointerChangedHint::~ScPointerChangedHint() +{ +} + +// ----------------------------------------------------------------------- +// ScLinkRefreshedHint - a link has been refreshed +// ----------------------------------------------------------------------- + +ScLinkRefreshedHint::ScLinkRefreshedHint() : + nLinkType( SC_LINKREFTYPE_NONE ), + nDdeMode( 0 ) +{ +} + +ScLinkRefreshedHint::~ScLinkRefreshedHint() +{ +} + +void ScLinkRefreshedHint::SetSheetLink( const String& rSourceUrl ) +{ + nLinkType = SC_LINKREFTYPE_SHEET; + aUrl = rSourceUrl; +} + +void ScLinkRefreshedHint::SetDdeLink( + const String& rA, const String& rT, const String& rI, BYTE nM ) +{ + nLinkType = SC_LINKREFTYPE_DDE; + aDdeAppl = rA; + aDdeTopic = rT; + aDdeItem = rI; + nDdeMode = nM; +} + +void ScLinkRefreshedHint::SetAreaLink( const ScAddress& rPos ) +{ + nLinkType = SC_LINKREFTYPE_AREA; + aDestPos = rPos; +} + +// ----------------------------------------------------------------------- +// ScAutoStyleHint - STYLE() function has been called +// ----------------------------------------------------------------------- + +ScAutoStyleHint::ScAutoStyleHint( const ScRange& rR, const String& rSt1, + ULONG nT, const String& rSt2 ) : + aRange( rR ), + aStyle1( rSt1 ), + aStyle2( rSt2 ), + nTimeout( nT ) +{ +} + +ScAutoStyleHint::~ScAutoStyleHint() +{ +} + + +ScDBRangeRefreshedHint::ScDBRangeRefreshedHint( const ScImportParam& rP ) + : aParam(rP) +{ +} +ScDBRangeRefreshedHint::~ScDBRangeRefreshedHint() +{ +} + + +ScDataPilotModifiedHint::ScDataPilotModifiedHint( const String& rName ) + : maName(rName) +{ +} +ScDataPilotModifiedHint::~ScDataPilotModifiedHint() +{ +} + + diff --git a/sc/source/core/tool/inputopt.cxx b/sc/source/core/tool/inputopt.cxx new file mode 100644 index 000000000000..ed5a74582348 --- /dev/null +++ b/sc/source/core/tool/inputopt.cxx @@ -0,0 +1,274 @@ +/************************************************************************* + * + * 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 <tools/debug.hxx> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include "cfgids.hxx" +#include "inputopt.hxx" +#include "rechead.hxx" +#include "scresid.hxx" +#include "global.hxx" +#include "sc.hrc" +#include "miscuno.hxx" + +using namespace utl; +using namespace rtl; +using namespace com::sun::star::uno; + +//------------------------------------------------------------------ + +// Version, ab der das Item kompatibel ist +#define SC_VERSION ((USHORT)351) + + +//======================================================================== +// ScInputOptions - Eingabe-Optionen +//======================================================================== + +ScInputOptions::ScInputOptions() +{ + SetDefaults(); +} + +//------------------------------------------------------------------------ + +ScInputOptions::ScInputOptions( const ScInputOptions& rCpy ) +{ + *this = rCpy; +} + +//------------------------------------------------------------------------ + +ScInputOptions::~ScInputOptions() +{ +} + +//------------------------------------------------------------------------ + +void ScInputOptions::SetDefaults() +{ + nMoveDir = DIR_BOTTOM; + bMoveSelection = TRUE; + bEnterEdit = FALSE; + bExtendFormat = FALSE; + bRangeFinder = TRUE; + bExpandRefs = FALSE; + bMarkHeader = TRUE; + bUseTabCol = FALSE; + bTextWysiwyg = FALSE; + bReplCellsWarn = TRUE; +} + +//------------------------------------------------------------------------ + +const ScInputOptions& ScInputOptions::operator=( const ScInputOptions& rCpy ) +{ + nMoveDir = rCpy.nMoveDir; + bMoveSelection = rCpy.bMoveSelection; + bEnterEdit = rCpy.bEnterEdit; + bExtendFormat = rCpy.bExtendFormat; + bRangeFinder = rCpy.bRangeFinder; + bExpandRefs = rCpy.bExpandRefs; + bMarkHeader = rCpy.bMarkHeader; + bUseTabCol = rCpy.bUseTabCol; + bTextWysiwyg = rCpy.bTextWysiwyg; + bReplCellsWarn = rCpy.bReplCellsWarn; + + return *this; +} + + +//================================================================== +// Config Item containing input options +//================================================================== + +#define CFGPATH_INPUT "Office.Calc/Input" + +#define SCINPUTOPT_MOVEDIR 0 +#define SCINPUTOPT_MOVESEL 1 +#define SCINPUTOPT_EDTEREDIT 2 +#define SCINPUTOPT_EXTENDFMT 3 +#define SCINPUTOPT_RANGEFIND 4 +#define SCINPUTOPT_EXPANDREFS 5 +#define SCINPUTOPT_MARKHEADER 6 +#define SCINPUTOPT_USETABCOL 7 +#define SCINPUTOPT_TEXTWYSIWYG 8 +#define SCINPUTOPT_REPLCELLSWARN 9 +#define SCINPUTOPT_COUNT 10 + +Sequence<OUString> ScInputCfg::GetPropertyNames() +{ + static const char* aPropNames[] = + { + "MoveSelectionDirection", // SCINPUTOPT_MOVEDIR + "MoveSelection", // SCINPUTOPT_MOVESEL + "SwitchToEditMode", // SCINPUTOPT_EDTEREDIT + "ExpandFormatting", // SCINPUTOPT_EXTENDFMT + "ShowReference", // SCINPUTOPT_RANGEFIND + "ExpandReference", // SCINPUTOPT_EXPANDREFS + "HighlightSelection", // SCINPUTOPT_MARKHEADER + "UseTabCol", // SCINPUTOPT_USETABCOL + "UsePrinterMetrics", // SCINPUTOPT_TEXTWYSIWYG + "ReplaceCellsWarning" // SCINPUTOPT_REPLCELLSWARN + }; + Sequence<OUString> aNames(SCINPUTOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCINPUTOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + return aNames; +} + +ScInputCfg::ScInputCfg() : + ConfigItem( OUString::createFromAscii( CFGPATH_INPUT ) ) +{ + sal_Int32 nIntVal = 0; + + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues = GetProperties(aNames); + EnableNotification(aNames); + const Any* pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCINPUTOPT_MOVEDIR: + if ( pValues[nProp] >>= nIntVal ) + SetMoveDir( (USHORT)nIntVal ); + break; + case SCINPUTOPT_MOVESEL: + SetMoveSelection( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCINPUTOPT_EDTEREDIT: + SetEnterEdit( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCINPUTOPT_EXTENDFMT: + SetExtendFormat( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCINPUTOPT_RANGEFIND: + SetRangeFinder( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCINPUTOPT_EXPANDREFS: + SetExpandRefs( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCINPUTOPT_MARKHEADER: + SetMarkHeader( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCINPUTOPT_USETABCOL: + SetUseTabCol( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCINPUTOPT_TEXTWYSIWYG: + SetTextWysiwyg( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCINPUTOPT_REPLCELLSWARN: + SetReplaceCellsWarn( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + } + } + } + } +} + + +void ScInputCfg::Commit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCINPUTOPT_MOVEDIR: + pValues[nProp] <<= (sal_Int32) GetMoveDir(); + break; + case SCINPUTOPT_MOVESEL: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetMoveSelection() ); + break; + case SCINPUTOPT_EDTEREDIT: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetEnterEdit() ); + break; + case SCINPUTOPT_EXTENDFMT: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetExtendFormat() ); + break; + case SCINPUTOPT_RANGEFIND: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetRangeFinder() ); + break; + case SCINPUTOPT_EXPANDREFS: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetExpandRefs() ); + break; + case SCINPUTOPT_MARKHEADER: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetMarkHeader() ); + break; + case SCINPUTOPT_USETABCOL: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetUseTabCol() ); + break; + case SCINPUTOPT_TEXTWYSIWYG: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetTextWysiwyg() ); + break; + case SCINPUTOPT_REPLCELLSWARN: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetReplaceCellsWarn() ); + break; + } + } + PutProperties(aNames, aValues); +} + +void ScInputCfg::Notify( const Sequence<rtl::OUString>& /* aPropertyNames */ ) +{ + DBG_ERROR("properties have been changed"); +} + +void ScInputCfg::SetOptions( const ScInputOptions& rNew ) +{ + *(ScInputOptions*)this = rNew; + SetModified(); +} + +void ScInputCfg::OptionsChanged() +{ + SetModified(); +} + + diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx new file mode 100644 index 000000000000..fcc7a24e7a42 --- /dev/null +++ b/sc/source/core/tool/interpr1.cxx @@ -0,0 +1,7444 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include "scitems.hxx" +#include <editeng/langitem.hxx> +#include <svx/algitem.hxx> +#include <unotools/textsearch.hxx> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <tools/urlobj.hxx> +#include <unotools/charclass.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/printer.hxx> +#include <unotools/collatorwrapper.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <rtl/ustring.hxx> +#include <rtl/logfile.hxx> + +#include "interpre.hxx" +#include "patattr.hxx" +#include "global.hxx" +#include "document.hxx" +#include "dociter.hxx" +#include "cell.hxx" +#include "scmatrix.hxx" +#include "docoptio.hxx" +#include "globstr.hrc" +#include "attrib.hxx" +#include "jumpmatrix.hxx" + +#ifndef _COMPHELPER_PROCESSFACTORY_HXX_ +#include <comphelper/processfactory.hxx> +#endif + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <vector> +#include <memory> +#include "cellkeytranslator.hxx" +#include "lookupcache.hxx" +#include "rangenam.hxx" +#include "compiler.hxx" +#include "externalrefmgr.hxx" +#include "doubleref.hxx" +#include "queryparam.hxx" + +#define SC_DOUBLE_MAXVALUE 1.7e307 + +IMPL_FIXEDMEMPOOL_NEWDEL( ScTokenStack, 8, 4 ) +IMPL_FIXEDMEMPOOL_NEWDEL( ScInterpreter, 32, 16 ) + +ScTokenStack* ScInterpreter::pGlobalStack = NULL; +BOOL ScInterpreter::bGlobalStackInUse = FALSE; + +using namespace formula; +using ::std::auto_ptr; + +//----------------------------------------------------------------------------- +// Funktionen +//----------------------------------------------------------------------------- + + +void ScInterpreter::ScIfJump() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIfJump" ); + const short* pJump = pCur->GetJump(); + short nJumpCount = pJump[ 0 ]; + MatrixDoubleRefToMatrix(); + switch ( GetStackType() ) + { + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + PushIllegalParameter(); + else + { + FormulaTokenRef xNew; + ScTokenMatrixMap::const_iterator aMapIter; + // DoubleError handled by JumpMatrix + pMat->SetErrorInterpreter( NULL); + SCSIZE nCols, nRows; + pMat->GetDimensions( nCols, nRows ); + if ( nCols == 0 || nRows == 0 ) + PushIllegalArgument(); + else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( + pCur)) != pTokenMatrixMap->end())) + xNew = (*aMapIter).second; + else + { + ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows ); + for ( SCSIZE nC=0; nC < nCols; ++nC ) + { + for ( SCSIZE nR=0; nR < nRows; ++nR ) + { + double fVal; + bool bTrue; + ScMatValType nType = 0; + const ScMatrixValue* pMatVal = pMat->Get( nC, nR, + nType); + bool bIsValue = ScMatrix::IsValueType( nType); + if ( bIsValue ) + { + fVal = pMatVal->fVal; + bIsValue = ::rtl::math::isFinite( fVal ); + bTrue = bIsValue && (fVal != 0.0); + if ( bTrue ) + fVal = 1.0; + } + else + { + // Treat empty and empty path as 0, but string + // as error. + bIsValue = !ScMatrix::IsRealStringType( nType); + bTrue = false; + fVal = (bIsValue ? 0.0 : CreateDoubleError( errNoValue)); + } + if ( bTrue ) + { // TRUE + if( nJumpCount >= 2 ) + { // THEN path + pJumpMat->SetJump( nC, nR, fVal, + pJump[ 1 ], + pJump[ nJumpCount ]); + } + else + { // no parameter given for THEN + pJumpMat->SetJump( nC, nR, fVal, + pJump[ nJumpCount ], + pJump[ nJumpCount ]); + } + } + else + { // FALSE + if( nJumpCount == 3 && bIsValue ) + { // ELSE path + pJumpMat->SetJump( nC, nR, fVal, + pJump[ 2 ], + pJump[ nJumpCount ]); + } + else + { // no parameter given for ELSE, + // or DoubleError + pJumpMat->SetJump( nC, nR, fVal, + pJump[ nJumpCount ], + pJump[ nJumpCount ]); + } + } + } + } + xNew = new ScJumpMatrixToken( pJumpMat ); + GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type(pCur, xNew)); + } + PushTempToken( xNew); + // set endpoint of path for main code line + aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); + } + } + break; + default: + { + if ( GetBool() ) + { // TRUE + if( nJumpCount >= 2 ) + { // THEN path + aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] ); + } + else + { // no parameter given for THEN + nFuncFmtType = NUMBERFORMAT_LOGICAL; + PushInt(1); + aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); + } + } + else + { // FALSE + if( nJumpCount == 3 ) + { // ELSE path + aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] ); + } + else + { // no parameter given for ELSE + nFuncFmtType = NUMBERFORMAT_LOGICAL; + PushInt(0); + aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); + } + } + } + } +} + + +void ScInterpreter::ScChoseJump() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScChoseJump" ); + // We have to set a jump, if there was none chosen because of an error set + // it to endpoint. + bool bHaveJump = false; + const short* pJump = pCur->GetJump(); + short nJumpCount = pJump[ 0 ]; + MatrixDoubleRefToMatrix(); + switch ( GetStackType() ) + { + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + PushIllegalParameter(); + else + { + FormulaTokenRef xNew; + ScTokenMatrixMap::const_iterator aMapIter; + // DoubleError handled by JumpMatrix + pMat->SetErrorInterpreter( NULL); + SCSIZE nCols, nRows; + pMat->GetDimensions( nCols, nRows ); + if ( nCols == 0 || nRows == 0 ) + PushIllegalParameter(); + else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( + pCur)) != pTokenMatrixMap->end())) + xNew = (*aMapIter).second; + else + { + ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows ); + for ( SCSIZE nC=0; nC < nCols; ++nC ) + { + for ( SCSIZE nR=0; nR < nRows; ++nR ) + { + double fVal; + ScMatValType nType; + const ScMatrixValue* pMatVal = pMat->Get( nC, nR, + nType); + bool bIsValue = ScMatrix::IsValueType( nType); + if ( bIsValue ) + { + fVal = pMatVal->fVal; + bIsValue = ::rtl::math::isFinite( fVal ); + if ( bIsValue ) + { + fVal = ::rtl::math::approxFloor( fVal); + if ( (fVal < 1) || (fVal >= nJumpCount)) + { + bIsValue = FALSE; + fVal = CreateDoubleError( + errIllegalArgument); + } + } + } + else + { + fVal = CreateDoubleError( errNoValue); + } + if ( bIsValue ) + { + pJumpMat->SetJump( nC, nR, fVal, + pJump[ (short)fVal ], + pJump[ nJumpCount ]); + } + else + { + pJumpMat->SetJump( nC, nR, fVal, + pJump[ nJumpCount ], + pJump[ nJumpCount ]); + } + } + } + xNew = new ScJumpMatrixToken( pJumpMat ); + GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type( + pCur, xNew)); + } + PushTempToken( xNew); + // set endpoint of path for main code line + aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); + bHaveJump = true; + } + } + break; + default: + { + double nJumpIndex = ::rtl::math::approxFloor( GetDouble() ); + if (!nGlobalError && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount)) + { + aCode.Jump( pJump[ (short) nJumpIndex ], pJump[ nJumpCount ] ); + bHaveJump = true; + } + else + PushIllegalArgument(); + } + } + if (!bHaveJump) + aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); +} + +void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, ScMatrixRef& pResMat, SCSIZE nParmCols, SCSIZE nParmRows ) +{ + SCSIZE nJumpCols, nJumpRows; + SCSIZE nResCols, nResRows; + SCSIZE nAdjustCols, nAdjustRows; + pJumpM->GetDimensions( nJumpCols, nJumpRows ); + pJumpM->GetResMatDimensions( nResCols, nResRows ); + if (( nJumpCols == 1 && nParmCols > nResCols ) || + ( nJumpRows == 1 && nParmRows > nResRows )) + { + if ( nJumpCols == 1 && nJumpRows == 1 ) + { + nAdjustCols = nParmCols > nResCols ? nParmCols : nResCols; + nAdjustRows = nParmRows > nResRows ? nParmRows : nResRows; + } + else if ( nJumpCols == 1 ) + { + nAdjustCols = nParmCols; + nAdjustRows = nResRows; + } + else + { + nAdjustCols = nResCols; + nAdjustRows = nParmRows; + } + pJumpM->SetNewResMat( nAdjustCols, nAdjustRows ); + pResMat = pJumpM->GetResultMatrix(); + } +} + +bool ScInterpreter::JumpMatrix( short nStackLevel ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::JumpMatrix" ); + pJumpMatrix = static_cast<ScToken*>(pStack[sp-nStackLevel])->GetJumpMatrix(); + ScMatrixRef pResMat = pJumpMatrix->GetResultMatrix(); + SCSIZE nC, nR; + if ( nStackLevel == 2 ) + { + if ( aCode.HasStacked() ) + aCode.Pop(); // pop what Jump() pushed + else + { + DBG_ERRORFILE( "ScInterpreter::JumpMatrix: pop goes the weasel" ); + } + + if ( !pResMat ) + { + Pop(); + SetError( errUnknownStackVariable ); + } + else + { + pJumpMatrix->GetPos( nC, nR ); + switch ( GetStackType() ) + { + case svDouble: + { + double fVal = GetDouble(); + if ( nGlobalError ) + { + fVal = CreateDoubleError( nGlobalError ); + nGlobalError = 0; + } + pResMat->PutDouble( fVal, nC, nR ); + } + break; + case svString: + { + const String& rStr = GetString(); + if ( nGlobalError ) + { + pResMat->PutDouble( CreateDoubleError( nGlobalError), + nC, nR); + nGlobalError = 0; + } + else + pResMat->PutString( rStr, nC, nR ); + } + break; + case svSingleRef: + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( nGlobalError ) + { + pResMat->PutDouble( CreateDoubleError( nGlobalError), + nC, nR); + nGlobalError = 0; + } + else + { + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellEmptyData( pCell)) + pResMat->PutEmpty( nC, nR ); + else if (HasCellValueData( pCell)) + { + double fVal = GetCellValue( aAdr, pCell); + if ( nGlobalError ) + { + fVal = CreateDoubleError( + nGlobalError); + nGlobalError = 0; + } + pResMat->PutDouble( fVal, nC, nR ); + } + else + { + String aStr; + GetCellString( aStr, pCell ); + if ( nGlobalError ) + { + pResMat->PutDouble( CreateDoubleError( + nGlobalError), nC, nR); + nGlobalError = 0; + } + else + pResMat->PutString( aStr, nC, nR); + } + } + } + break; + case svDoubleRef: + { // upper left plus offset within matrix + double fVal; + ScRange aRange; + PopDoubleRef( aRange ); + if ( nGlobalError ) + { + fVal = CreateDoubleError( nGlobalError ); + nGlobalError = 0; + pResMat->PutDouble( fVal, nC, nR ); + } + else + { + // Do not modify the original range because we use it + // to adjust the size of the result matrix if necessary. + ScAddress aAdr( aRange.aStart); + ULONG nCol = (ULONG)aAdr.Col() + nC; + ULONG nRow = (ULONG)aAdr.Row() + nR; + if ((nCol > static_cast<ULONG>(aRange.aEnd.Col()) && + aRange.aEnd.Col() != aRange.aStart.Col()) + || (nRow > static_cast<ULONG>(aRange.aEnd.Row()) && + aRange.aEnd.Row() != aRange.aStart.Row())) + { + fVal = CreateDoubleError( NOTAVAILABLE ); + pResMat->PutDouble( fVal, nC, nR ); + } + else + { + // Replicate column and/or row of a vector if it is + // one. Note that this could be a range reference + // that in fact consists of only one cell, e.g. A1:A1 + if (aRange.aEnd.Col() == aRange.aStart.Col()) + nCol = aRange.aStart.Col(); + if (aRange.aEnd.Row() == aRange.aStart.Row()) + nRow = aRange.aStart.Row(); + aAdr.SetCol( static_cast<SCCOL>(nCol) ); + aAdr.SetRow( static_cast<SCROW>(nRow) ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellEmptyData( pCell)) + pResMat->PutEmpty( nC, nR ); + else if (HasCellValueData( pCell)) + { + double fCellVal = GetCellValue( aAdr, pCell); + if ( nGlobalError ) + { + fCellVal = CreateDoubleError( + nGlobalError); + nGlobalError = 0; + } + pResMat->PutDouble( fCellVal, nC, nR ); + } + else + { + String aStr; + GetCellString( aStr, pCell ); + if ( nGlobalError ) + { + pResMat->PutDouble( CreateDoubleError( + nGlobalError), nC, nR); + nGlobalError = 0; + } + else + pResMat->PutString( aStr, nC, nR ); + } + } + SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1; + SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1; + lcl_AdjustJumpMatrix( pJumpMatrix, pResMat, nParmCols, nParmRows ); + } + } + break; + case svMatrix: + { // match matrix offsets + double fVal; + ScMatrixRef pMat = PopMatrix(); + if ( nGlobalError ) + { + fVal = CreateDoubleError( nGlobalError ); + nGlobalError = 0; + pResMat->PutDouble( fVal, nC, nR ); + } + else if ( !pMat ) + { + fVal = CreateDoubleError( errUnknownVariable ); + pResMat->PutDouble( fVal, nC, nR ); + } + else + { + SCSIZE nCols, nRows; + pMat->GetDimensions( nCols, nRows ); + if ((nCols <= nC && nCols != 1) || + (nRows <= nR && nRows != 1)) + { + fVal = CreateDoubleError( NOTAVAILABLE ); + pResMat->PutDouble( fVal, nC, nR ); + } + else + { + if ( pMat->IsValue( nC, nR ) ) + { + fVal = pMat->GetDouble( nC, nR ); + pResMat->PutDouble( fVal, nC, nR ); + } + else if ( pMat->IsEmpty( nC, nR ) ) + pResMat->PutEmpty( nC, nR ); + else + { + const String& rStr = pMat->GetString( nC, nR ); + pResMat->PutString( rStr, nC, nR ); + } + } + lcl_AdjustJumpMatrix( pJumpMatrix, pResMat, nCols, nRows ); + } + } + break; + case svError: + { + PopError(); + double fVal = CreateDoubleError( nGlobalError); + nGlobalError = 0; + pResMat->PutDouble( fVal, nC, nR ); + } + break; + default: + { + Pop(); + double fVal = CreateDoubleError( errIllegalArgument); + pResMat->PutDouble( fVal, nC, nR ); + } + } + } + } + bool bCont = pJumpMatrix->Next( nC, nR ); + if ( bCont ) + { + double fBool; + short nStart, nNext, nStop; + pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop ); + while ( bCont && nStart == nNext ) + { // push all results that have no jump path + if ( pResMat ) + { + // a FALSE without path results in an empty path value + if ( fBool == 0.0 ) + pResMat->PutEmptyPath( nC, nR ); + else + pResMat->PutDouble( fBool, nC, nR ); + } + bCont = pJumpMatrix->Next( nC, nR ); + if ( bCont ) + pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop ); + } + if ( bCont && nStart != nNext ) + { + const ScTokenVec* pParams = pJumpMatrix->GetJumpParameters(); + if ( pParams ) + { + for ( ScTokenVec::const_iterator i = pParams->begin(); + i != pParams->end(); ++i ) + { + // This is not the current state of the interpreter, so + // push without error, and elements' errors are coded into + // double. + PushWithoutError( *(*i)); + } + } + aCode.Jump( nStart, nNext, nStop ); + } + } + if ( !bCont ) + { // we're done with it, throw away jump matrix, keep result + pJumpMatrix = NULL; + Pop(); + PushMatrix( pResMat ); + // Remove jump matrix from map and remember result matrix in case it + // could be reused in another path of the same condition. + if (pTokenMatrixMap) + { + pTokenMatrixMap->erase( pCur); + pTokenMatrixMap->insert( ScTokenMatrixMap::value_type( pCur, + pStack[sp-1])); + } + return true; + } + return false; +} + + +ScCompareOptions::ScCompareOptions( ScDocument* pDoc, const ScQueryEntry& rEntry, bool bReg ) : + aQueryEntry(rEntry), + bRegEx(bReg), + bMatchWholeCell(pDoc->GetDocOptions().IsMatchWholeCell()), + bIgnoreCase(true) +{ + bRegEx = (bRegEx && (aQueryEntry.eOp == SC_EQUAL || aQueryEntry.eOp == SC_NOT_EQUAL)); + // Interpreter functions usually are case insensitive, except the simple + // comparison operators, for which these options aren't used. Override in + // struct if needed. +} + + +double ScInterpreter::CompareFunc( const ScCompare& rComp, ScCompareOptions* pOptions ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CompareFunc" ); + // Keep DoubleError if encountered + // #i40539# if bEmpty is set, bVal/nVal are uninitialized + if ( !rComp.bEmpty[0] && rComp.bVal[0] && !::rtl::math::isFinite( rComp.nVal[0])) + return rComp.nVal[0]; + if ( !rComp.bEmpty[1] && rComp.bVal[1] && !::rtl::math::isFinite( rComp.nVal[1])) + return rComp.nVal[1]; + + double fRes = 0; + if ( rComp.bEmpty[ 0 ] ) + { + if ( rComp.bEmpty[ 1 ] ) + ; // empty cell == empty cell, fRes 0 + else if( rComp.bVal[ 1 ] ) + { + if ( !::rtl::math::approxEqual( rComp.nVal[ 1 ], 0.0 ) ) + { + if ( rComp.nVal[ 1 ] < 0.0 ) + fRes = 1; // empty cell > -x + else + fRes = -1; // empty cell < x + } + // else: empty cell == 0.0 + } + else + { + if ( rComp.pVal[ 1 ]->Len() ) + fRes = -1; // empty cell < "..." + // else: empty cell == "" + } + } + else if ( rComp.bEmpty[ 1 ] ) + { + if( rComp.bVal[ 0 ] ) + { + if ( !::rtl::math::approxEqual( rComp.nVal[ 0 ], 0.0 ) ) + { + if ( rComp.nVal[ 0 ] < 0.0 ) + fRes = -1; // -x < empty cell + else + fRes = 1; // x > empty cell + } + // else: empty cell == 0.0 + } + else + { + if ( rComp.pVal[ 0 ]->Len() ) + fRes = 1; // "..." > empty cell + // else: "" == empty cell + } + } + else if( rComp.bVal[ 0 ] ) + { + if( rComp.bVal[ 1 ] ) + { + if ( !::rtl::math::approxEqual( rComp.nVal[ 0 ], rComp.nVal[ 1 ] ) ) + { + if( rComp.nVal[ 0 ] - rComp.nVal[ 1 ] < 0 ) + fRes = -1; + else + fRes = 1; + } + } + else + fRes = -1; // number is less than string + } + else if( rComp.bVal[ 1 ] ) + fRes = 1; // number is less than string + else + { + // Both strings. + if (pOptions) + { + // All similar to Sctable::ValidQuery(), *rComp.pVal[1] actually + // is/must be identical to *rEntry.pStr, which is essential for + // regex to work through GetSearchTextPtr(). + ScQueryEntry& rEntry = pOptions->aQueryEntry; + DBG_ASSERT( *rComp.pVal[1] == *rEntry.pStr, "ScInterpreter::CompareFunc: broken options"); + if (pOptions->bRegEx) + { + xub_StrLen nStart = 0; + xub_StrLen nStop = rComp.pVal[0]->Len(); + bool bMatch = rEntry.GetSearchTextPtr( + !pOptions->bIgnoreCase)->SearchFrwrd( *rComp.pVal[0], + &nStart, &nStop); + if (bMatch && pOptions->bMatchWholeCell && (nStart != 0 || nStop != rComp.pVal[0]->Len())) + bMatch = false; // RegEx must match entire string. + fRes = (bMatch ? 0 : 1); + } + else if (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL) + { + ::utl::TransliterationWrapper* pTransliteration = + (pOptions->bIgnoreCase ? ScGlobal::GetpTransliteration() : + ScGlobal::GetCaseTransliteration()); + bool bMatch; + if (pOptions->bMatchWholeCell) + bMatch = pTransliteration->isEqual( *rComp.pVal[0], *rComp.pVal[1]); + else + { + String aCell( pTransliteration->transliterate( + *rComp.pVal[0], ScGlobal::eLnge, 0, + rComp.pVal[0]->Len(), NULL)); + String aQuer( pTransliteration->transliterate( + *rComp.pVal[1], ScGlobal::eLnge, 0, + rComp.pVal[1]->Len(), NULL)); + bMatch = (aCell.Search( aQuer ) != STRING_NOTFOUND); + } + fRes = (bMatch ? 0 : 1); + } + else if (pOptions->bIgnoreCase) + fRes = (double) ScGlobal::GetCollator()->compareString( + *rComp.pVal[ 0 ], *rComp.pVal[ 1 ] ); + else + fRes = (double) ScGlobal::GetCaseCollator()->compareString( + *rComp.pVal[ 0 ], *rComp.pVal[ 1 ] ); + } + else if (pDok->GetDocOptions().IsIgnoreCase()) + fRes = (double) ScGlobal::GetCollator()->compareString( + *rComp.pVal[ 0 ], *rComp.pVal[ 1 ] ); + else + fRes = (double) ScGlobal::GetCaseCollator()->compareString( + *rComp.pVal[ 0 ], *rComp.pVal[ 1 ] ); + } + return fRes; +} + + +double ScInterpreter::Compare() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::Compare" ); + String aVal1, aVal2; + ScCompare aComp( &aVal1, &aVal2 ); + for( short i = 1; i >= 0; i-- ) + { + switch ( GetRawStackType() ) + { + case svEmptyCell: + Pop(); + aComp.bEmpty[ i ] = TRUE; + break; + case svMissing: + case svDouble: + aComp.nVal[ i ] = GetDouble(); + aComp.bVal[ i ] = TRUE; + break; + case svString: + *aComp.pVal[ i ] = GetString(); + aComp.bVal[ i ] = FALSE; + break; + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellEmptyData( pCell)) + aComp.bEmpty[ i ] = TRUE; + else if (HasCellStringData( pCell)) + { + GetCellString( *aComp.pVal[ i ], pCell); + aComp.bVal[ i ] = FALSE; + } + else + { + aComp.nVal[ i ] = GetCellValue( aAdr, pCell ); + aComp.bVal[ i ] = TRUE; + } + } + break; + default: + SetError( errIllegalParameter); + break; + } + } + if( nGlobalError ) + return 0; + nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL; + return CompareFunc( aComp ); +} + + +ScMatrixRef ScInterpreter::CompareMat( ScCompareOptions* pOptions ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CompareMat" ); + String aVal1, aVal2; + ScCompare aComp( &aVal1, &aVal2 ); + ScMatrixRef pMat[2]; + ScAddress aAdr; + for( short i = 1; i >= 0; i-- ) + { + switch (GetRawStackType()) + { + case svEmptyCell: + Pop(); + aComp.bEmpty[ i ] = TRUE; + break; + case svMissing: + case svDouble: + aComp.nVal[ i ] = GetDouble(); + aComp.bVal[ i ] = TRUE; + break; + case svString: + *aComp.pVal[ i ] = GetString(); + aComp.bVal[ i ] = FALSE; + break; + case svSingleRef: + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellEmptyData( pCell)) + aComp.bEmpty[ i ] = TRUE; + else if (HasCellStringData( pCell)) + { + GetCellString( *aComp.pVal[ i ], pCell); + aComp.bVal[ i ] = FALSE; + } + else + { + aComp.nVal[ i ] = GetCellValue( aAdr, pCell ); + aComp.bVal[ i ] = TRUE; + } + } + break; + case svDoubleRef: + case svMatrix: + pMat[ i ] = GetMatrix(); + if ( !pMat[ i ] ) + SetError( errIllegalParameter); + else + pMat[i]->SetErrorInterpreter( NULL); + // errors are transported as DoubleError inside matrix + break; + default: + SetError( errIllegalParameter); + break; + } + } + ScMatrixRef pResMat = NULL; + if( !nGlobalError ) + { + if ( pMat[0] && pMat[1] ) + { + SCSIZE nC0, nC1; + SCSIZE nR0, nR1; + pMat[0]->GetDimensions( nC0, nR0 ); + pMat[1]->GetDimensions( nC1, nR1 ); + SCSIZE nC = Max( nC0, nC1 ); + SCSIZE nR = Max( nR0, nR1 ); + pResMat = GetNewMat( nC, nR); + if ( !pResMat ) + return NULL; + for ( SCSIZE j=0; j<nC; j++ ) + { + for ( SCSIZE k=0; k<nR; k++ ) + { + SCSIZE nCol = j, nRow = k; + if ( pMat[0]->ValidColRowOrReplicated( nCol, nRow ) && + pMat[1]->ValidColRowOrReplicated( nCol, nRow )) + { + for ( short i=1; i>=0; i-- ) + { + if ( pMat[i]->IsString(j,k) ) + { + aComp.bVal[i] = FALSE; + *aComp.pVal[i] = pMat[i]->GetString(j,k); + aComp.bEmpty[i] = pMat[i]->IsEmpty(j,k); + } + else + { + aComp.bVal[i] = TRUE; + aComp.nVal[i] = pMat[i]->GetDouble(j,k); + aComp.bEmpty[i] = FALSE; + } + } + pResMat->PutDouble( CompareFunc( aComp, pOptions ), j,k ); + } + else + pResMat->PutString( ScGlobal::GetRscString(STR_NO_VALUE), j,k ); + } + } + } + else if ( pMat[0] || pMat[1] ) + { + short i = ( pMat[0] ? 0 : 1); + SCSIZE nC, nR; + pMat[i]->GetDimensions( nC, nR ); + pResMat = GetNewMat( nC, nR); + if ( !pResMat ) + return NULL; + SCSIZE n = nC * nR; + for ( SCSIZE j=0; j<n; j++ ) + { + if ( pMat[i]->IsValue(j) ) + { + aComp.bVal[i] = TRUE; + aComp.nVal[i] = pMat[i]->GetDouble(j); + aComp.bEmpty[i] = FALSE; + } + else + { + aComp.bVal[i] = FALSE; + *aComp.pVal[i] = pMat[i]->GetString(j); + aComp.bEmpty[i] = pMat[i]->IsEmpty(j); + } + pResMat->PutDouble( CompareFunc( aComp, pOptions ), j ); + } + } + } + nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL; + return pResMat; +} + + +ScMatrixRef ScInterpreter::QueryMat( ScMatrix* pMat, ScCompareOptions& rOptions ) +{ + short nSaveCurFmtType = nCurFmtType; + short nSaveFuncFmtType = nFuncFmtType; + PushMatrix( pMat); + if (rOptions.aQueryEntry.bQueryByString) + PushString( *rOptions.aQueryEntry.pStr); + else + PushDouble( rOptions.aQueryEntry.nVal); + ScMatrixRef pResultMatrix = CompareMat( &rOptions); + nCurFmtType = nSaveCurFmtType; + nFuncFmtType = nSaveFuncFmtType; + if (nGlobalError || !pResultMatrix) + { + SetError( errIllegalParameter); + return pResultMatrix; + } + + switch (rOptions.aQueryEntry.eOp) + { + case SC_EQUAL: + pResultMatrix->CompareEqual(); + break; + case SC_LESS: + pResultMatrix->CompareLess(); + break; + case SC_GREATER: + pResultMatrix->CompareGreater(); + break; + case SC_LESS_EQUAL: + pResultMatrix->CompareLessEqual(); + break; + case SC_GREATER_EQUAL: + pResultMatrix->CompareGreaterEqual(); + break; + case SC_NOT_EQUAL: + pResultMatrix->CompareNotEqual(); + break; + default: + SetError( errIllegalArgument); + DBG_ERROR1( "ScInterpreter::QueryMat: unhandled comparison operator: %d", (int)rOptions.aQueryEntry.eOp); + } + return pResultMatrix; +} + + +void ScInterpreter::ScEqual() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScEqual" ); + if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) + { + ScMatrixRef pMat = CompareMat(); + if ( !pMat ) + PushIllegalParameter(); + else + { + pMat->CompareEqual(); + PushMatrix( pMat ); + } + } + else + PushInt( Compare() == 0 ); +} + + +void ScInterpreter::ScNotEqual() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNotEqual" ); + if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) + { + ScMatrixRef pMat = CompareMat(); + if ( !pMat ) + PushIllegalParameter(); + else + { + pMat->CompareNotEqual(); + PushMatrix( pMat ); + } + } + else + PushInt( Compare() != 0 ); +} + + +void ScInterpreter::ScLess() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLess" ); + if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) + { + ScMatrixRef pMat = CompareMat(); + if ( !pMat ) + PushIllegalParameter(); + else + { + pMat->CompareLess(); + PushMatrix( pMat ); + } + } + else + PushInt( Compare() < 0 ); +} + + +void ScInterpreter::ScGreater() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGreater" ); + if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) + { + ScMatrixRef pMat = CompareMat(); + if ( !pMat ) + PushIllegalParameter(); + else + { + pMat->CompareGreater(); + PushMatrix( pMat ); + } + } + else + PushInt( Compare() > 0 ); +} + + +void ScInterpreter::ScLessEqual() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLessEqual" ); + if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) + { + ScMatrixRef pMat = CompareMat(); + if ( !pMat ) + PushIllegalParameter(); + else + { + pMat->CompareLessEqual(); + PushMatrix( pMat ); + } + } + else + PushInt( Compare() <= 0 ); +} + + +void ScInterpreter::ScGreaterEqual() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGreaterEqual" ); + if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) + { + ScMatrixRef pMat = CompareMat(); + if ( !pMat ) + PushIllegalParameter(); + else + { + pMat->CompareGreaterEqual(); + PushMatrix( pMat ); + } + } + else + PushInt( Compare() >= 0 ); +} + + +void ScInterpreter::ScAnd() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAnd" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nParamCount = GetByte(); + if ( MustHaveParamCountMin( nParamCount, 1 ) ) + { + BOOL bHaveValue = FALSE; + short nRes = TRUE; + size_t nRefInList = 0; + while( nParamCount-- > 0) + { + if ( !nGlobalError ) + { + switch ( GetStackType() ) + { + case svDouble : + bHaveValue = TRUE; + nRes &= ( PopDouble() != 0.0 ); + break; + case svString : + Pop(); + SetError( errNoValue ); + break; + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( !nGlobalError ) + { + ScBaseCell* pCell = GetCell( aAdr ); + if ( HasCellValueData( pCell ) ) + { + bHaveValue = TRUE; + nRes &= ( GetCellValue( aAdr, pCell ) != 0.0 ); + } + // else: Xcl setzt hier keinen Fehler + } + } + break; + case svDoubleRef: + case svRefList: + { + ScRange aRange; + PopDoubleRef( aRange, nParamCount, nRefInList); + if ( !nGlobalError ) + { + double fVal; + USHORT nErr = 0; + ScValueIterator aValIter( pDok, aRange ); + if ( aValIter.GetFirst( fVal, nErr ) ) + { + bHaveValue = TRUE; + do + { + nRes &= ( fVal != 0.0 ); + } while ( (nErr == 0) && + aValIter.GetNext( fVal, nErr ) ); + } + SetError( nErr ); + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = GetMatrix(); + if ( pMat ) + { + bHaveValue = TRUE; + double fVal = pMat->And(); + USHORT nErr = GetDoubleErrorValue( fVal ); + if ( nErr ) + { + SetError( nErr ); + nRes = FALSE; + } + else + nRes &= (fVal != 0.0); + } + // else: GetMatrix did set errIllegalParameter + } + break; + default: + Pop(); + SetError( errIllegalParameter); + } + } + else + Pop(); + } + if ( bHaveValue ) + PushInt( nRes ); + else + PushNoValue(); + } +} + + +void ScInterpreter::ScOr() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScOr" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nParamCount = GetByte(); + if ( MustHaveParamCountMin( nParamCount, 1 ) ) + { + BOOL bHaveValue = FALSE; + short nRes = FALSE; + size_t nRefInList = 0; + while( nParamCount-- > 0) + { + if ( !nGlobalError ) + { + switch ( GetStackType() ) + { + case svDouble : + bHaveValue = TRUE; + nRes |= ( PopDouble() != 0.0 ); + break; + case svString : + Pop(); + SetError( errNoValue ); + break; + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( !nGlobalError ) + { + ScBaseCell* pCell = GetCell( aAdr ); + if ( HasCellValueData( pCell ) ) + { + bHaveValue = TRUE; + nRes |= ( GetCellValue( aAdr, pCell ) != 0.0 ); + } + // else: Xcl setzt hier keinen Fehler + } + } + break; + case svDoubleRef: + case svRefList: + { + ScRange aRange; + PopDoubleRef( aRange, nParamCount, nRefInList); + if ( !nGlobalError ) + { + double fVal; + USHORT nErr = 0; + ScValueIterator aValIter( pDok, aRange ); + if ( aValIter.GetFirst( fVal, nErr ) ) + { + bHaveValue = TRUE; + do + { + nRes |= ( fVal != 0.0 ); + } while ( (nErr == 0) && + aValIter.GetNext( fVal, nErr ) ); + } + SetError( nErr ); + } + } + break; + case svMatrix: + { + bHaveValue = TRUE; + ScMatrixRef pMat = GetMatrix(); + if ( pMat ) + { + bHaveValue = TRUE; + double fVal = pMat->Or(); + USHORT nErr = GetDoubleErrorValue( fVal ); + if ( nErr ) + { + SetError( nErr ); + nRes = FALSE; + } + else + nRes |= (fVal != 0.0); + } + // else: GetMatrix did set errIllegalParameter + } + break; + default: + Pop(); + SetError( errIllegalParameter); + } + } + else + Pop(); + } + if ( bHaveValue ) + PushInt( nRes ); + else + PushNoValue(); + } +} + + +void ScInterpreter::ScNeg() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNeg" ); + // Simple negation doesn't change current format type to number, keep + // current type. + nFuncFmtType = nCurFmtType; + switch ( GetStackType() ) + { + case svMatrix : + { + ScMatrixRef pMat = GetMatrix(); + if ( !pMat ) + PushIllegalParameter(); + else + { + SCSIZE nC, nR; + pMat->GetDimensions( nC, nR ); + ScMatrixRef pResMat = GetNewMat( nC, nR); + if ( !pResMat ) + PushIllegalArgument(); + else + { + SCSIZE nCount = nC * nR; + for ( SCSIZE j=0; j<nCount; ++j ) + { + if ( pMat->IsValueOrEmpty(j) ) + pResMat->PutDouble( -pMat->GetDouble(j), j ); + else + pResMat->PutString( + ScGlobal::GetRscString( STR_NO_VALUE ), j ); + } + PushMatrix( pResMat ); + } + } + } + break; + default: + PushDouble( -GetDouble() ); + } +} + + +void ScInterpreter::ScPercentSign() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPercentSign" ); + nFuncFmtType = NUMBERFORMAT_PERCENT; + const FormulaToken* pSaveCur = pCur; + BYTE nSavePar = cPar; + PushInt( 100 ); + cPar = 2; + FormulaByteToken aDivOp( ocDiv, cPar ); + pCur = &aDivOp; + ScDiv(); + pCur = pSaveCur; + cPar = nSavePar; +} + + +void ScInterpreter::ScNot() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNot" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + switch ( GetStackType() ) + { + case svMatrix : + { + ScMatrixRef pMat = GetMatrix(); + if ( !pMat ) + PushIllegalParameter(); + else + { + SCSIZE nC, nR; + pMat->GetDimensions( nC, nR ); + ScMatrixRef pResMat = GetNewMat( nC, nR); + if ( !pResMat ) + PushIllegalArgument(); + else + { + SCSIZE nCount = nC * nR; + for ( SCSIZE j=0; j<nCount; ++j ) + { + if ( pMat->IsValueOrEmpty(j) ) + pResMat->PutDouble( (pMat->GetDouble(j) == 0.0), j ); + else + pResMat->PutString( + ScGlobal::GetRscString( STR_NO_VALUE ), j ); + } + PushMatrix( pResMat ); + } + } + } + break; + default: + PushInt( GetDouble() == 0.0 ); + } +} + + +void ScInterpreter::ScPi() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPi" ); + PushDouble(F_PI); +} + + +void ScInterpreter::ScRandom() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRandom" ); + PushDouble((double)rand() / ((double)RAND_MAX+1.0)); +} + + +void ScInterpreter::ScTrue() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTrue" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + PushInt(1); +} + + +void ScInterpreter::ScFalse() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFalse" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + PushInt(0); +} + + +void ScInterpreter::ScDeg() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDeg" ); + PushDouble((GetDouble() / F_PI) * 180.0); +} + + +void ScInterpreter::ScRad() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRad" ); + PushDouble(GetDouble() * (F_PI / 180)); +} + + +void ScInterpreter::ScSin() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSin" ); + PushDouble(::rtl::math::sin(GetDouble())); +} + + +void ScInterpreter::ScCos() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCos" ); + PushDouble(::rtl::math::cos(GetDouble())); +} + + +void ScInterpreter::ScTan() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTan" ); + PushDouble(::rtl::math::tan(GetDouble())); +} + + +void ScInterpreter::ScCot() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCot" ); + PushDouble(1.0 / ::rtl::math::tan(GetDouble())); +} + + +void ScInterpreter::ScArcSin() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcSin" ); + PushDouble(asin(GetDouble())); +} + + +void ScInterpreter::ScArcCos() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcCos" ); + PushDouble(acos(GetDouble())); +} + + +void ScInterpreter::ScArcTan() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcTan" ); + PushDouble(atan(GetDouble())); +} + + +void ScInterpreter::ScArcCot() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcCot" ); + PushDouble((F_PI2) - atan(GetDouble())); +} + + +void ScInterpreter::ScSinHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSinHyp" ); + PushDouble(sinh(GetDouble())); +} + + +void ScInterpreter::ScCosHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCosHyp" ); + PushDouble(cosh(GetDouble())); +} + + +void ScInterpreter::ScTanHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTanHyp" ); + PushDouble(tanh(GetDouble())); +} + + +void ScInterpreter::ScCotHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCotHyp" ); + PushDouble(1.0 / tanh(GetDouble())); +} + + +void ScInterpreter::ScArcSinHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcSinHyp" ); + PushDouble( ::rtl::math::asinh( GetDouble())); +} + +void ScInterpreter::ScArcCosHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcCosHyp" ); + double fVal = GetDouble(); + if (fVal < 1.0) + PushIllegalArgument(); + else + PushDouble( ::rtl::math::acosh( fVal)); +} + +void ScInterpreter::ScArcTanHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcTanHyp" ); + double fVal = GetDouble(); + if (fabs(fVal) >= 1.0) + PushIllegalArgument(); + else + PushDouble( ::rtl::math::atanh( fVal)); +} + + +void ScInterpreter::ScArcCotHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcCotHyp" ); + double nVal = GetDouble(); + if (fabs(nVal) <= 1.0) + PushIllegalArgument(); + else + PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0))); +} + + +void ScInterpreter::ScExp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScExp" ); + PushDouble(exp(GetDouble())); +} + + +void ScInterpreter::ScSqrt() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSqrt" ); + double fVal = GetDouble(); + if (fVal >= 0.0) + PushDouble(sqrt(fVal)); + else + PushIllegalArgument(); +} + + +void ScInterpreter::ScIsEmpty() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsEmpty" ); + short nRes = 0; + nFuncFmtType = NUMBERFORMAT_LOGICAL; + switch ( GetRawStackType() ) + { + case svEmptyCell: + { + FormulaTokenRef p = PopToken(); + if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited()) + nRes = 1; + } + break; + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + // NOTE: this could test also on inherited emptiness, but then the + // cell tested wouldn't be empty. Must correspond with + // ScCountEmptyCells(). + // if (HasCellEmptyData( GetCell( aAdr))) + CellType eCellType = GetCellType( GetCell( aAdr ) ); + if((eCellType == CELLTYPE_NONE) || (eCellType == CELLTYPE_NOTE)) + nRes = 1; + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + ; // nothing + else if ( !pJumpMatrix ) + nRes = pMat->IsEmpty( 0 ); + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + nRes = pMat->IsEmpty( nC, nR); + // else: FALSE, not empty (which is what Xcl does) + } + } + break; + default: + Pop(); + } + nGlobalError = 0; + PushInt( nRes ); +} + + +short ScInterpreter::IsString() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::IsString" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetRawStackType() ) + { + case svString: + Pop(); + nRes = 1; + break; + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + if (GetCellErrCode( pCell ) == 0) + { + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_STRING : + case CELLTYPE_EDIT : + nRes = 1; + break; + case CELLTYPE_FORMULA : + nRes = !((ScFormulaCell*)pCell)->IsValue() && + !((ScFormulaCell*)pCell)->IsEmpty(); + break; + default: + ; // nothing + } + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + ; // nothing + else if ( !pJumpMatrix ) + nRes = pMat->IsString(0) && !pMat->IsEmpty(0); + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + nRes = pMat->IsString( nC, nR) && !pMat->IsEmpty( nC, nR); + } + } + break; + default: + Pop(); + } + nGlobalError = 0; + return nRes; +} + + +void ScInterpreter::ScIsString() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsString" ); + PushInt( IsString() ); +} + + +void ScInterpreter::ScIsNonString() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsNonString" ); + PushInt( !IsString() ); +} + + +void ScInterpreter::ScIsLogical() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsLogical" ); + short nRes = 0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + if (GetCellErrCode( pCell ) == 0) + { + if (HasCellValueData(pCell)) + { + ULONG nFormat = GetCellNumberFormat( aAdr, pCell ); + nRes = ( pFormatter->GetType(nFormat) + == NUMBERFORMAT_LOGICAL); + } + } + } + break; + case svMatrix: + // TODO: we don't have type information for arrays except + // numerical/string. + // Fall thru + default: + PopError(); + if ( !nGlobalError ) + nRes = ( nCurFmtType == NUMBERFORMAT_LOGICAL ); + } + nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL; + nGlobalError = 0; + PushInt( nRes ); +} + + +void ScInterpreter::ScType() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScType" ); + short nType = 0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + if (GetCellErrCode( pCell ) == 0) + { + switch ( GetCellType( pCell ) ) + { + // NOTE: this is Xcl nonsense! + case CELLTYPE_NOTE : + nType = 1; // empty cell is value (0) + break; + case CELLTYPE_STRING : + case CELLTYPE_EDIT : + nType = 2; + break; + case CELLTYPE_VALUE : + { + ULONG nFormat = GetCellNumberFormat( aAdr, pCell ); + if (pFormatter->GetType(nFormat) + == NUMBERFORMAT_LOGICAL) + nType = 4; + else + nType = 1; + } + break; + case CELLTYPE_FORMULA : + nType = 8; + break; + default: + PushIllegalArgument(); + } + } + else + nType = 16; + } + break; + case svString: + PopError(); + if ( nGlobalError ) + { + nType = 16; + nGlobalError = 0; + } + else + nType = 2; + break; + case svMatrix: + PopMatrix(); + if ( nGlobalError ) + { + nType = 16; + nGlobalError = 0; + } + else + nType = 64; + // we could return the type of one element if in JumpMatrix or + // ForceArray mode, but Xcl doesn't ... + break; + default: + PopError(); + if ( nGlobalError ) + { + nType = 16; + nGlobalError = 0; + } + else + nType = 1; + } + PushInt( nType ); +} + + +inline BOOL lcl_FormatHasNegColor( const SvNumberformat* pFormat ) +{ + return pFormat && pFormat->GetColor( 1 ); +} + + +inline BOOL lcl_FormatHasOpenPar( const SvNumberformat* pFormat ) +{ + return pFormat && (pFormat->GetFormatstring().Search( '(' ) != STRING_NOTFOUND); +} + + +void ScInterpreter::ScCell() +{ // ATTRIBUTE ; [REF] + BYTE nParamCount = GetByte(); + if( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + ScAddress aCellPos( aPos ); + BOOL bError = FALSE; + if( nParamCount == 2 ) + bError = !PopDoubleRefOrSingleRef( aCellPos ); + String aInfoType( GetString() ); + if( bError || nGlobalError ) + PushIllegalParameter(); + else + { + String aFuncResult; + ScBaseCell* pCell = GetCell( aCellPos ); + + ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::GetLocale(), ocCell); + +// *** ADDRESS INFO *** + if( aInfoType.EqualsAscii( "COL" ) ) + { // column number (1-based) + PushInt( aCellPos.Col() + 1 ); + } + else if( aInfoType.EqualsAscii( "ROW" ) ) + { // row number (1-based) + PushInt( aCellPos.Row() + 1 ); + } + else if( aInfoType.EqualsAscii( "SHEET" ) ) + { // table number (1-based) + PushInt( aCellPos.Tab() + 1 ); + } + else if( aInfoType.EqualsAscii( "ADDRESS" ) ) + { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW + USHORT nFlags = (aCellPos.Tab() == aPos.Tab()) ? (SCA_ABS) : (SCA_ABS_3D); + aCellPos.Format( aFuncResult, nFlags, pDok, pDok->GetAddressConvention() ); + PushString( aFuncResult ); + } + else if( aInfoType.EqualsAscii( "FILENAME" ) ) + { // file name and table name: 'FILENAME'#$TABLE + SCTAB nTab = aCellPos.Tab(); + if( nTab < pDok->GetTableCount() ) + { + if( pDok->GetLinkMode( nTab ) == SC_LINK_VALUE ) + pDok->GetName( nTab, aFuncResult ); + else + { + SfxObjectShell* pShell = pDok->GetDocumentShell(); + if( pShell && pShell->GetMedium() ) + { + aFuncResult = (sal_Unicode) '\''; + const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject(); + aFuncResult += String( rURLObj.GetMainURL( INetURLObject::DECODE_UNAMBIGUOUS ) ); + aFuncResult.AppendAscii( "'#$" ); + String aTabName; + pDok->GetName( nTab, aTabName ); + aFuncResult += aTabName; + } + } + } + PushString( aFuncResult ); + } + else if( aInfoType.EqualsAscii( "COORD" ) ) + { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW + // Yes, passing tab as col is intentional! + ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format( + aFuncResult, (SCA_COL_ABSOLUTE|SCA_VALID_COL), NULL, pDok->GetAddressConvention() ); + aFuncResult += ':'; + String aCellStr; + aCellPos.Format( aCellStr, (SCA_COL_ABSOLUTE|SCA_VALID_COL|SCA_ROW_ABSOLUTE|SCA_VALID_ROW), + NULL, pDok->GetAddressConvention() ); + aFuncResult += aCellStr; + PushString( aFuncResult ); + } + +// *** CELL PROPERTIES *** + else if( aInfoType.EqualsAscii( "CONTENTS" ) ) + { // contents of the cell, no formatting + if( pCell && pCell->HasStringData() ) + { + GetCellString( aFuncResult, pCell ); + PushString( aFuncResult ); + } + else + PushDouble( GetCellValue( aCellPos, pCell ) ); + } + else if( aInfoType.EqualsAscii( "TYPE" ) ) + { // b = blank; l = string (label); v = otherwise (value) + if( HasCellStringData( pCell ) ) + aFuncResult = 'l'; + else + aFuncResult = HasCellValueData( pCell ) ? 'v' : 'b'; + PushString( aFuncResult ); + } + else if( aInfoType.EqualsAscii( "WIDTH" ) ) + { // column width (rounded off as count of zero characters in standard font and size) + Printer* pPrinter = pDok->GetPrinter(); + MapMode aOldMode( pPrinter->GetMapMode() ); + Font aOldFont( pPrinter->GetFont() ); + Font aDefFont; + + pPrinter->SetMapMode( MAP_TWIP ); + // font color doesn't matter here + pDok->GetDefPattern()->GetFont( aDefFont, SC_AUTOCOL_BLACK, pPrinter ); + pPrinter->SetFont( aDefFont ); + long nZeroWidth = pPrinter->GetTextWidth( String( '0' ) ); + pPrinter->SetFont( aOldFont ); + pPrinter->SetMapMode( aOldMode ); + int nZeroCount = (int)(pDok->GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth); + PushInt( nZeroCount ); + } + else if( aInfoType.EqualsAscii( "PREFIX" ) ) + { // ' = left; " = right; ^ = centered + if( HasCellStringData( pCell ) ) + { + const SvxHorJustifyItem* pJustAttr = (const SvxHorJustifyItem*) + pDok->GetAttr( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab(), ATTR_HOR_JUSTIFY ); + switch( pJustAttr->GetValue() ) + { + case SVX_HOR_JUSTIFY_STANDARD: + case SVX_HOR_JUSTIFY_LEFT: + case SVX_HOR_JUSTIFY_BLOCK: aFuncResult = '\''; break; + case SVX_HOR_JUSTIFY_CENTER: aFuncResult = '^'; break; + case SVX_HOR_JUSTIFY_RIGHT: aFuncResult = '"'; break; + case SVX_HOR_JUSTIFY_REPEAT: aFuncResult = '\\'; break; + } + } + PushString( aFuncResult ); + } + else if( aInfoType.EqualsAscii( "PROTECT" ) ) + { // 1 = cell locked + const ScProtectionAttr* pProtAttr = (const ScProtectionAttr*) + pDok->GetAttr( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab(), ATTR_PROTECTION ); + PushInt( pProtAttr->GetProtection() ? 1 : 0 ); + } + +// *** FORMATTING *** + else if( aInfoType.EqualsAscii( "FORMAT" ) ) + { // specific format code for standard formats + ULONG nFormat = pDok->GetNumberFormat( aCellPos ); + BOOL bAppendPrec = TRUE; + USHORT nPrec, nLeading; + BOOL bThousand, bIsRed; + pFormatter->GetFormatSpecialInfo( nFormat, bThousand, bIsRed, nPrec, nLeading ); + + switch( pFormatter->GetType( nFormat ) ) + { + case NUMBERFORMAT_NUMBER: aFuncResult = (bThousand ? ',' : 'F'); break; + case NUMBERFORMAT_CURRENCY: aFuncResult = 'C'; break; + case NUMBERFORMAT_SCIENTIFIC: aFuncResult = 'S'; break; + case NUMBERFORMAT_PERCENT: aFuncResult = 'P'; break; + default: + { + bAppendPrec = FALSE; + switch( pFormatter->GetIndexTableOffset( nFormat ) ) + { + case NF_DATE_SYSTEM_SHORT: + case NF_DATE_SYS_DMMMYY: + case NF_DATE_SYS_DDMMYY: + case NF_DATE_SYS_DDMMYYYY: + case NF_DATE_SYS_DMMMYYYY: + case NF_DATE_DIN_DMMMYYYY: + case NF_DATE_SYS_DMMMMYYYY: + case NF_DATE_DIN_DMMMMYYYY: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D1" ) ); break; + case NF_DATE_SYS_DDMMM: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D2" ) ); break; + case NF_DATE_SYS_MMYY: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D3" ) ); break; + case NF_DATETIME_SYSTEM_SHORT_HHMM: + case NF_DATETIME_SYS_DDMMYYYY_HHMMSS: + aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D4" ) ); break; + case NF_DATE_DIN_MMDD: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D5" ) ); break; + case NF_TIME_HHMMSSAMPM: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D6" ) ); break; + case NF_TIME_HHMMAMPM: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D7" ) ); break; + case NF_TIME_HHMMSS: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D8" ) ); break; + case NF_TIME_HHMM: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D9" ) ); break; + default: aFuncResult = 'G'; + } + } + } + if( bAppendPrec ) + aFuncResult += String::CreateFromInt32( nPrec ); + const SvNumberformat* pFormat = pFormatter->GetEntry( nFormat ); + if( lcl_FormatHasNegColor( pFormat ) ) + aFuncResult += '-'; + if( lcl_FormatHasOpenPar( pFormat ) ) + aFuncResult.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "()" ) ); + PushString( aFuncResult ); + } + else if( aInfoType.EqualsAscii( "COLOR" ) ) + { // 1 = negative values are colored, otherwise 0 + const SvNumberformat* pFormat = pFormatter->GetEntry( pDok->GetNumberFormat( aCellPos ) ); + PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 ); + } + else if( aInfoType.EqualsAscii( "PARENTHESES" ) ) + { // 1 = format string contains a '(' character, otherwise 0 + const SvNumberformat* pFormat = pFormatter->GetEntry( pDok->GetNumberFormat( aCellPos ) ); + PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 ); + } + else + PushIllegalArgument(); + } + } +} + + +void ScInterpreter::ScIsRef() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCell" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetStackType() ) + { + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( !nGlobalError ) + nRes = 1; + } + break; + case svDoubleRef : + { + ScRange aRange; + PopDoubleRef( aRange ); + if ( !nGlobalError ) + nRes = 1; + } + break; + case svRefList : + { + FormulaTokenRef x = PopToken(); + if ( !nGlobalError ) + nRes = !static_cast<ScToken*>(x.get())->GetRefList()->empty(); + } + break; + default: + Pop(); + } + nGlobalError = 0; + PushInt( nRes ); +} + + +void ScInterpreter::ScIsValue() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsValue" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetRawStackType() ) + { + case svDouble: + Pop(); + nRes = 1; + break; + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + if (GetCellErrCode( pCell ) == 0) + { + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_VALUE : + nRes = 1; + break; + case CELLTYPE_FORMULA : + nRes = ((ScFormulaCell*)pCell)->IsValue() && + !((ScFormulaCell*)pCell)->IsEmpty(); + break; + default: + ; // nothing + } + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + ; // nothing + else if ( !pJumpMatrix ) + { + if (pMat->GetErrorIfNotString( 0 ) == 0) + nRes = pMat->IsValue( 0 ); + } + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + if (pMat->GetErrorIfNotString( nC, nR) == 0) + nRes = pMat->IsValue( nC, nR); + } + } + break; + default: + Pop(); + } + nGlobalError = 0; + PushInt( nRes ); +} + + +void ScInterpreter::ScIsFormula() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsFormula" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + nRes = (GetCellType( GetCell( aAdr ) ) == CELLTYPE_FORMULA); + } + break; + default: + Pop(); + } + nGlobalError = 0; + PushInt( nRes ); +} + + +void ScInterpreter::ScFormula() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFormula" ); + String aFormula; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_FORMULA : + ((ScFormulaCell*)pCell)->GetFormula( aFormula ); + break; + default: + SetError( NOTAVAILABLE ); + } + } + break; + default: + Pop(); + SetError( NOTAVAILABLE ); + } + PushString( aFormula ); +} + + + +void ScInterpreter::ScIsNV() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsNV" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + PopDoubleRefOrSingleRef( aAdr ); + if ( nGlobalError == NOTAVAILABLE ) + nRes = 1; + else + { + ScBaseCell* pCell = GetCell( aAdr ); + USHORT nErr = GetCellErrCode( pCell ); + nRes = (nErr == NOTAVAILABLE); + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + ; // nothing + else if ( !pJumpMatrix ) + nRes = (pMat->GetErrorIfNotString( 0 ) == NOTAVAILABLE); + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + nRes = (pMat->GetErrorIfNotString( nC, nR) == NOTAVAILABLE); + } + } + break; + default: + PopError(); + if ( nGlobalError == NOTAVAILABLE ) + nRes = 1; + } + nGlobalError = 0; + PushInt( nRes ); +} + + +void ScInterpreter::ScIsErr() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsErr" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + PopDoubleRefOrSingleRef( aAdr ); + if ( nGlobalError && nGlobalError != NOTAVAILABLE ) + nRes = 1; + else + { + ScBaseCell* pCell = GetCell( aAdr ); + USHORT nErr = GetCellErrCode( pCell ); + nRes = (nErr && nErr != NOTAVAILABLE); + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( nGlobalError || !pMat ) + nRes = ((nGlobalError && nGlobalError != NOTAVAILABLE) || !pMat); + else if ( !pJumpMatrix ) + { + USHORT nErr = pMat->GetErrorIfNotString( 0 ); + nRes = (nErr && nErr != NOTAVAILABLE); + } + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + { + USHORT nErr = pMat->GetErrorIfNotString( nC, nR); + nRes = (nErr && nErr != NOTAVAILABLE); + } + } + } + break; + default: + PopError(); + if ( nGlobalError && nGlobalError != NOTAVAILABLE ) + nRes = 1; + } + nGlobalError = 0; + PushInt( nRes ); +} + + +void ScInterpreter::ScIsError() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsError" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + nRes = 1; + break; + } + if ( nGlobalError ) + nRes = 1; + else + { + ScBaseCell* pCell = GetCell( aAdr ); + nRes = (GetCellErrCode( pCell ) != 0); + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( nGlobalError || !pMat ) + nRes = 1; + else if ( !pJumpMatrix ) + nRes = (pMat->GetErrorIfNotString( 0 ) != 0); + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + nRes = (pMat->GetErrorIfNotString( nC, nR) != 0); + } + } + break; + default: + PopError(); + if ( nGlobalError ) + nRes = 1; + } + nGlobalError = 0; + PushInt( nRes ); +} + + +short ScInterpreter::IsEven() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::IsEven" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + double fVal = 0.0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + USHORT nErr = GetCellErrCode( pCell ); + if (nErr != 0) + SetError(nErr); + else + { + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_VALUE : + fVal = GetCellValue( aAdr, pCell ); + nRes = 1; + break; + case CELLTYPE_FORMULA : + if( ((ScFormulaCell*)pCell)->IsValue() ) + { + fVal = GetCellValue( aAdr, pCell ); + nRes = 1; + } + break; + default: + ; // nothing + } + } + } + break; + case svDouble: + { + fVal = PopDouble(); + nRes = 1; + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + ; // nothing + else if ( !pJumpMatrix ) + { + nRes = pMat->IsValue( 0 ); + if ( nRes ) + fVal = pMat->GetDouble( 0 ); + } + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + { + nRes = pMat->IsValue( nC, nR); + if ( nRes ) + fVal = pMat->GetDouble( nC, nR); + } + else + SetError( errNoValue); + } + } + break; + default: + ; // nothing + } + if ( !nRes ) + SetError( errIllegalParameter); + else + nRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 ); + return nRes; +} + + +void ScInterpreter::ScIsEven() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsEven" ); + PushInt( IsEven() ); +} + + +void ScInterpreter::ScIsOdd() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsOdd" ); + PushInt( !IsEven() ); +} + + +void ScInterpreter::ScN() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScN" ); + USHORT nErr = nGlobalError; + nGlobalError = 0; + // Temporarily override the ConvertStringToValue() error for + // GetCellValue() / GetCellValueOrZero() + USHORT nSErr = mnStringNoValueError; + mnStringNoValueError = errCellNoValue; + double fVal = GetDouble(); + mnStringNoValueError = nSErr; + if ( nGlobalError == NOTAVAILABLE || nGlobalError == errCellNoValue ) + nGlobalError = 0; // N(#NA) and N("text") are ok + if ( !nGlobalError && nErr != NOTAVAILABLE ) + nGlobalError = nErr; + PushDouble( fVal ); +} + + +void ScInterpreter::ScTrim() +{ // trimmt nicht nur sondern schnibbelt auch doppelte raus! + String aVal( GetString() ); + aVal.EraseLeadingChars(); + aVal.EraseTrailingChars(); + String aStr; + register const sal_Unicode* p = aVal.GetBuffer(); + register const sal_Unicode* const pEnd = p + aVal.Len(); + while ( p < pEnd ) + { + if ( *p != ' ' || p[-1] != ' ' ) // erster kann kein ' ' sein, -1 ist also ok + aStr += *p; + p++; + } + PushString( aStr ); +} + + +void ScInterpreter::ScUpper() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTrim" ); + String aString = GetString(); + ScGlobal::pCharClass->toUpper(aString); + PushString(aString); +} + + +void ScInterpreter::ScPropper() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPropper" ); +//2do: what to do with I18N-CJK ?!? + String aStr( GetString() ); + const xub_StrLen nLen = aStr.Len(); + // #i82487# don't try to write to empty string's BufferAccess + // (would crash now that the empty string is const) + if ( nLen > 0 ) + { + String aUpr( ScGlobal::pCharClass->upper( aStr ) ); + String aLwr( ScGlobal::pCharClass->lower( aStr ) ); + register sal_Unicode* pStr = aStr.GetBufferAccess(); + const sal_Unicode* pUpr = aUpr.GetBuffer(); + const sal_Unicode* pLwr = aLwr.GetBuffer(); + *pStr = *pUpr; + String aTmpStr( 'x' ); + xub_StrLen nPos = 1; + while( nPos < nLen ) + { + aTmpStr.SetChar( 0, pStr[nPos-1] ); + if ( !ScGlobal::pCharClass->isLetter( aTmpStr, 0 ) ) + pStr[nPos] = pUpr[nPos]; + else + pStr[nPos] = pLwr[nPos]; + nPos++; + } + aStr.ReleaseBufferAccess( nLen ); + } + PushString( aStr ); +} + + +void ScInterpreter::ScLower() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLower" ); + String aString( GetString() ); + ScGlobal::pCharClass->toLower(aString); + PushString(aString); +} + + +void ScInterpreter::ScLen() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLen" ); + String aStr( GetString() ); + PushDouble( aStr.Len() ); +} + + +void ScInterpreter::ScT() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScT" ); + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + PushInt(0); + return ; + } + BOOL bValue = FALSE; + ScBaseCell* pCell = GetCell( aAdr ); + if ( GetCellErrCode( pCell ) == 0 ) + { + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_VALUE : + bValue = TRUE; + break; + case CELLTYPE_FORMULA : + bValue = ((ScFormulaCell*)pCell)->IsValue(); + break; + default: + ; // nothing + } + } + if ( bValue ) + PushString( EMPTY_STRING ); + else + { + // wie GetString() + GetCellString( aTempStr, pCell ); + PushString( aTempStr ); + } + } + break; + case svDouble : + { + PopError(); + PushString( EMPTY_STRING ); + } + break; + case svString : + ; // leave on stack + break; + default : + PushError( errUnknownOpCode); + } +} + + +void ScInterpreter::ScValue() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScValue" ); + String aInputString; + double fVal; + + switch ( GetRawStackType() ) + { + case svMissing: + case svEmptyCell: + Pop(); + PushInt(0); + return; + case svDouble: + return; // leave on stack + //break; + + case svSingleRef: + case svDoubleRef: + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + PushInt(0); + return; + } + ScBaseCell* pCell = GetCell( aAdr ); + if ( pCell && pCell->HasStringData() ) + GetCellString( aInputString, pCell ); + else if ( pCell && pCell->HasValueData() ) + { + PushDouble( GetCellValue(aAdr, pCell) ); + return; + } + else + { + PushDouble(0.0); + return; + } + } + break; + case svMatrix: + { + ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, + aInputString); + switch (nType) + { + case SC_MATVAL_EMPTY: + fVal = 0.0; + // fallthru + case SC_MATVAL_VALUE: + case SC_MATVAL_BOOLEAN: + PushDouble( fVal); + return; + //break; + case SC_MATVAL_STRING: + // evaluated below + break; + default: + PushIllegalArgument(); + } + } + break; + default: + aInputString = GetString(); + break; + } + + sal_uInt32 nFIndex = 0; // 0 for default locale + if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal)) + PushDouble(fVal); + else + PushIllegalArgument(); +} + + +//2do: this should be a proper unicode string method +inline BOOL lcl_ScInterpreter_IsPrintable( sal_Unicode c ) +{ + return 0x20 <= c && c != 0x7f; +} + +void ScInterpreter::ScClean() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScClean" ); + String aStr( GetString() ); + for ( xub_StrLen i = 0; i < aStr.Len(); i++ ) + { + if ( !lcl_ScInterpreter_IsPrintable( aStr.GetChar( i ) ) ) + aStr.Erase(i,1); + } + PushString(aStr); +} + + +void ScInterpreter::ScCode() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCode" ); +//2do: make it full range unicode? + const String& rStr = GetString(); + PushInt( (sal_uChar) ByteString::ConvertFromUnicode( rStr.GetChar(0), gsl_getSystemTextEncoding() ) ); +} + + +void ScInterpreter::ScChar() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScChar" ); +//2do: make it full range unicode? + double fVal = GetDouble(); + if (fVal < 0.0 || fVal >= 256.0) + PushIllegalArgument(); + else + { + String aStr( '0' ); + aStr.SetChar( 0, ByteString::ConvertToUnicode( (sal_Char) fVal, gsl_getSystemTextEncoding() ) ); + PushString( aStr ); + } +} + + +/* #i70213# fullwidth/halfwidth conversion provided by + * Takashi Nakamoto <bluedwarf@ooo> + * erAck: added Excel compatibility conversions as seen in issue's test case. */ + +static ::rtl::OUString lcl_convertIntoHalfWidth( const ::rtl::OUString & rStr ) +{ + static bool bFirstASCCall = true; + static utl::TransliterationWrapper aTrans( ::comphelper::getProcessServiceFactory(), 0 ); + + if( bFirstASCCall ) + { + aTrans.loadModuleByImplName( ::rtl::OUString::createFromAscii( "FULLWIDTH_HALFWIDTH_LIKE_ASC" ), LANGUAGE_SYSTEM ); + bFirstASCCall = false; + } + + return aTrans.transliterate( rStr, 0, USHORT( rStr.getLength() ), NULL ); +} + + +static ::rtl::OUString lcl_convertIntoFullWidth( const ::rtl::OUString & rStr ) +{ + static bool bFirstJISCall = true; + static utl::TransliterationWrapper aTrans( ::comphelper::getProcessServiceFactory(), 0 ); + + if( bFirstJISCall ) + { + aTrans.loadModuleByImplName( ::rtl::OUString::createFromAscii( "HALFWIDTH_FULLWIDTH_LIKE_JIS" ), LANGUAGE_SYSTEM ); + bFirstJISCall = false; + } + + return aTrans.transliterate( rStr, 0, USHORT( rStr.getLength() ), NULL ); +} + + +/* ODFF: + * Summary: Converts half-width to full-width ASCII and katakana characters. + * Semantics: Conversion is done for half-width ASCII and katakana characters, + * other characters are simply copied from T to the result. This is the + * complementary function to ASC. + * For references regarding halfwidth and fullwidth characters see + * http://www.unicode.org/reports/tr11/ + * http://www.unicode.org/charts/charindex2.html#H + * http://www.unicode.org/charts/charindex2.html#F + */ +void ScInterpreter::ScJis() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScJis" ); + if (MustHaveParamCount( GetByte(), 1)) + PushString( lcl_convertIntoFullWidth( GetString())); +} + + +/* ODFF: + * Summary: Converts full-width to half-width ASCII and katakana characters. + * Semantics: Conversion is done for full-width ASCII and katakana characters, + * other characters are simply copied from T to the result. This is the + * complementary function to JIS. + */ +void ScInterpreter::ScAsc() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAsc" ); + if (MustHaveParamCount( GetByte(), 1)) + PushString( lcl_convertIntoHalfWidth( GetString())); +} + +void ScInterpreter::ScUnicode() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScUnicode" ); + if ( MustHaveParamCount( GetByte(), 1 ) ) + { + const rtl::OUString& rStr = GetString(); + if (rStr.getLength() <= 0) + PushIllegalParameter(); + else + { + sal_Int32 i = 0; + PushDouble( rStr.iterateCodePoints(&i) ); + } + } +} + +void ScInterpreter::ScUnichar() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScUnichar" ); + if ( MustHaveParamCount( GetByte(), 1 ) ) + { + double dVal = ::rtl::math::approxFloor( GetDouble() ); + if ((dVal < 0x000000) || (dVal > 0x10FFFF)) + PushIllegalArgument(); + else + { + sal_uInt32 nCodePoint = static_cast<sal_uInt32>( dVal ); + rtl::OUString aStr( &nCodePoint, 1 ); + PushString( aStr ); + } + } +} + + +void ScInterpreter::ScMin( BOOL bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMin" ); + short nParamCount = GetByte(); + if (!MustHaveParamCountMin( nParamCount, 1)) + return; + double nMin = ::std::numeric_limits<double>::max(); + double nVal = 0.0; + ScAddress aAdr; + ScRange aRange; + size_t nRefInList = 0; + while (nParamCount-- > 0) + { + switch (GetStackType()) + { + case svDouble : + { + nVal = GetDouble(); + if (nMin > nVal) nMin = nVal; + nFuncFmtType = NUMBERFORMAT_NUMBER; + } + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + nVal = GetCellValue( aAdr, pCell ); + CurFmtToFuncFmt(); + if (nMin > nVal) nMin = nVal; + } + else if ( bTextAsZero && HasCellStringData( pCell ) ) + { + if ( nMin > 0.0 ) + nMin = 0.0; + } + } + break; + case svDoubleRef : + case svRefList : + { + USHORT nErr = 0; + PopDoubleRef( aRange, nParamCount, nRefInList); + ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero ); + if (aValIter.GetFirst(nVal, nErr)) + { + if (nMin > nVal) + nMin = nVal; + aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex ); + while ((nErr == 0) && aValIter.GetNext(nVal, nErr)) + { + if (nMin > nVal) + nMin = nVal; + } + SetError(nErr); + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nC, nR; + nFuncFmtType = NUMBERFORMAT_NUMBER; + pMat->GetDimensions(nC, nR); + if (pMat->IsNumeric()) + { + for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) + for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) + { + nVal = pMat->GetDouble(nMatCol,nMatRow); + if (nMin > nVal) nMin = nVal; + } + } + else + { + for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) + { + for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) + { + if (!pMat->IsString(nMatCol,nMatRow)) + { + nVal = pMat->GetDouble(nMatCol,nMatRow); + if (nMin > nVal) nMin = nVal; + } + else if ( bTextAsZero ) + { + if ( nMin > 0.0 ) + nMin = 0.0; + } + } + } + } + } + } + break; + case svString : + { + Pop(); + if ( bTextAsZero ) + { + if ( nMin > 0.0 ) + nMin = 0.0; + } + else + SetError(errIllegalParameter); + } + break; + default : + Pop(); + SetError(errIllegalParameter); + } + } + if ( nVal < nMin ) + PushDouble(0.0); + else + PushDouble(nMin); +} + +void ScInterpreter::ScMax( BOOL bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMax" ); + short nParamCount = GetByte(); + if (!MustHaveParamCountMin( nParamCount, 1)) + return; + double nMax = -(::std::numeric_limits<double>::max()); + double nVal = 0.0; + ScAddress aAdr; + ScRange aRange; + size_t nRefInList = 0; + while (nParamCount-- > 0) + { + switch (GetStackType()) + { + case svDouble : + { + nVal = GetDouble(); + if (nMax < nVal) nMax = nVal; + nFuncFmtType = NUMBERFORMAT_NUMBER; + } + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + nVal = GetCellValue( aAdr, pCell ); + CurFmtToFuncFmt(); + if (nMax < nVal) nMax = nVal; + } + else if ( bTextAsZero && HasCellStringData( pCell ) ) + { + if ( nMax < 0.0 ) + nMax = 0.0; + } + } + break; + case svDoubleRef : + case svRefList : + { + USHORT nErr = 0; + PopDoubleRef( aRange, nParamCount, nRefInList); + ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero ); + if (aValIter.GetFirst(nVal, nErr)) + { + if (nMax < nVal) + nMax = nVal; + aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex ); + while ((nErr == 0) && aValIter.GetNext(nVal, nErr)) + { + if (nMax < nVal) + nMax = nVal; + } + SetError(nErr); + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + nFuncFmtType = NUMBERFORMAT_NUMBER; + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + if (pMat->IsNumeric()) + { + for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) + for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) + { + nVal = pMat->GetDouble(nMatCol,nMatRow); + if (nMax < nVal) nMax = nVal; + } + } + else + { + for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) + { + for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) + { + if (!pMat->IsString(nMatCol,nMatRow)) + { + nVal = pMat->GetDouble(nMatCol,nMatRow); + if (nMax < nVal) nMax = nVal; + } + else if ( bTextAsZero ) + { + if ( nMax < 0.0 ) + nMax = 0.0; + } + } + } + } + } + } + break; + case svString : + { + Pop(); + if ( bTextAsZero ) + { + if ( nMax < 0.0 ) + nMax = 0.0; + } + else + SetError(errIllegalParameter); + } + break; + default : + Pop(); + SetError(errIllegalParameter); + } + } + if ( nVal > nMax ) + PushDouble(0.0); + else + PushDouble(nMax); +} + +double ScInterpreter::IterateParameters( ScIterFunc eFunc, BOOL bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::IterateParameters" ); + short nParamCount = GetByte(); + double fRes = ( eFunc == ifPRODUCT ) ? 1.0 : 0.0; + double fVal = 0.0; + double fMem = 0.0; + BOOL bNull = TRUE; + ULONG nCount = 0; + ScAddress aAdr; + ScRange aRange; + size_t nRefInList = 0; + if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ) ) + nGlobalError = 0; + while (nParamCount-- > 0) + { + switch (GetStackType()) + { + + case svString: + { + if( eFunc == ifCOUNT ) + { + String aStr( PopString() ); + sal_uInt32 nFIndex = 0; // damit default Land/Spr. + if ( bTextAsZero || pFormatter->IsNumberFormat(aStr, nFIndex, fVal)) + nCount++; + } + else + { + switch ( eFunc ) + { + case ifAVERAGE: + case ifSUM: + case ifSUMSQ: + case ifPRODUCT: + { + if ( bTextAsZero ) + { + Pop(); + nCount++; + if ( eFunc == ifPRODUCT ) + fRes = 0.0; + } + else + { + while (nParamCount-- > 0) + Pop(); + SetError( errNoValue ); + } + } + break; + default: + Pop(); + nCount++; + } + } + } + break; + case svDouble : + fVal = GetDouble(); + nCount++; + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: + if ( bNull && fVal != 0.0 ) + { + bNull = FALSE; + fMem = fVal; + } + else + fRes += fVal; + break; + case ifSUMSQ: fRes += fVal * fVal; break; + case ifPRODUCT: fRes *= fVal; break; + default: ; // nothing + } + nFuncFmtType = NUMBERFORMAT_NUMBER; + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ) ) + { + nGlobalError = 0; + if ( eFunc == ifCOUNT2 ) + ++nCount; + break; + } + ScBaseCell* pCell = GetCell( aAdr ); + if ( pCell ) + { + if( eFunc == ifCOUNT2 ) + { + CellType eCellType = pCell->GetCellType(); + if (eCellType != CELLTYPE_NONE && eCellType != CELLTYPE_NOTE) + nCount++; + if ( nGlobalError ) + nGlobalError = 0; + } + else if ( pCell->HasValueData() ) + { + nCount++; + fVal = GetCellValue( aAdr, pCell ); + CurFmtToFuncFmt(); + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: + if ( bNull && fVal != 0.0 ) + { + bNull = FALSE; + fMem = fVal; + } + else + fRes += fVal; + break; + case ifSUMSQ: fRes += fVal * fVal; break; + case ifPRODUCT: fRes *= fVal; break; + case ifCOUNT: + if ( nGlobalError ) + { + nGlobalError = 0; + nCount--; + } + break; + default: ; // nothing + } + } + else if ( bTextAsZero && pCell->HasStringData() ) + { + nCount++; + if ( eFunc == ifPRODUCT ) + fRes = 0.0; + } + } + } + break; + case svDoubleRef : + case svRefList : + { + USHORT nErr = 0; + PopDoubleRef( aRange, nParamCount, nRefInList); + if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ) ) + { + nGlobalError = 0; + if ( eFunc == ifCOUNT2 ) + ++nCount; + break; + } + if( eFunc == ifCOUNT2 ) + { + ScBaseCell* pCell; + ScCellIterator aIter( pDok, aRange, glSubTotal ); + if ( (pCell = aIter.GetFirst()) != NULL ) + { + do + { + CellType eType = pCell->GetCellType(); + if( eType != CELLTYPE_NONE && eType != CELLTYPE_NOTE ) + nCount++; + } + while ( (pCell = aIter.GetNext()) != NULL ); + } + if ( nGlobalError ) + nGlobalError = 0; + } + else + { + ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero ); + if (aValIter.GetFirst(fVal, nErr)) + { + // Schleife aus Performance-Gruenden nach innen verlegt: + aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex ); + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: + do + { + SetError(nErr); + if ( bNull && fVal != 0.0 ) + { + bNull = FALSE; + fMem = fVal; + } + else + fRes += fVal; + nCount++; + } + while (aValIter.GetNext(fVal, nErr)); + break; + case ifSUMSQ: + do + { + SetError(nErr); + fRes += fVal * fVal; + nCount++; + } + while (aValIter.GetNext(fVal, nErr)); + break; + case ifPRODUCT: + do + { + SetError(nErr); + fRes *= fVal; + nCount++; + } + while (aValIter.GetNext(fVal, nErr)); + break; + case ifCOUNT: + do + { + if ( !nErr ) + nCount++; + } + while (aValIter.GetNext(fVal, nErr)); + break; + default: ; // nothing + } + SetError( nErr ); + } + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nC, nR; + nFuncFmtType = NUMBERFORMAT_NUMBER; + pMat->GetDimensions(nC, nR); + if( eFunc == ifCOUNT2 ) + nCount += (ULONG) nC * nR; + else + { + for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) + { + for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) + { + if (!pMat->IsString(nMatCol,nMatRow)) + { + nCount++; + fVal = pMat->GetDouble(nMatCol,nMatRow); + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: + if ( bNull && fVal != 0.0 ) + { + bNull = FALSE; + fMem = fVal; + } + else + fRes += fVal; + break; + case ifSUMSQ: fRes += fVal * fVal; break; + case ifPRODUCT: fRes *= fVal; break; + default: ; // nothing + } + } + else if ( bTextAsZero ) + { + nCount++; + if ( eFunc == ifPRODUCT ) + fRes = 0.0; + } + } + } + } + } + } + break; + case svError: + { + Pop(); + if ( eFunc == ifCOUNT ) + { + nGlobalError = 0; + } + else if ( eFunc == ifCOUNT2 ) + { + nCount++; + nGlobalError = 0; + } + } + break; + default : + while (nParamCount-- > 0) + PopError(); + SetError(errIllegalParameter); + } + } + switch( eFunc ) + { + case ifSUM: fRes = ::rtl::math::approxAdd( fRes, fMem ); break; + case ifAVERAGE: fRes = div(::rtl::math::approxAdd( fRes, fMem ), nCount); break; + case ifCOUNT2: + case ifCOUNT: fRes = nCount; break; + case ifPRODUCT: if ( !nCount ) fRes = 0.0; break; + default: ; // nothing + } + // Bei Summen etc. macht ein BOOL-Ergebnis keinen Sinn + // und Anzahl ist immer Number (#38345#) + if( eFunc == ifCOUNT || nFuncFmtType == NUMBERFORMAT_LOGICAL ) + nFuncFmtType = NUMBERFORMAT_NUMBER; + return fRes; +} + + +void ScInterpreter::ScSumSQ() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSumSQ" ); + PushDouble( IterateParameters( ifSUMSQ ) ); +} + + +void ScInterpreter::ScSum() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSum" ); + PushDouble( IterateParameters( ifSUM ) ); +} + + +void ScInterpreter::ScProduct() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScProduct" ); + PushDouble( IterateParameters( ifPRODUCT ) ); +} + + +void ScInterpreter::ScAverage( BOOL bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAverage" ); + PushDouble( IterateParameters( ifAVERAGE, bTextAsZero ) ); +} + + +void ScInterpreter::ScCount() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCount" ); + PushDouble( IterateParameters( ifCOUNT ) ); +} + + +void ScInterpreter::ScCount2() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCount2" ); + PushDouble( IterateParameters( ifCOUNT2 ) ); +} + + +void ScInterpreter::GetStVarParams( double& rVal, double& rValCount, + BOOL bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetStVarParams" ); + short nParamCount = GetByte(); + + std::vector<double> values; + double fSum = 0.0; + double vSum = 0.0; + double vMean = 0.0; + double fVal = 0.0; + rValCount = 0.0; + ScAddress aAdr; + ScRange aRange; + size_t nRefInList = 0; + while (nParamCount-- > 0) + { + switch (GetStackType()) + { + case svDouble : + { + fVal = GetDouble(); + values.push_back(fVal); + fSum += fVal; + rValCount++; + } + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + fVal = GetCellValue( aAdr, pCell ); + values.push_back(fVal); + fSum += fVal; + rValCount++; + } + else if ( bTextAsZero && HasCellStringData( pCell ) ) + { + values.push_back(0.0); + rValCount++; + } + } + break; + case svDoubleRef : + case svRefList : + { + USHORT nErr = 0; + PopDoubleRef( aRange, nParamCount, nRefInList); + ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero ); + if (aValIter.GetFirst(fVal, nErr)) + { + do + { + values.push_back(fVal); + fSum += fVal; + rValCount++; + } + while ((nErr == 0) && aValIter.GetNext(fVal, nErr)); + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) + { + for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) + { + if (!pMat->IsString(nMatCol,nMatRow)) + { + fVal= pMat->GetDouble(nMatCol,nMatRow); + values.push_back(fVal); + fSum += fVal; + rValCount++; + } + else if ( bTextAsZero ) + { + values.push_back(0.0); + rValCount++; + } + } + } + } + } + break; + case svString : + { + Pop(); + if ( bTextAsZero ) + { + values.push_back(0.0); + rValCount++; + } + else + SetError(errIllegalParameter); + } + break; + default : + Pop(); + SetError(errIllegalParameter); + } + } + + ::std::vector<double>::size_type n = values.size(); + vMean = fSum / n; + for (::std::vector<double>::size_type i = 0; i < n; i++) + vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean); + rVal = vSum; +} + + +void ScInterpreter::ScVar( BOOL bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScVar" ); + double nVal; + double nValCount; + GetStVarParams( nVal, nValCount, bTextAsZero ); + + if (nValCount <= 1.0) + PushError( errDivisionByZero ); + else + PushDouble( nVal / (nValCount - 1.0)); +} + + +void ScInterpreter::ScVarP( BOOL bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScVarP" ); + double nVal; + double nValCount; + GetStVarParams( nVal, nValCount, bTextAsZero ); + + PushDouble( div( nVal, nValCount)); +} + + +void ScInterpreter::ScStDev( BOOL bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScStDev" ); + double nVal; + double nValCount; + GetStVarParams( nVal, nValCount, bTextAsZero ); + if (nValCount <= 1.0) + PushError( errDivisionByZero ); + else + PushDouble( sqrt( nVal / (nValCount - 1.0))); +} + + +void ScInterpreter::ScStDevP( BOOL bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScStDevP" ); + double nVal; + double nValCount; + GetStVarParams( nVal, nValCount, bTextAsZero ); + if (nValCount == 0.0) + PushError( errDivisionByZero ); + else + PushDouble( sqrt( nVal / nValCount)); + + /* this was: PushDouble( sqrt( div( nVal, nValCount))); + * + * Besides that the special NAN gets lost in the call through sqrt(), + * unxlngi6.pro then looped back and forth somewhere between div() and + * ::rtl::math::setNan(). Tests showed that + * + * sqrt( div( 1, 0)); + * + * produced a loop, but + * + * double f1 = div( 1, 0); + * sqrt( f1 ); + * + * was fine. There seems to be some compiler optimization problem. It does + * not occur when compiled with debug=t. + */ +} + + +void ScInterpreter::ScColumns() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScColumns" ); + BYTE nParamCount = GetByte(); + ULONG nVal = 0; + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + while (nParamCount-- > 0) + { + switch ( GetStackType() ) + { + case svSingleRef: + PopError(); + nVal++; + break; + case svDoubleRef: + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + nVal += static_cast<ULONG>(nTab2 - nTab1 + 1) * + static_cast<ULONG>(nCol2 - nCol1 + 1); + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + nVal += nC; + } + } + break; + default: + PopError(); + SetError(errIllegalParameter); + } + } + PushDouble((double)nVal); +} + + +void ScInterpreter::ScRows() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRows" ); + BYTE nParamCount = GetByte(); + ULONG nVal = 0; + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + while (nParamCount-- > 0) + { + switch ( GetStackType() ) + { + case svSingleRef: + PopError(); + nVal++; + break; + case svDoubleRef: + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + nVal += static_cast<ULONG>(nTab2 - nTab1 + 1) * + static_cast<ULONG>(nRow2 - nRow1 + 1); + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + nVal += nR; + } + } + break; + default: + PopError(); + SetError(errIllegalParameter); + } + } + PushDouble((double)nVal); +} + +void ScInterpreter::ScTables() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTables" ); + BYTE nParamCount = GetByte(); + ULONG nVal; + if ( nParamCount == 0 ) + nVal = pDok->GetTableCount(); + else + { + nVal = 0; + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + while (nParamCount-- > 0) + { + switch ( GetStackType() ) + { + case svSingleRef: + PopError(); + nVal++; + break; + case svDoubleRef: + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + nVal += static_cast<ULONG>(nTab2 - nTab1 + 1); + break; + case svMatrix: + PopError(); + nVal++; + break; + default: + PopError(); + SetError( errIllegalParameter ); + } + } + } + PushDouble( (double) nVal ); +} + + +void ScInterpreter::ScColumn() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScColumn" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 0, 1 ) ) + { + double nVal = 0; + if (nParamCount == 0) + { + nVal = aPos.Col() + 1; + if (bMatrixFormula) + { + SCCOL nCols; + SCROW nRows; + pMyFormulaCell->GetMatColsRows( nCols, nRows); + ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1); + if (pResMat) + { + for (SCCOL i=0; i < nCols; ++i) + pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0); + PushMatrix( pResMat); + return; + } + } + } + else + { + switch ( GetStackType() ) + { + case svSingleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + PopSingleRef( nCol1, nRow1, nTab1 ); + nVal = (double) (nCol1 + 1); + } + break; + case svDoubleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if (nCol2 > nCol1) + { + ScMatrixRef pResMat = GetNewMat( + static_cast<SCSIZE>(nCol2-nCol1+1), 1); + if (pResMat) + { + for (SCCOL i = nCol1; i <= nCol2; i++) + pResMat->PutDouble((double)(i+1), + static_cast<SCSIZE>(i-nCol1), 0); + PushMatrix(pResMat); + return; + } + else + nVal = 0.0; + } + else + nVal = (double) (nCol1 + 1); + } + break; + default: + SetError( errIllegalParameter ); + nVal = 0.0; + } + } + PushDouble( nVal ); + } +} + + +void ScInterpreter::ScRow() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRow" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 0, 1 ) ) + { + double nVal = 0; + if (nParamCount == 0) + { + nVal = aPos.Row() + 1; + if (bMatrixFormula) + { + SCCOL nCols; + SCROW nRows; + pMyFormulaCell->GetMatColsRows( nCols, nRows); + ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows)); + if (pResMat) + { + for (SCROW i=0; i < nRows; i++) + pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i)); + PushMatrix( pResMat); + return; + } + } + } + else + { + switch ( GetStackType() ) + { + case svSingleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + PopSingleRef( nCol1, nRow1, nTab1 ); + nVal = (double) (nRow1 + 1); + } + break; + case svDoubleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if (nRow2 > nRow1) + { + ScMatrixRef pResMat = GetNewMat( 1, + static_cast<SCSIZE>(nRow2-nRow1+1)); + if (pResMat) + { + for (SCROW i = nRow1; i <= nRow2; i++) + pResMat->PutDouble((double)(i+1), 0, + static_cast<SCSIZE>(i-nRow1)); + PushMatrix(pResMat); + return; + } + else + nVal = 0.0; + } + else + nVal = (double) (nRow1 + 1); + } + break; + default: + SetError( errIllegalParameter ); + nVal = 0.0; + } + } + PushDouble( nVal ); + } +} + +void ScInterpreter::ScTable() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTable" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 0, 1 ) ) + { + SCTAB nVal = 0; + if ( nParamCount == 0 ) + nVal = aPos.Tab() + 1; + else + { + switch ( GetStackType() ) + { + case svString : + { + String aStr( PopString() ); + if ( pDok->GetTable( aStr, nVal ) ) + ++nVal; + else + SetError( errIllegalArgument ); + } + break; + case svSingleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + PopSingleRef( nCol1, nRow1, nTab1 ); + nVal = nTab1 + 1; + } + break; + case svDoubleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + nVal = nTab1 + 1; + } + break; + default: + SetError( errIllegalParameter ); + } + if ( nGlobalError ) + nVal = 0; + } + PushDouble( (double) nVal ); + } +} + +/** returns -1 when the matrix value is smaller than the query value, 0 when + they are equal, and 1 when the matrix value is larger than the query + value. */ +static sal_Int32 lcl_CompareMatrix2Query( SCSIZE i, const ScMatrix& rMat, + const ScQueryEntry& rEntry) +{ + if (rMat.IsEmpty(i)) + { + /* TODO: in case we introduced query for real empty this would have to + * be changed! */ + return -1; // empty always less than anything else + } + + /* FIXME: what is an empty path (result of IF(false;true_path) in + * comparisons? */ + + if (rMat.IsValue(i)) + { + if (rEntry.bQueryByString) + return -1; // numeric always less than string + + const double nVal1 = rMat.GetDouble(i); + const double nVal2 = rEntry.nVal; + if (nVal1 == nVal2) + return 0; + + return nVal1 < nVal2 ? -1 : 1; + } + + if (!rEntry.bQueryByString) + return 1; // string always greater than numeric + + if (!rEntry.pStr) + // this should not happen! + return 1; + + const String& rStr1 = rMat.GetString(i); + const String& rStr2 = *rEntry.pStr; + + return ScGlobal::GetCollator()->compareString( rStr1, rStr2); // case-insensitive +} + +/** returns the last item with the identical value as the original item + value. */ +static void lcl_GetLastMatch( SCSIZE& rIndex, const ScMatrix& rMat, + SCSIZE nMatCount, bool bReverse) +{ + if (rMat.IsValue(rIndex)) + { + double nVal = rMat.GetDouble(rIndex); + if (bReverse) + while (rIndex > 0 && rMat.IsValue(rIndex-1) && + nVal == rMat.GetDouble(rIndex-1)) + --rIndex; + else + while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) && + nVal == rMat.GetDouble(rIndex+1)) + ++rIndex; + } + //! Order of IsEmptyPath, IsEmpty, IsString is significant! + else if (rMat.IsEmptyPath(rIndex)) + { + if (bReverse) + while (rIndex > 0 && rMat.IsEmptyPath(rIndex-1)) + --rIndex; + else + while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1)) + ++rIndex; + } + else if (rMat.IsEmpty(rIndex)) + { + if (bReverse) + while (rIndex > 0 && rMat.IsEmpty(rIndex-1)) + --rIndex; + else + while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1)) + ++rIndex; + } + else if (rMat.IsString(rIndex)) + { + String aStr( rMat.GetString(rIndex)); + if (bReverse) + while (rIndex > 0 && rMat.IsString(rIndex-1) && + aStr == rMat.GetString(rIndex-1)) + --rIndex; + else + while (rIndex < nMatCount-1 && rMat.IsString(rIndex+1) && + aStr == rMat.GetString(rIndex+1)) + ++rIndex; + } + else + { + DBG_ERRORFILE("lcl_GetLastMatch: unhandled matrix type"); + } +} + +void ScInterpreter::ScMatch() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMatch" ); + ScMatrixRef pMatSrc = NULL; + + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + double fTyp; + if (nParamCount == 3) + fTyp = GetDouble(); + else + fTyp = 1.0; + SCCOL nCol1 = 0; + SCROW nRow1 = 0; + SCTAB nTab1 = 0; + SCCOL nCol2 = 0; + SCROW nRow2 = 0; + SCTAB nTab2 = 0; + if (GetStackType() == svDoubleRef) + { + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2)) + { + PushIllegalParameter(); + return; + } + } + else if (GetStackType() == svMatrix) + { + pMatSrc = PopMatrix(); + if (!pMatSrc) + { + PushIllegalParameter(); + return; + } + } + else + { + PushIllegalParameter(); + return; + } + if (nGlobalError == 0) + { + double fVal; + String sStr; + ScQueryParam rParam; + rParam.nCol1 = nCol1; + rParam.nRow1 = nRow1; + rParam.nCol2 = nCol2; + rParam.nTab = nTab1; + rParam.bMixedComparison = TRUE; + + ScQueryEntry& rEntry = rParam.GetEntry(0); + rEntry.bDoQuery = TRUE; + if (fTyp < 0.0) + rEntry.eOp = SC_GREATER_EQUAL; + else if (fTyp > 0.0) + rEntry.eOp = SC_LESS_EQUAL; + switch ( GetStackType() ) + { + case svDouble: + { + fVal = GetDouble(); + rEntry.bQueryByString = FALSE; + rEntry.nVal = fVal; + } + break; + case svString: + { + sStr = GetString(); + rEntry.bQueryByString = TRUE; + *rEntry.pStr = sStr; + } + break; + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + PushInt(0); + return ; + } + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + fVal = GetCellValue( aAdr, pCell ); + rEntry.bQueryByString = FALSE; + rEntry.nVal = fVal; + } + else + { + GetCellString(sStr, pCell); + rEntry.bQueryByString = TRUE; + *rEntry.pStr = sStr; + } + } + break; + case svMatrix : + { + ScMatValType nType = GetDoubleOrStringFromMatrix( + rEntry.nVal, *rEntry.pStr); + rEntry.bQueryByString = ScMatrix::IsNonValueType( nType); + } + break; + default: + { + PushIllegalParameter(); + return; + } + } + if ( rEntry.bQueryByString ) + rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok ); + + if (pMatSrc) // The source data is matrix array. + { + SCSIZE nC, nR; + pMatSrc->GetDimensions( nC, nR); + if (nC > 1 && nR > 1) + { + // The source matrix must be a vector. + PushIllegalParameter(); + return; + } + SCSIZE nMatCount = (nC == 1) ? nR : nC; + + // simple serial search for equality mode (source data doesn't + // need to be sorted). + + if (rEntry.eOp == SC_EQUAL) + { + for (SCSIZE i = 0; i < nMatCount; ++i) + { + if (lcl_CompareMatrix2Query( i, *pMatSrc, rEntry) == 0) + { + PushDouble(i+1); // found ! + return; + } + } + PushNA(); // not found + return; + } + + // binary search for non-equality mode (the source data is + // assumed to be sorted). + + bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL); + SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0; + for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst) + { + SCSIZE nMid = nFirst + nLen/2; + sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, *pMatSrc, rEntry); + if (nCmp == 0) + { + // exact match. find the last item with the same value. + lcl_GetLastMatch( nMid, *pMatSrc, nMatCount, !bAscOrder); + PushDouble( nMid+1); + return; + } + + if (nLen == 1) // first and last items are next to each other. + { + if (nCmp < 0) + nHitIndex = bAscOrder ? nLast : nFirst; + else + nHitIndex = bAscOrder ? nFirst : nLast; + break; + } + + if (nCmp < 0) + { + if (bAscOrder) + nFirst = nMid; + else + nLast = nMid; + } + else + { + if (bAscOrder) + nLast = nMid; + else + nFirst = nMid; + } + } + + if (nHitIndex == nMatCount-1) // last item + { + sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, *pMatSrc, rEntry); + if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0)) + { + // either the last item is an exact match or the real + // hit is beyond the last item. + PushDouble( nHitIndex+1); + return; + } + } + + if (nHitIndex > 0) // valid hit must be 2nd item or higher + { + PushDouble( nHitIndex); // non-exact match + return; + } + + PushNA(); + return; + } + + SCCOLROW nDelta = 0; + if (nCol1 == nCol2) + { // search row in column + rParam.nRow2 = nRow2; + rEntry.nField = nCol1; + ScAddress aResultPos( nCol1, nRow1, nTab1); + if (!LookupQueryWithCache( aResultPos, rParam)) + { + PushNA(); + return; + } + nDelta = aResultPos.Row() - nRow1; + } + else + { // search column in row + SCCOL nC; + rParam.bByRow = FALSE; + rParam.nRow2 = nRow1; + rEntry.nField = nCol1; + ScQueryCellIterator aCellIter(pDok, nTab1, rParam, FALSE); + // Advance Entry.nField in Iterator if column changed + aCellIter.SetAdvanceQueryParamEntryField( TRUE ); + if (fTyp == 0.0) + { // EQUAL + if ( aCellIter.GetFirst() ) + nC = aCellIter.GetCol(); + else + { + PushNA(); + return; + } + } + else + { // <= or >= + SCROW nR; + if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) ) + { + PushNA(); + return; + } + } + nDelta = nC - nCol1; + } + PushDouble((double) (nDelta + 1)); + } + else + PushIllegalParameter(); + } +} + + +void ScInterpreter::ScCountEmptyCells() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCountEmptyCells" ); + if ( MustHaveParamCount( GetByte(), 1 ) ) + { + ULONG nMaxCount = 0, nCount = 0; + CellType eCellType; + switch (GetStackType()) + { + case svSingleRef : + { + nMaxCount = 1; + ScAddress aAdr; + PopSingleRef( aAdr ); + eCellType = GetCellType( GetCell( aAdr ) ); + if (eCellType != CELLTYPE_NONE && eCellType != CELLTYPE_NOTE) + nCount = 1; + } + break; + case svDoubleRef : + case svRefList : + { + ScRange aRange; + short nParam = 1; + size_t nRefInList = 0; + while (nParam-- > 0) + { + PopDoubleRef( aRange, nParam, nRefInList); + nMaxCount += + static_cast<ULONG>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) * + static_cast<ULONG>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) * + static_cast<ULONG>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1); + ScBaseCell* pCell; + ScCellIterator aDocIter( pDok, aRange, glSubTotal); + if ( (pCell = aDocIter.GetFirst()) != NULL ) + { + do + { + if ((eCellType = pCell->GetCellType()) != CELLTYPE_NONE + && eCellType != CELLTYPE_NOTE) + nCount++; + } while ( (pCell = aDocIter.GetNext()) != NULL ); + } + } + } + break; + default : SetError(errIllegalParameter); break; + } + PushDouble(nMaxCount - nCount); + } +} + + +void ScInterpreter::ScCountIf() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCountIf" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + String rString; + double fVal = 0.0; + BOOL bIsString = TRUE; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + PushInt(0); + return ; + } + ScBaseCell* pCell = GetCell( aAdr ); + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_VALUE : + fVal = GetCellValue( aAdr, pCell ); + bIsString = FALSE; + break; + case CELLTYPE_FORMULA : + if( ((ScFormulaCell*)pCell)->IsValue() ) + { + fVal = GetCellValue( aAdr, pCell ); + bIsString = FALSE; + } + else + GetCellString(rString, pCell); + break; + case CELLTYPE_STRING : + case CELLTYPE_EDIT : + GetCellString(rString, pCell); + break; + default: + fVal = 0.0; + bIsString = FALSE; + } + } + break; + case svMatrix : + { + ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, + rString); + bIsString = ScMatrix::IsNonValueType( nType); + } + break; + case svString: + rString = GetString(); + break; + default: + { + fVal = GetDouble(); + bIsString = FALSE; + } + } + double fSum = 0.0; + short nParam = 1; + size_t nRefInList = 0; + while (nParam-- > 0) + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + ScMatrixRef pQueryMatrix; + switch ( GetStackType() ) + { + case svDoubleRef : + case svRefList : + { + ScRange aRange; + PopDoubleRef( aRange, nParam, nRefInList); + aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + } + break; + case svSingleRef : + PopSingleRef( nCol1, nRow1, nTab1 ); + nCol2 = nCol1; + nRow2 = nRow1; + nTab2 = nTab1; + break; + case svMatrix: + { + pQueryMatrix = PopMatrix(); + if (!pQueryMatrix) + { + PushIllegalParameter(); + return; + } + nCol1 = 0; + nRow1 = 0; + nTab1 = 0; + SCSIZE nC, nR; + pQueryMatrix->GetDimensions( nC, nR); + nCol2 = static_cast<SCCOL>(nC - 1); + nRow2 = static_cast<SCROW>(nR - 1); + nTab2 = 0; + } + break; + default: + PushIllegalParameter(); + return ; + } + if ( nTab1 != nTab2 ) + { + PushIllegalParameter(); + return; + } + if (nCol1 > nCol2) + { + PushIllegalParameter(); + return; + } + if (nGlobalError == 0) + { + ScQueryParam rParam; + rParam.nRow1 = nRow1; + rParam.nRow2 = nRow2; + + ScQueryEntry& rEntry = rParam.GetEntry(0); + rEntry.bDoQuery = TRUE; + if (!bIsString) + { + rEntry.bQueryByString = FALSE; + rEntry.nVal = fVal; + rEntry.eOp = SC_EQUAL; + } + else + { + rParam.FillInExcelSyntax(rString, 0); + sal_uInt32 nIndex = 0; + rEntry.bQueryByString = + !(pFormatter->IsNumberFormat( + *rEntry.pStr, nIndex, rEntry.nVal)); + if ( rEntry.bQueryByString ) + rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok ); + } + rParam.nCol1 = nCol1; + rParam.nCol2 = nCol2; + rEntry.nField = nCol1; + if (pQueryMatrix) + { + // Never case-sensitive. + ScCompareOptions aOptions( pDok, rEntry, rParam.bRegExp); + ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions); + if (nGlobalError || !pResultMatrix) + { + PushIllegalParameter(); + return; + } + + SCSIZE nSize = pResultMatrix->GetElementCount(); + for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex) + { + if (pResultMatrix->IsValue( nIndex) && + pResultMatrix->GetDouble( nIndex)) + ++fSum; + } + } + else + { + ScQueryCellIterator aCellIter(pDok, nTab1, rParam, FALSE); + // Entry.nField im Iterator bei Spaltenwechsel weiterschalten + aCellIter.SetAdvanceQueryParamEntryField( TRUE ); + if ( aCellIter.GetFirst() ) + { + do + { + fSum++; + } while ( aCellIter.GetNext() ); + } + } + } + else + { + PushIllegalParameter(); + return; + } + } + PushDouble(fSum); + } +} + + +void ScInterpreter::ScSumIf() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSumIf" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + SCCOL nCol3 = 0; + SCROW nRow3 = 0; + SCTAB nTab3 = 0; + + ScMatrixRef pSumExtraMatrix; + bool bSumExtraRange = (nParamCount == 3); + if (bSumExtraRange) + { + // Save only the upperleft cell in case of cell range. The geometry + // of the 3rd parameter is taken from the 1st parameter. + + switch ( GetStackType() ) + { + case svDoubleRef : + { + SCCOL nColJunk = 0; + SCROW nRowJunk = 0; + SCTAB nTabJunk = 0; + PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk ); + if ( nTabJunk != nTab3 ) + { + PushIllegalParameter(); + return; + } + } + break; + case svSingleRef : + PopSingleRef( nCol3, nRow3, nTab3 ); + break; + case svMatrix: + pSumExtraMatrix = PopMatrix(); + //! nCol3, nRow3, nTab3 remain 0 + break; + default: + PushIllegalParameter(); + return ; + } + } + String rString; + double fVal = 0.0; + BOOL bIsString = TRUE; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + PushInt(0); + return ; + } + ScBaseCell* pCell = GetCell( aAdr ); + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_VALUE : + fVal = GetCellValue( aAdr, pCell ); + bIsString = FALSE; + break; + case CELLTYPE_FORMULA : + if( ((ScFormulaCell*)pCell)->IsValue() ) + { + fVal = GetCellValue( aAdr, pCell ); + bIsString = FALSE; + } + else + GetCellString(rString, pCell); + break; + case CELLTYPE_STRING : + case CELLTYPE_EDIT : + GetCellString(rString, pCell); + break; + default: + fVal = 0.0; + bIsString = FALSE; + } + } + break; + case svString: + rString = GetString(); + break; + case svMatrix : + { + ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, + rString); + bIsString = ScMatrix::IsNonValueType( nType); + } + break; + default: + { + fVal = GetDouble(); + bIsString = FALSE; + } + } + + double fSum = 0.0; + double fMem = 0.0; + BOOL bNull = TRUE; + short nParam = 1; + size_t nRefInList = 0; + while (nParam-- > 0) + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + ScMatrixRef pQueryMatrix; + switch ( GetStackType() ) + { + case svRefList : + if (bSumExtraRange) + { + PushIllegalParameter(); + return; + } + else + { + ScRange aRange; + PopDoubleRef( aRange, nParam, nRefInList); + aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + } + break; + case svDoubleRef : + PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + break; + case svSingleRef : + PopSingleRef( nCol1, nRow1, nTab1 ); + nCol2 = nCol1; + nRow2 = nRow1; + nTab2 = nTab1; + break; + case svMatrix: + { + pQueryMatrix = PopMatrix(); + if (!pQueryMatrix) + { + PushIllegalParameter(); + return; + } + nCol1 = 0; + nRow1 = 0; + nTab1 = 0; + SCSIZE nC, nR; + pQueryMatrix->GetDimensions( nC, nR); + nCol2 = static_cast<SCCOL>(nC - 1); + nRow2 = static_cast<SCROW>(nR - 1); + nTab2 = 0; + } + break; + default: + PushIllegalParameter(); + return ; + } + if ( nTab1 != nTab2 ) + { + PushIllegalArgument(); + return; + } + + if (bSumExtraRange) + { + // Take the range geometry of the 1st parameter and apply it to + // the 3rd. If parts of the resulting range would point outside + // the sheet, don't complain but silently ignore and simply cut + // them away, this is what Xcl does :-/ + + // For the cut-away part we also don't need to determine the + // criteria match, so shrink the source range accordingly, + // instead of the result range. + SCCOL nColDelta = nCol2 - nCol1; + SCROW nRowDelta = nRow2 - nRow1; + SCCOL nMaxCol; + SCROW nMaxRow; + if (pSumExtraMatrix) + { + SCSIZE nC, nR; + pSumExtraMatrix->GetDimensions( nC, nR); + nMaxCol = static_cast<SCCOL>(nC - 1); + nMaxRow = static_cast<SCROW>(nR - 1); + } + else + { + nMaxCol = MAXCOL; + nMaxRow = MAXROW; + } + if (nCol3 + nColDelta > nMaxCol) + { + SCCOL nNewDelta = nMaxCol - nCol3; + nCol2 = nCol1 + nNewDelta; + } + + if (nRow3 + nRowDelta > nMaxRow) + { + SCROW nNewDelta = nMaxRow - nRow3; + nRow2 = nRow1 + nNewDelta; + } + } + else + { + nCol3 = nCol1; + nRow3 = nRow1; + nTab3 = nTab1; + } + + if (nGlobalError == 0) + { + ScQueryParam rParam; + rParam.nRow1 = nRow1; + rParam.nRow2 = nRow2; + + ScQueryEntry& rEntry = rParam.GetEntry(0); + rEntry.bDoQuery = TRUE; + if (!bIsString) + { + rEntry.bQueryByString = FALSE; + rEntry.nVal = fVal; + rEntry.eOp = SC_EQUAL; + } + else + { + rParam.FillInExcelSyntax(rString, 0); + sal_uInt32 nIndex = 0; + rEntry.bQueryByString = + !(pFormatter->IsNumberFormat( + *rEntry.pStr, nIndex, rEntry.nVal)); + if ( rEntry.bQueryByString ) + rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok ); + } + ScAddress aAdr; + aAdr.SetTab( nTab3 ); + rParam.nCol1 = nCol1; + rParam.nCol2 = nCol2; + rEntry.nField = nCol1; + SCsCOL nColDiff = nCol3 - nCol1; + SCsROW nRowDiff = nRow3 - nRow1; + if (pQueryMatrix) + { + // Never case-sensitive. + ScCompareOptions aOptions( pDok, rEntry, rParam.bRegExp); + ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions); + if (nGlobalError || !pResultMatrix) + { + PushIllegalParameter(); + return; + } + + if (pSumExtraMatrix) + { + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + { + for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) + { + if (pResultMatrix->IsValue( nCol, nRow) && + pResultMatrix->GetDouble( nCol, nRow)) + { + SCSIZE nC = nCol + nColDiff; + SCSIZE nR = nRow + nRowDiff; + if (pSumExtraMatrix->IsValue( nC, nR)) + { + fVal = pSumExtraMatrix->GetDouble( nC, nR); + if ( bNull && fVal != 0.0 ) + { + bNull = FALSE; + fMem = fVal; + } + else + fSum += fVal; + } + } + } + } + } + else + { + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + { + for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) + { + if (pResultMatrix->GetDouble( nCol, nRow)) + { + aAdr.SetCol( nCol + nColDiff); + aAdr.SetRow( nRow + nRowDiff); + ScBaseCell* pCell = GetCell( aAdr ); + if ( HasCellValueData(pCell) ) + { + fVal = GetCellValue( aAdr, pCell ); + if ( bNull && fVal != 0.0 ) + { + bNull = FALSE; + fMem = fVal; + } + else + fSum += fVal; + } + } + } + } + } + } + else + { + ScQueryCellIterator aCellIter(pDok, nTab1, rParam, FALSE); + // Increment Entry.nField in iterator when switching to next column. + aCellIter.SetAdvanceQueryParamEntryField( TRUE ); + if ( aCellIter.GetFirst() ) + { + if (pSumExtraMatrix) + { + do + { + SCSIZE nC = aCellIter.GetCol() + nColDiff; + SCSIZE nR = aCellIter.GetRow() + nRowDiff; + if (pSumExtraMatrix->IsValue( nC, nR)) + { + fVal = pSumExtraMatrix->GetDouble( nC, nR); + if ( bNull && fVal != 0.0 ) + { + bNull = FALSE; + fMem = fVal; + } + else + fSum += fVal; + } + } while ( aCellIter.GetNext() ); + } + else + { + do + { + aAdr.SetCol( aCellIter.GetCol() + nColDiff); + aAdr.SetRow( aCellIter.GetRow() + nRowDiff); + ScBaseCell* pCell = GetCell( aAdr ); + if ( HasCellValueData(pCell) ) + { + fVal = GetCellValue( aAdr, pCell ); + if ( bNull && fVal != 0.0 ) + { + bNull = FALSE; + fMem = fVal; + } + else + fSum += fVal; + } + } while ( aCellIter.GetNext() ); + } + } + } + } + else + { + PushIllegalParameter(); + return; + } + } + PushDouble( ::rtl::math::approxAdd( fSum, fMem ) ); + } +} + + +void ScInterpreter::ScLookup() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLookup" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) + return ; + + ScMatrixRef pDataMat = NULL, pResMat = NULL; + SCCOL nCol1 = 0, nCol2 = 0, nResCol1 = 0, nResCol2 = 0; + SCROW nRow1 = 0, nRow2 = 0, nResRow1 = 0, nResRow2 = 0; + SCTAB nTab1 = 0, nResTab = 0; + SCSIZE nLenMajor = 0; // length of major direction + bool bVertical = true; // whether to lookup vertically or horizontally + + // The third parameter, result array, for double, string and single reference. + double fResVal = 0.0; + String aResStr; + ScAddress aResAdr; + StackVar eResArrayType = svUnknown; + + if (nParamCount == 3) + { + eResArrayType = GetStackType(); + switch (eResArrayType) + { + case svDoubleRef: + { + SCTAB nTabJunk; + PopDoubleRef(nResCol1, nResRow1, nResTab, + nResCol2, nResRow2, nTabJunk); + if (nResTab != nTabJunk || + ((nResRow2 - nResRow1) > 0 && (nResCol2 - nResCol1) > 0)) + { + // The result array must be a vector. + PushIllegalParameter(); + return; + } + } + break; + case svMatrix: + { + pResMat = PopMatrix(); + if (!pResMat) + { + PushIllegalParameter(); + return; + } + SCSIZE nC, nR; + pResMat->GetDimensions(nC, nR); + if (nC != 1 && nR != 1) + { + // Result matrix must be a vector. + PushIllegalParameter(); + return; + } + } + break; + case svDouble: + fResVal = GetDouble(); + break; + case svString: + aResStr = GetString(); + break; + case svSingleRef: + PopSingleRef( aResAdr ); + break; + default: + PushIllegalParameter(); + return; + } + } + + // For double, string and single reference. + double fDataVal = 0.0; + String aDataStr; + ScAddress aDataAdr; + bool bValueData = false; + + // Get the data-result range and also determine whether this is vertical + // lookup or horizontal lookup. + + StackVar eDataArrayType = GetStackType(); + switch (eDataArrayType) + { + case svDoubleRef: + { + SCTAB nTabJunk; + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTabJunk); + if (nTab1 != nTabJunk) + { + PushIllegalParameter(); + return; + } + bVertical = (nRow2 - nRow1) >= (nCol2 - nCol1); + nLenMajor = bVertical ? nRow2 - nRow1 + 1 : nCol2 - nCol1 + 1; + } + break; + case svMatrix: + { + pDataMat = PopMatrix(); + if (!pDataMat) + { + PushIllegalParameter(); + return; + } + + SCSIZE nC, nR; + pDataMat->GetDimensions(nC, nR); + bVertical = (nR >= nC); + nLenMajor = bVertical ? nR : nC; + } + break; + case svDouble: + { + fDataVal = GetDouble(); + bValueData = true; + } + break; + case svString: + { + aDataStr = GetString(); + } + break; + case svSingleRef: + { + PopSingleRef( aDataAdr ); + const ScBaseCell* pDataCell = GetCell( aDataAdr ); + if (HasCellEmptyData( pDataCell)) + { + // Empty cells aren't found anywhere, bail out early. + SetError( NOTAVAILABLE); + } + else if (HasCellValueData( pDataCell)) + { + fDataVal = GetCellValue( aDataAdr, pDataCell ); + bValueData = true; + } + else + GetCellString( aDataStr, pDataCell ); + } + break; + default: + SetError( errIllegalParameter); + } + + + if (nGlobalError) + { + PushError( nGlobalError); + return; + } + + // Get the lookup value. + + ScQueryParam aParam; + ScQueryEntry& rEntry = aParam.GetEntry(0); + if ( !FillEntry(rEntry) ) + return; + + if ( eDataArrayType == svDouble || eDataArrayType == svString || + eDataArrayType == svSingleRef ) + { + // Delta position for a single value is always 0. + + // Found if data <= query, but not if query is string and found data is + // numeric or vice versa. This is how Excel does it but doesn't + // document it. + + bool bFound = false; + if ( bValueData ) + { + if ( rEntry.bQueryByString ) + bFound = false; + else + bFound = (fDataVal <= rEntry.nVal); + } + else + { + if ( !rEntry.bQueryByString ) + bFound = false; + else + bFound = (ScGlobal::GetCollator()->compareString( aDataStr, *rEntry.pStr) <= 0); + } + + if (!bFound) + { + PushNA(); + return; + } + + if (pResMat) + { + if (pResMat->IsValue( 0 )) + PushDouble(pResMat->GetDouble( 0 )); + else + PushString(pResMat->GetString( 0 )); + } + else if (nParamCount == 3) + { + switch (eResArrayType) + { + case svDouble: + PushDouble( fResVal ); + break; + case svString: + PushString( aResStr ); + break; + case svDoubleRef: + aResAdr.Set( nResCol1, nResRow1, nResTab); + // fallthru + case svSingleRef: + PushCellResultToken( true, aResAdr, NULL, NULL); + break; + default: + DBG_ERRORFILE( "ScInterpreter::ScLookup: unhandled eResArrayType, single value data"); + } + } + else + { + switch (eDataArrayType) + { + case svDouble: + PushDouble( fDataVal ); + break; + case svString: + PushString( aDataStr ); + break; + case svSingleRef: + PushCellResultToken( true, aDataAdr, NULL, NULL); + break; + default: + DBG_ERRORFILE( "ScInterpreter::ScLookup: unhandled eDataArrayType, single value data"); + } + } + return; + } + + // Now, perform the search to compute the delta position (nDelta). + + if (pDataMat) + { + // Data array is given as a matrix. + rEntry.bDoQuery = true; + rEntry.eOp = SC_LESS_EQUAL; + bool bFound = false; + + SCSIZE nC, nR; + pDataMat->GetDimensions(nC, nR); + + // In case of non-vector matrix, only search the first row or column. + ScMatrixRef pDataMat2; + if (bVertical) + { + ScMatrixRef pTempMat(new ScMatrix(1, nR)); + for (SCSIZE i = 0; i < nR; ++i) + if (pDataMat->IsValue(0, i)) + pTempMat->PutDouble(pDataMat->GetDouble(0, i), 0, i); + else + pTempMat->PutString(pDataMat->GetString(0, i), 0, i); + pDataMat2 = pTempMat; + } + else + { + ScMatrixRef pTempMat(new ScMatrix(nC, 1)); + for (SCSIZE i = 0; i < nC; ++i) + if (pDataMat->IsValue(i, 0)) + pTempMat->PutDouble(pDataMat->GetDouble(i, 0), i, 0); + else + pTempMat->PutString(pDataMat->GetString(i, 0), i, 0); + pDataMat2 = pTempMat; + } + + // binary search for non-equality mode (the source data is + // assumed to be sorted in ascending order). + + SCCOLROW nDelta = -1; + + SCSIZE nFirst = 0, nLast = nLenMajor-1; //, nHitIndex = 0; + for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst) + { + SCSIZE nMid = nFirst + nLen/2; + sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, *pDataMat2, rEntry); + if (nCmp == 0) + { + // exact match. find the last item with the same value. + lcl_GetLastMatch( nMid, *pDataMat2, nLenMajor, false); + nDelta = nMid; + bFound = true; + break; + } + + if (nLen == 1) // first and last items are next to each other. + { + nDelta = nCmp < 0 ? nLast - 1 : nFirst - 1; + // If already the 1st item is greater there's nothing found. + bFound = (nDelta >= 0); + break; + } + + if (nCmp < 0) + nFirst = nMid; + else + nLast = nMid; + } + + if (nDelta == static_cast<SCCOLROW>(nLenMajor-2)) // last item + { + sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, *pDataMat2, rEntry); + if (nCmp <= 0) + { + // either the last item is an exact match or the real + // hit is beyond the last item. + nDelta += 1; + bFound = true; + } + } + else if (nDelta > 0) // valid hit must be 2nd item or higher + { + // non-exact match + bFound = true; + } + + // With 0-9 < A-Z, if query is numeric and data found is string, or + // vice versa, the (yet another undocumented) Excel behavior is to + // return #N/A instead. + + if (bFound) + { + SCCOLROW i = nDelta; + SCSIZE n = pDataMat->GetElementCount(); + if (static_cast<SCSIZE>(i) >= n) + i = static_cast<SCCOLROW>(n); + if (bool(rEntry.bQueryByString) == bool(pDataMat->IsValue(i))) + bFound = false; + } + + if (!bFound) + { + PushNA(); + return; + } + + // Now that we've found the delta, push the result back to the cell. + + if (pResMat) + { + // result array is matrix. + if (static_cast<SCSIZE>(nDelta) >= pResMat->GetElementCount()) + { + PushNA(); + return; + } + if (pResMat->IsValue(nDelta)) + PushDouble(pResMat->GetDouble(nDelta)); + else + PushString(pResMat->GetString(nDelta)); + } + else if (nParamCount == 3) + { + // result array is cell range. + ScAddress aAdr; + aAdr.SetTab(nResTab); + bool bResVertical = (nResRow2 - nResRow1) > 0; + if (bResVertical) + { + SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta); + if (nTempRow > MAXROW) + { + PushDouble(0); + return; + } + aAdr.SetCol(nResCol1); + aAdr.SetRow(nTempRow); + } + else + { + SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta); + if (nTempCol > MAXCOL) + { + PushDouble(0); + return; + } + aAdr.SetCol(nTempCol); + aAdr.SetRow(nResRow1); + } + PushCellResultToken(true, aAdr, NULL, NULL); + } + else + { + // no result array. Use the data array to get the final value from. + if (bVertical) + { + if (pDataMat->IsValue(nC-1, nDelta)) + PushDouble(pDataMat->GetDouble(nC-1, nDelta)); + else + PushString(pDataMat->GetString(nC-1, nDelta)); + } + else + { + if (pDataMat->IsValue(nDelta, nR-1)) + PushDouble(pDataMat->GetDouble(nDelta, nR-1)); + else + PushString(pDataMat->GetString(nDelta, nR-1)); + } + } + + return; + } + + // Perform cell range search. + + aParam.nCol1 = nCol1; + aParam.nRow1 = nRow1; + aParam.nCol2 = bVertical ? nCol1 : nCol2; + aParam.nRow2 = bVertical ? nRow2 : nRow1; + aParam.bByRow = bVertical; + aParam.bMixedComparison = true; + + rEntry.bDoQuery = TRUE; + rEntry.eOp = SC_LESS_EQUAL; + rEntry.nField = nCol1; + if ( rEntry.bQueryByString ) + aParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok ); + + ScQueryCellIterator aCellIter(pDok, nTab1, aParam, FALSE); + SCCOL nC; + SCROW nR; + // Advance Entry.nField in iterator upon switching columns if + // lookup in row. + aCellIter.SetAdvanceQueryParamEntryField(!bVertical); + if ( !aCellIter.FindEqualOrSortedLastInRange(nC, nR) ) + { + PushNA(); + return; + } + + SCCOLROW nDelta = bVertical ? static_cast<SCSIZE>(nR-nRow1) : static_cast<SCSIZE>(nC-nCol1); + + if (pResMat) + { + // Use the matrix result array. + if (pResMat->IsValue(nDelta)) + PushDouble(pResMat->GetDouble(nDelta)); + else + PushString(pResMat->GetString(nDelta)); + } + else if (nParamCount == 3) + { + switch (eResArrayType) + { + case svDoubleRef: + { + // Use the result array vector. Note that the result array is assumed + // to be a vector (i.e. 1-dimensinoal array). + + ScAddress aAdr; + aAdr.SetTab(nResTab); + bool bResVertical = (nResRow2 - nResRow1) > 0; + if (bResVertical) + { + SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta); + if (nTempRow > MAXROW) + { + PushDouble(0); + return; + } + aAdr.SetCol(nResCol1); + aAdr.SetRow(nTempRow); + } + else + { + SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta); + if (nTempCol > MAXCOL) + { + PushDouble(0); + return; + } + aAdr.SetCol(nTempCol); + aAdr.SetRow(nResRow1); + } + PushCellResultToken( true, aAdr, NULL, NULL); + } + break; + case svDouble: + case svString: + case svSingleRef: + { + if (nDelta != 0) + PushNA(); + else + { + switch (eResArrayType) + { + case svDouble: + PushDouble( fResVal ); + break; + case svString: + PushString( aResStr ); + break; + case svSingleRef: + PushCellResultToken( true, aResAdr, NULL, NULL); + break; + default: + ; // nothing + } + } + } + break; + default: + DBG_ERRORFILE( "ScInterpreter::ScLookup: unhandled eResArrayType, range search"); + } + } + else + { + // Regardless of whether or not the result array exists, the last + // array is always used as the "result" array. + + ScAddress aAdr; + aAdr.SetTab(nTab1); + if (bVertical) + { + SCROW nTempRow = static_cast<SCROW>(nRow1 + nDelta); + if (nTempRow > MAXROW) + { + PushDouble(0); + return; + } + aAdr.SetCol(nCol2); + aAdr.SetRow(nTempRow); + } + else + { + SCCOL nTempCol = static_cast<SCCOL>(nCol1 + nDelta); + if (nTempCol > MAXCOL) + { + PushDouble(0); + return; + } + aAdr.SetCol(nTempCol); + aAdr.SetRow(nRow2); + } + PushCellResultToken(true, aAdr, NULL, NULL); + } +} + + +void ScInterpreter::ScHLookup() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScHLookup" ); + CalculateLookup(TRUE); +} +void ScInterpreter::CalculateLookup(BOOL HLookup) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateLookup" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 3, 4 ) ) + { + BOOL bSorted; + if (nParamCount == 4) + bSorted = GetBool(); + else + bSorted = TRUE; + double fIndex = ::rtl::math::approxFloor( GetDouble() ) - 1.0; + ScMatrixRef pMat = NULL; + SCSIZE nC = 0, nR = 0; + SCCOL nCol1 = 0; + SCROW nRow1 = 0; + SCTAB nTab1 = 0; + SCCOL nCol2 = 0; + SCROW nRow2 = 0; + SCTAB nTab2; + if (GetStackType() == svDoubleRef) + { + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + if (nTab1 != nTab2) + { + PushIllegalParameter(); + return; + } + } + else if (GetStackType() == svMatrix) + { + pMat = PopMatrix(); + if (pMat) + pMat->GetDimensions(nC, nR); + else + { + PushIllegalParameter(); + return; + } + } + else + { + PushIllegalParameter(); + return; + } + if ( fIndex < 0.0 || (HLookup ? (pMat ? (fIndex >= nR) : (fIndex+nRow1 > nRow2)) : (pMat ? (fIndex >= nC) : (fIndex+nCol1 > nCol2)) ) ) + { + PushIllegalArgument(); + return; + } + SCROW nZIndex = static_cast<SCROW>(fIndex); + SCCOL nSpIndex = static_cast<SCCOL>(fIndex); + + if (!pMat) + { + nZIndex += nRow1; // Wertzeile + nSpIndex = sal::static_int_cast<SCCOL>( nSpIndex + nCol1 ); // value column + } + + if (nGlobalError == 0) + { + ScQueryParam rParam; + rParam.nCol1 = nCol1; + rParam.nRow1 = nRow1; + if ( HLookup ) + { + rParam.nCol2 = nCol2; + rParam.nRow2 = nRow1; // nur in der ersten Zeile suchen + rParam.bByRow = FALSE; + } // if ( HLookup ) + else + { + rParam.nCol2 = nCol1; // nur in der ersten Spalte suchen + rParam.nRow2 = nRow2; + rParam.nTab = nTab1; + } + rParam.bMixedComparison = TRUE; + + ScQueryEntry& rEntry = rParam.GetEntry(0); + rEntry.bDoQuery = TRUE; + if ( bSorted ) + rEntry.eOp = SC_LESS_EQUAL; + if ( !FillEntry(rEntry) ) + return; + if ( rEntry.bQueryByString ) + rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok ); + if (pMat) + { + SCSIZE nMatCount = HLookup ? nC : nR; + SCSIZE nDelta = SCSIZE_MAX; + if (rEntry.bQueryByString) + { + //!!!!!!! + //! TODO: enable regex on matrix strings + //!!!!!!! + String aParamStr = *rEntry.pStr; + if ( bSorted ) + { + static CollatorWrapper* pCollator = ScGlobal::GetCollator(); + for (SCSIZE i = 0; i < nMatCount; i++) + { + if (HLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i)) + { + sal_Int32 nRes = + pCollator->compareString( HLookup ? pMat->GetString(i,0) : pMat->GetString(0,i), aParamStr); + if (nRes <= 0) + nDelta = i; + else if (i>0) // #i2168# ignore first mismatch + i = nMatCount+1; + } + else + nDelta = i; + } + } + else + { + for (SCSIZE i = 0; i < nMatCount; i++) + { + if (HLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i)) + { + if ( ScGlobal::GetpTransliteration()->isEqual( + HLookup ? pMat->GetString(i,0) : pMat->GetString(0,i), aParamStr ) ) + { + nDelta = i; + i = nMatCount + 1; + } + } + } + } + } + else + { + if ( bSorted ) + { + // #i2168# ignore strings + for (SCSIZE i = 0; i < nMatCount; i++) + { + if (!(HLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i))) + { + if ((HLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) <= rEntry.nVal) + nDelta = i; + else + i = nMatCount+1; + } + } + } + else + { + for (SCSIZE i = 0; i < nMatCount; i++) + { + if (!(HLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i))) + { + if ((HLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) == rEntry.nVal) + { + nDelta = i; + i = nMatCount + 1; + } + } + } + } + } + if ( nDelta != SCSIZE_MAX ) + { + SCSIZE nX = static_cast<SCSIZE>(nSpIndex); + SCSIZE nY = nDelta; + if ( HLookup ) + { + nX = nDelta; + nY = static_cast<SCSIZE>(nZIndex); + } + if ( pMat->IsString( nX, nY) ) + PushString(pMat->GetString( nX,nY)); + else + PushDouble(pMat->GetDouble( nX,nY)); + } + else + PushNA(); + } + else + { + rEntry.nField = nCol1; + BOOL bFound = FALSE; + SCCOL nCol = 0; + SCROW nRow = 0; + if ( bSorted ) + rEntry.eOp = SC_LESS_EQUAL; + if ( HLookup ) + { + ScQueryCellIterator aCellIter(pDok, nTab1, rParam, FALSE); + // advance Entry.nField in Iterator upon switching columns + aCellIter.SetAdvanceQueryParamEntryField( TRUE ); + if ( bSorted ) + { + SCROW nRow1_temp; + bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow1_temp ); + } + else if ( aCellIter.GetFirst() ) + { + bFound = TRUE; + nCol = aCellIter.GetCol(); + } + nRow = nZIndex; + } // if ( HLookup ) + else + { + ScAddress aResultPos( nCol1, nRow1, nTab1); + bFound = LookupQueryWithCache( aResultPos, rParam); + nRow = aResultPos.Row(); + nCol = nSpIndex; + } + if ( bFound ) + { + ScAddress aAdr( nCol, nRow, nTab1 ); + PushCellResultToken( true, aAdr, NULL, NULL); + } + else + PushNA(); + } + } + else + PushIllegalParameter(); + } +} + +bool ScInterpreter::FillEntry(ScQueryEntry& rEntry) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::FillEntry" ); + switch ( GetStackType() ) + { + case svDouble: + { + rEntry.bQueryByString = FALSE; + rEntry.nVal = GetDouble(); + } + break; + case svString: + { + const String sStr = GetString(); + rEntry.bQueryByString = TRUE; + *rEntry.pStr = sStr; + } + break; + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + PushInt(0); + return false; + } + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + rEntry.bQueryByString = FALSE; + rEntry.nVal = GetCellValue( aAdr, pCell ); + } + else + { + if ( GetCellType( pCell ) == CELLTYPE_NOTE ) + { + rEntry.bQueryByString = FALSE; + rEntry.nVal = 0.0; + } + else + { + String sStr; + GetCellString(sStr, pCell); + rEntry.bQueryByString = TRUE; + *rEntry.pStr = sStr; + } + } + } + break; + case svMatrix : + { + const ScMatValType nType = GetDoubleOrStringFromMatrix(rEntry.nVal, *rEntry.pStr); + rEntry.bQueryByString = ScMatrix::IsNonValueType( nType); + } + break; + default: + { + PushIllegalParameter(); + return false; + } + } // switch ( GetStackType() ) + return true; +} +void ScInterpreter::ScVLookup() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScVLookup" ); + CalculateLookup(FALSE); +} + +void ScInterpreter::ScSubTotal() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSubTotal" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCountMin( nParamCount, 2 ) ) + { + // We must fish the 1st parameter deep from the stack! And push it on top. + const FormulaToken* p = pStack[ sp - nParamCount ]; + PushTempToken( *p ); + int nFunc = (int) ::rtl::math::approxFloor( GetDouble() ); + if( nFunc < 1 || nFunc > 11 ) + PushIllegalArgument(); // simulate return on stack, not SetError(...) + else + { + cPar = nParamCount - 1; + glSubTotal = TRUE; + switch( nFunc ) + { + case SUBTOTAL_FUNC_AVE : ScAverage(); break; + case SUBTOTAL_FUNC_CNT : ScCount(); break; + case SUBTOTAL_FUNC_CNT2 : ScCount2(); break; + case SUBTOTAL_FUNC_MAX : ScMax(); break; + case SUBTOTAL_FUNC_MIN : ScMin(); break; + case SUBTOTAL_FUNC_PROD : ScProduct(); break; + case SUBTOTAL_FUNC_STD : ScStDev(); break; + case SUBTOTAL_FUNC_STDP : ScStDevP(); break; + case SUBTOTAL_FUNC_SUM : ScSum(); break; + case SUBTOTAL_FUNC_VAR : ScVar(); break; + case SUBTOTAL_FUNC_VARP : ScVarP(); break; + default : PushIllegalArgument(); break; + } + glSubTotal = FALSE; + } + // Get rid of the 1st (fished) parameter. + double nVal = GetDouble(); + Pop(); + PushDouble( nVal ); + } +} + +ScDBQueryParamBase* ScInterpreter::GetDBParams( BOOL& rMissingField ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetDBParams" ); + BOOL bAllowMissingField = FALSE; + if ( rMissingField ) + { + bAllowMissingField = TRUE; + rMissingField = FALSE; + } + if ( GetByte() == 3 ) + { + // First, get the query criteria range. + ::std::auto_ptr<ScDBRangeBase> pQueryRef( PopDoubleRef() ); + if (!pQueryRef.get()) + return NULL; + + BOOL bByVal = TRUE; + double nVal = 0.0; + String aStr; + ScRange aMissingRange; + BOOL bRangeFake = FALSE; + switch (GetStackType()) + { + case svDouble : + nVal = ::rtl::math::approxFloor( GetDouble() ); + if ( bAllowMissingField && nVal == 0.0 ) + rMissingField = TRUE; // fake missing parameter + break; + case svString : + bByVal = FALSE; + aStr = GetString(); + break; + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + nVal = GetCellValue( aAdr, pCell ); + else + { + bByVal = FALSE; + GetCellString(aStr, pCell); + } + } + break; + case svDoubleRef : + if ( bAllowMissingField ) + { // fake missing parameter for old SO compatibility + bRangeFake = TRUE; + PopDoubleRef( aMissingRange ); + } + else + { + PopError(); + SetError( errIllegalParameter ); + } + break; + case svMissing : + PopError(); + if ( bAllowMissingField ) + rMissingField = TRUE; + else + SetError( errIllegalParameter ); + break; + default: + PopError(); + SetError( errIllegalParameter ); + } + + auto_ptr<ScDBRangeBase> pDBRef( PopDoubleRef() ); + + if (nGlobalError || !pDBRef.get()) + return NULL; + + if ( bRangeFake ) + { + // range parameter must match entire database range + if (pDBRef->isRangeEqual(aMissingRange)) + rMissingField = TRUE; + else + SetError( errIllegalParameter ); + } + + if (nGlobalError) + return NULL; + + SCCOL nField = pDBRef->getFirstFieldColumn(); + if (rMissingField) + ; // special case + else if (bByVal) + nField = pDBRef->findFieldColumn(static_cast<SCCOL>(nVal)); + else + { + sal_uInt16 nErr = 0; + nField = pDBRef->findFieldColumn(aStr, &nErr); + SetError(nErr); + } + + if (!ValidCol(nField)) + return NULL; + + auto_ptr<ScDBQueryParamBase> pParam( pDBRef->createQueryParam(pQueryRef.get()) ); + + if (pParam.get()) + { + // An allowed missing field parameter sets the result field + // to any of the query fields, just to be able to return + // some cell from the iterator. + if ( rMissingField ) + nField = static_cast<SCCOL>(pParam->GetEntry(0).nField); + pParam->mnField = nField; + + SCSIZE nCount = pParam->GetEntryCount(); + for ( SCSIZE i=0; i < nCount; i++ ) + { + ScQueryEntry& rEntry = pParam->GetEntry(i); + if ( rEntry.bDoQuery ) + { + sal_uInt32 nIndex = 0; + rEntry.bQueryByString = !pFormatter->IsNumberFormat( + *rEntry.pStr, nIndex, rEntry.nVal ); + if ( rEntry.bQueryByString && !pParam->bRegExp ) + pParam->bRegExp = MayBeRegExp( *rEntry.pStr, pDok ); + } + else + break; // for + } + return pParam.release(); + } + } + return false; +} + + +void ScInterpreter::DBIterator( ScIterFunc eFunc ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::DBIterator" ); + double nErg = 0.0; + double fMem = 0.0; + BOOL bNull = TRUE; + ULONG nCount = 0; + BOOL bMissingField = FALSE; + auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); + if (pQueryParam.get()) + { + ScDBQueryDataIterator aValIter(pDok, pQueryParam.release()); + ScDBQueryDataIterator::Value aValue; + if ( aValIter.GetFirst(aValue) && !aValue.mnError ) + { + switch( eFunc ) + { + case ifPRODUCT: nErg = 1; break; + case ifMAX: nErg = -MAXDOUBLE; break; + case ifMIN: nErg = MAXDOUBLE; break; + default: ; // nothing + } + do + { + nCount++; + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: + if ( bNull && aValue.mfValue != 0.0 ) + { + bNull = FALSE; + fMem = aValue.mfValue; + } + else + nErg += aValue.mfValue; + break; + case ifSUMSQ: nErg += aValue.mfValue * aValue.mfValue; break; + case ifPRODUCT: nErg *= aValue.mfValue; break; + case ifMAX: if( aValue.mfValue > nErg ) nErg = aValue.mfValue; break; + case ifMIN: if( aValue.mfValue < nErg ) nErg = aValue.mfValue; break; + default: ; // nothing + } + } + while ( aValIter.GetNext(aValue) && !aValue.mnError ); + } + SetError(aValue.mnError); + } + else + SetError( errIllegalParameter); + switch( eFunc ) + { + case ifCOUNT: nErg = nCount; break; + case ifSUM: nErg = ::rtl::math::approxAdd( nErg, fMem ); break; + case ifAVERAGE: nErg = ::rtl::math::approxAdd( nErg, fMem ) / nCount; break; + default: ; // nothing + } + PushDouble( nErg ); +} + + +void ScInterpreter::ScDBSum() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBSum" ); + DBIterator( ifSUM ); +} + + +void ScInterpreter::ScDBCount() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBCount" ); + BOOL bMissingField = TRUE; + auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); + if (pQueryParam.get()) + { + ULONG nCount = 0; + if ( bMissingField && pQueryParam->GetType() == ScDBQueryParamBase::INTERNAL ) + { // count all matching records + // TODO: currently the QueryIterators only return cell pointers of + // existing cells, so if a query matches an empty cell there's + // nothing returned, and therefor not counted! + // Since this has ever been the case and this code here only came + // into existance to fix #i6899 and it never worked before we'll + // have to live with it until we reimplement the iterators to also + // return empty cells, which would mean to adapt all callers of + // iterators. + ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pQueryParam.get()); + SCTAB nTab = p->nTab; + // ScQueryCellIterator doesn't make use of ScDBQueryParamBase::mnField, + // so the source range has to be restricted, like before the introduction + // of ScDBQueryParamBase. + p->nCol1 = p->nCol2 = p->mnField; + ScQueryCellIterator aCellIter( pDok, nTab, *p); + if ( aCellIter.GetFirst() ) + { + do + { + nCount++; + } while ( aCellIter.GetNext() ); + } + } + else + { // count only matching records with a value in the "result" field + ScDBQueryDataIterator aValIter( pDok, pQueryParam.release()); + ScDBQueryDataIterator::Value aValue; + if ( aValIter.GetFirst(aValue) && !aValue.mnError ) + { + do + { + nCount++; + } + while ( aValIter.GetNext(aValue) && !aValue.mnError ); + } + SetError(aValue.mnError); + } + PushDouble( nCount ); + } + else + PushIllegalParameter(); +} + + +void ScInterpreter::ScDBCount2() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBCount2" ); + BOOL bMissingField = TRUE; + auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); + if (pQueryParam.get()) + { + ULONG nCount = 0; + pQueryParam->mbSkipString = false; + ScDBQueryDataIterator aValIter( pDok, pQueryParam.release()); + ScDBQueryDataIterator::Value aValue; + if ( aValIter.GetFirst(aValue) && !aValue.mnError ) + { + do + { + nCount++; + } + while ( aValIter.GetNext(aValue) && !aValue.mnError ); + } + SetError(aValue.mnError); + PushDouble( nCount ); + } + else + PushIllegalParameter(); +} + + +void ScInterpreter::ScDBAverage() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBAverage" ); + DBIterator( ifAVERAGE ); +} + + +void ScInterpreter::ScDBMax() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBMax" ); + DBIterator( ifMAX ); +} + + +void ScInterpreter::ScDBMin() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBMin" ); + DBIterator( ifMIN ); +} + + +void ScInterpreter::ScDBProduct() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBProduct" ); + DBIterator( ifPRODUCT ); +} + + +void ScInterpreter::GetDBStVarParams( double& rVal, double& rValCount ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetDBStVarParams" ); + std::vector<double> values; + double vSum = 0.0; + double vMean = 0.0; + + rValCount = 0.0; + double fSum = 0.0; + BOOL bMissingField = FALSE; + auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); + if (pQueryParam.get()) + { + ScDBQueryDataIterator aValIter(pDok, pQueryParam.release()); + ScDBQueryDataIterator::Value aValue; + if (aValIter.GetFirst(aValue) && !aValue.mnError) + { + do + { + rValCount++; + values.push_back(aValue.mfValue); + fSum += aValue.mfValue; + } + while ((aValue.mnError == 0) && aValIter.GetNext(aValue)); + } + SetError(aValue.mnError); + } + else + SetError( errIllegalParameter); + + vMean = fSum / values.size(); + + for (size_t i = 0; i < values.size(); i++) + vSum += (values[i] - vMean) * (values[i] - vMean); + + rVal = vSum; +} + + +void ScInterpreter::ScDBStdDev() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBStdDev" ); + double fVal, fCount; + GetDBStVarParams( fVal, fCount ); + PushDouble( sqrt(fVal/(fCount-1))); +} + + +void ScInterpreter::ScDBStdDevP() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBStdDevP" ); + double fVal, fCount; + GetDBStVarParams( fVal, fCount ); + PushDouble( sqrt(fVal/fCount)); +} + + +void ScInterpreter::ScDBVar() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBVar" ); + double fVal, fCount; + GetDBStVarParams( fVal, fCount ); + PushDouble(fVal/(fCount-1)); +} + + +void ScInterpreter::ScDBVarP() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBVarP" ); + double fVal, fCount; + GetDBStVarParams( fVal, fCount ); + PushDouble(fVal/fCount); +} + + +ScTokenArray* lcl_CreateExternalRefTokenArray( const ScAddress& rPos, ScDocument* pDoc, + const ScAddress::ExternalInfo& rExtInfo, const ScRefAddress& rRefAd1, + const ScRefAddress* pRefAd2 ) +{ + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + size_t nSheets = 1; + const String* pRealTab = pRefMgr->getRealTableName( rExtInfo.mnFileId, rExtInfo.maTabName); + ScTokenArray* pTokenArray = new ScTokenArray; + if (pRefAd2) + { + ScComplexRefData aRef; + aRef.InitRangeRel( ScRange( rRefAd1.GetAddress(), pRefAd2->GetAddress()), rPos); + aRef.Ref1.SetColRel( rRefAd1.IsRelCol()); + aRef.Ref1.SetRowRel( rRefAd1.IsRelRow()); + aRef.Ref1.SetTabRel( rRefAd1.IsRelTab()); + aRef.Ref1.SetFlag3D( true); + aRef.Ref2.SetColRel( pRefAd2->IsRelCol()); + aRef.Ref2.SetRowRel( pRefAd2->IsRelRow()); + aRef.Ref2.SetTabRel( pRefAd2->IsRelTab()); + nSheets = aRef.Ref2.nTab - aRef.Ref1.nTab + 1; + aRef.Ref2.SetFlag3D( nSheets > 1 ); + pTokenArray->AddExternalDoubleReference( rExtInfo.mnFileId, + (pRealTab ? *pRealTab : rExtInfo.maTabName), aRef); + } + else + { + ScSingleRefData aRef; + aRef.InitAddressRel( rRefAd1.GetAddress(), rPos); + aRef.SetColRel( rRefAd1.IsRelCol()); + aRef.SetRowRel( rRefAd1.IsRelRow()); + aRef.SetTabRel( rRefAd1.IsRelTab()); + aRef.SetFlag3D( true); + pTokenArray->AddExternalSingleReference( rExtInfo.mnFileId, + (pRealTab ? *pRealTab : rExtInfo.maTabName), aRef); + } + // The indirect usage of the external table can't be detected during the + // store-to-file cycle, mark it as permanently referenced so it gets stored + // even if not directly referenced anywhere. + pRefMgr->setCacheTableReferencedPermanently( rExtInfo.mnFileId, + rExtInfo.maTabName, nSheets); + ScCompiler aComp( pDoc, rPos, *pTokenArray); + aComp.CompileTokenArray(); + return pTokenArray; +} + + +void ScInterpreter::ScIndirect() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIndirect" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + bool bTryXlA1 = true; // whether to try XL_A1 style as well. + FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; + if (nParamCount == 2 && 0.0 == ::rtl::math::approxFloor( GetDouble())) + { + eConv = FormulaGrammar::CONV_XL_R1C1; + bTryXlA1 = false; + } + const ScAddress::Details aDetails( eConv, aPos ); + const ScAddress::Details aDetailsXlA1( FormulaGrammar::CONV_XL_A1, aPos ); + SCTAB nTab = aPos.Tab(); + String sRefStr( GetString() ); + ScRefAddress aRefAd, aRefAd2; + ScAddress::ExternalInfo aExtInfo; + if ( ConvertDoubleRef( pDok, sRefStr, nTab, aRefAd, aRefAd2, aDetails, &aExtInfo) || + (bTryXlA1 && ConvertDoubleRef( pDok, sRefStr, nTab, aRefAd, + aRefAd2, aDetailsXlA1, &aExtInfo))) + { + if (aExtInfo.mbExternal) + { + /* TODO: future versions should implement a proper subroutine + * token. This procedure here is a minimally invasive fix for + * #i101645# in OOo3.1.1 */ + // Push a subroutine on the instruction code stack that + // resolves the external reference as the next instruction. + aCode.Push( lcl_CreateExternalRefTokenArray( aPos, pDok, + aExtInfo, aRefAd, &aRefAd2)); + // Signal subroutine call to interpreter. + PushTempToken( new FormulaUnknownToken( ocCall)); + } + else + PushDoubleRef( aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(), + aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab() ); + } + else if ( ConvertSingleRef ( pDok, sRefStr, nTab, aRefAd, aDetails, &aExtInfo) || + (bTryXlA1 && ConvertSingleRef ( pDok, sRefStr, nTab, aRefAd, + aDetailsXlA1, &aExtInfo))) + { + if (aExtInfo.mbExternal) + { + /* TODO: future versions should implement a proper subroutine + * token. This procedure here is a minimally invasive fix for + * #i101645# in OOo3.1.1 */ + // Push a subroutine on the instruction code stack that + // resolves the external reference as the next instruction. + aCode.Push( lcl_CreateExternalRefTokenArray( aPos, pDok, + aExtInfo, aRefAd, NULL)); + // Signal subroutine call to interpreter. + PushTempToken( new FormulaUnknownToken( ocCall)); + } + else + PushSingleRef( aRefAd.Col(), aRefAd.Row(), aRefAd.Tab() ); + } + else + { + do + { + ScRangeName* pNames = pDok->GetRangeName(); + if (!pNames) + break; + + USHORT nPos = 0; + if (!pNames->SearchName( sRefStr, nPos)) + break; + + ScRangeData* rData = (*pNames)[nPos]; + if (!rData) + break; + + // We need this in order to obtain a good range. + rData->ValidateTabRefs(); + + ScRange aRange; +#if 0 + // This is some really odd Excel behavior and renders named + // ranges containing relative references totally useless. + if (!rData->IsReference(aRange, ScAddress( aPos.Tab(), 0, 0))) + break; +#else + // This is the usual way to treat named ranges containing + // relative references. + if (!rData->IsReference( aRange, aPos)) + break; +#endif + + if (aRange.aStart == aRange.aEnd) + PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aStart.Tab()); + else + PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aStart.Tab(), aRange.aEnd.Col(), + aRange.aEnd.Row(), aRange.aEnd.Tab()); + + // success! + return; + } + while (false); + + PushIllegalArgument(); + } + } +} + + +void ScInterpreter::ScAddressFunc() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAddressFunc" ); + String sTabStr; + + BYTE nParamCount = GetByte(); + if( !MustHaveParamCount( nParamCount, 2, 5 ) ) + return; + + if( nParamCount >= 5 ) + sTabStr = GetString(); + + FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; // default + if( nParamCount >= 4 && 0.0 == ::rtl::math::approxFloor( GetDoubleWithDefault( 1.0))) + eConv = FormulaGrammar::CONV_XL_R1C1; + + USHORT nFlags = SCA_COL_ABSOLUTE | SCA_ROW_ABSOLUTE; // default + if( nParamCount >= 3 ) + { + USHORT n = (USHORT) ::rtl::math::approxFloor( GetDoubleWithDefault( 1.0)); + switch ( n ) + { + default : + PushNoValue(); + return; + + case 5: + case 1 : break; // default + case 6: + case 2 : nFlags = SCA_ROW_ABSOLUTE; break; + case 7: + case 3 : nFlags = SCA_COL_ABSOLUTE; break; + case 8: + case 4 : nFlags = 0; break; // both relative + } + } + nFlags |= SCA_VALID | SCA_VALID_ROW | SCA_VALID_COL; + + SCCOL nCol = (SCCOL) ::rtl::math::approxFloor(GetDouble()); + SCROW nRow = (SCROW) ::rtl::math::approxFloor(GetDouble()); + if( eConv == FormulaGrammar::CONV_XL_R1C1 ) + { + // YUCK! The XL interface actually treats rel R1C1 refs differently + // than A1 + if( !(nFlags & SCA_COL_ABSOLUTE) ) + nCol += aPos.Col() + 1; + if( !(nFlags & SCA_ROW_ABSOLUTE) ) + nRow += aPos.Row() + 1; + } + + --nCol; + --nRow; + if(!ValidCol( nCol) || !ValidRow( nRow)) + { + PushIllegalArgument(); + return; + } + + String aRefStr; + const ScAddress::Details aDetails( eConv, aPos ); + const ScAddress aAdr( nCol, nRow, 0); + aAdr.Format( aRefStr, nFlags, pDok, aDetails ); + + if( nParamCount >= 5 ) + { + ScCompiler::CheckTabQuotes( sTabStr, eConv); + sTabStr += static_cast<sal_Unicode>(eConv == FormulaGrammar::CONV_XL_R1C1 ? '!' : '.'); + sTabStr += aRefStr; + PushString( sTabStr ); + } + else + PushString( aRefStr ); +} + + +void ScInterpreter::ScOffset() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScOffset" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 3, 5 ) ) + { + long nColNew = -1, nRowNew = -1, nColPlus, nRowPlus; + if (nParamCount == 5) + nColNew = (long) ::rtl::math::approxFloor(GetDouble()); + if (nParamCount >= 4) + nRowNew = (long) ::rtl::math::approxFloor(GetDoubleWithDefault( -1.0 )); + nColPlus = (long) ::rtl::math::approxFloor(GetDouble()); + nRowPlus = (long) ::rtl::math::approxFloor(GetDouble()); + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + if (nColNew == 0 || nRowNew == 0) + { + PushIllegalArgument(); + return; + } + if (GetStackType() == svSingleRef) + { + PopSingleRef(nCol1, nRow1, nTab1); + if (nParamCount == 3 || (nColNew < 0 && nRowNew < 0)) + { + nCol1 = (SCCOL)((long) nCol1 + nColPlus); + nRow1 = (SCROW)((long) nRow1 + nRowPlus); + if (!ValidCol(nCol1) || !ValidRow(nRow1)) + PushIllegalArgument(); + else + PushSingleRef(nCol1, nRow1, nTab1); + } + else + { + if (nColNew < 0) + nColNew = 1; + if (nRowNew < 0) + nRowNew = 1; + nCol1 = (SCCOL)((long)nCol1+nColPlus); // ! nCol1 wird veraendert! + nRow1 = (SCROW)((long)nRow1+nRowPlus); + nCol2 = (SCCOL)((long)nCol1+nColNew-1); + nRow2 = (SCROW)((long)nRow1+nRowNew-1); + if (!ValidCol(nCol1) || !ValidRow(nRow1) || + !ValidCol(nCol2) || !ValidRow(nRow2)) + PushIllegalArgument(); + else + PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1); + } + } + else if (GetStackType() == svDoubleRef) + { + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + if (nColNew < 0) + nColNew = nCol2 - nCol1 + 1; + if (nRowNew < 0) + nRowNew = nRow2 - nRow1 + 1; + nCol1 = (SCCOL)((long)nCol1+nColPlus); + nRow1 = (SCROW)((long)nRow1+nRowPlus); + nCol2 = (SCCOL)((long)nCol1+nColNew-1); + nRow2 = (SCROW)((long)nRow1+nRowNew-1); + if (!ValidCol(nCol1) || !ValidRow(nRow1) || + !ValidCol(nCol2) || !ValidRow(nRow2) || nTab1 != nTab2) + PushIllegalArgument(); + else + PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1); + } + else + PushIllegalParameter(); + } +} + + +void ScInterpreter::ScIndex() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIndex" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 4 ) ) + { + long nArea; + size_t nAreaCount; + SCCOL nCol; + SCROW nRow; + if (nParamCount == 4) + nArea = (long) ::rtl::math::approxFloor(GetDouble()); + else + nArea = 1; + if (nParamCount >= 3) + nCol = (SCCOL) ::rtl::math::approxFloor(GetDouble()); + else + nCol = 0; + if (nParamCount >= 2) + nRow = (SCROW) ::rtl::math::approxFloor(GetDouble()); + else + nRow = 0; + if (GetStackType() == svRefList) + nAreaCount = (sp ? static_cast<ScToken*>(pStack[sp-1])->GetRefList()->size() : 0); + else + nAreaCount = 1; // one reference or array or whatever + if (nAreaCount == 0 || (size_t)nArea > nAreaCount) + { + PushError( errNoRef); + return; + } + else if (nArea < 1 || nCol < 0 || nRow < 0) + { + PushIllegalArgument(); + return; + } + switch (GetStackType()) + { + case svMatrix: + { + if (nArea != 1) + SetError(errIllegalArgument); + USHORT nOldSp = sp; + ScMatrixRef pMat = GetMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + // Access one element of a vector independent of col/row + // orientation? + bool bVector = ((nCol == 0 || nRow == 0) && (nC == 1 || nR == 1)); + SCSIZE nElement = ::std::max( static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow)); + if (nC == 0 || nR == 0 || + (!bVector && (static_cast<SCSIZE>(nCol) > nC || + static_cast<SCSIZE>(nRow) > nR)) || + (bVector && nElement > nC * nR)) + PushIllegalArgument(); + else if (nCol == 0 && nRow == 0) + sp = nOldSp; + else if (bVector) + { + --nElement; + if (pMat->IsString( nElement)) + PushString( pMat->GetString( nElement)); + else + PushDouble( pMat->GetDouble( nElement)); + } + else if (nCol == 0) + { + ScMatrixRef pResMat = GetNewMat(nC, 1); + if (pResMat) + { + SCSIZE nRowMinus1 = static_cast<SCSIZE>(nRow - 1); + for (SCSIZE i = 0; i < nC; i++) + if (!pMat->IsString(i, nRowMinus1)) + pResMat->PutDouble(pMat->GetDouble(i, + nRowMinus1), i, 0); + else + pResMat->PutString(pMat->GetString(i, + nRowMinus1), i, 0); + PushMatrix(pResMat); + } + else + PushIllegalArgument(); + } + else if (nRow == 0) + { + ScMatrixRef pResMat = GetNewMat(1, nR); + if (pResMat) + { + SCSIZE nColMinus1 = static_cast<SCSIZE>(nCol - 1); + for (SCSIZE i = 0; i < nR; i++) + if (!pMat->IsString(nColMinus1, i)) + pResMat->PutDouble(pMat->GetDouble(nColMinus1, + i), i); + else + pResMat->PutString(pMat->GetString(nColMinus1, + i), i); + PushMatrix(pResMat); + } + else + PushIllegalArgument(); + } + else + { + if (!pMat->IsString( static_cast<SCSIZE>(nCol-1), + static_cast<SCSIZE>(nRow-1))) + PushDouble( pMat->GetDouble( + static_cast<SCSIZE>(nCol-1), + static_cast<SCSIZE>(nRow-1))); + else + PushString( pMat->GetString( + static_cast<SCSIZE>(nCol-1), + static_cast<SCSIZE>(nRow-1))); + } + } + } + break; + case svSingleRef: + { + SCCOL nCol1 = 0; + SCROW nRow1 = 0; + SCTAB nTab1 = 0; + PopSingleRef( nCol1, nRow1, nTab1); + if (nCol > 1 || nRow > 1) + PushIllegalArgument(); + else + PushSingleRef( nCol1, nRow1, nTab1); + } + break; + case svDoubleRef: + case svRefList: + { + SCCOL nCol1 = 0; + SCROW nRow1 = 0; + SCTAB nTab1 = 0; + SCCOL nCol2 = 0; + SCROW nRow2 = 0; + SCTAB nTab2 = 0; + BOOL bRowArray = FALSE; + if (GetStackType() == svRefList) + { + FormulaTokenRef xRef = PopToken(); + if (nGlobalError || !xRef) + { + PushIllegalParameter(); + return; + } + ScRange aRange( ScAddress::UNINITIALIZED); + DoubleRefToRange( (*(static_cast<ScToken*>(xRef.get())->GetRefList()))[nArea-1], aRange); + aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + if ( nParamCount == 2 && nRow1 == nRow2 ) + bRowArray = TRUE; + } + else + { + PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + if ( nParamCount == 2 && nRow1 == nRow2 ) + bRowArray = TRUE; + } + if ( nTab1 != nTab2 || + (nCol > 0 && nCol1+nCol-1 > nCol2) || + (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) || + ( nRow > nCol2 - nCol1 + 1 && bRowArray )) + PushIllegalArgument(); + else if (nCol == 0 && nRow == 0) + { + if ( nCol1 == nCol2 && nRow1 == nRow2 ) + PushSingleRef( nCol1, nRow1, nTab1 ); + else + PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab1 ); + } + else if (nRow == 0) + { + if ( nRow1 == nRow2 ) + PushSingleRef( nCol1+nCol-1, nRow1, nTab1 ); + else + PushDoubleRef( nCol1+nCol-1, nRow1, nTab1, + nCol1+nCol-1, nRow2, nTab1 ); + } + else if (nCol == 0) + { + if ( nCol1 == nCol2 ) + PushSingleRef( nCol1, nRow1+nRow-1, nTab1 ); + else if ( bRowArray ) + { + nCol =(SCCOL) nRow; + nRow = 1; + PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1); + } + else + PushDoubleRef( nCol1, nRow1+nRow-1, nTab1, + nCol2, nRow1+nRow-1, nTab1); + } + else + PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1); + } + break; + default: + PushIllegalParameter(); + } + } +} + + +void ScInterpreter::ScMultiArea() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMultiArea" ); + // Legacy support, convert to RefList + BYTE nParamCount = GetByte(); + if (MustHaveParamCountMin( nParamCount, 1)) + { + while (!nGlobalError && nParamCount-- > 1) + { + ScUnionFunc(); + } + } +} + + +void ScInterpreter::ScAreas() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAreas" ); + BYTE nParamCount = GetByte(); + if (MustHaveParamCount( nParamCount, 1)) + { + size_t nCount = 0; + switch (GetStackType()) + { + case svSingleRef: + { + FormulaTokenRef xT = PopToken(); + ValidateRef( static_cast<ScToken*>(xT.get())->GetSingleRef()); + ++nCount; + } + break; + case svDoubleRef: + { + FormulaTokenRef xT = PopToken(); + ValidateRef( static_cast<ScToken*>(xT.get())->GetDoubleRef()); + ++nCount; + } + break; + case svRefList: + { + FormulaTokenRef xT = PopToken(); + ValidateRef( *(static_cast<ScToken*>(xT.get())->GetRefList())); + nCount += static_cast<ScToken*>(xT.get())->GetRefList()->size(); + } + break; + default: + SetError( errIllegalParameter); + } + PushDouble( double(nCount)); + } +} + + +void ScInterpreter::ScCurrency() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCurrency" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + String aStr; + double fDec; + if (nParamCount == 2) + { + fDec = ::rtl::math::approxFloor(GetDouble()); + if (fDec < -15.0 || fDec > 15.0) + { + PushIllegalArgument(); + return; + } + } + else + fDec = 2.0; + double fVal = GetDouble(); + double fFac; + if ( fDec != 0.0 ) + fFac = pow( (double)10, fDec ); + else + fFac = 1.0; + if (fVal < 0.0) + fVal = ceil(fVal*fFac-0.5)/fFac; + else + fVal = floor(fVal*fFac+0.5)/fFac; + Color* pColor = NULL; + if ( fDec < 0.0 ) + fDec = 0.0; + ULONG nIndex = pFormatter->GetStandardFormat( + NUMBERFORMAT_CURRENCY, + ScGlobal::eLnge); + if ( (USHORT) fDec != pFormatter->GetFormatPrecision( nIndex ) ) + { + String sFormatString; + pFormatter->GenerateFormat(sFormatString, + nIndex, + ScGlobal::eLnge, + TRUE, // mit Tausenderpunkt + FALSE, // nicht rot + (USHORT) fDec,// Nachkommastellen + 1); // 1 Vorkommanull + if (!pFormatter->GetPreviewString(sFormatString, + fVal, + aStr, + &pColor, + ScGlobal::eLnge)) + SetError(errIllegalArgument); + } + else + { + pFormatter->GetOutputString(fVal, nIndex, aStr, &pColor); + } + PushString(aStr); + } +} + + +void ScInterpreter::ScReplace() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScReplace" ); + if ( MustHaveParamCount( GetByte(), 4 ) ) + { + String aNewStr( GetString() ); + double fCount = ::rtl::math::approxFloor( GetDouble()); + double fPos = ::rtl::math::approxFloor( GetDouble()); + String aOldStr( GetString() ); + if (fPos < 1.0 || fPos > static_cast<double>(STRING_MAXLEN) + || fCount < 0.0 || fCount > static_cast<double>(STRING_MAXLEN)) + PushIllegalArgument(); + else + { + xub_StrLen nCount = static_cast<xub_StrLen>(fCount); + xub_StrLen nPos = static_cast<xub_StrLen>(fPos); + xub_StrLen nLen = aOldStr.Len(); + if (nPos > nLen + 1) + nPos = nLen + 1; + if (nCount > nLen - nPos + 1) + nCount = nLen - nPos + 1; + aOldStr.Erase( nPos-1, nCount ); + if ( CheckStringResultLen( aOldStr, aNewStr ) ) + aOldStr.Insert( aNewStr, nPos-1 ); + PushString( aOldStr ); + } + } +} + + +void ScInterpreter::ScFixed() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFixed" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 3 ) ) + { + String aStr; + double fDec; + BOOL bThousand; + if (nParamCount == 3) + bThousand = !GetBool(); // Param TRUE: keine Tausenderpunkte + else + bThousand = TRUE; + if (nParamCount >= 2) + { + fDec = ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 )); + if (fDec < -15.0 || fDec > 15.0) + { + PushIllegalArgument(); + return; + } + } + else + fDec = 2.0; + double fVal = GetDouble(); + double fFac; + if ( fDec != 0.0 ) + fFac = pow( (double)10, fDec ); + else + fFac = 1.0; + if (fVal < 0.0) + fVal = ceil(fVal*fFac-0.5)/fFac; + else + fVal = floor(fVal*fFac+0.5)/fFac; + Color* pColor = NULL; + String sFormatString; + if (fDec < 0.0) + fDec = 0.0; + ULONG nIndex = pFormatter->GetStandardFormat( + NUMBERFORMAT_NUMBER, + ScGlobal::eLnge); + pFormatter->GenerateFormat(sFormatString, + nIndex, + ScGlobal::eLnge, + bThousand, // mit Tausenderpunkt + FALSE, // nicht rot + (USHORT) fDec,// Nachkommastellen + 1); // 1 Vorkommanull + if (!pFormatter->GetPreviewString(sFormatString, + fVal, + aStr, + &pColor, + ScGlobal::eLnge)) + PushIllegalArgument(); + else + PushString(aStr); + } +} + + +void ScInterpreter::ScFind() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFind" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + double fAnz; + if (nParamCount == 3) + fAnz = GetDouble(); + else + fAnz = 1.0; + String sStr = GetString(); + if( fAnz < 1.0 || fAnz > (double) sStr.Len() ) + PushNoValue(); + else + { + xub_StrLen nPos = sStr.Search( GetString(), (xub_StrLen) fAnz - 1 ); + if (nPos == STRING_NOTFOUND) + PushNoValue(); + else + PushDouble((double)(nPos + 1)); + } + } +} + + +void ScInterpreter::ScExact() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScExact" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + String s1( GetString() ); + String s2( GetString() ); + PushInt( s1 == s2 ); + } +} + + +void ScInterpreter::ScLeft() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLeft" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + xub_StrLen n; + if (nParamCount == 2) + { + double nVal = ::rtl::math::approxFloor(GetDouble()); + if ( nVal < 0.0 || nVal > STRING_MAXLEN ) + { + PushIllegalArgument(); + return ; + } + else + n = (xub_StrLen) nVal; + } + else + n = 1; + String aStr( GetString() ); + aStr.Erase( n ); + PushString( aStr ); + } +} + + +void ScInterpreter::ScRight() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRight" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + xub_StrLen n; + if (nParamCount == 2) + { + double nVal = ::rtl::math::approxFloor(GetDouble()); + if ( nVal < 0.0 || nVal > STRING_MAXLEN ) + { + PushIllegalArgument(); + return ; + } + else + n = (xub_StrLen) nVal; + } + else + n = 1; + String aStr( GetString() ); + if( n < aStr.Len() ) + aStr.Erase( 0, aStr.Len() - n ); + PushString( aStr ); + } +} + + +void ScInterpreter::ScSearch() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSearch" ); + double fAnz; + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + if (nParamCount == 3) + { + fAnz = ::rtl::math::approxFloor(GetDouble()); + if (fAnz > double(STRING_MAXLEN)) + { + PushIllegalArgument(); + return; + } + } + else + fAnz = 1.0; + String sStr = GetString(); + String SearchStr = GetString(); + xub_StrLen nPos = (xub_StrLen) fAnz - 1; + xub_StrLen nEndPos = sStr.Len(); + if( nPos >= nEndPos ) + PushNoValue(); + else + { + utl::SearchParam::SearchType eSearchType = + (MayBeRegExp( SearchStr, pDok ) ? + utl::SearchParam::SRCH_REGEXP : utl::SearchParam::SRCH_NORMAL); + utl::SearchParam sPar(SearchStr, eSearchType, FALSE, FALSE, FALSE); + utl::TextSearch sT( sPar, *ScGlobal::pCharClass ); + int nBool = sT.SearchFrwrd(sStr, &nPos, &nEndPos); + if (!nBool) + PushNoValue(); + else + PushDouble((double)(nPos) + 1); + } + } +} + + +void ScInterpreter::ScMid() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMid" ); + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double fAnz = ::rtl::math::approxFloor(GetDouble()); + double fAnfang = ::rtl::math::approxFloor(GetDouble()); + const String& rStr = GetString(); + if (fAnfang < 1.0 || fAnz < 0.0 || fAnfang > double(STRING_MAXLEN) || fAnz > double(STRING_MAXLEN)) + PushIllegalArgument(); + else + PushString(rStr.Copy( (xub_StrLen) fAnfang - 1, (xub_StrLen) fAnz )); + } +} + + +void ScInterpreter::ScText() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScText" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + String sFormatString = GetString(); + String aStr; + bool bString = false; + double fVal = 0.0; + switch (GetStackType()) + { + case svError: + PopError(); + break; + case svDouble: + fVal = PopDouble(); + break; + default: + { + FormulaTokenRef xTok( PopToken()); + if (!nGlobalError) + { + PushTempToken( xTok); + // Temporarily override the ConvertStringToValue() + // error for GetCellValue() / GetCellValueOrZero() + USHORT nSErr = mnStringNoValueError; + mnStringNoValueError = errNotNumericString; + fVal = GetDouble(); + mnStringNoValueError = nSErr; + if (nGlobalError == errNotNumericString) + { + // Not numeric. + nGlobalError = 0; + PushTempToken( xTok); + aStr = GetString(); + bString = true; + } + } + } + } + if (nGlobalError) + PushError( nGlobalError); + else + { + String aResult; + Color* pColor = NULL; + LanguageType eCellLang; + const ScPatternAttr* pPattern = pDok->GetPattern( + aPos.Col(), aPos.Row(), aPos.Tab() ); + if ( pPattern ) + eCellLang = ((const SvxLanguageItem&) + pPattern->GetItem( ATTR_LANGUAGE_FORMAT )).GetValue(); + else + eCellLang = ScGlobal::eLnge; + if (bString) + { + if (!pFormatter->GetPreviewString( sFormatString, aStr, + aResult, &pColor, eCellLang)) + PushIllegalArgument(); + else + PushString( aResult); + } + else + { + if (!pFormatter->GetPreviewStringGuess( sFormatString, fVal, + aResult, &pColor, eCellLang)) + PushIllegalArgument(); + else + PushString( aResult); + } + } + } +} + + +void ScInterpreter::ScSubstitute() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSubstitute" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 3, 4 ) ) + { + xub_StrLen nAnz; + if (nParamCount == 4) + { + double fAnz = ::rtl::math::approxFloor(GetDouble()); + if( fAnz < 1 || fAnz > STRING_MAXLEN ) + { + PushIllegalArgument(); + return; + } + else + nAnz = (xub_StrLen) fAnz; + } + else + nAnz = 0; + String sNewStr = GetString(); + String sOldStr = GetString(); + String sStr = GetString(); + xub_StrLen nPos = 0; + xub_StrLen nCount = 0; + xub_StrLen nNewLen = sNewStr.Len(); + xub_StrLen nOldLen = sOldStr.Len(); + while( TRUE ) + { + nPos = sStr.Search( sOldStr, nPos ); + if (nPos != STRING_NOTFOUND) + { + nCount++; + if( !nAnz || nCount == nAnz ) + { + sStr.Erase(nPos,nOldLen); + if ( CheckStringResultLen( sStr, sNewStr ) ) + { + sStr.Insert(sNewStr,nPos); + nPos = sal::static_int_cast<xub_StrLen>( nPos + nNewLen ); + } + else + break; + } + else + nPos++; + } + else + break; + } + PushString( sStr ); + } +} + + +void ScInterpreter::ScRept() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRept" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + double fAnz = ::rtl::math::approxFloor(GetDouble()); + String aStr( GetString() ); + if ( fAnz < 0.0 ) + PushIllegalArgument(); + else if ( fAnz * aStr.Len() > STRING_MAXLEN ) + { + PushError( errStringOverflow ); + } + else if ( fAnz == 0.0 ) + PushString( EMPTY_STRING ); + else + { + xub_StrLen n = (xub_StrLen) fAnz; + const xub_StrLen nLen = aStr.Len(); + String aRes; + const sal_Unicode* const pSrc = aStr.GetBuffer(); + sal_Unicode* pDst = aRes.AllocBuffer( n * nLen ); + while( n-- ) + { + memcpy( pDst, pSrc, nLen * sizeof(sal_Unicode) ); + pDst += nLen; + } + PushString( aRes ); + } + } +} + + +void ScInterpreter::ScConcat() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScConcat" ); + BYTE nParamCount = GetByte(); + String aRes; + while( nParamCount-- > 0) + { + const String& rStr = GetString(); + aRes.Insert( rStr, 0 ); + } + PushString( aRes ); +} + + +void ScInterpreter::ScErrorType() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScErrorType" ); + USHORT nErr; + USHORT nOldError = nGlobalError; + nGlobalError = 0; + switch ( GetStackType() ) + { + case svRefList : + { + FormulaTokenRef x = PopToken(); + if (nGlobalError) + nErr = nGlobalError; + else + { + const ScRefList* pRefList = static_cast<ScToken*>(x.get())->GetRefList(); + size_t n = pRefList->size(); + if (!n) + nErr = errNoRef; + else if (n > 1) + nErr = errNoValue; + else + { + ScRange aRange; + DoubleRefToRange( (*pRefList)[0], aRange); + if (nGlobalError) + nErr = nGlobalError; + else + { + ScAddress aAdr; + if ( DoubleRefToPosSingleRef( aRange, aAdr ) ) + nErr = pDok->GetErrCode( aAdr ); + else + nErr = nGlobalError; + } + } + } + } + break; + case svDoubleRef : + { + ScRange aRange; + PopDoubleRef( aRange ); + if ( nGlobalError ) + nErr = nGlobalError; + else + { + ScAddress aAdr; + if ( DoubleRefToPosSingleRef( aRange, aAdr ) ) + nErr = pDok->GetErrCode( aAdr ); + else + nErr = nGlobalError; + } + } + break; + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( nGlobalError ) + nErr = nGlobalError; + else + nErr = pDok->GetErrCode( aAdr ); + } + break; + default: + PopError(); + nErr = nGlobalError; + } + if ( nErr ) + { + nGlobalError = 0; + PushDouble( nErr ); + } + else + { + nGlobalError = nOldError; + PushNA(); + } +} + + +BOOL ScInterpreter::MayBeRegExp( const String& rStr, const ScDocument* pDoc ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::MayBeRegExp" ); + if ( pDoc && !pDoc->GetDocOptions().IsFormulaRegexEnabled() ) + return FALSE; + if ( !rStr.Len() || (rStr.Len() == 1 && rStr.GetChar(0) != '.') ) + return FALSE; // einzelnes Metazeichen kann keine RegExp sein + static const sal_Unicode cre[] = { '.','*','+','?','[',']','^','$','\\','<','>','(',')','|', 0 }; + const sal_Unicode* p1 = rStr.GetBuffer(); + sal_Unicode c1; + while ( ( c1 = *p1++ ) != 0 ) + { + const sal_Unicode* p2 = cre; + while ( *p2 ) + { + if ( c1 == *p2++ ) + return TRUE; + } + } + return FALSE; +} + +static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument * pDoc, + const ScQueryParam & rParam, const ScQueryEntry & rEntry ) +{ + bool bFound = false; + ScQueryCellIterator aCellIter( pDoc, rParam.nTab, rParam, FALSE); + if (rEntry.eOp != SC_EQUAL) + { + // range lookup <= or >= + SCCOL nCol; + SCROW nRow; + bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow); + if (bFound) + { + o_rResultPos.SetCol( nCol); + o_rResultPos.SetRow( nRow); + } + } + else if (aCellIter.GetFirst()) + { + // EQUAL + bFound = true; + o_rResultPos.SetCol( aCellIter.GetCol()); + o_rResultPos.SetRow( aCellIter.GetRow()); + } + return bFound; +} + +#define erDEBUG_LOOKUPCACHE 0 +#if erDEBUG_LOOKUPCACHE +#include <cstdio> +using ::std::fprintf; +using ::std::fflush; +static struct LookupCacheDebugCounter +{ + unsigned long nMiss; + unsigned long nHit; + LookupCacheDebugCounter() : nMiss(0), nHit(0) {} + ~LookupCacheDebugCounter() + { + fprintf( stderr, "\nmiss: %lu, hit: %lu, total: %lu, hit/miss: %lu, hit/total %lu%\n", + nMiss, nHit, nHit+nMiss, (nMiss>0 ? nHit/nMiss : 0), + ((nHit+nMiss)>0 ? (100*nHit)/(nHit+nMiss) : 0)); + fflush( stderr); + } +} aLookupCacheDebugCounter; +#endif + +bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos, + const ScQueryParam & rParam ) const +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::LookupQueryWithCache" ); + bool bFound = false; + const ScQueryEntry& rEntry = rParam.GetEntry(0); + bool bColumnsMatch = (rParam.nCol1 == rEntry.nField); + DBG_ASSERT( bColumnsMatch, "ScInterpreter::LookupQueryWithCache: columns don't match"); + if (!bColumnsMatch) + bFound = lcl_LookupQuery( o_rResultPos, pDok, rParam, rEntry); + else + { + ScRange aLookupRange( rParam.nCol1, rParam.nRow1, rParam.nTab, + rParam.nCol2, rParam.nRow2, rParam.nTab); + ScLookupCache& rCache = pDok->GetLookupCache( aLookupRange); + ScLookupCache::QueryCriteria aCriteria( rEntry); + ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos, + aCriteria, aPos); + switch (eCacheResult) + { + case ScLookupCache::NOT_CACHED : + case ScLookupCache::CRITERIA_DIFFERENT : +#if erDEBUG_LOOKUPCACHE + ++aLookupCacheDebugCounter.nMiss; +#if erDEBUG_LOOKUPCACHE > 1 + fprintf( stderr, "miss %d,%d,%d\n", (int)aPos.Col(), (int)aPos.Row(), (int)aPos.Tab()); +#endif +#endif + bFound = lcl_LookupQuery( o_rResultPos, pDok, rParam, rEntry); + if (eCacheResult == ScLookupCache::NOT_CACHED) + rCache.insert( o_rResultPos, aCriteria, aPos, bFound); + break; + case ScLookupCache::FOUND : +#if erDEBUG_LOOKUPCACHE + ++aLookupCacheDebugCounter.nHit; +#if erDEBUG_LOOKUPCACHE > 1 + fprintf( stderr, "hit %d,%d,%d\n", (int)aPos.Col(), (int)aPos.Row(), (int)aPos.Tab()); +#endif +#endif + bFound = true; + break; + case ScLookupCache::NOT_AVAILABLE : + ; // nothing, bFound remains FALSE + break; + } + } + return bFound; +} diff --git a/sc/source/core/tool/interpr2.cxx b/sc/source/core/tool/interpr2.cxx new file mode 100644 index 000000000000..404eaf763a51 --- /dev/null +++ b/sc/source/core/tool/interpr2.cxx @@ -0,0 +1,3024 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <sfx2/linkmgr.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/objsh.hxx> +#include <svl/stritem.hxx> +#include <svl/zforlist.hxx> +#include <rtl/logfile.hxx> + +#include "interpre.hxx" +#include "attrib.hxx" +#include "sc.hrc" +#include "ddelink.hxx" +#include "scmatrix.hxx" +#include "compiler.hxx" +#include "cell.hxx" +#include "document.hxx" +#include "dociter.hxx" +#include "docoptio.hxx" +#include "unitconv.hxx" +#include "globstr.hrc" +#include "hints.hxx" +#include "dpobject.hxx" +#include "postit.hxx" + +#include <string.h> +#include <math.h> + +using namespace formula; +// STATIC DATA ----------------------------------------------------------- + +#define D_TIMEFACTOR 86400.0 +#define SCdEpsilon 1.0E-7 + +//----------------------------------------------------------------------------- +// Datum und Zeit +//----------------------------------------------------------------------------- + +double ScInterpreter::GetDateSerial( INT16 nYear, INT16 nMonth, INT16 nDay, bool bStrict ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetDateSerial" ); + if ( nYear < 100 && !bStrict ) + nYear = pFormatter->ExpandTwoDigitYear( nYear ); + // Do not use a default Date ctor here because it asks system time with a + // performance penalty. + INT16 nY, nM, nD; + if (bStrict) + nY = nYear, nM = nMonth, nD = nDay; + else + { + if (nMonth > 0) + { + nY = nYear + (nMonth-1) / 12; + nM = ((nMonth-1) % 12) + 1; + } + else + { + nY = nYear + (nMonth-12) / 12; + nM = 12 - (-nMonth) % 12; + } + nD = 1; + } + Date aDate( nD, nM, nY); + if (!bStrict) + aDate += nDay - 1; + if (aDate.IsValid()) + return (double) (aDate - *(pFormatter->GetNullDate())); + else + { + SetError(errNoValue); + return 0; + } +} + +//----------------------------------------------------------------------------- +// Funktionen +//----------------------------------------------------------------------------- + +void ScInterpreter::ScGetActDate() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetActDate" ); + nFuncFmtType = NUMBERFORMAT_DATE; + Date aActDate; + long nDiff = aActDate - *(pFormatter->GetNullDate()); + PushDouble((double) nDiff); +} + +void ScInterpreter::ScGetActTime() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetActTime" ); + nFuncFmtType = NUMBERFORMAT_DATETIME; + Date aActDate; + long nDiff = aActDate - *(pFormatter->GetNullDate()); + Time aActTime; + double nTime = ((double)aActTime.Get100Sec() / 100 + + (double)(aActTime.GetSec() + + (aActTime.GetMin() * 60) + + (aActTime.GetHour() * 3600))) / D_TIMEFACTOR; + PushDouble( (double) nDiff + nTime ); +} + +void ScInterpreter::ScGetYear() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetYear" ); + Date aDate = *(pFormatter->GetNullDate()); + aDate += (long) ::rtl::math::approxFloor(GetDouble()); + PushDouble( (double) aDate.GetYear() ); +} + +void ScInterpreter::ScGetMonth() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetMonth" ); + Date aDate = *(pFormatter->GetNullDate()); + aDate += (long) ::rtl::math::approxFloor(GetDouble()); + PushDouble( (double) aDate.GetMonth() ); +} + +void ScInterpreter::ScGetDay() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetDay" ); + Date aDate = *(pFormatter->GetNullDate()); + aDate += (long)::rtl::math::approxFloor(GetDouble()); + PushDouble((double) aDate.GetDay()); +} + +void ScInterpreter::ScGetMin() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetMin" ); + double fTime = GetDouble(); + fTime -= ::rtl::math::approxFloor(fTime); // Datumsanteil weg + long nVal = (long)::rtl::math::approxFloor(fTime*D_TIMEFACTOR+0.5) % 3600; + PushDouble( (double) (nVal/60) ); +} + +void ScInterpreter::ScGetSec() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetSec" ); + double fTime = GetDouble(); + fTime -= ::rtl::math::approxFloor(fTime); // Datumsanteil weg + long nVal = (long)::rtl::math::approxFloor(fTime*D_TIMEFACTOR+0.5) % 60; + PushDouble( (double) nVal ); +} + +void ScInterpreter::ScGetHour() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetHour" ); + double fTime = GetDouble(); + fTime -= ::rtl::math::approxFloor(fTime); // Datumsanteil weg + long nVal = (long)::rtl::math::approxFloor(fTime*D_TIMEFACTOR+0.5) / 3600; + PushDouble((double) nVal); +} + +void ScInterpreter::ScGetDateValue() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetDateValue" ); + String aInputString = GetString(); + sal_uInt32 nFIndex = 0; // damit default Land/Spr. + double fVal; + if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal)) + { + short eType = pFormatter->GetType(nFIndex); + if (eType == NUMBERFORMAT_DATE || eType == NUMBERFORMAT_DATETIME) + PushDouble(::rtl::math::approxFloor(fVal)); + else + PushIllegalArgument(); + } + else + PushIllegalArgument(); +} + +void ScInterpreter::ScGetDayOfWeek() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetDayOfWeek" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + short nFlag; + if (nParamCount == 2) + nFlag = (short) ::rtl::math::approxFloor(GetDouble()); + else + nFlag = 1; + + Date aDate = *(pFormatter->GetNullDate()); + aDate += (long)::rtl::math::approxFloor(GetDouble()); + int nVal = (int) aDate.GetDayOfWeek(); + if (nFlag == 1) + { + if (nVal == 6) + nVal = 1; + else + nVal += 2; + } + else if (nFlag == 2) + nVal += 1; + PushInt( nVal ); + } +} + +void ScInterpreter::ScGetWeekOfYear() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetWeekOfYear" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + short nFlag = (short) ::rtl::math::approxFloor(GetDouble()); + + Date aDate = *(pFormatter->GetNullDate()); + aDate += (long)::rtl::math::approxFloor(GetDouble()); + PushInt( (int) aDate.GetWeekOfYear( nFlag == 1 ? SUNDAY : MONDAY )); + } +} + +void ScInterpreter::ScEasterSunday() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScEasterSunday" ); + nFuncFmtType = NUMBERFORMAT_DATE; + if ( MustHaveParamCount( GetByte(), 1 ) ) + { + INT16 nDay, nMonth, nYear; + nYear = (INT16) ::rtl::math::approxFloor( GetDouble() ); + if ( nYear < 100 ) + nYear = pFormatter->ExpandTwoDigitYear( nYear ); + // don't worry, be happy :) + int B,C,D,E,F,G,H,I,K,L,M,N,O; + N = nYear % 19; + B = int(nYear / 100); + C = nYear % 100; + D = int(B / 4); + E = B % 4; + F = int((B + 8) / 25); + G = int((B - F + 1) / 3); + H = (19 * N + B - D - G + 15) % 30; + I = int(C / 4); + K = C % 4; + L = (32 + 2 * E + 2 * I - H - K) % 7; + M = int((N + 11 * H + 22 * L) / 451); + O = H + L - 7 * M + 114; + nDay = sal::static_int_cast<INT16>( O % 31 + 1 ); + nMonth = sal::static_int_cast<INT16>( int(O / 31) ); + PushDouble( GetDateSerial( nYear, nMonth, nDay, true ) ); + } +} + +void ScInterpreter::ScGetDate() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetDate" ); + nFuncFmtType = NUMBERFORMAT_DATE; + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + INT16 nDay = (INT16) ::rtl::math::approxFloor(GetDouble()); + INT16 nMonth = (INT16) ::rtl::math::approxFloor(GetDouble()); + INT16 nYear = (INT16) ::rtl::math::approxFloor(GetDouble()); + if (nYear < 0) + PushIllegalArgument(); + else + { + PushDouble(GetDateSerial(nYear, nMonth, nDay, false)); + } + } +} + +void ScInterpreter::ScGetTime() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetTime" ); + nFuncFmtType = NUMBERFORMAT_TIME; + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double nSec = GetDouble(); + double nMin = GetDouble(); + double nHour = GetDouble(); + PushDouble( ( (nHour * 3600) + (nMin * 60) + nSec ) / D_TIMEFACTOR ); + } +} + +void ScInterpreter::ScGetDiffDate() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetDiffDate" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + double nDate2 = GetDouble(); + double nDate1 = GetDouble(); + PushDouble(nDate1 - nDate2); + } +} + +void ScInterpreter::ScGetDiffDate360() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetDiffDate360" ); + /* Implementation follows + * http://www.bondmarkets.com/eCommerce/SMD_Fields_030802.pdf + * Appendix B: Day-Count Bases, there are 7 different ways to calculate the + * 30-days count. That document also claims that Excel implements the "PSA + * 30" or "NASD 30" method (funny enough they also state that Excel is the + * only tool that does so). + * + * Note that the definiton given in + * http://msdn.microsoft.com/library/en-us/office97/html/SEB7C.asp + * is _not_ the way how it is actually calculated by Excel (that would not + * even match any of the 7 methods mentioned above) and would result in the + * following test cases producing wrong results according to that appendix B: + * + * 28-Feb-95 31-Aug-95 181 instead of 180 + * 29-Feb-96 31-Aug-96 181 instead of 180 + * 30-Jan-96 31-Mar-96 61 instead of 60 + * 31-Jan-96 31-Mar-96 61 instead of 60 + * + * Still, there is a difference between OOoCalc and Excel: + * In Excel: + * 02-Feb-99 31-Mar-00 results in 419 + * 31-Mar-00 02-Feb-99 results in -418 + * In Calc the result is 419 respectively -419. I consider the -418 a bug in Excel. + */ + + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + BOOL bFlag; + if (nParamCount == 3) + bFlag = GetBool(); + else + bFlag = FALSE; + double nDate2 = GetDouble(); + double nDate1 = GetDouble(); + double fSign; + if (nGlobalError) + PushError( nGlobalError); + else + { + // #i84934# only for non-US European algorithm swap dates. Else + // follow Excel's meaningless extrapolation for "interoperability". + if (bFlag && (nDate2 < nDate1)) + { + fSign = nDate1; + nDate1 = nDate2; + nDate2 = fSign; + fSign = -1.0; + } + else + fSign = 1.0; + Date aDate1 = *(pFormatter->GetNullDate()); + aDate1 += (long) ::rtl::math::approxFloor(nDate1); + Date aDate2 = *(pFormatter->GetNullDate()); + aDate2 += (long) ::rtl::math::approxFloor(nDate2); + if (aDate1.GetDay() == 31) + aDate1 -= (ULONG) 1; + else if (!bFlag) + { + if (aDate1.GetMonth() == 2) + { + switch ( aDate1.GetDay() ) + { + case 28 : + if ( !aDate1.IsLeapYear() ) + aDate1.SetDay(30); + break; + case 29 : + aDate1.SetDay(30); + break; + } + } + } + if (aDate2.GetDay() == 31) + { + if (!bFlag ) + { + if (aDate1.GetDay() == 30) + aDate2 -= (ULONG) 1; + } + else + aDate2.SetDay(30); + } + PushDouble( fSign * (double) + ( (double) aDate2.GetDay() + (double) aDate2.GetMonth() * 30.0 + + (double) aDate2.GetYear() * 360.0 + - (double) aDate1.GetDay() - (double) aDate1.GetMonth() * 30.0 + - (double)aDate1.GetYear() * 360.0) ); + } + } +} + +void ScInterpreter::ScGetTimeValue() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetTimeValue" ); + String aInputString = GetString(); + sal_uInt32 nFIndex = 0; // damit default Land/Spr. + double fVal; + if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal)) + { + short eType = pFormatter->GetType(nFIndex); + if (eType == NUMBERFORMAT_TIME || eType == NUMBERFORMAT_DATETIME) + { + double fDateVal = rtl::math::approxFloor(fVal); + double fTimeVal = fVal - fDateVal; + PushDouble(fTimeVal); + } + else + PushIllegalArgument(); + } + else + PushIllegalArgument(); +} + +void ScInterpreter::ScPlusMinus() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPlusMinus" ); + double nVal = GetDouble(); + short n = 0; + if (nVal < 0.0) + n = -1; + else if (nVal > 0.0) + n = 1; + PushInt( n ); +} + +void ScInterpreter::ScAbs() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAbs" ); + PushDouble(fabs(GetDouble())); +} + +void ScInterpreter::ScInt() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScInt" ); + PushDouble(::rtl::math::approxFloor(GetDouble())); +} + + +void ScInterpreter::RoundNumber( rtl_math_RoundingMode eMode ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::RoundNumber" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + double fVal = 0.0; + if (nParamCount == 1) + fVal = ::rtl::math::round( GetDouble(), 0, eMode ); + else + { + INT32 nDec = (INT32) ::rtl::math::approxFloor(GetDouble()); + if( nDec < -20 || nDec > 20 ) + PushIllegalArgument(); + else + fVal = ::rtl::math::round( GetDouble(), (short)nDec, eMode ); + } + PushDouble(fVal); + } +} + +void ScInterpreter::ScRound() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRound" ); + RoundNumber( rtl_math_RoundingMode_Corrected ); +} + +void ScInterpreter::ScRoundDown() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRoundDown" ); + RoundNumber( rtl_math_RoundingMode_Down ); +} + +void ScInterpreter::ScRoundUp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRoundUp" ); + RoundNumber( rtl_math_RoundingMode_Up ); +} + +void ScInterpreter::ScCeil() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCeil" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + BOOL bAbs = ( nParamCount == 3 ? GetBool() : FALSE ); + double fDec = GetDouble(); + double fVal = GetDouble(); + if ( fDec == 0.0 ) + PushInt(0); + else if (fVal*fDec < 0.0) + PushIllegalArgument(); + else + { + if ( !bAbs && fVal < 0.0 ) + PushDouble(::rtl::math::approxFloor(fVal/fDec) * fDec); + else + PushDouble(::rtl::math::approxCeil(fVal/fDec) * fDec); + } + } +} + +void ScInterpreter::ScFloor() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFloor" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + BOOL bAbs = ( nParamCount == 3 ? GetBool() : FALSE ); + double fDec = GetDouble(); + double fVal = GetDouble(); + if ( fDec == 0.0 ) + PushInt(0); + else if (fVal*fDec < 0.0) + PushIllegalArgument(); + else + { + if ( !bAbs && fVal < 0.0 ) + PushDouble(::rtl::math::approxCeil(fVal/fDec) * fDec); + else + PushDouble(::rtl::math::approxFloor(fVal/fDec) * fDec); + } + } +} + +void ScInterpreter::ScEven() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScEven" ); + double fVal = GetDouble(); + if (fVal < 0.0) + PushDouble(::rtl::math::approxFloor(fVal/2.0) * 2.0); + else + PushDouble(::rtl::math::approxCeil(fVal/2.0) * 2.0); +} + +void ScInterpreter::ScOdd() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScOdd" ); + double fVal = GetDouble(); + if (fVal >= 0.0) + { + fVal = ::rtl::math::approxCeil(fVal); + if (fmod(fVal, 2.0) == 0.0) + fVal += 1.0; + } + else + { + fVal = ::rtl::math::approxFloor(fVal); + if (fmod(fVal, 2.0) == 0.0) + fVal -= 1.0; + } + PushDouble(fVal); +} + +void ScInterpreter::ScArcTan2() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcTan2" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + double nVal2 = GetDouble(); + double nVal1 = GetDouble(); + PushDouble(atan2(nVal2, nVal1)); + } +} + +void ScInterpreter::ScLog() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLog" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + double nBase; + if (nParamCount == 2) + nBase = GetDouble(); + else + nBase = 10.0; + double nVal = GetDouble(); + if (nVal > 0.0 && nBase > 0.0 && nBase != 1.0) + PushDouble(log(nVal) / log(nBase)); + else + PushIllegalArgument(); + } +} + +void ScInterpreter::ScLn() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLn" ); + double fVal = GetDouble(); + if (fVal > 0.0) + PushDouble(log(fVal)); + else + PushIllegalArgument(); +} + +void ScInterpreter::ScLog10() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLog10" ); + double fVal = GetDouble(); + if (fVal > 0.0) + PushDouble(log10(fVal)); + else + PushIllegalArgument(); +} + +void ScInterpreter::ScNPV() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNPV" ); + nFuncFmtType = NUMBERFORMAT_CURRENCY; + short nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 31 ) ) + { + double nVal = 0.0; + // Wir drehen den Stack um!! + FormulaToken* pTemp[ 31 ]; + for( short i = 0; i < nParamCount; i++ ) + pTemp[ i ] = pStack[ sp - i - 1 ]; + memcpy( &pStack[ sp - nParamCount ], pTemp, nParamCount * sizeof( FormulaToken* ) ); + if (nGlobalError == 0) + { + double nCount = 1.0; + double nZins = GetDouble(); + --nParamCount; + size_t nRefInList = 0; + ScRange aRange; + while (nParamCount-- > 0) + { + switch (GetStackType()) + { + case svDouble : + { + nVal += (GetDouble() / pow(1.0 + nZins, (double)nCount)); + nCount++; + } + break; + case svSingleRef : + { + nVal += (GetDouble() / pow(1.0 + nZins, (double)nCount)); + nCount++; + } + break; + case svDoubleRef : + case svRefList : + { + USHORT nErr = 0; + double nCellVal; + PopDoubleRef( aRange, nParamCount, nRefInList); + ScValueIterator aValIter(pDok, aRange, glSubTotal); + if (aValIter.GetFirst(nCellVal, nErr)) + { + nVal += (nCellVal / pow(1.0 + nZins, (double)nCount)); + nCount++; + while ((nErr == 0) && aValIter.GetNext(nCellVal, nErr)) + { + nVal += (nCellVal / pow(1.0 + nZins, (double)nCount)); + nCount++; + } + SetError(nErr); + } + } + break; + default : SetError(errIllegalParameter); break; + } + } + } + PushDouble(nVal); + } +} + +void ScInterpreter::ScIRR() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIRR" ); + double fSchaetzwert; + nFuncFmtType = NUMBERFORMAT_PERCENT; + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) + return; + if (nParamCount == 2) + fSchaetzwert = GetDouble(); + else + fSchaetzwert = 0.1; + USHORT sPos = sp; // Stack-Position merken + double fEps = 1.0; + double x, xNeu, fWert, fZaehler, fNenner, nCount; + if (fSchaetzwert == -1.0) + x = 0.1; // default gegen Nulldivisionen + else + x = fSchaetzwert; // Startwert + switch (GetStackType()) + { + case svDoubleRef : + break; + default: + { + PushIllegalParameter(); + return; + } + } + const USHORT nIterationsMax = 20; + USHORT nItCount = 0; + ScRange aRange; + while (fEps > SCdEpsilon && nItCount < nIterationsMax) + { // Newton-Verfahren: + sp = sPos; // Stack zuruecksetzen + nCount = 0.0; + fZaehler = 0.0; + fNenner = 0.0; + USHORT nErr = 0; + PopDoubleRef( aRange ); + ScValueIterator aValIter(pDok, aRange, glSubTotal); + if (aValIter.GetFirst(fWert, nErr)) + { + fZaehler += fWert / pow(1.0+x,(double)nCount); + fNenner += -nCount * fWert / pow(1.0+x,nCount+1.0); + nCount++; + while ((nErr == 0) && aValIter.GetNext(fWert, nErr)) + { + fZaehler += fWert / pow(1.0+x,(double)nCount); + fNenner += -nCount * fWert / pow(1.0+x,nCount+1.0); + nCount++; + } + SetError(nErr); + } + xNeu = x - fZaehler / fNenner; // x(i+1) = x(i)-f(x(i))/f'(x(i)) + nItCount++; + fEps = fabs(xNeu - x); + x = xNeu; + } + if (fSchaetzwert == 0.0 && fabs(x) < SCdEpsilon) + x = 0.0; // auf Null normieren + if (fEps < SCdEpsilon) + PushDouble(x); + else + PushError( errNoConvergence); +} + +void ScInterpreter::ScMIRR() +{ // range_of_values ; rate_invest ; rate_reinvest + nFuncFmtType = NUMBERFORMAT_PERCENT; + if( MustHaveParamCount( GetByte(), 3 ) ) + { + double fRate1_reinvest = GetDouble() + 1; + double fNPV_reinvest = 0.0; + double fPow_reinvest = 1.0; + + double fRate1_invest = GetDouble() + 1; + double fNPV_invest = 0.0; + double fPow_invest = 1.0; + + ScRange aRange; + PopDoubleRef( aRange ); + + if( nGlobalError ) + PushError( nGlobalError); + else + { + ScValueIterator aValIter( pDok, aRange, glSubTotal ); + double fCellValue; + ULONG nCount = 0; + USHORT nIterError = 0; + + BOOL bLoop = aValIter.GetFirst( fCellValue, nIterError ); + while( bLoop ) + { + if( fCellValue > 0.0 ) // reinvestments + fNPV_reinvest += fCellValue * fPow_reinvest; + else if( fCellValue < 0.0 ) // investments + fNPV_invest += fCellValue * fPow_invest; + fPow_reinvest /= fRate1_reinvest; + fPow_invest /= fRate1_invest; + nCount++; + + bLoop = aValIter.GetNext( fCellValue, nIterError ); + } + if( nIterError ) + PushError( nIterError ); + else + { + double fResult = -fNPV_reinvest / fNPV_invest; + fResult *= pow( fRate1_reinvest, (double) nCount - 1 ); + fResult = pow( fResult, 1.0 / (nCount - 1) ); + PushDouble( fResult - 1.0 ); + } + } + } +} + + +void ScInterpreter::ScISPMT() +{ // rate ; period ; total_periods ; invest + if( MustHaveParamCount( GetByte(), 4 ) ) + { + double fInvest = GetDouble(); + double fTotal = GetDouble(); + double fPeriod = GetDouble(); + double fRate = GetDouble(); + + if( nGlobalError ) + PushError( nGlobalError); + else + PushDouble( fInvest * fRate * (fPeriod / fTotal - 1.0) ); + } +} + + +//----------------------- Finanzfunktionen ------------------------------------ + +double ScInterpreter::ScGetBw(double fZins, double fZzr, double fRmz, + double fZw, double fF) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMIRR" ); + double fBw; + if (fZins == 0.0) + fBw = fZw + fRmz * fZzr; + else if (fF > 0.0) + fBw = (fZw * pow(1.0 + fZins, -fZzr)) + + (fRmz * (1.0 - pow(1.0 + fZins, -fZzr + 1.0)) / fZins) + + fRmz; + else + fBw = (fZw * pow(1.0 + fZins, -fZzr)) + + (fRmz * (1.0 - pow(1.0 + fZins, -fZzr)) / fZins); + return -fBw; +} + +void ScInterpreter::ScBW() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBW" ); + nFuncFmtType = NUMBERFORMAT_CURRENCY; + double nRmz, nZzr, nZins, nZw = 0, nFlag = 0; + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 3, 5 ) ) + return; + if (nParamCount == 5) + nFlag = GetDouble(); + if (nParamCount >= 4) + nZw = GetDouble(); + nRmz = GetDouble(); + nZzr = GetDouble(); + nZins = GetDouble(); + PushDouble(ScGetBw(nZins, nZzr, nRmz, nZw, nFlag)); +} + +void ScInterpreter::ScDIA() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDIA" ); + nFuncFmtType = NUMBERFORMAT_CURRENCY; + if ( MustHaveParamCount( GetByte(), 4 ) ) + { + double nZr = GetDouble(); + double nDauer = GetDouble(); + double nRest = GetDouble(); + double nWert = GetDouble(); + double nDia = ((nWert - nRest) * (nDauer - nZr + 1.0)) / + ((nDauer * (nDauer + 1.0)) / 2.0); + PushDouble(nDia); + } +} + +double ScInterpreter::ScGetGDA(double fWert, double fRest, double fDauer, + double fPeriode, double fFaktor) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetGDA" ); + double fGda, fZins, fAlterWert, fNeuerWert; + fZins = fFaktor / fDauer; + if (fZins >= 1.0) + { + fZins = 1.0; + if (fPeriode == 1.0) + fAlterWert = fWert; + else + fAlterWert = 0.0; + } + else + fAlterWert = fWert * pow(1.0 - fZins, fPeriode - 1.0); + fNeuerWert = fWert * pow(1.0 - fZins, fPeriode); + + if (fNeuerWert < fRest) + fGda = fAlterWert - fRest; + else + fGda = fAlterWert - fNeuerWert; + if (fGda < 0.0) + fGda = 0.0; + return fGda; +} + +void ScInterpreter::ScGDA() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGDA" ); + nFuncFmtType = NUMBERFORMAT_CURRENCY; + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 4, 5 ) ) + { + double nFaktor; + if (nParamCount == 5) + nFaktor = GetDouble(); + else + nFaktor = 2.0; + double nPeriode = GetDouble(); + double nDauer = GetDouble(); + double nRest = GetDouble(); + double nWert = GetDouble(); + if (nWert < 0.0 || nRest < 0.0 || nFaktor <= 0.0 || nRest > nWert + || nPeriode < 1.0 || nPeriode > nDauer) + PushIllegalArgument(); + else + PushDouble(ScGetGDA(nWert, nRest, nDauer, nPeriode, nFaktor)); + } +} + +void ScInterpreter::ScGDA2() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGDA2" ); + nFuncFmtType = NUMBERFORMAT_CURRENCY; + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 4, 5 ) ) + return ; + double nMonate; + if (nParamCount == 4) + nMonate = 12.0; + else + nMonate = ::rtl::math::approxFloor(GetDouble()); + double nPeriode = GetDouble(); + double nDauer = GetDouble(); + double nRest = GetDouble(); + double nWert = GetDouble(); + if (nMonate < 1.0 || nMonate > 12.0 || nDauer > 1200.0 || nRest < 0.0 || + nPeriode > (nDauer + 1.0) || nRest > nWert || nWert < 0.0) + { + PushIllegalArgument(); + return; + } + double nAbRate = 1.0 - pow(nRest / nWert, 1.0 / nDauer); + nAbRate = ::rtl::math::approxFloor((nAbRate * 1000.0) + 0.5) / 1000.0; + double nErsteAbRate = nWert * nAbRate * nMonate / 12.0; + double nGda2 = 0.0; + if (::rtl::math::approxFloor(nPeriode) == 1) + nGda2 = nErsteAbRate; + else + { + double nSummAbRate = nErsteAbRate; + double nMin = nDauer; + if (nMin > nPeriode) nMin = nPeriode; + USHORT iMax = (USHORT)::rtl::math::approxFloor(nMin); + for (USHORT i = 2; i <= iMax; i++) + { + nGda2 = (nWert - nSummAbRate) * nAbRate; + nSummAbRate += nGda2; + } + if (nPeriode > nDauer) + nGda2 = ((nWert - nSummAbRate) * nAbRate * (12.0 - nMonate)) / 12.0; + } + PushDouble(nGda2); +} + + +double ScInterpreter::ScInterVDB(double fWert,double fRest,double fDauer, + double fDauer1,double fPeriode,double fFaktor) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScInterVDB" ); + double fVdb=0; + double fIntEnd = ::rtl::math::approxCeil(fPeriode); + ULONG nLoopEnd = (ULONG) fIntEnd; + + double fTerm, fLia; + double fRestwert = fWert - fRest; + BOOL bNowLia = FALSE; + + double fGda; + ULONG i; + fLia=0; + for ( i = 1; i <= nLoopEnd; i++) + { + if(!bNowLia) + { + fGda = ScGetGDA(fWert, fRest, fDauer, (double) i, fFaktor); + fLia = fRestwert/ (fDauer1 - (double) (i-1)); + + if (fLia > fGda) + { + fTerm = fLia; + bNowLia = TRUE; + } + else + { + fTerm = fGda; + fRestwert -= fGda; + } + } + else + { + fTerm = fLia; + } + + if ( i == nLoopEnd) + fTerm *= ( fPeriode + 1.0 - fIntEnd ); + + fVdb += fTerm; + } + return fVdb; +} + + +inline double DblMin( double a, double b ) +{ + return (a < b) ? a : b; +} + +void ScInterpreter::ScVDB() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScVDB" ); + nFuncFmtType = NUMBERFORMAT_CURRENCY; + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 5, 7 ) ) + { + double fWert, fRest, fDauer, fAnfang, fEnde, fFaktor, fVdb = 0.0; + BOOL bFlag; + if (nParamCount == 7) + bFlag = GetBool(); + else + bFlag = FALSE; + if (nParamCount >= 6) + fFaktor = GetDouble(); + else + fFaktor = 2.0; + fEnde = GetDouble(); + fAnfang = GetDouble(); + fDauer = GetDouble(); + fRest = GetDouble(); + fWert = GetDouble(); + if (fAnfang < 0.0 || fEnde < fAnfang || fEnde > fDauer || fWert < 0.0 + || fRest > fWert || fFaktor <= 0.0) + PushIllegalArgument(); + else + { + double fIntStart = ::rtl::math::approxFloor(fAnfang); + double fIntEnd = ::rtl::math::approxCeil(fEnde); + ULONG nLoopStart = (ULONG) fIntStart; + ULONG nLoopEnd = (ULONG) fIntEnd; + + fVdb = 0.0; + if (bFlag) + { + for (ULONG i = nLoopStart + 1; i <= nLoopEnd; i++) + { + double fTerm = ScGetGDA(fWert, fRest, fDauer, (double) i, fFaktor); + + // Teilperioden am Anfang / Ende beruecksichtigen: + if ( i == nLoopStart+1 ) + fTerm *= ( DblMin( fEnde, fIntStart + 1.0 ) - fAnfang ); + else if ( i == nLoopEnd ) + fTerm *= ( fEnde + 1.0 - fIntEnd ); + + fVdb += fTerm; + } + } + else + { + + double fDauer1=fDauer; + double fPart; + + //@Die Frage aller Fragen: "Ist das hier richtig" + if(!::rtl::math::approxEqual(fAnfang,::rtl::math::approxFloor(fAnfang))) + { + if(fFaktor>1) + { + if(fAnfang>fDauer/2 || ::rtl::math::approxEqual(fAnfang,fDauer/2)) + { + fPart=fAnfang-fDauer/2; + fAnfang=fDauer/2; + fEnde-=fPart; + fDauer1+=1; + } + } + } + + fWert-=ScInterVDB(fWert,fRest,fDauer,fDauer1,fAnfang,fFaktor); + fVdb=ScInterVDB(fWert,fRest,fDauer,fDauer-fAnfang,fEnde-fAnfang,fFaktor); + } + } + PushDouble(fVdb); + } +} + +void ScInterpreter::ScLaufz() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLaufz" ); + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double nZukunft = GetDouble(); + double nGegenwart = GetDouble(); + double nZins = GetDouble(); + PushDouble(log(nZukunft / nGegenwart) / log(1.0 + nZins)); + } +} + +void ScInterpreter::ScLIA() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLIA" ); + nFuncFmtType = NUMBERFORMAT_CURRENCY; + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double nDauer = GetDouble(); + double nRest = GetDouble(); + double nWert = GetDouble(); + PushDouble((nWert - nRest) / nDauer); + } +} + +double ScInterpreter::ScGetRmz(double fZins, double fZzr, double fBw, + double fZw, double fF) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetRmz" ); + double fRmz; + if (fZins == 0.0) + fRmz = (fBw + fZw) / fZzr; + else + { + double fTerm = pow(1.0 + fZins, fZzr); + if (fF > 0.0) + fRmz = (fZw * fZins / (fTerm - 1.0) + + fBw * fZins / (1.0 - 1.0 / fTerm)) / (1.0+fZins); + else + fRmz = fZw * fZins / (fTerm - 1.0) + + fBw * fZins / (1.0 - 1.0 / fTerm); + } + return -fRmz; +} + +void ScInterpreter::ScRMZ() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRMZ" ); + double nZins, nZzr, nBw, nZw = 0, nFlag = 0; + nFuncFmtType = NUMBERFORMAT_CURRENCY; + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 3, 5 ) ) + return; + if (nParamCount == 5) + nFlag = GetDouble(); + if (nParamCount >= 4) + nZw = GetDouble(); + nBw = GetDouble(); + nZzr = GetDouble(); + nZins = GetDouble(); + PushDouble(ScGetRmz(nZins, nZzr, nBw, nZw, nFlag)); +} + +void ScInterpreter::ScZGZ() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZGZ" ); + nFuncFmtType = NUMBERFORMAT_PERCENT; + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double nZukunftswert = GetDouble(); + double nGegenwartswert = GetDouble(); + double nZeitraum = GetDouble(); + PushDouble(pow(nZukunftswert / nGegenwartswert, 1.0 / nZeitraum) - 1.0); + } +} + +double ScInterpreter::ScGetZw(double fZins, double fZzr, double fRmz, + double fBw, double fF) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetZw" ); + double fZw; + if (fZins == 0.0) + fZw = fBw + fRmz * fZzr; + else + { + double fTerm = pow(1.0 + fZins, fZzr); + if (fF > 0.0) + fZw = fBw * fTerm + fRmz*(1.0 + fZins)*(fTerm - 1.0)/fZins; + else + fZw = fBw * fTerm + fRmz*(fTerm - 1.0)/fZins; + } + return -fZw; +} + +void ScInterpreter::ScZW() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZW" ); + double nZins, nZzr, nRmz, nBw = 0, nFlag = 0; + nFuncFmtType = NUMBERFORMAT_CURRENCY; + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 3, 5 ) ) + return; + if (nParamCount == 5) + nFlag = GetDouble(); + if (nParamCount >= 4) + nBw = GetDouble(); + nRmz = GetDouble(); + nZzr = GetDouble(); + nZins = GetDouble(); + PushDouble(ScGetZw(nZins, nZzr, nRmz, nBw, nFlag)); +} + +void ScInterpreter::ScZZR() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZZR" ); + double nZins, nRmz, nBw, nZw = 0, nFlag = 0; + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 3, 5 ) ) + return; + if (nParamCount == 5) + nFlag = GetDouble(); + if (nParamCount >= 4) + nZw = GetDouble(); + nBw = GetDouble(); + nRmz = GetDouble(); + nZins = GetDouble(); + if (nZins == 0.0) + PushDouble(-(nBw + nZw)/nRmz); + else if (nFlag > 0.0) + PushDouble(log(-(nZins*nZw-nRmz*(1.0+nZins))/(nZins*nBw+nRmz*(1.0+nZins))) + /log(1.0+nZins)); + else + PushDouble(log(-(nZins*nZw-nRmz)/(nZins*nBw+nRmz))/log(1.0+nZins)); +} + +bool ScInterpreter::RateIteration( double fNper, double fPayment, double fPv, + double fFv, double fPayType, double & fGuess ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::RateIteration" ); + // See also #i15090# + // Newton-Raphson method: x(i+1) = x(i) - f(x(i)) / f'(x(i)) + // This solution handles integer and non-integer values of Nper different. + // If ODFF will constraint Nper to integer, the distinction of cases can be + // removed; only the integer-part is needed then. + bool bValid = true, bFound = false; + double fX, fXnew, fTerm, fTermDerivation; + double fGeoSeries, fGeoSeriesDerivation; + const USHORT nIterationsMax = 150; + USHORT nCount = 0; + const double fEpsilonSmall = 1.0E-14; + // convert any fPayType situation to fPayType == zero situation + fFv = fFv - fPayment * fPayType; + fPv = fPv + fPayment * fPayType; + if (fNper == ::rtl::math::round( fNper, 0, rtl_math_RoundingMode_Corrected )) + { // Nper is an integer value + fX = fGuess; + double fPowN, fPowNminus1; // for (1.0+fX)^Nper and (1.0+fX)^(Nper-1) + while (!bFound && nCount < nIterationsMax) + { + fPowNminus1 = pow( 1.0+fX, fNper-1.0); + fPowN = fPowNminus1 * (1.0+fX); + if (rtl::math::approxEqual( fabs(fX), 0.0)) + { + fGeoSeries = fNper; + fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0; + } + else + { + fGeoSeries = (fPowN-1.0)/fX; + fGeoSeriesDerivation = fNper * fPowNminus1 / fX - fGeoSeries / fX; + } + fTerm = fFv + fPv *fPowN+ fPayment * fGeoSeries; + fTermDerivation = fPv * fNper * fPowNminus1 + fPayment * fGeoSeriesDerivation; + if (fabs(fTerm) < fEpsilonSmall) + bFound = true; // will catch root which is at an extreme + else + { + if (rtl::math::approxEqual( fabs(fTermDerivation), 0.0)) + fXnew = fX + 1.1 * SCdEpsilon; // move away from zero slope + else + fXnew = fX - fTerm / fTermDerivation; + nCount++; + // more accuracy not possible in oscillating cases + bFound = (fabs(fXnew - fX) < SCdEpsilon); + fX = fXnew; + } + } + // Gnumeric returns roots < -1, Excel gives an error in that cases, + // ODFF says nothing about it. Enable the statement, if you want Excel's + // behavior + //bValid =(fX >=-1.0); + } + else + { // Nper is not an integer value. + fX = (fGuess < -1.0) ? -1.0 : fGuess; // start with a valid fX + while (bValid && !bFound && nCount < nIterationsMax) + { + if (rtl::math::approxEqual( fabs(fX), 0.0)) + { + fGeoSeries = fNper; + fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0; + } + else + { + fGeoSeries = (pow( 1.0+fX, fNper) - 1.0) / fX; + fGeoSeriesDerivation = fNper * pow( 1.0+fX, fNper-1.0) / fX - fGeoSeries / fX; + } + fTerm = fFv + fPv *pow(1.0 + fX,fNper)+ fPayment * fGeoSeries; + fTermDerivation = fPv * fNper * pow( 1.0+fX, fNper-1.0) + fPayment * fGeoSeriesDerivation; + if (fabs(fTerm) < fEpsilonSmall) + bFound = true; // will catch root which is at an extreme + else + { + if (rtl::math::approxEqual( fabs(fTermDerivation), 0.0)) + fXnew = fX + 1.1 * SCdEpsilon; // move away from zero slope + else + fXnew = fX - fTerm / fTermDerivation; + nCount++; + // more accuracy not possible in oscillating cases + bFound = (fabs(fXnew - fX) < SCdEpsilon); + fX = fXnew; + bValid = (fX >= -1.0); // otherwise pow(1.0+fX,fNper) will fail + } + } + } + fGuess = fX; // return approximate root + return bValid && bFound; +} + +// In Calc UI it is the function RATE(Nper;Pmt;Pv;Fv;Type;Guess) +void ScInterpreter::ScZins() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZins" ); + double fPv, fPayment, fNper; + // defaults for missing arguments, see ODFF spec + double fFv = 0, fPayType = 0, fGuess = 0.1; + bool bValid = true; + nFuncFmtType = NUMBERFORMAT_PERCENT; + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 3, 6 ) ) + return; + if (nParamCount == 6) + fGuess = GetDouble(); + if (nParamCount >= 5) + fPayType = GetDouble(); + if (nParamCount >= 4) + fFv = GetDouble(); + fPv = GetDouble(); + fPayment = GetDouble(); + fNper = GetDouble(); + if (fNper <= 0.0) // constraint from ODFF spec + { + PushIllegalArgument(); + return; + } + // other values for fPayType might be meaningful, + // ODFF spec is not clear yet, enable statement if you want only 0 and 1 + //if (fPayType != 0.0) fPayType = 1.0; + bValid = RateIteration(fNper, fPayment, fPv, fFv, fPayType, fGuess); + if (!bValid) + SetError(errNoConvergence); + PushDouble(fGuess); +} + +double ScInterpreter::ScGetZinsZ(double fZins, double fZr, double fZzr, double fBw, + double fZw, double fF, double& fRmz) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetZinsZ" ); + fRmz = ScGetRmz(fZins, fZzr, fBw, fZw, fF); // fuer kapz auch bei fZr == 1 + double fZinsZ; + nFuncFmtType = NUMBERFORMAT_CURRENCY; + if (fZr == 1.0) + { + if (fF > 0.0) + fZinsZ = 0.0; + else + fZinsZ = -fBw; + } + else + { + if (fF > 0.0) + fZinsZ = ScGetZw(fZins, fZr-2.0, fRmz, fBw, 1.0) - fRmz; + else + fZinsZ = ScGetZw(fZins, fZr-1.0, fRmz, fBw, 0.0); + } + return fZinsZ * fZins; +} + +void ScInterpreter::ScZinsZ() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZinsZ" ); + double nZins, nZr, nRmz, nZzr, nBw, nZw = 0, nFlag = 0; + nFuncFmtType = NUMBERFORMAT_CURRENCY; + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 4, 6 ) ) + return; + if (nParamCount == 6) + nFlag = GetDouble(); + if (nParamCount >= 5) + nZw = GetDouble(); + nBw = GetDouble(); + nZzr = GetDouble(); + nZr = GetDouble(); + nZins = GetDouble(); + if (nZr < 1.0 || nZr > nZzr) + PushIllegalArgument(); + else + PushDouble(ScGetZinsZ(nZins, nZr, nZzr, nBw, nZw, nFlag, nRmz)); +} + +void ScInterpreter::ScKapz() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScKapz" ); + double nZins, nZr, nZzr, nBw, nZw = 0, nFlag = 0, nRmz, nZinsz; + nFuncFmtType = NUMBERFORMAT_CURRENCY; + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 4, 6 ) ) + return; + if (nParamCount == 6) + nFlag = GetDouble(); + if (nParamCount >= 5) + nZw = GetDouble(); + nBw = GetDouble(); + nZzr = GetDouble(); + nZr = GetDouble(); + nZins = GetDouble(); + if (nZr < 1.0 || nZr > nZzr) + PushIllegalArgument(); + else + { + nZinsz = ScGetZinsZ(nZins, nZr, nZzr, nBw, nZw, nFlag, nRmz); + PushDouble(nRmz - nZinsz); + } +} + +void ScInterpreter::ScKumZinsZ() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScKumZinsZ" ); + nFuncFmtType = NUMBERFORMAT_CURRENCY; + if ( MustHaveParamCount( GetByte(), 6 ) ) + { + double fZins, fZzr, fBw, fAnfang, fEnde, fF, fRmz, fZinsZ; + fF = GetDouble(); + fEnde = ::rtl::math::approxFloor(GetDouble()); + fAnfang = ::rtl::math::approxFloor(GetDouble()); + fBw = GetDouble(); + fZzr = GetDouble(); + fZins = GetDouble(); + if (fAnfang < 1.0 || fEnde < fAnfang || fZins <= 0.0 || + fEnde > fZzr || fZzr <= 0.0 || fBw <= 0.0) + PushIllegalArgument(); + else + { + ULONG nAnfang = (ULONG) fAnfang; + ULONG nEnde = (ULONG) fEnde ; + fRmz = ScGetRmz(fZins, fZzr, fBw, 0.0, fF); + fZinsZ = 0.0; + if (nAnfang == 1) + { + if (fF <= 0.0) + fZinsZ = -fBw; + nAnfang++; + } + for (ULONG i = nAnfang; i <= nEnde; i++) + { + if (fF > 0.0) + fZinsZ += ScGetZw(fZins, (double)(i-2), fRmz, fBw, 1.0) - fRmz; + else + fZinsZ += ScGetZw(fZins, (double)(i-1), fRmz, fBw, 0.0); + } + fZinsZ *= fZins; + PushDouble(fZinsZ); + } + } +} + +void ScInterpreter::ScKumKapZ() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScKumKapZ" ); + nFuncFmtType = NUMBERFORMAT_CURRENCY; + if ( MustHaveParamCount( GetByte(), 6 ) ) + { + double fZins, fZzr, fBw, fAnfang, fEnde, fF, fRmz, fKapZ; + fF = GetDouble(); + fEnde = ::rtl::math::approxFloor(GetDouble()); + fAnfang = ::rtl::math::approxFloor(GetDouble()); + fBw = GetDouble(); + fZzr = GetDouble(); + fZins = GetDouble(); + if (fAnfang < 1.0 || fEnde < fAnfang || fZins <= 0.0 || + fEnde > fZzr || fZzr <= 0.0 || fBw <= 0.0) + PushIllegalArgument(); + else + { + fRmz = ScGetRmz(fZins, fZzr, fBw, 0.0, fF); + fKapZ = 0.0; + ULONG nAnfang = (ULONG) fAnfang; + ULONG nEnde = (ULONG) fEnde; + if (nAnfang == 1) + { + if (fF <= 0.0) + fKapZ = fRmz + fBw * fZins; + else + fKapZ = fRmz; + nAnfang++; + } + for (ULONG i = nAnfang; i <= nEnde; i++) + { + if (fF > 0.0) + fKapZ += fRmz - (ScGetZw(fZins, (double)(i-2), fRmz, fBw, 1.0) - fRmz) * fZins; + else + fKapZ += fRmz - ScGetZw(fZins, (double)(i-1), fRmz, fBw, 0.0) * fZins; + } + PushDouble(fKapZ); + } + } +} + +void ScInterpreter::ScEffektiv() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScEffektiv" ); + nFuncFmtType = NUMBERFORMAT_PERCENT; + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + double fPerioden = GetDouble(); + double fNominal = GetDouble(); + if (fPerioden < 1.0 || fNominal <= 0.0) + PushIllegalArgument(); + else + { + fPerioden = ::rtl::math::approxFloor(fPerioden); + PushDouble(pow(1.0 + fNominal/fPerioden, fPerioden) - 1.0); + } + } +} + +void ScInterpreter::ScNominal() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNominal" ); + nFuncFmtType = NUMBERFORMAT_PERCENT; + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + double fPerioden = GetDouble(); + double fEffektiv = GetDouble(); + if (fPerioden < 1.0 || fEffektiv <= 0.0) + PushIllegalArgument(); + else + { + fPerioden = ::rtl::math::approxFloor(fPerioden); + PushDouble( (pow(fEffektiv + 1.0, 1.0 / fPerioden) - 1.0) * fPerioden ); + } + } +} + +void ScInterpreter::ScMod() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMod" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + double fVal2 = GetDouble(); // Denominator + double fVal1 = GetDouble(); // Numerator + if (fVal2 == floor(fVal2)) // a pure integral number stored in double + { + double fResult = fmod(fVal1,fVal2); + if ( (fResult != 0.0) && + ((fVal1 > 0.0 && fVal2 < 0.0) || (fVal1 < 0.0 && fVal2 > 0.0))) + fResult += fVal2 ; + PushDouble( fResult ); + } + else + { + PushDouble( ::rtl::math::approxSub( fVal1, + ::rtl::math::approxFloor(fVal1 / fVal2) * fVal2)); + } + } +} + +/** (Goal Seek) Find a value of x that is a root of f(x) + + This function is used internally for the goal seek operation. It uses the + Regula Falsi (aka false position) algorithm to find a root of f(x). The + start value and the target value are to be given by the user in the + goal seek dialog. The f(x) in this case is defined as the formula in the + formula cell minus target value. This function may also perform additional + search in the horizontal directions when the f(x) is discrete in order to + ensure a non-zero slope necessary for deriving a subsequent x that is + reasonably close to the root of interest. + + @change 24.10.2004 by Kohei Yoshida (kohei@openoffice.org) + + @see #i28955# +*/ +void ScInterpreter::ScBackSolver() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBackSolver" ); + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + BOOL bDoneIteration = FALSE; + ScAddress aValueAdr, aFormulaAdr; + double fTargetVal = GetDouble(); + PopSingleRef( aFormulaAdr ); + PopSingleRef( aValueAdr ); + + if (nGlobalError == 0) + { + ScBaseCell* pVCell = GetCell( aValueAdr ); + // CELLTYPE_NOTE: kein Value aber von Formel referiert + BOOL bTempCell = (!pVCell || pVCell->GetCellType() == CELLTYPE_NOTE); + ScBaseCell* pFCell = GetCell( aFormulaAdr ); + + if ( ((pVCell && pVCell->GetCellType() == CELLTYPE_VALUE) || bTempCell) + && pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA ) + { + ScRange aVRange( aValueAdr, aValueAdr ); // fuer SetDirty + double fSaveVal; // Original value to be restored later if necessary + ScPostIt* pNote = 0; + + if ( bTempCell ) + { + pNote = pVCell ? pVCell->ReleaseNote() : 0; + fSaveVal = 0.0; + pVCell = new ScValueCell( fSaveVal ); + pDok->PutCell( aValueAdr, pVCell ); + } + else + fSaveVal = GetCellValue( aValueAdr, pVCell ); + + const USHORT nMaxIter = 100; + const double fEps = 1E-10; + const double fDelta = 1E-6; + + double fBestX, fXPrev; + double fBestF, fFPrev; + fBestX = fXPrev = fSaveVal; + + ScFormulaCell* pFormula = (ScFormulaCell*) pFCell; + ScValueCell* pValue = (ScValueCell*) pVCell; + + pFormula->Interpret(); + BOOL bError = ( pFormula->GetErrCode() != 0 ); + // bError always corresponds with fF + + fFPrev = pFormula->GetValue() - fTargetVal; + + fBestF = fabs( fFPrev ); + if ( fBestF < fDelta ) + bDoneIteration = TRUE; + + double fX = fXPrev + fEps; + double fF = fFPrev; + double fSlope; + + USHORT nIter = 0; + + BOOL bHorMoveError = FALSE; + // Nach der Regula Falsi Methode + while ( !bDoneIteration && ( nIter++ < nMaxIter ) ) + { + pValue->SetValue( fX ); + pDok->SetDirty( aVRange ); + pFormula->Interpret(); + bError = ( pFormula->GetErrCode() != 0 ); + fF = pFormula->GetValue() - fTargetVal; + + if ( fF == fFPrev && !bError ) + { + // HORIZONTAL SEARCH: Keep moving x in both directions until the f(x) + // becomes different from the previous f(x). This routine is needed + // when a given function is discrete, in which case the resulting slope + // may become zero which ultimately causes the goal seek operation + // to fail. #i28955# + + USHORT nHorIter = 0; + const double fHorStepAngle = 5.0; + const double fHorMaxAngle = 80.0; + int nHorMaxIter = static_cast<int>( fHorMaxAngle / fHorStepAngle ); + BOOL bDoneHorMove = FALSE; + + while ( !bDoneHorMove && !bHorMoveError && nHorIter++ < nHorMaxIter ) + { + double fHorAngle = fHorStepAngle * static_cast<double>( nHorIter ); + double fHorTangent = ::rtl::math::tan( fHorAngle * F_PI / 180 ); + + USHORT nIdx = 0; + while( nIdx++ < 2 && !bDoneHorMove ) + { + double fHorX; + if ( nIdx == 1 ) + fHorX = fX + fabs(fF)*fHorTangent; + else + fHorX = fX - fabs(fF)*fHorTangent; + + pValue->SetValue( fHorX ); + pDok->SetDirty( aVRange ); + pFormula->Interpret(); + bHorMoveError = ( pFormula->GetErrCode() != 0 ); + if ( bHorMoveError ) + break; + + fF = pFormula->GetValue() - fTargetVal; + if ( fF != fFPrev ) + { + fX = fHorX; + bDoneHorMove = TRUE; + } + } + } + if ( !bDoneHorMove ) + bHorMoveError = TRUE; + } + + if ( bError ) + { + // move closer to last valid value (fXPrev), keep fXPrev & fFPrev + double fDiff = ( fXPrev - fX ) / 2; + if (fabs(fDiff) < fEps) + fDiff = (fDiff < 0.0) ? - fEps : fEps; + fX += fDiff; + } + else if ( bHorMoveError ) + break; + else if ( fabs(fF) < fDelta ) + { + // converged to root + fBestX = fX; + bDoneIteration = TRUE; + } + else + { + if ( fabs(fF) + fDelta < fBestF ) + { + fBestX = fX; + fBestF = fabs(fF); + } + + if ( ( fXPrev - fX ) != 0 ) + { + fSlope = ( fFPrev - fF ) / ( fXPrev - fX ); + if ( fabs( fSlope ) < fEps ) + fSlope = fSlope < 0.0 ? -fEps : fEps; + } + else + fSlope = fEps; + + fXPrev = fX; + fFPrev = fF; + fX = fX - ( fF / fSlope ); + } + } + + // Try a nice rounded input value if possible. + const double fNiceDelta = (bDoneIteration && fabs(fBestX) >= 1e-3 ? 1e-3 : fDelta); + double nX = ::rtl::math::approxFloor((fBestX / fNiceDelta) + 0.5) * fNiceDelta; +// double nX = ::rtl::math::approxFloor((fBestX / fDelta) + 0.5) * fDelta; + + if ( bDoneIteration ) + { + pValue->SetValue( nX ); + pDok->SetDirty( aVRange ); + pFormula->Interpret(); + if ( fabs( pFormula->GetValue() - fTargetVal ) > fabs( fF ) ) + nX = fBestX; + } + else if ( bError || bHorMoveError ) + { + nX = fBestX; + } + if ( bTempCell ) + { + pVCell = pNote ? new ScNoteCell( pNote ) : 0; + pDok->PutCell( aValueAdr, pVCell ); + } + else + pValue->SetValue( fSaveVal ); + pDok->SetDirty( aVRange ); + pFormula->Interpret(); + if ( !bDoneIteration ) + SetError(NOTAVAILABLE); + PushDouble(nX); + } + else + { + if ( !bDoneIteration ) + SetError(NOTAVAILABLE); + PushInt(0); // falsche Zelltypen + } + } + else + { + if ( !bDoneIteration ) + SetError(NOTAVAILABLE); + PushInt(0); // nGlobalError + } + } +} + +void ScInterpreter::ScIntersect() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIntersect" ); + formula::FormulaTokenRef p2nd = PopToken(); + formula::FormulaTokenRef p1st = PopToken(); + + if (nGlobalError || !p2nd || !p1st) + { + PushIllegalArgument(); + return; + } // if (nGlobalError || !xT2 || !xT1) + + StackVar sv1 = p1st->GetType(); + StackVar sv2 = p2nd->GetType(); + if ((sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList) || + (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList)) + { + PushIllegalArgument(); + return; + } + + ScToken* x1 = static_cast<ScToken*>(p1st.get()); + ScToken* x2 = static_cast<ScToken*>(p2nd.get()); + if (sv1 == svRefList || sv2 == svRefList) + { + // Now this is a bit nasty but it simplifies things, and having + // intersections with lists isn't too common, if at all.. + // Convert a reference to list. + ScToken* xt[2] = { x1, x2 }; + StackVar sv[2] = { sv1, sv2 }; + for (size_t i=0; i<2; ++i) + { + if (sv[i] == svSingleRef) + { + ScComplexRefData aRef; + aRef.Ref1 = aRef.Ref2 = xt[i]->GetSingleRef(); + xt[i] = new ScRefListToken; + xt[i]->GetRefList()->push_back( aRef); + } + else if (sv[i] == svDoubleRef) + { + ScComplexRefData aRef = xt[i]->GetDoubleRef(); + xt[i] = new ScRefListToken; + xt[i]->GetRefList()->push_back( aRef); + } + } + x1 = xt[0], x2 = xt[1]; + + x1->CalcAbsIfRel( aPos); + x2->CalcAbsIfRel( aPos); + ScTokenRef xRes = new ScRefListToken; + ScRefList* pRefList = xRes->GetRefList(); + ScRefList::const_iterator end1( x1->GetRefList()->end()); + ScRefList::const_iterator end2( x2->GetRefList()->end()); + for (ScRefList::const_iterator it1( x1->GetRefList()->begin()); + it1 != end1; ++it1) + { + const ScSingleRefData& r11 = (*it1).Ref1; + const ScSingleRefData& r12 = (*it1).Ref2; + for (ScRefList::const_iterator it2( x2->GetRefList()->begin()); + it2 != end2; ++it2) + { + const ScSingleRefData& r21 = (*it2).Ref1; + const ScSingleRefData& r22 = (*it2).Ref2; + SCCOL nCol1 = ::std::max( r11.nCol, r21.nCol); + SCROW nRow1 = ::std::max( r11.nRow, r21.nRow); + SCTAB nTab1 = ::std::max( r11.nTab, r21.nTab); + SCCOL nCol2 = ::std::min( r12.nCol, r22.nCol); + SCROW nRow2 = ::std::min( r12.nRow, r22.nRow); + SCTAB nTab2 = ::std::min( r12.nTab, r22.nTab); + if (nCol2 < nCol1 || nRow2 < nRow1 || nTab2 < nTab1) + ; // nothing + else + { + ScComplexRefData aRef; + aRef.InitRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + pRefList->push_back( aRef); + } + } + } + size_t n = pRefList->size(); + if (!n) + PushError( errNoRef); + else if (n == 1) + { + const ScComplexRefData& rRef = (*pRefList)[0]; + if (rRef.Ref1 == rRef.Ref2) + PushTempToken( new ScSingleRefToken( rRef.Ref1)); + else + PushTempToken( new ScDoubleRefToken( rRef)); + } + else + PushTempToken( xRes); + } + else + { + ScToken* pt[2] = { x1, x2 }; + StackVar sv[2] = { sv1, sv2 }; + SCCOL nC1[2], nC2[2]; + SCROW nR1[2], nR2[2]; + SCTAB nT1[2], nT2[2]; + for (size_t i=0; i<2; ++i) + { + switch (sv[i]) + { + case svSingleRef: + case svDoubleRef: + pt[i]->CalcAbsIfRel( aPos); + { + const ScSingleRefData& r = pt[i]->GetSingleRef(); + nC1[i] = r.nCol; + nR1[i] = r.nRow; + nT1[i] = r.nTab; + } + if (sv[i] == svDoubleRef) + { + const ScSingleRefData& r = pt[i]->GetSingleRef2(); + nC2[i] = r.nCol; + nR2[i] = r.nRow; + nT2[i] = r.nTab; + } + else + { + nC2[i] = nC1[i]; + nR2[i] = nR1[i]; + nT2[i] = nT1[i]; + } + break; + default: + ; // nothing, prevent compiler warning + } + } + SCCOL nCol1 = ::std::max( nC1[0], nC1[1]); + SCROW nRow1 = ::std::max( nR1[0], nR1[1]); + SCTAB nTab1 = ::std::max( nT1[0], nT1[1]); + SCCOL nCol2 = ::std::min( nC2[0], nC2[1]); + SCROW nRow2 = ::std::min( nR2[0], nR2[1]); + SCTAB nTab2 = ::std::min( nT2[0], nT2[1]); + if (nCol2 < nCol1 || nRow2 < nRow1 || nTab2 < nTab1) + PushError( errNoRef); + else if (nCol2 == nCol1 && nRow2 == nRow1 && nTab2 == nTab1) + PushSingleRef( nCol1, nRow1, nTab1); + else + PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + } +} + + +void ScInterpreter::ScRangeFunc() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRangeFunc" ); + formula::FormulaTokenRef x2 = PopToken(); + formula::FormulaTokenRef x1 = PopToken(); + + if (nGlobalError || !x2 || !x1) + { + PushIllegalArgument(); + return; + } // if (nGlobalError || !xT2 || !xT1) + FormulaTokenRef xRes = ScToken::ExtendRangeReference( *x1, *x2, aPos, false); + if (!xRes) + PushIllegalArgument(); + else + PushTempToken( xRes); +} + + +void ScInterpreter::ScUnionFunc() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScUnionFunc" ); + formula::FormulaTokenRef p2nd = PopToken(); + formula::FormulaTokenRef p1st = PopToken(); + + if (nGlobalError || !p2nd || !p1st) + { + PushIllegalArgument(); + return; + } // if (nGlobalError || !xT2 || !xT1) + + StackVar sv1 = p1st->GetType(); + StackVar sv2 = p2nd->GetType(); + if ((sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList) || + (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList)) + { + PushIllegalArgument(); + return; + } + + ScToken* x1 = static_cast<ScToken*>(p1st.get()); + ScToken* x2 = static_cast<ScToken*>(p2nd.get()); + + + ScTokenRef xRes; + // Append to an existing RefList if there is one. + if (sv1 == svRefList) + { + xRes = x1; + sv1 = svUnknown; // mark as handled + } + else if (sv2 == svRefList) + { + xRes = x2; + sv2 = svUnknown; // mark as handled + } + else + xRes = new ScRefListToken; + ScRefList* pRes = xRes->GetRefList(); + ScToken* pt[2] = { x1, x2 }; + StackVar sv[2] = { sv1, sv2 }; + for (size_t i=0; i<2; ++i) + { + if (pt[i] == xRes) + continue; + switch (sv[i]) + { + case svSingleRef: + { + ScComplexRefData aRef; + aRef.Ref1 = aRef.Ref2 = pt[i]->GetSingleRef(); + pRes->push_back( aRef); + } + break; + case svDoubleRef: + pRes->push_back( pt[i]->GetDoubleRef()); + break; + case svRefList: + { + const ScRefList* p = pt[i]->GetRefList(); + ScRefList::const_iterator it( p->begin()); + ScRefList::const_iterator end( p->end()); + for ( ; it != end; ++it) + { + pRes->push_back( *it); + } + } + break; + default: + ; // nothing, prevent compiler warning + } + } + ValidateRef( *pRes); // set #REF! if needed + PushTempToken( xRes); +} + + +void ScInterpreter::ScCurrent() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCurrent" ); + FormulaTokenRef xTok( PopToken()); + if (xTok) + { + PushTempToken( xTok); + PushTempToken( xTok); + } + else + PushError( errUnknownStackVariable); +} + +void ScInterpreter::ScStyle() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScStyle" ); + BYTE nParamCount = GetByte(); + if (nParamCount >= 1 && nParamCount <= 3) + { + String aStyle2; // Vorlage nach Timer + if (nParamCount >= 3) + aStyle2 = GetString(); + long nTimeOut = 0; // Timeout + if (nParamCount >= 2) + nTimeOut = (long)(GetDouble()*1000.0); + String aStyle1 = GetString(); // Vorlage fuer sofort + + if (nTimeOut < 0) + nTimeOut = 0; + + // + // Request ausfuehren, um Vorlage anzuwenden + // + + if ( !pDok->IsClipOrUndo() ) + { + SfxObjectShell* pShell = pDok->GetDocumentShell(); + if (pShell) + { + //! notify object shell directly + + ScRange aRange(aPos); + ScAutoStyleHint aHint( aRange, aStyle1, nTimeOut, aStyle2 ); + pShell->Broadcast( aHint ); + } + } + + PushDouble(0.0); + } + else + PushIllegalParameter(); +} + +ScDdeLink* lcl_GetDdeLink( sfx2::LinkManager* pLinkMgr, + const String& rA, const String& rT, const String& rI, BYTE nM ) +{ + USHORT nCount = pLinkMgr->GetLinks().Count(); + for (USHORT i=0; i<nCount; i++ ) + { + ::sfx2::SvBaseLink* pBase = *pLinkMgr->GetLinks()[i]; + if (pBase->ISA(ScDdeLink)) + { + ScDdeLink* pLink = (ScDdeLink*)pBase; + if ( pLink->GetAppl() == rA && + pLink->GetTopic() == rT && + pLink->GetItem() == rI && + pLink->GetMode() == nM ) + return pLink; + } + } + + return NULL; +} + +void ScInterpreter::ScDde() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDde" ); + // Applikation, Datei, Bereich + // Application, Topic, Item + + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 3, 4 ) ) + { + BYTE nMode = SC_DDE_DEFAULT; + if (nParamCount == 4) + nMode = (BYTE) ::rtl::math::approxFloor(GetDouble()); + String aItem = GetString(); + String aTopic = GetString(); + String aAppl = GetString(); + + if (nMode > SC_DDE_TEXT) + nMode = SC_DDE_DEFAULT; + + // temporary documents (ScFunctionAccess) have no DocShell + // and no LinkManager -> abort + + sfx2::LinkManager* pLinkMgr = pDok->GetLinkManager(); + if (!pLinkMgr) + { + PushNoValue(); + return; + } + + // Nach dem Laden muss neu interpretiert werden (Verknuepfungen aufbauen) + + if ( pMyFormulaCell->GetCode()->IsRecalcModeNormal() ) + pMyFormulaCell->GetCode()->SetRecalcModeOnLoad(); + + // solange der Link nicht ausgewertet ist, Idle abklemmen + // (um zirkulaere Referenzen zu vermeiden) + + BOOL bOldDis = pDok->IsIdleDisabled(); + pDok->DisableIdle( TRUE ); + + // Link-Objekt holen / anlegen + + ScDdeLink* pLink = lcl_GetDdeLink( pLinkMgr, aAppl, aTopic, aItem, nMode ); + + //! Dde-Links (zusaetzlich) effizienter am Dokument speichern !!!!! + // ScDdeLink* pLink = pDok->GetDdeLink( aAppl, aTopic, aItem ); + + BOOL bWasError = ( pMyFormulaCell->GetRawError() != 0 ); + + if (!pLink) + { + pLink = new ScDdeLink( pDok, aAppl, aTopic, aItem, nMode ); + pLinkMgr->InsertDDELink( pLink, aAppl, aTopic, aItem ); + if ( pLinkMgr->GetLinks().Count() == 1 ) // erster ? + { + SfxBindings* pBindings = pDok->GetViewBindings(); + if (pBindings) + pBindings->Invalidate( SID_LINKS ); // Link-Manager enablen + } + + //! asynchron auswerten ??? + pLink->TryUpdate(); // TryUpdate ruft Update nicht mehrfach auf + + // StartListening erst nach dem Update, sonst circular reference + pMyFormulaCell->StartListening( *pLink ); + } + else + { + pMyFormulaCell->StartListening( *pLink ); + } + + // Wenn aus dem Reschedule beim Ausfuehren des Links ein Fehler + // (z.B. zirkulaere Referenz) entstanden ist, der vorher nicht da war, + // das Fehler-Flag zuruecksetzen: + + if ( pMyFormulaCell->GetRawError() && !bWasError ) + pMyFormulaCell->SetErrCode(0); + + // Wert abfragen + + const ScMatrix* pLinkMat = pLink->GetResult(); + if (pLinkMat) + { + SCSIZE nC, nR; + pLinkMat->GetDimensions(nC, nR); + ScMatrixRef pNewMat = GetNewMat( nC, nR); + if (pNewMat) + { + pLinkMat->MatCopy(*pNewMat); // kopieren + PushMatrix( pNewMat ); + } + else + PushIllegalArgument(); + } + else + PushNA(); + + pDok->DisableIdle( bOldDis ); + } +} + +void ScInterpreter::ScBase() +{ // Value, Base [, MinLen] + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + static const sal_Unicode __FAR_DATA pDigits[] = { + '0','1','2','3','4','5','6','7','8','9', + 'A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 0 + }; + static const int nDigits = (sizeof(pDigits)/sizeof(sal_Unicode))-1; + xub_StrLen nMinLen; + if ( nParamCount == 3 ) + { + double fLen = ::rtl::math::approxFloor( GetDouble() ); + if ( 1.0 <= fLen && fLen < STRING_MAXLEN ) + nMinLen = (xub_StrLen) fLen; + else if ( fLen == 0.0 ) + nMinLen = 1; + else + nMinLen = 0; // Error + } + else + nMinLen = 1; + double fBase = ::rtl::math::approxFloor( GetDouble() ); + double fVal = ::rtl::math::approxFloor( GetDouble() ); + double fChars = ((fVal > 0.0 && fBase > 0.0) ? + (ceil( log( fVal ) / log( fBase ) ) + 2.0) : + 2.0); + if ( fChars >= STRING_MAXLEN ) + nMinLen = 0; // Error + + if ( !nGlobalError && nMinLen && 2 <= fBase && fBase <= nDigits && 0 <= fVal ) + { + const xub_StrLen nConstBuf = 128; + sal_Unicode aBuf[nConstBuf]; + xub_StrLen nBuf = Max( (xub_StrLen) fChars, (xub_StrLen) (nMinLen+1) ); + sal_Unicode* pBuf = (nBuf <= nConstBuf ? aBuf : new sal_Unicode[nBuf]); + for ( xub_StrLen j = 0; j < nBuf; ++j ) + { + pBuf[j] = '0'; + } + sal_Unicode* p = pBuf + nBuf - 1; + *p = 0; + if ( fVal <= (ULONG)(~0) ) + { + ULONG nVal = (ULONG) fVal; + ULONG nBase = (ULONG) fBase; + while ( nVal && p > pBuf ) + { + *--p = pDigits[ nVal % nBase ]; + nVal /= nBase; + } + fVal = (double) nVal; + } + else + { + BOOL bDirt = FALSE; + while ( fVal && p > pBuf ) + { +//! mit fmod Rundungsfehler ab 2**48 +// double fDig = ::rtl::math::approxFloor( fmod( fVal, fBase ) ); +// so ist es etwas besser + double fInt = ::rtl::math::approxFloor( fVal / fBase ); + double fMult = fInt * fBase; +#if OSL_DEBUG_LEVEL > 1 + // #53943# =BASIS(1e308;36) => GPF mit + // nDig = (size_t) ::rtl::math::approxFloor( fVal - fMult ); + // trotz vorheriger Pruefung ob fVal >= fMult + double fDebug1 = fVal - fMult; + // fVal := 7,5975311883090e+290 + // fMult := 7,5975311883090e+290 + // fDebug1 := 1,3848924157003e+275 <- RoundOff-Error + // fVal != fMult, aber: ::rtl::math::approxEqual( fVal, fMult ) == TRUE + double fDebug2 = ::rtl::math::approxSub( fVal, fMult ); + // und ::rtl::math::approxSub( fVal, fMult ) == 0 + double fDebug3 = ( fInt ? fVal / fInt : 0.0 ); + // Nach dem strange fDebug1 und fVal < fMult ist eigentlich + // fDebug2 == fBase, trotzdem wird das mit einem Vergleich + // nicht erkannt, dann schlaegt bDirt zu und alles wird wieder gut.. + + // prevent compiler warnings + (void)fDebug1; (void)fDebug2; (void)fDebug3; +#endif + size_t nDig; + if ( fVal < fMult ) + { // da ist was gekippt + bDirt = TRUE; + nDig = 0; + } + else + { + double fDig = ::rtl::math::approxFloor( ::rtl::math::approxSub( fVal, fMult ) ); + if ( bDirt ) + { + bDirt = FALSE; + --fDig; + } + if ( fDig <= 0.0 ) + nDig = 0; + else if ( fDig >= fBase ) + nDig = ((size_t) fBase) - 1; + else + nDig = (size_t) fDig; + } + *--p = pDigits[ nDig ]; + fVal = fInt; + } + } + if ( fVal ) + PushError( errStringOverflow ); + else + { + if ( nBuf - (p - pBuf) <= nMinLen ) + p = pBuf + nBuf - 1 - nMinLen; + PushStringBuffer( p ); + } + if ( pBuf != aBuf ) + delete [] pBuf; + } + else + PushIllegalArgument(); + } +} + + +void ScInterpreter::ScDecimal() +{ // Text, Base + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + double fBase = ::rtl::math::approxFloor( GetDouble() ); + String aStr( GetString() ); + if ( !nGlobalError && 2 <= fBase && fBase <= 36 ) + { + double fVal = 0.0; + int nBase = (int) fBase; + register const sal_Unicode* p = aStr.GetBuffer(); + while ( *p == ' ' || *p == '\t' ) + p++; // strip leading white space + if ( nBase == 16 ) + { // evtl. hex-prefix strippen + if ( *p == 'x' || *p == 'X' ) + p++; + else if ( *p == '0' && (*(p+1) == 'x' || *(p+1) == 'X') ) + p += 2; + } + while ( *p ) + { + int n; + if ( '0' <= *p && *p <= '9' ) + n = *p - '0'; + else if ( 'A' <= *p && *p <= 'Z' ) + n = 10 + (*p - 'A'); + else if ( 'a' <= *p && *p <= 'z' ) + n = 10 + (*p - 'a'); + else + n = nBase; + if ( nBase <= n ) + { + if ( *(p+1) == 0 && + ( (nBase == 2 && (*p == 'b' || *p == 'B')) + ||(nBase == 16 && (*p == 'h' || *p == 'H')) ) + ) + ; // 101b und F00Dh sind ok + else + { + PushIllegalArgument(); + return ; + } + } + else + fVal = fVal * fBase + n; + p++; + + } + PushDouble( fVal ); + } + else + PushIllegalArgument(); + } +} + + +void ScInterpreter::ScConvert() +{ // Value, FromUnit, ToUnit + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + String aToUnit( GetString() ); + String aFromUnit( GetString() ); + double fVal = GetDouble(); + if ( nGlobalError ) + PushError( nGlobalError); + else + { // erst die angegebene Reihenfolge suchen, wenn nicht gefunden den Kehrwert + double fConv; + if ( ScGlobal::GetUnitConverter()->GetValue( fConv, aFromUnit, aToUnit ) ) + PushDouble( fVal * fConv ); + else if ( ScGlobal::GetUnitConverter()->GetValue( fConv, aToUnit, aFromUnit ) ) + PushDouble( fVal / fConv ); + else + PushNA(); + } + } +} + + +void ScInterpreter::ScRoman() +{ // Value [Mode] + BYTE nParamCount = GetByte(); + if( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + double fMode = (nParamCount == 2) ? ::rtl::math::approxFloor( GetDouble() ) : 0.0; + double fVal = ::rtl::math::approxFloor( GetDouble() ); + if( nGlobalError ) + PushError( nGlobalError); + else if( (fMode >= 0.0) && (fMode < 5.0) && (fVal >= 0.0) && (fVal < 4000.0) ) + { + static const sal_Unicode pChars[] = { 'M', 'D', 'C', 'L', 'X', 'V', 'I' }; + static const USHORT pValues[] = { 1000, 500, 100, 50, 10, 5, 1 }; + static const USHORT nMaxIndex = (USHORT)(sizeof(pValues) / sizeof(pValues[0]) - 1); + + String aRoman; + USHORT nVal = (USHORT) fVal; + USHORT nMode = (USHORT) fMode; + + for( UINT16 i = 0; i <= nMaxIndex / 2; i++ ) + { + USHORT nIndex = 2 * i; + USHORT nDigit = nVal / pValues[ nIndex ]; + + if( (nDigit % 5) == 4 ) + { + USHORT nIndex2 = (nDigit == 4) ? nIndex - 1 : nIndex - 2; + USHORT nSteps = 0; + while( (nSteps < nMode) && (nIndex < nMaxIndex) ) + { + nSteps++; + if( pValues[ nIndex2 ] - pValues[ nIndex + 1 ] <= nVal ) + nIndex++; + else + nSteps = nMode; + } + aRoman += pChars[ nIndex ]; + aRoman += pChars[ nIndex2 ]; + nVal = sal::static_int_cast<USHORT>( nVal + pValues[ nIndex ] ); + nVal = sal::static_int_cast<USHORT>( nVal - pValues[ nIndex2 ] ); + } + else + { + if( nDigit > 4 ) + aRoman += pChars[ nIndex - 1 ]; + aRoman.Expand( aRoman.Len() + (nDigit % 5), pChars[ nIndex ] ); + nVal %= pValues[ nIndex ]; + } + } + + PushString( aRoman ); + } + else + PushIllegalArgument(); + } +} + + +BOOL lcl_GetArabicValue( sal_Unicode cChar, USHORT& rnValue, BOOL& rbIsDec ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBase" ); + switch( cChar ) + { + case 'M': rnValue = 1000; rbIsDec = TRUE; break; + case 'D': rnValue = 500; rbIsDec = FALSE; break; + case 'C': rnValue = 100; rbIsDec = TRUE; break; + case 'L': rnValue = 50; rbIsDec = FALSE; break; + case 'X': rnValue = 10; rbIsDec = TRUE; break; + case 'V': rnValue = 5; rbIsDec = FALSE; break; + case 'I': rnValue = 1; rbIsDec = TRUE; break; + default: return FALSE; + } + return TRUE; +} + + +void ScInterpreter::ScArabic() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArabic" ); + String aRoman( GetString() ); + if( nGlobalError ) + PushError( nGlobalError); + else + { + aRoman.ToUpperAscii(); + + USHORT nValue = 0; + USHORT nValidRest = 3999; + USHORT nCharIndex = 0; + USHORT nCharCount = aRoman.Len(); + BOOL bValid = TRUE; + + while( bValid && (nCharIndex < nCharCount) ) + { + USHORT nDigit1 = 0; + USHORT nDigit2 = 0; + BOOL bIsDec1 = FALSE; + BOOL bIsDec2 = FALSE; + bValid = lcl_GetArabicValue( aRoman.GetChar( nCharIndex ), nDigit1, bIsDec1 ); + if( bValid && (nCharIndex + 1 < nCharCount) ) + bValid = lcl_GetArabicValue( aRoman.GetChar( nCharIndex + 1 ), nDigit2, bIsDec2 ); + if( bValid ) + { + if( nDigit1 >= nDigit2 ) + { + nValue = sal::static_int_cast<USHORT>( nValue + nDigit1 ); + nValidRest %= (nDigit1 * (bIsDec1 ? 5 : 2)); + bValid = (nValidRest >= nDigit1); + if( bValid ) + nValidRest = sal::static_int_cast<USHORT>( nValidRest - nDigit1 ); + nCharIndex++; + } + else if( nDigit1 * 2 != nDigit2 ) + { + USHORT nDiff = nDigit2 - nDigit1; + nValue = sal::static_int_cast<USHORT>( nValue + nDiff ); + bValid = (nValidRest >= nDiff); + if( bValid ) + nValidRest = nDigit1 - 1; + nCharIndex += 2; + } + else + bValid = FALSE; + } + } + if( bValid ) + PushInt( nValue ); + else + PushIllegalArgument(); + } +} + + +void ScInterpreter::ScHyperLink() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScHyperLink" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + double fVal = 0.0; + String aStr; + ScMatValType nResultType = SC_MATVAL_STRING; + + if ( nParamCount == 2 ) + { + switch ( GetStackType() ) + { + case svDouble: + fVal = GetDouble(); + nResultType = SC_MATVAL_VALUE; + break; + case svString: + aStr = GetString(); + break; + case svSingleRef: + case svDoubleRef: + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellEmptyData( pCell)) + nResultType = SC_MATVAL_EMPTY; + else + { + USHORT nErr = GetCellErrCode( pCell ); + if (nErr) + SetError( nErr); + else if (HasCellValueData( pCell)) + { + fVal = GetCellValue( aAdr, pCell ); + nResultType = SC_MATVAL_VALUE; + } + else + GetCellString( aStr, pCell ); + } + } + break; + case svMatrix: + nResultType = GetDoubleOrStringFromMatrix( fVal, aStr); + break; + case svMissing: + case svEmptyCell: + Pop(); + // mimic xcl + fVal = 0.0; + nResultType = SC_MATVAL_VALUE; + break; + default: + PopError(); + SetError( errIllegalArgument); + } + } + String aUrl = GetString(); + ScMatrixRef pResMat = GetNewMat( 1, 2); + if (nGlobalError) + { + fVal = CreateDoubleError( nGlobalError); + nResultType = SC_MATVAL_VALUE; + } + if (nParamCount == 2 || nGlobalError) + { + if (ScMatrix::IsValueType( nResultType)) + pResMat->PutDouble( fVal, 0); + else if (ScMatrix::IsRealStringType( nResultType)) + pResMat->PutString( aStr, 0); + else // EmptyType, EmptyPathType, mimic xcl + pResMat->PutDouble( 0.0, 0 ); + } + else + pResMat->PutString( aUrl, 0 ); + pResMat->PutString( aUrl, 1 ); + bMatrixFormula = true; + PushMatrix(pResMat); + } +} + + +BOOL lclConvertMoney( const String& aSearchUnit, double& rfRate, int& rnDec ) +{ + struct ConvertInfo + { + const sal_Char* pCurrText; + double fRate; + int nDec; + }; + ConvertInfo aConvertTable[] = { + { "EUR", 1.0, 2 }, + { "ATS", 13.7603, 2 }, + { "BEF", 40.3399, 0 }, + { "DEM", 1.95583, 2 }, + { "ESP", 166.386, 0 }, + { "FIM", 5.94573, 2 }, + { "FRF", 6.55957, 2 }, + { "IEP", 0.787564, 2 }, + { "ITL", 1936.27, 0 }, + { "LUF", 40.3399, 0 }, + { "NLG", 2.20371, 2 }, + { "PTE", 200.482, 2 }, + { "GRD", 340.750, 2 }, + { "SIT", 239.640, 2 }, + { "MTL", 0.429300, 2 }, + { "CYP", 0.585274, 2 }, + { "SKK", 30.1260, 2 } + }; + + const size_t nConversionCount = sizeof( aConvertTable ) / sizeof( aConvertTable[0] ); + for ( size_t i = 0; i < nConversionCount; i++ ) + if ( aSearchUnit.EqualsIgnoreCaseAscii( aConvertTable[i].pCurrText ) ) + { + rfRate = aConvertTable[i].fRate; + rnDec = aConvertTable[i].nDec; + return TRUE; + } + return FALSE; +} + +void ScInterpreter::ScEuroConvert() +{ //Value, FromUnit, ToUnit[, FullPrecision, [TriangulationPrecision]] + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 3, 5 ) ) + { + double nPrecision = 0.0; + if ( nParamCount == 5 ) + { + nPrecision = ::rtl::math::approxFloor(GetDouble()); + if ( nPrecision < 3 ) + { + PushIllegalArgument(); + return; + } + } + BOOL bFullPrecision = FALSE; + if ( nParamCount >= 4 ) + bFullPrecision = GetBool(); + String aToUnit( GetString() ); + String aFromUnit( GetString() ); + double fVal = GetDouble(); + if ( nGlobalError ) + PushError( nGlobalError); + else + { + double fRes; + double fFromRate; + double fToRate; + int nFromDec; + int nToDec; + String aEur( RTL_CONSTASCII_USTRINGPARAM("EUR")); + if ( lclConvertMoney( aFromUnit, fFromRate, nFromDec ) + && lclConvertMoney( aToUnit, fToRate, nToDec ) ) + { + if ( aFromUnit.EqualsIgnoreCaseAscii( aToUnit ) ) + fRes = fVal; + else + { + if ( aFromUnit.EqualsIgnoreCaseAscii( aEur ) ) + fRes = fVal * fToRate; + else + { + double fIntermediate = fVal / fFromRate; + if ( nPrecision ) + fIntermediate = ::rtl::math::round( fIntermediate, + (int) nPrecision ); + fRes = fIntermediate * fToRate; + } + if ( !bFullPrecision ) + fRes = ::rtl::math::round( fRes, nToDec ); + } + PushDouble( fRes ); + } + else + PushIllegalArgument(); + } + } +} + + +// BAHTTEXT =================================================================== + +#define UTF8_TH_0 "\340\270\250\340\270\271\340\270\231\340\270\242\340\271\214" +#define UTF8_TH_1 "\340\270\253\340\270\231\340\270\266\340\271\210\340\270\207" +#define UTF8_TH_2 "\340\270\252\340\270\255\340\270\207" +#define UTF8_TH_3 "\340\270\252\340\270\262\340\270\241" +#define UTF8_TH_4 "\340\270\252\340\270\265\340\271\210" +#define UTF8_TH_5 "\340\270\253\340\271\211\340\270\262" +#define UTF8_TH_6 "\340\270\253\340\270\201" +#define UTF8_TH_7 "\340\271\200\340\270\210\340\271\207\340\270\224" +#define UTF8_TH_8 "\340\271\201\340\270\233\340\270\224" +#define UTF8_TH_9 "\340\271\200\340\270\201\340\271\211\340\270\262" +#define UTF8_TH_10 "\340\270\252\340\270\264\340\270\232" +#define UTF8_TH_11 "\340\271\200\340\270\255\340\271\207\340\270\224" +#define UTF8_TH_20 "\340\270\242\340\270\265\340\271\210" +#define UTF8_TH_1E2 "\340\270\243\340\271\211\340\270\255\340\270\242" +#define UTF8_TH_1E3 "\340\270\236\340\270\261\340\270\231" +#define UTF8_TH_1E4 "\340\270\253\340\270\241\340\270\267\340\271\210\340\270\231" +#define UTF8_TH_1E5 "\340\271\201\340\270\252\340\270\231" +#define UTF8_TH_1E6 "\340\270\245\340\271\211\340\270\262\340\270\231" +#define UTF8_TH_DOT0 "\340\270\226\340\271\211\340\270\247\340\270\231" +#define UTF8_TH_BAHT "\340\270\232\340\270\262\340\270\227" +#define UTF8_TH_SATANG "\340\270\252\340\270\225\340\270\262\340\270\207\340\270\204\340\271\214" +#define UTF8_TH_MINUS "\340\270\245\340\270\232" + +#define UTF8_STRINGPARAM( ascii ) ascii, static_cast< xub_StrLen >( sizeof( ascii ) - 1 ) +#define UTF8_CREATE( ascii ) ByteString( UTF8_STRINGPARAM( ascii ) ) +#define UTF8_APPEND( ascii ) Append( UTF8_STRINGPARAM( ascii ) ) +#define UTF8_PREPEND( ascii ) Insert( UTF8_CREATE( ascii ), 0 ) + +// local functions ------------------------------------------------------------ + +namespace { + +inline void lclSplitBlock( double& rfInt, sal_Int32& rnBlock, double fValue, double fSize ) +{ + rnBlock = static_cast< sal_Int32 >( modf( (fValue + 0.1) / fSize, &rfInt ) * fSize + 0.1 ); +} + +/** Appends a digit (0 to 9) to the passed string. */ +void lclAppendDigit( ByteString& rText, sal_Int32 nDigit ) +{ + switch( nDigit ) + { + case 0: rText.UTF8_APPEND( UTF8_TH_0 ); break; + case 1: rText.UTF8_APPEND( UTF8_TH_1 ); break; + case 2: rText.UTF8_APPEND( UTF8_TH_2 ); break; + case 3: rText.UTF8_APPEND( UTF8_TH_3 ); break; + case 4: rText.UTF8_APPEND( UTF8_TH_4 ); break; + case 5: rText.UTF8_APPEND( UTF8_TH_5 ); break; + case 6: rText.UTF8_APPEND( UTF8_TH_6 ); break; + case 7: rText.UTF8_APPEND( UTF8_TH_7 ); break; + case 8: rText.UTF8_APPEND( UTF8_TH_8 ); break; + case 9: rText.UTF8_APPEND( UTF8_TH_9 ); break; + default: DBG_ERRORFILE( "lclAppendDigit - illegal digit" ); + } +} + +/** Appends a value raised to a power of 10: nDigit*10^nPow10. + @param nDigit A digit in the range from 1 to 9. + @param nPow10 A value in the range from 2 to 5. + */ +void lclAppendPow10( ByteString& rText, sal_Int32 nDigit, sal_Int32 nPow10 ) +{ + DBG_ASSERT( (1 <= nDigit) && (nDigit <= 9), "lclAppendPow10 - illegal digit" ); + lclAppendDigit( rText, nDigit ); + switch( nPow10 ) + { + case 2: rText.UTF8_APPEND( UTF8_TH_1E2 ); break; + case 3: rText.UTF8_APPEND( UTF8_TH_1E3 ); break; + case 4: rText.UTF8_APPEND( UTF8_TH_1E4 ); break; + case 5: rText.UTF8_APPEND( UTF8_TH_1E5 ); break; + default: DBG_ERRORFILE( "lclAppendPow10 - illegal power" ); + } +} + +/** Appends a block of 6 digits (value from 1 to 999,999) to the passed string. */ +void lclAppendBlock( ByteString& rText, sal_Int32 nValue ) +{ + DBG_ASSERT( (1 <= nValue) && (nValue <= 999999), "lclAppendBlock - illegal value" ); + if( nValue >= 100000 ) + { + lclAppendPow10( rText, nValue / 100000, 5 ); + nValue %= 100000; + } + if( nValue >= 10000 ) + { + lclAppendPow10( rText, nValue / 10000, 4 ); + nValue %= 10000; + } + if( nValue >= 1000 ) + { + lclAppendPow10( rText, nValue / 1000, 3 ); + nValue %= 1000; + } + if( nValue >= 100 ) + { + lclAppendPow10( rText, nValue / 100, 2 ); + nValue %= 100; + } + if( nValue > 0 ) + { + sal_Int32 nTen = nValue / 10; + sal_Int32 nOne = nValue % 10; + if( nTen >= 1 ) + { + if( nTen >= 3 ) + lclAppendDigit( rText, nTen ); + else if( nTen == 2 ) + rText.UTF8_APPEND( UTF8_TH_20 ); + rText.UTF8_APPEND( UTF8_TH_10 ); + } + if( (nTen > 0) && (nOne == 1) ) + rText.UTF8_APPEND( UTF8_TH_11 ); + else if( nOne > 0 ) + lclAppendDigit( rText, nOne ); + } +} + +} // namespace + +// ---------------------------------------------------------------------------- + +void ScInterpreter::ScBahtText() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBahtText" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1 ) ) + { + double fValue = GetDouble(); + if( nGlobalError ) + { + PushError( nGlobalError); + return; + } + + // sign + bool bMinus = fValue < 0.0; + fValue = fabs( fValue ); + + // round to 2 digits after decimal point, fValue contains Satang as integer + fValue = ::rtl::math::approxFloor( fValue * 100.0 + 0.5 ); + + // split Baht and Satang + double fBaht = 0.0; + sal_Int32 nSatang = 0; + lclSplitBlock( fBaht, nSatang, fValue, 100.0 ); + + ByteString aText; + + // generate text for Baht value + if( fBaht == 0.0 ) + { + if( nSatang == 0 ) + aText.UTF8_APPEND( UTF8_TH_0 ); + } + else while( fBaht > 0.0 ) + { + ByteString aBlock; + sal_Int32 nBlock = 0; + lclSplitBlock( fBaht, nBlock, fBaht, 1.0e6 ); + if( nBlock > 0 ) + lclAppendBlock( aBlock, nBlock ); + // add leading "million", if there will come more blocks + if( fBaht > 0.0 ) + aBlock.UTF8_PREPEND( UTF8_TH_1E6 ); + aText.Insert( aBlock, 0 ); + } + if( aText.Len() > 0 ) + aText.UTF8_APPEND( UTF8_TH_BAHT ); + + // generate text for Satang value + if( nSatang == 0 ) + { + aText.UTF8_APPEND( UTF8_TH_DOT0 ); + } + else + { + lclAppendBlock( aText, nSatang ); + aText.UTF8_APPEND( UTF8_TH_SATANG ); + } + + // add the minus sign + if( bMinus ) + aText.UTF8_PREPEND( UTF8_TH_MINUS ); + + PushString( String( aText, RTL_TEXTENCODING_UTF8 ) ); + } +} + +// ============================================================================ + +void ScInterpreter::ScGetPivotData() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGetPivotData" ); + BYTE nParamCount = GetByte(); + + if ( MustHaveParamCount( nParamCount, 2, 30 ) ) + { + // there must be an even number of args + // target, ref, then field/item pairs + if( (nParamCount % 2) == 1) + goto failed; + + bool bOldSyntax = false; + if ( nParamCount == 2 ) + { + // if the first parameter is a ref, assume old syntax + StackVar eFirstType = GetStackType( 2 ); + if ( eFirstType == svSingleRef || eFirstType == svDoubleRef ) + bOldSyntax = true; + } + + ScDPGetPivotDataField aTarget; // target field, and returns result + std::vector< ScDPGetPivotDataField > aFilters; + String aFilterList; + if ( bOldSyntax ) + aFilterList = GetString(); // old syntax: second parameter is list of constraints + else + { + // new syntax: separate name/value pairs + + USHORT nFilterCount = nParamCount / 2 - 1; + aFilters.resize( nFilterCount ); + + USHORT i = nFilterCount; + while( i-- > 0 ) + { + //! should allow numeric constraint values + aFilters[i].mbValIsStr = TRUE; + aFilters[i].maValStr = GetString(); + + aFilters[i].maFieldName = GetString(); + } + } + + // common to both syntaxes: a reference to the data pilot table + + ScRange aBlock; + switch ( GetStackType() ) + { + case svDoubleRef : + PopDoubleRef( aBlock ); + break; + + case svSingleRef : + { + ScAddress aAddr; + PopSingleRef( aAddr ); + aBlock = aAddr; + break; + } + default: + goto failed; + } + // NOTE : MS Excel docs claim to use the 'most recent' which is not + // exactly the same as what we do in ScDocument::GetDPAtBlock + // However we do need to use GetDPABlock + ScDPObject* pDPObj = pDok->GetDPAtBlock ( aBlock ); + if( NULL == pDPObj) + goto failed; + + if ( bOldSyntax ) + { + // fill aFilters / aTarget from aFilterList string + if ( !pDPObj->ParseFilters( aTarget, aFilters, aFilterList ) ) + goto failed; + } + else + aTarget.maFieldName = GetString(); // new syntax: first parameter is data field name + + if( pDPObj->GetPivotData( aTarget, aFilters ) ) + { + if( aTarget.mbValIsStr ) + PushString( aTarget.maValStr ); + else + PushDouble( aTarget.mnValNum ); + return; + } + } + +failed : + PushError( errNoRef ); +} + diff --git a/sc/source/core/tool/interpr3.cxx b/sc/source/core/tool/interpr3.cxx new file mode 100644 index 000000000000..bcea57703d78 --- /dev/null +++ b/sc/source/core/tool/interpr3.cxx @@ -0,0 +1,4244 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <tools/solar.h> +#include <stdlib.h> +#include <string.h> +#include <rtl/logfile.hxx> + +#include "interpre.hxx" +#include "global.hxx" +#include "compiler.hxx" +#include "cell.hxx" +#include "document.hxx" +#include "dociter.hxx" +#include "scmatrix.hxx" +#include "globstr.hrc" + +#include <math.h> +#include <vector> +#include <algorithm> + +using ::std::vector; +using namespace formula; + +// STATIC DATA ----------------------------------------------------------- + +#define SCdEpsilon 1.0E-7 +#define SC_MAX_ITERATION_COUNT 20 +#define MAX_ANZ_DOUBLE_FOR_SORT 100000 +// PI jetzt als F_PI aus solar.h +//#define PI 3.1415926535897932 + +const double ScInterpreter::fMaxGammaArgument = 171.624376956302; // found experimental +const double fMachEps = ::std::numeric_limits<double>::epsilon(); + +//----------------------------------------------------------------------------- + +class ScDistFunc +{ +public: + virtual double GetValue(double x) const = 0; +}; + +// iteration for inverse distributions + +//template< class T > double lcl_IterateInverse( const T& rFunction, double x0, double x1, bool& rConvError ) + +/** u*w<0.0 fails for values near zero */ +inline bool lcl_HasChangeOfSign( double u, double w ) +{ + return (u < 0.0 && w > 0.0) || (u > 0.0 && w < 0.0); +} + +double lcl_IterateInverse( const ScDistFunc& rFunction, double fAx, double fBx, bool& rConvError ) +{ + rConvError = false; + const double fYEps = 1.0E-307; + const double fXEps = ::std::numeric_limits<double>::epsilon(); + + DBG_ASSERT(fAx<fBx, "IterateInverse: wrong interval"); + + // find enclosing interval + + double fAy = rFunction.GetValue(fAx); + double fBy = rFunction.GetValue(fBx); + double fTemp; + unsigned short nCount; + for (nCount = 0; nCount < 1000 && !lcl_HasChangeOfSign(fAy,fBy); nCount++) + { + if (fabs(fAy) <= fabs(fBy)) + { + fTemp = fAx; + fAx += 2.0 * (fAx - fBx); + if (fAx < 0.0) + fAx = 0.0; + fBx = fTemp; + fBy = fAy; + fAy = rFunction.GetValue(fAx); + } + else + { + fTemp = fBx; + fBx += 2.0 * (fBx - fAx); + fAx = fTemp; + fAy = fBy; + fBy = rFunction.GetValue(fBx); + } + } + + if (fAy == 0.0) + return fAx; + if (fBy == 0.0) + return fBx; + if (!lcl_HasChangeOfSign( fAy, fBy)) + { + rConvError = true; + return 0.0; + } + // inverse quadric interpolation with additional brackets + // set three points + double fPx = fAx; + double fPy = fAy; + double fQx = fBx; + double fQy = fBy; + double fRx = fAx; + double fRy = fAy; + double fSx = 0.5 * (fAx + fBx); // potential next point + bool bHasToInterpolate = true; + nCount = 0; + while ( nCount < 500 && fabs(fRy) > fYEps && + (fBx-fAx) > ::std::max( fabs(fAx), fabs(fBx)) * fXEps ) + { + if (bHasToInterpolate) + { + if (fPy!=fQy && fQy!=fRy && fRy!=fPy) + { + fSx = fPx * fRy * fQy / (fRy-fPy) / (fQy-fPy) + + fRx * fQy * fPy / (fQy-fRy) / (fPy-fRy) + + fQx * fPy * fRy / (fPy-fQy) / (fRy-fQy); + bHasToInterpolate = (fAx < fSx) && (fSx < fBx); // inside the brackets? + } + else + bHasToInterpolate = false; + } + if(!bHasToInterpolate) + { + fSx = 0.5 * (fAx + fBx); + // reset points + fPx = fAx; fPy = fAy; + fQx = fBx; fQy = fBy; + bHasToInterpolate = true; + } + // shift points for next interpolation + fPx = fQx; fQx = fRx; fRx = fSx; + fPy = fQy; fQy = fRy; fRy = rFunction.GetValue(fSx); + // update brackets + if (lcl_HasChangeOfSign( fAy, fRy)) + { + fBx = fRx; fBy = fRy; + } + else + { + fAx = fRx; fAy = fRy; + } + // if last interration brought to small advance, then do bisection next + // time, for safety + bHasToInterpolate = bHasToInterpolate && (fabs(fRy) * 2.0 <= fabs(fQy)); + ++nCount; + } + return fRx; +} + +//----------------------------------------------------------------------------- +// Allgemeine Funktionen +//----------------------------------------------------------------------------- + +void ScInterpreter::ScNoName() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNoName" ); + PushError(errNoName); +} + +void ScInterpreter::ScBadName() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBadName" ); + short nParamCount = GetByte(); + while (nParamCount-- > 0) + { + PopError(); + } + PushError( errNoName); +} + +double ScInterpreter::phi(double x) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::phi" ); + return 0.39894228040143268 * exp(-(x * x) / 2.0); +} + +double ScInterpreter::integralPhi(double x) +{ // Using gauss(x)+0.5 has severe cancellation errors for x<-4 + return 0.5 * ::rtl::math::erfc(-x * 0.7071067811865475); // * 1/sqrt(2) +} + +double ScInterpreter::taylor(double* pPolynom, USHORT nMax, double x) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::taylor" ); + double nVal = pPolynom[nMax]; + for (short i = nMax-1; i >= 0; i--) + { + nVal = pPolynom[i] + (nVal * x); + } + return nVal; +} + +double ScInterpreter::gauss(double x) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::gauss" ); + double t0[] = + { 0.39894228040143268, -0.06649038006690545, 0.00997355701003582, + -0.00118732821548045, 0.00011543468761616, -0.00000944465625950, + 0.00000066596935163, -0.00000004122667415, 0.00000000227352982, + 0.00000000011301172, 0.00000000000511243, -0.00000000000021218 }; + double t2[] = + { 0.47724986805182079, 0.05399096651318805, -0.05399096651318805, + 0.02699548325659403, -0.00449924720943234, -0.00224962360471617, + 0.00134977416282970, -0.00011783742691370, -0.00011515930357476, + 0.00003704737285544, 0.00000282690796889, -0.00000354513195524, + 0.00000037669563126, 0.00000019202407921, -0.00000005226908590, + -0.00000000491799345, 0.00000000366377919, -0.00000000015981997, + -0.00000000017381238, 0.00000000002624031, 0.00000000000560919, + -0.00000000000172127, -0.00000000000008634, 0.00000000000007894 }; + double t4[] = + { 0.49996832875816688, 0.00013383022576489, -0.00026766045152977, + 0.00033457556441221, -0.00028996548915725, 0.00018178605666397, + -0.00008252863922168, 0.00002551802519049, -0.00000391665839292, + -0.00000074018205222, 0.00000064422023359, -0.00000017370155340, + 0.00000000909595465, 0.00000000944943118, -0.00000000329957075, + 0.00000000029492075, 0.00000000011874477, -0.00000000004420396, + 0.00000000000361422, 0.00000000000143638, -0.00000000000045848 }; + double asympt[] = { -1.0, 1.0, -3.0, 15.0, -105.0 }; + + double xAbs = fabs(x); + USHORT xShort = (USHORT)::rtl::math::approxFloor(xAbs); + double nVal = 0.0; + if (xShort == 0) + nVal = taylor(t0, 11, (xAbs * xAbs)) * xAbs; + else if ((xShort >= 1) && (xShort <= 2)) + nVal = taylor(t2, 23, (xAbs - 2.0)); + else if ((xShort >= 3) && (xShort <= 4)) + nVal = taylor(t4, 20, (xAbs - 4.0)); + else + nVal = 0.5 + phi(xAbs) * taylor(asympt, 4, 1.0 / (xAbs * xAbs)) / xAbs; + if (x < 0.0) + return -nVal; + else + return nVal; +} + +// +// #i26836# new gaussinv implementation by Martin Eitzenberger <m.eitzenberger@unix.net> +// + +double ScInterpreter::gaussinv(double x) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::gaussinv" ); + double q,t,z; + + q=x-0.5; + + if(fabs(q)<=.425) + { + t=0.180625-q*q; + + z= + q* + ( + ( + ( + ( + ( + ( + ( + t*2509.0809287301226727+33430.575583588128105 + ) + *t+67265.770927008700853 + ) + *t+45921.953931549871457 + ) + *t+13731.693765509461125 + ) + *t+1971.5909503065514427 + ) + *t+133.14166789178437745 + ) + *t+3.387132872796366608 + ) + / + ( + ( + ( + ( + ( + ( + ( + t*5226.495278852854561+28729.085735721942674 + ) + *t+39307.89580009271061 + ) + *t+21213.794301586595867 + ) + *t+5394.1960214247511077 + ) + *t+687.1870074920579083 + ) + *t+42.313330701600911252 + ) + *t+1.0 + ); + + } + else + { + if(q>0) t=1-x; + else t=x; + + t=sqrt(-log(t)); + + if(t<=5.0) + { + t+=-1.6; + + z= + ( + ( + ( + ( + ( + ( + ( + t*7.7454501427834140764e-4+0.0227238449892691845833 + ) + *t+0.24178072517745061177 + ) + *t+1.27045825245236838258 + ) + *t+3.64784832476320460504 + ) + *t+5.7694972214606914055 + ) + *t+4.6303378461565452959 + ) + *t+1.42343711074968357734 + ) + / + ( + ( + ( + ( + ( + ( + ( + t*1.05075007164441684324e-9+5.475938084995344946e-4 + ) + *t+0.0151986665636164571966 + ) + *t+0.14810397642748007459 + ) + *t+0.68976733498510000455 + ) + *t+1.6763848301838038494 + ) + *t+2.05319162663775882187 + ) + *t+1.0 + ); + + } + else + { + t+=-5.0; + + z= + ( + ( + ( + ( + ( + ( + ( + t*2.01033439929228813265e-7+2.71155556874348757815e-5 + ) + *t+0.0012426609473880784386 + ) + *t+0.026532189526576123093 + ) + *t+0.29656057182850489123 + ) + *t+1.7848265399172913358 + ) + *t+5.4637849111641143699 + ) + *t+6.6579046435011037772 + ) + / + ( + ( + ( + ( + ( + ( + ( + t*2.04426310338993978564e-15+1.4215117583164458887e-7 + ) + *t+1.8463183175100546818e-5 + ) + *t+7.868691311456132591e-4 + ) + *t+0.0148753612908506148525 + ) + *t+0.13692988092273580531 + ) + *t+0.59983220655588793769 + ) + *t+1.0 + ); + + } + + if(q<0.0) z=-z; + } + + return z; +} + +double ScInterpreter::Fakultaet(double x) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::Fakultaet" ); + x = ::rtl::math::approxFloor(x); + if (x < 0.0) + return 0.0; + else if (x == 0.0) + return 1.0; + else if (x <= 170.0) + { + double fTemp = x; + while (fTemp > 2.0) + { + fTemp--; + x *= fTemp; + } + } + else + SetError(errNoValue); +/* // Stirlingsche Naeherung zu ungenau + else + x = pow(x/exp(1), x) * sqrt(x) * SQRT_2_PI * (1.0 + 1.0 / (12.0 * x)); +*/ + return x; +} + +double ScInterpreter::BinomKoeff(double n, double k) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::BinomKoeff" ); + double nVal = 0.0; + k = ::rtl::math::approxFloor(k); + if (n < k) + nVal = 0.0; + else if (k == 0.0) + nVal = 1.0; + else + { + nVal = n/k; + n--; + k--; + while (k > 0.0) + { + nVal *= n/k; + k--; + n--; + } +/* + double f1 = n; // Zaehler + double f2 = k; // Nenner + n--; + k--; + while (k > 0.0) + { + f2 *= k; + f1 *= n; + k--; + n--; + } + nVal = f1 / f2; +*/ + } + return nVal; +} + + +// The algorithm is based on lanczos13m53 in lanczos.hpp +// in math library from http://www.boost.org +/** you must ensure fZ>0 + Uses a variant of the Lanczos sum with a rational function. */ +double lcl_getLanczosSum(double fZ) +{ + const double fNum[13] ={ + 23531376880.41075968857200767445163675473, + 42919803642.64909876895789904700198885093, + 35711959237.35566804944018545154716670596, + 17921034426.03720969991975575445893111267, + 6039542586.35202800506429164430729792107, + 1439720407.311721673663223072794912393972, + 248874557.8620541565114603864132294232163, + 31426415.58540019438061423162831820536287, + 2876370.628935372441225409051620849613599, + 186056.2653952234950402949897160456992822, + 8071.672002365816210638002902272250613822, + 210.8242777515793458725097339207133627117, + 2.506628274631000270164908177133837338626 + }; + const double fDenom[13] = { + 0, + 39916800, + 120543840, + 150917976, + 105258076, + 45995730, + 13339535, + 2637558, + 357423, + 32670, + 1925, + 66, + 1 + }; + // Horner scheme + double fSumNum; + double fSumDenom; + int nI; + double fZInv; + if (fZ<=1.0) + { + fSumNum = fNum[12]; + fSumDenom = fDenom[12]; + for (nI = 11; nI >= 0; --nI) + { + fSumNum *= fZ; + fSumNum += fNum[nI]; + fSumDenom *= fZ; + fSumDenom += fDenom[nI]; + } + } + else + // Cancel down with fZ^12; Horner scheme with reverse coefficients + { + fZInv = 1/fZ; + fSumNum = fNum[0]; + fSumDenom = fDenom[0]; + for (nI = 1; nI <=12; ++nI) + { + fSumNum *= fZInv; + fSumNum += fNum[nI]; + fSumDenom *= fZInv; + fSumDenom += fDenom[nI]; + } + } + return fSumNum/fSumDenom; +} + +// The algorithm is based on tgamma in gamma.hpp +// in math library from http://www.boost.org +/** You must ensure fZ>0; fZ>171.624376956302 will overflow. */ +double lcl_GetGammaHelper(double fZ) +{ + double fGamma = lcl_getLanczosSum(fZ); + const double fg = 6.024680040776729583740234375; + double fZgHelp = fZ + fg - 0.5; + // avoid intermediate overflow + double fHalfpower = pow( fZgHelp, fZ / 2 - 0.25); + fGamma *= fHalfpower; + fGamma /= exp(fZgHelp); + fGamma *= fHalfpower; + if (fZ <= 20.0 && fZ == ::rtl::math::approxFloor(fZ)) + fGamma = ::rtl::math::round(fGamma); + return fGamma; +} + +// The algorithm is based on tgamma in gamma.hpp +// in math library from http://www.boost.org +/** You must ensure fZ>0 */ +double lcl_GetLogGammaHelper(double fZ) +{ + const double fg = 6.024680040776729583740234375; + double fZgHelp = fZ + fg - 0.5; + return log( lcl_getLanczosSum(fZ)) + (fZ-0.5) * log(fZgHelp) - fZgHelp; +} + +/** You must ensure non integer arguments for fZ<1 */ +double ScInterpreter::GetGamma(double fZ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetGamma" ); + const double fLogPi = log(F_PI); + const double fLogDblMax = log( ::std::numeric_limits<double>::max()); + + if (fZ > fMaxGammaArgument) + { + SetError(errIllegalFPOperation); + return HUGE_VAL; + } + + if (fZ >= 1.0) + return lcl_GetGammaHelper(fZ); + + if (fZ >= 0.5) // shift to x>=1 using Gamma(x)=Gamma(x+1)/x + return lcl_GetGammaHelper(fZ+1) / fZ; + + if (fZ >= -0.5) // shift to x>=1, might overflow + { + double fLogTest = lcl_GetLogGammaHelper(fZ+2) - log(fZ+1) - log( fabs(fZ)); + if (fLogTest >= fLogDblMax) + { + SetError( errIllegalFPOperation); + return HUGE_VAL; + } + return lcl_GetGammaHelper(fZ+2) / (fZ+1) / fZ; + } + // fZ<-0.5 + // Use Euler's reflection formula: gamma(x)= pi/ ( gamma(1-x)*sin(pi*x) ) + double fLogDivisor = lcl_GetLogGammaHelper(1-fZ) + log( fabs( ::rtl::math::sin( F_PI*fZ))); + if (fLogDivisor - fLogPi >= fLogDblMax) // underflow + return 0.0; + + if (fLogDivisor<0.0) + if (fLogPi - fLogDivisor > fLogDblMax) // overflow + { + SetError(errIllegalFPOperation); + return HUGE_VAL; + } + + return exp( fLogPi - fLogDivisor) * ((::rtl::math::sin( F_PI*fZ) < 0.0) ? -1.0 : 1.0); +} + + +/** You must ensure fZ>0 */ +double ScInterpreter::GetLogGamma(double fZ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetLogGamma" ); + if (fZ >= fMaxGammaArgument) + return lcl_GetLogGammaHelper(fZ); + if (fZ >= 1.0) + return log(lcl_GetGammaHelper(fZ)); + if (fZ >= 0.5) + return log( lcl_GetGammaHelper(fZ+1) / fZ); + return lcl_GetLogGammaHelper(fZ+2) - log(fZ+1) - log(fZ); +} + +double ScInterpreter::GetFDist(double x, double fF1, double fF2) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetFDist" ); + double arg = fF2/(fF2+fF1*x); + double alpha = fF2/2.0; + double beta = fF1/2.0; + return (GetBetaDist(arg, alpha, beta)); +/* + double Z = (pow(fF,1.0/3.0)*(1.0-2.0/(9.0*fF2)) - (1.0-2.0/(9.0*fF1))) / + sqrt(2.0/(9.0*fF1) + pow(fF,2.0/3.0)*2.0/(9.0*fF2)); + return (0.5-gauss(Z)); +*/ +} + +double ScInterpreter::GetTDist(double T, double fDF) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetTDist" ); + return 0.5 * GetBetaDist(fDF/(fDF+T*T), fDF/2.0, 0.5); +/* + USHORT DF = (USHORT) fDF; + double A = T / sqrt(DF); + double B = 1.0 + A*A; + double R; + if (DF == 1) + R = 0.5 + atan(A)/F_PI; + else if (DF % 2 == 0) + { + double S0 = A/(2.0 * sqrt(B)); + double C0 = S0; + for (USHORT i = 2; i <= DF-2; i+=2) + { + C0 *= (1.0 - 1.0/(double)i)/B; + S0 += C0; + } + R = 0.5 + S0; + } + else + { + double S1 = A / (B * F_PI); + double C1 = S1; + for (USHORT i = 3; i <= DF-2; i+=2) + { + C1 *= (1.0 - 1.0/(double)i)/B; + S1 += C1; + } + R = 0.5 + atan(A)/F_PI + S1; + } + return 1.0 - R; +*/ +} + +// for LEGACY.CHIDIST, returns right tail, fDF=degrees of freedom +/** You must ensure fDF>0.0 */ +double ScInterpreter::GetChiDist(double fX, double fDF) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetChiDist" ); + if (fX <= 0.0) + return 1.0; // see ODFF + else + return GetUpRegIGamma( fDF/2.0, fX/2.0); +} + +// ready for ODF 1.2 +// for ODF CHISQDIST; cumulative distribution function, fDF=degrees of freedom +// returns left tail +/** You must ensure fDF>0.0 */ +double ScInterpreter::GetChiSqDistCDF(double fX, double fDF) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetChiSqDistCDF" ); + if (fX <= 0.0) + return 0.0; // see ODFF + else + return GetLowRegIGamma( fDF/2.0, fX/2.0); +} + +double ScInterpreter::GetChiSqDistPDF(double fX, double fDF) +{ + // you must ensure fDF is positive integer + double fValue; + double fCount; + if (fX <= 0.0) + return 0.0; // see ODFF + if (fDF*fX > 1391000.0) + { + // intermediate invalid values, use log + fValue = exp((0.5*fDF - 1) * log(fX*0.5) - 0.5 * fX - log(2.0) - GetLogGamma(0.5*fDF)); + } + else // fDF is small in most cases, we can iterate + { + if (fmod(fDF,2.0)<0.5) + { + // even + fValue = 0.5; + fCount = 2.0; + } + else + { + fValue = 1/sqrt(fX*2*F_PI); + fCount = 1.0; + } + while ( fCount < fDF) + { + fValue *= (fX / fCount); + fCount += 2.0; + } + if (fX>=1425.0) // underflow in e^(-x/2) + fValue = exp(log(fValue)-fX/2); + else + fValue *= exp(-fX/2); + } + return fValue; +} + +void ScInterpreter::ScChiSqDist() +{ + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) + return; + double fX; + bool bCumulative; + if (nParamCount == 3) + bCumulative = GetBool(); + else + bCumulative = true; + double fDF = ::rtl::math::approxFloor(GetDouble()); + if (fDF < 1.0) + PushIllegalArgument(); + else + { + fX = GetDouble(); + if (bCumulative) + PushDouble(GetChiSqDistCDF(fX,fDF)); + else + PushDouble(GetChiSqDistPDF(fX,fDF)); + } +} + +void ScInterpreter::ScGamma() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGamma" ); + double x = GetDouble(); + double fResult; + if (x <= 0.0 && x == ::rtl::math::approxFloor(x)) + PushIllegalArgument(); + else + { + fResult = GetGamma(x); + if (nGlobalError) + { + PushError( nGlobalError); + return; + } + PushDouble(fResult); + } +} + + +void ScInterpreter::ScLogGamma() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLogGamma" ); + double x = GetDouble(); + if (x > 0.0) // constraint from ODFF + PushDouble( GetLogGamma(x)); + else + PushIllegalArgument(); +} + +double ScInterpreter::GetBeta(double fAlpha, double fBeta) +{ + double fA; + double fB; + if (fAlpha > fBeta) + { + fA = fAlpha; fB = fBeta; + } + else + { + fA = fBeta; fB = fAlpha; + } + if (fA+fB < fMaxGammaArgument) // simple case + return GetGamma(fA)/GetGamma(fA+fB)*GetGamma(fB); + // need logarithm + // GetLogGamma is not accurate enough, back to Lanczos for all three + // GetGamma and arrange factors newly. + const double fg = 6.024680040776729583740234375; //see GetGamma + double fgm = fg - 0.5; + double fLanczos = lcl_getLanczosSum(fA); + fLanczos /= lcl_getLanczosSum(fA+fB); + fLanczos *= lcl_getLanczosSum(fB); + double fABgm = fA+fB+fgm; + fLanczos *= sqrt((fABgm/(fA+fgm))/(fB+fgm)); + double fTempA = fB/(fA+fgm); // (fA+fgm)/fABgm = 1 / ( 1 + fB/(fA+fgm)) + double fTempB = fA/(fB+fgm); + double fResult = exp(-fA * ::rtl::math::log1p(fTempA) + -fB * ::rtl::math::log1p(fTempB)-fgm); + fResult *= fLanczos; + return fResult; +} + +// Same as GetBeta but with logarithm +double ScInterpreter::GetLogBeta(double fAlpha, double fBeta) +{ + double fA; + double fB; + if (fAlpha > fBeta) + { + fA = fAlpha; fB = fBeta; + } + else + { + fA = fBeta; fB = fAlpha; + } + const double fg = 6.024680040776729583740234375; //see GetGamma + double fgm = fg - 0.5; + double fLanczos = lcl_getLanczosSum(fA); + fLanczos /= lcl_getLanczosSum(fA+fB); + fLanczos *= lcl_getLanczosSum(fB); + double fLogLanczos = log(fLanczos); + double fABgm = fA+fB+fgm; + fLogLanczos += 0.5*(log(fABgm)-log(fA+fgm)-log(fB+fgm)); + double fTempA = fB/(fA+fgm); // (fA+fgm)/fABgm = 1 / ( 1 + fB/(fA+fgm)) + double fTempB = fA/(fB+fgm); + double fResult = -fA * ::rtl::math::log1p(fTempA) + -fB * ::rtl::math::log1p(fTempB)-fgm; + fResult += fLogLanczos; + return fResult; +} + +// beta distribution probability density function +double ScInterpreter::GetBetaDistPDF(double fX, double fA, double fB) +{ + // special cases + if (fA == 1.0) // result b*(1-x)^(b-1) + { + if (fB == 1.0) + return 1.0; + if (fB == 2.0) + return -2.0*fX + 2.0; + if (fX == 1.0 && fB < 1.0) + { + SetError(errIllegalArgument); + return HUGE_VAL; + } + if (fX <= 0.01) + return fB + fB * ::rtl::math::expm1((fB-1.0) * ::rtl::math::log1p(-fX)); + else + return fB * pow(0.5-fX+0.5,fB-1.0); + } + if (fB == 1.0) // result a*x^(a-1) + { + if (fA == 2.0) + return fA * fX; + if (fX == 0.0 && fA < 1.0) + { + SetError(errIllegalArgument); + return HUGE_VAL; + } + return fA * pow(fX,fA-1); + } + if (fX <= 0.0) + { + if (fA < 1.0 && fX == 0.0) + { + SetError(errIllegalArgument); + return HUGE_VAL; + } + else + return 0.0; + } + if (fX >= 1.0) + { + if (fB < 1.0 && fX == 1.0) + { + SetError(errIllegalArgument); + return HUGE_VAL; + } + else + return 0.0; + } + + // normal cases; result x^(a-1)*(1-x)^(b-1)/Beta(a,b) + const double fLogDblMax = log( ::std::numeric_limits<double>::max()); + const double fLogDblMin = log( ::std::numeric_limits<double>::min()); + double fLogY = (fX < 0.1) ? ::rtl::math::log1p(-fX) : log(0.5-fX+0.5); + double fLogX = log(fX); + double fAm1 = fA-1.0; + double fBm1 = fB-1.0; + double fLogBeta = GetLogBeta(fA,fB); + // check whether parts over- or underflow + if ( fAm1 * fLogX < fLogDblMax && fAm1 * fLogX > fLogDblMin + && fBm1 * fLogY < fLogDblMax && fBm1* fLogY > fLogDblMin + && fLogBeta < fLogDblMax && fLogBeta > fLogDblMin ) + return pow(fX,fA-1.0) * pow(0.5-fX+0.5,fB-1.0) / GetBeta(fA,fB); + else // need logarithm; + // might overflow as a whole, but seldom, not worth to pre-detect it + return exp((fA-1.0)*fLogX + (fB-1.0)* fLogY - fLogBeta); +} + + +/* + x^a * (1-x)^b + I_x(a,b) = ---------------- * result of ContFrac + a * Beta(a,b) +*/ +double lcl_GetBetaHelperContFrac(double fX, double fA, double fB) +{ // like old version + double a1, b1, a2, b2, fnorm, apl2m, d2m, d2m1, cfnew, cf; + a1 = 1.0; b1 = 1.0; + b2 = 1.0 - (fA+fB)/(fA+1.0)*fX; + if (b2 == 0.0) + { + a2 = 0.0; + fnorm = 1.0; + cf = 1.0; + } + else + { + a2 = 1.0; + fnorm = 1.0/b2; + cf = a2*fnorm; + } + cfnew = 1.0; + double rm = 1.0; + + const double fMaxIter = 50000.0; + // loop security, normal cases converge in less than 100 iterations. + // FIXME: You will get so much iteratons for fX near mean, + // I do not know a better algorithm. + bool bfinished = false; + do + { + apl2m = fA + 2.0*rm; + d2m = rm*(fB-rm)*fX/((apl2m-1.0)*apl2m); + d2m1 = -(fA+rm)*(fA+fB+rm)*fX/(apl2m*(apl2m+1.0)); + a1 = (a2+d2m*a1)*fnorm; + b1 = (b2+d2m*b1)*fnorm; + a2 = a1 + d2m1*a2*fnorm; + b2 = b1 + d2m1*b2*fnorm; + if (b2 != 0.0) + { + fnorm = 1.0/b2; + cfnew = a2*fnorm; + bfinished = (fabs(cf-cfnew) < fabs(cf)*fMachEps); + } + cf = cfnew; + rm += 1.0; + } + while (rm < fMaxIter && !bfinished); + return cf; +} + +// cumulative distribution function, normalized +double ScInterpreter::GetBetaDist(double fXin, double fAlpha, double fBeta) +{ +// special cases + if (fXin <= 0.0) // values are valid, see spec + return 0.0; + if (fXin >= 1.0) // values are valid, see spec + return 1.0; + if (fBeta == 1.0) + return pow(fXin, fAlpha); + if (fAlpha == 1.0) + // 1.0 - pow(1.0-fX,fBeta) is not accurate enough + return -::rtl::math::expm1(fBeta * ::rtl::math::log1p(-fXin)); + //FIXME: need special algorithm for fX near fP for large fA,fB + double fResult; + // I use always continued fraction, power series are neither + // faster nor more accurate. + double fY = (0.5-fXin)+0.5; + double flnY = ::rtl::math::log1p(-fXin); + double fX = fXin; + double flnX = log(fXin); + double fA = fAlpha; + double fB = fBeta; + bool bReflect = fXin > fAlpha/(fAlpha+fBeta); + if (bReflect) + { + fA = fBeta; + fB = fAlpha; + fX = fY; + fY = fXin; + flnX = flnY; + flnY = log(fXin); + } + fResult = lcl_GetBetaHelperContFrac(fX,fA,fB); + fResult = fResult/fA; + double fP = fA/(fA+fB); + double fQ = fB/(fA+fB); + double fTemp; + if (fA > 1.0 && fB > 1.0 && fP < 0.97 && fQ < 0.97) //found experimental + fTemp = GetBetaDistPDF(fX,fA,fB)*fX*fY; + else + fTemp = exp(fA*flnX + fB*flnY - GetLogBeta(fA,fB)); + fResult *= fTemp; + if (bReflect) + fResult = 0.5 - fResult + 0.5; + if (fResult > 1.0) // ensure valid range + fResult = 1.0; + if (fResult < 0.0) + fResult = 0.0; + return fResult; +} + + void ScInterpreter::ScBetaDist() + { + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 3, 6 ) ) // expanded, see #i91547# + return; + double fLowerBound, fUpperBound; + double alpha, beta, x; + bool bIsCumulative; + if (nParamCount == 6) + bIsCumulative = GetBool(); + else + bIsCumulative = true; + if (nParamCount >= 5) + fUpperBound = GetDouble(); + else + fUpperBound = 1.0; + if (nParamCount >= 4) + fLowerBound = GetDouble(); + else + fLowerBound = 0.0; + beta = GetDouble(); + alpha = GetDouble(); + x = GetDouble(); + double fScale = fUpperBound - fLowerBound; + if (fScale <= 0.0 || alpha <= 0.0 || beta <= 0.0) + { + PushIllegalArgument(); + return; + } + if (bIsCumulative) // cumulative distribution function + { + // special cases + if (x < fLowerBound) + { + PushDouble(0.0); return; //see spec + } + if (x > fUpperBound) + { + PushDouble(1.0); return; //see spec + } + // normal cases + x = (x-fLowerBound)/fScale; // convert to standard form + PushDouble(GetBetaDist(x, alpha, beta)); + return; + } + else // probability density function + { + if (x < fLowerBound || x > fUpperBound) + { + PushDouble(0.0); + return; + } + x = (x-fLowerBound)/fScale; + PushDouble(GetBetaDistPDF(x, alpha, beta)/fScale); + return; + } +} + +void ScInterpreter::ScPhi() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPhi" ); + PushDouble(phi(GetDouble())); +} + +void ScInterpreter::ScGauss() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGauss" ); + PushDouble(gauss(GetDouble())); +} + +void ScInterpreter::ScFisher() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFisher" ); + double fVal = GetDouble(); + if (fabs(fVal) >= 1.0) + PushIllegalArgument(); + else + PushDouble( ::rtl::math::atanh( fVal)); +} + +void ScInterpreter::ScFisherInv() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFisherInv" ); + PushDouble( tanh( GetDouble())); +} + +void ScInterpreter::ScFact() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFact" ); + double nVal = GetDouble(); + if (nVal < 0.0) + PushIllegalArgument(); + else + PushDouble(Fakultaet(nVal)); +} + +void ScInterpreter::ScKombin() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScKombin" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + double k = ::rtl::math::approxFloor(GetDouble()); + double n = ::rtl::math::approxFloor(GetDouble()); + if (k < 0.0 || n < 0.0 || k > n) + PushIllegalArgument(); + else + PushDouble(BinomKoeff(n, k)); + } +} + +void ScInterpreter::ScKombin2() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScKombin2" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + double k = ::rtl::math::approxFloor(GetDouble()); + double n = ::rtl::math::approxFloor(GetDouble()); + if (k < 0.0 || n < 0.0 || k > n) + PushIllegalArgument(); + else + PushDouble(BinomKoeff(n + k - 1, k)); + } +} + +void ScInterpreter::ScVariationen() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScVariationen" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + double k = ::rtl::math::approxFloor(GetDouble()); + double n = ::rtl::math::approxFloor(GetDouble()); + if (n < 0.0 || k < 0.0 || k > n) + PushIllegalArgument(); + else if (k == 0.0) + PushInt(1); // (n! / (n - 0)!) == 1 + else + { + double nVal = n; + for (ULONG i = (ULONG)k-1; i >= 1; i--) + nVal *= n-(double)i; + PushDouble(nVal); + } + } +} + +void ScInterpreter::ScVariationen2() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScVariationen2" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + double k = ::rtl::math::approxFloor(GetDouble()); + double n = ::rtl::math::approxFloor(GetDouble()); + if (n < 0.0 || k < 0.0 || k > n) + PushIllegalArgument(); + else + PushDouble(pow(n,k)); + } +} + +void ScInterpreter::ScB() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScB" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 3, 4 ) ) + return ; + if (nParamCount == 3) + { + double x = ::rtl::math::approxFloor(GetDouble()); + double p = GetDouble(); + double n = ::rtl::math::approxFloor(GetDouble()); + if (n < 0.0 || x < 0.0 || x > n || p < 0.0 || p > 1.0) + PushIllegalArgument(); + else + { + double q = 1.0 - p; + double fFactor = pow(q, n); + if (fFactor == 0.0) + { + fFactor = pow(p, n); + if (fFactor == 0.0) + PushNoValue(); + else + { + ULONG max = (ULONG) (n - x); + for (ULONG i = 0; i < max && fFactor > 0.0; i++) + fFactor *= (n-i)/(i+1)*q/p; + PushDouble(fFactor); + } + } + else + { + ULONG max = (ULONG) x; + for (ULONG i = 0; i < max && fFactor > 0.0; i++) + fFactor *= (n-i)/(i+1)*p/q; + PushDouble(fFactor); + } + } + } + else if (nParamCount == 4) + { + double xe = GetDouble(); + double xs = GetDouble(); + double p = GetDouble(); + double n = GetDouble(); +// alter Stand 300-SC +// if ((xs < n) && (xe < n) && (p < 1.0)) +// { +// double Varianz = sqrt(n * p * (1.0 - p)); +// xs = fabs(xs - (n * p /* / 2.0 STE */ )); +// xe = fabs(xe - (n * p /* / 2.0 STE */ )); +//// STE double nVal = gauss((xs + 0.5) / Varianz) + gauss((xe + 0.5) / Varianz); +// double nVal = fabs(gauss(xs / Varianz) - gauss(xe / Varianz)); +// PushDouble(nVal); +// } + bool bIsValidX = ( 0.0 <= xs && xs <= xe && xe <= n); + if ( bIsValidX && 0.0 < p && p < 1.0 ) + { + double q = 1.0 - p; + double fFactor = pow(q, n); + if (fFactor == 0.0) + { + fFactor = pow(p, n); + if (fFactor == 0.0) + PushNoValue(); + else + { + double fSum = 0.0; + ULONG max; + if (xe < (ULONG) n) + max = (ULONG) (n-xe)-1; + else + max = 0; + ULONG i; + for (i = 0; i < max && fFactor > 0.0; i++) + fFactor *= (n-i)/(i+1)*q/p; + if (xs < (ULONG) n) + max = (ULONG) (n-xs); + else + fSum = fFactor; + for (; i < max && fFactor > 0.0; i++) + { + fFactor *= (n-i)/(i+1)*q/p; + fSum += fFactor; + } + PushDouble(fSum); + } + } + else + { + ULONG max; + double fSum; + if ( (ULONG) xs == 0) + { + fSum = fFactor; + max = 0; + } + else + { + max = (ULONG) xs-1; + fSum = 0.0; + } + ULONG i; + for (i = 0; i < max && fFactor > 0.0; i++) + fFactor *= (n-i)/(i+1)*p/q; + if ((ULONG)xe == 0) // beide 0 + fSum = fFactor; + else + max = (ULONG) xe; + for (; i < max && fFactor > 0.0; i++) + { + fFactor *= (n-i)/(i+1)*p/q; + fSum += fFactor; + } + PushDouble(fSum); + } + } + else + { + if ( bIsValidX ) // not(0<p<1) + { + if ( p == 0.0 ) + PushDouble( (xs == 0.0) ? 1.0 : 0.0 ); + else if ( p == 1.0 ) + PushDouble( (xe == n) ? 1.0 : 0.0 ); + else + PushIllegalArgument(); + } + else + PushIllegalArgument(); + } + } +} + +void ScInterpreter::ScBinomDist() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBinomDist" ); + if ( MustHaveParamCount( GetByte(), 4 ) ) + { + double kum = GetDouble(); // 0 oder 1 + double p = GetDouble(); // p + double n = ::rtl::math::approxFloor(GetDouble()); // n + double x = ::rtl::math::approxFloor(GetDouble()); // x + double fFactor, q, fSum; + if (n < 0.0 || x < 0.0 || x > n || p < 0.0 || p > 1.0) + PushIllegalArgument(); + else if (kum == 0.0) // Dichte + { + q = 1.0 - p; + fFactor = pow(q, n); + if (fFactor == 0.0) + { + fFactor = pow(p, n); + if (fFactor == 0.0) + PushNoValue(); + else + { + ULONG max = (ULONG) (n - x); + for (ULONG i = 0; i < max && fFactor > 0.0; i++) + fFactor *= (n-i)/(i+1)*q/p; + PushDouble(fFactor); + } + } + else + { + ULONG max = (ULONG) x; + for (ULONG i = 0; i < max && fFactor > 0.0; i++) + fFactor *= (n-i)/(i+1)*p/q; + PushDouble(fFactor); + } + } + else // Verteilung + { + if (n == x) + PushDouble(1.0); + else + { + q = 1.0 - p; + fFactor = pow(q, n); + if (fFactor == 0.0) + { + fFactor = pow(p, n); + if (fFactor == 0.0) + PushNoValue(); + else + { + fSum = 1.0 - fFactor; + ULONG max = (ULONG) (n - x) - 1; + for (ULONG i = 0; i < max && fFactor > 0.0; i++) + { + fFactor *= (n-i)/(i+1)*q/p; + fSum -= fFactor; + } + if (fSum < 0.0) + PushDouble(0.0); + else + PushDouble(fSum); + } + } + else + { + fSum = fFactor; + ULONG max = (ULONG) x; + for (ULONG i = 0; i < max && fFactor > 0.0; i++) + { + fFactor *= (n-i)/(i+1)*p/q; + fSum += fFactor; + } + PushDouble(fSum); + } + } + } + } +} + +void ScInterpreter::ScCritBinom() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCritBinom" ); + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double alpha = GetDouble(); // alpha + double p = GetDouble(); // p + double n = ::rtl::math::approxFloor(GetDouble()); + if (n < 0.0 || alpha <= 0.0 || alpha >= 1.0 || p < 0.0 || p > 1.0) + PushIllegalArgument(); + else + { + double q = 1.0 - p; + double fFactor = pow(q,n); + if (fFactor == 0.0) + { + fFactor = pow(p, n); + if (fFactor == 0.0) + PushNoValue(); + else + { + double fSum = 1.0 - fFactor; ULONG max = (ULONG) n; + ULONG i; + + for ( i = 0; i < max && fSum >= alpha; i++) + { + fFactor *= (n-i)/(i+1)*q/p; + fSum -= fFactor; + } + PushDouble(n-i); + } + } + else + { + double fSum = fFactor; ULONG max = (ULONG) n; + ULONG i; + + for ( i = 0; i < max && fSum < alpha; i++) + { + fFactor *= (n-i)/(i+1)*p/q; + fSum += fFactor; + } + PushDouble(i); + } + } + } +} + +void ScInterpreter::ScNegBinomDist() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNegBinomDist" ); + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double p = GetDouble(); // p + double r = GetDouble(); // r + double x = GetDouble(); // x + if (r < 0.0 || x < 0.0 || p < 0.0 || p > 1.0) + PushIllegalArgument(); + else + { + double q = 1.0 - p; + double fFactor = pow(p,r); + for (double i = 0.0; i < x; i++) + fFactor *= (i+r)/(i+1.0)*q; + PushDouble(fFactor); + } + } +} + +void ScInterpreter::ScNormDist() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNormDist" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 3, 4)) + return; + bool bCumulative = nParamCount == 4 ? GetBool() : true; + double sigma = GetDouble(); // standard deviation + double mue = GetDouble(); // mean + double x = GetDouble(); // x + if (sigma <= 0.0) + { + PushIllegalArgument(); + return; + } + if (bCumulative) + PushDouble(integralPhi((x-mue)/sigma)); + else + PushDouble(phi((x-mue)/sigma)/sigma); +} + +void ScInterpreter::ScLogNormDist() //expanded, see #i100119# +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLogNormDist" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 1, 4)) + return; + bool bCumulative = nParamCount == 4 ? GetBool() : true; // cumulative + double sigma = nParamCount >= 3 ? GetDouble() : 1.0; // standard deviation + double mue = nParamCount >= 2 ? GetDouble() : 0.0; // mean + double x = GetDouble(); // x + if (sigma <= 0.0) + { + PushIllegalArgument(); + return; + } + if (bCumulative) + { // cumulative + if (x <= 0.0) + PushDouble(0.0); + else + PushDouble(integralPhi((log(x)-mue)/sigma)); + } + else + { // density + if (x <= 0.0) + PushIllegalArgument(); + else + PushDouble(phi((log(x)-mue)/sigma)/sigma/x); + } +} + +void ScInterpreter::ScStdNormDist() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScStdNormDist" ); + PushDouble(integralPhi(GetDouble())); +} + +void ScInterpreter::ScExpDist() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScExpDist" ); + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double kum = GetDouble(); // 0 oder 1 + double lambda = GetDouble(); // lambda + double x = GetDouble(); // x + if (lambda <= 0.0) + PushIllegalArgument(); + else if (kum == 0.0) // Dichte + { + if (x >= 0.0) + PushDouble(lambda * exp(-lambda*x)); + else + PushInt(0); + } + else // Verteilung + { + if (x > 0.0) + PushDouble(1.0 - exp(-lambda*x)); + else + PushInt(0); + } + } +} + +void ScInterpreter::ScTDist() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTDist" ); + if ( !MustHaveParamCount( GetByte(), 3 ) ) + return; + double fFlag = ::rtl::math::approxFloor(GetDouble()); + double fDF = ::rtl::math::approxFloor(GetDouble()); + double T = GetDouble(); + if (fDF < 1.0 || T < 0.0 || (fFlag != 1.0 && fFlag != 2.0) ) + { + PushIllegalArgument(); + return; + } + double R = GetTDist(T, fDF); + if (fFlag == 1.0) + PushDouble(R); + else + PushDouble(2.0*R); +} + +void ScInterpreter::ScFDist() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFDist" ); + if ( !MustHaveParamCount( GetByte(), 3 ) ) + return; + double fF2 = ::rtl::math::approxFloor(GetDouble()); + double fF1 = ::rtl::math::approxFloor(GetDouble()); + double fF = GetDouble(); + if (fF < 0.0 || fF1 < 1.0 || fF2 < 1.0 || fF1 >= 1.0E10 || fF2 >= 1.0E10) + { + PushIllegalArgument(); + return; + } + PushDouble(GetFDist(fF, fF1, fF2)); +} + +void ScInterpreter::ScChiDist() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScChiDist" ); + double fResult; + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + double fDF = ::rtl::math::approxFloor(GetDouble()); + double fChi = GetDouble(); + if (fDF < 1.0) // x<=0 returns 1, see ODFF 6.17.10 + { + PushIllegalArgument(); + return; + } + fResult = GetChiDist( fChi, fDF); + if (nGlobalError) + { + PushError( nGlobalError); + return; + } + PushDouble(fResult); +} + +void ScInterpreter::ScWeibull() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScWeibull" ); + if ( MustHaveParamCount( GetByte(), 4 ) ) + { + double kum = GetDouble(); // 0 oder 1 + double beta = GetDouble(); // beta + double alpha = GetDouble(); // alpha + double x = GetDouble(); // x + if (alpha <= 0.0 || beta <= 0.0 || x < 0.0) + PushIllegalArgument(); + else if (kum == 0.0) // Dichte + PushDouble(alpha/pow(beta,alpha)*pow(x,alpha-1.0)* + exp(-pow(x/beta,alpha))); + else // Verteilung + PushDouble(1.0 - exp(-pow(x/beta,alpha))); + } +} + +void ScInterpreter::ScPoissonDist() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPoissonDist" ); + BYTE nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + bool bCumulative = (nParamCount == 3 ? GetBool() : true); // default cumulative + double lambda = GetDouble(); // Mean + double x = ::rtl::math::approxFloor(GetDouble()); // discrete distribution + if (lambda < 0.0 || x < 0.0) + PushIllegalArgument(); + else if (!bCumulative) // Probability mass function + { + if (lambda == 0.0) + PushInt(0); + else + { + if (lambda >712) // underflow in exp(-lambda) + { // accuracy 11 Digits + PushDouble( exp(x*log(lambda)-lambda-GetLogGamma(x+1.0))); + } + else + { + double fPoissonVar = 1.0; + for ( double f = 0.0; f < x; ++f ) + fPoissonVar *= lambda / ( f + 1.0 ); + PushDouble( fPoissonVar * exp( -lambda ) ); + } + } + } + else // Cumulative distribution function + { + if (lambda == 0.0) + PushInt(1); + else + { + if (lambda > 712 ) // underflow in exp(-lambda) + { // accuracy 12 Digits + PushDouble(GetUpRegIGamma(x+1.0,lambda)); + } + else + { + if (x >= 936.0) // result is always undistinghable from 1 + PushDouble (1.0); + else + { + double fSummand = exp(-lambda); + double fSum = fSummand; + int nEnd = sal::static_int_cast<int>( x ); + for (int i = 1; i <= nEnd; i++) + { + fSummand = (fSummand * lambda)/(double)i; + fSum += fSummand; + } + PushDouble(fSum); + } + } + } + } + } +} + +/** Local function used in the calculation of the hypergeometric distribution. + */ +void lcl_PutFactorialElements( ::std::vector< double >& cn, double fLower, double fUpper, double fBase ) +{ + for ( double i = fLower; i <= fUpper; ++i ) + { + double fVal = fBase - i; + if ( fVal > 1.0 ) + cn.push_back( fVal ); + } +} + +/** Calculates a value of the hypergeometric distribution. + + The algorithm is designed to avoid unnecessary multiplications and division + by expanding all factorial elements (9 of them). It is done by excluding + those ranges that overlap in the numerator and the denominator. This allows + for a fast calculation for large values which would otherwise cause an overflow + in the intermediate values. + + @author Kohei Yoshida <kohei@openoffice.org> + + @see #i47296# + + */ +void ScInterpreter::ScHypGeomDist() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScHypGeomDist" ); + const size_t nMaxArraySize = 500000; // arbitrary max array size + + if ( !MustHaveParamCount( GetByte(), 4 ) ) + return; + + double N = ::rtl::math::approxFloor(GetDouble()); + double M = ::rtl::math::approxFloor(GetDouble()); + double n = ::rtl::math::approxFloor(GetDouble()); + double x = ::rtl::math::approxFloor(GetDouble()); + + if( (x < 0.0) || (n < x) || (M < x) || (N < n) || (N < M) || (x < n - N + M) ) + { + PushIllegalArgument(); + return; + } + + typedef ::std::vector< double > HypContainer; + HypContainer cnNumer, cnDenom; + + size_t nEstContainerSize = static_cast<size_t>( x + ::std::min( n, M ) ); + size_t nMaxSize = ::std::min( cnNumer.max_size(), nMaxArraySize ); + if ( nEstContainerSize > nMaxSize ) + { + PushNoValue(); + return; + } + cnNumer.reserve( nEstContainerSize + 10 ); + cnDenom.reserve( nEstContainerSize + 10 ); + + // Trim coefficient C first + double fCNumVarUpper = N - n - M + x - 1.0; + double fCDenomVarLower = 1.0; + if ( N - n - M + x >= M - x + 1.0 ) + { + fCNumVarUpper = M - x - 1.0; + fCDenomVarLower = N - n - 2.0*(M - x) + 1.0; + } + +#ifdef DBG_UTIL + double fCNumLower = N - n - fCNumVarUpper; +#endif + double fCDenomUpper = N - n - M + x + 1.0 - fCDenomVarLower; + + double fDNumVarLower = n - M; + + if ( n >= M + 1.0 ) + { + if ( N - M < n + 1.0 ) + { + // Case 1 + + if ( N - n < n + 1.0 ) + { + // no overlap + lcl_PutFactorialElements( cnNumer, 0.0, fCNumVarUpper, N - n ); + lcl_PutFactorialElements( cnDenom, 0.0, N - n - 1.0, N ); + } + else + { + // overlap + DBG_ASSERT( fCNumLower < n + 1.0, "ScHypGeomDist: wrong assertion" ); + lcl_PutFactorialElements( cnNumer, N - 2.0*n, fCNumVarUpper, N - n ); + lcl_PutFactorialElements( cnDenom, 0.0, n - 1.0, N ); + } + + DBG_ASSERT( fCDenomUpper <= N - M, "ScHypGeomDist: wrong assertion" ); + + if ( fCDenomUpper < n - x + 1.0 ) + // no overlap + lcl_PutFactorialElements( cnNumer, 1.0, N - M - n + x, N - M + 1.0 ); + else + { + // overlap + lcl_PutFactorialElements( cnNumer, 1.0, N - M - fCDenomUpper, N - M + 1.0 ); + + fCDenomUpper = n - x; + fCDenomVarLower = N - M - 2.0*(n - x) + 1.0; + } + } + else + { + // Case 2 + + if ( n > M - 1.0 ) + { + // no overlap + lcl_PutFactorialElements( cnNumer, 0.0, fCNumVarUpper, N - n ); + lcl_PutFactorialElements( cnDenom, 0.0, M - 1.0, N ); + } + else + { + lcl_PutFactorialElements( cnNumer, M - n, fCNumVarUpper, N - n ); + lcl_PutFactorialElements( cnDenom, 0.0, n - 1.0, N ); + } + + DBG_ASSERT( fCDenomUpper <= n, "ScHypGeomDist: wrong assertion" ); + + if ( fCDenomUpper < n - x + 1.0 ) + // no overlap + lcl_PutFactorialElements( cnNumer, N - M - n + 1.0, N - M - n + x, N - M + 1.0 ); + else + { + lcl_PutFactorialElements( cnNumer, N - M - n + 1.0, N - M - fCDenomUpper, N - M + 1.0 ); + fCDenomUpper = n - x; + fCDenomVarLower = N - M - 2.0*(n - x) + 1.0; + } + } + + DBG_ASSERT( fCDenomUpper <= M, "ScHypGeomDist: wrong assertion" ); + } + else + { + if ( N - M < M + 1.0 ) + { + // Case 3 + + if ( N - n < M + 1.0 ) + { + // No overlap + lcl_PutFactorialElements( cnNumer, 0.0, fCNumVarUpper, N - n ); + lcl_PutFactorialElements( cnDenom, 0.0, N - M - 1.0, N ); + } + else + { + lcl_PutFactorialElements( cnNumer, N - n - M, fCNumVarUpper, N - n ); + lcl_PutFactorialElements( cnDenom, 0.0, n - 1.0, N ); + } + + if ( n - x + 1.0 > fCDenomUpper ) + // No overlap + lcl_PutFactorialElements( cnNumer, 1.0, N - M - n + x, N - M + 1.0 ); + else + { + // Overlap + lcl_PutFactorialElements( cnNumer, 1.0, N - M - fCDenomUpper, N - M + 1.0 ); + + fCDenomVarLower = N - M - 2.0*(n - x) + 1.0; + fCDenomUpper = n - x; + } + } + else + { + // Case 4 + + DBG_ASSERT( M >= n - x, "ScHypGeomDist: wrong assertion" ); + DBG_ASSERT( M - x <= N - M + 1.0, "ScHypGeomDist: wrong assertion" ); + + if ( N - n < N - M + 1.0 ) + { + // No overlap + lcl_PutFactorialElements( cnNumer, 0.0, fCNumVarUpper, N - n ); + lcl_PutFactorialElements( cnDenom, 0.0, M - 1.0, N ); + } + else + { + // Overlap + DBG_ASSERT( fCNumLower <= N - M + 1.0, "ScHypGeomDist: wrong assertion" ); + + lcl_PutFactorialElements( cnNumer, M - n, fCNumVarUpper, N - n ); + lcl_PutFactorialElements( cnDenom, 0.0, n - 1.0, N ); + } + + if ( n - x + 1.0 > fCDenomUpper ) + // No overlap + lcl_PutFactorialElements( cnNumer, N - 2.0*M + 1.0, N - M - n + x, N - M + 1.0 ); + else if ( M >= fCDenomUpper ) + { + lcl_PutFactorialElements( cnNumer, N - 2.0*M + 1.0, N - M - fCDenomUpper, N - M + 1.0 ); + + fCDenomUpper = n - x; + fCDenomVarLower = N - M - 2.0*(n - x) + 1.0; + } + else + { + DBG_ASSERT( M <= fCDenomUpper, "ScHypGeomDist: wrong assertion" ); + lcl_PutFactorialElements( cnDenom, fCDenomVarLower, N - n - 2.0*M + x, + N - n - M + x + 1.0 ); + + fCDenomUpper = n - x; + fCDenomVarLower = N - M - 2.0*(n - x) + 1.0; + } + } + + DBG_ASSERT( fCDenomUpper <= n, "ScHypGeomDist: wrong assertion" ); + + fDNumVarLower = 0.0; + } + + double nDNumVarUpper = fCDenomUpper < x + 1.0 ? n - x - 1.0 : n - fCDenomUpper - 1.0; + double nDDenomVarLower = fCDenomUpper < x + 1.0 ? fCDenomVarLower : N - n - M + 1.0; + lcl_PutFactorialElements( cnNumer, fDNumVarLower, nDNumVarUpper, n ); + lcl_PutFactorialElements( cnDenom, nDDenomVarLower, N - n - M + x, N - n - M + x + 1.0 ); + + ::std::sort( cnNumer.begin(), cnNumer.end() ); + ::std::sort( cnDenom.begin(), cnDenom.end() ); + HypContainer::reverse_iterator it1 = cnNumer.rbegin(), it1End = cnNumer.rend(); + HypContainer::reverse_iterator it2 = cnDenom.rbegin(), it2End = cnDenom.rend(); + + double fFactor = 1.0; + for ( ; it1 != it1End || it2 != it2End; ) + { + double fEnum = 1.0, fDenom = 1.0; + if ( it1 != it1End ) + fEnum = *it1++; + if ( it2 != it2End ) + fDenom = *it2++; + fFactor *= fEnum / fDenom; + } + + PushDouble(fFactor); +} + +void ScInterpreter::ScGammaDist() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGammaDist" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 3, 4 ) ) + return; + double bCumulative; + if (nParamCount == 4) + bCumulative = GetBool(); + else + bCumulative = true; + double fBeta = GetDouble(); // scale + double fAlpha = GetDouble(); // shape + double fX = GetDouble(); // x + if (fAlpha <= 0.0 || fBeta <= 0.0) + PushIllegalArgument(); + else + { + if (bCumulative) // distribution + PushDouble( GetGammaDist( fX, fAlpha, fBeta)); + else // density + PushDouble( GetGammaDistPDF( fX, fAlpha, fBeta)); + } +} + +void ScInterpreter::ScNormInv() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNormInv" ); + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double sigma = GetDouble(); + double mue = GetDouble(); + double x = GetDouble(); + if (sigma <= 0.0 || x < 0.0 || x > 1.0) + PushIllegalArgument(); + else if (x == 0.0 || x == 1.0) + PushNoValue(); + else + PushDouble(gaussinv(x)*sigma + mue); + } +} + +void ScInterpreter::ScSNormInv() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSNormInv" ); + double x = GetDouble(); + if (x < 0.0 || x > 1.0) + PushIllegalArgument(); + else if (x == 0.0 || x == 1.0) + PushNoValue(); + else + PushDouble(gaussinv(x)); +} + +void ScInterpreter::ScLogNormInv() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLogNormInv" ); + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double sigma = GetDouble(); // Stdabw + double mue = GetDouble(); // Mittelwert + double y = GetDouble(); // y + if (sigma <= 0.0 || y <= 0.0 || y >= 1.0) + PushIllegalArgument(); + else + PushDouble(exp(mue+sigma*gaussinv(y))); + } +} + +class ScGammaDistFunction : public ScDistFunc +{ + ScInterpreter& rInt; + double fp, fAlpha, fBeta; + +public: + ScGammaDistFunction( ScInterpreter& rI, double fpVal, double fAlphaVal, double fBetaVal ) : + rInt(rI), fp(fpVal), fAlpha(fAlphaVal), fBeta(fBetaVal) {} + + double GetValue( double x ) const { return fp - rInt.GetGammaDist(x, fAlpha, fBeta); } +}; + +void ScInterpreter::ScGammaInv() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGammaInv" ); + if ( !MustHaveParamCount( GetByte(), 3 ) ) + return; + double fBeta = GetDouble(); + double fAlpha = GetDouble(); + double fP = GetDouble(); + if (fAlpha <= 0.0 || fBeta <= 0.0 || fP < 0.0 || fP >= 1.0 ) + { + PushIllegalArgument(); + return; + } + if (fP == 0.0) + PushInt(0); + else + { + bool bConvError; + ScGammaDistFunction aFunc( *this, fP, fAlpha, fBeta ); + double fStart = fAlpha * fBeta; + double fVal = lcl_IterateInverse( aFunc, fStart*0.5, fStart, bConvError ); + if (bConvError) + SetError(errNoConvergence); + PushDouble(fVal); + } +} + +class ScBetaDistFunction : public ScDistFunc +{ + ScInterpreter& rInt; + double fp, fAlpha, fBeta; + +public: + ScBetaDistFunction( ScInterpreter& rI, double fpVal, double fAlphaVal, double fBetaVal ) : + rInt(rI), fp(fpVal), fAlpha(fAlphaVal), fBeta(fBetaVal) {} + + double GetValue( double x ) const { return fp - rInt.GetBetaDist(x, fAlpha, fBeta); } +}; + +void ScInterpreter::ScBetaInv() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScBetaInv" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 3, 5 ) ) + return; + double fP, fA, fB, fAlpha, fBeta; + if (nParamCount == 5) + fB = GetDouble(); + else + fB = 1.0; + if (nParamCount >= 4) + fA = GetDouble(); + else + fA = 0.0; + fBeta = GetDouble(); + fAlpha = GetDouble(); + fP = GetDouble(); + if (fP < 0.0 || fP >= 1.0 || fA == fB || fAlpha <= 0.0 || fBeta <= 0.0) + { + PushIllegalArgument(); + return; + } + if (fP == 0.0) + PushInt(0); + else + { + bool bConvError; + ScBetaDistFunction aFunc( *this, fP, fAlpha, fBeta ); + // 0..1 as range for iteration so it isn't extended beyond the valid range + double fVal = lcl_IterateInverse( aFunc, 0.0, 1.0, bConvError ); + if (bConvError) + PushError( errNoConvergence); + else + PushDouble(fA + fVal*(fB-fA)); // scale to (A,B) + } +} + + // Achtung: T, F und Chi + // sind monoton fallend, + // deshalb 1-Dist als Funktion + +class ScTDistFunction : public ScDistFunc +{ + ScInterpreter& rInt; + double fp, fDF; + +public: + ScTDistFunction( ScInterpreter& rI, double fpVal, double fDFVal ) : + rInt(rI), fp(fpVal), fDF(fDFVal) {} + + double GetValue( double x ) const { return fp - 2 * rInt.GetTDist(x, fDF); } +}; + +void ScInterpreter::ScTInv() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTInv" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + double fDF = ::rtl::math::approxFloor(GetDouble()); + double fP = GetDouble(); + if (fDF < 1.0 || fDF >= 1.0E5 || fP <= 0.0 || fP > 1.0 ) + { + PushIllegalArgument(); + return; + } + + bool bConvError; + ScTDistFunction aFunc( *this, fP, fDF ); + double fVal = lcl_IterateInverse( aFunc, fDF*0.5, fDF, bConvError ); + if (bConvError) + SetError(errNoConvergence); + PushDouble(fVal); +} + +class ScFDistFunction : public ScDistFunc +{ + ScInterpreter& rInt; + double fp, fF1, fF2; + +public: + ScFDistFunction( ScInterpreter& rI, double fpVal, double fF1Val, double fF2Val ) : + rInt(rI), fp(fpVal), fF1(fF1Val), fF2(fF2Val) {} + + double GetValue( double x ) const { return fp - rInt.GetFDist(x, fF1, fF2); } +}; + +void ScInterpreter::ScFInv() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFInv" ); + if ( !MustHaveParamCount( GetByte(), 3 ) ) + return; + double fF2 = ::rtl::math::approxFloor(GetDouble()); + double fF1 = ::rtl::math::approxFloor(GetDouble()); + double fP = GetDouble(); + if (fP <= 0.0 || fF1 < 1.0 || fF2 < 1.0 || fF1 >= 1.0E10 || fF2 >= 1.0E10 || fP > 1.0) + { + PushIllegalArgument(); + return; + } + + bool bConvError; + ScFDistFunction aFunc( *this, fP, fF1, fF2 ); + double fVal = lcl_IterateInverse( aFunc, fF1*0.5, fF1, bConvError ); + if (bConvError) + SetError(errNoConvergence); + PushDouble(fVal); +} + +class ScChiDistFunction : public ScDistFunc +{ + ScInterpreter& rInt; + double fp, fDF; + +public: + ScChiDistFunction( ScInterpreter& rI, double fpVal, double fDFVal ) : + rInt(rI), fp(fpVal), fDF(fDFVal) {} + + double GetValue( double x ) const { return fp - rInt.GetChiDist(x, fDF); } +}; + +void ScInterpreter::ScChiInv() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScChiInv" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + double fDF = ::rtl::math::approxFloor(GetDouble()); + double fP = GetDouble(); + if (fDF < 1.0 || fP <= 0.0 || fP > 1.0 ) + { + PushIllegalArgument(); + return; + } + + bool bConvError; + ScChiDistFunction aFunc( *this, fP, fDF ); + double fVal = lcl_IterateInverse( aFunc, fDF*0.5, fDF, bConvError ); + if (bConvError) + SetError(errNoConvergence); + PushDouble(fVal); +} + +/***********************************************/ +class ScChiSqDistFunction : public ScDistFunc +{ + ScInterpreter& rInt; + double fp, fDF; + +public: + ScChiSqDistFunction( ScInterpreter& rI, double fpVal, double fDFVal ) : + rInt(rI), fp(fpVal), fDF(fDFVal) {} + + double GetValue( double x ) const { return fp - rInt.GetChiSqDistCDF(x, fDF); } +}; + + +void ScInterpreter::ScChiSqInv() +{ + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + double fDF = ::rtl::math::approxFloor(GetDouble()); + double fP = GetDouble(); + if (fDF < 1.0 || fP < 0.0 || fP >= 1.0 ) + { + PushIllegalArgument(); + return; + } + + bool bConvError; + ScChiSqDistFunction aFunc( *this, fP, fDF ); + double fVal = lcl_IterateInverse( aFunc, fDF*0.5, fDF, bConvError ); + if (bConvError) + SetError(errNoConvergence); + PushDouble(fVal); +} + + +void ScInterpreter::ScConfidence() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScConfidence" ); + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double n = ::rtl::math::approxFloor(GetDouble()); + double sigma = GetDouble(); + double alpha = GetDouble(); + if (sigma <= 0.0 || alpha <= 0.0 || alpha >= 1.0 || n < 1.0) + PushIllegalArgument(); + else + PushDouble( gaussinv(1.0-alpha/2.0) * sigma/sqrt(n) ); + } +} + +void ScInterpreter::ScZTest() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScZTest" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) + return; + double sigma = 0.0, mue, x; + if (nParamCount == 3) + { + sigma = GetDouble(); + if (sigma <= 0.0) + { + PushIllegalArgument(); + return; + } + } + x = GetDouble(); + + double fSum = 0.0; + double fSumSqr = 0.0; + double fVal; + double rValCount = 0.0; + switch (GetStackType()) + { + case formula::svDouble : + { + fVal = GetDouble(); + fSum += fVal; + fSumSqr += fVal*fVal; + rValCount++; + } + break; + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + fVal = GetCellValue( aAdr, pCell ); + fSum += fVal; + fSumSqr += fVal*fVal; + rValCount++; + } + } + break; + case svRefList : + case formula::svDoubleRef : + { + short nParam = 1; + size_t nRefInList = 0; + while (nParam-- > 0) + { + ScRange aRange; + USHORT nErr = 0; + PopDoubleRef( aRange, nParam, nRefInList); + ScValueIterator aValIter(pDok, aRange, glSubTotal); + if (aValIter.GetFirst(fVal, nErr)) + { + fSum += fVal; + fSumSqr += fVal*fVal; + rValCount++; + while ((nErr == 0) && aValIter.GetNext(fVal, nErr)) + { + fSum += fVal; + fSumSqr += fVal*fVal; + rValCount++; + } + SetError(nErr); + } + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nCount = pMat->GetElementCount(); + if (pMat->IsNumeric()) + { + for ( SCSIZE i = 0; i < nCount; i++ ) + { + fVal= pMat->GetDouble(i); + fSum += fVal; + fSumSqr += fVal * fVal; + rValCount++; + } + } + else + { + for (SCSIZE i = 0; i < nCount; i++) + if (!pMat->IsString(i)) + { + fVal= pMat->GetDouble(i); + fSum += fVal; + fSumSqr += fVal * fVal; + rValCount++; + } + } + } + } + break; + default : SetError(errIllegalParameter); break; + } + if (rValCount <= 1.0) + PushError( errDivisionByZero); + else + { + mue = fSum/rValCount; + if (nParamCount != 3) + { + sigma = (fSumSqr - fSum*fSum/rValCount)/(rValCount-1.0); + PushDouble(0.5 - gauss((mue-x)/sqrt(sigma/rValCount))); + } + else + PushDouble(0.5 - gauss((mue-x)*sqrt(rValCount)/sigma)); + } +} +bool ScInterpreter::CalculateTest(BOOL _bTemplin + ,const SCSIZE nC1, const SCSIZE nC2,const SCSIZE nR1,const SCSIZE nR2 + ,const ScMatrixRef& pMat1,const ScMatrixRef& pMat2 + ,double& fT,double& fF) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateTest" ); + double fCount1 = 0.0; + double fCount2 = 0.0; + double fSum1 = 0.0; + double fSumSqr1 = 0.0; + double fSum2 = 0.0; + double fSumSqr2 = 0.0; + double fVal; + SCSIZE i,j; + for (i = 0; i < nC1; i++) + for (j = 0; j < nR1; j++) + { + if (!pMat1->IsString(i,j)) + { + fVal = pMat1->GetDouble(i,j); + fSum1 += fVal; + fSumSqr1 += fVal * fVal; + fCount1++; + } + } + for (i = 0; i < nC2; i++) + for (j = 0; j < nR2; j++) + { + if (!pMat2->IsString(i,j)) + { + fVal = pMat2->GetDouble(i,j); + fSum2 += fVal; + fSumSqr2 += fVal * fVal; + fCount2++; + } + } + if (fCount1 < 2.0 || fCount2 < 2.0) + { + PushNoValue(); + return false; + } // if (fCount1 < 2.0 || fCount2 < 2.0) + if ( _bTemplin ) + { + double fS1 = (fSumSqr1-fSum1*fSum1/fCount1)/(fCount1-1.0)/fCount1; + double fS2 = (fSumSqr2-fSum2*fSum2/fCount2)/(fCount2-1.0)/fCount2; + if (fS1 + fS2 == 0.0) + { + PushNoValue(); + return false; + } + fT = fabs(fSum1/fCount1 - fSum2/fCount2)/sqrt(fS1+fS2); + double c = fS1/(fS1+fS2); +// s.u. fF = ::rtl::math::approxFloor(1.0/(c*c/(fCount1-1.0)+(1.0-c)*(1.0-c)/(fCount2-1.0))); +// fF = ::rtl::math::approxFloor((fS1+fS2)*(fS1+fS2)/(fS1*fS1/(fCount1-1.0) + fS2*fS2/(fCount2-1.0))); + + // GetTDist wird mit GetBetaDist berechnet und kommt auch mit nicht ganzzahligen + // Freiheitsgraden klar. Dann stimmt das Ergebnis auch mit Excel ueberein (#52406#): + fF = 1.0/(c*c/(fCount1-1.0)+(1.0-c)*(1.0-c)/(fCount2-1.0)); + } + else + { + // laut Bronstein-Semendjajew + double fS1 = (fSumSqr1 - fSum1*fSum1/fCount1) / (fCount1 - 1.0); // Varianz + double fS2 = (fSumSqr2 - fSum2*fSum2/fCount2) / (fCount2 - 1.0); + fT = fabs( fSum1/fCount1 - fSum2/fCount2 ) / + sqrt( (fCount1-1.0)*fS1 + (fCount2-1.0)*fS2 ) * + sqrt( fCount1*fCount2*(fCount1+fCount2-2)/(fCount1+fCount2) ); + fF = fCount1 + fCount2 - 2; + } + return true; +} +void ScInterpreter::ScTTest() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTTest" ); + if ( !MustHaveParamCount( GetByte(), 4 ) ) + return; + double fTyp = ::rtl::math::approxFloor(GetDouble()); + double fAnz = ::rtl::math::approxFloor(GetDouble()); + if (fAnz != 1.0 && fAnz != 2.0) + { + PushIllegalArgument(); + return; + } + + ScMatrixRef pMat2 = GetMatrix(); + ScMatrixRef pMat1 = GetMatrix(); + if (!pMat1 || !pMat2) + { + PushIllegalParameter(); + return; + } + double fT, fF; + SCSIZE nC1, nC2; + SCSIZE nR1, nR2; + SCSIZE i, j; + pMat1->GetDimensions(nC1, nR1); + pMat2->GetDimensions(nC2, nR2); + if (fTyp == 1.0) + { + if (nC1 != nC2 || nR1 != nR2) + { + PushIllegalArgument(); + return; + } + double fCount = 0.0; + double fSum1 = 0.0; + double fSum2 = 0.0; + double fSumSqrD = 0.0; + double fVal1, fVal2; + for (i = 0; i < nC1; i++) + for (j = 0; j < nR1; j++) + { + if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) + { + fVal1 = pMat1->GetDouble(i,j); + fVal2 = pMat2->GetDouble(i,j); + fSum1 += fVal1; + fSum2 += fVal2; + fSumSqrD += (fVal1 - fVal2)*(fVal1 - fVal2); + fCount++; + } + } + if (fCount < 1.0) + { + PushNoValue(); + return; + } + fT = sqrt(fCount-1.0) * fabs(fSum1 - fSum2) / + sqrt(fCount * fSumSqrD - (fSum1-fSum2)*(fSum1-fSum2)); + fF = fCount - 1.0; + } + else if (fTyp == 2.0) + { + CalculateTest(FALSE,nC1, nC2,nR1, nR2,pMat1,pMat2,fT,fF); + } + else if (fTyp == 3.0) + { + CalculateTest(TRUE,nC1, nC2,nR1, nR2,pMat1,pMat2,fT,fF); + } + + else + { + PushIllegalArgument(); + return; + } + if (fAnz == 1.0) + PushDouble(GetTDist(fT, fF)); + else + PushDouble(2.0*GetTDist(fT, fF)); +} + +void ScInterpreter::ScFTest() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFTest" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + ScMatrixRef pMat2 = GetMatrix(); + ScMatrixRef pMat1 = GetMatrix(); + if (!pMat1 || !pMat2) + { + PushIllegalParameter(); + return; + } + SCSIZE nC1, nC2; + SCSIZE nR1, nR2; + SCSIZE i, j; + pMat1->GetDimensions(nC1, nR1); + pMat2->GetDimensions(nC2, nR2); + double fCount1 = 0.0; + double fCount2 = 0.0; + double fSum1 = 0.0; + double fSumSqr1 = 0.0; + double fSum2 = 0.0; + double fSumSqr2 = 0.0; + double fVal; + for (i = 0; i < nC1; i++) + for (j = 0; j < nR1; j++) + { + if (!pMat1->IsString(i,j)) + { + fVal = pMat1->GetDouble(i,j); + fSum1 += fVal; + fSumSqr1 += fVal * fVal; + fCount1++; + } + } + for (i = 0; i < nC2; i++) + for (j = 0; j < nR2; j++) + { + if (!pMat2->IsString(i,j)) + { + fVal = pMat2->GetDouble(i,j); + fSum2 += fVal; + fSumSqr2 += fVal * fVal; + fCount2++; + } + } + if (fCount1 < 2.0 || fCount2 < 2.0) + { + PushNoValue(); + return; + } + double fS1 = (fSumSqr1-fSum1*fSum1/fCount1)/(fCount1-1.0); + double fS2 = (fSumSqr2-fSum2*fSum2/fCount2)/(fCount2-1.0); + if (fS1 == 0.0 || fS2 == 0.0) + { + PushNoValue(); + return; + } + double fF, fF1, fF2; + if (fS1 > fS2) + { + fF = fS1/fS2; + fF1 = fCount1-1.0; + fF2 = fCount2-1.0; + } + else + { + fF = fS2/fS1; + fF1 = fCount2-1.0; + fF2 = fCount1-1.0; + } + PushDouble(2.0*GetFDist(fF, fF1, fF2)); +/* + double Z = (pow(fF,1.0/3.0)*(1.0-2.0/(9.0*fF2)) - (1.0-2.0/(9.0*fF1))) / + sqrt(2.0/(9.0*fF1) + pow(fF,2.0/3.0)*2.0/(9.0*fF2)); + PushDouble(1.0-2.0*gauss(Z)); +*/ +} + +void ScInterpreter::ScChiTest() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScChiTest" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + ScMatrixRef pMat2 = GetMatrix(); + ScMatrixRef pMat1 = GetMatrix(); + if (!pMat1 || !pMat2) + { + PushIllegalParameter(); + return; + } + SCSIZE nC1, nC2; + SCSIZE nR1, nR2; + pMat1->GetDimensions(nC1, nR1); + pMat2->GetDimensions(nC2, nR2); + if (nR1 != nR2 || nC1 != nC2) + { + PushIllegalArgument(); + return; + } + double fChi = 0.0; + for (SCSIZE i = 0; i < nC1; i++) + { + for (SCSIZE j = 0; j < nR1; j++) + { + if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) + { + double fValX = pMat1->GetDouble(i,j); + double fValE = pMat2->GetDouble(i,j); + fChi += (fValX - fValE) * (fValX - fValE) / fValE; + } + else + { + PushIllegalArgument(); + return; + } + } + } + double fDF; + if (nC1 == 1 || nR1 == 1) + { + fDF = (double)(nC1*nR1 - 1); + if (fDF == 0.0) + { + PushNoValue(); + return; + } + } + else + fDF = (double)(nC1-1)*(double)(nR1-1); + PushDouble(GetChiDist(fChi, fDF)); +/* + double fX, fS, fT, fG; + fX = 1.0; + for (double fi = fDF; fi >= 2.0; fi -= 2.0) + fX *= fChi/fi; + fX *= exp(-fChi/2.0); + if (fmod(fDF, 2.0) != 0.0) + fX *= sqrt(2.0*fChi/F_PI); + fS = 1.0; + fT = 1.0; + fG = fDF; + while (fT >= 1.0E-7) + { + fG += 2.0; + fT *= fChi/fG; + fS += fT; + } + PushDouble(1.0 - fX*fS); +*/ +} + +void ScInterpreter::ScKurt() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScKurt" ); + double fSum,fCount,vSum; + std::vector<double> values; + if ( !CalculateSkew(fSum,fCount,vSum,values) ) + return; + + if (fCount == 0.0) + { + PushError( errDivisionByZero); + return; + } + + double fMean = fSum / fCount; + + for (size_t i = 0; i < values.size(); i++) + vSum += (values[i] - fMean) * (values[i] - fMean); + + double fStdDev = sqrt(vSum / (fCount - 1.0)); + double dx = 0.0; + double xpower4 = 0.0; + + if (fStdDev == 0.0) + { + PushError( errDivisionByZero); + return; + } + + for (size_t i = 0; i < values.size(); i++) + { + dx = (values[i] - fMean) / fStdDev; + xpower4 = xpower4 + (dx * dx * dx * dx); + } + + double k_d = (fCount - 2.0) * (fCount - 3.0); + double k_l = fCount * (fCount + 1.0) / ((fCount - 1.0) * k_d); + double k_t = 3.0 * (fCount - 1.0) * (fCount - 1.0) / k_d; + + PushDouble(xpower4 * k_l - k_t); +} + +void ScInterpreter::ScHarMean() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScHarMean" ); + short nParamCount = GetByte(); + double nVal = 0.0; + double nValCount = 0.0; + ScAddress aAdr; + ScRange aRange; + size_t nRefInList = 0; + while ((nGlobalError == 0) && (nParamCount-- > 0)) + { + switch (GetStackType()) + { + case formula::svDouble : + { + double x = GetDouble(); + if (x > 0.0) + { + nVal += 1.0/x; + nValCount++; + } + else + SetError( errIllegalArgument); + break; + } + case svSingleRef : + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + double x = GetCellValue( aAdr, pCell ); + if (x > 0.0) + { + nVal += 1.0/x; + nValCount++; + } + else + SetError( errIllegalArgument); + } + break; + } + case formula::svDoubleRef : + case svRefList : + { + USHORT nErr = 0; + PopDoubleRef( aRange, nParamCount, nRefInList); + double nCellVal; + ScValueIterator aValIter(pDok, aRange, glSubTotal); + if (aValIter.GetFirst(nCellVal, nErr)) + { + if (nCellVal > 0.0) + { + nVal += 1.0/nCellVal; + nValCount++; + } + else + SetError( errIllegalArgument); + SetError(nErr); + while ((nErr == 0) && aValIter.GetNext(nCellVal, nErr)) + { + if (nCellVal > 0.0) + { + nVal += 1.0/nCellVal; + nValCount++; + } + else + SetError( errIllegalArgument); + } + SetError(nErr); + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nCount = pMat->GetElementCount(); + if (pMat->IsNumeric()) + { + for (SCSIZE nElem = 0; nElem < nCount; nElem++) + { + double x = pMat->GetDouble(nElem); + if (x > 0.0) + { + nVal += 1.0/x; + nValCount++; + } + else + SetError( errIllegalArgument); + } + } + else + { + for (SCSIZE nElem = 0; nElem < nCount; nElem++) + if (!pMat->IsString(nElem)) + { + double x = pMat->GetDouble(nElem); + if (x > 0.0) + { + nVal += 1.0/x; + nValCount++; + } + else + SetError( errIllegalArgument); + } + } + } + } + break; + default : SetError(errIllegalParameter); break; + } + } + if (nGlobalError == 0) + PushDouble((double)nValCount/nVal); + else + PushError( nGlobalError); +} + +void ScInterpreter::ScGeoMean() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGeoMean" ); + short nParamCount = GetByte(); + double nVal = 0.0; + double nValCount = 0.0; + ScAddress aAdr; + ScRange aRange; + + size_t nRefInList = 0; + while ((nGlobalError == 0) && (nParamCount-- > 0)) + { + switch (GetStackType()) + { + case formula::svDouble : + { + double x = GetDouble(); + if (x > 0.0) + { + nVal += log(x); + nValCount++; + } + else + SetError( errIllegalArgument); + break; + } + case svSingleRef : + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + double x = GetCellValue( aAdr, pCell ); + if (x > 0.0) + { + nVal += log(x); + nValCount++; + } + else + SetError( errIllegalArgument); + } + break; + } + case formula::svDoubleRef : + case svRefList : + { + USHORT nErr = 0; + PopDoubleRef( aRange, nParamCount, nRefInList); + double nCellVal; + ScValueIterator aValIter(pDok, aRange, glSubTotal); + if (aValIter.GetFirst(nCellVal, nErr)) + { + if (nCellVal > 0.0) + { + nVal += log(nCellVal); + nValCount++; + } + else + SetError( errIllegalArgument); + SetError(nErr); + while ((nErr == 0) && aValIter.GetNext(nCellVal, nErr)) + { + if (nCellVal > 0.0) + { + nVal += log(nCellVal); + nValCount++; + } + else + SetError( errIllegalArgument); + } + SetError(nErr); + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nCount = pMat->GetElementCount(); + if (pMat->IsNumeric()) + { + for (SCSIZE ui = 0; ui < nCount; ui++) + { + double x = pMat->GetDouble(ui); + if (x > 0.0) + { + nVal += log(x); + nValCount++; + } + else + SetError( errIllegalArgument); + } + } + else + { + for (SCSIZE ui = 0; ui < nCount; ui++) + if (!pMat->IsString(ui)) + { + double x = pMat->GetDouble(ui); + if (x > 0.0) + { + nVal += log(x); + nValCount++; + } + else + SetError( errIllegalArgument); + } + } + } + } + break; + default : SetError(errIllegalParameter); break; + } + } + if (nGlobalError == 0) + PushDouble(exp(nVal / nValCount)); + else + PushError( nGlobalError); +} + +void ScInterpreter::ScStandard() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScStandard" ); + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double sigma = GetDouble(); + double mue = GetDouble(); + double x = GetDouble(); + if (sigma < 0.0) + PushError( errIllegalArgument); + else if (sigma == 0.0) + PushError( errDivisionByZero); + else + PushDouble((x-mue)/sigma); + } +} +bool ScInterpreter::CalculateSkew(double& fSum,double& fCount,double& vSum,std::vector<double>& values) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateSkew" ); + short nParamCount = GetByte(); + if ( !MustHaveParamCountMin( nParamCount, 1 ) ) + return false; + + fSum = 0.0; + fCount = 0.0; + vSum = 0.0; + double fVal = 0.0; + ScAddress aAdr; + ScRange aRange; + size_t nRefInList = 0; + while (nParamCount-- > 0) + { + switch (GetStackType()) + { + case formula::svDouble : + { + fVal = GetDouble(); + fSum += fVal; + values.push_back(fVal); + fCount++; + } + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + fVal = GetCellValue( aAdr, pCell ); + fSum += fVal; + values.push_back(fVal); + fCount++; + } + } + break; + case formula::svDoubleRef : + case svRefList : + { + PopDoubleRef( aRange, nParamCount, nRefInList); + USHORT nErr = 0; + ScValueIterator aValIter(pDok, aRange); + if (aValIter.GetFirst(fVal, nErr)) + { + fSum += fVal; + values.push_back(fVal); + fCount++; + SetError(nErr); + while ((nErr == 0) && aValIter.GetNext(fVal, nErr)) + { + fSum += fVal; + values.push_back(fVal); + fCount++; + } + SetError(nErr); + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nCount = pMat->GetElementCount(); + if (pMat->IsNumeric()) + { + for (SCSIZE nElem = 0; nElem < nCount; nElem++) + { + fVal = pMat->GetDouble(nElem); + fSum += fVal; + values.push_back(fVal); + fCount++; + } + } + else + { + for (SCSIZE nElem = 0; nElem < nCount; nElem++) + if (!pMat->IsString(nElem)) + { + fVal = pMat->GetDouble(nElem); + fSum += fVal; + values.push_back(fVal); + fCount++; + } + } + } + } + break; + default : + SetError(errIllegalParameter); + break; + } + } + + if (nGlobalError) + { + PushError( nGlobalError); + return false; + } // if (nGlobalError) + return true; +} + +void ScInterpreter::ScSkew() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSkew" ); + double fSum,fCount,vSum; + std::vector<double> values; + if ( !CalculateSkew(fSum,fCount,vSum,values) ) + return; + + double fMean = fSum / fCount; + + for (size_t i = 0; i < values.size(); i++) + vSum += (values[i] - fMean) * (values[i] - fMean); + + double fStdDev = sqrt(vSum / (fCount - 1.0)); + double dx = 0.0; + double xcube = 0.0; + + if (fStdDev == 0) + { + PushIllegalArgument(); + return; + } + + for (size_t i = 0; i < values.size(); i++) + { + dx = (values[i] - fMean) / fStdDev; + xcube = xcube + (dx * dx * dx); + } + + PushDouble(((xcube * fCount) / (fCount - 1.0)) / (fCount - 2.0)); +} + +double ScInterpreter::GetMedian( vector<double> & rArray ) +{ + size_t nSize = rArray.size(); + if (rArray.empty() || nSize == 0 || nGlobalError) + { + SetError( errNoValue); + return 0.0; + } + + // Upper median. + size_t nMid = nSize / 2; + vector<double>::iterator iMid = rArray.begin() + nMid; + ::std::nth_element( rArray.begin(), iMid, rArray.end()); + if (nSize & 1) + return *iMid; // Lower and upper median are equal. + else + { + double fUp = *iMid; + // Lower median. + iMid = rArray.begin() + nMid - 1; + ::std::nth_element( rArray.begin(), iMid, rArray.end()); + return (fUp + *iMid) / 2; + } +} + +void ScInterpreter::ScMedian() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMedian" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCountMin( nParamCount, 1 ) ) + return; + vector<double> aArray; + GetNumberSequenceArray( nParamCount, aArray); + PushDouble( GetMedian( aArray)); +} + +double ScInterpreter::GetPercentile( vector<double> & rArray, double fPercentile ) +{ + size_t nSize = rArray.size(); + if (rArray.empty() || nSize == 0 || nGlobalError) + { + SetError( errNoValue); + return 0.0; + } + + if (nSize == 1) + return rArray[0]; + else + { + size_t nIndex = (size_t)::rtl::math::approxFloor( fPercentile * (nSize-1)); + double fDiff = fPercentile * (nSize-1) - ::rtl::math::approxFloor( fPercentile * (nSize-1)); + DBG_ASSERT(nIndex < nSize, "GetPercentile: wrong index(1)"); + vector<double>::iterator iter = rArray.begin() + nIndex; + ::std::nth_element( rArray.begin(), iter, rArray.end()); + if (fDiff == 0.0) + return *iter; + else + { + DBG_ASSERT(nIndex < nSize-1, "GetPercentile: wrong index(2)"); + double fVal = *iter; + iter = rArray.begin() + nIndex+1; + ::std::nth_element( rArray.begin(), iter, rArray.end()); + return fVal + fDiff * (*iter - fVal); + } + } +} + +void ScInterpreter::ScPercentile() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPercentile" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + double alpha = GetDouble(); + if (alpha < 0.0 || alpha > 1.0) + { + PushIllegalArgument(); + return; + } + vector<double> aArray; + GetNumberSequenceArray( 1, aArray); + PushDouble( GetPercentile( aArray, alpha)); +} + +void ScInterpreter::ScQuartile() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScQuartile" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + double fFlag = ::rtl::math::approxFloor(GetDouble()); + if (fFlag < 0.0 || fFlag > 4.0) + { + PushIllegalArgument(); + return; + } + vector<double> aArray; + GetNumberSequenceArray( 1, aArray); + PushDouble( fFlag == 2.0 ? GetMedian( aArray) : GetPercentile( aArray, 0.25 * fFlag)); +} + +void ScInterpreter::ScModalValue() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScModalValue" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCountMin( nParamCount, 1 ) ) + return; + vector<double> aSortArray; + GetSortArray(nParamCount, aSortArray); + SCSIZE nSize = aSortArray.size(); + if (aSortArray.empty() || nSize == 0 || nGlobalError) + PushNoValue(); + else + { + SCSIZE nMaxIndex = 0, nMax = 1, nCount = 1; + double nOldVal = aSortArray[0]; + SCSIZE i; + + for ( i = 1; i < nSize; i++) + { + if (aSortArray[i] == nOldVal) + nCount++; + else + { + nOldVal = aSortArray[i]; + if (nCount > nMax) + { + nMax = nCount; + nMaxIndex = i-1; + } + nCount = 1; + } + } + if (nCount > nMax) + { + nMax = nCount; + nMaxIndex = i-1; + } + if (nMax == 1 && nCount == 1) + PushNoValue(); + else if (nMax == 1) + PushDouble(nOldVal); + else + PushDouble(aSortArray[nMaxIndex]); + } +} + +void ScInterpreter::CalculateSmallLarge(BOOL bSmall) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateSmallLarge" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + double f = ::rtl::math::approxFloor(GetDouble()); + if (f < 1.0) + { + PushIllegalArgument(); + return; + } + SCSIZE k = static_cast<SCSIZE>(f); + vector<double> aSortArray; + /* TODO: using nth_element() is best for one single value, but LARGE/SMALL + * actually are defined to return an array of values if an array of + * positions was passed, in which case, depending on the number of values, + * we may or will need a real sorted array again, see #i32345. */ + //GetSortArray(1, aSortArray); + GetNumberSequenceArray(1, aSortArray); + SCSIZE nSize = aSortArray.size(); + if (aSortArray.empty() || nSize == 0 || nGlobalError || nSize < k) + PushNoValue(); + else + { + // TODO: the sorted case for array: PushDouble( aSortArray[ bSmall ? k-1 : nSize-k ] ); + vector<double>::iterator iPos = aSortArray.begin() + (bSmall ? k-1 : nSize-k); + ::std::nth_element( aSortArray.begin(), iPos, aSortArray.end()); + PushDouble( *iPos); + } +} + +void ScInterpreter::ScLarge() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLarge" ); + CalculateSmallLarge(FALSE); +} + +void ScInterpreter::ScSmall() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSmall" ); + CalculateSmallLarge(TRUE); +} + +void ScInterpreter::ScPercentrank() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPercentrank" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 2 ) ) + return; +#if 0 +/* wird nicht unterstuetzt + double fPrec; + if (nParamCount == 3) + { + fPrec = ::rtl::math::approxFloor(GetDouble()); + if (fPrec < 1.0) + { + PushIllegalArgument(); + return; + } + } + else + fPrec = 3.0; +*/ +#endif + double fNum = GetDouble(); + vector<double> aSortArray; + GetSortArray(1, aSortArray); + SCSIZE nSize = aSortArray.size(); + if (aSortArray.empty() || nSize == 0 || nGlobalError) + PushNoValue(); + else + { + if (fNum < aSortArray[0] || fNum > aSortArray[nSize-1]) + PushNoValue(); + else if ( nSize == 1 ) + PushDouble(1.0); // fNum == pSortArray[0], see test above + else + { + double fRes; + SCSIZE nOldCount = 0; + double fOldVal = aSortArray[0]; + SCSIZE i; + for (i = 1; i < nSize && aSortArray[i] < fNum; i++) + { + if (aSortArray[i] != fOldVal) + { + nOldCount = i; + fOldVal = aSortArray[i]; + } + } + if (aSortArray[i] != fOldVal) + nOldCount = i; + if (fNum == aSortArray[i]) + fRes = (double)nOldCount/(double)(nSize-1); + else + { + // #75312# nOldCount is the count of smaller entries + // fNum is between pSortArray[nOldCount-1] and pSortArray[nOldCount] + // use linear interpolation to find a position between the entries + + if ( nOldCount == 0 ) + { + DBG_ERROR("should not happen"); + fRes = 0.0; + } + else + { + double fFract = ( fNum - aSortArray[nOldCount-1] ) / + ( aSortArray[nOldCount] - aSortArray[nOldCount-1] ); + fRes = ( (double)(nOldCount-1)+fFract )/(double)(nSize-1); + } + } + PushDouble(fRes); + } + } +} + +void ScInterpreter::ScTrimMean() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTrimMean" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + double alpha = GetDouble(); + if (alpha < 0.0 || alpha >= 1.0) + { + PushIllegalArgument(); + return; + } + vector<double> aSortArray; + GetSortArray(1, aSortArray); + SCSIZE nSize = aSortArray.size(); + if (aSortArray.empty() || nSize == 0 || nGlobalError) + PushNoValue(); + else + { + ULONG nIndex = (ULONG) ::rtl::math::approxFloor(alpha*(double)nSize); + if (nIndex % 2 != 0) + nIndex--; + nIndex /= 2; + DBG_ASSERT(nIndex < nSize, "ScTrimMean: falscher Index"); + double fSum = 0.0; + for (SCSIZE i = nIndex; i < nSize-nIndex; i++) + fSum += aSortArray[i]; + PushDouble(fSum/(double)(nSize-2*nIndex)); + } +} + +void ScInterpreter::GetNumberSequenceArray( BYTE nParamCount, vector<double>& rArray ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetSortArray" ); + ScAddress aAdr; + ScRange aRange; + short nParam = nParamCount; + size_t nRefInList = 0; + while (nParam-- > 0) + { + switch (GetStackType()) + { + case formula::svDouble : + rArray.push_back( PopDouble()); + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + rArray.push_back( GetCellValue( aAdr, pCell)); + } + break; + case formula::svDoubleRef : + case svRefList : + { + PopDoubleRef( aRange, nParam, nRefInList); + if (nGlobalError) + break; + + aRange.Justify(); + SCSIZE nCellCount = aRange.aEnd.Col() - aRange.aStart.Col() + 1; + nCellCount *= aRange.aEnd.Row() - aRange.aStart.Row() + 1; + rArray.reserve( rArray.size() + nCellCount); + + USHORT nErr = 0; + double fCellVal; + ScValueIterator aValIter(pDok, aRange); + if (aValIter.GetFirst( fCellVal, nErr)) + { + rArray.push_back( fCellVal); + SetError(nErr); + while ((nErr == 0) && aValIter.GetNext( fCellVal, nErr)) + rArray.push_back( fCellVal); + SetError(nErr); + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (!pMat) + break; + + SCSIZE nCount = pMat->GetElementCount(); + rArray.reserve( rArray.size() + nCount); + if (pMat->IsNumeric()) + { + for (SCSIZE i = 0; i < nCount; ++i) + rArray.push_back( pMat->GetDouble(i)); + } + else + { + for (SCSIZE i = 0; i < nCount; ++i) + if (!pMat->IsString(i)) + rArray.push_back( pMat->GetDouble(i)); + } + } + break; + default : + PopError(); + SetError( errIllegalParameter); + break; + } + if (nGlobalError) + break; // while + } + // nParam > 0 in case of error, clean stack environment and obtain earlier + // error if there was one. + while (nParam-- > 0) + PopError(); +} + +void ScInterpreter::GetSortArray( BYTE nParamCount, vector<double>& rSortArray, vector<long>* pIndexOrder ) +{ + GetNumberSequenceArray( nParamCount, rSortArray); + + if (rSortArray.size() > MAX_ANZ_DOUBLE_FOR_SORT) + SetError( errStackOverflow); + else if (rSortArray.empty()) + SetError( errNoValue); + + if (nGlobalError == 0) + QuickSort( rSortArray, pIndexOrder); +} + +static void lcl_QuickSort( long nLo, long nHi, vector<double>& rSortArray, vector<long>* pIndexOrder ) +{ + // If pIndexOrder is not NULL, we assume rSortArray.size() == pIndexOrder->size(). + + using ::std::swap; + + if (nHi - nLo == 1) + { + if (rSortArray[nLo] > rSortArray[nHi]) + { + swap(rSortArray[nLo], rSortArray[nHi]); + if (pIndexOrder) + swap(pIndexOrder->at(nLo), pIndexOrder->at(nHi)); + } + return; + } + + long ni = nLo; + long nj = nHi; + do + { + double fLo = rSortArray[nLo]; + while (ni <= nHi && rSortArray[ni] < fLo) ni++; + while (nj >= nLo && fLo < rSortArray[nj]) nj--; + if (ni <= nj) + { + if (ni != nj) + { + swap(rSortArray[ni], rSortArray[nj]); + if (pIndexOrder) + swap(pIndexOrder->at(ni), pIndexOrder->at(nj)); + } + + ++ni; + --nj; + } + } + while (ni < nj); + + if ((nj - nLo) < (nHi - ni)) + { + if (nLo < nj) lcl_QuickSort(nLo, nj, rSortArray, pIndexOrder); + if (ni < nHi) lcl_QuickSort(ni, nHi, rSortArray, pIndexOrder); + } + else + { + if (ni < nHi) lcl_QuickSort(ni, nHi, rSortArray, pIndexOrder); + if (nLo < nj) lcl_QuickSort(nLo, nj, rSortArray, pIndexOrder); + } +} + +void ScInterpreter::QuickSort( vector<double>& rSortArray, vector<long>* pIndexOrder ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::QuickSort" ); + long n = static_cast<long>(rSortArray.size()); + + if (pIndexOrder) + { + pIndexOrder->clear(); + pIndexOrder->reserve(n); + for (long i = 0; i < n; ++i) + pIndexOrder->push_back(i); + } + + if (n < 2) + return; + + size_t nValCount = rSortArray.size(); + for (size_t i = 0; (i + 4) <= nValCount-1; i += 4) + { + size_t nInd = rand() % (int) (nValCount-1); + ::std::swap( rSortArray[i], rSortArray[nInd]); + if (pIndexOrder) + ::std::swap( pIndexOrder->at(i), pIndexOrder->at(nInd)); + } + + lcl_QuickSort(0, n-1, rSortArray, pIndexOrder); +} + +void ScInterpreter::ScRank() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRank" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) + return; + BOOL bDescending; + if (nParamCount == 3) + bDescending = GetBool(); + else + bDescending = FALSE; + double fCount = 1.0; + BOOL bValid = FALSE; + switch (GetStackType()) + { + case formula::svDouble : + { + double x = GetDouble(); + double fVal = GetDouble(); + if (x == fVal) + bValid = TRUE; + break; + } + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + double fVal = GetDouble(); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + double x = GetCellValue( aAdr, pCell ); + if (x == fVal) + bValid = TRUE; + } + break; + } + case formula::svDoubleRef : + case svRefList : + { + ScRange aRange; + short nParam = 1; + size_t nRefInList = 0; + while (nParam-- > 0) + { + USHORT nErr = 0; + // Preserve stack until all RefList elements are done! + USHORT nSaveSP = sp; + PopDoubleRef( aRange, nParam, nRefInList); + if (nParam) + --sp; // simulate pop + double fVal = GetDouble(); + if (nParam) + sp = nSaveSP; + double nCellVal; + ScValueIterator aValIter(pDok, aRange, glSubTotal); + if (aValIter.GetFirst(nCellVal, nErr)) + { + if (nCellVal == fVal) + bValid = TRUE; + else if ((!bDescending && nCellVal > fVal) || + (bDescending && nCellVal < fVal) ) + fCount++; + SetError(nErr); + while ((nErr == 0) && aValIter.GetNext(nCellVal, nErr)) + { + if (nCellVal == fVal) + bValid = TRUE; + else if ((!bDescending && nCellVal > fVal) || + (bDescending && nCellVal < fVal) ) + fCount++; + } + } + SetError(nErr); + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + double fVal = GetDouble(); + if (pMat) + { + SCSIZE nCount = pMat->GetElementCount(); + if (pMat->IsNumeric()) + { + for (SCSIZE i = 0; i < nCount; i++) + { + double x = pMat->GetDouble(i); + if (x == fVal) + bValid = TRUE; + else if ((!bDescending && x > fVal) || + (bDescending && x < fVal) ) + fCount++; + } + } + else + { + for (SCSIZE i = 0; i < nCount; i++) + if (!pMat->IsString(i)) + { + double x = pMat->GetDouble(i); + if (x == fVal) + bValid = TRUE; + else if ((!bDescending && x > fVal) || + (bDescending && x < fVal) ) + fCount++; + } + } + } + } + break; + default : SetError(errIllegalParameter); break; + } + if (bValid) + PushDouble(fCount); + else + PushNoValue(); +} + +void ScInterpreter::ScAveDev() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAveDev" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCountMin( nParamCount, 1 ) ) + return; + USHORT SaveSP = sp; + double nMiddle = 0.0; + double rVal = 0.0; + double rValCount = 0.0; + ScAddress aAdr; + ScRange aRange; + short nParam = nParamCount; + size_t nRefInList = 0; + while (nParam-- > 0) + { + switch (GetStackType()) + { + case formula::svDouble : + rVal += GetDouble(); + rValCount++; + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + rVal += GetCellValue( aAdr, pCell ); + rValCount++; + } + } + break; + case formula::svDoubleRef : + case svRefList : + { + USHORT nErr = 0; + double nCellVal; + PopDoubleRef( aRange, nParam, nRefInList); + ScValueIterator aValIter(pDok, aRange); + if (aValIter.GetFirst(nCellVal, nErr)) + { + rVal += nCellVal; + rValCount++; + SetError(nErr); + while ((nErr == 0) && aValIter.GetNext(nCellVal, nErr)) + { + rVal += nCellVal; + rValCount++; + } + SetError(nErr); + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nCount = pMat->GetElementCount(); + if (pMat->IsNumeric()) + { + for (SCSIZE nElem = 0; nElem < nCount; nElem++) + { + rVal += pMat->GetDouble(nElem); + rValCount++; + } + } + else + { + for (SCSIZE nElem = 0; nElem < nCount; nElem++) + if (!pMat->IsString(nElem)) + { + rVal += pMat->GetDouble(nElem); + rValCount++; + } + } + } + } + break; + default : + SetError(errIllegalParameter); + break; + } + } + if (nGlobalError) + { + PushError( nGlobalError); + return; + } + nMiddle = rVal / rValCount; + sp = SaveSP; + rVal = 0.0; + nParam = nParamCount; + nRefInList = 0; + while (nParam-- > 0) + { + switch (GetStackType()) + { + case formula::svDouble : + rVal += fabs(GetDouble() - nMiddle); + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + rVal += fabs(GetCellValue( aAdr, pCell ) - nMiddle); + } + break; + case formula::svDoubleRef : + case svRefList : + { + USHORT nErr = 0; + double nCellVal; + PopDoubleRef( aRange, nParam, nRefInList); + ScValueIterator aValIter(pDok, aRange); + if (aValIter.GetFirst(nCellVal, nErr)) + { + rVal += (fabs(nCellVal - nMiddle)); + while (aValIter.GetNext(nCellVal, nErr)) + rVal += fabs(nCellVal - nMiddle); + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nCount = pMat->GetElementCount(); + if (pMat->IsNumeric()) + { + for (SCSIZE nElem = 0; nElem < nCount; nElem++) + { + rVal += fabs(pMat->GetDouble(nElem) - nMiddle); + } + } + else + { + for (SCSIZE nElem = 0; nElem < nCount; nElem++) + { + if (!pMat->IsString(nElem)) + rVal += fabs(pMat->GetDouble(nElem) - nMiddle); + } + } + } + } + break; + default : SetError(errIllegalParameter); break; + } + } + PushDouble(rVal / rValCount); +} + +void ScInterpreter::ScDevSq() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDevSq" ); + double nVal; + double nValCount; + GetStVarParams(nVal, nValCount); + PushDouble(nVal); +} + +void ScInterpreter::ScProbability() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScProbability" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 3, 4 ) ) + return; + double fUp, fLo; + fUp = GetDouble(); + if (nParamCount == 4) + fLo = GetDouble(); + else + fLo = fUp; + if (fLo > fUp) + { + double fTemp = fLo; + fLo = fUp; + fUp = fTemp; + } + ScMatrixRef pMatP = GetMatrix(); + ScMatrixRef pMatW = GetMatrix(); + if (!pMatP || !pMatW) + PushIllegalParameter(); + else + { + SCSIZE nC1, nC2; + SCSIZE nR1, nR2; + pMatP->GetDimensions(nC1, nR1); + pMatW->GetDimensions(nC2, nR2); + if (nC1 != nC2 || nR1 != nR2 || nC1 == 0 || nR1 == 0 || + nC2 == 0 || nR2 == 0) + PushNA(); + else + { + double fSum = 0.0; + double fRes = 0.0; + BOOL bStop = FALSE; + double fP, fW; + SCSIZE nCount1 = nC1 * nR1; + for ( SCSIZE i = 0; i < nCount1 && !bStop; i++ ) + { + if (pMatP->IsValue(i) && pMatW->IsValue(i)) + { + fP = pMatP->GetDouble(i); + fW = pMatW->GetDouble(i); + if (fP < 0.0 || fP > 1.0) + bStop = TRUE; + else + { + fSum += fP; + if (fW >= fLo && fW <= fUp) + fRes += fP; + } + } + else + SetError( errIllegalArgument); + } + if (bStop || fabs(fSum -1.0) > 1.0E-7) + PushNoValue(); + else + PushDouble(fRes); + } + } +} + +void ScInterpreter::ScCorrel() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCorrel" ); + // This is identical to ScPearson() + ScPearson(); +} + +void ScInterpreter::ScCovar() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCovar" ); + CalculatePearsonCovar(FALSE,FALSE); +} + +void ScInterpreter::ScPearson() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPearson" ); + CalculatePearsonCovar(TRUE,FALSE); +} +void ScInterpreter::CalculatePearsonCovar(BOOL _bPearson,BOOL _bStexy) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculatePearsonCovar" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + ScMatrixRef pMat1 = GetMatrix(); + ScMatrixRef pMat2 = GetMatrix(); + if (!pMat1 || !pMat2) + { + PushIllegalParameter(); + return; + } + SCSIZE nC1, nC2; + SCSIZE nR1, nR2; + pMat1->GetDimensions(nC1, nR1); + pMat2->GetDimensions(nC2, nR2); + if (nR1 != nR2 || nC1 != nC2) + { + PushIllegalArgument(); + return; + } + /* #i78250# + * (sum((X-MeanX)(Y-MeanY)))/N equals (SumXY)/N-MeanX*MeanY mathematically, + * but the latter produces wrong results if the absolute values are high, + * for example above 10^8 + */ + double fCount = 0.0; + double fSumX = 0.0; + double fSumY = 0.0; + double fSumDeltaXDeltaY = 0.0; // sum of (ValX-MeanX)*(ValY-MeanY) + double fSumSqrDeltaX = 0.0; // sum of (ValX-MeanX)^2 + double fSumSqrDeltaY = 0.0; // sum of (ValY-MeanY)^2 + for (SCSIZE i = 0; i < nC1; i++) + { + for (SCSIZE j = 0; j < nR1; j++) + { + if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) + { + double fValX = pMat1->GetDouble(i,j); + double fValY = pMat2->GetDouble(i,j); + fSumX += fValX; + fSumY += fValY; + fCount++; + } + } + } + if (fCount < (_bStexy ? 3.0 : 1.0)) // fCount==1 is handled by checking denominator later on + PushNoValue(); + else + { + const double fMeanX = fSumX / fCount; + const double fMeanY = fSumY / fCount; + for (SCSIZE i = 0; i < nC1; i++) + { + for (SCSIZE j = 0; j < nR1; j++) + { + if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) + { + const double fValX = pMat1->GetDouble(i,j); + const double fValY = pMat2->GetDouble(i,j); + fSumDeltaXDeltaY += (fValX - fMeanX) * (fValY - fMeanY); + if ( _bPearson ) + { + fSumSqrDeltaX += (fValX - fMeanX) * (fValX - fMeanX); + fSumSqrDeltaY += (fValY - fMeanY) * (fValY - fMeanY); + } + } + } + } // for (SCSIZE i = 0; i < nC1; i++) + if ( _bPearson ) + { + if (fSumSqrDeltaX == 0.0 || ( !_bStexy && fSumSqrDeltaY == 0.0) ) + PushError( errDivisionByZero); + else if ( _bStexy ) + PushDouble( sqrt( (fSumSqrDeltaY - fSumDeltaXDeltaY * + fSumDeltaXDeltaY / fSumSqrDeltaX) / (fCount-2))); + else + PushDouble( fSumDeltaXDeltaY / sqrt( fSumSqrDeltaX * fSumSqrDeltaY)); + } // if ( _bPearson ) + else + { + PushDouble( fSumDeltaXDeltaY / fCount); + } + } +} + +void ScInterpreter::ScRSQ() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRSQ" ); + // Same as ScPearson()*ScPearson() + ScPearson(); + if (!nGlobalError) + { + switch (GetStackType()) + { + case formula::svDouble: + { + double fVal = PopDouble(); + PushDouble( fVal * fVal); + } + break; + default: + PopError(); + PushNoValue(); + } + } +} + +void ScInterpreter::ScSTEXY() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSTEXY" ); + CalculatePearsonCovar(TRUE,TRUE); +} +void ScInterpreter::CalculateSlopeIntercept(BOOL bSlope) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateSlopeIntercept" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + ScMatrixRef pMat1 = GetMatrix(); + ScMatrixRef pMat2 = GetMatrix(); + if (!pMat1 || !pMat2) + { + PushIllegalParameter(); + return; + } + SCSIZE nC1, nC2; + SCSIZE nR1, nR2; + pMat1->GetDimensions(nC1, nR1); + pMat2->GetDimensions(nC2, nR2); + if (nR1 != nR2 || nC1 != nC2) + { + PushIllegalArgument(); + return; + } + // #i78250# numerical stability improved + double fCount = 0.0; + double fSumX = 0.0; + double fSumY = 0.0; + double fSumDeltaXDeltaY = 0.0; // sum of (ValX-MeanX)*(ValY-MeanY) + double fSumSqrDeltaX = 0.0; // sum of (ValX-MeanX)^2 + for (SCSIZE i = 0; i < nC1; i++) + { + for (SCSIZE j = 0; j < nR1; j++) + { + if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) + { + double fValX = pMat1->GetDouble(i,j); + double fValY = pMat2->GetDouble(i,j); + fSumX += fValX; + fSumY += fValY; + fCount++; + } + } + } + if (fCount < 1.0) + PushNoValue(); + else + { + double fMeanX = fSumX / fCount; + double fMeanY = fSumY / fCount; + for (SCSIZE i = 0; i < nC1; i++) + { + for (SCSIZE j = 0; j < nR1; j++) + { + if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) + { + double fValX = pMat1->GetDouble(i,j); + double fValY = pMat2->GetDouble(i,j); + fSumDeltaXDeltaY += (fValX - fMeanX) * (fValY - fMeanY); + fSumSqrDeltaX += (fValX - fMeanX) * (fValX - fMeanX); + } + } + } + if (fSumSqrDeltaX == 0.0) + PushError( errDivisionByZero); + else + { + if ( bSlope ) + PushDouble( fSumDeltaXDeltaY / fSumSqrDeltaX); + else + PushDouble( fMeanY - fSumDeltaXDeltaY / fSumSqrDeltaX * fMeanX); + } + } +} + +void ScInterpreter::ScSlope() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSlope" ); + CalculateSlopeIntercept(TRUE); +} + +void ScInterpreter::ScIntercept() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIntercept" ); + CalculateSlopeIntercept(FALSE); +} + +void ScInterpreter::ScForecast() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScForecast" ); + if ( !MustHaveParamCount( GetByte(), 3 ) ) + return; + ScMatrixRef pMat1 = GetMatrix(); + ScMatrixRef pMat2 = GetMatrix(); + if (!pMat1 || !pMat2) + { + PushIllegalParameter(); + return; + } + SCSIZE nC1, nC2; + SCSIZE nR1, nR2; + pMat1->GetDimensions(nC1, nR1); + pMat2->GetDimensions(nC2, nR2); + if (nR1 != nR2 || nC1 != nC2) + { + PushIllegalArgument(); + return; + } + double fVal = GetDouble(); + // #i78250# numerical stability improved + double fCount = 0.0; + double fSumX = 0.0; + double fSumY = 0.0; + double fSumDeltaXDeltaY = 0.0; // sum of (ValX-MeanX)*(ValY-MeanY) + double fSumSqrDeltaX = 0.0; // sum of (ValX-MeanX)^2 + for (SCSIZE i = 0; i < nC1; i++) + { + for (SCSIZE j = 0; j < nR1; j++) + { + if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) + { + double fValX = pMat1->GetDouble(i,j); + double fValY = pMat2->GetDouble(i,j); + fSumX += fValX; + fSumY += fValY; + fCount++; + } + } + } + if (fCount < 1.0) + PushNoValue(); + else + { + double fMeanX = fSumX / fCount; + double fMeanY = fSumY / fCount; + for (SCSIZE i = 0; i < nC1; i++) + { + for (SCSIZE j = 0; j < nR1; j++) + { + if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) + { + double fValX = pMat1->GetDouble(i,j); + double fValY = pMat2->GetDouble(i,j); + fSumDeltaXDeltaY += (fValX - fMeanX) * (fValY - fMeanY); + fSumSqrDeltaX += (fValX - fMeanX) * (fValX - fMeanX); + } + } + } + if (fSumSqrDeltaX == 0.0) + PushError( errDivisionByZero); + else + PushDouble( fMeanY + fSumDeltaXDeltaY / fSumSqrDeltaX * (fVal - fMeanX)); + } +} + diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx new file mode 100755 index 000000000000..47cde7186067 --- /dev/null +++ b/sc/source/core/tool/interpr4.cxx @@ -0,0 +1,3979 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <rangelst.hxx> +#include <sfx2/app.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/objsh.hxx> +#include <basic/sbmeth.hxx> +#include <basic/sbmod.hxx> +#include <basic/sbstar.hxx> +#include <basic/sbx.hxx> +#include <svl/zforlist.hxx> +#include <tools/urlobj.hxx> +#include <rtl/logfile.hxx> +#include <stdlib.h> +#include <string.h> +#include <signal.h> + +#include <com/sun/star/table/XCellRange.hpp> + +#include "interpre.hxx" +#include "global.hxx" +#include "dbcolect.hxx" +#include "cell.hxx" +#include "callform.hxx" +#include "addincol.hxx" +#include "document.hxx" +#include "dociter.hxx" +#include "docoptio.hxx" +#include "scmatrix.hxx" +#include "adiasync.hxx" +#include "sc.hrc" +#include "cellsuno.hxx" +#include "optuno.hxx" +#include "rangeseq.hxx" +#include "addinlis.hxx" +#include "jumpmatrix.hxx" +#include "parclass.hxx" +#include "externalrefmgr.hxx" +#include "doubleref.hxx" + +#include <math.h> +#include <float.h> +#include <map> +#include <algorithm> +#include <functional> +#include <memory> + +using namespace com::sun::star; +using namespace formula; +using ::std::auto_ptr; + +#define ADDIN_MAXSTRLEN 256 + +//-----------------------------static data ----------------- + +//------------------------------------------------------------------------- +// Funktionen fuer den Zugriff auf das Document +//------------------------------------------------------------------------- + + +void ScInterpreter::ReplaceCell( ScAddress& rPos ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ReplaceCell" ); + ScInterpreterTableOpParams* pTOp = pDok->aTableOpList.First(); + while (pTOp) + { + if ( rPos == pTOp->aOld1 ) + { + rPos = pTOp->aNew1; + return ; + } + else if ( rPos == pTOp->aOld2 ) + { + rPos = pTOp->aNew2; + return ; + } + else + pTOp = pDok->aTableOpList.Next(); + } +} + + +void ScInterpreter::ReplaceCell( SCCOL& rCol, SCROW& rRow, SCTAB& rTab ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ReplaceCell" ); + ScAddress aCellPos( rCol, rRow, rTab ); + ScInterpreterTableOpParams* pTOp = pDok->aTableOpList.First(); + while (pTOp) + { + if ( aCellPos == pTOp->aOld1 ) + { + rCol = pTOp->aNew1.Col(); + rRow = pTOp->aNew1.Row(); + rTab = pTOp->aNew1.Tab(); + return ; + } + else if ( aCellPos == pTOp->aOld2 ) + { + rCol = pTOp->aNew2.Col(); + rRow = pTOp->aNew2.Row(); + rTab = pTOp->aNew2.Tab(); + return ; + } + else + pTOp = pDok->aTableOpList.Next(); + } +} + + +BOOL ScInterpreter::IsTableOpInRange( const ScRange& rRange ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::IsTableOpInRange" ); + if ( rRange.aStart == rRange.aEnd ) + return FALSE; // not considered to be a range in TableOp sense + + // we can't replace a single cell in a range + ScInterpreterTableOpParams* pTOp = pDok->aTableOpList.First(); + while (pTOp) + { + if ( rRange.In( pTOp->aOld1 ) ) + return TRUE; + if ( rRange.In( pTOp->aOld2 ) ) + return TRUE; + pTOp = pDok->aTableOpList.Next(); + } + return FALSE; +} + + +ULONG ScInterpreter::GetCellNumberFormat( const ScAddress& rPos, const ScBaseCell* pCell) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetCellNumberFormat" ); + ULONG nFormat; + USHORT nErr; + if ( pCell ) + { + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + nErr = ((ScFormulaCell*)pCell)->GetErrCode(); + else + nErr = 0; + nFormat = pDok->GetNumberFormat( rPos ); + if ( pCell->GetCellType() == CELLTYPE_FORMULA + && ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0) ) + nFormat = ((ScFormulaCell*)pCell)->GetStandardFormat( *pFormatter, + nFormat ); + } + else + { + nFormat = pDok->GetNumberFormat( rPos ); + nErr = 0; + } + SetError(nErr); + return nFormat; +} + + +/// Only ValueCell, formula cells already store the result rounded. +double ScInterpreter::GetValueCellValue( const ScAddress& rPos, const ScValueCell* pCell ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetValueCellValue" ); + double fVal = pCell->GetValue(); + if ( bCalcAsShown && fVal != 0.0 ) + { + ULONG nFormat = pDok->GetNumberFormat( rPos ); + fVal = pDok->RoundValueAsShown( fVal, nFormat ); + } + return fVal; +} + + +/** Convert string content to numeric value. + + Converted are only integer numbers including exponent, and ISO 8601 dates + and times in their extended formats with separators. Anything else, + especially fractional numeric values with decimal separators or dates other + than ISO 8601 would be locale dependent and is a no-no. Leading and + trailing blanks are ignored. + + The following ISO 8601 formats are converted: + + CCYY-MM-DD + CCYY-MM-DDThh:mm + CCYY-MM-DDThh:mm:ss + CCYY-MM-DDThh:mm:ss,s + CCYY-MM-DDThh:mm:ss.s + hh:mm + hh:mm:ss + hh:mm:ss,s + hh:mm:ss.s + + The century CC may not be omitted and the two-digit year setting is not + taken into account. Instead of the T date and time separator exactly one + blank may be used. + + If a date is given, it must be a valid Gregorian calendar date. In this + case the optional time must be in the range 00:00 to 23:59:59.99999... + If only time is given, it may have any value for hours, taking elapsed time + into account; minutes and seconds are limited to the value 59 as well. + */ + +double ScInterpreter::ConvertStringToValue( const String& rStr ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ConvertStringToValue" ); + double fValue = 0.0; + if (mnStringNoValueError == errCellNoValue) + { + // Requested that all strings result in 0, error handled by caller. + SetError( mnStringNoValueError); + return fValue; + } + ::rtl::OUString aStr( rStr); + rtl_math_ConversionStatus eStatus; + sal_Int32 nParseEnd; + // Decimal and group separator 0 => only integer and possibly exponent, + // stops at first non-digit non-sign. + fValue = ::rtl::math::stringToDouble( aStr, 0, 0, &eStatus, &nParseEnd); + sal_Int32 nLen; + if (eStatus == rtl_math_ConversionStatus_Ok && nParseEnd < (nLen = aStr.getLength())) + { + // Not at string end, check for trailing blanks or switch to date or + // time parsing or bail out. + const sal_Unicode* const pStart = aStr.getStr(); + const sal_Unicode* p = pStart + nParseEnd; + const sal_Unicode* const pStop = pStart + nLen; + switch (*p++) + { + case ' ': + while (p < pStop && *p == ' ') + ++p; + if (p < pStop) + SetError( mnStringNoValueError); + break; + case '-': + case ':': + { + bool bDate = (*(p-1) == '-'); + enum State { year = 0, month, day, hour, minute, second, fraction, done, blank, stop }; + sal_Int32 nUnit[done] = {0,0,0,0,0,0,0}; + const sal_Int32 nLimit[done] = {0,12,31,0,59,59,0}; + State eState = (bDate ? month : minute); + nCurFmtType = (bDate ? NUMBERFORMAT_DATE : NUMBERFORMAT_TIME); + nUnit[eState-1] = aStr.copy( 0, nParseEnd).toInt32(); + const sal_Unicode* pLastStart = p; + // Ensure there's no preceding sign. Negative dates + // currently aren't handled correctly. Also discard + // +CCYY-MM-DD + p = pStart; + while (p < pStop && *p == ' ') + ++p; + if (p < pStop && !CharClass::isAsciiDigit(*p)) + SetError( mnStringNoValueError); + p = pLastStart; + while (p < pStop && !nGlobalError && eState < blank) + { + if (eState == minute) + nCurFmtType |= NUMBERFORMAT_TIME; + if (CharClass::isAsciiDigit(*p)) + { + // Maximum 2 digits per unit, except fractions. + if (p - pLastStart >= 2 && eState != fraction) + SetError( mnStringNoValueError); + } + else if (p > pLastStart) + { + // We had at least one digit. + if (eState < done) + { + nUnit[eState] = aStr.copy( pLastStart - pStart, p - pLastStart).toInt32(); + if (nLimit[eState] && nLimit[eState] < nUnit[eState]) + SetError( mnStringNoValueError); + } + pLastStart = p + 1; // hypothetical next start + // Delimiters must match, a trailing delimiter + // yields an invalid date/time. + switch (eState) + { + case month: + // Month must be followed by separator and + // day, no trailing blanks. + if (*p != '-' || (p+1 == pStop)) + SetError( mnStringNoValueError); + break; + case day: + if ((*p != 'T' || (p+1 == pStop)) && *p != ' ') + SetError( mnStringNoValueError); + // Take one blank as a valid delimiter + // between date and time. + break; + case hour: + // Hour must be followed by separator and + // minute, no trailing blanks. + if (*p != ':' || (p+1 == pStop)) + SetError( mnStringNoValueError); + break; + case minute: + if ((*p != ':' || (p+1 == pStop)) && *p != ' ') + SetError( mnStringNoValueError); + if (*p == ' ') + eState = done; + break; + case second: + if (((*p != ',' && *p != '.') || (p+1 == pStop)) && *p != ' ') + SetError( mnStringNoValueError); + if (*p == ' ') + eState = done; + break; + case fraction: + eState = done; + break; + case year: + case done: + case blank: + case stop: + SetError( mnStringNoValueError); + break; + } + eState = static_cast<State>(eState + 1); + } + else + SetError( mnStringNoValueError); + ++p; + } + if (eState == blank) + { + while (p < pStop && *p == ' ') + ++p; + if (p < pStop) + SetError( mnStringNoValueError); + eState = stop; + } + + // Month without day, or hour without minute. + if (eState == month || (eState == day && p <= pLastStart) || + eState == hour || (eState == minute && p <= pLastStart)) + SetError( mnStringNoValueError); + + if (!nGlobalError) + { + // Catch the very last unit at end of string. + if (p > pLastStart && eState < done) + { + nUnit[eState] = aStr.copy( pLastStart - pStart, p - pLastStart).toInt32(); + if (nLimit[eState] && nLimit[eState] < nUnit[eState]) + SetError( mnStringNoValueError); + } + if (bDate && nUnit[hour] > 23) + SetError( mnStringNoValueError); + if (!nGlobalError) + { + if (bDate && nUnit[day] == 0) + nUnit[day] = 1; + double fFraction = (nUnit[fraction] <= 0 ? 0.0 : + ::rtl::math::pow10Exp( nUnit[fraction], + static_cast<int>( -ceil( log10( static_cast<double>( nUnit[fraction])))))); + fValue = (bDate ? GetDateSerial( + sal::static_int_cast<INT16>(nUnit[year]), + sal::static_int_cast<INT16>(nUnit[month]), + sal::static_int_cast<INT16>(nUnit[day]), + true) : 0.0); + fValue += ((nUnit[hour] * 3600) + (nUnit[minute] * 60) + nUnit[second] + fFraction) / 86400.0; + } + } + } + break; + default: + SetError( mnStringNoValueError); + } + if (nGlobalError) + fValue = 0.0; + } + return fValue; +} + + +double ScInterpreter::GetCellValue( const ScAddress& rPos, const ScBaseCell* pCell ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetCellValue" ); + USHORT nErr = nGlobalError; + nGlobalError = 0; + double nVal = GetCellValueOrZero( rPos, pCell ); + if ( !nGlobalError || nGlobalError == errCellNoValue ) + nGlobalError = nErr; + return nVal; +} + + +double ScInterpreter::GetCellValueOrZero( const ScAddress& rPos, const ScBaseCell* pCell ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetCellValueOrZero" ); + double fValue = 0.0; + if (pCell) + { + CellType eType = pCell->GetCellType(); + switch ( eType ) + { + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFCell = (ScFormulaCell*) pCell; + USHORT nErr = pFCell->GetErrCode(); + if( !nErr ) + { + if (pFCell->IsValue()) + { + fValue = pFCell->GetValue(); + pDok->GetNumberFormatInfo( nCurFmtType, nCurFmtIndex, + rPos, pFCell ); + } + else + { + String aStr; + pFCell->GetString( aStr ); + fValue = ConvertStringToValue( aStr ); + } + } + else + { + fValue = 0.0; + SetError(nErr); + } + } + break; + case CELLTYPE_VALUE: + { + fValue = ((ScValueCell*)pCell)->GetValue(); + nCurFmtIndex = pDok->GetNumberFormat( rPos ); + nCurFmtType = pFormatter->GetType( nCurFmtIndex ); + if ( bCalcAsShown && fValue != 0.0 ) + fValue = pDok->RoundValueAsShown( fValue, nCurFmtIndex ); + } + break; + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + { + // SUM(A1:A2) differs from A1+A2. No good. But people insist on + // it ... #i5658# + String aStr; + if ( eType == CELLTYPE_STRING ) + ((ScStringCell*)pCell)->GetString( aStr ); + else + ((ScEditCell*)pCell)->GetString( aStr ); + fValue = ConvertStringToValue( aStr ); + } + break; + case CELLTYPE_NONE: + case CELLTYPE_NOTE: + fValue = 0.0; // empty or broadcaster cell + break; + case CELLTYPE_SYMBOLS: +#if DBG_UTIL + case CELLTYPE_DESTROYED: +#endif + SetError(errCellNoValue); + fValue = 0.0; + break; + } + } + else + fValue = 0.0; + return fValue; +} + + +void ScInterpreter::GetCellString( String& rStr, const ScBaseCell* pCell ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetCellString" ); + USHORT nErr = 0; + if (pCell) + { + switch (pCell->GetCellType()) + { + case CELLTYPE_STRING: + ((ScStringCell*) pCell)->GetString(rStr); + break; + case CELLTYPE_EDIT: + ((ScEditCell*) pCell)->GetString(rStr); + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFCell = (ScFormulaCell*) pCell; + nErr = pFCell->GetErrCode(); + if (pFCell->IsValue()) + { + double fVal = pFCell->GetValue(); + ULONG nIndex = pFormatter->GetStandardFormat( + NUMBERFORMAT_NUMBER, + ScGlobal::eLnge); + pFormatter->GetInputLineString(fVal, nIndex, rStr); + } + else + pFCell->GetString(rStr); + } + break; + case CELLTYPE_VALUE: + { + double fVal = ((ScValueCell*) pCell)->GetValue(); + ULONG nIndex = pFormatter->GetStandardFormat( + NUMBERFORMAT_NUMBER, + ScGlobal::eLnge); + pFormatter->GetInputLineString(fVal, nIndex, rStr); + } + break; + default: + rStr = ScGlobal::GetEmptyString(); + break; + } + } + else + rStr = ScGlobal::GetEmptyString(); + SetError(nErr); +} + + +BOOL ScInterpreter::CreateDoubleArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, BYTE* pCellArr) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CreateDoubleArr" ); + + // Old Add-Ins are hard limited to USHORT values. +#if MAXCOLCOUNT_DEFINE > USHRT_MAX +#error Add check for columns > USHRT_MAX! +#endif + if (nRow1 > USHRT_MAX || nRow2 > USHRT_MAX) + return FALSE; + + USHORT nCount = 0; + USHORT* p = (USHORT*) pCellArr; + *p++ = static_cast<USHORT>(nCol1); + *p++ = static_cast<USHORT>(nRow1); + *p++ = static_cast<USHORT>(nTab1); + *p++ = static_cast<USHORT>(nCol2); + *p++ = static_cast<USHORT>(nRow2); + *p++ = static_cast<USHORT>(nTab2); + USHORT* pCount = p; + *p++ = 0; + USHORT nPos = 14; + SCTAB nTab = nTab1; + ScAddress aAdr; + while (nTab <= nTab2) + { + aAdr.SetTab( nTab ); + SCROW nRow = nRow1; + while (nRow <= nRow2) + { + aAdr.SetRow( nRow ); + SCCOL nCol = nCol1; + while (nCol <= nCol2) + { + aAdr.SetCol( nCol ); + ScBaseCell* pCell = pDok->GetCell( aAdr ); + if (pCell) + { + USHORT nErr = 0; + double nVal = 0.0; + BOOL bOk = TRUE; + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : + nVal = GetValueCellValue( aAdr, (ScValueCell*)pCell ); + break; + case CELLTYPE_FORMULA : + if (((ScFormulaCell*)pCell)->IsValue()) + { + nErr = ((ScFormulaCell*)pCell)->GetErrCode(); + nVal = ((ScFormulaCell*)pCell)->GetValue(); + } + else + bOk = FALSE; + break; + default : + bOk = FALSE; + break; + } + if (bOk) + { + if ((nPos + (4 * sizeof(USHORT)) + sizeof(double)) > MAXARRSIZE) + return FALSE; + *p++ = static_cast<USHORT>(nCol); + *p++ = static_cast<USHORT>(nRow); + *p++ = static_cast<USHORT>(nTab); + *p++ = nErr; + memcpy( p, &nVal, sizeof(double)); + nPos += 8 + sizeof(double); + p = (USHORT*) ( pCellArr + nPos ); + nCount++; + } + } + nCol++; + } + nRow++; + } + nTab++; + } + *pCount = nCount; + return TRUE; +} + + +BOOL ScInterpreter::CreateStringArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, + BYTE* pCellArr) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CreateStringArr" ); + + // Old Add-Ins are hard limited to USHORT values. +#if MAXCOLCOUNT_DEFINE > USHRT_MAX +#error Add check for columns > USHRT_MAX! +#endif + if (nRow1 > USHRT_MAX || nRow2 > USHRT_MAX) + return FALSE; + + USHORT nCount = 0; + USHORT* p = (USHORT*) pCellArr; + *p++ = static_cast<USHORT>(nCol1); + *p++ = static_cast<USHORT>(nRow1); + *p++ = static_cast<USHORT>(nTab1); + *p++ = static_cast<USHORT>(nCol2); + *p++ = static_cast<USHORT>(nRow2); + *p++ = static_cast<USHORT>(nTab2); + USHORT* pCount = p; + *p++ = 0; + USHORT nPos = 14; + SCTAB nTab = nTab1; + while (nTab <= nTab2) + { + SCROW nRow = nRow1; + while (nRow <= nRow2) + { + SCCOL nCol = nCol1; + while (nCol <= nCol2) + { + ScBaseCell* pCell; + pDok->GetCell(nCol, nRow, nTab, pCell); + if (pCell) + { + String aStr; + USHORT nErr = 0; + BOOL bOk = TRUE; + switch ( pCell->GetCellType() ) + { + case CELLTYPE_STRING : + ((ScStringCell*)pCell)->GetString(aStr); + break; + case CELLTYPE_EDIT : + ((ScEditCell*)pCell)->GetString(aStr); + break; + case CELLTYPE_FORMULA : + if (!((ScFormulaCell*)pCell)->IsValue()) + { + nErr = ((ScFormulaCell*)pCell)->GetErrCode(); + ((ScFormulaCell*)pCell)->GetString(aStr); + } + else + bOk = FALSE; + break; + default : + bOk = FALSE; + break; + } + if (bOk) + { + ByteString aTmp( aStr, osl_getThreadTextEncoding() ); + // In case the xub_StrLen will be longer than USHORT + // one day, and room for pad byte check. + if ( aTmp.Len() > ((USHORT)(~0)) - 2 ) + return FALSE; + // Append a 0-pad-byte if string length is not even + //! MUST be USHORT and not xub_StrLen + USHORT nStrLen = (USHORT) aTmp.Len(); + USHORT nLen = ( nStrLen + 2 ) & ~1; + + if (((ULONG)nPos + (5 * sizeof(USHORT)) + nLen) > MAXARRSIZE) + return FALSE; + *p++ = static_cast<USHORT>(nCol); + *p++ = static_cast<USHORT>(nRow); + *p++ = static_cast<USHORT>(nTab); + *p++ = nErr; + *p++ = nLen; + memcpy( p, aTmp.GetBuffer(), nStrLen + 1); + nPos += 10 + nStrLen + 1; + BYTE* q = ( pCellArr + nPos ); + if( !nStrLen & 1 ) + *q++ = 0, nPos++; + p = (USHORT*) ( pCellArr + nPos ); + nCount++; + } + } + nCol++; + } + nRow++; + } + nTab++; + } + *pCount = nCount; + return TRUE; +} + + +BOOL ScInterpreter::CreateCellArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, + BYTE* pCellArr) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CreateCellArr" ); + + // Old Add-Ins are hard limited to USHORT values. +#if MAXCOLCOUNT_DEFINE > USHRT_MAX +#error Add check for columns > USHRT_MAX! +#endif + if (nRow1 > USHRT_MAX || nRow2 > USHRT_MAX) + return FALSE; + + USHORT nCount = 0; + USHORT* p = (USHORT*) pCellArr; + *p++ = static_cast<USHORT>(nCol1); + *p++ = static_cast<USHORT>(nRow1); + *p++ = static_cast<USHORT>(nTab1); + *p++ = static_cast<USHORT>(nCol2); + *p++ = static_cast<USHORT>(nRow2); + *p++ = static_cast<USHORT>(nTab2); + USHORT* pCount = p; + *p++ = 0; + USHORT nPos = 14; + SCTAB nTab = nTab1; + ScAddress aAdr; + while (nTab <= nTab2) + { + aAdr.SetTab( nTab ); + SCROW nRow = nRow1; + while (nRow <= nRow2) + { + aAdr.SetRow( nRow ); + SCCOL nCol = nCol1; + while (nCol <= nCol2) + { + aAdr.SetCol( nCol ); + ScBaseCell* pCell = pDok->GetCell( aAdr ); + if (pCell) + { + USHORT nErr = 0; + USHORT nType = 0; // 0 = Zahl; 1 = String + double nVal = 0.0; + String aStr; + BOOL bOk = TRUE; + switch ( pCell->GetCellType() ) + { + case CELLTYPE_STRING : + ((ScStringCell*)pCell)->GetString(aStr); + nType = 1; + break; + case CELLTYPE_EDIT : + ((ScEditCell*)pCell)->GetString(aStr); + nType = 1; + break; + case CELLTYPE_VALUE : + nVal = GetValueCellValue( aAdr, (ScValueCell*)pCell ); + break; + case CELLTYPE_FORMULA : + nErr = ((ScFormulaCell*)pCell)->GetErrCode(); + if (((ScFormulaCell*)pCell)->IsValue()) + nVal = ((ScFormulaCell*)pCell)->GetValue(); + else + ((ScFormulaCell*)pCell)->GetString(aStr); + break; + default : + bOk = FALSE; + break; + } + if (bOk) + { + if ((nPos + (5 * sizeof(USHORT))) > MAXARRSIZE) + return FALSE; + *p++ = static_cast<USHORT>(nCol); + *p++ = static_cast<USHORT>(nRow); + *p++ = static_cast<USHORT>(nTab); + *p++ = nErr; + *p++ = nType; + nPos += 10; + if (nType == 0) + { + if ((nPos + sizeof(double)) > MAXARRSIZE) + return FALSE; + memcpy( p, &nVal, sizeof(double)); + nPos += sizeof(double); + } + else + { + ByteString aTmp( aStr, osl_getThreadTextEncoding() ); + // In case the xub_StrLen will be longer than USHORT + // one day, and room for pad byte check. + if ( aTmp.Len() > ((USHORT)(~0)) - 2 ) + return FALSE; + // Append a 0-pad-byte if string length is not even + //! MUST be USHORT and not xub_StrLen + USHORT nStrLen = (USHORT) aTmp.Len(); + USHORT nLen = ( nStrLen + 2 ) & ~1; + if ( ((ULONG)nPos + 2 + nLen) > MAXARRSIZE) + return FALSE; + *p++ = nLen; + memcpy( p, aTmp.GetBuffer(), nStrLen + 1); + nPos += 2 + nStrLen + 1; + BYTE* q = ( pCellArr + nPos ); + if( !nStrLen & 1 ) + *q++ = 0, nPos++; + } + nCount++; + p = (USHORT*) ( pCellArr + nPos ); + } + } + nCol++; + } + nRow++; + } + nTab++; + } + *pCount = nCount; + return TRUE; +} + + +//----------------------------------------------------------------------------- +// Stack operations +//----------------------------------------------------------------------------- + + +// Also releases a TempToken if appropriate. + +void ScInterpreter::PushWithoutError( FormulaToken& r ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushWithoutError" ); + if ( sp >= MAXSTACK ) + SetError( errStackOverflow ); + else + { + nCurFmtType = NUMBERFORMAT_UNDEFINED; + r.IncRef(); + if( sp >= maxsp ) + maxsp = sp + 1; + else + pStack[ sp ]->DecRef(); + pStack[ sp ] = (ScToken*) &r; + ++sp; + } +} + +void ScInterpreter::Push( FormulaToken& r ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::Push" ); + if ( sp >= MAXSTACK ) + SetError( errStackOverflow ); + else + { + if (nGlobalError) + { + if (r.GetType() == svError) + { + r.SetError( nGlobalError); + PushWithoutError( r); + } + else + PushWithoutError( *(new FormulaErrorToken( nGlobalError))); + } + else + PushWithoutError( r); + } +} + + +void ScInterpreter::PushTempToken( FormulaToken* p ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushTempToken" ); + if ( sp >= MAXSTACK ) + { + SetError( errStackOverflow ); + if (!p->GetRef()) + //! p is a dangling pointer hereafter! + p->Delete(); + } + else + { + if (nGlobalError) + { + if (p->GetType() == svError) + { + p->SetError( nGlobalError); + PushTempTokenWithoutError( p); + } + else + { + if (!p->GetRef()) + //! p is a dangling pointer hereafter! + p->Delete(); + PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError)); + } + } + else + PushTempTokenWithoutError( p); + } +} + + +void ScInterpreter::PushTempTokenWithoutError( FormulaToken* p ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushTempTokenWithoutError" ); + p->IncRef(); + if ( sp >= MAXSTACK ) + { + SetError( errStackOverflow ); + //! p may be a dangling pointer hereafter! + p->DecRef(); + } + else + { + if( sp >= maxsp ) + maxsp = sp + 1; + else + pStack[ sp ]->DecRef(); + pStack[ sp ] = p; + ++sp; + } +} + + +void ScInterpreter::PushTempToken( const FormulaToken& r ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushTempToken" ); + if (!IfErrorPushError()) + PushTempTokenWithoutError( r.Clone()); +} + + +void ScInterpreter::PushCellResultToken( bool bDisplayEmptyAsString, + const ScAddress & rAddress, short * pRetTypeExpr, ULONG * pRetIndexExpr ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushCellResultToken" ); + ScBaseCell* pCell = pDok->GetCell( rAddress); + if (!pCell || pCell->HasEmptyData()) + { + if (pRetTypeExpr && pRetIndexExpr) + pDok->GetNumberFormatInfo( *pRetTypeExpr, *pRetIndexExpr, rAddress, pCell); + bool bInherited = (GetCellType( pCell) == CELLTYPE_FORMULA); + PushTempToken( new ScEmptyCellToken( bInherited, bDisplayEmptyAsString)); + return; + } + USHORT nErr; + if ((nErr = pCell->GetErrorCode()) != 0) + { + PushError( nErr); + if (pRetTypeExpr) + *pRetTypeExpr = NUMBERFORMAT_UNDEFINED; + if (pRetIndexExpr) + *pRetIndexExpr = 0; + } + else if (pCell->HasStringData()) + { + String aRes; + GetCellString( aRes, pCell); + PushString( aRes); + if (pRetTypeExpr) + *pRetTypeExpr = NUMBERFORMAT_TEXT; + if (pRetIndexExpr) + *pRetIndexExpr = 0; + } + else + { + double fVal = GetCellValue( rAddress, pCell); + PushDouble( fVal); + if (pRetTypeExpr) + *pRetTypeExpr = nCurFmtType; + if (pRetIndexExpr) + *pRetIndexExpr = nCurFmtIndex; + } +} + + +// Simply throw away TOS. + +void ScInterpreter::Pop() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::Pop" ); + if( sp ) + sp--; + else + SetError(errUnknownStackVariable); +} + + +// Simply throw away TOS and set error code, used with ocIsError et al. + +void ScInterpreter::PopError() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PopError" ); + if( sp ) + { + sp--; + if (pStack[sp]->GetType() == svError) + nGlobalError = pStack[sp]->GetError(); + } + else + SetError(errUnknownStackVariable); +} + + +FormulaTokenRef ScInterpreter::PopToken() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PopToken" ); + if (sp) + { + sp--; + FormulaToken* p = pStack[ sp ]; + if (p->GetType() == svError) + nGlobalError = p->GetError(); + return p; + } + else + SetError(errUnknownStackVariable); + return NULL; +} + + +double ScInterpreter::PopDouble() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PopDouble" ); + nCurFmtType = NUMBERFORMAT_NUMBER; + nCurFmtIndex = 0; + if( sp ) + { + --sp; + FormulaToken* p = pStack[ sp ]; + switch (p->GetType()) + { + case svError: + nGlobalError = p->GetError(); + break; + case svDouble: + return p->GetDouble(); + case svEmptyCell: + case svMissing: + return 0.0; + default: + SetError( errIllegalArgument); + } + } + else + SetError( errUnknownStackVariable); + return 0.0; +} + + +const String& ScInterpreter::PopString() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PopString" ); + nCurFmtType = NUMBERFORMAT_TEXT; + nCurFmtIndex = 0; + if( sp ) + { + --sp; + FormulaToken* p = pStack[ sp ]; + switch (p->GetType()) + { + case svError: + nGlobalError = p->GetError(); + break; + case svString: + return p->GetString(); + case svEmptyCell: + case svMissing: + return EMPTY_STRING; + default: + SetError( errIllegalArgument); + } + } + else + SetError( errUnknownStackVariable); + return EMPTY_STRING; +} + + +void ScInterpreter::ValidateRef( const ScSingleRefData & rRef ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ValidateRef" ); + SCCOL nCol; + SCROW nRow; + SCTAB nTab; + SingleRefToVars( rRef, nCol, nRow, nTab); +} + + +void ScInterpreter::ValidateRef( const ScComplexRefData & rRef ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ValidateRef" ); + ValidateRef( rRef.Ref1); + ValidateRef( rRef.Ref2); +} + + +void ScInterpreter::ValidateRef( const ScRefList & rRefList ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ValidateRef" ); + ScRefList::const_iterator it( rRefList.begin()); + ScRefList::const_iterator end( rRefList.end()); + for ( ; it != end; ++it) + { + ValidateRef( *it); + } +} + + +void ScInterpreter::SingleRefToVars( const ScSingleRefData & rRef, + SCCOL & rCol, SCROW & rRow, SCTAB & rTab ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::SingleRefToVars" ); + if ( rRef.IsColRel() ) + rCol = aPos.Col() + rRef.nRelCol; + else + rCol = rRef.nCol; + if ( rRef.IsRowRel() ) + rRow = aPos.Row() + rRef.nRelRow; + else + rRow = rRef.nRow; + if ( rRef.IsTabRel() ) + rTab = aPos.Tab() + rRef.nRelTab; + else + rTab = rRef.nTab; + if( !ValidCol( rCol) || rRef.IsColDeleted() ) + SetError( errNoRef ), rCol = 0; + if( !ValidRow( rRow) || rRef.IsRowDeleted() ) + SetError( errNoRef ), rRow = 0; + if( !ValidTab( rTab, pDok->GetTableCount() - 1) || rRef.IsTabDeleted() ) + SetError( errNoRef ), rTab = 0; +} + + +void ScInterpreter::PopSingleRef(SCCOL& rCol, SCROW &rRow, SCTAB& rTab) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PopSingleRef" ); + if( sp ) + { + --sp; + FormulaToken* p = pStack[ sp ]; + switch (p->GetType()) + { + case svError: + nGlobalError = p->GetError(); + break; + case svSingleRef: + SingleRefToVars( static_cast<ScToken*>(p)->GetSingleRef(), rCol, rRow, rTab); + if ( pDok->aTableOpList.Count() > 0 ) + ReplaceCell( rCol, rRow, rTab ); + break; + default: + SetError( errIllegalParameter); + } + } + else + SetError( errUnknownStackVariable); +} + + +void ScInterpreter::PopSingleRef( ScAddress& rAdr ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PopSingleRef" ); + if( sp ) + { + --sp; + FormulaToken* p = pStack[ sp ]; + switch (p->GetType()) + { + case svError: + nGlobalError = p->GetError(); + break; + case svSingleRef: + { + SCCOL nCol; + SCROW nRow; + SCTAB nTab; + SingleRefToVars( static_cast<ScToken*>(p)->GetSingleRef(), nCol, nRow, nTab); + rAdr.Set( nCol, nRow, nTab ); + if ( pDok->aTableOpList.Count() > 0 ) + ReplaceCell( rAdr ); + } + break; + default: + SetError( errIllegalParameter); + } + } + else + SetError( errUnknownStackVariable); +} + + +void ScInterpreter::DoubleRefToVars( const ScToken* p, + SCCOL& rCol1, SCROW &rRow1, SCTAB& rTab1, + SCCOL& rCol2, SCROW &rRow2, SCTAB& rTab2, + BOOL bDontCheckForTableOp ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::DoubleRefToVars" ); + const ScComplexRefData& rCRef = p->GetDoubleRef(); + SingleRefToVars( rCRef.Ref1, rCol1, rRow1, rTab1); + SingleRefToVars( rCRef.Ref2, rCol2, rRow2, rTab2); + if ( pDok->aTableOpList.Count() > 0 && !bDontCheckForTableOp ) + { + ScRange aRange( rCol1, rRow1, rTab1, rCol2, rRow2, rTab2 ); + if ( IsTableOpInRange( aRange ) ) + SetError( errIllegalParameter ); + } +} + +ScDBRangeBase* ScInterpreter::PopDoubleRef() +{ + if (!sp) + { + SetError(errUnknownStackVariable); + return NULL; + } + + --sp; + FormulaToken* p = pStack[sp]; + switch (p->GetType()) + { + case svError: + nGlobalError = p->GetError(); + break; + case svDoubleRef: + { + SCCOL nCol1, nCol2; + SCROW nRow1, nRow2; + SCTAB nTab1, nTab2; + DoubleRefToVars(static_cast<ScToken*>(p), + nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, false); + + return new ScDBInternalRange(pDok, + ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2)); + } + case svMatrix: + { + ScMatrixRef pMat = static_cast<ScToken*>(p)->GetMatrix(); + return new ScDBExternalRange(pDok, pMat); + } + default: + SetError( errIllegalParameter); + } + return NULL; +} + +void ScInterpreter::PopDoubleRef(SCCOL& rCol1, SCROW &rRow1, SCTAB& rTab1, + SCCOL& rCol2, SCROW &rRow2, SCTAB& rTab2, + BOOL bDontCheckForTableOp ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PopDoubleRef" ); + if( sp ) + { + --sp; + FormulaToken* p = pStack[ sp ]; + switch (p->GetType()) + { + case svError: + nGlobalError = p->GetError(); + break; + case svDoubleRef: + DoubleRefToVars( static_cast<ScToken*>(p), rCol1, rRow1, rTab1, rCol2, rRow2, rTab2, + bDontCheckForTableOp); + break; + default: + SetError( errIllegalParameter); + } + } + else + SetError( errUnknownStackVariable); +} + + +void ScInterpreter::DoubleRefToRange( const ScComplexRefData & rCRef, + ScRange & rRange, BOOL bDontCheckForTableOp ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::DoubleRefToRange" ); + SCCOL nCol; + SCROW nRow; + SCTAB nTab; + SingleRefToVars( rCRef.Ref1, nCol, nRow, nTab); + rRange.aStart.Set( nCol, nRow, nTab ); + SingleRefToVars( rCRef.Ref2, nCol, nRow, nTab); + rRange.aEnd.Set( nCol, nRow, nTab ); + if ( pDok->aTableOpList.Count() > 0 && !bDontCheckForTableOp ) + { + if ( IsTableOpInRange( rRange ) ) + SetError( errIllegalParameter ); + } +} + + +void ScInterpreter::PopDoubleRef( ScRange & rRange, short & rParam, size_t & rRefInList ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PopDoubleRef" ); + if (sp) + { + formula::FormulaToken* pToken = pStack[ sp-1 ]; + ScToken* p = static_cast<ScToken*>(pToken); + switch (pToken->GetType()) + { + case svError: + nGlobalError = p->GetError(); + break; + case svDoubleRef: + --sp; + DoubleRefToRange( p->GetDoubleRef(), rRange); + break; + case svRefList: + { + const ScRefList* pList = p->GetRefList(); + if (rRefInList < pList->size()) + { + DoubleRefToRange( (*pList)[rRefInList], rRange); + if (++rRefInList < pList->size()) + ++rParam; + else + { + --sp; + rRefInList = 0; + } + } + else + { + --sp; + rRefInList = 0; + SetError( errIllegalParameter); + } + } + break; + default: + SetError( errIllegalParameter); + } + } + else + SetError( errUnknownStackVariable); +} + + +void ScInterpreter::PopDoubleRef( ScRange& rRange, BOOL bDontCheckForTableOp ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PopDoubleRef" ); + if( sp ) + { + --sp; + FormulaToken* p = pStack[ sp ]; + switch (p->GetType()) + { + case svError: + nGlobalError = p->GetError(); + break; + case svDoubleRef: + DoubleRefToRange( static_cast<ScToken*>(p)->GetDoubleRef(), rRange, bDontCheckForTableOp); + break; + default: + SetError( errIllegalParameter); + } + } + else + SetError( errUnknownStackVariable); +} + + +BOOL ScInterpreter::PopDoubleRefOrSingleRef( ScAddress& rAdr ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PopDoubleRefOrSingleRef" ); + switch ( GetStackType() ) + { + case svDoubleRef : + { + ScRange aRange; + PopDoubleRef( aRange, TRUE ); + return DoubleRefToPosSingleRef( aRange, rAdr ); + } + //break; + case svSingleRef : + { + PopSingleRef( rAdr ); + return TRUE; + } + //break; + default: + PopError(); + SetError( errNoRef ); + } + return FALSE; +} + + +void ScInterpreter::PopDoubleRefPushMatrix() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PopDoubleRefPushMatrix" ); + if ( GetStackType() == svDoubleRef ) + { + ScMatrixRef pMat = GetMatrix(); + if ( pMat ) + PushMatrix( pMat ); + else + PushIllegalParameter(); + } + else + SetError( errNoRef ); +} + + +ScTokenMatrixMap* ScInterpreter::CreateTokenMatrixMap() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CreateTokenMatrixMap" ); + return new ScTokenMatrixMap; +} + + +bool ScInterpreter::ConvertMatrixParameters() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ConvertMatrixParameters" ); + USHORT nParams = pCur->GetParamCount(); + DBG_ASSERT( nParams <= sp, "ConvertMatrixParameters: stack/param count mismatch"); + SCSIZE nJumpCols = 0, nJumpRows = 0; + for ( USHORT i=1; i <= nParams && i <= sp; ++i ) + { + FormulaToken* p = pStack[ sp - i ]; + if ( p->GetOpCode() != ocPush ) + { + DBG_ERRORFILE( "ConvertMatrixParameters: not a push"); + } + else + { + switch ( p->GetType() ) + { + case svDouble: + case svString: + case svSingleRef: + case svMissing: + case svError: + case svEmptyCell: + // nothing to do + break; + case svMatrix: + { + if ( ScParameterClassification::GetParameterType( pCur, nParams - i) + == ScParameterClassification::Value ) + { // only if single value expected + ScMatrixRef pMat = static_cast<ScToken*>(p)->GetMatrix(); + if ( !pMat ) + SetError( errUnknownVariable); + else + { + SCSIZE nCols, nRows; + pMat->GetDimensions( nCols, nRows); + if ( nJumpCols < nCols ) + nJumpCols = nCols; + if ( nJumpRows < nRows ) + nJumpRows = nRows; + } + } + } + break; + case svDoubleRef: + { + ScParameterClassification::Type eType = + ScParameterClassification::GetParameterType( pCur, nParams - i); + if ( eType != ScParameterClassification::Reference && + eType != ScParameterClassification::ReferenceOrForceArray) + { + SCCOL nCol1, nCol2; + SCROW nRow1, nRow2; + SCTAB nTab1, nTab2; + DoubleRefToVars( static_cast<const ScToken*>( p), nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + // Make sure the map exists, created if not. + GetTokenMatrixMap(); + ScMatrixRef pMat = CreateMatrixFromDoubleRef( p, + nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + if (pMat) + { + if ( eType == ScParameterClassification::Value ) + { // only if single value expected + if ( nJumpCols < static_cast<SCSIZE>(nCol2 - nCol1 + 1) ) + nJumpCols = static_cast<SCSIZE>(nCol2 - nCol1 + 1); + if ( nJumpRows < static_cast<SCSIZE>(nRow2 - nRow1 + 1) ) + nJumpRows = static_cast<SCSIZE>(nRow2 - nRow1 + 1); + } + ScToken* pNew = new ScMatrixToken( pMat); + pNew->IncRef(); + pStack[ sp - i ] = pNew; + p->DecRef(); // p may be dead now! + } + } + } + break; + case svRefList: + { + ScParameterClassification::Type eType = + ScParameterClassification::GetParameterType( pCur, nParams - i); + if ( eType != ScParameterClassification::Reference && + eType != ScParameterClassification::ReferenceOrForceArray) + { + // can't convert to matrix + SetError( errNoValue); + } + } + break; + default: + DBG_ERRORFILE( "ConvertMatrixParameters: unknown parameter type"); + } + } + } + if( nJumpCols && nJumpRows ) + { + short nPC = aCode.GetPC(); + short nStart = nPC - 1; // restart on current code (-1) + short nNext = nPC; // next instruction after subroutine + short nStop = nPC + 1; // stop subroutine before reaching that + FormulaTokenRef xNew; + ScTokenMatrixMap::const_iterator aMapIter; + if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != + pTokenMatrixMap->end())) + xNew = (*aMapIter).second; + else + { + ScJumpMatrix* pJumpMat = new ScJumpMatrix( nJumpCols, nJumpRows); + pJumpMat->SetAllJumps( 1.0, nStart, nNext, nStop); + // pop parameters and store in ScJumpMatrix, push in JumpMatrix() + ScTokenVec* pParams = new ScTokenVec( nParams); + for ( USHORT i=1; i <= nParams && sp > 0; ++i ) + { + FormulaToken* p = pStack[ --sp ]; + p->IncRef(); + // store in reverse order such that a push may simply iterate + (*pParams)[ nParams - i ] = p; + } + pJumpMat->SetJumpParameters( pParams); + xNew = new ScJumpMatrixToken( pJumpMat ); + GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type( pCur, + xNew)); + } + PushTempToken( xNew); + // set continuation point of path for main code line + aCode.Jump( nNext, nNext); + return true; + } + return false; +} + + +ScMatrixRef ScInterpreter::PopMatrix() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PopMatrix" ); + if( sp ) + { + --sp; + FormulaToken* p = pStack[ sp ]; + switch (p->GetType()) + { + case svError: + nGlobalError = p->GetError(); + break; + case svMatrix: + { + ScMatrix* pMat = static_cast<ScToken*>(p)->GetMatrix(); + if ( pMat ) + pMat->SetErrorInterpreter( this); + else + SetError( errUnknownVariable); + return pMat; + } + default: + SetError( errIllegalParameter); + } + } + else + SetError( errUnknownStackVariable); + return NULL; +} + + +void ScInterpreter::PushDouble(double nVal) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushDouble" ); + TreatDoubleError( nVal ); + if (!IfErrorPushError()) + PushTempTokenWithoutError( new FormulaDoubleToken( nVal ) ); +} + + +void ScInterpreter::PushInt(int nVal) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushInt" ); + if (!IfErrorPushError()) + PushTempTokenWithoutError( new FormulaDoubleToken( nVal ) ); +} + + +void ScInterpreter::PushStringBuffer( const sal_Unicode* pString ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushStringBuffer" ); + if ( pString ) + PushString( String( pString ) ); + else + PushString( EMPTY_STRING ); +} + + +void ScInterpreter::PushString( const String& rString ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushString" ); + if (!IfErrorPushError()) + PushTempTokenWithoutError( new FormulaStringToken( rString ) ); +} + + +void ScInterpreter::PushSingleRef(SCCOL nCol, SCROW nRow, SCTAB nTab) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushSingleRef" ); + if (!IfErrorPushError()) + { + ScSingleRefData aRef; + aRef.InitFlags(); + aRef.nCol = nCol; + aRef.nRow = nRow; + aRef.nTab = nTab; + PushTempTokenWithoutError( new ScSingleRefToken( aRef ) ); + } +} + + +void ScInterpreter::PushDoubleRef(SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushDoubleRef" ); + if (!IfErrorPushError()) + { + ScComplexRefData aRef; + aRef.InitFlags(); + aRef.Ref1.nCol = nCol1; + aRef.Ref1.nRow = nRow1; + aRef.Ref1.nTab = nTab1; + aRef.Ref2.nCol = nCol2; + aRef.Ref2.nRow = nRow2; + aRef.Ref2.nTab = nTab2; + PushTempTokenWithoutError( new ScDoubleRefToken( aRef ) ); + } +} + + +void ScInterpreter::PushMatrix(ScMatrix* pMat) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushMatrix" ); + pMat->SetErrorInterpreter( NULL); + // No if (!IfErrorPushError()) because ScMatrix stores errors itself, + // but with notifying ScInterpreter via nGlobalError, substituting it would + // mean to inherit the error on all array elements in all following + // operations. + nGlobalError = 0; + PushTempTokenWithoutError( new ScMatrixToken( pMat ) ); +} + + +void ScInterpreter::PushError( USHORT nError ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushError" ); + SetError( nError ); // only sets error if not already set + PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError)); +} + +void ScInterpreter::PushParameterExpected() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushParameterExpected" ); + PushError( errParameterExpected); +} + + +void ScInterpreter::PushIllegalParameter() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushIllegalParameter" ); + PushError( errIllegalParameter); +} + + +void ScInterpreter::PushIllegalArgument() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushIllegalArgument" ); + PushError( errIllegalArgument); +} + + +void ScInterpreter::PushNA() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushNA" ); + PushError( NOTAVAILABLE); +} + + +void ScInterpreter::PushNoValue() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::PushNoValue" ); + PushError( errNoValue); +} + + +BOOL ScInterpreter::IsMissing() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::IsMissing" ); + return sp && pStack[sp - 1]->GetType() == svMissing; +} + + +StackVar ScInterpreter::GetRawStackType() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetRawStackType" ); + StackVar eRes; + if( sp ) + { + eRes = pStack[sp - 1]->GetType(); + } + else + { + SetError(errUnknownStackVariable); + eRes = svUnknown; + } + return eRes; +} + + +StackVar ScInterpreter::GetStackType() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetStackType" ); + StackVar eRes; + if( sp ) + { + eRes = pStack[sp - 1]->GetType(); + if( eRes == svMissing || eRes == svEmptyCell ) + eRes = svDouble; // default! + } + else + { + SetError(errUnknownStackVariable); + eRes = svUnknown; + } + return eRes; +} + + +StackVar ScInterpreter::GetStackType( BYTE nParam ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetStackType" ); + StackVar eRes; + if( sp > nParam-1 ) + { + eRes = pStack[sp - nParam]->GetType(); + if( eRes == svMissing || eRes == svEmptyCell ) + eRes = svDouble; // default! + } + else + eRes = svUnknown; + return eRes; +} + + +BOOL ScInterpreter::DoubleRefToPosSingleRef( const ScRange& rRange, ScAddress& rAdr ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::DoubleRefToPosSingleRef" ); + // Check for a singleton first - no implicit intersection for them. + if( rRange.aStart == rRange.aEnd ) + { + rAdr = rRange.aStart; + return TRUE; + } + + BOOL bOk = FALSE; + + if ( pJumpMatrix ) + { + bOk = rRange.aStart.Tab() == rRange.aEnd.Tab(); + if ( !bOk ) + SetError( errIllegalArgument); + else + { + SCSIZE nC, nR; + pJumpMatrix->GetPos( nC, nR); + rAdr.SetCol( sal::static_int_cast<SCCOL>( rRange.aStart.Col() + nC ) ); + rAdr.SetRow( sal::static_int_cast<SCROW>( rRange.aStart.Row() + nR ) ); + rAdr.SetTab( rRange.aStart.Tab()); + bOk = rRange.aStart.Col() <= rAdr.Col() && rAdr.Col() <= + rRange.aEnd.Col() && rRange.aStart.Row() <= rAdr.Row() && + rAdr.Row() <= rRange.aEnd.Row(); + if ( !bOk ) + SetError( errNoValue); + } + return bOk; + } + + SCCOL nMyCol = aPos.Col(); + SCROW nMyRow = aPos.Row(); + SCTAB nMyTab = aPos.Tab(); + SCCOL nCol = 0; + SCROW nRow = 0; + SCTAB nTab; + nTab = rRange.aStart.Tab(); + if ( rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() ) + { + nRow = rRange.aStart.Row(); + if ( nRow == rRange.aEnd.Row() ) + { + bOk = TRUE; + nCol = nMyCol; + } + else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab() + && rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() ) + { + bOk = TRUE; + nCol = nMyCol; + nRow = nMyRow; + } + } + else if ( rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() ) + { + nCol = rRange.aStart.Col(); + if ( nCol == rRange.aEnd.Col() ) + { + bOk = TRUE; + nRow = nMyRow; + } + else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab() + && rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() ) + { + bOk = TRUE; + nCol = nMyCol; + nRow = nMyRow; + } + } + if ( bOk ) + { + if ( nTab == rRange.aEnd.Tab() ) + ; // all done + else if ( nTab <= nMyTab && nMyTab <= rRange.aEnd.Tab() ) + nTab = nMyTab; + else + bOk = FALSE; + if ( bOk ) + rAdr.Set( nCol, nRow, nTab ); + } + if ( !bOk ) + SetError( errNoValue ); + return bOk; +} + + +double ScInterpreter::GetDouble() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetDouble" ); + double nVal; + switch( GetRawStackType() ) + { + case svDouble: + nVal = PopDouble(); + break; + case svString: + nVal = ConvertStringToValue( PopString()); + break; + case svSingleRef: + { + ScAddress aAdr; + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + nVal = GetCellValue( aAdr, pCell ); + } + break; + case svDoubleRef: + { // generate position dependent SingleRef + ScRange aRange; + PopDoubleRef( aRange ); + ScAddress aAdr; + if ( !nGlobalError && DoubleRefToPosSingleRef( aRange, aAdr ) ) + { + ScBaseCell* pCell = GetCell( aAdr ); + nVal = GetCellValue( aAdr, pCell ); + } + else + nVal = 0.0; + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + nVal = 0.0; + else if ( !pJumpMatrix ) + nVal = pMat->GetDouble( 0 ); + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + nVal = pMat->GetDouble( nC, nR); + else + { + SetError( errNoValue); + nVal = 0.0; + } + } + } + break; + case svError: + PopError(); + nVal = 0.0; + break; + case svEmptyCell: + case svMissing: + Pop(); + nVal = 0.0; + break; + default: + PopError(); + SetError( errIllegalParameter); + nVal = 0.0; + } + if ( nFuncFmtType == nCurFmtType ) + nFuncFmtIndex = nCurFmtIndex; + return nVal; +} + + +double ScInterpreter::GetDoubleWithDefault(double nDefault) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetDoubleWithDefault" ); + bool bMissing = IsMissing(); + double nResultVal = GetDouble(); + if ( bMissing ) + nResultVal = nDefault; + return nResultVal; +} + + +const String& ScInterpreter::GetString() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetString" ); + switch (GetRawStackType()) + { + case svError: + PopError(); + return EMPTY_STRING; + //break; + case svMissing: + case svEmptyCell: + Pop(); + return EMPTY_STRING; + //break; + case svDouble: + { + double fVal = PopDouble(); + ULONG nIndex = pFormatter->GetStandardFormat( + NUMBERFORMAT_NUMBER, + ScGlobal::eLnge); + pFormatter->GetInputLineString(fVal, nIndex, aTempStr); + return aTempStr; + } + //break; + case svString: + return PopString(); + //break; + case svSingleRef: + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if (nGlobalError == 0) + { + ScBaseCell* pCell = GetCell( aAdr ); + GetCellString( aTempStr, pCell ); + return aTempStr; + } + else + return EMPTY_STRING; + } + //break; + case svDoubleRef: + { // generate position dependent SingleRef + ScRange aRange; + PopDoubleRef( aRange ); + ScAddress aAdr; + if ( !nGlobalError && DoubleRefToPosSingleRef( aRange, aAdr ) ) + { + ScBaseCell* pCell = GetCell( aAdr ); + GetCellString( aTempStr, pCell ); + return aTempStr; + } + else + return EMPTY_STRING; + } + //break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + ; // nothing + else if ( !pJumpMatrix ) + { + aTempStr = pMat->GetString( *pFormatter, 0, 0); + return aTempStr; + } + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + { + aTempStr = pMat->GetString( *pFormatter, nC, nR); + return aTempStr; + } + else + SetError( errNoValue); + } + } + break; + default: + PopError(); + SetError( errIllegalArgument); + } + return EMPTY_STRING; +} + + + +ScMatValType ScInterpreter::GetDoubleOrStringFromMatrix( double& rDouble, + String& rString ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetDoubleOrStringFromMatrix" ); + ScMatValType nMatValType = SC_MATVAL_EMPTY; + switch ( GetStackType() ) + { + case svMatrix: + { + const ScMatrixValue* pMatVal = 0; + ScMatrixRef pMat = PopMatrix(); + if (!pMat) + ; // nothing + else if (!pJumpMatrix) + pMatVal = pMat->Get( 0, 0, nMatValType); + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + pMatVal = pMat->Get( nC, nR, nMatValType); + else + SetError( errNoValue); + } + if (!pMatVal) + { + rDouble = 0.0; + rString.Erase(); + } + else if (nMatValType == SC_MATVAL_VALUE) + rDouble = pMatVal->fVal; + else if (nMatValType == SC_MATVAL_BOOLEAN) + { + rDouble = pMatVal->fVal; + nMatValType = SC_MATVAL_VALUE; + } + else + rString = pMatVal->GetString(); + } + break; + default: + PopError(); + rDouble = 0.0; + rString.Erase(); + SetError( errIllegalParameter); + } + return nMatValType; +} + + +void ScInterpreter::ScDBGet() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBGet" ); + BOOL bMissingField = FALSE; + auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); + if (!pQueryParam.get()) + { + // Failed to create query param. + PushIllegalParameter(); + return; + } + + pQueryParam->mbSkipString = false; + ScDBQueryDataIterator aValIter(pDok, pQueryParam.release()); + ScDBQueryDataIterator::Value aValue; + if (!aValIter.GetFirst(aValue) || aValue.mnError) + { + // No match found. + PushNoValue(); + return; + } + + ScDBQueryDataIterator::Value aValNext; + if (aValIter.GetNext(aValNext) && !aValNext.mnError) + { + // There should be only one unique match. + PushIllegalArgument(); + return; + } + + if (aValue.mbIsNumber) + PushDouble(aValue.mfValue); + else + PushString(aValue.maString); +} + + +void ScInterpreter::ScExternal() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScExternal" ); + USHORT nIndex; + BYTE nParamCount = GetByte(); + String aUnoName; + String aFuncName( ScGlobal::pCharClass->upper( pCur->GetExternal() ) ); + if (ScGlobal::GetFuncCollection()->SearchFunc(aFuncName, nIndex)) + { + FuncData* pFuncData = (FuncData*)ScGlobal::GetFuncCollection()->At(nIndex); + if (nParamCount <= MAXFUNCPARAM && nParamCount == pFuncData->GetParamCount() - 1) + { + ParamType eParamType[MAXFUNCPARAM]; + void* ppParam[MAXFUNCPARAM]; + double nVal[MAXFUNCPARAM]; + sal_Char* pStr[MAXFUNCPARAM]; + BYTE* pCellArr[MAXFUNCPARAM]; + short i; + + for (i = 0; i < MAXFUNCPARAM; i++) + { + eParamType[i] = pFuncData->GetParamType(i); + ppParam[i] = NULL; + nVal[i] = 0.0; + pStr[i] = NULL; + pCellArr[i] = NULL; + } + + for (i = nParamCount; (i > 0) && (nGlobalError == 0); i--) + { + switch (eParamType[i]) + { + case PTR_DOUBLE : + { + nVal[i-1] = GetDouble(); + ppParam[i] = &nVal[i-1]; + } + break; + case PTR_STRING : + { + ByteString aStr( GetString(), osl_getThreadTextEncoding() ); + if ( aStr.Len() >= ADDIN_MAXSTRLEN ) + SetError( errStringOverflow ); + else + { + pStr[i-1] = new sal_Char[ADDIN_MAXSTRLEN]; + strncpy( pStr[i-1], aStr.GetBuffer(), ADDIN_MAXSTRLEN ); + pStr[i-1][ADDIN_MAXSTRLEN-1] = 0; + ppParam[i] = pStr[i-1]; + } + } + break; + case PTR_DOUBLE_ARR : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + pCellArr[i-1] = new BYTE[MAXARRSIZE]; + if (!CreateDoubleArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1])) + SetError(errCodeOverflow); + else + ppParam[i] = pCellArr[i-1]; + } + break; + case PTR_STRING_ARR : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + pCellArr[i-1] = new BYTE[MAXARRSIZE]; + if (!CreateStringArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1])) + SetError(errCodeOverflow); + else + ppParam[i] = pCellArr[i-1]; + } + break; + case PTR_CELL_ARR : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + pCellArr[i-1] = new BYTE[MAXARRSIZE]; + if (!CreateCellArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1])) + SetError(errCodeOverflow); + else + ppParam[i] = pCellArr[i-1]; + } + break; + default : + SetError(errIllegalParameter); + break; + } + } + while ( i-- ) + Pop(); // im Fehlerfall (sonst ist i==0) Parameter wegpoppen + + if (nGlobalError == 0) + { + if ( pFuncData->GetAsyncType() == NONE ) + { + switch ( eParamType[0] ) + { + case PTR_DOUBLE : + { + double nErg = 0.0; + ppParam[0] = &nErg; + pFuncData->Call(ppParam); + PushDouble(nErg); + } + break; + case PTR_STRING : + { + sal_Char* pcErg = new sal_Char[ADDIN_MAXSTRLEN]; + ppParam[0] = pcErg; + pFuncData->Call(ppParam); + String aUni( pcErg, osl_getThreadTextEncoding() ); + PushString( aUni ); + delete[] pcErg; + } + break; + default: + PushError( errUnknownState ); + } + } + else + { + // nach dem Laden Asyncs wieder anwerfen + if ( pMyFormulaCell->GetCode()->IsRecalcModeNormal() ) + pMyFormulaCell->GetCode()->SetRecalcModeOnLoad(); + // garantiert identischer Handle bei identischem Aufruf?!? + // sonst schei*e ... + double nErg = 0.0; + ppParam[0] = &nErg; + pFuncData->Call(ppParam); + ULONG nHandle = ULONG( nErg ); + if ( nHandle >= 65536 ) + { + ScAddInAsync* pAs = ScAddInAsync::Get( nHandle ); + if ( !pAs ) + { + pAs = new ScAddInAsync( nHandle, nIndex, pDok ); + pMyFormulaCell->StartListening( *pAs ); + } + else + { + // falls per cut/copy/paste + pMyFormulaCell->StartListening( *pAs ); + // in anderes Dokument? + if ( !pAs->HasDocument( pDok ) ) + pAs->AddDocument( pDok ); + } + if ( pAs->IsValid() ) + { + switch ( pAs->GetType() ) + { + case PTR_DOUBLE : + PushDouble( pAs->GetValue() ); + break; + case PTR_STRING : + PushString( pAs->GetString() ); + break; + default: + PushError( errUnknownState ); + } + } + else + PushNA(); + } + else + PushNoValue(); + } + } + + for (i = 0; i < MAXFUNCPARAM; i++) + { + delete[] pStr[i]; + delete[] pCellArr[i]; + } + } + else + { + while( nParamCount-- > 0) + Pop(); + PushIllegalParameter(); + } + } + else if ( ( aUnoName = ScGlobal::GetAddInCollection()->FindFunction(aFuncName, FALSE) ).Len() ) + { + // bLocalFirst=FALSE in FindFunction, cFunc should be the stored internal name + + ScUnoAddInCall aCall( *ScGlobal::GetAddInCollection(), aUnoName, nParamCount ); + + if ( !aCall.ValidParamCount() ) + SetError( errIllegalParameter ); + + if ( aCall.NeedsCaller() && !GetError() ) + { + SfxObjectShell* pShell = pDok->GetDocumentShell(); + if (pShell) + aCall.SetCallerFromObjectShell( pShell ); + else + { + // use temporary model object (without document) to supply options + aCall.SetCaller( static_cast<beans::XPropertySet*>( + new ScDocOptionsObj( pDok->GetDocOptions() ) ) ); + } + } + + short nPar = nParamCount; + while ( nPar > 0 && !GetError() ) + { + --nPar; // 0 .. (nParamCount-1) + + ScAddInArgumentType eType = aCall.GetArgType( nPar ); + BYTE nStackType = sal::static_int_cast<BYTE>( GetStackType() ); + + uno::Any aParam; + switch (eType) + { + case SC_ADDINARG_INTEGER: + { + double fVal = GetDouble(); + double fInt = (fVal >= 0.0) ? ::rtl::math::approxFloor( fVal ) : + ::rtl::math::approxCeil( fVal ); + if ( fInt >= LONG_MIN && fInt <= LONG_MAX ) + aParam <<= (INT32)fInt; + else + SetError(errIllegalArgument); + } + break; + + case SC_ADDINARG_DOUBLE: + aParam <<= (double) GetDouble(); + break; + + case SC_ADDINARG_STRING: + aParam <<= rtl::OUString( GetString() ); + break; + + case SC_ADDINARG_INTEGER_ARRAY: + switch( nStackType ) + { + case svDouble: + case svString: + case svSingleRef: + { + double fVal = GetDouble(); + double fInt = (fVal >= 0.0) ? ::rtl::math::approxFloor( fVal ) : + ::rtl::math::approxCeil( fVal ); + if ( fInt >= LONG_MIN && fInt <= LONG_MAX ) + { + INT32 nIntVal = (long)fInt; + uno::Sequence<INT32> aInner( &nIntVal, 1 ); + uno::Sequence< uno::Sequence<INT32> > aOuter( &aInner, 1 ); + aParam <<= aOuter; + } + else + SetError(errIllegalArgument); + } + break; + case svDoubleRef: + { + ScRange aRange; + PopDoubleRef( aRange ); + if (!ScRangeToSequence::FillLongArray( aParam, pDok, aRange )) + SetError(errIllegalParameter); + } + break; + case svMatrix: + if (!ScRangeToSequence::FillLongArray( aParam, PopMatrix() )) + SetError(errIllegalParameter); + break; + default: + PopError(); + SetError(errIllegalParameter); + } + break; + + case SC_ADDINARG_DOUBLE_ARRAY: + switch( nStackType ) + { + case svDouble: + case svString: + case svSingleRef: + { + double fVal = GetDouble(); + uno::Sequence<double> aInner( &fVal, 1 ); + uno::Sequence< uno::Sequence<double> > aOuter( &aInner, 1 ); + aParam <<= aOuter; + } + break; + case svDoubleRef: + { + ScRange aRange; + PopDoubleRef( aRange ); + if (!ScRangeToSequence::FillDoubleArray( aParam, pDok, aRange )) + SetError(errIllegalParameter); + } + break; + case svMatrix: + if (!ScRangeToSequence::FillDoubleArray( aParam, PopMatrix() )) + SetError(errIllegalParameter); + break; + default: + PopError(); + SetError(errIllegalParameter); + } + break; + + case SC_ADDINARG_STRING_ARRAY: + switch( nStackType ) + { + case svDouble: + case svString: + case svSingleRef: + { + rtl::OUString aString = rtl::OUString( GetString() ); + uno::Sequence<rtl::OUString> aInner( &aString, 1 ); + uno::Sequence< uno::Sequence<rtl::OUString> > aOuter( &aInner, 1 ); + aParam <<= aOuter; + } + break; + case svDoubleRef: + { + ScRange aRange; + PopDoubleRef( aRange ); + if (!ScRangeToSequence::FillStringArray( aParam, pDok, aRange )) + SetError(errIllegalParameter); + } + break; + case svMatrix: + if (!ScRangeToSequence::FillStringArray( aParam, PopMatrix(), pFormatter )) + SetError(errIllegalParameter); + break; + default: + PopError(); + SetError(errIllegalParameter); + } + break; + + case SC_ADDINARG_MIXED_ARRAY: + switch( nStackType ) + { + case svDouble: + case svString: + case svSingleRef: + { + uno::Any aElem; + if ( nStackType == svDouble ) + aElem <<= (double) GetDouble(); + else if ( nStackType == svString ) + aElem <<= rtl::OUString( GetString() ); + else + { + ScAddress aAdr; + if ( PopDoubleRefOrSingleRef( aAdr ) ) + { + ScBaseCell* pCell = GetCell( aAdr ); + if ( pCell && pCell->HasStringData() ) + { + String aStr; + GetCellString( aStr, pCell ); + aElem <<= rtl::OUString( aStr ); + } + else + aElem <<= (double) GetCellValue( aAdr, pCell ); + } + } + uno::Sequence<uno::Any> aInner( &aElem, 1 ); + uno::Sequence< uno::Sequence<uno::Any> > aOuter( &aInner, 1 ); + aParam <<= aOuter; + } + break; + case svDoubleRef: + { + ScRange aRange; + PopDoubleRef( aRange ); + if (!ScRangeToSequence::FillMixedArray( aParam, pDok, aRange )) + SetError(errIllegalParameter); + } + break; + case svMatrix: + if (!ScRangeToSequence::FillMixedArray( aParam, PopMatrix() )) + SetError(errIllegalParameter); + break; + default: + PopError(); + SetError(errIllegalParameter); + } + break; + + case SC_ADDINARG_VALUE_OR_ARRAY: + if ( IsMissing() ) + nStackType = svMissing; + switch( nStackType ) + { + case svDouble: + aParam <<= (double) GetDouble(); + break; + case svString: + aParam <<= rtl::OUString( GetString() ); + break; + case svSingleRef: + { + ScAddress aAdr; + if ( PopDoubleRefOrSingleRef( aAdr ) ) + { + ScBaseCell* pCell = GetCell( aAdr ); + if ( pCell && pCell->HasStringData() ) + { + String aStr; + GetCellString( aStr, pCell ); + aParam <<= rtl::OUString( aStr ); + } + else + aParam <<= (double) GetCellValue( aAdr, pCell ); + } + } + break; + case svDoubleRef: + { + ScRange aRange; + PopDoubleRef( aRange ); + if (!ScRangeToSequence::FillMixedArray( aParam, pDok, aRange )) + SetError(errIllegalParameter); + } + break; + case svMatrix: + if (!ScRangeToSequence::FillMixedArray( aParam, PopMatrix() )) + SetError(errIllegalParameter); + break; + case svMissing: + Pop(); + aParam.clear(); + break; + default: + PopError(); + SetError(errIllegalParameter); + } + break; + + case SC_ADDINARG_CELLRANGE: + switch( nStackType ) + { + case svSingleRef: + { + ScAddress aAdr; + PopSingleRef( aAdr ); + ScRange aRange( aAdr ); + uno::Reference<table::XCellRange> xObj = + ScCellRangeObj::CreateRangeFromDoc( pDok, aRange ); + if (xObj.is()) + aParam <<= xObj; + else + SetError(errIllegalParameter); + } + break; + case svDoubleRef: + { + ScRange aRange; + PopDoubleRef( aRange ); + uno::Reference<table::XCellRange> xObj = + ScCellRangeObj::CreateRangeFromDoc( pDok, aRange ); + if (xObj.is()) + aParam <<= xObj; + else + SetError(errIllegalParameter); + } + break; + default: + PopError(); + SetError(errIllegalParameter); + } + break; + + default: + PopError(); + SetError(errIllegalParameter); + } + aCall.SetParam( nPar, aParam ); + } + + while (nPar-- > 0) + Pop(); // in case of error, remove remaining args + + if ( !GetError() ) + { + aCall.ExecuteCall(); + + if ( aCall.HasVarRes() ) // handle async functions + { + if ( pMyFormulaCell->GetCode()->IsRecalcModeNormal() ) + pMyFormulaCell->GetCode()->SetRecalcModeOnLoad(); + + uno::Reference<sheet::XVolatileResult> xRes = aCall.GetVarRes(); + ScAddInListener* pLis = ScAddInListener::Get( xRes ); + if ( !pLis ) + { + pLis = ScAddInListener::CreateListener( xRes, pDok ); + pMyFormulaCell->StartListening( *pLis ); + } + else + { + pMyFormulaCell->StartListening( *pLis ); + if ( !pLis->HasDocument( pDok ) ) + pLis->AddDocument( pDok ); + } + + aCall.SetResult( pLis->GetResult() ); // use result from async + } + + if ( aCall.GetErrCode() ) + PushError( aCall.GetErrCode() ); + else if ( aCall.HasMatrix() ) + { + ScMatrixRef xMat = aCall.GetMatrix(); + PushMatrix( xMat ); + } + else if ( aCall.HasString() ) + PushString( aCall.GetString() ); + else + PushDouble( aCall.GetValue() ); + } + else // error... + PushError( GetError()); + } + else + { + while( nParamCount-- > 0) + Pop(); + PushError( errNoAddin ); + } +} + + +void ScInterpreter::ScMissing() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMissing" ); + PushTempToken( new FormulaMissingToken ); +} + + +void ScInterpreter::ScMacro() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMacro" ); + SbxBase::ResetError(); + + BYTE nParamCount = GetByte(); + String aMacro( pCur->GetExternal() ); + + SfxObjectShell* pDocSh = pDok->GetDocumentShell(); + if ( !pDocSh || !pDok->CheckMacroWarn() ) + { + PushNoValue(); // ohne DocShell kein CallBasic + return; + } + + // keine Sicherheitsabfrage mehr vorneweg (nur CheckMacroWarn), das passiert im CallBasic + + SfxApplication* pSfxApp = SFX_APP(); + pSfxApp->EnterBasicCall(); // Dok-Basic anlegen etc. + + // Wenn das Dok waehrend eines Basic-Calls geladen wurde, + // ist das Sbx-Objekt evtl. nicht angelegt (?) +// pDocSh->GetSbxObject(); + + // Funktion ueber den einfachen Namen suchen, + // dann aBasicStr, aMacroStr fuer SfxObjectShell::CallBasic zusammenbauen + + StarBASIC* pRoot = pDocSh->GetBasic(); + SbxVariable* pVar = pRoot->Find( aMacro, SbxCLASS_METHOD ); + if( !pVar || pVar->GetType() == SbxVOID || !pVar->ISA(SbMethod) ) + { + PushError( errNoMacro ); + pSfxApp->LeaveBasicCall(); + return; + } + + SbMethod* pMethod = (SbMethod*)pVar; + SbModule* pModule = pMethod->GetModule(); + SbxObject* pObject = pModule->GetParent(); + DBG_ASSERT(pObject->IsA(TYPE(StarBASIC)), "Kein Basic gefunden!"); + String aMacroStr = pObject->GetName(); + aMacroStr += '.'; + aMacroStr += pModule->GetName(); + aMacroStr += '.'; + aMacroStr += pMethod->GetName(); + String aBasicStr; + if (pObject->GetParent()) + aBasicStr = pObject->GetParent()->GetName(); // Dokumentenbasic + else + aBasicStr = SFX_APP()->GetName(); // Applikationsbasic + + // Parameter-Array zusammenbauen + + SbxArrayRef refPar = new SbxArray; + BOOL bOk = TRUE; + for( short i = nParamCount; i && bOk ; i-- ) + { + SbxVariable* pPar = refPar->Get( (USHORT) i ); + BYTE nStackType = sal::static_int_cast<BYTE>( GetStackType() ); + switch( nStackType ) + { + case svDouble: + pPar->PutDouble( GetDouble() ); + break; + case svString: + pPar->PutString( GetString() ); + break; + case svSingleRef: + { + ScAddress aAdr; + PopSingleRef( aAdr ); + bOk = SetSbxVariable( pPar, aAdr ); + } + break; + case svDoubleRef: + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if( nTab1 != nTab2 ) + { + SetError( errIllegalParameter ); + bOk = FALSE; + } + else + { + SbxDimArrayRef refArray = new SbxDimArray; + refArray->AddDim32( 1, nRow2 - nRow1 + 1 ); + refArray->AddDim32( 1, nCol2 - nCol1 + 1 ); + ScAddress aAdr( nCol1, nRow1, nTab1 ); + for( SCROW nRow = nRow1; bOk && nRow <= nRow2; nRow++ ) + { + aAdr.SetRow( nRow ); + INT32 nIdx[ 2 ]; + nIdx[ 0 ] = nRow-nRow1+1; + for( SCCOL nCol = nCol1; bOk && nCol <= nCol2; nCol++ ) + { + aAdr.SetCol( nCol ); + nIdx[ 1 ] = nCol-nCol1+1; + SbxVariable* p = refArray->Get32( nIdx ); + bOk = SetSbxVariable( p, aAdr ); + } + } + pPar->PutObject( refArray ); + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + SCSIZE nC, nR; + if (pMat) + { + pMat->GetDimensions(nC, nR); + SbxDimArrayRef refArray = new SbxDimArray; + refArray->AddDim32( 1, static_cast<INT32>(nR) ); + refArray->AddDim32( 1, static_cast<INT32>(nC) ); + for( SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++ ) + { + INT32 nIdx[ 2 ]; + nIdx[ 0 ] = static_cast<INT32>(nMatRow+1); + for( SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++ ) + { + nIdx[ 1 ] = static_cast<INT32>(nMatCol+1); + SbxVariable* p = refArray->Get32( nIdx ); + if (pMat->IsString(nMatCol, nMatRow)) + p->PutString( pMat->GetString(nMatCol, nMatRow) ); + else + p->PutDouble( pMat->GetDouble(nMatCol, nMatRow)); + } + } + pPar->PutObject( refArray ); + } + else + SetError( errIllegalParameter ); + } + break; + default: + SetError( errIllegalParameter ); + bOk = FALSE; + } + } + if( bOk ) + { + pDok->LockTable( aPos.Tab() ); + SbxVariableRef refRes = new SbxVariable; + pDok->IncMacroInterpretLevel(); + ErrCode eRet = pDocSh->CallBasic( aMacroStr, aBasicStr, NULL, refPar, refRes ); + pDok->DecMacroInterpretLevel(); + pDok->UnlockTable( aPos.Tab() ); + + SbxDataType eResType = refRes->GetType(); + if( pVar->GetError() ) + SetError( errNoValue); + if ( eRet != ERRCODE_NONE ) + PushNoValue(); + else if( eResType >= SbxINTEGER && eResType <= SbxDOUBLE ) + PushDouble( refRes->GetDouble() ); + else if ( eResType & SbxARRAY ) + { + SbxBase* pElemObj = refRes->GetObject(); + SbxDimArray* pDimArray = PTR_CAST(SbxDimArray,pElemObj); + short nDim = pDimArray->GetDims(); + if ( 1 <= nDim && nDim <= 2 ) + { + INT32 nCs, nCe, nRs, nRe; + SCSIZE nC, nR; + SCCOL nColIdx; + SCROW nRowIdx; + if ( nDim == 1 ) + { // array( cols ) eine Zeile, mehrere Spalten + pDimArray->GetDim32( 1, nCs, nCe ); + nC = static_cast<SCSIZE>(nCe - nCs + 1); + nRs = nRe = 0; + nR = 1; + nColIdx = 0; + nRowIdx = 1; + } + else + { // array( rows, cols ) + pDimArray->GetDim32( 1, nRs, nRe ); + nR = static_cast<SCSIZE>(nRe - nRs + 1); + pDimArray->GetDim32( 2, nCs, nCe ); + nC = static_cast<SCSIZE>(nCe - nCs + 1); + nColIdx = 1; + nRowIdx = 0; + } + ScMatrixRef pMat = GetNewMat( nC, nR); + if ( pMat ) + { + SbxVariable* pV; + SbxDataType eType; + for ( SCSIZE j=0; j < nR; j++ ) + { + INT32 nIdx[ 2 ]; + // bei eindimensionalem array( cols ) wird nIdx[1] + // von SbxDimArray::Get ignoriert + nIdx[ nRowIdx ] = nRs + static_cast<INT32>(j); + for ( SCSIZE i=0; i < nC; i++ ) + { + nIdx[ nColIdx ] = nCs + static_cast<INT32>(i); + pV = pDimArray->Get32( nIdx ); + eType = pV->GetType(); + if ( eType >= SbxINTEGER && eType <= SbxDOUBLE ) + pMat->PutDouble( pV->GetDouble(), i, j ); + else + pMat->PutString( pV->GetString(), i, j ); + } + } + PushMatrix( pMat ); + } + else + PushIllegalArgument(); + } + else + PushNoValue(); + } + else + PushString( refRes->GetString() ); + } + + pSfxApp->LeaveBasicCall(); +} + + +BOOL ScInterpreter::SetSbxVariable( SbxVariable* pVar, const ScAddress& rPos ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::SetSbxVariable" ); + BOOL bOk = TRUE; + ScBaseCell* pCell = pDok->GetCell( rPos ); + if (pCell) + { + USHORT nErr; + double nVal; + switch( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : + nVal = GetValueCellValue( rPos, (ScValueCell*)pCell ); + pVar->PutDouble( nVal ); + break; + case CELLTYPE_STRING : + { + String aVal; + ((ScStringCell*)pCell)->GetString( aVal ); + pVar->PutString( aVal ); + break; + } + case CELLTYPE_EDIT : + { + String aVal; + ((ScEditCell*) pCell)->GetString( aVal ); + pVar->PutString( aVal ); + break; + } + case CELLTYPE_FORMULA : + nErr = ((ScFormulaCell*)pCell)->GetErrCode(); + if( !nErr ) + { + if( ((ScFormulaCell*)pCell)->IsValue() ) + { + nVal = ((ScFormulaCell*)pCell)->GetValue(); + pVar->PutDouble( nVal ); + } + else + { + String aVal; + ((ScFormulaCell*)pCell)->GetString( aVal ); + pVar->PutString( aVal ); + } + } + else + SetError( nErr ), bOk = FALSE; + break; + default : + pVar->PutDouble( 0.0 ); + } + } + else + pVar->PutDouble( 0.0 ); + return bOk; +} + + +void ScInterpreter::ScTableOp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTableOp" ); + BYTE nParamCount = GetByte(); + if (nParamCount != 3 && nParamCount != 5) + { + PushIllegalParameter(); + return; + } + ScInterpreterTableOpParams* pTableOp = new ScInterpreterTableOpParams; + if (nParamCount == 5) + { + PopSingleRef( pTableOp->aNew2 ); + PopSingleRef( pTableOp->aOld2 ); + } + PopSingleRef( pTableOp->aNew1 ); + PopSingleRef( pTableOp->aOld1 ); + PopSingleRef( pTableOp->aFormulaPos ); + + pTableOp->bValid = TRUE; + pDok->aTableOpList.Insert( pTableOp ); + pDok->IncInterpreterTableOpLevel(); + + BOOL bReuseLastParams = (pDok->aLastTableOpParams == *pTableOp); + if ( bReuseLastParams ) + { + pTableOp->aNotifiedFormulaPos = pDok->aLastTableOpParams.aNotifiedFormulaPos; + pTableOp->bRefresh = TRUE; + for ( ::std::vector< ScAddress >::const_iterator iBroadcast( + pTableOp->aNotifiedFormulaPos.begin() ); + iBroadcast != pTableOp->aNotifiedFormulaPos.end(); + ++iBroadcast ) + { // emulate broadcast and indirectly collect cell pointers + ScBaseCell* pCell = pDok->GetCell( *iBroadcast ); + if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pCell)->SetTableOpDirty(); + } + } + else + { // broadcast and indirectly collect cell pointers and positions + pDok->SetTableOpDirty( pTableOp->aOld1 ); + if ( nParamCount == 5 ) + pDok->SetTableOpDirty( pTableOp->aOld2 ); + } + pTableOp->bCollectNotifications = FALSE; + + ScBaseCell* pFCell = pDok->GetCell( pTableOp->aFormulaPos ); + if ( pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pFCell)->SetDirtyVar(); + if ( HasCellValueData( pFCell ) ) + PushDouble( GetCellValue( pTableOp->aFormulaPos, pFCell )); + else + { + String aCellString; + GetCellString( aCellString, pFCell ); + PushString( aCellString ); + } + + pDok->aTableOpList.Remove( pTableOp ); + // set dirty again once more to be able to recalculate original + for ( ::std::vector< ScFormulaCell* >::const_iterator iBroadcast( + pTableOp->aNotifiedFormulaCells.begin() ); + iBroadcast != pTableOp->aNotifiedFormulaCells.end(); + ++iBroadcast ) + { + (*iBroadcast)->SetTableOpDirty(); + } + + // save these params for next incarnation + if ( !bReuseLastParams ) + pDok->aLastTableOpParams = *pTableOp; + + if ( pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA ) + { + ((ScFormulaCell*)pFCell)->SetDirtyVar(); + ((ScFormulaCell*)pFCell)->GetErrCode(); // recalculate original + } + + // Reset all dirty flags so next incarnation does really collect all cell + // pointers during notifications and not just non-dirty ones, which may + // happen if a formula cell is used by more than one TableOp block. + for ( ::std::vector< ScFormulaCell* >::const_iterator iBroadcast2( + pTableOp->aNotifiedFormulaCells.begin() ); + iBroadcast2 != pTableOp->aNotifiedFormulaCells.end(); + ++iBroadcast2 ) + { + (*iBroadcast2)->ResetTableOpDirtyVar(); + } + delete pTableOp; + + pDok->DecInterpreterTableOpLevel(); +} + + +/* + +void ScInterpreter::ScErrCell() +{ +RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScErrCell" ); + double fErrNum = GetDouble(); + PushError((USHORT) fErrNum); +} +*/ + +void ScInterpreter::ScDBArea() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBArea" ); + ScDBData* pDBData = pDok->GetDBCollection()->FindIndex( pCur->GetIndex()); + if (pDBData) + { + ScComplexRefData aRefData; + aRefData.InitFlags(); + pDBData->GetArea( (SCTAB&) aRefData.Ref1.nTab, + (SCCOL&) aRefData.Ref1.nCol, + (SCROW&) aRefData.Ref1.nRow, + (SCCOL&) aRefData.Ref2.nCol, + (SCROW&) aRefData.Ref2.nRow); + aRefData.Ref2.nTab = aRefData.Ref1.nTab; + aRefData.CalcRelFromAbs( aPos ); + PushTempToken( new ScDoubleRefToken( aRefData ) ); + } + else + PushError( errNoName); +} + + +void ScInterpreter::ScColRowNameAuto() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScColRowNameAuto" ); + ScComplexRefData aRefData( static_cast<const ScToken*>(pCur)->GetDoubleRef() ); + aRefData.CalcAbsIfRel( aPos ); + if ( aRefData.Valid() ) + { + SCsCOL nStartCol; + SCsROW nStartRow; + SCsCOL nCol2; + SCsROW nRow2; + // evtl. Begrenzung durch definierte ColRowNameRanges merken + nCol2 = aRefData.Ref2.nCol; + nRow2 = aRefData.Ref2.nRow; + // DataArea der ersten Zelle + nStartCol = aRefData.Ref2.nCol = aRefData.Ref1.nCol; + nStartRow = aRefData.Ref2.nRow = aRefData.Ref1.nRow; + aRefData.Ref2.nTab = aRefData.Ref1.nTab; + pDok->GetDataArea( (SCTAB&) aRefData.Ref1.nTab, + (SCCOL&) aRefData.Ref1.nCol, + (SCROW&) aRefData.Ref1.nRow, + (SCCOL&) aRefData.Ref2.nCol, + (SCROW&) aRefData.Ref2.nRow, + TRUE, false ); + // DataArea im Ursprung begrenzen + aRefData.Ref1.nCol = nStartCol; + aRefData.Ref1.nRow = nStartRow; + + //! korrespondiert mit ScCompiler::GetToken + if ( aRefData.Ref1.IsColRel() ) + { // ColName + aRefData.Ref2.nCol = nStartCol; + // evtl. vorherige Begrenzung durch definierte ColRowNameRanges erhalten + if ( aRefData.Ref2.nRow > nRow2 ) + aRefData.Ref2.nRow = nRow2; + SCROW nMyRow; + if ( aPos.Col() == nStartCol + && nStartRow <= (nMyRow = aPos.Row()) && nMyRow <= aRefData.Ref2.nRow ) + { // Formel in gleicher Spalte und innerhalb des Range + if ( nMyRow == nStartRow ) + { // direkt unter dem Namen den Rest nehmen + nStartRow++; + if ( nStartRow > MAXROW ) + nStartRow = MAXROW; + aRefData.Ref1.nRow = nStartRow; + } + else + { // weiter unten vom Namen bis zur Formelzelle + aRefData.Ref2.nRow = nMyRow - 1; + } + } + } + else + { // RowName + aRefData.Ref2.nRow = nStartRow; + // evtl. vorherige Begrenzung durch definierte ColRowNameRanges erhalten + if ( aRefData.Ref2.nCol > nCol2 ) + aRefData.Ref2.nCol = nCol2; + SCCOL nMyCol; + if ( aPos.Row() == nStartRow + && nStartCol <= (nMyCol = aPos.Col()) && nMyCol <= aRefData.Ref2.nCol ) + { // Formel in gleicher Zeile und innerhalb des Range + if ( nMyCol == nStartCol ) + { // direkt neben dem Namen den Rest nehmen + nStartCol++; + if ( nStartCol > MAXCOL ) + nStartCol = MAXCOL; + aRefData.Ref1.nCol = nStartCol; + } + else + { // weiter rechts vom Namen bis zur Formelzelle + aRefData.Ref2.nCol = nMyCol - 1; + } + } + } + aRefData.CalcRelFromAbs( aPos ); + PushTempToken( new ScDoubleRefToken( aRefData ) ); + } + else + PushError( errNoRef ); +} + +void ScInterpreter::ScExternalRef() +{ + ScExternalRefManager* pRefMgr = pDok->GetExternalRefManager(); + const String* pFile = pRefMgr->getExternalFileName(pCur->GetIndex()); + if (!pFile) + PushError(errNoName); + + switch (pCur->GetType()) + { + case svExternalSingleRef: + { + ScSingleRefData aData(static_cast<const ScToken*>(pCur)->GetSingleRef()); + if (aData.IsTabRel()) + { + DBG_ERROR("ScCompiler::GetToken: external single reference must have an absolute table reference!"); + break; + } + + aData.CalcAbsIfRel(aPos); + ScAddress aAddr(aData.nCol, aData.nRow, aData.nTab); + ScExternalRefCache::CellFormat aFmt; + ScExternalRefCache::TokenRef xNew = pRefMgr->getSingleRefToken( + pCur->GetIndex(), pCur->GetString(), aAddr, &aPos, NULL, &aFmt); + + if (!xNew) + break; + + PushTempToken( *xNew); // push a clone + + if (aFmt.mbIsSet) + { + nFuncFmtType = aFmt.mnType; + nFuncFmtIndex = aFmt.mnIndex; + } + return; + } + //break; // unreachable, prevent compiler warning + case svExternalDoubleRef: + { + ScComplexRefData aData(static_cast<const ScToken*>(pCur)->GetDoubleRef()); + if (aData.Ref1.IsTabRel() || aData.Ref2.IsTabRel()) + { + DBG_ERROR("ScCompiler::GetToken: external double reference must have an absolute table reference!"); + break; + } + + aData.CalcAbsIfRel(aPos); + ScRange aRange(aData.Ref1.nCol, aData.Ref1.nRow, aData.Ref1.nTab, + aData.Ref2.nCol, aData.Ref2.nRow, aData.Ref2.nTab); + ScExternalRefCache::TokenArrayRef xNew = pRefMgr->getDoubleRefTokens( + pCur->GetIndex(), pCur->GetString(), aRange, &aPos); + + if (!xNew) + break; + + ScToken* p = static_cast<ScToken*>(xNew->First()); + if (p->GetType() != svMatrix) + break; + + if (xNew->Next()) + { + // Can't handle more than one matrix per parameter. + SetError( errIllegalArgument); + break; + } + + PushMatrix(p->GetMatrix()); + return; + } + //break; // unreachable, prevent compiler warning + default: + ; + } + PushError(errNoRef); +} + +// --- internals ------------------------------------------------------------ + + +void ScInterpreter::ScTTT() +{ // Temporaerer Test-Tanz, zum auspropieren von Funktionen etc. + BYTE nParamCount = GetByte(); + // do something, nParamCount bei Pops runterzaehlen! + + // Stack aufraeumen + while ( nParamCount-- > 0) + Pop(); + PushError(errNoValue); +} + +// ------------------------------------------------------------------------- + + +ScInterpreter::ScInterpreter( ScFormulaCell* pCell, ScDocument* pDoc, + const ScAddress& rPos, ScTokenArray& r ) : + aCode( r ), + aPos( rPos ), + rArr( r ), + pDok( pDoc ), + pTokenMatrixMap( NULL ), + pMyFormulaCell( pCell ), + pFormatter( pDoc->GetFormatTable() ), + mnStringNoValueError( errNoValue), + bCalcAsShown( pDoc->GetDocOptions().IsCalcAsShown() ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTTT" ); +// pStack = new ScToken*[ MAXSTACK ]; + + BYTE cMatFlag = pMyFormulaCell->GetMatrixFlag(); + bMatrixFormula = ( cMatFlag == MM_FORMULA || cMatFlag == MM_FAKE ); + if (!bGlobalStackInUse) + { + bGlobalStackInUse = TRUE; + if (!pGlobalStack) + pGlobalStack = new ScTokenStack; + pStackObj = pGlobalStack; + } + else + { + pStackObj = new ScTokenStack; + } + pStack = pStackObj->pPointer; +} + +ScInterpreter::~ScInterpreter() +{ +// delete pStack; + + if ( pStackObj == pGlobalStack ) + bGlobalStackInUse = FALSE; + else + delete pStackObj; + if (pTokenMatrixMap) + delete pTokenMatrixMap; +} + + +void ScInterpreter::GlobalExit() // static +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GlobalExit" ); + DBG_ASSERT(!bGlobalStackInUse, "wer benutzt noch den TokenStack?"); + DELETEZ(pGlobalStack); +} + + +StackVar ScInterpreter::Interpret() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::Interpret" ); + short nRetTypeExpr = NUMBERFORMAT_UNDEFINED; + ULONG nRetIndexExpr = 0; + USHORT nErrorFunction = 0; + USHORT nErrorFunctionCount = 0; + USHORT nStackBase; + + nGlobalError = 0; + nStackBase = sp = maxsp = 0; + nRetFmtType = NUMBERFORMAT_UNDEFINED; + nFuncFmtType = NUMBERFORMAT_UNDEFINED; + nFuncFmtIndex = nCurFmtIndex = nRetFmtIndex = 0; + xResult = NULL; + pJumpMatrix = NULL; + glSubTotal = FALSE; + ScTokenMatrixMap::const_iterator aTokenMatrixMapIter; + + // Once upon a time we used to have FP exceptions on, and there was a + // Windows printer driver that kept switching off exceptions, so we had to + // switch them back on again every time. Who knows if there isn't a driver + // that keeps switching exceptions on, now that we run with exceptions off, + // so reassure exceptions are really off. + SAL_MATH_FPEXCEPTIONS_OFF(); + + aCode.Reset(); + while( ( pCur = aCode.Next() ) != NULL + && (!nGlobalError || nErrorFunction <= nErrorFunctionCount) ) + { + OpCode eOp = pCur->GetOpCode(); + cPar = pCur->GetByte(); + if ( eOp == ocPush ) + { + // RPN code push without error + PushWithoutError( (FormulaToken&) *pCur ); + } + else if (pTokenMatrixMap && !(eOp == ocIf || eOp == ocChose) && + ((aTokenMatrixMapIter = pTokenMatrixMap->find( pCur)) != + pTokenMatrixMap->end()) && + (*aTokenMatrixMapIter).second->GetType() != svJumpMatrix) + { + // Path already calculated, reuse result. + nStackBase = sp - pCur->GetParamCount(); + if ( nStackBase > sp ) + nStackBase = sp; // underflow?!? + sp = nStackBase; + PushTempToken( (*aTokenMatrixMapIter).second); + } + else + { + // previous expression determines the current number format + nCurFmtType = nRetTypeExpr; + nCurFmtIndex = nRetIndexExpr; + // default function's format, others are set if needed + nFuncFmtType = NUMBERFORMAT_NUMBER; + nFuncFmtIndex = 0; + + if ( eOp == ocIf || eOp == ocChose ) + nStackBase = sp; // don't mess around with the jumps + else + { + // Convert parameters to matrix if in array/matrix formula and + // parameters of function indicate doing so. Create JumpMatrix + // if necessary. + if ( MatrixParameterConversion() ) + { + eOp = ocNone; // JumpMatrix created + nStackBase = sp; + } + else + nStackBase = sp - pCur->GetParamCount(); + } + if ( nStackBase > sp ) + nStackBase = sp; // underflow?!? + + switch( eOp ) + { + case ocSep: + case ocClose: // pushed by the compiler + case ocMissing : ScMissing(); break; + case ocMacro : ScMacro(); break; + case ocDBArea : ScDBArea(); break; + case ocColRowNameAuto : ScColRowNameAuto(); break; +// separated case ocPush : Push( (ScToken&) *pCur ); break; + case ocExternalRef : ScExternalRef(); break; + case ocIf : ScIfJump(); break; + case ocChose : ScChoseJump(); break; + case ocAdd : ScAdd(); break; + case ocSub : ScSub(); break; + case ocMul : ScMul(); break; + case ocDiv : ScDiv(); break; + case ocAmpersand : ScAmpersand(); break; + case ocPow : ScPow(); break; + case ocEqual : ScEqual(); break; + case ocNotEqual : ScNotEqual(); break; + case ocLess : ScLess(); break; + case ocGreater : ScGreater(); break; + case ocLessEqual : ScLessEqual(); break; + case ocGreaterEqual : ScGreaterEqual(); break; + case ocAnd : ScAnd(); break; + case ocOr : ScOr(); break; + case ocIntersect : ScIntersect(); break; + case ocRange : ScRangeFunc(); break; + case ocUnion : ScUnionFunc(); break; + case ocNot : ScNot(); break; + case ocNegSub : + case ocNeg : ScNeg(); break; + case ocPercentSign : ScPercentSign(); break; + case ocPi : ScPi(); break; +// case ocDefPar : ScDefPar(); break; + case ocRandom : ScRandom(); break; + case ocTrue : ScTrue(); break; + case ocFalse : ScFalse(); break; + case ocGetActDate : ScGetActDate(); break; + case ocGetActTime : ScGetActTime(); break; + case ocNotAvail : PushError( NOTAVAILABLE); break; + case ocDeg : ScDeg(); break; + case ocRad : ScRad(); break; + case ocSin : ScSin(); break; + case ocCos : ScCos(); break; + case ocTan : ScTan(); break; + case ocCot : ScCot(); break; + case ocArcSin : ScArcSin(); break; + case ocArcCos : ScArcCos(); break; + case ocArcTan : ScArcTan(); break; + case ocArcCot : ScArcCot(); break; + case ocSinHyp : ScSinHyp(); break; + case ocCosHyp : ScCosHyp(); break; + case ocTanHyp : ScTanHyp(); break; + case ocCotHyp : ScCotHyp(); break; + case ocArcSinHyp : ScArcSinHyp(); break; + case ocArcCosHyp : ScArcCosHyp(); break; + case ocArcTanHyp : ScArcTanHyp(); break; + case ocArcCotHyp : ScArcCotHyp(); break; + case ocExp : ScExp(); break; + case ocLn : ScLn(); break; + case ocLog10 : ScLog10(); break; + case ocSqrt : ScSqrt(); break; + case ocFact : ScFact(); break; + case ocGetYear : ScGetYear(); break; + case ocGetMonth : ScGetMonth(); break; + case ocGetDay : ScGetDay(); break; + case ocGetDayOfWeek : ScGetDayOfWeek(); break; + case ocWeek : ScGetWeekOfYear(); break; + case ocEasterSunday : ScEasterSunday(); break; + case ocGetHour : ScGetHour(); break; + case ocGetMin : ScGetMin(); break; + case ocGetSec : ScGetSec(); break; + case ocPlusMinus : ScPlusMinus(); break; + case ocAbs : ScAbs(); break; + case ocInt : ScInt(); break; + case ocEven : ScEven(); break; + case ocOdd : ScOdd(); break; + case ocPhi : ScPhi(); break; + case ocGauss : ScGauss(); break; + case ocStdNormDist : ScStdNormDist(); break; + case ocFisher : ScFisher(); break; + case ocFisherInv : ScFisherInv(); break; + case ocIsEmpty : ScIsEmpty(); break; + case ocIsString : ScIsString(); break; + case ocIsNonString : ScIsNonString(); break; + case ocIsLogical : ScIsLogical(); break; + case ocType : ScType(); break; + case ocCell : ScCell(); break; + case ocIsRef : ScIsRef(); break; + case ocIsValue : ScIsValue(); break; + case ocIsFormula : ScIsFormula(); break; + case ocFormula : ScFormula(); break; + case ocIsNA : ScIsNV(); break; + case ocIsErr : ScIsErr(); break; + case ocIsError : ScIsError(); break; + case ocIsEven : ScIsEven(); break; + case ocIsOdd : ScIsOdd(); break; + case ocN : ScN(); break; + case ocGetDateValue : ScGetDateValue(); break; + case ocGetTimeValue : ScGetTimeValue(); break; + case ocCode : ScCode(); break; + case ocTrim : ScTrim(); break; + case ocUpper : ScUpper(); break; + case ocPropper : ScPropper(); break; + case ocLower : ScLower(); break; + case ocLen : ScLen(); break; + case ocT : ScT(); break; + case ocClean : ScClean(); break; + case ocValue : ScValue(); break; + case ocChar : ScChar(); break; + case ocArcTan2 : ScArcTan2(); break; + case ocMod : ScMod(); break; + case ocPower : ScPower(); break; + case ocRound : ScRound(); break; + case ocRoundUp : ScRoundUp(); break; + case ocTrunc : + case ocRoundDown : ScRoundDown(); break; + case ocCeil : ScCeil(); break; + case ocFloor : ScFloor(); break; + case ocSumProduct : ScSumProduct(); break; + case ocSumSQ : ScSumSQ(); break; + case ocSumX2MY2 : ScSumX2MY2(); break; + case ocSumX2DY2 : ScSumX2DY2(); break; + case ocSumXMY2 : ScSumXMY2(); break; + case ocLog : ScLog(); break; + case ocGCD : ScGCD(); break; + case ocLCM : ScLCM(); break; + case ocGetDate : ScGetDate(); break; + case ocGetTime : ScGetTime(); break; + case ocGetDiffDate : ScGetDiffDate(); break; + case ocGetDiffDate360 : ScGetDiffDate360(); break; + case ocMin : ScMin( FALSE ); break; + case ocMinA : ScMin( TRUE ); break; + case ocMax : ScMax( FALSE ); break; + case ocMaxA : ScMax( TRUE ); break; + case ocSum : ScSum(); break; + case ocProduct : ScProduct(); break; + case ocNPV : ScNPV(); break; + case ocIRR : ScIRR(); break; + case ocMIRR : ScMIRR(); break; + case ocISPMT : ScISPMT(); break; + case ocAverage : ScAverage( FALSE ); break; + case ocAverageA : ScAverage( TRUE ); break; + case ocCount : ScCount(); break; + case ocCount2 : ScCount2(); break; + case ocVar : ScVar( FALSE ); break; + case ocVarA : ScVar( TRUE ); break; + case ocVarP : ScVarP( FALSE ); break; + case ocVarPA : ScVarP( TRUE ); break; + case ocStDev : ScStDev( FALSE ); break; + case ocStDevA : ScStDev( TRUE ); break; + case ocStDevP : ScStDevP( FALSE ); break; + case ocStDevPA : ScStDevP( TRUE ); break; + case ocBW : ScBW(); break; + case ocDIA : ScDIA(); break; + case ocGDA : ScGDA(); break; + case ocGDA2 : ScGDA2(); break; + case ocVBD : ScVDB(); break; + case ocLaufz : ScLaufz(); break; + case ocLIA : ScLIA(); break; + case ocRMZ : ScRMZ(); break; + case ocColumns : ScColumns(); break; + case ocRows : ScRows(); break; + case ocTables : ScTables(); break; + case ocColumn : ScColumn(); break; + case ocRow : ScRow(); break; + case ocTable : ScTable(); break; + case ocZGZ : ScZGZ(); break; + case ocZW : ScZW(); break; + case ocZZR : ScZZR(); break; + case ocZins : ScZins(); break; + case ocZinsZ : ScZinsZ(); break; + case ocKapz : ScKapz(); break; + case ocKumZinsZ : ScKumZinsZ(); break; + case ocKumKapZ : ScKumKapZ(); break; + case ocEffektiv : ScEffektiv(); break; + case ocNominal : ScNominal(); break; + case ocSubTotal : ScSubTotal(); break; + case ocDBSum : ScDBSum(); break; + case ocDBCount : ScDBCount(); break; + case ocDBCount2 : ScDBCount2(); break; + case ocDBAverage : ScDBAverage(); break; + case ocDBGet : ScDBGet(); break; + case ocDBMax : ScDBMax(); break; + case ocDBMin : ScDBMin(); break; + case ocDBProduct : ScDBProduct(); break; + case ocDBStdDev : ScDBStdDev(); break; + case ocDBStdDevP : ScDBStdDevP(); break; + case ocDBVar : ScDBVar(); break; + case ocDBVarP : ScDBVarP(); break; + case ocIndirect : ScIndirect(); break; + case ocAddress : ScAddressFunc(); break; + case ocMatch : ScMatch(); break; + case ocCountEmptyCells : ScCountEmptyCells(); break; + case ocCountIf : ScCountIf(); break; + case ocSumIf : ScSumIf(); break; + case ocLookup : ScLookup(); break; + case ocVLookup : ScVLookup(); break; + case ocHLookup : ScHLookup(); break; + case ocIndex : ScIndex(); break; + case ocMultiArea : ScMultiArea(); break; + case ocOffset : ScOffset(); break; + case ocAreas : ScAreas(); break; + case ocCurrency : ScCurrency(); break; + case ocReplace : ScReplace(); break; + case ocFixed : ScFixed(); break; + case ocFind : ScFind(); break; + case ocExact : ScExact(); break; + case ocLeft : ScLeft(); break; + case ocRight : ScRight(); break; + case ocSearch : ScSearch(); break; + case ocMid : ScMid(); break; + case ocText : ScText(); break; + case ocSubstitute : ScSubstitute(); break; + case ocRept : ScRept(); break; + case ocConcat : ScConcat(); break; + case ocMatValue : ScMatValue(); break; + case ocMatrixUnit : ScEMat(); break; + case ocMatDet : ScMatDet(); break; + case ocMatInv : ScMatInv(); break; + case ocMatMult : ScMatMult(); break; + case ocMatTrans : ScMatTrans(); break; + case ocMatRef : ScMatRef(); break; + case ocBackSolver : ScBackSolver(); break; + case ocB : ScB(); break; + case ocNormDist : ScNormDist(); break; + case ocExpDist : ScExpDist(); break; + case ocBinomDist : ScBinomDist(); break; + case ocPoissonDist : ScPoissonDist(); break; + case ocKombin : ScKombin(); break; + case ocKombin2 : ScKombin2(); break; + case ocVariationen : ScVariationen(); break; + case ocVariationen2 : ScVariationen2(); break; + case ocHypGeomDist : ScHypGeomDist(); break; + case ocLogNormDist : ScLogNormDist(); break; + case ocTDist : ScTDist(); break; + case ocFDist : ScFDist(); break; + case ocChiDist : ScChiDist(); break; + case ocChiSqDist : ScChiSqDist(); break; + case ocStandard : ScStandard(); break; + case ocAveDev : ScAveDev(); break; + case ocDevSq : ScDevSq(); break; + case ocKurt : ScKurt(); break; + case ocSchiefe : ScSkew(); break; + case ocModalValue : ScModalValue(); break; + case ocMedian : ScMedian(); break; + case ocGeoMean : ScGeoMean(); break; + case ocHarMean : ScHarMean(); break; + case ocWeibull : ScWeibull(); break; + case ocKritBinom : ScCritBinom(); break; + case ocNegBinomVert : ScNegBinomDist(); break; + case ocNoName : ScNoName(); break; + case ocBad : ScBadName(); break; + case ocZTest : ScZTest(); break; + case ocTTest : ScTTest(); break; + case ocFTest : ScFTest(); break; + case ocRank : ScRank(); break; + case ocPercentile : ScPercentile(); break; + case ocPercentrank : ScPercentrank(); break; + case ocLarge : ScLarge(); break; + case ocSmall : ScSmall(); break; + case ocFrequency : ScFrequency(); break; + case ocQuartile : ScQuartile(); break; + case ocNormInv : ScNormInv(); break; + case ocSNormInv : ScSNormInv(); break; + case ocConfidence : ScConfidence(); break; + case ocTrimMean : ScTrimMean(); break; + case ocProb : ScProbability(); break; + case ocCorrel : ScCorrel(); break; + case ocCovar : ScCovar(); break; + case ocPearson : ScPearson(); break; + case ocRSQ : ScRSQ(); break; + case ocSTEYX : ScSTEXY(); break; + case ocSlope : ScSlope(); break; + case ocIntercept : ScIntercept(); break; + case ocTrend : ScTrend(); break; + case ocGrowth : ScGrowth(); break; + case ocRGP : ScRGP(); break; + case ocRKP : ScRKP(); break; + case ocForecast : ScForecast(); break; + case ocGammaLn : ScLogGamma(); break; + case ocGamma : ScGamma(); break; + case ocGammaDist : ScGammaDist(); break; + case ocGammaInv : ScGammaInv(); break; + case ocChiTest : ScChiTest(); break; + case ocChiInv : ScChiInv(); break; + case ocChiSqInv : ScChiSqInv(); break; + case ocTInv : ScTInv(); break; + case ocFInv : ScFInv(); break; + case ocLogInv : ScLogNormInv(); break; + case ocBetaDist : ScBetaDist(); break; + case ocBetaInv : ScBetaInv(); break; + case ocExternal : ScExternal(); break; + case ocTableOp : ScTableOp(); break; +// case ocErrCell : ScErrCell(); break; + case ocStop : break; + case ocErrorType : ScErrorType(); break; + case ocCurrent : ScCurrent(); break; + case ocStyle : ScStyle(); break; + case ocDde : ScDde(); break; + case ocBase : ScBase(); break; + case ocDecimal : ScDecimal(); break; + case ocConvert : ScConvert(); break; + case ocEuroConvert : ScEuroConvert(); break; + case ocRoman : ScRoman(); break; + case ocArabic : ScArabic(); break; + case ocInfo : ScInfo(); break; + case ocHyperLink : ScHyperLink(); break; + case ocBahtText : ScBahtText(); break; + case ocGetPivotData : ScGetPivotData(); break; + case ocJis : ScJis(); break; + case ocAsc : ScAsc(); break; + case ocUnicode : ScUnicode(); break; + case ocUnichar : ScUnichar(); break; + case ocTTT : ScTTT(); break; + case ocNone : nFuncFmtType = NUMBERFORMAT_UNDEFINED; break; + default : PushError( errUnknownOpCode); break; + } + + // If the function signalled that it pushed a subroutine on the + // instruction code stack instead of a result, continue with + // execution of the subroutine. + if (sp > nStackBase && pStack[sp-1]->GetOpCode() == ocCall) + { + Pop(); + continue; // while( ( pCur = aCode.Next() ) != NULL ... + } + + // Remember result matrix in case it could be reused. + if (pTokenMatrixMap && sp && GetStackType() == svMatrix) + pTokenMatrixMap->insert( ScTokenMatrixMap::value_type( pCur, + pStack[sp-1])); + + // outer function determines format of an expression + if ( nFuncFmtType != NUMBERFORMAT_UNDEFINED ) + { + nRetTypeExpr = nFuncFmtType; + // inherit the format index only for currency formats + nRetIndexExpr = ( nFuncFmtType == NUMBERFORMAT_CURRENCY ? + nFuncFmtIndex : 0 ); + } + } + + // Need a clean stack environment for the JumpMatrix to work. + if (nGlobalError && eOp != ocPush && sp > nStackBase + 1) + { + // Not all functions pop all parameters in case an error is + // generated. Clean up stack. Assumes that every function pushes a + // result, may be arbitrary in case of error. + const FormulaToken* pLocalResult = pStack[ sp - 1 ]; + while (sp > nStackBase) + Pop(); + PushTempToken( *pLocalResult ); + } + + bool bGotResult; + do + { + bGotResult = false; + BYTE nLevel = 0; + if ( GetStackType( ++nLevel ) == svJumpMatrix ) + ; // nothing + else if ( GetStackType( ++nLevel ) == svJumpMatrix ) + ; // nothing + else + nLevel = 0; + if ( nLevel == 1 || (nLevel == 2 && aCode.IsEndOfPath()) ) + bGotResult = JumpMatrix( nLevel ); + else + pJumpMatrix = NULL; + } while ( bGotResult ); + + +// Functions that evaluate an error code and directly set nGlobalError to 0, +// usage: switch( OpCode ) { CASE_OCERRFUNC statements; } +#define CASE_OCERRFUNC \ + case ocCount : \ + case ocCount2 : \ + case ocErrorType : \ + case ocIsEmpty : \ + case ocIsErr : \ + case ocIsError : \ + case ocIsFormula : \ + case ocIsLogical : \ + case ocIsNA : \ + case ocIsNonString : \ + case ocIsRef : \ + case ocIsString : \ + case ocIsValue : \ + case ocN : \ + case ocType : + + switch ( eOp ) + { + CASE_OCERRFUNC + ++ nErrorFunction; + default: + ; // nothing + } + if ( nGlobalError ) + { + if ( !nErrorFunctionCount ) + { // count of errorcode functions in formula + for ( FormulaToken* t = rArr.FirstRPN(); t; t = rArr.NextRPN() ) + { + switch ( t->GetOpCode() ) + { + CASE_OCERRFUNC + ++nErrorFunctionCount; + default: + ; // nothing + } + } + } + if ( nErrorFunction >= nErrorFunctionCount ) + ++nErrorFunction; // that's it, error => terminate + } + } + + // End: obtain result + + if( sp ) + { + pCur = pStack[ sp-1 ]; + if( pCur->GetOpCode() == ocPush ) + { + switch( pCur->GetType() ) + { + case svEmptyCell: + ; // nothing + break; + case svError: + nGlobalError = pCur->GetError(); + break; + case svDouble : + if ( nFuncFmtType == NUMBERFORMAT_UNDEFINED ) + { + nRetTypeExpr = NUMBERFORMAT_NUMBER; + nRetIndexExpr = 0; + } + break; + case svString : + nRetTypeExpr = NUMBERFORMAT_TEXT; + nRetIndexExpr = 0; + break; + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if( !nGlobalError ) + PushCellResultToken( false, aAdr, + &nRetTypeExpr, &nRetIndexExpr); + } + break; + case svRefList : + PopError(); // maybe #REF! takes precedence over #VALUE! + PushError( errNoValue); + break; + case svDoubleRef : + { + if ( bMatrixFormula ) + { // create matrix for {=A1:A5} + PopDoubleRefPushMatrix(); + // no break, continue with svMatrix + } + else + { + ScRange aRange; + PopDoubleRef( aRange ); + ScAddress aAdr; + if ( !nGlobalError && DoubleRefToPosSingleRef( aRange, aAdr)) + PushCellResultToken( false, aAdr, + &nRetTypeExpr, &nRetIndexExpr); + break; + } + } + // no break + case svMatrix : + { + ScMatrixRef xMat = PopMatrix(); + if (xMat) + { + ScMatValType nMatValType; + const ScMatrixValue* pMatVal = xMat->Get(0, 0, nMatValType); + if ( pMatVal ) + { + if (ScMatrix::IsNonValueType( nMatValType)) + { + if ( xMat->IsEmptyPath( 0, 0)) + { // result of empty FALSE jump path + FormulaTokenRef xRes = new FormulaDoubleToken( 0.0); + PushTempToken( new ScMatrixCellResultToken( xMat, xRes)); + nRetTypeExpr = NUMBERFORMAT_LOGICAL; + } + else + { + String aStr( pMatVal->GetString()); + FormulaTokenRef xRes = new FormulaStringToken( aStr); + PushTempToken( new ScMatrixCellResultToken( xMat, xRes)); + nRetTypeExpr = NUMBERFORMAT_TEXT; + } + } + else + { + USHORT nErr = GetDoubleErrorValue( pMatVal->fVal); + FormulaTokenRef xRes; + if (nErr) + xRes = new FormulaErrorToken( nErr); + else + xRes = new FormulaDoubleToken( pMatVal->fVal); + PushTempToken( new ScMatrixCellResultToken( xMat, xRes)); + if ( nRetTypeExpr != NUMBERFORMAT_LOGICAL ) + nRetTypeExpr = NUMBERFORMAT_NUMBER; + } + nRetIndexExpr = 0; + } + else + SetError( errUnknownStackVariable); + xMat->SetErrorInterpreter( NULL); + } + else + SetError( errUnknownStackVariable); + } + break; + default : + SetError( errUnknownStackVariable); + } + } + else + SetError( errUnknownStackVariable); + } + else + SetError( errNoCode); + + if( nRetTypeExpr != NUMBERFORMAT_UNDEFINED ) + { + nRetFmtType = nRetTypeExpr; + nRetFmtIndex = nRetIndexExpr; + } + else if( nFuncFmtType != NUMBERFORMAT_UNDEFINED ) + { + nRetFmtType = nFuncFmtType; + nRetFmtIndex = nFuncFmtIndex; + } + else + nRetFmtType = NUMBERFORMAT_NUMBER; + // inherit the format index only for currency formats + if ( nRetFmtType != NUMBERFORMAT_CURRENCY ) + nRetFmtIndex = 0; + + if (nGlobalError && GetStackType() != svError ) + PushError( nGlobalError); + + // THE final result. + xResult = PopToken(); + if (!xResult) + xResult = new FormulaErrorToken( errUnknownStackVariable); + + // release tokens in expression stack + FormulaToken** p = pStack; + while( maxsp-- ) + (*p++)->DecRef(); + + StackVar eType = xResult->GetType(); + if (eType == svMatrix) + // Results are immutable in case they would be reused as input for new + // interpreters. + static_cast<ScToken*>(xResult.operator->())->GetMatrix()->SetImmutable( true); + return eType; +} diff --git a/sc/source/core/tool/interpr5.cxx b/sc/source/core/tool/interpr5.cxx new file mode 100644 index 000000000000..73794cf15b2d --- /dev/null +++ b/sc/source/core/tool/interpr5.cxx @@ -0,0 +1,2815 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#ifndef INCLUDED_RTL_MATH_HXX +#include <rtl/math.hxx> +#endif +#include <rtl/logfile.hxx> +#include <string.h> +#include <math.h> +#include <stdio.h> + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +#endif +#include <unotools/bootstrap.hxx> +#include <svl/zforlist.hxx> + +#include "interpre.hxx" +#include "global.hxx" +#include "compiler.hxx" +#include "cell.hxx" +#include "document.hxx" +#include "dociter.hxx" +#include "scmatrix.hxx" +#include "globstr.hrc" +#include "cellkeytranslator.hxx" +#include "osversiondef.hxx" + +#include <string.h> +#include <math.h> +#include <vector> + +using ::std::vector; +using namespace formula; + +const double fInvEpsilon = 1.0E-7; + +// ----------------------------------------------------------------------- + struct MatrixAdd : public ::std::binary_function<double,double,double> + { + inline double operator() (const double& lhs, const double& rhs) const + { + return ::rtl::math::approxAdd( lhs,rhs); + } + }; + struct MatrixSub : public ::std::binary_function<double,double,double> + { + inline double operator() (const double& lhs, const double& rhs) const + { + return ::rtl::math::approxSub( lhs,rhs); + } + }; + struct MatrixMul : public ::std::binary_function<double,double,double> + { + inline double operator() (const double& lhs, const double& rhs) const + { + return lhs * rhs; + } + }; + struct MatrixDiv : public ::std::binary_function<double,double,double> + { + inline double operator() (const double& lhs, const double& rhs) const + { + return ScInterpreter::div( lhs,rhs); + } + }; + struct MatrixPow : public ::std::binary_function<double,double,double> + { + inline double operator() (const double& lhs, const double& rhs) const + { + return ::pow( lhs,rhs); + } + }; + +double ScInterpreter::ScGetGCD(double fx, double fy) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::div" ); + // By ODFF definition GCD(0,a) => a. This is also vital for the code in + // ScGCD() to work correctly with a preset fy=0.0 + if (fy == 0.0) + return fx; + else if (fx == 0.0) + return fy; + else + { + double fz = fmod(fx, fy); + while (fz > 0.0) + { + fx = fy; + fy = fz; + fz = fmod(fx, fy); + } + return fy; + } +} + +void ScInterpreter::ScGCD() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGCD" ); + short nParamCount = GetByte(); + if ( MustHaveParamCountMin( nParamCount, 1 ) ) + { + double fx, fy = 0.0; + ScRange aRange; + size_t nRefInList = 0; + while (!nGlobalError && nParamCount-- > 0) + { + switch (GetStackType()) + { + case svDouble : + case svString: + case svSingleRef: + { + fx = ::rtl::math::approxFloor( GetDouble()); + if (fx < 0.0) + { + PushIllegalArgument(); + return; + } + fy = ScGetGCD(fx, fy); + } + break; + case svDoubleRef : + case svRefList : + { + USHORT nErr = 0; + PopDoubleRef( aRange, nParamCount, nRefInList); + double nCellVal; + ScValueIterator aValIter(pDok, aRange, glSubTotal); + if (aValIter.GetFirst(nCellVal, nErr)) + { + do + { + fx = ::rtl::math::approxFloor( nCellVal); + if (fx < 0.0) + { + PushIllegalArgument(); + return; + } + fy = ScGetGCD(fx, fy); + } while (nErr == 0 && aValIter.GetNext(nCellVal, nErr)); + } + SetError(nErr); + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + if (nC == 0 || nR == 0) + SetError(errIllegalArgument); + else + { + SCSIZE nCount = nC * nR; + for ( SCSIZE j = 0; j < nCount; j++ ) + { + if (!pMat->IsValue(j)) + { + PushIllegalArgument(); + return; + } + fx = ::rtl::math::approxFloor( pMat->GetDouble(j)); + if (fx < 0.0) + { + PushIllegalArgument(); + return; + } + fy = ScGetGCD(fx, fy); + } + } + } + } + break; + default : SetError(errIllegalParameter); break; + } + } + PushDouble(fy); + } +} + +void ScInterpreter:: ScLCM() +{ + short nParamCount = GetByte(); + if ( MustHaveParamCountMin( nParamCount, 1 ) ) + { + double fx, fy = 1.0; + ScRange aRange; + size_t nRefInList = 0; + while (!nGlobalError && nParamCount-- > 0) + { + switch (GetStackType()) + { + case svDouble : + case svString: + case svSingleRef: + { + fx = ::rtl::math::approxFloor( GetDouble()); + if (fx < 0.0) + { + PushIllegalArgument(); + return; + } + if (fx == 0.0 || fy == 0.0) + fy = 0.0; + else + fy = fx * fy / ScGetGCD(fx, fy); + } + break; + case svDoubleRef : + case svRefList : + { + USHORT nErr = 0; + PopDoubleRef( aRange, nParamCount, nRefInList); + double nCellVal; + ScValueIterator aValIter(pDok, aRange, glSubTotal); + if (aValIter.GetFirst(nCellVal, nErr)) + { + do + { + fx = ::rtl::math::approxFloor( nCellVal); + if (fx < 0.0) + { + PushIllegalArgument(); + return; + } + if (fx == 0.0 || fy == 0.0) + fy = 0.0; + else + fy = fx * fy / ScGetGCD(fx, fy); + } while (nErr == 0 && aValIter.GetNext(nCellVal, nErr)); + } + SetError(nErr); + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + if (nC == 0 || nR == 0) + SetError(errIllegalArgument); + else + { + SCSIZE nCount = nC * nR; + for ( SCSIZE j = 0; j < nCount; j++ ) + { + if (!pMat->IsValue(j)) + { + PushIllegalArgument(); + return; + } + fx = ::rtl::math::approxFloor( pMat->GetDouble(j)); + if (fx < 0.0) + { + PushIllegalArgument(); + return; + } + if (fx == 0.0 || fy == 0.0) + fy = 0.0; + else + fy = fx * fy / ScGetGCD(fx, fy); + } + } + } + } + break; + default : SetError(errIllegalParameter); break; + } + } + PushDouble(fy); + } +} + +ScMatrixRef ScInterpreter::GetNewMat(SCSIZE nC, SCSIZE nR) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetNewMat" ); + ScMatrix* pMat = new ScMatrix( nC, nR); + pMat->SetErrorInterpreter( this); + // A temporary matrix is mutable and ScMatrix::CloneIfConst() returns the + // very matrix. + pMat->SetImmutable( false); + SCSIZE nCols, nRows; + pMat->GetDimensions( nCols, nRows); + if ( nCols != nC || nRows != nR ) + { // arbitray limit of elements exceeded + SetError( errStackOverflow); + pMat->Delete(); + pMat = NULL; + } + return pMat; +} + +ScMatrixRef ScInterpreter::CreateMatrixFromDoubleRef( const FormulaToken* pToken, + SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2 ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CreateMatrixFromDoubleRef" ); + ScMatrixRef pMat = NULL; + if (nTab1 == nTab2 && !nGlobalError) + { + ScTokenMatrixMap::const_iterator aIter; + if ( static_cast<SCSIZE>(nRow2 - nRow1 + 1) * + static_cast<SCSIZE>(nCol2 - nCol1 + 1) > + ScMatrix::GetElementsMax() ) + SetError(errStackOverflow); + else if (pTokenMatrixMap && ((aIter = pTokenMatrixMap->find( pToken)) + != pTokenMatrixMap->end())) + pMat = static_cast<ScToken*>((*aIter).second.get())->GetMatrix(); + else + { + SCSIZE nMatCols = static_cast<SCSIZE>(nCol2 - nCol1 + 1); + SCSIZE nMatRows = static_cast<SCSIZE>(nRow2 - nRow1 + 1); + pMat = GetNewMat( nMatCols, nMatRows); + if (pMat && !nGlobalError) + { + // Set position where the next entry is expected. + SCROW nNextRow = nRow1; + SCCOL nNextCol = nCol1; + // Set last position as if there was a previous entry. + SCROW nThisRow = nRow2; + SCCOL nThisCol = nCol1 - 1; + ScCellIterator aCellIter( pDok, nCol1, nRow1, nTab1, nCol2, + nRow2, nTab2); + for (ScBaseCell* pCell = aCellIter.GetFirst(); pCell; pCell = + aCellIter.GetNext()) + { + nThisCol = aCellIter.GetCol(); + nThisRow = aCellIter.GetRow(); + if (nThisCol != nNextCol || nThisRow != nNextRow) + { + // Fill empty between iterator's positions. + for ( ; nNextCol <= nThisCol; ++nNextCol) + { + SCSIZE nC = nNextCol - nCol1; + SCSIZE nMatStopRow = ((nNextCol < nThisCol) ? + nMatRows : nThisRow - nRow1); + for (SCSIZE nR = nNextRow - nRow1; nR < + nMatStopRow; ++nR) + { + pMat->PutEmpty( nC, nR); + } + nNextRow = nRow1; + } + } + if (nThisRow == nRow2) + { + nNextCol = nThisCol + 1; + nNextRow = nRow1; + } + else + { + nNextCol = nThisCol; + nNextRow = nThisRow + 1; + } + if (HasCellEmptyData(pCell)) + { + pMat->PutEmpty( static_cast<SCSIZE>(nThisCol-nCol1), + static_cast<SCSIZE>(nThisRow-nRow1)); + } + else if (HasCellValueData(pCell)) + { + ScAddress aAdr( nThisCol, nThisRow, nTab1); + double fVal = GetCellValue( aAdr, pCell); + if ( nGlobalError ) + { + fVal = CreateDoubleError( nGlobalError); + nGlobalError = 0; + } + pMat->PutDouble( fVal, + static_cast<SCSIZE>(nThisCol-nCol1), + static_cast<SCSIZE>(nThisRow-nRow1)); + } + else + { + String aStr; + GetCellString( aStr, pCell); + if ( nGlobalError ) + { + double fVal = CreateDoubleError( nGlobalError); + nGlobalError = 0; + pMat->PutDouble( fVal, + static_cast<SCSIZE>(nThisCol-nCol1), + static_cast<SCSIZE>(nThisRow-nRow1)); + } + else + pMat->PutString( aStr, + static_cast<SCSIZE>(nThisCol-nCol1), + static_cast<SCSIZE>(nThisRow-nRow1)); + } + } + // Fill empty if iterator's last position wasn't the end. + if (nThisCol != nCol2 || nThisRow != nRow2) + { + for ( ; nNextCol <= nCol2; ++nNextCol) + { + SCSIZE nC = nNextCol - nCol1; + for (SCSIZE nR = nNextRow - nRow1; nR < nMatRows; ++nR) + { + pMat->PutEmpty( nC, nR); + } + nNextRow = nRow1; + } + } + if (pTokenMatrixMap) + pTokenMatrixMap->insert( ScTokenMatrixMap::value_type( + pToken, new ScMatrixToken( pMat))); + } + } + } + else // not a 2D matrix + SetError(errIllegalParameter); + return pMat; +} + + +ScMatrixRef ScInterpreter::GetMatrix() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetMatrix" ); + ScMatrixRef pMat = NULL; + switch (GetRawStackType()) + { + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + pMat = GetNewMat(1, 1); + if (pMat) + { + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellEmptyData(pCell)) + pMat->PutEmpty( 0 ); + else if (HasCellValueData(pCell)) + pMat->PutDouble(GetCellValue(aAdr, pCell), 0); + else + { + String aStr; + GetCellString(aStr, pCell); + pMat->PutString(aStr, 0); + } + } + } + break; + case svDoubleRef: + { + SCCOL nCol1, nCol2; + SCROW nRow1, nRow2; + SCTAB nTab1, nTab2; + const ScToken* p = sp ? static_cast<const ScToken*>(pStack[sp-1]) : NULL; + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + pMat = CreateMatrixFromDoubleRef( p, nCol1, nRow1, nTab1, + nCol2, nRow2, nTab2); + } + break; + case svMatrix: + pMat = PopMatrix(); + break; + case svError : + case svMissing : + case svDouble : + { + double fVal = GetDouble(); + pMat = GetNewMat( 1, 1); + if ( pMat ) + { + if ( nGlobalError ) + { + fVal = CreateDoubleError( nGlobalError); + nGlobalError = 0; + } + pMat->PutDouble( fVal, 0); + } + } + break; + case svString : + { + String aStr = GetString(); + pMat = GetNewMat( 1, 1); + if ( pMat ) + { + if ( nGlobalError ) + { + double fVal = CreateDoubleError( nGlobalError); + pMat->PutDouble( fVal, 0); + nGlobalError = 0; + } + else + pMat->PutString( aStr, 0); + } + } + break; + default: + PopError(); + SetError( errIllegalArgument); + break; + } + return pMat; +} + +void ScInterpreter::ScMatValue() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMatValue" ); + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + // 0 to count-1 + SCSIZE nR = static_cast<SCSIZE>(::rtl::math::approxFloor(GetDouble())); + SCSIZE nC = static_cast<SCSIZE>(::rtl::math::approxFloor(GetDouble())); + switch (GetStackType()) + { + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (pCell && pCell->GetCellType() == CELLTYPE_FORMULA) + { + USHORT nErrCode = ((ScFormulaCell*)pCell)->GetErrCode(); + if (nErrCode != 0) + PushError( nErrCode); + else + { + const ScMatrix* pMat = ((ScFormulaCell*)pCell)->GetMatrix(); + CalculateMatrixValue(pMat,nC,nR); + } + } + else + PushIllegalParameter(); + } + break; + case svDoubleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + if (nCol2 - nCol1 >= static_cast<SCCOL>(nR) && + nRow2 - nRow1 >= static_cast<SCROW>(nC) && + nTab1 == nTab2) + { + ScAddress aAdr( sal::static_int_cast<SCCOL>( nCol1 + nR ), + sal::static_int_cast<SCROW>( nRow1 + nC ), nTab1 ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + PushDouble(GetCellValue( aAdr, pCell )); + else + { + String aStr; + GetCellString(aStr, pCell); + PushString(aStr); + } + } + else + PushNoValue(); + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + CalculateMatrixValue(pMat,nC,nR); + } + break; + default: + PopError(); + PushIllegalParameter(); + break; + } + } +} +void ScInterpreter::CalculateMatrixValue(const ScMatrix* pMat,SCSIZE nC,SCSIZE nR) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateMatrixValue" ); + if (pMat) + { + SCSIZE nCl, nRw; + pMat->GetDimensions(nCl, nRw); + if (nC < nCl && nR < nRw) + { + ScMatValType nMatValType; + const ScMatrixValue* pMatVal = pMat->Get( nC, nR,nMatValType); + if (ScMatrix::IsNonValueType( nMatValType)) + PushString( pMatVal->GetString() ); + else + PushDouble(pMatVal->fVal); + // also handles DoubleError + } + else + PushNoValue(); + } + else + PushNoValue(); +} + +void ScInterpreter::ScEMat() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScEMat" ); + if ( MustHaveParamCount( GetByte(), 1 ) ) + { + SCSIZE nDim = static_cast<SCSIZE>(::rtl::math::approxFloor(GetDouble())); + if ( nDim * nDim > ScMatrix::GetElementsMax() || nDim == 0) + PushIllegalArgument(); + else + { + ScMatrixRef pRMat = GetNewMat(nDim, nDim); + if (pRMat) + { + MEMat(pRMat, nDim); + PushMatrix(pRMat); + } + else + PushIllegalArgument(); + } + } +} + +void ScInterpreter::MEMat(ScMatrix* mM, SCSIZE n) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::MEMat" ); + mM->FillDouble(0.0, 0, 0, n-1, n-1); + for (SCSIZE i = 0; i < n; i++) + mM->PutDouble(1.0, i, i); +} + +void ScInterpreter::MFastMult(ScMatrix* pA, ScMatrix* pB, ScMatrix* pR, + SCSIZE n, SCSIZE m, SCSIZE l) + // Multipliziert n x m Mat a mit m x l Mat b nach Mat r +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::MFastMult" ); + double sum; + for (SCSIZE i = 0; i < n; i++) + { + for (SCSIZE j = 0; j < l; j++) + { + sum = 0.0; + for (SCSIZE k = 0; k < m; k++) + sum += pA->GetDouble(i,k)*pB->GetDouble(k,j); + pR->PutDouble(sum, i, j); + } + } +} + + +/* Matrix LUP decomposition according to the pseudocode of "Introduction to + * Algorithms" by Cormen, Leiserson, Rivest, Stein. + * + * Added scaling for numeric stability. + * + * Given an n x n nonsingular matrix A, find a permutation matrix P, a unit + * lower-triangular matrix L, and an upper-triangular matrix U such that PA=LU. + * Compute L and U "in place" in the matrix A, the original content is + * destroyed. Note that the diagonal elements of the U triangular matrix + * replace the diagonal elements of the L-unit matrix (that are each ==1). The + * permutation matrix P is an array, where P[i]=j means that the i-th row of P + * contains a 1 in column j. Additionally keep track of the number of + * permutations (row exchanges). + * + * Returns 0 if a singular matrix is encountered, else +1 if an even number of + * permutations occured, or -1 if odd, which is the sign of the determinant. + * This may be used to calculate the determinant by multiplying the sign with + * the product of the diagonal elements of the LU matrix. + */ +static int lcl_LUP_decompose( ScMatrix* mA, const SCSIZE n, + ::std::vector< SCSIZE> & P ) +{ + int nSign = 1; + // Find scale of each row. + ::std::vector< double> aScale(n); + for (SCSIZE i=0; i < n; ++i) + { + double fMax = 0.0; + for (SCSIZE j=0; j < n; ++j) + { + double fTmp = fabs( mA->GetDouble( j, i)); + if (fMax < fTmp) + fMax = fTmp; + } + if (fMax == 0.0) + return 0; // singular matrix + aScale[i] = 1.0 / fMax; + } + // Represent identity permutation, P[i]=i + for (SCSIZE i=0; i < n; ++i) + P[i] = i; + // "Recursion" on the diagonale. + SCSIZE l = n - 1; + for (SCSIZE k=0; k < l; ++k) + { + // Implicit pivoting. With the scale found for a row, compare values of + // a column and pick largest. + double fMax = 0.0; + double fScale = aScale[k]; + SCSIZE kp = k; + for (SCSIZE i = k; i < n; ++i) + { + double fTmp = fScale * fabs( mA->GetDouble( k, i)); + if (fMax < fTmp) + { + fMax = fTmp; + kp = i; + } + } + if (fMax == 0.0) + return 0; // singular matrix + // Swap rows. The pivot element will be at mA[k,kp] (row,col notation) + if (k != kp) + { + // permutations + SCSIZE nTmp = P[k]; + P[k] = P[kp]; + P[kp] = nTmp; + nSign = -nSign; + // scales + double fTmp = aScale[k]; + aScale[k] = aScale[kp]; + aScale[kp] = fTmp; + // elements + for (SCSIZE i=0; i < n; ++i) + { + double fMatTmp = mA->GetDouble( i, k); + mA->PutDouble( mA->GetDouble( i, kp), i, k); + mA->PutDouble( fMatTmp, i, kp); + } + } + // Compute Schur complement. + for (SCSIZE i = k+1; i < n; ++i) + { + double fTmp = mA->GetDouble( k, i) / mA->GetDouble( k, k); + mA->PutDouble( fTmp, k, i); + for (SCSIZE j = k+1; j < n; ++j) + mA->PutDouble( mA->GetDouble( j, i) - fTmp * mA->GetDouble( j, + k), j, i); + } + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "\n%s\n", "lcl_LUP_decompose(): LU"); + for (SCSIZE i=0; i < n; ++i) + { + for (SCSIZE j=0; j < n; ++j) + fprintf( stderr, "%8.2g ", mA->GetDouble( j, i)); + fprintf( stderr, "\n%s\n", ""); + } + fprintf( stderr, "\n%s\n", "lcl_LUP_decompose(): P"); + for (SCSIZE j=0; j < n; ++j) + fprintf( stderr, "%5u ", (unsigned)P[j]); + fprintf( stderr, "\n%s\n", ""); +#endif + return nSign; +} + + +/* Solve a LUP decomposed equation Ax=b. LU is a combined matrix of L and U + * triangulars and P the permutation vector as obtained from + * lcl_LUP_decompose(). B is the right-hand side input vector, X is used to + * return the solution vector. + */ +static void lcl_LUP_solve( const ScMatrix* mLU, const SCSIZE n, + const ::std::vector< SCSIZE> & P, const ::std::vector< double> & B, + ::std::vector< double> & X ) +{ + SCSIZE nFirst = SCSIZE_MAX; + // Ax=b => PAx=Pb, with decomposition LUx=Pb. + // Define y=Ux and solve for y in Ly=Pb using forward substitution. + for (SCSIZE i=0; i < n; ++i) + { + double fSum = B[P[i]]; + // Matrix inversion comes with a lot of zeros in the B vectors, we + // don't have to do all the computing with results multiplied by zero. + // Until then, simply lookout for the position of the first nonzero + // value. + if (nFirst != SCSIZE_MAX) + { + for (SCSIZE j = nFirst; j < i; ++j) + fSum -= mLU->GetDouble( j, i) * X[j]; // X[j] === y[j] + } + else if (fSum) + nFirst = i; + X[i] = fSum; // X[i] === y[i] + } + // Solve for x in Ux=y using back substitution. + for (SCSIZE i = n; i--; ) + { + double fSum = X[i]; // X[i] === y[i] + for (SCSIZE j = i+1; j < n; ++j) + fSum -= mLU->GetDouble( j, i) * X[j]; // X[j] === x[j] + X[i] = fSum / mLU->GetDouble( i, i); // X[i] === x[i] + } +#if OSL_DEBUG_LEVEL >1 + fprintf( stderr, "\n%s\n", "lcl_LUP_solve():"); + for (SCSIZE i=0; i < n; ++i) + fprintf( stderr, "%8.2g ", X[i]); + fprintf( stderr, "%s\n", ""); +#endif +} + + +void ScInterpreter::ScMatDet() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMatDet" ); + if ( MustHaveParamCount( GetByte(), 1 ) ) + { + ScMatrixRef pMat = GetMatrix(); + if (!pMat) + { + PushIllegalParameter(); + return; + } + if ( !pMat->IsNumeric() ) + { + PushNoValue(); + return; + } + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + if ( nC != nR || nC == 0 || (ULONG) nC * nC > ScMatrix::GetElementsMax() ) + PushIllegalArgument(); + else + { + // LUP decomposition is done inplace, use copy. + ScMatrixRef xLU = pMat->Clone(); + if (!xLU) + PushError( errCodeOverflow); + else + { + ::std::vector< SCSIZE> P(nR); + int nDetSign = lcl_LUP_decompose( xLU, nR, P); + if (!nDetSign) + PushInt(0); // singular matrix + else + { + // In an LU matrix the determinant is simply the product of + // all diagonal elements. + double fDet = nDetSign; + ScMatrix* pLU = xLU; + for (SCSIZE i=0; i < nR; ++i) + fDet *= pLU->GetDouble( i, i); + PushDouble( fDet); + } + } + } + } +} + +void ScInterpreter::ScMatInv() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMatInv" ); + if ( MustHaveParamCount( GetByte(), 1 ) ) + { + ScMatrixRef pMat = GetMatrix(); + if (!pMat) + { + PushIllegalParameter(); + return; + } + if ( !pMat->IsNumeric() ) + { + PushNoValue(); + return; + } + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + if ( nC != nR || nC == 0 || (ULONG) nC * nC > ScMatrix::GetElementsMax() ) + PushIllegalArgument(); + else + { + // LUP decomposition is done inplace, use copy. + ScMatrixRef xLU = pMat->Clone(); + // The result matrix. + ScMatrixRef xY = GetNewMat( nR, nR); + if (!xLU || !xY) + PushError( errCodeOverflow); + else + { + ::std::vector< SCSIZE> P(nR); + int nDetSign = lcl_LUP_decompose( xLU, nR, P); + if (!nDetSign) + PushIllegalArgument(); + else + { + // Solve equation for each column. + ScMatrix* pY = xY; + ::std::vector< double> B(nR); + ::std::vector< double> X(nR); + for (SCSIZE j=0; j < nR; ++j) + { + for (SCSIZE i=0; i < nR; ++i) + B[i] = 0.0; + B[j] = 1.0; + lcl_LUP_solve( xLU, nR, P, B, X); + for (SCSIZE i=0; i < nR; ++i) + pY->PutDouble( X[i], j, i); + } +#if 0 + /* Possible checks for ill-condition: + * 1. Scale matrix, invert scaled matrix. If there are + * elements of the inverted matrix that are several + * orders of magnitude greater than 1 => + * ill-conditioned. + * Just how much is "several orders"? + * 2. Invert the inverted matrix and assess whether the + * result is sufficiently close to the original matrix. + * If not => ill-conditioned. + * Just what is sufficient? + * 3. Multiplying the inverse by the original matrix should + * produce a result sufficiently close to the identity + * matrix. + * Just what is sufficient? + * + * The following is #3. + */ + ScMatrixRef xR = GetNewMat( nR, nR); + if (xR) + { + ScMatrix* pR = xR; + MFastMult( pMat, pY, pR, nR, nR, nR); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "\n%s\n", "ScMatInv(): mult-identity"); +#endif + for (SCSIZE i=0; i < nR; ++i) + { + for (SCSIZE j=0; j < nR; ++j) + { + double fTmp = pR->GetDouble( j, i); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%8.2g ", fTmp); +#endif + if (fabs( fTmp - (i == j)) > fInvEpsilon) + SetError( errIllegalArgument); + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "\n%s\n", ""); +#endif + } + } +#endif + if (nGlobalError) + PushError( nGlobalError); + else + PushMatrix( pY); + } + } + } + } +} + +void ScInterpreter::ScMatMult() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMatMult" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + ScMatrixRef pMat2 = GetMatrix(); + ScMatrixRef pMat1 = GetMatrix(); + ScMatrixRef pRMat; + if (pMat1 && pMat2) + { + if ( pMat1->IsNumeric() && pMat2->IsNumeric() ) + { + SCSIZE nC1, nC2; + SCSIZE nR1, nR2; + pMat1->GetDimensions(nC1, nR1); + pMat2->GetDimensions(nC2, nR2); + if (nC1 != nR2) + PushIllegalArgument(); + else + { + pRMat = GetNewMat(nC2, nR1); + if (pRMat) + { + double sum; + for (SCSIZE i = 0; i < nR1; i++) + { + for (SCSIZE j = 0; j < nC2; j++) + { + sum = 0.0; + for (SCSIZE k = 0; k < nC1; k++) + { + sum += pMat1->GetDouble(k,i)*pMat2->GetDouble(j,k); + } + pRMat->PutDouble(sum, j, i); + } + } + PushMatrix(pRMat); + } + else + PushIllegalArgument(); + } + } + else + PushNoValue(); + } + else + PushIllegalParameter(); + } +} + +void ScInterpreter::ScMatTrans() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMatTrans" ); + if ( MustHaveParamCount( GetByte(), 1 ) ) + { + ScMatrixRef pMat = GetMatrix(); + ScMatrixRef pRMat; + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + pRMat = GetNewMat(nR, nC); + if ( pRMat ) + { + pMat->MatTrans(*pRMat); + PushMatrix(pRMat); + } + else + PushIllegalArgument(); + } + else + PushIllegalParameter(); + } +} + + +/** Minimum extent of one result matrix dimension. + For a row or column vector to be replicated the larger matrix dimension is + returned, else the smaller dimension. + */ +inline SCSIZE lcl_GetMinExtent( SCSIZE n1, SCSIZE n2 ) +{ + if (n1 == 1) + return n2; + else if (n2 == 1) + return n1; + else if (n1 < n2) + return n1; + else + return n2; +} + +template<class _Function> +ScMatrixRef lcl_MatrixCalculation(const _Function& _pOperation,ScMatrix* pMat1, ScMatrix* pMat2,ScInterpreter* _pIterpreter) +{ + SCSIZE nC1, nC2, nMinC; + SCSIZE nR1, nR2, nMinR; + SCSIZE i, j; + pMat1->GetDimensions(nC1, nR1); + pMat2->GetDimensions(nC2, nR2); + nMinC = lcl_GetMinExtent( nC1, nC2); + nMinR = lcl_GetMinExtent( nR1, nR2); + ScMatrixRef xResMat = _pIterpreter->GetNewMat(nMinC, nMinR); + if (xResMat) + { + ScMatrix* pResMat = xResMat; + for (i = 0; i < nMinC; i++) + { + for (j = 0; j < nMinR; j++) + { + if (pMat1->IsValueOrEmpty(i,j) && pMat2->IsValueOrEmpty(i,j)) + { + double d = _pOperation(pMat1->GetDouble(i,j),pMat2->GetDouble(i,j)); + pResMat->PutDouble( d, i, j); + } + else + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), i, j); + } + } + } + return xResMat; +} + +ScMatrixRef ScInterpreter::MatConcat(ScMatrix* pMat1, ScMatrix* pMat2) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::MatConcat" ); + SCSIZE nC1, nC2, nMinC; + SCSIZE nR1, nR2, nMinR; + SCSIZE i, j; + pMat1->GetDimensions(nC1, nR1); + pMat2->GetDimensions(nC2, nR2); + nMinC = lcl_GetMinExtent( nC1, nC2); + nMinR = lcl_GetMinExtent( nR1, nR2); + ScMatrixRef xResMat = GetNewMat(nMinC, nMinR); + if (xResMat) + { + ScMatrix* pResMat = xResMat; + for (i = 0; i < nMinC; i++) + { + for (j = 0; j < nMinR; j++) + { + USHORT nErr = pMat1->GetErrorIfNotString( i, j); + if (!nErr) + nErr = pMat2->GetErrorIfNotString( i, j); + if (nErr) + pResMat->PutError( nErr, i, j); + else + { + String aTmp( pMat1->GetString( *pFormatter, i, j)); + aTmp += pMat2->GetString( *pFormatter, i, j); + pResMat->PutString( aTmp, i, j); + } + } + } + } + return xResMat; +} + + +// fuer DATE, TIME, DATETIME +void lcl_GetDiffDateTimeFmtType( short& nFuncFmt, short nFmt1, short nFmt2 ) +{ + if ( nFmt1 != NUMBERFORMAT_UNDEFINED || nFmt2 != NUMBERFORMAT_UNDEFINED ) + { + if ( nFmt1 == nFmt2 ) + { + if ( nFmt1 == NUMBERFORMAT_TIME || nFmt1 == NUMBERFORMAT_DATETIME ) + nFuncFmt = NUMBERFORMAT_TIME; // Zeiten ergeben Zeit + // else: nichts besonderes, Zahl (Datum - Datum := Tage) + } + else if ( nFmt1 == NUMBERFORMAT_UNDEFINED ) + nFuncFmt = nFmt2; // z.B. Datum + Tage := Datum + else if ( nFmt2 == NUMBERFORMAT_UNDEFINED ) + nFuncFmt = nFmt1; + else + { + if ( nFmt1 == NUMBERFORMAT_DATE || nFmt2 == NUMBERFORMAT_DATE || + nFmt1 == NUMBERFORMAT_DATETIME || nFmt2 == NUMBERFORMAT_DATETIME ) + { + if ( nFmt1 == NUMBERFORMAT_TIME || nFmt2 == NUMBERFORMAT_TIME ) + nFuncFmt = NUMBERFORMAT_DATETIME; // Datum + Zeit + } + } + } +} + + +void ScInterpreter::ScAdd() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAdd" ); + CalculateAddSub(FALSE); +} +void ScInterpreter::CalculateAddSub(BOOL _bSub) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateAddSub" ); + ScMatrixRef pMat1 = NULL; + ScMatrixRef pMat2 = NULL; + double fVal1 = 0.0, fVal2 = 0.0; + short nFmt1, nFmt2; + nFmt1 = nFmt2 = NUMBERFORMAT_UNDEFINED; + short nFmtCurrencyType = nCurFmtType; + ULONG nFmtCurrencyIndex = nCurFmtIndex; + short nFmtPercentType = nCurFmtType; + if ( GetStackType() == svMatrix ) + pMat2 = GetMatrix(); + else + { + fVal2 = GetDouble(); + switch ( nCurFmtType ) + { + case NUMBERFORMAT_DATE : + case NUMBERFORMAT_TIME : + case NUMBERFORMAT_DATETIME : + nFmt2 = nCurFmtType; + break; + case NUMBERFORMAT_CURRENCY : + nFmtCurrencyType = nCurFmtType; + nFmtCurrencyIndex = nCurFmtIndex; + break; + case NUMBERFORMAT_PERCENT : + nFmtPercentType = NUMBERFORMAT_PERCENT; + break; + } + } + if ( GetStackType() == svMatrix ) + pMat1 = GetMatrix(); + else + { + fVal1 = GetDouble(); + switch ( nCurFmtType ) + { + case NUMBERFORMAT_DATE : + case NUMBERFORMAT_TIME : + case NUMBERFORMAT_DATETIME : + nFmt1 = nCurFmtType; + break; + case NUMBERFORMAT_CURRENCY : + nFmtCurrencyType = nCurFmtType; + nFmtCurrencyIndex = nCurFmtIndex; + break; + case NUMBERFORMAT_PERCENT : + nFmtPercentType = NUMBERFORMAT_PERCENT; + break; + } + } + if (pMat1 && pMat2) + { + ScMatrixRef pResMat; + if ( _bSub ) + { + MatrixSub aSub; + pResMat = lcl_MatrixCalculation(aSub ,pMat1, pMat2,this); + } + else + { + MatrixAdd aAdd; + pResMat = lcl_MatrixCalculation(aAdd ,pMat1, pMat2,this); + } + + if (!pResMat) + PushNoValue(); + else + PushMatrix(pResMat); + } + else if (pMat1 || pMat2) + { + double fVal; + BOOL bFlag; + ScMatrixRef pMat = pMat1; + if (!pMat) + { + fVal = fVal1; + pMat = pMat2; + bFlag = TRUE; // double - Matrix + } + else + { + fVal = fVal2; + bFlag = FALSE; // Matrix - double + } + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + ScMatrixRef pResMat = GetNewMat(nC, nR); + if (pResMat) + { + SCSIZE nCount = nC * nR; + if (bFlag || !_bSub ) + { + for ( SCSIZE i = 0; i < nCount; i++ ) + { + if (pMat->IsValue(i)) + pResMat->PutDouble( _bSub ? ::rtl::math::approxSub( fVal, pMat->GetDouble(i)) : ::rtl::math::approxAdd( pMat->GetDouble(i), fVal), i); + else + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), i); + } // for ( SCSIZE i = 0; i < nCount; i++ ) + } // if (bFlag || !_bSub ) + else + { + for ( SCSIZE i = 0; i < nCount; i++ ) + { if (pMat->IsValue(i)) + pResMat->PutDouble( ::rtl::math::approxSub( pMat->GetDouble(i), fVal), i); + else + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), i); + } // for ( SCSIZE i = 0; i < nCount; i++ ) + } + PushMatrix(pResMat); + } + else + PushIllegalArgument(); + } + else if ( _bSub ) + PushDouble( ::rtl::math::approxSub( fVal1, fVal2 ) ); + else + PushDouble( ::rtl::math::approxAdd( fVal1, fVal2 ) ); + if ( nFmtCurrencyType == NUMBERFORMAT_CURRENCY ) + { + nFuncFmtType = nFmtCurrencyType; + nFuncFmtIndex = nFmtCurrencyIndex; + } + else + { + lcl_GetDiffDateTimeFmtType( nFuncFmtType, nFmt1, nFmt2 ); + if ( nFmtPercentType == NUMBERFORMAT_PERCENT && nFuncFmtType == NUMBERFORMAT_NUMBER ) + nFuncFmtType = NUMBERFORMAT_PERCENT; + } +} + +void ScInterpreter::ScAmpersand() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAmpersand" ); + ScMatrixRef pMat1 = NULL; + ScMatrixRef pMat2 = NULL; + String sStr1, sStr2; + if ( GetStackType() == svMatrix ) + pMat2 = GetMatrix(); + else + sStr2 = GetString(); + if ( GetStackType() == svMatrix ) + pMat1 = GetMatrix(); + else + sStr1 = GetString(); + if (pMat1 && pMat2) + { + ScMatrixRef pResMat = MatConcat(pMat1, pMat2); + if (!pResMat) + PushNoValue(); + else + PushMatrix(pResMat); + } + else if (pMat1 || pMat2) + { + String sStr; + BOOL bFlag; + ScMatrixRef pMat = pMat1; + if (!pMat) + { + sStr = sStr1; + pMat = pMat2; + bFlag = TRUE; // double - Matrix + } + else + { + sStr = sStr2; + bFlag = FALSE; // Matrix - double + } + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + ScMatrixRef pResMat = GetNewMat(nC, nR); + if (pResMat) + { + SCSIZE nCount = nC * nR; + if (nGlobalError) + { + for ( SCSIZE i = 0; i < nCount; i++ ) + pResMat->PutError( nGlobalError, i); + } + else if (bFlag) + { + for ( SCSIZE i = 0; i < nCount; i++ ) + { + USHORT nErr = pMat->GetErrorIfNotString( i); + if (nErr) + pResMat->PutError( nErr, i); + else + { + String aTmp( sStr); + aTmp += pMat->GetString( *pFormatter, i); + pResMat->PutString( aTmp, i); + } + } + } + else + { + for ( SCSIZE i = 0; i < nCount; i++ ) + { + USHORT nErr = pMat->GetErrorIfNotString( i); + if (nErr) + pResMat->PutError( nErr, i); + else + { + String aTmp( pMat->GetString( *pFormatter, i)); + aTmp += sStr; + pResMat->PutString( aTmp, i); + } + } + } + PushMatrix(pResMat); + } + else + PushIllegalArgument(); + } + else + { + if ( CheckStringResultLen( sStr1, sStr2 ) ) + sStr1 += sStr2; + PushString(sStr1); + } +} + +void ScInterpreter::ScSub() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSub" ); + CalculateAddSub(TRUE); +} + +void ScInterpreter::ScMul() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMul" ); + ScMatrixRef pMat1 = NULL; + ScMatrixRef pMat2 = NULL; + double fVal1 = 0.0, fVal2 = 0.0; + short nFmtCurrencyType = nCurFmtType; + ULONG nFmtCurrencyIndex = nCurFmtIndex; + if ( GetStackType() == svMatrix ) + pMat2 = GetMatrix(); + else + { + fVal2 = GetDouble(); + switch ( nCurFmtType ) + { + case NUMBERFORMAT_CURRENCY : + nFmtCurrencyType = nCurFmtType; + nFmtCurrencyIndex = nCurFmtIndex; + break; + } + } + if ( GetStackType() == svMatrix ) + pMat1 = GetMatrix(); + else + { + fVal1 = GetDouble(); + switch ( nCurFmtType ) + { + case NUMBERFORMAT_CURRENCY : + nFmtCurrencyType = nCurFmtType; + nFmtCurrencyIndex = nCurFmtIndex; + break; + } + } + if (pMat1 && pMat2) + { + MatrixMul aMul; + ScMatrixRef pResMat = lcl_MatrixCalculation(aMul,pMat1, pMat2,this); + if (!pResMat) + PushNoValue(); + else + PushMatrix(pResMat); + } + else if (pMat1 || pMat2) + { + double fVal; + ScMatrixRef pMat = pMat1; + if (!pMat) + { + fVal = fVal1; + pMat = pMat2; + } + else + fVal = fVal2; + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + ScMatrixRef pResMat = GetNewMat(nC, nR); + if (pResMat) + { + SCSIZE nCount = nC * nR; + for ( SCSIZE i = 0; i < nCount; i++ ) + if (pMat->IsValue(i)) + pResMat->PutDouble(pMat->GetDouble(i)*fVal, i); + else + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), i); + PushMatrix(pResMat); + } + else + PushIllegalArgument(); + } + else + PushDouble(fVal1 * fVal2); + if ( nFmtCurrencyType == NUMBERFORMAT_CURRENCY ) + { + nFuncFmtType = nFmtCurrencyType; + nFuncFmtIndex = nFmtCurrencyIndex; + } +} + +void ScInterpreter::ScDiv() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDiv" ); + ScMatrixRef pMat1 = NULL; + ScMatrixRef pMat2 = NULL; + double fVal1 = 0.0, fVal2 = 0.0; + short nFmtCurrencyType = nCurFmtType; + ULONG nFmtCurrencyIndex = nCurFmtIndex; + short nFmtCurrencyType2 = NUMBERFORMAT_UNDEFINED; + if ( GetStackType() == svMatrix ) + pMat2 = GetMatrix(); + else + { + fVal2 = GetDouble(); + // hier kein Currency uebernehmen, 123kg/456DM sind nicht DM + nFmtCurrencyType2 = nCurFmtType; + } + if ( GetStackType() == svMatrix ) + pMat1 = GetMatrix(); + else + { + fVal1 = GetDouble(); + switch ( nCurFmtType ) + { + case NUMBERFORMAT_CURRENCY : + nFmtCurrencyType = nCurFmtType; + nFmtCurrencyIndex = nCurFmtIndex; + break; + } + } + if (pMat1 && pMat2) + { + MatrixDiv aDiv; + ScMatrixRef pResMat = lcl_MatrixCalculation(aDiv,pMat1, pMat2,this); + if (!pResMat) + PushNoValue(); + else + PushMatrix(pResMat); + } + else if (pMat1 || pMat2) + { + double fVal; + BOOL bFlag; + ScMatrixRef pMat = pMat1; + if (!pMat) + { + fVal = fVal1; + pMat = pMat2; + bFlag = TRUE; // double - Matrix + } + else + { + fVal = fVal2; + bFlag = FALSE; // Matrix - double + } + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + ScMatrixRef pResMat = GetNewMat(nC, nR); + if (pResMat) + { + SCSIZE nCount = nC * nR; + if (bFlag) + { for ( SCSIZE i = 0; i < nCount; i++ ) + if (pMat->IsValue(i)) + pResMat->PutDouble( div( fVal, pMat->GetDouble(i)), i); + else + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), i); + } + else + { for ( SCSIZE i = 0; i < nCount; i++ ) + if (pMat->IsValue(i)) + pResMat->PutDouble( div( pMat->GetDouble(i), fVal), i); + else + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), i); + } + PushMatrix(pResMat); + } + else + PushIllegalArgument(); + } + else + { + PushDouble( div( fVal1, fVal2) ); + } + if ( nFmtCurrencyType == NUMBERFORMAT_CURRENCY && nFmtCurrencyType2 != NUMBERFORMAT_CURRENCY ) + { // auch DM/DM ist nicht DM bzw. DEM/EUR nicht DEM + nFuncFmtType = nFmtCurrencyType; + nFuncFmtIndex = nFmtCurrencyIndex; + } +} + +void ScInterpreter::ScPower() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPower" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + ScPow(); +} + +void ScInterpreter::ScPow() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPow" ); + ScMatrixRef pMat1 = NULL; + ScMatrixRef pMat2 = NULL; + double fVal1 = 0.0, fVal2 = 0.0; + if ( GetStackType() == svMatrix ) + pMat2 = GetMatrix(); + else + fVal2 = GetDouble(); + if ( GetStackType() == svMatrix ) + pMat1 = GetMatrix(); + else + fVal1 = GetDouble(); + if (pMat1 && pMat2) + { + MatrixPow aPow; + ScMatrixRef pResMat = lcl_MatrixCalculation(aPow,pMat1, pMat2,this); + if (!pResMat) + PushNoValue(); + else + PushMatrix(pResMat); + } + else if (pMat1 || pMat2) + { + double fVal; + BOOL bFlag; + ScMatrixRef pMat = pMat1; + if (!pMat) + { + fVal = fVal1; + pMat = pMat2; + bFlag = TRUE; // double - Matrix + } + else + { + fVal = fVal2; + bFlag = FALSE; // Matrix - double + } + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + ScMatrixRef pResMat = GetNewMat(nC, nR); + if (pResMat) + { + SCSIZE nCount = nC * nR; + if (bFlag) + { for ( SCSIZE i = 0; i < nCount; i++ ) + if (pMat->IsValue(i)) + pResMat->PutDouble(pow(fVal,pMat->GetDouble(i)), i); + else + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), i); + } + else + { for ( SCSIZE i = 0; i < nCount; i++ ) + if (pMat->IsValue(i)) + pResMat->PutDouble(pow(pMat->GetDouble(i),fVal), i); + else + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), i); + } + PushMatrix(pResMat); + } + else + PushIllegalArgument(); + } + else + PushDouble(pow(fVal1,fVal2)); +} + +void ScInterpreter::ScSumProduct() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSumProduct" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 1, 30 ) ) + return; + + ScMatrixRef pMat1 = NULL; + ScMatrixRef pMat2 = NULL; + ScMatrixRef pMat = NULL; + pMat2 = GetMatrix(); + if (!pMat2) + { + PushIllegalParameter(); + return; + } + SCSIZE nC, nC1; + SCSIZE nR, nR1; + pMat2->GetDimensions(nC, nR); + pMat = pMat2; + MatrixMul aMul; + for (USHORT i = 1; i < nParamCount; i++) + { + pMat1 = GetMatrix(); + if (!pMat1) + { + PushIllegalParameter(); + return; + } + pMat1->GetDimensions(nC1, nR1); + if (nC1 != nC || nR1 != nR) + { + PushNoValue(); + return; + } + ScMatrixRef pResMat = lcl_MatrixCalculation(aMul,pMat1, pMat,this); + if (!pResMat) + { + PushNoValue(); + return; + } + else + pMat = pResMat; + } + double fSum = 0.0; + SCSIZE nCount = pMat->GetElementCount(); + for (SCSIZE j = 0; j < nCount; j++) + { + if (!pMat->IsString(j)) + fSum += pMat->GetDouble(j); + } + PushDouble(fSum); +} + +void ScInterpreter::ScSumX2MY2() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSumX2MY2" ); + CalculateSumX2MY2SumX2DY2(FALSE); +} +void ScInterpreter::CalculateSumX2MY2SumX2DY2(BOOL _bSumX2DY2) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateSumX2MY2SumX2DY2" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + + ScMatrixRef pMat1 = NULL; + ScMatrixRef pMat2 = NULL; + SCSIZE i, j; + pMat2 = GetMatrix(); + pMat1 = GetMatrix(); + if (!pMat2 || !pMat1) + { + PushIllegalParameter(); + return; + } + SCSIZE nC1, nC2; + SCSIZE nR1, nR2; + pMat2->GetDimensions(nC2, nR2); + pMat1->GetDimensions(nC1, nR1); + if (nC1 != nC2 || nR1 != nR2) + { + PushNoValue(); + return; + } + double fVal, fSum = 0.0; + for (i = 0; i < nC1; i++) + for (j = 0; j < nR1; j++) + if (!pMat1->IsString(i,j) && !pMat2->IsString(i,j)) + { + fVal = pMat1->GetDouble(i,j); + fSum += fVal * fVal; + fVal = pMat2->GetDouble(i,j); + if ( _bSumX2DY2 ) + fSum += fVal * fVal; + else + fSum -= fVal * fVal; + } + PushDouble(fSum); +} + +void ScInterpreter::ScSumX2DY2() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSumX2DY2" ); + CalculateSumX2MY2SumX2DY2(TRUE); +} + +void ScInterpreter::ScSumXMY2() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSumXMY2" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + + ScMatrixRef pMat1 = NULL; + ScMatrixRef pMat2 = NULL; + pMat2 = GetMatrix(); + pMat1 = GetMatrix(); + if (!pMat2 || !pMat1) + { + PushIllegalParameter(); + return; + } + SCSIZE nC1, nC2; + SCSIZE nR1, nR2; + pMat2->GetDimensions(nC2, nR2); + pMat1->GetDimensions(nC1, nR1); + if (nC1 != nC2 || nR1 != nR2) + { + PushNoValue(); + return; + } // if (nC1 != nC2 || nR1 != nR2) + MatrixSub aSub; + ScMatrixRef pResMat = lcl_MatrixCalculation(aSub,pMat1, pMat2,this); + if (!pResMat) + { + PushNoValue(); + } + else + { + double fVal, fSum = 0.0; + SCSIZE nCount = pResMat->GetElementCount(); + for (SCSIZE i = 0; i < nCount; i++) + if (!pResMat->IsString(i)) + { + fVal = pResMat->GetDouble(i); + fSum += fVal * fVal; + } + PushDouble(fSum); + } +} + +void ScInterpreter::ScFrequency() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFrequency" ); + if ( !MustHaveParamCount( GetByte(), 2 ) ) + return; + + vector<double> aBinArray; + vector<long> aBinIndexOrder; + + GetSortArray(1, aBinArray, &aBinIndexOrder); + SCSIZE nBinSize = aBinArray.size(); + if (nGlobalError) + { + PushNoValue(); + return; + } + + vector<double> aDataArray; + GetSortArray(1, aDataArray); + SCSIZE nDataSize = aDataArray.size(); + + if (aDataArray.empty() || nGlobalError) + { + PushNoValue(); + return; + } + ScMatrixRef pResMat = GetNewMat(1, nBinSize+1); + if (!pResMat) + { + PushIllegalArgument(); + return; + } + + if (nBinSize != aBinIndexOrder.size()) + { + PushIllegalArgument(); + return; + } + + SCSIZE j; + SCSIZE i = 0; + for (j = 0; j < nBinSize; ++j) + { + SCSIZE nCount = 0; + while (i < nDataSize && aDataArray[i] <= aBinArray[j]) + { + ++nCount; + ++i; + } + pResMat->PutDouble(static_cast<double>(nCount), aBinIndexOrder[j]); + } + pResMat->PutDouble(static_cast<double>(nDataSize-i), j); + PushMatrix(pResMat); +} + +BOOL ScInterpreter::RGetVariances( ScMatrix* pV, ScMatrix* pX, + SCSIZE nC, SCSIZE nR, BOOL bSwapColRow, BOOL bZeroConstant ) +{ // multiple Regression: Varianzen der Koeffizienten + // bSwapColRow==TRUE : Koeffizienten in Zeilen statt Spalten angeordnet + SCSIZE i, j, k; + double sum; + ScMatrixRef pC = GetNewMat(nC, nC); + if ( !pC ) + return FALSE; + // X transformiert mit X multipziert, X'X Matrix + if ( !bZeroConstant ) + { // in der X-Designmatrix existiert ein gedachtes X0j==1 + if ( bSwapColRow ) + { + for ( i=0; i<nC; i++ ) + { + for ( j=0; j<nC; j++ ) + { + sum = 0.0; + for ( k=0; k<nR; k++ ) + { + sum += (j==0 ? 1 : pX->GetDouble(k,j-1)) + * (i==0 ? 1 : pX->GetDouble(k,i-1)); + } + pC->PutDouble(sum, i, j); + } + } + } + else + { + for ( i=0; i<nC; i++ ) + { + for ( j=0; j<nC; j++ ) + { + sum = 0.0; + for ( k=0; k<nR; k++ ) + { + sum += (j==0 ? 1 : pX->GetDouble(j-1,k)) + * (i==0 ? 1 : pX->GetDouble(i-1,k)); + } + pC->PutDouble(sum, i, j); + } + } + } + } + else + { + if ( bSwapColRow ) + { + for ( i=0; i<nC; i++ ) + { + for ( j=0; j<nC; j++ ) + { + sum = 0.0; + for ( k=0; k<nR; k++ ) + { + sum += pX->GetDouble(k,j) * pX->GetDouble(k,i); + } + pC->PutDouble(sum, i, j); + } + } + } + else + { + for ( i=0; i<nC; i++ ) + { + for ( j=0; j<nC; j++ ) + { + sum = 0.0; + for ( k=0; k<nR; k++ ) + { + sum += pX->GetDouble(j,k) * pX->GetDouble(i,k); + } + pC->PutDouble(sum, i, j); + } + } + } + } + // X'X Inverse + BOOL bOk = TRUE; + USHORT nErr = nGlobalError; + PushMatrix(pC); + BYTE nTmp = cPar; + cPar = 1; + ScMatInv(); + cPar = nTmp; + if ( nGlobalError ) + { + nGlobalError = nErr; + bOk = FALSE; + } + else + { + // #i61216# ScMatInv no longer modifies the original matrix, so just calling Pop() doesn't work + pC = PopMatrix(); + if ( pC.Is() ) + { + // Varianzen auf der Diagonalen, andere sind Kovarianzen + for (i = 0; i < nC; i++) + pV->PutDouble(pC->GetDouble(i, i), i); + } + } + return bOk; +} +// ----------------------------------------------------------------------------- +void ScInterpreter::Calculate(ScMatrixRef& pResMat,ScMatrixRef& pE,ScMatrixRef& pQ,ScMatrixRef& pV,ScMatrixRef& pMatX,BOOL bConstant,SCSIZE N,SCSIZE M,BYTE nCase) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::RGetVariances" ); + // pE[0] := Sigma i=1...n (Yi) + // pE[k] := Sigma i=1...n (Xki*Yi) + // pE[M+1] := Sigma i=1...n (Yi**2) + // pQ[0,M+1]:= B + // pQ[k,M+1]:= Mk + double fSQR, fSQT, fSQE; + fSQT = pE->GetDouble(M+1) + - pE->GetDouble(0) * pE->GetDouble(0) / (double)N; + fSQR = pE->GetDouble(M+1); + SCSIZE i, j; + for (i = 0; i < M+1; i++) + fSQR -= pQ->GetDouble(i, M+1) * pE->GetDouble(i); + fSQE = fSQT-fSQR; + // r2 (Bestimmtheitsmass, 0...1) + if (fSQT == 0.0) + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), 0, 2); + else + pResMat->PutDouble (fSQE/fSQT, 0, 2); + // ssReg (Regressions-Quadratsumme) + pResMat->PutDouble(fSQE, 0, 4); + // ssResid (Residual-Quadratsumme, Summe der Abweichungsquadrate) + pResMat->PutDouble(fSQR, 1, 4); + for (i = 2; i < 5; i++) + for (j = 2; j < M+1; j++) + pResMat->PutString(ScGlobal::GetRscString(STR_NV_STR), j, i); + if (bConstant) + { + if (N-M-1 == 0) + { + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), 1, 2); + for (i = 0; i < M+1; i++) + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), i, 1); + } + else + { + double fSE2 = fSQR/(N-M-1); + // sey (Standardfehler des Schaetzwertes y) + pResMat->PutDouble(sqrt(fSE2), 1, 2); + // sen...se1 (Standardfehler der Koeffizienten mn...m1) + // seb (Standardfehler der Konstanten b) + if ( RGetVariances( pV, pMatX, M+1, N, nCase != 2, FALSE ) ) + { + for (i = 0; i < M+1; i++) + pResMat->PutDouble( sqrt(fSE2 * pV->GetDouble(i)), M-i, 1 ); + } + else + { + for (i = 0; i < M+1; i++) + pResMat->PutString(ScGlobal::GetRscString(STR_NV_STR), i, 1); + } + } + // F (F-Statistik) + if (fSQR == 0.0) + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), 0, 3); + else + pResMat->PutDouble(((double)(N-M-1))*fSQE/fSQR/((double)M),0, 3); + // df (Freiheitsgrad) + pResMat->PutDouble(((double)(N-M-1)), 1, 3); + } + else + { + if (N-M == 0) + { + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), 1, 2); + for (i = 0; i < M+1; i++) + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), i, 1); + } + else + { + double fSE2 = fSQR/(N-M); + pResMat->PutDouble(sqrt(fSE2), 1, 2); + if ( RGetVariances( pV, pMatX, M, N, nCase != 2, TRUE ) ) + { + for (i = 0; i < M; i++) + pResMat->PutDouble( sqrt(fSE2 * pV->GetDouble(i)), M-i-1, 1 ); + pResMat->PutString(ScGlobal::GetRscString(STR_NV_STR), M, 1); + } + else + { + for (i = 0; i < M+1; i++) + pResMat->PutString(ScGlobal::GetRscString(STR_NV_STR), i, 1); + } + } + if (fSQR == 0.0) + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), 0, 3); + else + pResMat->PutDouble(((double)(N-M))*fSQE/fSQR/((double)M),0, 3); + pResMat->PutDouble(((double)(N-M)), 1, 3); + } +} + +void ScInterpreter::ScRGP() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRGP" ); + CalulateRGPRKP(FALSE); +} +bool ScInterpreter::CheckMatrix(BOOL _bLOG,BOOL _bTrendGrowth,BYTE& nCase,SCSIZE& nCX,SCSIZE& nCY,SCSIZE& nRX,SCSIZE& nRY,SCSIZE& M,SCSIZE& N,ScMatrixRef& pMatX,ScMatrixRef& pMatY) +{ + nCX = 0; + nCY = 0; + nRX = 0; + nRY = 0; + M = 0; + N = 0; + pMatY->GetDimensions(nCY, nRY); + const SCSIZE nCountY = nCY * nRY; + for ( SCSIZE i = 0; i < nCountY; i++ ) + { + if (!pMatY->IsValue(i)) + { + PushIllegalArgument(); + return false; + } + } + + if ( _bLOG ) + { + ScMatrixRef pNewY = pMatY->CloneIfConst(); + for (SCSIZE nElem = 0; nElem < nCountY; nElem++) + { + const double fVal = pNewY->GetDouble(nElem); + if (fVal <= 0.0) + { + PushIllegalArgument(); + return false; + } + else + pNewY->PutDouble(log(fVal), nElem); + } + pMatY = pNewY; + } + + if (pMatX) + { + pMatX->GetDimensions(nCX, nRX); + const SCSIZE nCountX = nCX * nRX; + for ( SCSIZE i = 0; i < nCountX; i++ ) + if (!pMatX->IsValue(i)) + { + PushIllegalArgument(); + return false; + } + if (nCX == nCY && nRX == nRY) + nCase = 1; // einfache Regression + else if (nCY != 1 && nRY != 1) + { + PushIllegalArgument(); + return false; + } + else if (nCY == 1) + { + if (nRX != nRY) + { + PushIllegalArgument(); + return false; + } + else + { + nCase = 2; // zeilenweise + N = nRY; + M = nCX; + } + } + else if (nCX != nCY) + { + PushIllegalArgument(); + return false; + } + else + { + nCase = 3; // spaltenweise + N = nCY; + M = nRX; + } + } + else + { + pMatX = GetNewMat(nCY, nRY); + if ( _bTrendGrowth ) + { + nCX = nCY; + nRX = nRY; + } + if (!pMatX) + { + PushIllegalArgument(); + return false; + } + for ( SCSIZE i = 1; i <= nCountY; i++ ) + pMatX->PutDouble((double)i, i-1); + nCase = 1; + } + return true; +} +void ScInterpreter::CalulateRGPRKP(BOOL _bRKP) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CheckMatrix" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 1, 4 ) ) + return; + BOOL bConstant, bStats; + if (nParamCount == 4) + bStats = GetBool(); + else + bStats = FALSE; + if (nParamCount >= 3) + bConstant = GetBool(); + else + bConstant = TRUE; + ScMatrixRef pMatX; + ScMatrixRef pMatY; + if (nParamCount >= 2) + pMatX = GetMatrix(); + else + pMatX = NULL; + pMatY = GetMatrix(); + if (!pMatY) + { + PushIllegalParameter(); + return; + } // if (!pMatY) + BYTE nCase; // 1 = normal, 2,3 = mehrfach + SCSIZE nCX, nCY; + SCSIZE nRX, nRY; + SCSIZE M = 0, N = 0; + if ( !CheckMatrix(_bRKP,FALSE,nCase,nCX,nCY,nRX,nRY,M,N,pMatX,pMatY) ) + return; + + ScMatrixRef pResMat; + if (nCase == 1) + { + if (!bStats) + pResMat = GetNewMat(2,1); + else + pResMat = GetNewMat(2,5); + if (!pResMat) + { + PushIllegalArgument(); + return; + } + double fCount = 0.0; + double fSumX = 0.0; + double fSumSqrX = 0.0; + double fSumY = 0.0; + double fSumSqrY = 0.0; + double fSumXY = 0.0; + double fValX, fValY; + for (SCSIZE i = 0; i < nCY; i++) + for (SCSIZE j = 0; j < nRY; j++) + { + fValX = pMatX->GetDouble(i,j); + fValY = pMatY->GetDouble(i,j); + fSumX += fValX; + fSumSqrX += fValX * fValX; + fSumY += fValY; + fSumSqrY += fValY * fValY; + fSumXY += fValX*fValY; + fCount++; + } + if (fCount < 1.0) + PushNoValue(); + else + { + double f1 = fCount*fSumXY-fSumX*fSumY; + double fX = fCount*fSumSqrX-fSumX*fSumX; + double b, m; + if (bConstant) + { + b = fSumY/fCount - f1/fX*fSumX/fCount; + m = f1/fX; + } + else + { + b = 0.0; + m = fSumXY/fSumSqrX; + } + pResMat->PutDouble(_bRKP ? exp(m) : m, 0, 0); + pResMat->PutDouble(_bRKP ? exp(b) : b, 1, 0); + if (bStats) + { + double fY = fCount*fSumSqrY-fSumY*fSumY; + double fSyx = fSumSqrY-b*fSumY-m*fSumXY; + double fR2 = f1*f1/(fX*fY); + pResMat->PutDouble (fR2, 0, 2); + if (fCount < 3.0) + { + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), 0, 1 ); + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), 1, 1 ); + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), 1, 2 ); + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), 0, 3 ); + } + else + { + pResMat->PutDouble(sqrt(fSyx*fCount/(fX*(fCount-2.0))), 0, 1); + pResMat->PutDouble(sqrt(fSyx*fSumSqrX/fX/(fCount-2.0)), 1, 1); + pResMat->PutDouble( + sqrt((fCount*fSumSqrY - fSumY*fSumY - f1*f1/fX)/ + (fCount*(fCount-2.0))), 1, 2); + if (fR2 == 1.0) + pResMat->PutString(ScGlobal::GetRscString(STR_NO_VALUE), 0, 3 ); + else + pResMat->PutDouble(fR2*(fCount-2.0)/(1.0-fR2), 0, 3); + } + pResMat->PutDouble(((double)(nCY*nRY))-2.0, 1, 3); + pResMat->PutDouble(fY/fCount-fSyx, 0, 4); + pResMat->PutDouble(fSyx, 1, 4); + } + } + } // if (nCase == 1) + if ( nCase != 1 ) + { + SCSIZE i, j, k; + if (!bStats) + pResMat = GetNewMat(M+1,1); + else + pResMat = GetNewMat(M+1,5); + if (!pResMat) + { + PushIllegalArgument(); + return; + } + ScMatrixRef pQ = GetNewMat(M+1, M+2); + ScMatrixRef pE = GetNewMat(M+2, 1); + ScMatrixRef pV = GetNewMat(M+1, 1); + pE->PutDouble(0.0, M+1); + pQ->FillDouble(0.0, 0, 0, M, M+1); + if (nCase == 2) + { + for (k = 0; k < N; k++) + { + double Yk = pMatY->GetDouble(k); + pE->PutDouble( pE->GetDouble(M+1)+Yk*Yk, M+1 ); + double sumYk = pQ->GetDouble(0, M+1) + Yk; + pQ->PutDouble( sumYk, 0, M+1 ); + pE->PutDouble( sumYk, 0 ); + for (i = 0; i < M; i++) + { + double Xik = pMatX->GetDouble(i,k); + double sumXik = pQ->GetDouble(0, i+1) + Xik; + pQ->PutDouble( sumXik, 0, i+1); + pQ->PutDouble( sumXik, i+1, 0); + double sumXikYk = pQ->GetDouble(i+1, M+1) + Xik * Yk; + pQ->PutDouble( sumXikYk, i+1, M+1); + pE->PutDouble( sumXikYk, i+1); + for (j = i; j < M; j++) + { + const double fVal = pMatX->GetDouble(j,k); + double sumXikXjk = pQ->GetDouble(j+1, i+1) + + Xik * fVal; + pQ->PutDouble( sumXikXjk, j+1, i+1); + pQ->PutDouble( sumXikXjk, i+1, j+1); + } + } + } + } + else + { + for (k = 0; k < N; k++) + { + double Yk = pMatY->GetDouble(k); + pE->PutDouble( pE->GetDouble(M+1)+Yk*Yk, M+1 ); + double sumYk = pQ->GetDouble(0, M+1) + Yk; + pQ->PutDouble( sumYk, 0, M+1 ); + pE->PutDouble( sumYk, 0 ); + for (i = 0; i < M; i++) + { + double Xki = pMatX->GetDouble(k,i); + double sumXki = pQ->GetDouble(0, i+1) + Xki; + pQ->PutDouble( sumXki, 0, i+1); + pQ->PutDouble( sumXki, i+1, 0); + double sumXkiYk = pQ->GetDouble(i+1, M+1) + Xki * Yk; + pQ->PutDouble( sumXkiYk, i+1, M+1); + pE->PutDouble( sumXkiYk, i+1); + for (j = i; j < M; j++) + { + const double fVal = pMatX->GetDouble(k,j); + double sumXkiXkj = pQ->GetDouble(j+1, i+1) + + Xki * fVal; + pQ->PutDouble( sumXkiXkj, j+1, i+1); + pQ->PutDouble( sumXkiXkj, i+1, j+1); + } + } + } + } + if ( !Calculate4(_bRKP,pResMat,pQ,bConstant,N,M) ) + return; + + if (bStats) + Calculate(pResMat,pE,pQ,pV,pMatX,bConstant,N,M,nCase); + } + PushMatrix(pResMat); +} + +void ScInterpreter::ScRKP() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRKP" ); + CalulateRGPRKP(TRUE); +} +// ----------------------------------------------------------------------------- +bool ScInterpreter::Calculate4(BOOL _bExp,ScMatrixRef& pResMat,ScMatrixRef& pQ,BOOL bConstant,SCSIZE N,SCSIZE M) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::Calculate4" ); + pQ->PutDouble((double)N, 0, 0); + if (bConstant) + { + SCSIZE S, L; + for (S = 0; S < M+1; S++) + { + SCSIZE i = S; + while (i < M+1 && pQ->GetDouble(i, S) == 0.0) + i++; + if (i >= M+1) + { + PushNoValue(); + return false; + } + double fVal; + for (L = 0; L < M+2; L++) + { + fVal = pQ->GetDouble(S, L); + pQ->PutDouble(pQ->GetDouble(i, L), S, L); + pQ->PutDouble(fVal, i, L); + } + fVal = 1.0/pQ->GetDouble(S, S); + for (L = 0; L < M+2; L++) + pQ->PutDouble(pQ->GetDouble(S, L)*fVal, S, L); + for (i = 0; i < M+1; i++) + { + if (i != S) + { + fVal = -pQ->GetDouble(i, S); + for (L = 0; L < M+2; L++) + pQ->PutDouble( + pQ->GetDouble(i,L)+fVal*pQ->GetDouble(S,L),i,L); + } + } + } + } + else + { + if ( !Calculate3(M,pQ) ) + return false; + + } + for (SCSIZE i = 0; i < M+1; i++) + { + const double d = pQ->GetDouble(M-i,M+1); + pResMat->PutDouble(_bExp ? exp(d) : d, i, 0); + } // for (SCSIZE i = 0; i < M+1; i++) + return true; +} + +ScMatrixRef ScInterpreter::Calculate2(const BOOL bConstant,const SCSIZE M ,const SCSIZE N,ScMatrixRef& pMatX,ScMatrixRef& pMatY,BYTE nCase) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::Calculate2" ); + SCSIZE i, j, k; + ScMatrixRef pQ = GetNewMat(M+1, M+2); + ScMatrixRef pE = GetNewMat(M+2, 1); + pE->PutDouble(0.0, M+1); + pQ->FillDouble(0.0, 0, 0, M, M+1); + if (nCase == 2) + { + for (k = 0; k < N; k++) + { + pE->PutDouble( + pE->GetDouble(M+1)+pMatY->GetDouble(k)*pMatY->GetDouble(k), M+1); + pQ->PutDouble(pQ->GetDouble(0, M+1) + pMatY->GetDouble(k), 0, M+1); + pE->PutDouble(pQ->GetDouble(0, M+1), 0); + for (i = 0; i < M; i++) + { + pQ->PutDouble(pQ->GetDouble(0, i+1)+pMatX->GetDouble(i,k), 0, i+1); + pQ->PutDouble(pQ->GetDouble(0, i+1), i+1, 0); + pQ->PutDouble(pQ->GetDouble(i+1, M+1) + + pMatX->GetDouble(i,k)*pMatY->GetDouble(k), i+1, M+1); + pE->PutDouble(pQ->GetDouble(i+1, M+1), i+1); + for (j = i; j < M; j++) + { + pQ->PutDouble(pQ->GetDouble(j+1, i+1) + + pMatX->GetDouble(i,k)*pMatX->GetDouble(j,k), j+1, i+1); + pQ->PutDouble(pQ->GetDouble(j+1, i+1), i+1, j+1); + } + } + } + } + else + { + for (k = 0; k < N; k++) + { + pE->PutDouble( + pE->GetDouble(M+1)+pMatY->GetDouble(k)*pMatY->GetDouble(k), M+1); + pQ->PutDouble(pQ->GetDouble(0, M+1) + pMatY->GetDouble(k), 0, M+1); + pE->PutDouble(pQ->GetDouble(0, M+1), 0); + for (i = 0; i < M; i++) + { + pQ->PutDouble(pQ->GetDouble(0, i+1)+pMatX->GetDouble(k,i), 0, i+1); + pQ->PutDouble(pQ->GetDouble(0, i+1), i+1, 0); + pQ->PutDouble(pQ->GetDouble(i+1, M+1) + + pMatX->GetDouble(k,i)*pMatY->GetDouble(k), i+1, M+1); + pE->PutDouble(pQ->GetDouble(i+1, M+1), i+1); + for (j = i; j < M; j++) + { + pQ->PutDouble(pQ->GetDouble(j+1, i+1) + + pMatX->GetDouble(k, i)*pMatX->GetDouble(k, j), j+1, i+1); + pQ->PutDouble(pQ->GetDouble(j+1, i+1), i+1, j+1); + } + } + } + } + pQ->PutDouble((double)N, 0, 0); + if (bConstant) + { + SCSIZE S, L; + for (S = 0; S < M+1; S++) + { + i = S; + while (i < M+1 && pQ->GetDouble(i, S) == 0.0) + i++; + if (i >= M+1) + { + PushNoValue(); + return ScMatrixRef(); + } + double fVal; + for (L = 0; L < M+2; L++) + { + fVal = pQ->GetDouble(S, L); + pQ->PutDouble(pQ->GetDouble(i, L), S, L); + pQ->PutDouble(fVal, i, L); + } + fVal = 1.0/pQ->GetDouble(S, S); + for (L = 0; L < M+2; L++) + pQ->PutDouble(pQ->GetDouble(S, L)*fVal, S, L); + for (i = 0; i < M+1; i++) + { + if (i != S) + { + fVal = -pQ->GetDouble(i, S); + for (L = 0; L < M+2; L++) + pQ->PutDouble( + pQ->GetDouble(i,L)+fVal*pQ->GetDouble(S,L),i,L); + } + } + } + } + else + { + if ( !Calculate3(M,pQ) ) + return ScMatrixRef(); + } + return pQ; +} +bool ScInterpreter::Calculate3(const SCSIZE M ,ScMatrixRef& pQ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::Calculate3" ); + SCSIZE S, L; + for (S = 1; S < M+1; S++) + { + SCSIZE i = S; + while (i < M+1 && pQ->GetDouble(i, S) == 0.0) + i++; + if (i >= M+1) + { + PushNoValue(); + return ScMatrixRef(); + } + double fVal; + for (L = 1; L < M+2; L++) + { + fVal = pQ->GetDouble(S, L); + pQ->PutDouble(pQ->GetDouble(i, L), S, L); + pQ->PutDouble(fVal, i, L); + } + fVal = 1.0/pQ->GetDouble(S, S); + for (L = 1; L < M+2; L++) + pQ->PutDouble(pQ->GetDouble(S, L)*fVal, S, L); + for (i = 1; i < M+1; i++) + { + if (i != S) + { + fVal = -pQ->GetDouble(i, S); + for (L = 1; L < M+2; L++) + pQ->PutDouble( + pQ->GetDouble(i,L)+fVal*pQ->GetDouble(S,L),i,L); + } + } + pQ->PutDouble(0.0, 0, M+1); + } // for (S = 1; S < M+1; S++) + return true; +} + +void ScInterpreter::ScTrend() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTrend" ); + CalculateTrendGrowth(FALSE); +} +void ScInterpreter::CalculateTrendGrowth(BOOL _bGrowth) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateTrendGrowth" ); + BYTE nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 1, 4 ) ) + return; + BOOL bConstant; + if (nParamCount == 4) + bConstant = GetBool(); + else + bConstant = TRUE; + ScMatrixRef pMatX; + ScMatrixRef pMatY; + ScMatrixRef pMatNewX; + if (nParamCount >= 3) + pMatNewX = GetMatrix(); + else + pMatNewX = NULL; + if (nParamCount >= 2) + pMatX = GetMatrix(); + else + pMatX = NULL; + pMatY = GetMatrix(); + if (!pMatY) + { + PushIllegalParameter(); + return; + } // if (!pMatY) + + BYTE nCase; // 1 = normal, 2,3 = mehrfach + SCSIZE nCX, nCY; + SCSIZE nRX, nRY; + SCSIZE M = 0, N = 0; + if ( !CheckMatrix(_bGrowth,TRUE,nCase,nCX,nCY,nRX,nRY,M,N,pMatX,pMatY) ) + return; + + + SCSIZE nCXN, nRXN; + SCSIZE nCountXN; + if (!pMatNewX) + { + nCXN = nCX; + nRXN = nRX; + nCountXN = nCXN * nRXN; + pMatNewX = pMatX; + } + else + { + pMatNewX->GetDimensions(nCXN, nRXN); + if ((nCase == 2 && nCX != nCXN) || (nCase == 3 && nRX != nRXN)) + { + PushIllegalArgument(); + return; + } + nCountXN = nCXN * nRXN; + for ( SCSIZE i = 0; i < nCountXN; i++ ) + if (!pMatNewX->IsValue(i)) + { + PushIllegalArgument(); + return; + } + } + ScMatrixRef pResMat; + if (nCase == 1) + { + double fCount = 0.0; + double fSumX = 0.0; + double fSumSqrX = 0.0; + double fSumY = 0.0; + double fSumSqrY = 0.0; + double fSumXY = 0.0; + double fValX, fValY; + SCSIZE i; + for (i = 0; i < nCY; i++) + for (SCSIZE j = 0; j < nRY; j++) + { + fValX = pMatX->GetDouble(i,j); + fValY = pMatY->GetDouble(i,j); + fSumX += fValX; + fSumSqrX += fValX * fValX; + fSumY += fValY; + fSumSqrY += fValY * fValY; + fSumXY += fValX*fValY; + fCount++; + } + if (fCount < 1.0) + { + PushNoValue(); + return; + } + else + { + double f1 = fCount*fSumXY-fSumX*fSumY; + double fX = fCount*fSumSqrX-fSumX*fSumX; + double b, m; + if (bConstant) + { + b = fSumY/fCount - f1/fX*fSumX/fCount; + m = f1/fX; + } + else + { + b = 0.0; + m = fSumXY/fSumSqrX; + } + pResMat = GetNewMat(nCXN, nRXN); + if (!pResMat) + { + PushIllegalArgument(); + return; + } + for (i = 0; i < nCountXN; i++) + { + const double d = pMatNewX->GetDouble(i)*m+b; + pResMat->PutDouble(_bGrowth ? exp(d) : d, i); + } + } + } + else + { + ScMatrixRef pQ = Calculate2(bConstant,M ,N,pMatX,pMatY,nCase); + if ( !pQ.Is() ) + return; + if (nCase == 2) + { + pResMat = GetNewMat(1, nRXN); + if (!pResMat) + { + PushIllegalArgument(); + return; + } + double fVal; + for (SCSIZE i = 0; i < nRXN; i++) + { + fVal = pQ->GetDouble(0, M+1); + for (SCSIZE j = 0; j < M; j++) + fVal += pQ->GetDouble(j+1, M+1)*pMatNewX->GetDouble(j, i); + pResMat->PutDouble(_bGrowth ? exp(fVal) : fVal, i); + } + } + else + { + pResMat = GetNewMat(nCXN, 1); + if (!pResMat) + { + PushIllegalArgument(); + return; + } + double fVal; + for (SCSIZE i = 0; i < nCXN; i++) + { + fVal = pQ->GetDouble(0, M+1); + for (SCSIZE j = 0; j < M; j++) + fVal += pQ->GetDouble(j+1, M+1)*pMatNewX->GetDouble(i, j); + pResMat->PutDouble(_bGrowth ? exp(fVal) : fVal, i); + } + } + } + PushMatrix(pResMat); +} + +void ScInterpreter::ScGrowth() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGrowth" ); + CalculateTrendGrowth(TRUE); +} + +void ScInterpreter::ScMatRef() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMatRef" ); + // Falls Deltarefs drin sind... + Push( (FormulaToken&)*pCur ); + ScAddress aAdr; + PopSingleRef( aAdr ); + ScFormulaCell* pCell = (ScFormulaCell*) GetCell( aAdr ); + if( pCell && pCell->GetCellType() == CELLTYPE_FORMULA ) + { + const ScMatrix* pMat = pCell->GetMatrix(); + if( pMat ) + { + SCSIZE nCols, nRows; + pMat->GetDimensions( nCols, nRows ); + SCSIZE nC = static_cast<SCSIZE>(aPos.Col() - aAdr.Col()); + SCSIZE nR = static_cast<SCSIZE>(aPos.Row() - aAdr.Row()); + if ((nCols <= nC && nCols != 1) || (nRows <= nR && nRows != 1)) + PushNA(); + else + { + ScMatValType nMatValType; + const ScMatrixValue* pMatVal = pMat->Get( nC, nR, nMatValType); + if (ScMatrix::IsNonValueType( nMatValType)) + { + if (ScMatrix::IsEmptyPathType( nMatValType)) + { // result of empty FALSE jump path + nFuncFmtType = NUMBERFORMAT_LOGICAL; + PushInt(0); + } + else if (ScMatrix::IsEmptyType( nMatValType)) + { + // Not inherited (really?) and display as empty string, not 0. + PushTempToken( new ScEmptyCellToken( false, true)); + } + else + PushString( pMatVal->GetString() ); + } + else + { + PushDouble(pMatVal->fVal); // handles DoubleError + pDok->GetNumberFormatInfo( nCurFmtType, nCurFmtIndex, aAdr, pCell ); + nFuncFmtType = nCurFmtType; + nFuncFmtIndex = nCurFmtIndex; + } + } + } + else + { + // If not a result matrix, obtain the cell value. + USHORT nErr = pCell->GetErrCode(); + if (nErr) + PushError( nErr ); + else if( pCell->IsValue() ) + PushDouble( pCell->GetValue() ); + else + { + String aVal; + pCell->GetString( aVal ); + PushString( aVal ); + } + pDok->GetNumberFormatInfo( nCurFmtType, nCurFmtIndex, aAdr, pCell ); + nFuncFmtType = nCurFmtType; + nFuncFmtIndex = nCurFmtIndex; + } + } + else + PushError( errNoRef ); +} + +void ScInterpreter::ScInfo() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScInfo" ); + if( MustHaveParamCount( GetByte(), 1 ) ) + { + String aStr = GetString(); + ScCellKeywordTranslator::transKeyword(aStr, ScGlobal::GetLocale(), ocInfo); + if( aStr.EqualsAscii( "SYSTEM" ) ) + PushString( String( RTL_CONSTASCII_USTRINGPARAM( SC_INFO_OSVERSION ) ) ); + else if( aStr.EqualsAscii( "OSVERSION" ) ) + PushString( String( RTL_CONSTASCII_USTRINGPARAM( "Windows (32-bit) NT 5.01" ) ) ); + else if( aStr.EqualsAscii( "RELEASE" ) ) + PushString( ::utl::Bootstrap::getBuildIdData( ::rtl::OUString() ) ); + else if( aStr.EqualsAscii( "NUMFILE" ) ) + PushDouble( 1 ); + else if( aStr.EqualsAscii( "RECALC" ) ) + PushString( ScGlobal::GetRscString( pDok->GetAutoCalc() ? STR_RECALC_AUTO : STR_RECALC_MANUAL ) ); + else + PushIllegalArgument(); + } +} diff --git a/sc/source/core/tool/interpr6.cxx b/sc/source/core/tool/interpr6.cxx new file mode 100644 index 000000000000..1e3a9a292f16 --- /dev/null +++ b/sc/source/core/tool/interpr6.cxx @@ -0,0 +1,199 @@ +/************************************************************************* + * + * 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 <math.h> + +#include <tools/debug.hxx> +#include <rtl/logfile.hxx> +#include "interpre.hxx" + +double const fHalfMachEps = 0.5 * ::std::numeric_limits<double>::epsilon(); + +// The idea how this group of gamma functions is calculated, is +// based on the Cephes library +// online http://www.moshier.net/#Cephes [called 2008-02] + +/** You must ensure fA>0.0 && fX>0.0 + valid results only if fX > fA+1.0 + uses continued fraction with odd items */ +double ScInterpreter::GetGammaContFraction( double fA, double fX ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetGammaContFraction" ); + + double const fBigInv = ::std::numeric_limits<double>::epsilon(); + double const fBig = 1.0/fBigInv; + double fCount = 0.0; + double fNum = 0.0; // dummy value + double fY = 1.0 - fA; + double fDenom = fX + 2.0-fA; + double fPk = 0.0; // dummy value + double fPkm1 = fX + 1.0; + double fPkm2 = 1.0; + double fQk = 1.0; // dummy value + double fQkm1 = fDenom * fX; + double fQkm2 = fX; + double fApprox = fPkm1/fQkm1; + bool bFinished = false; + double fR = 0.0; // dummy value + do + { + fCount = fCount +1.0; + fY = fY+ 1.0; + fNum = fY * fCount; + fDenom = fDenom +2.0; + fPk = fPkm1 * fDenom - fPkm2 * fNum; + fQk = fQkm1 * fDenom - fQkm2 * fNum; + if (fQk != 0.0) + { + fR = fPk/fQk; + bFinished = (fabs( (fApprox - fR)/fR ) <= fHalfMachEps); + fApprox = fR; + } + fPkm2 = fPkm1; + fPkm1 = fPk; + fQkm2 = fQkm1; + fQkm1 = fQk; + if (fabs(fPk) > fBig) + { + // reduce a fraction does not change the value + fPkm2 = fPkm2 * fBigInv; + fPkm1 = fPkm1 * fBigInv; + fQkm2 = fQkm2 * fBigInv; + fQkm1 = fQkm1 * fBigInv; + } + } while (!bFinished && fCount<10000); + // most iterations, if fX==fAlpha+1.0; approx sqrt(fAlpha) iterations then + if (!bFinished) + { + SetError(errNoConvergence); + } + return fApprox; +} + +/** You must ensure fA>0.0 && fX>0.0 + valid results only if fX <= fA+1.0 + uses power series */ +double ScInterpreter::GetGammaSeries( double fA, double fX ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetGammaSeries" ); + double fDenomfactor = fA; + double fSummand = 1.0/fA; + double fSum = fSummand; + int nCount=1; + do + { + fDenomfactor = fDenomfactor + 1.0; + fSummand = fSummand * fX/fDenomfactor; + fSum = fSum + fSummand; + nCount = nCount+1; + } while ( fSummand/fSum > fHalfMachEps && nCount<=10000); + // large amount of iterations will be carried out for huge fAlpha, even + // if fX <= fAlpha+1.0 + if (nCount>10000) + { + SetError(errNoConvergence); + } + return fSum; +} + +/** You must ensure fA>0.0 && fX>0.0) */ +double ScInterpreter::GetLowRegIGamma( double fA, double fX ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetLowRegIGamma" ); + double fLnFactor = fA * log(fX) - fX - GetLogGamma(fA); + double fFactor = exp(fLnFactor); // Do we need more accuracy than exp(ln()) has? + if (fX>fA+1.0) // includes fX>1.0; 1-GetUpRegIGamma, continued fraction + return 1.0 - fFactor * GetGammaContFraction(fA,fX); + else // fX<=1.0 || fX<=fA+1.0, series + return fFactor * GetGammaSeries(fA,fX); +} + +/** You must ensure fA>0.0 && fX>0.0) */ +double ScInterpreter::GetUpRegIGamma( double fA, double fX ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetUpRegIGamma" ); + + double fLnFactor= fA*log(fX)-fX-GetLogGamma(fA); + double fFactor = exp(fLnFactor); //Do I need more accuracy than exp(ln()) has?; + if (fX>fA+1.0) // includes fX>1.0 + return fFactor * GetGammaContFraction(fA,fX); + else //fX<=1 || fX<=fA+1, 1-GetLowRegIGamma, series + return 1.0 -fFactor * GetGammaSeries(fA,fX); +} + +/** Gamma distribution, probability density function. + fLambda is "scale" parameter + You must ensure fAlpha>0.0 and fLambda>0.0 */ +double ScInterpreter::GetGammaDistPDF( double fX, double fAlpha, double fLambda ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetGammaDistPDF" ); + if (fX <= 0.0) + return 0.0; // see ODFF + else + { + double fXr = fX / fLambda; + // use exp(ln()) only for large arguments because of less accuracy + if (fXr > 1.0) + { + const double fLogDblMax = log( ::std::numeric_limits<double>::max()); + if (log(fXr) * (fAlpha-1.0) < fLogDblMax && fAlpha < fMaxGammaArgument) + { + return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / GetGamma(fAlpha); + } + else + { + return exp( (fAlpha-1.0) * log(fXr) - fXr - log(fLambda) - GetLogGamma(fAlpha)); + } + } + else // fXr near to zero + { + if (fAlpha<fMaxGammaArgument) + { + return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / GetGamma(fAlpha); + } + else + { + return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / exp( GetLogGamma(fAlpha)); + } + } + } +} + +/** Gamma distribution, cumulative distribution function. + fLambda is "scale" parameter + You must ensure fAlpha>0.0 and fLambda>0.0 */ +double ScInterpreter::GetGammaDist( double fX, double fAlpha, double fLambda ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetGammaDist" ); + if (fX <= 0.0) + return 0.0; + else + return GetLowRegIGamma( fAlpha, fX / fLambda); +} diff --git a/sc/source/core/tool/lookupcache.cxx b/sc/source/core/tool/lookupcache.cxx new file mode 100644 index 000000000000..3ba66011d0e4 --- /dev/null +++ b/sc/source/core/tool/lookupcache.cxx @@ -0,0 +1,126 @@ +/************************************************************************* + * + * 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 "lookupcache.hxx" +#include "document.hxx" + +#ifdef erDEBUG +#include <cstdio> +using ::std::fprintf; +static long nCacheCount = 0; +#endif + + +ScLookupCache::ScLookupCache( ScDocument * pDoc, const ScRange & rRange ) : + maRange( rRange), + mpDoc( pDoc) +{ +#ifdef erDEBUG + ++nCacheCount; + fprintf( stderr, "\nctor ScLookupCache %ld: %d, %d, %d, %d, %d, %d; buckets: %lu, size: %lu\n", + nCacheCount, + (int)getRange().aStart.Col(), (int)getRange().aStart.Row(), + (int)getRange().aStart.Tab(), (int)getRange().aEnd.Col(), + (int)getRange().aEnd.Row(), (int)getRange().aEnd.Tab(), + (unsigned long)maQueryMap.bucket_count(), (unsigned long)maQueryMap.size()); +#endif +} + + +ScLookupCache::~ScLookupCache() +{ +#ifdef erDEBUG + fprintf( stderr, "\ndtor ScLookupCache %ld: %d, %d, %d, %d, %d, %d; buckets: %lu, size: %lu\n", + nCacheCount, + (int)getRange().aStart.Col(), (int)getRange().aStart.Row(), + (int)getRange().aStart.Tab(), (int)getRange().aEnd.Col(), + (int)getRange().aEnd.Row(), (int)getRange().aEnd.Tab(), + (unsigned long)maQueryMap.bucket_count(), (unsigned long)maQueryMap.size()); + --nCacheCount; +#endif +} + + +ScLookupCache::Result ScLookupCache::lookup( ScAddress & o_rResultAddress, + const QueryCriteria & rCriteria, const ScAddress & rQueryAddress ) const +{ + QueryMap::const_iterator it( maQueryMap.find( QueryKey( rQueryAddress, + rCriteria.getQueryOp()))); + if (it == maQueryMap.end()) + return NOT_CACHED; + const QueryCriteriaAndResult& rResult = (*it).second; + if (!(rResult.maCriteria == rCriteria)) + return CRITERIA_DIFFERENT; + if (rResult.maAddress.Row() < 0 ) + return NOT_AVAILABLE; + o_rResultAddress = rResult.maAddress; + return FOUND; +} + + +bool ScLookupCache::insert( const ScAddress & rResultAddress, + const QueryCriteria & rCriteria, const ScAddress & rQueryAddress, + const bool bAvailable ) +{ +#ifdef erDEBUG + size_t nBuckets = maQueryMap.bucket_count(); +#endif + QueryKey aKey( rQueryAddress, rCriteria.getQueryOp()); + QueryCriteriaAndResult aResult( rCriteria, rResultAddress); + if (!bAvailable) + aResult.maAddress.SetRow(-1); + bool bInserted = maQueryMap.insert( ::std::pair< const QueryKey, + QueryCriteriaAndResult>( aKey, aResult)).second; +#ifdef erDEBUG + if (nBuckets != maQueryMap.bucket_count()) + { + fprintf( stderr, "\nbuck ScLookupCache: %d, %d, %d, %d, %d, %d; buckets: %lu, size: %lu\n", + (int)getRange().aStart.Col(), (int)getRange().aStart.Row(), + (int)getRange().aStart.Tab(), (int)getRange().aEnd.Col(), + (int)getRange().aEnd.Row(), (int)getRange().aEnd.Tab(), + (unsigned long)maQueryMap.bucket_count(), (unsigned long)maQueryMap.size()); + } +#endif + return bInserted; +} + + +void ScLookupCache::Notify( SvtBroadcaster & /* rBC */ , const SfxHint & rHint ) +{ + if (!mpDoc->IsInDtorClear()) + { + const ScHint* p = PTR_CAST( ScHint, &rHint ); + if (p && (p->GetId() & (SC_HINT_DATACHANGED | SC_HINT_DYING))) + { + mpDoc->RemoveLookupCache( *this); + delete this; + } + } +} diff --git a/sc/source/core/tool/makefile.mk b/sc/source/core/tool/makefile.mk new file mode 100644 index 000000000000..c0258e6f0575 --- /dev/null +++ b/sc/source/core/tool/makefile.mk @@ -0,0 +1,167 @@ +#************************************************************************* +# +# 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=tool + +PROJECTPCH4DLL=TRUE +PROJECTPCH=core_pch +PROJECTPCHSOURCE=..\pch\core_pch + +AUTOSEG=true + +# --- Settings ----------------------------------------------------- + +.INCLUDE : scpre.mk +.INCLUDE : settings.mk +.INCLUDE : sc.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +SLOFILES = \ + $(SLO)$/addincfg.obj \ + $(SLO)$/addincol.obj \ + $(SLO)$/addinhelpid.obj \ + $(SLO)$/addinlis.obj \ + $(SLO)$/address.obj \ + $(SLO)$/adiasync.obj \ + $(SLO)$/appoptio.obj \ + $(SLO)$/autoform.obj \ + $(SLO)$/callform.obj \ + $(SLO)$/cellform.obj \ + $(SLO)$/cellkeytranslator.obj \ + $(SLO)$/charthelper.obj \ + $(SLO)$/chartarr.obj \ + $(SLO)$/chartpos.obj \ + $(SLO)$/chartlis.obj \ + $(SLO)$/chartlock.obj \ + $(SLO)$/chgtrack.obj \ + $(SLO)$/chgviset.obj \ + $(SLO)$/collect.obj \ + $(SLO)$/compiler.obj \ + $(SLO)$/consoli.obj \ + $(SLO)$/dbcolect.obj \ + $(SLO)$/ddelink.obj \ + $(SLO)$/detdata.obj \ + $(SLO)$/detfunc.obj \ + $(SLO)$/docoptio.obj \ + $(SLO)$/doubleref.obj \ + $(SLO)$/editutil.obj \ + $(SLO)$/filtopt.obj \ + $(SLO)$/formulaparserpool.obj \ + $(SLO)$/hints.obj \ + $(SLO)$/inputopt.obj \ + $(SLO)$/interpr1.obj \ + $(SLO)$/interpr2.obj \ + $(SLO)$/interpr3.obj \ + $(SLO)$/interpr4.obj \ + $(SLO)$/interpr5.obj \ + $(SLO)$/interpr6.obj \ + $(SLO)$/lookupcache.obj \ + $(SLO)$/navicfg.obj \ + $(SLO)$/odffmap.obj \ + $(SLO)$/optutil.obj \ + $(SLO)$/parclass.obj \ + $(SLO)$/printopt.obj \ + $(SLO)$/prnsave.obj \ + $(SLO)$/progress.obj \ + $(SLO)$/queryparam.obj \ + $(SLO)$/rangelst.obj \ + $(SLO)$/rangenam.obj \ + $(SLO)$/rangeseq.obj \ + $(SLO)$/rangeutl.obj \ + $(SLO)$/rechead.obj \ + $(SLO)$/refdata.obj \ + $(SLO)$/reffind.obj \ + $(SLO)$/refreshtimer.obj \ + $(SLO)$/reftokenhelper.obj \ + $(SLO)$/refupdat.obj \ + $(SLO)$/scmatrix.obj \ + $(SLO)$/stringutil.obj \ + $(SLO)$/subtotal.obj \ + $(SLO)$/token.obj \ + $(SLO)$/unitconv.obj \ + $(SLO)$/userlist.obj \ + $(SLO)$/viewopti.obj \ + $(SLO)$/zforauto.obj + +EXCEPTIONSFILES= \ + $(SLO)$/addincol.obj \ + $(SLO)$/cellkeytranslator.obj \ + $(SLO)$/charthelper.obj \ + $(SLO)$/chartarr.obj \ + $(SLO)$/chartlis.obj \ + $(SLO)$/chartlock.obj \ + $(SLO)$/chgtrack.obj \ + $(SLO)$/compiler.obj \ + $(SLO)$/doubleref.obj \ + $(SLO)$/formulaparserpool.obj \ + $(SLO)$/interpr1.obj \ + $(SLO)$/interpr2.obj \ + $(SLO)$/interpr3.obj \ + $(SLO)$/interpr4.obj \ + $(SLO)$/interpr5.obj \ + $(SLO)$/lookupcache.obj \ + $(SLO)$/prnsave.obj \ + $(SLO)$/queryparam.obj \ + $(SLO)$/reftokenhelper.obj \ + $(SLO)$/stringutil.obj \ + $(SLO)$/token.obj + +# [kh] POWERPC compiler problem +.IF "$(OS)$(COM)$(CPUNAME)"=="LINUXGCCPOWERPC" +NOOPTFILES= \ + $(SLO)$/subtotal.obj +.ENDIF + +.IF "$(OS)$(COM)$(CPUNAME)"=="LINUXGCCSPARC" +NOOPTFILES= \ + $(SLO)$/interpr2.obj \ + $(SLO)$/interpr4.obj \ + $(SLO)$/token.obj \ + $(SLO)$/chartarr.obj +.ENDIF + +.IF "$(GUI)"=="OS2" +NOOPTFILES= \ + $(SLO)$/interpr6.obj +.ENDIF + +# --- Tagets ------------------------------------------------------- + +.INCLUDE : target.mk + +# avoid quotung problems +$(INCCOM)$/osversiondef.hxx : + @@-$(RM) $@ + @$(TYPE) $(mktmp #define SC_INFO_OSVERSION "$(OS)") > $@ + +$(SLO)$/interpr5.obj : $(INCCOM)$/osversiondef.hxx + diff --git a/sc/source/core/tool/navicfg.cxx b/sc/source/core/tool/navicfg.cxx new file mode 100644 index 000000000000..0991071774ed --- /dev/null +++ b/sc/source/core/tool/navicfg.cxx @@ -0,0 +1,80 @@ +/************************************************************************* + * + * 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 "navicfg.hxx" + +//------------------------------------------------------------------ + +//! #define CFGPATH_NAVIPI "Office.Calc/Navigator" + +//------------------------------------------------------------------ + +ScNavipiCfg::ScNavipiCfg() : +//! ConfigItem( OUString::createFromAscii( CFGPATH_NAVIPI ) ), + nListMode(0), + nDragMode(0), + nRootType(0) +{ +} + +//------------------------------------------------------------------------ + +void ScNavipiCfg::SetListMode(USHORT nNew) +{ + if ( nListMode != nNew ) + { + nListMode = nNew; +//! SetModified(); + } +} + +void ScNavipiCfg::SetDragMode(USHORT nNew) +{ + if ( nDragMode != nNew ) + { + nDragMode = nNew; +//! SetModified(); + } +} + +void ScNavipiCfg::SetRootType(USHORT nNew) +{ + if ( nRootType != nNew ) + { + nRootType = nNew; +//! SetModified(); + } +} + + diff --git a/sc/source/core/tool/odffmap.cxx b/sc/source/core/tool/odffmap.cxx new file mode 100644 index 000000000000..0ffaa4aae025 --- /dev/null +++ b/sc/source/core/tool/odffmap.cxx @@ -0,0 +1,149 @@ +/************************************************************************* + * + * 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 "compiler.hxx" + +// ODFF, English, Programmatical, ODF_11 +ScCompiler::AddInMap ScCompiler::maAddInMap[] = +{ + { "ORG.OPENOFFICE.WEEKS", "WEEKS", false, "com.sun.star.sheet.addin.DateFunctions.getDiffWeeks", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFWEEKS" }, + { "ORG.OPENOFFICE.MONTHS", "MONTHS", false, "com.sun.star.sheet.addin.DateFunctions.getDiffMonths", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFMONTHS" }, + { "ORG.OPENOFFICE.YEARS", "YEARS", false, "com.sun.star.sheet.addin.DateFunctions.getDiffYears", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFYEARS" }, + { "ORG.OPENOFFICE.ISLEAPYEAR", "ISLEAPYEAR", false, "com.sun.star.sheet.addin.DateFunctions.getIsLeapYear", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETISLEAPYEAR" }, + { "ORG.OPENOFFICE.DAYSINMONTH", "DAYSINMONTH", false, "com.sun.star.sheet.addin.DateFunctions.getDaysInMonth", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDAYSINMONTH" }, + { "ORG.OPENOFFICE.DAYSINYEAR", "DAYSINYEAR", false, "com.sun.star.sheet.addin.DateFunctions.getDaysInYear", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDAYSINYEAR" }, + { "ORG.OPENOFFICE.WEEKSINYEAR", "WEEKSINYEAR", false, "com.sun.star.sheet.addin.DateFunctions.getWeeksInYear", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETWEEKSINYEAR" }, + { "ORG.OPENOFFICE.ROT13", "ROT13", false, "com.sun.star.sheet.addin.DateFunctions.getRot13", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETROT13" }, + { "WORKDAY", "WORKDAY", false, "com.sun.star.sheet.addin.Analysis.getWorkday", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETWORKDAY" }, + { "YEARFRAC", "YEARFRAC", false, "com.sun.star.sheet.addin.Analysis.getYearfrac", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETYEARFRAC" }, + { "EDATE", "EDATE", false, "com.sun.star.sheet.addin.Analysis.getEdate", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETEDATE" }, + { "WEEKNUM", "WEEKNUM_ADD", false, "com.sun.star.sheet.addin.Analysis.getWeeknum", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETWEEKNUM" }, + { "EOMONTH", "EOMONTH", false, "com.sun.star.sheet.addin.Analysis.getEomonth", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETEOMONTH" }, + { "NETWORKDAYS", "NETWORKDAYS", false, "com.sun.star.sheet.addin.Analysis.getNetworkdays", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETNETWORKDAYS" }, + { "ISEVEN", "ISEVEN_ADD", true, "com.sun.star.sheet.addin.Analysis.getIseven", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETISEVEN" }, + { "ISODD", "ISODD_ADD", true, "com.sun.star.sheet.addin.Analysis.getIsodd", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETISODD" }, + { "MULTINOMIAL", "MULTINOMIAL", false, "com.sun.star.sheet.addin.Analysis.getMultinomial", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETMULTINOMIAL" }, + { "SERIESSUM", "SERIESSUM", false, "com.sun.star.sheet.addin.Analysis.getSeriessum", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETSERIESSUM" }, + { "QUOTIENT", "QUOTIENT", false, "com.sun.star.sheet.addin.Analysis.getQuotient", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETQUOTIENT" }, + { "MROUND", "MROUND", false, "com.sun.star.sheet.addin.Analysis.getMround", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETMROUND" }, + { "SQRTPI", "SQRTPI", false, "com.sun.star.sheet.addin.Analysis.getSqrtpi", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETSQRTPI" }, + { "RANDBETWEEN", "RANDBETWEEN", false, "com.sun.star.sheet.addin.Analysis.getRandbetween", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETRANDBETWEEN" }, + { "GCD", "GCD_ADD", true, "com.sun.star.sheet.addin.Analysis.getGcd", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETGCD" }, + { "LCM", "LCM_ADD", true, "com.sun.star.sheet.addin.Analysis.getLcm", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETLCM" }, + { "BESSELI", "BESSELI", false, "com.sun.star.sheet.addin.Analysis.getBesseli", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBESSELI" }, + { "BESSELJ", "BESSELJ", false, "com.sun.star.sheet.addin.Analysis.getBesselj", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBESSELJ" }, + { "BESSELK", "BESSELK", false, "com.sun.star.sheet.addin.Analysis.getBesselk", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBESSELK" }, + { "BESSELY", "BESSELY", false, "com.sun.star.sheet.addin.Analysis.getBessely", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBESSELY" }, + { "BIN2OCT", "BIN2OCT", false, "com.sun.star.sheet.addin.Analysis.getBin2Oct", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBIN2OCT" }, + { "BIN2DEC", "BIN2DEC", false, "com.sun.star.sheet.addin.Analysis.getBin2Dec", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBIN2DEC" }, + { "BIN2HEX", "BIN2HEX", false, "com.sun.star.sheet.addin.Analysis.getBin2Hex", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBIN2HEX" }, + { "OCT2BIN", "OCT2BIN", false, "com.sun.star.sheet.addin.Analysis.getOct2Bin", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETOCT2BIN" }, + { "OCT2DEC", "OCT2DEC", false, "com.sun.star.sheet.addin.Analysis.getOct2Dec", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETOCT2DEC" }, + { "OCT2HEX", "OCT2HEX", false, "com.sun.star.sheet.addin.Analysis.getOct2Hex", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETOCT2HEX" }, + { "DEC2BIN", "DEC2BIN", false, "com.sun.star.sheet.addin.Analysis.getDec2Bin", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDEC2BIN" }, + { "DEC2OCT", "DEC2OCT", false, "com.sun.star.sheet.addin.Analysis.getDec2Oct", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDEC2OCT" }, + { "DEC2HEX", "DEC2HEX", false, "com.sun.star.sheet.addin.Analysis.getDec2Hex", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDEC2HEX" }, + { "HEX2BIN", "HEX2BIN", false, "com.sun.star.sheet.addin.Analysis.getHex2Bin", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETHEX2BIN" }, + { "HEX2DEC", "HEX2DEC", false, "com.sun.star.sheet.addin.Analysis.getHex2Dec", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETHEX2DEC" }, + { "HEX2OCT", "HEX2OCT", false, "com.sun.star.sheet.addin.Analysis.getHex2Oct", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETHEX2OCT" }, + { "DELTA", "DELTA", false, "com.sun.star.sheet.addin.Analysis.getDelta", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDELTA" }, + { "ERF", "ERF", false, "com.sun.star.sheet.addin.Analysis.getErf", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETERF" }, + { "ERFC", "ERFC", false, "com.sun.star.sheet.addin.Analysis.getErfc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETERFC" }, + { "GESTEP", "GESTEP", false, "com.sun.star.sheet.addin.Analysis.getGestep", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETGESTEP" }, + { "FACTDOUBLE", "FACTDOUBLE", false, "com.sun.star.sheet.addin.Analysis.getFactdouble", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETFACTDOUBLE" }, + { "IMABS", "IMABS", false, "com.sun.star.sheet.addin.Analysis.getImabs", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMABS" }, + { "IMAGINARY", "IMAGINARY", false, "com.sun.star.sheet.addin.Analysis.getImaginary", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMAGINARY" }, + { "IMPOWER", "IMPOWER", false, "com.sun.star.sheet.addin.Analysis.getImpower", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMPOWER" }, + { "IMARGUMENT", "IMARGUMENT", false, "com.sun.star.sheet.addin.Analysis.getImargument", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMARGUMENT" }, + { "IMCOS", "IMCOS", false, "com.sun.star.sheet.addin.Analysis.getImcos", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMCOS" }, + { "IMDIV", "IMDIV", false, "com.sun.star.sheet.addin.Analysis.getImdiv", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMDIV" }, + { "IMEXP", "IMEXP", false, "com.sun.star.sheet.addin.Analysis.getImexp", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMEXP" }, + { "IMCONJUGATE", "IMCONJUGATE", false, "com.sun.star.sheet.addin.Analysis.getImconjugate", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMCONJUGATE" }, + { "IMLN", "IMLN", false, "com.sun.star.sheet.addin.Analysis.getImln", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMLN" }, + { "IMLOG10", "IMLOG10", false, "com.sun.star.sheet.addin.Analysis.getImlog10", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMLOG10" }, + { "IMLOG2", "IMLOG2", false, "com.sun.star.sheet.addin.Analysis.getImlog2", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMLOG2" }, + { "IMPRODUCT", "IMPRODUCT", false, "com.sun.star.sheet.addin.Analysis.getImproduct", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMPRODUCT" }, + { "IMREAL", "IMREAL", false, "com.sun.star.sheet.addin.Analysis.getImreal", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMREAL" }, + { "IMSIN", "IMSIN", false, "com.sun.star.sheet.addin.Analysis.getImsin", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSIN" }, + { "IMSUB", "IMSUB", false, "com.sun.star.sheet.addin.Analysis.getImsub", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSUB" }, + { "IMSUM", "IMSUM", false, "com.sun.star.sheet.addin.Analysis.getImsum", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSUM" }, + { "IMSQRT", "IMSQRT", false, "com.sun.star.sheet.addin.Analysis.getImsqrt", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSQRT" }, + { "COMPLEX", "COMPLEX", false, "com.sun.star.sheet.addin.Analysis.getComplex", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOMPLEX" }, + { "CONVERT", "CONVERT_ADD", false, "com.sun.star.sheet.addin.Analysis.getConvert", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCONVERT" }, + { "AMORDEGRC", "AMORDEGRC", false, "com.sun.star.sheet.addin.Analysis.getAmordegrc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETAMORDEGRC" }, + { "AMORLINC", "AMORLINC", false, "com.sun.star.sheet.addin.Analysis.getAmorlinc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETAMORLINC" }, + { "ACCRINT", "ACCRINT", false, "com.sun.star.sheet.addin.Analysis.getAccrint", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETACCRINT" }, + { "ACCRINTM", "ACCRINTM", false, "com.sun.star.sheet.addin.Analysis.getAccrintm", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETACCRINTM" }, + { "RECEIVED", "RECEIVED", false, "com.sun.star.sheet.addin.Analysis.getReceived", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETRECEIVED" }, + { "DISC", "DISC", false, "com.sun.star.sheet.addin.Analysis.getDisc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDISC" }, + { "DURATION", "DURATION_ADD", false, "com.sun.star.sheet.addin.Analysis.getDuration", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDURATION" }, + { "EFFECT", "EFFECT_ADD", true, "com.sun.star.sheet.addin.Analysis.getEffect", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETEFFECT" }, + { "CUMPRINC", "CUMPRINC_ADD", true, "com.sun.star.sheet.addin.Analysis.getCumprinc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCUMPRINC" }, + { "CUMIPMT", "CUMIPMT_ADD", true, "com.sun.star.sheet.addin.Analysis.getCumipmt", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCUMIPMT" }, + { "PRICE", "PRICE", false, "com.sun.star.sheet.addin.Analysis.getPrice", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETPRICE" }, + { "PRICEDISC", "PRICEDISC", false, "com.sun.star.sheet.addin.Analysis.getPricedisc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETPRICEDISC" }, + { "PRICEMAT", "PRICEMAT", false, "com.sun.star.sheet.addin.Analysis.getPricemat", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETPRICEMAT" }, + { "MDURATION", "MDURATION", false, "com.sun.star.sheet.addin.Analysis.getMduration", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETMDURATION" }, + { "NOMINAL", "NOMINAL_ADD", true, "com.sun.star.sheet.addin.Analysis.getNominal", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETNOMINAL" }, + { "DOLLARFR", "DOLLARFR", false, "com.sun.star.sheet.addin.Analysis.getDollarfr", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDOLLARFR" }, + { "DOLLARDE", "DOLLARDE", false, "com.sun.star.sheet.addin.Analysis.getDollarde", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDOLLARDE" }, + { "YIELD", "YIELD", false, "com.sun.star.sheet.addin.Analysis.getYield", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETYIELD" }, + { "YIELDDISC", "YIELDDISC", false, "com.sun.star.sheet.addin.Analysis.getYielddisc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETYIELDDISC" }, + { "YIELDMAT", "YIELDMAT", false, "com.sun.star.sheet.addin.Analysis.getYieldmat", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETYIELDMAT" }, + { "TBILLEQ", "TBILLEQ", false, "com.sun.star.sheet.addin.Analysis.getTbilleq", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETTBILLEQ" }, + { "TBILLPRICE", "TBILLPRICE", false, "com.sun.star.sheet.addin.Analysis.getTbillprice", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETTBILLPRICE" }, + { "TBILLYIELD", "TBILLYIELD", false, "com.sun.star.sheet.addin.Analysis.getTbillyield", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETTBILLYIELD" }, + { "ODDFPRICE", "ODDFPRICE", false, "com.sun.star.sheet.addin.Analysis.getOddfprice", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETODDFPRICE" }, + { "ODDFYIELD", "ODDFYIELD", false, "com.sun.star.sheet.addin.Analysis.getOddfyield", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETODDFYIELD" }, + { "ODDLPRICE", "ODDLPRICE", false, "com.sun.star.sheet.addin.Analysis.getOddlprice", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETODDLPRICE" }, + { "ODDLYIELD", "ODDLYIELD", false, "com.sun.star.sheet.addin.Analysis.getOddlyield", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETODDLYIELD" }, + { "XIRR", "XIRR", false, "com.sun.star.sheet.addin.Analysis.getXirr", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETXIRR" }, + { "XNPV", "XNPV", false, "com.sun.star.sheet.addin.Analysis.getXnpv", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETXNPV" }, + { "INTRATE", "INTRATE", false, "com.sun.star.sheet.addin.Analysis.getIntrate", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETINTRATE" }, + { "COUPNCD", "COUPNCD", false, "com.sun.star.sheet.addin.Analysis.getCoupncd", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPNCD" }, + { "COUPDAYS", "COUPDAYS", false, "com.sun.star.sheet.addin.Analysis.getCoupdays", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPDAYS" }, + { "COUPDAYSNC", "COUPDAYSNC", false, "com.sun.star.sheet.addin.Analysis.getCoupdaysnc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPDAYSNC" }, + { "COUPDAYBS", "COUPDAYBS", false, "com.sun.star.sheet.addin.Analysis.getCoupdaybs", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPDAYBS" }, + { "COUPPCD", "COUPPCD", false, "com.sun.star.sheet.addin.Analysis.getCouppcd", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPPCD" }, + { "COUPNUM", "COUPNUM", false, "com.sun.star.sheet.addin.Analysis.getCoupnum", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPNUM" }, + { "FVSCHEDULE", "FVSCHEDULE", false, "com.sun.star.sheet.addin.Analysis.getFvschedule", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETFVSCHEDULE" }, +}; + +// static +const ScCompiler::AddInMap* ScCompiler::GetAddInMap() +{ + return maAddInMap; +} + +// static +size_t ScCompiler::GetAddInMapCount() +{ + return sizeof(maAddInMap)/sizeof(maAddInMap[0]); +} diff --git a/sc/source/core/tool/optutil.cxx b/sc/source/core/tool/optutil.cxx new file mode 100644 index 000000000000..dc63c7819eff --- /dev/null +++ b/sc/source/core/tool/optutil.cxx @@ -0,0 +1,79 @@ +/************************************************************************* + * + * 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 <vcl/svapp.hxx> + +#include "optutil.hxx" +#include "global.hxx" // for pSysLocale +#include <unotools/syslocale.hxx> + +//------------------------------------------------------------------ + +// static +BOOL ScOptionsUtil::IsMetricSystem() +{ + //! which language should be used here - system language or installed office language? + +// MeasurementSystem eSys = Application::GetAppInternational().GetMeasurementSystem(); + MeasurementSystem eSys = ScGlobal::pLocaleData->getMeasurementSystemEnum(); + + return ( eSys == MEASURE_METRIC ); +} + +//------------------------------------------------------------------ + +ScLinkConfigItem::ScLinkConfigItem( const rtl::OUString& rSubTree ) : + ConfigItem( rSubTree ) +{ +} + +ScLinkConfigItem::ScLinkConfigItem( const rtl::OUString& rSubTree, sal_Int16 nMode ) : + ConfigItem( rSubTree, nMode ) +{ +} + +void ScLinkConfigItem::SetCommitLink( const Link& rLink ) +{ + aCommitLink = rLink; +} + +void ScLinkConfigItem::Notify( const com::sun::star::uno::Sequence<rtl::OUString>& /* aPropertyNames */ ) +{ + //! not implemented yet... +} + +void ScLinkConfigItem::Commit() +{ + aCommitLink.Call( this ); +} + + diff --git a/sc/source/core/tool/parclass.cxx b/sc/source/core/tool/parclass.cxx new file mode 100644 index 000000000000..a863bc0a68c2 --- /dev/null +++ b/sc/source/core/tool/parclass.cxx @@ -0,0 +1,578 @@ +/************************************************************************* + * + * 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 "parclass.hxx" +#include "token.hxx" +#include "global.hxx" +#include "callform.hxx" +#include "addincol.hxx" +#include "funcdesc.hxx" +#include <unotools/charclass.hxx> +#include <tools/debug.hxx> +#include <string.h> + +#if OSL_DEBUG_LEVEL > 1 +// the documentation thingy +#include <stdio.h> +#include <com/sun/star/sheet/FormulaLanguage.hpp> +#include "compiler.hxx" +#include "sc.hrc" // VAR_ARGS +#endif + + +/* Following assumptions are made: + * - OpCodes not specified at all will have at least one and only parameters of + * type Value, no check is done on the count of parameters => no Bounds type + * is returned. + * - For OpCodes with a variable number of parameters the type of the last + * parameter specified determines the type of all following parameters. + */ + +const ScParameterClassification::RawData ScParameterClassification::pRawData[] = +{ + // IF() and CHOOSE() are somewhat special, since the ScJumpMatrix is + // created inside those functions and ConvertMatrixParameters() is not + // called for them. + { ocIf, {{ Array, Reference, Reference }, false }}, + { ocChose, {{ Array, Reference }, true }}, + // Other specials. + { ocOpen, {{ Bounds }, false }}, + { ocClose, {{ Bounds }, false }}, + { ocSep, {{ Bounds }, false }}, + { ocNoName, {{ Bounds }, false }}, + { ocErrCell, {{ Bounds }, false }}, + { ocStop, {{ Bounds }, false }}, + { ocUnion, {{ Reference, Reference }, false }}, + { ocRange, {{ Reference, Reference }, false }}, + // Functions with Value parameters only but not in resource. + { ocBackSolver, {{ Value, Value, Value }, false }}, + { ocTableOp, {{ Value, Value, Value, Value, Value }, false }}, + // Operators and functions. + { ocAdd, {{ Array, Array }, false }}, + { ocAmpersand, {{ Array, Array }, false }}, + { ocAnd, {{ Reference }, true }}, + { ocAreas, {{ Reference }, false }}, + { ocAveDev, {{ Reference }, true }}, + { ocAverage, {{ Reference }, true }}, + { ocAverageA, {{ Reference }, true }}, + { ocCell, {{ Value, Reference }, false }}, + { ocColumn, {{ Reference }, false }}, + { ocColumns, {{ Reference }, true }}, + { ocCorrel, {{ ForceArray, ForceArray }, false }}, + { ocCount, {{ Reference }, true }}, + { ocCount2, {{ Reference }, true }}, + { ocCountEmptyCells, {{ Reference }, false }}, + { ocCountIf, {{ Reference, Value }, false }}, + { ocCovar, {{ ForceArray, ForceArray }, false }}, + { ocDBAverage, {{ Reference, Reference, Reference }, false }}, + { ocDBCount, {{ Reference, Reference, Reference }, false }}, + { ocDBCount2, {{ Reference, Reference, Reference }, false }}, + { ocDBGet, {{ Reference, Reference, Reference }, false }}, + { ocDBMax, {{ Reference, Reference, Reference }, false }}, + { ocDBMin, {{ Reference, Reference, Reference }, false }}, + { ocDBProduct, {{ Reference, Reference, Reference }, false }}, + { ocDBStdDev, {{ Reference, Reference, Reference }, false }}, + { ocDBStdDevP, {{ Reference, Reference, Reference }, false }}, + { ocDBSum, {{ Reference, Reference, Reference }, false }}, + { ocDBVar, {{ Reference, Reference, Reference }, false }}, + { ocDBVarP, {{ Reference, Reference, Reference }, false }}, + { ocDevSq, {{ Reference }, true }}, + { ocDiv, {{ Array, Array }, false }}, + { ocEqual, {{ Array, Array }, false }}, + { ocForecast, {{ Value, ForceArray, ForceArray }, false }}, + { ocFrequency, {{ Reference, Reference }, false }}, + { ocFTest, {{ ForceArray, ForceArray }, false }}, + { ocGeoMean, {{ Reference }, true }}, + { ocGCD, {{ Reference }, true }}, + { ocGreater, {{ Array, Array }, false }}, + { ocGreaterEqual, {{ Array, Array }, false }}, + { ocGrowth, {{ Reference, Reference, Reference, Value }, false }}, + { ocHarMean, {{ Reference }, true }}, + { ocHLookup, {{ Value, Reference, Value, Value }, false }}, + { ocIRR, {{ Reference, Value }, false }}, + { ocIndex, {{ Reference, Value, Value, Value }, false }}, + { ocIntercept, {{ ForceArray, ForceArray }, false }}, + { ocIntersect, {{ Reference, Reference }, false }}, + { ocIsRef, {{ Reference }, false }}, + { ocLCM, {{ Reference }, true }}, + { ocKurt, {{ Reference }, true }}, + { ocLarge, {{ Reference, Value }, false }}, + { ocLess, {{ Array, Array }, false }}, + { ocLessEqual, {{ Array, Array }, false }}, + { ocLookup, {{ Value, ReferenceOrForceArray, ReferenceOrForceArray }, false }}, + { ocMatch, {{ Value, Reference, Reference }, false }}, + { ocMatDet, {{ ForceArray }, false }}, + { ocMatInv, {{ ForceArray }, false }}, + { ocMatMult, {{ ForceArray, ForceArray }, false }}, + { ocMatTrans, {{ Array }, false }}, // strange, but Xcl doesn't force MatTrans array + { ocMatValue, {{ Reference, Value, Value }, false }}, + { ocMax, {{ Reference }, true }}, + { ocMaxA, {{ Reference }, true }}, + { ocMedian, {{ Reference }, true }}, + { ocMin, {{ Reference }, true }}, + { ocMinA, {{ Reference }, true }}, + { ocMIRR, {{ Reference, Value, Value }, false }}, + { ocModalValue, {{ ForceArray }, true }}, + { ocMul, {{ Array, Array }, false }}, + { ocMultiArea, {{ Reference }, true }}, + { ocN, {{ Reference }, false }}, + { ocNPV, {{ Value, Reference }, true }}, + { ocNeg, {{ Array }, false }}, + { ocNegSub, {{ Array }, false }}, + { ocNot, {{ Array }, false }}, + { ocNotEqual, {{ Array, Array }, false }}, + { ocOffset, {{ Reference, Value, Value, Value, Value }, false }}, + { ocOr, {{ Reference }, true }}, + { ocPearson, {{ ForceArray, ForceArray }, false }}, + { ocPercentile, {{ Reference, Value }, false }}, + { ocPercentrank, {{ Reference, Value }, false }}, + { ocPow, {{ Array, Array }, false }}, + { ocPower, {{ Array, Array }, false }}, + { ocProb, {{ ForceArray, ForceArray, Value, Value }, false }}, + { ocProduct, {{ Reference }, true }}, + { ocQuartile, {{ Reference, Value }, false }}, + { ocRank, {{ Value, Reference, Value }, false }}, + { ocRGP, {{ Reference, Reference, Value, Value }, false }}, + { ocRKP, {{ Reference, Reference, Value, Value }, false }}, + { ocRow, {{ Reference }, false }}, + { ocRows, {{ Reference }, true }}, + { ocRSQ, {{ ForceArray, ForceArray }, false }}, + { ocSchiefe, {{ Reference }, true }}, + { ocSlope, {{ ForceArray, ForceArray }, false }}, + { ocSmall, {{ Reference, Value }, false }}, + { ocStDev, {{ Reference }, true }}, + { ocStDevA, {{ Reference }, true }}, + { ocStDevP, {{ Reference }, true }}, + { ocStDevPA, {{ Reference }, true }}, + { ocSTEYX, {{ ForceArray, ForceArray }, false }}, + { ocSub, {{ Array, Array }, false }}, + { ocSubTotal, {{ Value, Reference }, true }}, + { ocSum, {{ Reference }, true }}, + { ocSumIf, {{ Reference, Value, Reference }, false }}, + { ocSumProduct, {{ ForceArray }, true }}, + { ocSumSQ, {{ Reference }, true }}, + { ocSumX2MY2, {{ ForceArray, ForceArray }, false }}, + { ocSumX2DY2, {{ ForceArray, ForceArray }, false }}, + { ocSumXMY2, {{ ForceArray, ForceArray }, false }}, + { ocTable, {{ Reference }, false }}, + { ocTables, {{ Reference }, true }}, + { ocTrend, {{ Reference, Reference, Reference, Value }, false }}, + { ocTrimMean, {{ Reference, Value }, false }}, + { ocTTest, {{ ForceArray, ForceArray, Value, Value }, false }}, + { ocVar, {{ Reference }, true }}, + { ocVarA, {{ Reference }, true }}, + { ocVarP, {{ Reference }, true }}, + { ocVarPA, {{ Reference }, true }}, + { ocVLookup, {{ Value, Reference, Value, Value }, false }}, + { ocZTest, {{ Reference, Value, Value }, false }}, + // Excel doubts: + // ocT: Excel says (and handles) Reference, error? This means no position + // dependent SingleRef if DoubleRef, and no array calculation, just the + // upper left corner. We never did that. + { ocT, {{ Value }, false }}, + // The stopper. + { ocNone, {{ Bounds }, false } } +}; + +ScParameterClassification::RunData * ScParameterClassification::pData = NULL; + + +void ScParameterClassification::Init() +{ + if ( pData ) + return; + pData = new RunData[ SC_OPCODE_LAST_OPCODE_ID + 1 ]; + memset( pData, 0, sizeof(RunData) * (SC_OPCODE_LAST_OPCODE_ID + 1)); + + // init from specified static data above + for ( size_t i=0; i < sizeof(pRawData) / sizeof(RawData); ++i ) + { + const RawData* pRaw = &pRawData[i]; + if ( pRaw->eOp > SC_OPCODE_LAST_OPCODE_ID ) + { + DBG_ASSERT( pRaw->eOp == ocNone, "RawData OpCode error"); + } + else + { + RunData* pRun = &pData[ pRaw->eOp ]; +#ifdef DBG_UTIL + if ( pRun->aData.nParam[0] != Unknown ) + { + DBG_ERROR1( "already assigned: %d", pRaw->eOp); + } +#endif + memcpy( &(pRun->aData), &(pRaw->aData), sizeof(CommonData)); + // fill 0-initialized fields with real values + if ( pRun->aData.bRepeatLast ) + { + Type eLast = Unknown; + for ( size_t j=0; j < CommonData::nMaxParams; ++j ) + { + if ( pRun->aData.nParam[j] ) + { + eLast = pRun->aData.nParam[j]; + pRun->nMinParams = sal::static_int_cast<BYTE>( j+1 ); + } + else + pRun->aData.nParam[j] = eLast; + } + } + else + { + for ( size_t j=0; j < CommonData::nMaxParams; ++j ) + { + if ( !pRun->aData.nParam[j] ) + { + if ( j == 0 || pRun->aData.nParam[j-1] != Bounds ) + pRun->nMinParams = sal::static_int_cast<BYTE>( j ); + pRun->aData.nParam[j] = Bounds; + } + } + if ( !pRun->nMinParams && + pRun->aData.nParam[CommonData::nMaxParams-1] != Bounds) + pRun->nMinParams = CommonData::nMaxParams; + } + for ( size_t j=0; j < CommonData::nMaxParams; ++j ) + { + if ( pRun->aData.nParam[j] == ForceArray || pRun->aData.nParam[j] == ReferenceOrForceArray ) + { + pRun->bHasForceArray = true; + break; // for + } + } + } + } + +#if OSL_DEBUG_LEVEL > 1 + GenerateDocumentation(); +#endif +} + + +void ScParameterClassification::Exit() +{ + delete [] pData; + pData = NULL; +} + + +ScParameterClassification::Type ScParameterClassification::GetParameterType( + const formula::FormulaToken* pToken, USHORT nParameter) +{ + OpCode eOp = pToken->GetOpCode(); + switch ( eOp ) + { + case ocExternal: + return GetExternalParameterType( pToken, nParameter); + //break; + case ocMacro: + return Reference; + //break; + default: + { + // added to avoid warnings + } + } + if ( 0 <= (short)eOp && eOp <= SC_OPCODE_LAST_OPCODE_ID ) + { + if ( nParameter < CommonData::nMaxParams ) + { + Type eT = pData[eOp].aData.nParam[nParameter]; + return eT == Unknown ? Value : eT; + } + else if ( pData[eOp].aData.bRepeatLast ) + return pData[eOp].aData.nParam[CommonData::nMaxParams-1]; + else + return Bounds; + } + return Unknown; +} + + +ScParameterClassification::Type +ScParameterClassification::GetExternalParameterType( const formula::FormulaToken* pToken, + USHORT nParameter) +{ + Type eRet = Unknown; + // similar to ScInterpreter::ScExternal() + USHORT nIndex; + String aUnoName; + String aFuncName( ScGlobal::pCharClass->upper( pToken->GetExternal())); + if ( ScGlobal::GetFuncCollection()->SearchFunc( aFuncName, nIndex) ) + { + FuncData* pFuncData = (FuncData*)ScGlobal::GetFuncCollection()->At( + nIndex); + if ( nParameter >= pFuncData->GetParamCount() ) + eRet = Bounds; + else + { + switch ( pFuncData->GetParamType( nParameter) ) + { + case PTR_DOUBLE: + case PTR_STRING: + eRet = Value; + break; + default: + eRet = Reference; + // also array types are created using an area reference + } + } + } + else if ( (aUnoName = ScGlobal::GetAddInCollection()->FindFunction( + aFuncName, FALSE)).Len() ) + { + // the relevant parts of ScUnoAddInCall without having to create one + const ScUnoAddInFuncData* pFuncData = + ScGlobal::GetAddInCollection()->GetFuncData( aUnoName, true ); // need fully initialized data + if ( pFuncData ) + { + long nCount = pFuncData->GetArgumentCount(); + if ( nCount <= 0 ) + eRet = Bounds; + else + { + const ScAddInArgDesc* pArgs = pFuncData->GetArguments(); + if ( nParameter >= nCount && + pArgs[nCount-1].eType == SC_ADDINARG_VARARGS ) + eRet = Value; + // last arg is sequence, optional "any"s, we simply can't + // determine the type + if ( eRet == Unknown ) + { + if ( nParameter >= nCount ) + eRet = Bounds; + else + { + switch ( pArgs[nParameter].eType ) + { + case SC_ADDINARG_INTEGER: + case SC_ADDINARG_DOUBLE: + case SC_ADDINARG_STRING: + eRet = Value; + break; + default: + eRet = Reference; + } + } + } + } + } + } + return eRet; +} + +//----------------------------------------------------------------------------- + +#if OSL_DEBUG_LEVEL > 1 + +// add remaining functions, all Value parameters +void ScParameterClassification::MergeArgumentsFromFunctionResource() +{ + ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList(); + for ( const ScFuncDesc* pDesc = pFuncList->First(); pDesc; + pDesc = pFuncList->Next() ) + { + if ( pDesc->nFIndex > SC_OPCODE_LAST_OPCODE_ID || + pData[pDesc->nFIndex].aData.nParam[0] != Unknown ) + continue; // not an internal opcode or already done + + RunData* pRun = &pData[ pDesc->nFIndex ]; + USHORT nArgs = pDesc->GetSuppressedArgCount(); + if ( nArgs >= VAR_ARGS ) + { + nArgs -= VAR_ARGS - 1; + pRun->aData.bRepeatLast = true; + } + if ( nArgs > CommonData::nMaxParams ) + { + DBG_ERROR2( "ScParameterClassification::Init: too many arguments in listed function: %s: %d", + ByteString( *(pDesc->pFuncName), + RTL_TEXTENCODING_UTF8).GetBuffer(), nArgs); + nArgs = CommonData::nMaxParams; + pRun->aData.bRepeatLast = true; + } + pRun->nMinParams = static_cast< BYTE >( nArgs ); + for ( size_t j=0; j < nArgs; ++j ) + { + pRun->aData.nParam[j] = Value; + } + if ( pRun->aData.bRepeatLast ) + { + for ( size_t j = nArgs; j < CommonData::nMaxParams; ++j ) + { + pRun->aData.nParam[j] = Value; + } + } + else + { + for ( size_t j = nArgs; j < CommonData::nMaxParams; ++j ) + { + pRun->aData.nParam[j] = Bounds; + } + } + } +} + + +void ScParameterClassification::GenerateDocumentation() +{ + static const sal_Char aEnvVarName[] = "OOO_CALC_GENPARCLASSDOC"; + if ( !getenv( aEnvVarName) ) + return; + MergeArgumentsFromFunctionResource(); + ScAddress aAddress; + ScCompiler aComp(NULL,aAddress); + ScCompiler::OpCodeMapPtr xMap( aComp.GetOpCodeMap(::com::sun::star::sheet::FormulaLanguage::ENGLISH)); + if (!xMap) + return; + fflush( stderr); + size_t nCount = xMap->getSymbolCount(); + for ( size_t i=0; i<nCount; ++i ) + { + OpCode eOp = OpCode(i); + if ( xMap->getSymbol(eOp).Len() ) + { + fprintf( stdout, "%s: ", aEnvVarName); + ByteString aStr( xMap->getSymbol(eOp), RTL_TEXTENCODING_UTF8); + aStr += "("; + formula::FormulaByteToken aToken( eOp); + BYTE nParams = GetMinimumParameters( eOp); + // preset parameter count according to opcode value, with some + // special handling + if ( eOp < SC_OPCODE_STOP_DIV ) + { + switch ( eOp ) + { + case ocIf: + aToken.SetByte(3); + break; + case ocChose: + aToken.SetByte(2); + break; + case ocPercentSign: + aToken.SetByte(1); + break; + default:; + } + } + else if ( eOp < SC_OPCODE_STOP_ERRORS ) + aToken.SetByte(0); + else if ( eOp < SC_OPCODE_STOP_BIN_OP ) + { + switch ( eOp ) + { + case ocAnd: + case ocOr: + aToken.SetByte(1); // (r1)AND(r2) --> AND( r1, ...) + break; + default: + aToken.SetByte(2); + } + } + else if ( eOp < SC_OPCODE_STOP_UN_OP ) + aToken.SetByte(1); + else if ( eOp < SC_OPCODE_STOP_NO_PAR ) + aToken.SetByte(0); + else if ( eOp < SC_OPCODE_STOP_1_PAR ) + aToken.SetByte(1); + else + aToken.SetByte( nParams); + // compare (this is a mere test for opcode order Div, BinOp, UnOp, + // NoPar, 1Par, ...) and override parameter count with + // classification + if ( nParams != aToken.GetByte() ) + fprintf( stdout, "(parameter count differs, token Byte: %d classification: %d) ", + aToken.GetByte(), nParams); + aToken.SetByte( nParams); + if ( nParams != aToken.GetParamCount() ) + fprintf( stdout, "(parameter count differs, token ParamCount: %d classification: %d) ", + aToken.GetParamCount(), nParams); + for ( USHORT j=0; j < nParams; ++j ) + { + if ( j > 0 ) + aStr += ","; + Type eType = GetParameterType( &aToken, j); + switch ( eType ) + { + case Value : + aStr += " Value"; + break; + case Reference : + aStr += " Reference"; + break; + case Array : + aStr += " Array"; + break; + case ForceArray : + aStr += " ForceArray"; + break; + case ReferenceOrForceArray : + aStr += " ReferenceOrForceArray"; + break; + case Bounds : + aStr += " (Bounds, classification error?)"; + break; + default: + aStr += " (???, classification error?)"; + } + } + if ( HasRepeatParameters( eOp) ) + aStr += ", ..."; + if ( nParams ) + aStr += " "; + aStr += ")"; + switch ( eOp ) + { + case ocZGZ: + aStr += " // RRI in English resource, but ZGZ in English-only section"; + break; + case ocMultiArea: + aStr += " // e.g. combined first parameter of INDEX() function, not a real function"; + break; + case ocBackSolver: + aStr += " // goal seek via menu, not a real function"; + break; + case ocTableOp: + aStr += " // MULTIPLE.OPERATIONS in English resource, but TABLE in English-only section"; + break; + case ocNoName: + aStr += " // error function, not a real function"; + break; + default:; + } + fprintf( stdout, "%s\n", aStr.GetBuffer()); + } + } + fflush( stdout); +} + +#endif // OSL_DEBUG_LEVEL + diff --git a/sc/source/core/tool/printopt.cxx b/sc/source/core/tool/printopt.cxx new file mode 100644 index 000000000000..c701c558310b --- /dev/null +++ b/sc/source/core/tool/printopt.cxx @@ -0,0 +1,211 @@ +/************************************************************************* + * + * 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/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include "printopt.hxx" +#include "miscuno.hxx" + +using namespace utl; +using namespace rtl; +using namespace com::sun::star::uno; + +// ----------------------------------------------------------------------- + +TYPEINIT1(ScTpPrintItem, SfxPoolItem); + +// ----------------------------------------------------------------------- + +ScPrintOptions::ScPrintOptions() +{ + SetDefaults(); +} + +ScPrintOptions::ScPrintOptions( const ScPrintOptions& rCpy ) : + bSkipEmpty( rCpy.bSkipEmpty ), + bAllSheets( rCpy.bAllSheets ) +{ +} + +ScPrintOptions::~ScPrintOptions() +{ +} + +void ScPrintOptions::SetDefaults() +{ + bSkipEmpty = TRUE; + bAllSheets = FALSE; +} + +const ScPrintOptions& ScPrintOptions::operator=( const ScPrintOptions& rCpy ) +{ + bSkipEmpty = rCpy.bSkipEmpty; + bAllSheets = rCpy.bAllSheets; + return *this; +} + +int ScPrintOptions::operator==( const ScPrintOptions& rOpt ) const +{ + return bSkipEmpty == rOpt.bSkipEmpty + && bAllSheets == rOpt.bAllSheets; +} + +int ScPrintOptions::operator!=( const ScPrintOptions& rOpt ) const +{ + return !(operator==(rOpt)); +} + +// ----------------------------------------------------------------------- + +//UNUSED2008-05 ScTpPrintItem::ScTpPrintItem( USHORT nWhichP ) : SfxPoolItem( nWhichP ) +//UNUSED2008-05 { +//UNUSED2008-05 } + +ScTpPrintItem::ScTpPrintItem( USHORT nWhichP, const ScPrintOptions& rOpt ) : + SfxPoolItem ( nWhichP ), + theOptions ( rOpt ) +{ +} + +ScTpPrintItem::ScTpPrintItem( const ScTpPrintItem& rItem ) : + SfxPoolItem ( rItem ), + theOptions ( rItem.theOptions ) +{ +} + +ScTpPrintItem::~ScTpPrintItem() +{ +} + +String ScTpPrintItem::GetValueText() const +{ + return String::CreateFromAscii( "ScTpPrintItem" ); +} + +int ScTpPrintItem::operator==( const SfxPoolItem& rItem ) const +{ + DBG_ASSERT( SfxPoolItem::operator==( rItem ), "unequal Which or Type" ); + + const ScTpPrintItem& rPItem = (const ScTpPrintItem&)rItem; + return ( theOptions == rPItem.theOptions ); +} + +SfxPoolItem* ScTpPrintItem::Clone( SfxItemPool * ) const +{ + return new ScTpPrintItem( *this ); +} + +// ----------------------------------------------------------------------- + +#define CFGPATH_PRINT "Office.Calc/Print" + +#define SCPRINTOPT_EMPTYPAGES 0 +#define SCPRINTOPT_ALLSHEETS 1 +#define SCPRINTOPT_COUNT 2 + +Sequence<OUString> ScPrintCfg::GetPropertyNames() +{ + static const char* aPropNames[] = + { + "Page/EmptyPages", // SCPRINTOPT_EMPTYPAGES + "Other/AllSheets" // SCPRINTOPT_ALLSHEETS + }; + Sequence<OUString> aNames(SCPRINTOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCPRINTOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + return aNames; +} + +ScPrintCfg::ScPrintCfg() : + ConfigItem( OUString::createFromAscii( CFGPATH_PRINT ) ) +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues = GetProperties(aNames); +// EnableNotification(aNames); + const Any* pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCPRINTOPT_EMPTYPAGES: + // reversed + SetSkipEmpty( !ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCPRINTOPT_ALLSHEETS: + SetAllSheets( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + } + } + } + } +} + + +void ScPrintCfg::Commit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCPRINTOPT_EMPTYPAGES: + // reversed + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], !GetSkipEmpty() ); + break; + case SCPRINTOPT_ALLSHEETS: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetAllSheets() ); + break; + } + } + PutProperties(aNames, aValues); +} + +void ScPrintCfg::SetOptions( const ScPrintOptions& rNew ) +{ + *(ScPrintOptions*)this = rNew; + SetModified(); +} + +void ScPrintCfg::Notify( const ::com::sun::star::uno::Sequence< rtl::OUString >& ) {} + diff --git a/sc/source/core/tool/prnsave.cxx b/sc/source/core/tool/prnsave.cxx new file mode 100644 index 000000000000..b432f585e854 --- /dev/null +++ b/sc/source/core/tool/prnsave.cxx @@ -0,0 +1,135 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <tools/debug.hxx> + +#include "prnsave.hxx" +#include "global.hxx" +#include "address.hxx" + +// STATIC DATA ----------------------------------------------------------- + +//------------------------------------------------------------------ + +// +// Daten pro Tabelle +// + +ScPrintSaverTab::ScPrintSaverTab() : + mpRepeatCol(NULL), + mpRepeatRow(NULL), + mbEntireSheet(FALSE) +{ +} + +ScPrintSaverTab::~ScPrintSaverTab() +{ + delete mpRepeatCol; + delete mpRepeatRow; +} + +void ScPrintSaverTab::SetAreas( const ScRangeVec& rRanges, BOOL bEntireSheet ) +{ + maPrintRanges = rRanges; + mbEntireSheet = bEntireSheet; +} + +void ScPrintSaverTab::SetRepeat( const ScRange* pCol, const ScRange* pRow ) +{ + delete mpRepeatCol; + mpRepeatCol = pCol ? new ScRange(*pCol) : NULL; + delete mpRepeatRow; + mpRepeatRow = pRow ? new ScRange(*pRow) : NULL; +} + +inline BOOL PtrEqual( const ScRange* p1, const ScRange* p2 ) +{ + return ( !p1 && !p2 ) || ( p1 && p2 && *p1 == *p2 ); +} + +BOOL ScPrintSaverTab::operator==( const ScPrintSaverTab& rCmp ) const +{ + return + PtrEqual( mpRepeatCol, rCmp.mpRepeatCol ) && + PtrEqual( mpRepeatRow, rCmp.mpRepeatRow ) && + (mbEntireSheet == rCmp.mbEntireSheet) && + (maPrintRanges == rCmp.maPrintRanges); +} + +// +// Daten fuer das ganze Dokument +// + +ScPrintRangeSaver::ScPrintRangeSaver( SCTAB nCount ) : + nTabCount( nCount ) +{ + if (nCount > 0) + pData = new ScPrintSaverTab[nCount]; + else + pData = NULL; +} + +ScPrintRangeSaver::~ScPrintRangeSaver() +{ + delete[] pData; +} + +ScPrintSaverTab& ScPrintRangeSaver::GetTabData(SCTAB nTab) +{ + DBG_ASSERT(nTab<nTabCount,"ScPrintRangeSaver Tab zu gross"); + return pData[nTab]; +} + +const ScPrintSaverTab& ScPrintRangeSaver::GetTabData(SCTAB nTab) const +{ + DBG_ASSERT(nTab<nTabCount,"ScPrintRangeSaver Tab zu gross"); + return pData[nTab]; +} + +BOOL ScPrintRangeSaver::operator==( const ScPrintRangeSaver& rCmp ) const +{ + BOOL bEqual = ( nTabCount == rCmp.nTabCount ); + if (bEqual) + for (SCTAB i=0; i<nTabCount; i++) + if (!(pData[i]==rCmp.pData[i])) + { + bEqual = FALSE; + break; + } + return bEqual; +} + + + + diff --git a/sc/source/core/tool/progress.cxx b/sc/source/core/tool/progress.cxx new file mode 100644 index 000000000000..a2fa45764bcc --- /dev/null +++ b/sc/source/core/tool/progress.cxx @@ -0,0 +1,198 @@ +/************************************************************************* + * + * 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/app.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/progress.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/sfxsids.hrc> +#include <svl/eitem.hxx> +#include <svl/itemset.hxx> + +#define SC_PROGRESS_CXX +#include "progress.hxx" +#include "document.hxx" +#include "global.hxx" +#include "globstr.hrc" + + + +static ScProgress theDummyInterpretProgress; +SfxProgress* ScProgress::pGlobalProgress = NULL; +ULONG ScProgress::nGlobalRange = 0; +ULONG ScProgress::nGlobalPercent = 0; +BOOL ScProgress::bGlobalNoUserBreak = TRUE; +ScProgress* ScProgress::pInterpretProgress = &theDummyInterpretProgress; +ScProgress* ScProgress::pOldInterpretProgress = NULL; +ULONG ScProgress::nInterpretProgress = 0; +BOOL ScProgress::bAllowInterpretProgress = TRUE; +ScDocument* ScProgress::pInterpretDoc; +BOOL ScProgress::bIdleWasDisabled = FALSE; + + +BOOL lcl_IsHiddenDocument( SfxObjectShell* pObjSh ) +{ + if (pObjSh) + { + SfxMedium* pMed = pObjSh->GetMedium(); + if (pMed) + { + SfxItemSet* pSet = pMed->GetItemSet(); + const SfxPoolItem* pItem; + if ( pSet && SFX_ITEM_SET == pSet->GetItemState( SID_HIDDEN, TRUE, &pItem ) && + ((const SfxBoolItem*)pItem)->GetValue() ) + return TRUE; + } + } + return FALSE; +} + +ScProgress::ScProgress( SfxObjectShell* pObjSh, const String& rText, + ULONG nRange, BOOL bAllDocs, BOOL bWait ) +{ + + if ( pGlobalProgress || SfxProgress::GetActiveProgress( NULL ) ) + { + if ( lcl_IsHiddenDocument(pObjSh) ) + { + // loading a hidden document while a progress is active is possible - no error + pProgress = NULL; + } + else + { + DBG_ERROR( "ScProgress: there can be only one!" ); + pProgress = NULL; + } + } + else if ( SFX_APP()->IsDowning() ) + { + // kommt vor z.B. beim Speichern des Clipboard-Inhalts als OLE beim Beenden + // Dann wuerde ein SfxProgress wild im Speicher rummuellen + //! Soll das so sein ??? + + pProgress = NULL; + } + else if ( pObjSh && ( pObjSh->GetCreateMode() == SFX_CREATE_MODE_EMBEDDED || + pObjSh->GetProgress() ) ) + { + // #62808# no own progress for embedded objects, + // #73633# no second progress if the document already has one + + pProgress = NULL; + } + else + { + pProgress = new SfxProgress( pObjSh, rText, nRange, bAllDocs, bWait ); + pGlobalProgress = pProgress; + nGlobalRange = nRange; + nGlobalPercent = 0; + bGlobalNoUserBreak = TRUE; + } +} + + +ScProgress::ScProgress() : + pProgress( NULL ) +{ // DummyInterpret +} + + +ScProgress::~ScProgress() +{ + if ( pProgress ) + { + delete pProgress; + pGlobalProgress = NULL; + nGlobalRange = 0; + nGlobalPercent = 0; + bGlobalNoUserBreak = TRUE; + } +} + +// static + +void ScProgress::CreateInterpretProgress( ScDocument* pDoc, BOOL bWait ) +{ + if ( bAllowInterpretProgress ) + { + if ( nInterpretProgress ) + nInterpretProgress++; + else if ( pDoc->GetAutoCalc() ) + { + nInterpretProgress = 1; + bIdleWasDisabled = pDoc->IsIdleDisabled(); + pDoc->DisableIdle( TRUE ); + // Interpreter may be called in many circumstances, also if another + // progress bar is active, for example while adapting row heights. + // Keep the dummy interpret progress. + if ( !pGlobalProgress ) + pInterpretProgress = new ScProgress( pDoc->GetDocumentShell(), + ScGlobal::GetRscString( STR_PROGRESS_CALCULATING ), + pDoc->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE, FALSE, bWait ); + pInterpretDoc = pDoc; + } + } +} + + +// static + +void ScProgress::DeleteInterpretProgress() +{ + if ( bAllowInterpretProgress && nInterpretProgress ) + { + /* Do not decrement 'nInterpretProgress', before 'pInterpretProgress' + is deleted. In rare cases, deletion of 'pInterpretProgress' causes + a refresh of the sheet window which may call CreateInterpretProgress + and DeleteInterpretProgress again (from Output::DrawStrings), + resulting in double deletion of 'pInterpretProgress'. */ +// if ( --nInterpretProgress == 0 ) + if ( nInterpretProgress == 1 ) + { + if ( pInterpretProgress != &theDummyInterpretProgress ) + { + // move pointer to local temporary to avoid double deletion + ScProgress* pTmpProgress = pInterpretProgress; + pInterpretProgress = &theDummyInterpretProgress; + delete pTmpProgress; + } + if ( pInterpretDoc ) + pInterpretDoc->DisableIdle( bIdleWasDisabled ); + } + --nInterpretProgress; + } +} + + + diff --git a/sc/source/core/tool/queryparam.cxx b/sc/source/core/tool/queryparam.cxx new file mode 100644 index 000000000000..5b3b92f78ee4 --- /dev/null +++ b/sc/source/core/tool/queryparam.cxx @@ -0,0 +1,369 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: interpr4.cxx,v $ + * $Revision: 1.57.92.5 $ + * + * 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 --------------------------------------------------------------- + +#include "queryparam.hxx" + +using ::std::vector; + +// ============================================================================ + +ScQueryParamBase::ScQueryParamBase() +{ + Resize( MAXQUERY ); + for (USHORT i=0; i<MAXQUERY; i++) + maEntries[i].Clear(); +} + +ScQueryParamBase::ScQueryParamBase(const ScQueryParamBase& r) : + bHasHeader(r.bHasHeader), bByRow(r.bByRow), bInplace(r.bInplace), bCaseSens(r.bCaseSens), + bRegExp(r.bRegExp), bDuplicate(r.bDuplicate), bMixedComparison(r.bMixedComparison), + maEntries(r.maEntries) +{ +} + +ScQueryParamBase::~ScQueryParamBase() +{ +} + +SCSIZE ScQueryParamBase::GetEntryCount() const +{ + return maEntries.size(); +} + +ScQueryEntry& ScQueryParamBase::GetEntry(SCSIZE n) const +{ + return maEntries[n]; +} + +void ScQueryParamBase::Resize(SCSIZE nNew) +{ + if ( nNew < MAXQUERY ) + nNew = MAXQUERY; // nie weniger als MAXQUERY + + vector<ScQueryEntry> aNewEntries(nNew); + SCSIZE nCopy = ::std::min(maEntries.size(), nNew); + for (SCSIZE i=0; i<nCopy; i++) + aNewEntries[i] = maEntries[i]; + + maEntries.swap(aNewEntries); +} + +void ScQueryParamBase::DeleteQuery( SCSIZE nPos ) +{ + if (nPos >= maEntries.size()) + return; + + size_t n = maEntries.size(); + vector<ScQueryEntry> aNewEntries; + aNewEntries.reserve(n); + for (size_t i = 0; i < n; ++i) + if (i != nPos) + aNewEntries.push_back(maEntries[i]); + + // Don't forget to append an empty entry to make up for the removed one. + // The size of the entries is not supposed to change. + aNewEntries.push_back(ScQueryEntry()); + + maEntries.swap(aNewEntries); +} + +void ScQueryParamBase::FillInExcelSyntax(String& aCellStr, SCSIZE nIndex) +{ + if (aCellStr.Len() > 0) + { + if ( nIndex >= maEntries.size() ) + Resize( nIndex+1 ); + + ScQueryEntry& rEntry = GetEntry(nIndex); + + rEntry.bDoQuery = TRUE; + // Operatoren herausfiltern + if (aCellStr.GetChar(0) == '<') + { + if (aCellStr.GetChar(1) == '>') + { + *rEntry.pStr = aCellStr.Copy(2); + rEntry.eOp = SC_NOT_EQUAL; + } + else if (aCellStr.GetChar(1) == '=') + { + *rEntry.pStr = aCellStr.Copy(2); + rEntry.eOp = SC_LESS_EQUAL; + } + else + { + *rEntry.pStr = aCellStr.Copy(1); + rEntry.eOp = SC_LESS; + } + } + else if (aCellStr.GetChar(0) == '>') + { + if (aCellStr.GetChar(1) == '=') + { + *rEntry.pStr = aCellStr.Copy(2); + rEntry.eOp = SC_GREATER_EQUAL; + } + else + { + *rEntry.pStr = aCellStr.Copy(1); + rEntry.eOp = SC_GREATER; + } + } + else + { + if (aCellStr.GetChar(0) == '=') + *rEntry.pStr = aCellStr.Copy(1); + else + *rEntry.pStr = aCellStr; + rEntry.eOp = SC_EQUAL; + } + } +} + +// ============================================================================ + +ScQueryParamTable::ScQueryParamTable() +{ +} + +ScQueryParamTable::ScQueryParamTable(const ScQueryParamTable& r) : + nCol1(r.nCol1),nRow1(r.nRow1),nCol2(r.nCol2),nRow2(r.nRow2),nTab(r.nTab) +{ +} + +ScQueryParamTable::~ScQueryParamTable() +{ +} + +// ============================================================================ + +ScQueryParam::ScQueryParam() : + ScQueryParamBase(), + ScQueryParamTable(), + bDestPers(true), + nDestTab(0), + nDestCol(0), + nDestRow(0) +{ + Clear(); +} + +//------------------------------------------------------------------------ + +ScQueryParam::ScQueryParam( const ScQueryParam& r ) : + ScQueryParamBase(r), + ScQueryParamTable(r), + bDestPers(r.bDestPers), nDestTab(r.nDestTab), nDestCol(r.nDestCol), nDestRow(r.nDestRow) +{ +} + +ScQueryParam::ScQueryParam( const ScDBQueryParamInternal& r ) : + ScQueryParamBase(r), + ScQueryParamTable(r), + bDestPers(true), + nDestTab(0), + nDestCol(0), + nDestRow(0) +{ +} + + +//------------------------------------------------------------------------ + +ScQueryParam::~ScQueryParam() +{ +} + +//------------------------------------------------------------------------ + +void ScQueryParam::Clear() +{ + nCol1=nCol2 = 0; + nRow1=nRow2 = 0; + nTab = SCTAB_MAX; + bHasHeader = bCaseSens = bRegExp = bMixedComparison = FALSE; + bInplace = bByRow = bDuplicate = TRUE; + + Resize( MAXQUERY ); + for (USHORT i=0; i<MAXQUERY; i++) + maEntries[i].Clear(); + + ClearDestParams(); +} + +void ScQueryParam::ClearDestParams() +{ + bDestPers = true; + nDestTab = 0; + nDestCol = 0; + nDestRow = 0; +} + +//------------------------------------------------------------------------ + +ScQueryParam& ScQueryParam::operator=( const ScQueryParam& r ) +{ + nCol1 = r.nCol1; + nRow1 = r.nRow1; + nCol2 = r.nCol2; + nRow2 = r.nRow2; + nTab = r.nTab; + nDestTab = r.nDestTab; + nDestCol = r.nDestCol; + nDestRow = r.nDestRow; + bHasHeader = r.bHasHeader; + bInplace = r.bInplace; + bCaseSens = r.bCaseSens; + bRegExp = r.bRegExp; + bMixedComparison = r.bMixedComparison; + bDuplicate = r.bDuplicate; + bByRow = r.bByRow; + bDestPers = r.bDestPers; + + maEntries = r.maEntries; + + return *this; +} + +//------------------------------------------------------------------------ + +BOOL ScQueryParam::operator==( const ScQueryParam& rOther ) const +{ + BOOL bEqual = FALSE; + + // Anzahl der Queries gleich? + SCSIZE nUsed = 0; + SCSIZE nOtherUsed = 0; + SCSIZE nEntryCount = GetEntryCount(); + SCSIZE nOtherEntryCount = rOther.GetEntryCount(); + + while ( nUsed<nEntryCount && maEntries[nUsed].bDoQuery ) ++nUsed; + while ( nOtherUsed<nOtherEntryCount && rOther.maEntries[nOtherUsed].bDoQuery ) + ++nOtherUsed; + + if ( (nUsed == nOtherUsed) + && (nCol1 == rOther.nCol1) + && (nRow1 == rOther.nRow1) + && (nCol2 == rOther.nCol2) + && (nRow2 == rOther.nRow2) + && (nTab == rOther.nTab) + && (bHasHeader == rOther.bHasHeader) + && (bByRow == rOther.bByRow) + && (bInplace == rOther.bInplace) + && (bCaseSens == rOther.bCaseSens) + && (bRegExp == rOther.bRegExp) + && (bMixedComparison == rOther.bMixedComparison) + && (bDuplicate == rOther.bDuplicate) + && (bDestPers == rOther.bDestPers) + && (nDestTab == rOther.nDestTab) + && (nDestCol == rOther.nDestCol) + && (nDestRow == rOther.nDestRow) ) + { + bEqual = TRUE; + for ( SCSIZE i=0; i<nUsed && bEqual; i++ ) + bEqual = maEntries[i] == rOther.maEntries[i]; + } + return bEqual; +} + +//------------------------------------------------------------------------ + +void ScQueryParam::MoveToDest() +{ + if (!bInplace) + { + SCsCOL nDifX = ((SCsCOL) nDestCol) - ((SCsCOL) nCol1); + SCsROW nDifY = ((SCsROW) nDestRow) - ((SCsROW) nRow1); + SCsTAB nDifZ = ((SCsTAB) nDestTab) - ((SCsTAB) nTab); + + nCol1 = sal::static_int_cast<SCCOL>( nCol1 + nDifX ); + nRow1 = sal::static_int_cast<SCROW>( nRow1 + nDifY ); + nCol2 = sal::static_int_cast<SCCOL>( nCol2 + nDifX ); + nRow2 = sal::static_int_cast<SCROW>( nRow2 + nDifY ); + nTab = sal::static_int_cast<SCTAB>( nTab + nDifZ ); + size_t n = maEntries.size(); + for (size_t i=0; i<n; i++) + maEntries[i].nField += nDifX; + + bInplace = TRUE; + } + else + { + DBG_ERROR("MoveToDest, bInplace == TRUE"); + } +} + +// ============================================================================ + +ScDBQueryParamBase::ScDBQueryParamBase(DataType eType) : + ScQueryParamBase(), + mnField(-1), + mbSkipString(true), + meType(eType) +{ +} + +ScDBQueryParamBase::~ScDBQueryParamBase() +{ +} + +ScDBQueryParamBase::DataType ScDBQueryParamBase::GetType() const +{ + return meType; +} + +// ============================================================================ + +ScDBQueryParamInternal::ScDBQueryParamInternal() : + ScDBQueryParamBase(ScDBQueryParamBase::INTERNAL), + ScQueryParamTable() +{ +} + +ScDBQueryParamInternal::~ScDBQueryParamInternal() +{ +} + +// ============================================================================ + +ScDBQueryParamMatrix::ScDBQueryParamMatrix() : + ScDBQueryParamBase(ScDBQueryParamBase::MATRIX) +{ +} + +ScDBQueryParamMatrix::~ScDBQueryParamMatrix() +{ +} + diff --git a/sc/source/core/tool/rangelst.cxx b/sc/source/core/tool/rangelst.cxx new file mode 100644 index 000000000000..1ab978866c67 --- /dev/null +++ b/sc/source/core/tool/rangelst.cxx @@ -0,0 +1,703 @@ +/************************************************************************* + * + * 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" + + +//------------------------------------------------------------------------ + +#define SC_RANGELST_CXX //fuer ICC + +#include <tools/debug.hxx> +#include <stdlib.h> // qsort +#include <unotools/collatorwrapper.hxx> + +#include "rangelst.hxx" +#include "document.hxx" +#include "refupdat.hxx" +#include "rechead.hxx" +#include "compiler.hxx" + +// === ScRangeList ==================================================== + +ScRangeList::~ScRangeList() +{ + for ( ScRangePtr pR = First(); pR; pR = Next() ) + delete pR; +} + +void ScRangeList::RemoveAll() +{ + for ( ScRangePtr pR = First(); pR; pR = Next() ) + delete pR; + Clear(); +} + +USHORT ScRangeList::Parse( const String& rStr, ScDocument* pDoc, USHORT nMask, + formula::FormulaGrammar::AddressConvention eConv, + sal_Unicode cDelimiter ) +{ + if ( rStr.Len() ) + { + if (!cDelimiter) + cDelimiter = ScCompiler::GetNativeSymbol(ocSep).GetChar(0); + + nMask |= SCA_VALID; // falls das jemand vergessen sollte + USHORT nResult = (USHORT)~0; // alle Bits setzen + ScRange aRange; + String aOne; + SCTAB nTab = 0; + if ( pDoc ) + { + //! erste markierte Tabelle gibts nicht mehr am Dokument + //! -> uebergeben? oder spaeter an den Ranges setzen + } + else + nTab = 0; + USHORT nTCount = rStr.GetTokenCount( cDelimiter ); + for ( USHORT i=0; i<nTCount; i++ ) + { + aOne = rStr.GetToken( i, cDelimiter ); + // FIXME : broken for Lotus + if ( aOne.Search( ':' ) == STRING_NOTFOUND ) + { // Range muss es sein + String aStrTmp( aOne ); + aOne += ':'; + aOne += aStrTmp; + } + aRange.aStart.SetTab( nTab ); // Default Tab wenn nicht angegeben + USHORT nRes = aRange.Parse( aOne, pDoc, eConv ); + if ( (nRes & nMask) == nMask ) + Append( aRange ); + nResult &= nRes; // alle gemeinsamen Bits bleiben erhalten + } + return nResult; // SCA_VALID gesetzt wenn alle ok + } + else + return 0; +} + + +void ScRangeList::Format( String& rStr, USHORT nFlags, ScDocument* pDoc, + formula::FormulaGrammar::AddressConvention eConv, + sal_Unicode cDelimiter ) const +{ + rStr.Erase(); + + if (!cDelimiter) + cDelimiter = ScCompiler::GetNativeSymbol(ocSep).GetChar(0); + + ULONG nCnt = Count(); + for ( ULONG nIdx = 0; nIdx < nCnt; nIdx++ ) + { + String aStr; + GetObject( nIdx )->Format( aStr, nFlags, pDoc, eConv ); + if ( nIdx ) + rStr += cDelimiter; + rStr += aStr; + } +} + + +void ScRangeList::Join( const ScRange& r, BOOL bIsInList ) +{ + if ( !Count() ) + { + Append( r ); + return ; + } + SCCOL nCol1 = r.aStart.Col(); + SCROW nRow1 = r.aStart.Row(); + SCTAB nTab1 = r.aStart.Tab(); + SCCOL nCol2 = r.aEnd.Col(); + SCROW nRow2 = r.aEnd.Row(); + SCTAB nTab2 = r.aEnd.Tab(); + ScRangePtr pOver = (ScRangePtr) &r; // fies aber wahr wenn bInList + ULONG nOldPos = 0; + if ( bIsInList ) + { // merken um ggbf. zu loeschen bzw. wiederherzustellen + nOldPos = GetPos( pOver ); + } + BOOL bJoinedInput = FALSE; + for ( ScRangePtr p = First(); p && pOver; p = Next() ) + { + if ( p == pOver ) + continue; // derselbe, weiter mit dem naechsten + BOOL bJoined = FALSE; + if ( p->In( r ) ) + { // Range r in Range p enthalten oder identisch + if ( bIsInList ) + bJoined = TRUE; // weg mit Range r + else + { // das war's dann + bJoinedInput = TRUE; // nicht anhaengen + break; // for + } + } + else if ( r.In( *p ) ) + { // Range p in Range r enthalten, r zum neuen Range machen + *p = r; + bJoined = TRUE; + } + if ( !bJoined && p->aStart.Tab() == nTab1 && p->aEnd.Tab() == nTab2 ) + { // 2D + if ( p->aStart.Col() == nCol1 && p->aEnd.Col() == nCol2 ) + { + if ( p->aStart.Row() == nRow2+1 ) + { // oben + p->aStart.SetRow( nRow1 ); + bJoined = TRUE; + } + else if ( p->aEnd.Row() == nRow1-1 ) + { // unten + p->aEnd.SetRow( nRow2 ); + bJoined = TRUE; + } + } + else if ( p->aStart.Row() == nRow1 && p->aEnd.Row() == nRow2 ) + { + if ( p->aStart.Col() == nCol2+1 ) + { // links + p->aStart.SetCol( nCol1 ); + bJoined = TRUE; + } + else if ( p->aEnd.Col() == nCol1-1 ) + { // rechts + p->aEnd.SetCol( nCol2 ); + bJoined = TRUE; + } + } + } + if ( bJoined ) + { + if ( bIsInList ) + { // innerhalb der Liste Range loeschen + Remove( nOldPos ); + delete pOver; + pOver = NULL; + if ( nOldPos ) + nOldPos--; // Seek richtig aufsetzen + } + bJoinedInput = TRUE; + Join( *p, TRUE ); // rekursiv! + } + } + if ( bIsInList ) + Seek( nOldPos ); + else if ( !bJoinedInput ) + Append( r ); +} + + +BOOL ScRangeList::operator==( const ScRangeList& r ) const +{ + if ( this == &r ) + return TRUE; // identische Referenz + if ( Count() != r.Count() ) + return FALSE; + ULONG nCnt = Count(); + for ( ULONG nIdx = 0; nIdx < nCnt; nIdx++ ) + { + if ( *GetObject( nIdx ) != *r.GetObject( nIdx ) ) + return FALSE; // auch andere Reihenfolge ist ungleich + } + return TRUE; +} + +BOOL ScRangeList::operator!=( const ScRangeList& r ) const +{ + return !operator==( r ); +} + +BOOL ScRangeList::UpdateReference( UpdateRefMode eUpdateRefMode, + ScDocument* pDoc, const ScRange& rWhere, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + BOOL bChanged = FALSE; + if ( Count() ) + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + rWhere.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + for ( ScRange* pR = First(); pR; pR = Next() ) + { + SCCOL theCol1; + SCROW theRow1; + SCTAB theTab1; + SCCOL theCol2; + SCROW theRow2; + SCTAB theTab2; + pR->GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 ); + if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, + nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, + nDx, nDy, nDz, + theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 ) + != UR_NOTHING ) + { + bChanged = TRUE; + pR->aStart.Set( theCol1, theRow1, theTab1 ); + pR->aEnd.Set( theCol2, theRow2, theTab2 ); + } + } + } + return bChanged; +} + + +ScRange* ScRangeList::Find( const ScAddress& rAdr ) const +{ + ULONG nListCount = Count(); + for ( ULONG j = 0; j < nListCount; j++ ) + { + ScRange* pR = GetObject( j ); + if ( pR->In( rAdr ) ) + return pR; + } + return NULL; +} + + +ScRangeList::ScRangeList( const ScRangeList& rList ) : + ScRangeListBase(), + SvRefBase() +{ + ULONG nListCount = rList.Count(); + for ( ULONG j = 0; j < nListCount; j++ ) + Append( *rList.GetObject( j ) ); +} + + +ScRangeList& ScRangeList::operator=(const ScRangeList& rList) +{ + RemoveAll(); + + ULONG nListCount = rList.Count(); + for ( ULONG j = 0; j < nListCount; j++ ) + Append( *rList.GetObject( j ) ); + + return *this; +} + + +BOOL ScRangeList::Intersects( const ScRange& rRange ) const +{ + ULONG nListCount = Count(); + for ( ULONG j = 0; j < nListCount; j++ ) + if ( GetObject(j)->Intersects( rRange ) ) + return TRUE; + + return FALSE; +} + + +BOOL ScRangeList::In( const ScRange& rRange ) const +{ + ULONG nListCount = Count(); + for ( ULONG j = 0; j < nListCount; j++ ) + if ( GetObject(j)->In( rRange ) ) + return TRUE; + + return FALSE; +} + + +ULONG ScRangeList::GetCellCount() const +{ + ULONG nCellCount = 0; + ULONG nListCount = Count(); + for ( ULONG j = 0; j < nListCount; j++ ) + { + ScRange* pR = GetObject( j ); + nCellCount += ULONG(pR->aEnd.Col() - pR->aStart.Col() + 1) + * ULONG(pR->aEnd.Row() - pR->aStart.Row() + 1) + * ULONG(pR->aEnd.Tab() - pR->aStart.Tab() + 1); + } + return nCellCount; +} + + +// === ScRangePairList ==================================================== + +ScRangePairList::~ScRangePairList() +{ + for ( ScRangePair* pR = First(); pR; pR = Next() ) + delete pR; +} + + +void ScRangePairList::Join( const ScRangePair& r, BOOL bIsInList ) +{ + if ( !Count() ) + { + Append( r ); + return ; + } + const ScRange& r1 = r.GetRange(0); + const ScRange& r2 = r.GetRange(1); + SCCOL nCol1 = r1.aStart.Col(); + SCROW nRow1 = r1.aStart.Row(); + SCTAB nTab1 = r1.aStart.Tab(); + SCCOL nCol2 = r1.aEnd.Col(); + SCROW nRow2 = r1.aEnd.Row(); + SCTAB nTab2 = r1.aEnd.Tab(); + ScRangePair* pOver = (ScRangePair*) &r; // fies aber wahr wenn bInList + ULONG nOldPos = 0; + if ( bIsInList ) + { // merken um ggbf. zu loeschen bzw. wiederherzustellen + nOldPos = GetPos( pOver ); + } + BOOL bJoinedInput = FALSE; + for ( ScRangePair* p = First(); p && pOver; p = Next() ) + { + if ( p == pOver ) + continue; // derselbe, weiter mit dem naechsten + BOOL bJoined = FALSE; + ScRange& rp1 = p->GetRange(0); + ScRange& rp2 = p->GetRange(1); + if ( rp2 == r2 ) + { // nur wenn Range2 gleich ist + if ( rp1.In( r1 ) ) + { // RangePair r in RangePair p enthalten oder identisch + if ( bIsInList ) + bJoined = TRUE; // weg mit RangePair r + else + { // das war's dann + bJoinedInput = TRUE; // nicht anhaengen + break; // for + } + } + else if ( r1.In( rp1 ) ) + { // RangePair p in RangePair r enthalten, r zum neuen RangePair machen + *p = r; + bJoined = TRUE; + } + } + if ( !bJoined && rp1.aStart.Tab() == nTab1 && rp1.aEnd.Tab() == nTab2 + && rp2.aStart.Tab() == r2.aStart.Tab() + && rp2.aEnd.Tab() == r2.aEnd.Tab() ) + { // 2D, Range2 muss genauso nebeneinander liegen wie Range1 + if ( rp1.aStart.Col() == nCol1 && rp1.aEnd.Col() == nCol2 + && rp2.aStart.Col() == r2.aStart.Col() + && rp2.aEnd.Col() == r2.aEnd.Col() ) + { + if ( rp1.aStart.Row() == nRow2+1 + && rp2.aStart.Row() == r2.aEnd.Row()+1 ) + { // oben + rp1.aStart.SetRow( nRow1 ); + rp2.aStart.SetRow( r2.aStart.Row() ); + bJoined = TRUE; + } + else if ( rp1.aEnd.Row() == nRow1-1 + && rp2.aEnd.Row() == r2.aStart.Row()-1 ) + { // unten + rp1.aEnd.SetRow( nRow2 ); + rp2.aEnd.SetRow( r2.aEnd.Row() ); + bJoined = TRUE; + } + } + else if ( rp1.aStart.Row() == nRow1 && rp1.aEnd.Row() == nRow2 + && rp2.aStart.Row() == r2.aStart.Row() + && rp2.aEnd.Row() == r2.aEnd.Row() ) + { + if ( rp1.aStart.Col() == nCol2+1 + && rp2.aStart.Col() == r2.aEnd.Col()+1 ) + { // links + rp1.aStart.SetCol( nCol1 ); + rp2.aStart.SetCol( r2.aStart.Col() ); + bJoined = TRUE; + } + else if ( rp1.aEnd.Col() == nCol1-1 + && rp2.aEnd.Col() == r2.aEnd.Col()-1 ) + { // rechts + rp1.aEnd.SetCol( nCol2 ); + rp2.aEnd.SetCol( r2.aEnd.Col() ); + bJoined = TRUE; + } + } + } + if ( bJoined ) + { + if ( bIsInList ) + { // innerhalb der Liste RangePair loeschen + Remove( nOldPos ); + delete pOver; + pOver = NULL; + if ( nOldPos ) + nOldPos--; // Seek richtig aufsetzen + } + bJoinedInput = TRUE; + Join( *p, TRUE ); // rekursiv! + } + } + if ( bIsInList ) + Seek( nOldPos ); + else if ( !bJoinedInput ) + Append( r ); +} + + +BOOL ScRangePairList::operator==( const ScRangePairList& r ) const +{ + if ( this == &r ) + return TRUE; // identische Referenz + if ( Count() != r.Count() ) + return FALSE; + ULONG nCnt = Count(); + for ( ULONG nIdx = 0; nIdx < nCnt; nIdx++ ) + { + if ( *GetObject( nIdx ) != *r.GetObject( nIdx ) ) + return FALSE; // auch andere Reihenfolge ist ungleich + } + return TRUE; +} + + +BOOL ScRangePairList::UpdateReference( UpdateRefMode eUpdateRefMode, + ScDocument* pDoc, const ScRange& rWhere, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + BOOL bChanged = FALSE; + if ( Count() ) + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + rWhere.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + for ( ScRangePair* pR = First(); pR; pR = Next() ) + { + for ( USHORT j=0; j<2; j++ ) + { + ScRange& rRange = pR->GetRange(j); + SCCOL theCol1; + SCROW theRow1; + SCTAB theTab1; + SCCOL theCol2; + SCROW theRow2; + SCTAB theTab2; + rRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 ); + if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, + nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, + nDx, nDy, nDz, + theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 ) + != UR_NOTHING ) + { + bChanged = TRUE; + rRange.aStart.Set( theCol1, theRow1, theTab1 ); + rRange.aEnd.Set( theCol2, theRow2, theTab2 ); + } + } + } + } + return bChanged; +} + + +void ScRangePairList::DeleteOnTab( SCTAB nTab ) +{ + // Delete entries that have the labels (first range) on nTab + + ULONG nListCount = Count(); + ULONG nPos = 0; + while ( nPos < nListCount ) + { + ScRangePair* pR = GetObject( nPos ); + ScRange aRange = pR->GetRange(0); + if ( aRange.aStart.Tab() == nTab && aRange.aEnd.Tab() == nTab ) + { + Remove( nPos ); + delete pR; + nListCount = Count(); + } + else + ++nPos; + } +} + + +ScRangePair* ScRangePairList::Find( const ScAddress& rAdr ) const +{ + ULONG nListCount = Count(); + for ( ULONG j = 0; j < nListCount; j++ ) + { + ScRangePair* pR = GetObject( j ); + if ( pR->GetRange(0).In( rAdr ) ) + return pR; + } + return NULL; +} + + +ScRangePair* ScRangePairList::Find( const ScRange& rRange ) const +{ + ULONG nListCount = Count(); + for ( ULONG j = 0; j < nListCount; j++ ) + { + ScRangePair* pR = GetObject( j ); + if ( pR->GetRange(0) == rRange ) + return pR; + } + return NULL; +} + + +ScRangePairList* ScRangePairList::Clone() const +{ + ScRangePairList* pNew = new ScRangePairList; + ULONG nListCount = Count(); + for ( ULONG j = 0; j < nListCount; j++ ) + { + pNew->Append( *GetObject( j ) ); + } + return pNew; +} + + +struct ScRangePairNameSort +{ + ScRangePair* pPair; + ScDocument* pDoc; +}; + + +extern "C" int +#ifdef WNT +__cdecl +#endif +ScRangePairList_QsortNameCompare( const void* p1, const void* p2 ) +{ + const ScRangePairNameSort* ps1 = (const ScRangePairNameSort*)p1; + const ScRangePairNameSort* ps2 = (const ScRangePairNameSort*)p2; + const ScAddress& rStartPos1 = ps1->pPair->GetRange(0).aStart; + const ScAddress& rStartPos2 = ps2->pPair->GetRange(0).aStart; + String aStr1, aStr2; + sal_Int32 nComp; + if ( rStartPos1.Tab() == rStartPos2.Tab() ) + nComp = COMPARE_EQUAL; + else + { + ps1->pDoc->GetName( rStartPos1.Tab(), aStr1 ); + ps2->pDoc->GetName( rStartPos2.Tab(), aStr2 ); + nComp = ScGlobal::GetCollator()->compareString( aStr1, aStr2 ); + } + switch ( nComp ) + { + case COMPARE_LESS: + return -1; + //break; + case COMPARE_GREATER: + return 1; + //break; + default: + // gleiche Tabs + if ( rStartPos1.Col() < rStartPos2.Col() ) + return -1; + if ( rStartPos1.Col() > rStartPos2.Col() ) + return 1; + // gleiche Cols + if ( rStartPos1.Row() < rStartPos2.Row() ) + return -1; + if ( rStartPos1.Row() > rStartPos2.Row() ) + return 1; + // erste Ecke gleich, zweite Ecke + { + const ScAddress& rEndPos1 = ps1->pPair->GetRange(0).aEnd; + const ScAddress& rEndPos2 = ps2->pPair->GetRange(0).aEnd; + if ( rEndPos1.Tab() == rEndPos2.Tab() ) + nComp = COMPARE_EQUAL; + else + { + ps1->pDoc->GetName( rEndPos1.Tab(), aStr1 ); + ps2->pDoc->GetName( rEndPos2.Tab(), aStr2 ); + nComp = ScGlobal::GetCollator()->compareString( aStr1, aStr2 ); + } + switch ( nComp ) + { + case COMPARE_LESS: + return -1; + //break; + case COMPARE_GREATER: + return 1; + //break; + default: + // gleiche Tabs + if ( rEndPos1.Col() < rEndPos2.Col() ) + return -1; + if ( rEndPos1.Col() > rEndPos2.Col() ) + return 1; + // gleiche Cols + if ( rEndPos1.Row() < rEndPos2.Row() ) + return -1; + if ( rEndPos1.Row() > rEndPos2.Row() ) + return 1; + return 0; + } + } + return 0; + } + return 0; // just in case +} + + +ScRangePair** ScRangePairList::CreateNameSortedArray( ULONG& nListCount, + ScDocument* pDoc ) const +{ + nListCount = Count(); + DBG_ASSERT( nListCount * sizeof(ScRangePairNameSort) <= (size_t)~0x1F, + "ScRangePairList::CreateNameSortedArray nListCount * sizeof(ScRangePairNameSort) > (size_t)~0x1F" ); + ScRangePairNameSort* pSortArray = (ScRangePairNameSort*) + new BYTE [ nListCount * sizeof(ScRangePairNameSort) ]; + ULONG j; + for ( j=0; j < nListCount; j++ ) + { + pSortArray[j].pPair = GetObject( j ); + pSortArray[j].pDoc = pDoc; + } +#if !(defined(ICC ) && defined(OS2)) + qsort( (void*)pSortArray, nListCount, sizeof(ScRangePairNameSort), &ScRangePairList_QsortNameCompare ); +#else + qsort( (void*)pSortArray, nListCount, sizeof(ScRangePairNameSort), ICCQsortRPairCompare ); +#endif + // ScRangePair Pointer aufruecken + ScRangePair** ppSortArray = (ScRangePair**)pSortArray; + for ( j=0; j < nListCount; j++ ) + { + ppSortArray[j] = pSortArray[j].pPair; + } + return ppSortArray; +} + + + + diff --git a/sc/source/core/tool/rangenam.cxx b/sc/source/core/tool/rangenam.cxx new file mode 100644 index 000000000000..e81f1f3bd94d --- /dev/null +++ b/sc/source/core/tool/rangenam.cxx @@ -0,0 +1,824 @@ +/************************************************************************* + * + * 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 <tools/debug.hxx> +#include <string.h> +#include <memory> +#include <unotools/collatorwrapper.hxx> +#include <unotools/transliterationwrapper.hxx> + +#include "token.hxx" +#include "tokenarray.hxx" +#include "rangenam.hxx" +#include "global.hxx" +#include "compiler.hxx" +#include "rangeutl.hxx" +#include "rechead.hxx" +#include "refupdat.hxx" +#include "document.hxx" + +using namespace formula; + +//======================================================================== +// ScRangeData +//======================================================================== + +// Interner ctor fuer das Suchen nach einem Index + +ScRangeData::ScRangeData( USHORT n ) + : pCode( NULL ), nIndex( n ), bModified( FALSE ), mnMaxRow(-1), mnMaxCol(-1) +{} + +ScRangeData::ScRangeData( ScDocument* pDok, + const String& rName, + const String& rSymbol, + const ScAddress& rAddress, + RangeType nType, + const FormulaGrammar::Grammar eGrammar ) : + aName ( rName ), + aUpperName ( ScGlobal::pCharClass->upper( rName ) ), + pCode ( NULL ), + aPos ( rAddress ), + eType ( nType ), + pDoc ( pDok ), + nIndex ( 0 ), + bModified ( FALSE ), + mnMaxRow (-1), + mnMaxCol (-1) +{ + if (rSymbol.Len() > 0) + { + ScCompiler aComp( pDoc, aPos ); + aComp.SetGrammar(eGrammar); + pCode = aComp.CompileString( rSymbol ); + if( !pCode->GetCodeError() ) + { + pCode->Reset(); + FormulaToken* p = pCode->GetNextReference(); + if( p )// genau eine Referenz als erstes + { + if( p->GetType() == svSingleRef ) + eType = eType | RT_ABSPOS; + else + eType = eType | RT_ABSAREA; + } + // ggf. den Fehlercode wg. unvollstaendiger Formel setzen! + // Dies ist fuer die manuelle Eingabe + aComp.CompileTokenArray(); + pCode->DelRPN(); + } + } + else + { + // #i63513#/#i65690# don't leave pCode as NULL. + // Copy ctor default-constructs pCode if it was NULL, so it's initialized here, too, + // to ensure same behavior if unnecessary copying is left out. + + pCode = new ScTokenArray(); + } +} + +ScRangeData::ScRangeData( ScDocument* pDok, + const String& rName, + const ScTokenArray& rArr, + const ScAddress& rAddress, + RangeType nType ) : + aName ( rName ), + aUpperName ( ScGlobal::pCharClass->upper( rName ) ), + pCode ( new ScTokenArray( rArr ) ), + aPos ( rAddress ), + eType ( nType ), + pDoc ( pDok ), + nIndex ( 0 ), + bModified ( FALSE ), + mnMaxRow (-1), + mnMaxCol (-1) +{ + if( !pCode->GetCodeError() ) + { + pCode->Reset(); + FormulaToken* p = pCode->GetNextReference(); + if( p )// genau eine Referenz als erstes + { + if( p->GetType() == svSingleRef ) + eType = eType | RT_ABSPOS; + else + eType = eType | RT_ABSAREA; + } + // Die Importfilter haben diesen Test nicht, + // da die benannten Bereiche z.T. noch unvollstaendig sind. +// if( !pCode->GetCodeLen() ) +// { +// // ggf. den Fehlercode wg. unvollstaendiger Formel setzen! +// ScCompiler aComp( pDok, aPos, *pCode ); +// aComp.CompileTokenArray(); +// pCode->DelRPN(); +// } + } +} + +ScRangeData::ScRangeData( ScDocument* pDok, + const String& rName, + const ScAddress& rTarget ) : + aName ( rName ), + aUpperName ( ScGlobal::pCharClass->upper( rName ) ), + pCode ( new ScTokenArray() ), + aPos ( rTarget ), + eType ( RT_NAME ), + pDoc ( pDok ), + nIndex ( 0 ), + bModified ( FALSE ), + mnMaxRow (-1), + mnMaxCol (-1) +{ + ScSingleRefData aRefData; + aRefData.InitAddress( rTarget ); + aRefData.SetFlag3D( TRUE ); + pCode->AddSingleReference( aRefData ); + ScCompiler aComp( pDoc, aPos, *pCode ); + aComp.SetGrammar(pDoc->GetGrammar()); + aComp.CompileTokenArray(); + if ( !pCode->GetCodeError() ) + eType |= RT_ABSPOS; +} + +ScRangeData::ScRangeData(const ScRangeData& rScRangeData) : + ScDataObject(), + aName (rScRangeData.aName), + aUpperName (rScRangeData.aUpperName), + pCode (rScRangeData.pCode ? rScRangeData.pCode->Clone() : new ScTokenArray()), // echte Kopie erzeugen (nicht copy-ctor) + aPos (rScRangeData.aPos), + eType (rScRangeData.eType), + pDoc (rScRangeData.pDoc), + nIndex (rScRangeData.nIndex), + bModified (rScRangeData.bModified), + mnMaxRow (rScRangeData.mnMaxRow), + mnMaxCol (rScRangeData.mnMaxCol) +{} + +ScRangeData::~ScRangeData() +{ + delete pCode; +} + +ScDataObject* ScRangeData::Clone() const +{ + return new ScRangeData(*this); +} + +void ScRangeData::GuessPosition() +{ + // setzt eine Position, mit der alle relative Referenzen bei CalcAbsIfRel + // ohne Fehler verabsolutiert werden koennen + + DBG_ASSERT(aPos == ScAddress(), "die Position geht jetzt verloren"); + + SCsCOL nMinCol = 0; + SCsROW nMinRow = 0; + SCsTAB nMinTab = 0; + + ScToken* t; + pCode->Reset(); + while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL ) + { + ScSingleRefData& rRef1 = t->GetSingleRef(); + if ( rRef1.IsColRel() && rRef1.nRelCol < nMinCol ) + nMinCol = rRef1.nRelCol; + if ( rRef1.IsRowRel() && rRef1.nRelRow < nMinRow ) + nMinRow = rRef1.nRelRow; + if ( rRef1.IsTabRel() && rRef1.nRelTab < nMinTab ) + nMinTab = rRef1.nRelTab; + + if ( t->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; + if ( rRef2.IsColRel() && rRef2.nRelCol < nMinCol ) + nMinCol = rRef2.nRelCol; + if ( rRef2.IsRowRel() && rRef2.nRelRow < nMinRow ) + nMinRow = rRef2.nRelRow; + if ( rRef2.IsTabRel() && rRef2.nRelTab < nMinTab ) + nMinTab = rRef2.nRelTab; + } + } + + aPos = ScAddress( (SCCOL)(-nMinCol), (SCROW)(-nMinRow), (SCTAB)(-nMinTab) ); + + //! Test +// DBG_ERROR(String("Pos ")+String((SCCOL)(-nMinCol))+String("/")+ +// String((SCROW)(-nMinRow))+String("/")+String((SCTAB)(-nMinTab))); +} + +void ScRangeData::GetSymbol( String& rSymbol, const FormulaGrammar::Grammar eGrammar ) const +{ + ScCompiler aComp(pDoc, aPos, *pCode); + aComp.SetGrammar(eGrammar); + aComp.CreateStringFromTokenArray( rSymbol ); +} + +void ScRangeData::UpdateSymbol( rtl::OUStringBuffer& rBuffer, const ScAddress& rPos, + const FormulaGrammar::Grammar eGrammar ) +{ + ::std::auto_ptr<ScTokenArray> pTemp( pCode->Clone() ); + ScCompiler aComp( pDoc, rPos, *pTemp.get()); + aComp.SetGrammar(eGrammar); + aComp.MoveRelWrap(GetMaxCol(), GetMaxRow()); + aComp.CreateStringFromTokenArray( rBuffer ); +} + +void ScRangeData::UpdateReference( UpdateRefMode eUpdateRefMode, + const ScRange& r, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + BOOL bChanged = FALSE; + + pCode->Reset(); + if( pCode->GetNextReference() ) + { + BOOL bSharedFormula = ((eType & RT_SHARED) == RT_SHARED); + ScCompiler aComp( pDoc, aPos, *pCode ); + aComp.SetGrammar(pDoc->GetGrammar()); + const BOOL bRelRef = aComp.UpdateNameReference( eUpdateRefMode, r, + nDx, nDy, nDz, + bChanged, bSharedFormula); + if (bSharedFormula) + { + if (bRelRef) + eType = eType | RT_SHAREDMOD; + else + eType = eType & ~RT_SHAREDMOD; + } + } + + bModified = bChanged; +} + + +void ScRangeData::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest ) +{ + BOOL bChanged = FALSE; + + ScToken* t; + pCode->Reset(); + + while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL ) + { + if( t->GetType() != svIndex ) + { + SingleDoubleRefModifier aMod( *t ); + ScComplexRefData& rRef = aMod.Ref(); + if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() && + (!rRef.Ref1.IsFlag3D() || !rRef.Ref1.IsTabRel()) && + ( t->GetType() == svSingleRef || + (!rRef.Ref2.IsColRel() && !rRef.Ref2.IsRowRel() && + (!rRef.Ref2.IsFlag3D() || !rRef.Ref2.IsTabRel())))) + { + if ( ScRefUpdate::UpdateTranspose( pDoc, rSource, rDest, rRef ) != UR_NOTHING ) + bChanged = TRUE; + } + } + } + + bModified = bChanged; +} + +void ScRangeData::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) +{ + BOOL bChanged = FALSE; + + ScToken* t; + pCode->Reset(); + + while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL ) + { + if( t->GetType() != svIndex ) + { + SingleDoubleRefModifier aMod( *t ); + ScComplexRefData& rRef = aMod.Ref(); + if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() && + (!rRef.Ref1.IsFlag3D() || !rRef.Ref1.IsTabRel()) && + ( t->GetType() == svSingleRef || + (!rRef.Ref2.IsColRel() && !rRef.Ref2.IsRowRel() && + (!rRef.Ref2.IsFlag3D() || !rRef.Ref2.IsTabRel())))) + { + if ( ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY, rRef ) != UR_NOTHING ) + bChanged = TRUE; + } + } + } + + bModified = bChanged; // muss direkt hinterher ausgewertet werden +} + +BOOL ScRangeData::operator== (const ScRangeData& rData) const // fuer Undo +{ + if ( nIndex != rData.nIndex || + aName != rData.aName || + aPos != rData.aPos || + eType != rData.eType ) return FALSE; + + USHORT nLen = pCode->GetLen(); + if ( nLen != rData.pCode->GetLen() ) return FALSE; + + FormulaToken** ppThis = pCode->GetArray(); + FormulaToken** ppOther = rData.pCode->GetArray(); + + for ( USHORT i=0; i<nLen; i++ ) + if ( ppThis[i] != ppOther[i] && !(*ppThis[i] == *ppOther[i]) ) + return FALSE; + + return TRUE; +} + +//UNUSED2009-05 BOOL ScRangeData::IsRangeAtCursor( const ScAddress& rPos, BOOL bStartOnly ) const +//UNUSED2009-05 { +//UNUSED2009-05 BOOL bRet = FALSE; +//UNUSED2009-05 ScRange aRange; +//UNUSED2009-05 if ( IsReference(aRange) ) +//UNUSED2009-05 { +//UNUSED2009-05 if ( bStartOnly ) +//UNUSED2009-05 bRet = ( rPos == aRange.aStart ); +//UNUSED2009-05 else +//UNUSED2009-05 bRet = ( aRange.In( rPos ) ); +//UNUSED2009-05 } +//UNUSED2009-05 return bRet; +//UNUSED2009-05 } + +BOOL ScRangeData::IsRangeAtBlock( const ScRange& rBlock ) const +{ + BOOL bRet = FALSE; + ScRange aRange; + if ( IsReference(aRange) ) + bRet = ( rBlock == aRange ); + return bRet; +} + +BOOL ScRangeData::IsReference( ScRange& rRange ) const +{ + if ( (eType & ( RT_ABSAREA | RT_REFAREA | RT_ABSPOS )) && pCode ) + return pCode->IsReference( rRange ); + + return FALSE; +} + +BOOL ScRangeData::IsReference( ScRange& rRange, const ScAddress& rPos ) const +{ + if ( (eType & ( RT_ABSAREA | RT_REFAREA | RT_ABSPOS ) ) && pCode ) + { + ::std::auto_ptr<ScTokenArray> pTemp( pCode->Clone() ); + ScCompiler aComp( pDoc, rPos, *pTemp); + aComp.SetGrammar(pDoc->GetGrammar()); + aComp.MoveRelWrap(MAXCOL, MAXROW); + return pTemp->IsReference( rRange ); + } + + return FALSE; +} + +BOOL ScRangeData::IsValidReference( ScRange& rRange ) const +{ + if ( (eType & ( RT_ABSAREA | RT_REFAREA | RT_ABSPOS ) ) && pCode ) + return pCode->IsValidReference( rRange ); + + return FALSE; +} + +void ScRangeData::UpdateTabRef(SCTAB nOldTable, USHORT nFlag, SCTAB nNewTable) +{ + pCode->Reset(); + if( pCode->GetNextReference() ) + { + ScRangeData* pRangeData = NULL; // must not be dereferenced + BOOL bChanged; + ScCompiler aComp( pDoc, aPos, *pCode); + aComp.SetGrammar(pDoc->GetGrammar()); + switch (nFlag) + { + case 1: // einfache InsertTab (doc.cxx) + pRangeData = aComp.UpdateInsertTab(nOldTable, TRUE ); // und CopyTab (doc2.cxx) + break; + case 2: // einfaches delete (doc.cxx) + pRangeData = aComp.UpdateDeleteTab(nOldTable, FALSE, TRUE, bChanged); + break; + case 3: // move (doc2.cxx) + { + pRangeData = aComp.UpdateMoveTab(nOldTable, nNewTable, TRUE ); + } + break; + default: + { + DBG_ERROR("ScRangeName::UpdateTabRef: Unknown Flag"); + } + break; + } + if (eType&RT_SHARED) + { + if (pRangeData) + eType = eType | RT_SHAREDMOD; + else + eType = eType & ~RT_SHAREDMOD; + } + } +} + + +void ScRangeData::MakeValidName( String& rName ) // static +{ + //ScCompiler::InitSymbolsNative(); + + // strip leading invalid characters + xub_StrLen nPos = 0; + xub_StrLen nLen = rName.Len(); + while ( nPos < nLen && !ScCompiler::IsCharFlagAllConventions( rName, nPos, SC_COMPILER_C_NAME) ) + ++nPos; + if ( nPos>0 ) + rName.Erase(0,nPos); + + // if the first character is an invalid start character, precede with '_' + if ( rName.Len() && !ScCompiler::IsCharFlagAllConventions( rName, 0, SC_COMPILER_C_CHAR_NAME ) ) + rName.Insert('_',0); + + // replace invalid with '_' + nLen = rName.Len(); + for (nPos=0; nPos<nLen; nPos++) + { + if ( !ScCompiler::IsCharFlagAllConventions( rName, nPos, SC_COMPILER_C_NAME) ) + rName.SetChar( nPos, '_' ); + } + + // Ensure that the proposed name is not a reference under any convention, + // same as in IsNameValid() + ScAddress aAddr; + ScRange aRange; + for (int nConv = FormulaGrammar::CONV_UNSPECIFIED; ++nConv < FormulaGrammar::CONV_LAST; ) + { + ScAddress::Details details( static_cast<FormulaGrammar::AddressConvention>( nConv ) ); + // Don't check Parse on VALID, any partial only VALID may result in + // #REF! during compile later! + while (aRange.Parse( rName, NULL, details) || aAddr.Parse( rName, NULL, details)) + { + //! Range Parse is partially valid also with invalid sheet name, + //! Address Parse dito, during compile name would generate a #REF! + if ( rName.SearchAndReplace( '.', '_' ) == STRING_NOTFOUND ) + rName.Insert('_',0); + } + } +} + +BOOL ScRangeData::IsNameValid( const String& rName, ScDocument* pDoc ) +{ + /* XXX If changed, sc/source/filter/ftools/ftools.cxx + * ScfTools::ConvertToScDefinedName needs to be changed too. */ + xub_StrLen nPos = 0; + xub_StrLen nLen = rName.Len(); + if ( !nLen || !ScCompiler::IsCharFlagAllConventions( rName, nPos++, SC_COMPILER_C_CHAR_NAME ) ) + return FALSE; + while ( nPos < nLen ) + { + if ( !ScCompiler::IsCharFlagAllConventions( rName, nPos++, SC_COMPILER_C_NAME ) ) + return FALSE; + } + ScAddress aAddr; + ScRange aRange; + for (int nConv = FormulaGrammar::CONV_UNSPECIFIED; ++nConv < FormulaGrammar::CONV_LAST; ) + { + ScAddress::Details details( static_cast<FormulaGrammar::AddressConvention>( nConv ) ); + // Don't check Parse on VALID, any partial only VALID may result in + // #REF! during compile later! + if (aRange.Parse( rName, pDoc, details) || aAddr.Parse( rName, pDoc, details)) + return FALSE; + } + return TRUE; +} + +void ScRangeData::SetMaxRow(SCROW nRow) +{ + mnMaxRow = nRow; +} + +SCROW ScRangeData::GetMaxRow() const +{ + return mnMaxRow >= 0 ? mnMaxRow : MAXROW; +} + +void ScRangeData::SetMaxCol(SCCOL nCol) +{ + mnMaxCol = nCol; +} + +SCCOL ScRangeData::GetMaxCol() const +{ + return mnMaxCol >= 0 ? mnMaxCol : MAXCOL; +} + + +USHORT ScRangeData::GetErrCode() +{ + return pCode ? pCode->GetCodeError() : 0; +} + +BOOL ScRangeData::HasReferences() const +{ + pCode->Reset(); + return BOOL( pCode->GetNextReference() != NULL ); +} + +// bei TransferTab von einem in ein anderes Dokument anpassen, +// um Referenzen auf die eigene Tabelle mitzubekommen + +void ScRangeData::TransferTabRef( SCTAB nOldTab, SCTAB nNewTab ) +{ + long nTabDiff = (long)nNewTab - nOldTab; + long nPosDiff = (long)nNewTab - aPos.Tab(); + aPos.SetTab( nNewTab ); + ScToken* t; + pCode->Reset(); + while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL ) + { + ScSingleRefData& rRef1 = t->GetSingleRef(); + if ( rRef1.IsTabRel() ) + rRef1.nTab = sal::static_int_cast<SCsTAB>( rRef1.nTab + nPosDiff ); + else + rRef1.nTab = sal::static_int_cast<SCsTAB>( rRef1.nTab + nTabDiff ); + if ( t->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; + if ( rRef2.IsTabRel() ) + rRef2.nTab = sal::static_int_cast<SCsTAB>( rRef2.nTab + nPosDiff ); + else + rRef2.nTab = sal::static_int_cast<SCsTAB>( rRef2.nTab + nTabDiff ); + } + } +} + +void ScRangeData::ReplaceRangeNamesInUse( const IndexMap& rMap ) +{ + bool bCompile = false; + for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) + { + if ( p->GetOpCode() == ocName ) + { + const sal_uInt16 nOldIndex = p->GetIndex(); + IndexMap::const_iterator itr = rMap.find(nOldIndex); + const sal_uInt16 nNewIndex = itr == rMap.end() ? nOldIndex : itr->second; + if ( nOldIndex != nNewIndex ) + { + p->SetIndex( nNewIndex ); + bCompile = true; + } + } + } + if ( bCompile ) + { + ScCompiler aComp( pDoc, aPos, *pCode); + aComp.SetGrammar(pDoc->GetGrammar()); + aComp.CompileTokenArray(); + } +} + + +void ScRangeData::ValidateTabRefs() +{ + // try to make sure all relative references and the reference position + // are within existing tables, so they can be represented as text + // (if the range of used tables is more than the existing tables, + // the result may still contain invalid tables, because the relative + // references aren't changed so formulas stay the same) + + // find range of used tables + + SCTAB nMinTab = aPos.Tab(); + SCTAB nMaxTab = nMinTab; + ScToken* t; + pCode->Reset(); + while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL ) + { + ScSingleRefData& rRef1 = t->GetSingleRef(); + if ( rRef1.IsTabRel() && !rRef1.IsTabDeleted() ) + { + if ( rRef1.nTab < nMinTab ) + nMinTab = rRef1.nTab; + if ( rRef1.nTab > nMaxTab ) + nMaxTab = rRef1.nTab; + } + if ( t->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; + if ( rRef2.IsTabRel() && !rRef2.IsTabDeleted() ) + { + if ( rRef2.nTab < nMinTab ) + nMinTab = rRef2.nTab; + if ( rRef2.nTab > nMaxTab ) + nMaxTab = rRef2.nTab; + } + } + } + + SCTAB nTabCount = pDoc->GetTableCount(); + if ( nMaxTab >= nTabCount && nMinTab > 0 ) + { + // move position and relative tab refs + // The formulas that use the name are not changed by this + + SCTAB nMove = nMinTab; + aPos.SetTab( aPos.Tab() - nMove ); + + pCode->Reset(); + while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL ) + { + ScSingleRefData& rRef1 = t->GetSingleRef(); + if ( rRef1.IsTabRel() && !rRef1.IsTabDeleted() ) + rRef1.nTab = sal::static_int_cast<SCsTAB>( rRef1.nTab - nMove ); + if ( t->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; + if ( rRef2.IsTabRel() && !rRef2.IsTabDeleted() ) + rRef2.nTab = sal::static_int_cast<SCsTAB>( rRef2.nTab - nMove ); + } + } + } +} + + +extern "C" int +#ifdef WNT +__cdecl +#endif +ScRangeData_QsortNameCompare( const void* p1, const void* p2 ) +{ + return (int) ScGlobal::GetCollator()->compareString( + (*(const ScRangeData**)p1)->GetName(), + (*(const ScRangeData**)p2)->GetName() ); +} + + +//======================================================================== +// ScRangeName +//======================================================================== + +ScRangeName::ScRangeName(const ScRangeName& rScRangeName, ScDocument* pDocument) : + ScSortedCollection ( rScRangeName ), + pDoc ( pDocument ), + nSharedMaxIndex (rScRangeName.nSharedMaxIndex) +{ + for (USHORT i = 0; i < nCount; i++) + { + ((ScRangeData*)At(i))->SetDocument(pDocument); + ((ScRangeData*)At(i))->SetIndex(((ScRangeData*)rScRangeName.At(i))->GetIndex()); + } +} + +short ScRangeName::Compare(ScDataObject* pKey1, ScDataObject* pKey2) const +{ + USHORT i1 = ((ScRangeData*)pKey1)->GetIndex(); + USHORT i2 = ((ScRangeData*)pKey2)->GetIndex(); + return (short) i1 - (short) i2; +} + +BOOL ScRangeName::SearchNameUpper( const String& rUpperName, USHORT& rIndex ) const +{ + // SearchNameUpper must be called with an upper-case search string + + USHORT i = 0; + while (i < nCount) + { + if ( ((*this)[i])->GetUpperName() == rUpperName ) + { + rIndex = i; + return TRUE; + } + i++; + } + return FALSE; +} + +BOOL ScRangeName::SearchName( const String& rName, USHORT& rIndex ) const +{ + if ( nCount > 0 ) + return SearchNameUpper( ScGlobal::pCharClass->upper( rName ), rIndex ); + else + return FALSE; +} + +void ScRangeName::UpdateReference( UpdateRefMode eUpdateRefMode, + const ScRange& rRange, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + for (USHORT i=0; i<nCount; i++) + ((ScRangeData*)pItems[i])->UpdateReference(eUpdateRefMode, rRange, + nDx, nDy, nDz); +} + +void ScRangeName::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest ) +{ + for (USHORT i=0; i<nCount; i++) + ((ScRangeData*)pItems[i])->UpdateTranspose( rSource, rDest ); +} + +void ScRangeName::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) +{ + for (USHORT i=0; i<nCount; i++) + ((ScRangeData*)pItems[i])->UpdateGrow( rArea, nGrowX, nGrowY ); +} + +BOOL ScRangeName::IsEqual(ScDataObject* pKey1, ScDataObject* pKey2) const +{ + return *(ScRangeData*)pKey1 == *(ScRangeData*)pKey2; +} + +BOOL ScRangeName::Insert(ScDataObject* pScDataObject) +{ + if (!((ScRangeData*)pScDataObject)->GetIndex()) // schon gesetzt? + { + ((ScRangeData*)pScDataObject)->SetIndex( GetEntryIndex() ); + } + + return ScSortedCollection::Insert(pScDataObject); +} + +// Suche nach einem freien Index + +USHORT ScRangeName::GetEntryIndex() +{ + USHORT nLast = 0; + for ( USHORT i = 0; i < nCount; i++ ) + { + USHORT nIdx = ((ScRangeData*)pItems[i])->GetIndex(); + if( nIdx > nLast ) + { + nLast = nIdx; + } + } + return nLast + 1; +} + +ScRangeData* ScRangeName::FindIndex( USHORT nIndex ) +{ + ScRangeData aDataObj( nIndex ); + USHORT n; + if( Search( &aDataObj, n ) ) + return (*this)[ n ]; + else + return NULL; +} + +//UNUSED2009-05 ScRangeData* ScRangeName::GetRangeAtCursor( const ScAddress& rPos, BOOL bStartOnly ) const +//UNUSED2009-05 { +//UNUSED2009-05 if ( pItems ) +//UNUSED2009-05 { +//UNUSED2009-05 for ( USHORT i = 0; i < nCount; i++ ) +//UNUSED2009-05 if ( ((ScRangeData*)pItems[i])->IsRangeAtCursor( rPos, bStartOnly ) ) +//UNUSED2009-05 return (ScRangeData*)pItems[i]; +//UNUSED2009-05 } +//UNUSED2009-05 return NULL; +//UNUSED2009-05 } + +ScRangeData* ScRangeName::GetRangeAtBlock( const ScRange& rBlock ) const +{ + if ( pItems ) + { + for ( USHORT i = 0; i < nCount; i++ ) + if ( ((ScRangeData*)pItems[i])->IsRangeAtBlock( rBlock ) ) + return (ScRangeData*)pItems[i]; + } + return NULL; +} + +void ScRangeName::UpdateTabRef(SCTAB nOldTable, USHORT nFlag, SCTAB nNewTable) +{ + for (USHORT i=0; i<nCount; i++) + ((ScRangeData*)pItems[i])->UpdateTabRef(nOldTable, nFlag, nNewTable); +} + + + + diff --git a/sc/source/core/tool/rangeseq.cxx b/sc/source/core/tool/rangeseq.cxx new file mode 100644 index 000000000000..5584fb1c37e2 --- /dev/null +++ b/sc/source/core/tool/rangeseq.cxx @@ -0,0 +1,476 @@ +/************************************************************************* + * + * 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 <svl/zforlist.hxx> +#include <rtl/math.hxx> +#include <tools/debug.hxx> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include "rangeseq.hxx" +#include "document.hxx" +#include "scmatrix.hxx" +#include "cell.hxx" + +using namespace com::sun::star; + +//------------------------------------------------------------------------ + +long lcl_DoubleToLong( double fVal ) +{ + double fInt = (fVal >= 0.0) ? ::rtl::math::approxFloor( fVal ) : + ::rtl::math::approxCeil( fVal ); + if ( fInt >= LONG_MIN && fInt <= LONG_MAX ) + return (long)fInt; + else + return 0; // out of range +} + +BOOL ScRangeToSequence::FillLongArray( uno::Any& rAny, ScDocument* pDoc, const ScRange& rRange ) +{ + SCTAB nTab = rRange.aStart.Tab(); + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + long nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col(); + long nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row(); + + uno::Sequence< uno::Sequence<INT32> > aRowSeq( nRowCount ); + uno::Sequence<INT32>* pRowAry = aRowSeq.getArray(); + for (long nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<INT32> aColSeq( nColCount ); + INT32* pColAry = aColSeq.getArray(); + for (long nCol = 0; nCol < nColCount; nCol++) + pColAry[nCol] = lcl_DoubleToLong( pDoc->GetValue( + ScAddress( (SCCOL)(nStartCol+nCol), (SCROW)(nStartRow+nRow), nTab ) ) ); + + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return TRUE; //! check for errors +} + + +BOOL ScRangeToSequence::FillLongArray( uno::Any& rAny, const ScMatrix* pMatrix ) +{ + if (!pMatrix) + return FALSE; + + SCSIZE nColCount; + SCSIZE nRowCount; + pMatrix->GetDimensions( nColCount, nRowCount ); + + uno::Sequence< uno::Sequence<INT32> > aRowSeq( static_cast<sal_Int32>(nRowCount) ); + uno::Sequence<INT32>* pRowAry = aRowSeq.getArray(); + for (SCSIZE nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<INT32> aColSeq( static_cast<sal_Int32>(nColCount) ); + INT32* pColAry = aColSeq.getArray(); + for (SCSIZE nCol = 0; nCol < nColCount; nCol++) + if ( pMatrix->IsString( nCol, nRow ) ) + pColAry[nCol] = 0; + else + pColAry[nCol] = lcl_DoubleToLong( pMatrix->GetDouble( nCol, nRow ) ); + + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return TRUE; +} + +//------------------------------------------------------------------------ + +BOOL ScRangeToSequence::FillDoubleArray( uno::Any& rAny, ScDocument* pDoc, const ScRange& rRange ) +{ + SCTAB nTab = rRange.aStart.Tab(); + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + long nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col(); + long nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row(); + + uno::Sequence< uno::Sequence<double> > aRowSeq( nRowCount ); + uno::Sequence<double>* pRowAry = aRowSeq.getArray(); + for (long nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<double> aColSeq( nColCount ); + double* pColAry = aColSeq.getArray(); + for (long nCol = 0; nCol < nColCount; nCol++) + pColAry[nCol] = pDoc->GetValue( + ScAddress( (SCCOL)(nStartCol+nCol), (SCROW)(nStartRow+nRow), nTab ) ); + + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return TRUE; //! check for errors +} + + +BOOL ScRangeToSequence::FillDoubleArray( uno::Any& rAny, const ScMatrix* pMatrix ) +{ + if (!pMatrix) + return FALSE; + + SCSIZE nColCount; + SCSIZE nRowCount; + pMatrix->GetDimensions( nColCount, nRowCount ); + + uno::Sequence< uno::Sequence<double> > aRowSeq( static_cast<sal_Int32>(nRowCount) ); + uno::Sequence<double>* pRowAry = aRowSeq.getArray(); + for (SCSIZE nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<double> aColSeq( static_cast<sal_Int32>(nColCount) ); + double* pColAry = aColSeq.getArray(); + for (SCSIZE nCol = 0; nCol < nColCount; nCol++) + if ( pMatrix->IsString( nCol, nRow ) ) + pColAry[nCol] = 0.0; + else + pColAry[nCol] = pMatrix->GetDouble( nCol, nRow ); + + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return TRUE; +} + +//------------------------------------------------------------------------ + +BOOL ScRangeToSequence::FillStringArray( uno::Any& rAny, ScDocument* pDoc, const ScRange& rRange ) +{ + SCTAB nTab = rRange.aStart.Tab(); + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + long nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col(); + long nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row(); + + String aDocStr; + + uno::Sequence< uno::Sequence<rtl::OUString> > aRowSeq( nRowCount ); + uno::Sequence<rtl::OUString>* pRowAry = aRowSeq.getArray(); + for (long nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<rtl::OUString> aColSeq( nColCount ); + rtl::OUString* pColAry = aColSeq.getArray(); + for (long nCol = 0; nCol < nColCount; nCol++) + { + pDoc->GetString( (SCCOL)(nStartCol+nCol), (SCROW)(nStartRow+nRow), nTab, aDocStr ); + pColAry[nCol] = rtl::OUString( aDocStr ); + } + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return TRUE; //! check for errors +} + + +BOOL ScRangeToSequence::FillStringArray( uno::Any& rAny, const ScMatrix* pMatrix, + SvNumberFormatter* pFormatter ) +{ + if (!pMatrix) + return FALSE; + + SCSIZE nColCount; + SCSIZE nRowCount; + pMatrix->GetDimensions( nColCount, nRowCount ); + + uno::Sequence< uno::Sequence<rtl::OUString> > aRowSeq( static_cast<sal_Int32>(nRowCount) ); + uno::Sequence<rtl::OUString>* pRowAry = aRowSeq.getArray(); + for (SCSIZE nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<rtl::OUString> aColSeq( static_cast<sal_Int32>(nColCount) ); + rtl::OUString* pColAry = aColSeq.getArray(); + for (SCSIZE nCol = 0; nCol < nColCount; nCol++) + { + String aStr; + if ( pMatrix->IsString( nCol, nRow ) ) + { + if ( !pMatrix->IsEmpty( nCol, nRow ) ) + aStr = pMatrix->GetString( nCol, nRow ); + } + else if ( pFormatter ) + { + double fVal = pMatrix->GetDouble( nCol, nRow ); + Color* pColor; + pFormatter->GetOutputString( fVal, 0, aStr, &pColor ); + } + pColAry[nCol] = rtl::OUString( aStr ); + } + + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return TRUE; +} + +//------------------------------------------------------------------------ + +double lcl_GetValueFromCell( ScBaseCell& rCell ) +{ + //! ScBaseCell member function? + + CellType eType = rCell.GetCellType(); + if ( eType == CELLTYPE_VALUE ) + return ((ScValueCell&)rCell).GetValue(); + else if ( eType == CELLTYPE_FORMULA ) + return ((ScFormulaCell&)rCell).GetValue(); // called only if result is value + + DBG_ERROR( "GetValueFromCell: wrong type" ); + return 0; +} + +BOOL ScRangeToSequence::FillMixedArray( uno::Any& rAny, ScDocument* pDoc, const ScRange& rRange, + BOOL bAllowNV ) +{ + SCTAB nTab = rRange.aStart.Tab(); + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + long nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col(); + long nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row(); + + String aDocStr; + BOOL bHasErrors = FALSE; + + uno::Sequence< uno::Sequence<uno::Any> > aRowSeq( nRowCount ); + uno::Sequence<uno::Any>* pRowAry = aRowSeq.getArray(); + for (long nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<uno::Any> aColSeq( nColCount ); + uno::Any* pColAry = aColSeq.getArray(); + for (long nCol = 0; nCol < nColCount; nCol++) + { + uno::Any& rElement = pColAry[nCol]; + + ScAddress aPos( (SCCOL)(nStartCol+nCol), (SCROW)(nStartRow+nRow), nTab ); + ScBaseCell* pCell = pDoc->GetCell( aPos ); + if ( pCell ) + { + if ( pCell->GetCellType() == CELLTYPE_FORMULA && + ((ScFormulaCell*)pCell)->GetErrCode() != 0 ) + { + // if NV is allowed, leave empty for errors + bHasErrors = TRUE; + } + else if ( pCell->HasValueData() ) + rElement <<= (double) lcl_GetValueFromCell( *pCell ); + else + rElement <<= rtl::OUString( pCell->GetStringData() ); + } + else + rElement <<= rtl::OUString(); // empty: empty string + } + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return bAllowNV || !bHasErrors; +} + + +BOOL ScRangeToSequence::FillMixedArray( uno::Any& rAny, const ScMatrix* pMatrix, bool bDataTypes ) +{ + if (!pMatrix) + return FALSE; + + SCSIZE nColCount; + SCSIZE nRowCount; + pMatrix->GetDimensions( nColCount, nRowCount ); + + uno::Sequence< uno::Sequence<uno::Any> > aRowSeq( static_cast<sal_Int32>(nRowCount) ); + uno::Sequence<uno::Any>* pRowAry = aRowSeq.getArray(); + for (SCSIZE nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<uno::Any> aColSeq( static_cast<sal_Int32>(nColCount) ); + uno::Any* pColAry = aColSeq.getArray(); + for (SCSIZE nCol = 0; nCol < nColCount; nCol++) + { + if ( pMatrix->IsString( nCol, nRow ) ) + { + String aStr; + if ( !pMatrix->IsEmpty( nCol, nRow ) ) + aStr = pMatrix->GetString( nCol, nRow ); + pColAry[nCol] <<= rtl::OUString( aStr ); + } + else + { + double fVal = pMatrix->GetDouble( nCol, nRow ); + if (bDataTypes && pMatrix->IsBoolean( nCol, nRow )) + pColAry[nCol] <<= (fVal ? true : false); + else + pColAry[nCol] <<= fVal; + } + } + + pRowAry[nRow] = aColSeq; + } + + rAny <<= aRowSeq; + return TRUE; +} + +//------------------------------------------------------------------------ + +// static +bool ScApiTypeConversion::ConvertAnyToDouble( double & o_fVal, + com::sun::star::uno::TypeClass & o_eClass, + const com::sun::star::uno::Any & rAny ) +{ + bool bRet = false; + o_eClass = rAny.getValueTypeClass(); + switch (o_eClass) + { + //! extract integer values + case uno::TypeClass_ENUM: + case uno::TypeClass_BOOLEAN: + case uno::TypeClass_CHAR: + case uno::TypeClass_BYTE: + case uno::TypeClass_SHORT: + case uno::TypeClass_UNSIGNED_SHORT: + case uno::TypeClass_LONG: + case uno::TypeClass_UNSIGNED_LONG: + case uno::TypeClass_FLOAT: + case uno::TypeClass_DOUBLE: + rAny >>= o_fVal; + bRet = true; + break; + default: + ; // nothing, avoid warning + } + if (!bRet) + o_fVal = 0.0; + return bRet; +} + +//------------------------------------------------------------------------ + +// static +ScMatrixRef ScSequenceToMatrix::CreateMixedMatrix( const com::sun::star::uno::Any & rAny ) +{ + ScMatrixRef xMatrix; + uno::Sequence< uno::Sequence< uno::Any > > aSequence; + if ( rAny >>= aSequence ) + { + sal_Int32 nRowCount = aSequence.getLength(); + const uno::Sequence<uno::Any>* pRowArr = aSequence.getConstArray(); + sal_Int32 nMaxColCount = 0; + sal_Int32 nCol, nRow; + for (nRow=0; nRow<nRowCount; nRow++) + { + sal_Int32 nTmp = pRowArr[nRow].getLength(); + if ( nTmp > nMaxColCount ) + nMaxColCount = nTmp; + } + if ( nMaxColCount && nRowCount ) + { + rtl::OUString aUStr; + xMatrix = new ScMatrix( + static_cast<SCSIZE>(nMaxColCount), + static_cast<SCSIZE>(nRowCount) ); + ScMatrix* pMatrix = xMatrix; + SCSIZE nCols, nRows; + pMatrix->GetDimensions( nCols, nRows); + if (nCols != static_cast<SCSIZE>(nMaxColCount) || nRows != static_cast<SCSIZE>(nRowCount)) + { + DBG_ERRORFILE( "ScSequenceToMatrix::CreateMixedMatrix: matrix exceeded max size, returning NULL matrix"); + return NULL; + } + for (nRow=0; nRow<nRowCount; nRow++) + { + sal_Int32 nColCount = pRowArr[nRow].getLength(); + const uno::Any* pColArr = pRowArr[nRow].getConstArray(); + for (nCol=0; nCol<nColCount; nCol++) + { + double fVal; + uno::TypeClass eClass; + if (ScApiTypeConversion::ConvertAnyToDouble( fVal, eClass, pColArr[nCol])) + { + if (eClass == uno::TypeClass_BOOLEAN) + pMatrix->PutBoolean( (fVal ? true : false), + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + else + pMatrix->PutDouble( fVal, + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + } + else + { + // Try string, else use empty as last resort. + + //Reflection* pRefl = pColArr[nCol].getReflection(); + //if ( pRefl->equals( *OUString_getReflection() ) ) + if ( pColArr[nCol] >>= aUStr ) + pMatrix->PutString( String( aUStr ), + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + else + pMatrix->PutEmpty( + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + } + } + for (nCol=nColCount; nCol<nMaxColCount; nCol++) + { + pMatrix->PutEmpty( + static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow) ); + } + } + } + } + return xMatrix; +} + + +//------------------------------------------------------------------------ + +BOOL ScByteSequenceToString::GetString( String& rString, const uno::Any& rAny, + sal_uInt16 nEncoding ) +{ + uno::Sequence<sal_Int8> aSeq; + if ( rAny >>= aSeq ) + { + rString = String( (const sal_Char*)aSeq.getConstArray(), + (xub_StrLen)aSeq.getLength(), nEncoding ); + rString.EraseTrailingChars( (sal_Unicode) 0 ); + return TRUE; + } + return FALSE; +} + +//------------------------------------------------------------------------ + diff --git a/sc/source/core/tool/rangeutl.cxx b/sc/source/core/tool/rangeutl.cxx new file mode 100644 index 000000000000..9f6526a54cf6 --- /dev/null +++ b/sc/source/core/tool/rangeutl.cxx @@ -0,0 +1,1054 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <tools/debug.hxx> + +#include "rangeutl.hxx" +#include "document.hxx" +#include "global.hxx" +#include "dbcolect.hxx" +#include "rangenam.hxx" +#include "scresid.hxx" +#include "globstr.hrc" +#include "convuno.hxx" +#include "externalrefmgr.hxx" +#include "compiler.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::formula::FormulaGrammar; +using namespace ::com::sun::star; + +//------------------------------------------------------------------------ + +BOOL ScRangeUtil::MakeArea( const String& rAreaStr, + ScArea& rArea, + ScDocument* pDoc, + SCTAB nTab, + ScAddress::Details const & rDetails ) const +{ + // Eingabe in rAreaStr: "$Tabelle1.$A1:$D17" + + // BROKEN BROKEN BROKEN + // but it is only used in the consolidate dialog. Ignore for now. + + BOOL nSuccess = FALSE; + USHORT nPointPos = rAreaStr.Search('.'); + USHORT nColonPos = rAreaStr.Search(':'); + String aStrArea( rAreaStr ); + ScRefAddress startPos; + ScRefAddress endPos; + + if ( nColonPos == STRING_NOTFOUND ) + if ( nPointPos != STRING_NOTFOUND ) + { + aStrArea += ':'; + aStrArea += rAreaStr.Copy( nPointPos+1 ); // '.' nicht mitkopieren + } + + nSuccess = ConvertDoubleRef( pDoc, aStrArea, nTab, startPos, endPos, rDetails ); + + if ( nSuccess ) + rArea = ScArea( startPos.Tab(), + startPos.Col(), startPos.Row(), + endPos.Col(), endPos.Row() ); + + return nSuccess; +} + +//------------------------------------------------------------------------ + +void ScRangeUtil::CutPosString( const String& theAreaStr, + String& thePosStr ) const +{ + String aPosStr; + // BROKEN BROKEN BROKEN + // but it is only used in the consolidate dialog. Ignore for now. + + USHORT nColonPos = theAreaStr.Search(':'); + + if ( nColonPos != STRING_NOTFOUND ) + aPosStr = theAreaStr.Copy( 0, nColonPos ); // ':' nicht mitkopieren + else + aPosStr = theAreaStr; + + thePosStr = aPosStr; +} + +//------------------------------------------------------------------------ + +BOOL ScRangeUtil::IsAbsTabArea( const String& rAreaStr, + ScDocument* pDoc, + ScArea*** pppAreas, + USHORT* pAreaCount, + BOOL /* bAcceptCellRef */, + ScAddress::Details const & rDetails ) const +{ + DBG_ASSERT( pDoc, "Kein Dokument uebergeben!" ); + if ( !pDoc ) + return FALSE; + + // BROKEN BROKEN BROKEN + // but it is only used in the consolidate dialog. Ignore for now. + + /* + * Erwartet wird ein String der Form + * "$Tabelle1.$A$1:$Tabelle3.$D$17" + * Wenn bAcceptCellRef == TRUE ist, wird auch ein String der Form + * "$Tabelle1.$A$1" + * akzeptiert. + * + * als Ergebnis wird ein ScArea-Array angelegt, + * welches ueber ppAreas bekannt gegeben wird und auch + * wieder geloescht werden muss! + */ + + BOOL bStrOk = FALSE; + String aTempAreaStr(rAreaStr); + String aStartPosStr; + String aEndPosStr; + + if ( STRING_NOTFOUND == aTempAreaStr.Search(':') ) + { + aTempAreaStr.Append(':'); + aTempAreaStr.Append(rAreaStr); + } + + USHORT nColonPos = aTempAreaStr.Search(':'); + + if ( STRING_NOTFOUND != nColonPos + && STRING_NOTFOUND != aTempAreaStr.Search('.') ) + { + ScRefAddress aStartPos; + ScRefAddress aEndPos; + + aStartPosStr = aTempAreaStr.Copy( 0, nColonPos ); + aEndPosStr = aTempAreaStr.Copy( nColonPos+1, STRING_LEN ); + + if ( ConvertSingleRef( pDoc, aStartPosStr, 0, aStartPos, rDetails ) ) + { + if ( ConvertSingleRef( pDoc, aEndPosStr, aStartPos.Tab(), aEndPos, rDetails ) ) + { + aStartPos.SetRelCol( FALSE ); + aStartPos.SetRelRow( FALSE ); + aStartPos.SetRelTab( FALSE ); + aEndPos.SetRelCol( FALSE ); + aEndPos.SetRelRow( FALSE ); + aEndPos.SetRelTab( FALSE ); + + bStrOk = TRUE; + + if ( pppAreas && pAreaCount ) // Array zurueckgegeben? + { + SCTAB nStartTab = aStartPos.Tab(); + SCTAB nEndTab = aEndPos.Tab(); + USHORT nTabCount = static_cast<USHORT>(nEndTab-nStartTab+1); + ScArea** theAreas = new ScArea*[nTabCount]; + SCTAB nTab = 0; + USHORT i = 0; + ScArea theArea( 0, aStartPos.Col(), aStartPos.Row(), + aEndPos.Col(), aEndPos.Row() ); + + nTab = nStartTab; + for ( i=0; i<nTabCount; i++ ) + { + theAreas[i] = new ScArea( theArea ); + theAreas[i]->nTab = nTab; + nTab++; + } + *pppAreas = theAreas; + *pAreaCount = nTabCount; + } + } + } + } + + return bStrOk; +} + +//------------------------------------------------------------------------ + +BOOL ScRangeUtil::IsAbsArea( const String& rAreaStr, + ScDocument* pDoc, + SCTAB nTab, + String* pCompleteStr, + ScRefAddress* pStartPos, + ScRefAddress* pEndPos, + ScAddress::Details const & rDetails ) const +{ + BOOL bIsAbsArea = FALSE; + ScRefAddress startPos; + ScRefAddress endPos; + + bIsAbsArea = ConvertDoubleRef( pDoc, rAreaStr, nTab, startPos, endPos, rDetails ); + + if ( bIsAbsArea ) + { + startPos.SetRelCol( FALSE ); + startPos.SetRelRow( FALSE ); + startPos.SetRelTab( FALSE ); + endPos .SetRelCol( FALSE ); + endPos .SetRelRow( FALSE ); + endPos .SetRelTab( FALSE ); + + if ( pCompleteStr ) + { + *pCompleteStr = startPos.GetRefString( pDoc, MAXTAB+1, rDetails ); + *pCompleteStr += ':'; + *pCompleteStr += endPos .GetRefString( pDoc, nTab, rDetails ); + } + + if ( pStartPos && pEndPos ) + { + *pStartPos = startPos; + *pEndPos = endPos; + } + } + + return bIsAbsArea; +} + +//------------------------------------------------------------------------ + +BOOL ScRangeUtil::IsAbsPos( const String& rPosStr, + ScDocument* pDoc, + SCTAB nTab, + String* pCompleteStr, + ScRefAddress* pPosTripel, + ScAddress::Details const & rDetails ) const +{ + BOOL bIsAbsPos = FALSE; + ScRefAddress thePos; + + bIsAbsPos = ConvertSingleRef( pDoc, rPosStr, nTab, thePos, rDetails ); + thePos.SetRelCol( FALSE ); + thePos.SetRelRow( FALSE ); + thePos.SetRelTab( FALSE ); + + if ( bIsAbsPos ) + { + if ( pPosTripel ) + *pPosTripel = thePos; + if ( pCompleteStr ) + *pCompleteStr = thePos.GetRefString( pDoc, MAXTAB+1, rDetails ); + } + + return bIsAbsPos; +} + +//------------------------------------------------------------------------ + +BOOL ScRangeUtil::MakeRangeFromName ( + const String& rName, + ScDocument* pDoc, + SCTAB nCurTab, + ScRange& rRange, + RutlNameScope eScope, + ScAddress::Details const & rDetails ) const +{ + BOOL bResult=FALSE; + ScRangeUtil aRangeUtil; + SCTAB nTab = 0; + SCCOL nColStart = 0; + SCCOL nColEnd = 0; + SCROW nRowStart = 0; + SCROW nRowEnd = 0; + + if( eScope==RUTL_NAMES ) + { + ScRangeName& rRangeNames = *(pDoc->GetRangeName()); + USHORT nAt = 0; + + if ( rRangeNames.SearchName( rName, nAt ) ) + { + ScRangeData* pData = rRangeNames[nAt]; + String aStrArea; + ScRefAddress aStartPos; + ScRefAddress aEndPos; + + pData->GetSymbol( aStrArea ); + + if ( IsAbsArea( aStrArea, pDoc, nCurTab, + NULL, &aStartPos, &aEndPos, rDetails ) ) + { + nTab = aStartPos.Tab(); + nColStart = aStartPos.Col(); + nRowStart = aStartPos.Row(); + nColEnd = aEndPos.Col(); + nRowEnd = aEndPos.Row(); + bResult = TRUE; + } + else + { + CutPosString( aStrArea, aStrArea ); + + if ( IsAbsPos( aStrArea, pDoc, nCurTab, + NULL, &aStartPos, rDetails ) ) + { + nTab = aStartPos.Tab(); + nColStart = nColEnd = aStartPos.Col(); + nRowStart = nRowEnd = aStartPos.Row(); + bResult = TRUE; + } + } + } + } + else if( eScope==RUTL_DBASE ) + { + ScDBCollection& rDbNames = *(pDoc->GetDBCollection()); + USHORT nAt = 0; + + if ( rDbNames.SearchName( rName, nAt ) ) + { + ScDBData* pData = rDbNames[nAt]; + + pData->GetArea( nTab, nColStart, nRowStart, + nColEnd, nRowEnd ); + bResult = TRUE; + } + } + else + { + DBG_ERROR( "ScRangeUtil::MakeRangeFromName" ); + } + + if( bResult ) + { + rRange = ScRange( nColStart, nRowStart, nTab, nColEnd, nRowEnd, nTab ); + } + + return bResult; +} + +//======================================================================== + +void ScRangeStringConverter::AssignString( + OUString& rString, + const OUString& rNewStr, + sal_Bool bAppendStr, + sal_Unicode cSeperator) +{ + if( bAppendStr ) + { + if( rNewStr.getLength() ) + { + if( rString.getLength() ) + rString += rtl::OUString(cSeperator); + rString += rNewStr; + } + } + else + rString = rNewStr; +} + +sal_Int32 ScRangeStringConverter::IndexOf( + const OUString& rString, + sal_Unicode cSearchChar, + sal_Int32 nOffset, + sal_Unicode cQuote ) +{ + sal_Int32 nLength = rString.getLength(); + sal_Int32 nIndex = nOffset; + sal_Bool bQuoted = sal_False; + sal_Bool bExitLoop = sal_False; + + while( !bExitLoop && (nIndex < nLength) ) + { + sal_Unicode cCode = rString[ nIndex ]; + bExitLoop = (cCode == cSearchChar) && !bQuoted; + bQuoted = (bQuoted != (cCode == cQuote)); + if( !bExitLoop ) + nIndex++; + } + return (nIndex < nLength) ? nIndex : -1; +} + +sal_Int32 ScRangeStringConverter::IndexOfDifferent( + const OUString& rString, + sal_Unicode cSearchChar, + sal_Int32 nOffset ) +{ + sal_Int32 nLength = rString.getLength(); + sal_Int32 nIndex = nOffset; + sal_Bool bExitLoop = sal_False; + + while( !bExitLoop && (nIndex < nLength) ) + { + bExitLoop = (rString[ nIndex ] != cSearchChar); + if( !bExitLoop ) + nIndex++; + } + return (nIndex < nLength) ? nIndex : -1; +} + +void ScRangeStringConverter::GetTokenByOffset( + OUString& rToken, + const OUString& rString, + sal_Int32& nOffset, + sal_Unicode cSeperator, + sal_Unicode cQuote) +{ + sal_Int32 nLength = rString.getLength(); + if( nOffset >= nLength ) + { + rToken = OUString(); + nOffset = -1; + } + else + { + sal_Int32 nTokenEnd = IndexOf( rString, cSeperator, nOffset, cQuote ); + if( nTokenEnd < 0 ) + nTokenEnd = nLength; + rToken = rString.copy( nOffset, nTokenEnd - nOffset ); + + sal_Int32 nNextBegin = IndexOfDifferent( rString, cSeperator, nTokenEnd ); + nOffset = (nNextBegin < 0) ? nLength : nNextBegin; + } +} + +void ScRangeStringConverter::AppendTableName(OUStringBuffer& rBuf, const OUString& rTabName, sal_Unicode /* cQuote */) +{ + // quote character is always "'" + String aQuotedTab(rTabName); + ScCompiler::CheckTabQuotes(aQuotedTab, ::formula::FormulaGrammar::CONV_OOO); + rBuf.append(aQuotedTab); +} + +sal_Int32 ScRangeStringConverter::GetTokenCount( const OUString& rString, sal_Unicode cSeperator, sal_Unicode cQuote ) +{ + OUString sToken; + sal_Int32 nCount = 0; + sal_Int32 nOffset = 0; + while( nOffset >= 0 ) + { + GetTokenByOffset( sToken, rString, nOffset, cQuote, cSeperator ); + if( nOffset >= 0 ) + nCount++; + } + return nCount; +} + +//___________________________________________________________________ + +sal_Bool ScRangeStringConverter::GetAddressFromString( + ScAddress& rAddress, + const OUString& rAddressStr, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Int32& nOffset, + sal_Unicode cSeperator, + sal_Unicode cQuote ) +{ + OUString sToken; + GetTokenByOffset( sToken, rAddressStr, nOffset, cSeperator, cQuote ); + if( nOffset >= 0 ) + { + if ((rAddress.Parse( sToken, const_cast<ScDocument*>(pDocument), eConv ) & SCA_VALID) == SCA_VALID) + return true; + } + return sal_False; +} + +sal_Bool ScRangeStringConverter::GetRangeFromString( + ScRange& rRange, + const OUString& rRangeStr, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Int32& nOffset, + sal_Unicode cSeperator, + sal_Unicode cQuote ) +{ + OUString sToken; + sal_Bool bResult(sal_False); + GetTokenByOffset( sToken, rRangeStr, nOffset, cSeperator, cQuote ); + if( nOffset >= 0 ) + { + sal_Int32 nIndex = IndexOf( sToken, ':', 0, cQuote ); + String aUIString(sToken); + + if( nIndex < 0 ) + { + if ( aUIString.GetChar(0) == (sal_Unicode) '.' ) + aUIString.Erase( 0, 1 ); + bResult = ((rRange.aStart.Parse( aUIString, const_cast<ScDocument*> (pDocument), eConv) & SCA_VALID) == SCA_VALID); + rRange.aEnd = rRange.aStart; + } + else + { + if ( aUIString.GetChar(0) == (sal_Unicode) '.' ) + { + aUIString.Erase( 0, 1 ); + --nIndex; + } + + if ( nIndex < aUIString.Len() - 1 && + aUIString.GetChar((xub_StrLen)nIndex + 1) == (sal_Unicode) '.' ) + aUIString.Erase( (xub_StrLen)nIndex + 1, 1 ); + + bResult = ((rRange.Parse(aUIString, const_cast<ScDocument*> (pDocument), eConv) & SCA_VALID) == SCA_VALID); + + // #i77703# chart ranges in the file format contain both sheet names, even for an external reference sheet. + // This isn't parsed by ScRange, so try to parse the two Addresses then. + if (!bResult) + { + bResult = ((rRange.aStart.Parse( aUIString.Copy(0, (xub_StrLen)nIndex), const_cast<ScDocument*>(pDocument), + eConv) & SCA_VALID) == SCA_VALID) && + ((rRange.aEnd.Parse( aUIString.Copy((xub_StrLen)nIndex+1), const_cast<ScDocument*>(pDocument), + eConv) & SCA_VALID) == SCA_VALID); + } + } + } + return bResult; +} + +sal_Bool ScRangeStringConverter::GetRangeListFromString( + ScRangeList& rRangeList, + const OUString& rRangeListStr, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Unicode cSeperator, + sal_Unicode cQuote ) +{ + sal_Bool bRet = sal_True; + DBG_ASSERT( rRangeListStr.getLength(), "ScXMLConverter::GetRangeListFromString - empty string!" ); + sal_Int32 nOffset = 0; + while( nOffset >= 0 ) + { + ScRange* pRange = new ScRange; + if( GetRangeFromString( *pRange, rRangeListStr, pDocument, eConv, nOffset, cSeperator, cQuote ) && (nOffset >= 0) ) + rRangeList.Insert( pRange, LIST_APPEND ); + else if (nOffset > -1) + bRet = sal_False; + } + return bRet; +} + + +//___________________________________________________________________ + +sal_Bool ScRangeStringConverter::GetAreaFromString( + ScArea& rArea, + const OUString& rRangeStr, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Int32& nOffset, + sal_Unicode cSeperator, + sal_Unicode cQuote ) +{ + ScRange aScRange; + sal_Bool bResult(sal_False); + if( GetRangeFromString( aScRange, rRangeStr, pDocument, eConv, nOffset, cSeperator, cQuote ) && (nOffset >= 0) ) + { + rArea.nTab = aScRange.aStart.Tab(); + rArea.nColStart = aScRange.aStart.Col(); + rArea.nRowStart = aScRange.aStart.Row(); + rArea.nColEnd = aScRange.aEnd.Col(); + rArea.nRowEnd = aScRange.aEnd.Row(); + bResult = sal_True; + } + return bResult; +} + + +//___________________________________________________________________ + +sal_Bool ScRangeStringConverter::GetAddressFromString( + table::CellAddress& rAddress, + const OUString& rAddressStr, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Int32& nOffset, + sal_Unicode cSeperator, + sal_Unicode cQuote ) +{ + ScAddress aScAddress; + sal_Bool bResult(sal_False); + if( GetAddressFromString( aScAddress, rAddressStr, pDocument, eConv, nOffset, cSeperator, cQuote ) && (nOffset >= 0) ) + { + ScUnoConversion::FillApiAddress( rAddress, aScAddress ); + bResult = sal_True; + } + return bResult; +} + +sal_Bool ScRangeStringConverter::GetRangeFromString( + table::CellRangeAddress& rRange, + const OUString& rRangeStr, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Int32& nOffset, + sal_Unicode cSeperator, + sal_Unicode cQuote ) +{ + ScRange aScRange; + sal_Bool bResult(sal_False); + if( GetRangeFromString( aScRange, rRangeStr, pDocument, eConv, nOffset, cSeperator, cQuote ) && (nOffset >= 0) ) + { + ScUnoConversion::FillApiRange( rRange, aScRange ); + bResult = sal_True; + } + return bResult; +} + +sal_Bool ScRangeStringConverter::GetRangeListFromString( + uno::Sequence< table::CellRangeAddress >& rRangeSeq, + const OUString& rRangeListStr, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Unicode cSeperator, + sal_Unicode cQuote ) +{ + sal_Bool bRet = sal_True; + DBG_ASSERT( rRangeListStr.getLength(), "ScXMLConverter::GetRangeListFromString - empty string!" ); + table::CellRangeAddress aRange; + sal_Int32 nOffset = 0; + while( nOffset >= 0 ) + { + if( GetRangeFromString( aRange, rRangeListStr, pDocument, eConv, nOffset, cSeperator, cQuote ) && (nOffset >= 0) ) + { + rRangeSeq.realloc( rRangeSeq.getLength() + 1 ); + rRangeSeq[ rRangeSeq.getLength() - 1 ] = aRange; + } + else + bRet = sal_False; + } + return bRet; +} + + +//___________________________________________________________________ + +void ScRangeStringConverter::GetStringFromAddress( + OUString& rString, + const ScAddress& rAddress, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Unicode cSeperator, + sal_Bool bAppendStr, + sal_uInt16 nFormatFlags ) +{ + if (pDocument && pDocument->HasTable(rAddress.Tab())) + { + String sAddress; + rAddress.Format( sAddress, nFormatFlags, (ScDocument*) pDocument, eConv ); + AssignString( rString, sAddress, bAppendStr, cSeperator ); + } +} + +void ScRangeStringConverter::GetStringFromRange( + OUString& rString, + const ScRange& rRange, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Unicode cSeperator, + sal_Bool bAppendStr, + sal_uInt16 nFormatFlags ) +{ + if (pDocument && pDocument->HasTable(rRange.aStart.Tab())) + { + ScAddress aStartAddress( rRange.aStart ); + ScAddress aEndAddress( rRange.aEnd ); + String sStartAddress; + String sEndAddress; + aStartAddress.Format( sStartAddress, nFormatFlags, (ScDocument*) pDocument, eConv ); + aEndAddress.Format( sEndAddress, nFormatFlags, (ScDocument*) pDocument, eConv ); + OUString sOUStartAddress( sStartAddress ); + sOUStartAddress += OUString(':'); + sOUStartAddress += OUString( sEndAddress ); + AssignString( rString, sOUStartAddress, bAppendStr, cSeperator ); + } +} + +void ScRangeStringConverter::GetStringFromRangeList( + OUString& rString, + const ScRangeList* pRangeList, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Unicode cSeperator, + sal_uInt16 nFormatFlags ) +{ + OUString sRangeListStr; + if( pRangeList ) + { + sal_Int32 nCount = pRangeList->Count(); + for( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ ) + { + const ScRange* pRange = pRangeList->GetObject( nIndex ); + if( pRange ) + GetStringFromRange( sRangeListStr, *pRange, pDocument, eConv, cSeperator, sal_True, nFormatFlags ); + } + } + rString = sRangeListStr; +} + + +//___________________________________________________________________ + +void ScRangeStringConverter::GetStringFromArea( + OUString& rString, + const ScArea& rArea, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Unicode cSeperator, + sal_Bool bAppendStr, + sal_uInt16 nFormatFlags ) +{ + ScRange aRange( rArea.nColStart, rArea.nRowStart, rArea.nTab, rArea.nColEnd, rArea.nRowEnd, rArea.nTab ); + GetStringFromRange( rString, aRange, pDocument, eConv, cSeperator, bAppendStr, nFormatFlags ); +} + + +//___________________________________________________________________ + +void ScRangeStringConverter::GetStringFromAddress( + OUString& rString, + const table::CellAddress& rAddress, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Unicode cSeperator, + sal_Bool bAppendStr, + sal_uInt16 nFormatFlags ) +{ + ScAddress aScAddress( static_cast<SCCOL>(rAddress.Column), static_cast<SCROW>(rAddress.Row), rAddress.Sheet ); + GetStringFromAddress( rString, aScAddress, pDocument, eConv, cSeperator, bAppendStr, nFormatFlags ); +} + +void ScRangeStringConverter::GetStringFromRange( + OUString& rString, + const table::CellRangeAddress& rRange, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Unicode cSeperator, + sal_Bool bAppendStr, + sal_uInt16 nFormatFlags ) +{ + ScRange aScRange( static_cast<SCCOL>(rRange.StartColumn), static_cast<SCROW>(rRange.StartRow), rRange.Sheet, + static_cast<SCCOL>(rRange.EndColumn), static_cast<SCROW>(rRange.EndRow), rRange.Sheet ); + GetStringFromRange( rString, aScRange, pDocument, eConv, cSeperator, bAppendStr, nFormatFlags ); +} + +void ScRangeStringConverter::GetStringFromRangeList( + OUString& rString, + const uno::Sequence< table::CellRangeAddress >& rRangeSeq, + const ScDocument* pDocument, + FormulaGrammar::AddressConvention eConv, + sal_Unicode cSeperator, + sal_uInt16 nFormatFlags ) +{ + OUString sRangeListStr; + sal_Int32 nCount = rRangeSeq.getLength(); + for( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ ) + { + const table::CellRangeAddress& rRange = rRangeSeq[ nIndex ]; + GetStringFromRange( sRangeListStr, rRange, pDocument, eConv, cSeperator, sal_True, nFormatFlags ); + } + rString = sRangeListStr; +} + +static void lcl_appendCellAddress( + rtl::OUStringBuffer& rBuf, ScDocument* pDoc, const ScAddress& rCell, + const ScAddress::ExternalInfo& rExtInfo) +{ + if (rExtInfo.mbExternal) + { + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + const String* pFilePath = pRefMgr->getExternalFileName(rExtInfo.mnFileId, true); + if (!pFilePath) + return; + + sal_Unicode cQuote = '\''; + rBuf.append(cQuote); + rBuf.append(*pFilePath); + rBuf.append(cQuote); + rBuf.append(sal_Unicode('#')); + rBuf.append(sal_Unicode('$')); + ScRangeStringConverter::AppendTableName(rBuf, rExtInfo.maTabName); + rBuf.append(sal_Unicode('.')); + + String aAddr; + rCell.Format(aAddr, SCA_ABS, NULL, ::formula::FormulaGrammar::CONV_OOO); + rBuf.append(aAddr); + } + else + { + String aAddr; + rCell.Format(aAddr, SCA_ABS_3D, pDoc, ::formula::FormulaGrammar::CONV_OOO); + rBuf.append(aAddr); + } +} + +static void lcl_appendCellRangeAddress( + rtl::OUStringBuffer& rBuf, ScDocument* pDoc, const ScAddress& rCell1, const ScAddress& rCell2, + const ScAddress::ExternalInfo& rExtInfo1, const ScAddress::ExternalInfo& rExtInfo2) +{ + if (rExtInfo1.mbExternal) + { + DBG_ASSERT(rExtInfo2.mbExternal, "2nd address is not external!?"); + DBG_ASSERT(rExtInfo1.mnFileId == rExtInfo2.mnFileId, "File IDs do not match between 1st and 2nd addresses."); + + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + const String* pFilePath = pRefMgr->getExternalFileName(rExtInfo1.mnFileId, true); + if (!pFilePath) + return; + + sal_Unicode cQuote = '\''; + rBuf.append(cQuote); + rBuf.append(*pFilePath); + rBuf.append(cQuote); + rBuf.append(sal_Unicode('#')); + rBuf.append(sal_Unicode('$')); + ScRangeStringConverter::AppendTableName(rBuf, rExtInfo1.maTabName); + rBuf.append(sal_Unicode('.')); + + String aAddr; + rCell1.Format(aAddr, SCA_ABS, NULL, ::formula::FormulaGrammar::CONV_OOO); + rBuf.append(aAddr); + + rBuf.appendAscii(":"); + + if (rExtInfo1.maTabName != rExtInfo2.maTabName) + { + rBuf.append(sal_Unicode('$')); + ScRangeStringConverter::AppendTableName(rBuf, rExtInfo2.maTabName); + rBuf.append(sal_Unicode('.')); + } + + rCell2.Format(aAddr, SCA_ABS, NULL, ::formula::FormulaGrammar::CONV_OOO); + rBuf.append(aAddr); + } + else + { + ScRange aRange; + aRange.aStart = rCell1; + aRange.aEnd = rCell2; + String aAddr; + aRange.Format(aAddr, SCR_ABS_3D, pDoc, ::formula::FormulaGrammar::CONV_OOO); + rBuf.append(aAddr); + } +} + +void ScRangeStringConverter::GetStringFromXMLRangeString( OUString& rString, const OUString& rXMLRange, ScDocument* pDoc ) +{ + const sal_Unicode cSep = ' '; + const sal_Unicode cQuote = '\''; + + OUStringBuffer aRetStr; + sal_Int32 nOffset = 0; + bool bFirst = true; + + while (nOffset >= 0) + { + OUString aToken; + GetTokenByOffset(aToken, rXMLRange, nOffset, cSep, cQuote); + if (nOffset < 0) + break; + + sal_Int32 nSepPos = IndexOf(aToken, ':', 0, cQuote); + if (nSepPos >= 0) + { + // Cell range + OUString aBeginCell = aToken.copy(0, nSepPos); + OUString aEndCell = aToken.copy(nSepPos+1); + + if (!aBeginCell.getLength() || !aEndCell.getLength()) + // both cell addresses must exist for this to work. + continue; + + sal_Int32 nEndCellDotPos = aEndCell.indexOf('.'); + if (nEndCellDotPos <= 0) + { + // initialize buffer with table name... + sal_Int32 nDotPos = IndexOf(aBeginCell, sal_Unicode('.'), 0, cQuote); + OUStringBuffer aBuf = aBeginCell.copy(0, nDotPos); + + if (nEndCellDotPos == 0) + { + // workaround for old syntax (probably pre-chart2 age?) + // e.g. Sheet1.A1:.B2 + aBuf.append(aEndCell); + } + else if (nEndCellDotPos < 0) + { + // sheet name in the end cell is omitted (e.g. Sheet2.A1:B2). + aBuf.append(sal_Unicode('.')); + aBuf.append(aEndCell); + } + aEndCell = aBuf.makeStringAndClear(); + } + + ScAddress::ExternalInfo aExtInfo1, aExtInfo2; + ScAddress aCell1, aCell2; + rtl::OUString aBuf; + USHORT nRet = aCell1.Parse(aBeginCell, pDoc, FormulaGrammar::CONV_OOO, &aExtInfo1); + if ((nRet & SCA_VALID) != SCA_VALID) + // first cell is invalid. + continue; + + nRet = aCell2.Parse(aEndCell, pDoc, FormulaGrammar::CONV_OOO, &aExtInfo2); + if ((nRet & SCA_VALID) != SCA_VALID) + // second cell is invalid. + continue; + + if (aExtInfo1.mnFileId != aExtInfo2.mnFileId || aExtInfo1.mbExternal != aExtInfo2.mbExternal) + // external info inconsistency. + continue; + + // All looks good! + + if (bFirst) + bFirst = false; + else + aRetStr.appendAscii(";"); + + lcl_appendCellRangeAddress(aRetStr, pDoc, aCell1, aCell2, aExtInfo1, aExtInfo2); + } + else + { + // Chart always saves ranges using CONV_OOO convention. + ScAddress::ExternalInfo aExtInfo; + ScAddress aCell; + USHORT nRet = aCell.Parse(aToken, pDoc, ::formula::FormulaGrammar::CONV_OOO, &aExtInfo); + if ((nRet & SCA_VALID) != SCA_VALID) + continue; + + // Looks good! + + if (bFirst) + bFirst = false; + else + aRetStr.appendAscii(";"); + + lcl_appendCellAddress(aRetStr, pDoc, aCell, aExtInfo); + } + } + + rString = aRetStr.makeStringAndClear(); +} + +//======================================================================== + +ScArea::ScArea( SCTAB tab, + SCCOL colStart, SCROW rowStart, + SCCOL colEnd, SCROW rowEnd ) : + nTab ( tab ), + nColStart( colStart ), nRowStart( rowStart ), + nColEnd ( colEnd ), nRowEnd ( rowEnd ) +{ +} + +//------------------------------------------------------------------------ + +ScArea::ScArea( const ScArea& r ) : + nTab ( r.nTab ), + nColStart( r.nColStart ), nRowStart( r.nRowStart ), + nColEnd ( r.nColEnd ), nRowEnd ( r.nRowEnd ) +{ +} + +//------------------------------------------------------------------------ + +ScArea& ScArea::operator=( const ScArea& r ) +{ + nTab = r.nTab; + nColStart = r.nColStart; + nRowStart = r.nRowStart; + nColEnd = r.nColEnd; + nRowEnd = r.nRowEnd; + return *this; +} + +//------------------------------------------------------------------------ + +BOOL ScArea::operator==( const ScArea& r ) const +{ + return ( (nTab == r.nTab) + && (nColStart == r.nColStart) + && (nRowStart == r.nRowStart) + && (nColEnd == r.nColEnd) + && (nRowEnd == r.nRowEnd) ); +} + +//------------------------------------------------------------------------ + +ScAreaNameIterator::ScAreaNameIterator( ScDocument* pDoc ) : + aStrNoName( ScGlobal::GetRscString(STR_DB_NONAME) ) +{ + pRangeName = pDoc->GetRangeName(); + pDBCollection = pDoc->GetDBCollection(); + nPos = 0; + bFirstPass = TRUE; +} + +BOOL ScAreaNameIterator::Next( String& rName, ScRange& rRange ) +{ + for (;;) + { + if ( bFirstPass ) // erst Bereichsnamen + { + if ( pRangeName && nPos < pRangeName->GetCount() ) + { + ScRangeData* pData = (*pRangeName)[nPos++]; + if ( pData && pData->IsValidReference(rRange) ) + { + rName = pData->GetName(); + return TRUE; // gefunden + } + } + else + { + bFirstPass = FALSE; + nPos = 0; + } + } + if ( !bFirstPass ) // dann DB-Bereiche + { + if ( pDBCollection && nPos < pDBCollection->GetCount() ) + { + ScDBData* pData = (*pDBCollection)[nPos++]; + if (pData && pData->GetName() != aStrNoName) + { + pData->GetArea( rRange ); + rName = pData->GetName(); + return TRUE; // gefunden + } + } + else + return FALSE; // gibt nichts mehr + } + } +} + + + + diff --git a/sc/source/core/tool/rechead.cxx b/sc/source/core/tool/rechead.cxx new file mode 100644 index 000000000000..6da946463697 --- /dev/null +++ b/sc/source/core/tool/rechead.cxx @@ -0,0 +1,173 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <tools/debug.hxx> + +#include "rechead.hxx" +#include "scerrors.hxx" + +// STATIC DATA ----------------------------------------------------------- + +// ======================================================================= + +ScMultipleReadHeader::ScMultipleReadHeader(SvStream& rNewStream) : + rStream( rNewStream ) +{ + sal_uInt32 nDataSize; + rStream >> nDataSize; + ULONG nDataPos = rStream.Tell(); + nTotalEnd = nDataPos + nDataSize; + nEntryEnd = nTotalEnd; + + rStream.SeekRel(nDataSize); + USHORT nID; + rStream >> nID; + if (nID != SCID_SIZES) + { + DBG_ERROR("SCID_SIZES nicht gefunden"); + if ( rStream.GetError() == SVSTREAM_OK ) + rStream.SetError( SVSTREAM_FILEFORMAT_ERROR ); + + // alles auf 0, damit BytesLeft() wenigstens abbricht + pBuf = NULL; pMemStream = NULL; + nEntryEnd = nDataPos; + } + else + { + sal_uInt32 nSizeTableLen; + rStream >> nSizeTableLen; + pBuf = new BYTE[nSizeTableLen]; + rStream.Read( pBuf, nSizeTableLen ); + pMemStream = new SvMemoryStream( (char*)pBuf, nSizeTableLen, STREAM_READ ); + } + + nEndPos = rStream.Tell(); + rStream.Seek( nDataPos ); +} + +ScMultipleReadHeader::~ScMultipleReadHeader() +{ + if ( pMemStream && pMemStream->Tell() != pMemStream->GetEndOfData() ) + { + DBG_ERRORFILE( "Sizes nicht vollstaendig gelesen" ); + if ( rStream.GetError() == SVSTREAM_OK ) + rStream.SetError( SCWARN_IMPORT_INFOLOST ); + } + delete pMemStream; + delete[] pBuf; + + rStream.Seek(nEndPos); +} + +void ScMultipleReadHeader::EndEntry() +{ + ULONG nPos = rStream.Tell(); + DBG_ASSERT( nPos <= nEntryEnd, "zuviel gelesen" ); + if ( nPos != nEntryEnd ) + { + if ( rStream.GetError() == SVSTREAM_OK ) + rStream.SetError( SCWARN_IMPORT_INFOLOST ); + rStream.Seek( nEntryEnd ); // Rest ueberspringen + } + + nEntryEnd = nTotalEnd; // den ganzen Rest, wenn kein StartEntry kommt +} + +void ScMultipleReadHeader::StartEntry() +{ + ULONG nPos = rStream.Tell(); + sal_uInt32 nEntrySize; + (*pMemStream) >> nEntrySize; + + nEntryEnd = nPos + nEntrySize; + DBG_ASSERT( nEntryEnd <= nTotalEnd, "zuviele Eintraege gelesen" ); +} + +ULONG ScMultipleReadHeader::BytesLeft() const +{ + ULONG nReadEnd = rStream.Tell(); + if (nReadEnd <= nEntryEnd) + return nEntryEnd-nReadEnd; + + DBG_ERROR("Fehler bei ScMultipleReadHeader::BytesLeft"); + return 0; +} + +// ----------------------------------------------------------------------- + +ScMultipleWriteHeader::ScMultipleWriteHeader(SvStream& rNewStream, sal_uInt32 nDefault) : + rStream( rNewStream ), + aMemStream( 4096, 4096 ) +{ + nDataSize = nDefault; + rStream << nDataSize; + + nDataPos = rStream.Tell(); + nEntryStart = nDataPos; +} + +ScMultipleWriteHeader::~ScMultipleWriteHeader() +{ + ULONG nDataEnd = rStream.Tell(); + + rStream << (USHORT) SCID_SIZES; + rStream << static_cast<sal_uInt32>(aMemStream.Tell()); + rStream.Write( aMemStream.GetData(), aMemStream.Tell() ); + + if ( nDataEnd - nDataPos != nDataSize ) // Default getroffen? + { + nDataSize = nDataEnd - nDataPos; + ULONG nPos = rStream.Tell(); + rStream.Seek(nDataPos-sizeof(sal_uInt32)); + rStream << nDataSize; // Groesse am Anfang eintragen + rStream.Seek(nPos); + } +} + +void ScMultipleWriteHeader::EndEntry() +{ + ULONG nPos = rStream.Tell(); + aMemStream << static_cast<sal_uInt32>(nPos - nEntryStart); +} + +void ScMultipleWriteHeader::StartEntry() +{ + ULONG nPos = rStream.Tell(); + nEntryStart = nPos; +} + + + + + diff --git a/sc/source/core/tool/refdata.cxx b/sc/source/core/tool/refdata.cxx new file mode 100644 index 000000000000..47774d348044 --- /dev/null +++ b/sc/source/core/tool/refdata.cxx @@ -0,0 +1,372 @@ +/************************************************************************* + * + * 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 "refdata.hxx" + + +void ScSingleRefData::CalcRelFromAbs( const ScAddress& rPos ) +{ + nRelCol = nCol - rPos.Col(); + nRelRow = nRow - rPos.Row(); + nRelTab = nTab - rPos.Tab(); +} + + +void ScSingleRefData::SmartRelAbs( const ScAddress& rPos ) +{ + if ( Flags.bColRel ) + nCol = nRelCol + rPos.Col(); + else + nRelCol = nCol - rPos.Col(); + + if ( Flags.bRowRel ) + nRow = nRelRow + rPos.Row(); + else + nRelRow = nRow - rPos.Row(); + + if ( Flags.bTabRel ) + nTab = nRelTab + rPos.Tab(); + else + nRelTab = nTab - rPos.Tab(); +} + + +void ScSingleRefData::CalcAbsIfRel( const ScAddress& rPos ) +{ + if ( Flags.bColRel ) + { + nCol = nRelCol + rPos.Col(); + if ( !VALIDCOL( nCol ) ) + Flags.bColDeleted = TRUE; + } + if ( Flags.bRowRel ) + { + nRow = nRelRow + rPos.Row(); + if ( !VALIDROW( nRow ) ) + Flags.bRowDeleted = TRUE; + } + if ( Flags.bTabRel ) + { + nTab = nRelTab + rPos.Tab(); + if ( !VALIDTAB( nTab ) ) + Flags.bTabDeleted = TRUE; + } +} + +//UNUSED2008-05 void ScSingleRefData::OldBoolsToNewFlags( const OldSingleRefBools& rBools ) +//UNUSED2008-05 { +//UNUSED2008-05 switch ( rBools.bRelCol ) +//UNUSED2008-05 { +//UNUSED2008-05 case SR_DELETED : +//UNUSED2008-05 Flags.bColRel = TRUE; // der war verlorengegangen +//UNUSED2008-05 Flags.bColDeleted = TRUE; +//UNUSED2008-05 break; +//UNUSED2008-05 case SR_ABSOLUTE : +//UNUSED2008-05 Flags.bColRel = FALSE; +//UNUSED2008-05 Flags.bColDeleted = FALSE; +//UNUSED2008-05 break; +//UNUSED2008-05 case SR_RELABS : +//UNUSED2008-05 case SR_RELATIVE : +//UNUSED2008-05 default: +//UNUSED2008-05 Flags.bColRel = TRUE; +//UNUSED2008-05 Flags.bColDeleted = FALSE; +//UNUSED2008-05 } +//UNUSED2008-05 switch ( rBools.bRelRow ) +//UNUSED2008-05 { +//UNUSED2008-05 case SR_DELETED : +//UNUSED2008-05 Flags.bRowRel = TRUE; // der war verlorengegangen +//UNUSED2008-05 Flags.bRowDeleted = TRUE; +//UNUSED2008-05 break; +//UNUSED2008-05 case SR_ABSOLUTE : +//UNUSED2008-05 Flags.bRowRel = FALSE; +//UNUSED2008-05 Flags.bRowDeleted = FALSE; +//UNUSED2008-05 break; +//UNUSED2008-05 case SR_RELABS : +//UNUSED2008-05 case SR_RELATIVE : +//UNUSED2008-05 default: +//UNUSED2008-05 Flags.bRowRel = TRUE; +//UNUSED2008-05 Flags.bRowDeleted = FALSE; +//UNUSED2008-05 } +//UNUSED2008-05 switch ( rBools.bRelTab ) +//UNUSED2008-05 { +//UNUSED2008-05 case SR_DELETED : +//UNUSED2008-05 Flags.bTabRel = TRUE; // der war verlorengegangen +//UNUSED2008-05 Flags.bTabDeleted = TRUE; +//UNUSED2008-05 break; +//UNUSED2008-05 case SR_ABSOLUTE : +//UNUSED2008-05 Flags.bTabRel = FALSE; +//UNUSED2008-05 Flags.bTabDeleted = FALSE; +//UNUSED2008-05 break; +//UNUSED2008-05 case SR_RELABS : +//UNUSED2008-05 case SR_RELATIVE : +//UNUSED2008-05 default: +//UNUSED2008-05 Flags.bTabRel = TRUE; +//UNUSED2008-05 Flags.bTabDeleted = FALSE; +//UNUSED2008-05 } +//UNUSED2008-05 Flags.bFlag3D = (rBools.bOldFlag3D & SRF_3D ? TRUE : FALSE); +//UNUSED2008-05 Flags.bRelName = (rBools.bOldFlag3D & SRF_RELNAME ? TRUE : FALSE); +//UNUSED2008-05 if ( !Flags.bFlag3D ) +//UNUSED2008-05 Flags.bTabRel = TRUE; // ist bei einigen aelteren Dokumenten nicht gesetzt +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 +//UNUSED2008-05 /* +//UNUSED2008-05 bis Release 3.1 sah Store so aus +//UNUSED2008-05 +//UNUSED2008-05 BYTE n = ( ( r.bOldFlag3D & 0x03 ) << 6 ) // RelName, 3D +//UNUSED2008-05 | ( ( r.bRelTab & 0x03 ) << 4 ) // Relative, RelAbs +//UNUSED2008-05 | ( ( r.bRelRow & 0x03 ) << 2 ) +//UNUSED2008-05 | ( r.bRelCol & 0x03 ); +//UNUSED2008-05 +//UNUSED2008-05 bis Release 3.1 sah Load so aus +//UNUSED2008-05 +//UNUSED2008-05 r.bRelCol = ( n & 0x03 ); +//UNUSED2008-05 r.bRelRow = ( ( n >> 2 ) & 0x03 ); +//UNUSED2008-05 r.bRelTab = ( ( n >> 4 ) & 0x03 ); +//UNUSED2008-05 r.bOldFlag3D = ( ( n >> 6 ) & 0x03 ); +//UNUSED2008-05 +//UNUSED2008-05 bRelCol == SR_DELETED war identisch mit bRelCol == (SR_RELATIVE | SR_RELABS) +//UNUSED2008-05 leider.. +//UNUSED2008-05 3.1 liest Zukunft: Deleted wird nicht unbedingt erkannt, nur wenn auch Relativ. +//UNUSED2008-05 Aber immer noch nCol > MAXCOL und gut sollte sein.. +//UNUSED2008-05 */ +//UNUSED2008-05 +//UNUSED2008-05 BYTE ScSingleRefData::CreateStoreByteFromFlags() const +//UNUSED2008-05 { +//UNUSED2008-05 return (BYTE)( +//UNUSED2008-05 ( (Flags.bRelName & 0x01) << 7 ) +//UNUSED2008-05 | ( (Flags.bFlag3D & 0x01) << 6 ) +//UNUSED2008-05 | ( (Flags.bTabDeleted & 0x01) << 5 ) +//UNUSED2008-05 | ( (Flags.bTabRel & 0x01) << 4 ) +//UNUSED2008-05 | ( (Flags.bRowDeleted & 0x01) << 3 ) +//UNUSED2008-05 | ( (Flags.bRowRel & 0x01) << 2 ) +//UNUSED2008-05 | ( (Flags.bColDeleted & 0x01) << 1 ) +//UNUSED2008-05 | (Flags.bColRel & 0x01) +//UNUSED2008-05 ); +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 +//UNUSED2008-05 void ScSingleRefData::CreateFlagsFromLoadByte( BYTE n ) +//UNUSED2008-05 { +//UNUSED2008-05 Flags.bColRel = (n & 0x01 ); +//UNUSED2008-05 Flags.bColDeleted = ( (n >> 1) & 0x01 ); +//UNUSED2008-05 Flags.bRowRel = ( (n >> 2) & 0x01 ); +//UNUSED2008-05 Flags.bRowDeleted = ( (n >> 3) & 0x01 ); +//UNUSED2008-05 Flags.bTabRel = ( (n >> 4) & 0x01 ); +//UNUSED2008-05 Flags.bTabDeleted = ( (n >> 5) & 0x01 ); +//UNUSED2008-05 Flags.bFlag3D = ( (n >> 6) & 0x01 ); +//UNUSED2008-05 Flags.bRelName = ( (n >> 7) & 0x01 ); +//UNUSED2008-05 } + + +BOOL ScSingleRefData::operator==( const ScSingleRefData& r ) const +{ + return bFlags == r.bFlags && + (Flags.bColRel ? nRelCol == r.nRelCol : nCol == r.nCol) && + (Flags.bRowRel ? nRelRow == r.nRelRow : nRow == r.nRow) && + (Flags.bTabRel ? nRelTab == r.nRelTab : nTab == r.nTab); +} + +bool ScSingleRefData::operator!=( const ScSingleRefData& r ) const +{ + return !operator==(r); +} + +static void lcl_putInOrder( ScSingleRefData & rRef1, ScSingleRefData & rRef2 ) +{ + SCCOL nCol1, nCol2; + SCROW nRow1, nRow2; + SCTAB nTab1, nTab2; + BOOL bTmp; + BYTE nRelState1, nRelState2; + if ( rRef1.Flags.bRelName ) + nRelState1 = + ((rRef1.Flags.bTabRel & 0x01) << 2) + | ((rRef1.Flags.bRowRel & 0x01) << 1) + | ((rRef1.Flags.bColRel & 0x01)); + else + nRelState1 = 0; + if ( rRef2.Flags.bRelName ) + nRelState2 = + ((rRef2.Flags.bTabRel & 0x01) << 2) + | ((rRef2.Flags.bRowRel & 0x01) << 1) + | ((rRef2.Flags.bColRel & 0x01)); + else + nRelState2 = 0; + if ( (nCol1 = rRef1.nCol) > (nCol2 = rRef2.nCol) ) + { + rRef1.nCol = nCol2; + rRef2.nCol = nCol1; + nCol1 = rRef1.nRelCol; + rRef1.nRelCol = rRef2.nRelCol; + rRef2.nRelCol = nCol1; + if ( rRef1.Flags.bRelName && rRef1.Flags.bColRel ) + nRelState2 |= 1; + else + nRelState2 &= ~1; + if ( rRef2.Flags.bRelName && rRef2.Flags.bColRel ) + nRelState1 |= 1; + else + nRelState1 &= ~1; + bTmp = rRef1.Flags.bColRel; + rRef1.Flags.bColRel = rRef2.Flags.bColRel; + rRef2.Flags.bColRel = bTmp; + bTmp = rRef1.Flags.bColDeleted; + rRef1.Flags.bColDeleted = rRef2.Flags.bColDeleted; + rRef2.Flags.bColDeleted = bTmp; + } + if ( (nRow1 = rRef1.nRow) > (nRow2 = rRef2.nRow) ) + { + rRef1.nRow = nRow2; + rRef2.nRow = nRow1; + nRow1 = rRef1.nRelRow; + rRef1.nRelRow = rRef2.nRelRow; + rRef2.nRelRow = nRow1; + if ( rRef1.Flags.bRelName && rRef1.Flags.bRowRel ) + nRelState2 |= 2; + else + nRelState2 &= ~2; + if ( rRef2.Flags.bRelName && rRef2.Flags.bRowRel ) + nRelState1 |= 2; + else + nRelState1 &= ~2; + bTmp = rRef1.Flags.bRowRel; + rRef1.Flags.bRowRel = rRef2.Flags.bRowRel; + rRef2.Flags.bRowRel = bTmp; + bTmp = rRef1.Flags.bRowDeleted; + rRef1.Flags.bRowDeleted = rRef2.Flags.bRowDeleted; + rRef2.Flags.bRowDeleted = bTmp; + } + if ( (nTab1 = rRef1.nTab) > (nTab2 = rRef2.nTab) ) + { + rRef1.nTab = nTab2; + rRef2.nTab = nTab1; + nTab1 = rRef1.nRelTab; + rRef1.nRelTab = rRef2.nRelTab; + rRef2.nRelTab = nTab1; + if ( rRef1.Flags.bRelName && rRef1.Flags.bTabRel ) + nRelState2 |= 4; + else + nRelState2 &= ~4; + if ( rRef2.Flags.bRelName && rRef2.Flags.bTabRel ) + nRelState1 |= 4; + else + nRelState1 &= ~4; + bTmp = rRef1.Flags.bTabRel; + rRef1.Flags.bTabRel = rRef2.Flags.bTabRel; + rRef2.Flags.bTabRel = bTmp; + bTmp = rRef1.Flags.bTabDeleted; + rRef1.Flags.bTabDeleted = rRef2.Flags.bTabDeleted; + rRef2.Flags.bTabDeleted = bTmp; + } + rRef1.Flags.bRelName = ( nRelState1 ? TRUE : FALSE ); + rRef2.Flags.bRelName = ( nRelState2 ? TRUE : FALSE ); +} + + +void ScComplexRefData::PutInOrder() +{ + lcl_putInOrder( Ref1, Ref2); +} + + +static void lcl_adjustInOrder( ScSingleRefData & rRef1, ScSingleRefData & rRef2, bool bFirstLeader ) +{ + // a1:a2:a3, bFirstLeader: rRef1==a1==r1, rRef2==a3==r2 + // else: rRef1==a3==r2, rRef2==a2==r1 + ScSingleRefData& r1 = (bFirstLeader ? rRef1 : rRef2); + ScSingleRefData& r2 = (bFirstLeader ? rRef2 : rRef1); + if (r1.Flags.bFlag3D && !r2.Flags.bFlag3D) + { + // [$]Sheet1.A5:A6 on Sheet2 do still refer only Sheet1. + r2.nTab = r1.nTab; + r2.nRelTab = r1.nRelTab; + r2.Flags.bTabRel = r1.Flags.bTabRel; + } + lcl_putInOrder( rRef1, rRef2); +} + + +ScComplexRefData& ScComplexRefData::Extend( const ScSingleRefData & rRef, const ScAddress & rPos ) +{ + CalcAbsIfRel( rPos); + ScSingleRefData aRef = rRef; + aRef.CalcAbsIfRel( rPos); + bool bInherit3D = Ref1.IsFlag3D() && !Ref2.IsFlag3D(); + bool bInherit3Dtemp = bInherit3D && !rRef.IsFlag3D(); + if (aRef.nCol < Ref1.nCol || aRef.nRow < Ref1.nRow || aRef.nTab < Ref1.nTab) + { + lcl_adjustInOrder( Ref1, aRef, true); + aRef = rRef; + aRef.CalcAbsIfRel( rPos); + } + if (aRef.nCol > Ref2.nCol || aRef.nRow > Ref2.nRow || aRef.nTab > Ref2.nTab) + { + if (bInherit3D) + Ref2.SetFlag3D( true); + lcl_adjustInOrder( aRef, Ref2, false); + if (bInherit3Dtemp) + Ref2.SetFlag3D( false); + aRef = rRef; + aRef.CalcAbsIfRel( rPos); + } + // In Ref2 use absolute/relative addressing from non-extended parts if + // equal and therefor not adjusted. + // A$5:A5 => A$5:A$5:A5 => A$5:A5, and not A$5:A$5 + // A$6:$A5 => A$6:A$6:$A5 => A5:$A$6 + if (Ref2.nCol == aRef.nCol) + Ref2.SetColRel( aRef.IsColRel()); + if (Ref2.nRow == aRef.nRow) + Ref2.SetRowRel( aRef.IsRowRel()); + // $Sheet1.$A$5:$A$6 => $Sheet1.$A$5:$A$5:$A$6 => $Sheet1.$A$5:$A$6, and + // not $Sheet1.$A$5:Sheet1.$A$6 (with invisible second 3D, but relative). + if (Ref2.nTab == aRef.nTab) + Ref2.SetTabRel( bInherit3Dtemp ? Ref1.IsTabRel() : aRef.IsTabRel()); + Ref2.CalcRelFromAbs( rPos); + // Force 3D if necessary. References to other sheets always. + if (Ref1.nTab != rPos.Tab()) + Ref1.SetFlag3D( true); + // In the second part only if different sheet thus not inherited. + if (Ref2.nTab != Ref1.nTab) + Ref2.SetFlag3D( true); + // Merge Flag3D to Ref2 in case there was nothing to inherit and/or range + // wasn't extended as in A5:A5:Sheet1.A5 if on Sheet1. + if (rRef.IsFlag3D()) + Ref2.SetFlag3D( true); + return *this; +} + + +ScComplexRefData& ScComplexRefData::Extend( const ScComplexRefData & rRef, const ScAddress & rPos ) +{ + return Extend( rRef.Ref1, rPos).Extend( rRef.Ref2, rPos); +} diff --git a/sc/source/core/tool/reffind.cxx b/sc/source/core/tool/reffind.cxx new file mode 100644 index 000000000000..cdb5962b2b9c --- /dev/null +++ b/sc/source/core/tool/reffind.cxx @@ -0,0 +1,168 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <string.h> + +#include "reffind.hxx" +#include "global.hxx" +#include "compiler.hxx" +#include "document.hxx" + +// STATIC DATA ----------------------------------------------------------- + +// incl. Doppelpunkt -> Doppelte Referenzen werden einzeln behandelt +const sal_Unicode __FAR_DATA ScRefFinder::pDelimiters[] = { + '=','(',')',';','+','-','*','/','^','&',' ','{','}','<','>',':', 0 +}; + +// ======================================================================= + +inline BOOL IsText( sal_Unicode c ) +{ + return !ScGlobal::UnicodeStrChr( ScRefFinder::pDelimiters, c ); +} + +inline BOOL IsText( BOOL& bQuote, sal_Unicode c ) +{ + if ( c == '\'' ) + { + bQuote = !bQuote; + return TRUE; + } + if ( bQuote ) + return TRUE; + return IsText( c ); +} + +ScRefFinder::ScRefFinder(const String& rFormula, ScDocument* pDocument, + formula::FormulaGrammar::AddressConvention eConvP) : + aFormula( rFormula ), + eConv( eConvP ), + pDoc( pDocument ) +{ + nSelStart = nSelEnd = nFound = 0; +} + +ScRefFinder::~ScRefFinder() +{ +} + +USHORT lcl_NextFlags( USHORT nOld ) +{ + USHORT nNew = nOld & 7; // die drei Abs-Flags + nNew = ( nNew - 1 ) & 7; // weiterzaehlen + + if (!(nOld & SCA_TAB_3D)) + nNew &= ~SCA_TAB_ABSOLUTE; // nicht 3D -> nie absolut! + + return ( nOld & 0xfff8 ) | nNew; +} + +void ScRefFinder::ToggleRel( xub_StrLen nStartPos, xub_StrLen nEndPos ) +{ + xub_StrLen nLen = aFormula.Len(); + if (!nLen) + return; + const sal_Unicode* pSource = aFormula.GetBuffer(); // fuer schnellen Zugriff + + // Selektion erweitern, und statt Selektion Start- und Endindex + + if ( nEndPos < nStartPos ) + { + xub_StrLen nTemp = nStartPos; nStartPos = nEndPos; nEndPos = nTemp; + } + while (nStartPos > 0 && IsText(pSource[nStartPos - 1]) ) + --nStartPos; + if (nEndPos) + --nEndPos; + while (nEndPos+1 < nLen && IsText(pSource[nEndPos + 1]) ) + ++nEndPos; + + String aResult; + String aExpr; + String aSep; + ScAddress aAddr; + nFound = 0; + + xub_StrLen nLoopStart = nStartPos; + while ( nLoopStart <= nEndPos ) + { + // Formel zerlegen + + xub_StrLen nEStart = nLoopStart; + while ( nEStart <= nEndPos && !IsText(pSource[nEStart]) ) + ++nEStart; + + BOOL bQuote = FALSE; + xub_StrLen nEEnd = nEStart; + while ( nEEnd <= nEndPos && IsText(bQuote,pSource[nEEnd]) ) + ++nEEnd; + + aSep = aFormula.Copy( nLoopStart, nEStart-nLoopStart ); + aExpr = aFormula.Copy( nEStart, nEEnd-nEStart ); + + // Test, ob aExpr eine Referenz ist + + USHORT nResult = aAddr.Parse( aExpr, pDoc, pDoc->GetAddressConvention() ); + if ( nResult & SCA_VALID ) + { + USHORT nFlags = lcl_NextFlags( nResult ); + aAddr.Format( aExpr, nFlags, pDoc, pDoc->GetAddressConvention() ); + + xub_StrLen nAbsStart = nStartPos+aResult.Len()+aSep.Len(); + + if (!nFound) // erste Referenz ? + nSelStart = nAbsStart; + nSelEnd = nAbsStart+aExpr.Len(); // Selektion, keine Indizes + ++nFound; + } + + // zusammenbauen + + aResult += aSep; + aResult += aExpr; + + nLoopStart = nEEnd; + } + + String aTotal = aFormula.Copy( 0, nStartPos ); + aTotal += aResult; + aTotal += aFormula.Copy( nEndPos+1 ); + + aFormula = aTotal; +} + + + + diff --git a/sc/source/core/tool/refreshtimer.cxx b/sc/source/core/tool/refreshtimer.cxx new file mode 100644 index 000000000000..5520aac6d6eb --- /dev/null +++ b/sc/source/core/tool/refreshtimer.cxx @@ -0,0 +1,81 @@ +/************************************************************************* + * + * 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 "refreshtimer.hxx" + + +ScRefreshTimerProtector::ScRefreshTimerProtector( ScRefreshTimerControl * const * pp ) + : + ppControl( pp ) +{ + if ( ppControl && *ppControl ) + { + (*ppControl)->SetAllowRefresh( FALSE ); + // wait for any running refresh in another thread to finnish + ::vos::OGuard aGuard( (*ppControl)->GetMutex() ); + } +} + + +ScRefreshTimer::~ScRefreshTimer() +{ + if ( IsActive() ) + Stop(); + RemoveFromControl(); +} + + +void ScRefreshTimer::SetRefreshDelay( ULONG nSeconds ) +{ + BOOL bActive = IsActive(); + if ( bActive && !nSeconds ) + Stop(); + SetTimeout( nSeconds * 1000 ); + if ( !bActive && nSeconds ) + Start(); +} + + +void ScRefreshTimer::Timeout() +{ + if ( ppControl && *ppControl && (*ppControl)->IsRefreshAllowed() ) + { + // now we COULD make the call in another thread ... + ::vos::OGuard aGuard( (*ppControl)->GetMutex() ); + maTimeoutHdl.Call( this ); + // restart from now on, don't execute immediately again if timed out + // a second time during refresh + if ( IsActive() ) + Start(); + } +} + diff --git a/sc/source/core/tool/reftokenhelper.cxx b/sc/source/core/tool/reftokenhelper.cxx new file mode 100644 index 000000000000..f4976a914c39 --- /dev/null +++ b/sc/source/core/tool/reftokenhelper.cxx @@ -0,0 +1,479 @@ +/************************************************************************* + * + * 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 "reftokenhelper.hxx" +#include "document.hxx" +#include "rangeutl.hxx" +#include "compiler.hxx" +#include "tokenarray.hxx" + +#include "rtl/ustring.hxx" +#include "formula/grammar.hxx" +#include "formula/token.hxx" + +using namespace formula; + +using ::std::vector; +using ::std::auto_ptr; +using ::rtl::OUString; + +void ScRefTokenHelper::compileRangeRepresentation( + vector<ScSharedTokenRef>& rRefTokens, const OUString& rRangeStr, ScDocument* pDoc, FormulaGrammar::Grammar eGrammar) +{ + const sal_Unicode cSep = GetScCompilerNativeSymbol(ocSep).GetChar(0); + const sal_Unicode cQuote = '\''; + + // #i107275# ignore parentheses + OUString aRangeStr = rRangeStr; + while( (aRangeStr.getLength() >= 2) && (aRangeStr[ 0 ] == '(') && (aRangeStr[ aRangeStr.getLength() - 1 ] == ')') ) + aRangeStr = aRangeStr.copy( 1, aRangeStr.getLength() - 2 ); + + bool bFailure = false; + sal_Int32 nOffset = 0; + while (nOffset >= 0 && !bFailure) + { + OUString aToken; + ScRangeStringConverter::GetTokenByOffset(aToken, aRangeStr, nOffset, cSep, cQuote); + if (nOffset < 0) + break; + + ScCompiler aCompiler(pDoc, ScAddress(0,0,0)); + aCompiler.SetGrammar(eGrammar); + auto_ptr<ScTokenArray> pArray(aCompiler.CompileString(aToken)); + + // There MUST be exactly one reference per range token and nothing + // else, and it MUST be a valid reference, not some #REF! + USHORT nLen = pArray->GetLen(); + if (!nLen) + continue; // Should a missing range really be allowed? + if (nLen != 1) + bFailure = true; + else + { + pArray->Reset(); + const FormulaToken* p = pArray->GetNextReference(); + if (!p) + bFailure = true; + else + { + const ScToken* pT = static_cast<const ScToken*>(p); + switch (pT->GetType()) + { + case svSingleRef: + if (!pT->GetSingleRef().Valid()) + bFailure = true; + break; + case svDoubleRef: + if (!pT->GetDoubleRef().Valid()) + bFailure = true; + break; + case svExternalSingleRef: + if (!pT->GetSingleRef().ValidExternal()) + bFailure = true; + break; + case svExternalDoubleRef: + if (!pT->GetDoubleRef().ValidExternal()) + bFailure = true; + break; + default: + ; + } + if (!bFailure) + rRefTokens.push_back( + ScSharedTokenRef(static_cast<ScToken*>(p->Clone()))); + } + } + +#if 0 + switch (p->GetType()) + { + case svSingleRef: + fprintf(stdout, "ScChart2DataProvider::compileRangeRepresentation: single ref\n"); + break; + case svDoubleRef: + fprintf(stdout, "ScChart2DataProvider::compileRangeRepresentation: double ref\n"); + break; + case svExternalSingleRef: + fprintf(stdout, "ScChart2DataProvider::compileRangeRepresentation: external single ref\n"); + break; + case svExternalDoubleRef: + fprintf(stdout, "ScChart2DataProvider::compileRangeRepresentation: external double ref\n"); + break; + default: + ; + } +#endif + + } + if (bFailure) + rRefTokens.clear(); +} + +bool ScRefTokenHelper::getRangeFromToken(ScRange& rRange, const ScSharedTokenRef& pToken, bool bExternal) +{ + StackVar eType = pToken->GetType(); + switch (pToken->GetType()) + { + case svSingleRef: + case svExternalSingleRef: + { + if ((eType == svExternalSingleRef && !bExternal) || + (eType == svSingleRef && bExternal)) + return false; + + const ScSingleRefData& rRefData = pToken->GetSingleRef(); + rRange.aStart.SetCol(rRefData.nCol); + rRange.aStart.SetRow(rRefData.nRow); + rRange.aStart.SetTab(rRefData.nTab); + rRange.aEnd = rRange.aStart; + return true; + } + case svDoubleRef: + case svExternalDoubleRef: + { + if ((eType == svExternalDoubleRef && !bExternal) || + (eType == svDoubleRef && bExternal)) + return false; + + const ScComplexRefData& rRefData = pToken->GetDoubleRef(); + rRange.aStart.SetCol(rRefData.Ref1.nCol); + rRange.aStart.SetRow(rRefData.Ref1.nRow); + rRange.aStart.SetTab(rRefData.Ref1.nTab); + rRange.aEnd.SetCol(rRefData.Ref2.nCol); + rRange.aEnd.SetRow(rRefData.Ref2.nRow); + rRange.aEnd.SetTab(rRefData.Ref2.nTab); + return true; + } + default: + ; // do nothing + } + return false; +} + +void ScRefTokenHelper::getRangeListFromTokens(ScRangeList& rRangeList, const vector<ScSharedTokenRef>& rTokens) +{ + vector<ScSharedTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end(); + for (; itr != itrEnd; ++itr) + { + ScRange aRange; + getRangeFromToken(aRange, *itr); + rRangeList.Append(aRange); + } +} + +void ScRefTokenHelper::getTokenFromRange(ScSharedTokenRef& pToken, const ScRange& rRange) +{ + ScComplexRefData aData; + aData.InitFlags(); + aData.Ref1.nCol = rRange.aStart.Col(); + aData.Ref1.nRow = rRange.aStart.Row(); + aData.Ref1.nTab = rRange.aStart.Tab(); + aData.Ref1.SetColRel(false); + aData.Ref1.SetRowRel(false); + aData.Ref1.SetTabRel(false); + aData.Ref1.SetFlag3D(true); + + aData.Ref2.nCol = rRange.aEnd.Col(); + aData.Ref2.nRow = rRange.aEnd.Row(); + aData.Ref2.nTab = rRange.aEnd.Tab(); + aData.Ref2.SetColRel(false); + aData.Ref2.SetRowRel(false); + aData.Ref2.SetTabRel(false); + // Display sheet name on 2nd reference only when the 1st and 2nd refs are on + // different sheets. + aData.Ref2.SetFlag3D(aData.Ref1.nTab != aData.Ref2.nTab); + + pToken.reset(new ScDoubleRefToken(aData)); +} + +void ScRefTokenHelper::getTokensFromRangeList(vector<ScSharedTokenRef>& pTokens, const ScRangeList& rRanges) +{ + vector<ScSharedTokenRef> aTokens; + sal_uInt32 nCount = rRanges.Count(); + aTokens.reserve(nCount); + for (sal_uInt32 i = 0; i < nCount; ++i) + { + ScRange* pRange = static_cast<ScRange*>(rRanges.GetObject(i)); + if (!pRange) + // failed. + return; + + ScSharedTokenRef pToken; + ScRefTokenHelper::getTokenFromRange(pToken,* pRange); + aTokens.push_back(pToken); + } + pTokens.swap(aTokens); +} + +bool ScRefTokenHelper::isRef(const ScSharedTokenRef& pToken) +{ + switch (pToken->GetType()) + { + case svSingleRef: + case svDoubleRef: + case svExternalSingleRef: + case svExternalDoubleRef: + return true; + default: + ; + } + return false; +} + +bool ScRefTokenHelper::isExternalRef(const ScSharedTokenRef& pToken) +{ + switch (pToken->GetType()) + { + case svExternalSingleRef: + case svExternalDoubleRef: + return true; + default: + ; + } + return false; +} + +bool ScRefTokenHelper::intersects(const vector<ScSharedTokenRef>& rTokens, const ScSharedTokenRef& pToken) +{ + if (!isRef(pToken)) + return false; + + bool bExternal = isExternalRef(pToken); + sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0; + + ScRange aRange; + getRangeFromToken(aRange, pToken, bExternal); + + vector<ScSharedTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end(); + for (; itr != itrEnd; ++itr) + { + const ScSharedTokenRef& p = *itr; + if (!isRef(p)) + continue; + + if (bExternal != isExternalRef(p)) + continue; + + ScRange aRange2; + getRangeFromToken(aRange2, p, bExternal); + + if (bExternal && nFileId != p->GetIndex()) + // different external file + continue; + + if (aRange.Intersects(aRange2)) + return true; + } + return false; +} + +namespace { + +class JoinRefTokenRanges +{ +public: + /** + * Insert a new reference token into the existing list of reference tokens, + * but in that process, try to join as many adjacent ranges as possible. + * + * @param rTokens existing list of reference tokens + * @param rToken new token + */ + void operator() (vector<ScSharedTokenRef>& rTokens, const ScSharedTokenRef& pToken) + { + join(rTokens, pToken); + } + +private: + + /** + * Check two 1-dimensional ranges to see if they overlap each other. + * + * @param nMin1 min value of range 1 + * @param nMax1 max value of range 1 + * @param nMin2 min value of range 2 + * @param nMax2 max value of range 2 + * @param rNewMin min value of new range in case they overlap + * @param rNewMax max value of new range in case they overlap + */ + template<typename T> + static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax) + { + bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1); + bool bDisjoint2 = (nMin2 > nMax1) && (nMin2 - nMax1 > 1); + if (bDisjoint1 || bDisjoint2) + // These two ranges cannot be joined. Move on. + return false; + + T nMin = nMin1 < nMin2 ? nMin1 : nMin2; + T nMax = nMax1 > nMax2 ? nMax1 : nMax2; + + rNewMin = nMin; + rNewMax = nMax; + + return true; + } + + bool isContained(const ScComplexRefData& aOldData, const ScComplexRefData& aData) const + { + // Check for containment. + bool bRowsContained = (aOldData.Ref1.nRow <= aData.Ref1.nRow) && (aData.Ref2.nRow <= aOldData.Ref2.nRow); + bool bColsContained = (aOldData.Ref1.nCol <= aData.Ref1.nCol) && (aData.Ref2.nCol <= aOldData.Ref2.nCol); + return (bRowsContained && bColsContained); + } + + void join(vector<ScSharedTokenRef>& rTokens, const ScSharedTokenRef& pToken) + { + // Normalize the token to a double reference. + ScComplexRefData aData; + if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken)) + return; + + // Get the information of the new token. + bool bExternal = ScRefTokenHelper::isExternalRef(pToken); + sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0; + String aTabName = bExternal ? pToken->GetString() : String(); + + bool bJoined = false; + vector<ScSharedTokenRef>::iterator itr = rTokens.begin(), itrEnd = rTokens.end(); + for (; itr != itrEnd; ++itr) + { + ScSharedTokenRef& pOldToken = *itr; + + if (!ScRefTokenHelper::isRef(pOldToken)) + // A non-ref token should not have been added here in the first + // place! + continue; + + if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken)) + // External and internal refs don't mix. + continue; + + if (bExternal) + { + if (nFileId != pOldToken->GetIndex()) + // Different external files. + continue; + + if (aTabName != pOldToken->GetString()) + // Different table names. + continue; + } + + ScComplexRefData aOldData; + if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken)) + continue; + + if (aData.Ref1.nTab != aOldData.Ref1.nTab || aData.Ref2.nTab != aOldData.Ref2.nTab) + // Sheet ranges differ. + continue; + + if (isContained(aOldData, aData)) + // This new range is part of an existing range. Skip it. + return; + + bool bSameRows = (aData.Ref1.nRow == aOldData.Ref1.nRow) && (aData.Ref2.nRow == aOldData.Ref2.nRow); + bool bSameCols = (aData.Ref1.nCol == aOldData.Ref1.nCol) && (aData.Ref2.nCol == aOldData.Ref2.nCol); + ScComplexRefData aNewData = aOldData; + bool bJoinRanges = false; + if (bSameRows) + { + bJoinRanges = overlaps( + aData.Ref1.nCol, aData.Ref2.nCol, aOldData.Ref1.nCol, aOldData.Ref2.nCol, + aNewData.Ref1.nCol, aNewData.Ref2.nCol); + } + else if (bSameCols) + { + bJoinRanges = overlaps( + aData.Ref1.nRow, aData.Ref2.nRow, aOldData.Ref1.nRow, aOldData.Ref2.nRow, + aNewData.Ref1.nRow, aNewData.Ref2.nRow); + } + + if (bJoinRanges) + { + if (bExternal) + pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData)); + else + pOldToken.reset(new ScDoubleRefToken(aNewData)); + + bJoined = true; + break; + } + } + + if (bJoined) + { + if (rTokens.size() == 1) + // There is only one left. No need to do more joining. + return; + + // Pop the last token from the list, and keep joining recursively. + ScSharedTokenRef p = rTokens.back(); + rTokens.pop_back(); + join(rTokens, p); + } + else + rTokens.push_back(pToken); + } +}; + +} + +void ScRefTokenHelper::join(vector<ScSharedTokenRef>& rTokens, const ScSharedTokenRef& pToken) +{ + JoinRefTokenRanges join; + join(rTokens, pToken); +} + +bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData& rData, const ScSharedTokenRef& pToken) +{ + switch (pToken->GetType()) + { + case svSingleRef: + case svExternalSingleRef: + { + const ScSingleRefData& r = pToken->GetSingleRef(); + rData.Ref1 = r; + rData.Ref1.SetFlag3D(true); + rData.Ref2 = r; + rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference. + } + break; + case svDoubleRef: + case svExternalDoubleRef: + rData = pToken->GetDoubleRef(); + break; + default: + // Not a reference token. Bail out. + return false; + } + return true; +} diff --git a/sc/source/core/tool/refupdat.cxx b/sc/source/core/tool/refupdat.cxx new file mode 100644 index 000000000000..ad11190be75d --- /dev/null +++ b/sc/source/core/tool/refupdat.cxx @@ -0,0 +1,939 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#include <tools/debug.hxx> + +#include "refupdat.hxx" +#include "document.hxx" +#include "compiler.hxx" +#include "bigrange.hxx" +#include "chgtrack.hxx" + +//------------------------------------------------------------------------ + +template< typename R, typename S, typename U > +BOOL lcl_MoveStart( R& rRef, U nStart, S nDelta, U nMask ) +{ + BOOL bCut = FALSE; + if ( rRef >= nStart ) + rRef = sal::static_int_cast<R>( rRef + nDelta ); + else if ( nDelta < 0 && rRef >= nStart + nDelta ) + rRef = nStart + nDelta; //! begrenzen ??? + if ( rRef < 0 ) + { + rRef = 0; + bCut = TRUE; + } + else if ( rRef > nMask ) + { + rRef = nMask; + bCut = TRUE; + } + return bCut; +} + +template< typename R, typename S, typename U > +BOOL lcl_MoveEnd( R& rRef, U nStart, S nDelta, U nMask ) +{ + BOOL bCut = FALSE; + if ( rRef >= nStart ) + rRef = sal::static_int_cast<R>( rRef + nDelta ); + else if ( nDelta < 0 && rRef >= nStart + nDelta ) + rRef = nStart + nDelta - 1; //! begrenzen ??? + if ( rRef < 0 ) + { + rRef = 0; + bCut = TRUE; + } + else if ( rRef > nMask ) + { + rRef = nMask; + bCut = TRUE; + } + return bCut; +} + +template< typename R, typename S, typename U > +BOOL lcl_MoveReorder( R& rRef, U nStart, U nEnd, S nDelta ) +{ + if ( rRef >= nStart && rRef <= nEnd ) + { + rRef = sal::static_int_cast<R>( rRef + nDelta ); + return TRUE; + } + + if ( nDelta > 0 ) // nach hinten schieben + { + if ( rRef >= nStart && rRef <= nEnd + nDelta ) + { + if ( rRef <= nEnd ) + rRef = sal::static_int_cast<R>( rRef + nDelta ); // in the moved range + else + rRef -= nEnd - nStart + 1; // nachruecken + return TRUE; + } + } + else // nach vorne schieben + { + if ( rRef >= nStart + nDelta && rRef <= nEnd ) + { + if ( rRef >= nStart ) + rRef = sal::static_int_cast<R>( rRef + nDelta ); // in the moved range + else + rRef += nEnd - nStart + 1; // nachruecken + return TRUE; + } + } + + return FALSE; +} + +template< typename R, typename S, typename U > +BOOL lcl_MoveItCut( R& rRef, S nDelta, U nMask ) +{ + BOOL bCut = FALSE; + rRef = sal::static_int_cast<R>( rRef + nDelta ); + if ( rRef < 0 ) + { + rRef = 0; + bCut = TRUE; + } + else if ( rRef > nMask ) + { + rRef = nMask; + bCut = TRUE; + } + return bCut; +} + +template< typename R, typename S, typename U > +void lcl_MoveItWrap( R& rRef, S nDelta, U nMask ) +{ + rRef = sal::static_int_cast<R>( rRef + nDelta ); + if ( rRef < 0 ) + rRef += nMask+1; + else if ( rRef > nMask ) + rRef -= nMask+1; +} + +template< typename R, typename S, typename U > +BOOL lcl_MoveRefPart( R& rRef1Val, BOOL& rRef1Del, BOOL bDo1, + R& rRef2Val, BOOL& rRef2Del, BOOL bDo2, + U nStart, U nEnd, S nDelta, U nMask ) +{ + if ( nDelta ) + { + BOOL bDel, bCut1, bCut2; + bDel = bCut1 = bCut2 = FALSE; + S n; + if (bDo1 && bDo2) + { + if ( nDelta < 0 ) + { + n = nStart + nDelta; + if ( n <= rRef1Val && rRef1Val < nStart + && n <= rRef2Val && rRef2Val < nStart ) + bDel = TRUE; + } + else + { + n = nEnd + nDelta; + if ( nEnd < rRef1Val && rRef1Val <= n + && nEnd < rRef2Val && rRef2Val <= n ) + bDel = TRUE; + } + } + if ( bDel ) + { // move deleted along + rRef1Val = sal::static_int_cast<R>( rRef1Val + nDelta ); + rRef2Val = sal::static_int_cast<R>( rRef2Val + nDelta ); + } + else + { + if (bDo1) + { + if ( rRef1Del ) + rRef1Val = sal::static_int_cast<R>( rRef1Val + nDelta ); + else + bCut1 = lcl_MoveStart( rRef1Val, nStart, nDelta, nMask ); + } + if (bDo2) + { + if ( rRef2Del ) + rRef2Val = sal::static_int_cast<R>( rRef2Val + nDelta ); + else + bCut2 = lcl_MoveEnd( rRef2Val, nStart, nDelta, nMask ); + } + } + if ( bDel || (bCut1 && bCut2) ) + rRef1Del = rRef2Del = TRUE; + return bDel || bCut1 || bCut2 || rRef1Del || rRef2Del; + } + else + return FALSE; +} + +template< typename R, typename S, typename U > +BOOL IsExpand( R n1, R n2, U nStart, S nD ) +{ //! vor normalem Move... + return + nD > 0 // Insert + && n1 < n2 // mindestens zwei Cols/Rows/Tabs in Ref + && ( + (nStart <= n1 && n1 < nStart + nD) // n1 innerhalb des Insert + || (n2 + 1 == nStart) // n2 direkt vor Insert + ); // n1 < nStart <= n2 wird sowieso expanded! +} + + +template< typename R, typename S, typename U > +void Expand( R& n1, R& n2, U nStart, S nD ) +{ //! nach normalem Move..., nur wenn IsExpand vorher TRUE war! + //! erst das Ende + if ( n2 + 1 == nStart ) + { // am Ende + n2 = sal::static_int_cast<R>( n2 + nD ); + return; + } + // am Anfang + n1 = sal::static_int_cast<R>( n1 - nD ); +} + + +BOOL lcl_IsWrapBig( INT32 nRef, INT32 nDelta ) +{ + if ( nRef > 0 && nDelta > 0 ) + return nRef + nDelta <= 0; + else if ( nRef < 0 && nDelta < 0 ) + return nRef + nDelta >= 0; + return FALSE; +} + + +BOOL lcl_MoveBig( INT32& rRef, INT32 nStart, INT32 nDelta ) +{ + BOOL bCut = FALSE; + if ( rRef >= nStart ) + { + if ( nDelta > 0 ) + bCut = lcl_IsWrapBig( rRef, nDelta ); + if ( bCut ) + rRef = nInt32Max; + else + rRef += nDelta; + } + return bCut; +} + +BOOL lcl_MoveItCutBig( INT32& rRef, INT32 nDelta ) +{ + BOOL bCut = lcl_IsWrapBig( rRef, nDelta ); + rRef += nDelta; + return bCut; +} + + +ScRefUpdateRes ScRefUpdate::Update( ScDocument* pDoc, UpdateRefMode eUpdateRefMode, + SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz, + SCCOL& theCol1, SCROW& theRow1, SCTAB& theTab1, + SCCOL& theCol2, SCROW& theRow2, SCTAB& theTab2 ) +{ + ScRefUpdateRes eRet = UR_NOTHING; + + SCCOL oldCol1 = theCol1; + SCROW oldRow1 = theRow1; + SCTAB oldTab1 = theTab1; + SCCOL oldCol2 = theCol2; + SCROW oldRow2 = theRow2; + SCTAB oldTab2 = theTab2; + + BOOL bCut1, bCut2; + + if (eUpdateRefMode == URM_INSDEL) + { + BOOL bExpand = pDoc->IsExpandRefs(); + if ( nDx && (theRow1 >= nRow1) && (theRow2 <= nRow2) && + (theTab1 >= nTab1) && (theTab2 <= nTab2) ) + { + BOOL bExp = (bExpand && IsExpand( theCol1, theCol2, nCol1, nDx )); + bCut1 = lcl_MoveStart( theCol1, nCol1, nDx, MAXCOL ); + bCut2 = lcl_MoveEnd( theCol2, nCol1, nDx, MAXCOL ); + if ( theCol2 < theCol1 ) + { + eRet = UR_INVALID; + theCol2 = theCol1; + } + else if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + if ( bExp ) + { + Expand( theCol1, theCol2, nCol1, nDx ); + eRet = UR_UPDATED; + } + } + if ( nDy && (theCol1 >= nCol1) && (theCol2 <= nCol2) && + (theTab1 >= nTab1) && (theTab2 <= nTab2) ) + { + BOOL bExp = (bExpand && IsExpand( theRow1, theRow2, nRow1, nDy )); + bCut1 = lcl_MoveStart( theRow1, nRow1, nDy, MAXROW ); + bCut2 = lcl_MoveEnd( theRow2, nRow1, nDy, MAXROW ); + if ( theRow2 < theRow1 ) + { + eRet = UR_INVALID; + theRow2 = theRow1; + } + else if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + if ( bExp ) + { + Expand( theRow1, theRow2, nRow1, nDy ); + eRet = UR_UPDATED; + } + } + if ( nDz && (theCol1 >= nCol1) && (theCol2 <= nCol2) && + (theRow1 >= nRow1) && (theRow2 <= nRow2) ) + { + SCsTAB nMaxTab = pDoc->GetTableCount() - 1; + nMaxTab = sal::static_int_cast<SCsTAB>(nMaxTab + nDz); // adjust to new count + BOOL bExp = (bExpand && IsExpand( theTab1, theTab2, nTab1, nDz )); + bCut1 = lcl_MoveStart( theTab1, nTab1, nDz, static_cast<SCTAB>(nMaxTab) ); + bCut2 = lcl_MoveEnd( theTab2, nTab1, nDz, static_cast<SCTAB>(nMaxTab) ); + if ( theTab2 < theTab1 ) + { + eRet = UR_INVALID; + theTab2 = theTab1; + } + else if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + if ( bExp ) + { + Expand( theTab1, theTab2, nTab1, nDz ); + eRet = UR_UPDATED; + } + } + } + else if (eUpdateRefMode == URM_MOVE) + { + if ((theCol1 >= nCol1-nDx) && (theRow1 >= nRow1-nDy) && (theTab1 >= nTab1-nDz) && + (theCol2 <= nCol2-nDx) && (theRow2 <= nRow2-nDy) && (theTab2 <= nTab2-nDz)) + { + if ( nDx ) + { + bCut1 = lcl_MoveItCut( theCol1, nDx, MAXCOL ); + bCut2 = lcl_MoveItCut( theCol2, nDx, MAXCOL ); + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + } + if ( nDy ) + { + bCut1 = lcl_MoveItCut( theRow1, nDy, MAXROW ); + bCut2 = lcl_MoveItCut( theRow2, nDy, MAXROW ); + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + } + if ( nDz ) + { + SCsTAB nMaxTab = (SCsTAB) pDoc->GetTableCount() - 1; + bCut1 = lcl_MoveItCut( theTab1, nDz, static_cast<SCTAB>(nMaxTab) ); + bCut2 = lcl_MoveItCut( theTab2, nDz, static_cast<SCTAB>(nMaxTab) ); + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + } + } + } + else if (eUpdateRefMode == URM_REORDER) + { + // bisher nur fuer nDz (MoveTab) + DBG_ASSERT ( !nDx && !nDy, "URM_REORDER fuer x und y noch nicht implementiert" ); + + if ( nDz && (theCol1 >= nCol1) && (theCol2 <= nCol2) && + (theRow1 >= nRow1) && (theRow2 <= nRow2) ) + { + bCut1 = lcl_MoveReorder( theTab1, nTab1, nTab2, nDz ); + bCut2 = lcl_MoveReorder( theTab2, nTab1, nTab2, nDz ); + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + } + } + + if ( eRet == UR_NOTHING ) + { + if (oldCol1 != theCol1 + || oldRow1 != theRow1 + || oldTab1 != theTab1 + || oldCol2 != theCol2 + || oldRow2 != theRow2 + || oldTab2 != theTab2 + ) + eRet = UR_UPDATED; + } + return eRet; +} + + +// simples UpdateReference fuer ScBigRange (ScChangeAction/ScChangeTrack) +// Referenzen koennen auch ausserhalb des Dokuments liegen! +// Ganze Spalten/Zeilen (nInt32Min..nInt32Max) bleiben immer solche! +ScRefUpdateRes ScRefUpdate::Update( UpdateRefMode eUpdateRefMode, + const ScBigRange& rWhere, INT32 nDx, INT32 nDy, INT32 nDz, + ScBigRange& rWhat ) +{ + ScRefUpdateRes eRet = UR_NOTHING; + const ScBigRange aOldRange( rWhat ); + + INT32 nCol1, nRow1, nTab1, nCol2, nRow2, nTab2; + INT32 theCol1, theRow1, theTab1, theCol2, theRow2, theTab2; + rWhere.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + rWhat.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 ); + + BOOL bCut1, bCut2; + + if (eUpdateRefMode == URM_INSDEL) + { + if ( nDx && (theRow1 >= nRow1) && (theRow2 <= nRow2) && + (theTab1 >= nTab1) && (theTab2 <= nTab2) && + !(theCol1 == nInt32Min && theCol2 == nInt32Max) ) + { + bCut1 = lcl_MoveBig( theCol1, nCol1, nDx ); + bCut2 = lcl_MoveBig( theCol2, nCol1, nDx ); + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + rWhat.aStart.SetCol( theCol1 ); + rWhat.aEnd.SetCol( theCol2 ); + } + if ( nDy && (theCol1 >= nCol1) && (theCol2 <= nCol2) && + (theTab1 >= nTab1) && (theTab2 <= nTab2) && + !(theRow1 == nInt32Min && theRow2 == nInt32Max) ) + { + bCut1 = lcl_MoveBig( theRow1, nRow1, nDy ); + bCut2 = lcl_MoveBig( theRow2, nRow1, nDy ); + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + rWhat.aStart.SetRow( theRow1 ); + rWhat.aEnd.SetRow( theRow2 ); + } + if ( nDz && (theCol1 >= nCol1) && (theCol2 <= nCol2) && + (theRow1 >= nRow1) && (theRow2 <= nRow2) && + !(theTab1 == nInt32Min && theTab2 == nInt32Max) ) + { + bCut1 = lcl_MoveBig( theTab1, nTab1, nDz ); + bCut2 = lcl_MoveBig( theTab2, nTab1, nDz ); + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + rWhat.aStart.SetTab( theTab1 ); + rWhat.aEnd.SetTab( theTab2 ); + } + } + else if (eUpdateRefMode == URM_MOVE) + { + if ( rWhere.In( rWhat ) ) + { + if ( nDx && !(theCol1 == nInt32Min && theCol2 == nInt32Max) ) + { + bCut1 = lcl_MoveItCutBig( theCol1, nDx ); + bCut2 = lcl_MoveItCutBig( theCol2, nDx ); + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + rWhat.aStart.SetCol( theCol1 ); + rWhat.aEnd.SetCol( theCol2 ); + } + if ( nDy && !(theRow1 == nInt32Min && theRow2 == nInt32Max) ) + { + bCut1 = lcl_MoveItCutBig( theRow1, nDy ); + bCut2 = lcl_MoveItCutBig( theRow2, nDy ); + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + rWhat.aStart.SetRow( theRow1 ); + rWhat.aEnd.SetRow( theRow2 ); + } + if ( nDz && !(theTab1 == nInt32Min && theTab2 == nInt32Max) ) + { + bCut1 = lcl_MoveItCutBig( theTab1, nDz ); + bCut2 = lcl_MoveItCutBig( theTab2, nDz ); + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + rWhat.aStart.SetTab( theTab1 ); + rWhat.aEnd.SetTab( theTab2 ); + } + } + } + + if ( eRet == UR_NOTHING && rWhat != aOldRange ) + eRet = UR_UPDATED; + + return eRet; +} + + +ScRefUpdateRes ScRefUpdate::Update( ScDocument* pDoc, UpdateRefMode eMode, + const ScAddress& rPos, const ScRange& r, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz, + ScComplexRefData& rRef, WhatType eWhat ) +{ + ScRefUpdateRes eRet = UR_NOTHING; + + SCCOL nCol1 = r.aStart.Col(); + SCROW nRow1 = r.aStart.Row(); + SCTAB nTab1 = r.aStart.Tab(); + SCCOL nCol2 = r.aEnd.Col(); + SCROW nRow2 = r.aEnd.Row(); + SCTAB nTab2 = r.aEnd.Tab(); + + if( eMode == URM_INSDEL ) + { + BOOL bExpand = pDoc->IsExpandRefs(); + + const ScChangeTrack* pChangeTrack = pDoc->GetChangeTrack(); + BOOL bInDeleteUndo = + ( pChangeTrack ? pChangeTrack->IsInDeleteUndo() : FALSE ); + + SCCOL oldCol1 = rRef.Ref1.nCol; + SCROW oldRow1 = rRef.Ref1.nRow; + SCTAB oldTab1 = rRef.Ref1.nTab; + SCCOL oldCol2 = rRef.Ref2.nCol; + SCROW oldRow2 = rRef.Ref2.nRow; + SCTAB oldTab2 = rRef.Ref2.nTab; + + BOOL bRef1ColDel = rRef.Ref1.IsColDeleted(); + BOOL bRef2ColDel = rRef.Ref2.IsColDeleted(); + BOOL bRef1RowDel = rRef.Ref1.IsRowDeleted(); + BOOL bRef2RowDel = rRef.Ref2.IsRowDeleted(); + BOOL bRef1TabDel = rRef.Ref1.IsTabDeleted(); + BOOL bRef2TabDel = rRef.Ref2.IsTabDeleted(); + + if( nDx && + ((rRef.Ref1.nRow >= nRow1 + && rRef.Ref2.nRow <= nRow2) || (bRef1RowDel || bRef2RowDel)) + && + ((rRef.Ref1.nTab >= nTab1 + && rRef.Ref2.nTab <= nTab2) || (bRef1TabDel || bRef2TabDel)) + ) + { + BOOL bExp = (bExpand && !bInDeleteUndo && IsExpand( rRef.Ref1.nCol, + rRef.Ref2.nCol, nCol1, nDx )); + BOOL bDo1 = (eWhat == ScRefUpdate::ALL || (eWhat == + ScRefUpdate::ABSOLUTE && !rRef.Ref1.IsColRel())); + BOOL bDo2 = (eWhat == ScRefUpdate::ALL || (eWhat == + ScRefUpdate::ABSOLUTE && !rRef.Ref2.IsColRel())); + if ( lcl_MoveRefPart( rRef.Ref1.nCol, bRef1ColDel, bDo1, + rRef.Ref2.nCol, bRef2ColDel, bDo2, + nCol1, nCol2, nDx, MAXCOL ) ) + { + eRet = UR_UPDATED; + if ( bInDeleteUndo && (bRef1ColDel || bRef2ColDel) ) + { + if ( bRef1ColDel && nCol1 <= rRef.Ref1.nCol && + rRef.Ref1.nCol <= nCol1 + nDx ) + rRef.Ref1.SetColDeleted( FALSE ); + if ( bRef2ColDel && nCol1 <= rRef.Ref2.nCol && + rRef.Ref2.nCol <= nCol1 + nDx ) + rRef.Ref2.SetColDeleted( FALSE ); + } + else + { + if ( bRef1ColDel ) + rRef.Ref1.SetColDeleted( TRUE ); + if ( bRef2ColDel ) + rRef.Ref2.SetColDeleted( TRUE ); + } + } + if ( bExp ) + { + Expand( rRef.Ref1.nCol, rRef.Ref2.nCol, nCol1, nDx ); + eRet = UR_UPDATED; + } + } + if( nDy && + ((rRef.Ref1.nCol >= nCol1 + && rRef.Ref2.nCol <= nCol2) || (bRef1ColDel || bRef2ColDel)) + && + ((rRef.Ref1.nTab >= nTab1 + && rRef.Ref2.nTab <= nTab2) || (bRef1TabDel || bRef2TabDel)) + ) + { + BOOL bExp = (bExpand && !bInDeleteUndo && IsExpand( rRef.Ref1.nRow, + rRef.Ref2.nRow, nRow1, nDy )); + BOOL bDo1 = (eWhat == ScRefUpdate::ALL || (eWhat == + ScRefUpdate::ABSOLUTE && !rRef.Ref1.IsRowRel())); + BOOL bDo2 = (eWhat == ScRefUpdate::ALL || (eWhat == + ScRefUpdate::ABSOLUTE && !rRef.Ref2.IsRowRel())); + if ( lcl_MoveRefPart( rRef.Ref1.nRow, bRef1RowDel, bDo1, + rRef.Ref2.nRow, bRef2RowDel, bDo2, + nRow1, nRow2, nDy, MAXROW ) ) + { + eRet = UR_UPDATED; + if ( bInDeleteUndo && (bRef1RowDel || bRef2RowDel) ) + { + if ( bRef1RowDel && nRow1 <= rRef.Ref1.nRow && + rRef.Ref1.nRow <= nRow1 + nDy ) + rRef.Ref1.SetRowDeleted( FALSE ); + if ( bRef2RowDel && nRow1 <= rRef.Ref2.nRow && + rRef.Ref2.nRow <= nRow1 + nDy ) + rRef.Ref2.SetRowDeleted( FALSE ); + } + else + { + if ( bRef1RowDel ) + rRef.Ref1.SetRowDeleted( TRUE ); + if ( bRef2RowDel ) + rRef.Ref2.SetRowDeleted( TRUE ); + } + } + if ( bExp ) + { + Expand( rRef.Ref1.nRow, rRef.Ref2.nRow, nRow1, nDy ); + eRet = UR_UPDATED; + } + } + if( nDz && + ((rRef.Ref1.nCol >= nCol1 + && rRef.Ref2.nCol <= nCol2) || (bRef1ColDel || bRef2ColDel)) + && + ((rRef.Ref1.nRow >= nRow1 + && rRef.Ref2.nRow <= nRow2) || (bRef1RowDel || bRef2RowDel)) + ) + { + BOOL bExp = (bExpand && !bInDeleteUndo && IsExpand( rRef.Ref1.nTab, + rRef.Ref2.nTab, nTab1, nDz )); + SCTAB nMaxTab = pDoc->GetTableCount() - 1; + BOOL bDo1 = (eWhat == ScRefUpdate::ALL || (eWhat == + ScRefUpdate::ABSOLUTE && !rRef.Ref1.IsTabRel())); + BOOL bDo2 = (eWhat == ScRefUpdate::ALL || (eWhat == + ScRefUpdate::ABSOLUTE && !rRef.Ref2.IsTabRel())); + if ( lcl_MoveRefPart( rRef.Ref1.nTab, bRef1TabDel, bDo1, + rRef.Ref2.nTab, bRef2TabDel, bDo2, + nTab1, nTab2, nDz, nMaxTab ) ) + { + eRet = UR_UPDATED; + if ( bInDeleteUndo && (bRef1TabDel || bRef2TabDel) ) + { + if ( bRef1TabDel && nTab1 <= rRef.Ref1.nTab && + rRef.Ref1.nTab <= nTab1 + nDz ) + rRef.Ref1.SetTabDeleted( FALSE ); + if ( bRef2TabDel && nTab1 <= rRef.Ref2.nTab && + rRef.Ref2.nTab <= nTab1 + nDz ) + rRef.Ref2.SetTabDeleted( FALSE ); + } + else + { + if ( bRef1TabDel ) + rRef.Ref1.SetTabDeleted( TRUE ); + if ( bRef2TabDel ) + rRef.Ref2.SetTabDeleted( TRUE ); + } + } + if ( bExp ) + { + Expand( rRef.Ref1.nTab, rRef.Ref2.nTab, nTab1, nDz ); + eRet = UR_UPDATED; + } + } + if ( eRet == UR_NOTHING ) + { + if (oldCol1 != rRef.Ref1.nCol + || oldRow1 != rRef.Ref1.nRow + || oldTab1 != rRef.Ref1.nTab + || oldCol2 != rRef.Ref2.nCol + || oldRow2 != rRef.Ref2.nRow + || oldTab2 != rRef.Ref2.nTab + ) + eRet = UR_UPDATED; + } + if (eWhat != ScRefUpdate::ABSOLUTE) + rRef.CalcRelFromAbs( rPos ); + } + else + { + if( eMode == URM_MOVE ) + { + if ( rRef.Ref1.nCol >= nCol1-nDx + && rRef.Ref1.nRow >= nRow1-nDy + && rRef.Ref1.nTab >= nTab1-nDz + && rRef.Ref2.nCol <= nCol2-nDx + && rRef.Ref2.nRow <= nRow2-nDy + && rRef.Ref2.nTab <= nTab2-nDz ) + { + eRet = Move( pDoc, rPos, nDx, nDy, nDz, rRef, FALSE, TRUE ); // immer verschieben + } + else if ( nDz && r.In( rPos ) ) + { + rRef.Ref1.SetFlag3D( TRUE ); + rRef.Ref2.SetFlag3D( TRUE ); + eRet = UR_UPDATED; + if (eWhat != ScRefUpdate::ABSOLUTE) + rRef.CalcRelFromAbs( rPos ); + } + else if (eWhat != ScRefUpdate::ABSOLUTE) + rRef.CalcRelFromAbs( rPos ); + } + else if( eMode == URM_COPY && r.In( rPos ) ) + eRet = Move( pDoc, rPos, nDx, nDy, nDz, rRef, FALSE, FALSE ); // nur relative + // sollte nicht mehr verwendet werden muessen + else if (eWhat != ScRefUpdate::ABSOLUTE) + rRef.CalcRelFromAbs( rPos ); + } + return eRet; +} + + +ScRefUpdateRes ScRefUpdate::Move( ScDocument* pDoc, const ScAddress& rPos, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz, + ScComplexRefData& rRef, BOOL bWrap, BOOL bAbsolute ) +{ + ScRefUpdateRes eRet = UR_NOTHING; + + SCCOL oldCol1 = rRef.Ref1.nCol; + SCROW oldRow1 = rRef.Ref1.nRow; + SCTAB oldTab1 = rRef.Ref1.nTab; + SCCOL oldCol2 = rRef.Ref2.nCol; + SCROW oldRow2 = rRef.Ref2.nRow; + SCTAB oldTab2 = rRef.Ref2.nTab; + + BOOL bCut1, bCut2; + if ( nDx ) + { + bCut1 = bCut2 = FALSE; + if( bAbsolute || rRef.Ref1.IsColRel() ) + { + if( bWrap ) + lcl_MoveItWrap( rRef.Ref1.nCol, nDx, MAXCOL ); + else + bCut1 = lcl_MoveItCut( rRef.Ref1.nCol, nDx, MAXCOL ); + } + if( bAbsolute || rRef.Ref2.IsColRel() ) + { + if( bWrap ) + lcl_MoveItWrap( rRef.Ref2.nCol, nDx, MAXCOL ); + else + bCut2 = lcl_MoveItCut( rRef.Ref2.nCol, nDx, MAXCOL ); + } + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + if ( bCut1 && bCut2 ) + { + rRef.Ref1.SetColDeleted( TRUE ); + rRef.Ref2.SetColDeleted( TRUE ); + } + } + if ( nDy ) + { + bCut1 = bCut2 = FALSE; + if( bAbsolute || rRef.Ref1.IsRowRel() ) + { + if( bWrap ) + lcl_MoveItWrap( rRef.Ref1.nRow, nDy, MAXROW ); + else + bCut1 = lcl_MoveItCut( rRef.Ref1.nRow, nDy, MAXROW ); + } + if( bAbsolute || rRef.Ref2.IsRowRel() ) + { + if( bWrap ) + lcl_MoveItWrap( rRef.Ref2.nRow, nDy, MAXROW ); + else + bCut2 = lcl_MoveItCut( rRef.Ref2.nRow, nDy, MAXROW ); + } + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + if ( bCut1 && bCut2 ) + { + rRef.Ref1.SetRowDeleted( TRUE ); + rRef.Ref2.SetRowDeleted( TRUE ); + } + } + if ( nDz ) + { + bCut1 = bCut2 = FALSE; + SCsTAB nMaxTab = (SCsTAB) pDoc->GetTableCount() - 1; + if( bAbsolute || rRef.Ref1.IsTabRel() ) + { + if( bWrap ) + lcl_MoveItWrap( rRef.Ref1.nTab, nDz, static_cast<SCTAB>(nMaxTab) ); + else + bCut1 = lcl_MoveItCut( rRef.Ref1.nTab, nDz, static_cast<SCTAB>(nMaxTab) ); + rRef.Ref1.SetFlag3D( rPos.Tab() != rRef.Ref1.nTab ); + } + if( bAbsolute || rRef.Ref2.IsTabRel() ) + { + if( bWrap ) + lcl_MoveItWrap( rRef.Ref2.nTab, nDz, static_cast<SCTAB>(nMaxTab) ); + else + bCut2 = lcl_MoveItCut( rRef.Ref2.nTab, nDz, static_cast<SCTAB>(nMaxTab) ); + rRef.Ref2.SetFlag3D( rPos.Tab() != rRef.Ref2.nTab ); + } + if ( bCut1 || bCut2 ) + eRet = UR_UPDATED; + if ( bCut1 && bCut2 ) + { + rRef.Ref1.SetTabDeleted( TRUE ); + rRef.Ref2.SetTabDeleted( TRUE ); + } + } + + if ( eRet == UR_NOTHING ) + { + if (oldCol1 != rRef.Ref1.nCol + || oldRow1 != rRef.Ref1.nRow + || oldTab1 != rRef.Ref1.nTab + || oldCol2 != rRef.Ref2.nCol + || oldRow2 != rRef.Ref2.nRow + || oldTab2 != rRef.Ref2.nTab + ) + eRet = UR_UPDATED; + } + if ( bWrap && eRet != UR_NOTHING ) + rRef.PutInOrder(); + rRef.CalcRelFromAbs( rPos ); + return eRet; +} + +void ScRefUpdate::MoveRelWrap( ScDocument* pDoc, const ScAddress& rPos, + SCCOL nMaxCol, SCROW nMaxRow, ScComplexRefData& rRef ) +{ + if( rRef.Ref1.IsColRel() ) + { + rRef.Ref1.nCol = rRef.Ref1.nRelCol + rPos.Col(); + lcl_MoveItWrap( rRef.Ref1.nCol, static_cast<SCsCOL>(0), nMaxCol ); + } + if( rRef.Ref2.IsColRel() ) + { + rRef.Ref2.nCol = rRef.Ref2.nRelCol + rPos.Col(); + lcl_MoveItWrap( rRef.Ref2.nCol, static_cast<SCsCOL>(0), nMaxCol ); + } + if( rRef.Ref1.IsRowRel() ) + { + rRef.Ref1.nRow = rRef.Ref1.nRelRow + rPos.Row(); + lcl_MoveItWrap( rRef.Ref1.nRow, static_cast<SCsROW>(0), nMaxRow ); + } + if( rRef.Ref2.IsRowRel() ) + { + rRef.Ref2.nRow = rRef.Ref2.nRelRow + rPos.Row(); + lcl_MoveItWrap( rRef.Ref2.nRow, static_cast<SCsROW>(0), nMaxRow ); + } + SCsTAB nMaxTab = (SCsTAB) pDoc->GetTableCount() - 1; + if( rRef.Ref1.IsTabRel() ) + { + rRef.Ref1.nTab = rRef.Ref1.nRelTab + rPos.Tab(); + lcl_MoveItWrap( rRef.Ref1.nTab, static_cast<SCsTAB>(0), static_cast<SCTAB>(nMaxTab) ); + } + if( rRef.Ref2.IsTabRel() ) + { + rRef.Ref2.nTab = rRef.Ref2.nRelTab + rPos.Tab(); + lcl_MoveItWrap( rRef.Ref2.nTab, static_cast<SCsTAB>(0), static_cast<SCTAB>(nMaxTab) ); + } + rRef.PutInOrder(); + rRef.CalcRelFromAbs( rPos ); +} + +//------------------------------------------------------------------ + +void ScRefUpdate::DoTranspose( SCsCOL& rCol, SCsROW& rRow, SCsTAB& rTab, + ScDocument* pDoc, const ScRange& rSource, const ScAddress& rDest ) +{ + SCsTAB nDz = ((SCsTAB)rDest.Tab())-(SCsTAB)rSource.aStart.Tab(); + if (nDz) + { + SCsTAB nNewTab = rTab+nDz; + SCsTAB nCount = pDoc->GetTableCount(); + while (nNewTab<0) nNewTab = sal::static_int_cast<SCsTAB>( nNewTab + nCount ); + while (nNewTab>=nCount) nNewTab = sal::static_int_cast<SCsTAB>( nNewTab - nCount ); + rTab = nNewTab; + } + DBG_ASSERT( rCol>=rSource.aStart.Col() && rRow>=rSource.aStart.Row(), + "UpdateTranspose: Pos. falsch" ); + + SCsCOL nRelX = rCol - (SCsCOL)rSource.aStart.Col(); + SCsROW nRelY = rRow - (SCsROW)rSource.aStart.Row(); + + rCol = static_cast<SCsCOL>(static_cast<SCsCOLROW>(rDest.Col()) + + static_cast<SCsCOLROW>(nRelY)); + rRow = static_cast<SCsROW>(static_cast<SCsCOLROW>(rDest.Row()) + + static_cast<SCsCOLROW>(nRelX)); +} + + +ScRefUpdateRes ScRefUpdate::UpdateTranspose( ScDocument* pDoc, + const ScRange& rSource, const ScAddress& rDest, + ScComplexRefData& rRef ) +{ + ScRefUpdateRes eRet = UR_NOTHING; + if ( rRef.Ref1.nCol >= rSource.aStart.Col() && rRef.Ref2.nCol <= rSource.aEnd.Col() && + rRef.Ref1.nRow >= rSource.aStart.Row() && rRef.Ref2.nRow <= rSource.aEnd.Row() && + rRef.Ref1.nTab >= rSource.aStart.Tab() && rRef.Ref2.nTab <= rSource.aEnd.Tab() ) + { + DoTranspose( rRef.Ref1.nCol, rRef.Ref1.nRow, rRef.Ref1.nTab, pDoc, rSource, rDest ); + DoTranspose( rRef.Ref2.nCol, rRef.Ref2.nRow, rRef.Ref2.nTab, pDoc, rSource, rDest ); + eRet = UR_UPDATED; + } + return eRet; +} + +//------------------------------------------------------------------ + +// UpdateGrow - erweitert Referenzen, die genau auf den Bereich zeigen +// kommt ohne Dokument aus + + +ScRefUpdateRes ScRefUpdate::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY, + ScComplexRefData& rRef ) +{ + ScRefUpdateRes eRet = UR_NOTHING; + + // in Y-Richtung darf die Ref auch eine Zeile weiter unten anfangen, + // falls ein Bereich Spaltenkoepfe enthaelt + + BOOL bUpdateX = ( nGrowX && + rRef.Ref1.nCol == rArea.aStart.Col() && rRef.Ref2.nCol == rArea.aEnd.Col() && + rRef.Ref1.nRow >= rArea.aStart.Row() && rRef.Ref2.nRow <= rArea.aEnd.Row() && + rRef.Ref1.nTab >= rArea.aStart.Tab() && rRef.Ref2.nTab <= rArea.aEnd.Tab() ); + BOOL bUpdateY = ( nGrowY && + rRef.Ref1.nCol >= rArea.aStart.Col() && rRef.Ref2.nCol <= rArea.aEnd.Col() && + ( rRef.Ref1.nRow == rArea.aStart.Row() || rRef.Ref1.nRow == rArea.aStart.Row()+1 ) && + rRef.Ref2.nRow == rArea.aEnd.Row() && + rRef.Ref1.nTab >= rArea.aStart.Tab() && rRef.Ref2.nTab <= rArea.aEnd.Tab() ); + + if ( bUpdateX ) + { + rRef.Ref2.nCol = sal::static_int_cast<SCsCOL>( rRef.Ref2.nCol + nGrowX ); + eRet = UR_UPDATED; + } + if ( bUpdateY ) + { + rRef.Ref2.nRow = sal::static_int_cast<SCsROW>( rRef.Ref2.nRow + nGrowY ); + eRet = UR_UPDATED; + } + + return eRet; +} + + diff --git a/sc/source/core/tool/scmatrix.cxx b/sc/source/core/tool/scmatrix.cxx new file mode 100644 index 000000000000..2ecc20f53405 --- /dev/null +++ b/sc/source/core/tool/scmatrix.cxx @@ -0,0 +1,859 @@ +/************************************************************************* + * + * 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 <tools/debug.hxx> + +#include "scmatrix.hxx" +#include "global.hxx" +#include "address.hxx" +#include "formula/errorcodes.hxx" +#include "interpre.hxx" +#include <svl/zforlist.hxx> +#include <tools/stream.hxx> +#include <rtl/math.hxx> + +#include <math.h> + +//------------------------------------------------------------------------ + +void ScMatrix::CreateMatrix(SCSIZE nC, SCSIZE nR) // nur fuer ctor +{ + pErrorInterpreter = NULL; + nColCount = nC; + nRowCount = nR; + SCSIZE nCount = nColCount * nRowCount; + if ( !nCount || nCount > GetElementsMax() ) + { + DBG_ERRORFILE("ScMatrix::CreateMatrix: dimension error"); + nColCount = nRowCount = 1; + pMat = new ScMatrixValue[1]; + pMat[0].fVal = CreateDoubleError( errStackOverflow); + } + else + pMat = new ScMatrixValue[nCount]; + mnValType = NULL; + mnNonValue = 0; +} + +void ScMatrix::Clear() +{ + DeleteIsString(); + delete [] pMat; +} + +ScMatrix::~ScMatrix() +{ + Clear(); +} + +ScMatrix* ScMatrix::Clone() const +{ + ScMatrix* pScMat = new ScMatrix( nColCount, nRowCount); + MatCopy(*pScMat); + pScMat->SetErrorInterpreter( pErrorInterpreter); // TODO: really? + return pScMat; +} + +ScMatrix* ScMatrix::CloneIfConst() +{ + return (mbCloneIfConst || IsEternalRef()) ? Clone() : this; +} + +void ScMatrix::Resize( SCSIZE nC, SCSIZE nR) +{ + Clear(); + CreateMatrix(nC, nR); +} + +ScMatrix* ScMatrix::CloneAndExtend( SCSIZE nNewCols, SCSIZE nNewRows ) const +{ + ScMatrix* pScMat = new ScMatrix( nNewCols, nNewRows); + MatCopy(*pScMat); + pScMat->SetErrorInterpreter( pErrorInterpreter); + return pScMat; +} + +void ScMatrix::SetErrorAtInterpreter( USHORT nError ) const +{ + if ( pErrorInterpreter ) + pErrorInterpreter->SetError( nError); +} + +// +// File format: USHORT columns, USHORT rows, (columns*rows) entries: +// BYTE type ( CELLTYPE_NONE, CELLTYPE_VALUE, CELLTYPE_STRING ); nothing, double or String +// + +ScMatrix::ScMatrix(SvStream& /* rStream */) + : pErrorInterpreter( NULL) + , nRefCnt(0) +{ +#if SC_ROWLIMIT_STREAM_ACCESS +#error address types changed! + USHORT nC; + USHORT nR; + + rStream >> nC; + rStream >> nR; + + CreateMatrix(nC, nR); + DBG_ASSERT( pMat, "pMat == NULL" ); + + String aMatStr; + double fVal; + rtl_TextEncoding eCharSet = rStream.GetStreamCharSet(); + SCSIZE nCount = nColCount * nRowCount; + SCSIZE nReadCount = (SCSIZE) nC * nR; + for (SCSIZE i=0; i<nReadCount; i++) + { + BYTE nType; + rStream >> nType; + if ( nType == CELLTYPE_VALUE ) + { + if ( i < nCount ) + rStream >> pMat[i].fVal; + else + rStream >> fVal; + } + else + { + // For unknown types read and forget string (upwards compatibility) + + if ( nType != CELLTYPE_NONE ) + rStream.ReadByteString( aMatStr, eCharSet ); + + if ( i < nCount ) + { + if (!mnValType) + ResetIsString(); // init string flags + mnValType[i] = ( nType == CELLTYPE_NONE ? SC_MATVAL_EMPTY : SC_MATVAL_STRING ); + mnNonValue++; + + if ( nType == CELLTYPE_STRING ) + pMat[i].pS = new String(aMatStr); + else + pMat[i].pS = NULL; + } + } + } +#else + CreateMatrix(0,0); +#endif // SC_ROWLIMIT_STREAM_ACCESS +} + +void ScMatrix::Store(SvStream& /* rStream */) const +{ +#if SC_ROWLIMIT_STREAM_ACCESS +#error address types changed! + SCSIZE nCount = nColCount * nRowCount; + // Don't store matrix with more than USHORT max elements, old versions + // might get confused in loops for(USHORT i=0; i<nC*nR; i++) + if ( !pMat || nCount > ((USHORT)(~0)) ) + { + DBG_ASSERT( pMat, "ScMatrix::Store: pMat == NULL" ); + // We can't store a 0 dimension because old versions rely on some + // matrix being present, e.g. DDE link results, and old versions didn't + // create a matrix if dimension was 0. Store an error result. + rStream << (USHORT) 1; + rStream << (USHORT) 1; + rStream << (BYTE) CELLTYPE_VALUE; + double fVal; + ::rtl::math::setNan( &fVal ); + rStream << fVal; + return; + } + + rStream << (USHORT) nColCount; +#if SC_ROWLIMIT_MORE_THAN_32K + #error row32k +#endif + rStream << (USHORT) nRowCount; + + String aMatStr; + rtl_TextEncoding eCharSet = rStream.GetStreamCharSet(); + for (SCSIZE i=0; i<nCount; i++) + { + BYTE nType = CELLTYPE_VALUE; + if ( mnValType && IsNonValueType( mnValType[i])) + { + if ( pMat[i].pS ) + aMatStr = *pMat[i].pS; + else + aMatStr.Erase(); + + if ( mnValType[i] == SC_MATVAL_STRING ) + nType = CELLTYPE_STRING; + else + nType = CELLTYPE_NONE; + } + rStream << nType; + if ( nType == CELLTYPE_VALUE ) + rStream << pMat[i].fVal; + else if ( nType == CELLTYPE_STRING ) + rStream.WriteByteString( aMatStr, eCharSet ); + } +#endif // SC_ROWLIMIT_STREAM_ACCESS +} + +void ScMatrix::ResetIsString() +{ + SCSIZE nCount = nColCount * nRowCount; + if (mnValType) + { + for (SCSIZE i = 0; i < nCount; i++) + { + if ( IsNonValueType( mnValType[i])) + delete pMat[i].pS; + } + } + else + mnValType = new BYTE[nCount]; + memset( mnValType, 0, nCount * sizeof( BYTE ) ); + mnNonValue = 0; +} + +void ScMatrix::DeleteIsString() +{ + if ( mnValType ) + { + SCSIZE nCount = nColCount * nRowCount; + for ( SCSIZE i = 0; i < nCount; i++ ) + { + if (IsNonValueType( mnValType[i])) + delete pMat[i].pS; + } + delete [] mnValType; + mnValType = NULL; + mnNonValue = 0; + } +} + +void ScMatrix::PutDouble(double fVal, SCSIZE nC, SCSIZE nR) +{ + if (ValidColRow( nC, nR)) + PutDouble( fVal, CalcOffset( nC, nR) ); + else + { + DBG_ERRORFILE("ScMatrix::PutDouble: dimension error"); + } +} + +void ScMatrix::PutString(const String& rStr, SCSIZE nC, SCSIZE nR) +{ + if (ValidColRow( nC, nR)) + PutString( rStr, CalcOffset( nC, nR) ); + else + { + DBG_ERRORFILE("ScMatrix::PutString: dimension error"); + } +} + +void ScMatrix::PutString(const String& rStr, SCSIZE nIndex) +{ + if (mnValType == NULL) + ResetIsString(); + if ( IsNonValueType( mnValType[nIndex]) && pMat[nIndex].pS ) + *(pMat[nIndex].pS) = rStr; + else + { + pMat[nIndex].pS = new String(rStr); + mnNonValue++; + } + mnValType[nIndex] = SC_MATVAL_STRING; +} + +void ScMatrix::PutStringEntry( const String* pStr, BYTE bFlag, SCSIZE nIndex ) +{ + DBG_ASSERT( bFlag, "ScMatrix::PutStringEntry: bFlag == 0" ); + if (mnValType == NULL) + ResetIsString(); + // Make sure all bytes of the union are initialized to be able to access + // the value with if (IsValueOrEmpty()) GetDouble(). Backup pS first. + String* pS = pMat[nIndex].pS; + pMat[nIndex].fVal = 0.0; + // An EMPTY or EMPTYPATH entry must not have a string pointer therefor. + DBG_ASSERT( (((bFlag & SC_MATVAL_EMPTY) == SC_MATVAL_EMPTY) && !pStr) || TRUE, + "ScMatrix::PutStringEntry: pStr passed through EMPTY entry"); + if ( IsNonValueType( mnValType[nIndex]) && pS ) + { + if ((bFlag & SC_MATVAL_EMPTY) == SC_MATVAL_EMPTY) + delete pS, pS = NULL; + if ( pStr ) + *pS = *pStr; + else if (pS) + pS->Erase(); + pMat[nIndex].pS = pS; + } + else + { + pMat[nIndex].pS = (pStr ? new String(*pStr) : NULL); + mnNonValue++; + } + mnValType[nIndex] = bFlag; +} + +void ScMatrix::PutEmpty(SCSIZE nC, SCSIZE nR) +{ + if (ValidColRow( nC, nR)) + PutEmpty( CalcOffset( nC, nR) ); + else + { + DBG_ERRORFILE("ScMatrix::PutEmpty: dimension error"); + } +} + +void ScMatrix::PutEmpty(SCSIZE nIndex) +{ + if (mnValType == NULL) + ResetIsString(); + if ( IsNonValueType( mnValType[nIndex]) && pMat[nIndex].pS ) + { + delete pMat[nIndex].pS; + } + else + { + mnNonValue++; + } + mnValType[nIndex] = SC_MATVAL_EMPTY; + pMat[nIndex].pS = NULL; + pMat[nIndex].fVal = 0.0; +} + +void ScMatrix::PutEmptyPath(SCSIZE nC, SCSIZE nR) +{ + if (ValidColRow( nC, nR)) + PutEmptyPath( CalcOffset( nC, nR) ); + else + { + DBG_ERRORFILE("ScMatrix::PutEmptyPath: dimension error"); + } +} + +void ScMatrix::PutEmptyPath(SCSIZE nIndex) +{ + if (mnValType == NULL) + ResetIsString(); + if ( IsNonValueType( mnValType[nIndex]) && pMat[nIndex].pS ) + { + delete pMat[nIndex].pS; + } + else + { + mnNonValue++; + } + mnValType[nIndex] = SC_MATVAL_EMPTYPATH; + pMat[nIndex].pS = NULL; + pMat[nIndex].fVal = 0.0; +} + +void ScMatrix::PutBoolean(bool bVal, SCSIZE nC, SCSIZE nR) +{ + if (ValidColRow( nC, nR)) + PutBoolean( bVal, CalcOffset( nC, nR) ); + else + { + DBG_ERRORFILE("ScMatrix::PutBoolean: dimension error"); + } +} + +void ScMatrix::PutBoolean( bool bVal, SCSIZE nIndex) +{ + if (mnValType == NULL) + ResetIsString(); + if ( IsNonValueType( mnValType[nIndex]) && pMat[nIndex].pS ) + { + delete pMat[nIndex].pS; + mnNonValue--; + } + + mnValType[nIndex] = SC_MATVAL_BOOLEAN; + pMat[nIndex].pS = NULL; + pMat[nIndex].fVal = bVal ? 1. : 0.; +} + +USHORT ScMatrix::GetError( SCSIZE nC, SCSIZE nR) const +{ + if (ValidColRowOrReplicated( nC, nR )) + return GetError( CalcOffset( nC, nR) ); + else + { + DBG_ERRORFILE("ScMatrix::GetError: dimension error"); + return errNoValue; + } +} + +double ScMatrix::GetDouble(SCSIZE nC, SCSIZE nR) const +{ + if (ValidColRowOrReplicated( nC, nR )) + return GetDouble( CalcOffset( nC, nR) ); + else + { + DBG_ERRORFILE("ScMatrix::GetDouble: dimension error"); + return CreateDoubleError( errNoValue); + } +} + +const String& ScMatrix::GetString(SCSIZE nC, SCSIZE nR) const +{ + if (ValidColRowOrReplicated( nC, nR )) + { + SCSIZE nIndex = CalcOffset( nC, nR); + if ( IsString( nIndex ) ) + return GetString( nIndex ); + else + { + SetErrorAtInterpreter( GetError( nIndex)); + DBG_ERRORFILE("ScMatrix::GetString: access error, no string"); + } + } + else + { + DBG_ERRORFILE("ScMatrix::GetString: dimension error"); + } + return ScGlobal::GetEmptyString(); +} + + +String ScMatrix::GetString( SvNumberFormatter& rFormatter, SCSIZE nIndex) const +{ + if (IsString( nIndex)) + { + if (IsEmptyPath( nIndex)) + { // result of empty FALSE jump path + ULONG nKey = rFormatter.GetStandardFormat( NUMBERFORMAT_LOGICAL, + ScGlobal::eLnge); + String aStr; + Color* pColor = NULL; + rFormatter.GetOutputString( 0.0, nKey, aStr, &pColor); + return aStr; + } + return GetString( nIndex ); + } + + USHORT nError = GetError( nIndex); + if (nError) + { + SetErrorAtInterpreter( nError); + return ScGlobal::GetErrorString( nError); + } + + double fVal= GetDouble( nIndex); + ULONG nKey = rFormatter.GetStandardFormat( NUMBERFORMAT_NUMBER, + ScGlobal::eLnge); + String aStr; + rFormatter.GetInputLineString( fVal, nKey, aStr); + return aStr; +} + + +String ScMatrix::GetString( SvNumberFormatter& rFormatter, SCSIZE nC, SCSIZE nR) const +{ + if (ValidColRowOrReplicated( nC, nR )) + { + SCSIZE nIndex = CalcOffset( nC, nR); + return GetString( rFormatter, nIndex); + } + else + { + DBG_ERRORFILE("ScMatrix::GetString: dimension error"); + } + return String(); +} + + +const ScMatrixValue* ScMatrix::Get(SCSIZE nC, SCSIZE nR, ScMatValType& nType) const +{ + if (ValidColRowOrReplicated( nC, nR )) + { + SCSIZE nIndex = CalcOffset( nC, nR); + if (mnValType) + nType = mnValType[nIndex]; + else + nType = SC_MATVAL_VALUE; + return &pMat[nIndex]; + } + else + { + DBG_ERRORFILE("ScMatrix::Get: dimension error"); + } + nType = SC_MATVAL_EMPTY; + return NULL; +} + +void ScMatrix::MatCopy(ScMatrix& mRes) const +{ + if (nColCount > mRes.nColCount || nRowCount > mRes.nRowCount) + { + DBG_ERRORFILE("ScMatrix::MatCopy: dimension error"); + } + else if ( nColCount == mRes.nColCount && nRowCount == mRes.nRowCount ) + { + if (mnValType) + { + ScMatValType nType; + mRes.ResetIsString(); + for (SCSIZE i = 0; i < nColCount; i++) + { + SCSIZE nStart = i * nRowCount; + for (SCSIZE j = 0; j < nRowCount; j++) + { + if (IsNonValueType( (nType = mnValType[nStart+j]))) + mRes.PutStringEntry( pMat[nStart+j].pS, nType, nStart+j ); + else + { + mRes.pMat[nStart+j].fVal = pMat[nStart+j].fVal; + mRes.mnValType[nStart+j] = nType; + } + } + } + } + else + { + mRes.DeleteIsString(); + SCSIZE nCount = nColCount * nRowCount; + for (SCSIZE i = 0; i < nCount; i++) + mRes.pMat[i].fVal = pMat[i].fVal; + } + } + else + { + // Copy this matrix to upper left rectangle of result matrix. + if (mnValType) + { + ScMatValType nType; + mRes.ResetIsString(); + for (SCSIZE i = 0; i < nColCount; i++) + { + SCSIZE nStart = i * nRowCount; + SCSIZE nResStart = i * mRes.nRowCount; + for (SCSIZE j = 0; j < nRowCount; j++) + { + if (IsNonValueType( (nType = mnValType[nStart+j]))) + mRes.PutStringEntry( pMat[nStart+j].pS, nType, nResStart+j ); + else + { + mRes.pMat[nResStart+j].fVal = pMat[nStart+j].fVal; + mRes.mnValType[nResStart+j] = nType; + } + } + } + } + else + { + mRes.DeleteIsString(); + for (SCSIZE i = 0; i < nColCount; i++) + { + SCSIZE nStart = i * nRowCount; + SCSIZE nResStart = i * mRes.nRowCount; + for (SCSIZE j = 0; j < nRowCount; j++) + mRes.pMat[nResStart+j].fVal = pMat[nStart+j].fVal; + } + } + } +} + +void ScMatrix::MatTrans(ScMatrix& mRes) const +{ + if (nColCount != mRes.nRowCount || nRowCount != mRes.nColCount) + { + DBG_ERRORFILE("ScMatrix::MatTrans: dimension error"); + } + else + { + if (mnValType) + { + ScMatValType nType; + mRes.ResetIsString(); + for ( SCSIZE i = 0; i < nColCount; i++ ) + { + SCSIZE nStart = i * nRowCount; + for ( SCSIZE j = 0; j < nRowCount; j++ ) + { + if (IsNonValueType( (nType = mnValType[nStart+j]))) + mRes.PutStringEntry( pMat[nStart+j].pS, nType, j*mRes.nRowCount+i ); + else + { + mRes.pMat[j*mRes.nRowCount+i].fVal = pMat[nStart+j].fVal; + mRes.mnValType[j*mRes.nRowCount+i] = nType; + } + } + } + } + else + { + mRes.DeleteIsString(); + for ( SCSIZE i = 0; i < nColCount; i++ ) + { + SCSIZE nStart = i * nRowCount; + for ( SCSIZE j = 0; j < nRowCount; j++ ) + { + mRes.pMat[j*mRes.nRowCount+i].fVal = pMat[nStart+j].fVal; + } + } + } + } +} + +//UNUSED2009-05 void ScMatrix::MatCopyUpperLeft(ScMatrix& mRes) const +//UNUSED2009-05 { +//UNUSED2009-05 if (nColCount < mRes.nColCount || nRowCount < mRes.nRowCount) +//UNUSED2009-05 { +//UNUSED2009-05 DBG_ERRORFILE("ScMatrix::MatCopyUpperLeft: dimension error"); +//UNUSED2009-05 } +//UNUSED2009-05 else +//UNUSED2009-05 { +//UNUSED2009-05 if (mnValType) +//UNUSED2009-05 { +//UNUSED2009-05 ScMatValType nType; +//UNUSED2009-05 mRes.ResetIsString(); +//UNUSED2009-05 for ( SCSIZE i = 0; i < mRes.nColCount; i++ ) +//UNUSED2009-05 { +//UNUSED2009-05 SCSIZE nStart = i * nRowCount; +//UNUSED2009-05 for ( SCSIZE j = 0; j < mRes.nRowCount; j++ ) +//UNUSED2009-05 { +//UNUSED2009-05 if ( IsNonValueType( (nType = mnValType[nStart+j]) )) +//UNUSED2009-05 mRes.PutStringEntry( pMat[nStart+j].pS, nType, +//UNUSED2009-05 i*mRes.nRowCount+j ); +//UNUSED2009-05 else +//UNUSED2009-05 { +//UNUSED2009-05 mRes.pMat[i*mRes.nRowCount+j].fVal = pMat[nStart+j].fVal; +//UNUSED2009-05 mRes.mnValType[i*mRes.nRowCount+j] = nType; +//UNUSED2009-05 } +//UNUSED2009-05 } +//UNUSED2009-05 } +//UNUSED2009-05 } +//UNUSED2009-05 else +//UNUSED2009-05 { +//UNUSED2009-05 mRes.DeleteIsString(); +//UNUSED2009-05 for ( SCSIZE i = 0; i < mRes.nColCount; i++ ) +//UNUSED2009-05 { +//UNUSED2009-05 SCSIZE nStart = i * nRowCount; +//UNUSED2009-05 for ( SCSIZE j = 0; j < mRes.nRowCount; j++ ) +//UNUSED2009-05 { +//UNUSED2009-05 mRes.pMat[i*mRes.nRowCount+j].fVal = pMat[nStart+j].fVal; +//UNUSED2009-05 } +//UNUSED2009-05 } +//UNUSED2009-05 } +//UNUSED2009-05 } +//UNUSED2009-05 } + +void ScMatrix::FillDouble( double fVal, SCSIZE nC1, SCSIZE nR1, SCSIZE nC2, SCSIZE nR2 ) +{ + if (ValidColRow( nC1, nR1) && ValidColRow( nC2, nR2)) + { + if ( nC1 == 0 && nR1 == 0 && nC2 == nColCount-1 && nR2 == nRowCount-1 ) + { + SCSIZE nEnd = nColCount * nRowCount; + for ( SCSIZE j=0; j<nEnd; j++ ) + pMat[j].fVal = fVal; + } + else + { + for ( SCSIZE i=nC1; i<=nC2; i++ ) + { + SCSIZE nOff1 = i * nRowCount + nR1; + SCSIZE nOff2 = nOff1 + nR2 - nR1; + for ( SCSIZE j=nOff1; j<=nOff2; j++ ) + pMat[j].fVal = fVal; + } + } + } + else + { + DBG_ERRORFILE("ScMatrix::FillDouble: dimension error"); + } +} + +void ScMatrix::CompareEqual() +{ + SCSIZE n = nColCount * nRowCount; + if ( mnValType ) + { + for ( SCSIZE j=0; j<n; j++ ) + if ( IsValueType( mnValType[j]) ) // else: #WERT! + if ( ::rtl::math::isFinite( pMat[j].fVal)) // else: DoubleError + pMat[j].fVal = (pMat[j].fVal == 0.0); + } + else + { + for ( SCSIZE j=0; j<n; j++ ) + if ( ::rtl::math::isFinite( pMat[j].fVal)) // else: DoubleError + pMat[j].fVal = (pMat[j].fVal == 0.0); + } +} + +void ScMatrix::CompareNotEqual() +{ + SCSIZE n = nColCount * nRowCount; + if ( mnValType ) + { + for ( SCSIZE j=0; j<n; j++ ) + if ( IsValueType( mnValType[j]) ) // else: #WERT! + if ( ::rtl::math::isFinite( pMat[j].fVal)) // else: DoubleError + pMat[j].fVal = (pMat[j].fVal != 0.0); + } + else + { + for ( SCSIZE j=0; j<n; j++ ) + if ( ::rtl::math::isFinite( pMat[j].fVal)) // else: DoubleError + pMat[j].fVal = (pMat[j].fVal != 0.0); + } +} + +void ScMatrix::CompareLess() +{ + SCSIZE n = nColCount * nRowCount; + if ( mnValType ) + { + for ( SCSIZE j=0; j<n; j++ ) + if ( IsValueType( mnValType[j]) ) // else: #WERT! + if ( ::rtl::math::isFinite( pMat[j].fVal)) // else: DoubleError + pMat[j].fVal = (pMat[j].fVal < 0.0); + } + else + { + for ( SCSIZE j=0; j<n; j++ ) + if ( ::rtl::math::isFinite( pMat[j].fVal)) // else: DoubleError + pMat[j].fVal = (pMat[j].fVal < 0.0); + } +} + +void ScMatrix::CompareGreater() +{ + SCSIZE n = nColCount * nRowCount; + if ( mnValType ) + { + for ( SCSIZE j=0; j<n; j++ ) + if ( IsValueType( mnValType[j]) ) // else: #WERT! + if ( ::rtl::math::isFinite( pMat[j].fVal)) // else: DoubleError + pMat[j].fVal = (pMat[j].fVal > 0.0); + } + else + { + for ( SCSIZE j=0; j<n; j++ ) + if ( ::rtl::math::isFinite( pMat[j].fVal)) // else: DoubleError + pMat[j].fVal = (pMat[j].fVal > 0.0); + } +} + +void ScMatrix::CompareLessEqual() +{ + SCSIZE n = nColCount * nRowCount; + if ( mnValType ) + { + for ( SCSIZE j=0; j<n; j++ ) + if ( IsValueType( mnValType[j]) ) // else: #WERT! + if ( ::rtl::math::isFinite( pMat[j].fVal)) // else: DoubleError + pMat[j].fVal = (pMat[j].fVal <= 0.0); + } + else + { + for ( SCSIZE j=0; j<n; j++ ) + if ( ::rtl::math::isFinite( pMat[j].fVal)) // else: DoubleError + pMat[j].fVal = (pMat[j].fVal <= 0.0); + } +} + +void ScMatrix::CompareGreaterEqual() +{ + SCSIZE n = nColCount * nRowCount; + if ( mnValType ) + { + for ( SCSIZE j=0; j<n; j++ ) + if ( IsValueType( mnValType[j]) ) // else: #WERT! + if ( ::rtl::math::isFinite( pMat[j].fVal)) // else: DoubleError + pMat[j].fVal = (pMat[j].fVal >= 0.0); + } + else + { + for ( SCSIZE j=0; j<n; j++ ) + if ( ::rtl::math::isFinite( pMat[j].fVal)) // else: DoubleError + pMat[j].fVal = (pMat[j].fVal >= 0.0); + } +} + +double ScMatrix::And() +{ + SCSIZE n = nColCount * nRowCount; + bool bAnd = true; + if ( mnValType ) + { + for ( SCSIZE j=0; bAnd && j<n; j++ ) + { + if ( !IsValueType( mnValType[j]) ) + { // assuming a CompareMat this is an error + return CreateDoubleError( errIllegalArgument ); + } + else if ( ::rtl::math::isFinite( pMat[j].fVal)) + bAnd = (pMat[j].fVal != 0.0); + else + return pMat[j].fVal; // DoubleError + } + } + else + { + for ( SCSIZE j=0; bAnd && j<n; j++ ) + { + if ( ::rtl::math::isFinite( pMat[j].fVal)) + bAnd = (pMat[j].fVal != 0.0); + else + return pMat[j].fVal; // DoubleError + } + } + return bAnd; +} + +double ScMatrix::Or() +{ + SCSIZE n = nColCount * nRowCount; + bool bOr = false; + if ( mnValType ) + { + for ( SCSIZE j=0; !bOr && j<n; j++ ) + if ( !IsValueType( mnValType[j]) ) + { // assuming a CompareMat this is an error + return CreateDoubleError( errIllegalArgument ); + } + else if ( ::rtl::math::isFinite( pMat[j].fVal)) + bOr = (pMat[j].fVal != 0.0); + else + return pMat[j].fVal; // DoubleError + } + else + { + for ( SCSIZE j=0; !bOr && j<n; j++ ) + if ( ::rtl::math::isFinite( pMat[j].fVal)) + bOr = (pMat[j].fVal != 0.0); + else + return pMat[j].fVal; // DoubleError + } + return bOr; +} + diff --git a/sc/source/core/tool/stringutil.cxx b/sc/source/core/tool/stringutil.cxx new file mode 100644 index 000000000000..28a4bc6755c2 --- /dev/null +++ b/sc/source/core/tool/stringutil.cxx @@ -0,0 +1,131 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: table.hxx,v $ + * $Revision: 1.35 $ + * + * 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" + +// System - Includes ----------------------------------------------------- + +#include "stringutil.hxx" +#include "rtl/ustrbuf.hxx" +#include "rtl/math.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; + +bool ScStringUtil::parseSimpleNumber( + const OUString& rStr, sal_Unicode dsep, sal_Unicode gsep, double& rVal) +{ + if (gsep == 0x00A0) + // unicode space to ascii space + gsep = 0x0020; + + OUStringBuffer aBuf; + sal_Int32 n = rStr.getLength(); + const sal_Unicode* p = rStr.getStr(); + sal_Int32 nPosDSep = -1, nPosGSep = -1; + sal_uInt32 nDigitCount = 0; + + for (sal_Int32 i = 0; i < n; ++i) + { + sal_Unicode c = p[i]; + if (c == 0x00A0) + // unicode space to ascii space + c = 0x0020; + + if (sal_Unicode('0') <= c && c <= sal_Unicode('9')) + { + // this is a digit. + aBuf.append(c); + ++nDigitCount; + } + else if (c == dsep) + { + // this is a decimal separator. + + if (nPosDSep >= 0) + // a second decimal separator -> not a valid number. + return false; + + if (nPosGSep >= 0 && i - nPosGSep != 4) + // the number has a group separator and the decimal sep is not + // positioned correctly. + return false; + + nPosDSep = i; + nPosGSep = -1; + aBuf.append(c); + nDigitCount = 0; + } + else if (c == gsep) + { + // this is a group (thousand) separator. + + if (i == 0) + // not allowed as the first character. + return false; + + if (nPosDSep >= 0) + // not allowed after the decimal separator. + return false; + + if (nPosGSep >= 0 && nDigitCount != 3) + // must be exactly 3 digits since the last group separator. + return false; + + nPosGSep = i; + nDigitCount = 0; + } + else if (c == sal_Unicode('-') || c == sal_Unicode('+')) + { + // A sign must be the first character if it's given. + if (i == 0) + aBuf.append(c); + else + return false; + } + else + return false; + } + + // finished parsing the number. + + if (nPosGSep >= 0 && nDigitCount != 3) + // must be exactly 3 digits since the last group separator. + return false; + + rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok; + sal_Int32 nParseEnd = 0; + rVal = ::rtl::math::stringToDouble(aBuf.makeStringAndClear(), dsep, gsep, &eStatus, &nParseEnd); + if (eStatus != rtl_math_ConversionStatus_Ok) + return false; + + return true; +} diff --git a/sc/source/core/tool/subtotal.cxx b/sc/source/core/tool/subtotal.cxx new file mode 100644 index 000000000000..077b5a9d9751 --- /dev/null +++ b/sc/source/core/tool/subtotal.cxx @@ -0,0 +1,81 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + + + +#include "subtotal.hxx" +#include "interpre.hxx" + +// ----------------------------------------------------------------------- + +BOOL SubTotal::SafePlus(double& fVal1, double fVal2) +{ + BOOL bOk = TRUE; + SAL_MATH_FPEXCEPTIONS_OFF(); + fVal1 += fVal2; + if (!::rtl::math::isFinite(fVal1)) + { + bOk = FALSE; + if (fVal2 > 0.0) + fVal1 = DBL_MAX; + else + fVal1 = -DBL_MAX; + } + return bOk; +} + + +BOOL SubTotal::SafeMult(double& fVal1, double fVal2) +{ + BOOL bOk = TRUE; + SAL_MATH_FPEXCEPTIONS_OFF(); + fVal1 *= fVal2; + if (!::rtl::math::isFinite(fVal1)) + { + bOk = FALSE; + fVal1 = DBL_MAX; + } + return bOk; +} + + +BOOL SubTotal::SafeDiv(double& fVal1, double fVal2) +{ + BOOL bOk = TRUE; + SAL_MATH_FPEXCEPTIONS_OFF(); + fVal1 /= fVal2; + if (!::rtl::math::isFinite(fVal1)) + { + bOk = FALSE; + fVal1 = DBL_MAX; + } + return bOk; +} diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx new file mode 100644 index 000000000000..81862c49fd21 --- /dev/null +++ b/sc/source/core/tool/token.cxx @@ -0,0 +1,1836 @@ +/************************************************************************* + * + * 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 --------------------------------------------------------------- + +#if STLPORT_VERSION<321 +#include <stddef.h> +#else +#include <cstddef> +#endif +#include <cstdio> + +#include <string.h> +#include <tools/mempool.hxx> +#include <tools/debug.hxx> + +#include "token.hxx" +#include "tokenarray.hxx" +#include "compiler.hxx" +#include <formula/compiler.hrc> +#include "rechead.hxx" +#include "parclass.hxx" +#include "jumpmatrix.hxx" +#include "rangeseq.hxx" +#include "externalrefmgr.hxx" +#include "document.hxx" + +using ::std::vector; + +#include <com/sun/star/sheet/ComplexReference.hpp> +#include <com/sun/star/sheet/ExternalReference.hpp> +#include <com/sun/star/sheet/ReferenceFlags.hpp> + +using namespace formula; +using namespace com::sun::star; + +namespace +{ + void lcl_SingleRefToCalc( ScSingleRefData& rRef, const sheet::SingleReference& rAPI ) + { + rRef.InitFlags(); + + rRef.nCol = static_cast<SCsCOL>(rAPI.Column); + rRef.nRow = static_cast<SCsROW>(rAPI.Row); + rRef.nTab = static_cast<SCsTAB>(rAPI.Sheet); + rRef.nRelCol = static_cast<SCsCOL>(rAPI.RelativeColumn); + rRef.nRelRow = static_cast<SCsROW>(rAPI.RelativeRow); + rRef.nRelTab = static_cast<SCsTAB>(rAPI.RelativeSheet); + + rRef.SetColRel( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_RELATIVE ) != 0 ); + rRef.SetRowRel( ( rAPI.Flags & sheet::ReferenceFlags::ROW_RELATIVE ) != 0 ); + rRef.SetTabRel( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_RELATIVE ) != 0 ); + rRef.SetColDeleted( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_DELETED ) != 0 ); + rRef.SetRowDeleted( ( rAPI.Flags & sheet::ReferenceFlags::ROW_DELETED ) != 0 ); + rRef.SetTabDeleted( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_DELETED ) != 0 ); + rRef.SetFlag3D( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_3D ) != 0 ); + rRef.SetRelName( ( rAPI.Flags & sheet::ReferenceFlags::RELATIVE_NAME ) != 0 ); + } + + void lcl_ExternalRefToCalc( ScSingleRefData& rRef, const sheet::SingleReference& rAPI ) + { + rRef.InitFlags(); + + rRef.nCol = static_cast<SCsCOL>(rAPI.Column); + rRef.nRow = static_cast<SCsROW>(rAPI.Row); + rRef.nTab = 0; + rRef.nRelCol = static_cast<SCsCOL>(rAPI.RelativeColumn); + rRef.nRelRow = static_cast<SCsROW>(rAPI.RelativeRow); + rRef.nRelTab = 0; + + rRef.SetColRel( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_RELATIVE ) != 0 ); + rRef.SetRowRel( ( rAPI.Flags & sheet::ReferenceFlags::ROW_RELATIVE ) != 0 ); + rRef.SetTabRel( false ); // sheet index must be absolute for external refs + rRef.SetColDeleted( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_DELETED ) != 0 ); + rRef.SetRowDeleted( ( rAPI.Flags & sheet::ReferenceFlags::ROW_DELETED ) != 0 ); + rRef.SetTabDeleted( false ); // sheet must not be deleted for external refs + rRef.SetFlag3D( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_3D ) != 0 ); + rRef.SetRelName( false ); + } +// +} // namespace +// +// ImpTokenIterator wird je Interpreter angelegt, mehrfache auch durch +// SubCode via FormulaTokenIterator Push/Pop moeglich +IMPL_FIXEDMEMPOOL_NEWDEL( ImpTokenIterator, 32, 16 ) + +// Align MemPools on 4k boundaries - 64 bytes (4k is a MUST for OS/2) + +// Since RawTokens are temporary for the compiler, don't align on 4k and waste memory. +// ScRawToken size is FixMembers + MAXSTRLEN + ~4 ~= 1036 +IMPL_FIXEDMEMPOOL_NEWDEL( ScRawToken, 8, 4 ) +// Some ScDoubleRawToken, FixMembers + sizeof(double) ~= 16 +const USHORT nMemPoolDoubleRawToken = 0x0400 / sizeof(ScDoubleRawToken); +IMPL_FIXEDMEMPOOL_NEWDEL( ScDoubleRawToken, nMemPoolDoubleRawToken, nMemPoolDoubleRawToken ) + +// Need a whole bunch of ScSingleRefToken +const USHORT nMemPoolSingleRefToken = (0x4000 - 64) / sizeof(ScSingleRefToken); +IMPL_FIXEDMEMPOOL_NEWDEL( ScSingleRefToken, nMemPoolSingleRefToken, nMemPoolSingleRefToken ) +// Need quite a lot of ScDoubleRefToken +const USHORT nMemPoolDoubleRefToken = (0x2000 - 64) / sizeof(ScDoubleRefToken); +IMPL_FIXEDMEMPOOL_NEWDEL( ScDoubleRefToken, nMemPoolDoubleRefToken, nMemPoolDoubleRefToken ) + +// --- helpers -------------------------------------------------------------- + +inline BOOL lcl_IsReference( OpCode eOp, StackVar eType ) +{ + return + (eOp == ocPush && (eType == svSingleRef || eType == svDoubleRef)) + || (eOp == ocColRowNameAuto && eType == svDoubleRef) + || (eOp == ocColRowName && eType == svSingleRef) + || (eOp == ocMatRef && eType == svSingleRef) + ; +} + + +// --- class ScRawToken ----------------------------------------------------- + +xub_StrLen ScRawToken::GetStrLen( const sal_Unicode* pStr ) +{ + if ( !pStr ) + return 0; + register const sal_Unicode* p = pStr; + while ( *p ) + p++; + return sal::static_int_cast<xub_StrLen>( p - pStr ); +} + + +void ScRawToken::SetOpCode( OpCode e ) +{ + eOp = e; + switch (eOp) + { + case ocIf: + eType = svJump; + nJump[ 0 ] = 3; // If, Else, Behind + break; + case ocChose: + eType = svJump; + nJump[ 0 ] = MAXJUMPCOUNT+1; + break; + case ocMissing: + eType = svMissing; + break; + case ocSep: + case ocOpen: + case ocClose: + case ocArrayRowSep: + case ocArrayColSep: + case ocArrayOpen: + case ocArrayClose: + eType = svSep; + break; + default: + eType = svByte; + sbyte.cByte = 0; + sbyte.bHasForceArray = ScParameterClassification::HasForceArray( eOp); + } + nRefCnt = 0; +} + +void ScRawToken::SetString( const sal_Unicode* pStr ) +{ + eOp = ocPush; + eType = svString; + if ( pStr ) + { + xub_StrLen nLen = GetStrLen( pStr ) + 1; + if( nLen > MAXSTRLEN ) + nLen = MAXSTRLEN; + memcpy( cStr, pStr, GetStrLenBytes( nLen ) ); + cStr[ nLen-1 ] = 0; + } + else + cStr[0] = 0; + nRefCnt = 0; +} + +void ScRawToken::SetSingleReference( const ScSingleRefData& rRef ) +{ + eOp = ocPush; + eType = svSingleRef; + aRef.Ref1 = + aRef.Ref2 = rRef; + nRefCnt = 0; +} + +void ScRawToken::SetDoubleReference( const ScComplexRefData& rRef ) +{ + eOp = ocPush; + eType = svDoubleRef; + aRef = rRef; + nRefCnt = 0; +} + +void ScRawToken::SetDouble(double rVal) +{ + eOp = ocPush; + eType = svDouble; + nValue = rVal; + nRefCnt = 0; +} + +void ScRawToken::SetName( USHORT n ) +{ + eOp = ocName; + eType = svIndex; + nIndex = n; + nRefCnt = 0; +} + +void ScRawToken::SetExternalSingleRef( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef ) +{ + eOp = ocExternalRef; + eType = svExternalSingleRef; + nRefCnt = 0; + + extref.nFileId = nFileId; + extref.aRef.Ref1 = + extref.aRef.Ref2 = rRef; + + xub_StrLen n = rTabName.Len(); + memcpy(extref.cTabName, rTabName.GetBuffer(), n*sizeof(sal_Unicode)); + extref.cTabName[n] = 0; +} + +void ScRawToken::SetExternalDoubleRef( sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef ) +{ + eOp = ocExternalRef; + eType = svExternalDoubleRef; + nRefCnt = 0; + + extref.nFileId = nFileId; + extref.aRef = rRef; + + xub_StrLen n = rTabName.Len(); + memcpy(extref.cTabName, rTabName.GetBuffer(), n*sizeof(sal_Unicode)); + extref.cTabName[n] = 0; +} + +void ScRawToken::SetExternalName( sal_uInt16 nFileId, const String& rName ) +{ + eOp = ocExternalRef; + eType = svExternalName; + nRefCnt = 0; + + extname.nFileId = nFileId; + + xub_StrLen n = rName.Len(); + memcpy(extname.cName, rName.GetBuffer(), n*sizeof(sal_Unicode)); + extname.cName[n] = 0; +} + +//UNUSED2008-05 void ScRawToken::SetInt(int rVal) +//UNUSED2008-05 { +//UNUSED2008-05 eOp = ocPush; +//UNUSED2008-05 eType = svDouble; +//UNUSED2008-05 nValue = (double)rVal; +//UNUSED2008-05 nRefCnt = 0; +//UNUSED2008-05 +//UNUSED2008-05 } +//UNUSED2008-05 void ScRawToken::SetMatrix( ScMatrix* p ) +//UNUSED2008-05 { +//UNUSED2008-05 eOp = ocPush; +//UNUSED2008-05 eType = svMatrix; +//UNUSED2008-05 pMat = p; +//UNUSED2008-05 nRefCnt = 0; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 ScComplexRefData& ScRawToken::GetReference() +//UNUSED2008-05 { +//UNUSED2008-05 DBG_ASSERT( lcl_IsReference( eOp, GetType() ), "GetReference: no Ref" ); +//UNUSED2008-05 return aRef; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 void ScRawToken::SetReference( ScComplexRefData& rRef ) +//UNUSED2008-05 { +//UNUSED2008-05 DBG_ASSERT( lcl_IsReference( eOp, GetType() ), "SetReference: no Ref" ); +//UNUSED2008-05 aRef = rRef; +//UNUSED2008-05 if( GetType() == svSingleRef ) +//UNUSED2008-05 aRef.Ref2 = aRef.Ref1; +//UNUSED2008-05 } + +void ScRawToken::SetExternal( const sal_Unicode* pStr ) +{ + eOp = ocExternal; + eType = svExternal; + xub_StrLen nLen = GetStrLen( pStr ) + 1; + if( nLen >= MAXSTRLEN ) + nLen = MAXSTRLEN-1; + // Platz fuer Byte-Parameter lassen! + memcpy( cStr+1, pStr, GetStrLenBytes( nLen ) ); + cStr[ nLen+1 ] = 0; + nRefCnt = 0; +} + +USHORT lcl_ScRawTokenOffset() +{ + // offset of sbyte in ScRawToken + // offsetof(ScRawToken, sbyte) gives a warning with gcc, because ScRawToken is no POD + + ScRawToken aToken; + return static_cast<USHORT>( reinterpret_cast<char*>(&aToken.sbyte) - reinterpret_cast<char*>(&aToken) ); +} + +ScRawToken* ScRawToken::Clone() const +{ + ScRawToken* p; + if ( eType == svDouble ) + { + p = (ScRawToken*) new ScDoubleRawToken; + p->eOp = eOp; + p->eType = eType; + p->nValue = nValue; + } + else + { + static USHORT nOffset = lcl_ScRawTokenOffset(); // offset of sbyte + USHORT n = nOffset; + + if (eOp == ocExternalRef) + { + switch (eType) + { + case svExternalSingleRef: + case svExternalDoubleRef: n += sizeof(extref); break; + case svExternalName: n += sizeof(extname); break; + default: + { + DBG_ERROR1( "unknown ScRawToken::Clone() external type %d", int(eType)); + } + } + } + else + { + switch( eType ) + { + case svSep: break; + case svByte: n += sizeof(ScRawToken::sbyte); break; + case svDouble: n += sizeof(double); break; + case svString: n = sal::static_int_cast<USHORT>( n + GetStrLenBytes( cStr ) + GetStrLenBytes( 1 ) ); break; + case svSingleRef: + case svDoubleRef: n += sizeof(aRef); break; + case svMatrix: n += sizeof(ScMatrix*); break; + case svIndex: n += sizeof(USHORT); break; + case svJump: n += nJump[ 0 ] * 2 + 2; break; + case svExternal: n = sal::static_int_cast<USHORT>( n + GetStrLenBytes( cStr+1 ) + GetStrLenBytes( 2 ) ); break; + default: + { + DBG_ERROR1( "unknown ScRawToken::Clone() type %d", int(eType)); + } + } + } + p = (ScRawToken*) new BYTE[ n ]; + memcpy( p, this, n * sizeof(BYTE) ); + } + p->nRefCnt = 0; + p->bRaw = FALSE; + return p; +} + + +FormulaToken* ScRawToken::CreateToken() const +{ +#ifdef DBG_UTIL +#define IF_NOT_OPCODE_ERROR(o,c) if (eOp!=o) DBG_ERROR1( #c "::ctor: OpCode %d lost, converted to " #o "; maybe inherit from FormulaToken instead!", int(eOp)) +#else +#define IF_NOT_OPCODE_ERROR(o,c) +#endif + switch ( GetType() ) + { + case svByte : + return new FormulaByteToken( eOp, sbyte.cByte, sbyte.bHasForceArray ); + case svDouble : + IF_NOT_OPCODE_ERROR( ocPush, FormulaDoubleToken); + return new FormulaDoubleToken( nValue ); + case svString : + if (eOp == ocPush) + return new FormulaStringToken( String( cStr ) ); + else + return new FormulaStringOpToken( eOp, String( cStr ) ); + case svSingleRef : + if (eOp == ocPush) + return new ScSingleRefToken( aRef.Ref1 ); + else + return new ScSingleRefToken( aRef.Ref1, eOp ); + case svDoubleRef : + if (eOp == ocPush) + return new ScDoubleRefToken( aRef ); + else + return new ScDoubleRefToken( aRef, eOp ); + case svMatrix : + IF_NOT_OPCODE_ERROR( ocPush, ScMatrixToken); + return new ScMatrixToken( pMat ); + case svIndex : + return new FormulaIndexToken( eOp, nIndex ); + case svExternalSingleRef: + { + String aTabName(extref.cTabName); + return new ScExternalSingleRefToken(extref.nFileId, aTabName, extref.aRef.Ref1); + } + case svExternalDoubleRef: + { + String aTabName(extref.cTabName); + return new ScExternalDoubleRefToken(extref.nFileId, aTabName, extref.aRef); + } + case svExternalName: + { + String aName(extname.cName); + return new ScExternalNameToken( extname.nFileId, aName ); + } + case svJump : + return new FormulaJumpToken( eOp, (short*) nJump ); + case svExternal : + return new FormulaExternalToken( eOp, sbyte.cByte, String( cStr+1 ) ); + case svFAP : + return new FormulaFAPToken( eOp, sbyte.cByte, NULL ); + case svMissing : + IF_NOT_OPCODE_ERROR( ocMissing, FormulaMissingToken); + return new FormulaMissingToken; + case svSep : + return new FormulaToken( svSep,eOp ); + case svUnknown : + return new FormulaUnknownToken( eOp ); + default: + { + DBG_ERROR1( "unknown ScRawToken::CreateToken() type %d", int(GetType())); + return new FormulaUnknownToken( ocBad ); + } + } +#undef IF_NOT_OPCODE_ERROR +} + + +void ScRawToken::Delete() +{ + if ( bRaw ) + delete this; // FixedMemPool ScRawToken + else + { // created per Clone + switch ( eType ) + { + case svDouble : + delete (ScDoubleRawToken*) this; // FixedMemPool ScDoubleRawToken + break; + default: + delete [] (BYTE*) this; + } + } +} + + +// --- class ScToken -------------------------------------------------------- + +ScSingleRefData lcl_ScToken_InitSingleRef() +{ + ScSingleRefData aRef; + aRef.InitAddress( ScAddress() ); + return aRef; +} + +ScComplexRefData lcl_ScToken_InitDoubleRef() +{ + ScComplexRefData aRef; + aRef.Ref1 = lcl_ScToken_InitSingleRef(); + aRef.Ref2 = aRef.Ref1; + return aRef; +} + +ScToken::~ScToken() +{ +} + +// TextEqual: if same formula entered (for optimization in sort) +BOOL ScToken::TextEqual( const FormulaToken& _rToken ) const +{ + if ( eType == svSingleRef || eType == svDoubleRef ) + { + // in relative Refs only compare relative parts + + if ( eType != _rToken.GetType() || GetOpCode() != _rToken.GetOpCode() ) + return FALSE; + + const ScToken& rToken = static_cast<const ScToken&>(_rToken); + ScComplexRefData aTemp1; + if ( eType == svSingleRef ) + { + aTemp1.Ref1 = GetSingleRef(); + aTemp1.Ref2 = aTemp1.Ref1; + } + else + aTemp1 = GetDoubleRef(); + + ScComplexRefData aTemp2; + if ( rToken.eType == svSingleRef ) + { + aTemp2.Ref1 = rToken.GetSingleRef(); + aTemp2.Ref2 = aTemp2.Ref1; + } + else + aTemp2 = rToken.GetDoubleRef(); + + ScAddress aPos; + aTemp1.SmartRelAbs(aPos); + aTemp2.SmartRelAbs(aPos); + + // memcmp doesn't work because of the alignment byte after bFlags. + // After SmartRelAbs only absolute parts have to be compared. + return aTemp1.Ref1.nCol == aTemp2.Ref1.nCol && + aTemp1.Ref1.nRow == aTemp2.Ref1.nRow && + aTemp1.Ref1.nTab == aTemp2.Ref1.nTab && + aTemp1.Ref1.bFlags == aTemp2.Ref1.bFlags && + aTemp1.Ref2.nCol == aTemp2.Ref2.nCol && + aTemp1.Ref2.nRow == aTemp2.Ref2.nRow && + aTemp1.Ref2.nTab == aTemp2.Ref2.nTab && + aTemp1.Ref2.bFlags == aTemp2.Ref2.bFlags; + } + else + return *this == _rToken; // else normal operator== +} + + +BOOL ScToken::Is3DRef() const +{ + switch ( eType ) + { + case svDoubleRef : + if ( GetSingleRef2().IsFlag3D() ) + return TRUE; + //! fallthru + case svSingleRef : + if ( GetSingleRef().IsFlag3D() ) + return TRUE; + break; + default: + { + // added to avoid warnings + } + } + return FALSE; +} + +// static +FormulaTokenRef ScToken::ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2, + const ScAddress & rPos, bool bReuseDoubleRef ) +{ + + StackVar sv1, sv2; + // Doing a RangeOp with RefList is probably utter nonsense, but Xcl + // supports it, so do we. + if (((sv1 = rTok1.GetType()) != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList && + sv1 != svExternalSingleRef && sv1 != svExternalDoubleRef ) || + ((sv2 = rTok2.GetType()) != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList)) + return NULL; + + ScToken *p1 = static_cast<ScToken*>(&rTok1); + ScToken *p2 = static_cast<ScToken*>(&rTok2); + + ScTokenRef xRes; + bool bExternal = (sv1 == svExternalSingleRef); + if ((sv1 == svSingleRef || bExternal) && sv2 == svSingleRef) + { + // Range references like Sheet1.A1:A2 are generalized and built by + // first creating a DoubleRef from the first SingleRef, effectively + // generating Sheet1.A1:A1, and then extending that with A2 as if + // Sheet1.A1:A1:A2 was encountered, so the mechanisms to adjust the + // references apply as well. + + /* Given the current structure of external references an external + * reference can only be extended if the second reference does not + * point to a different sheet. 'file'#Sheet1.A1:A2 is ok, + * 'file'#Sheet1.A1:Sheet2.A2 is not. Since we can't determine from a + * svSingleRef whether the sheet would be different from the one given + * in the external reference, we have to bail out if there is any sheet + * specified. NOTE: Xcl does handle external 3D references as in + * '[file]Sheet1:Sheet2'!A1:A2 + * + * FIXME: For OOo syntax be smart and remember an external singleref + * encountered and if followed by ocRange and singleref, create an + * external singleref for the second singleref. Both could then be + * merged here. For Xcl syntax already parse an external range + * reference entirely, cumbersome. */ + + const ScSingleRefData& rRef2 = p2->GetSingleRef(); + if (bExternal && rRef2.IsFlag3D()) + return NULL; + + ScComplexRefData aRef; + aRef.Ref1 = aRef.Ref2 = p1->GetSingleRef(); + aRef.Ref2.SetFlag3D( false); + aRef.Extend( rRef2, rPos); + if (bExternal) + xRes = new ScExternalDoubleRefToken( p1->GetIndex(), p1->GetString(), aRef); + else + xRes = new ScDoubleRefToken( aRef); + } + else + { + bExternal |= (sv1 == svExternalDoubleRef); + const ScRefList* pRefList = NULL; + if (sv1 == svDoubleRef) + { + xRes = (bReuseDoubleRef && p1->GetRef() == 1 ? p1 : static_cast<ScToken*>(p1->Clone())); + sv1 = svUnknown; // mark as handled + } + else if (sv2 == svDoubleRef) + { + xRes = (bReuseDoubleRef && p2->GetRef() == 1 ? p2 : static_cast<ScToken*>(p2->Clone())); + sv2 = svUnknown; // mark as handled + } + else if (sv1 == svRefList) + pRefList = p1->GetRefList(); + else if (sv2 == svRefList) + pRefList = p2->GetRefList(); + if (pRefList) + { + if (!pRefList->size()) + return NULL; + if (bExternal) + return NULL; // external reference list not possible + xRes = new ScDoubleRefToken( (*pRefList)[0] ); + } + if (!xRes) + return NULL; // shouldn't happen.. + StackVar sv[2] = { sv1, sv2 }; + ScToken* pt[2] = { p1, p2 }; + ScComplexRefData& rRef = xRes->GetDoubleRef(); + for (size_t i=0; i<2; ++i) + { + switch (sv[i]) + { + case svSingleRef: + rRef.Extend( pt[i]->GetSingleRef(), rPos); + break; + case svDoubleRef: + rRef.Extend( pt[i]->GetDoubleRef(), rPos); + break; + case svRefList: + { + const ScRefList* p = pt[i]->GetRefList(); + if (!p->size()) + return NULL; + ScRefList::const_iterator it( p->begin()); + ScRefList::const_iterator end( p->end()); + for ( ; it != end; ++it) + { + rRef.Extend( *it, rPos); + } + } + break; + case svExternalSingleRef: + if (rRef.Ref1.IsFlag3D() || rRef.Ref2.IsFlag3D()) + return NULL; // no other sheets with external refs + else + rRef.Extend( pt[i]->GetSingleRef(), rPos); + break; + case svExternalDoubleRef: + if (rRef.Ref1.IsFlag3D() || rRef.Ref2.IsFlag3D()) + return NULL; // no other sheets with external refs + else + rRef.Extend( pt[i]->GetDoubleRef(), rPos); + break; + default: + ; // nothing, prevent compiler warning + } + } + } + return FormulaTokenRef(xRes.get()); +} + +const ScSingleRefData& ScToken::GetSingleRef() const +{ + DBG_ERRORFILE( "ScToken::GetSingleRef: virtual dummy called" ); + static ScSingleRefData aDummySingleRef = lcl_ScToken_InitSingleRef(); + return aDummySingleRef; +} + +ScSingleRefData& ScToken::GetSingleRef() +{ + DBG_ERRORFILE( "ScToken::GetSingleRef: virtual dummy called" ); + static ScSingleRefData aDummySingleRef = lcl_ScToken_InitSingleRef(); + return aDummySingleRef; +} + +const ScComplexRefData& ScToken::GetDoubleRef() const +{ + DBG_ERRORFILE( "ScToken::GetDoubleRef: virtual dummy called" ); + static ScComplexRefData aDummyDoubleRef = lcl_ScToken_InitDoubleRef(); + return aDummyDoubleRef; +} + +ScComplexRefData& ScToken::GetDoubleRef() +{ + DBG_ERRORFILE( "ScToken::GetDoubleRef: virtual dummy called" ); + static ScComplexRefData aDummyDoubleRef = lcl_ScToken_InitDoubleRef(); + return aDummyDoubleRef; +} + +const ScSingleRefData& ScToken::GetSingleRef2() const +{ + DBG_ERRORFILE( "ScToken::GetSingleRef2: virtual dummy called" ); + static ScSingleRefData aDummySingleRef = lcl_ScToken_InitSingleRef(); + return aDummySingleRef; +} + +ScSingleRefData& ScToken::GetSingleRef2() +{ + DBG_ERRORFILE( "ScToken::GetSingleRef2: virtual dummy called" ); + static ScSingleRefData aDummySingleRef = lcl_ScToken_InitSingleRef(); + return aDummySingleRef; +} + +void ScToken::CalcAbsIfRel( const ScAddress& /* rPos */ ) +{ + DBG_ERRORFILE( "ScToken::CalcAbsIfRel: virtual dummy called" ); +} + +void ScToken::CalcRelFromAbs( const ScAddress& /* rPos */ ) +{ + DBG_ERRORFILE( "ScToken::CalcRelFromAbs: virtual dummy called" ); +} + +const ScMatrix* ScToken::GetMatrix() const +{ + DBG_ERRORFILE( "ScToken::GetMatrix: virtual dummy called" ); + return NULL; +} + +ScMatrix* ScToken::GetMatrix() +{ + DBG_ERRORFILE( "ScToken::GetMatrix: virtual dummy called" ); + return NULL; +} + + +ScJumpMatrix* ScToken::GetJumpMatrix() const +{ + DBG_ERRORFILE( "ScToken::GetJumpMatrix: virtual dummy called" ); + return NULL; +} +const ScRefList* ScToken::GetRefList() const +{ + DBG_ERRORFILE( "ScToken::GetRefList: virtual dummy called" ); + return NULL; +} + +ScRefList* ScToken::GetRefList() +{ + DBG_ERRORFILE( "ScToken::GetRefList: virtual dummy called" ); + return NULL; +} +// ========================================================================== +// real implementations of virtual functions +// -------------------------------------------------------------------------- + + + + +const ScSingleRefData& ScSingleRefToken::GetSingleRef() const { return aSingleRef; } +ScSingleRefData& ScSingleRefToken::GetSingleRef() { return aSingleRef; } +void ScSingleRefToken::CalcAbsIfRel( const ScAddress& rPos ) + { aSingleRef.CalcAbsIfRel( rPos ); } +void ScSingleRefToken::CalcRelFromAbs( const ScAddress& rPos ) + { aSingleRef.CalcRelFromAbs( rPos ); } +BOOL ScSingleRefToken::operator==( const FormulaToken& r ) const +{ + return FormulaToken::operator==( r ) && aSingleRef == static_cast<const ScToken&>(r).GetSingleRef(); +} + + +const ScSingleRefData& ScDoubleRefToken::GetSingleRef() const { return aDoubleRef.Ref1; } +ScSingleRefData& ScDoubleRefToken::GetSingleRef() { return aDoubleRef.Ref1; } +const ScComplexRefData& ScDoubleRefToken::GetDoubleRef() const { return aDoubleRef; } +ScComplexRefData& ScDoubleRefToken::GetDoubleRef() { return aDoubleRef; } +const ScSingleRefData& ScDoubleRefToken::GetSingleRef2() const { return aDoubleRef.Ref2; } +ScSingleRefData& ScDoubleRefToken::GetSingleRef2() { return aDoubleRef.Ref2; } +void ScDoubleRefToken::CalcAbsIfRel( const ScAddress& rPos ) + { aDoubleRef.CalcAbsIfRel( rPos ); } +void ScDoubleRefToken::CalcRelFromAbs( const ScAddress& rPos ) + { aDoubleRef.CalcRelFromAbs( rPos ); } +BOOL ScDoubleRefToken::operator==( const FormulaToken& r ) const +{ + return FormulaToken::operator==( r ) && aDoubleRef == static_cast<const ScToken&>(r).GetDoubleRef(); +} + + +const ScRefList* ScRefListToken::GetRefList() const { return &aRefList; } + ScRefList* ScRefListToken::GetRefList() { return &aRefList; } +void ScRefListToken::CalcAbsIfRel( const ScAddress& rPos ) +{ + for (ScRefList::iterator it( aRefList.begin()); it != aRefList.end(); ++it) + (*it).CalcAbsIfRel( rPos); +} +void ScRefListToken::CalcRelFromAbs( const ScAddress& rPos ) +{ + for (ScRefList::iterator it( aRefList.begin()); it != aRefList.end(); ++it) + (*it).CalcRelFromAbs( rPos); +} +BOOL ScRefListToken::operator==( const FormulaToken& r ) const +{ + return FormulaToken::operator==( r ) && &aRefList == static_cast<const ScToken&>(r).GetRefList(); +} + + +const ScMatrix* ScMatrixToken::GetMatrix() const { return pMatrix; } +ScMatrix* ScMatrixToken::GetMatrix() { return pMatrix; } +BOOL ScMatrixToken::operator==( const FormulaToken& r ) const +{ + return FormulaToken::operator==( r ) && pMatrix == static_cast<const ScToken&>(r).GetMatrix(); +} + +// ============================================================================ + +ScExternalSingleRefToken::ScExternalSingleRefToken( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& r ) : + ScToken( svExternalSingleRef, ocExternalRef), + mnFileId(nFileId), + maTabName(rTabName), + maSingleRef(r) +{ +} + +ScExternalSingleRefToken::ScExternalSingleRefToken( const ScExternalSingleRefToken& r ) : + ScToken(r), + mnFileId(r.mnFileId), + maTabName(r.maTabName), + maSingleRef(r.maSingleRef) +{ +} + +ScExternalSingleRefToken::~ScExternalSingleRefToken() +{ +} + +USHORT ScExternalSingleRefToken::GetIndex() const +{ + return mnFileId; +} + +const String& ScExternalSingleRefToken::GetString() const +{ + return maTabName; +} + +const ScSingleRefData& ScExternalSingleRefToken::GetSingleRef() const +{ + return maSingleRef; +} + +ScSingleRefData& ScExternalSingleRefToken::GetSingleRef() +{ + return maSingleRef; +} + +void ScExternalSingleRefToken::CalcAbsIfRel( const ScAddress& rPos ) +{ + maSingleRef.CalcAbsIfRel( rPos ); +} + +void ScExternalSingleRefToken::CalcRelFromAbs( const ScAddress& rPos ) +{ + maSingleRef.CalcRelFromAbs( rPos ); +} + +BOOL ScExternalSingleRefToken::operator ==( const FormulaToken& r ) const +{ + if (!FormulaToken::operator==(r)) + return false; + + if (mnFileId != r.GetIndex()) + return false; + + if (maTabName != r.GetString()) + return false; + + return maSingleRef == static_cast<const ScToken&>(r).GetSingleRef(); +} + +// ============================================================================ + +ScExternalDoubleRefToken::ScExternalDoubleRefToken( sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& r ) : + ScToken( svExternalDoubleRef, ocExternalRef), + mnFileId(nFileId), + maTabName(rTabName), + maDoubleRef(r) +{ +} + +ScExternalDoubleRefToken::ScExternalDoubleRefToken( const ScExternalDoubleRefToken& r ) : + ScToken(r), + mnFileId(r.mnFileId), + maTabName(r.maTabName), + maDoubleRef(r.maDoubleRef) +{ +} + +ScExternalDoubleRefToken::~ScExternalDoubleRefToken() +{ +} + +USHORT ScExternalDoubleRefToken::GetIndex() const +{ + return mnFileId; +} + +const String& ScExternalDoubleRefToken::GetString() const +{ + return maTabName; +} + +const ScSingleRefData& ScExternalDoubleRefToken::GetSingleRef() const +{ + return maDoubleRef.Ref1; +} + +ScSingleRefData& ScExternalDoubleRefToken::GetSingleRef() +{ + return maDoubleRef.Ref1; +} + +const ScSingleRefData& ScExternalDoubleRefToken::GetSingleRef2() const +{ + return maDoubleRef.Ref2; +} + +ScSingleRefData& ScExternalDoubleRefToken::GetSingleRef2() +{ + return maDoubleRef.Ref2; +} + +const ScComplexRefData& ScExternalDoubleRefToken::GetDoubleRef() const +{ + return maDoubleRef; +} + +ScComplexRefData& ScExternalDoubleRefToken::GetDoubleRef() +{ + return maDoubleRef; +} + +void ScExternalDoubleRefToken::CalcAbsIfRel( const ScAddress& rPos ) +{ + maDoubleRef.CalcAbsIfRel( rPos ); +} + +void ScExternalDoubleRefToken::CalcRelFromAbs( const ScAddress& rPos ) +{ + maDoubleRef.CalcRelFromAbs( rPos ); +} + +BOOL ScExternalDoubleRefToken::operator ==( const FormulaToken& r ) const +{ + if (!ScToken::operator==(r)) + return false; + + if (mnFileId != r.GetIndex()) + return false; + + if (maTabName != r.GetString()) + return false; + + return maDoubleRef == static_cast<const ScToken&>(r).GetDoubleRef(); +} + +// ============================================================================ + +ScExternalNameToken::ScExternalNameToken( sal_uInt16 nFileId, const String& rName ) : + ScToken( svExternalName, ocExternalRef), + mnFileId(nFileId), + maName(rName) +{ +} + +ScExternalNameToken::ScExternalNameToken( const ScExternalNameToken& r ) : + ScToken(r), + mnFileId(r.mnFileId), + maName(r.maName) +{ +} + +ScExternalNameToken::~ScExternalNameToken() {} + +USHORT ScExternalNameToken::GetIndex() const +{ + return mnFileId; +} + +const String& ScExternalNameToken::GetString() const +{ + return maName; +} + +BOOL ScExternalNameToken::operator==( const FormulaToken& r ) const +{ + if ( !FormulaToken::operator==(r) ) + return false; + + if (mnFileId != r.GetIndex()) + return false; + + xub_StrLen nLen = maName.Len(); + const String& rName = r.GetString(); + if (nLen != rName.Len()) + return false; + + const sal_Unicode* p1 = maName.GetBuffer(); + const sal_Unicode* p2 = rName.GetBuffer(); + for (xub_StrLen j = 0; j < nLen; ++j) + { + if (p1[j] != p2[j]) + return false; + } + return true; +} + +// ============================================================================ + +ScJumpMatrix* ScJumpMatrixToken::GetJumpMatrix() const { return pJumpMatrix; } +BOOL ScJumpMatrixToken::operator==( const FormulaToken& r ) const +{ + return FormulaToken::operator==( r ) && pJumpMatrix == static_cast<const ScToken&>(r).GetJumpMatrix(); +} +ScJumpMatrixToken::~ScJumpMatrixToken() +{ + delete pJumpMatrix; +} + +double ScEmptyCellToken::GetDouble() const { return 0.0; } +const String & ScEmptyCellToken::GetString() const +{ + static String aDummyString; + return aDummyString; +} +BOOL ScEmptyCellToken::operator==( const FormulaToken& r ) const +{ + return FormulaToken::operator==( r ) && + bInherited == static_cast< const ScEmptyCellToken & >(r).IsInherited() && + bDisplayedAsString == static_cast< const ScEmptyCellToken & >(r).IsDisplayedAsString(); +} + + +double ScMatrixCellResultToken::GetDouble() const { return xUpperLeft->GetDouble(); } +const String & ScMatrixCellResultToken::GetString() const { return xUpperLeft->GetString(); } +const ScMatrix* ScMatrixCellResultToken::GetMatrix() const { return xMatrix; } +// Non-const GetMatrix() is private and unused but must be implemented to +// satisfy vtable linkage. +ScMatrix* ScMatrixCellResultToken::GetMatrix() +{ + return const_cast<ScMatrix*>(xMatrix.operator->()); +} +BOOL ScMatrixCellResultToken::operator==( const FormulaToken& r ) const +{ + return FormulaToken::operator==( r ) && + xUpperLeft == static_cast<const ScMatrixCellResultToken &>(r).xUpperLeft && + xMatrix == static_cast<const ScMatrixCellResultToken &>(r).xMatrix; +} + + +BOOL ScMatrixFormulaCellToken::operator==( const FormulaToken& r ) const +{ + const ScMatrixFormulaCellToken* p = dynamic_cast<const ScMatrixFormulaCellToken*>(&r); + return p && ScMatrixCellResultToken::operator==( r ) && + nCols == p->nCols && nRows == p->nRows; +} +void ScMatrixFormulaCellToken::Assign( const formula::FormulaToken& r ) +{ + if (this == &r) + return; + const ScMatrixCellResultToken* p = dynamic_cast<const ScMatrixCellResultToken*>(&r); + if (p) + ScMatrixCellResultToken::Assign( *p); + else + { + DBG_ASSERT( r.GetType() != svMatrix, "ScMatrixFormulaCellToken::operator=: assigning ScMatrixToken to ScMatrixFormulaCellToken is not proper, use ScMatrixCellResultToken instead"); + if (r.GetType() == svMatrix) + { + xUpperLeft = NULL; + xMatrix = static_cast<const ScToken&>(r).GetMatrix(); + } + else + { + xUpperLeft = &r; + xMatrix = NULL; + } + } +} +void ScMatrixFormulaCellToken::SetUpperLeftDouble( double f ) +{ + switch (GetUpperLeftType()) + { + case svDouble: + const_cast<FormulaToken*>(xUpperLeft.get())->GetDoubleAsReference() = f; + break; + case svUnknown: + if (!xUpperLeft) + { + xUpperLeft = new FormulaDoubleToken( f); + break; + } + // fall thru + default: + { + DBG_ERRORFILE("ScMatrixFormulaCellToken::SetUpperLeftDouble: not modifying unhandled token type"); + } + } +} + + +double ScHybridCellToken::GetDouble() const { return fDouble; } +const String & ScHybridCellToken::GetString() const { return aString; } +BOOL ScHybridCellToken::operator==( const FormulaToken& r ) const +{ + return FormulaToken::operator==( r ) && + fDouble == r.GetDouble() && aString == r.GetString() && + aFormula == static_cast<const ScHybridCellToken &>(r).GetFormula(); +} + + + + +////////////////////////////////////////////////////////////////////////// + +bool ScTokenArray::AddFormulaToken(const com::sun::star::sheet::FormulaToken& _aToken,formula::ExternalReferenceHelper* _pRef) +{ + bool bError = FormulaTokenArray::AddFormulaToken(_aToken,_pRef); + if ( bError ) + { + bError = false; + const OpCode eOpCode = static_cast<OpCode>(_aToken.OpCode); //! assuming equal values for the moment + + const uno::TypeClass eClass = _aToken.Data.getValueTypeClass(); + switch ( eClass ) + { + case uno::TypeClass_STRUCT: + { + uno::Type aType = _aToken.Data.getValueType(); + if ( aType.equals( cppu::UnoType<sheet::SingleReference>::get() ) ) + { + ScSingleRefData aSingleRef; + sheet::SingleReference aApiRef; + _aToken.Data >>= aApiRef; + lcl_SingleRefToCalc( aSingleRef, aApiRef ); + if ( eOpCode == ocPush ) + AddSingleReference( aSingleRef ); + else if ( eOpCode == ocColRowName ) + AddColRowName( aSingleRef ); + else + bError = true; + } + else if ( aType.equals( cppu::UnoType<sheet::ComplexReference>::get() ) ) + { + ScComplexRefData aComplRef; + sheet::ComplexReference aApiRef; + _aToken.Data >>= aApiRef; + lcl_SingleRefToCalc( aComplRef.Ref1, aApiRef.Reference1 ); + lcl_SingleRefToCalc( aComplRef.Ref2, aApiRef.Reference2 ); + + if ( eOpCode == ocPush ) + AddDoubleReference( aComplRef ); + else + bError = true; + } + else if ( aType.equals( cppu::UnoType<sheet::ExternalReference>::get() ) ) + { + sheet::ExternalReference aApiExtRef; + if( (eOpCode == ocPush) && (_aToken.Data >>= aApiExtRef) && (0 <= aApiExtRef.Index) && (aApiExtRef.Index <= SAL_MAX_UINT16) ) + { + sal_uInt16 nFileId = static_cast< sal_uInt16 >( aApiExtRef.Index ); + sheet::SingleReference aApiSRef; + sheet::ComplexReference aApiCRef; + ::rtl::OUString aName; + if( aApiExtRef.Reference >>= aApiSRef ) + { + // try to resolve cache index to sheet name + size_t nCacheId = static_cast< size_t >( aApiSRef.Sheet ); + String aTabName = _pRef->getCacheTableName( nFileId, nCacheId ); + if( aTabName.Len() > 0 ) + { + ScSingleRefData aSingleRef; + // convert column/row settings, set sheet index to absolute + lcl_ExternalRefToCalc( aSingleRef, aApiSRef ); + AddExternalSingleReference( nFileId, aTabName, aSingleRef ); + } + else + bError = true; + } + else if( aApiExtRef.Reference >>= aApiCRef ) + { + // try to resolve cache index to sheet name. + size_t nCacheId = static_cast< size_t >( aApiCRef.Reference1.Sheet ); + String aTabName = _pRef->getCacheTableName( nFileId, nCacheId ); + if( aTabName.Len() > 0 ) + { + ScComplexRefData aComplRef; + // convert column/row settings, set sheet index to absolute + lcl_ExternalRefToCalc( aComplRef.Ref1, aApiCRef.Reference1 ); + lcl_ExternalRefToCalc( aComplRef.Ref2, aApiCRef.Reference2 ); + // NOTE: This assumes that cached sheets are in consecutive order! + aComplRef.Ref2.nTab = aComplRef.Ref1.nTab + static_cast<SCsTAB>(aApiCRef.Reference2.Sheet - aApiCRef.Reference1.Sheet); + AddExternalDoubleReference( nFileId, aTabName, aComplRef ); + } + else + bError = true; + } + else if( aApiExtRef.Reference >>= aName ) + { + if( aName.getLength() > 0 ) + AddExternalName( nFileId, aName ); + else + bError = true; + } + else + bError = true; + } + else + bError = true; + } + else + bError = true; // unknown struct + } + break; + case uno::TypeClass_SEQUENCE: + { + if ( eOpCode != ocPush ) + bError = true; // not an inline array + else if (!_aToken.Data.getValueType().equals( getCppuType( + (uno::Sequence< uno::Sequence< uno::Any > > *)0))) + bError = true; // unexpected sequence type + else + { + ScMatrixRef xMat = ScSequenceToMatrix::CreateMixedMatrix( _aToken.Data); + if (xMat) + AddMatrix( xMat); + else + bError = true; + } + } + break; + default: + bError = true; + } + } + return bError; +} +BOOL ScTokenArray::ImplGetReference( ScRange& rRange, BOOL bValidOnly ) const +{ + BOOL bIs = FALSE; + if ( pCode && nLen == 1 ) + { + const FormulaToken* pToken = pCode[0]; + if ( pToken ) + { + if ( pToken->GetType() == svSingleRef ) + { + const ScSingleRefData& rRef = ((const ScSingleRefToken*)pToken)->GetSingleRef(); + rRange.aStart = rRange.aEnd = ScAddress( rRef.nCol, rRef.nRow, rRef.nTab ); + bIs = !bValidOnly || !rRef.IsDeleted(); + } + else if ( pToken->GetType() == svDoubleRef ) + { + const ScComplexRefData& rCompl = ((const ScDoubleRefToken*)pToken)->GetDoubleRef(); + const ScSingleRefData& rRef1 = rCompl.Ref1; + const ScSingleRefData& rRef2 = rCompl.Ref2; + rRange.aStart = ScAddress( rRef1.nCol, rRef1.nRow, rRef1.nTab ); + rRange.aEnd = ScAddress( rRef2.nCol, rRef2.nRow, rRef2.nTab ); + bIs = !bValidOnly || (!rRef1.IsDeleted() && !rRef2.IsDeleted()); + } + } + } + return bIs; +} + +BOOL ScTokenArray::IsReference( ScRange& rRange ) const +{ + return ImplGetReference( rRange, FALSE ); +} + +BOOL ScTokenArray::IsValidReference( ScRange& rRange ) const +{ + return ImplGetReference( rRange, TRUE ); +} + +//////////////////////////////////////////////////////////////////////////// + +ScTokenArray::ScTokenArray() +{ +} + +ScTokenArray::ScTokenArray( const ScTokenArray& rArr ) : FormulaTokenArray(rArr) +{ +} + +ScTokenArray::~ScTokenArray() +{ +} + + + +ScTokenArray& ScTokenArray::operator=( const ScTokenArray& rArr ) +{ + Clear(); + Assign( rArr ); + return *this; +} + +ScTokenArray* ScTokenArray::Clone() const +{ + ScTokenArray* p = new ScTokenArray(); + p->nLen = nLen; + p->nRPN = nRPN; + p->nRefs = nRefs; + p->nMode = nMode; + p->nError = nError; + p->bHyperLink = bHyperLink; + FormulaToken** pp; + if( nLen ) + { + pp = p->pCode = new FormulaToken*[ nLen ]; + memcpy( pp, pCode, nLen * sizeof( ScToken* ) ); + for( USHORT i = 0; i < nLen; i++, pp++ ) + { + *pp = (*pp)->Clone(); + (*pp)->IncRef(); + } + } + if( nRPN ) + { + pp = p->pRPN = new FormulaToken*[ nRPN ]; + memcpy( pp, pRPN, nRPN * sizeof( ScToken* ) ); + for( USHORT i = 0; i < nRPN; i++, pp++ ) + { + FormulaToken* t = *pp; + if( t->GetRef() > 1 ) + { + FormulaToken** p2 = pCode; + USHORT nIdx = 0xFFFF; + for( USHORT j = 0; j < nLen; j++, p2++ ) + { + if( *p2 == t ) + { + nIdx = j; break; + } + } + if( nIdx == 0xFFFF ) + *pp = t->Clone(); + else + *pp = p->pCode[ nIdx ]; + } + else + *pp = t->Clone(); + (*pp)->IncRef(); + } + } + return p; +} + +FormulaToken* ScTokenArray::AddRawToken( const ScRawToken& r ) +{ + return Add( r.CreateToken() ); +} + +// Utility function to ensure that there is strict alternation of values and +// seperators. +static bool +checkArraySep( bool & bPrevWasSep, bool bNewVal ) +{ + bool bResult = (bPrevWasSep == bNewVal); + bPrevWasSep = bNewVal; + return bResult; +} + +FormulaToken* ScTokenArray::MergeArray( ) +{ + int nCol = -1, nRow = 0; + int i, nPrevRowSep = -1, nStart = 0; + bool bPrevWasSep = false; // top of stack is ocArrayClose + FormulaToken* t; + bool bNumeric = false; // numeric value encountered in current element + + // (1) Iterate from the end to the start to find matrix dims + // and do basic validation. + for ( i = nLen ; i-- > nStart ; ) + { + t = pCode[i]; + switch ( t->GetOpCode() ) + { + case ocPush : + if( checkArraySep( bPrevWasSep, false ) ) + { + return NULL; + } + + // no references or nested arrays + if ( t->GetType() != svDouble && t->GetType() != svString ) + { + return NULL; + } + bNumeric = (t->GetType() == svDouble); + break; + + case ocMissing : + case ocTrue : + case ocFalse : + if( checkArraySep( bPrevWasSep, false ) ) + { + return NULL; + } + bNumeric = false; + break; + + case ocArrayColSep : + case ocSep : + if( checkArraySep( bPrevWasSep, true ) ) + { + return NULL; + } + bNumeric = false; + break; + + case ocArrayClose : + // not possible with the , but check just in case + // something changes in the future + if( i != (nLen-1)) + { + return NULL; + } + + if( checkArraySep( bPrevWasSep, true ) ) + { + return NULL; + } + + nPrevRowSep = i; + bNumeric = false; + break; + + case ocArrayOpen : + nStart = i; // stop iteration + // fall through to ArrayRowSep + + case ocArrayRowSep : + if( checkArraySep( bPrevWasSep, true ) ) + { + return NULL; + } + + if( nPrevRowSep < 0 || // missing ocArrayClose + ((nPrevRowSep - i) % 2) == 1) // no complex elements + { + return NULL; + } + + if( nCol < 0 ) + { + nCol = (nPrevRowSep - i) / 2; + } + else if( (nPrevRowSep - i)/2 != nCol) // irregular array + { + return NULL; + } + + nPrevRowSep = i; + nRow++; + bNumeric = false; + break; + + case ocNegSub : + case ocAdd : + // negation or unary plus must precede numeric value + if( !bNumeric ) + { + return NULL; + } + --nPrevRowSep; // shorten this row by 1 + bNumeric = false; // one level only, no --42 + break; + + case ocSpaces : + // ignore spaces + --nPrevRowSep; // shorten this row by 1 + break; + + default : + // no functions or operators + return NULL; + } + } + if( nCol <= 0 || nRow <= 0 ) + return NULL; + + // fprintf (stderr, "Array (cols = %d, rows = %d)\n", nCol, nRow ); + + int nSign = 1; + ScMatrix* pArray = new ScMatrix( nCol, nRow ); + for ( i = nStart, nCol = 0, nRow = 0 ; i < nLen ; i++ ) + { + t = pCode[i]; + + switch ( t->GetOpCode() ) + { + case ocPush : + if ( t->GetType() == svDouble ) + { + pArray->PutDouble( t->GetDouble() * nSign, nCol, nRow ); + nSign = 1; + } + else if ( t->GetType() == svString ) + { + pArray->PutString( t->GetString(), nCol, nRow ); + } + break; + + case ocMissing : + pArray->PutEmpty( nCol, nRow ); + break; + + case ocTrue : + pArray->PutBoolean( true, nCol, nRow ); + break; + + case ocFalse : + pArray->PutBoolean( false, nCol, nRow ); + break; + + case ocArrayColSep : + case ocSep : + nCol++; + break; + + case ocArrayRowSep : + nRow++; nCol = 0; + break; + + case ocNegSub : + nSign = -nSign; + break; + + default : + break; + } + pCode[i] = NULL; + t->DecRef(); + } + nLen = USHORT( nStart ); + return AddMatrix( pArray ); +} + + +FormulaToken* ScTokenArray::MergeRangeReference( const ScAddress & rPos ) +{ + if (!pCode || !nLen) + return NULL; + USHORT nIdx = nLen; + FormulaToken *p1, *p2, *p3; // ref, ocRange, ref + // The actual types are checked in ExtendRangeReference(). + if (((p3 = PeekPrev(nIdx)) != 0) && + (((p2 = PeekPrev(nIdx)) != 0) && p2->GetOpCode() == ocRange) && + ((p1 = PeekPrev(nIdx)) != 0)) + { + FormulaTokenRef p = ScToken::ExtendRangeReference( *p1, *p3, rPos, true); + if (p) + { + p->IncRef(); + p1->DecRef(); + p2->DecRef(); + p3->DecRef(); + nLen -= 2; + pCode[ nLen-1 ] = p; + nRefs--; + } + } + return pCode[ nLen-1 ]; +} + +FormulaToken* ScTokenArray::AddOpCode( OpCode e ) +{ + ScRawToken t; + t.SetOpCode( e ); + return AddRawToken( t ); +} + +FormulaToken* ScTokenArray::AddSingleReference( const ScSingleRefData& rRef ) +{ + return Add( new ScSingleRefToken( rRef ) ); +} + +FormulaToken* ScTokenArray::AddMatrixSingleReference( const ScSingleRefData& rRef ) +{ + return Add( new ScSingleRefToken( rRef, ocMatRef ) ); +} + +FormulaToken* ScTokenArray::AddDoubleReference( const ScComplexRefData& rRef ) +{ + return Add( new ScDoubleRefToken( rRef ) ); +} + +FormulaToken* ScTokenArray::AddMatrix( ScMatrix* p ) +{ + return Add( new ScMatrixToken( p ) ); +} + +FormulaToken* ScTokenArray::AddExternalName( sal_uInt16 nFileId, const String& rName ) +{ + return Add( new ScExternalNameToken(nFileId, rName) ); +} + +FormulaToken* ScTokenArray::AddExternalSingleReference( sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef ) +{ + return Add( new ScExternalSingleRefToken(nFileId, rTabName, rRef) ); +} + +FormulaToken* ScTokenArray::AddExternalDoubleReference( sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef ) +{ + return Add( new ScExternalDoubleRefToken(nFileId, rTabName, rRef) ); +} + +FormulaToken* ScTokenArray::AddColRowName( const ScSingleRefData& rRef ) +{ + return Add( new ScSingleRefToken( rRef, ocColRowName ) ); +} + +BOOL ScTokenArray::GetAdjacentExtendOfOuterFuncRefs( SCCOLROW& nExtend, + const ScAddress& rPos, ScDirection eDir ) +{ + SCCOL nCol = 0; + SCROW nRow = 0; + switch ( eDir ) + { + case DIR_BOTTOM : + if ( rPos.Row() < MAXROW ) + nRow = (nExtend = rPos.Row()) + 1; + else + return FALSE; + break; + case DIR_RIGHT : + if ( rPos.Col() < MAXCOL ) + nCol = static_cast<SCCOL>(nExtend = rPos.Col()) + 1; + else + return FALSE; + break; + case DIR_TOP : + if ( rPos.Row() > 0 ) + nRow = (nExtend = rPos.Row()) - 1; + else + return FALSE; + break; + case DIR_LEFT : + if ( rPos.Col() > 0 ) + nCol = static_cast<SCCOL>(nExtend = rPos.Col()) - 1; + else + return FALSE; + break; + default: + DBG_ERRORFILE( "unknown Direction" ); + return FALSE; + } + if ( pRPN && nRPN ) + { + FormulaToken* t = pRPN[nRPN-1]; + if ( t->GetType() == svByte ) + { + BYTE nParamCount = t->GetByte(); + if ( nParamCount && nRPN > nParamCount ) + { + BOOL bRet = FALSE; + USHORT nParam = nRPN - nParamCount - 1; + for ( ; nParam < nRPN-1; nParam++ ) + { + FormulaToken* p = pRPN[nParam]; + switch ( p->GetType() ) + { + case svSingleRef : + { + ScSingleRefData& rRef = static_cast<ScToken*>(p)->GetSingleRef(); + rRef.CalcAbsIfRel( rPos ); + switch ( eDir ) + { + case DIR_BOTTOM : + if ( rRef.nRow == nRow + && rRef.nRow > nExtend ) + { + nExtend = rRef.nRow; + bRet = TRUE; + } + break; + case DIR_RIGHT : + if ( rRef.nCol == nCol + && static_cast<SCCOLROW>(rRef.nCol) + > nExtend ) + { + nExtend = rRef.nCol; + bRet = TRUE; + } + break; + case DIR_TOP : + if ( rRef.nRow == nRow + && rRef.nRow < nExtend ) + { + nExtend = rRef.nRow; + bRet = TRUE; + } + break; + case DIR_LEFT : + if ( rRef.nCol == nCol + && static_cast<SCCOLROW>(rRef.nCol) + < nExtend ) + { + nExtend = rRef.nCol; + bRet = TRUE; + } + break; + } + } + break; + case svDoubleRef : + { + ScComplexRefData& rRef = static_cast<ScToken*>(p)->GetDoubleRef(); + rRef.CalcAbsIfRel( rPos ); + switch ( eDir ) + { + case DIR_BOTTOM : + if ( rRef.Ref1.nRow == nRow + && rRef.Ref2.nRow > nExtend ) + { + nExtend = rRef.Ref2.nRow; + bRet = TRUE; + } + break; + case DIR_RIGHT : + if ( rRef.Ref1.nCol == nCol && + static_cast<SCCOLROW>(rRef.Ref2.nCol) + > nExtend ) + { + nExtend = rRef.Ref2.nCol; + bRet = TRUE; + } + break; + case DIR_TOP : + if ( rRef.Ref2.nRow == nRow + && rRef.Ref1.nRow < nExtend ) + { + nExtend = rRef.Ref1.nRow; + bRet = TRUE; + } + break; + case DIR_LEFT : + if ( rRef.Ref2.nCol == nCol && + static_cast<SCCOLROW>(rRef.Ref1.nCol) + < nExtend ) + { + nExtend = rRef.Ref1.nCol; + bRet = TRUE; + } + break; + } + } + break; + default: + { + // added to avoid warnings + } + } // switch + } // for + return bRet; + } + } + } + return FALSE; +} + + +void ScTokenArray::ReadjustRelative3DReferences( const ScAddress& rOldPos, + const ScAddress& rNewPos ) +{ + for ( USHORT j=0; j<nLen; ++j ) + { + switch ( pCode[j]->GetType() ) + { + case svDoubleRef : + { + ScSingleRefData& rRef2 = static_cast<ScToken*>(pCode[j])->GetSingleRef2(); + // Also adjust if the reference is of the form Sheet1.A2:A3 + if ( rRef2.IsFlag3D() || static_cast<ScToken*>(pCode[j])->GetSingleRef().IsFlag3D() ) + { + rRef2.CalcAbsIfRel( rOldPos ); + rRef2.CalcRelFromAbs( rNewPos ); + } + } + //! fallthru + case svSingleRef : + { + ScSingleRefData& rRef1 = static_cast<ScToken*>(pCode[j])->GetSingleRef(); + if ( rRef1.IsFlag3D() ) + { + rRef1.CalcAbsIfRel( rOldPos ); + rRef1.CalcRelFromAbs( rNewPos ); + } + } + break; + default: + { + // added to avoid warnings + } + } + } +} + + diff --git a/sc/source/core/tool/unitconv.cxx b/sc/source/core/tool/unitconv.cxx new file mode 100644 index 000000000000..21f80cb1c628 --- /dev/null +++ b/sc/source/core/tool/unitconv.cxx @@ -0,0 +1,178 @@ +/************************************************************************* + * + * 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/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include "unitconv.hxx" +#include "global.hxx" +#include "viewopti.hxx" //! move ScLinkConfigItem to separate header! + +using namespace utl; +using namespace rtl; +using namespace com::sun::star::uno; + +// -------------------------------------------------------------------- + +const sal_Unicode cDelim = 0x01; // Delimiter zwischen From und To + + +// --- ScUnitConverterData -------------------------------------------- + +ScUnitConverterData::ScUnitConverterData( const String& rFromUnit, + const String& rToUnit, double fVal ) + : + StrData( rFromUnit ), + fValue( fVal ) +{ + String aTmp; + ScUnitConverterData::BuildIndexString( aTmp, rFromUnit, rToUnit ); + SetString( aTmp ); +} + + +ScUnitConverterData::ScUnitConverterData( const ScUnitConverterData& r ) + : + StrData( r ), + fValue( r.fValue ) +{ +} + + +ScDataObject* ScUnitConverterData::Clone() const +{ + return new ScUnitConverterData( *this ); +} + + +// static +void ScUnitConverterData::BuildIndexString( String& rStr, + const String& rFromUnit, const String& rToUnit ) +{ +#if 1 +// case sensitive + rStr = rFromUnit; + rStr += cDelim; + rStr += rToUnit; +#else +// not case sensitive + rStr = rFromUnit; + String aTo( rToUnit ); + ScGlobal::pCharClass->toUpper( rStr ); + ScGlobal::pCharClass->toUpper( aTo ); + rStr += cDelim; + rStr += aTo; +#endif +} + + +// --- ScUnitConverter ------------------------------------------------ + +#define CFGPATH_UNIT "Office.Calc/UnitConversion" +#define CFGSTR_UNIT_FROM "FromUnit" +#define CFGSTR_UNIT_TO "ToUnit" +#define CFGSTR_UNIT_FACTOR "Factor" + +ScUnitConverter::ScUnitConverter( USHORT nInit, USHORT nDeltaP ) : + ScStrCollection( nInit, nDeltaP, FALSE ) +{ + // read from configuration - "convert.ini" is no longer used + //! config item as member to allow change of values + + ScLinkConfigItem aConfigItem( OUString::createFromAscii( CFGPATH_UNIT ) ); + + // empty node name -> use the config item's path itself + OUString aEmptyString; + Sequence<OUString> aNodeNames = aConfigItem.GetNodeNames( aEmptyString ); + + long nNodeCount = aNodeNames.getLength(); + if ( nNodeCount ) + { + const OUString* pNodeArray = aNodeNames.getConstArray(); + Sequence<OUString> aValNames( nNodeCount * 3 ); + OUString* pValNameArray = aValNames.getArray(); + const OUString sSlash('/'); + + long nIndex = 0; + for (long i=0; i<nNodeCount; i++) + { + OUString sPrefix = pNodeArray[i]; + sPrefix += sSlash; + + pValNameArray[nIndex] = sPrefix; + pValNameArray[nIndex++] += OUString::createFromAscii( CFGSTR_UNIT_FROM ); + pValNameArray[nIndex] = sPrefix; + pValNameArray[nIndex++] += OUString::createFromAscii( CFGSTR_UNIT_TO ); + pValNameArray[nIndex] = sPrefix; + pValNameArray[nIndex++] += OUString::createFromAscii( CFGSTR_UNIT_FACTOR ); + } + + Sequence<Any> aProperties = aConfigItem.GetProperties(aValNames); + + if (aProperties.getLength() == aValNames.getLength()) + { + const Any* pProperties = aProperties.getConstArray(); + + OUString sFromUnit; + OUString sToUnit; + double fFactor = 0; + + nIndex = 0; + for (long i=0; i<nNodeCount; i++) + { + pProperties[nIndex++] >>= sFromUnit; + pProperties[nIndex++] >>= sToUnit; + pProperties[nIndex++] >>= fFactor; + + ScUnitConverterData* pNew = new ScUnitConverterData( sFromUnit, sToUnit, fFactor ); + if ( !Insert( pNew ) ) + delete pNew; + } + } + } +} + +BOOL ScUnitConverter::GetValue( double& fValue, const String& rFromUnit, + const String& rToUnit ) const +{ + ScUnitConverterData aSearch( rFromUnit, rToUnit ); + USHORT nIndex; + if ( Search( &aSearch, nIndex ) ) + { + fValue = ((const ScUnitConverterData*)(At( nIndex )))->GetValue(); + return TRUE; + } + fValue = 1.0; + return FALSE; +} + + diff --git a/sc/source/core/tool/userlist.cxx b/sc/source/core/tool/userlist.cxx new file mode 100644 index 000000000000..08609eeff940 --- /dev/null +++ b/sc/source/core/tool/userlist.cxx @@ -0,0 +1,297 @@ +/************************************************************************* + * + * 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/charclass.hxx> +#include <string.h> + +#include "global.hxx" +#include "userlist.hxx" +#include <unotools/localedatawrapper.hxx> +#include <unotools/calendarwrapper.hxx> +#include <unotools/transliterationwrapper.hxx> + +// STATIC DATA ----------------------------------------------------------- + + +//------------------------------------------------------------------------ + +void ScUserListData::InitTokens() +{ + sal_Unicode cSep = ScGlobal::cListDelimiter; + nTokenCount = (USHORT) aStr.GetTokenCount(cSep); + if (nTokenCount) + { + pSubStrings = new String[nTokenCount]; + pUpperSub = new String[nTokenCount]; + for (USHORT i=0; i<nTokenCount; i++) + { + pUpperSub[i] = pSubStrings[i] = aStr.GetToken((xub_StrLen)i,cSep); + ScGlobal::pCharClass->toUpper(pUpperSub[i]); + } + } + else + pSubStrings = pUpperSub = NULL; +} + +ScUserListData::ScUserListData(const String& rStr) : + aStr(rStr) +{ + InitTokens(); +} + +ScUserListData::ScUserListData(const ScUserListData& rData) : + ScDataObject(), + aStr(rData.aStr) +{ + InitTokens(); +} + +__EXPORT ScUserListData::~ScUserListData() +{ + delete[] pSubStrings; + delete[] pUpperSub; +} + +void ScUserListData::SetString( const String& rStr ) +{ + delete[] pSubStrings; + delete[] pUpperSub; + + aStr = rStr; + InitTokens(); +} + +USHORT ScUserListData::GetSubCount() const +{ + return nTokenCount; +} + +BOOL ScUserListData::GetSubIndex(const String& rSubStr, USHORT& rIndex) const +{ + USHORT i; + for (i=0; i<nTokenCount; i++) + if (rSubStr == pSubStrings[i]) + { + rIndex = i; + return TRUE; + } + + String aUpStr = rSubStr; + ScGlobal::pCharClass->toUpper(aUpStr); + for (i=0; i<nTokenCount; i++) + if (aUpStr == pUpperSub[i]) + { + rIndex = i; + return TRUE; + } + + return FALSE; +} + +String ScUserListData::GetSubStr(USHORT nIndex) const +{ + if (nIndex < nTokenCount) + return pSubStrings[nIndex]; + else + return EMPTY_STRING; +} + +StringCompare ScUserListData::Compare(const String& rSubStr1, const String& rSubStr2) const +{ + USHORT nIndex1; + USHORT nIndex2; + BOOL bFound1 = GetSubIndex(rSubStr1, nIndex1); + BOOL bFound2 = GetSubIndex(rSubStr2, nIndex2); + if (bFound1) + { + if (bFound2) + { + if (nIndex1 < nIndex2) + return COMPARE_LESS; + else if (nIndex1 > nIndex2) + return COMPARE_GREATER; + else + return COMPARE_EQUAL; + } + else + return COMPARE_LESS; + } + else if (bFound2) + return COMPARE_GREATER; + else + return (StringCompare) ScGlobal::GetCaseTransliteration()->compareString( rSubStr1, rSubStr2 ); +} + +StringCompare ScUserListData::ICompare(const String& rSubStr1, const String& rSubStr2) const +{ + USHORT nIndex1; + USHORT nIndex2; + BOOL bFound1 = GetSubIndex(rSubStr1, nIndex1); + BOOL bFound2 = GetSubIndex(rSubStr2, nIndex2); + if (bFound1) + { + if (bFound2) + { + if (nIndex1 < nIndex2) + return COMPARE_LESS; + else if (nIndex1 > nIndex2) + return COMPARE_GREATER; + else + return COMPARE_EQUAL; + } + else + return COMPARE_LESS; + } + else if (bFound2) + return COMPARE_GREATER; + else + return (StringCompare) ScGlobal::GetpTransliteration()->compareString( rSubStr1, rSubStr2 ); +} + +ScUserList::ScUserList(USHORT nLim, USHORT nDel) : + ScCollection ( nLim, nDel ) +{ + using namespace ::com::sun::star; + + sal_Unicode cDelimiter = ScGlobal::cListDelimiter; + uno::Sequence< i18n::CalendarItem > xCal; + + uno::Sequence< i18n::Calendar > xCalendars( + ScGlobal::pLocaleData->getAllCalendars() ); + + for ( sal_Int32 j = 0; j < xCalendars.getLength(); ++j ) + { + xCal = xCalendars[j].Days; + if ( xCal.getLength() ) + { + String sDayShort, sDayLong; + sal_Int32 i; + sal_Int32 nLen = xCal.getLength(); + rtl::OUString sStart = xCalendars[j].StartOfWeek; + sal_Int16 nStart = sal::static_int_cast<sal_Int16>(nLen); + while (nStart > 0) + { + if (xCal[--nStart].ID == sStart) + break; + } + sal_Int16 nLast = sal::static_int_cast<sal_Int16>( (nStart + nLen - 1) % nLen ); + for (i = nStart; i != nLast; i = (i+1) % nLen) + { + sDayShort += String( xCal[i].AbbrevName ); + sDayShort += cDelimiter; + sDayLong += String( xCal[i].FullName ); + sDayLong += cDelimiter; + } + sDayShort += String( xCal[i].AbbrevName ); + sDayLong += String( xCal[i].FullName ); + + if ( !HasEntry( sDayShort ) ) + Insert( new ScUserListData( sDayShort )); + if ( !HasEntry( sDayLong ) ) + Insert( new ScUserListData( sDayLong )); + } + + xCal = xCalendars[j].Months; + if ( xCal.getLength() ) + { + String sMonthShort, sMonthLong; + sal_Int32 i; + sal_Int32 nLen = xCal.getLength() - 1; + for (i = 0; i < nLen; i++) + { + sMonthShort += String( xCal[i].AbbrevName ); + sMonthShort += cDelimiter; + sMonthLong += String( xCal[i].FullName ); + sMonthLong += cDelimiter; + } + sMonthShort += String( xCal[i].AbbrevName ); + sMonthLong += String( xCal[i].FullName ); + + if ( !HasEntry( sMonthShort ) ) + Insert( new ScUserListData( sMonthShort )); + if ( !HasEntry( sMonthLong ) ) + Insert( new ScUserListData( sMonthLong )); + } + } +} + +ScDataObject* ScUserList::Clone() const +{ + return ( new ScUserList( *this ) ); +} + +ScUserListData* ScUserList::GetData(const String& rSubStr) const +{ + USHORT nIndex; + USHORT i = 0; + for (i=0; i < nCount; i++) + if (((ScUserListData*)pItems[i])->GetSubIndex(rSubStr, nIndex)) + return (ScUserListData*)pItems[i]; + return NULL; +} + +BOOL ScUserList::operator==( const ScUserList& r ) const +{ + BOOL bEqual = (nCount == r.nCount); + + if ( bEqual ) + { + ScUserListData* pMyData = NULL; + ScUserListData* pOtherData = NULL; + + for ( USHORT i=0; i<nCount && bEqual; i++) + { + pMyData = (ScUserListData*)At(i); + pOtherData = (ScUserListData*)r.At(i); + + bEqual =( (pMyData->nTokenCount == pOtherData->nTokenCount) + && (pMyData->aStr == pOtherData->aStr) ); + } + } + + return bEqual; +} + + +BOOL ScUserList::HasEntry( const String& rStr ) const +{ + for ( USHORT i=0; i<nCount; i++) + { + const ScUserListData* pMyData = (ScUserListData*) At(i); + if ( pMyData->aStr == rStr ) + return TRUE; + } + return FALSE; +} + diff --git a/sc/source/core/tool/viewopti.cxx b/sc/source/core/tool/viewopti.cxx new file mode 100644 index 000000000000..7dc36bc1b548 --- /dev/null +++ b/sc/source/core/tool/viewopti.cxx @@ -0,0 +1,754 @@ +/************************************************************************* + * + * 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 <vcl/svapp.hxx> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include "global.hxx" +#include "globstr.hrc" +#include "cfgids.hxx" +#include "viewopti.hxx" +#include "rechead.hxx" +#include "scresid.hxx" +#include "sc.hrc" +#include "miscuno.hxx" + +using namespace utl; +using namespace rtl; +using namespace com::sun::star::uno; + +//------------------------------------------------------------------ + +TYPEINIT1(ScTpViewItem, SfxPoolItem); + +#define SC_VERSION ((USHORT)302) + + +//======================================================================== +// class ScGridOptions +//======================================================================== + + +void ScGridOptions::SetDefaults() +{ + *this = ScGridOptions(); + + // Raster-Defaults sind jetzt zwischen den Apps unterschiedlich + // darum hier selber eintragen (alles in 1/100mm) + + if ( ScOptionsUtil::IsMetricSystem() ) + { + nFldDrawX = 1000; // 1cm + nFldDrawY = 1000; + nFldSnapX = 1000; + nFldSnapY = 1000; + } + else + { + nFldDrawX = 1270; // 0,5" + nFldDrawY = 1270; + nFldSnapX = 1270; + nFldSnapY = 1270; + } + nFldDivisionX = 1; + nFldDivisionY = 1; +} + +//------------------------------------------------------------------------ + +const ScGridOptions& ScGridOptions::operator=( const ScGridOptions& rCpy ) +{ + nFldDrawX = rCpy.nFldDrawX; // UINT32 + nFldDrawX = rCpy.nFldDrawX; + nFldDivisionX = rCpy.nFldDivisionX; + nFldDrawY = rCpy.nFldDrawY; + nFldDivisionY = rCpy.nFldDivisionY; + nFldSnapX = rCpy.nFldSnapX; + nFldSnapY = rCpy.nFldSnapY; + bUseGridsnap = rCpy.bUseGridsnap; // BitBool + bSynchronize = rCpy.bSynchronize; + bGridVisible = rCpy.bGridVisible; + bEqualGrid = rCpy.bEqualGrid; + + return *this; +} + +//------------------------------------------------------------------------ + +int ScGridOptions::operator==( const ScGridOptions& rCpy ) const +{ + return ( nFldDrawX == rCpy.nFldDrawX + && nFldDrawX == rCpy.nFldDrawX + && nFldDivisionX == rCpy.nFldDivisionX + && nFldDrawY == rCpy.nFldDrawY + && nFldDivisionY == rCpy.nFldDivisionY + && nFldSnapX == rCpy.nFldSnapX + && nFldSnapY == rCpy.nFldSnapY + && bUseGridsnap == rCpy.bUseGridsnap + && bSynchronize == rCpy.bSynchronize + && bGridVisible == rCpy.bGridVisible + && bEqualGrid == rCpy.bEqualGrid ); +} + + +//======================================================================== +// class ScViewOptions +//======================================================================== + +ScViewOptions::ScViewOptions() +{ + SetDefaults(); +} + +//------------------------------------------------------------------------ + +ScViewOptions::ScViewOptions( const ScViewOptions& rCpy ) +{ + *this = rCpy; +} + +//------------------------------------------------------------------------ + +__EXPORT ScViewOptions::~ScViewOptions() +{ +} + +//------------------------------------------------------------------------ + +void ScViewOptions::SetDefaults() +{ + aOptArr[ VOPT_FORMULAS ] = + aOptArr[ VOPT_SYNTAX ] = + aOptArr[ VOPT_HELPLINES ] = + aOptArr[ VOPT_BIGHANDLES ] = FALSE; + aOptArr[ VOPT_NOTES ] = + aOptArr[ VOPT_NULLVALS ] = + aOptArr[ VOPT_VSCROLL ] = + aOptArr[ VOPT_HSCROLL ] = + aOptArr[ VOPT_TABCONTROLS ] = + aOptArr[ VOPT_OUTLINER ] = + aOptArr[ VOPT_HEADER ] = + aOptArr[ VOPT_GRID ] = + aOptArr[ VOPT_ANCHOR ] = + aOptArr[ VOPT_PAGEBREAKS ] = + aOptArr[ VOPT_SOLIDHANDLES] = + aOptArr[ VOPT_CLIPMARKS ] = TRUE; + + aModeArr[VOBJ_TYPE_OLE ] = + aModeArr[VOBJ_TYPE_CHART] = + aModeArr[VOBJ_TYPE_DRAW ] = VOBJ_MODE_SHOW; + + aGridCol = Color( SC_STD_GRIDCOLOR ); + aGridColName = ScGlobal::GetRscString( STR_GRIDCOLOR ); + + aGridOpt.SetDefaults(); +} + +//------------------------------------------------------------------------ + +Color ScViewOptions::GetGridColor( String* pStrName ) const +{ + if ( pStrName ) + *pStrName = aGridColName; + + return aGridCol; +} + +//------------------------------------------------------------------------ + +const ScViewOptions& ScViewOptions::operator=( const ScViewOptions& rCpy ) +{ + USHORT i; + + for ( i=0; i<MAX_OPT; i++ ) aOptArr [i] = rCpy.aOptArr[i]; + for ( i=0; i<MAX_TYPE; i++ ) aModeArr[i] = rCpy.aModeArr[i]; + + aGridCol = rCpy.aGridCol; + aGridColName = rCpy.aGridColName; + aGridOpt = rCpy.aGridOpt; + + return *this; +} + +//------------------------------------------------------------------------ + +int ScViewOptions::operator==( const ScViewOptions& rOpt ) const +{ + BOOL bEqual = TRUE; + USHORT i; + + for ( i=0; i<MAX_OPT && bEqual; i++ ) bEqual = (aOptArr [i] == rOpt.aOptArr[i]); + for ( i=0; i<MAX_TYPE && bEqual; i++ ) bEqual = (aModeArr[i] == rOpt.aModeArr[i]); + + bEqual = bEqual && (aGridCol == rOpt.aGridCol); + bEqual = bEqual && (aGridColName == rOpt.aGridColName); + bEqual = bEqual && (aGridOpt == rOpt.aGridOpt); + + return bEqual; +} + +//------------------------------------------------------------------------ + +SvxGridItem* ScViewOptions::CreateGridItem( USHORT nId /* = SID_ATTR_GRID_OPTIONS */ ) const +{ + SvxGridItem* pItem = new SvxGridItem( nId ); + + pItem->SetFldDrawX ( aGridOpt.GetFldDrawX() ); + pItem->SetFldDivisionX ( aGridOpt.GetFldDivisionX() ); + pItem->SetFldDrawY ( aGridOpt.GetFldDrawY() ); + pItem->SetFldDivisionY ( aGridOpt.GetFldDivisionY() ); + pItem->SetFldSnapX ( aGridOpt.GetFldSnapX() ); + pItem->SetFldSnapY ( aGridOpt.GetFldSnapY() ); + pItem->SetUseGridSnap ( aGridOpt.GetUseGridSnap() ); + pItem->SetSynchronize ( aGridOpt.GetSynchronize() ); + pItem->SetGridVisible ( aGridOpt.GetGridVisible() ); + pItem->SetEqualGrid ( aGridOpt.GetEqualGrid() ); + + return pItem; +} + +//======================================================================== +// ScTpViewItem - Daten fuer die ViewOptions-TabPage +//======================================================================== + +//UNUSED2008-05 ScTpViewItem::ScTpViewItem( USHORT nWhichP ) : SfxPoolItem( nWhichP ) +//UNUSED2008-05 { +//UNUSED2008-05 } + +//------------------------------------------------------------------------ + +ScTpViewItem::ScTpViewItem( USHORT nWhichP, const ScViewOptions& rOpt ) + : SfxPoolItem ( nWhichP ), + theOptions ( rOpt ) +{ +} + +//------------------------------------------------------------------------ + +ScTpViewItem::ScTpViewItem( const ScTpViewItem& rItem ) + : SfxPoolItem ( rItem ), + theOptions ( rItem.theOptions ) +{ +} + +//------------------------------------------------------------------------ + +__EXPORT ScTpViewItem::~ScTpViewItem() +{ +} + +//------------------------------------------------------------------------ + +String __EXPORT ScTpViewItem::GetValueText() const +{ + return String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM("ScTpViewItem") ); +} + +//------------------------------------------------------------------------ + +int __EXPORT ScTpViewItem::operator==( const SfxPoolItem& rItem ) const +{ + DBG_ASSERT( SfxPoolItem::operator==( rItem ), "unequal Which or Type" ); + + const ScTpViewItem& rPItem = (const ScTpViewItem&)rItem; + + return ( theOptions == rPItem.theOptions ); +} + +//------------------------------------------------------------------------ + +SfxPoolItem* __EXPORT ScTpViewItem::Clone( SfxItemPool * ) const +{ + return new ScTpViewItem( *this ); +} + +//================================================================== +// Config Item containing view options +//================================================================== + +#define CFGPATH_LAYOUT "Office.Calc/Layout" + +#define SCLAYOUTOPT_GRIDLINES 0 +#define SCLAYOUTOPT_GRIDCOLOR 1 +#define SCLAYOUTOPT_PAGEBREAK 2 +#define SCLAYOUTOPT_GUIDE 3 +#define SCLAYOUTOPT_SIMPLECONT 4 +#define SCLAYOUTOPT_LARGECONT 5 +#define SCLAYOUTOPT_COLROWHDR 6 +#define SCLAYOUTOPT_HORISCROLL 7 +#define SCLAYOUTOPT_VERTSCROLL 8 +#define SCLAYOUTOPT_SHEETTAB 9 +#define SCLAYOUTOPT_OUTLINE 10 +#define SCLAYOUTOPT_COUNT 11 + +#define CFGPATH_DISPLAY "Office.Calc/Content/Display" + +#define SCDISPLAYOPT_FORMULA 0 +#define SCDISPLAYOPT_ZEROVALUE 1 +#define SCDISPLAYOPT_NOTETAG 2 +#define SCDISPLAYOPT_VALUEHI 3 +#define SCDISPLAYOPT_ANCHOR 4 +#define SCDISPLAYOPT_TEXTOVER 5 +#define SCDISPLAYOPT_OBJECTGRA 6 +#define SCDISPLAYOPT_CHART 7 +#define SCDISPLAYOPT_DRAWING 8 +#define SCDISPLAYOPT_COUNT 9 + +#define CFGPATH_GRID "Office.Calc/Grid" + +#define SCGRIDOPT_RESOLU_X 0 +#define SCGRIDOPT_RESOLU_Y 1 +#define SCGRIDOPT_SUBDIV_X 2 +#define SCGRIDOPT_SUBDIV_Y 3 +#define SCGRIDOPT_OPTION_X 4 +#define SCGRIDOPT_OPTION_Y 5 +#define SCGRIDOPT_SNAPTOGRID 6 +#define SCGRIDOPT_SYNCHRON 7 +#define SCGRIDOPT_VISIBLE 8 +#define SCGRIDOPT_SIZETOGRID 9 +#define SCGRIDOPT_COUNT 10 + + +Sequence<OUString> ScViewCfg::GetLayoutPropertyNames() +{ + static const char* aPropNames[] = + { + "Line/GridLine", // SCLAYOUTOPT_GRIDLINES + "Line/GridLineColor", // SCLAYOUTOPT_GRIDCOLOR + "Line/PageBreak", // SCLAYOUTOPT_PAGEBREAK + "Line/Guide", // SCLAYOUTOPT_GUIDE + "Line/SimpleControlPoint", // SCLAYOUTOPT_SIMPLECONT + "Line/LargeControlPoint", // SCLAYOUTOPT_LARGECONT + "Window/ColumnRowHeader", // SCLAYOUTOPT_COLROWHDR + "Window/HorizontalScroll", // SCLAYOUTOPT_HORISCROLL + "Window/VerticalScroll", // SCLAYOUTOPT_VERTSCROLL + "Window/SheetTab", // SCLAYOUTOPT_SHEETTAB + "Window/OutlineSymbol" // SCLAYOUTOPT_OUTLINE + }; + Sequence<OUString> aNames(SCLAYOUTOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCLAYOUTOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + return aNames; +} + +Sequence<OUString> ScViewCfg::GetDisplayPropertyNames() +{ + static const char* aPropNames[] = + { + "Formula", // SCDISPLAYOPT_FORMULA + "ZeroValue", // SCDISPLAYOPT_ZEROVALUE + "NoteTag", // SCDISPLAYOPT_NOTETAG + "ValueHighlighting", // SCDISPLAYOPT_VALUEHI + "Anchor", // SCDISPLAYOPT_ANCHOR + "TextOverflow", // SCDISPLAYOPT_TEXTOVER + "ObjectGraphic", // SCDISPLAYOPT_OBJECTGRA + "Chart", // SCDISPLAYOPT_CHART + "DrawingObject" // SCDISPLAYOPT_DRAWING + }; + Sequence<OUString> aNames(SCDISPLAYOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCDISPLAYOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + return aNames; +} + +Sequence<OUString> ScViewCfg::GetGridPropertyNames() +{ + static const char* aPropNames[] = + { + "Resolution/XAxis/NonMetric", // SCGRIDOPT_RESOLU_X + "Resolution/YAxis/NonMetric", // SCGRIDOPT_RESOLU_Y + "Subdivision/XAxis", // SCGRIDOPT_SUBDIV_X + "Subdivision/YAxis", // SCGRIDOPT_SUBDIV_Y + "Option/XAxis/NonMetric", // SCGRIDOPT_OPTION_X + "Option/YAxis/NonMetric", // SCGRIDOPT_OPTION_Y + "Option/SnapToGrid", // SCGRIDOPT_SNAPTOGRID + "Option/Synchronize", // SCGRIDOPT_SYNCHRON + "Option/VisibleGrid", // SCGRIDOPT_VISIBLE + "Option/SizeToGrid" // SCGRIDOPT_SIZETOGRID + }; + Sequence<OUString> aNames(SCGRIDOPT_COUNT); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < SCGRIDOPT_COUNT; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + // adjust for metric system + if (ScOptionsUtil::IsMetricSystem()) + { + pNames[SCGRIDOPT_RESOLU_X] = OUString::createFromAscii( "Resolution/XAxis/Metric" ); + pNames[SCGRIDOPT_RESOLU_Y] = OUString::createFromAscii( "Resolution/YAxis/Metric" ); + pNames[SCGRIDOPT_OPTION_X] = OUString::createFromAscii( "Option/XAxis/Metric" ); + pNames[SCGRIDOPT_OPTION_Y] = OUString::createFromAscii( "Option/YAxis/Metric" ); + } + + return aNames; +} + + +ScViewCfg::ScViewCfg() : + aLayoutItem( OUString::createFromAscii( CFGPATH_LAYOUT ) ), + aDisplayItem( OUString::createFromAscii( CFGPATH_DISPLAY ) ), + aGridItem( OUString::createFromAscii( CFGPATH_GRID ) ) +{ + sal_Int32 nIntVal = 0; + + Sequence<OUString> aNames = GetLayoutPropertyNames(); + Sequence<Any> aValues = aLayoutItem.GetProperties(aNames); + aLayoutItem.EnableNotification(aNames); + const Any* pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCLAYOUTOPT_GRIDCOLOR: + if ( pValues[nProp] >>= nIntVal ) + SetGridColor( Color(nIntVal), EMPTY_STRING ); + break; + case SCLAYOUTOPT_GRIDLINES: + SetOption( VOPT_GRID, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCLAYOUTOPT_PAGEBREAK: + SetOption( VOPT_PAGEBREAKS, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCLAYOUTOPT_GUIDE: + SetOption( VOPT_HELPLINES, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCLAYOUTOPT_SIMPLECONT: + // content is reversed + SetOption( VOPT_SOLIDHANDLES, !ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCLAYOUTOPT_LARGECONT: + SetOption( VOPT_BIGHANDLES, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCLAYOUTOPT_COLROWHDR: + SetOption( VOPT_HEADER, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCLAYOUTOPT_HORISCROLL: + SetOption( VOPT_HSCROLL, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCLAYOUTOPT_VERTSCROLL: + SetOption( VOPT_VSCROLL, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCLAYOUTOPT_SHEETTAB: + SetOption( VOPT_TABCONTROLS, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCLAYOUTOPT_OUTLINE: + SetOption( VOPT_OUTLINER, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + } + } + } + } + aLayoutItem.SetCommitLink( LINK( this, ScViewCfg, LayoutCommitHdl ) ); + + aNames = GetDisplayPropertyNames(); + aValues = aDisplayItem.GetProperties(aNames); + aDisplayItem.EnableNotification(aNames); + pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCDISPLAYOPT_FORMULA: + SetOption( VOPT_FORMULAS, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCDISPLAYOPT_ZEROVALUE: + SetOption( VOPT_NULLVALS, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCDISPLAYOPT_NOTETAG: + SetOption( VOPT_NOTES, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCDISPLAYOPT_VALUEHI: + SetOption( VOPT_SYNTAX, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCDISPLAYOPT_ANCHOR: + SetOption( VOPT_ANCHOR, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCDISPLAYOPT_TEXTOVER: + SetOption( VOPT_CLIPMARKS, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCDISPLAYOPT_OBJECTGRA: + if ( pValues[nProp] >>= nIntVal ) + { + //#i80528# adapt to new range eventually + if((sal_Int32)VOBJ_MODE_HIDE < nIntVal) nIntVal = (sal_Int32)VOBJ_MODE_SHOW; + + SetObjMode( VOBJ_TYPE_OLE, (ScVObjMode)nIntVal); + } + break; + case SCDISPLAYOPT_CHART: + if ( pValues[nProp] >>= nIntVal ) + { + //#i80528# adapt to new range eventually + if((sal_Int32)VOBJ_MODE_HIDE < nIntVal) nIntVal = (sal_Int32)VOBJ_MODE_SHOW; + + SetObjMode( VOBJ_TYPE_CHART, (ScVObjMode)nIntVal); + } + break; + case SCDISPLAYOPT_DRAWING: + if ( pValues[nProp] >>= nIntVal ) + { + //#i80528# adapt to new range eventually + if((sal_Int32)VOBJ_MODE_HIDE < nIntVal) nIntVal = (sal_Int32)VOBJ_MODE_SHOW; + + SetObjMode( VOBJ_TYPE_DRAW, (ScVObjMode)nIntVal); + } + break; + } + } + } + } + aDisplayItem.SetCommitLink( LINK( this, ScViewCfg, DisplayCommitHdl ) ); + + ScGridOptions aGrid = GetGridOptions(); //! initialization necessary? + aNames = GetGridPropertyNames(); + aValues = aGridItem.GetProperties(aNames); + aGridItem.EnableNotification(aNames); + pValues = aValues.getConstArray(); + DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + DBG_ASSERT(pValues[nProp].hasValue(), "property value missing"); + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case SCGRIDOPT_RESOLU_X: + if (pValues[nProp] >>= nIntVal) aGrid.SetFldDrawX( nIntVal ); + break; + case SCGRIDOPT_RESOLU_Y: + if (pValues[nProp] >>= nIntVal) aGrid.SetFldDrawY( nIntVal ); + break; + case SCGRIDOPT_SUBDIV_X: + if (pValues[nProp] >>= nIntVal) aGrid.SetFldDivisionX( nIntVal ); + break; + case SCGRIDOPT_SUBDIV_Y: + if (pValues[nProp] >>= nIntVal) aGrid.SetFldDivisionY( nIntVal ); + break; + case SCGRIDOPT_OPTION_X: + if (pValues[nProp] >>= nIntVal) aGrid.SetFldSnapX( nIntVal ); + break; + case SCGRIDOPT_OPTION_Y: + if (pValues[nProp] >>= nIntVal) aGrid.SetFldSnapY( nIntVal ); + break; + case SCGRIDOPT_SNAPTOGRID: + aGrid.SetUseGridSnap( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCGRIDOPT_SYNCHRON: + aGrid.SetSynchronize( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCGRIDOPT_VISIBLE: + aGrid.SetGridVisible( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + case SCGRIDOPT_SIZETOGRID: + aGrid.SetEqualGrid( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) ); + break; + } + } + } + } + SetGridOptions( aGrid ); + aGridItem.SetCommitLink( LINK( this, ScViewCfg, GridCommitHdl ) ); +} + +IMPL_LINK( ScViewCfg, LayoutCommitHdl, void *, EMPTYARG ) +{ + Sequence<OUString> aNames = GetLayoutPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCLAYOUTOPT_GRIDCOLOR: + pValues[nProp] <<= (sal_Int32) GetGridColor().GetColor(); + break; + case SCLAYOUTOPT_GRIDLINES: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_GRID ) ); + break; + case SCLAYOUTOPT_PAGEBREAK: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_PAGEBREAKS ) ); + break; + case SCLAYOUTOPT_GUIDE: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_HELPLINES ) ); + break; + case SCLAYOUTOPT_SIMPLECONT: + // content is reversed + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], !GetOption( VOPT_SOLIDHANDLES ) ); + break; + case SCLAYOUTOPT_LARGECONT: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_BIGHANDLES ) ); + break; + case SCLAYOUTOPT_COLROWHDR: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_HEADER ) ); + break; + case SCLAYOUTOPT_HORISCROLL: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_HSCROLL ) ); + break; + case SCLAYOUTOPT_VERTSCROLL: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_VSCROLL ) ); + break; + case SCLAYOUTOPT_SHEETTAB: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_TABCONTROLS ) ); + break; + case SCLAYOUTOPT_OUTLINE: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_OUTLINER ) ); + break; + } + } + aLayoutItem.PutProperties(aNames, aValues); + + return 0; +} + +IMPL_LINK( ScViewCfg, DisplayCommitHdl, void *, EMPTYARG ) +{ + Sequence<OUString> aNames = GetDisplayPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCDISPLAYOPT_FORMULA: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_FORMULAS ) ); + break; + case SCDISPLAYOPT_ZEROVALUE: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_NULLVALS ) ); + break; + case SCDISPLAYOPT_NOTETAG: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_NOTES ) ); + break; + case SCDISPLAYOPT_VALUEHI: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_SYNTAX ) ); + break; + case SCDISPLAYOPT_ANCHOR: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_ANCHOR ) ); + break; + case SCDISPLAYOPT_TEXTOVER: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], GetOption( VOPT_CLIPMARKS ) ); + break; + case SCDISPLAYOPT_OBJECTGRA: + pValues[nProp] <<= (sal_Int32) GetObjMode( VOBJ_TYPE_OLE ); + break; + case SCDISPLAYOPT_CHART: + pValues[nProp] <<= (sal_Int32) GetObjMode( VOBJ_TYPE_CHART ); + break; + case SCDISPLAYOPT_DRAWING: + pValues[nProp] <<= (sal_Int32) GetObjMode( VOBJ_TYPE_DRAW ); + break; + } + } + aDisplayItem.PutProperties(aNames, aValues); + + return 0; +} + +IMPL_LINK( ScViewCfg, GridCommitHdl, void *, EMPTYARG ) +{ + const ScGridOptions& rGrid = GetGridOptions(); + + Sequence<OUString> aNames = GetGridPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case SCGRIDOPT_RESOLU_X: + pValues[nProp] <<= (sal_Int32) rGrid.GetFldDrawX(); + break; + case SCGRIDOPT_RESOLU_Y: + pValues[nProp] <<= (sal_Int32) rGrid.GetFldDrawY(); + break; + case SCGRIDOPT_SUBDIV_X: + pValues[nProp] <<= (sal_Int32) rGrid.GetFldDivisionX(); + break; + case SCGRIDOPT_SUBDIV_Y: + pValues[nProp] <<= (sal_Int32) rGrid.GetFldDivisionY(); + break; + case SCGRIDOPT_OPTION_X: + pValues[nProp] <<= (sal_Int32) rGrid.GetFldSnapX(); + break; + case SCGRIDOPT_OPTION_Y: + pValues[nProp] <<= (sal_Int32) rGrid.GetFldSnapY(); + break; + case SCGRIDOPT_SNAPTOGRID: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], rGrid.GetUseGridSnap() ); + break; + case SCGRIDOPT_SYNCHRON: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], rGrid.GetSynchronize() ); + break; + case SCGRIDOPT_VISIBLE: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], rGrid.GetGridVisible() ); + break; + case SCGRIDOPT_SIZETOGRID: + ScUnoHelpFunctions::SetBoolInAny( pValues[nProp], rGrid.GetEqualGrid() ); + break; + } + } + aGridItem.PutProperties(aNames, aValues); + + return 0; +} + +void ScViewCfg::SetOptions( const ScViewOptions& rNew ) +{ + *(ScViewOptions*)this = rNew; + aLayoutItem.SetModified(); + aDisplayItem.SetModified(); + aGridItem.SetModified(); +} + + diff --git a/sc/source/core/tool/zforauto.cxx b/sc/source/core/tool/zforauto.cxx new file mode 100644 index 000000000000..b505e5a5051c --- /dev/null +++ b/sc/source/core/tool/zforauto.cxx @@ -0,0 +1,106 @@ +/************************************************************************* + * + * 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 <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <vcl/svapp.hxx> +#include <tools/debug.hxx> + +#include "zforauto.hxx" +#include "global.hxx" + +static const sal_Char __FAR_DATA pStandardName[] = "Standard"; + +//------------------------------------------------------------------------ + +ScNumFormatAbbrev::ScNumFormatAbbrev() : + sFormatstring ( RTL_CONSTASCII_USTRINGPARAM( pStandardName ) ), + eLnge (LANGUAGE_SYSTEM), + eSysLnge (LANGUAGE_GERMAN) // sonst passt "Standard" nicht +{ +} + +ScNumFormatAbbrev::ScNumFormatAbbrev(const ScNumFormatAbbrev& aFormat) : + sFormatstring (aFormat.sFormatstring), + eLnge (aFormat.eLnge), + eSysLnge (aFormat.eSysLnge) +{ +} + +ScNumFormatAbbrev::ScNumFormatAbbrev(ULONG nFormat, + SvNumberFormatter& rFormatter) +{ + PutFormatIndex(nFormat, rFormatter); +} + +void ScNumFormatAbbrev::Load( SvStream& rStream, CharSet eByteStrSet ) +{ + USHORT nSysLang, nLang; + rStream.ReadByteString( sFormatstring, eByteStrSet ); + rStream >> nSysLang >> nLang; + eLnge = (LanguageType) nLang; + eSysLnge = (LanguageType) nSysLang; + if ( eSysLnge == LANGUAGE_SYSTEM ) // old versions did write it + eSysLnge = Application::GetSettings().GetLanguage(); +} + +void ScNumFormatAbbrev::Save( SvStream& rStream, CharSet eByteStrSet ) const +{ + rStream.WriteByteString( sFormatstring, eByteStrSet ); + rStream << (USHORT) eSysLnge << (USHORT) eLnge; +} + +void ScNumFormatAbbrev::PutFormatIndex(ULONG nFormat, + SvNumberFormatter& rFormatter) +{ + const SvNumberformat* pFormat = rFormatter.GetEntry(nFormat); + if (pFormat) + { + eSysLnge = Application::GetSettings().GetLanguage(); + eLnge = pFormat->GetLanguage(); + sFormatstring = ((SvNumberformat*)pFormat)->GetFormatstring(); + } + else + { + DBG_ERROR("SCNumFormatAbbrev:: unbekanntes Zahlformat"); + eLnge = LANGUAGE_SYSTEM; + eSysLnge = LANGUAGE_GERMAN; // sonst passt "Standard" nicht + sFormatstring.AssignAscii( RTL_CONSTASCII_STRINGPARAM( pStandardName ) ); + } +} + +ULONG ScNumFormatAbbrev::GetFormatIndex( SvNumberFormatter& rFormatter) +{ + short nType; + BOOL bNewInserted; + xub_StrLen nCheckPos; + return rFormatter.GetIndexPuttingAndConverting( sFormatstring, eLnge, + eSysLnge, nType, bNewInserted, nCheckPos); +} |