summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWinfried Donkers <winfrieddonkers@libreoffice.org>2016-03-29 19:03:43 +0200
committerEike Rathke <erack@redhat.com>2016-04-26 15:08:22 +0000
commita76c5e21378e5364f1f7554a32d89072feff8b0c (patch)
treeca65eb9aae585323ab033bbab08e4265d5359fa3
parent0bdb8baf57ac243f574db6273eed70127379e3fc (diff)
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 <erack@redhat.com> Tested-by: Eike Rathke <erack@redhat.com>
-rw-r--r--formula/source/core/resource/core_resource.src14
-rw-r--r--include/formula/compiler.hrc4
-rw-r--r--include/formula/opcode.hxx2
-rw-r--r--sc/inc/helpids.h2
-rw-r--r--sc/qa/unit/ucalc.cxx2
-rw-r--r--sc/source/core/inc/interpre.hxx2
-rw-r--r--sc/source/core/tool/interpr4.cxx2
-rw-r--r--sc/source/core/tool/interpr8.cxx464
-rw-r--r--sc/source/filter/excel/xlformula.cxx5
-rw-r--r--sc/source/filter/oox/formulabase.cxx5
-rw-r--r--sc/source/ui/src/scfuncs.src64
11 files changed, 563 insertions, 3 deletions
diff --git a/formula/source/core/resource/core_resource.src b/formula/source/core/resource/core_resource.src
index bf496242aa30..b966192a57a0 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 7eadfe5423b2..034b165e2c68 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 d2ebdfc288e7..1bf955fef939 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 e07f7109ed38..29b22bb7f6a8 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 87e5e11c107b..6265f08028b0 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 4f06f86d7f49..588bc801d62a 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 bc2ea8dea839..96063a980d6a 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 aa3f7e576713..722b1649ad03 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 3a7952d0e5be..ad2ba9707ba3 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 72470fc4c3c7..6195808982ea 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 f407cdf99a80..f611834792a0 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
{