nss: upgrade to release 3.73
[LibreOffice.git] / sc / source / filter / oox / sheetdatabuffer.cxx
blobde1d2c76f3c922c85505feb4722874c1a85c2f9a
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>
22 #include <algorithm>
23 #include <com/sun/star/sheet/XArrayFormulaTokens.hpp>
24 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
25 #include <com/sun/star/table/XCell.hpp>
26 #include <com/sun/star/table/XCellRange.hpp>
27 #include <com/sun/star/util/DateTime.hpp>
28 #include <com/sun/star/util/NumberFormat.hpp>
29 #include <com/sun/star/util/XNumberFormatTypes.hpp>
30 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
31 #include <sal/log.hxx>
32 #include <osl/diagnose.h>
33 #include <editeng/boxitem.hxx>
34 #include <oox/helper/containerhelper.hxx>
35 #include <oox/helper/propertyset.hxx>
36 #include <oox/token/properties.hxx>
37 #include <oox/token/tokens.hxx>
38 #include <addressconverter.hxx>
39 #include <formulaparser.hxx>
40 #include <sharedstringsbuffer.hxx>
41 #include <unitconverter.hxx>
42 #include <rangelst.hxx>
43 #include <document.hxx>
44 #include <scitems.hxx>
45 #include <docpool.hxx>
46 #include <paramisc.hxx>
47 #include <patattr.hxx>
48 #include <documentimport.hxx>
49 #include <formulabuffer.hxx>
50 #include <numformat.hxx>
51 #include <sax/tools/converter.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 CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) :
95 WorksheetHelper( rHelper ),
96 mnCurrRow( -1 )
100 void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
102 OSL_ENSURE( maColSpans.count( nRow ) == 0, "CellBlockBuffer::setColSpans - multiple column spans for the same row" );
103 OSL_ENSURE( (mnCurrRow < nRow) && (maColSpans.empty() || (maColSpans.rbegin()->first < nRow)), "CellBlockBuffer::setColSpans - rows are unsorted" );
104 if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) )
106 maColSpans[ nRow ] = rColSpans.getRanges();
107 mnCurrRow = nRow;
111 SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
112 WorksheetHelper( rHelper ),
113 maCellBlocks( rHelper ),
114 mbPendingSharedFmla( false )
118 void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
120 maCellBlocks.setColSpans( nRow, rColSpans );
123 void SheetDataBuffer::setBlankCell( const CellModel& rModel )
125 setCellFormat( rModel );
128 void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
130 getDocImport().setNumericCell(rModel.maCellAddr, fValue);
131 setCellFormat( rModel );
134 void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
136 if (!rText.isEmpty())
137 getDocImport().setStringCell(rModel.maCellAddr, rText);
139 setCellFormat( rModel );
142 void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
144 OSL_ENSURE( rxString, "SheetDataBuffer::setStringCell - missing rich string object" );
145 const oox::xls::Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
146 OUString aText;
147 if( rxString->extractPlainString( aText, pFirstPortionFont ) )
149 setStringCell( rModel, aText );
151 else
153 putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
154 setCellFormat( rModel );
158 void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
160 RichStringRef xString = getSharedStrings().getString( nStringId );
161 if( xString )
162 setStringCell( rModel, xString );
163 else
164 setBlankCell( rModel );
167 void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime )
169 // write serial date/time value into the cell
170 double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
171 setValueCell( rModel, fSerial );
172 // set appropriate number format
173 using namespace ::com::sun::star::util::NumberFormat;
174 sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
175 // set number format
178 Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY_THROW );
179 Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
180 sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdFmt, Locale() );
181 PropertySet aPropSet( getCell( rModel.maCellAddr ) );
182 aPropSet.setProperty( PROP_NumberFormat, nIndex );
184 catch( Exception& )
189 void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
191 getFormulaBuffer().setCellFormula(
192 rModel.maCellAddr, bValue ? OUString("TRUE()") : OUString("FALSE()"));
194 // #108770# set 'Standard' number format for all Boolean cells
195 setCellFormat( rModel );
198 void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
200 // Using the formula compiler now we can simply pass on the error string.
201 getFormulaBuffer().setCellFormula( rModel.maCellAddr, rErrorCode);
202 setCellFormat( rModel );
205 void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
207 setErrorCell( rModel, getUnitConverter().calcErrorString( nErrorCode));
210 void SheetDataBuffer::setDateCell( const CellModel& rModel, const OUString& rDateString )
212 css::util::DateTime aDateTime;
213 if (!sax::Converter::parseDateTime( aDateTime, rDateString))
215 SAL_WARN("sc.filter", "SheetDataBuffer::setDateCell - could not parse: " << rDateString);
216 // At least don't lose data.
217 setStringCell( rModel, rDateString);
218 return;
221 double fSerial = getUnitConverter().calcSerialFromDateTime( aDateTime);
222 setValueCell( rModel, fSerial);
225 void SheetDataBuffer::createSharedFormula(const ScAddress& rAddr, const ApiTokenSequence& rTokens)
227 BinAddress aAddr(rAddr);
228 maSharedFormulas[aAddr] = rTokens;
229 if( mbPendingSharedFmla )
230 setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) );
233 void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
235 mbPendingSharedFmla = false;
236 ApiTokenSequence aTokens;
238 /* Detect special token passed as placeholder for array formulas, shared
239 formulas, and table operations. In BIFF, these formulas are represented
240 by a single tExp resp. tTbl token. If the formula parser finds these
241 tokens, it puts a single OPCODE_BAD token with the base address and
242 formula type into the token sequence. This information will be
243 extracted here, and in case of a shared formula, the shared formula
244 buffer will generate the resulting formula token array. */
245 ApiSpecialTokenInfo aTokenInfo;
246 if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
248 /* The second member of the token info is set to true, if the formula
249 represents a table operation, which will be skipped. In BIFF12 it
250 is not possible to distinguish array and shared formulas
251 (BIFF5/BIFF8 provide this information with a special flag in the
252 FORMULA record). */
253 if( !aTokenInfo.Second )
255 /* Construct the token array representing the shared formula. If
256 the returned sequence is empty, the definition of the shared
257 formula has not been loaded yet, or the cell is part of an
258 array formula. In this case, the cell will be remembered. After
259 reading the formula definition it will be retried to insert the
260 formula via retryPendingSharedFormulaCell(). */
261 ScAddress aTokenAddr( aTokenInfo.First.Column, aTokenInfo.First.Row, aTokenInfo.First.Sheet );
262 aTokens = resolveSharedFormula( aTokenAddr );
263 if( !aTokens.hasElements() )
265 maSharedFmlaAddr = rModel.maCellAddr;
266 maSharedBaseAddr = aTokenAddr;
267 mbPendingSharedFmla = true;
271 else
273 // simple formula, use the passed token array
274 aTokens = rTokens;
277 setCellFormula( rModel.maCellAddr, aTokens );
278 setCellFormat( rModel );
281 void SheetDataBuffer::createArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens )
283 /* Array formulas will be inserted later in finalizeImport(). This is
284 needed to not disturb collecting all the cells, which will be put into
285 the sheet in large blocks to increase performance. */
286 maArrayFormulas.emplace_back( rRange, rTokens );
289 void SheetDataBuffer::createTableOperation( const ScRange& rRange, const DataTableModel& rModel )
291 /* Table operations will be inserted later in finalizeImport(). This is
292 needed to not disturb collecting all the cells, which will be put into
293 the sheet in large blocks to increase performance. */
294 maTableOperations.emplace_back( rRange, rModel );
297 void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
299 // set row formatting
300 if( bCustomFormat )
302 // try to expand cached row range, if formatting is equal
303 if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
306 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
307 maXfIdRowRange.set( nRow, nXfId );
310 else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
312 // finish last cached row range
313 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
314 maXfIdRowRange.set( -1, -1 );
318 void SheetDataBuffer::setMergedRange( const ScRange& rRange )
320 maMergedRanges.emplace_back( rRange );
323 typedef std::pair<sal_Int32, sal_Int32> FormatKeyPair;
325 static void addIfNotInMyMap( const StylesBuffer& rStyles, std::map< FormatKeyPair, ScRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList& rRangeList )
327 Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
328 if ( !pXf1 )
329 return;
331 auto it = std::find_if(rMap.begin(), rMap.end(),
332 [&nFormatId, &rStyles, &pXf1](const std::pair<FormatKeyPair, ScRangeList>& rEntry) {
333 if (rEntry.first.second != nFormatId)
334 return false;
335 Xf* pXf2 = rStyles.getCellXf( rEntry.first.first ).get();
336 return *pXf1 == *pXf2;
338 if (it != rMap.end()) // already exists
340 // add ranges from the rangelist to the existing rangelist for the
341 // matching style ( should we check if they overlap ? )
342 for (size_t i = 0, nSize = rRangeList.size(); i < nSize; ++i)
343 it->second.push_back(rRangeList[i]);
344 return;
346 rMap[ FormatKeyPair( nXfId, nFormatId ) ] = rRangeList;
349 void SheetDataBuffer::addColXfStyle( sal_Int32 nXfId, sal_Int32 nFormatId, const ScRange& rAddress, bool bProcessRowRange )
351 RowRangeStyle aStyleRows;
352 aStyleRows.mnNumFmt.first = nXfId;
353 aStyleRows.mnNumFmt.second = nFormatId;
354 aStyleRows.mnStartRow = rAddress.aStart.Row();
355 aStyleRows.mnEndRow = rAddress.aEnd.Row();
356 for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol )
358 if ( !bProcessRowRange )
359 maStylesPerColumn[ nCol ].insert( aStyleRows );
360 else
362 RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
363 // Reset row range for each column
364 aStyleRows.mnStartRow = rAddress.aStart.Row();
365 aStyleRows.mnEndRow = rAddress.aEnd.Row();
367 // If aStyleRows includes rows already allocated to a style
368 // in rRowStyles, then we need to split it into parts.
369 // ( to occupy only rows that have no style definition)
371 // Start iterating at the first element that is not completely before aStyleRows
372 RowStyles::iterator rows_it = rRowStyles.lower_bound(aStyleRows);
373 RowStyles::iterator rows_end = rRowStyles.end();
374 bool bAddRange = true;
375 for ( ; rows_it != rows_end; ++rows_it )
377 const RowRangeStyle& r = *rows_it;
379 // Add the part of aStyleRows that does not overlap with r
380 if ( aStyleRows.mnStartRow < r.mnStartRow )
382 RowRangeStyle aSplit = aStyleRows;
383 aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, r.mnStartRow - 1);
384 // Insert with hint that aSplit comes directly before the current position
385 rRowStyles.insert( rows_it, aSplit );
388 // Done if no part of aStyleRows extends beyond r
389 if ( aStyleRows.mnEndRow <= r.mnEndRow )
391 bAddRange = false;
392 break;
395 // Cut off the part aStyleRows that was handled above
396 aStyleRows.mnStartRow = r.mnEndRow + 1;
398 if ( bAddRange )
399 rRowStyles.insert( aStyleRows );
404 void SheetDataBuffer::finalizeImport()
406 // create all array formulas
407 for( const auto& [rRange, rTokens] : maArrayFormulas )
408 finalizeArrayFormula( rRange, rTokens );
410 // create all table operations
411 for( const auto& [rRange, rModel] : maTableOperations )
412 finalizeTableOperation( rRange, rModel );
414 // write default formatting of remaining row range
415 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
417 std::map< FormatKeyPair, ScRangeList > rangeStyleListMap;
418 for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists )
420 addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList );
422 // gather all ranges that have the same style and apply them in bulk
423 for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap )
425 for (size_t i = 0, nSize = rRanges.size(); i < nSize; ++i)
426 addColXfStyle( rFormatKeyPair.first, rFormatKeyPair.second, rRanges[i]);
429 for ( const auto& [rXfId, rRowRangeList] : maXfIdRowRangeList )
431 if ( rXfId == -1 ) // it's a dud skip it
432 continue;
433 AddressConverter& rAddrConv = getAddressConverter();
434 // get all row ranges for id
435 for ( const auto& rRange : rRowRangeList )
437 ScRange aRange( 0, rRange.mnFirst, getSheetIndex(),
438 rAddrConv.getMaxApiAddress().Col(), rRange.mnLast, getSheetIndex() );
440 addColXfStyle( rXfId, -1, aRange, true );
444 ScDocumentImport& rDocImport = getDocImport();
445 ScDocument& rDoc = rDocImport.getDoc();
446 StylesBuffer& rStyles = getStyles();
447 for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
449 SCCOL nScCol = static_cast< SCCOL >( rCol );
451 // tdf#91567 Get pattern from the first row without AutoFilter
452 const ScPatternAttr* pDefPattern = nullptr;
453 bool bAutoFilter = true;
454 SCROW nScRow = 0;
455 while ( bAutoFilter && nScRow < rDoc.MaxRow() )
457 pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
458 if ( pDefPattern )
460 const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
461 bAutoFilter = pAttr->HasAutoFilter();
463 else
464 break;
465 nScRow++;
467 if ( !pDefPattern || nScRow == rDoc.MaxRow() )
468 pDefPattern = rDoc.GetDefPattern();
470 Xf::AttrList aAttrs(pDefPattern);
471 for ( const auto& rRowStyle : rRowStyles )
473 Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
475 if ( pXf )
476 pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow, rRowStyle.mnNumFmt.second );
478 if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
480 ScAttrEntry aEntry;
481 aEntry.nEndRow = rDoc.MaxRow();
482 aEntry.pPattern = pDefPattern;
483 rDoc.GetPool()->Put(*aEntry.pPattern);
484 aAttrs.maAttrs.push_back(aEntry);
486 if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc))
487 aAttrs.mbLatinNumFmtOnly = false;
490 ScDocumentImport::Attrs aAttrParam;
491 aAttrParam.mvData.swap(aAttrs.maAttrs);
492 aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
494 rDocImport.setAttrEntries(getSheetIndex(), nScCol, std::move(aAttrParam));
497 // merge all cached merged ranges and update right/bottom cell borders
498 for( const auto& rMergedRange : maMergedRanges )
499 applyCellMerging( rMergedRange.maRange );
500 for( const auto& rCenterFillRange : maCenterFillRanges )
501 applyCellMerging( rCenterFillRange.maRange );
504 // private --------------------------------------------------------------------
506 SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
507 maRowRange( -1 ),
508 mnXfId( -1 )
512 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
514 maRowRange = ValueRange( nRow );
515 mnXfId = nXfId;
518 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
520 if( mnXfId == nXfId )
522 if( maRowRange.mnLast + 1 == nRow )
524 ++maRowRange.mnLast;
525 return true;
527 if( maRowRange.mnFirst == nRow + 1 )
529 --maRowRange.mnFirst;
530 return true;
533 return false;
536 SheetDataBuffer::MergedRange::MergedRange( const ScRange& rRange ) :
537 maRange( rRange ),
538 mnHorAlign( XML_TOKEN_INVALID )
542 SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
543 maRange( rAddress, rAddress ),
544 mnHorAlign( nHorAlign )
548 bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
550 if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
551 (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
553 maRange.aEnd.IncCol();
554 return true;
556 return false;
559 void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
561 if( rTokens.hasElements() )
563 putFormulaTokens( rCellAddr, rTokens );
568 ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const ScAddress& rAddr ) const
570 BinAddress aAddr(rAddr);
571 ApiTokenSequence aTokens = ContainerHelper::getMapElement( maSharedFormulas, aAddr, ApiTokenSequence() );
572 return aTokens;
575 void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
577 Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
578 OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
579 if( xTokens.is() )
580 xTokens->setArrayTokens( rTokens );
583 void SheetDataBuffer::finalizeTableOperation( const ScRange& rRange, const DataTableModel& rModel )
585 if (rModel.mbRef1Deleted)
586 return;
588 if (rModel.maRef1.isEmpty())
589 return;
591 if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
592 return;
594 sal_Int16 nSheet = getSheetIndex();
596 ScAddress aRef1( 0, 0, 0 );
597 if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
598 return;
600 ScDocumentImport& rDoc = getDocImport();
601 ScTabOpParam aParam;
603 ScRange aScRange(rRange);
605 if (rModel.mb2dTable)
607 // Two-variable data table.
608 if (rModel.mbRef2Deleted)
609 return;
611 if (rModel.maRef2.isEmpty())
612 return;
614 ScAddress aRef2( 0, 0, 0 );
615 if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
616 return;
618 aParam.meMode = ScTabOpParam::Both;
620 aScRange.aStart.IncCol(-1);
621 aScRange.aStart.IncRow(-1);
623 aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
624 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
626 // Ref1 is row input cell and Ref2 is column input cell.
627 aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
628 aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
629 rDoc.setTableOpCells(aScRange, aParam);
631 return;
634 // One-variable data table.
636 if (rModel.mbRowTable)
638 // One-variable row input cell (horizontal).
639 aParam.meMode = ScTabOpParam::Row;
640 aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
641 aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
642 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
643 aScRange.aStart.IncRow(-1);
644 rDoc.setTableOpCells(aScRange, aParam);
646 else
648 // One-variable column input cell (vertical).
649 aParam.meMode = ScTabOpParam::Column;
650 aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
651 aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
652 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
653 aScRange.aStart.IncCol(-1);
654 rDoc.setTableOpCells(aScRange, aParam);
658 void SheetDataBuffer::setCellFormat( const CellModel& rModel )
660 if( rModel.mnXfId < 0 )
661 return;
663 ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
664 ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
665 /* The xlsx sheet data contains row wise information.
666 * It is sufficient to check if the row range size is one
668 if (!rRangeList.empty() &&
669 *pLastRange == rModel.maCellAddr)
670 ; // do nothing - this probably bad data
671 else if (!rRangeList.empty() &&
672 pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
673 pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
674 pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
675 pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
677 pLastRange->aEnd.IncCol(); // Expand Column
679 else
681 rRangeList.push_back(ScRange(rModel.maCellAddr));
682 pLastRange = &rRangeList.back();
685 if (rRangeList.size() > 1)
687 for (size_t i = rRangeList.size() - 1; i != 0; --i)
689 ScRange& rMergeRange = rRangeList[i - 1];
690 if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
691 break;
693 /* Try to merge this with the previous range */
694 if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
695 pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
696 pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
698 rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
699 rRangeList.Remove(rRangeList.size() - 1);
700 break;
702 else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
703 break; // Un-necessary to check with any other rows
706 // update merged ranges for 'center across selection' and 'fill'
707 const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
708 if( !pXf )
709 return;
711 sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
712 if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
714 /* start new merged range, if cell is not empty (#108781#),
715 or try to expand last range with empty cell */
716 if( rModel.mnCellType != XML_TOKEN_INVALID )
717 maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
718 else if( !maCenterFillRanges.empty() )
719 maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
723 static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
725 SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
726 SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
728 const SvxBoxItem* pFromItem =
729 rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
730 const SvxBoxItem* pToItem =
731 rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
733 SvxBoxItem aNewItem( *pToItem );
734 aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
735 rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
738 void SheetDataBuffer::applyCellMerging( const ScRange& rRange )
740 bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
741 bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
743 const ScAddress& rStart = rRange.aStart;
744 const ScAddress& rEnd = rRange.aEnd;
745 ScDocument& rDoc = getScDocument();
746 // set correct right border
747 if( bMultiCol )
748 lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
749 // set correct lower border
750 if( bMultiRow )
751 lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
752 // do merge
753 if( bMultiCol || bMultiRow )
754 rDoc.DoMerge( getSheetIndex(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row() );
757 } // namespace oox
759 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */