sc: round-trip dynamic-array masters through XLSX, strip @ on XLS
XLSX marks a dynamic-array master with cm="1" on the cell and a matching dynamicArrayProperties metadata block. Both import and export now travel through mbDynamicArrayMaster on the cell. XLSX import plumbs the flag through the three formula paths. XML_normal and XML_shared land in applyCellFormulas and applySharedFormulas through setCellFormula. XML_array lands in applyArrayFormulas through setCellArrayFormula. Each path sets mbDynamicArrayMaster on the imported cell or on the matrix master. XLSX export reads the flag in XclExpFormulaCell::SaveXml to drive cm="1" and the dynamicArrayProperties block. The legacy pair of a Ctrl+Shift+Enter (CSE) master that calls a dynamic-array function stays as a fallback while the importers and the UI entry path are being wired through. XLS has no @ encoding and no dynamic-array marker. Save and load mirror the ODF behaviour: - The exporter strips ocSingleValue (alongside ocSpaces and ocWhitespace) in GetNextToken so the BIFF stream stays valid. The saved formula evaluates to the upper-left value the way XLS always did. - The importer walks the just-built RPN of every plain non-array formula and prepends @ when the formula intends an array. The resolve runs before the formula cell is constructed, while the parse array is still mutable. The static helper compiles a missing RPN on demand so the array-intent walk has the post-fix order it needs. - XLS array records keep the legacy CSE behaviour. They have no dynamic-array encoding, so loading them as static arrays matches what the file format can express. Tests cover an XLSX import of Spill.xlsx, an XLSX round trip of the same file, a direct-XML XLSX export check that the spilled master at C2 keeps cm="1" and the dynamicArrayProperties metadata block, and an XLS round trip of =@TRANSPOSE(B1:B4) where the @ reappears after reload. Signed-off-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> Change-Id: I30318bad4cdb50bbd92240fd71d70df0959a0eda Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/4664 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com> (cherry picked from commit 79eda1a177abf9a348dbc67c211b26cabd0cb4c1) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/207218 Tested-by: Xisco Fauli <xiscofauli@libreoffice.org> Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
committed by
Xisco Fauli
parent
bfb49b1038
commit
eb239f1a15
@@ -361,6 +361,13 @@ public:
|
||||
bool IsDynamicArrayMaster() const { return mbDynamicArrayMaster; }
|
||||
SC_DLLPUBLIC void SetDynamicArrayMaster( bool bDynamic );
|
||||
SC_DLLPUBLIC void ResolveImplicitIntersection();
|
||||
// Run this on the bare token array before it is wrapped in a cell.
|
||||
// The cell constructor finalizes the parse array. Once finalized,
|
||||
// appending a new token silently no-ops. The document and position
|
||||
// arguments compile a missing RPN so the array-intent walk has the
|
||||
// post-fix order it needs.
|
||||
SC_DLLPUBLIC static void ResolveImplicitIntersection(ScTokenArray& rCode, ScDocument& rDoc,
|
||||
const ScAddress& rPos);
|
||||
ScTokenArray* GetCode() { return pCode;}
|
||||
const ScTokenArray* GetCode() const { return pCode;}
|
||||
|
||||
|
||||
@@ -1872,6 +1872,36 @@ CPPUNIT_TEST_FIXTURE(ScExportTest2, testArrayFormulaSpillRoundtripXLSX)
|
||||
CPPUNIT_ASSERT_EQUAL(40.0, pDocument->GetValue(ScAddress(0, 3, 0)));
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(ScExportTest2, testDynamicArraySpilledExportXLSX)
|
||||
{
|
||||
// Spill.xlsx has C2 = UNIQUE(A2:A5) imported as a dynamic-array
|
||||
// master that already spilled into C2:C5. Saving back to XLSX must
|
||||
// write cm="1" on the master cell, t="array" with ref="C2:C5" on
|
||||
// the formula, and the matching dynamic-array metadata block.
|
||||
// Slave cells in the spilled range must not carry cm.
|
||||
|
||||
createScDoc("xlsx/Spill.xlsx");
|
||||
|
||||
save(TestFilter::XLSX);
|
||||
|
||||
xmlDocUniquePtr pSheet = parseExport(u"xl/worksheets/sheet1.xml"_ustr);
|
||||
CPPUNIT_ASSERT(pSheet);
|
||||
|
||||
assertXPath(pSheet, "/x:worksheet/x:sheetData/x:row[2]/x:c[@r='C2']", "cm", u"1");
|
||||
assertXPath(pSheet, "/x:worksheet/x:sheetData/x:row[2]/x:c[@r='C2']/x:f", "t", u"array");
|
||||
assertXPath(pSheet, "/x:worksheet/x:sheetData/x:row[2]/x:c[@r='C2']/x:f", "ref", u"C2:C5");
|
||||
assertXPathNoAttribute(pSheet, "/x:worksheet/x:sheetData/x:row[3]/x:c[@r='C3']", "cm");
|
||||
assertXPathNoAttribute(pSheet, "/x:worksheet/x:sheetData/x:row[4]/x:c[@r='C4']", "cm");
|
||||
assertXPathNoAttribute(pSheet, "/x:worksheet/x:sheetData/x:row[5]/x:c[@r='C5']", "cm");
|
||||
|
||||
xmlDocUniquePtr pMetadata = parseExport(u"xl/metadata.xml"_ustr);
|
||||
CPPUNIT_ASSERT_MESSAGE("xl/metadata.xml must be written", pMetadata);
|
||||
assertXPath(pMetadata,
|
||||
"/x:metadata/x:futureMetadata[@name='XLDAPR']/x:bk/x:extLst/x:ext"
|
||||
"/xda:dynamicArrayProperties",
|
||||
"fDynamic", u"1");
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(ScExportTest2, testArrayFormulaSpillRoundtripODS)
|
||||
{
|
||||
// Same scenario as testArrayFormulaSpillRoundtripXLSX but for ODS.
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <docsh.hxx>
|
||||
#include <scitems.hxx>
|
||||
#include <attrib.hxx>
|
||||
#include <formulacell.hxx>
|
||||
#include <stlpool.hxx>
|
||||
#include <validat.hxx>
|
||||
#include <scresid.hxx>
|
||||
@@ -19,6 +20,7 @@
|
||||
#include <subtotalparam.hxx>
|
||||
#include <globstr.hrc>
|
||||
#include <dpobject.hxx>
|
||||
#include <formula/errorcodes.hxx>
|
||||
|
||||
#include <comphelper/processfactory.hxx>
|
||||
#include <editeng/wghtitem.hxx>
|
||||
@@ -155,6 +157,33 @@ CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf120177)
|
||||
CPPUNIT_ASSERT_EQUAL(sGroupName1, sGroupName2);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(ScExportTest4, testSingleValueDroppedOnXlsExport)
|
||||
{
|
||||
// The @ implicit-intersection marker survives an XLS round trip
|
||||
// even though the BIFF stream has no token for it.
|
||||
|
||||
createScDoc();
|
||||
ScDocument* pDoc = getScDoc();
|
||||
|
||||
pDoc->SetValue(ScAddress(1, 0, 0), 10.0);
|
||||
pDoc->SetValue(ScAddress(1, 1, 0), 20.0);
|
||||
pDoc->SetValue(ScAddress(1, 2, 0), 30.0);
|
||||
pDoc->SetValue(ScAddress(1, 3, 0), 40.0);
|
||||
|
||||
pDoc->SetFormula(ScAddress(0, 0, 0), u"=@TRANSPOSE(B1:B4)"_ustr,
|
||||
formula::FormulaGrammar::GRAM_NATIVE);
|
||||
CPPUNIT_ASSERT_EQUAL(10.0, pDoc->GetValue(ScAddress(0, 0, 0)));
|
||||
|
||||
saveAndReload(TestFilter::XLS);
|
||||
|
||||
pDoc = getScDoc();
|
||||
ScFormulaCell* pCell = pDoc->GetFormulaCell(ScAddress(0, 0, 0));
|
||||
CPPUNIT_ASSERT(pCell);
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(FormulaError::NONE), sal_Int32(pCell->GetErrCode()));
|
||||
CPPUNIT_ASSERT_EQUAL(u"=@TRANSPOSE(B1:B4)"_ustr, pDoc->GetFormula(0, 0, 0));
|
||||
CPPUNIT_ASSERT_EQUAL(10.0, pDoc->GetValue(ScAddress(0, 0, 0)));
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf85553)
|
||||
{
|
||||
createScDoc("ods/tdf85553.ods");
|
||||
|
||||
@@ -1648,6 +1648,39 @@ CPPUNIT_TEST_FIXTURE(ScFiltersTest, testNonAsciiWithDotXLSX)
|
||||
CPPUNIT_ASSERT_EQUAL(5.0, aValue);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(ScFiltersTest, testCmImportSetsDynamicArrayMaster)
|
||||
{
|
||||
// The cm="1" attribute on an XLSX cell lands as a dynamic-array master.
|
||||
|
||||
createScDoc("xlsx/Spill.xlsx");
|
||||
ScDocument* pDoc = getScDoc();
|
||||
|
||||
// C2 carries cm="1" with t="array" ref="C2:C5", the spilled master.
|
||||
ScFormulaCell* pSpilled = pDoc->GetFormulaCell(ScAddress(2, 1, 0));
|
||||
CPPUNIT_ASSERT(pSpilled);
|
||||
CPPUNIT_ASSERT(pSpilled->IsDynamicArrayMaster());
|
||||
|
||||
// A2 is a plain value cell, so the document holds no formula at A2.
|
||||
ScFormulaCell* pValueCell = pDoc->GetFormulaCell(ScAddress(0, 1, 0));
|
||||
CPPUNIT_ASSERT(!pValueCell);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(ScFiltersTest, testCmRoundTripXLSX)
|
||||
{
|
||||
// The dynamic-array marker survives an XLSX save and reload through
|
||||
// the cm="1" attribute on the master cell.
|
||||
|
||||
createScDoc("xlsx/Spill.xlsx");
|
||||
|
||||
saveAndReload(TestFilter::XLSX);
|
||||
|
||||
ScDocument* pDoc = getScDoc();
|
||||
|
||||
ScFormulaCell* pMaster = pDoc->GetFormulaCell(ScAddress(2, 1, 0));
|
||||
CPPUNIT_ASSERT(pMaster);
|
||||
CPPUNIT_ASSERT(pMaster->IsDynamicArrayMaster());
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(ScFiltersTest, testArrayFormulaSpillXLSX)
|
||||
{
|
||||
// D2 holds =UNIQUE(A2:A5) imported in #SPILL! state with blocker text
|
||||
|
||||
@@ -2675,17 +2675,29 @@ void ScFormulaCell::SetDynamicArrayMaster(bool bDynamic)
|
||||
}
|
||||
}
|
||||
|
||||
void ScFormulaCell::ResolveImplicitIntersection(ScTokenArray& rCode, ScDocument& rDoc,
|
||||
const ScAddress& rPos)
|
||||
{
|
||||
if (rCode.IsHyperLink() || rCode.GetLen() == 0)
|
||||
return;
|
||||
if (rCode.GetCodeLen() == 0)
|
||||
{
|
||||
// Parse has tokens but RPN was not built yet. Build it so the
|
||||
// walk has post-fix order to inspect.
|
||||
ScCompiler aComp(rDoc, rPos, rCode, formula::FormulaGrammar::GRAM_DEFAULT, true, false);
|
||||
aComp.CompileTokenArray();
|
||||
}
|
||||
if (rpnTopIsImplicitIntersection(rCode))
|
||||
return;
|
||||
if (rpnIntendsArrayResult(rCode))
|
||||
prependImplicitIntersection(rCode);
|
||||
}
|
||||
|
||||
void ScFormulaCell::ResolveImplicitIntersection()
|
||||
{
|
||||
if (!pCode
|
||||
|| cMatrixFlag != ScMatrixMode::NONE
|
||||
|| pCode->IsHyperLink() || mxGroup
|
||||
|| pCode->GetCodeLen() == 0)
|
||||
if (!pCode || cMatrixFlag != ScMatrixMode::NONE || mxGroup)
|
||||
return;
|
||||
if (rpnTopIsImplicitIntersection(*pCode))
|
||||
return;
|
||||
if (rpnIntendsArrayResult(*pCode))
|
||||
prependImplicitIntersection(*pCode);
|
||||
ResolveImplicitIntersection(*pCode, rDocument, aPos);
|
||||
}
|
||||
|
||||
void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows )
|
||||
|
||||
@@ -119,7 +119,13 @@ void ImportExcel::Formula(
|
||||
if (pSharedCode)
|
||||
{
|
||||
ScFormulaCell* pCell;
|
||||
pCell = new ScFormulaCell(rD, aScPos, pSharedCode->Clone());
|
||||
// XLS has no @ encoding. Bake an implicit @ into the
|
||||
// RPN when the formula intends an array result so a
|
||||
// TRANSPOSE-style call returns the upper-left value at
|
||||
// the cell position.
|
||||
std::unique_ptr<ScTokenArray> pTokens = pSharedCode->Clone();
|
||||
ScFormulaCell::ResolveImplicitIntersection(*pTokens, rD, aScPos);
|
||||
pCell = new ScFormulaCell(rD, aScPos, std::move(pTokens));
|
||||
pCell->GetCode()->WrapReference(aScPos, EXC_MAXCOL8, EXC_MAXROW8);
|
||||
rDoc.getDoc().EnsureTable(aScPos.Tab());
|
||||
rDoc.setFormulaCell(aScPos, pCell);
|
||||
@@ -147,6 +153,10 @@ void ImportExcel::Formula(
|
||||
|
||||
if (pResult)
|
||||
{
|
||||
// XLS has no @ encoding. Bake an implicit @ into the RPN when
|
||||
// the formula intends an array result so a TRANSPOSE-style call
|
||||
// returns the upper-left value at the cell position.
|
||||
ScFormulaCell::ResolveImplicitIntersection(*pResult, rDoc.getDoc(), aScPos);
|
||||
pCell = new ScFormulaCell(rDoc.getDoc(), aScPos, std::move(pResult));
|
||||
pCell->GetCode()->WrapReference(aScPos, EXC_MAXCOL8, EXC_MAXROW8);
|
||||
rDoc.getDoc().CheckLinkFormulaNeedingCheck( *pCell->GetCode());
|
||||
|
||||
@@ -849,6 +849,10 @@ void ImportExcel::Shrfmla()
|
||||
|
||||
ScDocumentImport& rDoc = GetDocImport();
|
||||
|
||||
// XLS has no @ encoding. Bake an implicit @ into the RPN when the
|
||||
// formula intends an array result so a TRANSPOSE-style call returns
|
||||
// the upper-left value at the cell position.
|
||||
ScFormulaCell::ResolveImplicitIntersection(*pResult, rD, aPos);
|
||||
ScFormulaCell* pCell = new ScFormulaCell(rD, aPos, std::move(pResult));
|
||||
pCell->GetCode()->WrapReference(aPos, EXC_MAXCOL8, EXC_MAXROW8);
|
||||
rDoc.getDoc().CheckLinkFormulaNeedingCheck( *pCell->GetCode());
|
||||
|
||||
@@ -844,9 +844,16 @@ bool XclExpFmlaCompImpl::GetNextToken( XclExpScToken& rTokData )
|
||||
rTokData.mpScToken = GetNextRawToken();
|
||||
rTokData.mnSpaces = 0;
|
||||
/* TODO: handle ocWhitespace characters? */
|
||||
while (rTokData.GetOpCode() == ocSpaces || rTokData.GetOpCode() == ocWhitespace)
|
||||
// XLS predates the @ implicit-intersection operator. The BIFF token
|
||||
// stream has no encoding for ocSingleValue, so drop it on the way out
|
||||
// and let the operand flow through unchanged. XLS does not auto-spill
|
||||
// anyway, so the saved formula evaluates to the same upper-left value
|
||||
// it did before.
|
||||
while (rTokData.GetOpCode() == ocSpaces || rTokData.GetOpCode() == ocWhitespace
|
||||
|| rTokData.GetOpCode() == ocSingleValue)
|
||||
{
|
||||
rTokData.mnSpaces += static_cast<const FormulaByteToken*>(rTokData.mpScToken)->GetByte();
|
||||
if (rTokData.GetOpCode() != ocSingleValue)
|
||||
rTokData.mnSpaces += static_cast<const FormulaByteToken*>(rTokData.mpScToken)->GetByte();
|
||||
rTokData.mpScToken = GetNextRawToken();
|
||||
}
|
||||
return rTokData.Is();
|
||||
|
||||
@@ -949,10 +949,14 @@ void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
|
||||
|
||||
// Mark dynamic array masters with cm="1" so it is recognised as a
|
||||
// dynamic array and reads the matching dynamicArrayProperties out of
|
||||
// xl/metadata.xml.
|
||||
// xl/metadata.xml. The cell flag is the primary source. The legacy
|
||||
// pair of a Ctrl+Shift+Enter (CSE) master that calls a dynamic-array
|
||||
// function stays as a fallback while the importers and the UI entry
|
||||
// path are being wired through.
|
||||
const bool bDynamicArrayMaster
|
||||
= mrScFmlaCell.GetMatrixFlag() == ScMatrixMode::Formula
|
||||
&& mrScFmlaCell.GetCode() && mrScFmlaCell.GetCode()->HasDynamicArrayFunction();
|
||||
= mrScFmlaCell.IsDynamicArrayMaster()
|
||||
|| (mrScFmlaCell.GetMatrixFlag() == ScMatrixMode::Formula
|
||||
&& mrScFmlaCell.GetCode() && mrScFmlaCell.GetCode()->HasDynamicArrayFunction());
|
||||
if (bDynamicArrayMaster)
|
||||
rStrm.NoteDynamicArrayFormula();
|
||||
|
||||
|
||||
@@ -42,17 +42,28 @@ public:
|
||||
OUString maCellValue;
|
||||
sal_Int32 mnSharedId;
|
||||
sal_Int32 mnValueType;
|
||||
/// True when the XLSX cell carried cm="1", marking it as a dynamic-array
|
||||
/// master.
|
||||
bool mbDynamicArrayMaster = false;
|
||||
|
||||
SharedFormulaDesc(
|
||||
const ScAddress& rAddr, sal_Int32 nSharedId,
|
||||
OUString aCellValue, sal_Int32 nValueType );
|
||||
OUString aCellValue, sal_Int32 nValueType,
|
||||
bool bDynamicArrayMaster = false );
|
||||
};
|
||||
|
||||
struct TokenAddressItem
|
||||
{
|
||||
OUString maTokenStr;
|
||||
ScAddress maAddress;
|
||||
TokenAddressItem( OUString aTokenStr, const ScAddress& rAddress ) : maTokenStr(std::move( aTokenStr )), maAddress( rAddress ) {}
|
||||
/// True when the XLSX cell carried cm="1", marking it as a dynamic-array
|
||||
/// master.
|
||||
bool mbDynamicArrayMaster = false;
|
||||
TokenAddressItem( OUString aTokenStr, const ScAddress& rAddress,
|
||||
bool bDynamicArrayMaster = false )
|
||||
: maTokenStr(std::move( aTokenStr ))
|
||||
, maAddress( rAddress )
|
||||
, mbDynamicArrayMaster(bDynamicArrayMaster) {}
|
||||
};
|
||||
|
||||
struct TokenRangeAddressItem
|
||||
@@ -64,12 +75,17 @@ public:
|
||||
/// blocker preservation so reference cells aren't materialised over
|
||||
/// real user data sitting in the matrix range.
|
||||
bool mbCachedSpill;
|
||||
/// True when the XLSX cell carried cm="1", marking it as a dynamic-array
|
||||
/// master.
|
||||
bool mbDynamicArrayMaster = false;
|
||||
|
||||
TokenRangeAddressItem(TokenAddressItem aTokenAndAddress, const ScRange& rRange,
|
||||
bool bCachedSpill = false)
|
||||
bool bCachedSpill = false,
|
||||
bool bDynamicArrayMaster = false)
|
||||
: maTokenAndAddress(std::move(aTokenAndAddress))
|
||||
, maRange(rRange)
|
||||
, mbCachedSpill(bCachedSpill)
|
||||
, mbCachedSpill(bCachedSpill)
|
||||
, mbDynamicArrayMaster(bDynamicArrayMaster)
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -108,11 +124,13 @@ private:
|
||||
public:
|
||||
explicit FormulaBuffer( const WorkbookHelper& rHelper );
|
||||
void finalizeImport();
|
||||
void setCellFormula( const ScAddress& rAddress, const OUString& );
|
||||
void setCellFormula( const ScAddress& rAddress, const OUString&,
|
||||
bool bDynamicArrayMaster = false );
|
||||
|
||||
void setCellFormula(
|
||||
const ScAddress& rAddress, sal_Int32 nSharedId,
|
||||
const OUString& rCellValue, sal_Int32 nValueType );
|
||||
const OUString& rCellValue, sal_Int32 nValueType,
|
||||
bool bDynamicArrayMaster = false );
|
||||
|
||||
void setCellFormulaValue(
|
||||
const ScAddress& rAddress, const OUString& rValueStr, sal_Int32 nCellType );
|
||||
@@ -120,7 +138,8 @@ public:
|
||||
void setCellArrayFormula(const ScRange& rRangeAddress,
|
||||
const ScAddress& rTokenAddress,
|
||||
const OUString&,
|
||||
bool bCachedSpill = false);
|
||||
bool bCachedSpill = false,
|
||||
bool bDynamicArrayMaster = false);
|
||||
|
||||
void createSharedFormulaMapEntry( const ScAddress& rAddress,
|
||||
sal_Int32 nSharedId, const OUString& rTokens );
|
||||
|
||||
@@ -284,13 +284,17 @@ public:
|
||||
/** Finalizes the import of named sheet views, by creating the sheet views from the data. */
|
||||
void finalizeNamedSheetViews();
|
||||
|
||||
void setCellFormula( const ScAddress& rTokenAddress, const OUString& );
|
||||
void setCellFormula( const ScAddress& rTokenAddress, const OUString&,
|
||||
bool bDynamicArrayMaster = false );
|
||||
|
||||
void setCellFormula(
|
||||
const ScAddress& rAddr, sal_Int32 nSharedId,
|
||||
const OUString& rCellValue, sal_Int32 nValueType );
|
||||
const OUString& rCellValue, sal_Int32 nValueType,
|
||||
bool bDynamicArrayMaster = false );
|
||||
|
||||
void setCellArrayFormula(const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr, bool bCachedSpill = false);
|
||||
void setCellArrayFormula(const ScRange& rRangeAddress, const ScAddress& rTokenAddress,
|
||||
const OUString& rTokenStr, bool bCachedSpill = false,
|
||||
bool bDynamicArrayMaster = false);
|
||||
|
||||
void createSharedFormulaMapEntry(
|
||||
const ScAddress& rAddress,
|
||||
|
||||
@@ -154,6 +154,9 @@ void applySharedFormulas(
|
||||
else
|
||||
pCell = new ScFormulaCell(rDoc.getDoc(), aPos, *pArray);
|
||||
|
||||
if (rDesc.mbDynamicArrayMaster)
|
||||
pCell->SetDynamicArrayMaster(true);
|
||||
|
||||
rDoc.setFormulaCell(aPos, pCell);
|
||||
const bool bNeedNumberFormat = ((rDoc.getDoc().GetNumberFormat(
|
||||
aPos.Col(), aPos.Row(), aPos.Tab()) % SV_COUNTRY_LANGUAGE_OFFSET) == 0);
|
||||
@@ -263,6 +266,9 @@ void applyCellFormulas(
|
||||
else
|
||||
pCell = new ScFormulaCell(rDoc.getDoc(), aPos, p->mpCell->GetCode()->Clone());
|
||||
|
||||
if (rItem.mbDynamicArrayMaster)
|
||||
pCell->SetDynamicArrayMaster(true);
|
||||
|
||||
rDoc.setFormulaCell(aPos, pCell);
|
||||
if (rDoc.getDoc().GetNumberFormat(aPos.Col(), aPos.Row(), aPos.Tab()) % SV_COUNTRY_LANGUAGE_OFFSET == 0)
|
||||
pCell->SetNeedNumberFormat(true);
|
||||
@@ -282,6 +288,8 @@ void applyCellFormulas(
|
||||
aCompiler.CompileTokenArray(); // Generate RPN tokens.
|
||||
|
||||
ScFormulaCell* pCell = new ScFormulaCell(rDoc.getDoc(), aPos, std::move(pCode));
|
||||
if (rItem.mbDynamicArrayMaster)
|
||||
pCell->SetDynamicArrayMaster(true);
|
||||
rDoc.setFormulaCell(aPos, pCell);
|
||||
if (rDoc.getDoc().GetNumberFormat(aPos.Col(), aPos.Row(), aPos.Tab()) % SV_COUNTRY_LANGUAGE_OFFSET == 0)
|
||||
pCell->SetNeedNumberFormat(true);
|
||||
@@ -306,6 +314,12 @@ void applyArrayFormulas(
|
||||
rDoc.setMatrixCells(rAddressItem.maRange, *pArray,
|
||||
formula::FormulaGrammar::GRAM_OOXML,
|
||||
rAddressItem.mbCachedSpill);
|
||||
if (rAddressItem.mbDynamicArrayMaster)
|
||||
{
|
||||
ScFormulaCell* pMaster = rDoc.getDoc().GetFormulaCell(aPos);
|
||||
if (pMaster)
|
||||
pMaster->SetDynamicArrayMaster(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -422,9 +436,14 @@ FormulaBuffer::SharedFormulaEntry::SharedFormulaEntry(
|
||||
maAddress(rAddr), maTokenStr(std::move(aTokenStr)), mnSharedId(nSharedId) {}
|
||||
|
||||
FormulaBuffer::SharedFormulaDesc::SharedFormulaDesc(
|
||||
const ScAddress& rAddr, sal_Int32 nSharedId,
|
||||
OUString aCellValue, sal_Int32 nValueType ) :
|
||||
maAddress(rAddr), maCellValue(std::move(aCellValue)), mnSharedId(nSharedId), mnValueType(nValueType) {}
|
||||
const ScAddress& rAddr, sal_Int32 nSharedId,
|
||||
OUString aCellValue, sal_Int32 nValueType, bool bDynamicArrayMaster)
|
||||
: maAddress(rAddr)
|
||||
, maCellValue(std::move(aCellValue))
|
||||
, mnSharedId(nSharedId)
|
||||
, mnValueType(nValueType)
|
||||
, mbDynamicArrayMaster(bDynamicArrayMaster)
|
||||
{}
|
||||
|
||||
FormulaBuffer::SheetItem::SheetItem() :
|
||||
mpCellFormulas(nullptr),
|
||||
@@ -513,25 +532,30 @@ void FormulaBuffer::createSharedFormulaMapEntry(
|
||||
rSharedFormulas.push_back( aEntry );
|
||||
}
|
||||
|
||||
void FormulaBuffer::setCellFormula( const ScAddress& rAddress, const OUString& rTokenStr )
|
||||
void FormulaBuffer::setCellFormula(const ScAddress& rAddress, const OUString& rTokenStr,
|
||||
bool bDynamicArrayMaster)
|
||||
{
|
||||
assert( rAddress.Tab() >= 0 && o3tl::make_unsigned(rAddress.Tab()) < maCellFormulas.size() );
|
||||
maCellFormulas[ rAddress.Tab() ].emplace_back( rTokenStr, rAddress );
|
||||
maCellFormulas[rAddress.Tab()].emplace_back(rTokenStr, rAddress, bDynamicArrayMaster);
|
||||
}
|
||||
|
||||
void FormulaBuffer::setCellFormula(
|
||||
const ScAddress& rAddress, sal_Int32 nSharedId, const OUString& rCellValue, sal_Int32 nValueType )
|
||||
const ScAddress& rAddress, sal_Int32 nSharedId, const OUString& rCellValue, sal_Int32 nValueType,
|
||||
bool bDynamicArrayMaster)
|
||||
{
|
||||
assert( rAddress.Tab() >= 0 && o3tl::make_unsigned(rAddress.Tab()) < maSharedFormulaIds.size() );
|
||||
maSharedFormulaIds[rAddress.Tab()].emplace_back(rAddress, nSharedId, rCellValue, nValueType);
|
||||
maSharedFormulaIds[rAddress.Tab()].emplace_back(rAddress, nSharedId, rCellValue, nValueType,
|
||||
bDynamicArrayMaster);
|
||||
}
|
||||
|
||||
void FormulaBuffer::setCellArrayFormula( const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr, bool bCachedSpill)
|
||||
void FormulaBuffer::setCellArrayFormula(const ScRange& rRangeAddress, const ScAddress& rTokenAddress,
|
||||
const OUString& rTokenStr, bool bCachedSpill,
|
||||
bool bDynamicArrayMaster)
|
||||
{
|
||||
|
||||
TokenAddressItem tokenPair( rTokenStr, rTokenAddress );
|
||||
assert( rRangeAddress.aStart.Tab() >= 0 && o3tl::make_unsigned(rRangeAddress.aStart.Tab()) < maCellArrayFormulas.size() );
|
||||
maCellArrayFormulas[rRangeAddress.aStart.Tab()].emplace_back(tokenPair, rRangeAddress, bCachedSpill);
|
||||
maCellArrayFormulas[rRangeAddress.aStart.Tab()].emplace_back(tokenPair, rRangeAddress, bCachedSpill, bDynamicArrayMaster);
|
||||
}
|
||||
|
||||
void FormulaBuffer::setCellFormulaValue(
|
||||
|
||||
@@ -143,7 +143,7 @@ void SheetDataContext::onEndElement()
|
||||
// master will create the matrix cells via setMatrixCells.
|
||||
if (!maFormulaStr.isEmpty() || mbCalcAlways)
|
||||
{
|
||||
setCellFormula(maCellData.maCellAddr, maFormulaStr);
|
||||
setCellFormula(maCellData.maCellAddr, maFormulaStr, mbCellMetadata);
|
||||
mrSheetData.setCellFormat(maCellData);
|
||||
|
||||
// If a number cell has some preloaded value, stick it into the buffer
|
||||
@@ -161,7 +161,8 @@ void SheetDataContext::onEndElement()
|
||||
if( mbValidRange && maFmlaData.isValidSharedRef( maCellData.maCellAddr ) )
|
||||
createSharedFormulaMapEntry(maCellData.maCellAddr, maFmlaData.mnSharedId, maFormulaStr);
|
||||
|
||||
setCellFormula(maCellData.maCellAddr, maFmlaData.mnSharedId, maCellValue, maCellData.mnCellType);
|
||||
setCellFormula(maCellData.maCellAddr, maFmlaData.mnSharedId, maCellValue, maCellData.mnCellType,
|
||||
mbCellMetadata);
|
||||
mrSheetData.setCellFormat( maCellData );
|
||||
}
|
||||
else
|
||||
@@ -181,7 +182,8 @@ void SheetDataContext::onEndElement()
|
||||
= maCellData.mnCellType == XML_e
|
||||
&& mbCellMetadata
|
||||
&& maCellValue == u"#VALUE!";
|
||||
setCellArrayFormula(maFmlaData.maFormulaRef, maCellData.maCellAddr, maFormulaStr, bCachedSpill);
|
||||
setCellArrayFormula(maFmlaData.maFormulaRef, maCellData.maCellAddr, maFormulaStr,
|
||||
bCachedSpill, mbCellMetadata);
|
||||
}
|
||||
// set cell formatting, but do not set result as cell value
|
||||
mrSheetData.setBlankCell( maCellData );
|
||||
|
||||
@@ -1644,21 +1644,26 @@ void WorksheetHelper::finalizeNamedSheetViews()
|
||||
mrSheetGlob.finalizeNamedSheetViews();
|
||||
}
|
||||
|
||||
void WorksheetHelper::setCellFormula( const ScAddress& rTokenAddress, const OUString& rTokenStr )
|
||||
void WorksheetHelper::setCellFormula(const ScAddress& rTokenAddress, const OUString& rTokenStr,
|
||||
bool bDynamicArrayMaster)
|
||||
{
|
||||
getFormulaBuffer().setCellFormula( rTokenAddress, rTokenStr );
|
||||
getFormulaBuffer().setCellFormula(rTokenAddress, rTokenStr, bDynamicArrayMaster);
|
||||
}
|
||||
|
||||
void WorksheetHelper::setCellFormula(
|
||||
const ScAddress& rAddr, sal_Int32 nSharedId,
|
||||
const OUString& rCellValue, sal_Int32 nValueType )
|
||||
const OUString& rCellValue, sal_Int32 nValueType,
|
||||
bool bDynamicArrayMaster)
|
||||
{
|
||||
getFormulaBuffer().setCellFormula(rAddr, nSharedId, rCellValue, nValueType);
|
||||
getFormulaBuffer().setCellFormula(rAddr, nSharedId, rCellValue, nValueType, bDynamicArrayMaster);
|
||||
}
|
||||
|
||||
void WorksheetHelper::setCellArrayFormula(const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr, bool bCachedSpill)
|
||||
void WorksheetHelper::setCellArrayFormula(const ScRange& rRangeAddress, const ScAddress& rTokenAddress,
|
||||
const OUString& rTokenStr, bool bCachedSpill,
|
||||
bool bDynamicArrayMaster)
|
||||
{
|
||||
getFormulaBuffer().setCellArrayFormula(rRangeAddress, rTokenAddress, rTokenStr, bCachedSpill);
|
||||
getFormulaBuffer().setCellArrayFormula(rRangeAddress, rTokenAddress, rTokenStr, bCachedSpill,
|
||||
bDynamicArrayMaster);
|
||||
}
|
||||
|
||||
void WorksheetHelper::createSharedFormulaMapEntry(
|
||||
|
||||
Reference in New Issue
Block a user