Update git submodules
[LibreOffice.git] / sc / source / filter / oox / sheetdatabuffer.cxx
blobe9286d5f15233f43dd2bf740b8975b5261391f50
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 <sheetdatabuffer.hxx>
21 #include <patterncache.hxx>
23 #include <algorithm>
24 #include <com/sun/star/sheet/XArrayFormulaTokens.hpp>
25 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
26 #include <com/sun/star/util/DateTime.hpp>
27 #include <com/sun/star/util/NumberFormat.hpp>
28 #include <com/sun/star/util/XNumberFormatTypes.hpp>
29 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
30 #include <sal/log.hxx>
31 #include <osl/diagnose.h>
32 #include <editeng/boxitem.hxx>
33 #include <oox/helper/containerhelper.hxx>
34 #include <oox/helper/propertyset.hxx>
35 #include <oox/token/properties.hxx>
36 #include <oox/token/tokens.hxx>
37 #include <addressconverter.hxx>
38 #include <formulaparser.hxx>
39 #include <sharedstringsbuffer.hxx>
40 #include <unitconverter.hxx>
41 #include <rangelst.hxx>
42 #include <document.hxx>
43 #include <scitems.hxx>
44 #include <docpool.hxx>
45 #include <paramisc.hxx>
46 #include <patattr.hxx>
47 #include <documentimport.hxx>
48 #include <formulabuffer.hxx>
49 #include <numformat.hxx>
50 #include <sax/tools/converter.hxx>
51 #include <docuno.hxx>
53 namespace oox::xls {
55 using namespace ::com::sun::star::lang;
56 using namespace ::com::sun::star::sheet;
57 using namespace ::com::sun::star::uno;
58 using namespace ::com::sun::star::util;
60 CellModel::CellModel() :
61 mnCellType( XML_TOKEN_INVALID ),
62 mnXfId( -1 ),
63 mbShowPhonetic( false )
67 CellFormulaModel::CellFormulaModel() :
68 mnFormulaType( XML_TOKEN_INVALID ),
69 mnSharedId( -1 )
73 bool CellFormulaModel::isValidArrayRef( const ScAddress& rCellAddr )
75 return (maFormulaRef.aStart == rCellAddr );
78 bool CellFormulaModel::isValidSharedRef( const ScAddress& rCellAddr )
80 return
81 (maFormulaRef.aStart.Tab() == rCellAddr.Tab() ) &&
82 (maFormulaRef.aStart.Col() <= rCellAddr.Col() ) && (rCellAddr.Col() <= maFormulaRef.aEnd.Col()) &&
83 (maFormulaRef.aStart.Row() <= rCellAddr.Row() ) && (rCellAddr.Row() <= maFormulaRef.aEnd.Row());
86 DataTableModel::DataTableModel() :
87 mb2dTable( false ),
88 mbRowTable( false ),
89 mbRef1Deleted( false ),
90 mbRef2Deleted( false )
94 SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
95 WorksheetHelper( rHelper ),
96 mbPendingSharedFmla( false )
100 void SheetDataBuffer::setBlankCell( const CellModel& rModel )
102 setCellFormat( rModel );
105 void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
107 getDocImport().setNumericCell(rModel.maCellAddr, fValue);
108 setCellFormat( rModel );
111 void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
113 if (!rText.isEmpty())
114 getDocImport().setStringCell(rModel.maCellAddr, rText);
116 setCellFormat( rModel );
119 void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
121 OSL_ENSURE( rxString, "SheetDataBuffer::setStringCell - missing rich string object" );
122 const oox::xls::Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
123 const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
124 bool bSingleLine = pXf ? !pXf->getAlignment().getModel().mbWrapText : false;
125 OUString aText;
126 if( rxString->extractPlainString( aText, pFirstPortionFont ) )
128 setStringCell( rModel, aText );
130 else
132 putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont, bSingleLine );
133 setCellFormat( rModel );
137 void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
139 RichStringRef xString = getSharedStrings().getString( nStringId );
140 if( xString )
141 setStringCell( rModel, xString );
142 else
143 setBlankCell( rModel );
146 void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime )
148 // write serial date/time value into the cell
149 double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
150 setValueCell( rModel, fSerial );
151 // set appropriate number format
152 using namespace ::com::sun::star::util::NumberFormat;
153 sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
154 // set number format
157 Reference< XNumberFormatsSupplier > xNumFmtsSupp( static_cast<cppu::OWeakObject*>(getDocument().get()), UNO_QUERY_THROW );
158 Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
159 sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdFmt, Locale() );
160 PropertySet aPropSet( getCell( rModel.maCellAddr ) );
161 aPropSet.setProperty( PROP_NumberFormat, nIndex );
163 catch( Exception& )
168 void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
170 getFormulaBuffer().setCellFormula(
171 rModel.maCellAddr, bValue ? u"TRUE()"_ustr : u"FALSE()"_ustr);
173 // #108770# set 'Standard' number format for all Boolean cells
174 setCellFormat( rModel );
177 void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
179 // Using the formula compiler now we can simply pass on the error string.
180 getFormulaBuffer().setCellFormula( rModel.maCellAddr, rErrorCode);
181 setCellFormat( rModel );
184 void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
186 setErrorCell( rModel, getUnitConverter().calcErrorString( nErrorCode));
189 void SheetDataBuffer::setDateCell( const CellModel& rModel, const OUString& rDateString )
191 css::util::DateTime aDateTime;
192 if (!sax::Converter::parseDateTime( aDateTime, rDateString))
194 SAL_WARN("sc.filter", "SheetDataBuffer::setDateCell - could not parse: " << rDateString);
195 // At least don't lose data.
196 setStringCell( rModel, rDateString);
197 return;
200 double fSerial = getUnitConverter().calcSerialFromDateTime( aDateTime);
201 setValueCell( rModel, fSerial);
204 void SheetDataBuffer::createSharedFormula(const ScAddress& rAddr, const ApiTokenSequence& rTokens)
206 BinAddress aAddr(rAddr);
207 maSharedFormulas[aAddr] = rTokens;
208 if( mbPendingSharedFmla )
209 setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) );
212 void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
214 mbPendingSharedFmla = false;
215 ApiTokenSequence aTokens;
217 /* Detect special token passed as placeholder for array formulas, shared
218 formulas, and table operations. In BIFF, these formulas are represented
219 by a single tExp resp. tTbl token. If the formula parser finds these
220 tokens, it puts a single OPCODE_BAD token with the base address and
221 formula type into the token sequence. This information will be
222 extracted here, and in case of a shared formula, the shared formula
223 buffer will generate the resulting formula token array. */
224 ApiSpecialTokenInfo aTokenInfo;
225 if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
227 /* The second member of the token info is set to true, if the formula
228 represents a table operation, which will be skipped. In BIFF12 it
229 is not possible to distinguish array and shared formulas
230 (BIFF5/BIFF8 provide this information with a special flag in the
231 FORMULA record). */
232 if( !aTokenInfo.Second )
234 /* Construct the token array representing the shared formula. If
235 the returned sequence is empty, the definition of the shared
236 formula has not been loaded yet, or the cell is part of an
237 array formula. In this case, the cell will be remembered. After
238 reading the formula definition it will be retried to insert the
239 formula via retryPendingSharedFormulaCell(). */
240 ScAddress aTokenAddr( aTokenInfo.First.Column, aTokenInfo.First.Row, aTokenInfo.First.Sheet );
241 aTokens = resolveSharedFormula( aTokenAddr );
242 if( !aTokens.hasElements() )
244 maSharedFmlaAddr = rModel.maCellAddr;
245 maSharedBaseAddr = aTokenAddr;
246 mbPendingSharedFmla = true;
250 else
252 // simple formula, use the passed token array
253 aTokens = rTokens;
256 setCellFormula( rModel.maCellAddr, aTokens );
257 setCellFormat( rModel );
260 void SheetDataBuffer::createArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens )
262 /* Array formulas will be inserted later in finalizeImport(). This is
263 needed to not disturb collecting all the cells, which will be put into
264 the sheet in large blocks to increase performance. */
265 maArrayFormulas.emplace_back( rRange, rTokens );
268 void SheetDataBuffer::createTableOperation( const ScRange& rRange, const DataTableModel& rModel )
270 /* Table operations will be inserted later in finalizeImport(). This is
271 needed to not disturb collecting all the cells, which will be put into
272 the sheet in large blocks to increase performance. */
273 maTableOperations.emplace_back( rRange, rModel );
276 void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
278 // set row formatting
279 if( bCustomFormat )
281 // try to expand cached row range, if formatting is equal
282 if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
285 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
286 maXfIdRowRange.set( nRow, nXfId );
289 else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
291 // finish last cached row range
292 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
293 maXfIdRowRange.set( -1, -1 );
297 void SheetDataBuffer::setMergedRange( const ScRange& rRange )
299 maMergedRanges.emplace_back( rRange );
302 typedef std::pair<sal_Int32, sal_Int32> FormatKeyPair;
304 static void addIfNotInMyMap( const StylesBuffer& rStyles, std::map< FormatKeyPair, ScRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList& rRangeList )
306 Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
307 if ( !pXf1 )
308 return;
310 auto it = std::find_if(rMap.begin(), rMap.end(),
311 [&nFormatId, &rStyles, &pXf1](const std::pair<FormatKeyPair, ScRangeList>& rEntry) {
312 if (rEntry.first.second != nFormatId)
313 return false;
314 Xf* pXf2 = rStyles.getCellXf( rEntry.first.first ).get();
315 return *pXf1 == *pXf2;
317 if (it != rMap.end()) // already exists
319 // add ranges from the rangelist to the existing rangelist for the
320 // matching style ( should we check if they overlap ? )
321 it->second.insert(it->second.end(), rRangeList.begin(), rRangeList.end());
322 return;
324 rMap[ FormatKeyPair( nXfId, nFormatId ) ] = rRangeList;
327 void SheetDataBuffer::addColXfStyles()
329 std::map< FormatKeyPair, ScRangeList > rangeStyleListMap;
330 for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists )
332 addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList );
334 // gather all ranges that have the same style and apply them in bulk
335 // Collect data in unsorted vectors and sort them just once at the end
336 // instead of possibly slow repeated inserts.
337 TmpColStyles tmpStylesPerColumn;
338 for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap )
340 for (const ScRange & rAddress : rRanges)
342 RowRangeStyle aStyleRows;
343 aStyleRows.mnNumFmt.first = rFormatKeyPair.first;
344 aStyleRows.mnNumFmt.second = rFormatKeyPair.second;
345 aStyleRows.mnStartRow = rAddress.aStart.Row();
346 aStyleRows.mnEndRow = rAddress.aEnd.Row();
347 for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol )
348 tmpStylesPerColumn[ nCol ].push_back( aStyleRows );
351 for( auto& rowStyles : tmpStylesPerColumn )
353 TmpRowStyles& s = rowStyles.second;
354 std::sort( s.begin(), s.end(), StyleRowRangeComp());
355 s.erase( std::unique( s.begin(), s.end(),
356 [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
357 // Synthetize operator== from operator < . Do not create an actual operator==
358 // as operator< is somewhat specific (see StyleRowRangeComp).
359 { return !StyleRowRangeComp()(lhs,rhs) && !StyleRowRangeComp()(rhs,lhs); } ),
360 s.end());
361 // Broken documents may have overlapping ranges that cause problems, repeat once more.
362 if(!std::is_sorted(s.begin(), s.end(), StyleRowRangeComp()))
364 std::sort( s.begin(), s.end(), StyleRowRangeComp());
365 s.erase( std::unique( s.begin(), s.end(),
366 [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
367 { return !StyleRowRangeComp()(lhs,rhs) && !StyleRowRangeComp()(rhs,lhs); } ),
368 s.end());
370 maStylesPerColumn[ rowStyles.first ].insert_sorted_unique_vector( std::move( s ));
374 void SheetDataBuffer::addColXfStyleProcessRowRanges()
376 // count the number of row-range-styles we have
377 AddressConverter& rAddrConv = getAddressConverter();
378 int cnt = 0;
379 for ( const auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
381 if ( nXfId == -1 ) // it's a dud skip it
382 continue;
383 cnt += rRowRangeList.size();
385 // pre-allocate space in the sorted_vector
386 for ( sal_Int32 nCol = 0; nCol <= rAddrConv.getMaxApiAddress().Col(); ++nCol )
388 RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
389 rRowStyles.reserve(rRowStyles.size() + cnt);
391 const auto nMaxCol = rAddrConv.getMaxApiAddress().Col();
392 for ( sal_Int32 nCol = 0; nCol <= nMaxCol; ++nCol )
394 RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
395 for ( auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
397 if ( nXfId == -1 ) // it's a dud skip it
398 continue;
399 // sort the row ranges, so we spend less time moving data around
400 // when we insert into aStyleRows
401 std::sort(rRowRangeList.begin(), rRowRangeList.end(),
402 [](const ValueRange& lhs, const ValueRange& rhs)
404 return lhs.mnFirst < rhs.mnFirst;
406 // get all row ranges for id
407 for ( const auto& rRange : rRowRangeList )
409 RowRangeStyle aStyleRows;
410 aStyleRows.mnNumFmt.first = nXfId;
411 aStyleRows.mnNumFmt.second = -1;
413 // Reset row range for each column
414 aStyleRows.mnStartRow = rRange.mnFirst;
415 aStyleRows.mnEndRow = rRange.mnLast;
417 // If aStyleRows includes rows already allocated to a style
418 // in rRowStyles, then we need to split it into parts.
419 // ( to occupy only rows that have no style definition)
421 // Start iterating at the first element that is not completely before aStyleRows
422 RowStyles::const_iterator rows_it = rRowStyles.lower_bound(aStyleRows);
423 bool bAddRange = true;
424 for ( ; rows_it != rRowStyles.end(); ++rows_it )
426 // Add the part of aStyleRows that does not overlap with r
427 if ( aStyleRows.mnStartRow < rows_it->mnStartRow )
429 RowRangeStyle aSplit = aStyleRows;
430 aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, rows_it->mnStartRow - 1);
431 rows_it = rRowStyles.insert( aSplit ).first;
434 // Done if no part of aStyleRows extends beyond r
435 if ( aStyleRows.mnEndRow <= rows_it->mnEndRow )
437 bAddRange = false;
438 break;
441 // Cut off the part aStyleRows that was handled above
442 aStyleRows.mnStartRow = rows_it->mnEndRow + 1;
444 if ( bAddRange )
445 rRowStyles.insert( aStyleRows );
451 void SheetDataBuffer::finalizeImport()
453 ScDocumentImport& rDocImport = getDocImport();
455 SCTAB nStartTabInvalidatedIters(SCTAB_MAX);
456 SCTAB nEndTabInvalidatedIters(0);
458 // create all array formulas
459 for( const auto& [rRange, rTokens] : maArrayFormulas )
461 finalizeArrayFormula(rRange, rTokens);
463 nStartTabInvalidatedIters = std::min(rRange.aStart.Tab(), nStartTabInvalidatedIters);
464 nEndTabInvalidatedIters = std::max(rRange.aEnd.Tab(), nEndTabInvalidatedIters);
467 for (SCTAB nTab = nStartTabInvalidatedIters; nTab <= nEndTabInvalidatedIters; ++nTab)
468 rDocImport.invalidateBlockPositionSet(nTab);
470 // create all table operations
471 for( const auto& [rRange, rModel] : maTableOperations )
472 finalizeTableOperation( rRange, rModel );
474 // write default formatting of remaining row range
475 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
477 addColXfStyles();
479 addColXfStyleProcessRowRanges();
481 ScDocument& rDoc = rDocImport.getDoc();
482 StylesBuffer& rStyles = getStyles();
483 ScDocumentImport::Attrs aPendingAttrParam;
484 SCCOL pendingColStart = -1;
485 SCCOL pendingColEnd = -1;
486 ScPatternCache aPatternCache;
488 for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
490 SCCOL nScCol = static_cast< SCCOL >( rCol );
492 // tdf#91567 Get pattern from the first row without AutoFilter
493 const ScPatternAttr* pDefPattern = nullptr;
494 bool bAutoFilter = true;
495 SCROW nScRow = 0;
496 while ( bAutoFilter && nScRow < rDoc.MaxRow() )
498 pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
499 if ( pDefPattern )
501 const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
502 bAutoFilter = pAttr->HasAutoFilter();
504 else
505 break;
506 nScRow++;
508 if ( !pDefPattern || nScRow == rDoc.MaxRow() )
509 pDefPattern = &rDoc.getCellAttributeHelper().getDefaultCellAttribute();
511 Xf::AttrList aAttrs(pDefPattern);
512 for ( const auto& rRowStyle : rRowStyles )
514 Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
516 if ( pXf )
517 pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow,
518 rRowStyle.mnNumFmt.first, rRowStyle.mnNumFmt.second, aPatternCache );
520 if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
522 ScAttrEntry aEntry;
523 aEntry.nEndRow = rDoc.MaxRow();
524 aEntry.setScPatternAttr(pDefPattern, false);
525 aAttrs.maAttrs.push_back(aEntry);
527 if (!sc::NumFmtUtil::isLatinScript(*aEntry.getScPatternAttr(), rDoc))
528 aAttrs.mbLatinNumFmtOnly = false;
531 ScDocumentImport::Attrs aAttrParam;
532 aAttrParam.mvData.swap(aAttrs.maAttrs);
533 aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
535 // Compress setting the attributes, set the same set in one call.
536 if( pendingColStart != -1 && pendingColEnd == nScCol - 1 && aAttrParam == aPendingAttrParam )
537 ++pendingColEnd;
538 else
540 if( pendingColStart != -1 )
541 rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
542 pendingColStart = pendingColEnd = nScCol;
543 aPendingAttrParam = std::move( aAttrParam );
546 if( pendingColStart != -1 )
547 rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
549 // merge all cached merged ranges and update right/bottom cell borders
550 for( const auto& rMergedRange : maMergedRanges )
551 applyCellMerging( rMergedRange.maRange );
552 for( const auto& rCenterFillRange : maCenterFillRanges )
553 applyCellMerging( rCenterFillRange.maRange );
556 // private --------------------------------------------------------------------
558 SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
559 maRowRange( -1 ),
560 mnXfId( -1 )
564 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
566 maRowRange = ValueRange( nRow );
567 mnXfId = nXfId;
570 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
572 if( mnXfId == nXfId )
574 if( maRowRange.mnLast + 1 == nRow )
576 ++maRowRange.mnLast;
577 return true;
579 if( maRowRange.mnFirst == nRow + 1 )
581 --maRowRange.mnFirst;
582 return true;
585 return false;
588 SheetDataBuffer::MergedRange::MergedRange( const ScRange& rRange ) :
589 maRange( rRange ),
590 mnHorAlign( XML_TOKEN_INVALID )
594 SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
595 maRange( rAddress, rAddress ),
596 mnHorAlign( nHorAlign )
600 bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
602 if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
603 (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
605 maRange.aEnd.IncCol();
606 return true;
608 return false;
611 void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
613 if( rTokens.hasElements() )
615 putFormulaTokens( rCellAddr, rTokens );
620 ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const ScAddress& rAddr ) const
622 BinAddress aAddr(rAddr);
623 ApiTokenSequence aTokens = ContainerHelper::getMapElement( maSharedFormulas, aAddr, ApiTokenSequence() );
624 return aTokens;
627 void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
629 Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
630 OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
631 if( xTokens.is() )
632 xTokens->setArrayTokens( rTokens );
635 void SheetDataBuffer::finalizeTableOperation( const ScRange& rRange, const DataTableModel& rModel )
637 if (rModel.mbRef1Deleted)
638 return;
640 if (rModel.maRef1.isEmpty())
641 return;
643 if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
644 return;
646 sal_Int16 nSheet = getSheetIndex();
648 ScAddress aRef1( 0, 0, 0 );
649 if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
650 return;
652 ScDocumentImport& rDoc = getDocImport();
653 ScTabOpParam aParam;
655 ScRange aScRange(rRange);
657 if (rModel.mb2dTable)
659 // Two-variable data table.
660 if (rModel.mbRef2Deleted)
661 return;
663 if (rModel.maRef2.isEmpty())
664 return;
666 ScAddress aRef2( 0, 0, 0 );
667 if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
668 return;
670 aParam.meMode = ScTabOpParam::Both;
672 aScRange.aStart.IncCol(-1);
673 aScRange.aStart.IncRow(-1);
675 aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
676 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
678 // Ref1 is row input cell and Ref2 is column input cell.
679 aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
680 aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
681 rDoc.setTableOpCells(aScRange, aParam);
683 return;
686 // One-variable data table.
688 if (rModel.mbRowTable)
690 // One-variable row input cell (horizontal).
691 aParam.meMode = ScTabOpParam::Row;
692 aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
693 aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
694 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
695 aScRange.aStart.IncRow(-1);
696 rDoc.setTableOpCells(aScRange, aParam);
698 else
700 // One-variable column input cell (vertical).
701 aParam.meMode = ScTabOpParam::Column;
702 aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
703 aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
704 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
705 aScRange.aStart.IncCol(-1);
706 rDoc.setTableOpCells(aScRange, aParam);
710 void SheetDataBuffer::setCellFormat( const CellModel& rModel )
712 if( rModel.mnXfId < 0 )
713 return;
715 ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
716 ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
717 /* The xlsx sheet data contains row wise information.
718 * It is sufficient to check if the row range size is one
720 if (!rRangeList.empty() &&
721 *pLastRange == ScRange(rModel.maCellAddr))
722 ; // do nothing - this probably bad data
723 else if (!rRangeList.empty() &&
724 pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
725 pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
726 pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
727 pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
729 pLastRange->aEnd.IncCol(); // Expand Column
731 else
733 rRangeList.push_back(ScRange(rModel.maCellAddr));
734 pLastRange = &rRangeList.back();
737 if (rRangeList.size() > 1)
739 for (size_t i = rRangeList.size() - 1; i != 0; --i)
741 ScRange& rMergeRange = rRangeList[i - 1];
742 if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
743 break;
745 /* Try to merge this with the previous range */
746 if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
747 pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
748 pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
750 rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
751 rRangeList.Remove(rRangeList.size() - 1);
752 break;
754 else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
755 break; // Un-necessary to check with any other rows
758 // update merged ranges for 'center across selection' and 'fill'
759 const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
760 if( !pXf )
761 return;
763 sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
764 if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
766 /* start new merged range, if cell is not empty (#108781#),
767 or try to expand last range with empty cell */
768 if( rModel.mnCellType != XML_TOKEN_INVALID )
769 maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
770 else if( !maCenterFillRanges.empty() )
771 maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
775 static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
777 SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
778 SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
780 const SvxBoxItem* pFromItem =
781 rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
782 const SvxBoxItem* pToItem =
783 rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
785 SvxBoxItem aNewItem( *pToItem );
786 aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
787 rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
790 void SheetDataBuffer::applyCellMerging( const ScRange& rRange )
792 bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
793 bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
795 const ScAddress& rStart = rRange.aStart;
796 const ScAddress& rEnd = rRange.aEnd;
797 ScDocument& rDoc = getScDocument();
798 // set correct right border
799 if( bMultiCol )
800 lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
801 // set correct lower border
802 if( bMultiRow )
803 lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
804 // do merge
805 if( bMultiCol || bMultiRow )
806 rDoc.DoMerge( rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row(), getSheetIndex() );
809 } // namespace oox
811 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */