summaryrefslogtreecommitdiffstats
path: root/sc/source/filter/xml/xmlcelli.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/filter/xml/xmlcelli.cxx')
-rw-r--r--sc/source/filter/xml/xmlcelli.cxx1521
1 files changed, 1521 insertions, 0 deletions
diff --git a/sc/source/filter/xml/xmlcelli.cxx b/sc/source/filter/xml/xmlcelli.cxx
new file mode 100644
index 000000000..a14424eda
--- /dev/null
+++ b/sc/source/filter/xml/xmlcelli.cxx
@@ -0,0 +1,1521 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <limits>
+#include <memory>
+#include "xmlcelli.hxx"
+#include "xmlimprt.hxx"
+#include "xmlannoi.hxx"
+#include <global.hxx>
+#include <cellvalue.hxx>
+#include <document.hxx>
+#include <docuno.hxx>
+#include <postit.hxx>
+#include <sheetdata.hxx>
+#include <cellform.hxx>
+#include <validat.hxx>
+#include <patattr.hxx>
+#include <scitems.hxx>
+#include <docpool.hxx>
+
+#include "XMLTableShapeImportHelper.hxx"
+#include "XMLStylesImportHelper.hxx"
+#include "celltextparacontext.hxx"
+#include "XMLCellRangeSourceContext.hxx"
+
+#include <arealink.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <scerrors.hxx>
+#include <editutil.hxx>
+#include <formulacell.hxx>
+#include "editattributemap.hxx"
+#include <tokenarray.hxx>
+#include <scmatrix.hxx>
+#include <documentimport.hxx>
+#include <externalrefmgr.hxx>
+
+#include <xmloff/maptype.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlprmap.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/prstylei.hxx>
+#include <xmloff/xmlimppr.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svx/svdocapt.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/langitem.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sax/tools/converter.hxx>
+#include <sax/fastattribs.hxx>
+
+#include <com/sun/star/util/NumberFormat.hpp>
+
+#include <com/sun/star/sheet/ValidationType.hpp>
+#include <com/sun/star/sheet/ValidationAlertStyle.hpp>
+
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <i18nlangtag/lang.h>
+
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/lok.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTableRowCellContext::ParaFormat::ParaFormat(const ScEditEngineDefaulter& rEditEngine) :
+ maItemSet(rEditEngine.GetEmptyItemSet()) {}
+
+ScXMLTableRowCellContext::Field::Field(std::unique_ptr<SvxFieldData> pData) : mpData(std::move(pData)) {}
+
+ScXMLTableRowCellContext::Field::~Field()
+{
+}
+
+ScXMLTableRowCellContext::ScXMLTableRowCellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bTempIsCovered,
+ const sal_Int32 nTempRepeatedRows ) :
+ ScXMLImportContext( rImport ),
+ mpEditEngine(GetScImport().GetEditEngine()),
+ mnCurParagraph(0),
+ fValue(std::numeric_limits<double>::quiet_NaN()),
+ nMergedRows(1),
+ nMatrixRows(0),
+ nRepeatedRows(nTempRepeatedRows),
+ nMergedCols(1),
+ nMatrixCols(0),
+ nColsRepeated(1),
+ rXMLImport(rImport),
+ eGrammar( formula::FormulaGrammar::GRAM_STORAGE_DEFAULT),
+ nCellType(util::NumberFormat::TEXT),
+ bIsMerged(false),
+ bIsMatrix(false),
+ bIsCovered(bTempIsCovered),
+ bIsEmpty(true),
+ mbNewValueType(false),
+ mbErrorValue(false),
+ bSolarMutexLocked(false),
+ bFormulaTextResult(false),
+ mbPossibleErrorCell(false),
+ mbCheckWithCompilerForError(false),
+ mbEditEngineHasText(false),
+ mbHasFormatRuns(false),
+ mbHasStyle(false),
+ mbPossibleEmptyDisplay(false)
+{
+ rXMLImport.GetTables().AddColumn(bTempIsCovered);
+
+ std::optional<OUString> xStyleName;
+ std::optional<OUString> xCurrencySymbol;
+ if ( rAttrList.is() )
+ {
+ for (auto &it : *rAttrList)
+ {
+ switch ( it.getToken() )
+ {
+ case XML_ELEMENT( TABLE, XML_STYLE_NAME ):
+ xStyleName = it.toString();
+ mbHasStyle = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_CONTENT_VALIDATION_NAME ):
+ OSL_ENSURE(!maContentValidationName, "here should be only one Validation Name");
+ if (!it.isEmpty())
+ maContentValidationName = it.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_ROWS_SPANNED ):
+ bIsMerged = true;
+ nMergedRows = static_cast<SCROW>(it.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_SPANNED ):
+ bIsMerged = true;
+ nMergedCols = static_cast<SCCOL>(it.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_MATRIX_COLUMNS_SPANNED ):
+ bIsMatrix = true;
+ nMatrixCols = static_cast<SCCOL>(it.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_MATRIX_ROWS_SPANNED ):
+ bIsMatrix = true;
+ nMatrixRows = static_cast<SCROW>(it.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_REPEATED ):
+ nColsRepeated = static_cast<SCCOL>(
+ std::min<sal_Int32>( rImport.GetDocument()->GetSheetLimits().GetMaxColCount(),
+ std::max( it.toInt32(), static_cast<sal_Int32>(1) ) ));
+ break;
+ case XML_ELEMENT( OFFICE, XML_VALUE_TYPE ):
+ nCellType = ScXMLImport::GetCellType(it.toCString(), it.getLength());
+ bIsEmpty = false;
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_VALUE_TYPE ):
+ if(it.isString( "error" ) )
+ mbErrorValue = true;
+ else
+ nCellType = ScXMLImport::GetCellType(it.toCString(), it.getLength());
+ bIsEmpty = false;
+ mbNewValueType = true;
+ break;
+ case XML_ELEMENT( OFFICE, XML_VALUE ):
+ {
+ if (!it.isEmpty())
+ {
+ fValue = it.toDouble();
+ bIsEmpty = false;
+
+ //if office:value="0", let's get the text:p in case this is
+ //a special case in HasSpecialCaseFormulaText(). If it
+ //turns out not to be a special case, we'll use the 0 value.
+ if(fValue == 0.0)
+ bFormulaTextResult = true;
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_DATE_VALUE ):
+ {
+ if (!it.isEmpty() && rXMLImport.SetNullDateOnUnitConverter())
+ {
+ rXMLImport.GetMM100UnitConverter().convertDateTime(fValue, it.toView());
+ bIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_TIME_VALUE ):
+ {
+ if (!it.isEmpty())
+ {
+ ::sax::Converter::convertDuration(fValue, it.toView());
+ bIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_STRING_VALUE ):
+ {
+ if (!it.isEmpty())
+ {
+ OSL_ENSURE(!maStringValue, "here should be only one string value");
+ maStringValue = it.toString();
+ bIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE , XML_BOOLEAN_VALUE ):
+ {
+ if (!it.isEmpty())
+ {
+ if ( IsXMLToken( it, XML_TRUE ) )
+ fValue = 1.0;
+ else if ( IsXMLToken( it, XML_FALSE ) )
+ fValue = 0.0;
+ else
+ fValue = it.toDouble();
+ bIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FORMULA ):
+ {
+ if (!it.isEmpty())
+ {
+ OSL_ENSURE(!maFormula, "here should be only one formula");
+ OUString aFormula, aFormulaNmsp;
+ rXMLImport.ExtractFormulaNamespaceGrammar( aFormula, aFormulaNmsp, eGrammar, it.toString() );
+ maFormula = FormulaWithNamespace(aFormula, aFormulaNmsp);
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_CURRENCY ):
+ xCurrencySymbol = it.toString();
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ if (maFormula)
+ {
+ if (nCellType == util::NumberFormat::TEXT)
+ bFormulaTextResult = true;
+ if(nCellType == util::NumberFormat::DATETIME)
+ nCellType = util::NumberFormat::UNDEFINED;
+ //if bIsEmpty is true at this point, then there is no office value.
+ //we must get the text:p (even if it is empty) in case this a special
+ //case in HasSpecialCaseFormulaText().
+ if(bIsEmpty)
+ bFormulaTextResult = true;
+ }
+ rXMLImport.GetStylesImportHelper()->SetAttributes(std::move(xStyleName), std::move(xCurrencySymbol), nCellType);
+}
+
+ScXMLTableRowCellContext::~ScXMLTableRowCellContext()
+{
+}
+
+void ScXMLTableRowCellContext::LockSolarMutex()
+{
+ if (!bSolarMutexLocked)
+ {
+ GetScImport().LockSolarMutex();
+ bSolarMutexLocked = true;
+ }
+}
+
+namespace {
+
+bool cellExists( const ScDocument& rDoc, const ScAddress& rCellPos )
+{
+ return( rCellPos.Col() >= 0 && rCellPos.Row() >= 0 &&
+ rCellPos.Col() <= rDoc.MaxCol() && rCellPos.Row() <= rDoc.MaxRow() );
+}
+
+}
+
+void ScXMLTableRowCellContext::PushParagraphSpan(const OUString& rSpan, const OUString& rStyleName)
+{
+ sal_Int32 nBegin = maParagraph.getLength();
+ sal_Int32 nEnd = nBegin + rSpan.getLength();
+ maParagraph.append(rSpan);
+
+ PushFormat(nBegin, nEnd, rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphField(std::unique_ptr<SvxFieldData> pData, const OUString& rStyleName)
+{
+ mbHasFormatRuns = true;
+ maFields.push_back(std::make_unique<Field>(std::move(pData)));
+ Field& rField = *maFields.back();
+
+ sal_Int32 nPos = maParagraph.getLength();
+ maParagraph.append('\1'); // Placeholder text for inserted field item.
+ rField.maSelection.nStartPara = mnCurParagraph;
+ rField.maSelection.nEndPara = mnCurParagraph;
+ rField.maSelection.nStartPos = nPos;
+ rField.maSelection.nEndPos = nPos+1;
+
+ PushFormat(nPos, nPos+1, rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushFormat(sal_Int32 nBegin, sal_Int32 nEnd, const OUString& rStyleName)
+{
+ if (rStyleName.isEmpty())
+ return;
+
+ // Get the style information from xmloff.
+ rtl::Reference<XMLPropertySetMapper> xMapper = GetImport().GetTextImport()->GetTextImportPropertySetMapper()->getPropertySetMapper();
+ if (!xMapper.is())
+ // We can't do anything without the mapper.
+ return;
+
+ sal_Int32 nEntryCount = xMapper->GetEntryCount();
+
+ SvXMLStylesContext* pAutoStyles = GetImport().GetAutoStyles();
+ if (!pAutoStyles)
+ return;
+
+ // Style name for text span corresponds with the name of an automatic style.
+ const XMLPropStyleContext* pStyle = dynamic_cast<const XMLPropStyleContext*>(
+ pAutoStyles->FindStyleChildContext(XmlStyleFamily::TEXT_TEXT, rStyleName));
+
+ if (!pStyle)
+ // No style by that name found.
+ return;
+
+ const std::vector<XMLPropertyState>& rProps = pStyle->GetProperties();
+ if (rProps.empty())
+ return;
+
+ const ScXMLEditAttributeMap& rEditAttrMap = GetScImport().GetEditAttributeMap();
+
+ mbHasFormatRuns = true;
+ maFormats.push_back(std::make_unique<ParaFormat>(*mpEditEngine));
+ ParaFormat& rFmt = *maFormats.back();
+ rFmt.maSelection.nStartPara = rFmt.maSelection.nEndPara = mnCurParagraph;
+ rFmt.maSelection.nStartPos = nBegin;
+ rFmt.maSelection.nEndPos = nEnd;
+
+ // Store the used text styles for export.
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(rXMLImport.GetModel())->GetSheetSaveData();
+ ScAddress aCellPos = rXMLImport.GetTables().GetCurrentCellPos();
+ pSheetData->AddTextStyle(rStyleName, aCellPos, rFmt.maSelection);
+
+ std::unique_ptr<SfxPoolItem> pPoolItem;
+ sal_uInt16 nLastItemID = EE_CHAR_END + 1;
+
+ for (const auto& rProp : rProps)
+ {
+ if (rProp.mnIndex == -1 || rProp.mnIndex >= nEntryCount)
+ continue;
+
+ const OUString& rName = xMapper->GetEntryAPIName(rProp.mnIndex);
+ const ScXMLEditAttributeMap::Entry* pEntry = rEditAttrMap.getEntryByAPIName(rName);
+ if (!pEntry)
+ continue;
+
+ if (nLastItemID != pEntry->mnItemID && pPoolItem)
+ {
+ // Flush the last item when the item ID changes.
+ rFmt.maItemSet.Put(std::move(pPoolItem));
+ }
+
+ switch (pEntry->mnItemID)
+ {
+ case EE_CHAR_FONTINFO:
+ case EE_CHAR_FONTINFO_CJK:
+ case EE_CHAR_FONTINFO_CTL:
+ {
+ // Font properties need to be consolidated into a single item.
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxFontItem(pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_WEIGHT:
+ case EE_CHAR_WEIGHT_CJK:
+ case EE_CHAR_WEIGHT_CTL:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxWeightItem(WEIGHT_NORMAL, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_FONTHEIGHT:
+ case EE_CHAR_FONTHEIGHT_CJK:
+ case EE_CHAR_FONTHEIGHT_CTL:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxFontHeightItem(240, 100, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_ITALIC:
+ case EE_CHAR_ITALIC_CJK:
+ case EE_CHAR_ITALIC_CTL:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxPostureItem(ITALIC_NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_UNDERLINE:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxUnderlineItem(LINESTYLE_NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_OVERLINE:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxOverlineItem(LINESTYLE_NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_COLOR:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxColorItem(pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_WLM:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxWordLineModeItem(false, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_STRIKEOUT:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxCrossedOutItem(STRIKEOUT_NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_RELIEF:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxCharReliefItem(FontRelief::NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_OUTLINE:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxContourItem(false, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_SHADOW:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxShadowedItem(false, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_KERNING:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxKerningItem(0, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_PAIRKERNING:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxAutoKernItem(false, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_FONTWIDTH:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxCharScaleWidthItem(100, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_ESCAPEMENT:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxEscapementItem(pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_EMPHASISMARK:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxEmphasisMarkItem(FontEmphasisMark::NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_LANGUAGE:
+ case EE_CHAR_LANGUAGE_CJK:
+ case EE_CHAR_LANGUAGE_CTL:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxLanguageItem(LANGUAGE_DONTKNOW, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ default:
+ ;
+ }
+
+ nLastItemID = pEntry->mnItemID;
+ }
+
+ if (pPoolItem)
+ rFmt.maItemSet.Put(std::move(pPoolItem));
+}
+
+OUString ScXMLTableRowCellContext::GetFirstParagraph() const
+{
+ if (!maFirstParagraph)
+ return mpEditEngine->GetText(0);
+
+ return *maFirstParagraph;
+}
+
+void ScXMLTableRowCellContext::PushParagraphFieldDate(const OUString& rStyleName)
+{
+ PushParagraphField(std::make_unique<SvxDateField>(), rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphFieldSheetName(const OUString& rStyleName)
+{
+ SCTAB nTab = GetScImport().GetTables().GetCurrentCellPos().Tab();
+ PushParagraphField(std::make_unique<SvxTableField>(nTab), rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphFieldDocTitle(const OUString& rStyleName)
+{
+ PushParagraphField(std::make_unique<SvxFileField>(), rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphFieldURL(
+ const OUString& rURL, const OUString& rRep, const OUString& rStyleName, const OUString& rTargetFrame)
+{
+ OUString aAbsURL = GetScImport().GetAbsoluteReference(rURL);
+ std::unique_ptr<SvxURLField> pURLField(new SvxURLField(aAbsURL, rRep, SvxURLFormat::Repr));
+ pURLField->SetTargetFrame(rTargetFrame);
+ PushParagraphField(std::move(pURLField), rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphEnd()
+{
+ // EditEngine always has at least one paragraph even when its content is empty.
+
+ if (mbEditEngineHasText)
+ {
+ if (maFirstParagraph)
+ {
+ // Flush the cached first paragraph first.
+ mpEditEngine->Clear();
+ mpEditEngine->SetTextCurrentDefaults(*maFirstParagraph);
+ maFirstParagraph.reset();
+ }
+ mpEditEngine->InsertParagraph(mpEditEngine->GetParagraphCount(), maParagraph.makeStringAndClear());
+ }
+ else if (mbHasFormatRuns)
+ {
+ mpEditEngine->Clear();
+ mpEditEngine->SetTextCurrentDefaults(maParagraph.makeStringAndClear());
+ mbEditEngineHasText = true;
+ }
+ else if (mnCurParagraph == 0)
+ {
+ maFirstParagraph = maParagraph.makeStringAndClear();
+ mbEditEngineHasText = true;
+ }
+
+ ++mnCurParagraph;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLTableRowCellContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ bool bTextP(false);
+ switch (nElement)
+ {
+ case XML_ELEMENT( TEXT, XML_P ):
+ {
+ bIsEmpty = false;
+ bTextP = true;
+
+ pContext = new ScXMLCellTextParaContext(rXMLImport, *this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SUB_TABLE ):
+ {
+ SAL_WARN("sc", "ScXMLTableRowCellContext::createFastChildContext: subtables are not supported");
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DETECTIVE ):
+ {
+ bIsEmpty = false;
+ if (!pDetectiveObjVec)
+ pDetectiveObjVec.reset( new ScMyImpDetectiveObjVec );
+ pContext = new ScXMLDetectiveContext(
+ rXMLImport, pDetectiveObjVec.get() );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_RANGE_SOURCE ):
+ {
+ bIsEmpty = false;
+ if (!pCellRangeSource)
+ pCellRangeSource.reset(new ScMyImpCellRangeSource());
+ pContext = new ScXMLCellRangeSourceContext(
+ rXMLImport, pAttribList, pCellRangeSource.get() );
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_ANNOTATION):
+ {
+ bIsEmpty = false;
+ OSL_ENSURE(
+ !mxAnnotationData,
+ "ScXMLTableRowCellContext::CreateChildContext - multiple annotations in one cell");
+ mxAnnotationData.reset( new ScXMLAnnotationData );
+ pContext = new ScXMLAnnotationContext( rXMLImport, nElement,
+ xAttrList, *mxAnnotationData);
+ }
+ break;
+ }
+
+ if (!pContext && !bTextP)
+ {
+ ScAddress aCellPos = rXMLImport.GetTables().GetCurrentCellPos();
+ uno::Reference<drawing::XShapes> xShapes (rXMLImport.GetTables().GetCurrentXShapes());
+ if (xShapes.is())
+ {
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if (aCellPos.Col() > pDoc->MaxCol())
+ aCellPos.SetCol(pDoc->MaxCol());
+ if (aCellPos.Row() > pDoc->MaxRow())
+ aCellPos.SetRow(pDoc->MaxRow());
+ XMLTableShapeImportHelper* pTableShapeImport =
+ static_cast< XMLTableShapeImportHelper* >( rXMLImport.GetShapeImport().get() );
+ pTableShapeImport->SetOnTable(false);
+ pTableShapeImport->SetCell(aCellPos);
+ pContext = XMLShapeImportHelper::CreateGroupChildContext(
+ rXMLImport, nElement, xAttrList, xShapes);
+ if (pContext)
+ {
+ bIsEmpty = false;
+ rXMLImport.ProgressBarIncrement();
+ }
+ }
+ }
+
+ return pContext;
+}
+
+void ScXMLTableRowCellContext::DoMerge( const ScAddress& rScAddress, const SCCOL nCols, const SCROW nRows )
+{
+ SCCOL mergeToCol = rScAddress.Col() + nCols;
+ SCROW mergeToRow = rScAddress.Row() + nRows;
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ bool bInBounds = rScAddress.Col() <= pDoc->MaxCol() && rScAddress.Row() <= pDoc->MaxRow() &&
+ mergeToCol <= pDoc->MaxCol() && mergeToRow <= pDoc->MaxRow();
+ if( bInBounds )
+ {
+ pDoc->DoMerge( rScAddress.Col(), rScAddress.Row(),
+ mergeToCol, mergeToRow, rScAddress.Tab() );
+ }
+}
+
+namespace {
+
+ScValidationMode validationTypeToMode( const sheet::ValidationType eVType )
+{
+ ScValidationMode eMode;
+ switch( eVType )
+ {
+ case sheet::ValidationType_WHOLE: eMode = SC_VALID_WHOLE; break;
+ case sheet::ValidationType_DECIMAL: eMode = SC_VALID_DECIMAL; break;
+ case sheet::ValidationType_DATE: eMode = SC_VALID_DATE; break;
+ case sheet::ValidationType_TIME: eMode = SC_VALID_TIME; break;
+ case sheet::ValidationType_TEXT_LEN: eMode = SC_VALID_TEXTLEN; break;
+ case sheet::ValidationType_LIST: eMode = SC_VALID_LIST; break;
+ case sheet::ValidationType_CUSTOM: eMode = SC_VALID_CUSTOM; break;
+ default: eMode = SC_VALID_ANY; break;
+ }
+ return eMode;
+}
+
+ScValidErrorStyle validAlertToValidError( const sheet::ValidationAlertStyle eVAlertStyle )
+{
+ ScValidErrorStyle eVErrStyle;
+ switch( eVAlertStyle )
+ {
+ case sheet::ValidationAlertStyle_STOP: eVErrStyle = SC_VALERR_STOP; break;
+ case sheet::ValidationAlertStyle_WARNING: eVErrStyle = SC_VALERR_WARNING; break;
+ case sheet::ValidationAlertStyle_MACRO: eVErrStyle = SC_VALERR_MACRO; break;
+ default: eVErrStyle = SC_VALERR_INFO; break;
+ //should INFO be the default? seems to be the most unobtrusive choice.
+ }
+ return eVErrStyle;
+}
+
+}
+
+void ScXMLTableRowCellContext::SetContentValidation( const ScRange& rScRange )
+{
+ if (!maContentValidationName)
+ return;
+
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ ScMyImportValidation aValidation;
+ aValidation.eGrammar1 = aValidation.eGrammar2 = pDoc->GetStorageGrammar();
+ if( !rXMLImport.GetValidation(*maContentValidationName, aValidation) )
+ return;
+
+ ScValidationData aScValidationData(
+ validationTypeToMode(aValidation.aValidationType),
+ ScConditionEntry::GetModeFromApi(aValidation.aOperator),
+ aValidation.sFormula1, aValidation.sFormula2, *pDoc, ScAddress(),
+ aValidation.sFormulaNmsp1, aValidation.sFormulaNmsp2,
+ aValidation.eGrammar1, aValidation.eGrammar2
+ );
+
+ aScValidationData.SetIgnoreBlank( aValidation.bIgnoreBlanks );
+ aScValidationData.SetListType( aValidation.nShowList );
+
+ // set strings for error / input even if disabled (and disable afterwards)
+ aScValidationData.SetInput( aValidation.sInputTitle, aValidation.sInputMessage );
+ if( !aValidation.bShowInputMessage )
+ aScValidationData.ResetInput();
+ aScValidationData.SetError( aValidation.sErrorTitle, aValidation.sErrorMessage, validAlertToValidError(aValidation.aAlertStyle) );
+ if( !aValidation.bShowErrorMessage )
+ aScValidationData.ResetError();
+
+ if( !aValidation.sBaseCellAddress.isEmpty() )
+ aScValidationData.SetSrcString( aValidation.sBaseCellAddress );
+
+ sal_uLong nIndex = pDoc->AddValidationEntry( aScValidationData );
+
+ ScPatternAttr aPattern( pDoc->GetPool() );
+ aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nIndex ) );
+ if( rScRange.aStart == rScRange.aEnd ) //for a single cell
+ {
+ pDoc->ApplyPattern( rScRange.aStart.Col(), rScRange.aStart.Row(),
+ rScRange.aStart.Tab(), aPattern );
+ }
+ else //for repeating cells
+ {
+ pDoc->ApplyPatternAreaTab( rScRange.aStart.Col(), rScRange.aStart.Row(),
+ rScRange.aEnd.Col(), rScRange.aEnd.Row(),
+ rScRange.aStart.Tab(), aPattern );
+ }
+
+ // is the below still needed?
+ // For now, any sheet with validity is blocked from stream-copying.
+ // Later, the validation names could be stored along with the style names.
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(GetImport().GetModel())->GetSheetSaveData();
+ pSheetData->BlockSheet( GetScImport().GetTables().GetCurrentSheet() );
+}
+
+void ScXMLTableRowCellContext::SetContentValidation( const ScAddress& rCellPos )
+{
+ SetContentValidation( ScRange(rCellPos, rCellPos) );
+}
+
+void ScXMLTableRowCellContext::SetAnnotation(const ScAddress& rPos)
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if (!pDoc || !mxAnnotationData)
+ return;
+
+ LockSolarMutex();
+
+ ScPostIt* pNote = nullptr;
+
+ uno::Reference< drawing::XShapes > xShapes = rXMLImport.GetTables().GetCurrentXShapes();
+ sal_Int32 nOldShapeCount = xShapes.is() ? xShapes->getCount() : 0;
+
+ OSL_ENSURE( !mxAnnotationData->mxShape.is() || mxAnnotationData->mxShapes.is(),
+ "ScXMLTableRowCellContext::SetAnnotation - shape without drawing page" );
+ if( mxAnnotationData->mxShape.is() && mxAnnotationData->mxShapes.is() )
+ {
+ OSL_ENSURE( mxAnnotationData->mxShapes.get() == xShapes.get(), "ScXMLTableRowCellContext::SetAnnotation - different drawing pages" );
+ SdrObject* pObject = SdrObject::getSdrObjectFromXShape(mxAnnotationData->mxShape);
+ OSL_ENSURE( pObject, "ScXMLTableRowCellContext::SetAnnotation - cannot get SdrObject from shape" );
+
+ /* Try to reuse the drawing object already created (but only if the
+ note is visible, and the object is a caption object). */
+ if( mxAnnotationData->mbShown && mxAnnotationData->mbUseShapePos && !comphelper::LibreOfficeKit::isActive())
+ {
+ if( SdrCaptionObj* pCaption = dynamic_cast< SdrCaptionObj* >( pObject ) )
+ {
+ OSL_ENSURE( !pCaption->GetLogicRect().IsEmpty(), "ScXMLTableRowCellContext::SetAnnotation - invalid caption rectangle" );
+ // create the cell note with the caption object
+ pNote = ScNoteUtil::CreateNoteFromCaption( *pDoc, rPos, pCaption );
+ // forget pointer to object (do not create note again below)
+ pObject = nullptr;
+ }
+ }
+
+ // drawing object has not been used to create a note -> use shape data
+ if( pObject )
+ {
+ // rescue settings from drawing object before the shape is removed
+ SfxItemSet aItemSet( pObject->GetMergedItemSet() );
+ std::optional<OutlinerParaObject> pOutlinerObj;
+ if (auto p = pObject->GetOutlinerParaObject())
+ pOutlinerObj = *p;
+ tools::Rectangle aCaptionRect;
+ if( mxAnnotationData->mbUseShapePos )
+ aCaptionRect = pObject->GetLogicRect();
+ // remove the shape from the drawing page, this invalidates pObject
+ mxAnnotationData->mxShapes->remove( mxAnnotationData->mxShape );
+ pObject = nullptr;
+ // update current number of existing objects
+ if( xShapes.is() )
+ nOldShapeCount = xShapes->getCount();
+
+ // an outliner object is required (empty note captions not allowed)
+ if (pOutlinerObj)
+ {
+ // create cell note with all data from drawing object
+ if(!comphelper::LibreOfficeKit::isActive())
+ {
+ pNote = ScNoteUtil::CreateNoteFromObjectData( *pDoc, rPos,
+ std::move(aItemSet), *pOutlinerObj,
+ aCaptionRect, mxAnnotationData->mbShown );
+ }
+ else
+ {
+ pNote = ScNoteUtil::CreateNoteFromObjectData( *pDoc, rPos,
+ std::move(aItemSet), *pOutlinerObj,
+ aCaptionRect, false );
+ }
+
+ }
+ }
+ }
+ else if( !mxAnnotationData->maSimpleText.isEmpty() )
+ {
+ // create note from simple text
+ pNote = ScNoteUtil::CreateNoteFromString( *pDoc, rPos,
+ mxAnnotationData->maSimpleText, mxAnnotationData->mbShown, false );
+ }
+
+ // set author and date
+ if( pNote )
+ {
+ double fDate;
+ if (rXMLImport.GetMM100UnitConverter().convertDateTime(fDate, mxAnnotationData->maCreateDate))
+ {
+ SvNumberFormatter* pNumForm = pDoc->GetFormatTable();
+ sal_uInt32 nfIndex = pNumForm->GetFormatIndex( NF_DATE_SYS_DDMMYYYY, LANGUAGE_SYSTEM );
+ OUString aDate;
+ const Color* pColor = nullptr;
+ pNumForm->GetOutputString( fDate, nfIndex, aDate, &pColor );
+ pNote->SetDate( aDate );
+ }
+ pNote->SetAuthor( mxAnnotationData->maAuthor );
+ }
+
+ // register a shape that has been newly created in the ScNoteUtil functions
+ if( xShapes.is() && (nOldShapeCount < xShapes->getCount()) )
+ {
+ uno::Reference< drawing::XShape > xShape;
+ rXMLImport.GetShapeImport()->shapeWithZIndexAdded( xShape, xShapes->getCount() );
+ }
+
+ // store the style names for stream copying
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(rXMLImport.GetModel())->GetSheetSaveData();
+ pSheetData->HandleNoteStyles( mxAnnotationData->maStyleName, mxAnnotationData->maTextStyle, rPos );
+
+ for (const auto& rContentStyle : mxAnnotationData->maContentStyles)
+ {
+ pSheetData->AddNoteContentStyle( rContentStyle.mnFamily, rContentStyle.maName, rPos, rContentStyle.maSelection );
+ }
+}
+
+// core implementation
+void ScXMLTableRowCellContext::SetDetectiveObj( const ScAddress& rPosition )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if( !pDoc || !cellExists(*pDoc, rPosition) || !pDetectiveObjVec || pDetectiveObjVec->empty() )
+ return;
+
+ LockSolarMutex();
+ ScDetectiveFunc aDetFunc( *pDoc, rPosition.Tab() );
+ uno::Reference<container::XIndexAccess> xShapesIndex = rXMLImport.GetTables().GetCurrentXShapes(); // make draw page
+ for(const auto& rDetectiveObj : *pDetectiveObjVec)
+ {
+ aDetFunc.InsertObject( rDetectiveObj.eObjType, rPosition, rDetectiveObj.aSourceRange, rDetectiveObj.bHasError );
+ if (xShapesIndex.is())
+ {
+ sal_Int32 nShapes = xShapesIndex->getCount();
+ uno::Reference < drawing::XShape > xShape;
+ rXMLImport.GetShapeImport()->shapeWithZIndexAdded(xShape, nShapes);
+ }
+ }
+}
+
+// core implementation
+void ScXMLTableRowCellContext::SetCellRangeSource( const ScAddress& rPosition )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if( !pDoc || !cellExists(*pDoc, rPosition) || !pCellRangeSource || pCellRangeSource->sSourceStr.isEmpty() ||
+ pCellRangeSource->sFilterName.isEmpty() || pCellRangeSource->sURL.isEmpty() )
+ return;
+
+ LockSolarMutex();
+ ScRange aDestRange( rPosition.Col(), rPosition.Row(), rPosition.Tab(),
+ rPosition.Col() + static_cast<SCCOL>(pCellRangeSource->nColumns - 1),
+ rPosition.Row() + static_cast<SCROW>(pCellRangeSource->nRows - 1), rPosition.Tab() );
+ OUString sFilterName( pCellRangeSource->sFilterName );
+ OUString sSourceStr( pCellRangeSource->sSourceStr );
+ ScAreaLink* pLink = new ScAreaLink( pDoc->GetDocumentShell(), pCellRangeSource->sURL,
+ sFilterName, pCellRangeSource->sFilterOptions, sSourceStr, aDestRange, pCellRangeSource->nRefresh );
+ sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
+ pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, pCellRangeSource->sURL, &sFilterName, &sSourceStr );
+}
+
+void ScXMLTableRowCellContext::SetFormulaCell(ScFormulaCell* pFCell) const
+{
+ if(!pFCell)
+ return;
+
+ bool bMayForceNumberformat = true;
+
+ if(mbErrorValue)
+ {
+ // don't do anything here
+ // we need to recalc anyway
+ }
+ else if( bFormulaTextResult && maStringValue )
+ {
+ if( !IsPossibleErrorString() )
+ {
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ pFCell->SetHybridString(pDoc->GetSharedStringPool().intern(*maStringValue));
+ pFCell->ResetDirty();
+ // A General format doesn't force any other format for a string
+ // result, don't attempt to recalculate this later.
+ bMayForceNumberformat = false;
+ }
+ }
+ else if (std::isfinite(fValue))
+ {
+ pFCell->SetHybridDouble(fValue);
+ if (mbPossibleEmptyDisplay && fValue == 0.0)
+ {
+ // Needs to be recalculated to propagate, otherwise would be
+ // propagated as empty string. So don't ResetDirty().
+ pFCell->SetHybridEmptyDisplayedAsString();
+ }
+ else
+ pFCell->ResetDirty();
+ }
+
+ if (bMayForceNumberformat)
+ // Re-calculate to get number format only when style is not set.
+ pFCell->SetNeedNumberFormat(!mbHasStyle);
+}
+
+void ScXMLTableRowCellContext::PutTextCell( const ScAddress& rCurrentPos,
+ const SCCOL nCurrentCol, const ::std::optional< OUString >& pOUText )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ bool bDoIncrement = true;
+ //matrix reference cells that contain text formula results;
+ //cell was already put in document, just need to set text here.
+ if( pDoc && rXMLImport.GetTables().IsPartOfMatrix(rCurrentPos) )
+ {
+ ScRefCellValue aCell(*pDoc, rCurrentPos);
+ bDoIncrement = aCell.meType == CELLTYPE_FORMULA;
+ if ( bDoIncrement )
+ {
+ ScFormulaCell* pFCell = aCell.mpFormula;
+ OUString aCellString;
+ if (maStringValue)
+ aCellString = *maStringValue;
+ else if (mbEditEngineHasText)
+ aCellString = GetFirstParagraph();
+ else if ( nCurrentCol > 0 && pOUText && !pOUText->isEmpty() )
+ aCellString = *pOUText;
+ else
+ bDoIncrement = false;
+
+ if(mbErrorValue)
+ bDoIncrement = false;
+
+ if(!aCellString.isEmpty())
+ {
+ if (bDoIncrement && !IsPossibleErrorString() && pFCell)
+ {
+ pFCell->SetHybridString(pDoc->GetSharedStringPool().intern(aCellString));
+ pFCell->ResetDirty();
+ }
+ else
+ {
+ ScAddress aTopLeftMatrixCell;
+ if (pFCell && pFCell->GetMatrixOrigin(*pDoc, aTopLeftMatrixCell))
+ {
+ ScFormulaCell* pMatrixCell = pDoc->GetFormulaCell(aTopLeftMatrixCell);
+ if (pMatrixCell)
+ pMatrixCell->SetDirty();
+ }
+ else
+ SAL_WARN("sc", "matrix cell without matrix");
+ }
+ }
+ }
+ }
+ else //regular text cells
+ {
+ ScDocumentImport& rDoc = rXMLImport.GetDoc();
+ if (maStringValue)
+ {
+ rDoc.setStringCell(rCurrentPos, *maStringValue);
+ bDoIncrement = true;
+ }
+ else if (mbEditEngineHasText)
+ {
+ if (maFirstParagraph)
+ {
+ // This is a normal text without format runs.
+ rDoc.setStringCell(rCurrentPos, *maFirstParagraph);
+ }
+ else
+ {
+ // This text either has format runs, has field(s), or consists of multiple lines.
+ for (const auto& rxFormat : maFormats)
+ mpEditEngine->QuickSetAttribs(rxFormat->maItemSet, rxFormat->maSelection);
+
+ for (const auto& rxField : maFields)
+ mpEditEngine->QuickInsertField(SvxFieldItem(*rxField->mpData, EE_FEATURE_FIELD), rxField->maSelection);
+
+ // This edit engine uses the SfxItemPool instance returned
+ // from pDoc->GetEditPool() to create the text object, which
+ // is a prerequisite for using this constructor of ScEditCell.
+ rDoc.setEditCell(rCurrentPos, mpEditEngine->CreateTextObject());
+ }
+ bDoIncrement = true;
+ }
+ else if ( nCurrentCol > 0 && pOUText && !pOUText->isEmpty() )
+ {
+ rDoc.setStringCell(rCurrentPos, *pOUText);
+ bDoIncrement = true;
+ }
+ else
+ bDoIncrement = false;
+ }
+
+ // #i56027# This is about setting simple text, not edit cells,
+ // so ProgressBarIncrement must be called with bEditCell = FALSE.
+ // Formatted text that is put into the cell by the child context
+ // is handled in AddCellsToTable() (bIsEmpty is true then).
+ if (bDoIncrement)
+ rXMLImport.ProgressBarIncrement();
+}
+
+void ScXMLTableRowCellContext::PutValueCell( const ScAddress& rCurrentPos )
+{
+ //matrix reference cells that contain value formula results;
+ //cell was already put in document, just need to set value here.
+ if( rXMLImport.GetTables().IsPartOfMatrix(rCurrentPos) )
+ {
+ ScRefCellValue aCell(*rXMLImport.GetDocument(), rCurrentPos);
+ if (aCell.meType == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFCell = aCell.mpFormula;
+ SetFormulaCell(pFCell);
+ if (pFCell)
+ pFCell->SetNeedNumberFormat( true );
+ }
+ }
+ else //regular value cell
+ {
+ // fdo#62250 absent values are not NaN, set to 0.0
+ // PutValueCell() is called only for a known cell value type,
+ // bIsEmpty==false in all these cases, no sense to check it here.
+ if (!std::isfinite( fValue))
+ fValue = 0.0;
+
+ // #i62435# Initialize the value cell's script type if the default
+ // style's number format is latin-only. If the cell uses a different
+ // format, the script type will be reset when the style is applied.
+
+ rXMLImport.GetDoc().setNumericCell(rCurrentPos, fValue);
+ }
+ rXMLImport.ProgressBarIncrement();
+}
+
+namespace {
+
+bool isEmptyOrNote( const ScDocument* pDoc, const ScAddress& rCurrentPos )
+{
+ CellType eType = pDoc->GetCellType(rCurrentPos);
+ return (eType == CELLTYPE_NONE);
+}
+
+}
+
+void ScXMLTableRowCellContext::AddTextAndValueCell( const ScAddress& rCellPos,
+ const ::std::optional< OUString >& pOUText, ScAddress& rCurrentPos )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ ScMyTables& rTables = rXMLImport.GetTables();
+ bool bWasEmpty = bIsEmpty;
+ for (SCCOL i = 0; i < nColsRepeated; ++i)
+ {
+ rCurrentPos.SetCol( rCellPos.Col() + i );
+
+ // it makes no sense to import data after the last supported column
+ // fdo#58539 & gnome#627150
+ if(rCurrentPos.Col() > pDoc->MaxCol())
+ {
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_COLUMN_OVERFLOW);
+ break;
+ }
+
+ if (i > 0)
+ rTables.AddColumn(false);
+ if (!bIsEmpty)
+ {
+ for (SCROW j = 0; j < nRepeatedRows; ++j)
+ {
+ rCurrentPos.SetRow( rCellPos.Row() + j );
+
+ // it makes no sense to import data after last supported row
+ // fdo#58539 & gnome#627150
+ if(rCurrentPos.Row() > pDoc->MaxRow())
+ {
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_ROW_OVERFLOW);
+ break;
+ }
+
+ if( (rCurrentPos.Col() == 0) && (j > 0) )
+ {
+ rTables.AddRow();
+ rTables.AddColumn(false);
+ }
+ if( cellExists(*pDoc, rCurrentPos) )
+ {
+ if( !bIsCovered || isEmptyOrNote(pDoc, rCurrentPos) )
+ {
+ switch (nCellType)
+ {
+ case util::NumberFormat::TEXT:
+ {
+ PutTextCell( rCurrentPos, i, pOUText );
+ }
+ break;
+ case util::NumberFormat::NUMBER:
+ case util::NumberFormat::PERCENT:
+ case util::NumberFormat::CURRENCY:
+ case util::NumberFormat::TIME:
+ case util::NumberFormat::DATETIME:
+ case util::NumberFormat::LOGICAL:
+ {
+ PutValueCell( rCurrentPos );
+ }
+ break;
+ default:
+ {
+ OSL_FAIL("no cell type given");
+ }
+ break;
+ }
+ }
+
+ SetAnnotation( rCurrentPos );
+ SetDetectiveObj( rCurrentPos );
+ SetCellRangeSource( rCurrentPos );
+ }
+ else
+ {
+ if (!bWasEmpty || mxAnnotationData)
+ {
+ if (rCurrentPos.Row() > pDoc->MaxRow())
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_ROW_OVERFLOW);
+ else
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_COLUMN_OVERFLOW);
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((i == 0) && (rCellPos.Col() == 0))
+ {
+ for (sal_Int32 j = 1; j < nRepeatedRows; ++j)
+ {
+ rTables.AddRow();
+ rTables.AddColumn(false);
+ }
+ }
+ }
+ }
+}
+
+bool ScXMLTableRowCellContext::CellsAreRepeated() const
+{
+ return ( (nColsRepeated > 1) || (nRepeatedRows > 1) );
+}
+
+namespace {
+
+// from ScCellObj::GetOutputString_Imp(). all of it may not be necessary.
+OUString getOutputString( ScDocument* pDoc, const ScAddress& aCellPos )
+{
+ if (!pDoc)
+ return OUString();
+
+ ScRefCellValue aCell(*pDoc, aCellPos);
+ switch (aCell.meType)
+ {
+ case CELLTYPE_NONE:
+ return OUString();
+ case CELLTYPE_EDIT:
+ {
+ // GetString on EditCell replaces linebreaks with spaces;
+ // however here we need line breaks
+ const EditTextObject* pData = aCell.mpEditText;
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ rEngine.SetText(*pData);
+ return rEngine.GetText();
+ // also don't format EditCells per NumberFormatter
+ }
+ break;
+ default:
+ {
+ // like in GetString for document (column)
+ const Color* pColor;
+ sal_uInt32 nNumFmt = pDoc->GetNumberFormat(aCellPos);
+ return ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pDoc->GetFormatTable(), *pDoc);
+ }
+ }
+}
+
+}
+
+void ScXMLTableRowCellContext::AddNonFormulaCell( const ScAddress& rCellPos )
+{
+ ::std::optional< OUString > pOUText;
+
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if( nCellType == util::NumberFormat::TEXT )
+ {
+ if( !bIsEmpty && !maStringValue && !mbEditEngineHasText && cellExists(*pDoc, rCellPos) && CellsAreRepeated() )
+ pOUText = getOutputString(pDoc, rCellPos);
+
+ if (!mbEditEngineHasText && !pOUText && !maStringValue)
+ bIsEmpty = true;
+ }
+
+ ScAddress aCurrentPos( rCellPos );
+ if( mxAnnotationData || pDetectiveObjVec || pCellRangeSource ) // has special content
+ bIsEmpty = false;
+
+ AddTextAndValueCell( rCellPos, pOUText, aCurrentPos );
+
+ if( CellsAreRepeated() )
+ {
+ SCCOL nStartCol( std::min(rCellPos.Col(), pDoc->MaxCol()) );
+ SCROW nStartRow( std::min(rCellPos.Row(), pDoc->MaxRow()) );
+ SCCOL nEndCol( std::min<SCCOL>(rCellPos.Col() + nColsRepeated - 1, pDoc->MaxCol()) );
+ SCROW nEndRow( std::min(rCellPos.Row() + nRepeatedRows - 1, pDoc->MaxRow()) );
+ ScRange aScRange( nStartCol, nStartRow, rCellPos.Tab(), nEndCol, nEndRow, rCellPos.Tab() );
+ SetContentValidation( aScRange );
+ rXMLImport.GetStylesImportHelper()->AddRange( aScRange );
+ }
+ else if( cellExists(*pDoc, rCellPos) )
+ {
+ rXMLImport.GetStylesImportHelper()->AddCell(rCellPos);
+ SetContentValidation( rCellPos );
+ }
+}
+
+void ScXMLTableRowCellContext::PutFormulaCell( const ScAddress& rCellPos )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ ScDocumentImport& rDocImport = rXMLImport.GetDoc();
+
+ OUString aText = maFormula->first;
+
+ ScExternalRefManager::ApiGuard aExtRefGuard(*pDoc);
+
+ if ( aText.isEmpty() )
+ return;
+
+ // temporary formula string as string tokens
+ std::unique_ptr<ScTokenArray> pCode(new ScTokenArray(*pDoc));
+
+ // Check the special case of a single error constant without leading
+ // '=' and create an error formula cell without tokens.
+ FormulaError nError = GetScImport().GetFormulaErrorConstant(aText);
+ if (nError != FormulaError::NONE)
+ {
+ pCode->SetCodeError(nError);
+ }
+ else
+ {
+ // 5.2 and earlier wrote broken "Err:xxx" as formula to designate
+ // an error formula cell.
+ if (aText.startsWithIgnoreAsciiCase("Err:") && aText.getLength() <= 9 &&
+ ((nError =
+ GetScImport().GetFormulaErrorConstant( OUString::Concat("#ERR") + aText.subView(4) + "!")) != FormulaError::NONE))
+ {
+ pCode->SetCodeError(nError);
+ }
+ else
+ {
+ OUString aFormulaNmsp = maFormula->second;
+ if( eGrammar != formula::FormulaGrammar::GRAM_EXTERNAL )
+ aFormulaNmsp.clear();
+ pCode->AssignXMLString( aText, aFormulaNmsp );
+ rDocImport.getDoc().IncXMLImportedFormulaCount( aText.getLength() );
+ }
+ }
+
+ ScFormulaCell* pNewCell = new ScFormulaCell(*pDoc, rCellPos, std::move(pCode), eGrammar, ScMatrixMode::NONE);
+ SetFormulaCell(pNewCell);
+ rDocImport.setFormulaCell(rCellPos, pNewCell);
+}
+
+void ScXMLTableRowCellContext::AddFormulaCell( const ScAddress& rCellPos )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if( cellExists(*pDoc, rCellPos) )
+ {
+ SetContentValidation( rCellPos );
+ SAL_WARN_IF((nColsRepeated != 1) || (nRepeatedRows != 1), "sc", "repeated cells with formula not possible now");
+ rXMLImport.GetStylesImportHelper()->AddCell(rCellPos);
+
+ //add matrix
+ if(bIsMatrix)
+ {
+ if (nMatrixCols > 0 && nMatrixRows > 0)
+ {
+ //matrix cells are put in the document, but we must set the
+ //value/text of each matrix cell later
+ rXMLImport.GetTables().AddMatrixRange(
+ rCellPos.Col(), rCellPos.Row(),
+ std::min<SCCOL>(rCellPos.Col() + nMatrixCols - 1, pDoc->MaxCol()),
+ std::min<SCROW>(rCellPos.Row() + nMatrixRows - 1, pDoc->MaxRow()),
+ maFormula->first, maFormula->second, eGrammar);
+
+ // Set the value/text of the top-left matrix position in its
+ // cached result. For import, we only need to set the correct
+ // matrix geometry and the value type of the top-left element.
+ ScFormulaCell* pFCell = pDoc->GetFormulaCell(rCellPos);
+ if (pFCell)
+ {
+ ScMatrixRef pMat(new ScMatrix(nMatrixCols, nMatrixRows));
+ if (bFormulaTextResult && maStringValue)
+ {
+ if (!IsPossibleErrorString())
+ {
+ pFCell->SetResultMatrix(
+ nMatrixCols, nMatrixRows, pMat, new formula::FormulaStringToken(
+ pDoc->GetSharedStringPool().intern( *maStringValue)));
+ pFCell->ResetDirty();
+ }
+ }
+ else if (std::isfinite(fValue))
+ {
+ pFCell->SetResultMatrix(
+ nMatrixCols, nMatrixRows, pMat, new formula::FormulaDoubleToken(fValue));
+ pFCell->ResetDirty();
+ }
+ }
+ }
+ }
+ else
+ PutFormulaCell( rCellPos );
+
+ SetAnnotation( rCellPos );
+ SetDetectiveObj( rCellPos );
+ SetCellRangeSource( rCellPos );
+ rXMLImport.ProgressBarIncrement();
+ }
+ else
+ {
+ if (rCellPos.Row() > pDoc->MaxRow())
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_ROW_OVERFLOW);
+ else
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_COLUMN_OVERFLOW);
+ }
+}
+
+//There are cases where a formula cell is exported with an office:value of 0 or
+//no office:value at all, but the formula cell will have a text:p value which
+//contains the intended formula result.
+//These cases include when a formula result:
+// - is blank
+// - has a constant error value beginning with "#" (such as "#VALUE!" or "#N/A")
+// - has an "Err:[###]" (where "[###]" is an error number)
+// Libreoffice 4.1+ with ODF1.2 extended write however calcext:value-type="error" in that case
+void ScXMLTableRowCellContext::HasSpecialCaseFormulaText()
+{
+ if (!mbEditEngineHasText)
+ return;
+
+ const OUString aStr = GetFirstParagraph();
+
+ if (mbNewValueType)
+ {
+ if (aStr.isEmpty())
+ mbPossibleEmptyDisplay = true;
+ return;
+ }
+
+ if (aStr.isEmpty())
+ {
+ mbPossibleErrorCell = true;
+ mbPossibleEmptyDisplay = true;
+ }
+ else if (aStr.startsWith("Err:"))
+ mbPossibleErrorCell = true;
+ else if (aStr.startsWith("#"))
+ mbCheckWithCompilerForError = true;
+}
+
+bool ScXMLTableRowCellContext::IsPossibleErrorString() const
+{
+ if(mbNewValueType && !mbErrorValue)
+ return false;
+ else if(mbNewValueType && mbErrorValue)
+ return true;
+ return mbPossibleErrorCell || (mbCheckWithCompilerForError && maStringValue &&
+ GetScImport().GetFormulaErrorConstant(*maStringValue) != FormulaError::NONE);
+}
+
+void SAL_CALL ScXMLTableRowCellContext::endFastElement(sal_Int32 /*nElement*/)
+{
+ HasSpecialCaseFormulaText();
+ if( bFormulaTextResult && (mbPossibleErrorCell || mbCheckWithCompilerForError) )
+ {
+ maStringValue = GetFirstParagraph();
+ }
+
+ ScAddress aCellPos = rXMLImport.GetTables().GetCurrentCellPos();
+ if( aCellPos.Col() > 0 && nRepeatedRows > 1 )
+ aCellPos.SetRow( aCellPos.Row() - (nRepeatedRows - 1) );
+ if( bIsMerged )
+ DoMerge( aCellPos, nMergedCols - 1, nMergedRows - 1 );
+
+ if (maFormula)
+ AddFormulaCell(aCellPos);
+ else
+ AddNonFormulaCell(aCellPos);
+
+ //if LockSolarMutex got used, we presumably need to ensure an UnlockSolarMutex
+ if (bSolarMutexLocked)
+ {
+ GetScImport().UnlockSolarMutex();
+ bSolarMutexLocked = false;
+ }
+
+ bIsMerged = false;
+ nMergedCols = 1;
+ nMergedRows = 1;
+ nColsRepeated = 1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */