LibreOffice Module sc (master)  1
sheetdatabuffer.cxx
Go to the documentation of this file.
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>
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 
61  mnCellType( XML_TOKEN_INVALID ),
62  mnXfId( -1 ),
63  mbShowPhonetic( false )
64 {
65 }
66 
68  mnFormulaType( XML_TOKEN_INVALID ),
69  mnSharedId( -1 )
70 {
71 }
72 
74 {
75  return (maFormulaRef.aStart == rCellAddr );
76 }
77 
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 
87  mb2dTable( false ),
88  mbRowTable( false ),
89  mbRef1Deleted( false ),
90  mbRef2Deleted( false )
91 {
92 }
93 
95  WorksheetHelper( rHelper ),
96  mnCurrRow( -1 )
97 {
98 }
99 
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 )
105  return;
106  auto pair = maColSpans.try_emplace(nRow, rColSpans.getRanges());
107  if( pair.second ) // insert happened
108  mnCurrRow = nRow;
109 }
110 
112  WorksheetHelper( rHelper ),
113  maCellBlocks( rHelper ),
114  mbPendingSharedFmla( false )
115 {
116 }
117 
118 void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
119 {
120  maCellBlocks.setColSpans( nRow, rColSpans );
121 }
122 
124 {
125  setCellFormat( rModel );
126 }
127 
128 void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
129 {
130  getDocImport().setNumericCell(rModel.maCellAddr, fValue);
131  setCellFormat( rModel );
132 }
133 
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 
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 
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 
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 
189 void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
190 {
192  rModel.maCellAddr, bValue ? OUString("TRUE()") : OUString("FALSE()"));
193 
194  // #108770# set 'Standard' number format for all Boolean cells
195  setCellFormat( rModel );
196 }
197 
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 
205 void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
206 {
207  setErrorCell( rModel, getUnitConverter().calcErrorString( nErrorCode));
208 }
209 
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 
226 {
227  BinAddress aAddr(rAddr);
228  maSharedFormulas[aAddr] = rTokens;
229  if( mbPendingSharedFmla )
231 }
232 
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 
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 
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 
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 
307  maXfIdRowRange.set( nRow, nXfId );
308  }
309  }
310  else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
311  {
312  // finish last cached row range
314  maXfIdRowRange.set( -1, -1 );
315  }
316 }
317 
319 {
320  maMergedRanges.emplace_back( rRange );
321 }
322 
323 typedef std::pair<sal_Int32, sal_Int32> FormatKeyPair;
324 
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  it->second.insert(it->second.end(), rRangeList.begin(), rRangeList.end());
343  return;
344  }
345  rMap[ FormatKeyPair( nXfId, nFormatId ) ] = rRangeList;
346 }
347 
349 {
350  std::map< FormatKeyPair, ScRangeList > rangeStyleListMap;
351  for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists )
352  {
353  addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList );
354  }
355  // gather all ranges that have the same style and apply them in bulk
356  // Collect data in unsorted vectors and sort them just once at the end
357  // instead of possibly slow repeated inserts.
358  TmpColStyles tmpStylesPerColumn;
359  for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap )
360  {
361  for (const ScRange & rAddress : rRanges)
362  {
363  RowRangeStyle aStyleRows;
364  aStyleRows.mnNumFmt.first = rFormatKeyPair.first;
365  aStyleRows.mnNumFmt.second = rFormatKeyPair.second;
366  aStyleRows.mnStartRow = rAddress.aStart.Row();
367  aStyleRows.mnEndRow = rAddress.aEnd.Row();
368  for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol )
369  tmpStylesPerColumn[ nCol ].push_back( aStyleRows );
370  }
371  }
372  for( auto& rowStyles : tmpStylesPerColumn )
373  {
374  TmpRowStyles& s = rowStyles.second;
375  std::sort( s.begin(), s.end(), StyleRowRangeComp());
376  s.erase( std::unique( s.begin(), s.end(),
377  [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
378  // Synthetize operator== from operator < . Do not create an actual operator==
379  // as operator< is somewhat specific (see StyleRowRangeComp).
380  { return !StyleRowRangeComp()(lhs,rhs) && !StyleRowRangeComp()(rhs,lhs); } ),
381  s.end());
382  // Broken documents may have overlapping ranges that cause problems, repeat once more.
383  if(!std::is_sorted(s.begin(), s.end(), StyleRowRangeComp()))
384  {
385  std::sort( s.begin(), s.end(), StyleRowRangeComp());
386  s.erase( std::unique( s.begin(), s.end(),
387  [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
388  { return !StyleRowRangeComp()(lhs,rhs) && !StyleRowRangeComp()(rhs,lhs); } ),
389  s.end());
390  }
391  maStylesPerColumn[ rowStyles.first ].insert_sorted_unique_vector( std::move( s ));
392  }
393 }
394 
396 {
397  // count the number of row-range-styles we have
398  AddressConverter& rAddrConv = getAddressConverter();
399  int cnt = 0;
400  for ( const auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
401  {
402  if ( nXfId == -1 ) // it's a dud skip it
403  continue;
404  cnt += rRowRangeList.size();
405  }
406  // pre-allocate space in the sorted_vector
407  for ( sal_Int32 nCol = 0; nCol <= rAddrConv.getMaxApiAddress().Col(); ++nCol )
408  {
409  RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
410  rRowStyles.reserve(rRowStyles.size() + cnt);
411  }
412  const auto nMaxCol = rAddrConv.getMaxApiAddress().Col();
413  for ( sal_Int32 nCol = 0; nCol <= nMaxCol; ++nCol )
414  {
415  RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
416  for ( const auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
417  {
418  if ( nXfId == -1 ) // it's a dud skip it
419  continue;
420  // get all row ranges for id
421  for ( const auto& rRange : rRowRangeList )
422  {
423  RowRangeStyle aStyleRows;
424  aStyleRows.mnNumFmt.first = nXfId;
425  aStyleRows.mnNumFmt.second = -1;
426 
427  // Reset row range for each column
428  aStyleRows.mnStartRow = rRange.mnFirst;
429  aStyleRows.mnEndRow = rRange.mnLast;
430 
431  // If aStyleRows includes rows already allocated to a style
432  // in rRowStyles, then we need to split it into parts.
433  // ( to occupy only rows that have no style definition)
434 
435  // Start iterating at the first element that is not completely before aStyleRows
436  RowStyles::const_iterator rows_it = rRowStyles.lower_bound(aStyleRows);
437  bool bAddRange = true;
438  for ( ; rows_it != rRowStyles.end(); ++rows_it )
439  {
440  // Add the part of aStyleRows that does not overlap with r
441  if ( aStyleRows.mnStartRow < rows_it->mnStartRow )
442  {
443  RowRangeStyle aSplit = aStyleRows;
444  aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, rows_it->mnStartRow - 1);
445  rows_it = rRowStyles.insert( aSplit ).first;
446  }
447 
448  // Done if no part of aStyleRows extends beyond r
449  if ( aStyleRows.mnEndRow <= rows_it->mnEndRow )
450  {
451  bAddRange = false;
452  break;
453  }
454 
455  // Cut off the part aStyleRows that was handled above
456  aStyleRows.mnStartRow = rows_it->mnEndRow + 1;
457  }
458  if ( bAddRange )
459  rRowStyles.insert( aStyleRows );
460  }
461  }
462  }
463 }
464 
466 {
467  ScDocumentImport& rDocImport = getDocImport();
468 
469  SCTAB nStartTabInvalidatedIters(SCTAB_MAX);
470  SCTAB nEndTabInvalidatedIters(0);
471 
472  // create all array formulas
473  for( const auto& [rRange, rTokens] : maArrayFormulas )
474  {
475  finalizeArrayFormula(rRange, rTokens);
476 
477  nStartTabInvalidatedIters = std::min(rRange.aStart.Tab(), nStartTabInvalidatedIters);
478  nEndTabInvalidatedIters = std::max(rRange.aEnd.Tab(), nEndTabInvalidatedIters);
479  }
480 
481  for (SCTAB nTab = nStartTabInvalidatedIters; nTab <= nEndTabInvalidatedIters; ++nTab)
482  rDocImport.invalidateBlockPositionSet(nTab);
483 
484  // create all table operations
485  for( const auto& [rRange, rModel] : maTableOperations )
486  finalizeTableOperation( rRange, rModel );
487 
488  // write default formatting of remaining row range
490 
491  addColXfStyles();
492 
494 
495  ScDocument& rDoc = rDocImport.getDoc();
496  StylesBuffer& rStyles = getStyles();
497  ScDocumentImport::Attrs aPendingAttrParam;
498  SCCOL pendingColStart = -1;
499  SCCOL pendingColEnd = -1;
500  for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
501  {
502  SCCOL nScCol = static_cast< SCCOL >( rCol );
503 
504  // tdf#91567 Get pattern from the first row without AutoFilter
505  const ScPatternAttr* pDefPattern = nullptr;
506  bool bAutoFilter = true;
507  SCROW nScRow = 0;
508  while ( bAutoFilter && nScRow < rDoc.MaxRow() )
509  {
510  pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
511  if ( pDefPattern )
512  {
513  const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
514  bAutoFilter = pAttr->HasAutoFilter();
515  }
516  else
517  break;
518  nScRow++;
519  }
520  if ( !pDefPattern || nScRow == rDoc.MaxRow() )
521  pDefPattern = rDoc.GetDefPattern();
522 
523  Xf::AttrList aAttrs(pDefPattern);
524  for ( const auto& rRowStyle : rRowStyles )
525  {
526  Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
527 
528  if ( pXf )
529  pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow, rRowStyle.mnNumFmt.second );
530  }
531  if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
532  {
533  ScAttrEntry aEntry;
534  aEntry.nEndRow = rDoc.MaxRow();
535  aEntry.pPattern = pDefPattern;
536  rDoc.GetPool()->Put(*aEntry.pPattern);
537  aAttrs.maAttrs.push_back(aEntry);
538 
539  if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc))
540  aAttrs.mbLatinNumFmtOnly = false;
541  }
542 
543  ScDocumentImport::Attrs aAttrParam;
544  aAttrParam.mvData.swap(aAttrs.maAttrs);
545  aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
546 
547  // Compress setting the attributes, set the same set in one call.
548  if( pendingColStart != -1 && pendingColEnd == nScCol - 1 && aAttrParam == aPendingAttrParam )
549  ++pendingColEnd;
550  else
551  {
552  if( pendingColStart != -1 )
553  rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
554  pendingColStart = pendingColEnd = nScCol;
555  aPendingAttrParam = std::move( aAttrParam );
556  }
557  }
558  if( pendingColStart != -1 )
559  rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
560 
561  // merge all cached merged ranges and update right/bottom cell borders
562  for( const auto& rMergedRange : maMergedRanges )
563  applyCellMerging( rMergedRange.maRange );
564  for( const auto& rCenterFillRange : maCenterFillRanges )
565  applyCellMerging( rCenterFillRange.maRange );
566 }
567 
568 // private --------------------------------------------------------------------
569 
571  maRowRange( -1 ),
572  mnXfId( -1 )
573 {
574 }
575 
576 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
577 {
578  maRowRange = ValueRange( nRow );
579  mnXfId = nXfId;
580 }
581 
582 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
583 {
584  if( mnXfId == nXfId )
585  {
586  if( maRowRange.mnLast + 1 == nRow )
587  {
588  ++maRowRange.mnLast;
589  return true;
590  }
591  if( maRowRange.mnFirst == nRow + 1 )
592  {
593  --maRowRange.mnFirst;
594  return true;
595  }
596  }
597  return false;
598 }
599 
601  maRange( rRange ),
602  mnHorAlign( XML_TOKEN_INVALID )
603 {
604 }
605 
606 SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
607  maRange( rAddress, rAddress ),
608  mnHorAlign( nHorAlign )
609 {
610 }
611 
612 bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
613 {
614  if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
615  (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
616  {
617  maRange.aEnd.IncCol();
618  return true;
619  }
620  return false;
621 }
622 
623 void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
624 {
625  if( rTokens.hasElements() )
626  {
627  putFormulaTokens( rCellAddr, rTokens );
628  }
629 }
630 
631 
633 {
634  BinAddress aAddr(rAddr);
636  return aTokens;
637 }
638 
639 void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
640 {
641  Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
642  OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
643  if( xTokens.is() )
644  xTokens->setArrayTokens( rTokens );
645 }
646 
648 {
649  if (rModel.mbRef1Deleted)
650  return;
651 
652  if (rModel.maRef1.isEmpty())
653  return;
654 
655  if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
656  return;
657 
658  sal_Int16 nSheet = getSheetIndex();
659 
660  ScAddress aRef1( 0, 0, 0 );
661  if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
662  return;
663 
664  ScDocumentImport& rDoc = getDocImport();
665  ScTabOpParam aParam;
666 
667  ScRange aScRange(rRange);
668 
669  if (rModel.mb2dTable)
670  {
671  // Two-variable data table.
672  if (rModel.mbRef2Deleted)
673  return;
674 
675  if (rModel.maRef2.isEmpty())
676  return;
677 
678  ScAddress aRef2( 0, 0, 0 );
679  if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
680  return;
681 
682  aParam.meMode = ScTabOpParam::Both;
683 
684  aScRange.aStart.IncCol(-1);
685  aScRange.aStart.IncRow(-1);
686 
687  aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
688  aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
689 
690  // Ref1 is row input cell and Ref2 is column input cell.
691  aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
692  aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
693  rDoc.setTableOpCells(aScRange, aParam);
694 
695  return;
696  }
697 
698  // One-variable data table.
699 
700  if (rModel.mbRowTable)
701  {
702  // One-variable row input cell (horizontal).
703  aParam.meMode = ScTabOpParam::Row;
704  aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
705  aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
706  aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
707  aScRange.aStart.IncRow(-1);
708  rDoc.setTableOpCells(aScRange, aParam);
709  }
710  else
711  {
712  // One-variable column input cell (vertical).
713  aParam.meMode = ScTabOpParam::Column;
714  aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
715  aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
716  aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
717  aScRange.aStart.IncCol(-1);
718  rDoc.setTableOpCells(aScRange, aParam);
719  }
720 }
721 
723 {
724  if( rModel.mnXfId < 0 )
725  return;
726 
727  ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
728  ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
729  /* The xlsx sheet data contains row wise information.
730  * It is sufficient to check if the row range size is one
731  */
732  if (!rRangeList.empty() &&
733  *pLastRange == rModel.maCellAddr)
734  ; // do nothing - this probably bad data
735  else if (!rRangeList.empty() &&
736  pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
737  pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
738  pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
739  pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
740  {
741  pLastRange->aEnd.IncCol(); // Expand Column
742  }
743  else
744  {
745  rRangeList.push_back(ScRange(rModel.maCellAddr));
746  pLastRange = &rRangeList.back();
747  }
748 
749  if (rRangeList.size() > 1)
750  {
751  for (size_t i = rRangeList.size() - 1; i != 0; --i)
752  {
753  ScRange& rMergeRange = rRangeList[i - 1];
754  if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
755  break;
756 
757  /* Try to merge this with the previous range */
758  if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
759  pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
760  pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
761  {
762  rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
763  rRangeList.Remove(rRangeList.size() - 1);
764  break;
765  }
766  else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
767  break; // Un-necessary to check with any other rows
768  }
769  }
770  // update merged ranges for 'center across selection' and 'fill'
771  const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
772  if( !pXf )
773  return;
774 
775  sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
776  if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
777  {
778  /* start new merged range, if cell is not empty (#108781#),
779  or try to expand last range with empty cell */
780  if( rModel.mnCellType != XML_TOKEN_INVALID )
781  maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
782  else if( !maCenterFillRanges.empty() )
783  maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
784  }
785 }
786 
787 static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
788 {
789  SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
790  SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
791 
792  const SvxBoxItem* pFromItem =
793  rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
794  const SvxBoxItem* pToItem =
795  rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
796 
797  SvxBoxItem aNewItem( *pToItem );
798  aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
799  rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
800 }
801 
803 {
804  bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
805  bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
806 
807  const ScAddress& rStart = rRange.aStart;
808  const ScAddress& rEnd = rRange.aEnd;
809  ScDocument& rDoc = getScDocument();
810  // set correct right border
811  if( bMultiCol )
812  lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
813  // set correct lower border
814  if( bMultiRow )
815  lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
816  // do merge
817  if( bMultiCol || bMultiRow )
818  rDoc.DoMerge( rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row(), getSheetIndex() );
819 }
820 
821 } // namespace oox
822 
823 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void Set(const ScAddress &rAdr, bool bNewRelCol, bool bNewRelRow, bool bNewRelTab)
Definition: address.hxx:914
::std::map< BinAddress, ApiTokenSequence > maSharedFormulas
All table operations in the sheet.
sal_Int32 mnCurrRow
Buffered column spans, mapped by row index.
const_iterator lower_bound(const Value &x) const
sal_Int32 nIndex
ScAddress aStart
Definition: address.hxx:497
ScDocumentImport & getDocImport()
constexpr TypedWhichId< SvxBoxItem > ATTR_BORDER(150)
SCROW Row() const
Definition: address.hxx:274
void setDateTimeCell(const CellModel &rModel, const css::util::DateTime &rDateTime)
Inserts a date/time cell into the sheet and adjusts number format.
A 2D cell address struct for binary filters.
UnitConverter & getUnitConverter() const
Returns the measurement unit converter.
CellBlockBuffer(const WorksheetHelper &rHelper)
static void addIfNotInMyMap(const StylesBuffer &rStyles, std::map< FormatKeyPair, ScRangeList > &rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList &rRangeList)
XfRef getCellXf(sal_Int32 nXfId) const
Returns the specified cell format object.
ArrayFormulaVector maArrayFormulas
Manages all open cell blocks.
sal_Int32 mnCellType
The address of the current cell.
XfIdRowRange()
XF identifier for the row range.
const ScAddress & getMaxApiAddress() const
Returns the biggest valid cell address in the own Calc document.
MergedRangeVector maMergedRanges
Collected XF identifiers for cell rangelists.
RichStringRef getString(sal_Int32 nStringId) const
Returns the specified string.
css::uno::Reference< css::table::XCellRange > getCellRange(const ScRange &rRange) const
Returns the XCellRange interface for the passed cell range address.
ScAddress aEnd
Definition: address.hxx:498
::std::pair< sal_Int32, sal_Int32 > XfIdNumFmtKey
static bool parseDateTime(css::util::DateTime &rDateTime, std::u16string_view rString)
bool tryExpand(sal_Int32 nRow, sal_Int32 nXfId)
SC_DLLPUBLIC void ApplyAttr(SCCOL nCol, SCROW nRow, SCTAB nTab, const SfxPoolItem &rAttr)
Definition: document.cxx:4821
Accessor class to ScDocument.
StylesBuffer & getStyles() const
Returns all cell formatting objects read from the styles substream.
void putFormulaTokens(const ScAddress &rAddress, const ApiTokenSequence &rTokens)
Inserts a formula cell directly into the Calc sheet.
bool mbPendingSharedFmla
Merged cell ranges from 'center across' or 'fill' alignment.
bool mbRowTable
True = 2-dimensional data table.
SC_DLLPUBLIC ScDocumentPool * GetPool()
Definition: document.cxx:6156
double calcSerialFromDateTime(const css::util::DateTime &rDateTime) const
Returns the serial value of the passed datetime, based on current nulldate.
ApiTokenSequence resolveSharedFormula(const ScAddress &rMapKey) const
Creates a formula token array representing the shared formula with the passed identifier.
std::shared_ptr< RichString > RichStringRef
Definition: richstring.hxx:260
sal_Int32 mnXfId
Indexes of first and last row.
const ScPatternAttr * pPattern
Definition: attarray.hxx:84
bool mbRef1Deleted
True = row oriented data table.
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:891
void setBooleanCell(const CellModel &rModel, bool bValue)
Inserts a boolean cell into the sheet and adjusts number format.
void setRowFormat(sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat)
Sets default cell formatting for the specified range of rows.
void reserve(size_type amount)
void push_back(const ScRange &rRange)
Definition: rangelst.cxx:1137
bool isValidArrayRef(const ScAddress &rCellAddr)
Returns true, if the passed cell address is valid for an array formula.
void putRichString(const ScAddress &rAddress, const RichString &rString, const oox::xls::Font *pFirstPortionFont)
Inserts a rich-string cell directly into the Calc sheet.
SCTAB Tab() const
Definition: address.hxx:283
void SetRow(SCROW nRowP)
Definition: address.hxx:287
const editeng::SvxBorderLine * GetLine(SvxBoxItemLine nLine) const
ScRefAddress aRefRowCell
Definition: paramisc.hxx:51
sal_Int32 mnXfId
Data type of the cell value.
XML_TOKEN_INVALID
std::pair< sal_Int32, sal_Int32 > FormatKeyPair
size_type size() const
SC_DLLPUBLIC const ScPatternAttr * GetPattern(SCCOL nCol, SCROW nRow, SCTAB nTab) const
Definition: document.cxx:4799
void applyPatternToAttrList(AttrList &rAttrs, SCROW nRow1, SCROW nRow2, sal_Int32 nForceScNumFmt)
void setValueCell(const CellModel &rModel, double fValue)
Inserts a value cell into the sheet.
void setMergedRange(const ScRange &rRange)
Merges the cells in the passed cell range.
SC_DLLPUBLIC const SfxPoolItem * GetAttr(SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich) const
Definition: document.cxx:4764
bool empty() const
Definition: rangelst.hxx:88
void Remove(size_t nPos)
Definition: rangelst.cxx:1093
ScAddress maSharedFmlaAddr
Maps shared formula base address to defined name token index.
void setDateCell(const CellModel &rModel, const OUString &rDateString)
Inserts an ISO 8601 date cell into the sheet.
Parameter for data table aka multiple operations.
Definition: paramisc.hxx:45
CellFormulaModel()
Identifier of a shared formula (OOXML only).
Stores basic data about cell values and formatting.
std::vector< TableOperation > maTableOperations
All array formulas in the sheet.
int i
void setCellFormula(const ScAddress &rAddress, const OUString &)
CellModel()
True = show phonetic text.
void createTableOperation(const ScRange &rRange, const DataTableModel &rModel)
Sets a multiple table operation to the passed range.
std::vector< ScAttrEntry > mvData
MergedRangeVector maCenterFillRanges
Merged cell ranges.
void IncCol(SCCOL nDelta=1)
Definition: address.hxx:316
css::beans::Pair< css::table::CellAddress, sal_Bool > ApiSpecialTokenInfo
Contains the base address and type of a special token representing an array formula or a shared formu...
const css::uno::Reference< css::sheet::XSpreadsheetDocument > & getDocument() const
Returns a reference to the source/target spreadsheet document model.
sal_Int16 SCCOL
Definition: types.hxx:21
std::map< sal_Int32, std::vector< ValueRange > > maXfIdRowRangeList
True = maSharedFmlaAddr and maSharedBaseAddr are valid.
std::map< XfIdNumFmtKey, ScRangeList > maXfIdRangeLists
Cached XF identifier for a range of rows.
bool extractSpecialTokenInfo(ApiSpecialTokenInfo &orTokenInfo, const ApiTokenSequence &rTokens) const
Tries to extract information about a special token used for array formulas, shared formulas...
::std::vector< ScRange >::const_iterator end() const
Definition: rangelst.hxx:98
void setColSpans(sal_Int32 nRow, const ValueRangeSet &rColSpans)
Sets column span information for a row.
ScRefAddress aRefFormulaCell
Definition: paramisc.hxx:49
size_t size() const
Definition: rangelst.hxx:89
bool isValidSharedRef(const ScAddress &rCellAddr)
Returns true, if the passed cell address is valid for a shared formula.
void IncRow(SCROW nDelta=1)
Definition: address.hxx:312
SfxItemSet & GetItemSet()
Definition: patattr.hxx:155
const Alignment & getAlignment() const
Returns the alignment data of this style.
const_iterator end() const
CellBlockBuffer maCellBlocks
Stores cell styles by column ( in row ranges )
void setErrorCell(const CellModel &rModel, const OUString &rErrorCode)
Inserts an error cell from the passed error code into the sheet.
css::uno::Reference< css::table::XCell > getCell(const ScAddress &rAddress) const
Returns the XCell interface for the passed cell address.
static void lcl_SetBorderLine(ScDocument &rDoc, const ScRange &rRange, SCTAB nScTab, SvxBoxItemLine nLine)
void setTableOpCells(const ScRange &rRange, const ScTabOpParam &rParam)
ScRange & back()
Definition: rangelst.hxx:94
FontRef getFontFromCellXf(sal_Int32 nXfId) const
Returns the font object of the specified cell XF.
MergedRange(const ScRange &rRange)
Horizontal alignment in the range.
SCCOL Col() const
Definition: address.hxx:279
void invalidateBlockPositionSet(SCTAB nTab)
DataTableModel()
True = second reference cell deleted.
css::uno::Sequence< ApiToken > ApiTokenSequence
const AlignmentModel & getModel() const
Returns the alignment model structure.
static const MapType::mapped_type * getMapElement(const MapType &rMap, const typename MapType::key_type &rKey)
constexpr TypedWhichId< ScMergeFlagAttr > ATTR_MERGE_FLAG(145)
sal_Int32 SCROW
Definition: types.hxx:17
void setBlankCell(const CellModel &rModel)
Inserts a blank cell (with formatting) into the sheet.
ScAddress maSharedBaseAddr
Address of a cell containing a pending shared formula.
void setCellFormat(const CellModel &rModel)
Processes the cell formatting data of the passed cell.
void setColSpans(sal_Int32 nRow, const ValueRangeSet &rColSpans)
Sets column span information for a row.
Converter for cell addresses and cell ranges for OOXML and BIFF filters.
unsigned char sal_uInt8
void setStringCell(const ScAddress &rPos, const OUString &rStr)
ScRefAddress aRefColCell
Definition: paramisc.hxx:52
SvxBoxItemLine
SCTAB getSheetIndex() const
Returns the index of the current sheet.
const T & Put(std::unique_ptr< T > xItem, sal_uInt16 nWhich=0)
void setAttrEntries(SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, Attrs &&rAttrs)
Set an array of cell attributes to specified range of columns.
Represents a cell format or a cell style (called XF, extended format).
SharedStringsBuffer & getSharedStrings() const
Returns the shared strings read from the shared strings substream.
SC_DLLPUBLIC ScPatternAttr * GetDefPattern() const
Definition: document.cxx:6151
bool mbRef2Deleted
True = first reference cell deleted.
void applyCellMerging(const ScRange &rRange)
Writes all cell formatting attributes to the passed cell range list.
FormulaParser & getFormulaParser() const
Returns a shared import formula parser (import filter only!).
void createArrayFormula(const ScRange &rRange, const ApiTokenSequence &rTokens)
Inserts the passed token array as array formula.
::std::vector< RowRangeStyle > TmpRowStyles
void finalizeTableOperation(const ScRange &rRange, const DataTableModel &rModel)
Inserts the passed table operation into the sheet.
const ValueRangeVector & getRanges() const
static bool isLatinScript(const ScPatternAttr &rPat, ScDocument &rDoc)
Check if the attribute pattern has a number format that only produces latin script output...
Definition: numformat.cxx:31
#define SAL_WARN(area, stream)
XfIdRowRange maXfIdRowRange
Base address of the pending shared formula.
::std::vector< ScRange >::const_iterator begin() const
Definition: rangelst.hxx:97
OUString maRef2
First reference cell for table operations.
ScRefAddress aRefFormulaEnd
Definition: paramisc.hxx:50
void finalizeImport()
Final processing after the sheet has been imported.
void setNumericCell(const ScAddress &rPos, double fVal)
::std::map< sal_Int32, TmpRowStyles > TmpColStyles
bool HasAutoFilter() const
Definition: attrib.hxx:103
void createSharedFormula(const ScAddress &rRange, const ApiTokenSequence &rTokens)
std::pair< const_iterator, bool > insert(Value &&x)
void finalizeArrayFormula(const ScRange &rRange, const ApiTokenSequence &rTokens) const
Inserts the passed array formula into the sheet.
bool mb2dTable
Second reference cell for table operations.
const SfxPoolItem * GetItem(sal_uInt16 nWhich, bool bSearchInParent=true) const
const SCTAB SCTAB_MAX
Definition: address.hxx:57
SCROW nEndRow
Definition: attarray.hxx:83
void setFormulaCell(const CellModel &rModel, const ApiTokenSequence &rTokens)
Inserts a formula cell into the sheet.
Stores data about table operations.
void set(sal_Int32 nRow, sal_Int32 nXfId)
void setStringCell(const CellModel &rModel, const OUString &rText)
Inserts a simple string cell into the sheet.
SC_DLLPUBLIC void DoMerge(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, bool bDeleteCaptions=true)
Definition: documen3.cxx:2073
SheetDataBuffer(const WorksheetHelper &rHelper)
bool setProperty(sal_Int32 nPropId, const Type &rValue)
B2DRange maRange
sal_Int16 SCTAB
Definition: types.hxx:22
std::vector< Value >::const_iterator const_iterator
void setCellFormula(const ScAddress &rCellAddr, const ApiTokenSequence &rTokens)
Sets the passed formula token array into a cell.
AddressConverter & getAddressConverter() const
Returns the converter for string to cell address/range conversion.
FormulaBuffer & getFormulaBuffer() const
bool tryExpand(const ScAddress &rAddress, sal_Int32 nHorAlign)
sal_Int32 mnLast
bool m_bDetectedRangeSegmentation false
std::vector< ScAttrEntry > maAttrs
void SetLine(const editeng::SvxBorderLine *pNew, SvxBoxItemLine nLine)
ScDocument & getDoc()