summaryrefslogtreecommitdiffstats
path: root/formula/source
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /formula/source
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'formula/source')
-rw-r--r--formula/source/core/api/FormulaCompiler.cxx3119
-rw-r--r--formula/source/core/api/FormulaOpCodeMapperObj.cxx104
-rw-r--r--formula/source/core/api/grammar.cxx87
-rw-r--r--formula/source/core/api/token.cxx2033
-rw-r--r--formula/source/core/api/vectortoken.cxx109
-rw-r--r--formula/source/core/resource/core_resource.cxx23
-rw-r--r--formula/source/ui/dlg/ControlHelper.hxx106
-rw-r--r--formula/source/ui/dlg/FormulaHelper.cxx416
-rw-r--r--formula/source/ui/dlg/formula.cxx1961
-rw-r--r--formula/source/ui/dlg/funcpage.cxx267
-rw-r--r--formula/source/ui/dlg/funcpage.hxx93
-rw-r--r--formula/source/ui/dlg/funcutl.cxx486
-rw-r--r--formula/source/ui/dlg/parawin.cxx600
-rw-r--r--formula/source/ui/dlg/parawin.hxx145
-rw-r--r--formula/source/ui/dlg/structpg.cxx154
-rw-r--r--formula/source/ui/dlg/structpg.hxx74
16 files changed, 9777 insertions, 0 deletions
diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx
new file mode 100644
index 0000000000..dc9c73d793
--- /dev/null
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -0,0 +1,3119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <rtl/math.hxx>
+#include <formula/FormulaCompiler.hxx>
+#include <formula/errorcodes.hxx>
+#include <formula/token.hxx>
+#include <formula/tokenarray.hxx>
+#include <o3tl/string_view.hxx>
+#include <core_resource.hxx>
+#include <core_resource.hrc>
+
+#include <svl/zforlist.hxx>
+#include <unotools/charclass.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
+#include <com/sun/star/sheet/FormulaMapGroup.hpp>
+#include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
+#include <algorithm>
+#include <mutex>
+
+namespace formula
+{
+ using namespace ::com::sun::star;
+
+ static const char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
+
+namespace {
+
+class FormulaCompilerRecursionGuard
+{
+ private:
+ short& rRecursion;
+ public:
+ explicit FormulaCompilerRecursionGuard( short& rRec )
+ : rRecursion( rRec ) { ++rRecursion; }
+ ~FormulaCompilerRecursionGuard() { --rRecursion; }
+};
+
+SvNumFormatType lcl_GetRetFormat( OpCode eOpCode )
+{
+ switch (eOpCode)
+ {
+ case ocEqual:
+ case ocNotEqual:
+ case ocLess:
+ case ocGreater:
+ case ocLessEqual:
+ case ocGreaterEqual:
+ case ocAnd:
+ case ocOr:
+ case ocXor:
+ case ocNot:
+ case ocTrue:
+ case ocFalse:
+ case ocIsEmpty:
+ case ocIsString:
+ case ocIsNonString:
+ case ocIsLogical:
+ case ocIsRef:
+ case ocIsValue:
+ case ocIsFormula:
+ case ocIsNA:
+ case ocIsErr:
+ case ocIsError:
+ case ocIsEven:
+ case ocIsOdd:
+ case ocExact:
+ return SvNumFormatType::LOGICAL;
+ case ocGetActDate:
+ case ocGetDate:
+ case ocEasterSunday :
+ return SvNumFormatType::DATE;
+ case ocGetActTime:
+ return SvNumFormatType::DATETIME;
+ case ocGetTime:
+ return SvNumFormatType::TIME;
+ case ocNPV:
+ case ocPV:
+ case ocSYD:
+ case ocDDB:
+ case ocDB:
+ case ocVBD:
+ case ocSLN:
+ case ocPMT:
+ case ocFV:
+ case ocIpmt:
+ case ocPpmt:
+ case ocCumIpmt:
+ case ocCumPrinc:
+ return SvNumFormatType::CURRENCY;
+ case ocRate:
+ case ocIRR:
+ case ocMIRR:
+ case ocRRI:
+ case ocEffect:
+ case ocNominal:
+ case ocPercentSign:
+ return SvNumFormatType::PERCENT;
+ default:
+ return SvNumFormatType::NUMBER;
+ }
+}
+
+void lclPushOpCodeMapEntry( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
+ const OUString* pTable, sal_uInt16 nOpCode )
+{
+ sheet::FormulaOpCodeMapEntry aEntry;
+ aEntry.Token.OpCode = nOpCode;
+ aEntry.Name = pTable[nOpCode];
+ rVec.push_back( aEntry);
+}
+
+void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
+ const OUString* pTable, sal_uInt16 nOpCodeBeg, sal_uInt16 nOpCodeEnd )
+{
+ for (sal_uInt16 nOpCode = nOpCodeBeg; nOpCode < nOpCodeEnd; ++nOpCode)
+ lclPushOpCodeMapEntry( rVec, pTable, nOpCode );
+}
+
+void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
+ const OUString* pTable, const sal_uInt16* pnOpCodes, size_t nCount )
+{
+ for (const sal_uInt16* pnEnd = pnOpCodes + nCount; pnOpCodes < pnEnd; ++pnOpCodes)
+ lclPushOpCodeMapEntry( rVec, pTable, *pnOpCodes );
+}
+
+CharClass* createCharClassIfNonEnglishUI()
+{
+ const LanguageTag& rLanguageTag( Application::GetSettings().GetUILanguageTag());
+ if (rLanguageTag.getLanguage() == "en")
+ return nullptr;
+ return new CharClass( ::comphelper::getProcessComponentContext(), rLanguageTag);
+}
+
+class OpCodeList
+{
+public:
+
+ OpCodeList(const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr&,
+ FormulaCompiler::SeparatorType = FormulaCompiler::SeparatorType::SEMICOLON_BASE );
+ OpCodeList(const std::pair<TranslateId, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr&,
+ FormulaCompiler::SeparatorType = FormulaCompiler::SeparatorType::SEMICOLON_BASE );
+
+private:
+ bool getOpCodeString( OUString& rStr, sal_uInt16 nOp );
+ void putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp, const CharClass* pCharClass );
+
+private:
+ FormulaCompiler::SeparatorType meSepType;
+ const std::pair<const char*, int>* mpSymbols1;
+ const std::pair<TranslateId, int>* mpSymbols2;
+};
+
+OpCodeList::OpCodeList(const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr& xMap,
+ FormulaCompiler::SeparatorType eSepType)
+ : meSepType(eSepType)
+ , mpSymbols1(pSymbols)
+ , mpSymbols2(nullptr)
+{
+ std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
+ const CharClass* pCharClass = xCharClass.get();
+ if (meSepType == FormulaCompiler::SeparatorType::RESOURCE_BASE)
+ {
+ for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
+ {
+ putDefaultOpCode( xMap, i, pCharClass);
+ }
+ }
+ else
+ {
+ for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
+ {
+ OUString aOpStr;
+ if ( getOpCodeString( aOpStr, i) )
+ xMap->putOpCode( aOpStr, OpCode(i), pCharClass);
+ else
+ putDefaultOpCode( xMap, i, pCharClass);
+ }
+ }
+}
+
+OpCodeList::OpCodeList(const std::pair<TranslateId, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr& xMap,
+ FormulaCompiler::SeparatorType eSepType)
+ : meSepType(eSepType)
+ , mpSymbols1(nullptr)
+ , mpSymbols2(pSymbols)
+{
+ std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
+ const CharClass* pCharClass = xCharClass.get();
+ if (meSepType == FormulaCompiler::SeparatorType::RESOURCE_BASE)
+ {
+ for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
+ {
+ putDefaultOpCode( xMap, i, pCharClass);
+ }
+ }
+ else
+ {
+ for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
+ {
+ OUString aOpStr;
+ if ( getOpCodeString( aOpStr, i) )
+ xMap->putOpCode( aOpStr, OpCode(i), pCharClass);
+ else
+ putDefaultOpCode( xMap, i, pCharClass);
+ }
+ }
+}
+
+bool OpCodeList::getOpCodeString( OUString& rStr, sal_uInt16 nOp )
+{
+ switch (nOp)
+ {
+ case SC_OPCODE_SEP:
+ {
+ if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
+ {
+ rStr = ";";
+ return true;
+ }
+ }
+ break;
+ case SC_OPCODE_ARRAY_COL_SEP:
+ {
+ if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
+ {
+ rStr = ";";
+ return true;
+ }
+ }
+ break;
+ case SC_OPCODE_ARRAY_ROW_SEP:
+ {
+ if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
+ {
+ rStr = "|";
+ return true;
+ }
+ }
+ break;
+ }
+
+ return false;
+}
+
+void OpCodeList::putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp,
+ const CharClass* pCharClass )
+{
+ OUString sKey;
+ if (mpSymbols1)
+ {
+ const char* pKey = nullptr;
+ for (const std::pair<const char*, int>* pSymbol = mpSymbols1; pSymbol->first; ++pSymbol)
+ {
+ if (nOp == pSymbol->second)
+ {
+ pKey = pSymbol->first;
+ break;
+ }
+ }
+ if (!pKey)
+ return;
+ sKey = OUString::createFromAscii(pKey);
+ }
+ else if (mpSymbols2)
+ {
+ TranslateId pKey;
+ for (const std::pair<TranslateId, int>* pSymbol = mpSymbols2; pSymbol->first; ++pSymbol)
+ {
+ if (nOp == pSymbol->second)
+ {
+ pKey = pSymbol->first;
+ break;
+ }
+ }
+ if (!pKey)
+ return;
+ sKey = ForResId(pKey);
+ }
+ xMap->putOpCode(sKey, OpCode(nOp), pCharClass);
+}
+
+// static
+const sal_Unicode* lcl_UnicodeStrChr( const sal_Unicode* pStr, sal_Unicode c )
+{
+ if ( !pStr )
+ return nullptr;
+ while ( *pStr )
+ {
+ if ( *pStr == c )
+ return pStr;
+ pStr++;
+ }
+ return nullptr;
+}
+
+struct OpCodeMapData
+{
+ FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap;
+ std::mutex maMtx;
+};
+
+
+bool isPotentialRangeLeftOp( OpCode eOp )
+{
+ switch (eOp)
+ {
+ case ocClose:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool isRangeResultFunction( OpCode eOp )
+{
+ switch (eOp)
+ {
+ case ocIndirect:
+ case ocOffset:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool isRangeResultOpCode( OpCode eOp )
+{
+ switch (eOp)
+ {
+ case ocRange:
+ case ocUnion:
+ case ocIntersect:
+ case ocIndirect:
+ case ocOffset:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ @param pToken
+ MUST be a valid token, caller has to ensure.
+
+ @param bRight
+ If bRPN==false, bRight==false means opcodes for left side are
+ checked, bRight==true means opcodes for right side. If bRPN==true
+ it doesn't matter except for the ocSep converted to ocUnion case.
+ */
+bool isPotentialRangeType( FormulaToken const * pToken, bool bRPN, bool bRight )
+{
+ switch (pToken->GetType())
+ {
+ case svByte: // could be range result, but only a few
+ if (bRPN)
+ return isRangeResultOpCode( pToken->GetOpCode());
+ else if (bRight)
+ return isRangeResultFunction( pToken->GetOpCode());
+ else
+ return isPotentialRangeLeftOp( pToken->GetOpCode());
+ case svSingleRef:
+ case svDoubleRef:
+ case svIndex: // could be range
+ //case svRefList: // um..what?
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svExternalName: // could be range
+ return true;
+ case svSep:
+ // A special case if a previous ocSep was converted to ocUnion it
+ // stays svSep instead of svByte.
+ return bRPN && !bRight && pToken->GetOpCode() == ocUnion;
+ default:
+ // Separators are not part of RPN and right opcodes need to be
+ // other StackVar types or functions and thus svByte.
+ return !bRPN && !bRight && isPotentialRangeLeftOp( pToken->GetOpCode());
+ }
+}
+
+bool isIntersectable( FormulaToken** pCode1, FormulaToken** pCode2 )
+{
+ FormulaToken* pToken1 = *pCode1;
+ FormulaToken* pToken2 = *pCode2;
+ if (pToken1 && pToken2)
+ return isPotentialRangeType( pToken1, true, false) && isPotentialRangeType( pToken2, true, true);
+ return false;
+}
+
+bool isAdjacentRpnEnd( sal_uInt16 nPC,
+ FormulaToken const * const * const pCode,
+ FormulaToken const * const * const pCode1,
+ FormulaToken const * const * const pCode2 )
+{
+ return nPC >= 2 && pCode1 && pCode2 &&
+ (pCode2 - pCode1 == 1) && (pCode - pCode2 == 1) &&
+ (*pCode1 != nullptr) && (*pCode2 != nullptr);
+}
+
+bool isAdjacentOrGapRpnEnd( sal_uInt16 nPC,
+ FormulaToken const * const * const pCode,
+ FormulaToken const * const * const pCode1,
+ FormulaToken const * const * const pCode2 )
+{
+ return nPC >= 2 && pCode1 && pCode2 &&
+ (pCode2 > pCode1) && (pCode - pCode2 == 1) &&
+ (*pCode1 != nullptr) && (*pCode2 != nullptr);
+}
+
+
+} // namespace
+
+
+void FormulaCompiler::OpCodeMap::putExternal( const OUString & rSymbol, const OUString & rAddIn )
+{
+ // Different symbols may map to the same AddIn, but the same AddIn may not
+ // map to different symbols, the first pair wins. Same symbol of course may
+ // not map to different AddIns, again the first pair wins and also the
+ // AddIn->symbol mapping is not inserted in other cases.
+ bool bOk = maExternalHashMap.emplace(rSymbol, rAddIn).second;
+ SAL_WARN_IF( !bOk, "formula.core", "OpCodeMap::putExternal: symbol not inserted, " << rSymbol << " -> " << rAddIn);
+ if (bOk)
+ {
+ bOk = maReverseExternalHashMap.emplace(rAddIn, rSymbol).second;
+ // Failed insertion of the AddIn is ok for different symbols mapping to
+ // the same AddIn. Make this INFO only.
+ SAL_INFO_IF( !bOk, "formula.core", "OpCodeMap::putExternal: AddIn not inserted, " << rAddIn << " -> " << rSymbol);
+ }
+}
+
+void FormulaCompiler::OpCodeMap::putExternalSoftly( const OUString & rSymbol, const OUString & rAddIn )
+{
+ // Same as putExternal() but no warning, instead info whether inserted or not.
+ bool bOk = maExternalHashMap.emplace(rSymbol, rAddIn).second;
+ SAL_INFO( "formula.core", "OpCodeMap::putExternalSoftly: symbol " << (bOk ? "" : "not ") << "inserted, " << rSymbol << " -> " << rAddIn);
+ if (bOk)
+ {
+ bOk = maReverseExternalHashMap.emplace(rAddIn, rSymbol).second;
+ SAL_INFO_IF( !bOk, "formula.core", "OpCodeMap::putExternalSoftly: AddIn not inserted, " << rAddIn << " -> " << rSymbol);
+ }
+}
+
+uno::Sequence< sheet::FormulaToken > FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(
+ const FormulaCompiler& rCompiler, const uno::Sequence< OUString >& rNames ) const
+{
+ const sal_Int32 nLen = rNames.getLength();
+ uno::Sequence< sheet::FormulaToken > aTokens( nLen);
+ sheet::FormulaToken* pToken = aTokens.getArray();
+ OUString const * pName = rNames.getConstArray();
+ OUString const * const pStop = pName + nLen;
+ for ( ; pName < pStop; ++pName, ++pToken)
+ {
+ OpCodeHashMap::const_iterator iLook( maHashMap.find( *pName));
+ if (iLook != maHashMap.end())
+ pToken->OpCode = (*iLook).second;
+ else
+ {
+ OUString aIntName;
+ if (hasExternals())
+ {
+ ExternalHashMap::const_iterator iExt( maExternalHashMap.find( *pName));
+ if (iExt != maExternalHashMap.end())
+ aIntName = (*iExt).second;
+ // Check for existence not needed here, only name-mapping is of
+ // interest.
+ }
+ if (aIntName.isEmpty())
+ aIntName = rCompiler.FindAddInFunction(*pName, !isEnglish()); // bLocalFirst=false for english
+ if (aIntName.isEmpty())
+ pToken->OpCode = getOpCodeUnknown();
+ else
+ {
+ pToken->OpCode = ocExternal;
+ pToken->Data <<= aIntName;
+ }
+ }
+ }
+ return aTokens;
+}
+
+uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(
+ const FormulaCompiler& rCompiler, const sal_Int32 nGroups ) const
+{
+ using namespace sheet;
+
+ // Unfortunately uno::Sequence can't grow without cumbersome reallocs. As
+ // we don't know in advance how many elements it will have we use a
+ // temporary vector to add elements and then copy to Sequence :-(
+ ::std::vector< FormulaOpCodeMapEntry > aVec;
+
+ if (nGroups == FormulaMapGroup::SPECIAL)
+ {
+ // Use specific order, keep in sync with
+ // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
+ static const struct
+ {
+ sal_Int32 nOff;
+ OpCode eOp;
+ } aMap[] = {
+ { FormulaMapGroupSpecialOffset::PUSH , ocPush } ,
+ { FormulaMapGroupSpecialOffset::CALL , ocCall } ,
+ { FormulaMapGroupSpecialOffset::STOP , ocStop } ,
+ { FormulaMapGroupSpecialOffset::EXTERNAL , ocExternal } ,
+ { FormulaMapGroupSpecialOffset::NAME , ocName } ,
+ { FormulaMapGroupSpecialOffset::NO_NAME , ocNoName } ,
+ { FormulaMapGroupSpecialOffset::MISSING , ocMissing } ,
+ { FormulaMapGroupSpecialOffset::BAD , ocBad } ,
+ { FormulaMapGroupSpecialOffset::SPACES , ocSpaces } ,
+ { FormulaMapGroupSpecialOffset::MAT_REF , ocMatRef } ,
+ { FormulaMapGroupSpecialOffset::DB_AREA , ocDBArea } ,
+ /* TODO: { FormulaMapGroupSpecialOffset::TABLE_REF , ocTableRef } , */
+ { FormulaMapGroupSpecialOffset::MACRO , ocMacro } ,
+ { FormulaMapGroupSpecialOffset::COL_ROW_NAME , ocColRowName } ,
+ { FormulaMapGroupSpecialOffset::WHITESPACE , ocWhitespace }
+ };
+ const size_t nCount = SAL_N_ELEMENTS(aMap);
+ // Preallocate vector elements.
+ FormulaOpCodeMapEntry aEntry;
+ aEntry.Token.OpCode = getOpCodeUnknown();
+ aVec.resize(nCount, aEntry);
+
+ for (auto& i : aMap)
+ {
+ size_t nIndex = static_cast< size_t >( i.nOff );
+ if (aVec.size() <= nIndex)
+ {
+ // The offsets really should be aligned with the size, so if
+ // the vector was preallocated above this code to resize it is
+ // just a measure in case the table isn't in sync with the API,
+ // usually it isn't executed.
+ aEntry.Token.OpCode = getOpCodeUnknown();
+ aVec.resize( nIndex + 1, aEntry );
+ }
+ aEntry.Token.OpCode = i.eOp;
+ aVec[nIndex] = aEntry;
+ }
+ }
+ else
+ {
+ /* FIXME: Once we support error constants in formulas we'll need a map
+ * group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill
+ * SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */
+
+ // Anything else but SPECIAL.
+ if ((nGroups & FormulaMapGroup::SEPARATORS) != 0)
+ {
+ static const sal_uInt16 aOpCodes[] = {
+ SC_OPCODE_OPEN,
+ SC_OPCODE_CLOSE,
+ SC_OPCODE_SEP,
+ };
+ lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
+ }
+ if ((nGroups & FormulaMapGroup::ARRAY_SEPARATORS) != 0)
+ {
+ static const sal_uInt16 aOpCodes[] = {
+ SC_OPCODE_ARRAY_OPEN,
+ SC_OPCODE_ARRAY_CLOSE,
+ SC_OPCODE_ARRAY_ROW_SEP,
+ SC_OPCODE_ARRAY_COL_SEP
+ };
+ lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
+ }
+ if ((nGroups & FormulaMapGroup::UNARY_OPERATORS) != 0)
+ {
+ // Due to the nature of the percent operator following its operand
+ // it isn't sorted into unary operators for compiler interna.
+ lclPushOpCodeMapEntry( aVec, mpTable.get(), ocPercentSign );
+ // "+" can be used as unary operator too, push only if binary group is not set
+ if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) == 0)
+ lclPushOpCodeMapEntry( aVec, mpTable.get(), ocAdd );
+ // regular unary operators
+ for (sal_uInt16 nOp = SC_OPCODE_START_UN_OP; nOp < SC_OPCODE_STOP_UN_OP && nOp < mnSymbols; ++nOp)
+ {
+ lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
+ }
+ }
+ if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) != 0)
+ {
+ for (sal_uInt16 nOp = SC_OPCODE_START_BIN_OP; nOp < SC_OPCODE_STOP_BIN_OP && nOp < mnSymbols; ++nOp)
+ {
+ switch (nOp)
+ {
+ // AND and OR in fact are functions but for legacy reasons
+ // are sorted into binary operators for compiler interna.
+ case SC_OPCODE_AND :
+ case SC_OPCODE_OR :
+ break; // nothing,
+ default:
+ lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
+ }
+ }
+ }
+ if ((nGroups & FormulaMapGroup::FUNCTIONS) != 0)
+ {
+ // Function names are not consecutive, skip the gaps between
+ // functions with no parameter, functions with 1 parameter
+ lclPushOpCodeMapEntries( aVec, mpTable.get(), SC_OPCODE_START_NO_PAR,
+ ::std::min< sal_uInt16 >( SC_OPCODE_STOP_NO_PAR, mnSymbols ) );
+ lclPushOpCodeMapEntries( aVec, mpTable.get(), SC_OPCODE_START_1_PAR,
+ ::std::min< sal_uInt16 >( SC_OPCODE_STOP_1_PAR, mnSymbols ) );
+ // Additional functions not within range of functions.
+ static const sal_uInt16 aOpCodes[] = {
+ SC_OPCODE_IF,
+ SC_OPCODE_IF_ERROR,
+ SC_OPCODE_IF_NA,
+ SC_OPCODE_CHOOSE,
+ SC_OPCODE_AND,
+ SC_OPCODE_OR
+ };
+ lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
+ // functions with 2 or more parameters.
+ for (sal_uInt16 nOp = SC_OPCODE_START_2_PAR; nOp < SC_OPCODE_STOP_2_PAR && nOp < mnSymbols; ++nOp)
+ {
+ switch (nOp)
+ {
+ // NO_NAME is in SPECIAL.
+ case SC_OPCODE_NO_NAME :
+ break; // nothing,
+ default:
+ lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
+ }
+ }
+ // If AddIn functions are present in this mapping, use them, and only those.
+ if (hasExternals())
+ {
+ for (auto const& elem : maExternalHashMap)
+ {
+ FormulaOpCodeMapEntry aEntry;
+ aEntry.Name = elem.first;
+ aEntry.Token.Data <<= elem.second;
+ aEntry.Token.OpCode = ocExternal;
+ aVec.push_back( aEntry);
+ }
+ }
+ else
+ {
+ rCompiler.fillAddInToken( aVec, isEnglish());
+ }
+ }
+ }
+ return uno::Sequence< FormulaOpCodeMapEntry >(aVec.data(), aVec.size());
+}
+
+
+void FormulaCompiler::OpCodeMap::putOpCode( const OUString & rStr, const OpCode eOp, const CharClass* pCharClass )
+{
+ if (0 < eOp && sal_uInt16(eOp) < mnSymbols)
+ {
+ bool bPutOp = mpTable[eOp].isEmpty();
+ bool bRemoveFromMap = false;
+ if (!bPutOp)
+ {
+ switch (eOp)
+ {
+ // These OpCodes are meant to overwrite and also remove an
+ // existing mapping.
+ case ocCurrency:
+ bPutOp = true;
+ bRemoveFromMap = true;
+ break;
+ // These separator OpCodes are meant to overwrite and also
+ // remove an existing mapping if it is not used for one of the
+ // other separators.
+ case ocArrayColSep:
+ bPutOp = true;
+ bRemoveFromMap = (mpTable[ocArrayRowSep] != mpTable[eOp] && mpTable[ocSep] != mpTable[eOp]);
+ break;
+ case ocArrayRowSep:
+ bPutOp = true;
+ bRemoveFromMap = (mpTable[ocArrayColSep] != mpTable[eOp] && mpTable[ocSep] != mpTable[eOp]);
+ break;
+ // For ocSep keep the ";" in map but remove any other if it is
+ // not used for ocArrayColSep or ocArrayRowSep.
+ case ocSep:
+ bPutOp = true;
+ bRemoveFromMap = (mpTable[eOp] != ";" &&
+ mpTable[ocArrayColSep] != mpTable[eOp] &&
+ mpTable[ocArrayRowSep] != mpTable[eOp]);
+ break;
+ // These OpCodes are known to be duplicates in the Excel
+ // external API mapping because of different parameter counts
+ // in different BIFF versions. Names are identical and entries
+ // are ignored.
+ case ocLinest:
+ case ocTrend:
+ case ocLogest:
+ case ocGrowth:
+ case ocTrunc:
+ case ocFixed:
+ case ocGetDayOfWeek:
+ case ocHLookup:
+ case ocVLookup:
+ case ocGetDiffDate360:
+ if (rStr == mpTable[eOp])
+ return;
+ [[fallthrough]];
+ // These OpCodes are known to be added to an existing mapping,
+ // but only for the OOXML external API mapping. This is *not*
+ // FormulaLanguage::OOXML. Keep the first
+ // (correct) definition for the OpCode, all following are
+ // additional alias entries in the map.
+ case ocErrorType:
+ case ocMultiArea:
+ case ocBackSolver:
+ case ocEasterSunday:
+ case ocCurrent:
+ case ocStyle:
+ if (mbEnglish &&
+ FormulaGrammar::extractFormulaLanguage( meGrammar) == FormulaGrammar::GRAM_EXTERNAL)
+ {
+ // Both bPutOp and bRemoveFromMap stay false.
+ break;
+ }
+ [[fallthrough]];
+ default:
+ SAL_WARN("formula.core",
+ "OpCodeMap::putOpCode: reusing OpCode " << static_cast<sal_uInt16>(eOp)
+ << ", replacing '" << mpTable[eOp] << "' with '" << rStr << "' in "
+ << (mbEnglish ? "" : "non-") << "English map 0x" << ::std::hex << meGrammar);
+ }
+ }
+
+ // Case preserving opcode -> string, upper string -> opcode
+ if (bRemoveFromMap)
+ {
+ OUString aUpper( pCharClass ? pCharClass->uppercase( mpTable[eOp]) : rStr.toAsciiUpperCase());
+ // Ensure we remove a mapping only for the requested OpCode.
+ OpCodeHashMap::const_iterator it( maHashMap.find( aUpper));
+ if (it != maHashMap.end() && (*it).second == eOp)
+ maHashMap.erase( it);
+ }
+ if (bPutOp)
+ mpTable[eOp] = rStr;
+ OUString aUpper( pCharClass ? pCharClass->uppercase( rStr) : rStr.toAsciiUpperCase());
+ maHashMap.emplace(aUpper, eOp);
+ }
+ else
+ {
+ SAL_WARN( "formula.core", "OpCodeMap::putOpCode: OpCode out of range");
+ }
+}
+
+
+FormulaCompiler::FormulaCompiler( FormulaTokenArray& rArr, bool bComputeII, bool bMatrixFlag )
+ :
+ nCurrentFactorParam(0),
+ pArr( &rArr ),
+ maArrIterator( rArr ),
+ pCode( nullptr ),
+ pStack( nullptr ),
+ eLastOp( ocPush ),
+ nRecursion( 0 ),
+ nNumFmt( SvNumFormatType::UNDEFINED ),
+ pc( 0 ),
+ meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
+ bAutoCorrect( false ),
+ bCorrected( false ),
+ glSubTotal( false ),
+ needsRPNTokenCheck( false ),
+ mbJumpCommandReorder(true),
+ mbStopOnError(true),
+ mbComputeII(bComputeII),
+ mbMatrixFlag(bMatrixFlag)
+{
+}
+
+FormulaTokenArray FormulaCompiler::smDummyTokenArray;
+
+FormulaCompiler::FormulaCompiler(bool bComputeII, bool bMatrixFlag)
+ :
+ nCurrentFactorParam(0),
+ pArr( nullptr ),
+ maArrIterator( smDummyTokenArray ),
+ pCode( nullptr ),
+ pStack( nullptr ),
+ eLastOp( ocPush ),
+ nRecursion(0),
+ nNumFmt( SvNumFormatType::UNDEFINED ),
+ pc( 0 ),
+ meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
+ bAutoCorrect( false ),
+ bCorrected( false ),
+ glSubTotal( false ),
+ needsRPNTokenCheck( false ),
+ mbJumpCommandReorder(true),
+ mbStopOnError(true),
+ mbComputeII(bComputeII),
+ mbMatrixFlag(bMatrixFlag)
+{
+}
+
+FormulaCompiler::~FormulaCompiler()
+{
+}
+
+FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage ) const
+{
+ const bool bTemporary = !HasOpCodeMap(nLanguage);
+ OpCodeMapPtr xMap = GetFinalOpCodeMap(nLanguage);
+ if (bTemporary)
+ const_cast<FormulaCompiler*>(this)->DestroyOpCodeMap(nLanguage);
+ return xMap;
+}
+
+FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetFinalOpCodeMap( const sal_Int32 nLanguage ) const
+{
+ FormulaCompiler::OpCodeMapPtr xMap;
+ using namespace sheet;
+ switch (nLanguage)
+ {
+ case FormulaLanguage::ODFF :
+ if (!mxSymbolsODFF)
+ InitSymbolsODFF( InitSymbols::INIT);
+ xMap = mxSymbolsODFF;
+ break;
+ case FormulaLanguage::ODF_11 :
+ if (!mxSymbolsPODF)
+ InitSymbolsPODF( InitSymbols::INIT);
+ xMap = mxSymbolsPODF;
+ break;
+ case FormulaLanguage::ENGLISH :
+ if (!mxSymbolsEnglish)
+ InitSymbolsEnglish( InitSymbols::INIT);
+ xMap = mxSymbolsEnglish;
+ break;
+ case FormulaLanguage::NATIVE :
+ if (!mxSymbolsNative)
+ InitSymbolsNative( InitSymbols::INIT);
+ xMap = mxSymbolsNative;
+ break;
+ case FormulaLanguage::XL_ENGLISH:
+ if (!mxSymbolsEnglishXL)
+ InitSymbolsEnglishXL( InitSymbols::INIT);
+ xMap = mxSymbolsEnglishXL;
+ break;
+ case FormulaLanguage::OOXML:
+ if (!mxSymbolsOOXML)
+ InitSymbolsOOXML( InitSymbols::INIT);
+ xMap = mxSymbolsOOXML;
+ break;
+ case FormulaLanguage::API :
+ if (!mxSymbolsAPI)
+ InitSymbolsAPI( InitSymbols::INIT);
+ xMap = mxSymbolsAPI;
+ break;
+ default:
+ ; // nothing, NULL map returned
+ }
+ return xMap;
+}
+
+void FormulaCompiler::DestroyOpCodeMap( const sal_Int32 nLanguage )
+{
+ using namespace sheet;
+ switch (nLanguage)
+ {
+ case FormulaLanguage::ODFF :
+ InitSymbolsODFF( InitSymbols::DESTROY);
+ break;
+ case FormulaLanguage::ODF_11 :
+ InitSymbolsPODF( InitSymbols::DESTROY);
+ break;
+ case FormulaLanguage::ENGLISH :
+ InitSymbolsEnglish( InitSymbols::DESTROY);
+ break;
+ case FormulaLanguage::NATIVE :
+ InitSymbolsNative( InitSymbols::DESTROY);
+ break;
+ case FormulaLanguage::XL_ENGLISH:
+ InitSymbolsEnglishXL( InitSymbols::DESTROY);
+ break;
+ case FormulaLanguage::OOXML:
+ InitSymbolsOOXML( InitSymbols::DESTROY);
+ break;
+ case FormulaLanguage::API :
+ InitSymbolsAPI( InitSymbols::DESTROY);
+ break;
+ default:
+ ; // nothing
+ }
+}
+
+bool FormulaCompiler::HasOpCodeMap( const sal_Int32 nLanguage ) const
+{
+ using namespace sheet;
+ switch (nLanguage)
+ {
+ case FormulaLanguage::ODFF :
+ return InitSymbolsODFF( InitSymbols::ASK);
+ case FormulaLanguage::ODF_11 :
+ return InitSymbolsPODF( InitSymbols::ASK);
+ case FormulaLanguage::ENGLISH :
+ return InitSymbolsEnglish( InitSymbols::ASK);
+ case FormulaLanguage::NATIVE :
+ return InitSymbolsNative( InitSymbols::ASK);
+ case FormulaLanguage::XL_ENGLISH:
+ return InitSymbolsEnglishXL( InitSymbols::ASK);
+ case FormulaLanguage::OOXML:
+ return InitSymbolsOOXML( InitSymbols::ASK);
+ case FormulaLanguage::API :
+ return InitSymbolsAPI( InitSymbols::ASK);
+ default:
+ ; // nothing
+ }
+ return false;
+}
+
+OUString FormulaCompiler::FindAddInFunction( const OUString& /*rUpperName*/, bool /*bLocalFirst*/ ) const
+{
+ return OUString();
+}
+
+FormulaCompiler::OpCodeMapPtr FormulaCompiler::CreateOpCodeMap(
+ const uno::Sequence<
+ const sheet::FormulaOpCodeMapEntry > & rMapping,
+ bool bEnglish )
+{
+ using sheet::FormulaOpCodeMapEntry;
+ // Filter / API maps are never Core
+ NonConstOpCodeMapPtr xMap = std::make_shared<OpCodeMap>( SC_OPCODE_LAST_OPCODE_ID + 1, false,
+ FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(
+ FormulaGrammar::GRAM_EXTERNAL, bEnglish), FormulaGrammar::CONV_UNSPECIFIED));
+ std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
+ const CharClass* pCharClass = xCharClass.get();
+ for (auto const& rMapEntry : rMapping)
+ {
+ OpCode eOp = OpCode(rMapEntry.Token.OpCode);
+ if (eOp != ocExternal)
+ xMap->putOpCode( rMapEntry.Name, eOp, pCharClass);
+ else
+ {
+ OUString aExternalName;
+ if (rMapEntry.Token.Data >>= aExternalName)
+ xMap->putExternal( rMapEntry.Name, aExternalName);
+ else
+ {
+ SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
+ }
+ }
+ }
+ return xMap;
+}
+
+static bool lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr& xMap, FormulaCompiler::InitSymbols eWhat = FormulaCompiler::InitSymbols::INIT )
+{
+ static OpCodeMapData aSymbolMap;
+ std::unique_lock aGuard(aSymbolMap.maMtx);
+
+ if (eWhat == FormulaCompiler::InitSymbols::ASK)
+ {
+ return bool(aSymbolMap.mxSymbolMap);
+ }
+ else if (eWhat == FormulaCompiler::InitSymbols::DESTROY)
+ {
+ aSymbolMap.mxSymbolMap.reset();
+ }
+ else if (!aSymbolMap.mxSymbolMap)
+ {
+ // Core
+ aSymbolMap.mxSymbolMap =
+ std::make_shared<FormulaCompiler::OpCodeMap>(
+ SC_OPCODE_LAST_OPCODE_ID + 1, true, FormulaGrammar::GRAM_NATIVE_UI);
+ OpCodeList aOpCodeListSymbols(RID_STRLIST_FUNCTION_NAMES_SYMBOLS, aSymbolMap.mxSymbolMap);
+ OpCodeList aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES, aSymbolMap.mxSymbolMap);
+ // No AddInMap for native core mapping.
+ }
+
+ xMap = aSymbolMap.mxSymbolMap;
+
+ return true;
+}
+
+const OUString& FormulaCompiler::GetNativeSymbol( OpCode eOp )
+{
+ NonConstOpCodeMapPtr xSymbolsNative;
+ lcl_fillNativeSymbols( xSymbolsNative);
+ return xSymbolsNative->getSymbol( eOp );
+}
+
+sal_Unicode FormulaCompiler::GetNativeSymbolChar( OpCode eOp )
+{
+ return GetNativeSymbol(eOp)[0];
+}
+
+bool FormulaCompiler::InitSymbolsNative( FormulaCompiler::InitSymbols eWhat ) const
+{
+ return lcl_fillNativeSymbols( mxSymbolsNative, eWhat);
+}
+
+bool FormulaCompiler::InitSymbolsEnglish( FormulaCompiler::InitSymbols eWhat ) const
+{
+ static OpCodeMapData aMap;
+ std::unique_lock aGuard(aMap.maMtx);
+ if (eWhat == InitSymbols::ASK)
+ return bool(aMap.mxSymbolMap);
+ else if (eWhat == InitSymbols::DESTROY)
+ aMap.mxSymbolMap.reset();
+ else if (!aMap.mxSymbolMap)
+ loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
+ mxSymbolsEnglish = aMap.mxSymbolMap;
+ return true;
+}
+
+bool FormulaCompiler::InitSymbolsPODF( FormulaCompiler::InitSymbols eWhat ) const
+{
+ static OpCodeMapData aMap;
+ std::unique_lock aGuard(aMap.maMtx);
+ if (eWhat == InitSymbols::ASK)
+ return bool(aMap.mxSymbolMap);
+ else if (eWhat == InitSymbols::DESTROY)
+ aMap.mxSymbolMap.reset();
+ else if (!aMap.mxSymbolMap)
+ loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF, FormulaGrammar::GRAM_PODF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
+ mxSymbolsPODF = aMap.mxSymbolMap;
+ return true;
+}
+
+bool FormulaCompiler::InitSymbolsAPI( FormulaCompiler::InitSymbols eWhat ) const
+{
+ static OpCodeMapData aMap;
+ std::unique_lock aGuard(aMap.maMtx);
+ if (eWhat == InitSymbols::ASK)
+ return bool(aMap.mxSymbolMap);
+ else if (eWhat == InitSymbols::DESTROY)
+ aMap.mxSymbolMap.reset();
+ else if (!aMap.mxSymbolMap)
+ loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_API, FormulaGrammar::GRAM_API, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
+ mxSymbolsAPI = aMap.mxSymbolMap;
+ return true;
+}
+
+bool FormulaCompiler::InitSymbolsODFF( FormulaCompiler::InitSymbols eWhat ) const
+{
+ static OpCodeMapData aMap;
+ std::unique_lock aGuard(aMap.maMtx);
+ if (eWhat == InitSymbols::ASK)
+ return bool(aMap.mxSymbolMap);
+ else if (eWhat == InitSymbols::DESTROY)
+ aMap.mxSymbolMap.reset();
+ else if (!aMap.mxSymbolMap)
+ loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF, FormulaGrammar::GRAM_ODFF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
+ mxSymbolsODFF = aMap.mxSymbolMap;
+ return true;
+}
+
+bool FormulaCompiler::InitSymbolsEnglishXL( FormulaCompiler::InitSymbols eWhat ) const
+{
+ static OpCodeMapData aMap;
+ std::unique_lock aGuard(aMap.maMtx);
+ if (eWhat == InitSymbols::ASK)
+ return bool(aMap.mxSymbolMap);
+ else if (eWhat == InitSymbols::DESTROY)
+ aMap.mxSymbolMap.reset();
+ else if (!aMap.mxSymbolMap)
+ loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
+ mxSymbolsEnglishXL = aMap.mxSymbolMap;
+ if (eWhat != InitSymbols::INIT)
+ return true;
+
+ // TODO: For now, just replace the separators to the Excel English
+ // variants. Later, if we want to properly map Excel functions with Calc
+ // functions, we'll need to do a little more work here.
+ mxSymbolsEnglishXL->putOpCode( OUString(','), ocSep, nullptr);
+ mxSymbolsEnglishXL->putOpCode( OUString(','), ocArrayColSep, nullptr);
+ mxSymbolsEnglishXL->putOpCode( OUString(';'), ocArrayRowSep, nullptr);
+
+ return true;
+}
+
+bool FormulaCompiler::InitSymbolsOOXML( FormulaCompiler::InitSymbols eWhat ) const
+{
+ static OpCodeMapData aMap;
+ std::unique_lock aGuard(aMap.maMtx);
+ if (eWhat == InitSymbols::ASK)
+ return bool(aMap.mxSymbolMap);
+ else if (eWhat == InitSymbols::DESTROY)
+ aMap.mxSymbolMap.reset();
+ else if (!aMap.mxSymbolMap)
+ loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML, FormulaGrammar::GRAM_OOXML, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
+ mxSymbolsOOXML = aMap.mxSymbolMap;
+ return true;
+}
+
+
+void FormulaCompiler::loadSymbols(const std::pair<const char*, int>* pSymbols, FormulaGrammar::Grammar eGrammar,
+ NonConstOpCodeMapPtr& rxMap, SeparatorType eSepType) const
+{
+ if ( rxMap )
+ return;
+
+ // not Core
+ rxMap = std::make_shared<OpCodeMap>( SC_OPCODE_LAST_OPCODE_ID + 1, eGrammar != FormulaGrammar::GRAM_ODFF, eGrammar );
+ OpCodeList aOpCodeList(pSymbols, rxMap, eSepType);
+
+ fillFromAddInMap( rxMap, eGrammar);
+ // Fill from collection for AddIns not already present.
+ if (FormulaGrammar::GRAM_ENGLISH == eGrammar)
+ fillFromAddInCollectionEnglishName( rxMap);
+ else
+ {
+ fillFromAddInCollectionUpperName( rxMap);
+ if (FormulaGrammar::GRAM_API == eGrammar)
+ {
+ // Add known but not in AddInMap English names, e.g. from the
+ // PricingFunctions AddIn or any user supplied AddIn.
+ fillFromAddInCollectionEnglishName( rxMap);
+ }
+ }
+}
+
+void FormulaCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr& /*xMap */) const
+{
+}
+
+void FormulaCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr& /*xMap */) const
+{
+}
+
+void FormulaCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr& /*xMap*/, FormulaGrammar::Grammar /*_eGrammar */) const
+{
+}
+
+OpCode FormulaCompiler::GetEnglishOpCode( const OUString& rName ) const
+{
+ FormulaCompiler::OpCodeMapPtr xMap = GetOpCodeMap( sheet::FormulaLanguage::ENGLISH);
+
+ formula::OpCodeHashMap::const_iterator iLook( xMap->getHashMap().find( rName ) );
+ bool bFound = (iLook != xMap->getHashMap().end());
+ return bFound ? (*iLook).second : ocNone;
+}
+
+bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp )
+{
+ bool bRet = false;
+ switch (eOp)
+ {
+ // no parameters:
+ case ocRandom:
+ case ocGetActDate:
+ case ocGetActTime:
+ // one parameter:
+ case ocFormula:
+ case ocInfo:
+ // more than one parameters:
+ // ocIndirect otherwise would have to do
+ // StopListening and StartListening on a reference for every
+ // interpreted value.
+ case ocIndirect:
+ // ocOffset results in indirect references.
+ case ocOffset:
+ // ocDebugVar shows internal value that may change as the internal state changes.
+ case ocDebugVar:
+ bRet = true;
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+ return bRet;
+}
+
+bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp )
+{
+ switch (eOp)
+ {
+ case ocIf:
+ case ocIfError:
+ case ocIfNA:
+ case ocChoose:
+ return true;
+ default:
+ ;
+ }
+ return false;
+}
+
+// Remove quotes, escaped quotes are unescaped.
+bool FormulaCompiler::DeQuote( OUString& rStr )
+{
+ sal_Int32 nLen = rStr.getLength();
+ if ( nLen > 1 && rStr[0] == '\'' && rStr[ nLen-1 ] == '\'' )
+ {
+ rStr = rStr.copy( 1, nLen-2 );
+ rStr = rStr.replaceAll( "''", "'" );
+ return true;
+ }
+ return false;
+}
+
+void FormulaCompiler::fillAddInToken(
+ ::std::vector< sheet::FormulaOpCodeMapEntry >& /*_rVec*/,
+ bool /*_bIsEnglish*/) const
+{
+}
+
+bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode )
+{
+ switch (eOpCode)
+ {
+ case ocDde :
+ case ocGrowth :
+ case ocTrend :
+ case ocLogest :
+ case ocLinest :
+ case ocFrequency :
+ case ocMatTrans :
+ case ocMatMult :
+ case ocMatInv :
+ case ocMatrixUnit :
+ case ocModalValue_Multi :
+ case ocFourier :
+ return true;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ return false;
+}
+
+
+void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString& rSymbol, OpCode eOp )
+{
+ SAL_WARN_IF( !mpTable[eOp].isEmpty() && rSymbol.isEmpty(), "formula.core",
+ "OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast<sal_uInt16>(eOp)
+ << " '" << mpTable[eOp] << "' with empty name!");
+ if (!mpTable[eOp].isEmpty() && rSymbol.isEmpty())
+ maHashMap.emplace(mpTable[eOp], eOp);
+ else
+ {
+ mpTable[eOp] = rSymbol;
+ maHashMap.emplace(rSymbol, eOp);
+ }
+}
+
+void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap& r )
+{
+ maHashMap = OpCodeHashMap( mnSymbols);
+
+ sal_uInt16 n = r.getSymbolCount();
+ SAL_WARN_IF( n != mnSymbols, "formula.core",
+ "OpCodeMap::copyFrom: unequal size, this: " << mnSymbols << " that: " << n);
+ if (n > mnSymbols)
+ n = mnSymbols;
+
+ // OpCode 0 (ocPush) should never be in a map.
+ SAL_WARN_IF( !mpTable[0].isEmpty() || !r.mpTable[0].isEmpty(), "formula.core",
+ "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
+ << mpTable[0] << "' that: '" << r.mpTable[0] << "'");
+
+ // For bOverrideKnownBad when copying from the English core map (ODF 1.1
+ // and API) to the native map (UI "use English function names") replace the
+ // known bad legacy function names with correct ones.
+ if (r.mbCore &&
+ FormulaGrammar::extractFormulaLanguage( meGrammar) == sheet::FormulaLanguage::NATIVE &&
+ FormulaGrammar::extractFormulaLanguage( r.meGrammar) == sheet::FormulaLanguage::ENGLISH)
+ {
+ for (sal_uInt16 i = 1; i < n; ++i)
+ {
+ OUString aSymbol;
+ OpCode eOp = OpCode(i);
+ switch (eOp)
+ {
+ case ocRRI:
+ aSymbol = "RRI";
+ break;
+ case ocTableOp:
+ aSymbol = "MULTIPLE.OPERATIONS";
+ break;
+ default:
+ aSymbol = r.mpTable[i];
+ }
+ putCopyOpCode( aSymbol, eOp);
+ }
+ }
+ else
+ {
+ for (sal_uInt16 i = 1; i < n; ++i)
+ {
+ OpCode eOp = OpCode(i);
+ const OUString& rSymbol = r.mpTable[i];
+ putCopyOpCode( rSymbol, eOp);
+ }
+ }
+
+ // This was meant to copy to native map that does not have AddIn symbols
+ // but needs them from the source map. It is unclear what should happen if
+ // the destination already had externals, so do it only if it doesn't.
+ if (!hasExternals())
+ {
+ maExternalHashMap = r.maExternalHashMap;
+ maReverseExternalHashMap = r.maReverseExternalHashMap;
+ mbCore = r.mbCore;
+ if (mbEnglish != r.mbEnglish)
+ {
+ // For now keep mbEnglishLocale setting, which is false for a
+ // non-English native map we're copying to.
+ /* TODO:
+ if (!mbEnglish && r.mbEnglish)
+ mbEnglishLocale = "getUseEnglishLocaleFromConfiguration()";
+ or set from outside i.e. via ScCompiler.
+ */
+ mbEnglish = r.mbEnglish;
+ }
+ }
+}
+
+
+FormulaError FormulaCompiler::GetErrorConstant( const OUString& rName ) const
+{
+ FormulaError nError = FormulaError::NONE;
+ OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
+ if (iLook != mxSymbols->getHashMap().end())
+ {
+ switch ((*iLook).second)
+ {
+ // Not all may make sense in a formula, but these we know as
+ // opcodes.
+ case ocErrNull:
+ nError = FormulaError::NoCode;
+ break;
+ case ocErrDivZero:
+ nError = FormulaError::DivisionByZero;
+ break;
+ case ocErrValue:
+ nError = FormulaError::NoValue;
+ break;
+ case ocErrRef:
+ nError = FormulaError::NoRef;
+ break;
+ case ocErrName:
+ nError = FormulaError::NoName;
+ break;
+ case ocErrNum:
+ nError = FormulaError::IllegalFPOperation;
+ break;
+ case ocErrNA:
+ nError = FormulaError::NotAvailable;
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ else
+ {
+ // Per convention recognize detailed "#ERRxxx!" constants, always
+ // untranslated. Error numbers are sal_uInt16 so at most 5 decimal
+ // digits.
+ if (rName.startsWithIgnoreAsciiCase("#ERR") && rName.getLength() <= 10 && rName[rName.getLength()-1] == '!')
+ {
+ sal_uInt32 nErr = o3tl::toUInt32(rName.subView( 4, rName.getLength() - 5));
+ if (0 < nErr && nErr <= SAL_MAX_UINT16 && isPublishedFormulaError(static_cast<FormulaError>(nErr)))
+ nError = static_cast<FormulaError>(nErr);
+ }
+ }
+ return nError;
+}
+
+void FormulaCompiler::EnableJumpCommandReorder( bool bEnable )
+{
+ mbJumpCommandReorder = bEnable;
+}
+
+void FormulaCompiler::EnableStopOnError( bool bEnable )
+{
+ mbStopOnError = bEnable;
+}
+
+void FormulaCompiler::AppendErrorConstant( OUStringBuffer& rBuffer, FormulaError nError ) const
+{
+ OpCode eOp;
+ switch (nError)
+ {
+ case FormulaError::NoCode:
+ eOp = ocErrNull;
+ break;
+ case FormulaError::DivisionByZero:
+ eOp = ocErrDivZero;
+ break;
+ case FormulaError::NoValue:
+ eOp = ocErrValue;
+ break;
+ case FormulaError::NoRef:
+ eOp = ocErrRef;
+ break;
+ case FormulaError::NoName:
+ eOp = ocErrName;
+ break;
+ case FormulaError::IllegalFPOperation:
+ eOp = ocErrNum;
+ break;
+ case FormulaError::NotAvailable:
+ eOp = ocErrNA;
+ break;
+ default:
+ {
+ // Per convention create detailed "#ERRxxx!" constants, always
+ // untranslated.
+ rBuffer.append("#ERR");
+ rBuffer.append(static_cast<sal_Int32>(nError));
+ rBuffer.append('!');
+ return;
+ }
+ }
+ rBuffer.append( mxSymbols->getSymbol( eOp));
+}
+
+constexpr short nRecursionMax = 100;
+
+bool FormulaCompiler::GetToken()
+{
+ FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
+ if ( nRecursion > nRecursionMax )
+ {
+ SetError( FormulaError::StackOverflow );
+ mpLastToken = mpToken = new FormulaByteToken( ocStop );
+ return false;
+ }
+ if ( bAutoCorrect && !pStack )
+ { // don't merge stacked subroutine code into entered formula
+ aCorrectedFormula += aCorrectedSymbol;
+ aCorrectedSymbol.clear();
+ }
+ bool bStop = false;
+ if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
+ bStop = true;
+ else
+ {
+ FormulaTokenRef pSpacesToken;
+ short nWasColRowName;
+ if ( pArr->OpCodeBefore( maArrIterator.GetIndex() ) == ocColRowName )
+ nWasColRowName = 1;
+ else
+ nWasColRowName = 0;
+ OpCode eTmpOp;
+ mpToken = maArrIterator.Next();
+ while (mpToken && ((eTmpOp = mpToken->GetOpCode()) == ocSpaces || eTmpOp == ocWhitespace))
+ {
+ if (eTmpOp == ocSpaces)
+ {
+ // For significant whitespace remember last ocSpaces token.
+ // Usually there's only one even for multiple spaces.
+ pSpacesToken = mpToken;
+ if ( nWasColRowName )
+ nWasColRowName++;
+ }
+ if ( bAutoCorrect && !pStack )
+ CreateStringFromToken( aCorrectedFormula, mpToken.get() );
+ mpToken = maArrIterator.Next();
+ }
+ if ( bAutoCorrect && !pStack && mpToken )
+ CreateStringFromToken( aCorrectedSymbol, mpToken.get() );
+ if( !mpToken )
+ {
+ if( pStack )
+ {
+ PopTokenArray();
+ // mpLastToken was popped as well and corresponds to the
+ // then current last token during PushTokenArray(), e.g. for
+ // HandleRange().
+ return GetToken();
+ }
+ else
+ bStop = true;
+ }
+ else
+ {
+ if ( nWasColRowName >= 2 && mpToken->GetOpCode() == ocColRowName )
+ { // convert an ocSpaces to ocIntersect in RPN
+ mpLastToken = mpToken = new FormulaByteToken( ocIntersect );
+ maArrIterator.StepBack(); // we advanced to the second ocColRowName, step back
+ }
+ else if (pSpacesToken && FormulaGrammar::isExcelSyntax( meGrammar) &&
+ mpLastToken && mpToken &&
+ isPotentialRangeType( mpLastToken.get(), false, false) &&
+ isPotentialRangeType( mpToken.get(), false, true))
+ {
+ // Let IntersectionLine() <- Factor() decide how to treat this,
+ // once the actual arguments are determined in RPN.
+ mpLastToken = mpToken = pSpacesToken;
+ maArrIterator.StepBack(); // step back from next non-spaces token
+ return true;
+ }
+ }
+ }
+ if( bStop )
+ {
+ mpLastToken = mpToken = new FormulaByteToken( ocStop );
+ return false;
+ }
+
+ // Remember token for next round and any PushTokenArray() calls that may
+ // occur in handlers.
+ mpLastToken = mpToken;
+
+ if ( mpToken->IsExternalRef() )
+ {
+ return HandleExternalReference(*mpToken);
+ }
+ else
+ {
+ switch (mpToken->GetOpCode())
+ {
+ case ocSubTotal:
+ case ocAggregate:
+ glSubTotal = true;
+ break;
+ case ocName:
+ if( HandleRange())
+ {
+ // Expanding ocName might have introduced tokens such as ocStyle that prevent formula threading,
+ // but those wouldn't be present in the raw tokens array, so ensure RPN tokens will be checked too.
+ needsRPNTokenCheck = true;
+ return true;
+ }
+ return false;
+ case ocColRowName:
+ return HandleColRowName();
+ case ocDBArea:
+ return HandleDbData();
+ case ocTableRef:
+ return HandleTableRef();
+ case ocPush:
+ if( mbComputeII )
+ HandleIIOpCode(mpToken.get(), nullptr, 0);
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ return true;
+}
+
+
+// RPN creation by recursion
+void FormulaCompiler::Factor()
+{
+ if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
+ return;
+
+ CurrentFactor pFacToken( this );
+
+ OpCode eOp = mpToken->GetOpCode();
+ if (eOp == ocPush || eOp == ocColRowNameAuto || eOp == ocMatRef || eOp == ocDBArea
+ || eOp == ocTableRef
+ || (!mbJumpCommandReorder && ((eOp == ocName) || (eOp == ocColRowName) || (eOp == ocBad)))
+ )
+ {
+ PutCode( mpToken );
+ eOp = NextToken();
+ if( eOp == ocOpen )
+ {
+ // PUSH( is an error that may be caused by an unknown function.
+ SetError(
+ ( mpToken->GetType() == svString
+ || mpToken->GetType() == svSingleRef )
+ ? FormulaError::NoName : FormulaError::OperatorExpected );
+ if ( bAutoCorrect && !pStack )
+ { // assume multiplication
+ aCorrectedFormula += mxSymbols->getSymbol( ocMul);
+ bCorrected = true;
+ NextToken();
+ eOp = Expression();
+ if( eOp != ocClose )
+ SetError( FormulaError::PairExpected);
+ else
+ NextToken();
+ }
+ }
+ }
+ else if( eOp == ocOpen )
+ {
+ NextToken();
+ eOp = Expression();
+ while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
+ { // range list (A1;A2) converted to (A1~A2)
+ pFacToken = mpToken;
+ NextToken();
+ CheckSetForceArrayParameter( mpToken, 0);
+ eOp = Expression();
+ // Do not ignore error here, regardless of mbStopOnError, to not
+ // change the formula expression in case of an unexpected state.
+ if (pArr->GetCodeError() == FormulaError::NONE && pc >= 2)
+ {
+ // Left and right operands must be reference or function
+ // returning reference to form a range list.
+ const FormulaToken* p = pCode[-2];
+ if (p && isPotentialRangeType( p, true, false))
+ {
+ p = pCode[-1];
+ if (p && isPotentialRangeType( p, true, true))
+ {
+ pFacToken->NewOpCode( ocUnion, FormulaToken::PrivateAccess());
+ // XXX NOTE: the token's eType is still svSep here!
+ PutCode( pFacToken);
+ }
+ }
+ }
+ }
+ if (eOp != ocClose)
+ SetError( FormulaError::PairExpected);
+ else
+ NextToken();
+
+ /* TODO: if no conversion to ocUnion is involved this could collect
+ * such expression as a list or (matrix) vector to be passed as
+ * argument for one parameter (which in fact the ocUnion svRefList is a
+ * special case of), which would require a new StackVar type and needed
+ * to be handled by the interpreter for functions that could support it
+ * (i.e. already handle VAR_ARGS or svRefList parameters). This is also
+ * not defined by ODF.
+ * Does Excel handle =SUM((1;2))?
+ * As is, the interpreter catches extraneous uncalculated
+ * subexpressions like 1 of (1;2) as error. */
+ }
+ else
+ {
+ if( nNumFmt == SvNumFormatType::UNDEFINED )
+ nNumFmt = lcl_GetRetFormat( eOp );
+
+ if ( IsOpCodeVolatile( eOp) )
+ pArr->SetExclusiveRecalcModeAlways();
+ else
+ {
+ switch( eOp )
+ {
+ // Functions recalculated on every document load.
+ // ONLOAD_LENIENT here to be able to distinguish and not
+ // force a recalc (if not in an ALWAYS or ONLOAD_MUST
+ // context) but keep an imported result from for example
+ // OOXML a DDE call. Will be recalculated for ODFF.
+ case ocConvertOOo :
+ case ocDde:
+ case ocMacro:
+ case ocWebservice:
+ pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
+ break;
+ // RANDBETWEEN() is volatile like RAND(). Other Add-In
+ // functions may have to be recalculated or not, we don't
+ // know, classify as ONLOAD_LENIENT.
+ case ocExternal:
+ if (mpToken->GetExternal() == "com.sun.star.sheet.addin.Analysis.getRandbetween")
+ pArr->SetExclusiveRecalcModeAlways();
+ else
+ pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
+ break;
+ // If the referred cell is moved the value changes.
+ case ocColumn :
+ case ocRow :
+ pArr->SetRecalcModeOnRefMove();
+ break;
+ // ocCell needs recalc on move for some possible type values.
+ // And recalc mode on load, tdf#60645
+ case ocCell :
+ pArr->SetRecalcModeOnRefMove();
+ pArr->AddRecalcMode( ScRecalcMode::ONLOAD_MUST );
+ break;
+ case ocHyperLink :
+ // Cell with hyperlink needs to be calculated on load to
+ // get its matrix result generated.
+ pArr->AddRecalcMode( ScRecalcMode::ONLOAD_MUST );
+ pArr->SetHyperLink( true);
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)
+ {
+ pFacToken = mpToken;
+ eOp = NextToken();
+ if (eOp != ocOpen)
+ {
+ SetError( FormulaError::PairExpected);
+ PutCode( pFacToken );
+ }
+ else
+ {
+ eOp = NextToken();
+ if (eOp != ocClose)
+ SetError( FormulaError::PairExpected);
+ PutCode( pFacToken);
+ NextToken();
+ }
+ }
+ else if (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR)
+ {
+ if (eOp == ocIsoWeeknum && FormulaGrammar::isODFF( meGrammar ))
+ {
+ // tdf#50950 ocIsoWeeknum can have 2 arguments when saved by older versions of Calc;
+ // the opcode then has to be changed to ocWeek for backward compatibility
+ pFacToken = mpToken;
+ eOp = NextToken();
+ bool bNoParam = false;
+ if (eOp == ocOpen)
+ {
+ eOp = NextToken();
+ if (eOp == ocClose)
+ bNoParam = true;
+ else
+ {
+ CheckSetForceArrayParameter( mpToken, 0);
+ eOp = Expression();
+ }
+ }
+ else
+ SetError( FormulaError::PairExpected);
+ sal_uInt32 nSepCount = 0;
+ const sal_uInt16 nSepPos = maArrIterator.GetIndex() - 1; // separator position, if any
+ if( !bNoParam )
+ {
+ nSepCount++;
+ while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
+ {
+ NextToken();
+ CheckSetForceArrayParameter( mpToken, nSepCount);
+ nSepCount++;
+ if (nSepCount > FORMULA_MAXPARAMS)
+ SetError( FormulaError::CodeOverflow);
+ eOp = Expression();
+ }
+ }
+ if (eOp != ocClose)
+ SetError( FormulaError::PairExpected);
+ else
+ NextToken();
+ pFacToken->SetByte( nSepCount );
+ if (nSepCount == 2)
+ {
+ // An old mode!=1 indicates ISO week, remove argument if
+ // literal double value and keep function. Anything else
+ // can not be resolved, there exists no "like ISO but week
+ // starts on Sunday" mode in WEEKNUM and for an expression
+ // we can't determine.
+ // Current index is nSepPos+3 if expression stops, or
+ // nSepPos+4 if expression continues after the call because
+ // we just called NextToken() to move away from it.
+ if (pc >= 2 && (maArrIterator.GetIndex() == nSepPos + 3 || maArrIterator.GetIndex() == nSepPos + 4) &&
+ pArr->TokenAt(nSepPos+1)->GetType() == svDouble &&
+ pArr->TokenAt(nSepPos+1)->GetDouble() != 1.0 &&
+ pArr->TokenAt(nSepPos+2)->GetOpCode() == ocClose &&
+ pArr->RemoveToken( nSepPos, 2) == 2)
+ {
+ maArrIterator.AfterRemoveToken( nSepPos, 2);
+ // Remove the ocPush/svDouble just removed also from
+ // the compiler local RPN array.
+ --pCode; --pc;
+ (*pCode)->DecRef(); // may be dead now
+ pFacToken->SetByte( nSepCount - 1 );
+ }
+ else
+ {
+ // For the remaining two arguments cases use the
+ // compatibility function.
+ pFacToken->NewOpCode( ocWeeknumOOo, FormulaToken::PrivateAccess());
+ }
+ }
+ PutCode( pFacToken );
+ }
+ else
+ {
+ // standard handling of 1-parameter opcodes
+ pFacToken = mpToken;
+ eOp = NextToken();
+ if( nNumFmt == SvNumFormatType::UNDEFINED && eOp == ocNot )
+ nNumFmt = SvNumFormatType::LOGICAL;
+ if (eOp == ocOpen)
+ {
+ NextToken();
+ CheckSetForceArrayParameter( mpToken, 0);
+ eOp = Expression();
+ }
+ else
+ SetError( FormulaError::PairExpected);
+ if (eOp != ocClose)
+ SetError( FormulaError::PairExpected);
+ else if ( pArr->GetCodeError() == FormulaError::NONE )
+ {
+ pFacToken->SetByte( 1 );
+ if (mbComputeII)
+ {
+ FormulaToken** pArg = pCode - 1;
+ HandleIIOpCode(pFacToken, &pArg, 1);
+ }
+ }
+ PutCode( pFacToken );
+ NextToken();
+ }
+ }
+ else if ((SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR)
+ || eOp == ocExternal
+ || eOp == ocMacro
+ || eOp == ocAnd
+ || eOp == ocOr
+ || eOp == ocBad
+ || ( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
+ || (!mbJumpCommandReorder && IsOpCodeJumpCommand(eOp)))
+ {
+ pFacToken = mpToken;
+ OpCode eMyLastOp = eOp;
+ eOp = NextToken();
+ bool bNoParam = false;
+ bool bBadName = false;
+ if (eOp == ocOpen)
+ {
+ eOp = NextToken();
+ if (eOp == ocClose)
+ bNoParam = true;
+ else
+ {
+ CheckSetForceArrayParameter( mpToken, 0);
+ eOp = Expression();
+ }
+ }
+ else if (eMyLastOp == ocBad)
+ {
+ // Just a bad name, not an unknown function, no parameters, no
+ // closing expected.
+ bBadName = true;
+ bNoParam = true;
+ }
+ else
+ SetError( FormulaError::PairExpected);
+ sal_uInt32 nSepCount = 0;
+ if( !bNoParam )
+ {
+ bool bDoIICompute = mbComputeII;
+ // Array of FormulaToken double pointers to collect the parameters of II opcodes.
+ FormulaToken*** pArgArray = nullptr;
+ if (bDoIICompute)
+ {
+ pArgArray = static_cast<FormulaToken***>(alloca(sizeof(FormulaToken**)*FORMULA_MAXPARAMSII));
+ if (!pArgArray)
+ bDoIICompute = false;
+ }
+
+ nSepCount++;
+
+ if (bDoIICompute)
+ pArgArray[nSepCount-1] = pCode - 1; // Add first argument
+
+ while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
+ {
+ NextToken();
+ CheckSetForceArrayParameter( mpToken, nSepCount);
+ nSepCount++;
+ if (nSepCount > FORMULA_MAXPARAMS)
+ SetError( FormulaError::CodeOverflow);
+ eOp = Expression();
+ if (bDoIICompute && nSepCount <= FORMULA_MAXPARAMSII)
+ pArgArray[nSepCount - 1] = pCode - 1; // Add rest of the arguments
+ }
+ if (bDoIICompute)
+ HandleIIOpCode(pFacToken, pArgArray,
+ std::min(nSepCount, static_cast<sal_uInt32>(FORMULA_MAXPARAMSII)));
+ }
+ bool bDone = false;
+ if (bBadName)
+ ; // nothing, keep current token for return
+ else if (eOp != ocClose)
+ SetError( FormulaError::PairExpected);
+ else
+ {
+ NextToken();
+ bDone = true;
+ }
+ // Jumps are just normal functions for the FunctionAutoPilot tree view
+ if (!mbJumpCommandReorder && pFacToken->GetType() == svJump)
+ pFacToken = new FormulaFAPToken( pFacToken->GetOpCode(), nSepCount, pFacToken );
+ else
+ pFacToken->SetByte( nSepCount );
+ PutCode( pFacToken );
+
+ if (bDone)
+ AnnotateOperands();
+ }
+ else if (IsOpCodeJumpCommand(eOp))
+ {
+ // the PC counters are -1
+ pFacToken = mpToken;
+ switch (eOp)
+ {
+ case ocIf:
+ pFacToken->GetJump()[ 0 ] = 3; // if, else, behind
+ break;
+ case ocChoose:
+ pFacToken->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
+ break;
+ case ocIfError:
+ case ocIfNA:
+ pFacToken->GetJump()[ 0 ] = 2; // if, behind
+ break;
+ default:
+ SAL_WARN("formula.core","Jump OpCode: " << +eOp);
+ assert(!"FormulaCompiler::Factor: someone forgot to add a jump count case");
+ }
+ eOp = NextToken();
+ if (eOp == ocOpen)
+ {
+ NextToken();
+ CheckSetForceArrayParameter( mpToken, 0);
+ eOp = Expression();
+ }
+ else
+ SetError( FormulaError::PairExpected);
+ PutCode( pFacToken );
+ // During AutoCorrect (since pArr->GetCodeError() is
+ // ignored) an unlimited ocIf would crash because
+ // ScRawToken::Clone() allocates the JumpBuffer according to
+ // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
+ short nJumpMax;
+ OpCode eFacOpCode = pFacToken->GetOpCode();
+ switch (eFacOpCode)
+ {
+ case ocIf:
+ nJumpMax = 3;
+ break;
+ case ocChoose:
+ nJumpMax = FORMULA_MAXJUMPCOUNT;
+ break;
+ case ocIfError:
+ case ocIfNA:
+ nJumpMax = 2;
+ break;
+ case ocStop:
+ // May happen only if PutCode(pFacToken) ran into overflow.
+ nJumpMax = 0;
+ assert(pc == FORMULA_MAXTOKENS && pArr->GetCodeError() != FormulaError::NONE);
+ break;
+ default:
+ nJumpMax = 0;
+ SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode);
+ assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case");
+ }
+ short nJumpCount = 0;
+ while ( (nJumpCount < (FORMULA_MAXJUMPCOUNT - 1)) && (eOp == ocSep)
+ && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
+ {
+ if ( ++nJumpCount <= nJumpMax )
+ pFacToken->GetJump()[nJumpCount] = pc-1;
+ NextToken();
+ CheckSetForceArrayParameter( mpToken, nJumpCount - 1);
+ eOp = Expression();
+ // ocSep or ocClose terminate the subexpression
+ PutCode( mpToken );
+ }
+ if (eOp != ocClose)
+ SetError( FormulaError::PairExpected);
+ else
+ {
+ NextToken();
+ // always limit to nJumpMax, no arbitrary overwrites
+ if ( ++nJumpCount <= nJumpMax )
+ pFacToken->GetJump()[ nJumpCount ] = pc-1;
+ eFacOpCode = pFacToken->GetOpCode();
+ bool bLimitOk;
+ switch (eFacOpCode)
+ {
+ case ocIf:
+ bLimitOk = (nJumpCount <= 3);
+ break;
+ case ocChoose:
+ bLimitOk = (nJumpCount < FORMULA_MAXJUMPCOUNT);
+ break;
+ case ocIfError:
+ case ocIfNA:
+ bLimitOk = (nJumpCount <= 2);
+ break;
+ case ocStop:
+ // May happen only if PutCode(pFacToken) ran into overflow.
+ // This may had resulted from a stacked token array and
+ // error wasn't propagated so assert only the program
+ // counter.
+ bLimitOk = false;
+ assert(pc == FORMULA_MAXTOKENS);
+ break;
+ default:
+ bLimitOk = false;
+ SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode);
+ assert(!"FormulaCompiler::Factor: someone forgot to add a jump limit case");
+ }
+ if (bLimitOk)
+ pFacToken->GetJump()[ 0 ] = nJumpCount;
+ else
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else if ( eOp == ocMissing )
+ {
+ PutCode( mpToken );
+ NextToken();
+ }
+ else if ( eOp == ocClose )
+ {
+ SetError( FormulaError::ParameterExpected );
+ }
+ else if ( eOp == ocSep )
+ { // Subsequent ocSep
+ SetError( FormulaError::ParameterExpected );
+ if ( bAutoCorrect && !pStack )
+ {
+ aCorrectedSymbol.clear();
+ bCorrected = true;
+ }
+ }
+ else if ( mpToken->IsExternalRef() )
+ {
+ PutCode( mpToken);
+ NextToken();
+ }
+ else
+ {
+ SetError( FormulaError::UnknownToken );
+ if ( bAutoCorrect && !pStack )
+ {
+ if ( eOp == ocStop )
+ { // trailing operator w/o operand
+ sal_Int32 nLen = aCorrectedFormula.getLength();
+ if ( nLen )
+ aCorrectedFormula = aCorrectedFormula.copy( 0, nLen - 1 );
+ aCorrectedSymbol.clear();
+ bCorrected = true;
+ }
+ }
+ }
+ }
+}
+
+void FormulaCompiler::RangeLine()
+{
+ Factor();
+ while (mpToken->GetOpCode() == ocRange)
+ {
+ FormulaToken** pCode1 = pCode - 1;
+ FormulaTokenRef p = mpToken;
+ NextToken();
+ Factor();
+ FormulaToken** pCode2 = pCode - 1;
+ if (!MergeRangeReference( pCode1, pCode2))
+ PutCode(p);
+ }
+}
+
+void FormulaCompiler::IntersectionLine()
+{
+ RangeLine();
+ while (mpToken->GetOpCode() == ocIntersect || mpToken->GetOpCode() == ocSpaces)
+ {
+ sal_uInt16 nCodeIndex = maArrIterator.GetIndex() - 1;
+ FormulaToken** pCode1 = pCode - 1;
+ FormulaTokenRef p = mpToken;
+ NextToken();
+ RangeLine();
+ FormulaToken** pCode2 = pCode - 1;
+ if (p->GetOpCode() == ocSpaces)
+ {
+ // Convert to intersection if both left and right are references or
+ // functions (potentially returning references, if not then a space
+ // or no space would be a syntax error anyway), not other operators
+ // or operands. Else discard.
+ if (isAdjacentOrGapRpnEnd( pc, pCode, pCode1, pCode2) && isIntersectable( pCode1, pCode2))
+ {
+ FormulaTokenRef pIntersect( new FormulaByteToken( ocIntersect));
+ // Replace ocSpaces with ocIntersect so that when switching
+ // formula syntax the correct operator string is created.
+ // coverity[freed_arg : FALSE] - FormulaTokenRef has a ref so ReplaceToken won't delete pIntersect
+ pArr->ReplaceToken( nCodeIndex, pIntersect.get(), FormulaTokenArray::ReplaceMode::CODE_ONLY);
+ PutCode( pIntersect);
+ }
+ }
+ else
+ {
+ PutCode(p);
+ }
+ }
+}
+
+void FormulaCompiler::UnionLine()
+{
+ IntersectionLine();
+ while (mpToken->GetOpCode() == ocUnion)
+ {
+ FormulaTokenRef p = mpToken;
+ NextToken();
+ IntersectionLine();
+ PutCode(p);
+ }
+}
+
+void FormulaCompiler::UnaryLine()
+{
+ if( mpToken->GetOpCode() == ocAdd )
+ GetToken();
+ else if (SC_OPCODE_START_UN_OP <= mpToken->GetOpCode() &&
+ mpToken->GetOpCode() < SC_OPCODE_STOP_UN_OP)
+ {
+ FormulaTokenRef p = mpToken;
+ NextToken();
+ UnaryLine();
+ if (mbComputeII)
+ {
+ FormulaToken** pArg = pCode - 1;
+ HandleIIOpCode(p.get(), &pArg, 1);
+ }
+ PutCode( p );
+ }
+ else
+ UnionLine();
+}
+
+void FormulaCompiler::PostOpLine()
+{
+ UnaryLine();
+ while ( mpToken->GetOpCode() == ocPercentSign )
+ { // this operator _follows_ its operand
+ if (mbComputeII)
+ {
+ FormulaToken** pArg = pCode - 1;
+ HandleIIOpCode(mpToken.get(), &pArg, 1);
+ }
+ PutCode( mpToken );
+ NextToken();
+ }
+}
+
+void FormulaCompiler::PowLine()
+{
+ PostOpLine();
+ while (mpToken->GetOpCode() == ocPow)
+ {
+ FormulaTokenRef p = mpToken;
+ FormulaToken** pArgArray[2];
+ if (mbComputeII)
+ pArgArray[0] = pCode - 1; // Add first argument
+ NextToken();
+ PostOpLine();
+ if (mbComputeII)
+ {
+ pArgArray[1] = pCode - 1; // Add second argument
+ HandleIIOpCode(p.get(), pArgArray, 2);
+ }
+ PutCode(p);
+ }
+}
+
+void FormulaCompiler::MulDivLine()
+{
+ PowLine();
+ while (mpToken->GetOpCode() == ocMul || mpToken->GetOpCode() == ocDiv)
+ {
+ FormulaTokenRef p = mpToken;
+ FormulaToken** pArgArray[2];
+ if (mbComputeII)
+ pArgArray[0] = pCode - 1; // Add first argument
+ NextToken();
+ PowLine();
+ if (mbComputeII)
+ {
+ pArgArray[1] = pCode - 1; // Add second argument
+ HandleIIOpCode(p.get(), pArgArray, 2);
+ }
+ PutCode(p);
+ }
+}
+
+void FormulaCompiler::AddSubLine()
+{
+ MulDivLine();
+ while (mpToken->GetOpCode() == ocAdd || mpToken->GetOpCode() == ocSub)
+ {
+ FormulaTokenRef p = mpToken;
+ FormulaToken** pArgArray[2];
+ if (mbComputeII)
+ pArgArray[0] = pCode - 1; // Add first argument
+ NextToken();
+ MulDivLine();
+ if (mbComputeII)
+ {
+ pArgArray[1] = pCode - 1; // Add second argument
+ HandleIIOpCode(p.get(), pArgArray, 2);
+ }
+ PutCode(p);
+ }
+}
+
+void FormulaCompiler::ConcatLine()
+{
+ AddSubLine();
+ while (mpToken->GetOpCode() == ocAmpersand)
+ {
+ FormulaTokenRef p = mpToken;
+ FormulaToken** pArgArray[2];
+ if (mbComputeII)
+ pArgArray[0] = pCode - 1; // Add first argument
+ NextToken();
+ AddSubLine();
+ if (mbComputeII)
+ {
+ pArgArray[1] = pCode - 1; // Add second argument
+ HandleIIOpCode(p.get(), pArgArray, 2);
+ }
+ PutCode(p);
+ }
+}
+
+void FormulaCompiler::CompareLine()
+{
+ ConcatLine();
+ while (mpToken->GetOpCode() >= ocEqual && mpToken->GetOpCode() <= ocGreaterEqual)
+ {
+ FormulaTokenRef p = mpToken;
+ FormulaToken** pArgArray[2];
+ if (mbComputeII)
+ pArgArray[0] = pCode - 1; // Add first argument
+ NextToken();
+ ConcatLine();
+ if (mbComputeII)
+ {
+ pArgArray[1] = pCode - 1; // Add second argument
+ HandleIIOpCode(p.get(), pArgArray, 2);
+ }
+ PutCode(p);
+ }
+}
+
+OpCode FormulaCompiler::Expression()
+{
+ FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
+ if ( nRecursion > nRecursionMax )
+ {
+ SetError( FormulaError::StackOverflow );
+ return ocStop; //! generate token instead?
+ }
+ CompareLine();
+ while (mpToken->GetOpCode() == ocAnd || mpToken->GetOpCode() == ocOr)
+ {
+ FormulaTokenRef p = mpToken;
+ mpToken->SetByte( 2 ); // 2 parameters!
+ FormulaToken** pArgArray[2];
+ if (mbComputeII)
+ pArgArray[0] = pCode - 1; // Add first argument
+ NextToken();
+ CompareLine();
+ if (mbComputeII)
+ {
+ pArgArray[1] = pCode - 1; // Add second argument
+ HandleIIOpCode(p.get(), pArgArray, 2);
+ }
+ PutCode(p);
+ }
+ return mpToken->GetOpCode();
+}
+
+
+void FormulaCompiler::SetError( FormulaError /*nError*/ )
+{
+}
+
+FormulaTokenRef FormulaCompiler::ExtendRangeReference( FormulaToken & /*rTok1*/, FormulaToken & /*rTok2*/ )
+{
+ return FormulaTokenRef();
+}
+
+bool FormulaCompiler::MergeRangeReference( FormulaToken * * const pCode1, FormulaToken * const * const pCode2 )
+{
+ if (!isAdjacentRpnEnd( pc, pCode, pCode1, pCode2))
+ return false;
+
+ FormulaToken *p1 = *pCode1, *p2 = *pCode2;
+ FormulaTokenRef p = ExtendRangeReference( *p1, *p2);
+ if (!p)
+ return false;
+
+ p->IncRef();
+ p1->DecRef();
+ p2->DecRef();
+ *pCode1 = p.get();
+ --pCode;
+ --pc;
+
+ return true;
+}
+
+bool FormulaCompiler::CompileTokenArray()
+{
+ glSubTotal = false;
+ bCorrected = false;
+ needsRPNTokenCheck = false;
+ if (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError)
+ {
+ if ( bAutoCorrect )
+ {
+ aCorrectedFormula.clear();
+ aCorrectedSymbol.clear();
+ }
+ pArr->DelRPN();
+ maArrIterator.Reset();
+ pStack = nullptr;
+ FormulaToken* pDataArray[ FORMULA_MAXTOKENS + 1 ];
+ // Code in some places refers to the last token as 'pCode - 1', which may
+ // point before the first element if the expression is bad. So insert a dummy
+ // node in that place which will make that token be nullptr.
+ pDataArray[ 0 ] = nullptr;
+ FormulaToken** pData = pDataArray + 1;
+ pCode = pData;
+ bool bWasForced = pArr->IsRecalcModeForced();
+ if ( bWasForced && bAutoCorrect )
+ aCorrectedFormula = "=";
+ pArr->ClearRecalcMode();
+ maArrIterator.Reset();
+ eLastOp = ocOpen;
+ pc = 0;
+ NextToken();
+ OpCode eOp = Expression();
+ // Some trailing garbage that doesn't form an expression?
+ if (eOp != ocStop)
+ SetError( FormulaError::OperatorExpected);
+ PostProcessCode();
+
+ FormulaError nErrorBeforePop = pArr->GetCodeError();
+
+ while( pStack )
+ PopTokenArray();
+ if( pc )
+ {
+ pArr->CreateNewRPNArrayFromData( pData, pc );
+ if( needsRPNTokenCheck )
+ pArr->CheckAllRPNTokens();
+ }
+
+ // once an error, always an error
+ if( pArr->GetCodeError() == FormulaError::NONE && nErrorBeforePop != FormulaError::NONE )
+ pArr->SetCodeError( nErrorBeforePop);
+
+ if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
+ {
+ pArr->DelRPN();
+ maArrIterator.Reset();
+ pArr->SetHyperLink( false);
+ }
+
+ if ( bWasForced )
+ pArr->SetRecalcModeForced();
+ }
+ if( nNumFmt == SvNumFormatType::UNDEFINED )
+ nNumFmt = SvNumFormatType::NUMBER;
+ return glSubTotal;
+}
+
+void FormulaCompiler::PopTokenArray()
+{
+ if( !pStack )
+ return;
+
+ FormulaArrayStack* p = pStack;
+ pStack = p->pNext;
+ // obtain special RecalcMode from SharedFormula
+ if ( pArr->IsRecalcModeAlways() )
+ p->pArr->SetExclusiveRecalcModeAlways();
+ else if ( !pArr->IsRecalcModeNormal() && p->pArr->IsRecalcModeNormal() )
+ p->pArr->SetMaskedRecalcMode( pArr->GetRecalcMode() );
+ p->pArr->SetCombinedBitsRecalcMode( pArr->GetRecalcMode() );
+ if ( pArr->IsHyperLink() ) // fdo 87534
+ p->pArr->SetHyperLink( true );
+ if( p->bTemp )
+ delete pArr;
+ pArr = p->pArr;
+ maArrIterator = FormulaTokenArrayPlainIterator(*pArr);
+ maArrIterator.Jump(p->nIndex);
+ mpLastToken = p->mpLastToken;
+ delete p;
+}
+
+void FormulaCompiler::CreateStringFromTokenArray( OUString& rFormula )
+{
+ OUStringBuffer aBuffer( pArr->GetLen() * 5 );
+ CreateStringFromTokenArray( aBuffer );
+ rFormula = aBuffer.makeStringAndClear();
+}
+
+void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer& rBuffer )
+{
+ rBuffer.setLength(0);
+ if( !pArr->GetLen() )
+ return;
+
+ FormulaTokenArray* pSaveArr = pArr;
+ int nSaveIndex = maArrIterator.GetIndex();
+ bool bODFF = FormulaGrammar::isODFF( meGrammar);
+ if (bODFF || FormulaGrammar::isPODF( meGrammar) )
+ {
+ // Scan token array for missing args and re-write if present.
+ MissingConventionODF aConv( bODFF);
+ if (pArr->NeedsPodfRewrite( aConv))
+ {
+ pArr = pArr->RewriteMissing( aConv );
+ maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
+ }
+ }
+ else if ( FormulaGrammar::isOOXML( meGrammar ) )
+ {
+ // Scan token array for missing args and rewrite if present.
+ if (pArr->NeedsOoxmlRewrite())
+ {
+ MissingConventionOOXML aConv;
+ pArr = pArr->RewriteMissing( aConv );
+ maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
+ }
+ }
+
+ // At least one character per token, plus some are references, some are
+ // function names, some are numbers, ...
+ rBuffer.ensureCapacity( pArr->GetLen() * 5 );
+
+ if ( pArr->IsRecalcModeForced() )
+ rBuffer.append( '=');
+ const FormulaToken* t = maArrIterator.First();
+ while( t )
+ t = CreateStringFromToken( rBuffer, t, true );
+
+ if (pSaveArr != pArr)
+ {
+ delete pArr;
+ pArr = pSaveArr;
+ maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
+ maArrIterator.Jump(nSaveIndex);
+ }
+}
+
+const FormulaToken* FormulaCompiler::CreateStringFromToken( OUString& rFormula, const FormulaToken* pTokenP )
+{
+ OUStringBuffer aBuffer;
+ const FormulaToken* p = CreateStringFromToken( aBuffer, pTokenP );
+ rFormula += aBuffer;
+ return p;
+}
+
+const FormulaToken* FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuffer, const FormulaToken* pTokenP,
+ bool bAllowArrAdvance )
+{
+ bool bNext = true;
+ bool bSpaces = false;
+ const FormulaToken* t = pTokenP;
+ OpCode eOp = t->GetOpCode();
+ if( eOp >= ocAnd && eOp <= ocOr )
+ {
+ // AND, OR infix?
+ if ( bAllowArrAdvance )
+ t = maArrIterator.Next();
+ else
+ t = maArrIterator.PeekNext();
+ bNext = false;
+ bSpaces = ( !t || t->GetOpCode() != ocOpen );
+ }
+ if( bSpaces )
+ rBuffer.append( ' ');
+
+ if (eOp == ocSpaces || eOp == ocWhitespace)
+ {
+ bool bWriteSpaces = true;
+ if (eOp == ocSpaces && mxSymbols->isODFF())
+ {
+ const FormulaToken* p = maArrIterator.PeekPrevNoSpaces();
+ bool bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
+ if (bIntersectionOp)
+ {
+ p = maArrIterator.PeekNextNoSpaces();
+ bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
+ }
+ if (bIntersectionOp)
+ {
+ rBuffer.append( "!!");
+ bWriteSpaces = false;
+ }
+ }
+ if (bWriteSpaces)
+ {
+ // ODF v1.3 OpenFormula 5.14 Whitespace states "whitespace shall
+ // not separate a function name from its initial opening
+ // parenthesis".
+ //
+ // ECMA-376-1:2016 18.17.2 Syntax states "that no space characters
+ // shall separate a function-name from the left parenthesis (()
+ // that follows it." and Excel even chokes on it.
+ //
+ // Suppress/remove it in any case also in UI, it will not be
+ // preserved.
+ const FormulaToken* p = maArrIterator.PeekPrevNoSpaces();
+ if (p && p->IsFunction())
+ {
+ p = maArrIterator.PeekNextNoSpaces();
+ if (p && p->GetOpCode() == ocOpen)
+ bWriteSpaces = false;
+ }
+ }
+ if (bWriteSpaces)
+ {
+ // most times it's just one blank
+ sal_uInt8 n = t->GetByte();
+ for ( sal_uInt8 j=0; j<n; ++j )
+ {
+ if (eOp == ocWhitespace)
+ rBuffer.append( t->GetChar());
+ else
+ rBuffer.append( ' ');
+ }
+ }
+ }
+ else if( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
+ rBuffer.appendAscii( pInternal[ eOp - ocInternalBegin ] );
+ else if (eOp == ocIntersect)
+ {
+ // Nasty, ugly, horrific, terrifying...
+ if (FormulaGrammar::isExcelSyntax( meGrammar))
+ rBuffer.append(' ');
+ else
+ rBuffer.append( mxSymbols->getSymbol( eOp));
+ }
+ else if( static_cast<sal_uInt16>(eOp) < mxSymbols->getSymbolCount()) // Keyword:
+ rBuffer.append( mxSymbols->getSymbol( eOp));
+ else
+ {
+ SAL_WARN( "formula.core","unknown OpCode");
+ rBuffer.append( GetNativeSymbol( ocErrName ));
+ }
+ if( bNext )
+ {
+ if (t->IsExternalRef())
+ {
+ CreateStringFromExternal( rBuffer, pTokenP);
+ }
+ else
+ {
+ switch( t->GetType() )
+ {
+ case svDouble:
+ AppendDouble( rBuffer, t->GetDouble() );
+ break;
+
+ case svString:
+ if( eOp == ocBad || eOp == ocStringXML )
+ rBuffer.append( t->GetString().getString());
+ else
+ AppendString( rBuffer, t->GetString().getString() );
+ break;
+ case svSingleRef:
+ CreateStringFromSingleRef( rBuffer, t);
+ break;
+ case svDoubleRef:
+ CreateStringFromDoubleRef( rBuffer, t);
+ break;
+ case svMatrix:
+ case svMatrixCell:
+ CreateStringFromMatrix( rBuffer, t );
+ break;
+
+ case svIndex:
+ CreateStringFromIndex( rBuffer, t );
+ if (t->GetOpCode() == ocTableRef && bAllowArrAdvance && NeedsTableRefTransformation())
+ {
+ // Suppress all TableRef related tokens, the resulting
+ // range was written by CreateStringFromIndex().
+ const FormulaToken* const p = maArrIterator.PeekNext();
+ if (p && p->GetOpCode() == ocTableRefOpen)
+ {
+ int nLevel = 0;
+ do
+ {
+ t = maArrIterator.Next();
+ if (!t)
+ break;
+
+ // Switch cases correspond with those in
+ // ScCompiler::HandleTableRef()
+ switch (t->GetOpCode())
+ {
+ case ocTableRefOpen:
+ ++nLevel;
+ break;
+ case ocTableRefClose:
+ --nLevel;
+ break;
+ case ocTableRefItemAll:
+ case ocTableRefItemHeaders:
+ case ocTableRefItemData:
+ case ocTableRefItemTotals:
+ case ocTableRefItemThisRow:
+ case ocSep:
+ case ocPush:
+ case ocRange:
+ case ocSpaces:
+ case ocWhitespace:
+ break;
+ default:
+ nLevel = 0;
+ bNext = false;
+ }
+ } while (nLevel);
+ }
+ }
+ break;
+ case svExternal:
+ {
+ // mapped or translated name of AddIns
+ OUString aAddIn( t->GetExternal() );
+ bool bMapped = mxSymbols->isPODF(); // ODF 1.1 directly uses programmatical name
+ if (!bMapped && mxSymbols->hasExternals())
+ {
+ ExternalHashMap::const_iterator iLook = mxSymbols->getReverseExternalHashMap().find( aAddIn);
+ if (iLook != mxSymbols->getReverseExternalHashMap().end())
+ {
+ aAddIn = (*iLook).second;
+ bMapped = true;
+ }
+ }
+ if (!bMapped && !mxSymbols->isEnglish())
+ LocalizeString( aAddIn );
+ rBuffer.append( aAddIn);
+ }
+ break;
+ case svError:
+ AppendErrorConstant( rBuffer, t->GetError());
+ break;
+ case svByte:
+ case svJump:
+ case svFAP:
+ case svMissing:
+ case svSep:
+ break; // Opcodes
+ default:
+ SAL_WARN("formula.core", "FormulaCompiler::GetStringFromToken: unknown token type " << t->GetType());
+ } // of switch
+ }
+ }
+ if( bSpaces )
+ rBuffer.append( ' ');
+ if ( bAllowArrAdvance )
+ {
+ if( bNext )
+ t = maArrIterator.Next();
+ return t;
+ }
+ return pTokenP;
+}
+
+
+void FormulaCompiler::AppendDouble( OUStringBuffer& rBuffer, double fVal ) const
+{
+ if ( mxSymbols->isEnglishLocale() )
+ {
+ ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.', true );
+ }
+ else
+ {
+ SvtSysLocale aSysLocale;
+ ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max,
+ aSysLocale.GetLocaleData().getNumDecimalSep()[0],
+ true );
+ }
+}
+
+void FormulaCompiler::AppendBoolean( OUStringBuffer& rBuffer, bool bVal ) const
+{
+ rBuffer.append( mxSymbols->getSymbol( bVal ? ocTrue : ocFalse ) );
+}
+
+void FormulaCompiler::AppendString( OUStringBuffer& rBuffer, const OUString & rStr )
+{
+ rBuffer.append( '"');
+ if ( lcl_UnicodeStrChr( rStr.getStr(), '"' ) == nullptr )
+ rBuffer.append( rStr );
+ else
+ {
+ OUString aStr = rStr.replaceAll( "\"", "\"\"" );
+ rBuffer.append(aStr);
+ }
+ rBuffer.append( '"');
+}
+
+bool FormulaCompiler::NeedsTableRefTransformation() const
+{
+ // Currently only UI representations and OOXML export use Table structured
+ // references. Not defined in ODFF.
+ // Unnecessary to explicitly check for ODFF grammar as the ocTableRefOpen
+ // symbol is not defined there.
+ return mxSymbols->getSymbol( ocTableRefOpen).isEmpty() || FormulaGrammar::isPODF( meGrammar);
+}
+
+void FormulaCompiler::UpdateSeparatorsNative(
+ const OUString& rSep, const OUString& rArrayColSep, const OUString& rArrayRowSep )
+{
+ NonConstOpCodeMapPtr xSymbolsNative;
+ lcl_fillNativeSymbols( xSymbolsNative);
+ xSymbolsNative->putOpCode( rSep, ocSep, nullptr);
+ xSymbolsNative->putOpCode( rArrayColSep, ocArrayColSep, nullptr);
+ xSymbolsNative->putOpCode( rArrayRowSep, ocArrayRowSep, nullptr);
+}
+
+void FormulaCompiler::ResetNativeSymbols()
+{
+ NonConstOpCodeMapPtr xSymbolsNative;
+ lcl_fillNativeSymbols( xSymbolsNative, InitSymbols::DESTROY);
+ lcl_fillNativeSymbols( xSymbolsNative);
+}
+
+void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr& xMap )
+{
+ NonConstOpCodeMapPtr xSymbolsNative;
+ lcl_fillNativeSymbols( xSymbolsNative);
+ xSymbolsNative->copyFrom( *xMap );
+}
+
+
+OpCode FormulaCompiler::NextToken()
+{
+ if( !GetToken() )
+ return ocStop;
+ OpCode eOp = mpToken->GetOpCode();
+ // There must be an operator before a push
+ if ( (eOp == ocPush || eOp == ocColRowNameAuto) &&
+ !( (eLastOp == ocOpen) || (eLastOp == ocSep) ||
+ (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)) )
+ SetError( FormulaError::OperatorExpected);
+ // Operator and Plus => operator
+ if (eOp == ocAdd && (eLastOp == ocOpen || eLastOp == ocSep ||
+ (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
+ {
+ FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
+ eOp = NextToken();
+ }
+ else
+ {
+ // Before an operator there must not be another operator, with the
+ // exception of AND and OR.
+ if ( eOp != ocAnd && eOp != ocOr &&
+ (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP )
+ && (eLastOp == ocOpen || eLastOp == ocSep ||
+ (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
+ {
+ SetError( FormulaError::VariableExpected);
+ if ( bAutoCorrect && !pStack )
+ {
+ if ( eOp == eLastOp || eLastOp == ocOpen )
+ { // throw away duplicated operator
+ aCorrectedSymbol.clear();
+ bCorrected = true;
+ }
+ else
+ {
+ sal_Int32 nPos = aCorrectedFormula.getLength();
+ if ( nPos )
+ {
+ nPos--;
+ sal_Unicode c = aCorrectedFormula[ nPos ];
+ switch ( eOp )
+ { // swap operators
+ case ocGreater:
+ if ( c == mxSymbols->getSymbolChar( ocEqual) )
+ { // >= instead of =>
+ aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
+ rtl::OUStringChar( mxSymbols->getSymbolChar(ocGreater) ) );
+ aCorrectedSymbol = OUString(c);
+ bCorrected = true;
+ }
+ break;
+ case ocLess:
+ if ( c == mxSymbols->getSymbolChar( ocEqual) )
+ { // <= instead of =<
+ aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
+ rtl::OUStringChar( mxSymbols->getSymbolChar(ocLess) ) );
+ aCorrectedSymbol = OUString(c);
+ bCorrected = true;
+ }
+ else if ( c == mxSymbols->getSymbolChar( ocGreater) )
+ { // <> instead of ><
+ aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
+ rtl::OUStringChar( mxSymbols->getSymbolChar(ocLess) ) );
+ aCorrectedSymbol = OUString(c);
+ bCorrected = true;
+ }
+ break;
+ case ocMul:
+ if ( c == mxSymbols->getSymbolChar( ocSub) )
+ { // *- instead of -*
+ aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
+ rtl::OUStringChar( mxSymbols->getSymbolChar(ocMul) ) );
+ aCorrectedSymbol = OUString(c);
+ bCorrected = true;
+ }
+ break;
+ case ocDiv:
+ if ( c == mxSymbols->getSymbolChar( ocSub) )
+ { // /- instead of -/
+ aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
+ rtl::OUStringChar( mxSymbols->getSymbolChar(ocDiv) ) );
+ aCorrectedSymbol = OUString(c);
+ bCorrected = true;
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ }
+ }
+ // Nasty, ugly, horrific, terrifying... significant whitespace...
+ if (eOp == ocSpaces && FormulaGrammar::isExcelSyntax( meGrammar))
+ {
+ // Fake an intersection op as last op for the next round, but at
+ // least roughly check if it could make sense at all.
+ FormulaToken* pPrev = maArrIterator.PeekPrevNoSpaces();
+ if (pPrev && isPotentialRangeType( pPrev, false, false))
+ {
+ FormulaToken* pNext = maArrIterator.PeekNextNoSpaces();
+ if (pNext && isPotentialRangeType( pNext, false, true))
+ eLastOp = ocIntersect;
+ else
+ eLastOp = eOp;
+ }
+ else
+ eLastOp = eOp;
+ }
+ else
+ eLastOp = eOp;
+ }
+ return eOp;
+}
+
+void FormulaCompiler::PutCode( FormulaTokenRef& p )
+{
+ if( pc >= FORMULA_MAXTOKENS - 1 )
+ {
+ if ( pc == FORMULA_MAXTOKENS - 1 )
+ {
+ SAL_WARN("formula.core", "FormulaCompiler::PutCode - CodeOverflow with OpCode " << +p->GetOpCode());
+ p = new FormulaByteToken( ocStop );
+ p->IncRef();
+ *pCode++ = p.get();
+ ++pc;
+ }
+ SetError( FormulaError::CodeOverflow);
+ return;
+ }
+ if (pArr->GetCodeError() != FormulaError::NONE && mbJumpCommandReorder)
+ return;
+ ForceArrayOperator( p);
+ p->IncRef();
+ *pCode++ = p.get();
+ pc++;
+}
+
+
+bool FormulaCompiler::HandleExternalReference( const FormulaToken& /*_aToken*/)
+{
+ return true;
+}
+
+bool FormulaCompiler::HandleRange()
+{
+ return true;
+}
+
+bool FormulaCompiler::HandleColRowName()
+{
+ return true;
+}
+
+bool FormulaCompiler::HandleDbData()
+{
+ return true;
+}
+
+bool FormulaCompiler::HandleTableRef()
+{
+ return true;
+}
+
+void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
+{
+}
+
+void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
+{
+}
+
+void FormulaCompiler::CreateStringFromIndex( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
+{
+}
+
+void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
+{
+}
+
+void FormulaCompiler::CreateStringFromExternal( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
+{
+}
+
+void FormulaCompiler::LocalizeString( OUString& /*rName*/ ) const
+{
+}
+
+formula::ParamClass FormulaCompiler::GetForceArrayParameter( const FormulaToken* /*pToken*/, sal_uInt16 /*nParam*/ ) const
+{
+ return ParamClass::Unknown;
+}
+
+void FormulaCompiler::ForceArrayOperator( FormulaTokenRef const & rCurr )
+{
+ if (pCurrentFactorToken.get() == rCurr.get())
+ return;
+
+ const OpCode eOp = rCurr->GetOpCode();
+ const StackVar eType = rCurr->GetType();
+ const bool bInlineArray = (eOp == ocPush && eType == svMatrix);
+
+ if (!bInlineArray)
+ {
+ if (rCurr->GetInForceArray() != ParamClass::Unknown)
+ // Already set, unnecessary to evaluate again. This happens by calls to
+ // CurrentFactor::operator=() while descending through Factor() and
+ // then ascending back (and down and up, ...),
+ // CheckSetForceArrayParameter() and later PutCode().
+ return;
+
+ if (!(eOp != ocPush && (eType == svByte || eType == svJump)))
+ return;
+ }
+
+ // Return class for inline arrays and functions returning array/matrix.
+ // It's somewhat unclear what Excel actually does there and in
+ // ECMA-376-1:2016 OOXML mentions "call to ... shall be an array formula"
+ // only for FREQUENCY() and TRANSPOSE() but not for any other function
+ // returning array/matrix or inline arrays, though for the latter has one
+ // example in 18.17.2 Syntax:
+ // "SUM(SQRT({1,2,3,4})) returns 6.14 when entered normally". However,
+ // these need to be treated similar but not as ParamClass::ForceArray
+ // (which would contradict the example in
+ // https://bugs.documentfoundation.org/show_bug.cgi?id=122301#c19 and A6 of
+ // https://bugs.documentfoundation.org/show_bug.cgi?id=133260#c10 ).
+ // See also
+ // commit d0ded163d8e93dc5b10d7a7c9bdab1d0a6a50bac
+ // commit 5413c8871dec08eff19f514f5f391b946a45c86c
+ constexpr ParamClass eArrayReturn = ParamClass::ForceArrayReturn;
+
+ if (bInlineArray)
+ {
+ // rCurr->SetInForceArray() can not be used with ocPush, but ocPush
+ // with svMatrix has an implicit ParamClass::ForceArrayReturn.
+ if (nCurrentFactorParam > 0 && pCurrentFactorToken
+ && pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown
+ && GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
+ == ParamClass::Value)
+ {
+ // Propagate to caller as if a function returning an array/matrix
+ // was called (see also below).
+ pCurrentFactorToken->SetInForceArray( eArrayReturn);
+ }
+ return;
+ }
+
+ if (!pCurrentFactorToken)
+ {
+ if (mbMatrixFlag)
+ {
+ // An array/matrix formula acts as ForceArray on all top level
+ // operators and function calls, so that can be inherited properly
+ // below.
+ rCurr->SetInForceArray( ParamClass::ForceArray);
+ }
+ else if (pc >= 2 && SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP)
+ {
+ // Binary operators are not functions followed by arguments
+ // and need some peeking into RPN to inspect their operands.
+ // Note that array context is not forced if only one
+ // of the operands is an array like "={1;2}+A1:A2" returns #VALUE!
+ // if entered in column A and not input in array mode, because it
+ // involves a range reference with an implicit intersection. Check
+ // both arguments are arrays, or the other is ocPush without ranges
+ // for "={1;2}+3" or "={1;2}+A1".
+ // Note this does not catch "={1;2}+ABS(A1)" that could be forced
+ // to array, user still has to close in array mode.
+ // The IsMatrixFunction() is only necessary because not all
+ // functions returning matrix have ForceArrayReturn (yet?), see
+ // OOXML comment above.
+
+ const OpCode eOp1 = pCode[-1]->GetOpCode();
+ const OpCode eOp2 = pCode[-2]->GetOpCode();
+ const bool b1 = (pCode[-1]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(eOp1));
+ const bool b2 = (pCode[-2]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(eOp2));
+ if ((b1 && b2)
+ || (b1 && eOp2 == ocPush && pCode[-2]->GetType() != svDoubleRef)
+ || (b2 && eOp1 == ocPush && pCode[-1]->GetType() != svDoubleRef))
+ {
+ rCurr->SetInForceArray( eArrayReturn);
+ }
+ }
+ else if (pc >= 1 && SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)
+ {
+ // Similar for unary operators.
+ if (pCode[-1]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(pCode[-1]->GetOpCode()))
+ {
+ rCurr->SetInForceArray( eArrayReturn);
+ }
+ }
+ return;
+ }
+
+ // Inherited parameter class.
+ const formula::ParamClass eForceType = pCurrentFactorToken->GetInForceArray();
+ if (eForceType == ParamClass::ForceArray || eForceType == ParamClass::ReferenceOrRefArray)
+ {
+ // ReferenceOrRefArray was set only if in ForceArray context already,
+ // it is valid for the one function only to indicate the preferred
+ // return type. Propagate as ForceArray if not another parameter
+ // handling ReferenceOrRefArray.
+ if (nCurrentFactorParam > 0
+ && (GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
+ == ParamClass::ReferenceOrRefArray))
+ rCurr->SetInForceArray( ParamClass::ReferenceOrRefArray);
+ else
+ rCurr->SetInForceArray( ParamClass::ForceArray);
+ return;
+ }
+ else if (eForceType == ParamClass::ReferenceOrForceArray)
+ {
+ // Inherit further only if the return class of the nested function is
+ // not Reference. Else flag as suppressed.
+ if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) != ParamClass::Reference)
+ rCurr->SetInForceArray( eForceType);
+ else
+ rCurr->SetInForceArray( ParamClass::SuppressedReferenceOrForceArray);
+ return;
+ }
+
+ if (nCurrentFactorParam <= 0)
+ return;
+
+ // Actual current parameter's class.
+ const formula::ParamClass eParamType = GetForceArrayParameter(
+ pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1));
+ if (eParamType == ParamClass::ForceArray)
+ rCurr->SetInForceArray( eParamType);
+ else if (eParamType == ParamClass::ReferenceOrForceArray)
+ {
+ if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) != ParamClass::Reference)
+ rCurr->SetInForceArray( eParamType);
+ else
+ rCurr->SetInForceArray( formula::ParamClass::SuppressedReferenceOrForceArray);
+ }
+
+ // Propagate a ForceArrayReturn to caller if the called function
+ // returns one and the caller so far does not have a stronger array
+ // mode set and expects a scalar value for this parameter.
+ if (eParamType == ParamClass::Value && pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown)
+ {
+ if (IsMatrixFunction( eOp))
+ pCurrentFactorToken->SetInForceArray( eArrayReturn);
+ else if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) == ParamClass::ForceArrayReturn)
+ pCurrentFactorToken->SetInForceArray( ParamClass::ForceArrayReturn);
+ }
+}
+
+void FormulaCompiler::CheckSetForceArrayParameter( FormulaTokenRef const & rCurr, sal_uInt8 nParam )
+{
+ if (!pCurrentFactorToken)
+ return;
+
+ nCurrentFactorParam = nParam + 1;
+
+ ForceArrayOperator( rCurr);
+}
+
+void FormulaCompiler::PushTokenArray( FormulaTokenArray* pa, bool bTemp )
+{
+ if ( bAutoCorrect && !pStack )
+ { // don't merge stacked subroutine code into entered formula
+ aCorrectedFormula += aCorrectedSymbol;
+ aCorrectedSymbol.clear();
+ }
+ FormulaArrayStack* p = new FormulaArrayStack;
+ p->pNext = pStack;
+ p->pArr = pArr;
+ p->nIndex = maArrIterator.GetIndex();
+ p->mpLastToken = mpLastToken;
+ p->bTemp = bTemp;
+ pStack = p;
+ pArr = pa;
+ maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
+}
+
+} // namespace formula
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/core/api/FormulaOpCodeMapperObj.cxx b/formula/source/core/api/FormulaOpCodeMapperObj.cxx
new file mode 100644
index 0000000000..a3ffe56cc8
--- /dev/null
+++ b/formula/source/core/api/FormulaOpCodeMapperObj.cxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <sal/config.h>
+
+#include <utility>
+
+#include <formula/FormulaCompiler.hxx>
+#include <formula/FormulaOpCodeMapperObj.hxx>
+#include <formula/opcode.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+namespace formula
+{
+ using namespace ::com::sun::star;
+
+sal_Bool SAL_CALL FormulaOpCodeMapperObj::supportsService( const OUString& _rServiceName )
+{
+ return cppu::supportsService(this, _rServiceName);
+}
+
+FormulaOpCodeMapperObj::FormulaOpCodeMapperObj(::std::unique_ptr<FormulaCompiler> && _pCompiler)
+: m_pCompiler(std::move(_pCompiler))
+{
+}
+
+FormulaOpCodeMapperObj::~FormulaOpCodeMapperObj()
+{
+}
+
+::sal_Int32 SAL_CALL FormulaOpCodeMapperObj::getOpCodeExternal()
+{
+ return ocExternal;
+}
+
+
+::sal_Int32 SAL_CALL FormulaOpCodeMapperObj::getOpCodeUnknown()
+{
+ return FormulaCompiler::OpCodeMap::getOpCodeUnknown();
+}
+
+
+css::uno::Sequence< css::sheet::FormulaToken >
+SAL_CALL FormulaOpCodeMapperObj::getMappings(
+ const css::uno::Sequence< OUString >& rNames,
+ sal_Int32 nLanguage )
+{
+ FormulaCompiler::OpCodeMapPtr xMap = m_pCompiler->GetOpCodeMap( nLanguage);
+ if (!xMap)
+ throw lang::IllegalArgumentException();
+ return xMap->createSequenceOfFormulaTokens( *m_pCompiler,rNames);
+}
+
+
+css::uno::Sequence< css::sheet::FormulaOpCodeMapEntry >
+SAL_CALL FormulaOpCodeMapperObj::getAvailableMappings(
+ sal_Int32 nLanguage, sal_Int32 nGroups )
+{
+ FormulaCompiler::OpCodeMapPtr xMap = m_pCompiler->GetOpCodeMap( nLanguage);
+ if (!xMap)
+ throw lang::IllegalArgumentException();
+ return xMap->createSequenceOfAvailableMappings( *m_pCompiler,nGroups);
+}
+
+OUString SAL_CALL FormulaOpCodeMapperObj::getImplementationName( )
+{
+ return "simple.formula.FormulaOpCodeMapperObj";
+}
+
+uno::Sequence< OUString > SAL_CALL FormulaOpCodeMapperObj::getSupportedServiceNames( )
+{
+ return { "com.sun.star.sheet.FormulaOpCodeMapper" };
+}
+
+} // formula
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+simple_formula_FormulaOpCodeMapperObj(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(
+ new formula::FormulaOpCodeMapperObj(std::make_unique<formula::FormulaCompiler>()));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/core/api/grammar.cxx b/formula/source/core/api/grammar.cxx
new file mode 100644
index 0000000000..3974ee28d3
--- /dev/null
+++ b/formula/source/core/api/grammar.cxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <formula/grammar.hxx>
+#include <cassert>
+
+namespace formula {
+
+FormulaGrammar::Grammar FormulaGrammar::mapAPItoGrammar( const bool bEnglish, const bool bXML )
+{
+ Grammar eGrammar;
+ if (bEnglish && bXML)
+ eGrammar = GRAM_PODF;
+ else if (bEnglish && !bXML)
+ eGrammar = GRAM_API;
+ else if (!bEnglish && bXML)
+ eGrammar = GRAM_NATIVE_ODF;
+ else // (!bEnglish && !bXML)
+ eGrammar = GRAM_NATIVE;
+ return eGrammar;
+}
+
+bool FormulaGrammar::isSupported( const Grammar eGrammar )
+{
+ switch (eGrammar)
+ {
+ case GRAM_ODFF :
+ case GRAM_PODF :
+ case GRAM_ENGLISH :
+ case GRAM_NATIVE :
+ case GRAM_ODFF_UI :
+ case GRAM_ODFF_A1 :
+ case GRAM_PODF_UI :
+ case GRAM_PODF_A1 :
+ case GRAM_NATIVE_UI :
+ case GRAM_NATIVE_ODF :
+ case GRAM_NATIVE_XL_A1 :
+ case GRAM_NATIVE_XL_R1C1 :
+ case GRAM_ENGLISH_XL_A1 :
+ case GRAM_ENGLISH_XL_R1C1:
+ case GRAM_ENGLISH_XL_OOX :
+ case GRAM_OOXML :
+ case GRAM_API :
+ return true;
+ default:
+ return extractFormulaLanguage( eGrammar) == GRAM_EXTERNAL;
+ }
+}
+
+FormulaGrammar::Grammar FormulaGrammar::setEnglishBit( const Grammar eGrammar, const bool bEnglish )
+{
+ if (bEnglish)
+ return static_cast<Grammar>( eGrammar | kEnglishBit);
+ else
+ return static_cast<Grammar>( eGrammar & ~kEnglishBit);
+}
+
+FormulaGrammar::Grammar FormulaGrammar::mergeToGrammar( const Grammar eGrammar, const AddressConvention eConv )
+{
+ bool bEnglish = isEnglish( eGrammar);
+ Grammar eGram = static_cast<Grammar>(
+ extractFormulaLanguage( eGrammar) |
+ ((eConv + kConventionOffset) << kConventionShift));
+ eGram = setEnglishBit( eGram, bEnglish);
+ assert( isSupported( eGram));
+ return eGram;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/core/api/token.cxx b/formula/source/core/api/token.cxx
new file mode 100644
index 0000000000..74a212937b
--- /dev/null
+++ b/formula/source/core/api/token.cxx
@@ -0,0 +1,2033 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <algorithm>
+
+#include <string.h>
+#include <limits.h>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/sheet/FormulaToken.hpp>
+#include <formula/errorcodes.hxx>
+#include <formula/token.hxx>
+#include <formula/tokenarray.hxx>
+#include <formula/FormulaCompiler.hxx>
+#include <formula/compiler.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <memory>
+#include <utility>
+
+namespace formula
+{
+ using namespace com::sun::star;
+
+
+// --- helpers --------------------------------------------------------------
+
+static 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 FormulaToken --------------------------------------------------------
+
+FormulaToken::FormulaToken( StackVar eTypeP, OpCode e ) :
+ eOp(e), eType( eTypeP ), mnRefCnt(0)
+{
+}
+
+FormulaToken::FormulaToken( const FormulaToken& r ) :
+ eOp(r.eOp), eType( r.eType ), mnRefCnt(0)
+{
+}
+
+FormulaToken::~FormulaToken()
+{
+}
+
+bool FormulaToken::IsFunction() const
+{
+ return (eOp != ocPush && eOp != ocBad && eOp != ocColRowName &&
+ eOp != ocColRowNameAuto && eOp != ocName && eOp != ocDBArea &&
+ eOp != ocTableRef &&
+ (GetByte() != 0 // x parameters
+ || (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR) // no parameter
+ || FormulaCompiler::IsOpCodeJumpCommand( eOp ) // @ jump commands
+ || (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR) // one parameter
+ || (SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR) // x parameters (cByte==0 in
+ // FuncAutoPilot)
+ || eOp == ocMacro || eOp == ocExternal // macros, AddIns
+ || eOp == ocAnd || eOp == ocOr // former binary, now x parameters
+ || (eOp >= ocInternalBegin && eOp <= ocInternalEnd) // internal
+ ));
+}
+
+
+sal_uInt8 FormulaToken::GetParamCount() const
+{
+ if ( eOp < SC_OPCODE_STOP_DIV && eOp != ocExternal && eOp != ocMacro &&
+ !FormulaCompiler::IsOpCodeJumpCommand( eOp ) &&
+ eOp != ocPercentSign )
+ return 0; // parameters and specials
+ // ocIf... jump commands not for FAP, have cByte then
+//2do: bool parameter whether FAP or not?
+ else if (GetByte())
+ return GetByte(); // all functions, also ocExternal and ocMacro
+ else if (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP && eOp != ocAnd && eOp != ocOr)
+ return 2; // binary operators, compiler checked; OR and AND legacy but are functions
+ else if ((SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP) || eOp == ocPercentSign)
+ return 1; // unary operators, compiler checked
+ else if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)
+ return 0; // no parameter
+ else if (FormulaCompiler::IsOpCodeJumpCommand( eOp ))
+ return 1; // only the condition counts as parameter
+ else
+ return 0; // all the rest, no Parameter, or
+ // if so then it should be in cByte
+}
+
+bool FormulaToken::IsExternalRef() const
+{
+ bool bRet = false;
+ switch (eType)
+ {
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svExternalName:
+ bRet = true;
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+ return bRet;
+}
+
+bool FormulaToken::IsRef() const
+{
+ switch (eType)
+ {
+ case svSingleRef:
+ case svDoubleRef:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ return true;
+ default:
+ if (eOp == ocTableRef)
+ return true;
+ }
+
+ return false;
+}
+
+bool FormulaToken::IsInForceArray() const
+{
+ ParamClass eParam = GetInForceArray();
+ return eParam == ParamClass::ForceArray || eParam == ParamClass::ReferenceOrForceArray
+ || eParam == ParamClass::ReferenceOrRefArray || eParam == ParamClass::ForceArrayReturn;
+}
+
+bool FormulaToken::operator==( const FormulaToken& rToken ) const
+{
+ // don't compare reference count!
+ return eType == rToken.eType && GetOpCode() == rToken.GetOpCode();
+}
+
+
+// --- virtual dummy methods -------------------------------------------------
+
+sal_uInt8 FormulaToken::GetByte() const
+{
+ // ok to be called for any derived class
+ return 0;
+}
+
+void FormulaToken::SetByte( sal_uInt8 )
+{
+ assert( !"virtual dummy called" );
+}
+
+ParamClass FormulaToken::GetInForceArray() const
+{
+ // ok to be called for any derived class
+ return (eOp == ocPush && eType == svMatrix) ? ParamClass::ForceArrayReturn : ParamClass::Unknown;
+}
+
+void FormulaToken::SetInForceArray( ParamClass )
+{
+ assert( !"virtual dummy called" );
+}
+
+double FormulaToken::GetDouble() const
+{
+ // This Get is worth an assert.
+ assert( !"virtual dummy called" );
+ return 0.0;
+}
+
+double & FormulaToken::GetDoubleAsReference()
+{
+ // This Get is worth an assert.
+ assert( !"virtual dummy called" );
+ static double fVal = 0.0;
+ return fVal;
+}
+
+sal_Int16 FormulaToken::GetDoubleType() const
+{
+ SAL_WARN( "formula.core", "FormulaToken::GetDoubleType: virtual dummy called" );
+ return 0;
+}
+
+void FormulaToken::SetDoubleType( sal_Int16 )
+{
+ assert( !"virtual dummy called" );
+}
+
+const svl::SharedString INVALID_STRING;
+
+const svl::SharedString & FormulaToken::GetString() const
+{
+ SAL_WARN( "formula.core", "FormulaToken::GetString: virtual dummy called" );
+ return INVALID_STRING; // invalid string
+}
+
+void FormulaToken::SetString( const svl::SharedString& )
+{
+ assert( !"virtual dummy called" );
+}
+
+sal_uInt16 FormulaToken::GetIndex() const
+{
+ SAL_WARN( "formula.core", "FormulaToken::GetIndex: virtual dummy called" );
+ return 0;
+}
+
+void FormulaToken::SetIndex( sal_uInt16 )
+{
+ assert( !"virtual dummy called" );
+}
+
+sal_Int16 FormulaToken::GetSheet() const
+{
+ SAL_WARN( "formula.core", "FormulaToken::GetSheet: virtual dummy called" );
+ return -1;
+}
+
+void FormulaToken::SetSheet( sal_Int16 )
+{
+ assert( !"virtual dummy called" );
+}
+
+sal_Unicode FormulaToken::GetChar() const
+{
+ // This Get is worth an assert.
+ assert( !"virtual dummy called" );
+ return 0;
+}
+
+short* FormulaToken::GetJump() const
+{
+ SAL_WARN( "formula.core", "FormulaToken::GetJump: virtual dummy called" );
+ return nullptr;
+}
+
+
+const OUString& FormulaToken::GetExternal() const
+{
+ SAL_WARN( "formula.core", "FormulaToken::GetExternal: virtual dummy called" );
+ static OUString aDummyString;
+ return aDummyString;
+}
+
+FormulaToken* FormulaToken::GetFAPOrigToken() const
+{
+ SAL_WARN( "formula.core", "FormulaToken::GetFAPOrigToken: virtual dummy called" );
+ return nullptr;
+}
+
+FormulaError FormulaToken::GetError() const
+{
+ SAL_WARN( "formula.core", "FormulaToken::GetError: virtual dummy called" );
+ return FormulaError::NONE;
+}
+
+void FormulaToken::SetError( FormulaError )
+{
+ assert( !"virtual dummy called" );
+}
+
+const ScSingleRefData* FormulaToken::GetSingleRef() const
+{
+ OSL_FAIL( "FormulaToken::GetSingleRef: virtual dummy called" );
+ return nullptr;
+}
+
+ScSingleRefData* FormulaToken::GetSingleRef()
+{
+ OSL_FAIL( "FormulaToken::GetSingleRef: virtual dummy called" );
+ return nullptr;
+}
+
+const ScComplexRefData* FormulaToken::GetDoubleRef() const
+{
+ OSL_FAIL( "FormulaToken::GetDoubleRef: virtual dummy called" );
+ return nullptr;
+}
+
+ScComplexRefData* FormulaToken::GetDoubleRef()
+{
+ OSL_FAIL( "FormulaToken::GetDoubleRef: virtual dummy called" );
+ return nullptr;
+}
+
+const ScSingleRefData* FormulaToken::GetSingleRef2() const
+{
+ OSL_FAIL( "FormulaToken::GetSingleRef2: virtual dummy called" );
+ return nullptr;
+}
+
+ScSingleRefData* FormulaToken::GetSingleRef2()
+{
+ OSL_FAIL( "FormulaToken::GetSingleRef2: virtual dummy called" );
+ return nullptr;
+}
+
+const ScMatrix* FormulaToken::GetMatrix() const
+{
+ OSL_FAIL( "FormulaToken::GetMatrix: virtual dummy called" );
+ return nullptr;
+}
+
+ScMatrix* FormulaToken::GetMatrix()
+{
+ OSL_FAIL( "FormulaToken::GetMatrix: virtual dummy called" );
+ return nullptr;
+}
+
+ScJumpMatrix* FormulaToken::GetJumpMatrix() const
+{
+ OSL_FAIL( "FormulaToken::GetJumpMatrix: virtual dummy called" );
+ return nullptr;
+}
+const std::vector<ScComplexRefData>* FormulaToken::GetRefList() const
+{
+ OSL_FAIL( "FormulaToken::GetRefList: virtual dummy called" );
+ return nullptr;
+}
+
+std::vector<ScComplexRefData>* FormulaToken::GetRefList()
+{
+ OSL_FAIL( "FormulaToken::GetRefList: virtual dummy called" );
+ return nullptr;
+}
+
+bool FormulaToken::TextEqual( const FormulaToken& rToken ) const
+{
+ return *this == rToken;
+}
+
+// real implementations of virtual functions
+
+
+sal_uInt8 FormulaSpaceToken::GetByte() const { return nByte; }
+sal_Unicode FormulaSpaceToken::GetChar() const { return cChar; }
+bool FormulaSpaceToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && nByte == r.GetByte() &&
+ cChar == r.GetChar();
+}
+
+
+sal_uInt8 FormulaByteToken::GetByte() const { return nByte; }
+void FormulaByteToken::SetByte( sal_uInt8 n ) { nByte = n; }
+ParamClass FormulaByteToken::GetInForceArray() const { return eInForceArray; }
+void FormulaByteToken::SetInForceArray( ParamClass c ) { eInForceArray = c; }
+bool FormulaByteToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && nByte == r.GetByte() &&
+ eInForceArray == r.GetInForceArray();
+}
+
+
+FormulaToken* FormulaFAPToken::GetFAPOrigToken() const { return pOrigToken.get(); }
+bool FormulaFAPToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaByteToken::operator==( r ) && pOrigToken == r.GetFAPOrigToken();
+}
+
+
+short* FormulaJumpToken::GetJump() const { return pJump.get(); }
+ParamClass FormulaJumpToken::GetInForceArray() const { return eInForceArray; }
+void FormulaJumpToken::SetInForceArray( ParamClass c ) { eInForceArray = c; }
+bool FormulaJumpToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && pJump[0] == r.GetJump()[0] &&
+ memcmp( pJump.get()+1, r.GetJump()+1, pJump[0] * sizeof(short) ) == 0 &&
+ eInForceArray == r.GetInForceArray();
+}
+FormulaJumpToken::~FormulaJumpToken()
+{
+}
+
+
+bool FormulaTokenArray::AddFormulaToken(
+ const sheet::FormulaToken& rToken, svl::SharedStringPool& rSPool, ExternalReferenceHelper* /*pExtRef*/)
+{
+ bool bError = false;
+ const OpCode eOpCode = static_cast<OpCode>(rToken.OpCode); //! assuming equal values for the moment
+
+ const uno::TypeClass eClass = rToken.Data.getValueTypeClass();
+ switch ( eClass )
+ {
+ case uno::TypeClass_VOID:
+ // empty data -> use AddOpCode (does some special cases)
+ AddOpCode( eOpCode );
+ break;
+ case uno::TypeClass_DOUBLE:
+ // double is only used for "push"
+ if ( eOpCode == ocPush )
+ AddDouble( rToken.Data.get<double>() );
+ else
+ bError = true;
+ break;
+ case uno::TypeClass_LONG:
+ {
+ // long is svIndex, used for name / database area, or "byte" for spaces
+ sal_Int32 nValue = rToken.Data.get<sal_Int32>();
+ if ( eOpCode == ocDBArea )
+ Add( new formula::FormulaIndexToken( eOpCode, static_cast<sal_uInt16>(nValue) ) );
+ else if ( eOpCode == ocTableRef )
+ bError = true; /* TODO: implementation */
+ else if ( eOpCode == ocSpaces )
+ Add( new formula::FormulaByteToken( ocSpaces, static_cast<sal_uInt8>(nValue) ) );
+ else
+ bError = true;
+ }
+ break;
+ case uno::TypeClass_STRING:
+ {
+ OUString aStrVal( rToken.Data.get<OUString>() );
+ if ( eOpCode == ocPush )
+ AddString(rSPool.intern(aStrVal));
+ else if ( eOpCode == ocBad )
+ AddBad( aStrVal );
+ else if ( eOpCode == ocStringXML )
+ AddStringXML( aStrVal );
+ else if ( eOpCode == ocExternal || eOpCode == ocMacro )
+ Add( new formula::FormulaExternalToken( eOpCode, aStrVal ) );
+ else if ( eOpCode == ocWhitespace )
+ {
+ // Simply ignore empty string.
+ // Convention is one character repeated.
+ if (!aStrVal.isEmpty())
+ Add( new formula::FormulaSpaceToken( static_cast<sal_uInt8>(aStrVal.getLength()), aStrVal[0]));
+ }
+ else
+ bError = true; // unexpected string: don't know what to do with it
+ }
+ break;
+ default:
+ bError = true;
+ } // switch ( eClass )
+ return bError;
+}
+
+bool FormulaTokenArray::Fill(
+ const uno::Sequence<sheet::FormulaToken>& rSequence,
+ svl::SharedStringPool& rSPool, ExternalReferenceHelper* pExtRef )
+{
+ bool bError = false;
+ const sal_Int32 nCount = rSequence.getLength();
+ for (sal_Int32 nPos=0; nPos<nCount; nPos++)
+ {
+ bool bOneError = AddFormulaToken(rSequence[nPos], rSPool, pExtRef);
+ if (bOneError)
+ {
+ AddOpCode( ocErrName); // add something that indicates an error
+ bError = true;
+ }
+ }
+ return bError;
+}
+
+void FormulaTokenArray::DelRPN()
+{
+ if( nRPN )
+ {
+ FormulaToken** p = pRPN;
+ for( sal_uInt16 i = 0; i < nRPN; i++ )
+ {
+ (*p++)->DecRef();
+ }
+ delete [] pRPN;
+ }
+ pRPN = nullptr;
+ nRPN = 0;
+}
+
+FormulaToken* FormulaTokenArray::FirstToken() const
+{
+ if (!pCode || nLen == 0)
+ return nullptr;
+ return pCode[0];
+}
+
+FormulaToken* FormulaTokenArray::PeekPrev( sal_uInt16 & nIdx ) const
+{
+ if (0 < nIdx && nIdx <= nLen)
+ return pCode[--nIdx];
+ return nullptr;
+}
+
+FormulaToken* FormulaTokenArray::FirstRPNToken() const
+{
+ if (!pRPN || nRPN == 0)
+ return nullptr;
+ return pRPN[0];
+}
+
+FormulaToken* FormulaTokenArray::LastRPNToken() const
+{
+ if (!pRPN || nRPN == 0)
+ return nullptr;
+ return pRPN[nRPN - 1];
+}
+
+bool FormulaTokenArray::HasReferences() const
+{
+ for (auto i: Tokens())
+ {
+ if (i->IsRef())
+ return true;
+ }
+
+ for (auto i: RPNTokens())
+ {
+ if (i->IsRef())
+ return true;
+ }
+
+ return false;
+}
+
+bool FormulaTokenArray::HasExternalRef() const
+{
+ for (auto i: Tokens())
+ {
+ if (i->IsExternalRef())
+ return true;
+ }
+ return false;
+}
+
+bool FormulaTokenArray::HasOpCode( OpCode eOp ) const
+{
+ for (auto i: Tokens())
+ {
+ if (i->GetOpCode() == eOp)
+ return true;
+ }
+ return false;
+}
+
+bool FormulaTokenArray::HasOpCodeRPN( OpCode eOp ) const
+{
+ for (auto i: RPNTokens())
+ {
+ if (i->GetOpCode() == eOp)
+ return true;
+ }
+ return false;
+}
+
+bool FormulaTokenArray::HasNameOrColRowName() const
+{
+ for (auto i: Tokens())
+ {
+ if (i->GetType() == svIndex || i->GetOpCode() == ocColRowName )
+ return true;
+ }
+ return false;
+}
+
+bool FormulaTokenArray::HasOpCodes(const unordered_opcode_set& rOpCodes) const
+{
+ for (auto i: Tokens())
+ {
+ if (rOpCodes.count(i->GetOpCode()) > 0)
+ return true;
+ }
+
+ return false;
+}
+
+FormulaTokenArray::FormulaTokenArray() :
+ pRPN(nullptr),
+ nLen(0),
+ nRPN(0),
+ nError(FormulaError::NONE),
+ nMode(ScRecalcMode::NORMAL),
+ bHyperLink(false),
+ mbFromRangeName(false),
+ mbShareable(true),
+ mbFinalized(false)
+{
+}
+
+FormulaTokenArray::FormulaTokenArray( const FormulaTokenArray& rArr )
+{
+ Assign( rArr );
+}
+
+FormulaTokenArray::FormulaTokenArray( FormulaTokenArray&& rArr )
+{
+ Move( std::move(rArr) );
+}
+
+FormulaTokenArray::~FormulaTokenArray()
+{
+ FormulaTokenArray::Clear();
+}
+
+void FormulaTokenArray::Finalize()
+{
+ if( nLen && !mbFinalized )
+ {
+ // Add() overallocates, so reallocate to the minimum needed size.
+ std::unique_ptr<FormulaToken*[]> newCode(new FormulaToken*[ nLen ]);
+ std::copy(&pCode[0], &pCode[nLen], newCode.get());
+ pCode = std::move( newCode );
+ mbFinalized = true;
+ }
+}
+
+void FormulaTokenArray::Assign( const FormulaTokenArray& r )
+{
+ nLen = r.nLen;
+ nRPN = r.nRPN;
+ nError = r.nError;
+ nMode = r.nMode;
+ bHyperLink = r.bHyperLink;
+ mbFromRangeName = r.mbFromRangeName;
+ mbShareable = r.mbShareable;
+ mbFinalized = r.mbFinalized;
+ pCode = nullptr;
+ pRPN = nullptr;
+ FormulaToken** pp;
+ if( nLen )
+ {
+ pCode.reset(new FormulaToken*[ nLen ]);
+ pp = pCode.get();
+ memcpy( pp, r.pCode.get(), nLen * sizeof( FormulaToken* ) );
+ for( sal_uInt16 i = 0; i < nLen; i++ )
+ (*pp++)->IncRef();
+ mbFinalized = true;
+ }
+ if( nRPN )
+ {
+ pp = pRPN = new FormulaToken*[ nRPN ];
+ memcpy( pp, r.pRPN, nRPN * sizeof( FormulaToken* ) );
+ for( sal_uInt16 i = 0; i < nRPN; i++ )
+ (*pp++)->IncRef();
+ }
+}
+
+void FormulaTokenArray::Move( FormulaTokenArray&& r )
+{
+ pCode = std::move(r.pCode);
+ pRPN = r.pRPN;
+ r.pRPN = nullptr;
+ nLen = r.nLen;
+ r.nLen = 0;
+ nRPN = r.nRPN;
+ r.nRPN = 0;
+ nError = r.nError;
+ nMode = r.nMode;
+ bHyperLink = r.bHyperLink;
+ mbFromRangeName = r.mbFromRangeName;
+ mbShareable = r.mbShareable;
+ mbFinalized = r.mbFinalized;
+}
+
+/// Optimisation for efficiently creating StringXML placeholders
+void FormulaTokenArray::Assign( sal_uInt16 nCode, FormulaToken **pTokens )
+{
+ assert( nLen == 0 );
+ assert( pCode == nullptr );
+
+ nLen = nCode;
+ pCode.reset(new FormulaToken*[ nLen ]);
+ mbFinalized = true;
+
+ for( sal_uInt16 i = 0; i < nLen; i++ )
+ {
+ FormulaToken *t = pTokens[ i ];
+ assert( t->GetOpCode() == ocStringXML );
+ pCode[ i ] = t;
+ t->IncRef();
+ }
+}
+
+FormulaTokenArray& FormulaTokenArray::operator=( const FormulaTokenArray& rArr )
+{
+ if(this == &rArr)
+ return *this;
+
+ Clear();
+ Assign( rArr );
+ return *this;
+}
+
+FormulaTokenArray& FormulaTokenArray::operator=( FormulaTokenArray&& rArr )
+{
+ Clear();
+ Move( std::move(rArr) );
+ return *this;
+}
+
+void FormulaTokenArray::Clear()
+{
+ if( nRPN ) DelRPN();
+ if( pCode )
+ {
+ FormulaToken** p = pCode.get();
+ for( sal_uInt16 i = 0; i < nLen; i++ )
+ {
+ (*p++)->DecRef();
+ }
+ pCode.reset();
+ }
+ pRPN = nullptr;
+ nError = FormulaError::NONE;
+ nLen = nRPN = 0;
+ bHyperLink = false;
+ mbFromRangeName = false;
+ mbShareable = true;
+ mbFinalized = false;
+ ClearRecalcMode();
+}
+
+void FormulaTokenArray::CheckToken( const FormulaToken& /*r*/ )
+{
+ // Do nothing.
+}
+
+void FormulaTokenArray::CheckAllRPNTokens()
+{
+ if( nRPN )
+ {
+ FormulaToken** p = pRPN;
+ for( sal_uInt16 i = 0; i < nRPN; i++ )
+ {
+ CheckToken( *p[ i ] );
+ }
+ }
+}
+
+FormulaToken* FormulaTokenArray::AddToken( const FormulaToken& r )
+{
+ return Add( r.Clone() );
+}
+
+FormulaToken* FormulaTokenArray::MergeArray( )
+{
+ return nullptr;
+}
+
+FormulaToken* FormulaTokenArray::ReplaceToken( sal_uInt16 nOffset, FormulaToken* t,
+ FormulaTokenArray::ReplaceMode eMode )
+{
+ if (nOffset < nLen)
+ {
+ CheckToken(*t);
+ t->IncRef();
+ FormulaToken* p = pCode[nOffset];
+ pCode[nOffset] = t;
+ if (eMode == CODE_AND_RPN && p->GetRef() > 1)
+ {
+ for (sal_uInt16 i=0; i < nRPN; ++i)
+ {
+ if (pRPN[i] == p)
+ {
+ t->IncRef();
+ pRPN[i] = t;
+ p->DecRef();
+ if (p->GetRef() == 1)
+ break; // for
+ }
+ }
+ }
+ p->DecRef(); // may be dead now
+ return t;
+ }
+ else
+ {
+ t->DeleteIfZeroRef();
+ return nullptr;
+ }
+}
+
+sal_uInt16 FormulaTokenArray::RemoveToken( sal_uInt16 nOffset, sal_uInt16 nCount )
+{
+ if (nOffset < nLen)
+ {
+ SAL_WARN_IF( nOffset + nCount > nLen, "formula.core",
+ "FormulaTokenArray::RemoveToken - nOffset " << nOffset << " + nCount " << nCount << " > nLen " << nLen);
+ const sal_uInt16 nStop = ::std::min( static_cast<sal_uInt16>(nOffset + nCount), nLen);
+ nCount = nStop - nOffset;
+ for (sal_uInt16 j = nOffset; j < nStop; ++j)
+ {
+ FormulaToken* p = pCode[j];
+ if (p->GetRef() > 1)
+ {
+ for (sal_uInt16 i=0; i < nRPN; ++i)
+ {
+ if (pRPN[i] == p)
+ {
+ // Shift remaining tokens in pRPN down.
+ for (sal_uInt16 x=i+1; x < nRPN; ++x)
+ {
+ pRPN[x-1] = pRPN[x];
+ }
+ --nRPN;
+
+ p->DecRef();
+ if (p->GetRef() == 1)
+ break; // for
+ }
+ }
+ }
+ p->DecRef(); // may be dead now
+ }
+
+ // Shift remaining tokens in pCode down.
+ for (sal_uInt16 x = nStop; x < nLen; ++x)
+ {
+ pCode[x-nCount] = pCode[x];
+ }
+ nLen -= nCount;
+
+ return nCount;
+ }
+ else
+ {
+ SAL_WARN("formula.core","FormulaTokenArray::RemoveToken - nOffset " << nOffset << " >= nLen " << nLen);
+ return 0;
+ }
+}
+
+FormulaToken* FormulaTokenArray::Add( FormulaToken* t )
+{
+ assert(!mbFinalized);
+ if (mbFinalized)
+ {
+ t->DeleteIfZeroRef();
+ return nullptr;
+ }
+
+// Allocating an array of size FORMULA_MAXTOKENS is simple, but that results in relatively large
+// allocations that malloc() implementations usually do not handle as efficiently as smaller
+// sizes (not only in terms of memory usage but also speed). Since most token arrays are going
+// to be small, start with a small array and resize only if needed. Eventually Finalize() will
+// reallocate the memory to size exactly matching the requirements.
+ const size_t MAX_FAST_TOKENS = 32;
+ if( !pCode )
+ pCode.reset(new FormulaToken*[ MAX_FAST_TOKENS ]);
+ if( nLen == MAX_FAST_TOKENS )
+ {
+ FormulaToken** tmp = new FormulaToken*[ FORMULA_MAXTOKENS ];
+ std::copy(&pCode[0], &pCode[MAX_FAST_TOKENS], tmp);
+ pCode.reset(tmp);
+ }
+ if( nLen < FORMULA_MAXTOKENS - 1 )
+ {
+ CheckToken(*t);
+ pCode[ nLen++ ] = t;
+ t->IncRef();
+ if( t->GetOpCode() == ocArrayClose )
+ return MergeArray();
+ return t;
+ }
+ else
+ {
+ t->DeleteIfZeroRef();
+ if ( nLen == FORMULA_MAXTOKENS - 1 )
+ {
+ t = new FormulaByteToken( ocStop );
+ pCode[ nLen++ ] = t;
+ t->IncRef();
+ }
+ return nullptr;
+ }
+}
+
+FormulaToken* FormulaTokenArray::AddString( const svl::SharedString& rStr )
+{
+ return Add( new FormulaStringToken( rStr ) );
+}
+
+FormulaToken* FormulaTokenArray::AddDouble( double fVal )
+{
+ return Add( new FormulaDoubleToken( fVal ) );
+}
+
+void FormulaTokenArray::AddExternal( const sal_Unicode* pStr )
+{
+ AddExternal( OUString( pStr ) );
+}
+
+FormulaToken* FormulaTokenArray::AddExternal( const OUString& rStr,
+ OpCode eOp /* = ocExternal */ )
+{
+ return Add( new FormulaExternalToken( eOp, rStr ) );
+}
+
+FormulaToken* FormulaTokenArray::AddBad( const OUString& rStr )
+{
+ return Add( new FormulaStringOpToken( ocBad, svl::SharedString( rStr ) ) ); // string not interned
+}
+
+FormulaToken* FormulaTokenArray::AddStringXML( const OUString& rStr )
+{
+ return Add( new FormulaStringOpToken( ocStringXML, svl::SharedString( rStr ) ) ); // string not interned
+}
+
+
+void FormulaTokenArray::AddRecalcMode( ScRecalcMode nBits )
+{
+ const unsigned nExclusive = static_cast<sal_uInt8>(nBits & ScRecalcMode::EMask);
+ if (nExclusive)
+ {
+ unsigned nExBit;
+ if (nExclusive & (nExclusive - 1))
+ {
+ // More than one bit set, use highest priority.
+ for (nExBit = 1; (nExBit & static_cast<sal_uInt8>(ScRecalcMode::EMask)) != 0; nExBit <<= 1)
+ {
+ if (nExclusive & nExBit)
+ break;
+ }
+ }
+ else
+ {
+ // Only one bit is set.
+ nExBit = nExclusive;
+ }
+ // Set exclusive bit if priority is higher than existing.
+ if (nExBit < static_cast<sal_uInt8>(nMode & ScRecalcMode::EMask))
+ SetMaskedRecalcMode( static_cast<ScRecalcMode>(nExBit));
+ }
+ SetCombinedBitsRecalcMode( nBits );
+}
+
+
+bool FormulaTokenArray::HasMatrixDoubleRefOps() const
+{
+ if ( !pRPN || !nRPN )
+ return false;
+
+ // RPN-Interpreter simulation.
+ // Simply assumes a double as return value of each function.
+ std::unique_ptr<FormulaToken*[]> pStack(new FormulaToken* [nRPN]);
+ FormulaToken* pResult = new FormulaDoubleToken( 0.0 );
+ short sp = 0;
+ for ( auto t: RPNTokens() )
+ {
+ OpCode eOp = t->GetOpCode();
+ sal_uInt8 nParams = t->GetParamCount();
+ switch ( eOp )
+ {
+ case ocAdd :
+ case ocSub :
+ case ocMul :
+ case ocDiv :
+ case ocPow :
+ case ocPower :
+ case ocAmpersand :
+ case ocEqual :
+ case ocNotEqual :
+ case ocLess :
+ case ocGreater :
+ case ocLessEqual :
+ case ocGreaterEqual :
+ {
+ for ( sal_uInt8 k = nParams; k; k-- )
+ {
+ if ( sp >= k && pStack[sp-k]->GetType() == svDoubleRef )
+ {
+ pResult->Delete();
+ return true;
+ }
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ if ( eOp == ocPush || lcl_IsReference( eOp, t->GetType() ) )
+ pStack[sp++] = t;
+ else if (FormulaCompiler::IsOpCodeJumpCommand( eOp ))
+ { // ignore Jumps, pop previous Result (Condition)
+ if ( sp )
+ --sp;
+ }
+ else
+ { // pop parameters, push result
+ sp = sal::static_int_cast<short>( sp - nParams );
+ if ( sp < 0 )
+ {
+ SAL_WARN("formula.core", "FormulaTokenArray::HasMatrixDoubleRefOps: sp < 0" );
+ sp = 0;
+ }
+ pStack[sp++] = pResult;
+ }
+ }
+ pResult->Delete();
+
+ return false;
+}
+
+// --- Formula rewrite of a token array
+
+inline bool MissingConventionODF::isRewriteNeeded( OpCode eOp ) const
+{
+ switch (eOp)
+ {
+ case ocGammaDist:
+ case ocPoissonDist:
+ case ocAddress:
+ case ocLogInv:
+ case ocLogNormDist:
+ case ocNormDist:
+ return true;
+ case ocMissing:
+ case ocLog:
+ return isPODF(); // rewrite only for PODF
+ case ocDBCount:
+ case ocDBCount2:
+ return isODFF(); // rewrite only for ODFF
+ default:
+ return false;
+ }
+}
+
+/*
+ fdo 81596
+To be implemented yet:
+ ocExternal: ?
+ ocMacro: ?
+ ocIndex: INDEX() ?
+*/
+inline bool MissingConventionOOXML::isRewriteNeeded( OpCode eOp )
+{
+ switch (eOp)
+ {
+ case ocIf:
+
+ case ocExternal:
+ case ocEuroConvert:
+ case ocMacro:
+
+ case ocRound:
+ case ocRoundUp:
+ case ocRoundDown:
+
+ case ocIndex:
+
+ case ocCeil:
+ case ocFloor:
+
+ case ocGammaDist:
+ case ocFDist_LT:
+ case ocPoissonDist:
+ case ocNormDist:
+ case ocLogInv:
+ case ocLogNormDist:
+ case ocHypGeomDist:
+
+ case ocDBCount:
+ case ocDBCount2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+namespace {
+
+class FormulaMissingContext
+{
+ public:
+ const FormulaToken* mpFunc;
+ int mnCurArg;
+
+ void Clear() { mpFunc = nullptr; mnCurArg = 0; }
+ inline bool AddDefaultArg( FormulaTokenArray* pNewArr, int nArg, double f ) const;
+ bool AddMissingExternal( FormulaTokenArray* pNewArr ) const;
+ bool AddMissing( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const;
+ void AddMoreArgs( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const;
+};
+
+}
+
+void FormulaMissingContext::AddMoreArgs( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const
+{
+ if ( !mpFunc )
+ return;
+
+ switch (rConv.getConvention())
+ {
+ case MissingConvention::FORMULA_MISSING_CONVENTION_ODFF:
+ case MissingConvention::FORMULA_MISSING_CONVENTION_PODF:
+ {
+ switch (mpFunc->GetOpCode())
+ {
+ case ocGammaDist:
+ if (mnCurArg == 2)
+ {
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 1.0 ); // 4th, Cumulative=true()
+ }
+ break;
+ case ocPoissonDist:
+ if (mnCurArg == 1)
+ {
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 1.0 ); // 3rd, Cumulative=true()
+ }
+ break;
+ case ocNormDist:
+ if ( mnCurArg == 2 )
+ {
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 1.0 ); // 4th, Cumulative=true()
+ }
+ break;
+ case ocLogInv:
+ case ocLogNormDist:
+ if ( mnCurArg == 0 )
+ {
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 0.0 ); // 2nd, mean = 0.0
+ }
+ if ( mnCurArg <= 1 )
+ {
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 1.0 ); // 3rd, standard deviation = 1.0
+ }
+ break;
+ case ocLog:
+ if ( rConv.isPODF() && mnCurArg == 0 )
+ {
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 10.0 ); // 2nd, basis 10
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case MissingConvention::FORMULA_MISSING_CONVENTION_OOXML:
+ {
+ switch (mpFunc->GetOpCode())
+ {
+ case ocIf:
+ if( mnCurArg == 0 )
+ {
+ // Excel needs at least two parameters in IF function
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddOpCode( ocTrue ); // 2nd, true() as function
+ pNewArr->AddOpCode( ocOpen ); // so the result is of logical type
+ pNewArr->AddOpCode( ocClose ); // and survives roundtrip
+ }
+ break;
+
+ case ocEuroConvert:
+ if ( mnCurArg == 2 )
+ {
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 0.0 ); // 4th, FullPrecision = false()
+ }
+ break;
+
+ case ocPoissonDist:
+ if (mnCurArg == 1)
+ {
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 1.0 ); // 3rd, Cumulative=true()
+ }
+ break;
+
+ case ocGammaDist:
+ case ocFDist_LT:
+ case ocNormDist:
+ if (mnCurArg == 2)
+ {
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 1.0 ); // 4th, Cumulative=true()
+ }
+ break;
+
+ case ocLogInv:
+ case ocLogNormDist:
+ if ( mnCurArg == 0 )
+ {
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 0.0 ); // 2nd, mean = 0.0
+ }
+ if ( mnCurArg <= 1 )
+ {
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 1.0 ); // 3rd, standard deviation = 1.0
+ }
+ break;
+
+ case ocHypGeomDist:
+ if ( mnCurArg == 3 )
+ {
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 0.0 ); // 5th, Cumulative = false()
+ }
+ break;
+
+ case ocRound:
+ case ocRoundUp:
+ case ocRoundDown:
+ if( mnCurArg == 0 )
+ {
+ // ROUND, ROUNDUP, ROUNDDOWN functions are fixed to 2 parameters in Excel
+ pNewArr->AddOpCode( ocSep );
+ pNewArr->AddDouble( 0.0 ); // 2nd, 0.0
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+}
+
+inline bool FormulaMissingContext::AddDefaultArg( FormulaTokenArray* pNewArr, int nArg, double f ) const
+{
+ if (mnCurArg == nArg)
+ {
+ pNewArr->AddDouble( f );
+ return true;
+ }
+ return false;
+}
+
+bool FormulaMissingContext::AddMissingExternal( FormulaTokenArray *pNewArr ) const
+{
+ // Only called for PODF, not ODFF. No need to distinguish.
+
+ const OUString &rName = mpFunc->GetExternal();
+
+ // initial (fast) checks:
+ sal_Int32 nLength = rName.getLength();
+ if (!nLength)
+ return false;
+
+ sal_Unicode nLastChar = rName[ nLength - 1];
+ if ( nLastChar != 't' && nLastChar != 'm' )
+ return false;
+
+ if (rName.equalsIgnoreAsciiCase(
+ "com.sun.star.sheet.addin.Analysis.getAccrint" ))
+ {
+ return AddDefaultArg( pNewArr, 4, 1000.0 );
+ }
+ if (rName.equalsIgnoreAsciiCase(
+ "com.sun.star.sheet.addin.Analysis.getAccrintm" ))
+ {
+ return AddDefaultArg( pNewArr, 3, 1000.0 );
+ }
+ return false;
+}
+
+bool FormulaMissingContext::AddMissing( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const
+{
+ if ( !mpFunc )
+ return false;
+
+ bool bRet = false;
+ const OpCode eOp = mpFunc->GetOpCode();
+
+ switch (rConv.getConvention())
+ {
+ case MissingConvention::FORMULA_MISSING_CONVENTION_ODFF:
+ {
+ // Add for ODFF
+ switch (eOp)
+ {
+ case ocAddress:
+ return AddDefaultArg( pNewArr, 2, 1.0 ); // abs
+ default:
+ break;
+ }
+ }
+ break;
+ case MissingConvention::FORMULA_MISSING_CONVENTION_PODF:
+ {
+ // Add for PODF
+ switch (eOp)
+ {
+ case ocAddress:
+ return AddDefaultArg( pNewArr, 2, 1.0 ); // abs
+ case ocFixed:
+ return AddDefaultArg( pNewArr, 1, 2.0 );
+ case ocBetaDist:
+ case ocBetaInv:
+ case ocPMT:
+ return AddDefaultArg( pNewArr, 3, 0.0 );
+ case ocIpmt:
+ case ocPpmt:
+ return AddDefaultArg( pNewArr, 4, 0.0 );
+ case ocPV:
+ case ocFV:
+ bRet |= AddDefaultArg( pNewArr, 2, 0.0 ); // pmt
+ bRet |= AddDefaultArg( pNewArr, 3, 0.0 ); // [fp]v
+ break;
+ case ocRate:
+ bRet |= AddDefaultArg( pNewArr, 1, 0.0 ); // pmt
+ bRet |= AddDefaultArg( pNewArr, 3, 0.0 ); // fv
+ bRet |= AddDefaultArg( pNewArr, 4, 0.0 ); // type
+ break;
+ case ocExternal:
+ return AddMissingExternal( pNewArr );
+
+ // --- more complex cases ---
+
+ case ocOffset:
+ // FIXME: rather tough
+ // if arg 3 (height) omitted, export arg1 (rows)
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case MissingConvention::FORMULA_MISSING_CONVENTION_OOXML:
+ {
+ switch (eOp)
+ {
+ case ocExternal:
+ return AddMissingExternal( pNewArr );
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ return bRet;
+}
+
+bool FormulaTokenArray::NeedsPodfRewrite( const MissingConventionODF & rConv )
+{
+ for ( auto i: Tokens() )
+ {
+ if ( rConv.isRewriteNeeded( i->GetOpCode()))
+ return true;
+ }
+ return false;
+}
+
+bool FormulaTokenArray::NeedsOoxmlRewrite()
+{
+ for ( auto i: Tokens() )
+ {
+ if ( MissingConventionOOXML::isRewriteNeeded( i->GetOpCode()))
+ return true;
+ }
+ return false;
+}
+
+
+FormulaTokenArray * FormulaTokenArray::RewriteMissing( const MissingConvention & rConv )
+{
+ const size_t nAlloc = 256;
+ FormulaMissingContext aCtx[ nAlloc ];
+
+ /* TODO: with some effort we might be able to merge the two almost
+ * identical function stacks into one and generalize things, otherwise
+ * adding yet another "omit argument" would be copypasta. */
+
+ int aOpCodeAddressStack[ nAlloc ]; // use of ADDRESS() function
+ const int nOmitAddressArg = 3; // ADDRESS() 4th parameter A1/R1C1
+
+ int aOpCodeDcountStack[ nAlloc ]; // use of DCOUNT()/DCOUNTA() function
+ const int nOmitDcountArg = 1; // DCOUNT() and DCOUNTA() 2nd parameter DatabaseField if 0
+
+ sal_uInt16 nTokens = GetLen() + 1;
+ FormulaMissingContext* pCtx = (nAlloc < nTokens ? new FormulaMissingContext[nTokens] : &aCtx[0]);
+ int* pOcas = (nAlloc < nTokens ? new int[nTokens] : &aOpCodeAddressStack[0]);
+ int* pOcds = (nAlloc < nTokens ? new int[nTokens] : &aOpCodeDcountStack[0]);
+ // Never go below 0, never use 0, mpFunc always NULL.
+ pCtx[0].Clear();
+ int nFn = 0;
+ int nOcas = 0;
+ int nOcds = 0;
+
+ FormulaTokenArray *pNewArr = new FormulaTokenArray;
+ // At least ScRecalcMode::ALWAYS needs to be set.
+ pNewArr->AddRecalcMode( GetRecalcMode());
+
+ FormulaTokenArrayPlainIterator aIter(*this);
+ for ( FormulaToken *pCur = aIter.First(); pCur; pCur = aIter.Next() )
+ {
+ bool bAdd = true;
+ // Don't write the expression of the new inserted ADDRESS() parameter.
+ // Do NOT omit the new second parameter of INDIRECT() though. If that
+ // was done for both, INDIRECT() actually could calculate different and
+ // valid (but wrong) results with the then changed return value of
+ // ADDRESS(). Better let it generate an error instead.
+ for (int i = nOcas; i-- > 0 && bAdd; )
+ {
+ if (pCtx[ pOcas[ i ] ].mnCurArg == nOmitAddressArg)
+ {
+ // Omit everything except a trailing separator, the leading
+ // separator is omitted below. The other way around would leave
+ // an extraneous separator if no parameter followed.
+ if (pOcas[ i ] != nFn || pCur->GetOpCode() != ocSep)
+ bAdd = false;
+ }
+ }
+ // Strip the 2nd argument (leaving empty) of DCOUNT() and DCOUNTA() if
+ // it is 0.
+ for (int i = nOcds; i-- > 0 && bAdd; )
+ {
+ if (pCtx[ pOcds[ i ] ].mnCurArg == nOmitDcountArg)
+ {
+ // Omit only a literal 0 value, nothing else.
+ if (pOcds[ i ] == nFn && pCur->GetOpCode() == ocPush && pCur->GetType() == svDouble &&
+ pCur->GetDouble() == 0.0)
+ {
+ // No other expression, between separators.
+ FormulaToken* p = aIter.PeekPrevNoSpaces();
+ if (p && p->GetOpCode() == ocSep)
+ {
+ p = aIter.PeekNextNoSpaces();
+ if (p && p->GetOpCode() == ocSep)
+ bAdd = false;
+ }
+ }
+ }
+ }
+ switch ( pCur->GetOpCode() )
+ {
+ case ocOpen:
+ {
+ ++nFn; // all following operations on _that_ function
+ pCtx[ nFn ].mpFunc = aIter.PeekPrevNoSpaces();
+ pCtx[ nFn ].mnCurArg = 0;
+ if (rConv.isPODF() && pCtx[ nFn ].mpFunc && pCtx[ nFn ].mpFunc->GetOpCode() == ocAddress)
+ pOcas[ nOcas++ ] = nFn; // entering ADDRESS() if PODF
+ else if ((rConv.isODFF() || rConv.isOOXML()) && pCtx[ nFn ].mpFunc)
+ {
+ OpCode eOp = pCtx[ nFn ].mpFunc->GetOpCode();
+ if (eOp == ocDBCount || eOp == ocDBCount2)
+ pOcds[ nOcds++ ] = nFn; // entering DCOUNT() or DCOUNTA() if ODFF or OOXML
+ }
+ }
+ break;
+ case ocClose:
+ pCtx[ nFn ].AddMoreArgs( pNewArr, rConv );
+ SAL_WARN_IF(nFn <= 0, "formula.core", "FormulaTokenArray::RewriteMissing: underflow");
+ if (nOcas > 0 && pOcas[ nOcas-1 ] == nFn)
+ --nOcas; // leaving ADDRESS()
+ else if (nOcds > 0 && pOcds[ nOcds-1 ] == nFn)
+ --nOcds; // leaving DCOUNT() or DCOUNTA()
+ if (nFn > 0)
+ --nFn;
+ break;
+ case ocSep:
+ pCtx[ nFn ].mnCurArg++;
+ // Omit leading separator of ADDRESS() parameter.
+ if (nOcas && pOcas[ nOcas-1 ] == nFn && pCtx[ nFn ].mnCurArg == nOmitAddressArg)
+ {
+ bAdd = false;
+ }
+ break;
+ case ocMissing:
+ if ( bAdd )
+ bAdd = !pCtx[ nFn ].AddMissing( pNewArr, rConv );
+ break;
+ default:
+ break;
+ }
+ if (bAdd)
+ {
+ OpCode eOp = pCur->GetOpCode();
+ if ( ( eOp == ocCeil || eOp == ocFloor ||
+ ( eOp == ocLogNormDist && pCur->GetByte() == 4 ) ) &&
+ rConv.getConvention() == MissingConvention::FORMULA_MISSING_CONVENTION_OOXML )
+ {
+ switch ( eOp )
+ {
+ case ocCeil :
+ eOp = ocCeil_Math;
+ break;
+ case ocFloor :
+ eOp = ocFloor_Math;
+ break;
+ case ocLogNormDist :
+ eOp = ocLogNormDist_MS;
+ break;
+ default :
+ eOp = ocNone;
+ break;
+ }
+ FormulaToken *pToken = new FormulaToken( svByte, eOp );
+ pNewArr->Add( pToken );
+ }
+ else if ( eOp == ocHypGeomDist &&
+ rConv.getConvention() == MissingConvention::FORMULA_MISSING_CONVENTION_OOXML )
+ {
+ FormulaToken *pToken = new FormulaToken( svByte, ocHypGeomDist_MS );
+ pNewArr->Add( pToken );
+ }
+ else
+ pNewArr->AddToken( *pCur );
+ }
+ }
+
+ if (pOcds != &aOpCodeDcountStack[0])
+ delete [] pOcds;
+ if (pOcas != &aOpCodeAddressStack[0])
+ delete [] pOcas;
+ if (pCtx != &aCtx[0])
+ delete [] pCtx;
+
+ return pNewArr;
+}
+
+namespace {
+inline bool isWhitespace( OpCode eOp ) { return eOp == ocSpaces || eOp == ocWhitespace; }
+}
+
+bool FormulaTokenArray::MayReferenceFollow()
+{
+ if ( !pCode || nLen <= 0 )
+ return false;
+
+ // ignore trailing spaces
+ sal_uInt16 i = nLen - 1;
+ while (i > 0 && isWhitespace( pCode[i]->GetOpCode()))
+ {
+ --i;
+ }
+ if (i > 0 || !isWhitespace( pCode[i]->GetOpCode()))
+ {
+ OpCode eOp = pCode[i]->GetOpCode();
+ if ( (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP ) ||
+ (SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP ) ||
+ eOp == SC_OPCODE_OPEN || eOp == SC_OPCODE_SEP )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+FormulaToken* FormulaTokenArray::AddOpCode( OpCode eOp )
+{
+ FormulaToken* pRet = nullptr;
+ switch ( eOp )
+ {
+ case ocOpen:
+ case ocClose:
+ case ocSep:
+ case ocArrayOpen:
+ case ocArrayClose:
+ case ocArrayRowSep:
+ case ocArrayColSep:
+ pRet = new FormulaToken( svSep,eOp );
+ break;
+ case ocIf:
+ case ocIfError:
+ case ocIfNA:
+ case ocChoose:
+ {
+ short nJump[FORMULA_MAXJUMPCOUNT + 1];
+ if ( eOp == ocIf )
+ nJump[ 0 ] = 3;
+ else if ( eOp == ocChoose )
+ nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
+ else
+ nJump[ 0 ] = 2;
+ pRet = new FormulaJumpToken( eOp, nJump );
+ }
+ break;
+ default:
+ pRet = new FormulaByteToken( eOp, 0, ParamClass::Unknown );
+ break;
+ }
+ return Add( pRet );
+}
+
+void FormulaTokenArray::ReinternStrings( svl::SharedStringPool& rPool )
+{
+ for (auto i: Tokens())
+ {
+ switch (i->GetType())
+ {
+ case svString:
+ i->SetString( rPool.intern( i->GetString().getString()));
+ break;
+ default:
+ ; // nothing
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+
+FormulaTokenIterator::Item::Item(const FormulaTokenArray* pArray, short pc, short stop) :
+ pArr(pArray), nPC(pc), nStop(stop)
+{
+}
+
+FormulaTokenIterator::FormulaTokenIterator( const FormulaTokenArray& rArr )
+ : maStack{ FormulaTokenIterator::Item(&rArr, -1, SHRT_MAX) }
+{
+}
+
+FormulaTokenIterator::~FormulaTokenIterator()
+{
+}
+
+void FormulaTokenIterator::Push( const FormulaTokenArray* pArr )
+{
+ FormulaTokenIterator::Item item(pArr, -1, SHRT_MAX);
+
+ maStack.push_back(item);
+}
+
+void FormulaTokenIterator::Pop()
+{
+ maStack.pop_back();
+}
+
+void FormulaTokenIterator::Reset()
+{
+ while( maStack.size() > 1 )
+ maStack.pop_back();
+
+ maStack.back().nPC = -1;
+}
+
+FormulaToken* FormulaTokenArrayPlainIterator::GetNextName()
+{
+ if( mpFTA->GetArray() )
+ {
+ while ( mnIndex < mpFTA->GetLen() )
+ {
+ FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ];
+ if( t->GetType() == svIndex )
+ return t;
+ }
+ }
+ return nullptr;
+}
+
+const FormulaToken* FormulaTokenIterator::Next()
+{
+ const FormulaToken* t = GetNonEndOfPathToken( ++maStack.back().nPC );
+ if( !t && maStack.size() > 1 )
+ {
+ Pop();
+ t = Next();
+ }
+ return t;
+}
+
+const FormulaToken* FormulaTokenIterator::PeekNextOperator()
+{
+ const FormulaToken* t = nullptr;
+ short nIdx = maStack.back().nPC;
+ for (;;)
+ {
+ t = GetNonEndOfPathToken( ++nIdx);
+ if (t == nullptr || t->GetOpCode() != ocPush)
+ break; // ignore operands
+ }
+ if (!t && maStack.size() > 1)
+ {
+ FormulaTokenIterator::Item aHere = maStack.back();
+ maStack.pop_back();
+ t = PeekNextOperator();
+ maStack.push_back(aHere);
+ }
+ return t;
+}
+
+//! The nPC counts after a Push() are -1
+
+void FormulaTokenIterator::Jump( short nStart, short nNext, short nStop )
+{
+ maStack.back().nPC = nNext;
+ if( nStart != nNext )
+ {
+ Push( maStack.back().pArr );
+ maStack.back().nPC = nStart;
+ maStack.back().nStop = nStop;
+ }
+}
+
+void FormulaTokenIterator::ReInit( const FormulaTokenArray& rArr )
+{
+ maStack.clear();
+ Push( &rArr );
+}
+
+const FormulaToken* FormulaTokenIterator::GetNonEndOfPathToken( short nIdx ) const
+{
+ FormulaTokenIterator::Item cur = maStack.back();
+
+ if (nIdx < cur.pArr->GetCodeLen() && nIdx < cur.nStop)
+ {
+ const FormulaToken* t = cur.pArr->GetCode()[ nIdx ];
+ // such an OpCode ends an IF() or CHOOSE() path
+ return (t->GetOpCode() == ocSep || t->GetOpCode() == ocClose) ? nullptr : t;
+ }
+ return nullptr;
+}
+
+bool FormulaTokenIterator::IsEndOfPath() const
+{
+ return GetNonEndOfPathToken( maStack.back().nPC + 1) == nullptr;
+}
+
+FormulaToken* FormulaTokenArrayPlainIterator::GetNextReference()
+{
+ while( mnIndex < mpFTA->GetLen() )
+ {
+ FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ];
+ switch( t->GetType() )
+ {
+ case svSingleRef:
+ case svDoubleRef:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ return t;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ return nullptr;
+}
+
+FormulaToken* FormulaTokenArrayPlainIterator::GetNextColRowName()
+{
+ while( mnIndex < mpFTA->GetLen() )
+ {
+ FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ];
+ if ( t->GetOpCode() == ocColRowName )
+ return t;
+ }
+ return nullptr;
+}
+
+FormulaToken* FormulaTokenArrayPlainIterator::GetNextReferenceRPN()
+{
+ while( mnIndex < mpFTA->GetCodeLen() )
+ {
+ FormulaToken* t = mpFTA->GetCode()[ mnIndex++ ];
+ switch( t->GetType() )
+ {
+ case svSingleRef:
+ case svDoubleRef:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ return t;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ return nullptr;
+}
+
+FormulaToken* FormulaTokenArrayPlainIterator::GetNextReferenceOrName()
+{
+ if( !mpFTA->GetArray() )
+ return nullptr;
+
+ while ( mnIndex < mpFTA->GetLen() )
+ {
+ FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ];
+ switch( t->GetType() )
+ {
+ case svSingleRef:
+ case svDoubleRef:
+ case svIndex:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svExternalName:
+ return t;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ return nullptr;
+}
+
+FormulaToken* FormulaTokenArrayPlainIterator::Next()
+{
+ if( mpFTA->GetArray() && mnIndex < mpFTA->GetLen() )
+ return mpFTA->GetArray()[ mnIndex++ ];
+ else
+ return nullptr;
+}
+
+FormulaToken* FormulaTokenArrayPlainIterator::NextNoSpaces()
+{
+ if( mpFTA->GetArray() )
+ {
+ while ((mnIndex < mpFTA->GetLen()) && isWhitespace( mpFTA->GetArray()[ mnIndex ]->GetOpCode()))
+ ++mnIndex;
+ if( mnIndex < mpFTA->GetLen() )
+ return mpFTA->GetArray()[ mnIndex++ ];
+ }
+ return nullptr;
+}
+
+FormulaToken* FormulaTokenArrayPlainIterator::NextRPN()
+{
+ if( mpFTA->GetCode() && mnIndex < mpFTA->GetCodeLen() )
+ return mpFTA->GetCode()[ mnIndex++ ];
+ else
+ return nullptr;
+}
+
+FormulaToken* FormulaTokenArrayPlainIterator::PrevRPN()
+{
+ if( mpFTA->GetCode() && mnIndex )
+ return mpFTA->GetCode()[ --mnIndex ];
+ else
+ return nullptr;
+}
+
+FormulaToken* FormulaTokenArrayPlainIterator::PeekNext()
+{
+ if( mpFTA->GetArray() && mnIndex < mpFTA->GetLen() )
+ return mpFTA->GetArray()[ mnIndex ];
+ else
+ return nullptr;
+}
+
+FormulaToken* FormulaTokenArrayPlainIterator::PeekNextNoSpaces() const
+{
+ if( mpFTA->GetArray() && mnIndex < mpFTA->GetLen() )
+ {
+ sal_uInt16 j = mnIndex;
+ while (j < mpFTA->GetLen() && isWhitespace( mpFTA->GetArray()[j]->GetOpCode()))
+ j++;
+ if ( j < mpFTA->GetLen() )
+ return mpFTA->GetArray()[ j ];
+ else
+ return nullptr;
+ }
+ else
+ return nullptr;
+}
+
+FormulaToken* FormulaTokenArrayPlainIterator::PeekPrevNoSpaces() const
+{
+ if( mpFTA->GetArray() && mnIndex > 1 )
+ {
+ sal_uInt16 j = mnIndex - 2;
+ while (isWhitespace( mpFTA->GetArray()[j]->GetOpCode()) && j > 0 )
+ j--;
+ if (j > 0 || !isWhitespace( mpFTA->GetArray()[j]->GetOpCode()))
+ return mpFTA->GetArray()[ j ];
+ else
+ return nullptr;
+ }
+ else
+ return nullptr;
+}
+
+void FormulaTokenArrayPlainIterator::AfterRemoveToken( sal_uInt16 nOffset, sal_uInt16 nCount )
+{
+ const sal_uInt16 nStop = std::min( static_cast<sal_uInt16>(nOffset + nCount), mpFTA->GetLen());
+
+ if (mnIndex >= nOffset)
+ {
+ if (mnIndex < nStop)
+ mnIndex = nOffset + 1;
+ else
+ mnIndex -= nStop - nOffset;
+ }
+}
+
+// real implementations of virtual functions
+
+
+double FormulaDoubleToken::GetDouble() const { return fDouble; }
+double & FormulaDoubleToken::GetDoubleAsReference() { return fDouble; }
+
+sal_Int16 FormulaDoubleToken::GetDoubleType() const
+{
+ // This is a plain double value without type information, don't emit a
+ // warning via FormulaToken::GetDoubleType().
+ return 0;
+}
+
+bool FormulaDoubleToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && fDouble == r.GetDouble();
+}
+
+sal_Int16 FormulaTypedDoubleToken::GetDoubleType() const
+{
+ return mnType;
+}
+
+void FormulaTypedDoubleToken::SetDoubleType( sal_Int16 nType )
+{
+ mnType = nType;
+}
+
+bool FormulaTypedDoubleToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaDoubleToken::operator==( r ) && mnType == r.GetDoubleType();
+}
+
+FormulaStringToken::FormulaStringToken( svl::SharedString r ) :
+ FormulaToken( svString ), maString(std::move( r ))
+{
+}
+
+FormulaStringToken::FormulaStringToken( const FormulaStringToken& r ) :
+ FormulaToken( r ), maString( r.maString ) {}
+
+FormulaToken* FormulaStringToken::Clone() const
+{
+ return new FormulaStringToken(*this);
+}
+
+const svl::SharedString & FormulaStringToken::GetString() const
+{
+ return maString;
+}
+
+void FormulaStringToken::SetString( const svl::SharedString& rStr )
+{
+ maString = rStr;
+}
+
+bool FormulaStringToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && maString == r.GetString();
+}
+
+FormulaStringOpToken::FormulaStringOpToken( OpCode e, svl::SharedString r ) :
+ FormulaByteToken( e, 0, svString, ParamClass::Unknown ), maString(std::move( r )) {}
+
+FormulaStringOpToken::FormulaStringOpToken( const FormulaStringOpToken& r ) :
+ FormulaByteToken( r ), maString( r.maString ) {}
+
+FormulaToken* FormulaStringOpToken::Clone() const
+{
+ return new FormulaStringOpToken(*this);
+}
+
+const svl::SharedString & FormulaStringOpToken::GetString() const
+{
+ return maString;
+}
+
+void FormulaStringOpToken::SetString( const svl::SharedString& rStr )
+{
+ maString = rStr;
+}
+
+bool FormulaStringOpToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaByteToken::operator==( r ) && maString == r.GetString();
+}
+
+sal_uInt16 FormulaIndexToken::GetIndex() const { return nIndex; }
+void FormulaIndexToken::SetIndex( sal_uInt16 n ) { nIndex = n; }
+sal_Int16 FormulaIndexToken::GetSheet() const { return mnSheet; }
+void FormulaIndexToken::SetSheet( sal_Int16 n ) { mnSheet = n; }
+bool FormulaIndexToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && nIndex == r.GetIndex() &&
+ mnSheet == r.GetSheet();
+}
+const OUString& FormulaExternalToken::GetExternal() const { return aExternal; }
+bool FormulaExternalToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaByteToken::operator==( r ) && aExternal == r.GetExternal();
+}
+
+
+FormulaError FormulaErrorToken::GetError() const { return nError; }
+void FormulaErrorToken::SetError( FormulaError nErr ) { nError = nErr; }
+bool FormulaErrorToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) &&
+ nError == static_cast< const FormulaErrorToken & >(r).GetError();
+}
+double FormulaMissingToken::GetDouble() const { return 0.0; }
+
+const svl::SharedString & FormulaMissingToken::GetString() const
+{
+ return svl::SharedString::getEmptyString();
+}
+
+bool FormulaMissingToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r );
+}
+
+
+bool FormulaUnknownToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r );
+}
+
+
+} // formula
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/core/api/vectortoken.cxx b/formula/source/core/api/vectortoken.cxx
new file mode 100644
index 0000000000..c1bd521577
--- /dev/null
+++ b/formula/source/core/api/vectortoken.cxx
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <formula/vectortoken.hxx>
+#include <sal/log.hxx>
+
+namespace formula {
+
+VectorRefArray::VectorRefArray() :
+ mpNumericArray(nullptr),
+ mpStringArray(nullptr),
+ mbValid(true) {}
+
+VectorRefArray::VectorRefArray( InitInvalid ) :
+ mpNumericArray(nullptr),
+ mpStringArray(nullptr),
+ mbValid(false) {}
+
+VectorRefArray::VectorRefArray( const double* pArray ) :
+ mpNumericArray(pArray),
+ mpStringArray(nullptr),
+ mbValid(true) {}
+
+VectorRefArray::VectorRefArray( rtl_uString** pArray ) :
+ mpNumericArray(nullptr),
+ mpStringArray(pArray),
+ mbValid(true) {}
+
+VectorRefArray::VectorRefArray( const double* pNumArray, rtl_uString** pStrArray ) :
+ mpNumericArray(pNumArray),
+ mpStringArray(pStrArray),
+ mbValid(true) {}
+
+bool VectorRefArray::isValid() const
+{
+ return mbValid;
+}
+
+SingleVectorRefToken::SingleVectorRefToken( const VectorRefArray& rArray, size_t nArrayLength ) :
+ FormulaToken(svSingleVectorRef, ocPush), maArray(rArray), mnArrayLength(nArrayLength)
+{
+ SAL_INFO("formula.core", "Created SingleVectorRefToken nArrayLength=" << nArrayLength);
+}
+
+FormulaToken* SingleVectorRefToken::Clone() const
+{
+ return new SingleVectorRefToken(maArray, mnArrayLength);
+}
+
+const VectorRefArray& SingleVectorRefToken::GetArray() const
+{
+ return maArray;
+}
+
+size_t SingleVectorRefToken::GetArrayLength() const
+{
+ return mnArrayLength;
+}
+
+DoubleVectorRefToken::DoubleVectorRefToken(
+ std::vector<VectorRefArray>&& rArrays, size_t nArrayLength,
+ size_t nRefRowSize, bool bStartFixed, bool bEndFixed ) :
+ FormulaToken(svDoubleVectorRef, ocPush),
+ maArrays(std::move(rArrays)), mnArrayLength(nArrayLength),
+ mnRefRowSize(nRefRowSize), mbStartFixed(bStartFixed), mbEndFixed(bEndFixed)
+{
+ SAL_INFO("formula.core", "Created DoubleVectorRefToken nArrayLength=" << nArrayLength);
+}
+
+FormulaToken* DoubleVectorRefToken::Clone() const
+{
+ return new DoubleVectorRefToken(
+ std::vector(maArrays), mnArrayLength, mnRefRowSize, mbStartFixed, mbEndFixed);
+}
+
+const std::vector<VectorRefArray>& DoubleVectorRefToken::GetArrays() const
+{
+ return maArrays;
+}
+
+size_t DoubleVectorRefToken::GetArrayLength() const
+{
+ return mnArrayLength;
+}
+
+size_t DoubleVectorRefToken::GetRefRowSize() const
+{
+ return mnRefRowSize;
+}
+
+bool DoubleVectorRefToken::IsStartFixed() const
+{
+ return mbStartFixed;
+}
+
+bool DoubleVectorRefToken::IsEndFixed() const
+{
+ return mbEndFixed;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/core/resource/core_resource.cxx b/formula/source/core/resource/core_resource.cxx
new file mode 100644
index 0000000000..d184323579
--- /dev/null
+++ b/formula/source/core/resource/core_resource.cxx
@@ -0,0 +1,23 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <core_resource.hxx>
+
+OUString ForResId(TranslateId aId) { return Translate::get(aId, Translate::Create("for")); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/ui/dlg/ControlHelper.hxx b/formula/source/ui/dlg/ControlHelper.hxx
new file mode 100644
index 0000000000..2495474516
--- /dev/null
+++ b/formula/source/ui/dlg/ControlHelper.hxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <formula/funcutl.hxx>
+
+namespace formula
+{
+
+class ParaWin;
+
+
+class ArgEdit : public RefEdit
+{
+public:
+ ArgEdit(std::unique_ptr<weld::Entry> xControl);
+
+ void Init(ArgEdit* pPrevEdit, ArgEdit* pNextEdit,
+ weld::ScrolledWindow& rArgSlider,
+ ParaWin& rParaWin, sal_uInt16 nArgCount);
+
+protected:
+ virtual bool KeyInput(const KeyEvent& rKEvt) override;
+
+private:
+ ArgEdit* pEdPrev;
+ ArgEdit* pEdNext;
+ weld::ScrolledWindow* pSlider;
+ ParaWin* pParaWin;
+ sal_uInt16 nArgs;
+};
+
+
+
+class ArgInput final
+{
+private:
+ Link<ArgInput&,void> aFxClickLink;
+ Link<ArgInput&,void> aFxFocusLink;
+ Link<ArgInput&,void> aEdFocusLink;
+ Link<ArgInput&,void> aEdModifyLink;
+
+ weld::Label*pFtArg;
+ weld::Button* pBtnFx;
+ ArgEdit* pEdArg;
+ RefButton* pRefBtn;
+
+ DECL_LINK( FxBtnClickHdl, weld::Button&, void );
+ DECL_LINK( FxBtnFocusHdl, weld::Widget&, void );
+ DECL_LINK( EdFocusHdl, RefEdit&, void );
+ DECL_LINK( EdModifyHdl, RefEdit&, void );
+
+public:
+
+ ArgInput();
+
+ void InitArgInput(weld::Label* pftArg,
+ weld::Button* pbtnFx,
+ ArgEdit* pedArg,
+ RefButton* prefBtn);
+
+ void SetArgName(const OUString &aArg);
+ OUString GetArgName() const;
+ void SetArgNameFont(const vcl::Font&);
+
+ void SetArgVal(const OUString &aVal);
+ OUString GetArgVal() const;
+
+ void SelectAll();
+
+ ArgEdit* GetArgEdPtr() { return pEdArg; }
+
+
+ void SetFxClickHdl( const Link<ArgInput&,void>& rLink ) { aFxClickLink = rLink; }
+
+ void SetFxFocusHdl( const Link<ArgInput&,void>& rLink ) { aFxFocusLink = rLink; }
+
+ void SetEdFocusHdl( const Link<ArgInput&,void>& rLink ) { aEdFocusLink = rLink; }
+
+ void SetEdModifyHdl( const Link<ArgInput&,void>& rLink ) { aEdModifyLink = rLink; }
+
+ void Hide();
+ void Show();
+
+ void UpdateAccessibleNames();
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/ui/dlg/FormulaHelper.cxx b/formula/source/ui/dlg/FormulaHelper.cxx
new file mode 100644
index 0000000000..225cef3c7b
--- /dev/null
+++ b/formula/source/ui/dlg/FormulaHelper.cxx
@@ -0,0 +1,416 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <algorithm>
+
+#include <formula/formulahelper.hxx>
+#include <formula/IFunctionDescription.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/syslocale.hxx>
+
+namespace formula
+{
+
+ namespace
+ {
+
+ class OEmptyFunctionDescription : public IFunctionDescription
+ {
+ public:
+ OEmptyFunctionDescription(){}
+ virtual ~OEmptyFunctionDescription(){}
+
+ virtual OUString getFunctionName() const override { return OUString(); }
+ virtual const IFunctionCategory* getCategory() const override { return nullptr; }
+ virtual OUString getDescription() const override { return OUString(); }
+ virtual sal_Int32 getSuppressedArgumentCount() const override { return 0; }
+ virtual OUString getFormula(const ::std::vector< OUString >& ) const override { return OUString(); }
+ virtual void fillVisibleArgumentMapping(::std::vector<sal_uInt16>& ) const override {}
+ virtual void initArgumentInfo() const override {}
+ virtual OUString getSignature() const override { return OUString(); }
+ virtual OUString getHelpId() const override { return ""; }
+ virtual bool isHidden() const override { return false; }
+ virtual sal_uInt32 getParameterCount() const override { return 0; }
+ virtual sal_uInt32 getVarArgsStart() const override { return 0; }
+ virtual sal_uInt32 getVarArgsLimit() const override { return 0; }
+ virtual OUString getParameterName(sal_uInt32 ) const override { return OUString(); }
+ virtual OUString getParameterDescription(sal_uInt32 ) const override { return OUString(); }
+ virtual bool isParameterOptional(sal_uInt32 ) const override { return false; }
+ };
+ }
+
+// class FormulaHelper - static Method
+
+
+#define FUNC_NOTFOUND -1
+
+FormulaHelper::FormulaHelper(const IFunctionManager* _pFunctionManager)
+ :m_rCharClass(m_aSysLocale.GetCharClass())
+ ,m_pFunctionManager(_pFunctionManager)
+ ,open(_pFunctionManager->getSingleToken(IFunctionManager::eOk))
+ ,close(_pFunctionManager->getSingleToken(IFunctionManager::eClose))
+ ,sep(_pFunctionManager->getSingleToken(IFunctionManager::eSep))
+ ,arrayOpen(_pFunctionManager->getSingleToken(IFunctionManager::eArrayOpen))
+ ,arrayClose(_pFunctionManager->getSingleToken(IFunctionManager::eArrayClose))
+{
+}
+
+sal_Int32 FormulaHelper::GetCategoryCount() const
+{
+ return m_pFunctionManager->getCount();
+}
+
+bool FormulaHelper::GetNextFunc( const OUString& rFormula,
+ bool bBack,
+ sal_Int32& rFStart, // Input and output
+ sal_Int32* pFEnd, // = NULL
+ const IFunctionDescription** ppFDesc, // = NULL
+ ::std::vector< OUString>* pArgs ) const // = NULL
+{
+ sal_Int32 nOldStart = rFStart;
+ OUString aFname;
+
+ rFStart = GetFunctionStart( rFormula, rFStart, bBack, ppFDesc ? &aFname : nullptr );
+ bool bFound = ( rFStart != FUNC_NOTFOUND );
+
+ if ( bFound )
+ {
+ if ( pFEnd )
+ *pFEnd = GetFunctionEnd( rFormula, rFStart );
+
+ if ( ppFDesc )
+ {
+ *ppFDesc = nullptr;
+ const sal_uInt32 nCategoryCount = m_pFunctionManager->getCount();
+ for(sal_uInt32 j= 0; j < nCategoryCount && !*ppFDesc; ++j)
+ {
+ const IFunctionCategory* pCategory = m_pFunctionManager->getCategory(j);
+ const sal_uInt32 nCount = pCategory->getCount();
+ for(sal_uInt32 i = 0 ; i < nCount; ++i)
+ {
+ const IFunctionDescription* pCurrent = pCategory->getFunction(i);
+ if ( pCurrent->getFunctionName().equalsIgnoreAsciiCase(aFname) )
+ {
+ *ppFDesc = pCurrent;
+ break;
+ }
+ }// for(sal_uInt32 i = 0 ; i < nCount; ++i)
+ }
+ if ( *ppFDesc && pArgs )
+ {
+ GetArgStrings( *pArgs,rFormula, rFStart, static_cast<sal_uInt16>((*ppFDesc)->getParameterCount() ));
+ }
+ else
+ {
+ static OEmptyFunctionDescription s_aFunctionDescription;
+ *ppFDesc = &s_aFunctionDescription;
+ }
+ }
+ }
+ else
+ rFStart = nOldStart;
+
+ return bFound;
+}
+
+
+void FormulaHelper::FillArgStrings( std::u16string_view rFormula,
+ sal_Int32 nFuncPos,
+ sal_uInt16 nArgs,
+ ::std::vector< OUString >& _rArgs ) const
+{
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = 0;
+ sal_uInt16 i;
+ bool bLast = false;
+
+ for ( i=0; i<nArgs && !bLast; i++ )
+ {
+ nStart = GetArgStart( rFormula, nFuncPos, i );
+
+ if ( i+1<nArgs ) // last argument?
+ {
+ nEnd = GetArgStart( rFormula, nFuncPos, i+1 );
+
+ if ( nEnd != nStart )
+ _rArgs.push_back(OUString(rFormula.substr( nStart, nEnd-1-nStart )));
+ else
+ {
+ _rArgs.emplace_back();
+ bLast = true;
+ }
+ }
+ else
+ {
+ nEnd = GetFunctionEnd( rFormula, nFuncPos )-1;
+ if ( nStart < nEnd )
+ _rArgs.push_back( OUString(rFormula.substr( nStart, nEnd-nStart )) );
+ else
+ _rArgs.emplace_back();
+ }
+ }
+
+ if ( bLast )
+ for ( ; i<nArgs; i++ )
+ _rArgs.emplace_back();
+}
+
+
+void FormulaHelper::GetArgStrings( ::std::vector< OUString >& _rArgs,
+ std::u16string_view rFormula,
+ sal_Int32 nFuncPos,
+ sal_uInt16 nArgs ) const
+{
+ if (nArgs)
+ {
+ FillArgStrings( rFormula, nFuncPos, nArgs, _rArgs );
+ }
+}
+
+
+static bool IsFormulaText(const CharClass& rCharClass, const OUString& rStr, sal_Int32 nPos)
+{
+ if( rCharClass.isLetterNumeric( rStr, nPos ) )
+ return true;
+ else
+ { // In internationalized versions function names may contain a dot
+ // and in every version also an underscore... ;-)
+ sal_Unicode c = rStr[nPos];
+ return c == '.' || c == '_';
+ }
+
+}
+
+sal_Int32 FormulaHelper::GetFunctionStart( const OUString& rFormula,
+ sal_Int32 nStart,
+ bool bBack,
+ OUString* pFuncName ) const
+{
+ sal_Int32 nStrLen = rFormula.getLength();
+
+ if ( nStrLen < nStart )
+ return nStart;
+
+ sal_Int32 nFStart = FUNC_NOTFOUND;
+ sal_Int32 nParPos = bBack ? ::std::min( nStart, nStrLen - 1) : nStart;
+
+ bool bRepeat;
+ do
+ {
+ bool bFound = false;
+ bRepeat = false;
+
+ if ( bBack )
+ {
+ while ( !bFound && (nParPos > 0) )
+ {
+ if ( rFormula[nParPos] == '"' )
+ {
+ nParPos--;
+ while ( (nParPos > 0) && rFormula[nParPos] != '"' )
+ nParPos--;
+ if (nParPos > 0)
+ nParPos--;
+ }
+ else
+ {
+ bFound = rFormula[nParPos] == '(';
+ if ( !bFound )
+ nParPos--;
+ }
+ }
+ }
+ else
+ {
+ while ( !bFound && (0 <= nParPos && nParPos < nStrLen) )
+ {
+ if ( rFormula[nParPos] == '"' )
+ {
+ nParPos++;
+ while ( (nParPos < nStrLen) && rFormula[nParPos] != '"' )
+ nParPos++;
+ nParPos++;
+ }
+ else
+ {
+ bFound = rFormula[nParPos] == '(';
+ if ( !bFound )
+ nParPos++;
+ }
+ }
+ }
+
+ if ( bFound && (nParPos > 0) )
+ {
+ nFStart = nParPos-1;
+
+ while ( (nFStart > 0) && IsFormulaText(m_rCharClass, rFormula, nFStart ))
+ nFStart--;
+ }
+
+ nFStart++;
+
+ if ( bFound )
+ {
+ if ( IsFormulaText( m_rCharClass, rFormula, nFStart ) )
+ {
+ // Function found
+ if ( pFuncName )
+ *pFuncName = rFormula.copy( nFStart, nParPos-nFStart );
+ }
+ else // Brackets without function -> keep searching
+ {
+ bRepeat = true;
+ if ( !bBack )
+ nParPos++;
+ else if (nParPos > 0)
+ nParPos--;
+ else
+ bRepeat = false;
+ }
+ }
+ else // No brackets found
+ {
+ nFStart = FUNC_NOTFOUND;
+ if ( pFuncName )
+ pFuncName->clear();
+ }
+ }
+ while(bRepeat);
+
+ return nFStart;
+}
+
+
+sal_Int32 FormulaHelper::GetFunctionEnd( std::u16string_view rStr, sal_Int32 nStart ) const
+{
+ sal_Int32 nStrLen = rStr.size();
+
+ if ( nStrLen < nStart )
+ return nStart;
+
+ short nParCount = 0;
+ bool bInArray = false;
+ bool bFound = false;
+
+ while ( !bFound && (nStart < nStrLen) )
+ {
+ sal_Unicode c = rStr[nStart];
+
+ if ( c == '"' )
+ {
+ nStart++;
+ while ( (nStart < nStrLen) && rStr[nStart] != '"' )
+ nStart++;
+ }
+ else if ( c == open )
+ nParCount++;
+ else if ( c == close )
+ {
+ nParCount--;
+ if ( nParCount == 0 )
+ bFound = true;
+ else if ( nParCount < 0 )
+ {
+ bFound = true;
+ nStart--; // read one too far
+ }
+ }
+ else if ( c == arrayOpen )
+ {
+ bInArray = true;
+ }
+ else if ( c == arrayClose )
+ {
+ bInArray = false;
+ }
+ else if ( c == sep )
+ {
+ if ( !bInArray && nParCount == 0 )
+ {
+ bFound = true;
+ nStart--; // read one too far
+ }
+ }
+ nStart++; // Set behind found position
+ }
+
+ // nStart > nStrLen can happen if there was an unclosed quote; instead of
+ // checking that in every loop iteration check it once here.
+ return std::min(nStart, nStrLen);
+}
+
+
+sal_Int32 FormulaHelper::GetArgStart( std::u16string_view rStr, sal_Int32 nStart, sal_uInt16 nArg ) const
+{
+ sal_Int32 nStrLen = rStr.size();
+
+ if ( nStrLen < nStart )
+ return nStart;
+
+ short nParCount = 0;
+ bool bInArray = false;
+ bool bFound = false;
+
+ while ( !bFound && (nStart < nStrLen) )
+ {
+ sal_Unicode c = rStr[nStart];
+
+ if ( c == '"' )
+ {
+ nStart++;
+ while ( (nStart < nStrLen) && rStr[nStart] != '"' )
+ nStart++;
+ }
+ else if ( c == open )
+ {
+ bFound = ( nArg == 0 );
+ nParCount++;
+ }
+ else if ( c == close )
+ {
+ nParCount--;
+ bFound = ( nParCount == 0 );
+ }
+ else if ( c == arrayOpen )
+ {
+ bInArray = true;
+ }
+ else if ( c == arrayClose )
+ {
+ bInArray = false;
+ }
+ else if ( c == sep )
+ {
+ if ( !bInArray && nParCount == 1 )
+ {
+ nArg--;
+ bFound = ( nArg == 0 );
+ }
+ }
+ nStart++;
+ }
+
+ return nStart;
+}
+
+} // formula
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/ui/dlg/formula.cxx b/formula/source/ui/dlg/formula.cxx
new file mode 100644
index 0000000000..123642c46c
--- /dev/null
+++ b/formula/source/ui/dlg/formula.cxx
@@ -0,0 +1,1961 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <sal/log.hxx>
+
+#include <unotools/charclass.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include "funcpage.hxx"
+#include <formula/formula.hxx>
+#include <formula/IFunctionDescription.hxx>
+#include <formula/FormulaCompiler.hxx>
+#include <formula/token.hxx>
+#include <formula/tokenarray.hxx>
+#include <formula/formdata.hxx>
+#include <formula/formulahelper.hxx>
+#include "structpg.hxx"
+#include "parawin.hxx"
+#include <strings.hrc>
+#include <core_resource.hxx>
+#include <com/sun/star/sheet/FormulaToken.hpp>
+#include <com/sun/star/sheet/FormulaLanguage.hpp>
+#include <com/sun/star/sheet/FormulaMapGroup.hpp>
+#include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
+#include <com/sun/star/sheet/XFormulaOpCodeMapper.hpp>
+#include <com/sun/star/sheet/XFormulaParser.hpp>
+#include <map>
+
+// For tab page
+#define TOKEN_OPEN 0
+#define TOKEN_CLOSE 1
+namespace formula
+{
+
+using namespace ::com::sun::star;
+
+class FormulaDlg_Impl
+{
+public:
+ ::std::pair<RefButton*, RefEdit*>
+ RefInputStartBefore( RefEdit* pEdit, RefButton* pButton );
+ void RefInputStartAfter();
+ void RefInputDoneAfter( bool bForced );
+ bool CalcValue( const OUString& rStrExp, OUString& rStrResult, bool bForceMatrixFormula = false );
+ void CalcStruct( const OUString& rStrExp, bool bForceRecalcStruct = false );
+ void UpdateValues( bool bForceRecalcStruct = false );
+ void DeleteArgs();
+ sal_Int32 GetFunctionPos(sal_Int32 nPos);
+ void ClearAllParas();
+
+ void MakeTree(StructPage* _pTree, weld::TreeIter* pParent, const FormulaToken* pFuncToken,
+ const FormulaToken* _pToken, tools::Long Count);
+ void fillTree(StructPage* _pTree);
+ void UpdateTokenArray( const OUString& rStrExp);
+ OUString RepairFormula(const OUString& aFormula);
+ void FillDialog(bool bFlag = true);
+ bool EditNextFunc( bool bForward, sal_Int32 nFStart = NOT_FOUND );
+ void EditThisFunc(sal_Int32 nFStart);
+
+ OUString GetPrevFuncExpression( bool bStartFromEnd );
+
+ void StoreFormEditData(FormEditData* pEditData);
+
+ void Update();
+ void Update(const OUString& _sExp);
+
+ void SaveArg( sal_uInt16 nEd );
+ void UpdateSelection();
+ void DoEnter( bool bOk );
+ void FillListboxes();
+ void FillControls( bool &rbNext, bool &rbPrev);
+
+ FormulaDlgMode SetMeText( const OUString& _sText, sal_Int32 PrivStart, sal_Int32 PrivEnd, bool bMatrix, bool _bSelect, bool _bUpdate);
+ void SetMeText(const OUString& _sText);
+ bool CheckMatrix(OUString& aFormula /*IN/OUT*/);
+
+ void SetEdSelection();
+
+ bool UpdateParaWin(Selection& _rSelection);
+ void UpdateParaWin( const Selection& _rSelection, const OUString& _sRefStr);
+
+ void SetData( sal_Int32 nFStart, sal_Int32 nNextFStart, sal_Int32 nNextFEnd, sal_Int32& PrivStart, sal_Int32& PrivEnd);
+
+ RefEdit* GetCurrRefEdit();
+
+ const FormulaHelper& GetFormulaHelper() const { return m_aFormulaHelper;}
+ void InitFormulaOpCodeMapper();
+
+ void UpdateOldSel();
+ void FormulaCursor();
+
+ DECL_LINK( ModifyHdl, ParaWin&, void );
+ DECL_LINK( FxHdl, ParaWin&, void );
+
+ DECL_LINK( MatrixHdl, weld::Toggleable&, void );
+ DECL_LINK( FormulaHdl, weld::TextView&, void);
+ DECL_LINK( FormulaCursorHdl, weld::TextView&, void );
+ DECL_LINK( BtnHdl, weld::Button&, void );
+ DECL_LINK( DblClkHdl, FuncPage&, void );
+ DECL_LINK( FuncSelHdl, FuncPage&, void );
+ DECL_LINK( StructSelHdl, StructPage&, void );
+public:
+ mutable uno::Reference< sheet::XFormulaOpCodeMapper> m_xOpCodeMapper;
+ uno::Sequence< sheet::FormulaToken > m_aTokenList;
+ ::std::unique_ptr<FormulaTokenArray> m_pTokenArray;
+ ::std::optional<FormulaTokenArrayPlainIterator> m_oTokenArrayIterator;
+ mutable uno::Sequence< sheet::FormulaOpCodeMapEntry > m_aSpecialOpCodes;
+ mutable uno::Sequence< sheet::FormulaToken > m_aSeparatorsOpCodes;
+ mutable uno::Sequence< sheet::FormulaOpCodeMapEntry > m_aFunctionOpCodes;
+ mutable const sheet::FormulaOpCodeMapEntry* m_pFunctionOpCodesEnd;
+ ::std::map<const FormulaToken*, sheet::FormulaToken> m_aTokenMap;
+ IFormulaEditorHelper* m_pHelper;
+ weld::Dialog& m_rDialog;
+
+ OUString m_aOldFormula;
+ bool m_bStructUpdate;
+ bool m_bUserMatrixFlag;
+
+ const OUString m_aTitle1;
+ const OUString m_aTitle2;
+ FormulaHelper m_aFormulaHelper;
+
+ OUString m_aEditHelpId;
+
+ OUString m_aOldHelp;
+ bool m_bMakingTree; // in method of constructing tree
+
+ bool m_bEditFlag;
+ const IFunctionDescription* m_pFuncDesc;
+ sal_Int32 m_nArgs;
+ ::std::vector< OUString > m_aArguments;
+ Selection m_aFuncSel;
+
+ sal_Int32 m_nFuncExpStart; ///< current formula position for treeview results
+
+ int m_nSelectionStart;
+ int m_nSelectionEnd;
+
+ RefEdit* m_pTheRefEdit;
+ RefButton* m_pTheRefButton;
+
+ std::unique_ptr<weld::Notebook> m_xTabCtrl;
+ std::unique_ptr<weld::Container> m_xParaWinBox;
+ std::unique_ptr<ParaWin> m_xParaWin;
+ std::unique_ptr<weld::Label> m_xFtHeadLine;
+ std::unique_ptr<weld::Label> m_xFtFuncName;
+ std::unique_ptr<weld::Label> m_xFtFuncDesc;
+
+ std::unique_ptr<weld::Label> m_xFtEditName;
+
+ std::unique_ptr<weld::Label> m_xFtResult;
+ std::unique_ptr<weld::Entry> m_xWndResult;
+
+ std::unique_ptr<weld::Label> m_xFtFormula;
+ std::unique_ptr<weld::TextView> m_xMEdit;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnMatrix;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+
+ std::unique_ptr<weld::Button> m_xBtnBackward;
+ std::unique_ptr<weld::Button> m_xBtnForward;
+ std::unique_ptr<weld::Button> m_xBtnEnd;
+
+ std::unique_ptr<weld::Label> m_xFtFormResult;
+ std::unique_ptr<weld::Entry> m_xWndFormResult;
+
+ std::unique_ptr<RefEdit> m_xEdRef;
+ std::unique_ptr<RefButton> m_xRefBtn;
+
+ std::unique_ptr<FuncPage> m_xFuncPage;
+ std::unique_ptr<StructPage> m_xStructPage;
+
+ FormulaDlg_Impl(weld::Dialog& rDialog,
+ weld::Builder& rBuilder,
+ bool _bSupportFunctionResult,
+ bool _bSupportResult,
+ bool _bSupportMatrix,
+ IFormulaEditorHelper* _pHelper,
+ const IFunctionManager* _pFunctionMgr,
+ IControlReferenceHandler* _pDlg);
+ ~FormulaDlg_Impl();
+};
+
+FormulaDlg_Impl::FormulaDlg_Impl(weld::Dialog& rDialog,
+ weld::Builder& rBuilder,
+ bool _bSupportFunctionResult,
+ bool _bSupportResult,
+ bool _bSupportMatrix,
+ IFormulaEditorHelper* _pHelper,
+ const IFunctionManager* _pFunctionMgr,
+ IControlReferenceHandler* _pDlg)
+ : m_pFunctionOpCodesEnd(nullptr)
+ , m_pHelper(_pHelper)
+ , m_rDialog(rDialog)
+ , m_bUserMatrixFlag(false)
+ , m_aTitle1( ForResId( STR_TITLE1 ) )
+ , m_aTitle2( ForResId( STR_TITLE2 ) )
+ , m_aFormulaHelper(_pFunctionMgr)
+ , m_bMakingTree(false)
+ , m_pFuncDesc(nullptr)
+ , m_nArgs(0)
+ , m_nFuncExpStart(0)
+ , m_nSelectionStart(-1)
+ , m_nSelectionEnd(-1)
+ , m_pTheRefEdit(nullptr)
+ , m_pTheRefButton(nullptr)
+ , m_xTabCtrl(rBuilder.weld_notebook("tabcontrol"))
+ , m_xParaWinBox(rBuilder.weld_container("BOX"))
+ , m_xFtHeadLine(rBuilder.weld_label("headline"))
+ , m_xFtFuncName(rBuilder.weld_label("funcname"))
+ , m_xFtFuncDesc(rBuilder.weld_label("funcdesc"))
+ , m_xFtEditName(rBuilder.weld_label("editname"))
+ , m_xFtResult(rBuilder.weld_label("label2"))
+ , m_xWndResult(rBuilder.weld_entry("result"))
+ , m_xFtFormula(rBuilder.weld_label("formula"))
+ , m_xMEdit(rBuilder.weld_text_view("ed_formula"))
+ , m_xBtnMatrix(rBuilder.weld_check_button("array"))
+ , m_xBtnCancel(rBuilder.weld_button("cancel"))
+ , m_xBtnBackward(rBuilder.weld_button("back"))
+ , m_xBtnForward(rBuilder.weld_button("next"))
+ , m_xBtnEnd(rBuilder.weld_button("ok"))
+ , m_xFtFormResult(rBuilder.weld_label("label1"))
+ , m_xWndFormResult(rBuilder.weld_entry("formula_result"))
+ , m_xEdRef(new RefEdit(rBuilder.weld_entry("ED_REF")))
+ , m_xRefBtn(new RefButton(rBuilder.weld_button("RB_REF")))
+{
+ auto nWidth = m_xMEdit->get_approximate_digit_width() * 62;
+
+ //Space for two lines of text
+ m_xFtHeadLine->set_label("X\nX\n");
+ auto nHeight = m_xFtHeadLine->get_preferred_size().Height();
+ m_xFtHeadLine->set_size_request(nWidth, nHeight);
+ m_xFtHeadLine->set_label("");
+
+ m_xFtFuncName->set_label("X\nX\n");
+ nHeight = m_xFtFuncName->get_preferred_size().Height();
+ m_xFtFuncName->set_size_request(nWidth, nHeight);
+ m_xFtFuncDesc->set_size_request(nWidth, nHeight);
+ m_xFtFuncName->set_label("");
+
+ m_xMEdit->set_size_request(nWidth,
+ m_xMEdit->get_height_rows(5));
+
+ m_xEdRef->SetReferences(_pDlg, m_xFtEditName.get());
+ m_xRefBtn->SetReferences(_pDlg, m_xEdRef.get());
+
+ m_xParaWin.reset(new ParaWin(m_xParaWinBox.get(), _pDlg));
+ m_xParaWin->Show();
+ m_xParaWinBox->hide();
+ m_xFtEditName->hide();
+ m_xEdRef->GetWidget()->hide();
+ m_xRefBtn->GetWidget()->hide();
+
+ m_xMEdit->set_accessible_name(m_xFtFormula->get_label());
+
+ m_aEditHelpId = m_xMEdit->get_help_id();
+
+ m_bEditFlag =false;
+ m_bStructUpdate =true;
+ m_xParaWin->SetArgModifiedHdl( LINK( this, FormulaDlg_Impl, ModifyHdl ) );
+ m_xParaWin->SetFxHdl( LINK( this, FormulaDlg_Impl, FxHdl ) );
+
+ m_xFuncPage.reset(new FuncPage(m_xTabCtrl->get_page("functiontab"), _pFunctionMgr));
+ m_xStructPage.reset(new StructPage(m_xTabCtrl->get_page("structtab")));
+ m_xTabCtrl->set_current_page("functiontab");
+
+ m_aOldHelp = m_rDialog.get_help_id(); // HelpId from resource always for "Page 1"
+
+ m_xFtResult->set_visible( _bSupportResult );
+ m_xWndResult->set_visible( _bSupportResult );
+
+ m_xFtFormResult->set_visible( _bSupportFunctionResult );
+ m_xWndFormResult->set_visible( _bSupportFunctionResult );
+
+ if ( _bSupportMatrix )
+ m_xBtnMatrix->connect_toggled( LINK( this, FormulaDlg_Impl, MatrixHdl ) );
+ else
+ m_xBtnMatrix->hide();
+
+ m_xBtnCancel->connect_clicked( LINK( this, FormulaDlg_Impl, BtnHdl ) );
+ m_xBtnEnd->connect_clicked( LINK( this, FormulaDlg_Impl, BtnHdl ) );
+ m_xBtnForward->connect_clicked( LINK( this, FormulaDlg_Impl, BtnHdl ) );
+ m_xBtnBackward->connect_clicked( LINK( this, FormulaDlg_Impl, BtnHdl ) );
+
+ m_xFuncPage->SetDoubleClickHdl( LINK( this, FormulaDlg_Impl, DblClkHdl ) );
+ m_xFuncPage->SetSelectHdl( LINK( this, FormulaDlg_Impl, FuncSelHdl) );
+ m_xStructPage->SetSelectionHdl( LINK( this, FormulaDlg_Impl, StructSelHdl ) );
+ m_xMEdit->connect_changed( LINK( this, FormulaDlg_Impl, FormulaHdl ) );
+ m_xMEdit->connect_cursor_position( LINK( this, FormulaDlg_Impl, FormulaCursorHdl ) );
+
+ vcl::Font aFntLight = m_xFtFormula->get_font();
+ vcl::Font aFntBold = aFntLight;
+ aFntBold.SetWeight( WEIGHT_BOLD );
+
+ m_xParaWin->SetArgumentFonts( aFntBold, aFntLight);
+}
+
+FormulaDlg_Impl::~FormulaDlg_Impl()
+{
+ m_xTabCtrl->remove_page("functiontab");
+ m_xTabCtrl->remove_page("structtab");
+
+ DeleteArgs();
+}
+
+void FormulaDlg_Impl::StoreFormEditData(FormEditData* pData)
+{
+ if (!pData) // it won't be destroyed via Close
+ return;
+
+ int nStartPos, nEndPos;
+ m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
+ if (nStartPos > nEndPos)
+ std::swap(nStartPos, nEndPos);
+
+ pData->SetFStart(nStartPos);
+ pData->SetSelection(Selection(nStartPos, nEndPos));
+
+ if (m_xTabCtrl->get_current_page_ident() == "functiontab")
+ pData->SetMode( FormulaDlgMode::Formula );
+ else
+ pData->SetMode( FormulaDlgMode::Edit );
+ pData->SetUndoStr(m_xMEdit->get_text());
+ pData->SetMatrixFlag(m_xBtnMatrix->get_active());
+}
+
+void FormulaDlg_Impl::InitFormulaOpCodeMapper()
+{
+ if ( m_xOpCodeMapper.is() )
+ return;
+
+ m_xOpCodeMapper = m_pHelper->getFormulaOpCodeMapper();
+ m_aFunctionOpCodes = m_xOpCodeMapper->getAvailableMappings( sheet::FormulaLanguage::ODFF, sheet::FormulaMapGroup::FUNCTIONS);
+ m_pFunctionOpCodesEnd = m_aFunctionOpCodes.getConstArray() + m_aFunctionOpCodes.getLength();
+
+ // 0:TOKEN_OPEN, 1:TOKEN_CLOSE, 2:TOKEN_SEP
+ uno::Sequence< OUString > aArgs { "(", ")", ";" };
+ m_aSeparatorsOpCodes = m_xOpCodeMapper->getMappings( aArgs, sheet::FormulaLanguage::ODFF);
+
+ m_aSpecialOpCodes = m_xOpCodeMapper->getAvailableMappings( sheet::FormulaLanguage::ODFF, sheet::FormulaMapGroup::SPECIAL);
+}
+
+void FormulaDlg_Impl::DeleteArgs()
+{
+ ::std::vector< OUString>().swap(m_aArguments);
+ m_nArgs = 0;
+}
+
+sal_Int32 FormulaDlg_Impl::GetFunctionPos(sal_Int32 nPos)
+{
+ if ( !m_aTokenList.hasElements() )
+ return SAL_MAX_INT32;
+
+ const sal_Unicode sep = m_pHelper->getFunctionManager()->getSingleToken(IFunctionManager::eSep);
+
+ sal_Int32 nFuncPos = SAL_MAX_INT32;
+ OUString aFormString = m_aFormulaHelper.GetCharClass().uppercase(m_xMEdit->get_text());
+
+ const uno::Reference< sheet::XFormulaParser > xParser(m_pHelper->getFormulaParser());
+ const table::CellAddress aRefPos(m_pHelper->getReferencePosition());
+
+ const sheet::FormulaToken* pIter = m_aTokenList.getConstArray();
+ const sheet::FormulaToken* pEnd = pIter + m_aTokenList.getLength();
+ try
+ {
+ bool bFlag = false;
+ sal_Int32 nTokPos = 1;
+ sal_Int32 nOldTokPos = 1;
+ sal_Int32 nPrevFuncPos = 1;
+ short nBracketCount = 0;
+ const sal_Int32 nOpPush = m_aSpecialOpCodes[sheet::FormulaMapGroupSpecialOffset::PUSH].Token.OpCode;
+ const sal_Int32 nOpSpaces = m_aSpecialOpCodes[sheet::FormulaMapGroupSpecialOffset::SPACES].Token.OpCode;
+ const sal_Int32 nOpWhitespace = m_aSpecialOpCodes[sheet::FormulaMapGroupSpecialOffset::WHITESPACE].Token.OpCode;
+ while ( pIter != pEnd )
+ {
+ const sal_Int32 eOp = pIter->OpCode;
+ uno::Sequence<sheet::FormulaToken> aArgs { *pIter };
+ const OUString aString = xParser->printFormula( aArgs, aRefPos);
+ const sheet::FormulaToken* pNextToken = pIter + 1;
+
+ if ( !m_bUserMatrixFlag && FormulaCompiler::IsMatrixFunction(static_cast<OpCode>(eOp)) )
+ {
+ m_xBtnMatrix->set_active(true);
+ }
+
+ if (eOp == nOpPush || eOp == nOpSpaces || eOp == nOpWhitespace)
+ {
+ const sal_Int32 n1 = nTokPos < 0 ? -1 : aFormString.indexOf( sep, nTokPos);
+ const sal_Int32 n2 = nTokPos < 0 ? -1 : aFormString.indexOf( ')', nTokPos);
+ sal_Int32 nXXX = nTokPos;
+ if ( n1 < n2 && n1 != -1 )
+ {
+ nTokPos = n1;
+ }
+ else
+ {
+ nTokPos = n2;
+ }
+ if ( pNextToken != pEnd )
+ {
+ aArgs.getArray()[0] = *pNextToken;
+ const OUString a2String = xParser->printFormula( aArgs, aRefPos);
+ const sal_Int32 n3 = nXXX < 0 ? -1 : aFormString.indexOf( a2String, nXXX);
+ if (n3 < nTokPos && n3 != -1)
+ nTokPos = n3;
+ }
+ }
+ else
+ {
+ nTokPos = nTokPos + aString.getLength();
+ }
+
+ if ( eOp == m_aSeparatorsOpCodes[TOKEN_OPEN].OpCode )
+ {
+ nBracketCount++;
+ bFlag = true;
+ }
+ else if ( eOp == m_aSeparatorsOpCodes[TOKEN_CLOSE].OpCode )
+ {
+ nBracketCount--;
+ bFlag = false;
+ nFuncPos = nPrevFuncPos;
+ }
+ bool bIsFunction = std::any_of( m_aFunctionOpCodes.getConstArray(),
+ m_pFunctionOpCodesEnd,
+ [&eOp](const sheet::FormulaOpCodeMapEntry& aEntry) { return aEntry.Token.OpCode == eOp; });
+
+ if ( bIsFunction && nOpSpaces != eOp && nOpWhitespace != eOp )
+ {
+ nPrevFuncPos = nFuncPos;
+ nFuncPos = nOldTokPos;
+ }
+
+ if ( nOldTokPos <= nPos && nPos < nTokPos )
+ {
+ if ( !bIsFunction )
+ {
+ if ( nBracketCount < 1 )
+ {
+ nFuncPos = m_xMEdit->get_text().getLength();
+ }
+ else if ( !bFlag )
+ {
+ nFuncPos = nPrevFuncPos;
+ }
+ }
+ break;
+ }
+
+ pIter = pNextToken;
+ nOldTokPos = nTokPos;
+ } // while ( pIter != pEnd )
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("formula.ui", "FormulaDlg_Impl::GetFunctionPos");
+ }
+
+ return nFuncPos;
+}
+
+bool FormulaDlg_Impl::CalcValue( const OUString& rStrExp, OUString& rStrResult, bool bForceMatrixFormula )
+{
+ bool bResult = true;
+
+ if ( !rStrExp.isEmpty() )
+ {
+ // Only calculate the value when there isn't any more keyboard input:
+
+ // Make this debuggable by assigning to a variable that can be changed
+ // from within the debugger.
+ bool bInput = Application::AnyInput( VclInputFlags::KEYBOARD );
+ if ( !bInput )
+ {
+ bResult = m_pHelper->calculateValue( rStrExp, rStrResult, bForceMatrixFormula || m_xBtnMatrix->get_active());
+ }
+ else
+ bResult = false;
+ }
+
+ return bResult;
+}
+
+void FormulaDlg_Impl::UpdateValues( bool bForceRecalcStruct )
+{
+ // Take a force-array context into account. RPN creation propagated those
+ // to tokens that are ref-counted so also available in the token array.
+ bool bForceArray = false;
+ // Only necessary if it's not a matrix formula anyway and matrix evaluation
+ // is supported, i.e. the button is visible.
+ if (m_xBtnMatrix->get_visible() && !m_xBtnMatrix->get_active())
+ {
+ std::unique_ptr<FormulaCompiler> pCompiler(m_pHelper->createCompiler(*m_pTokenArray));
+ // In the case of the reportdesign dialog there is no currently active
+ // OpCode symbol mapping that could be used to create strings from
+ // tokens, it's all dreaded API mapping. However, in that case there's
+ // no array/matrix support anyway, but ensure checking.
+ if (pCompiler->GetCurrentOpCodeMap())
+ {
+ const sal_Int32 nPos = m_aFuncSel.Min();
+ assert( 0 <= nPos && nPos < m_pHelper->getCurrentFormula().getLength());
+ OUStringBuffer aBuf;
+ const FormulaToken* pToken = nullptr;
+ for (pToken = m_oTokenArrayIterator->First(); pToken; pToken = m_oTokenArrayIterator->Next())
+ {
+ pCompiler->CreateStringFromToken( aBuf, pToken);
+ if (nPos < aBuf.getLength())
+ break;
+ }
+ if (pToken && nPos < aBuf.getLength())
+ bForceArray = pToken->IsInForceArray();
+ }
+ }
+
+ OUString aStrResult;
+ if (m_pFuncDesc && CalcValue( m_pFuncDesc->getFormula( m_aArguments), aStrResult, bForceArray))
+ m_xWndResult->set_text( aStrResult );
+
+ if (m_bMakingTree)
+ return;
+
+ aStrResult.clear();
+ if ( CalcValue( m_pHelper->getCurrentFormula(), aStrResult ) )
+ m_xWndFormResult->set_text( aStrResult );
+ else
+ {
+ aStrResult.clear();
+ m_xWndFormResult->set_text( aStrResult );
+ }
+ CalcStruct( m_xMEdit->get_text(), bForceRecalcStruct);
+}
+
+void FormulaDlg_Impl::CalcStruct( const OUString& rStrExp, bool bForceRecalcStruct )
+{
+ sal_Int32 nLength = rStrExp.getLength();
+
+ if ( !(!rStrExp.isEmpty() && (bForceRecalcStruct || m_aOldFormula != rStrExp) && m_bStructUpdate))
+ return;
+
+ m_xStructPage->ClearStruct();
+
+ OUString aString = rStrExp;
+ if (rStrExp[nLength-1] == '(')
+ {
+ aString = aString.copy( 0, nLength-1);
+ }
+
+ aString = aString.replaceAll( "\n", "");
+ OUString aStrResult;
+
+ if ( CalcValue( aString, aStrResult ) )
+ m_xWndFormResult->set_text(aStrResult);
+
+ UpdateTokenArray(aString);
+ fillTree(m_xStructPage.get());
+
+ m_aOldFormula = rStrExp;
+ if (rStrExp[nLength-1] == '(')
+ UpdateTokenArray(rStrExp);
+}
+
+void FormulaDlg_Impl::MakeTree(StructPage* _pTree, weld::TreeIter* pParent, const FormulaToken* pFuncToken,
+ const FormulaToken* _pToken, tools::Long Count)
+{
+ if ( _pToken == nullptr || Count <= 0 )
+ return;
+
+ tools::Long nParas = _pToken->GetParamCount();
+ OpCode eOp = _pToken->GetOpCode();
+
+ // #i101512# for output, the original token is needed
+ const FormulaToken* pOrigToken = (_pToken->GetType() == svFAP) ? _pToken->GetFAPOrigToken() : _pToken;
+ ::std::map<const FormulaToken*, sheet::FormulaToken>::const_iterator itr = m_aTokenMap.find(pOrigToken);
+ if (itr == m_aTokenMap.end())
+ return;
+
+ uno::Sequence<sheet::FormulaToken> aArgs { itr->second };
+ try
+ {
+ const table::CellAddress aRefPos(m_pHelper->getReferencePosition());
+ const OUString aResult = m_pHelper->getFormulaParser()->printFormula( aArgs, aRefPos);
+
+ if ( nParas > 0 || (nParas == 0 && _pToken->IsFunction()) )
+ {
+ std::unique_ptr<weld::TreeIter> xEntry;
+ weld::TreeIter* pEntry;
+
+ bool bCalcSubformula = false;
+ OUString aTest = _pTree->GetEntryText(pParent);
+
+ if (aTest == aResult && (eOp == ocAdd || eOp == ocMul || eOp == ocAmpersand))
+ {
+ pEntry = pParent;
+ }
+ else
+ {
+ xEntry = m_xStructPage->GetTlbStruct().make_iterator();
+
+ if (eOp == ocBad)
+ {
+ _pTree->InsertEntry(aResult, pParent, STRUCT_ERROR, 0, _pToken, *xEntry);
+ }
+ else if (!((SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP) ||
+ (SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)))
+ {
+ // Not a binary or unary operator.
+ bCalcSubformula = true;
+ _pTree->InsertEntry(aResult, pParent, STRUCT_FOLDER, 0, _pToken, *xEntry);
+ }
+ else
+ {
+ /* TODO: question remains, why not sub calculate operators? */
+ _pTree->InsertEntry(aResult, pParent, STRUCT_FOLDER, 0, _pToken, *xEntry);
+ }
+
+ pEntry = xEntry.get();
+ }
+
+ MakeTree(_pTree, pEntry, _pToken, m_oTokenArrayIterator->PrevRPN(), nParas);
+
+ if (bCalcSubformula)
+ {
+ OUString aFormula;
+
+ if (!m_bMakingTree)
+ {
+ // gets the last subformula result
+ m_bMakingTree = true;
+ aFormula = GetPrevFuncExpression( true);
+ }
+ else
+ {
+ // gets subsequent subformula results (from the back)
+ aFormula = GetPrevFuncExpression( false);
+ }
+
+ OUString aStr;
+ if (CalcValue( aFormula, aStr, _pToken->IsInForceArray()))
+ m_xWndResult->set_text( aStr );
+ aStr = m_xWndResult->get_text();
+ m_xStructPage->GetTlbStruct().set_text(*pEntry, aResult + " = " + aStr);
+ }
+
+ --Count;
+ m_oTokenArrayIterator->NextRPN(); /* TODO: what's this to be? ThisRPN()? */
+ MakeTree( _pTree, pParent, _pToken, m_oTokenArrayIterator->PrevRPN(), Count);
+ }
+ else
+ {
+ std::unique_ptr<weld::TreeIter> xEntry(m_xStructPage->GetTlbStruct().make_iterator());
+ if (eOp == ocBad)
+ {
+ _pTree->InsertEntry( aResult, pParent, STRUCT_ERROR, 0, _pToken, *xEntry);
+ }
+ else if (eOp == ocPush)
+ {
+ // Interpret range reference in matrix context to resolve
+ // as array elements. Depending on parameter classification
+ // a scalar value (non-array context) is calculated first.
+ OUString aUnforcedResult;
+ bool bForceMatrix = (!m_xBtnMatrix->get_active() &&
+ (_pToken->GetType() == svDoubleRef || _pToken->GetType() == svExternalDoubleRef));
+ if (bForceMatrix && pFuncToken)
+ {
+ formula::ParamClass eParamClass = ParamClass::Reference;
+ if (pFuncToken->IsInForceArray())
+ eParamClass = ParamClass::ForceArray;
+ else
+ {
+ std::shared_ptr<FormulaCompiler> pCompiler = m_pHelper->getCompiler();
+ if (pCompiler)
+ eParamClass = pCompiler->GetForceArrayParameter( pFuncToken, Count - 1);
+ }
+ switch (eParamClass)
+ {
+ case ParamClass::Unknown:
+ case ParamClass::Bounds:
+ case ParamClass::Value:
+ if (CalcValue( "=" + aResult, aUnforcedResult, false) && aUnforcedResult != aResult)
+ aUnforcedResult += " ";
+ else
+ aUnforcedResult.clear();
+ break;
+ case ParamClass::Reference:
+ case ParamClass::ReferenceOrRefArray:
+ case ParamClass::Array:
+ case ParamClass::ForceArray:
+ case ParamClass::ReferenceOrForceArray:
+ case ParamClass::SuppressedReferenceOrForceArray:
+ case ParamClass::ForceArrayReturn:
+ ; // nothing, only as array/matrix
+ // no default to get compiler warning
+ }
+ }
+ OUString aCellResult;
+ if (CalcValue( "=" + aResult, aCellResult, bForceMatrix) && aCellResult != aResult)
+ {
+ // Cell is a formula, print subformula.
+ // With scalar values prints "A1:A3 = 2 {1;2;3}"
+ _pTree->InsertEntry( aResult + " = " + aUnforcedResult + aCellResult,
+ pParent, STRUCT_END, 0, _pToken, *xEntry);
+ }
+ else
+ _pTree->InsertEntry(aResult, pParent, STRUCT_END, 0, _pToken, *xEntry);
+ }
+ else
+ {
+ _pTree->InsertEntry(aResult, pParent, STRUCT_END, 0, _pToken, *xEntry);
+ }
+ --Count;
+ MakeTree( _pTree, pParent, _pToken, m_oTokenArrayIterator->PrevRPN(), Count);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("formula.ui");
+ }
+}
+
+void FormulaDlg_Impl::fillTree(StructPage* _pTree)
+{
+ InitFormulaOpCodeMapper();
+ FormulaToken* pToken = m_oTokenArrayIterator->LastRPN();
+
+ if ( pToken != nullptr)
+ {
+ MakeTree( _pTree, nullptr, nullptr, pToken, 1);
+ m_bMakingTree = false;
+ }
+}
+
+void FormulaDlg_Impl::UpdateTokenArray( const OUString& rStrExp)
+{
+ m_aTokenMap.clear();
+ m_aTokenList.realloc(0);
+ try
+ {
+ const table::CellAddress aRefPos(m_pHelper->getReferencePosition());
+ m_aTokenList = m_pHelper->getFormulaParser()->parseFormula( rStrExp, aRefPos);
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("formula.ui");
+ }
+ InitFormulaOpCodeMapper();
+ m_pTokenArray = m_pHelper->convertToTokenArray(m_aTokenList);
+ m_oTokenArrayIterator.emplace(*m_pTokenArray);
+ const sal_Int32 nLen = static_cast<sal_Int32>(m_pTokenArray->GetLen());
+ FormulaToken** pTokens = m_pTokenArray->GetArray();
+ if ( pTokens && nLen == m_aTokenList.getLength() )
+ {
+ for (sal_Int32 nPos = 0; nPos < nLen; nPos++)
+ {
+ m_aTokenMap.emplace( pTokens[nPos], m_aTokenList[nPos] );
+ }
+ } // if ( pTokens && nLen == m_aTokenList.getLength() )
+
+ std::unique_ptr<FormulaCompiler> pCompiler(m_pHelper->createCompiler(*m_pTokenArray));
+ // #i101512# Disable special handling of jump commands.
+ pCompiler->EnableJumpCommandReorder(false);
+ pCompiler->EnableStopOnError(false);
+ pCompiler->SetComputeIIFlag(true);
+ pCompiler->SetMatrixFlag(m_bUserMatrixFlag);
+ pCompiler->CompileTokenArray();
+}
+
+void FormulaDlg_Impl::FillDialog(bool bFlag)
+{
+ bool bNext = true, bPrev = true;
+ if (bFlag)
+ FillControls( bNext, bPrev);
+ FillListboxes();
+ if (bFlag)
+ {
+ m_xBtnBackward->set_sensitive(bPrev);
+ m_xBtnForward->set_sensitive(bNext);
+ }
+
+ OUString aStrResult;
+
+ if ( CalcValue( m_pHelper->getCurrentFormula(), aStrResult ) )
+ m_xWndFormResult->set_text( aStrResult );
+ else
+ {
+ aStrResult.clear();
+ m_xWndFormResult->set_text( aStrResult );
+ }
+}
+
+void FormulaDlg_Impl::FillListboxes()
+{
+ // Switch between the "Pages"
+ FormEditData* pData = m_pHelper->getFormEditData();
+ // 1. Page: select function
+ if ( m_pFuncDesc && m_pFuncDesc->getCategory() )
+ {
+ // We'll never have more than int32 max categories so this is safe ...
+ // Category listbox holds additional entries for Last Used and All, so
+ // the offset should be two but hard coded numbers are ugly...
+ const sal_Int32 nCategoryOffset = m_xFuncPage->GetCategoryEntryCount() - m_aFormulaHelper.GetCategoryCount();
+ if ( m_xFuncPage->GetCategory() != static_cast<sal_Int32>(m_pFuncDesc->getCategory()->getNumber() + nCategoryOffset) )
+ m_xFuncPage->SetCategory(m_pFuncDesc->getCategory()->getNumber() + nCategoryOffset);
+
+ sal_Int32 nPos = m_xFuncPage->GetFuncPos(m_pFuncDesc);
+
+ m_xFuncPage->SetFunction(nPos);
+ }
+ else if ( pData )
+ {
+ // tdf#104487 - remember last used function category
+ m_xFuncPage->SetCategory(FuncPage::GetRememeberdFunctionCategory());
+ m_xFuncPage->SetFunction( -1 );
+ }
+ FuncSelHdl(*m_xFuncPage);
+
+ m_pHelper->setDispatcherLock( true ); // Activate Modal-Mode
+
+ // HelpId for 1. page is the one from the resource
+ m_rDialog.set_help_id( m_aOldHelp );
+}
+
+void FormulaDlg_Impl::FillControls( bool &rbNext, bool &rbPrev)
+{
+ // Switch between the "Pages"
+ FormEditData* pData = m_pHelper->getFormEditData();
+ if (!pData )
+ return;
+
+ // 2. Page or Edit: show selected function
+
+ sal_Int32 nFStart = pData->GetFStart();
+ OUString aFormula = m_pHelper->getCurrentFormula() + " )";
+ sal_Int32 nNextFStart = nFStart;
+ sal_Int32 nNextFEnd = 0;
+
+ DeleteArgs();
+ const IFunctionDescription* pOldFuncDesc = m_pFuncDesc;
+
+ if ( m_aFormulaHelper.GetNextFunc( aFormula, false,
+ nNextFStart, &nNextFEnd, &m_pFuncDesc, &m_aArguments ) )
+ {
+ const bool bTestFlag = (pOldFuncDesc != m_pFuncDesc);
+ if (bTestFlag)
+ {
+ m_xFtHeadLine->hide();
+ m_xFtFuncName->hide();
+ m_xFtFuncDesc->hide();
+ m_xParaWin->SetFunctionDesc(m_pFuncDesc);
+ m_xFtEditName->set_label( m_pFuncDesc->getFunctionName() );
+ m_xFtEditName->show();
+ m_xParaWinBox->show();
+ const OUString aHelpId = m_pFuncDesc->getHelpId();
+ if ( !aHelpId.isEmpty() )
+ m_xMEdit->set_help_id(aHelpId);
+ }
+
+ sal_Int32 nOldStart, nOldEnd;
+ m_pHelper->getSelection( nOldStart, nOldEnd );
+ if ( nOldStart != nNextFStart || nOldEnd != nNextFEnd )
+ {
+ m_pHelper->setSelection( nNextFStart, nNextFEnd );
+ }
+ m_aFuncSel.Min() = nNextFStart;
+ m_aFuncSel.Max() = nNextFEnd;
+
+ if (!m_bEditFlag)
+ m_xMEdit->set_text(m_pHelper->getCurrentFormula());
+ sal_Int32 PrivStart, PrivEnd;
+ m_pHelper->getSelection( PrivStart, PrivEnd);
+ if (!m_bEditFlag)
+ m_xMEdit->select_region(PrivStart, PrivEnd);
+
+ m_nArgs = m_pFuncDesc->getSuppressedArgumentCount();
+ sal_uInt16 nOffset = pData->GetOffset();
+
+ // Concatenate the Edit's for Focus-Control
+
+ if (bTestFlag)
+ m_xParaWin->SetArgumentOffset(nOffset);
+ sal_uInt16 nActiv = 0;
+ sal_Int32 nArgPos = m_aFormulaHelper.GetArgStart( aFormula, nFStart, 0 );
+
+ int nStartPos, nEndPos;
+ m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
+ if (nStartPos > nEndPos)
+ std::swap(nStartPos, nEndPos);
+
+ sal_Int32 nEditPos = nStartPos;
+ bool bFlag = false;
+
+ for (sal_Int32 i = 0; i < m_nArgs; i++)
+ {
+ sal_Int32 nLength = m_aArguments[i].getLength()+1;
+ m_xParaWin->SetArgument( i, m_aArguments[i]);
+ if (nArgPos <= nEditPos && nEditPos < nArgPos+nLength)
+ {
+ nActiv = i;
+ bFlag = true;
+ }
+ nArgPos = nArgPos + nLength;
+ }
+ m_xParaWin->UpdateParas();
+
+ if (bFlag)
+ {
+ m_xParaWin->SetActiveLine(nActiv);
+ }
+
+ UpdateValues();
+ }
+ else
+ {
+ m_xFtEditName->set_label("");
+ m_xMEdit->set_help_id(m_aEditHelpId);
+ }
+ // test if before/after are anymore functions
+
+ sal_Int32 nTempStart = m_aFormulaHelper.GetArgStart( aFormula, nFStart, 0 );
+ rbNext = m_aFormulaHelper.GetNextFunc( aFormula, false, nTempStart );
+
+ int nStartPos, nEndPos;
+ m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
+ if (nStartPos > nEndPos)
+ std::swap(nStartPos, nEndPos);
+
+ nTempStart = nStartPos;
+ pData->SetFStart(nTempStart);
+ rbPrev = m_aFormulaHelper.GetNextFunc( aFormula, true, nTempStart );
+}
+
+
+void FormulaDlg_Impl::ClearAllParas()
+{
+ DeleteArgs();
+ m_pFuncDesc = nullptr;
+ m_xParaWin->ClearAll();
+ m_xWndResult->set_text(OUString());
+ m_xFtFuncName->set_label(OUString());
+ FuncSelHdl(*m_xFuncPage);
+
+ if (m_xFuncPage->IsVisible())
+ {
+ m_xFtEditName->hide();
+ m_xParaWinBox->hide();
+
+ m_xBtnForward->set_sensitive(true); //@new
+ m_xFtHeadLine->show();
+ m_xFtFuncName->show();
+ m_xFtFuncDesc->show();
+ }
+}
+
+OUString FormulaDlg_Impl::RepairFormula(const OUString& aFormula)
+{
+ OUString aResult('=');
+ try
+ {
+ UpdateTokenArray(aFormula);
+
+ if ( m_aTokenList.hasElements() )
+ {
+ const table::CellAddress aRefPos(m_pHelper->getReferencePosition());
+ const OUString sFormula( m_pHelper->getFormulaParser()->printFormula( m_aTokenList, aRefPos));
+ if ( sFormula.isEmpty() || sFormula[0] != '=' )
+ aResult += sFormula;
+ else
+ aResult = sFormula;
+
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("formula.ui", "FormulaDlg_Impl::RepairFormula");
+ }
+ return aResult;
+}
+
+void FormulaDlg_Impl::DoEnter(bool bOk)
+{
+ // Accept input to the document or cancel
+ if ( bOk)
+ {
+ // remove dummy arguments
+ OUString aInputFormula = m_pHelper->getCurrentFormula();
+ OUString aString = RepairFormula(m_xMEdit->get_text());
+ m_pHelper->setSelection( 0, aInputFormula.getLength());
+ m_pHelper->setCurrentFormula(aString);
+ }
+
+ m_pHelper->switchBack();
+
+ m_pHelper->dispatch( bOk, m_xBtnMatrix->get_active());
+ // Clear data
+ m_pHelper->deleteFormData();
+
+ // Close dialog
+ m_pHelper->doClose(bOk);
+}
+
+
+IMPL_LINK(FormulaDlg_Impl, BtnHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == m_xBtnCancel.get())
+ {
+ DoEnter(false); // closes the Dialog
+ }
+ else if (&rBtn == m_xBtnEnd.get())
+ {
+ DoEnter(true); // closes the Dialog
+ }
+ else if (&rBtn == m_xBtnForward.get())
+ {
+ const IFunctionDescription* pDesc;
+ sal_Int32 nSelFunc = m_xFuncPage->GetFunction();
+ if (nSelFunc != -1)
+ pDesc = m_xFuncPage->GetFuncDesc( nSelFunc );
+ else
+ {
+ // Do not overwrite the selected formula expression, just edit the
+ // unlisted function.
+ m_pFuncDesc = pDesc = nullptr;
+ }
+
+ if (pDesc == m_pFuncDesc || !m_xFuncPage->IsVisible())
+ EditNextFunc( true );
+ else
+ {
+ DblClkHdl(*m_xFuncPage); //new
+ m_xBtnForward->set_sensitive(false); //new
+ }
+ }
+ else if (&rBtn == m_xBtnBackward.get())
+ {
+ m_bEditFlag = false;
+ m_xBtnForward->set_sensitive(true);
+ EditNextFunc( false );
+ }
+}
+
+// Functions for 1. Page
+
+// Handler for Listboxes
+
+IMPL_LINK_NOARG( FormulaDlg_Impl, DblClkHdl, FuncPage&, void)
+{
+ sal_Int32 nFunc = m_xFuncPage->GetFunction();
+
+ // ex-UpdateLRUList
+ const IFunctionDescription* pDesc = m_xFuncPage->GetFuncDesc(nFunc);
+ m_pHelper->insertEntryToLRUList(pDesc);
+
+ OUString aFuncName = m_xFuncPage->GetSelFunctionName() + "()";
+ m_pHelper->setCurrentFormula(aFuncName);
+ m_xMEdit->replace_selection(aFuncName);
+
+ int nStartPos, nEndPos;
+ m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
+ if (nStartPos > nEndPos)
+ std::swap(nStartPos, nEndPos);
+
+ nEndPos = nEndPos - 1;
+ m_xMEdit->select_region(nStartPos, nEndPos);
+
+ FormulaHdl(*m_xMEdit);
+
+ nStartPos = nEndPos;
+ m_xMEdit->select_region(nStartPos, nEndPos);
+
+ if (m_nArgs == 0)
+ {
+ BtnHdl(*m_xBtnBackward);
+ }
+
+ m_xParaWin->SetEdFocus();
+ m_xBtnForward->set_sensitive(false); //@New
+}
+
+// Functions for right Page
+
+void FormulaDlg_Impl::SetData( sal_Int32 nFStart, sal_Int32 nNextFStart, sal_Int32 nNextFEnd, sal_Int32& PrivStart, sal_Int32& PrivEnd)
+{
+ sal_Int32 nFEnd;
+
+ // Notice and set new selection
+ m_pHelper->getSelection( nFStart, nFEnd );
+ m_pHelper->setSelection( nNextFStart, nNextFEnd );
+ if (!m_bEditFlag)
+ m_xMEdit->set_text(m_pHelper->getCurrentFormula());
+
+
+ m_pHelper->getSelection( PrivStart, PrivEnd);
+ if (!m_bEditFlag)
+ {
+ m_xMEdit->select_region(PrivStart, PrivEnd);
+ UpdateOldSel();
+ }
+
+ FormEditData* pData = m_pHelper->getFormEditData();
+ pData->SetFStart( nNextFStart );
+ pData->SetOffset( 0 );
+
+ FillDialog();
+}
+
+void FormulaDlg_Impl::EditThisFunc(sal_Int32 nFStart)
+{
+ FormEditData* pData = m_pHelper->getFormEditData();
+ if (!pData)
+ return;
+
+ OUString aFormula = m_pHelper->getCurrentFormula();
+
+ if (nFStart == NOT_FOUND)
+ {
+ nFStart = pData->GetFStart();
+ }
+ else
+ {
+ pData->SetFStart(nFStart);
+ }
+
+ sal_Int32 nNextFStart = nFStart;
+ sal_Int32 nNextFEnd = 0;
+
+ bool bFound;
+
+ bFound = m_aFormulaHelper.GetNextFunc( aFormula, false, nNextFStart, &nNextFEnd);
+ if ( bFound )
+ {
+ sal_Int32 PrivStart, PrivEnd;
+ SetData( nFStart, nNextFStart, nNextFEnd, PrivStart, PrivEnd);
+ m_pHelper->showReference( aFormula.copy( PrivStart, PrivEnd-PrivStart));
+ }
+ else
+ {
+ ClearAllParas();
+ }
+}
+
+bool FormulaDlg_Impl::EditNextFunc( bool bForward, sal_Int32 nFStart )
+{
+ FormEditData* pData = m_pHelper->getFormEditData();
+ if (!pData)
+ return false;
+
+ OUString aFormula = m_pHelper->getCurrentFormula();
+
+ if (nFStart == NOT_FOUND)
+ {
+ nFStart = pData->GetFStart();
+ }
+ else
+ {
+ pData->SetFStart(nFStart);
+ }
+
+ sal_Int32 nNextFStart = 0;
+ sal_Int32 nNextFEnd = 0;
+
+ bool bFound;
+ if ( bForward )
+ {
+ nNextFStart = m_aFormulaHelper.GetArgStart( aFormula, nFStart, 0 );
+ bFound = m_aFormulaHelper.GetNextFunc( aFormula, false, nNextFStart, &nNextFEnd);
+ }
+ else
+ {
+ nNextFStart = nFStart;
+ bFound = m_aFormulaHelper.GetNextFunc( aFormula, true, nNextFStart, &nNextFEnd);
+ }
+
+ if ( bFound )
+ {
+ sal_Int32 PrivStart, PrivEnd;
+ SetData( nFStart, nNextFStart, nNextFEnd, PrivStart, PrivEnd);
+ }
+
+ return bFound;
+}
+
+OUString FormulaDlg_Impl::GetPrevFuncExpression( bool bStartFromEnd )
+{
+ OUString aExpression;
+
+ OUString aFormula( m_pHelper->getCurrentFormula());
+ if (aFormula.isEmpty())
+ return aExpression;
+
+ if (bStartFromEnd || m_nFuncExpStart >= aFormula.getLength())
+ m_nFuncExpStart = aFormula.getLength() - 1;
+
+ sal_Int32 nFStart = m_nFuncExpStart;
+ sal_Int32 nFEnd = 0;
+ if (m_aFormulaHelper.GetNextFunc( aFormula, true, nFStart, &nFEnd))
+ {
+ aExpression = aFormula.copy( nFStart, nFEnd - nFStart); // nFEnd is exclusive
+ m_nFuncExpStart = nFStart;
+ }
+
+ return aExpression;
+}
+
+void FormulaDlg_Impl::SaveArg( sal_uInt16 nEd )
+{
+ if (nEd >= m_nArgs)
+ return;
+
+ for (sal_uInt16 i = 0; i <= nEd; i++)
+ {
+ if ( m_aArguments[i].isEmpty() )
+ m_aArguments[i] = " ";
+ }
+ if (!m_xParaWin->GetArgument(nEd).isEmpty())
+ m_aArguments[nEd] = m_xParaWin->GetArgument(nEd);
+
+ sal_uInt16 nClearPos = nEd+1;
+ for (sal_Int32 i = nEd+1; i < m_nArgs; i++)
+ {
+ if ( !m_xParaWin->GetArgument(i).isEmpty() )
+ {
+ nClearPos = i+1;
+ }
+ }
+
+ for (sal_Int32 i = nClearPos; i < m_nArgs; i++)
+ {
+ m_aArguments[i].clear();
+ }
+}
+
+IMPL_LINK( FormulaDlg_Impl, FxHdl, ParaWin&, rPtr, void )
+{
+ if (&rPtr != m_xParaWin.get())
+ return;
+
+ m_xBtnForward->set_sensitive(true); //@ In order to be able to input another function.
+ m_xTabCtrl->set_current_page("functiontab");
+
+ OUString aUndoStr = m_pHelper->getCurrentFormula(); // it will be added before a ";"
+ FormEditData* pData = m_pHelper->getFormEditData();
+ if (!pData)
+ return;
+
+ sal_uInt16 nArgNo = m_xParaWin->GetActiveLine();
+ sal_uInt16 nEdFocus = nArgNo;
+
+ SaveArg(nArgNo);
+ UpdateSelection();
+
+ sal_Int32 nFormulaStrPos = pData->GetFStart();
+
+ OUString aFormula = m_pHelper->getCurrentFormula();
+ sal_Int32 n1 = m_aFormulaHelper.GetArgStart( aFormula, nFormulaStrPos, nEdFocus + pData->GetOffset() );
+
+ pData->SaveValues();
+ pData->SetMode( FormulaDlgMode::Formula );
+ pData->SetFStart( n1 );
+ pData->SetUndoStr( aUndoStr );
+ ClearAllParas();
+
+ FillDialog(false);
+ m_xFuncPage->SetFocus(); //There Parawin is not visible anymore
+}
+
+IMPL_LINK( FormulaDlg_Impl, ModifyHdl, ParaWin&, rPtr, void )
+{
+ if (&rPtr == m_xParaWin.get())
+ {
+ SaveArg(m_xParaWin->GetActiveLine());
+ UpdateValues();
+
+ UpdateSelection();
+ CalcStruct(m_xMEdit->get_text());
+ }
+}
+
+IMPL_LINK_NOARG( FormulaDlg_Impl, FormulaHdl, weld::TextView&, void)
+{
+
+ FormEditData* pData = m_pHelper->getFormEditData();
+ if (!pData)
+ return;
+
+ m_bEditFlag = true;
+ OUString aInputFormula = m_pHelper->getCurrentFormula();
+ OUString aString = m_xMEdit->get_text();
+
+ int nStartPos, nEndPos;
+ m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
+ if (nStartPos > nEndPos)
+ std::swap(nStartPos, nEndPos);
+
+ if (aString.isEmpty()) // in case everything was cleared
+ {
+ aString += "=";
+ m_xMEdit->set_text(aString);
+ nStartPos = 1;
+ nEndPos = 1;
+ m_xMEdit->select_region(nStartPos, nEndPos);
+ }
+ else if (aString[0]!='=') // in case it's replaced
+ {
+ aString = "=" + aString;
+ m_xMEdit->set_text(aString);
+ nStartPos += 1;
+ nEndPos += 1;
+ m_xMEdit->select_region(nStartPos, nEndPos);
+ }
+
+ m_pHelper->setSelection( 0, aInputFormula.getLength());
+ m_pHelper->setCurrentFormula(aString);
+ m_pHelper->setSelection(nStartPos, nEndPos);
+
+ sal_Int32 nPos = nStartPos - 1;
+
+ OUString aStrResult;
+
+ if ( CalcValue( m_pHelper->getCurrentFormula(), aStrResult ) )
+ m_xWndFormResult->set_text( aStrResult );
+ else
+ {
+ aStrResult.clear();
+ m_xWndFormResult->set_text( aStrResult );
+ }
+ CalcStruct(aString);
+
+ nPos = GetFunctionPos(nPos);
+
+ if (nPos < nStartPos - 1)
+ {
+ sal_Int32 nPos1 = aString.indexOf( '(', nPos);
+ EditNextFunc( false, nPos1);
+ }
+ else
+ {
+ ClearAllParas();
+ }
+
+ m_pHelper->setSelection(nStartPos, nEndPos);
+ m_bEditFlag = false;
+}
+
+void FormulaDlg_Impl::FormulaCursor()
+{
+ FormEditData* pData = m_pHelper->getFormEditData();
+ if (!pData)
+ return;
+
+ m_bEditFlag = true;
+
+ OUString aString = m_xMEdit->get_text();
+
+ int nStartPos, nEndPos;
+ m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
+ if (nStartPos > nEndPos)
+ std::swap(nStartPos, nEndPos);
+
+ m_pHelper->setSelection(nStartPos, nEndPos);
+
+ if (nStartPos == 0)
+ {
+ nStartPos = 1;
+ m_xMEdit->select_region(nStartPos, nEndPos);
+ }
+ if (nStartPos != aString.getLength())
+ {
+ sal_Int32 nPos = nStartPos;
+
+ sal_Int32 nFStart = GetFunctionPos(nPos - 1);
+
+ if (nFStart < nPos)
+ {
+ sal_Int32 nPos1 = m_aFormulaHelper.GetFunctionEnd( aString, nFStart);
+
+ if (nPos1 > nPos)
+ {
+ EditThisFunc(nFStart);
+ }
+ else
+ {
+ sal_Int32 n = nPos;
+ short nCount = 1;
+ while(n > 0)
+ {
+ if (aString[n]==')')
+ nCount++;
+ else if (aString[n]=='(')
+ nCount--;
+ if (nCount == 0)
+ break;
+ n--;
+ }
+ if (nCount == 0)
+ {
+ nFStart = m_aFormulaHelper.GetFunctionStart( aString, n, true);
+ EditThisFunc(nFStart);
+ }
+ else
+ {
+ ClearAllParas();
+ }
+ }
+ }
+ else
+ {
+ ClearAllParas();
+ }
+ }
+ m_pHelper->setSelection(nStartPos, nEndPos);
+
+ m_bEditFlag = false;
+}
+
+void FormulaDlg_Impl::UpdateOldSel()
+{
+ m_xMEdit->get_selection_bounds(m_nSelectionStart, m_nSelectionEnd);
+ if (m_nSelectionStart > m_nSelectionEnd)
+ std::swap(m_nSelectionStart, m_nSelectionEnd);
+}
+
+IMPL_LINK_NOARG( FormulaDlg_Impl, FormulaCursorHdl, weld::TextView&, void)
+{
+ int nStartPos, nEndPos;
+ m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
+ if (nStartPos > nEndPos)
+ std::swap(nStartPos, nEndPos);
+
+ if (nStartPos != m_nSelectionStart || nEndPos != m_nSelectionEnd)
+ {
+ m_nSelectionStart = nStartPos;
+ m_nSelectionEnd = nEndPos;
+ FormulaCursor();
+ }
+}
+
+void FormulaDlg_Impl::UpdateSelection()
+{
+ m_pHelper->setSelection( m_aFuncSel.Min(), m_aFuncSel.Max());
+ if (m_pFuncDesc)
+ {
+ m_pHelper->setCurrentFormula( m_pFuncDesc->getFormula( m_aArguments ) );
+ m_nArgs = m_pFuncDesc->getSuppressedArgumentCount();
+ }
+ else
+ {
+ m_pHelper->setCurrentFormula("");
+ m_nArgs = 0;
+ }
+
+ m_xMEdit->set_text(m_pHelper->getCurrentFormula());
+ sal_Int32 PrivStart, PrivEnd;
+ m_pHelper->getSelection( PrivStart, PrivEnd);
+ m_aFuncSel.Min() = PrivStart;
+ m_aFuncSel.Max() = PrivEnd;
+
+ OUString aFormula = m_xMEdit->get_text();
+ sal_Int32 nArgPos = m_aFormulaHelper.GetArgStart( aFormula, PrivStart, 0);
+
+ sal_uInt16 nPos = m_xParaWin->GetActiveLine();
+ if (nPos >= m_aArguments.size())
+ {
+ SAL_WARN("formula.ui","FormulaDlg_Impl::UpdateSelection - shot in foot: nPos " <<
+ nPos << " >= m_aArguments.size() " << m_aArguments.size() <<
+ " for aFormula '" << aFormula << "'");
+ nPos = m_aArguments.size();
+ if (nPos)
+ --nPos;
+ }
+
+ for (sal_uInt16 i = 0; i < nPos; i++)
+ {
+ nArgPos += (m_aArguments[i].getLength() + 1);
+ }
+ sal_Int32 nLength = (nPos < m_aArguments.size()) ? m_aArguments[nPos].getLength() : 0;
+
+ m_pHelper->setSelection(nArgPos, nArgPos + nLength);
+ m_xMEdit->select_region(nArgPos, nArgPos + nLength);
+ UpdateOldSel();
+}
+
+::std::pair<RefButton*, RefEdit*> FormulaDlg_Impl::RefInputStartBefore(RefEdit* pEdit, RefButton* pButton)
+{
+ m_pTheRefEdit = pEdit;
+ m_pTheRefButton = pButton;
+
+ Selection aOrigSelection;
+ if (m_pTheRefEdit)
+ {
+ // grab selection before showing next widget in case the selection is blown away
+ // by it appearing
+ aOrigSelection = m_pTheRefEdit->GetSelection();
+ }
+
+ // because its initially hidden, give it its optimal size so clicking the
+ // refbutton has an initial size to work when retro-fitting this to .ui
+ m_xEdRef->GetWidget()->set_size_request(m_xEdRef->GetWidget()->get_preferred_size().Width(), -1);
+ m_xEdRef->GetWidget()->show();
+
+ if ( m_pTheRefEdit )
+ {
+ m_xEdRef->SetRefString(m_pTheRefEdit->GetText());
+ m_xEdRef->SetSelection(aOrigSelection);
+ m_xEdRef->GetWidget()->set_help_id(m_pTheRefEdit->GetWidget()->get_help_id());
+ }
+
+ m_xRefBtn->GetWidget()->set_visible(pButton != nullptr);
+
+ ::std::pair<RefButton*, RefEdit*> aPair;
+ aPair.first = pButton ? m_xRefBtn.get() : nullptr;
+ aPair.second = m_xEdRef.get();
+ return aPair;
+}
+
+void FormulaDlg_Impl::RefInputStartAfter()
+{
+ m_xRefBtn->SetEndImage();
+
+ if (!m_pTheRefEdit)
+ return;
+
+ OUString aStr = m_aTitle2 + " " + m_xFtEditName->get_label() + "( ";
+
+ if ( m_xParaWin->GetActiveLine() > 0 )
+ aStr += "...; ";
+ aStr += m_xParaWin->GetActiveArgName();
+ if ( m_xParaWin->GetActiveLine() + 1 < m_nArgs )
+ aStr += "; ...";
+ aStr += " )";
+
+ m_rDialog.set_title(m_rDialog.strip_mnemonic(aStr));
+}
+
+void FormulaDlg_Impl::RefInputDoneAfter( bool bForced )
+{
+ m_xRefBtn->SetStartImage();
+ if (!bForced && m_xRefBtn->GetWidget()->get_visible())
+ return;
+
+ m_xEdRef->GetWidget()->hide();
+ m_xRefBtn->GetWidget()->hide();
+ if ( m_pTheRefEdit )
+ {
+ m_pTheRefEdit->SetRefString( m_xEdRef->GetText() );
+ m_pTheRefEdit->GrabFocus();
+
+ if ( m_pTheRefButton )
+ m_pTheRefButton->SetStartImage();
+
+ sal_uInt16 nPrivActiv = m_xParaWin->GetActiveLine();
+ m_xParaWin->SetArgument( nPrivActiv, m_xEdRef->GetText() );
+ ModifyHdl( *m_xParaWin );
+ m_pTheRefEdit = nullptr;
+ }
+ m_rDialog.set_title(m_aTitle1);
+}
+
+RefEdit* FormulaDlg_Impl::GetCurrRefEdit()
+{
+ return m_xEdRef->GetWidget()->get_visible() ? m_xEdRef.get() : m_xParaWin->GetActiveEdit();
+}
+
+void FormulaDlg_Impl::Update()
+{
+ FormEditData* pData = m_pHelper->getFormEditData();
+ const OUString sExpression = m_xMEdit->get_text();
+ m_aOldFormula.clear();
+ UpdateTokenArray(sExpression);
+ FormulaCursor();
+ CalcStruct(sExpression);
+ if (pData->GetMode() == FormulaDlgMode::Formula)
+ m_xTabCtrl->set_current_page("functiontab");
+ else
+ m_xTabCtrl->set_current_page("structtab");
+ m_xBtnMatrix->set_active(pData->GetMatrixFlag());
+}
+
+void FormulaDlg_Impl::Update(const OUString& _sExp)
+{
+ CalcStruct(_sExp);
+ FillDialog();
+ FuncSelHdl(*m_xFuncPage);
+}
+
+void FormulaDlg_Impl::SetMeText(const OUString& _sText)
+{
+ FormEditData* pData = m_pHelper->getFormEditData();
+ m_xMEdit->set_text(_sText);
+ auto aSelection = pData->GetSelection();
+ m_xMEdit->select_region(aSelection.Min(), aSelection.Max());
+ UpdateOldSel();
+}
+
+FormulaDlgMode FormulaDlg_Impl::SetMeText( const OUString& _sText, sal_Int32 PrivStart, sal_Int32 PrivEnd, bool bMatrix, bool _bSelect, bool _bUpdate)
+{
+ FormulaDlgMode eMode = FormulaDlgMode::Formula;
+ if (!m_bEditFlag)
+ m_xMEdit->set_text(_sText);
+
+ if ( _bSelect || !m_bEditFlag )
+ m_xMEdit->select_region(PrivStart, PrivEnd);
+ if ( _bUpdate )
+ {
+ UpdateOldSel();
+ int nStartPos, nEndPos;
+ m_xMEdit->get_selection_bounds(nStartPos, nEndPos);
+ if (nStartPos > nEndPos)
+ std::swap(nStartPos, nEndPos);
+ m_pHelper->showReference(m_xMEdit->get_text().copy(nStartPos, nEndPos - nStartPos));
+ eMode = FormulaDlgMode::Edit;
+
+ m_xBtnMatrix->set_active( bMatrix );
+ } // if ( _bUpdate )
+ return eMode;
+}
+
+bool FormulaDlg_Impl::CheckMatrix(OUString& aFormula)
+{
+ m_xMEdit->grab_focus();
+ sal_Int32 nLen = aFormula.getLength();
+ bool bMatrix = nLen > 3 // Matrix-Formula
+ && aFormula[0] == '{'
+ && aFormula[1] == '='
+ && aFormula[nLen-1] == '}';
+ if ( bMatrix )
+ {
+ aFormula = aFormula.copy( 1, aFormula.getLength()-2 );
+ m_xBtnMatrix->set_active( bMatrix );
+ m_xBtnMatrix->set_sensitive(false);
+ } // if ( bMatrix )
+
+ m_xTabCtrl->set_current_page("structtab");
+ return bMatrix;
+}
+
+IMPL_LINK_NOARG( FormulaDlg_Impl, StructSelHdl, StructPage&, void)
+{
+ m_bStructUpdate = false;
+ if (m_xStructPage->IsVisible())
+ m_xBtnForward->set_sensitive(false); //@New
+ m_bStructUpdate = true;
+}
+
+IMPL_LINK_NOARG( FormulaDlg_Impl, MatrixHdl, weld::Toggleable&, void)
+{
+ m_bUserMatrixFlag = true;
+ UpdateValues(true);
+}
+
+IMPL_LINK_NOARG( FormulaDlg_Impl, FuncSelHdl, FuncPage&, void)
+{
+ if ( (m_xFuncPage->GetFunctionEntryCount() > 0)
+ && (m_xFuncPage->GetFunction() != -1) )
+ {
+ const IFunctionDescription* pDesc = m_xFuncPage->GetFuncDesc( m_xFuncPage->GetFunction() );
+
+ if (pDesc != m_pFuncDesc)
+ m_xBtnForward->set_sensitive(true); //new
+
+ if (pDesc)
+ {
+ pDesc->initArgumentInfo(); // full argument info is needed
+
+ OUString aSig = pDesc->getSignature();
+ m_xFtHeadLine->set_label( pDesc->getFunctionName() );
+ m_xFtFuncName->set_label( aSig );
+ m_xFtFuncDesc->set_label( pDesc->getDescription() );
+ }
+ }
+ else
+ {
+ m_xFtHeadLine->set_label( OUString() );
+ m_xFtFuncName->set_label( OUString() );
+ m_xFtFuncDesc->set_label( OUString() );
+ }
+}
+
+void FormulaDlg_Impl::UpdateParaWin( const Selection& _rSelection, const OUString& _sRefStr)
+{
+ Selection theSel = _rSelection;
+ m_xEdRef->GetWidget()->replace_selection(_sRefStr);
+ theSel.Max() = theSel.Min() + _sRefStr.getLength();
+ m_xEdRef->SetSelection( theSel );
+
+
+ // Manual Update of the results' fields:
+
+ sal_uInt16 nPrivActiv = m_xParaWin->GetActiveLine();
+ m_xParaWin->SetArgument( nPrivActiv, m_xEdRef->GetText());
+ m_xParaWin->UpdateParas();
+
+ RefEdit* pEd = GetCurrRefEdit();
+ if (pEd)
+ pEd->SetSelection( theSel );
+}
+
+bool FormulaDlg_Impl::UpdateParaWin(Selection& _rSelection)
+{
+ OUString aStrEd;
+ RefEdit* pEd = GetCurrRefEdit();
+ if (pEd && !m_pTheRefEdit)
+ {
+ _rSelection = pEd->GetSelection();
+ _rSelection.Normalize();
+ aStrEd = pEd->GetText();
+ m_xEdRef->SetRefString(aStrEd);
+ m_xEdRef->SetSelection( _rSelection );
+ }
+ else
+ {
+ _rSelection = m_xEdRef->GetSelection();
+ _rSelection.Normalize();
+ aStrEd = m_xEdRef->GetText();
+ }
+ return m_pTheRefEdit == nullptr;
+}
+
+void FormulaDlg_Impl::SetEdSelection()
+{
+ RefEdit* pEd = GetCurrRefEdit()/*aScParaWin.GetActiveEdit()*/;
+ if (pEd)
+ {
+ Selection theSel = m_xEdRef->GetSelection();
+ // Edit may have the focus -> call ModifyHdl in addition
+ // to what's happening in GetFocus
+ pEd->GetModifyHdl().Call(*pEd);
+ pEd->GrabFocus();
+ pEd->SetSelection(theSel);
+ } // if ( pEd )
+}
+
+FormulaModalDialog::FormulaModalDialog(weld::Window* pParent,
+ IFunctionManager const * _pFunctionMgr,
+ IControlReferenceHandler* _pDlg)
+ : GenericDialogController(pParent, "formula/ui/formuladialog.ui", "FormulaDialog")
+ , m_pImpl(new FormulaDlg_Impl(*m_xDialog, *m_xBuilder, false/*_bSupportFunctionResult*/,
+ false/*_bSupportResult*/, false/*_bSupportMatrix*/,
+ this, _pFunctionMgr, _pDlg))
+{
+ m_xDialog->set_title(m_pImpl->m_aTitle1);
+}
+
+FormulaModalDialog::~FormulaModalDialog() { }
+
+void FormulaModalDialog::Update(const OUString& _sExp)
+{
+ m_pImpl->Update(_sExp);
+}
+
+void FormulaModalDialog::SetMeText(const OUString& _sText)
+{
+ m_pImpl->SetMeText(_sText);
+}
+
+void FormulaModalDialog::CheckMatrix(OUString& aFormula)
+{
+ m_pImpl->CheckMatrix(aFormula);
+}
+
+void FormulaModalDialog::Update()
+{
+ m_pImpl->Update();
+}
+
+::std::pair<RefButton*, RefEdit*> FormulaModalDialog::RefInputStartBefore( RefEdit* pEdit, RefButton* pButton )
+{
+ return m_pImpl->RefInputStartBefore( pEdit, pButton );
+}
+
+void FormulaModalDialog::RefInputStartAfter()
+{
+ m_pImpl->RefInputStartAfter();
+}
+
+void FormulaModalDialog::RefInputDoneAfter()
+{
+ m_pImpl->RefInputDoneAfter( true/*bForced*/ );
+}
+
+void FormulaModalDialog::StoreFormEditData(FormEditData* pData)
+{
+ m_pImpl->StoreFormEditData(pData);
+}
+
+// Initialisation / General functions for Dialog
+FormulaDlg::FormulaDlg(SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent,
+ IFunctionManager const * _pFunctionMgr, IControlReferenceHandler* _pDlg)
+ : SfxModelessDialogController( pB, pCW, pParent, "formula/ui/formuladialog.ui", "FormulaDialog")
+ , m_pImpl(new FormulaDlg_Impl(*m_xDialog, *m_xBuilder, true/*_bSupportFunctionResult*/
+ , true/*_bSupportResult*/
+ , true/*_bSupportMatrix*/
+ , this, _pFunctionMgr, _pDlg))
+{
+ m_xDialog->set_title(m_pImpl->m_aTitle1);
+}
+
+FormulaDlg::~FormulaDlg()
+{
+}
+
+void FormulaDlg::Update(const OUString& _sExp)
+{
+ m_pImpl->Update(_sExp);
+}
+
+void FormulaDlg::SetMeText(const OUString& _sText)
+{
+ m_pImpl->SetMeText(_sText);
+}
+
+FormulaDlgMode FormulaDlg::SetMeText( const OUString& _sText, sal_Int32 PrivStart, sal_Int32 PrivEnd, bool bMatrix, bool _bSelect, bool _bUpdate)
+{
+ return m_pImpl->SetMeText( _sText, PrivStart, PrivEnd, bMatrix, _bSelect, _bUpdate);
+}
+
+bool FormulaDlg::CheckMatrix(OUString& aFormula)
+{
+ return m_pImpl->CheckMatrix(aFormula);
+}
+
+OUString FormulaDlg::GetMeText() const
+{
+ return m_pImpl->m_xMEdit->get_text();
+}
+
+void FormulaDlg::Update()
+{
+ m_pImpl->Update();
+}
+
+void FormulaDlg::DoEnter()
+{
+ m_pImpl->DoEnter(false);
+}
+
+::std::pair<RefButton*, RefEdit*> FormulaDlg::RefInputStartBefore( RefEdit* pEdit, RefButton* pButton )
+{
+ return m_pImpl->RefInputStartBefore( pEdit, pButton );
+}
+
+void FormulaDlg::RefInputStartAfter()
+{
+ m_pImpl->RefInputStartAfter();
+}
+
+void FormulaDlg::RefInputDoneAfter( bool bForced )
+{
+ m_pImpl->RefInputDoneAfter( bForced );
+}
+
+void FormulaDlg::disableOk()
+{
+ m_pImpl->m_xBtnEnd->set_sensitive(false);
+}
+
+void FormulaDlg::StoreFormEditData(FormEditData* pData)
+{
+ m_pImpl->StoreFormEditData(pData);
+}
+
+const IFunctionDescription* FormulaDlg::getCurrentFunctionDescription() const
+{
+ SAL_WARN_IF( (m_pImpl->m_pFuncDesc && m_pImpl->m_pFuncDesc->getSuppressedArgumentCount() != m_pImpl->m_nArgs),
+ "formula.ui", "FormulaDlg::getCurrentFunctionDescription: getSuppressedArgumentCount " <<
+ m_pImpl->m_pFuncDesc->getSuppressedArgumentCount() << " != m_nArgs " << m_pImpl->m_nArgs << " for " <<
+ m_pImpl->m_pFuncDesc->getFunctionName());
+ return m_pImpl->m_pFuncDesc;
+}
+
+void FormulaDlg::UpdateParaWin( const Selection& _rSelection, const OUString& _sRefStr)
+{
+ m_pImpl->UpdateParaWin( _rSelection, _sRefStr);
+}
+
+bool FormulaDlg::UpdateParaWin(Selection& _rSelection)
+{
+ return m_pImpl->UpdateParaWin(_rSelection);
+}
+
+RefEdit* FormulaDlg::GetActiveEdit()
+{
+ return m_pImpl->m_xParaWin->GetActiveEdit();
+}
+
+const FormulaHelper& FormulaDlg::GetFormulaHelper() const
+{
+ return m_pImpl->GetFormulaHelper();
+}
+
+void FormulaDlg::SetEdSelection()
+{
+ m_pImpl->SetEdSelection();
+}
+
+void FormEditData::SaveValues()
+{
+ Reset();
+}
+
+void FormEditData::Reset()
+{
+ nMode = FormulaDlgMode::Formula;
+ nFStart = 0;
+ nOffset = 0;
+ bMatrix = false;
+ aSelection.Min() = 0;
+ aSelection.Max() = 0;
+ aUndoStr.clear();
+}
+
+FormEditData& FormEditData::operator=( const FormEditData& r )
+{
+ nMode = r.nMode;
+ nFStart = r.nFStart;
+ nOffset = r.nOffset;
+ aUndoStr = r.aUndoStr;
+ bMatrix = r.bMatrix ;
+ aSelection = r.aSelection;
+ return *this;
+}
+
+FormEditData::FormEditData()
+{
+ Reset();
+}
+
+FormEditData::~FormEditData()
+{
+}
+
+FormEditData::FormEditData( const FormEditData& r )
+{
+ *this = r;
+}
+
+
+} // formula
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/ui/dlg/funcpage.cxx b/formula/source/ui/dlg/funcpage.cxx
new file mode 100644
index 0000000000..dbdb49464a
--- /dev/null
+++ b/formula/source/ui/dlg/funcpage.cxx
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <formula/IFunctionDescription.hxx>
+
+#include "funcpage.hxx"
+#include <unotools/syslocale.hxx>
+#include <unotools/charclass.hxx>
+
+namespace formula
+{
+IMPL_LINK(FuncPage, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ if (rKEvt.GetCharCode() == ' ')
+ {
+ aDoubleClickLink.Call(*this);
+ return true;
+ }
+ return false;
+}
+
+// tdf#104487 - remember last used function category - set default to All category
+sal_Int32 FuncPage::m_nRememberedFunctionCategory = 1;
+
+FuncPage::FuncPage(weld::Container* pParent, const IFunctionManager* _pFunctionManager)
+ : m_xBuilder(Application::CreateBuilder(pParent, "formula/ui/functionpage.ui"))
+ , m_xContainer(m_xBuilder->weld_container("FunctionPage"))
+ , m_xLbCategory(m_xBuilder->weld_combo_box("category"))
+ , m_xLbFunction(m_xBuilder->weld_tree_view("function"))
+ , m_xLbFunctionSearchString(m_xBuilder->weld_entry("search"))
+ , m_pFunctionManager(_pFunctionManager)
+{
+ m_xLbFunction->make_sorted();
+ m_aHelpId = m_xLbFunction->get_help_id();
+
+ m_pFunctionManager->fillLastRecentlyUsedFunctions(aLRUList);
+
+ const sal_uInt32 nCategoryCount = m_pFunctionManager->getCount();
+ for (sal_uInt32 j = 0; j < nCategoryCount; ++j)
+ {
+ const IFunctionCategory* pCategory = m_pFunctionManager->getCategory(j);
+ OUString sId(weld::toId(pCategory));
+ m_xLbCategory->append(sId, pCategory->getName());
+ }
+
+ // tdf#104487 - remember last used function category
+ m_xLbCategory->set_active(m_nRememberedFunctionCategory);
+ OUString searchStr = m_xLbFunctionSearchString->get_text();
+ UpdateFunctionList(searchStr);
+ // lock to its initial size
+ m_xLbFunction->set_size_request(m_xLbFunction->get_preferred_size().Width(),
+ m_xLbFunction->get_height_rows(15));
+ m_xLbCategory->connect_changed(LINK(this, FuncPage, SelComboBoxHdl));
+ m_xLbFunction->connect_changed(LINK(this, FuncPage, SelTreeViewHdl));
+ m_xLbFunction->connect_row_activated(LINK(this, FuncPage, DblClkHdl));
+ m_xLbFunction->connect_key_press(LINK(this, FuncPage, KeyInputHdl));
+ m_xLbFunctionSearchString->connect_changed(LINK(this, FuncPage, ModifyHdl));
+
+ m_xLbFunctionSearchString->grab_focus();
+}
+
+FuncPage::~FuncPage() {}
+
+void FuncPage::impl_addFunctions(const IFunctionCategory* _pCategory)
+{
+ const sal_uInt32 nCount = _pCategory->getCount();
+ for (sal_uInt32 i = 0; i < nCount; ++i)
+ {
+ TFunctionDesc pDesc(_pCategory->getFunction(i));
+ if (!pDesc->isHidden())
+ {
+ OUString sId(weld::toId(pDesc));
+ m_xLbFunction->append(sId, pDesc->getFunctionName());
+ }
+ }
+}
+
+//aStr is non-empty when user types in the search box to search some function
+void FuncPage::UpdateFunctionList(const OUString& aStr)
+{
+ m_xLbFunction->clear();
+ m_xLbFunction->freeze();
+
+ const sal_Int32 nSelPos = m_xLbCategory->get_active();
+ // tdf#104487 - remember last used function category
+ m_nRememberedFunctionCategory = nSelPos;
+
+ if (aStr.isEmpty() || nSelPos == 0)
+ {
+ const IFunctionCategory* pCategory
+ = weld::fromId<const IFunctionCategory*>(m_xLbCategory->get_id(nSelPos));
+
+ if (nSelPos > 0)
+ {
+ if (pCategory == nullptr)
+ {
+ const sal_uInt32 nCount = m_pFunctionManager->getCount();
+ for (sal_uInt32 i = 0; i < nCount; ++i)
+ {
+ impl_addFunctions(m_pFunctionManager->getCategory(i));
+ }
+ }
+ else
+ {
+ impl_addFunctions(pCategory);
+ }
+ }
+ else // LRU-List
+ {
+ for (auto const& elem : aLRUList)
+ {
+ if (elem) // may be null if a function is no longer available
+ {
+ OUString sId(weld::toId(elem));
+ m_xLbFunction->append(sId, elem->getFunctionName());
+ }
+ }
+ }
+ }
+ else
+ {
+ SvtSysLocale aSysLocale;
+ const CharClass& rCharClass = aSysLocale.GetCharClass();
+ const OUString aSearchStr(rCharClass.uppercase(aStr));
+
+ const sal_uInt32 nCategoryCount = m_pFunctionManager->getCount();
+ // Category listbox holds additional entries for Last Used and All, so
+ // the offset should be two but hard coded numbers are ugly...
+ const sal_Int32 nCategoryOffset = m_xLbCategory->get_count() - nCategoryCount;
+ // If a real category (not Last Used or All) is selected, list only
+ // functions of that category. Else list all, LRU is handled above.
+ sal_Int32 nCatBeg = (nSelPos == -1 ? -1 : nSelPos - nCategoryOffset);
+ sal_uInt32 nCatEnd;
+ if (nCatBeg < 0)
+ {
+ nCatBeg = 0;
+ nCatEnd = nCategoryCount;
+ }
+ else
+ {
+ nCatEnd = nCatBeg + 1;
+ }
+ for (sal_uInt32 i = nCatBeg; i < nCatEnd; ++i)
+ {
+ const IFunctionCategory* pCategory = m_pFunctionManager->getCategory(i);
+ const sal_uInt32 nFunctionCount = pCategory->getCount();
+ for (sal_uInt32 j = 0; j < nFunctionCount; ++j)
+ {
+ TFunctionDesc pDesc(pCategory->getFunction(j));
+ // tdf#146781 - search for the desired function also in the description
+ if (rCharClass.uppercase(pDesc->getFunctionName()).indexOf(aSearchStr) >= 0
+ || rCharClass.uppercase(pDesc->getDescription()).indexOf(aSearchStr) >= 0)
+ {
+ if (!pDesc->isHidden())
+ {
+ OUString sId(weld::toId(pDesc));
+ m_xLbFunction->append(sId, pDesc->getFunctionName());
+ }
+ }
+ }
+ }
+ }
+
+ m_xLbFunction->thaw();
+ // Ensure no function is selected so the Next button doesn't overwrite a
+ // function that is not in the list with an arbitrary selected one.
+ m_xLbFunction->unselect_all();
+
+ if (IsVisible())
+ SelTreeViewHdl(*m_xLbFunction);
+}
+
+IMPL_LINK_NOARG(FuncPage, SelComboBoxHdl, weld::ComboBox&, void)
+{
+ OUString searchStr = m_xLbFunctionSearchString->get_text();
+ m_xLbFunction->set_help_id(m_aHelpId);
+ UpdateFunctionList(searchStr);
+}
+
+IMPL_LINK_NOARG(FuncPage, SelTreeViewHdl, weld::TreeView&, void)
+{
+ const IFunctionDescription* pDesc = GetFuncDesc(GetFunction());
+ if (pDesc)
+ {
+ const OUString sHelpId = pDesc->getHelpId();
+ if (!sHelpId.isEmpty())
+ m_xLbFunction->set_help_id(sHelpId);
+ }
+ aSelectionLink.Call(*this);
+}
+
+IMPL_LINK_NOARG(FuncPage, DblClkHdl, weld::TreeView&, bool)
+{
+ aDoubleClickLink.Call(*this);
+ return true;
+}
+
+IMPL_LINK_NOARG(FuncPage, ModifyHdl, weld::Entry&, void)
+{
+ // While typing select All category.
+ m_xLbCategory->set_active(1);
+ OUString searchStr = m_xLbFunctionSearchString->get_text();
+ UpdateFunctionList(searchStr);
+}
+
+void FuncPage::SetCategory(sal_Int32 nCat)
+{
+ // tdf#104487 - remember last used function category
+ m_nRememberedFunctionCategory = nCat;
+ m_xLbCategory->set_active(nCat);
+ UpdateFunctionList(OUString());
+}
+
+sal_Int32 FuncPage::GetFuncPos(const IFunctionDescription* _pDesc)
+{
+ return m_xLbFunction->find_id(weld::toId(_pDesc));
+}
+
+void FuncPage::SetFunction(sal_Int32 nFunc)
+{
+ if (nFunc == -1)
+ m_xLbFunction->unselect_all();
+ else
+ m_xLbFunction->select(nFunc);
+}
+
+void FuncPage::SetFocus() { m_xLbFunction->grab_focus(); }
+
+sal_Int32 FuncPage::GetCategory() const { return m_xLbCategory->get_active(); }
+
+sal_Int32 FuncPage::GetCategoryEntryCount() const { return m_xLbCategory->get_count(); }
+
+sal_Int32 FuncPage::GetFunction() const { return m_xLbFunction->get_selected_index(); }
+
+sal_Int32 FuncPage::GetFunctionEntryCount() const { return m_xLbFunction->n_children(); }
+
+OUString FuncPage::GetSelFunctionName() const { return m_xLbFunction->get_selected_text(); }
+
+const IFunctionDescription* FuncPage::GetFuncDesc(sal_Int32 nPos) const
+{
+ if (nPos == -1)
+ return nullptr;
+ // not pretty, but hopefully rare
+ return weld::fromId<const IFunctionDescription*>(m_xLbFunction->get_id(nPos));
+}
+
+} // formula
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/ui/dlg/funcpage.hxx b/formula/source/ui/dlg/funcpage.hxx
new file mode 100644
index 0000000000..e7ca248d86
--- /dev/null
+++ b/formula/source/ui/dlg/funcpage.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <vector>
+
+namespace formula
+{
+
+class IFunctionDescription;
+class IFunctionManager;
+class IFunctionCategory;
+
+typedef const IFunctionDescription* TFunctionDesc;
+
+class FuncPage final
+{
+private:
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Container> m_xContainer;
+
+ std::unique_ptr<weld::ComboBox> m_xLbCategory;
+ std::unique_ptr<weld::TreeView> m_xLbFunction;
+ std::unique_ptr<weld::Entry> m_xLbFunctionSearchString;
+
+ Link<FuncPage&,void> aDoubleClickLink;
+ Link<FuncPage&,void> aSelectionLink;
+ const IFunctionManager* m_pFunctionManager;
+
+ ::std::vector< TFunctionDesc > aLRUList;
+ OUString m_aHelpId;
+
+ // tdf#104487 - remember last used function category
+ static sal_Int32 m_nRememberedFunctionCategory;
+
+ void impl_addFunctions(const IFunctionCategory* _pCategory);
+
+ DECL_LINK(SelComboBoxHdl, weld::ComboBox&, void);
+ DECL_LINK(SelTreeViewHdl, weld::TreeView&, void);
+ DECL_LINK(DblClkHdl, weld::TreeView&, bool);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(ModifyHdl, weld::Entry&, void);
+
+ void UpdateFunctionList(const OUString&);
+
+public:
+
+ FuncPage(weld::Container* pContainer, const IFunctionManager* _pFunctionManager);
+ ~FuncPage();
+
+ void SetCategory(sal_Int32 nCat);
+ void SetFunction(sal_Int32 nFunc);
+ void SetFocus();
+ sal_Int32 GetCategory() const;
+ sal_Int32 GetCategoryEntryCount() const;
+ sal_Int32 GetFunction() const;
+ sal_Int32 GetFunctionEntryCount() const;
+
+ // tdf#104487 - remember last used function category
+ static sal_Int32 GetRememeberdFunctionCategory() { return m_nRememberedFunctionCategory; };
+
+ sal_Int32 GetFuncPos(const IFunctionDescription* _pDesc);
+ const IFunctionDescription* GetFuncDesc( sal_Int32 nPos ) const;
+ OUString GetSelFunctionName() const;
+
+ void SetDoubleClickHdl( const Link<FuncPage&,void>& rLink ) { aDoubleClickLink = rLink; }
+
+ void SetSelectHdl( const Link<FuncPage&,void>& rLink ) { aSelectionLink = rLink; }
+
+ bool IsVisible() const { return m_xContainer->get_visible(); }
+};
+
+} // formula
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/ui/dlg/funcutl.cxx b/formula/source/ui/dlg/funcutl.cxx
new file mode 100644
index 0000000000..58c2492c55
--- /dev/null
+++ b/formula/source/ui/dlg/funcutl.cxx
@@ -0,0 +1,486 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/event.hxx>
+
+#include <formula/funcutl.hxx>
+#include <formula/IControlReferenceHandler.hxx>
+#include <vcl/svapp.hxx>
+#include "ControlHelper.hxx"
+#include "parawin.hxx"
+#include <strings.hrc>
+#include <bitmaps.hlst>
+#include <core_resource.hxx>
+
+namespace formula
+{
+
+ArgEdit::ArgEdit(std::unique_ptr<weld::Entry> xControl)
+ : RefEdit(std::move(xControl))
+ , pEdPrev(nullptr)
+ , pEdNext(nullptr)
+ , pSlider(nullptr)
+ , pParaWin(nullptr)
+ , nArgs(0)
+{
+}
+
+void ArgEdit::Init(ArgEdit* pPrevEdit, ArgEdit* pNextEdit,
+ weld::ScrolledWindow& rArgSlider,
+ ParaWin& rParaWin, sal_uInt16 nArgCount)
+{
+ pEdPrev = pPrevEdit;
+ pEdNext = pNextEdit;
+ pSlider = &rArgSlider;
+ pParaWin = &rParaWin;
+ nArgs = nArgCount;
+}
+
+// Cursor control for Edit Fields in Argument Dialog
+bool ArgEdit::KeyInput(const KeyEvent& rKEvt)
+{
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+ bool bUp = (aCode.GetCode() == KEY_UP);
+ bool bDown = (aCode.GetCode() == KEY_DOWN);
+
+ if ( pSlider
+ && ( !aCode.IsShift() && !aCode.IsMod1() && !aCode.IsMod2() )
+ && ( bUp || bDown ) )
+ {
+ if ( nArgs > 1 )
+ {
+ ArgEdit* pEd = nullptr;
+ int nThumb = pSlider->vadjustment_get_value();
+ bool bDoScroll = false;
+ bool bChangeFocus = false;
+
+ if ( bDown )
+ {
+ if ( nArgs > 4 )
+ {
+ if ( !pEdNext )
+ {
+ nThumb++;
+ bDoScroll = ( nThumb+3 < static_cast<tools::Long>(nArgs) );
+ }
+ else
+ {
+ pEd = pEdNext;
+ bChangeFocus = true;
+ }
+ }
+ else if ( pEdNext )
+ {
+ pEd = pEdNext;
+ bChangeFocus = true;
+ }
+ }
+ else // if ( bUp )
+ {
+ if ( nArgs > 4 )
+ {
+ if ( !pEdPrev )
+ {
+ nThumb--;
+ bDoScroll = ( nThumb >= 0 );
+ }
+ else
+ {
+ pEd = pEdPrev;
+ bChangeFocus = true;
+ }
+ }
+ else if ( pEdPrev )
+ {
+ pEd = pEdPrev;
+ bChangeFocus = true;
+ }
+ }
+
+ if ( bDoScroll )
+ {
+ pSlider->vadjustment_set_value( nThumb );
+ pParaWin->SliderMoved();
+ }
+ else if ( bChangeFocus )
+ {
+ pEd->GrabFocus();
+ }
+ }
+ return true;
+ }
+ return RefEdit::KeyInput(rKEvt);
+}
+
+ArgInput::ArgInput()
+{
+ pFtArg=nullptr;
+ pBtnFx=nullptr;
+ pEdArg=nullptr;
+ pRefBtn=nullptr;
+}
+
+void ArgInput::InitArgInput(weld::Label* pftArg, weld::Button* pbtnFx,
+ ArgEdit* pedArg, RefButton* prefBtn)
+{
+ pFtArg =pftArg;
+ pBtnFx =pbtnFx;
+ pEdArg =pedArg;
+ pRefBtn=prefBtn;
+
+ if(pBtnFx!=nullptr)
+ {
+ pBtnFx->connect_clicked( LINK( this, ArgInput, FxBtnClickHdl ) );
+ pBtnFx->connect_focus_in( LINK( this, ArgInput, FxBtnFocusHdl ) );
+ }
+ if(pEdArg!=nullptr)
+ {
+ pEdArg->SetGetFocusHdl ( LINK( this, ArgInput, EdFocusHdl ) );
+ pEdArg->SetModifyHdl ( LINK( this, ArgInput, EdModifyHdl ) );
+ }
+}
+
+// Sets the Name for the Argument
+void ArgInput::SetArgName(const OUString &aArg)
+{
+ if (pFtArg)
+ pFtArg->set_label(aArg );
+}
+
+// Returns the Name for the Argument
+OUString ArgInput::GetArgName() const
+{
+ OUString aPrivArgName;
+ if (pFtArg)
+ aPrivArgName = pFtArg->get_label();
+ return aPrivArgName;
+}
+
+//Sets the Name for the Argument
+void ArgInput::SetArgNameFont(const vcl::Font &aFont)
+{
+ if (pFtArg)
+ pFtArg->set_font(aFont);
+}
+
+//Sets up the Selection for the EditBox.
+void ArgInput::SelectAll()
+{
+ if (pEdArg)
+ pEdArg->SelectAll();
+}
+
+//Sets the Value for the Argument
+void ArgInput::SetArgVal(const OUString &rVal)
+{
+ if (pEdArg)
+ pEdArg->SetRefString(rVal);
+}
+
+//Returns the Value for the Argument
+OUString ArgInput::GetArgVal() const
+{
+ OUString aResult;
+ if (pEdArg)
+ aResult=pEdArg->GetText();
+ return aResult;
+}
+
+//Hides the Controls
+void ArgInput::Hide()
+{
+ if (pFtArg && pBtnFx && pEdArg && pRefBtn)
+ {
+ pFtArg->hide();
+ pBtnFx->hide();
+ pEdArg->GetWidget()->hide();
+ pRefBtn->GetWidget()->hide();
+ }
+}
+
+//Casts the Controls again.
+void ArgInput::Show()
+{
+ if (pFtArg && pBtnFx && pEdArg && pRefBtn)
+ {
+ pFtArg->show();
+ pBtnFx->show();
+ pEdArg->GetWidget()->show();
+ pRefBtn->GetWidget()->show();
+ }
+}
+
+void ArgInput::UpdateAccessibleNames()
+{
+ OUString aArgName = ":" + pFtArg->get_label();
+
+ OUString aName = pBtnFx->get_tooltip_text() + aArgName;
+ pBtnFx->set_accessible_name(aName);
+
+ aName = pRefBtn->GetWidget()->get_tooltip_text() + aArgName;
+ pRefBtn->GetWidget()->set_accessible_name(aName);
+}
+
+IMPL_LINK(ArgInput, FxBtnClickHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == pBtnFx)
+ aFxClickLink.Call(*this);
+}
+
+IMPL_LINK( ArgInput, FxBtnFocusHdl, weld::Widget&, rControl, void )
+{
+ if (&rControl == pBtnFx)
+ aFxFocusLink.Call(*this);
+}
+
+IMPL_LINK( ArgInput, EdFocusHdl, RefEdit&, rControl, void )
+{
+ if (&rControl == pEdArg)
+ aEdFocusLink.Call(*this);
+}
+
+IMPL_LINK( ArgInput, EdModifyHdl, RefEdit&, rEdit, void )
+{
+ if (&rEdit == pEdArg)
+ aEdModifyLink.Call(*this);
+}
+
+RefEdit::RefEdit(std::unique_ptr<weld::Entry> xControl)
+ : xEntry(std::move(xControl))
+ , aIdle("formula RefEdit Idle")
+ , pAnyRefDlg(nullptr)
+ , pLabelWidget(nullptr)
+ , mpFocusInEvent(nullptr)
+ , mpFocusOutEvent(nullptr)
+{
+ xEntry->connect_focus_in(LINK(this, RefEdit, GetFocusHdl));
+ xEntry->connect_focus_out(LINK(this, RefEdit, LoseFocusHdl));
+ xEntry->connect_key_press(LINK(this, RefEdit, KeyInputHdl));
+ xEntry->connect_changed(LINK(this, RefEdit, Modify));
+ aIdle.SetInvokeHandler( LINK( this, RefEdit, UpdateHdl ) );
+}
+
+RefEdit::~RefEdit()
+{
+ if (mpFocusInEvent)
+ Application::RemoveUserEvent(mpFocusInEvent);
+ if (mpFocusOutEvent)
+ Application::RemoveUserEvent(mpFocusOutEvent);
+ aIdle.ClearInvokeHandler();
+ aIdle.Stop();
+}
+
+void RefEdit::SetRefString( const OUString& rStr )
+{
+ // Prevent unwanted side effects by setting only a differing string.
+ // See commit message for reasons.
+ if (xEntry->get_text() != rStr)
+ xEntry->set_text(rStr);
+}
+
+void RefEdit::SetRefValid(bool bValid)
+{
+ xEntry->set_message_type(bValid ? weld::EntryMessageType::Normal : weld::EntryMessageType::Error);
+}
+
+void RefEdit::SetText(const OUString& rStr)
+{
+ xEntry->set_text(rStr);
+ UpdateHdl( &aIdle );
+}
+
+void RefEdit::StartUpdateData()
+{
+ aIdle.Start();
+}
+
+void RefEdit::SetReferences(IControlReferenceHandler* pDlg, weld::Label* pLabel)
+{
+ pAnyRefDlg = pDlg;
+ pLabelWidget = pLabel;
+
+ if( pDlg )
+ {
+ aIdle.SetInvokeHandler(LINK(this, RefEdit, UpdateHdl));
+ }
+ else
+ {
+ aIdle.ClearInvokeHandler();
+ aIdle.Stop();
+ }
+}
+
+IMPL_LINK_NOARG(RefEdit, Modify, weld::Entry&, void)
+{
+ maModifyHdl.Call(*this);
+ if (pAnyRefDlg)
+ pAnyRefDlg->HideReference();
+}
+
+IMPL_LINK(RefEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ return KeyInput(rKEvt);
+}
+
+bool RefEdit::KeyInput(const KeyEvent& rKEvt)
+{
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ if (pAnyRefDlg && !rKeyCode.GetModifier() && rKeyCode.GetCode() == KEY_F2)
+ {
+ pAnyRefDlg->ReleaseFocus( this );
+ return true;
+ }
+
+ switch (rKeyCode.GetCode())
+ {
+ case KEY_RETURN:
+ case KEY_ESCAPE:
+ return maActivateHdl.Call(*GetWidget());
+ }
+
+ return false;
+}
+
+void RefEdit::GetFocus()
+{
+ maGetFocusHdl.Call(*this);
+ StartUpdateData();
+}
+
+void RefEdit::LoseFocus()
+{
+ maLoseFocusHdl.Call(*this);
+ if( pAnyRefDlg )
+ pAnyRefDlg->HideReference();
+}
+
+IMPL_LINK_NOARG(RefEdit, GetFocusHdl, weld::Widget&, void)
+{
+ // in e.g. function wizard RefEdits we want to select all when we get focus
+ // but in the gtk case there are pending gtk handlers which change selection
+ // after our handler, so post our focus in event to happen after those complete
+ if (mpFocusInEvent)
+ Application::RemoveUserEvent(mpFocusInEvent);
+ mpFocusInEvent = Application::PostUserEvent(LINK(this, RefEdit, AsyncFocusInHdl));
+}
+
+IMPL_LINK_NOARG(RefEdit, LoseFocusHdl, weld::Widget&, void)
+{
+ // tdf#127262 because focus in is async, focus out must not appear out
+ // of sequence to focus in
+ if (mpFocusOutEvent)
+ Application::RemoveUserEvent(mpFocusOutEvent);
+ mpFocusOutEvent = Application::PostUserEvent(LINK(this, RefEdit, AsyncFocusOutHdl));
+}
+
+IMPL_LINK_NOARG(RefEdit, AsyncFocusInHdl, void*, void)
+{
+ mpFocusInEvent = nullptr;
+ GetFocus();
+}
+
+IMPL_LINK_NOARG(RefEdit, AsyncFocusOutHdl, void*, void)
+{
+ mpFocusOutEvent = nullptr;
+ LoseFocus();
+}
+
+IMPL_LINK_NOARG(RefEdit, UpdateHdl, Timer *, void)
+{
+ if( pAnyRefDlg )
+ pAnyRefDlg->ShowReference(xEntry->get_text());
+}
+
+RefButton::RefButton(std::unique_ptr<weld::Button> xControl)
+ : xButton(std::move(xControl))
+ , pAnyRefDlg( nullptr )
+ , pRefEdit( nullptr )
+{
+ xButton->connect_focus_in(LINK(this, RefButton, GetFocus));
+ xButton->connect_focus_out(LINK(this, RefButton, LoseFocus));
+ xButton->connect_key_press(LINK(this, RefButton, KeyInput));
+ xButton->connect_clicked(LINK(this, RefButton, Click));
+ SetStartImage();
+}
+
+RefButton::~RefButton()
+{
+}
+
+void RefButton::SetStartImage()
+{
+ xButton->set_from_icon_name(RID_BMP_REFBTN1);
+ xButton->set_tooltip_text(ForResId(RID_STR_SHRINK));
+}
+
+void RefButton::SetEndImage()
+{
+ xButton->set_from_icon_name(RID_BMP_REFBTN2);
+ xButton->set_tooltip_text(ForResId(RID_STR_EXPAND));
+}
+
+void RefButton::SetReferences( IControlReferenceHandler* pDlg, RefEdit* pEdit )
+{
+ pAnyRefDlg = pDlg;
+ pRefEdit = pEdit;
+}
+
+IMPL_LINK_NOARG(RefButton, Click, weld::Button&, void)
+{
+ maClickHdl.Call(*this);
+ if( pAnyRefDlg )
+ pAnyRefDlg->ToggleCollapsed( pRefEdit, this );
+}
+
+IMPL_LINK(RefButton, KeyInput, const KeyEvent&, rKEvt, bool)
+{
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ if (pAnyRefDlg && !rKeyCode.GetModifier() && rKeyCode.GetCode() == KEY_F2)
+ {
+ pAnyRefDlg->ReleaseFocus( pRefEdit );
+ return true;
+ }
+
+ switch (rKeyCode.GetCode())
+ {
+ case KEY_RETURN:
+ case KEY_ESCAPE:
+ return maActivateHdl.Call(*GetWidget());
+ }
+
+ return false;
+}
+
+IMPL_LINK_NOARG(RefButton, GetFocus, weld::Widget&, void)
+{
+ maGetFocusHdl.Call(*this);
+ if (pRefEdit)
+ pRefEdit->StartUpdateData();
+}
+
+IMPL_LINK_NOARG(RefButton, LoseFocus, weld::Widget&, void)
+{
+ maLoseFocusHdl.Call(*this);
+ if (pRefEdit)
+ pRefEdit->DoModify();
+}
+
+} // formula
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/ui/dlg/parawin.cxx b/formula/source/ui/dlg/parawin.cxx
new file mode 100644
index 0000000000..bcc1c2d1a9
--- /dev/null
+++ b/formula/source/ui/dlg/parawin.cxx
@@ -0,0 +1,600 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/string.hxx>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+
+#include "parawin.hxx"
+#include <formula/IFunctionDescription.hxx>
+#include <formula/funcvarargs.h>
+#include <strings.hrc>
+#include <bitmaps.hlst>
+#include <core_resource.hxx>
+
+namespace formula
+{
+
+// Formula token argument count is sal_uInt8, max 255, edit offset 254.
+constexpr sal_uInt16 kMaxArgCount = 255;
+constexpr sal_uInt16 kMaxArgOffset = kMaxArgCount - 1;
+
+ParaWin::ParaWin(weld::Container* pParent,IControlReferenceHandler* _pDlg)
+ : pFuncDesc(nullptr)
+ , pMyParent(_pDlg)
+ , m_sOptional(ForResId(STR_OPTIONAL))
+ , m_sRequired(ForResId(STR_REQUIRED))
+ , m_xBuilder(Application::CreateBuilder(pParent, "formula/ui/parameter.ui"))
+ , m_xContainer(m_xBuilder->weld_container("ParameterPage"))
+ , m_xSlider(m_xBuilder->weld_scrolled_window("scrollbar", true))
+ , m_xParamGrid(m_xBuilder->weld_widget("paramgrid"))
+ , m_xGrid(m_xBuilder->weld_widget("grid"))
+ , m_xFtEditDesc(m_xBuilder->weld_label("editdesc"))
+ , m_xFtArgName(m_xBuilder->weld_label("parname"))
+ , m_xFtArgDesc(m_xBuilder->weld_label("pardesc"))
+ , m_xBtnFx1(m_xBuilder->weld_button("FX1"))
+ , m_xBtnFx2(m_xBuilder->weld_button("FX2"))
+ , m_xBtnFx3(m_xBuilder->weld_button("FX3"))
+ , m_xBtnFx4(m_xBuilder->weld_button("FX4"))
+ , m_xFtArg1(m_xBuilder->weld_label("FT_ARG1"))
+ , m_xFtArg2(m_xBuilder->weld_label("FT_ARG2"))
+ , m_xFtArg3(m_xBuilder->weld_label("FT_ARG3"))
+ , m_xFtArg4(m_xBuilder->weld_label("FT_ARG4"))
+ , m_xEdArg1(new ArgEdit(m_xBuilder->weld_entry("ED_ARG1")))
+ , m_xEdArg2(new ArgEdit(m_xBuilder->weld_entry("ED_ARG2")))
+ , m_xEdArg3(new ArgEdit(m_xBuilder->weld_entry("ED_ARG3")))
+ , m_xEdArg4(new ArgEdit(m_xBuilder->weld_entry("ED_ARG4")))
+ , m_xRefBtn1(new RefButton(m_xBuilder->weld_button("RB_ARG1")))
+ , m_xRefBtn2(new RefButton(m_xBuilder->weld_button("RB_ARG2")))
+ , m_xRefBtn3(new RefButton(m_xBuilder->weld_button("RB_ARG3")))
+ , m_xRefBtn4(new RefButton(m_xBuilder->weld_button("RB_ARG4")))
+{
+ // Space for three lines of text in function description.
+ m_xFtEditDesc->set_label("X\nX\nX\n");
+ auto nEditHeight = m_xFtEditDesc->get_preferred_size().Height();
+ m_xFtEditDesc->set_size_request(-1, nEditHeight);
+ m_xFtEditDesc->set_label("");
+ // Space for two lines of text in parameter description.
+ m_xFtArgDesc->set_label("X\nX\n");
+ auto nArgHeight = m_xFtArgDesc->get_preferred_size().Height();
+ m_xFtArgDesc->set_size_request(-1, nArgHeight);
+ m_xFtArgDesc->set_label("");
+
+ m_xBtnFx1->set_from_icon_name(BMP_FX);
+ m_xBtnFx2->set_from_icon_name(BMP_FX);
+ m_xBtnFx3->set_from_icon_name(BMP_FX);
+ m_xBtnFx4->set_from_icon_name(BMP_FX);
+
+ //lock down initial preferences
+ m_xParamGrid->set_size_request(-1, m_xParamGrid->get_preferred_size().Height());
+ Size aSize(m_xContainer->get_preferred_size());
+ m_xContainer->set_size_request(aSize.Width(), aSize.Height());
+
+ aDefaultString = m_xFtEditDesc->get_label();
+ nEdFocus = NOT_FOUND;
+ nActiveLine = 0;
+
+ m_xSlider->connect_vadjustment_changed(LINK(this, ParaWin, ScrollHdl));
+
+ InitArgInput( 0, *m_xFtArg1, *m_xBtnFx1, *m_xEdArg1, *m_xRefBtn1);
+ InitArgInput( 1, *m_xFtArg2, *m_xBtnFx2, *m_xEdArg2, *m_xRefBtn2);
+ InitArgInput( 2, *m_xFtArg3, *m_xBtnFx3, *m_xEdArg3, *m_xRefBtn3);
+ InitArgInput( 3, *m_xFtArg4, *m_xBtnFx4, *m_xEdArg4, *m_xRefBtn4);
+ ClearAll();
+}
+
+void ParaWin::UpdateArgDesc( sal_uInt16 nArg )
+{
+ if (nArg == NOT_FOUND)
+ return;
+
+ if (nMaxArgs > 4)
+ nArg = sal::static_int_cast<sal_uInt16>( nArg + GetSliderPos() );
+
+ if ((nMaxArgs <= 0) || (nArg >= nMaxArgs))
+ return;
+
+ OUString aArgDesc;
+ OUString aArgName;
+
+ SetArgumentDesc( OUString() );
+ SetArgumentText( OUString() );
+
+ if ( nArgs < VAR_ARGS )
+ {
+ sal_uInt16 nRealArg = (nArg < aVisibleArgMapping.size()) ? aVisibleArgMapping[nArg] : nArg;
+ aArgDesc = pFuncDesc->getParameterDescription(nRealArg);
+ aArgName = pFuncDesc->getParameterName(nRealArg) + " " +
+ ((pFuncDesc->isParameterOptional(nRealArg)) ? m_sOptional : m_sRequired);
+ }
+ else if ( nArgs < PAIRED_VAR_ARGS )
+ {
+ sal_uInt16 nFix = nArgs - VAR_ARGS;
+ sal_uInt16 nPos = std::min( nArg, nFix );
+ sal_uInt16 nRealArg = (nPos < aVisibleArgMapping.size() ?
+ aVisibleArgMapping[nPos] : aVisibleArgMapping.back());
+ aArgDesc = pFuncDesc->getParameterDescription(nRealArg);
+ aArgName = pFuncDesc->getParameterName(nRealArg);
+ sal_uInt16 nVarArgsStart = pFuncDesc->getVarArgsStart();
+ if ( nArg >= nVarArgsStart )
+ aArgName += OUString::number( nArg-nVarArgsStart+1 );
+ aArgName += " " + ((nArg > nFix || pFuncDesc->isParameterOptional(nRealArg)) ? m_sOptional : m_sRequired) ;
+ }
+ else
+ {
+ sal_uInt16 nFix = nArgs - PAIRED_VAR_ARGS;
+ sal_uInt16 nPos;
+ if ( nArg < nFix )
+ nPos = nArg;
+ else
+ nPos = nFix + ( (nArg-nFix) % 2);
+ sal_uInt16 nRealArg = (nPos < aVisibleArgMapping.size() ?
+ aVisibleArgMapping[nPos] : aVisibleArgMapping.back());
+ aArgDesc = pFuncDesc->getParameterDescription(nRealArg);
+ aArgName = pFuncDesc->getParameterName(nRealArg);
+ sal_uInt16 nVarArgsStart = pFuncDesc->getVarArgsStart();
+ if ( nArg >= nVarArgsStart )
+ aArgName += OUString::number( (nArg-nVarArgsStart)/2 + 1 );
+ aArgName += " " + ((nArg > (nFix+1) || pFuncDesc->isParameterOptional(nRealArg)) ? m_sOptional : m_sRequired) ;
+ }
+
+ SetArgumentDesc(aArgDesc);
+ SetArgumentText(aArgName);
+}
+
+void ParaWin::UpdateArgInput( sal_uInt16 nOffset, sal_uInt16 i )
+{
+ sal_uInt16 nArg = nOffset + i;
+ if (nArg > kMaxArgOffset)
+ return;
+
+ if ( nArgs < VAR_ARGS)
+ {
+ if (nArg < nMaxArgs)
+ {
+ sal_uInt16 nRealArg = aVisibleArgMapping[nArg];
+ SetArgNameFont (i,(pFuncDesc->isParameterOptional(nRealArg))
+ ? aFntLight : aFntBold );
+ SetArgName (i,pFuncDesc->getParameterName(nRealArg));
+ }
+ }
+ else if ( nArgs < PAIRED_VAR_ARGS)
+ {
+ sal_uInt16 nFix = nArgs - VAR_ARGS;
+ sal_uInt16 nPos = std::min( nArg, nFix );
+ sal_uInt16 nRealArg = (nPos < aVisibleArgMapping.size() ?
+ aVisibleArgMapping[nPos] : aVisibleArgMapping.back());
+ SetArgNameFont( i,
+ (nArg > nFix || pFuncDesc->isParameterOptional(nRealArg)) ?
+ aFntLight : aFntBold );
+ sal_uInt16 nVarArgsStart = pFuncDesc->getVarArgsStart();
+ if ( nArg >= nVarArgsStart )
+ {
+ OUString aArgName = pFuncDesc->getParameterName(nRealArg) +
+ OUString::number(nArg-nVarArgsStart+1);
+ SetArgName( i, aArgName );
+ }
+ else
+ SetArgName( i, pFuncDesc->getParameterName(nRealArg) );
+ }
+ else
+ {
+ sal_uInt16 nFix = nArgs - PAIRED_VAR_ARGS;
+ sal_uInt16 nPos;
+ if ( nArg < nFix )
+ nPos = nArg;
+ else
+ nPos = nFix + ( (nArg-nFix) % 2);
+ sal_uInt16 nRealArg = (nPos < aVisibleArgMapping.size() ?
+ aVisibleArgMapping[nPos] : aVisibleArgMapping.back());
+ SetArgNameFont( i,
+ (nArg > (nFix+1) || pFuncDesc->isParameterOptional(nRealArg)) ?
+ aFntLight : aFntBold );
+ sal_uInt16 nVarArgsStart = pFuncDesc->getVarArgsStart();
+ if ( nArg >= nVarArgsStart )
+ {
+ OUString aArgName = pFuncDesc->getParameterName(nRealArg) +
+ OUString::number( (nArg-nVarArgsStart)/2 + 1 );
+ SetArgName( i, aArgName );
+ }
+ else
+ SetArgName( i, pFuncDesc->getParameterName(nRealArg) );
+ }
+ if (nArg < nMaxArgs)
+ aArgInput[i].SetArgVal(aParaArray[nArg]);
+}
+
+ParaWin::~ParaWin()
+{
+ // #i66422# if the focus changes during destruction of the controls,
+ // don't call the focus handlers
+ Link<weld::Widget&,void> aEmptyLink;
+ m_xBtnFx1->connect_focus_in(aEmptyLink);
+ m_xBtnFx2->connect_focus_in(aEmptyLink);
+ m_xBtnFx3->connect_focus_in(aEmptyLink);
+ m_xBtnFx4->connect_focus_in(aEmptyLink);
+}
+
+void ParaWin::SetActiveLine(sal_uInt16 no)
+{
+ if (no >= nMaxArgs)
+ return;
+
+ tools::Long nOffset = GetSliderPos();
+ nActiveLine=no;
+ tools::Long nNewEdPos=static_cast<tools::Long>(nActiveLine)-nOffset;
+ if(nNewEdPos<0 || nNewEdPos>3)
+ {
+ nOffset+=nNewEdPos;
+ SetSliderPos(static_cast<sal_uInt16>(nOffset));
+ nOffset=GetSliderPos();
+ }
+ nEdFocus=no-static_cast<sal_uInt16>(nOffset);
+ UpdateArgDesc( nEdFocus );
+}
+
+RefEdit* ParaWin::GetActiveEdit()
+{
+ if (nMaxArgs > 0 && nEdFocus != NOT_FOUND)
+ {
+ return aArgInput[nEdFocus].GetArgEdPtr();
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+
+OUString ParaWin::GetArgument(sal_uInt16 no)
+{
+ OUString aStr;
+ if(no<aParaArray.size())
+ {
+ aStr=aParaArray[no];
+ if(no==nActiveLine && aStr.isEmpty())
+ aStr += " ";
+ }
+ return aStr;
+}
+
+OUString ParaWin::GetActiveArgName() const
+{
+ OUString aStr;
+ if (nMaxArgs > 0 && nEdFocus != NOT_FOUND)
+ {
+ aStr=aArgInput[nEdFocus].GetArgName();
+ }
+ return aStr;
+}
+
+
+void ParaWin::SetArgument(sal_uInt16 no, std::u16string_view aString)
+{
+ if (no < aParaArray.size())
+ aParaArray[no] = comphelper::string::stripStart(aString, ' ');
+}
+
+void ParaWin::SetArgumentFonts(const vcl::Font&aBoldFont,const vcl::Font&aLightFont)
+{
+ aFntBold=aBoldFont;
+ aFntLight=aLightFont;
+}
+
+void ParaWin::SetFunctionDesc(const IFunctionDescription* pFDesc)
+{
+ pFuncDesc=pFDesc;
+
+ SetArgumentDesc( OUString() );
+ SetArgumentText( OUString() );
+ SetEditDesc( OUString() );
+ nMaxArgs = nArgs = 0;
+ if ( pFuncDesc!=nullptr)
+ {
+ if ( !pFuncDesc->getDescription().isEmpty() )
+ {
+ SetEditDesc(pFuncDesc->getDescription());
+ }
+ else
+ {
+ SetEditDesc(aDefaultString);
+ }
+ nArgs = pFuncDesc->getSuppressedArgumentCount();
+ nMaxArgs = std::min( nArgs, kMaxArgCount);
+ if (sal_uInt16 nVarArgsLimit = pFuncDesc->getVarArgsLimit())
+ nMaxArgs = std::min( nVarArgsLimit, nMaxArgs);
+ pFuncDesc->fillVisibleArgumentMapping(aVisibleArgMapping);
+ m_xSlider->set_vpolicy(VclPolicyType::NEVER);
+ m_xSlider->set_size_request(-1, -1);
+ OUString sHelpId = pFuncDesc->getHelpId();
+ m_xContainer->set_help_id(sHelpId);
+ m_xEdArg1->GetWidget()->set_help_id(sHelpId);
+ m_xEdArg2->GetWidget()->set_help_id(sHelpId);
+ m_xEdArg3->GetWidget()->set_help_id(sHelpId);
+ m_xEdArg4->GetWidget()->set_help_id(sHelpId);
+
+ SetActiveLine(0);
+ }
+ else
+ {
+ nActiveLine=0;
+ }
+
+}
+
+void ParaWin::SetArgumentText(const OUString& aText)
+{
+ m_xFtArgName->set_label(aText);
+}
+
+void ParaWin::SetArgumentDesc(const OUString& aText)
+{
+ m_xFtArgDesc->set_label(aText);
+}
+
+void ParaWin::SetEditDesc(const OUString& aText)
+{
+ m_xFtEditDesc->set_label(aText);
+}
+
+void ParaWin::SetArgName(sal_uInt16 no,const OUString& aText)
+{
+ aArgInput[no].SetArgName(aText);
+ aArgInput[no].UpdateAccessibleNames();
+}
+
+void ParaWin::SetArgNameFont(sal_uInt16 no,const vcl::Font& aFont)
+{
+ aArgInput[no].SetArgNameFont(aFont);
+}
+
+void ParaWin::SetEdFocus()
+{
+ UpdateArgDesc(0);
+ if(!aParaArray.empty())
+ aArgInput[0].GetArgEdPtr()->GrabFocus();
+}
+
+void ParaWin::InitArgInput(sal_uInt16 nPos, weld::Label& rFtArg, weld::Button& rBtnFx,
+ ArgEdit& rEdArg, RefButton& rRefBtn)
+{
+
+ rRefBtn.SetReferences(pMyParent, &rEdArg);
+ rEdArg.SetReferences(pMyParent, &rFtArg);
+
+ aArgInput[nPos].InitArgInput (&rFtArg,&rBtnFx,&rEdArg,&rRefBtn);
+
+ aArgInput[nPos].Hide();
+
+ aArgInput[nPos].SetFxClickHdl ( LINK( this, ParaWin, GetFxHdl ) );
+ aArgInput[nPos].SetFxFocusHdl ( LINK( this, ParaWin, GetFxFocusHdl ) );
+ aArgInput[nPos].SetEdFocusHdl ( LINK( this, ParaWin, GetEdFocusHdl ) );
+ aArgInput[nPos].SetEdModifyHdl ( LINK( this, ParaWin, ModifyHdl ) );
+ aArgInput[nPos].UpdateAccessibleNames();
+}
+
+void ParaWin::ClearAll()
+{
+ SetFunctionDesc(nullptr);
+ SetArgumentOffset(0);
+}
+
+void ParaWin::SetArgumentOffset(sal_uInt16 nOffset)
+{
+ aParaArray.clear();
+ m_xSlider->vadjustment_set_value(0);
+
+ aParaArray.resize(nMaxArgs);
+
+ if (nMaxArgs > 0)
+ {
+ for ( int i=0; i<4 && i<nMaxArgs; i++ )
+ {
+ aArgInput[i].SetArgVal(OUString());
+ aArgInput[i].GetArgEdPtr()->Init(
+ (i==0) ? nullptr : aArgInput[i-1].GetArgEdPtr(),
+ (i==3 || i==nMaxArgs-1) ? nullptr : aArgInput[i+1].GetArgEdPtr(),
+ *m_xSlider, *this, nMaxArgs );
+ }
+ }
+
+ UpdateParas();
+
+ if (nMaxArgs < 5)
+ {
+ m_xSlider->set_vpolicy(VclPolicyType::NEVER);
+ m_xSlider->set_size_request(-1, -1);
+ }
+ else
+ {
+ m_xSlider->vadjustment_configure(nOffset, 0, nMaxArgs, 1, 4, 4);
+ m_xSlider->set_vpolicy(VclPolicyType::ALWAYS);
+ Size aPrefSize(m_xGrid->get_preferred_size());
+ m_xSlider->set_size_request(aPrefSize.Width(), aPrefSize.Height());
+ }
+}
+
+void ParaWin::UpdateParas()
+{
+ sal_uInt16 i;
+ sal_uInt16 nOffset = GetSliderPos();
+
+ if ( nMaxArgs > 0 )
+ {
+ for ( i=0; (i<nMaxArgs) && (i<4); i++ )
+ {
+ UpdateArgInput( nOffset, i );
+ aArgInput[i].Show();
+ }
+ }
+
+ for ( i=nMaxArgs; i<4; i++ )
+ aArgInput[i].Hide();
+}
+
+
+sal_uInt16 ParaWin::GetSliderPos() const
+{
+ return static_cast<sal_uInt16>(m_xSlider->vadjustment_get_value());
+}
+
+void ParaWin::SetSliderPos(sal_uInt16 nSliderPos)
+{
+ sal_uInt16 nOffset = GetSliderPos();
+
+ if(m_xSlider->get_visible() && nOffset!=nSliderPos)
+ {
+ m_xSlider->vadjustment_set_value(nSliderPos);
+ for ( sal_uInt16 i=0; i<4; i++ )
+ {
+ UpdateArgInput( nSliderPos, i );
+ }
+ }
+}
+
+void ParaWin::SliderMoved()
+{
+ sal_uInt16 nOffset = GetSliderPos();
+
+ for ( sal_uInt16 i=0; i<4; i++ )
+ {
+ UpdateArgInput( nOffset, i );
+ }
+ if(nEdFocus!=NOT_FOUND)
+ {
+ UpdateArgDesc( nEdFocus );
+ aArgInput[nEdFocus].SelectAll();
+ nActiveLine=nEdFocus+nOffset;
+ ArgumentModified();
+ aArgInput[nEdFocus].SelectAll(); // ensure all is still selected
+ aArgInput[nEdFocus].UpdateAccessibleNames();
+ }
+}
+
+void ParaWin::ArgumentModified()
+{
+ aArgModifiedLink.Call(*this);
+}
+
+IMPL_LINK( ParaWin, GetFxHdl, ArgInput&, rPtr, void )
+{
+ sal_uInt16 nOffset = GetSliderPos();
+ nEdFocus=NOT_FOUND;
+ for (size_t nPos=0; nPos < std::size(aArgInput); ++nPos)
+ {
+ if(&rPtr == &aArgInput[nPos])
+ {
+ nEdFocus=nPos;
+ break;
+ }
+ }
+
+ if(nEdFocus!=NOT_FOUND)
+ {
+ aArgInput[nEdFocus].SelectAll();
+ nActiveLine=nEdFocus+nOffset;
+ aFxLink.Call(*this);
+ }
+}
+
+IMPL_LINK( ParaWin, GetFxFocusHdl, ArgInput&, rPtr, void )
+{
+ sal_uInt16 nOffset = GetSliderPos();
+ nEdFocus=NOT_FOUND;
+ for (size_t nPos=0; nPos < SAL_N_ELEMENTS(aArgInput); ++nPos)
+ {
+ if(&rPtr == &aArgInput[nPos])
+ {
+ nEdFocus=nPos;
+ break;
+ }
+ }
+
+ if(nEdFocus!=NOT_FOUND)
+ {
+ aArgInput[nEdFocus].SelectAll();
+ UpdateArgDesc( nEdFocus );
+ nActiveLine=nEdFocus+nOffset;
+ }
+}
+
+IMPL_LINK( ParaWin, GetEdFocusHdl, ArgInput&, rPtr, void )
+{
+ sal_uInt16 nOffset = GetSliderPos();
+ nEdFocus=NOT_FOUND;
+ for (size_t nPos=0; nPos < SAL_N_ELEMENTS(aArgInput); ++nPos)
+ {
+ if(&rPtr == &aArgInput[nPos])
+ {
+ nEdFocus=nPos;
+ break;
+ }
+ }
+
+ if(nEdFocus!=NOT_FOUND)
+ {
+ aArgInput[nEdFocus].SelectAll();
+ UpdateArgDesc( nEdFocus );
+ nActiveLine=nEdFocus+nOffset;
+ ArgumentModified();
+ aArgInput[nEdFocus].SelectAll(); // ensure all is still selected
+ aArgInput[nEdFocus].UpdateAccessibleNames();
+ }
+}
+
+IMPL_LINK_NOARG(ParaWin, ScrollHdl, weld::ScrolledWindow&, void)
+{
+ SliderMoved();
+}
+
+IMPL_LINK( ParaWin, ModifyHdl, ArgInput&, rPtr, void )
+{
+ sal_uInt16 nOffset = GetSliderPos();
+ nEdFocus=NOT_FOUND;
+ for (size_t nPos=0; nPos < SAL_N_ELEMENTS(aArgInput); ++nPos)
+ {
+ if(&rPtr == &aArgInput[nPos])
+ {
+ nEdFocus=nPos;
+ break;
+ }
+ }
+ if(nEdFocus!=NOT_FOUND)
+ {
+ size_t nPara = nEdFocus + nOffset;
+ if (nPara < aParaArray.size())
+ aParaArray[nPara] = aArgInput[nEdFocus].GetArgVal();
+ else
+ {
+ SAL_WARN("formula.ui","ParaWin::ModifyHdl - shot in foot: nPara " <<
+ nPara << " >= aParaArray.size() " << aParaArray.size() <<
+ " with nEdFocus " << nEdFocus <<
+ " and aArgInput[nEdFocus].GetArgVal() '" << aArgInput[nEdFocus].GetArgVal() << "'");
+ }
+ UpdateArgDesc( nEdFocus);
+ nActiveLine = static_cast<sal_uInt16>(nPara);
+ }
+
+ ArgumentModified();
+}
+
+
+} // formula
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/ui/dlg/parawin.hxx b/formula/source/ui/dlg/parawin.hxx
new file mode 100644
index 0000000000..b2fe7ece62
--- /dev/null
+++ b/formula/source/ui/dlg/parawin.hxx
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <formula/funcutl.hxx>
+#include <vcl/weld.hxx>
+#include "ControlHelper.hxx"
+#include <vector>
+
+namespace formula
+{
+
+#define NOT_FOUND 0xffff
+
+class IFunctionDescription;
+class IControlReferenceHandler;
+
+class ParaWin
+{
+private:
+ Link<ParaWin&,void> aFxLink;
+ Link<ParaWin&,void> aArgModifiedLink;
+
+ ::std::vector<sal_uInt16> aVisibleArgMapping;
+ const IFunctionDescription* pFuncDesc;
+ IControlReferenceHandler* pMyParent;
+ sal_uInt16 nArgs; // unsuppressed arguments, may be >= VAR_ARGS to indicate repeating parameters
+ sal_uInt16 nMaxArgs; // max arguments, limited to supported number of arguments
+ vcl::Font aFntBold;
+ vcl::Font aFntLight;
+
+ OUString m_sOptional;
+ OUString m_sRequired;
+
+ sal_uInt16 nEdFocus;
+ sal_uInt16 nActiveLine;
+
+ ArgInput aArgInput[4];
+ OUString aDefaultString;
+ ::std::vector<OUString> aParaArray;
+
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Container> m_xContainer;
+
+ std::unique_ptr<weld::ScrolledWindow> m_xSlider;
+ std::unique_ptr<weld::Widget> m_xParamGrid;
+ std::unique_ptr<weld::Widget> m_xGrid;
+
+ std::unique_ptr<weld::Label> m_xFtEditDesc;
+ std::unique_ptr<weld::Label> m_xFtArgName;
+ std::unique_ptr<weld::Label> m_xFtArgDesc;
+
+ std::unique_ptr<weld::Button> m_xBtnFx1;
+ std::unique_ptr<weld::Button> m_xBtnFx2;
+ std::unique_ptr<weld::Button> m_xBtnFx3;
+ std::unique_ptr<weld::Button> m_xBtnFx4;
+
+ std::unique_ptr<weld::Label> m_xFtArg1;
+ std::unique_ptr<weld::Label> m_xFtArg2;
+ std::unique_ptr<weld::Label> m_xFtArg3;
+ std::unique_ptr<weld::Label> m_xFtArg4;
+
+ std::unique_ptr<ArgEdit> m_xEdArg1;
+ std::unique_ptr<ArgEdit> m_xEdArg2;
+ std::unique_ptr<ArgEdit> m_xEdArg3;
+ std::unique_ptr<ArgEdit> m_xEdArg4;
+
+ std::unique_ptr<RefButton> m_xRefBtn1;
+ std::unique_ptr<RefButton> m_xRefBtn2;
+ std::unique_ptr<RefButton> m_xRefBtn3;
+ std::unique_ptr<RefButton> m_xRefBtn4;
+
+ DECL_LINK( ScrollHdl, weld::ScrolledWindow&, void);
+ DECL_LINK( ModifyHdl, ArgInput&, void );
+ DECL_LINK( GetEdFocusHdl, ArgInput&, void );
+ DECL_LINK( GetFxFocusHdl, ArgInput&, void );
+ DECL_LINK( GetFxHdl, ArgInput&, void );
+
+ void ArgumentModified();
+
+ void InitArgInput(sal_uInt16 nPos, weld::Label& rFtArg, weld::Button& rBtnFx,
+ ArgEdit& rEdArg, RefButton& rRefBtn);
+
+ void SetArgumentDesc(const OUString& aText);
+ void SetArgumentText(const OUString& aText);
+
+
+ void SetArgName (sal_uInt16 no,const OUString &aArg);
+ void SetArgNameFont (sal_uInt16 no,const vcl::Font&);
+
+ void UpdateArgDesc( sal_uInt16 nArg );
+ void UpdateArgInput( sal_uInt16 nOffset, sal_uInt16 i );
+
+public:
+ ParaWin(weld::Container* pParent, IControlReferenceHandler* _pDlg);
+ ~ParaWin();
+
+ void SetFunctionDesc(const IFunctionDescription* pFDesc);
+ void SetArgumentOffset(sal_uInt16 nOffset);
+ void SetEditDesc(const OUString& aText);
+ void UpdateParas();
+ void ClearAll();
+
+ sal_uInt16 GetActiveLine() const { return nActiveLine;}
+ void SetActiveLine(sal_uInt16 no);
+ RefEdit* GetActiveEdit();
+ OUString GetActiveArgName() const;
+
+ OUString GetArgument(sal_uInt16 no);
+ void SetArgument(sal_uInt16 no, std::u16string_view aString);
+ void SetArgumentFonts(const vcl::Font& aBoldFont,const vcl::Font& aLightFont);
+
+ void SetEdFocus(); // visible edit lines
+ sal_uInt16 GetSliderPos() const;
+ void SetSliderPos(sal_uInt16 nSliderPos);
+
+ void SetArgModifiedHdl( const Link<ParaWin&,void>& rLink ) { aArgModifiedLink = rLink; }
+ void SetFxHdl( const Link<ParaWin&,void>& rLink ) { aFxLink = rLink; }
+
+ void SliderMoved();
+
+ void Show() { m_xContainer->show(); }
+};
+
+
+} // formula
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/ui/dlg/structpg.cxx b/formula/source/ui/dlg/structpg.cxx
new file mode 100644
index 0000000000..6cfc34a96c
--- /dev/null
+++ b/formula/source/ui/dlg/structpg.cxx
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/svapp.hxx>
+
+#include "structpg.hxx"
+#include <formula/formula.hxx>
+#include <formula/token.hxx>
+#include <bitmaps.hlst>
+
+namespace formula
+{
+
+void StructPage::SetActiveFlag(bool bFlag)
+{
+ bActiveFlag = bFlag;
+}
+
+StructPage::StructPage(weld::Container* pParent)
+ : m_xBuilder(Application::CreateBuilder(pParent, "formula/ui/structpage.ui"))
+ , m_xContainer(m_xBuilder->weld_container("StructPage"))
+ , m_xTlbStruct(m_xBuilder->weld_tree_view("struct"))
+ , maImgEnd(BMP_STR_END)
+ , maImgError(BMP_STR_ERROR)
+ , pSelectedToken(nullptr)
+ , bActiveFlag(false)
+{
+ m_xTlbStruct->set_size_request(m_xTlbStruct->get_approximate_digit_width() * 20,
+ m_xTlbStruct->get_height_rows(17));
+
+ m_xTlbStruct->connect_changed(LINK( this, StructPage, SelectHdl ) );
+}
+
+StructPage::~StructPage()
+{
+}
+
+void StructPage::ClearStruct()
+{
+ SetActiveFlag(false);
+ m_xTlbStruct->clear();
+}
+
+bool StructPage::InsertEntry(const OUString& rText, const weld::TreeIter* pParent,
+ sal_uInt16 nFlag, int nPos,
+ const FormulaToken* pIFormulaToken,
+ weld::TreeIter& rRet)
+{
+ SetActiveFlag(false);
+
+ OUString sId(weld::toId(pIFormulaToken));
+
+ bool bEntry = false;
+ switch (nFlag)
+ {
+ case STRUCT_FOLDER:
+ m_xTlbStruct->insert(pParent, nPos, &rText, &sId, nullptr, nullptr,
+ false, &rRet);
+ m_xTlbStruct->set_image(rRet, BMP_STR_OPEN);
+ bEntry = true;
+ break;
+ case STRUCT_END:
+ m_xTlbStruct->insert(pParent, nPos, &rText, &sId, nullptr, nullptr,
+ false, &rRet);
+ m_xTlbStruct->set_image(rRet, maImgEnd);
+ bEntry = true;
+ break;
+ case STRUCT_ERROR:
+ m_xTlbStruct->insert(pParent, nPos, &rText, &sId, nullptr, nullptr,
+ false, &rRet);
+ m_xTlbStruct->set_image(rRet, maImgError);
+ bEntry = true;
+ break;
+ }
+
+ if (bEntry && pParent)
+ m_xTlbStruct->expand_row(*pParent);
+ return bEntry;
+}
+
+OUString StructPage::GetEntryText(const weld::TreeIter* pEntry) const
+{
+ OUString aString;
+ if (pEntry)
+ aString = m_xTlbStruct->get_text(*pEntry);
+ return aString;
+}
+
+const FormulaToken* StructPage::GetFunctionEntry(const weld::TreeIter* pEntry)
+{
+ if (!pEntry)
+ return nullptr;
+
+ const FormulaToken * pToken = weld::fromId<const FormulaToken*>(m_xTlbStruct->get_id(*pEntry));
+ if (pToken)
+ {
+ if ( !(pToken->IsFunction() || pToken->GetParamCount() > 1 ) )
+ {
+ std::unique_ptr<weld::TreeIter> xParent(m_xTlbStruct->make_iterator(pEntry));
+ if (!m_xTlbStruct->iter_parent(*xParent))
+ return nullptr;
+ return GetFunctionEntry(xParent.get());
+ }
+ else
+ {
+ return pToken;
+ }
+ }
+ return nullptr;
+}
+
+IMPL_LINK(StructPage, SelectHdl, weld::TreeView&, rTlb, void)
+{
+ if (!GetActiveFlag())
+ return;
+
+ if (&rTlb == m_xTlbStruct.get())
+ {
+ std::unique_ptr<weld::TreeIter> xCurEntry(m_xTlbStruct->make_iterator());
+ if (m_xTlbStruct->get_cursor(xCurEntry.get()))
+ {
+ pSelectedToken = weld::fromId<const FormulaToken*>(m_xTlbStruct->get_id(*xCurEntry));
+ if (pSelectedToken)
+ {
+ if ( !(pSelectedToken->IsFunction() || pSelectedToken->GetParamCount() > 1) )
+ {
+ pSelectedToken = GetFunctionEntry(xCurEntry.get());
+ }
+ }
+ }
+ }
+
+ aSelLink.Call(*this);
+}
+
+} // formula
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/formula/source/ui/dlg/structpg.hxx b/formula/source/ui/dlg/structpg.hxx
new file mode 100644
index 0000000000..1164031899
--- /dev/null
+++ b/formula/source/ui/dlg/structpg.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+namespace formula
+{
+
+class FormulaToken;
+
+
+class StructPage final
+{
+private:
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<weld::TreeView> m_xTlbStruct;
+
+ Link<StructPage&,void> aSelLink;
+
+ OUString maImgEnd;
+ OUString maImgError;
+
+ const FormulaToken* pSelectedToken;
+ bool bActiveFlag;
+
+ DECL_LINK(SelectHdl, weld::TreeView&, void);
+
+ const FormulaToken* GetFunctionEntry(const weld::TreeIter* pEntry);
+
+ void SetActiveFlag(bool bFlag);
+ bool GetActiveFlag() const { return bActiveFlag;}
+
+public:
+
+ explicit StructPage(weld::Container* pParent);
+ ~StructPage();
+
+ void ClearStruct();
+ bool InsertEntry(const OUString& rText, const weld::TreeIter* pParent,
+ sal_uInt16 nFlag, int nPos,
+ const FormulaToken* pIFormulaToken,
+ weld::TreeIter& rRet);
+
+ OUString GetEntryText(const weld::TreeIter* pEntry) const;
+
+ void SetSelectionHdl( const Link<StructPage&,void>& rLink ) { aSelLink = rLink; }
+
+ weld::TreeView& GetTlbStruct() const { return *m_xTlbStruct; }
+
+ bool IsVisible() const { return m_xContainer->get_visible(); }
+};
+
+} // formula
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */