diff options
Diffstat (limited to 'sc/source/filter/xml/xmlcelli.cxx')
-rw-r--r-- | sc/source/filter/xml/xmlcelli.cxx | 1521 |
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: */ |