tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / filter / xml / xmlcelli.cxx
blob84c0409d1f45d9b67a2cbb9ce7d46275824270eb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <limits>
21 #include <memory>
22 #include "xmlcelli.hxx"
23 #include "xmlimprt.hxx"
24 #include "xmlannoi.hxx"
25 #include <global.hxx>
26 #include <cellvalue.hxx>
27 #include <document.hxx>
28 #include <docuno.hxx>
29 #include <docsh.hxx>
30 #include <postit.hxx>
31 #include <sheetdata.hxx>
32 #include <cellform.hxx>
33 #include <validat.hxx>
34 #include <patattr.hxx>
35 #include <scitems.hxx>
36 #include <docpool.hxx>
38 #include "XMLTableShapeImportHelper.hxx"
39 #include "XMLStylesImportHelper.hxx"
40 #include "celltextparacontext.hxx"
41 #include "XMLCellRangeSourceContext.hxx"
43 #include <arealink.hxx>
44 #include <sfx2/linkmgr.hxx>
45 #include <scerrors.hxx>
46 #include <editutil.hxx>
47 #include <formulacell.hxx>
48 #include "editattributemap.hxx"
49 #include <tokenarray.hxx>
50 #include <scmatrix.hxx>
51 #include <stringutil.hxx>
52 #include <documentimport.hxx>
53 #include <externalrefmgr.hxx>
55 #include <xmloff/maptype.hxx>
56 #include <xmloff/xmltoken.hxx>
57 #include <xmloff/xmlprmap.hxx>
58 #include <xmloff/xmluconv.hxx>
59 #include <xmloff/families.hxx>
60 #include <xmloff/xmlnamespace.hxx>
61 #include <xmloff/prstylei.hxx>
62 #include <xmloff/xmlimppr.hxx>
63 #include <svl/numformat.hxx>
64 #include <svl/zforlist.hxx>
65 #include <svx/svdocapt.hxx>
66 #include <editeng/eeitem.hxx>
67 #include <editeng/outlobj.hxx>
68 #include <editeng/wghtitem.hxx>
69 #include <editeng/colritem.hxx>
70 #include <editeng/fhgtitem.hxx>
71 #include <editeng/postitem.hxx>
72 #include <editeng/flditem.hxx>
73 #include <editeng/fontitem.hxx>
74 #include <editeng/udlnitem.hxx>
75 #include <editeng/wrlmitem.hxx>
76 #include <editeng/crossedoutitem.hxx>
77 #include <editeng/charreliefitem.hxx>
78 #include <editeng/charscaleitem.hxx>
79 #include <editeng/contouritem.hxx>
80 #include <editeng/shdditem.hxx>
81 #include <editeng/kernitem.hxx>
82 #include <editeng/autokernitem.hxx>
83 #include <editeng/escapementitem.hxx>
84 #include <editeng/emphasismarkitem.hxx>
85 #include <editeng/langitem.hxx>
86 #include <svl/sharedstringpool.hxx>
87 #include <sax/tools/converter.hxx>
88 #include <sax/fastattribs.hxx>
90 #include <com/sun/star/util/NumberFormat.hpp>
92 #include <com/sun/star/sheet/ValidationType.hpp>
93 #include <com/sun/star/sheet/ValidationAlertStyle.hpp>
95 #include <rtl/ustrbuf.hxx>
96 #include <osl/diagnose.h>
97 #include <sal/log.hxx>
98 #include <i18nlangtag/lang.h>
100 #include <comphelper/servicehelper.hxx>
101 #include <comphelper/lok.hxx>
103 using namespace com::sun::star;
104 using namespace xmloff::token;
106 ScXMLTableRowCellContext::ParaFormat::ParaFormat(const ScEditEngineDefaulter& rEditEngine) :
107 maItemSet(rEditEngine.GetEmptyItemSet()) {}
109 ScXMLTableRowCellContext::Field::Field(std::unique_ptr<SvxFieldData> pData) : mpData(std::move(pData)) {}
111 ScXMLTableRowCellContext::Field::~Field()
115 ScXMLTableRowCellContext::ScXMLTableRowCellContext( ScXMLImport& rImport,
116 const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
117 const bool bTempIsCovered,
118 const sal_Int32 nTempRepeatedRows ) :
119 ScXMLImportContext( rImport ),
120 mpEditEngine(GetScImport().GetEditEngine()),
121 mnCurParagraph(0),
122 fValue(std::numeric_limits<double>::quiet_NaN()),
123 nMergedRows(1),
124 nMatrixRows(0),
125 nRepeatedRows(nTempRepeatedRows),
126 nMergedCols(1),
127 nMatrixCols(0),
128 nColsRepeated(1),
129 rXMLImport(rImport),
130 eGrammar( formula::FormulaGrammar::GRAM_STORAGE_DEFAULT),
131 nCellType(util::NumberFormat::TEXT),
132 bIsMerged(false),
133 bIsMatrix(false),
134 bIsCovered(bTempIsCovered),
135 bIsEmpty(true),
136 mbNewValueType(false),
137 mbErrorValue(false),
138 bSolarMutexLocked(false),
139 bFormulaTextResult(false),
140 mbPossibleErrorCell(false),
141 mbCheckWithCompilerForError(false),
142 mbEditEngineHasText(false),
143 mbHasFormatRuns(false),
144 mbHasStyle(false),
145 mbPossibleEmptyDisplay(false)
147 rXMLImport.GetTables().AddColumn(bTempIsCovered);
149 std::optional<OUString> xStyleName;
150 std::optional<OUString> xCurrencySymbol;
151 if ( rAttrList.is() )
153 for (auto &it : *rAttrList)
155 switch ( it.getToken() )
157 case XML_ELEMENT( TABLE, XML_STYLE_NAME ):
158 xStyleName = it.toString();
159 mbHasStyle = true;
160 break;
161 case XML_ELEMENT( TABLE, XML_CONTENT_VALIDATION_NAME ):
162 OSL_ENSURE(!maContentValidationName, "here should be only one Validation Name");
163 if (!it.isEmpty())
164 maContentValidationName = it.toString();
165 break;
166 case XML_ELEMENT( TABLE, XML_NUMBER_ROWS_SPANNED ):
167 bIsMerged = true;
168 nMergedRows = static_cast<SCROW>(it.toInt32());
169 break;
170 case XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_SPANNED ):
171 bIsMerged = true;
172 nMergedCols = static_cast<SCCOL>(it.toInt32());
173 break;
174 case XML_ELEMENT( TABLE, XML_NUMBER_MATRIX_COLUMNS_SPANNED ):
175 bIsMatrix = true;
176 nMatrixCols = static_cast<SCCOL>(it.toInt32());
177 break;
178 case XML_ELEMENT( TABLE, XML_NUMBER_MATRIX_ROWS_SPANNED ):
179 bIsMatrix = true;
180 nMatrixRows = static_cast<SCROW>(it.toInt32());
181 break;
182 case XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_REPEATED ):
184 if (ScDocument* pDoc = rImport.GetDocument())
186 nColsRepeated = static_cast<SCCOL>(
187 std::min<sal_Int32>( pDoc->GetSheetLimits().GetMaxColCount(),
188 std::max( it.toInt32(), static_cast<sal_Int32>(1) ) ));
191 break;
192 case XML_ELEMENT( OFFICE, XML_VALUE_TYPE ):
193 nCellType = ScXMLImport::GetCellType(it.toCString(), it.getLength());
194 bIsEmpty = false;
195 break;
196 case XML_ELEMENT( CALC_EXT, XML_VALUE_TYPE ):
197 if(it.isString( "error" ) )
198 mbErrorValue = true;
199 else
200 nCellType = ScXMLImport::GetCellType(it.toCString(), it.getLength());
201 bIsEmpty = false;
202 mbNewValueType = true;
203 break;
204 case XML_ELEMENT( OFFICE, XML_VALUE ):
206 if (!it.isEmpty())
208 fValue = it.toDouble();
209 bIsEmpty = false;
211 //if office:value="0", let's get the text:p in case this is
212 //a special case in HasSpecialCaseFormulaText(). If it
213 //turns out not to be a special case, we'll use the 0 value.
214 if(fValue == 0.0)
215 bFormulaTextResult = true;
218 break;
219 case XML_ELEMENT( OFFICE, XML_DATE_VALUE ):
221 if (!it.isEmpty() && rXMLImport.SetNullDateOnUnitConverter())
223 rXMLImport.GetMM100UnitConverter().convertDateTime(fValue, it.toView());
224 bIsEmpty = false;
227 break;
228 case XML_ELEMENT( OFFICE, XML_TIME_VALUE ):
230 if (!it.isEmpty())
232 ::sax::Converter::convertDuration(fValue, it.toView());
233 bIsEmpty = false;
236 break;
237 case XML_ELEMENT( OFFICE, XML_STRING_VALUE ):
239 if (!it.isEmpty())
241 OSL_ENSURE(!maStringValue, "here should be only one string value");
242 maStringValue = it.toString();
243 bIsEmpty = false;
246 break;
247 case XML_ELEMENT( OFFICE , XML_BOOLEAN_VALUE ):
249 if (!it.isEmpty())
251 if ( IsXMLToken( it, XML_TRUE ) )
252 fValue = 1.0;
253 else if ( IsXMLToken( it, XML_FALSE ) )
254 fValue = 0.0;
255 else
256 fValue = it.toDouble();
257 bIsEmpty = false;
260 break;
261 case XML_ELEMENT( TABLE, XML_FORMULA ):
263 if (!it.isEmpty())
265 OSL_ENSURE(!maFormula, "here should be only one formula");
266 OUString aFormula, aFormulaNmsp;
267 rXMLImport.ExtractFormulaNamespaceGrammar( aFormula, aFormulaNmsp, eGrammar, it.toString() );
268 maFormula = FormulaWithNamespace(aFormula, aFormulaNmsp);
271 break;
272 case XML_ELEMENT( OFFICE, XML_CURRENCY ):
273 xCurrencySymbol = it.toString();
274 break;
275 default:
281 if (maFormula)
283 if (nCellType == util::NumberFormat::TEXT)
284 bFormulaTextResult = true;
285 if(nCellType == util::NumberFormat::DATETIME)
286 nCellType = util::NumberFormat::UNDEFINED;
287 //if bIsEmpty is true at this point, then there is no office value.
288 //we must get the text:p (even if it is empty) in case this a special
289 //case in HasSpecialCaseFormulaText().
290 if(bIsEmpty)
291 bFormulaTextResult = true;
293 rXMLImport.GetStylesImportHelper()->SetAttributes(std::move(xStyleName), std::move(xCurrencySymbol), nCellType);
296 ScXMLTableRowCellContext::~ScXMLTableRowCellContext()
300 void ScXMLTableRowCellContext::LockSolarMutex()
302 if (!bSolarMutexLocked)
304 GetScImport().LockSolarMutex();
305 bSolarMutexLocked = true;
309 namespace {
311 bool cellExists( const ScDocument& rDoc, const ScAddress& rCellPos )
313 return( rCellPos.Col() >= 0 && rCellPos.Row() >= 0 &&
314 rCellPos.Col() <= rDoc.MaxCol() && rCellPos.Row() <= rDoc.MaxRow() );
319 void ScXMLTableRowCellContext::PushParagraphSpan(std::u16string_view rSpan, const OUString& rStyleName)
321 sal_Int32 nBegin = maParagraph.getLength();
322 sal_Int32 nEnd = nBegin + rSpan.size();
323 maParagraph.append(rSpan);
325 PushFormat(nBegin, nEnd, rStyleName);
328 void ScXMLTableRowCellContext::PushParagraphField(std::unique_ptr<SvxFieldData> pData, const OUString& rStyleName)
330 mbHasFormatRuns = true;
331 maFields.push_back(std::make_unique<Field>(std::move(pData)));
332 Field& rField = *maFields.back();
334 sal_Int32 nPos = maParagraph.getLength();
335 maParagraph.append('\1'); // Placeholder text for inserted field item.
336 rField.maSelection.start.nPara = rField.maSelection.end.nPara = mnCurParagraph;
337 rField.maSelection.start.nIndex = nPos;
338 rField.maSelection.end.nIndex = nPos+1;
340 PushFormat(nPos, nPos+1, rStyleName);
343 void ScXMLTableRowCellContext::PushFormat(sal_Int32 nBegin, sal_Int32 nEnd, const OUString& rStyleName)
345 if (rStyleName.isEmpty())
346 return;
348 // Get the style information from xmloff.
349 rtl::Reference<XMLPropertySetMapper> xMapper = GetImport().GetTextImport()->GetTextImportPropertySetMapper()->getPropertySetMapper();
350 if (!xMapper.is())
351 // We can't do anything without the mapper.
352 return;
354 sal_Int32 nEntryCount = xMapper->GetEntryCount();
356 SvXMLStylesContext* pAutoStyles = GetImport().GetAutoStyles();
357 if (!pAutoStyles)
358 return;
360 // Style name for text span corresponds with the name of an automatic style.
361 const XMLPropStyleContext* pStyle = dynamic_cast<const XMLPropStyleContext*>(
362 pAutoStyles->FindStyleChildContext(XmlStyleFamily::TEXT_TEXT, rStyleName));
364 if (!pStyle)
365 // No style by that name found.
366 return;
368 const std::vector<XMLPropertyState>& rProps = pStyle->GetProperties();
369 if (rProps.empty())
370 return;
372 const ScXMLEditAttributeMap& rEditAttrMap = GetScImport().GetEditAttributeMap();
374 mbHasFormatRuns = true;
375 maFormats.push_back(std::make_unique<ParaFormat>(*mpEditEngine));
376 ParaFormat& rFmt = *maFormats.back();
377 rFmt.maSelection.start.nPara = rFmt.maSelection.end.nPara = mnCurParagraph;
378 rFmt.maSelection.start.nIndex = nBegin;
379 rFmt.maSelection.end.nIndex = nEnd;
381 // Store the used text styles for export.
382 ScSheetSaveData* pSheetData = rXMLImport.GetScModel()->GetSheetSaveData();
383 ScAddress aCellPos = rXMLImport.GetTables().GetCurrentCellPos();
384 pSheetData->AddTextStyle(rStyleName, aCellPos, rFmt.maSelection);
386 std::unique_ptr<SfxPoolItem> pPoolItem;
387 sal_uInt16 nLastItemID = EE_CHAR_END + 1;
389 for (const auto& rProp : rProps)
391 if (rProp.mnIndex == -1 || rProp.mnIndex >= nEntryCount)
392 continue;
394 const OUString& rName = xMapper->GetEntryAPIName(rProp.mnIndex);
395 const ScXMLEditAttributeMap::Entry* pEntry = rEditAttrMap.getEntryByAPIName(rName);
396 if (!pEntry)
397 continue;
399 if (nLastItemID != pEntry->mnItemID && pPoolItem)
401 // Flush the last item when the item ID changes.
402 rFmt.maItemSet.Put(std::move(pPoolItem));
405 switch (pEntry->mnItemID)
407 case EE_CHAR_FONTINFO:
408 case EE_CHAR_FONTINFO_CJK:
409 case EE_CHAR_FONTINFO_CTL:
411 // Font properties need to be consolidated into a single item.
412 if (!pPoolItem)
413 pPoolItem.reset(new SvxFontItem(pEntry->mnItemID));
415 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
417 break;
418 case EE_CHAR_WEIGHT:
419 case EE_CHAR_WEIGHT_CJK:
420 case EE_CHAR_WEIGHT_CTL:
422 if (!pPoolItem)
423 pPoolItem.reset(new SvxWeightItem(WEIGHT_NORMAL, pEntry->mnItemID));
425 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
427 break;
428 case EE_CHAR_FONTHEIGHT:
429 case EE_CHAR_FONTHEIGHT_CJK:
430 case EE_CHAR_FONTHEIGHT_CTL:
432 if (!pPoolItem)
433 pPoolItem.reset(new SvxFontHeightItem(240, 100, pEntry->mnItemID));
435 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
437 break;
438 case EE_CHAR_ITALIC:
439 case EE_CHAR_ITALIC_CJK:
440 case EE_CHAR_ITALIC_CTL:
442 if (!pPoolItem)
443 pPoolItem.reset(new SvxPostureItem(ITALIC_NONE, pEntry->mnItemID));
445 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
447 break;
448 case EE_CHAR_UNDERLINE:
450 if (!pPoolItem)
451 pPoolItem.reset(new SvxUnderlineItem(LINESTYLE_NONE, pEntry->mnItemID));
453 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
455 break;
456 case EE_CHAR_OVERLINE:
458 if (!pPoolItem)
459 pPoolItem.reset(new SvxOverlineItem(LINESTYLE_NONE, pEntry->mnItemID));
461 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
463 break;
464 case EE_CHAR_COLOR:
466 if (!pPoolItem)
467 pPoolItem.reset(new SvxColorItem(pEntry->mnItemID));
469 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
471 break;
472 case EE_CHAR_WLM:
474 if (!pPoolItem)
475 pPoolItem.reset(new SvxWordLineModeItem(false, pEntry->mnItemID));
477 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
479 break;
480 case EE_CHAR_STRIKEOUT:
482 if (!pPoolItem)
483 pPoolItem.reset(new SvxCrossedOutItem(STRIKEOUT_NONE, pEntry->mnItemID));
485 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
487 break;
488 case EE_CHAR_RELIEF:
490 if (!pPoolItem)
491 pPoolItem.reset(new SvxCharReliefItem(FontRelief::NONE, pEntry->mnItemID));
493 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
495 break;
496 case EE_CHAR_OUTLINE:
498 if (!pPoolItem)
499 pPoolItem.reset(new SvxContourItem(false, pEntry->mnItemID));
501 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
503 break;
504 case EE_CHAR_SHADOW:
506 if (!pPoolItem)
507 pPoolItem.reset(new SvxShadowedItem(false, pEntry->mnItemID));
509 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
511 break;
512 case EE_CHAR_KERNING:
514 if (!pPoolItem)
515 pPoolItem.reset(new SvxKerningItem(0, pEntry->mnItemID));
517 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
519 break;
520 case EE_CHAR_PAIRKERNING:
522 if (!pPoolItem)
523 pPoolItem.reset(new SvxAutoKernItem(false, pEntry->mnItemID));
525 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
527 break;
528 case EE_CHAR_FONTWIDTH:
530 if (!pPoolItem)
531 pPoolItem.reset(new SvxCharScaleWidthItem(100, TypedWhichId<SvxCharScaleWidthItem>(pEntry->mnItemID)));
533 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
535 break;
536 case EE_CHAR_ESCAPEMENT:
538 if (!pPoolItem)
539 pPoolItem.reset(new SvxEscapementItem(pEntry->mnItemID));
541 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
543 break;
544 case EE_CHAR_EMPHASISMARK:
546 if (!pPoolItem)
547 pPoolItem.reset(new SvxEmphasisMarkItem(FontEmphasisMark::NONE, TypedWhichId<SvxEmphasisMarkItem>(pEntry->mnItemID)));
549 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
551 break;
552 case EE_CHAR_LANGUAGE:
553 case EE_CHAR_LANGUAGE_CJK:
554 case EE_CHAR_LANGUAGE_CTL:
556 if (!pPoolItem)
557 pPoolItem.reset(new SvxLanguageItem(LANGUAGE_DONTKNOW, pEntry->mnItemID));
559 pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
561 break;
562 default:
566 nLastItemID = pEntry->mnItemID;
569 if (pPoolItem)
570 rFmt.maItemSet.Put(std::move(pPoolItem));
573 OUString ScXMLTableRowCellContext::GetFirstParagraph() const
575 if (!maFirstParagraph)
576 return mpEditEngine->GetText(0);
578 return *maFirstParagraph;
581 void ScXMLTableRowCellContext::PushParagraphFieldDate(const OUString& rStyleName)
583 PushParagraphField(std::make_unique<SvxDateField>(), rStyleName);
586 void ScXMLTableRowCellContext::PushParagraphFieldSheetName(const OUString& rStyleName)
588 SCTAB nTab = GetScImport().GetTables().GetCurrentCellPos().Tab();
589 PushParagraphField(std::make_unique<SvxTableField>(nTab), rStyleName);
592 void ScXMLTableRowCellContext::PushParagraphFieldDocTitle(const OUString& rStyleName)
594 PushParagraphField(std::make_unique<SvxFileField>(), rStyleName);
597 void ScXMLTableRowCellContext::PushParagraphFieldURL(
598 const OUString& rURL, const OUString& rRep, const OUString& rStyleName, const OUString& rTargetFrame)
600 OUString aAbsURL = GetScImport().GetAbsoluteReference(rURL);
601 std::unique_ptr<SvxURLField> pURLField(new SvxURLField(aAbsURL, rRep, SvxURLFormat::Repr));
602 pURLField->SetTargetFrame(rTargetFrame);
603 PushParagraphField(std::move(pURLField), rStyleName);
606 void ScXMLTableRowCellContext::PushParagraphEnd()
608 // EditEngine always has at least one paragraph even when its content is empty.
610 if (mbEditEngineHasText)
612 if (maFirstParagraph)
614 // Flush the cached first paragraph first.
615 mpEditEngine->Clear();
616 mpEditEngine->SetTextCurrentDefaults(*maFirstParagraph);
617 maFirstParagraph.reset();
619 mpEditEngine->InsertParagraph(mpEditEngine->GetParagraphCount(), maParagraph.makeStringAndClear());
621 else if (mbHasFormatRuns || ScStringUtil::isMultiline(maParagraph))
623 mpEditEngine->Clear();
624 mpEditEngine->SetTextCurrentDefaults(maParagraph.makeStringAndClear());
625 mbEditEngineHasText = true;
627 else if (mnCurParagraph == 0)
629 maFirstParagraph = maParagraph.makeStringAndClear();
630 mbEditEngineHasText = true;
633 ++mnCurParagraph;
636 uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLTableRowCellContext::createFastChildContext(
637 sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
639 SvXMLImportContext *pContext = nullptr;
640 sax_fastparser::FastAttributeList *pAttribList =
641 &sax_fastparser::castToFastAttributeList( xAttrList );
643 bool bTextP(false);
644 switch (nElement)
646 case XML_ELEMENT( TEXT, XML_P ):
648 bIsEmpty = false;
649 bTextP = true;
651 pContext = new ScXMLCellTextParaContext(rXMLImport, *this);
653 break;
654 case XML_ELEMENT( TABLE, XML_SUB_TABLE ):
656 SAL_WARN("sc", "ScXMLTableRowCellContext::createFastChildContext: subtables are not supported");
658 break;
659 case XML_ELEMENT( TABLE, XML_DETECTIVE ):
661 bIsEmpty = false;
662 if (!pDetectiveObjVec)
663 pDetectiveObjVec.reset( new ScMyImpDetectiveObjVec );
664 pContext = new ScXMLDetectiveContext(
665 rXMLImport, pDetectiveObjVec.get() );
667 break;
668 case XML_ELEMENT( TABLE, XML_CELL_RANGE_SOURCE ):
670 bIsEmpty = false;
671 if (!pCellRangeSource)
672 pCellRangeSource.reset(new ScMyImpCellRangeSource());
673 pContext = new ScXMLCellRangeSourceContext(
674 rXMLImport, pAttribList, pCellRangeSource.get() );
676 break;
677 case XML_ELEMENT(OFFICE, XML_ANNOTATION):
679 bIsEmpty = false;
680 OSL_ENSURE(
681 !mxAnnotationData,
682 "ScXMLTableRowCellContext::CreateChildContext - multiple annotations in one cell");
683 mxAnnotationData.reset( new ScXMLAnnotationData );
684 pContext = new ScXMLAnnotationContext( rXMLImport, nElement,
685 xAttrList, *mxAnnotationData);
687 break;
690 if (!pContext && !bTextP)
692 ScAddress aCellPos = rXMLImport.GetTables().GetCurrentCellPos();
693 uno::Reference<drawing::XShapes> xShapes (rXMLImport.GetTables().GetCurrentXShapes());
694 if (xShapes.is())
696 if (ScDocument* pDoc = rXMLImport.GetDocument())
698 if (aCellPos.Col() > pDoc->MaxCol())
699 aCellPos.SetCol(pDoc->MaxCol());
700 if (aCellPos.Row() > pDoc->MaxRow())
701 aCellPos.SetRow(pDoc->MaxRow());
702 XMLTableShapeImportHelper* pTableShapeImport =
703 static_cast< XMLTableShapeImportHelper* >( rXMLImport.GetShapeImport().get() );
704 pTableShapeImport->SetOnTable(false);
705 pTableShapeImport->SetCell(aCellPos);
706 pContext = XMLShapeImportHelper::CreateGroupChildContext(
707 rXMLImport, nElement, xAttrList, xShapes);
708 if (pContext)
710 bIsEmpty = false;
711 rXMLImport.ProgressBarIncrement();
717 return pContext;
720 void ScXMLTableRowCellContext::DoMerge( const ScAddress& rScAddress, const SCCOL nCols, const SCROW nRows )
722 SCCOL mergeToCol = rScAddress.Col() + nCols;
723 SCROW mergeToRow = rScAddress.Row() + nRows;
724 if (ScDocument* pDoc = rXMLImport.GetDocument())
726 bool bInBounds = rScAddress.Col() <= pDoc->MaxCol() && rScAddress.Row() <= pDoc->MaxRow() &&
727 mergeToCol <= pDoc->MaxCol() && mergeToRow <= pDoc->MaxRow();
728 if( bInBounds )
730 pDoc->DoMerge( rScAddress.Col(), rScAddress.Row(),
731 mergeToCol, mergeToRow, rScAddress.Tab() );
736 namespace {
738 ScValidationMode validationTypeToMode( const sheet::ValidationType eVType )
740 ScValidationMode eMode;
741 switch( eVType )
743 case sheet::ValidationType_WHOLE: eMode = SC_VALID_WHOLE; break;
744 case sheet::ValidationType_DECIMAL: eMode = SC_VALID_DECIMAL; break;
745 case sheet::ValidationType_DATE: eMode = SC_VALID_DATE; break;
746 case sheet::ValidationType_TIME: eMode = SC_VALID_TIME; break;
747 case sheet::ValidationType_TEXT_LEN: eMode = SC_VALID_TEXTLEN; break;
748 case sheet::ValidationType_LIST: eMode = SC_VALID_LIST; break;
749 case sheet::ValidationType_CUSTOM: eMode = SC_VALID_CUSTOM; break;
750 default: eMode = SC_VALID_ANY; break;
752 return eMode;
755 ScValidErrorStyle validAlertToValidError( const sheet::ValidationAlertStyle eVAlertStyle )
757 ScValidErrorStyle eVErrStyle;
758 switch( eVAlertStyle )
760 case sheet::ValidationAlertStyle_STOP: eVErrStyle = SC_VALERR_STOP; break;
761 case sheet::ValidationAlertStyle_WARNING: eVErrStyle = SC_VALERR_WARNING; break;
762 case sheet::ValidationAlertStyle_MACRO: eVErrStyle = SC_VALERR_MACRO; break;
763 default: eVErrStyle = SC_VALERR_INFO; break;
764 //should INFO be the default? seems to be the most unobtrusive choice.
766 return eVErrStyle;
771 void ScXMLTableRowCellContext::SetContentValidation( const ScRange& rScRange )
773 if (!maContentValidationName)
774 return;
776 ScDocument* pDoc = rXMLImport.GetDocument();
777 if (!pDoc)
778 return;
780 ScMyImportValidation aValidation;
781 aValidation.eGrammar1 = aValidation.eGrammar2 = pDoc->GetStorageGrammar();
782 if( !rXMLImport.GetValidation(*maContentValidationName, aValidation) )
783 return;
785 ScValidationData aScValidationData(
786 validationTypeToMode(aValidation.aValidationType),
787 ScConditionEntry::GetModeFromApi(aValidation.aOperator),
788 aValidation.sFormula1, aValidation.sFormula2, *pDoc, ScAddress(),
789 aValidation.sFormulaNmsp1, aValidation.sFormulaNmsp2,
790 aValidation.eGrammar1, aValidation.eGrammar2
793 aScValidationData.SetIgnoreBlank( aValidation.bIgnoreBlanks );
794 aScValidationData.SetCaseSensitive( aValidation.bCaseSensitive );
795 aScValidationData.SetListType( aValidation.nShowList );
797 // set strings for error / input even if disabled (and disable afterwards)
798 aScValidationData.SetInput( aValidation.sInputTitle, aValidation.sInputMessage );
799 if( !aValidation.bShowInputMessage )
800 aScValidationData.ResetInput();
801 aScValidationData.SetError( aValidation.sErrorTitle, aValidation.sErrorMessage, validAlertToValidError(aValidation.aAlertStyle) );
802 if( !aValidation.bShowErrorMessage )
803 aScValidationData.ResetError();
805 if( !aValidation.sBaseCellAddress.isEmpty() )
806 aScValidationData.SetSrcString( aValidation.sBaseCellAddress );
808 sal_uInt32 nIndex = pDoc->AddValidationEntry( aScValidationData );
810 ScPatternAttr aPattern(pDoc->getCellAttributeHelper());
811 aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nIndex ) );
812 if( rScRange.aStart == rScRange.aEnd ) //for a single cell
814 pDoc->ApplyPattern( rScRange.aStart.Col(), rScRange.aStart.Row(),
815 rScRange.aStart.Tab(), aPattern );
817 else //for repeating cells
819 pDoc->ApplyPatternAreaTab( rScRange.aStart.Col(), rScRange.aStart.Row(),
820 rScRange.aEnd.Col(), rScRange.aEnd.Row(),
821 rScRange.aStart.Tab(), aPattern );
824 // is the below still needed?
825 // For now, any sheet with validity is blocked from stream-copying.
826 // Later, the validation names could be stored along with the style names.
827 ScSheetSaveData* pSheetData = GetScImport().GetScModel()->GetSheetSaveData();
828 pSheetData->BlockSheet( GetScImport().GetTables().GetCurrentSheet() );
831 void ScXMLTableRowCellContext::SetContentValidation( const ScAddress& rCellPos )
833 SetContentValidation( ScRange(rCellPos, rCellPos) );
836 void ScXMLTableRowCellContext::SetAnnotation(const ScAddress& rPos)
838 ScDocument* pDoc = rXMLImport.GetDocument();
839 if (!pDoc || !mxAnnotationData)
840 return;
842 LockSolarMutex();
844 ScPostIt* pNote = nullptr;
846 uno::Reference< drawing::XShapes > xShapes = rXMLImport.GetTables().GetCurrentXShapes();
847 sal_Int32 nOldShapeCount = xShapes.is() ? xShapes->getCount() : 0;
849 OSL_ENSURE( !mxAnnotationData->mxShape.is() || mxAnnotationData->mxShapes.is(),
850 "ScXMLTableRowCellContext::SetAnnotation - shape without drawing page" );
851 if( mxAnnotationData->mxShape.is() && mxAnnotationData->mxShapes.is() )
853 OSL_ENSURE( mxAnnotationData->mxShapes.get() == xShapes.get(), "ScXMLTableRowCellContext::SetAnnotation - different drawing pages" );
855 /* Don't attempt to get the style from the SdrObject,
856 as it might be a default assigned one. */
857 const SvXMLStylesContext* pStylesCtxt = rXMLImport.GetShapeImport()->GetAutoStylesContext();
858 const SvXMLStyleContext* pStyle = pStylesCtxt ? pStylesCtxt->FindStyleChildContext(
859 XmlStyleFamily::SD_GRAPHICS_ID, mxAnnotationData->maStyleName) : nullptr;
860 OUString aStyleName = pStyle ? pStyle->GetParentName() : mxAnnotationData->maStyleName;
861 assert(!pStylesCtxt || !pStylesCtxt->FindStyleChildContext(
862 XmlStyleFamily::SD_GRAPHICS_ID, aStyleName));
863 aStyleName = rXMLImport.GetStyleDisplayName(XmlStyleFamily::SD_GRAPHICS_ID, aStyleName);
865 SdrObject* pObject = SdrObject::getSdrObjectFromXShape(mxAnnotationData->mxShape);
866 OSL_ENSURE( pObject, "ScXMLTableRowCellContext::SetAnnotation - cannot get SdrObject from shape" );
868 /* Try to reuse the drawing object already created (but only if the
869 note is visible, and the object is a caption object). */
870 if( mxAnnotationData->mbShown && mxAnnotationData->mbUseShapePos && !comphelper::LibreOfficeKit::isActive())
872 if( SdrCaptionObj* pCaption = dynamic_cast< SdrCaptionObj* >( pObject ) )
874 OSL_ENSURE( !pCaption->GetLogicRect().IsEmpty(), "ScXMLTableRowCellContext::SetAnnotation - invalid caption rectangle" );
875 // create the cell note with the caption object
876 pNote = ScNoteUtil::CreateNoteFromCaption( *pDoc, rPos, pCaption, !aStyleName.isEmpty() );
877 // forget pointer to object (do not create note again below)
878 pObject = nullptr;
882 // drawing object has not been used to create a note -> use shape data
883 if( pObject )
885 // rescue settings from drawing object before the shape is removed
886 SfxItemSet aItemSet( pObject->GetMergedItemSet() );
887 std::optional<OutlinerParaObject> pOutlinerObj;
888 if (auto p = pObject->GetOutlinerParaObject())
889 pOutlinerObj = *p;
890 tools::Rectangle aCaptionRect;
891 if( mxAnnotationData->mbUseShapePos )
892 aCaptionRect = pObject->GetLogicRect();
893 // remove the shape from the drawing page, this invalidates pObject
894 mxAnnotationData->mxShapes->remove( mxAnnotationData->mxShape );
895 pObject = nullptr;
896 // update current number of existing objects
897 if( xShapes.is() )
898 nOldShapeCount = xShapes->getCount();
900 // an outliner object is required (empty note captions not allowed)
901 if (pOutlinerObj)
903 // create cell note with all data from drawing object
904 if(!comphelper::LibreOfficeKit::isActive())
906 pNote = ScNoteUtil::CreateNoteFromObjectData( *pDoc, rPos,
907 std::move(aItemSet), aStyleName, *pOutlinerObj,
908 aCaptionRect, mxAnnotationData->mbShown );
910 else
912 pNote = ScNoteUtil::CreateNoteFromObjectData( *pDoc, rPos,
913 std::move(aItemSet), aStyleName, *pOutlinerObj,
914 aCaptionRect, false );
920 else if( !mxAnnotationData->maSimpleText.isEmpty() )
922 // create note from simple text
923 pNote = ScNoteUtil::CreateNoteFromString( *pDoc, rPos,
924 mxAnnotationData->maSimpleText, mxAnnotationData->mbShown, false );
927 // set author and date
928 if( pNote )
930 double fDate;
931 if (rXMLImport.GetMM100UnitConverter().convertDateTime(fDate, mxAnnotationData->maCreateDate))
933 SvNumberFormatter* pNumForm = pDoc->GetFormatTable();
935 // Date string is in format ISO 8601 inside <dc:date>
936 // i.e: 2024-08-14 or 2024-08-14T23:55:06 or 20240814T235506
937 // Time always has prefix 'T'
938 sal_uInt32 nfIndex = pNumForm->GetFormatIndex(
939 mxAnnotationData->maCreateDate.indexOf('T') > -1 ? NF_DATETIME_SYS_DDMMYYYY_HHMMSS
940 : NF_DATE_SYS_DDMMYYYY,
941 LANGUAGE_SYSTEM);
942 OUString aDate;
943 const Color* pColor = nullptr;
944 pNumForm->GetOutputString( fDate, nfIndex, aDate, &pColor );
945 pNote->SetDate( aDate );
947 pNote->SetAuthor( mxAnnotationData->maAuthor );
950 // register a shape that has been newly created in the ScNoteUtil functions
951 if( xShapes.is() && (nOldShapeCount < xShapes->getCount()) )
953 uno::Reference< drawing::XShape > xShape;
954 rXMLImport.GetShapeImport()->shapeWithZIndexAdded( xShape, xShapes->getCount() );
957 // store the style names for stream copying
958 ScSheetSaveData* pSheetData = rXMLImport.GetScModel()->GetSheetSaveData();
959 pSheetData->HandleNoteStyles( mxAnnotationData->maStyleName, mxAnnotationData->maTextStyle, rPos );
961 for (const auto& rContentStyle : mxAnnotationData->maContentStyles)
963 pSheetData->AddNoteContentStyle( rContentStyle.mnFamily, rContentStyle.maName, rPos, rContentStyle.maSelection );
967 // core implementation
968 void ScXMLTableRowCellContext::SetDetectiveObj( const ScAddress& rPosition )
970 ScDocument* pDoc = rXMLImport.GetDocument();
971 if( !pDoc || !cellExists(*pDoc, rPosition) || !pDetectiveObjVec || pDetectiveObjVec->empty() )
972 return;
974 LockSolarMutex();
975 ScDetectiveFunc aDetFunc( *pDoc, rPosition.Tab() );
976 uno::Reference<container::XIndexAccess> xShapesIndex = rXMLImport.GetTables().GetCurrentXShapes(); // make draw page
977 for(const auto& rDetectiveObj : *pDetectiveObjVec)
979 aDetFunc.InsertObject( rDetectiveObj.eObjType, rPosition, rDetectiveObj.aSourceRange, rDetectiveObj.bHasError );
980 if (xShapesIndex.is())
982 sal_Int32 nShapes = xShapesIndex->getCount();
983 uno::Reference < drawing::XShape > xShape;
984 rXMLImport.GetShapeImport()->shapeWithZIndexAdded(xShape, nShapes);
989 // core implementation
990 void ScXMLTableRowCellContext::SetCellRangeSource( const ScAddress& rPosition )
992 ScDocument* pDoc = rXMLImport.GetDocument();
993 if( !pDoc || !cellExists(*pDoc, rPosition) || !pCellRangeSource || pCellRangeSource->sSourceStr.isEmpty() ||
994 pCellRangeSource->sFilterName.isEmpty() || pCellRangeSource->sURL.isEmpty() )
995 return;
997 LockSolarMutex();
998 ScRange aDestRange( rPosition.Col(), rPosition.Row(), rPosition.Tab(),
999 rPosition.Col() + static_cast<SCCOL>(pCellRangeSource->nColumns - 1),
1000 rPosition.Row() + static_cast<SCROW>(pCellRangeSource->nRows - 1), rPosition.Tab() );
1001 OUString sFilterName( pCellRangeSource->sFilterName );
1002 OUString sSourceStr( pCellRangeSource->sSourceStr );
1003 ScAreaLink* pLink = new ScAreaLink( pDoc->GetDocumentShell(), pCellRangeSource->sURL,
1004 sFilterName, pCellRangeSource->sFilterOptions, sSourceStr, aDestRange, pCellRangeSource->nRefresh );
1005 sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
1006 pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, pCellRangeSource->sURL, &sFilterName, &sSourceStr );
1009 void ScXMLTableRowCellContext::SetFormulaCell(ScFormulaCell* pFCell) const
1011 if(!pFCell)
1012 return;
1014 bool bMayForceNumberformat = true;
1016 if(mbErrorValue)
1018 // don't do anything here
1019 // we need to recalc anyway
1021 else if( bFormulaTextResult && maStringValue )
1023 if( !IsPossibleErrorString() )
1025 ScDocument* pDoc = rXMLImport.GetDocument();
1026 pFCell->SetHybridString(pDoc->GetSharedStringPool().intern(*maStringValue));
1027 pFCell->ResetDirty();
1028 // A General format doesn't force any other format for a string
1029 // result, don't attempt to recalculate this later.
1030 bMayForceNumberformat = false;
1033 else if (std::isfinite(fValue))
1035 pFCell->SetHybridDouble(fValue);
1036 if (mbPossibleEmptyDisplay && fValue == 0.0)
1038 // Needs to be recalculated to propagate, otherwise would be
1039 // propagated as empty string. So don't ResetDirty().
1040 pFCell->SetHybridEmptyDisplayedAsString();
1042 else
1043 pFCell->ResetDirty();
1046 if (bMayForceNumberformat)
1047 // Re-calculate to get number format only when style is not set.
1048 pFCell->SetNeedNumberFormat(!mbHasStyle);
1051 void ScXMLTableRowCellContext::PutTextCell( const ScAddress& rCurrentPos,
1052 const SCCOL nCurrentCol, const ::std::optional< OUString >& pOUText )
1054 ScDocument* pDoc = rXMLImport.GetDocument();
1055 bool bDoIncrement = true;
1056 //matrix reference cells that contain text formula results;
1057 //cell was already put in document, just need to set text here.
1058 if( pDoc && rXMLImport.GetTables().IsPartOfMatrix(rCurrentPos) )
1060 ScRefCellValue aCell(*pDoc, rCurrentPos);
1061 bDoIncrement = aCell.getType() == CELLTYPE_FORMULA;
1062 if ( bDoIncrement )
1064 ScFormulaCell* pFCell = aCell.getFormula();
1065 OUString aCellString;
1066 if (maStringValue)
1067 aCellString = *maStringValue;
1068 else if (mbEditEngineHasText)
1069 aCellString = GetFirstParagraph();
1070 else if ( nCurrentCol > 0 && pOUText && !pOUText->isEmpty() )
1071 aCellString = *pOUText;
1072 else
1073 bDoIncrement = false;
1075 if(mbErrorValue)
1076 bDoIncrement = false;
1078 if(!aCellString.isEmpty())
1080 if (bDoIncrement && !IsPossibleErrorString() && pFCell)
1082 pFCell->SetHybridString(pDoc->GetSharedStringPool().intern(aCellString));
1083 pFCell->ResetDirty();
1085 else
1087 ScAddress aTopLeftMatrixCell;
1088 if (pFCell && pFCell->GetMatrixOrigin(*pDoc, aTopLeftMatrixCell))
1090 ScFormulaCell* pMatrixCell = pDoc->GetFormulaCell(aTopLeftMatrixCell);
1091 if (pMatrixCell)
1092 pMatrixCell->SetDirty();
1094 else
1095 SAL_WARN("sc", "matrix cell without matrix");
1100 else //regular text cells
1102 ScDocumentImport& rDoc = rXMLImport.GetDoc();
1103 if (maStringValue)
1105 rDoc.setStringCell(rCurrentPos, *maStringValue);
1107 else if (mbEditEngineHasText)
1109 if (maFirstParagraph)
1111 // This is a normal text without format runs.
1112 rDoc.setStringCell(rCurrentPos, *maFirstParagraph);
1114 else
1116 // This text either has format runs, has field(s), or consists of multiple lines.
1117 for (const auto& rxFormat : maFormats)
1118 mpEditEngine->QuickSetAttribs(rxFormat->maItemSet, rxFormat->maSelection);
1120 for (const auto& rxField : maFields)
1121 mpEditEngine->QuickInsertField(SvxFieldItem(*rxField->mpData, EE_FEATURE_FIELD), rxField->maSelection);
1123 // This edit engine uses the SfxItemPool instance returned
1124 // from pDoc->GetEditPool() to create the text object, which
1125 // is a prerequisite for using this constructor of ScEditCell.
1126 rDoc.setEditCell(rCurrentPos, mpEditEngine->CreateTextObject());
1129 else if ( nCurrentCol > 0 && pOUText && !pOUText->isEmpty() )
1131 rDoc.setStringCell(rCurrentPos, *pOUText);
1133 else
1134 bDoIncrement = false;
1137 // #i56027# This is about setting simple text, not edit cells,
1138 // so ProgressBarIncrement must be called with bEditCell = FALSE.
1139 // Formatted text that is put into the cell by the child context
1140 // is handled in AddCellsToTable() (bIsEmpty is true then).
1141 if (bDoIncrement)
1142 rXMLImport.ProgressBarIncrement();
1145 void ScXMLTableRowCellContext::PutValueCell( const ScAddress& rCurrentPos )
1147 //matrix reference cells that contain value formula results;
1148 //cell was already put in document, just need to set value here.
1149 if( rXMLImport.GetTables().IsPartOfMatrix(rCurrentPos) )
1151 ScRefCellValue aCell(*rXMLImport.GetDocument(), rCurrentPos);
1152 if (aCell.getType() == CELLTYPE_FORMULA)
1154 ScFormulaCell* pFCell = aCell.getFormula();
1155 SetFormulaCell(pFCell);
1156 if (pFCell)
1157 pFCell->SetNeedNumberFormat( true );
1160 else //regular value cell
1162 // fdo#62250 absent values are not NaN, set to 0.0
1163 // PutValueCell() is called only for a known cell value type,
1164 // bIsEmpty==false in all these cases, no sense to check it here.
1165 if (!std::isfinite( fValue))
1166 fValue = 0.0;
1168 // #i62435# Initialize the value cell's script type if the default
1169 // style's number format is latin-only. If the cell uses a different
1170 // format, the script type will be reset when the style is applied.
1172 rXMLImport.GetDoc().setNumericCell(rCurrentPos, fValue);
1174 rXMLImport.ProgressBarIncrement();
1177 namespace {
1179 bool isEmptyOrNote( const ScDocument* pDoc, const ScAddress& rCurrentPos )
1181 CellType eType = pDoc->GetCellType(rCurrentPos);
1182 return (eType == CELLTYPE_NONE);
1187 void ScXMLTableRowCellContext::AddTextAndValueCell( const ScAddress& rCellPos,
1188 const ::std::optional< OUString >& pOUText, ScAddress& rCurrentPos )
1190 ScDocument* pDoc = rXMLImport.GetDocument();
1191 ScMyTables& rTables = rXMLImport.GetTables();
1192 bool bWasEmpty = bIsEmpty;
1193 for (SCCOL i = 0; i < nColsRepeated; ++i)
1195 rCurrentPos.SetCol( rCellPos.Col() + i );
1197 // it makes no sense to import data after the last supported column
1198 // fdo#58539 & gnome#627150
1199 if(rCurrentPos.Col() > pDoc->MaxCol())
1201 rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_COLUMN_OVERFLOW);
1202 break;
1205 if (i > 0)
1206 rTables.AddColumn(false);
1207 if (!bIsEmpty)
1209 for (SCROW j = 0; j < nRepeatedRows; ++j)
1211 rCurrentPos.SetRow( rCellPos.Row() + j );
1213 // it makes no sense to import data after last supported row
1214 // fdo#58539 & gnome#627150
1215 if(rCurrentPos.Row() > pDoc->MaxRow())
1217 rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_ROW_OVERFLOW);
1218 break;
1221 if( (rCurrentPos.Col() == 0) && (j > 0) )
1223 rTables.AddRow();
1224 rTables.AddColumn(false);
1226 if( cellExists(*pDoc, rCurrentPos) )
1228 if( !bIsCovered || isEmptyOrNote(pDoc, rCurrentPos) )
1230 switch (nCellType)
1232 case util::NumberFormat::TEXT:
1234 PutTextCell( rCurrentPos, i, pOUText );
1236 break;
1237 case util::NumberFormat::NUMBER:
1238 case util::NumberFormat::PERCENT:
1239 case util::NumberFormat::CURRENCY:
1240 case util::NumberFormat::TIME:
1241 case util::NumberFormat::DATETIME:
1242 case util::NumberFormat::LOGICAL:
1244 PutValueCell( rCurrentPos );
1246 break;
1247 default:
1249 OSL_FAIL("no cell type given");
1251 break;
1255 SetAnnotation( rCurrentPos );
1256 SetDetectiveObj( rCurrentPos );
1257 SetCellRangeSource( rCurrentPos );
1259 else
1261 if (!bWasEmpty || mxAnnotationData)
1263 if (rCurrentPos.Row() > pDoc->MaxRow())
1264 rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_ROW_OVERFLOW);
1265 else
1266 rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_COLUMN_OVERFLOW);
1271 else
1273 if ((i == 0) && (rCellPos.Col() == 0))
1275 for (sal_Int32 j = 1; j < nRepeatedRows; ++j)
1277 rTables.AddRow();
1278 rTables.AddColumn(false);
1281 // if nothing else useful can happen in the loop, just exit early
1282 if (i != 0 && bIsEmpty && rCurrentPos.Row() != 0)
1284 rCurrentPos.SetCol( rCellPos.Col() + nColsRepeated - 1 );
1285 rTables.AddColumns(nColsRepeated - i - 1);
1286 break;
1292 bool ScXMLTableRowCellContext::CellsAreRepeated() const
1294 return ( (nColsRepeated > 1) || (nRepeatedRows > 1) );
1297 namespace {
1299 // from ScCellObj::GetOutputString_Imp(). all of it may not be necessary.
1300 OUString getOutputString( ScDocument* pDoc, const ScAddress& aCellPos )
1302 if (!pDoc)
1303 return OUString();
1305 ScRefCellValue aCell(*pDoc, aCellPos);
1306 switch (aCell.getType())
1308 case CELLTYPE_NONE:
1309 return OUString();
1310 case CELLTYPE_EDIT:
1312 // GetString on EditCell replaces linebreaks with spaces;
1313 // however here we need line breaks
1314 const EditTextObject* pData = aCell.getEditText();
1315 EditEngine& rEngine = pDoc->GetEditEngine();
1316 rEngine.SetText(*pData);
1317 return rEngine.GetText();
1318 // also don't format EditCells per NumberFormatter
1320 break;
1321 default:
1323 // like in GetString for document (column)
1324 const Color* pColor;
1325 sal_uInt32 nNumFmt = pDoc->GetNumberFormat(ScRange(aCellPos));
1326 return ScCellFormat::GetString(aCell, nNumFmt, &pColor, nullptr, *pDoc);
1333 void ScXMLTableRowCellContext::AddNonFormulaCell( const ScAddress& rCellPos )
1335 ::std::optional< OUString > pOUText;
1337 ScDocument* pDoc = rXMLImport.GetDocument();
1338 if( nCellType == util::NumberFormat::TEXT )
1340 if( !bIsEmpty && !maStringValue && !mbEditEngineHasText && cellExists(*pDoc, rCellPos) && CellsAreRepeated() )
1341 pOUText = getOutputString(pDoc, rCellPos);
1343 if (!mbEditEngineHasText && !pOUText && !maStringValue)
1344 bIsEmpty = true;
1347 ScAddress aCurrentPos( rCellPos );
1348 if( mxAnnotationData || pDetectiveObjVec || pCellRangeSource ) // has special content
1349 bIsEmpty = false;
1351 AddTextAndValueCell( rCellPos, pOUText, aCurrentPos );
1353 if( CellsAreRepeated() )
1355 SCCOL nStartCol( std::min(rCellPos.Col(), pDoc->MaxCol()) );
1356 SCROW nStartRow( std::min(rCellPos.Row(), pDoc->MaxRow()) );
1357 SCCOL nEndCol( std::min<SCCOL>(rCellPos.Col() + nColsRepeated - 1, pDoc->MaxCol()) );
1358 SCROW nEndRow( std::min(rCellPos.Row() + nRepeatedRows - 1, pDoc->MaxRow()) );
1359 ScRange aScRange( nStartCol, nStartRow, rCellPos.Tab(), nEndCol, nEndRow, rCellPos.Tab() );
1360 SetContentValidation( aScRange );
1361 rXMLImport.GetStylesImportHelper()->AddRange( aScRange );
1363 else if( cellExists(*pDoc, rCellPos) )
1365 rXMLImport.GetStylesImportHelper()->AddCell(rCellPos);
1366 SetContentValidation( rCellPos );
1370 void ScXMLTableRowCellContext::PutFormulaCell( const ScAddress& rCellPos )
1372 ScDocument* pDoc = rXMLImport.GetDocument();
1373 if (!pDoc)
1374 return;
1376 ScDocumentImport& rDocImport = rXMLImport.GetDoc();
1378 const OUString & aText = maFormula->first;
1380 ScExternalRefManager::ApiGuard aExtRefGuard(*pDoc);
1382 if ( aText.isEmpty() )
1383 return;
1385 // temporary formula string as string tokens
1386 std::unique_ptr<ScTokenArray> pCode(new ScTokenArray(*pDoc));
1388 // Check the special case of a single error constant without leading
1389 // '=' and create an error formula cell without tokens.
1390 FormulaError nError = GetScImport().GetFormulaErrorConstant(aText);
1391 if (nError != FormulaError::NONE)
1393 pCode->SetCodeError(nError);
1395 else
1397 // 5.2 and earlier wrote broken "Err:xxx" as formula to designate
1398 // an error formula cell.
1399 if (aText.startsWithIgnoreAsciiCase("Err:") && aText.getLength() <= 9 &&
1400 ((nError =
1401 GetScImport().GetFormulaErrorConstant( OUString::Concat("#ERR") + aText.subView(4) + "!")) != FormulaError::NONE))
1403 pCode->SetCodeError(nError);
1405 else
1407 OUString aFormulaNmsp = maFormula->second;
1408 if( eGrammar != formula::FormulaGrammar::GRAM_EXTERNAL )
1409 aFormulaNmsp.clear();
1410 pCode->AssignXMLString( aText, aFormulaNmsp );
1411 rDocImport.getDoc().IncXMLImportedFormulaCount( aText.getLength() );
1415 ScFormulaCell* pNewCell = new ScFormulaCell(*pDoc, rCellPos, std::move(pCode), eGrammar, ScMatrixMode::NONE);
1416 SetFormulaCell(pNewCell);
1417 rDocImport.setFormulaCell(rCellPos, pNewCell);
1420 void ScXMLTableRowCellContext::AddFormulaCell( const ScAddress& rCellPos )
1422 ScDocument* pDoc = rXMLImport.GetDocument();
1423 if( cellExists(*pDoc, rCellPos) )
1425 SetContentValidation( rCellPos );
1426 SAL_WARN_IF((nColsRepeated != 1) || (nRepeatedRows != 1), "sc", "repeated cells with formula not possible now");
1427 rXMLImport.GetStylesImportHelper()->AddCell(rCellPos);
1429 //add matrix
1430 if(bIsMatrix)
1432 if (nMatrixCols > 0 && nMatrixRows > 0)
1434 //matrix cells are put in the document, but we must set the
1435 //value/text of each matrix cell later
1436 rXMLImport.GetTables().AddMatrixRange(
1437 rCellPos.Col(), rCellPos.Row(),
1438 std::min<SCCOL>(rCellPos.Col() + nMatrixCols - 1, pDoc->MaxCol()),
1439 std::min<SCROW>(rCellPos.Row() + nMatrixRows - 1, pDoc->MaxRow()),
1440 maFormula->first, maFormula->second, eGrammar);
1442 // Set the value/text of the top-left matrix position in its
1443 // cached result. For import, we only need to set the correct
1444 // matrix geometry and the value type of the top-left element.
1445 ScFormulaCell* pFCell = pDoc->GetFormulaCell(rCellPos);
1446 if (pFCell)
1448 ScMatrixRef pMat(new ScMatrix(nMatrixCols, nMatrixRows));
1449 if (bFormulaTextResult && maStringValue)
1451 if (!IsPossibleErrorString())
1453 pFCell->SetResultMatrix(
1454 nMatrixCols, nMatrixRows, pMat, new formula::FormulaStringToken(
1455 pDoc->GetSharedStringPool().intern( *maStringValue)));
1456 pFCell->ResetDirty();
1459 else if (std::isfinite(fValue))
1461 pFCell->SetResultMatrix(
1462 nMatrixCols, nMatrixRows, pMat, new formula::FormulaDoubleToken(fValue));
1463 pFCell->ResetDirty();
1468 else
1469 PutFormulaCell( rCellPos );
1471 SetAnnotation( rCellPos );
1472 SetDetectiveObj( rCellPos );
1473 SetCellRangeSource( rCellPos );
1474 rXMLImport.ProgressBarIncrement();
1476 else
1478 if (rCellPos.Row() > pDoc->MaxRow())
1479 rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_ROW_OVERFLOW);
1480 else
1481 rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_COLUMN_OVERFLOW);
1485 //There are cases where a formula cell is exported with an office:value of 0 or
1486 //no office:value at all, but the formula cell will have a text:p value which
1487 //contains the intended formula result.
1488 //These cases include when a formula result:
1489 // - is blank
1490 // - has a constant error value beginning with "#" (such as "#VALUE!" or "#N/A")
1491 // - has an "Err:[###]" (where "[###]" is an error number)
1492 // Libreoffice 4.1+ with ODF1.2 extended write however calcext:value-type="error" in that case
1493 void ScXMLTableRowCellContext::HasSpecialCaseFormulaText()
1495 if (!mbEditEngineHasText)
1496 return;
1498 const OUString aStr = GetFirstParagraph();
1500 if (mbNewValueType)
1502 if (aStr.isEmpty())
1503 mbPossibleEmptyDisplay = true;
1504 return;
1507 if (aStr.isEmpty())
1509 mbPossibleErrorCell = true;
1510 mbPossibleEmptyDisplay = true;
1512 else if (aStr.startsWith("Err:"))
1513 mbPossibleErrorCell = true;
1514 else if (aStr.startsWith("#"))
1515 mbCheckWithCompilerForError = true;
1518 bool ScXMLTableRowCellContext::IsPossibleErrorString() const
1520 if(mbNewValueType)
1521 return mbErrorValue;
1523 return mbPossibleErrorCell || (mbCheckWithCompilerForError && maStringValue &&
1524 GetScImport().GetFormulaErrorConstant(*maStringValue) != FormulaError::NONE);
1527 void SAL_CALL ScXMLTableRowCellContext::endFastElement(sal_Int32 /*nElement*/)
1529 HasSpecialCaseFormulaText();
1530 if( bFormulaTextResult && (mbPossibleErrorCell || mbCheckWithCompilerForError) )
1532 maStringValue = GetFirstParagraph();
1535 ScAddress aCellPos = rXMLImport.GetTables().GetCurrentCellPos();
1536 if( aCellPos.Col() > 0 && nRepeatedRows > 1 )
1537 aCellPos.SetRow( aCellPos.Row() - (nRepeatedRows - 1) );
1538 if( bIsMerged )
1539 DoMerge( aCellPos, nMergedCols - 1, nMergedRows - 1 );
1541 if (maFormula)
1542 AddFormulaCell(aCellPos);
1543 else
1544 AddNonFormulaCell(aCellPos);
1546 //if LockSolarMutex got used, we presumably need to ensure an UnlockSolarMutex
1547 if (bSolarMutexLocked)
1549 GetScImport().UnlockSolarMutex();
1550 bSolarMutexLocked = false;
1553 bIsMerged = false;
1554 nMergedCols = 1;
1555 nMergedRows = 1;
1556 nColsRepeated = 1;
1559 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */