[Libreoffice-commits] core.git: formula/source include/formula sc/inc sc/qa sc/source

Winfried Donkers winfrieddonkers at libreoffice.org
Tue Apr 26 15:08:47 UTC 2016


 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 <winfrieddonkers at 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 <erack at redhat.com>
    Tested-by: Eike Rathke <erack at 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
     {


More information about the Libreoffice-commits mailing list