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/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>
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
),
61 mbShowPhonetic( false )
65 CellFormulaModel::CellFormulaModel() :
66 mnFormulaType( XML_TOKEN_INVALID
),
71 bool CellFormulaModel::isValidArrayRef( const ScAddress
& rCellAddr
)
73 return (maFormulaRef
.aStart
== rCellAddr
);
76 bool CellFormulaModel::isValidSharedRef( const ScAddress
& rCellAddr
)
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() :
87 mbRef1Deleted( false ),
88 mbRef2Deleted( false )
92 CellBlockBuffer::CellBlockBuffer( const WorksheetHelper
& rHelper
) :
93 WorksheetHelper( rHelper
),
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();
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();
145 if( rxString
->extractPlainString( aText
, pFirstPortionFont
) )
147 setStringCell( rModel
, aText
);
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
);
160 setStringCell( rModel
, xString
);
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
);
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
);
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
);
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
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;
271 // simple formula, use the passed token array
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
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();
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
)
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
]);
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
);
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
)
393 // Cut off the part aStyleRows that was handled above
394 aStyleRows
.mnStartRow
= r
.mnEndRow
+ 1;
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
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;
453 while ( bAutoFilter
&& nScRow
< rDoc
.MaxRow() )
455 pDefPattern
= rDoc
.GetPattern( nScCol
, nScRow
, getSheetIndex() );
458 const ScMergeFlagAttr
* pAttr
= pDefPattern
->GetItemSet().GetItem( ATTR_MERGE_FLAG
);
459 bAutoFilter
= pAttr
->HasAutoFilter();
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();
474 pXf
->applyPatternToAttrList( aAttrs
, rRowStyle
.mnStartRow
, rRowStyle
.mnEndRow
, rRowStyle
.mnNumFmt
.second
);
476 if (aAttrs
.maAttrs
.empty() || aAttrs
.maAttrs
.back().nEndRow
!= rDoc
.MaxRow())
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() :
510 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow
, sal_Int32 nXfId
)
512 maRowRange
= ValueRange( nRow
);
516 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow
, sal_Int32 nXfId
)
518 if( mnXfId
== nXfId
)
520 if( maRowRange
.mnLast
+ 1 == nRow
)
525 if( maRowRange
.mnFirst
== nRow
+ 1 )
527 --maRowRange
.mnFirst
;
534 SheetDataBuffer::MergedRange::MergedRange( const ScRange
& 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();
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() );
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" );
578 xTokens
->setArrayTokens( rTokens
);
581 void SheetDataBuffer::finalizeTableOperation( const ScRange
& rRange
, const DataTableModel
& rModel
)
583 if (rModel
.mbRef1Deleted
)
586 if (rModel
.maRef1
.isEmpty())
589 if (rRange
.aStart
.Col() <= 0 || rRange
.aStart
.Row() <= 0)
592 sal_Int16 nSheet
= getSheetIndex();
594 ScAddress
aRef1( 0, 0, 0 );
595 if (!getAddressConverter().convertToCellAddress(aRef1
, rModel
.maRef1
, nSheet
, true))
598 ScDocumentImport
& rDoc
= getDocImport();
601 ScRange
aScRange(rRange
);
603 if (rModel
.mb2dTable
)
605 // Two-variable data table.
606 if (rModel
.mbRef2Deleted
)
609 if (rModel
.maRef2
.isEmpty())
612 ScAddress
aRef2( 0, 0, 0 );
613 if (!getAddressConverter().convertToCellAddress(aRef2
, rModel
.maRef2
, nSheet
, true))
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
);
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
);
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
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())
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);
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
745 lcl_SetBorderLine( rDoc
, rRange
, getSheetIndex(), SvxBoxItemLine::RIGHT
);
746 // set correct lower border
748 lcl_SetBorderLine( rDoc
, rRange
, getSheetIndex(), SvxBoxItemLine::BOTTOM
);
750 if( bMultiCol
|| bMultiRow
)
751 rDoc
.DoMerge( getSheetIndex(), rStart
.Col(), rStart
.Row(), rEnd
.Col(), rEnd
.Row() );
757 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */