1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <sheetdatabuffer.hxx>
21
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>
52
53 namespace oox::xls {
54
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;
59
CellModel()60 CellModel::CellModel() :
61 mnCellType( XML_TOKEN_INVALID ),
62 mnXfId( -1 ),
63 mbShowPhonetic( false )
64 {
65 }
66
CellFormulaModel()67 CellFormulaModel::CellFormulaModel() :
68 mnFormulaType( XML_TOKEN_INVALID ),
69 mnSharedId( -1 )
70 {
71 }
72
isValidArrayRef(const ScAddress & rCellAddr)73 bool CellFormulaModel::isValidArrayRef( const ScAddress& rCellAddr )
74 {
75 return (maFormulaRef.aStart == rCellAddr );
76 }
77
isValidSharedRef(const ScAddress & rCellAddr)78 bool CellFormulaModel::isValidSharedRef( const ScAddress& rCellAddr )
79 {
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());
84 }
85
DataTableModel()86 DataTableModel::DataTableModel() :
87 mb2dTable( false ),
88 mbRowTable( false ),
89 mbRef1Deleted( false ),
90 mbRef2Deleted( false )
91 {
92 }
93
CellBlockBuffer(const WorksheetHelper & rHelper)94 CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) :
95 WorksheetHelper( rHelper ),
96 mnCurrRow( -1 )
97 {
98 }
99
setColSpans(sal_Int32 nRow,const ValueRangeSet & rColSpans)100 void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
101 {
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) )
105 {
106 maColSpans[ nRow ] = rColSpans.getRanges();
107 mnCurrRow = nRow;
108 }
109 }
110
SheetDataBuffer(const WorksheetHelper & rHelper)111 SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
112 WorksheetHelper( rHelper ),
113 maCellBlocks( rHelper ),
114 mbPendingSharedFmla( false )
115 {
116 }
117
setColSpans(sal_Int32 nRow,const ValueRangeSet & rColSpans)118 void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
119 {
120 maCellBlocks.setColSpans( nRow, rColSpans );
121 }
122
setBlankCell(const CellModel & rModel)123 void SheetDataBuffer::setBlankCell( const CellModel& rModel )
124 {
125 setCellFormat( rModel );
126 }
127
setValueCell(const CellModel & rModel,double fValue)128 void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
129 {
130 getDocImport().setNumericCell(rModel.maCellAddr, fValue);
131 setCellFormat( rModel );
132 }
133
setStringCell(const CellModel & rModel,const OUString & rText)134 void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
135 {
136 if (!rText.isEmpty())
137 getDocImport().setStringCell(rModel.maCellAddr, rText);
138
139 setCellFormat( rModel );
140 }
141
setStringCell(const CellModel & rModel,const RichStringRef & rxString)142 void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
143 {
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 ) )
148 {
149 setStringCell( rModel, aText );
150 }
151 else
152 {
153 putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
154 setCellFormat( rModel );
155 }
156 }
157
setStringCell(const CellModel & rModel,sal_Int32 nStringId)158 void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
159 {
160 RichStringRef xString = getSharedStrings().getString( nStringId );
161 if( xString )
162 setStringCell( rModel, xString );
163 else
164 setBlankCell( rModel );
165 }
166
setDateTimeCell(const CellModel & rModel,const css::util::DateTime & rDateTime)167 void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime )
168 {
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
176 try
177 {
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 );
183 }
184 catch( Exception& )
185 {
186 }
187 }
188
setBooleanCell(const CellModel & rModel,bool bValue)189 void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
190 {
191 getFormulaBuffer().setCellFormula(
192 rModel.maCellAddr, bValue ? OUString("TRUE()") : OUString("FALSE()"));
193
194 // #108770# set 'Standard' number format for all Boolean cells
195 setCellFormat( rModel );
196 }
197
setErrorCell(const CellModel & rModel,const OUString & rErrorCode)198 void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
199 {
200 // Using the formula compiler now we can simply pass on the error string.
201 getFormulaBuffer().setCellFormula( rModel.maCellAddr, rErrorCode);
202 setCellFormat( rModel );
203 }
204
setErrorCell(const CellModel & rModel,sal_uInt8 nErrorCode)205 void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
206 {
207 setErrorCell( rModel, getUnitConverter().calcErrorString( nErrorCode));
208 }
209
setDateCell(const CellModel & rModel,const OUString & rDateString)210 void SheetDataBuffer::setDateCell( const CellModel& rModel, const OUString& rDateString )
211 {
212 css::util::DateTime aDateTime;
213 if (!sax::Converter::parseDateTime( aDateTime, rDateString))
214 {
215 SAL_WARN("sc.filter", "SheetDataBuffer::setDateCell - could not parse: " << rDateString);
216 // At least don't lose data.
217 setStringCell( rModel, rDateString);
218 return;
219 }
220
221 double fSerial = getUnitConverter().calcSerialFromDateTime( aDateTime);
222 setValueCell( rModel, fSerial);
223 }
224
createSharedFormula(const ScAddress & rAddr,const ApiTokenSequence & rTokens)225 void SheetDataBuffer::createSharedFormula(const ScAddress& rAddr, const ApiTokenSequence& rTokens)
226 {
227 BinAddress aAddr(rAddr);
228 maSharedFormulas[aAddr] = rTokens;
229 if( mbPendingSharedFmla )
230 setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) );
231 }
232
setFormulaCell(const CellModel & rModel,const ApiTokenSequence & rTokens)233 void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
234 {
235 mbPendingSharedFmla = false;
236 ApiTokenSequence aTokens;
237
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 ) )
247 {
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 )
254 {
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() )
264 {
265 maSharedFmlaAddr = rModel.maCellAddr;
266 maSharedBaseAddr = aTokenAddr;
267 mbPendingSharedFmla = true;
268 }
269 }
270 }
271 else
272 {
273 // simple formula, use the passed token array
274 aTokens = rTokens;
275 }
276
277 setCellFormula( rModel.maCellAddr, aTokens );
278 setCellFormat( rModel );
279 }
280
createArrayFormula(const ScRange & rRange,const ApiTokenSequence & rTokens)281 void SheetDataBuffer::createArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens )
282 {
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 );
287 }
288
createTableOperation(const ScRange & rRange,const DataTableModel & rModel)289 void SheetDataBuffer::createTableOperation( const ScRange& rRange, const DataTableModel& rModel )
290 {
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 );
295 }
296
setRowFormat(sal_Int32 nRow,sal_Int32 nXfId,bool bCustomFormat)297 void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
298 {
299 // set row formatting
300 if( bCustomFormat )
301 {
302 // try to expand cached row range, if formatting is equal
303 if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
304 {
305
306 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
307 maXfIdRowRange.set( nRow, nXfId );
308 }
309 }
310 else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
311 {
312 // finish last cached row range
313 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
314 maXfIdRowRange.set( -1, -1 );
315 }
316 }
317
setMergedRange(const ScRange & rRange)318 void SheetDataBuffer::setMergedRange( const ScRange& rRange )
319 {
320 maMergedRanges.emplace_back( rRange );
321 }
322
323 typedef std::pair<sal_Int32, sal_Int32> FormatKeyPair;
324
addIfNotInMyMap(const StylesBuffer & rStyles,std::map<FormatKeyPair,ScRangeList> & rMap,sal_Int32 nXfId,sal_Int32 nFormatId,const ScRangeList & rRangeList)325 static void addIfNotInMyMap( const StylesBuffer& rStyles, std::map< FormatKeyPair, ScRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList& rRangeList )
326 {
327 Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
328 if ( !pXf1 )
329 return;
330
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;
337 });
338 if (it != rMap.end()) // already exists
339 {
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;
345 }
346 rMap[ FormatKeyPair( nXfId, nFormatId ) ] = rRangeList;
347 }
348
addColXfStyles()349 void SheetDataBuffer::addColXfStyles()
350 {
351 std::map< FormatKeyPair, ScRangeList > rangeStyleListMap;
352 for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists )
353 {
354 addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList );
355 }
356 // gather all ranges that have the same style and apply them in bulk
357 for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap )
358 {
359 for (const ScRange & rAddress : rRanges)
360 {
361 RowRangeStyle aStyleRows;
362 aStyleRows.mnNumFmt.first = rFormatKeyPair.first;
363 aStyleRows.mnNumFmt.second = rFormatKeyPair.second;
364 aStyleRows.mnStartRow = rAddress.aStart.Row();
365 aStyleRows.mnEndRow = rAddress.aEnd.Row();
366 for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol )
367 maStylesPerColumn[ nCol ].insert( aStyleRows );
368 }
369 }
370 }
371
addColXfStyleProcessRowRanges()372 void SheetDataBuffer::addColXfStyleProcessRowRanges()
373 {
374 // count the number of row-range-styles we have
375 AddressConverter& rAddrConv = getAddressConverter();
376 int cnt = 0;
377 for ( const auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
378 {
379 if ( nXfId == -1 ) // it's a dud skip it
380 continue;
381 cnt += rRowRangeList.size();
382 }
383 // pre-allocate space in the sorted_vector
384 for ( sal_Int32 nCol = 0; nCol <= rAddrConv.getMaxApiAddress().Col(); ++nCol )
385 {
386 RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
387 rRowStyles.reserve(rRowStyles.size() + cnt);
388 }
389 const auto nMaxCol = rAddrConv.getMaxApiAddress().Col();
390 for ( sal_Int32 nCol = 0; nCol <= nMaxCol; ++nCol )
391 {
392 RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
393 for ( const auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
394 {
395 if ( nXfId == -1 ) // it's a dud skip it
396 continue;
397 // get all row ranges for id
398 for ( const auto& rRange : rRowRangeList )
399 {
400 RowRangeStyle aStyleRows;
401 aStyleRows.mnNumFmt.first = nXfId;
402 aStyleRows.mnNumFmt.second = -1;
403 aStyleRows.mnStartRow = rRange.mnFirst;
404 aStyleRows.mnEndRow = rRange.mnLast;
405
406 // Reset row range for each column
407 aStyleRows.mnStartRow = rRange.mnFirst;
408 aStyleRows.mnEndRow = rRange.mnLast;
409
410 // If aStyleRows includes rows already allocated to a style
411 // in rRowStyles, then we need to split it into parts.
412 // ( to occupy only rows that have no style definition)
413
414 // Start iterating at the first element that is not completely before aStyleRows
415 RowStyles::const_iterator rows_it = rRowStyles.lower_bound(aStyleRows);
416 bool bAddRange = true;
417 for ( ; rows_it != rRowStyles.end(); ++rows_it )
418 {
419 // Add the part of aStyleRows that does not overlap with r
420 if ( aStyleRows.mnStartRow < rows_it->mnStartRow )
421 {
422 RowRangeStyle aSplit = aStyleRows;
423 aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, rows_it->mnStartRow - 1);
424 rows_it = rRowStyles.insert( aSplit ).first;
425 }
426
427 // Done if no part of aStyleRows extends beyond r
428 if ( aStyleRows.mnEndRow <= rows_it->mnEndRow )
429 {
430 bAddRange = false;
431 break;
432 }
433
434 // Cut off the part aStyleRows that was handled above
435 aStyleRows.mnStartRow = rows_it->mnEndRow + 1;
436 }
437 if ( bAddRange )
438 rRowStyles.insert( aStyleRows );
439 }
440 }
441 }
442 }
443
finalizeImport()444 void SheetDataBuffer::finalizeImport()
445 {
446 ScDocumentImport& rDocImport = getDocImport();
447
448 SCTAB nStartTabInvalidatedIters(SCTAB_MAX);
449 SCTAB nEndTabInvalidatedIters(0);
450
451 // create all array formulas
452 for( const auto& [rRange, rTokens] : maArrayFormulas )
453 {
454 finalizeArrayFormula(rRange, rTokens);
455
456 nStartTabInvalidatedIters = std::min(rRange.aStart.Tab(), nStartTabInvalidatedIters);
457 nEndTabInvalidatedIters = std::max(rRange.aEnd.Tab(), nEndTabInvalidatedIters);
458 }
459
460 for (SCTAB nTab = nStartTabInvalidatedIters; nTab <= nEndTabInvalidatedIters; ++nTab)
461 rDocImport.invalidateBlockPositionSet(nTab);
462
463 // create all table operations
464 for( const auto& [rRange, rModel] : maTableOperations )
465 finalizeTableOperation( rRange, rModel );
466
467 // write default formatting of remaining row range
468 maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
469
470 addColXfStyles();
471
472 addColXfStyleProcessRowRanges();
473
474 ScDocument& rDoc = rDocImport.getDoc();
475 StylesBuffer& rStyles = getStyles();
476 for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
477 {
478 SCCOL nScCol = static_cast< SCCOL >( rCol );
479
480 // tdf#91567 Get pattern from the first row without AutoFilter
481 const ScPatternAttr* pDefPattern = nullptr;
482 bool bAutoFilter = true;
483 SCROW nScRow = 0;
484 while ( bAutoFilter && nScRow < rDoc.MaxRow() )
485 {
486 pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
487 if ( pDefPattern )
488 {
489 const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
490 bAutoFilter = pAttr->HasAutoFilter();
491 }
492 else
493 break;
494 nScRow++;
495 }
496 if ( !pDefPattern || nScRow == rDoc.MaxRow() )
497 pDefPattern = rDoc.GetDefPattern();
498
499 Xf::AttrList aAttrs(pDefPattern);
500 for ( const auto& rRowStyle : rRowStyles )
501 {
502 Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
503
504 if ( pXf )
505 pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow, rRowStyle.mnNumFmt.second );
506 }
507 if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
508 {
509 ScAttrEntry aEntry;
510 aEntry.nEndRow = rDoc.MaxRow();
511 aEntry.pPattern = pDefPattern;
512 rDoc.GetPool()->Put(*aEntry.pPattern);
513 aAttrs.maAttrs.push_back(aEntry);
514
515 if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc))
516 aAttrs.mbLatinNumFmtOnly = false;
517 }
518
519 ScDocumentImport::Attrs aAttrParam;
520 aAttrParam.mvData.swap(aAttrs.maAttrs);
521 aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
522
523 rDocImport.setAttrEntries(getSheetIndex(), nScCol, std::move(aAttrParam));
524 }
525
526 // merge all cached merged ranges and update right/bottom cell borders
527 for( const auto& rMergedRange : maMergedRanges )
528 applyCellMerging( rMergedRange.maRange );
529 for( const auto& rCenterFillRange : maCenterFillRanges )
530 applyCellMerging( rCenterFillRange.maRange );
531 }
532
533 // private --------------------------------------------------------------------
534
XfIdRowRange()535 SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
536 maRowRange( -1 ),
537 mnXfId( -1 )
538 {
539 }
540
set(sal_Int32 nRow,sal_Int32 nXfId)541 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
542 {
543 maRowRange = ValueRange( nRow );
544 mnXfId = nXfId;
545 }
546
tryExpand(sal_Int32 nRow,sal_Int32 nXfId)547 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
548 {
549 if( mnXfId == nXfId )
550 {
551 if( maRowRange.mnLast + 1 == nRow )
552 {
553 ++maRowRange.mnLast;
554 return true;
555 }
556 if( maRowRange.mnFirst == nRow + 1 )
557 {
558 --maRowRange.mnFirst;
559 return true;
560 }
561 }
562 return false;
563 }
564
MergedRange(const ScRange & rRange)565 SheetDataBuffer::MergedRange::MergedRange( const ScRange& rRange ) :
566 maRange( rRange ),
567 mnHorAlign( XML_TOKEN_INVALID )
568 {
569 }
570
MergedRange(const ScAddress & rAddress,sal_Int32 nHorAlign)571 SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
572 maRange( rAddress, rAddress ),
573 mnHorAlign( nHorAlign )
574 {
575 }
576
tryExpand(const ScAddress & rAddress,sal_Int32 nHorAlign)577 bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
578 {
579 if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
580 (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
581 {
582 maRange.aEnd.IncCol();
583 return true;
584 }
585 return false;
586 }
587
setCellFormula(const ScAddress & rCellAddr,const ApiTokenSequence & rTokens)588 void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
589 {
590 if( rTokens.hasElements() )
591 {
592 putFormulaTokens( rCellAddr, rTokens );
593 }
594 }
595
596
resolveSharedFormula(const ScAddress & rAddr) const597 ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const ScAddress& rAddr ) const
598 {
599 BinAddress aAddr(rAddr);
600 ApiTokenSequence aTokens = ContainerHelper::getMapElement( maSharedFormulas, aAddr, ApiTokenSequence() );
601 return aTokens;
602 }
603
finalizeArrayFormula(const ScRange & rRange,const ApiTokenSequence & rTokens) const604 void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
605 {
606 Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
607 OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
608 if( xTokens.is() )
609 xTokens->setArrayTokens( rTokens );
610 }
611
finalizeTableOperation(const ScRange & rRange,const DataTableModel & rModel)612 void SheetDataBuffer::finalizeTableOperation( const ScRange& rRange, const DataTableModel& rModel )
613 {
614 if (rModel.mbRef1Deleted)
615 return;
616
617 if (rModel.maRef1.isEmpty())
618 return;
619
620 if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
621 return;
622
623 sal_Int16 nSheet = getSheetIndex();
624
625 ScAddress aRef1( 0, 0, 0 );
626 if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
627 return;
628
629 ScDocumentImport& rDoc = getDocImport();
630 ScTabOpParam aParam;
631
632 ScRange aScRange(rRange);
633
634 if (rModel.mb2dTable)
635 {
636 // Two-variable data table.
637 if (rModel.mbRef2Deleted)
638 return;
639
640 if (rModel.maRef2.isEmpty())
641 return;
642
643 ScAddress aRef2( 0, 0, 0 );
644 if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
645 return;
646
647 aParam.meMode = ScTabOpParam::Both;
648
649 aScRange.aStart.IncCol(-1);
650 aScRange.aStart.IncRow(-1);
651
652 aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
653 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
654
655 // Ref1 is row input cell and Ref2 is column input cell.
656 aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
657 aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
658 rDoc.setTableOpCells(aScRange, aParam);
659
660 return;
661 }
662
663 // One-variable data table.
664
665 if (rModel.mbRowTable)
666 {
667 // One-variable row input cell (horizontal).
668 aParam.meMode = ScTabOpParam::Row;
669 aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
670 aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
671 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
672 aScRange.aStart.IncRow(-1);
673 rDoc.setTableOpCells(aScRange, aParam);
674 }
675 else
676 {
677 // One-variable column input cell (vertical).
678 aParam.meMode = ScTabOpParam::Column;
679 aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
680 aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
681 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
682 aScRange.aStart.IncCol(-1);
683 rDoc.setTableOpCells(aScRange, aParam);
684 }
685 }
686
setCellFormat(const CellModel & rModel)687 void SheetDataBuffer::setCellFormat( const CellModel& rModel )
688 {
689 if( rModel.mnXfId < 0 )
690 return;
691
692 ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
693 ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
694 /* The xlsx sheet data contains row wise information.
695 * It is sufficient to check if the row range size is one
696 */
697 if (!rRangeList.empty() &&
698 *pLastRange == rModel.maCellAddr)
699 ; // do nothing - this probably bad data
700 else if (!rRangeList.empty() &&
701 pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
702 pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
703 pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
704 pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
705 {
706 pLastRange->aEnd.IncCol(); // Expand Column
707 }
708 else
709 {
710 rRangeList.push_back(ScRange(rModel.maCellAddr));
711 pLastRange = &rRangeList.back();
712 }
713
714 if (rRangeList.size() > 1)
715 {
716 for (size_t i = rRangeList.size() - 1; i != 0; --i)
717 {
718 ScRange& rMergeRange = rRangeList[i - 1];
719 if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
720 break;
721
722 /* Try to merge this with the previous range */
723 if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
724 pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
725 pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
726 {
727 rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
728 rRangeList.Remove(rRangeList.size() - 1);
729 break;
730 }
731 else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
732 break; // Un-necessary to check with any other rows
733 }
734 }
735 // update merged ranges for 'center across selection' and 'fill'
736 const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
737 if( !pXf )
738 return;
739
740 sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
741 if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
742 {
743 /* start new merged range, if cell is not empty (#108781#),
744 or try to expand last range with empty cell */
745 if( rModel.mnCellType != XML_TOKEN_INVALID )
746 maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
747 else if( !maCenterFillRanges.empty() )
748 maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
749 }
750 }
751
lcl_SetBorderLine(ScDocument & rDoc,const ScRange & rRange,SCTAB nScTab,SvxBoxItemLine nLine)752 static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
753 {
754 SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
755 SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
756
757 const SvxBoxItem* pFromItem =
758 rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
759 const SvxBoxItem* pToItem =
760 rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
761
762 SvxBoxItem aNewItem( *pToItem );
763 aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
764 rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
765 }
766
applyCellMerging(const ScRange & rRange)767 void SheetDataBuffer::applyCellMerging( const ScRange& rRange )
768 {
769 bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
770 bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
771
772 const ScAddress& rStart = rRange.aStart;
773 const ScAddress& rEnd = rRange.aEnd;
774 ScDocument& rDoc = getScDocument();
775 // set correct right border
776 if( bMultiCol )
777 lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
778 // set correct lower border
779 if( bMultiRow )
780 lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
781 // do merge
782 if( bMultiCol || bMultiRow )
783 rDoc.DoMerge( getSheetIndex(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row() );
784 }
785
786 } // namespace oox
787
788 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
789