formula/source/core/resource/core_resource.src | 14 include/formula/compiler.hrc | 4 include/formula/opcode.hxx | 2 sc/inc/helpids.h | 2 sc/qa/unit/ucalc.cxx | 2 sc/source/core/inc/interpre.hxx | 2 sc/source/core/tool/interpr4.cxx | 2 sc/source/core/tool/interpr8.cxx | 464 +++++++++++++++++++++++++ sc/source/filter/excel/xlformula.cxx | 5 sc/source/filter/oox/formulabase.cxx | 5 sc/source/ui/src/scfuncs.src | 64 +++ 11 files changed, 563 insertions(+), 3 deletions(-)
New commits: commit a76c5e21378e5364f1f7554a32d89072feff8b0c Author: Winfried Donkers <winfrieddonk...@libreoffice.org> Date: Tue Mar 29 19:03:43 2016 +0200 tdf#97831 [part] Add Excel 2016-Office 365 functions to Calc Functions CONCAT and TEXTJOIN. Change-Id: I38092f77df719d11f6746ac10fe14dc53b7e93e7 Reviewed-on: https://gerrit.libreoffice.org/23601 Reviewed-by: Eike Rathke <er...@redhat.com> Tested-by: Eike Rathke <er...@redhat.com> diff --git a/formula/source/core/resource/core_resource.src b/formula/source/core/resource/core_resource.src index bf49624..b966192 100644 --- a/formula/source/core/resource/core_resource.src +++ b/formula/source/core/resource/core_resource.src @@ -287,6 +287,8 @@ Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF String SC_OPCODE_SUBSTITUTE { Text = "SUBSTITUTE" ; }; String SC_OPCODE_REPT { Text = "REPT" ; }; String SC_OPCODE_CONCAT { Text = "CONCATENATE" ; }; + String SC_OPCODE_CONCAT_MS { Text = "COM.MICROSOFT.CONCAT" ; }; + String SC_OPCODE_TEXTJOIN_MS { Text = "COM.MICROSOFT.TEXTJOIN" ; }; String SC_OPCODE_MAT_VALUE { Text = "MVALUE" ; }; String SC_OPCODE_MAT_DET { Text = "MDETERM" ; }; String SC_OPCODE_MAT_INV { Text = "MINVERSE" ; }; @@ -721,6 +723,8 @@ Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML String SC_OPCODE_SUBSTITUTE { Text = "SUBSTITUTE" ; }; String SC_OPCODE_REPT { Text = "REPT" ; }; String SC_OPCODE_CONCAT { Text = "CONCATENATE" ; }; + String SC_OPCODE_CONCAT_MS { Text = "_xlfn.CONCAT" ; }; + String SC_OPCODE_TEXTJOIN_MS { Text = "_xlfn.TEXTJOIN" ; }; String SC_OPCODE_MAT_VALUE { Text = "MVALUE" ; }; String SC_OPCODE_MAT_DET { Text = "MDETERM" ; }; String SC_OPCODE_MAT_INV { Text = "MINVERSE" ; }; @@ -1155,6 +1159,8 @@ Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH String SC_OPCODE_SUBSTITUTE { Text = "SUBSTITUTE" ; }; String SC_OPCODE_REPT { Text = "REPT" ; }; String SC_OPCODE_CONCAT { Text = "CONCATENATE" ; }; + String SC_OPCODE_CONCAT_MS { Text = "CONCAT" ; }; + String SC_OPCODE_TEXTJOIN_MS { Text = "TEXTJOIN" ; }; String SC_OPCODE_MAT_VALUE { Text = "MVALUE" ; }; String SC_OPCODE_MAT_DET { Text = "MDETERM" ; }; String SC_OPCODE_MAT_INV { Text = "MINVERSE" ; }; @@ -2321,6 +2327,14 @@ Resource RID_STRLIST_FUNCTION_NAMES { Text [ en-US ] = "CONCATENATE" ; }; + String SC_OPCODE_CONCAT_MS + { + Text [ en-US ] = "CONCAT" ; + }; + String SC_OPCODE_TEXTJOIN_MS + { + Text [ en-US ] = "TEXTJOIN" ; + }; String SC_OPCODE_MAT_VALUE { Text [ en-US ] = "MVALUE" ; diff --git a/include/formula/compiler.hrc b/include/formula/compiler.hrc index 7eadfe5..034b165 100644 --- a/include/formula/compiler.hrc +++ b/include/formula/compiler.hrc @@ -495,7 +495,9 @@ #define SC_OPCODE_FORECAST_ETS_STA 484 #define SC_OPCODE_FORECAST_ETS_STM 485 #define SC_OPCODE_FORECAST_LIN 486 -#define SC_OPCODE_STOP_2_PAR 487 /* last function with two or more parameters' OpCode + 1 */ +#define SC_OPCODE_CONCAT_MS 487 +#define SC_OPCODE_TEXTJOIN_MS 488 +#define SC_OPCODE_STOP_2_PAR 489 /* last function with two or more parameters' OpCode + 1 */ #define SC_OPCODE_STOP_FUNCTION SC_OPCODE_STOP_2_PAR /* last function's OpCode + 1 */ #define SC_OPCODE_LAST_OPCODE_ID (SC_OPCODE_STOP_FUNCTION - 1) /* last OpCode */ diff --git a/include/formula/opcode.hxx b/include/formula/opcode.hxx index d2ebdfc..1bf955f 100644 --- a/include/formula/opcode.hxx +++ b/include/formula/opcode.hxx @@ -329,6 +329,8 @@ enum OpCode : sal_uInt16 ocSubstitute = SC_OPCODE_SUBSTITUTE, ocRept = SC_OPCODE_REPT, ocConcat = SC_OPCODE_CONCAT, + ocConcat_MS = SC_OPCODE_CONCAT_MS, + ocTextJoin_MS = SC_OPCODE_TEXTJOIN_MS, ocLenB = SC_OPCODE_LENB, ocRightB = SC_OPCODE_RIGHTB, ocLeftB = SC_OPCODE_LEFTB, diff --git a/sc/inc/helpids.h b/sc/inc/helpids.h index e07f710..29b22bb 100644 --- a/sc/inc/helpids.h +++ b/sc/inc/helpids.h @@ -636,5 +636,7 @@ #define HID_FUNC_FORECAST_ETS_STA "SC_HID_FUNC_FORECAST_ETS_STA" #define HID_FUNC_FORECAST_ETS_STM "SC_HID_FUNC_FORECAST_ETS_STM" #define HID_FUNC_FORECAST_LIN "SC_HID_FUNC_FORECAST_LIN" +#define HID_FUNC_CONCAT_MS "SC_HID_FUNC_CONCAT_MS" +#define HID_FUNC_TEXTJOIN_MS "SC_HID_FUNC_TEXTJOIN_MS" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx index 87e5e11..6265f08 100644 --- a/sc/qa/unit/ucalc.cxx +++ b/sc/qa/unit/ucalc.cxx @@ -2791,6 +2791,7 @@ void Test::testFunctionLists() "CHAR", "CLEAN", "CODE", + "CONCAT", "CONCATENATE", "DECIMAL", "DOLLAR", @@ -2819,6 +2820,7 @@ void Test::testFunctionLists() "SUBSTITUTE", "T", "TEXT", + "TEXTJOIN", "TRIM", "UNICHAR", "UNICODE", diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx index 4f06f86..588bc80 100644 --- a/sc/source/core/inc/interpre.hxx +++ b/sc/source/core/inc/interpre.hxx @@ -602,6 +602,8 @@ void ScText(); void ScSubstitute(); void ScRept(); void ScConcat(); +void ScConcat_MS(); +void ScTextJoin_MS(); void ScExternal(); void ScMissing(); void ScMacro(); diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx index bc2ea8d..96063a9 100644 --- a/sc/source/core/tool/interpr4.cxx +++ b/sc/source/core/tool/interpr4.cxx @@ -3924,6 +3924,8 @@ StackVar ScInterpreter::Interpret() case ocSubstitute : ScSubstitute(); break; case ocRept : ScRept(); break; case ocConcat : ScConcat(); break; + case ocConcat_MS : ScConcat_MS(); break; + case ocTextJoin_MS : ScTextJoin_MS(); break; case ocMatValue : ScMatValue(); break; case ocMatrixUnit : ScEMat(); break; case ocMatDet : ScMatDet(); break; diff --git a/sc/source/core/tool/interpr8.cxx b/sc/source/core/tool/interpr8.cxx index aa3f7e5..722b164 100644 --- a/sc/source/core/tool/interpr8.cxx +++ b/sc/source/core/tool/interpr8.cxx @@ -10,10 +10,12 @@ #include <interpre.hxx> #include <global.hxx> +#include <dociter.hxx> #include <scmatrix.hxx> #include <comphelper/random.hxx> #include <formula/token.hxx> +#include <stack> #include <cmath> #include <vector> @@ -1388,4 +1390,466 @@ void ScInterpreter::ScForecast_Ets( ScETSType eETSType ) return; } +void ScInterpreter::ScConcat_MS() +{ + OUStringBuffer aResBuf; + short nParamCount = GetByte(); + + //reverse order of parameter stack to simplify concatenation: + FormulaToken* p; + for ( short i = 0; i < short( nParamCount / 2 ); i++ ) + { + p = pStack[ sp - ( nParamCount - i ) ]; + pStack[ sp - ( nParamCount - i ) ] = pStack[ sp - 1 - i ]; + pStack[ sp - 1 - i ] = p; + } + + size_t nRefInList = 0; + while ( nParamCount-- > 0 ) + { + switch ( GetStackType() ) + { + case svString: + case svDouble: + aResBuf.append( PopString().getString() ); + break; + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( nGlobalError ) + break; + ScRefCellValue aCell( *pDok, aAdr ); + if ( !aCell.isEmpty() ) + { + if ( aCell.hasString() ) + aResBuf.append( aCell.getString( pDok ) ); + else + { + if ( !aCell.hasEmptyValue() ) + aResBuf.append( OUString::number( aCell.getValue() ) ); + } + } + break; + } + case svDoubleRef : + case svRefList : + { + ScRange aRange; + PopDoubleRef( aRange, nParamCount, nRefInList); + if ( nGlobalError ) + break; + // we need to read row for row, so we can't use ScCellIter + SCCOL nCol1, nCol2; + SCROW nRow1, nRow2; + SCTAB nTab1, nTab2; + aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if ( nTab1 != nTab2 ) + { + SetError( errIllegalParameter); + break; + } + if ( nRow1 > nRow2 ) + std::swap( nRow1, nRow2 ); + if ( nCol1 > nCol2 ) + std::swap( nCol1, nCol2 ); + ScAddress aAdr; + aAdr.SetTab( nTab1 ); + for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ ) + { + for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ ) + { + aAdr.SetRow( nRow ); + aAdr.SetCol( nCol ); + ScRefCellValue aCell( *pDok, aAdr ); + if ( !aCell.isEmpty() ) + { + if ( aCell.hasString() ) + aResBuf.append( aCell.getString( pDok ) ); + else + { + if ( !aCell.hasEmptyValue() ) + aResBuf.append( OUString::number( aCell.getValue() ) ); + } + } + } + } + break; + } + case svMatrix : + case svExternalSingleRef: + case svExternalDoubleRef: + { + ScMatrixRef pMat = GetMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + if (nC == 0 || nR == 0) + SetError(errIllegalArgument); + else + { + for ( SCSIZE j = 0; j < nC; j++ ) + { + for (SCSIZE k = 0; k < nR; k++ ) + { + if ( pMat->IsString( j, k ) ) + aResBuf.append( pMat->GetString( j, k ).getString() ); + else + { + if ( pMat->IsValue( j, k ) ) + aResBuf.append( OUString::number( pMat->GetDouble( j, k ) ) ); + } + } + } + } + } + } + default: + break; + } + } + PushString( aResBuf.makeStringAndClear() ); +} + +void ScInterpreter::ScTextJoin_MS() +{ + short nParamCount = GetByte(); + + if ( MustHaveParamCountMin( nParamCount, 3 ) ) + { + //reverse order of parameter stack to simplify processing + FormulaToken* p; + for ( short i = 0; i < short( nParamCount / 2 ); i++ ) + { + p = pStack[ sp - ( nParamCount - i ) ]; + pStack[ sp - ( nParamCount - i ) ] = pStack[ sp - 1 - i ]; + pStack[ sp - 1 - i ] = p; + } + + // get xDelimiter and bSkipEmpty + std::vector< OUString > xDelimiter; + size_t nRefInList = 0; + switch ( GetStackType() ) + { + case svString: + case svDouble: + xDelimiter.push_back( PopString().getString() ); + break; + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( nGlobalError ) + break; + ScRefCellValue aCell( *pDok, aAdr ); + if ( !aCell.isEmpty() ) + { + if ( aCell.hasString() ) + xDelimiter.push_back( aCell.getString( pDok ) ); + else + { + if ( !aCell.hasEmptyValue() ) + xDelimiter.push_back( OUString::number( aCell.getValue() ) ); + } + } + break; + } + case svDoubleRef : + case svRefList : + { + ScRange aRange; + PopDoubleRef( aRange, nParamCount, nRefInList); + if ( nGlobalError ) + break; + // we need to read row for row, so we can't use ScCellIterator + SCCOL nCol1, nCol2; + SCROW nRow1, nRow2; + SCTAB nTab1, nTab2; + aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if ( nTab1 != nTab2 ) + { + SetError( errIllegalParameter); + break; + } + if ( nRow1 > nRow2 ) + std::swap( nRow1, nRow2 ); + if ( nCol1 > nCol2 ) + std::swap( nCol1, nCol2 ); + ScAddress aAdr; + aAdr.SetTab( nTab1 ); + for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ ) + { + for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ ) + { + aAdr.SetRow( nRow ); + aAdr.SetCol( nCol ); + ScRefCellValue aCell( *pDok, aAdr ); + if ( !aCell.isEmpty() ) + { + if ( aCell.hasString() ) + xDelimiter.push_back( aCell.getString( pDok ) ); + else + { + if ( !aCell.hasEmptyValue() ) + xDelimiter.push_back( OUString::number( aCell.getValue() ) ); + } + } + else + xDelimiter.push_back( "" ); + } + } + break; + } + case svMatrix : + case svExternalSingleRef: + case svExternalDoubleRef: + { + ScMatrixRef pMat = GetMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + if (nC == 0 || nR == 0) + SetError(errIllegalArgument); + else + { + for ( SCSIZE j = 0; j < nC; j++ ) + { + for (SCSIZE k = 0; k < nR; k++ ) + { + if ( !pMat->IsEmpty( j, k ) ) + { + if ( pMat->IsString( j, k ) ) + xDelimiter.push_back( pMat->GetString( j, k ).getString() ); + else + { + if ( pMat->IsValue( j, k ) ) + xDelimiter.push_back( OUString::number( pMat->GetDouble( j, k ) ) ); + } + } + else + xDelimiter.push_back( "" ); + } + } + } + } + } + default: + break; + } + if ( xDelimiter.empty() ) + { + PushIllegalArgument(); + return; + } + SCSIZE nSize = xDelimiter.size(); + bool bSkipEmpty = static_cast< bool >( GetDouble() ); + nParamCount -= 2; + + OUStringBuffer aResBuf; + bool bFirst = true; + SCSIZE nIdx = 0; + nRefInList = 0; + // get the strings to be joined + while ( nParamCount-- > 0 && !nGlobalError ) + { + switch ( GetStackType() ) + { + case svString: + case svDouble: + { + OUString aStr = PopString().getString(); + if ( !aStr.isEmpty() || !bSkipEmpty ) + { + if ( !bFirst ) + { + aResBuf.append( xDelimiter[ nIdx ] ); + if ( nSize > 1 ) + { + if ( ++nIdx >= nSize ) + nIdx = 0; + } + } + else + bFirst = false; + aResBuf.append( aStr ); + } + break; + } + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( nGlobalError ) + break; + ScRefCellValue aCell( *pDok, aAdr ); + OUString aStr; + if ( !aCell.isEmpty() ) + { + if ( aCell.hasString() ) + aStr = aCell.getString( pDok ); + else + { + if ( !aCell.hasEmptyValue() ) + aStr = OUString::number( aCell.getValue() ); + } + } + else + aStr = ""; + if ( !aStr.isEmpty() || !bSkipEmpty ) + { + if ( !bFirst ) + { + aResBuf.append( xDelimiter[ nIdx ] ); + if ( nSize > 1 ) + { + if ( ++nIdx >= nSize ) + nIdx = 0; + } + } + else + bFirst = false; + aResBuf.append( aStr ); + } + break; + } + case svDoubleRef : + case svRefList : + { + ScRange aRange; + PopDoubleRef( aRange, nParamCount, nRefInList); + if ( nGlobalError ) + break; + // we need to read row for row, so we can't use ScCellIterator + SCCOL nCol1, nCol2; + SCROW nRow1, nRow2; + SCTAB nTab1, nTab2; + aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if ( nTab1 != nTab2 ) + { + SetError( errIllegalParameter); + break; + } + if ( nRow1 > nRow2 ) + std::swap( nRow1, nRow2 ); + if ( nCol1 > nCol2 ) + std::swap( nCol1, nCol2 ); + ScAddress aAdr; + aAdr.SetTab( nTab1 ); + OUString aStr; + for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ ) + { + for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ ) + { + aAdr.SetRow( nRow ); + aAdr.SetCol( nCol ); + ScRefCellValue aCell( *pDok, aAdr ); + if ( !aCell.isEmpty() ) + { + if ( aCell.hasString() ) + aStr = aCell.getString( pDok ); + else + { + if ( !aCell.hasEmptyValue() ) + aStr = OUString::number( aCell.getValue() ); + } + } + else + aStr = ""; + if ( !aStr.isEmpty() || !bSkipEmpty ) + { + if ( !bFirst ) + { + aResBuf.append( xDelimiter[ nIdx ] ); + if ( nSize > 1 ) + { + if ( ++nIdx >= nSize ) + nIdx = 0; + } + } + else + bFirst = false; + aResBuf.append( aStr ); + } + } + } + break; + } + case svMatrix : + case svExternalSingleRef: + case svExternalDoubleRef: + { + ScMatrixRef pMat = GetMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + if (nC == 0 || nR == 0) + SetError(errIllegalArgument); + else + { + OUString aStr; + for ( SCSIZE j = 0; j < nC; j++ ) + { + for (SCSIZE k = 0; k < nR; k++ ) + { + if ( !pMat->IsEmpty( j, k ) ) + { + if ( pMat->IsString( j, k ) ) + aStr = pMat->GetString( j, k ).getString(); + else + { + if ( pMat->IsValue( j, k ) ) + aStr = OUString::number( pMat->GetDouble( j, k ) ); + } + } + else + aStr = ""; + if ( !aStr.isEmpty() || !bSkipEmpty ) + { + if ( !bFirst ) + { + aResBuf.append( xDelimiter[ nIdx ] ); + if ( nSize > 1 ) + { + if ( ++nIdx >= nSize ) + nIdx = 0; + } + } + else + bFirst = false; + aResBuf.append( aStr ); + } + } + } + } + } + } + case svMissing : + { + if ( !bSkipEmpty ) + { + if ( !bFirst ) + { + aResBuf.append( xDelimiter[ nIdx ] ); + if ( nSize > 1 ) + { + if ( ++nIdx >= nSize ) + nIdx = 0; + } + } + else + bFirst = false; + } + break; + } + default: + break; + } + } + PushString( aResBuf.makeStringAndClear() ); + } +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/filter/excel/xlformula.cxx b/sc/source/filter/excel/xlformula.cxx index 3a7952d..ad2ba97 100644 --- a/sc/source/filter/excel/xlformula.cxx +++ b/sc/source/filter/excel/xlformula.cxx @@ -569,6 +569,7 @@ static const XclFunctionInfo saFuncTable_2013[] = /** Functions new in Excel 2016. See https://support.office.com/en-us/article/Forecasting-functions-897a2fe9-6595-4680-a0b0-93e0308d5f6e?ui=en-US&rs=en-US&ad=US#_forecast.ets + and https://support.office.com/en-us/article/What-s-New-and-Improved-in-Office-2016-for-Office-365-95c8d81d-08ba-42c1-914f-bca4603e1426?ui=en-US&rs=en-US&ad=US @See sc/source/filter/oox/formulabase.cxx saFuncTable2016 for V,VR,RO,... */ @@ -578,7 +579,9 @@ static const XclFunctionInfo saFuncTable_2016[] = EXC_FUNCENTRY_V_VR( ocForecast_ETS_PIA, 3, 7, 0, "FORECAST.ETS.CONFINT" ), EXC_FUNCENTRY_V_VR( ocForecast_ETS_SEA, 2, 4, 0, "FORECAST.ETS.SEASONALITY" ), EXC_FUNCENTRY_V_VR( ocForecast_ETS_STA, 3, 6, 0, "FORECAST.ETS.STAT" ), - EXC_FUNCENTRY_V_VR( ocForecast_LIN, 3, 3, 0, "FORECAST.LINEAR" ) + EXC_FUNCENTRY_V_VR( ocForecast_LIN, 3, 3, 0, "FORECAST.LINEAR" ), + EXC_FUNCENTRY_V_VR( ocConcat_MS, 1, MX, 0, "CONCAT" ), + EXC_FUNCENTRY_V_VR( ocTextJoin_MS, 3, MX, 0, "TEXTJOIN" ) }; #define EXC_FUNCENTRY_ODF( opcode, minparam, maxparam, flags, asciiname ) \ diff --git a/sc/source/filter/oox/formulabase.cxx b/sc/source/filter/oox/formulabase.cxx index 72470fc..6195808 100644 --- a/sc/source/filter/oox/formulabase.cxx +++ b/sc/source/filter/oox/formulabase.cxx @@ -898,6 +898,7 @@ static const FunctionData saFuncTable2013[] = /** Functions new in Excel 2016. See https://support.office.com/en-us/article/Forecasting-functions-897a2fe9-6595-4680-a0b0-93e0308d5f6e?ui=en-US&rs=en-US&ad=US#_forecast.ets + and https://support.office.com/en-us/article/What-s-New-and-Improved-in-Office-2016-for-Office-365-95c8d81d-08ba-42c1-914f-bca4603e1426?ui=en-US&rs=en-US&ad=US @See sc/source/filter/excel/xlformula.cxx saFuncTable_2016 */ @@ -908,7 +909,9 @@ static const FunctionData saFuncTable2016[] = { "COM.MICROSOFT.FORECAST.ETS.CONFINT", "FORECAST.ETS.CONFINT", NOID, NOID, 4, 7, V, { VR, VA, VR }, FUNCFLAG_MACROCALL_NEW }, { "COM.MICROSOFT.FORECAST.ETS.SEASONALITY", "FORECAST.ETS.SEASONALITY", NOID, NOID, 2, 4, V, { VR, VA, VR }, FUNCFLAG_MACROCALL_NEW }, { "COM.MICROSOFT.FORECAST.ETS.STAT", "FORECAST.ETS.STAT", NOID, NOID, 3, 6, V, { VR, VA, VR }, FUNCFLAG_MACROCALL_NEW }, - { "COM.MICROSOFT.FORECAST.LINEAR", "FORECAST.LINEAR", NOID, NOID, 3, 3, V, { VR, VA }, FUNCFLAG_MACROCALL_NEW } + { "COM.MICROSOFT.FORECAST.LINEAR", "FORECAST.LINEAR", NOID, NOID, 3, 3, V, { VR, VA }, FUNCFLAG_MACROCALL_NEW }, + { "COM.MICROSOFT.CONCAT", "CONCAT", NOID, NOID, 1, MX, V, { VR }, FUNCFLAG_MACROCALL_NEW }, + { "COM.MICROSOFT.TEXTJOIN", "TEXTJOIN", NOID, NOID, 3, MX, V, { VR }, FUNCFLAG_MACROCALL_NEW } }; diff --git a/sc/source/ui/src/scfuncs.src b/sc/source/ui/src/scfuncs.src index f407cdf..f611834 100644 --- a/sc/source/ui/src/scfuncs.src +++ b/sc/source/ui/src/scfuncs.src @@ -11527,6 +11527,70 @@ Resource RID_SC_FUNCTION_DESCRIPTIONS2 Text [ en-US ] = "Text for the concatenation." ; }; }; + // -=*# Resource for function CONCAT #*=- + Resource SC_OPCODE_CONCAT_MS + { + String 1 // Description + { + Text [ en-US ] = "Combines several text items into one, accepts cell ranges as arguments." ; + }; + ExtraData = + { + 0; + ID_FUNCTION_GRP_TEXT; + HID_FUNC_CONCAT_MS; + VAR_ARGS; 0; + 0; + }; + String 2 // Name of Parameter 1 to last + { + Text [ en-US ] = "text " ; + }; + String 3 // Description of Parameter 1 to last + { + Text [ en-US ] = "Text and/or cell ranges for the concatenation." ; + }; + }; + // -=*# Resource for function TEXTJOIN #*=- + Resource SC_OPCODE_TEXTJOIN_MS + { + String 1 // Description + { + Text [ en-US ] = "Combines several text items into one, accepts cell ranges as arguments. Uses delimiter between items." ; + }; + ExtraData = + { + 0; + ID_FUNCTION_GRP_TEXT; + HID_FUNC_TEXTJOIN_MS; + VAR_ARGS + 2; 0; 0; 0; + 0; + }; + String 2 // Name of Parameter 1 + { + Text [ en-US ] = "delimiter " ; + }; + String 3 // Description of Parameter 1 + { + Text [ en-US ] = "Text string to be used as delimiter." ; + }; + String 4 // Name of Parameter 2 + { + Text [ en-US ] = "skip empty cells" ; + }; + String 5 // Description of Parameter 2 + { + Text [ en-US ] = "If TRUE, empty cells will be ignored." ; + }; + String 6 // Name of Parameter 3 to last + { + Text [ en-US ] = "text " ; + }; + String 7 // Description of Parameter 3 to last + { + Text [ en-US ] = "Text and/or cell ranges for the concatenation." ; + }; + }; // -=*# Resource for function EXACT #*=- Resource SC_OPCODE_EXACT { _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits