Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / oox / sheetdatabuffer.cxx
blob03b35ef76e356f6a5e180e18d4b5d4df3016de74
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/util/DateTime.hpp>
25 #include <com/sun/star/util/NumberFormat.hpp>
26 #include <com/sun/star/util/XNumberFormatTypes.hpp>
27 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
28 #include <sal/log.hxx>
29 #include <osl/diagnose.h>
30 #include <editeng/boxitem.hxx>
31 #include <oox/helper/containerhelper.hxx>
32 #include <oox/helper/propertyset.hxx>
33 #include <oox/token/properties.hxx>
34 #include <oox/token/tokens.hxx>
35 #include <addressconverter.hxx>
36 #include <formulaparser.hxx>
37 #include <sharedstringsbuffer.hxx>
38 #include <unitconverter.hxx>
39 #include <rangelst.hxx>
40 #include <document.hxx>
41 #include <scitems.hxx>
42 #include <docpool.hxx>
43 #include <paramisc.hxx>
44 #include <patattr.hxx>
45 #include <documentimport.hxx>
46 #include <formulabuffer.hxx>
47 #include <numformat.hxx>
48 #include <sax/tools/converter.hxx>
50 namespace oox {
51 namespace xls {
53 using namespace ::com::sun::star::lang;
54 using namespace ::com::sun::star::sheet;
55 using namespace ::com::sun::star::uno;
56 using namespace ::com::sun::star::util;
58 CellModel::CellModel() :
59 mnCellType( XML_TOKEN_INVALID ),
60 mnXfId( -1 ),
61 mbShowPhonetic( false )
65 CellFormulaModel::CellFormulaModel() :
66 mnFormulaType( XML_TOKEN_INVALID ),
67 mnSharedId( -1 )
71 bool CellFormulaModel::isValidArrayRef( const ScAddress& rCellAddr )
73 return (maFormulaRef.aStart == rCellAddr );
76 bool CellFormulaModel::isValidSharedRef( const ScAddress& rCellAddr )
78 return
79 (maFormulaRef.aStart.Tab() == rCellAddr.Tab() ) &&
80 (maFormulaRef.aStart.Col() <= rCellAddr.Col() ) && (rCellAddr.Col() <= maFormulaRef.aEnd.Col()) &&
81 (maFormulaRef.aStart.Row() <= rCellAddr.Row() ) && (rCellAddr.Row() <= maFormulaRef.aEnd.Row());
84 DataTableModel::DataTableModel() :
85 mb2dTable( false ),
86 mbRowTable( false ),
87 mbRef1Deleted( false ),
88 mbRef2Deleted( false )
92 CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) :
93 WorksheetHelper( rHelper ),
94 mnCurrRow( -1 )
98 void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
100 OSL_ENSURE( maColSpans.count( nRow ) == 0, "CellBlockBuffer::setColSpans - multiple column spans for the same row" );
101 OSL_ENSURE( (mnCurrRow < nRow) && (maColSpans.empty() || (maColSpans.rbegin()->first < nRow)), "CellBlockBuffer::setColSpans - rows are unsorted" );
102 if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) )
104 maColSpans[ nRow ] = rColSpans.getRanges();
105 mnCurrRow = nRow;
109 SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
110 WorksheetHelper( rHelper ),
111 maCellBlocks( rHelper ),
112 mbPendingSharedFmla( false )
116 void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
118 maCellBlocks.setColSpans( nRow, rColSpans );
121 void SheetDataBuffer::setBlankCell( const CellModel& rModel )
123 setCellFormat( rModel );
126 void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
128 getDocImport().setNumericCell(rModel.maCellAddr, fValue);
129 setCellFormat( rModel );
132 void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
134 if (!rText.isEmpty())
135 getDocImport().setStringCell(rModel.maCellAddr, rText);
137 setCellFormat( rModel );
140 void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
142 OSL_ENSURE( rxString.get(), "SheetDataBuffer::setStringCell - missing rich string object" );
143 const oox::xls::Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
144 OUString aText;
145 if( rxString->extractPlainString( aText, pFirstPortionFont ) )
147 setStringCell( rModel, aText );
149 else
151 putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
152 setCellFormat( rModel );
156 void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
158 RichStringRef xString = getSharedStrings().getString( nStringId );
159 if( xString.get() )
160 setStringCell( rModel, xString );
161 else
162 setBlankCell( rModel );
165 void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime )
167 // write serial date/time value into the cell
168 double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
169 setValueCell( rModel, fSerial );
170 // set appropriate number format
171 using namespace ::com::sun::star::util::NumberFormat;
172 sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
173 // set number format
176 Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY_THROW );
177 Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
178 sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdFmt, Locale() );
179 PropertySet aPropSet( getCell( rModel.maCellAddr ) );
180 aPropSet.setProperty( PROP_NumberFormat, nIndex );
182 catch( Exception& )
187 void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
189 getFormulaBuffer().setCellFormula(
190 rModel.maCellAddr, bValue ? OUString("TRUE()") : OUString("FALSE()"));
192 // #108770# set 'Standard' number format for all Boolean cells
193 setCellFormat( rModel );
196 void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
198 // Using the formula compiler now we can simply pass on the error string.
199 getFormulaBuffer().setCellFormula( rModel.maCellAddr, rErrorCode);
200 setCellFormat( rModel );
203 void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
205 setErrorCell( rModel, getUnitConverter().calcErrorString( nErrorCode));
208 void SheetDataBuffer::setDateCell( const CellModel& rModel, const OUString& rDateString )
210 css::util::DateTime aDateTime;
211 if (!sax::Converter::parseDateTime( aDateTime, rDateString))
213 SAL_WARN("sc.filter", "SheetDataBuffer::setDateCell - could not parse: " << rDateString);
214 // At least don't lose data.
215 setStringCell( rModel, rDateString);
216 return;
219 double fSerial = getUnitConverter().calcSerialFromDateTime( aDateTime);
220 setValueCell( rModel, fSerial);
223 void SheetDataBuffer::createSharedFormula(const ScAddress& rAddr, const ApiTokenSequence& rTokens)
225 BinAddress aAddr(rAddr);
226 maSharedFormulas[aAddr] = rTokens;
227 if( mbPendingSharedFmla )
228 setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) );
231 void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
233 mbPendingSharedFmla = false;
234 ApiTokenSequence aTokens;
236 /* Detect special token passed as placeholder for array formulas, shared
237 formulas, and table operations. In BIFF, these formulas are represented
238 by a single tExp resp. tTbl token. If the formula parser finds these
239 tokens, it puts a single OPCODE_BAD token with the base address and
240 formula type into the token sequence. This information will be
241 extracted here, and in case of a shared formula, the shared formula
242 buffer will generate the resulting formula token array. */
243 ApiSpecialTokenInfo aTokenInfo;
244 if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
246 /* The second member of the token info is set to true, if the formula
247 represents a table operation, which will be skipped. In BIFF12 it
248 is not possible to distinguish array and shared formulas
249 (BIFF5/BIFF8 provide this information with a special flag in the
250 FORMULA record). */
251 if( !aTokenInfo.Second )
253 /* Construct the token array representing the shared formula. If
254 the returned sequence is empty, the definition of the shared
255 formula has not been loaded yet, or the cell is part of an
256 array formula. In this case, the cell will be remembered. After
257 reading the formula definition it will be retried to insert the
258 formula via retryPendingSharedFormulaCell(). */
259 ScAddress aTokenAddr( aTokenInfo.First.Column, aTokenInfo.First.Row, aTokenInfo.First.Sheet );
260 aTokens = resolveSharedFormula( aTokenAddr );
261 if( !aTokens.hasElements() )
263 maSharedFmlaAddr = rModel.maCellAddr;
264 maSharedBaseAddr = aTokenAddr;
265 mbPendingSharedFmla = true;
269 else
271 // simple formula, use the passed token array
272 aTokens = rTokens;
275 setCellFormula( rModel.maCellAddr, aTokens );
276 setCellFormat( rModel );
279 void SheetDataBuffer::createArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens )
281 /* Array formulas will be inserted later in finalizeImport(). This is
282 needed to not disturb collecting all the cells, which will be put into
283 the sheet in large blocks to increase performance. */
284 maArrayFormulas.emplace_back( rRange, rTokens );
287 void SheetDataBuffer::createTableOperation( const ScRange& rRange, const DataTableModel& rModel )
289 /* Table operations will be inserted later in finalizeImport(). This is
290 needed to not disturb collecting all the cells, which will be put into
291 the sheet in large blocks to increase performance. */
292 maTableOperations.emplace_back( rRange, rModel );
295 void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
297 // set row formatting
298 if( bCustomFormat )
300 // try to expand cached row range, if formatting is equal
301 if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
304 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
305 maXfIdRowRange.set( nRow, nXfId );
308 else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
310 // finish last cached row range
311 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
312 maXfIdRowRange.set( -1, -1 );
316 void SheetDataBuffer::setMergedRange( const ScRange& rRange )
318 maMergedRanges.emplace_back( rRange );
321 typedef std::pair<sal_Int32, sal_Int32> FormatKeyPair;
323 static void addIfNotInMyMap( const StylesBuffer& rStyles, std::map< FormatKeyPair, ScRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList& rRangeList )
325 Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
326 if ( pXf1 )
328 auto it = std::find_if(rMap.begin(), rMap.end(),
329 [&nFormatId, &rStyles, &pXf1](const std::pair<FormatKeyPair, ScRangeList>& rEntry) {
330 if (rEntry.first.second != nFormatId)
331 return false;
332 Xf* pXf2 = rStyles.getCellXf( rEntry.first.first ).get();
333 return *pXf1 == *pXf2;
335 if (it != rMap.end()) // already exists
337 // add ranges from the rangelist to the existing rangelist for the
338 // matching style ( should we check if they overlap ? )
339 for (size_t i = 0, nSize = rRangeList.size(); i < nSize; ++i)
340 it->second.push_back(rRangeList[i]);
341 return;
343 rMap[ FormatKeyPair( nXfId, nFormatId ) ] = rRangeList;
347 void SheetDataBuffer::addColXfStyle( sal_Int32 nXfId, sal_Int32 nFormatId, const ScRange& rAddress, bool bProcessRowRange )
349 RowRangeStyle aStyleRows;
350 aStyleRows.mnNumFmt.first = nXfId;
351 aStyleRows.mnNumFmt.second = nFormatId;
352 aStyleRows.mnStartRow = rAddress.aStart.Row();
353 aStyleRows.mnEndRow = rAddress.aEnd.Row();
354 for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol )
356 if ( !bProcessRowRange )
357 maStylesPerColumn[ nCol ].insert( aStyleRows );
358 else
360 RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
361 // Reset row range for each column
362 aStyleRows.mnStartRow = rAddress.aStart.Row();
363 aStyleRows.mnEndRow = rAddress.aEnd.Row();
365 // If aStyleRows includes rows already allocated to a style
366 // in rRowStyles, then we need to split it into parts.
367 // ( to occupy only rows that have no style definition)
369 // Start iterating at the first element that is not completely before aStyleRows
370 RowStyles::iterator rows_it = rRowStyles.lower_bound(aStyleRows);
371 RowStyles::iterator rows_end = rRowStyles.end();
372 bool bAddRange = true;
373 for ( ; rows_it != rows_end; ++rows_it )
375 const RowRangeStyle& r = *rows_it;
377 // Add the part of aStyleRows that does not overlap with r
378 if ( aStyleRows.mnStartRow < r.mnStartRow )
380 RowRangeStyle aSplit = aStyleRows;
381 aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, r.mnStartRow - 1);
382 // Insert with hint that aSplit comes directly before the current position
383 rRowStyles.insert( rows_it, aSplit );
386 // Done if no part of aStyleRows extends beyond r
387 if ( aStyleRows.mnEndRow <= r.mnEndRow )
389 bAddRange = false;
390 break;
393 // Cut off the part aStyleRows that was handled above
394 aStyleRows.mnStartRow = r.mnEndRow + 1;
396 if ( bAddRange )
397 rRowStyles.insert( aStyleRows );
402 void SheetDataBuffer::finalizeImport()
404 // create all array formulas
405 for( const auto& [rRange, rTokens] : maArrayFormulas )
406 finalizeArrayFormula( rRange, rTokens );
408 // create all table operations
409 for( const auto& [rRange, rModel] : maTableOperations )
410 finalizeTableOperation( rRange, rModel );
412 // write default formatting of remaining row range
413 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
415 std::map< FormatKeyPair, ScRangeList > rangeStyleListMap;
416 for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists )
418 addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList );
420 // gather all ranges that have the same style and apply them in bulk
421 for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap )
423 for (size_t i = 0, nSize = rRanges.size(); i < nSize; ++i)
424 addColXfStyle( rFormatKeyPair.first, rFormatKeyPair.second, rRanges[i]);
427 for ( const auto& [rXfId, rRowRangeList] : maXfIdRowRangeList )
429 if ( rXfId == -1 ) // it's a dud skip it
430 continue;
431 AddressConverter& rAddrConv = getAddressConverter();
432 // get all row ranges for id
433 for ( const auto& rRange : rRowRangeList )
435 ScRange aRange( 0, rRange.mnFirst, getSheetIndex(),
436 rAddrConv.getMaxApiAddress().Col(), rRange.mnLast, getSheetIndex() );
438 addColXfStyle( rXfId, -1, aRange, true );
442 ScDocumentImport& rDocImport = getDocImport();
443 ScDocument& rDoc = rDocImport.getDoc();
444 StylesBuffer& rStyles = getStyles();
445 for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
447 SCCOL nScCol = static_cast< SCCOL >( rCol );
449 // tdf#91567 Get pattern from the first row without AutoFilter
450 const ScPatternAttr* pDefPattern = nullptr;
451 bool bAutoFilter = true;
452 SCROW nScRow = 0;
453 while ( bAutoFilter && nScRow < rDoc.MaxRow() )
455 pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
456 if ( pDefPattern )
458 const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
459 bAutoFilter = pAttr->HasAutoFilter();
461 else
462 break;
463 nScRow++;
465 if ( !pDefPattern || nScRow == rDoc.MaxRow() )
466 pDefPattern = rDoc.GetDefPattern();
468 Xf::AttrList aAttrs(pDefPattern);
469 for ( const auto& rRowStyle : rRowStyles )
471 Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
473 if ( pXf )
474 pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow, rRowStyle.mnNumFmt.second );
476 if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
478 ScAttrEntry aEntry;
479 aEntry.nEndRow = rDoc.MaxRow();
480 aEntry.pPattern = pDefPattern;
481 rDoc.GetPool()->Put(*aEntry.pPattern);
482 aAttrs.maAttrs.push_back(aEntry);
484 if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc))
485 aAttrs.mbLatinNumFmtOnly = false;
488 ScDocumentImport::Attrs aAttrParam;
489 aAttrParam.mvData.swap(aAttrs.maAttrs);
490 aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
492 rDocImport.setAttrEntries(getSheetIndex(), nScCol, std::move(aAttrParam));
495 // merge all cached merged ranges and update right/bottom cell borders
496 for( const auto& rMergedRange : maMergedRanges )
497 applyCellMerging( rMergedRange.maRange );
498 for( const auto& rCenterFillRange : maCenterFillRanges )
499 applyCellMerging( rCenterFillRange.maRange );
502 // private --------------------------------------------------------------------
504 SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
505 maRowRange( -1 ),
506 mnXfId( -1 )
510 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
512 maRowRange = ValueRange( nRow );
513 mnXfId = nXfId;
516 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
518 if( mnXfId == nXfId )
520 if( maRowRange.mnLast + 1 == nRow )
522 ++maRowRange.mnLast;
523 return true;
525 if( maRowRange.mnFirst == nRow + 1 )
527 --maRowRange.mnFirst;
528 return true;
531 return false;
534 SheetDataBuffer::MergedRange::MergedRange( const ScRange& rRange ) :
535 maRange( rRange ),
536 mnHorAlign( XML_TOKEN_INVALID )
540 SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
541 maRange( rAddress, rAddress ),
542 mnHorAlign( nHorAlign )
546 bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
548 if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
549 (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
551 maRange.aEnd.IncCol();
552 return true;
554 return false;
557 void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
559 if( rTokens.hasElements() )
561 putFormulaTokens( rCellAddr, rTokens );
566 ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const ScAddress& rAddr ) const
568 BinAddress aAddr(rAddr);
569 ApiTokenSequence aTokens = ContainerHelper::getMapElement( maSharedFormulas, aAddr, ApiTokenSequence() );
570 return aTokens;
573 void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
575 Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
576 OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
577 if( xTokens.is() )
578 xTokens->setArrayTokens( rTokens );
581 void SheetDataBuffer::finalizeTableOperation( const ScRange& rRange, const DataTableModel& rModel )
583 if (rModel.mbRef1Deleted)
584 return;
586 if (rModel.maRef1.isEmpty())
587 return;
589 if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
590 return;
592 sal_Int16 nSheet = getSheetIndex();
594 ScAddress aRef1( 0, 0, 0 );
595 if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
596 return;
598 ScDocumentImport& rDoc = getDocImport();
599 ScTabOpParam aParam;
601 ScRange aScRange(rRange);
603 if (rModel.mb2dTable)
605 // Two-variable data table.
606 if (rModel.mbRef2Deleted)
607 return;
609 if (rModel.maRef2.isEmpty())
610 return;
612 ScAddress aRef2( 0, 0, 0 );
613 if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
614 return;
616 aParam.meMode = ScTabOpParam::Both;
618 aScRange.aStart.IncCol(-1);
619 aScRange.aStart.IncRow(-1);
621 aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
622 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
624 // Ref1 is row input cell and Ref2 is column input cell.
625 aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
626 aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
627 rDoc.setTableOpCells(aScRange, aParam);
629 return;
632 // One-variable data table.
634 if (rModel.mbRowTable)
636 // One-variable row input cell (horizontal).
637 aParam.meMode = ScTabOpParam::Row;
638 aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
639 aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
640 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
641 aScRange.aStart.IncRow(-1);
642 rDoc.setTableOpCells(aScRange, aParam);
644 else
646 // One-variable column input cell (vertical).
647 aParam.meMode = ScTabOpParam::Column;
648 aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
649 aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
650 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
651 aScRange.aStart.IncCol(-1);
652 rDoc.setTableOpCells(aScRange, aParam);
656 void SheetDataBuffer::setCellFormat( const CellModel& rModel )
658 if( rModel.mnXfId >= 0 )
660 ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
661 ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
662 /* The xlsx sheet data contains row wise information.
663 * It is sufficient to check if the row range size is one
665 if (!rRangeList.empty() &&
666 *pLastRange == rModel.maCellAddr)
667 ; // do nothing - this probably bad data
668 else if (!rRangeList.empty() &&
669 pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
670 pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
671 pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
672 pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
674 pLastRange->aEnd.IncCol(); // Expand Column
676 else
678 rRangeList.push_back(ScRange(rModel.maCellAddr));
679 pLastRange = &rRangeList.back();
682 if (rRangeList.size() > 1)
684 for (size_t i = rRangeList.size() - 1; i != 0; --i)
686 ScRange& rMergeRange = rRangeList[i - 1];
687 if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
688 break;
690 /* Try to merge this with the previous range */
691 if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
692 pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
693 pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
695 rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
696 rRangeList.Remove(rRangeList.size() - 1);
697 break;
699 else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
700 break; // Un-necessary to check with any other rows
703 // update merged ranges for 'center across selection' and 'fill'
704 if( const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get() )
706 sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
707 if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
709 /* start new merged range, if cell is not empty (#108781#),
710 or try to expand last range with empty cell */
711 if( rModel.mnCellType != XML_TOKEN_INVALID )
712 maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
713 else if( !maCenterFillRanges.empty() )
714 maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
720 static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
722 SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
723 SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
725 const SvxBoxItem* pFromItem =
726 rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
727 const SvxBoxItem* pToItem =
728 rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
730 SvxBoxItem aNewItem( *pToItem );
731 aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
732 rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
735 void SheetDataBuffer::applyCellMerging( const ScRange& rRange )
737 bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
738 bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
740 const ScAddress& rStart = rRange.aStart;
741 const ScAddress& rEnd = rRange.aEnd;
742 ScDocument& rDoc = getScDocument();
743 // set correct right border
744 if( bMultiCol )
745 lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
746 // set correct lower border
747 if( bMultiRow )
748 lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
749 // do merge
750 if( bMultiCol || bMultiRow )
751 rDoc.DoMerge( getSheetIndex(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row() );
754 } // namespace xls
755 } // namespace oox
757 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */