1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
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
),
63 mbShowPhonetic( false )
67 CellFormulaModel::CellFormulaModel() :
68 mnFormulaType( XML_TOKEN_INVALID
),
73 bool CellFormulaModel::isValidArrayRef( const ScAddress
& rCellAddr
)
75 return (maFormulaRef
.aStart
== rCellAddr
);
78 bool CellFormulaModel::isValidSharedRef( const ScAddress
& rCellAddr
)
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() :
89 mbRef1Deleted( false ),
90 mbRef2Deleted( false )
94 CellBlockBuffer::CellBlockBuffer( const WorksheetHelper
& rHelper
) :
95 WorksheetHelper( rHelper
),
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();
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();
147 if( rxString
->extractPlainString( aText
, pFirstPortionFont
) )
149 setStringCell( rModel
, aText
);
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
);
162 setStringCell( rModel
, xString
);
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
);
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
);
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
);
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
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;
273 // simple formula, use the passed token array
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
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();
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
)
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
]);
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
);
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
)
395 // Cut off the part aStyleRows that was handled above
396 aStyleRows
.mnStartRow
= r
.mnEndRow
+ 1;
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
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;
455 while ( bAutoFilter
&& nScRow
< rDoc
.MaxRow() )
457 pDefPattern
= rDoc
.GetPattern( nScCol
, nScRow
, getSheetIndex() );
460 const ScMergeFlagAttr
* pAttr
= pDefPattern
->GetItemSet().GetItem( ATTR_MERGE_FLAG
);
461 bAutoFilter
= pAttr
->HasAutoFilter();
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();
476 pXf
->applyPatternToAttrList( aAttrs
, rRowStyle
.mnStartRow
, rRowStyle
.mnEndRow
, rRowStyle
.mnNumFmt
.second
);
478 if (aAttrs
.maAttrs
.empty() || aAttrs
.maAttrs
.back().nEndRow
!= rDoc
.MaxRow())
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() :
512 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow
, sal_Int32 nXfId
)
514 maRowRange
= ValueRange( nRow
);
518 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow
, sal_Int32 nXfId
)
520 if( mnXfId
== nXfId
)
522 if( maRowRange
.mnLast
+ 1 == nRow
)
527 if( maRowRange
.mnFirst
== nRow
+ 1 )
529 --maRowRange
.mnFirst
;
536 SheetDataBuffer::MergedRange::MergedRange( const ScRange
& 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();
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() );
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" );
580 xTokens
->setArrayTokens( rTokens
);
583 void SheetDataBuffer::finalizeTableOperation( const ScRange
& rRange
, const DataTableModel
& rModel
)
585 if (rModel
.mbRef1Deleted
)
588 if (rModel
.maRef1
.isEmpty())
591 if (rRange
.aStart
.Col() <= 0 || rRange
.aStart
.Row() <= 0)
594 sal_Int16 nSheet
= getSheetIndex();
596 ScAddress
aRef1( 0, 0, 0 );
597 if (!getAddressConverter().convertToCellAddress(aRef1
, rModel
.maRef1
, nSheet
, true))
600 ScDocumentImport
& rDoc
= getDocImport();
603 ScRange
aScRange(rRange
);
605 if (rModel
.mb2dTable
)
607 // Two-variable data table.
608 if (rModel
.mbRef2Deleted
)
611 if (rModel
.maRef2
.isEmpty())
614 ScAddress
aRef2( 0, 0, 0 );
615 if (!getAddressConverter().convertToCellAddress(aRef2
, rModel
.maRef2
, nSheet
, true))
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
);
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
);
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 )
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
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())
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);
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();
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
748 lcl_SetBorderLine( rDoc
, rRange
, getSheetIndex(), SvxBoxItemLine::RIGHT
);
749 // set correct lower border
751 lcl_SetBorderLine( rDoc
, rRange
, getSheetIndex(), SvxBoxItemLine::BOTTOM
);
753 if( bMultiCol
|| bMultiRow
)
754 rDoc
.DoMerge( getSheetIndex(), rStart
.Col(), rStart
.Row(), rEnd
.Col(), rEnd
.Row() );
759 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */