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  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 
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 
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 
445 {
446  // create all array formulas
447  for( const auto& [rRange, rTokens] : maArrayFormulas )
448  finalizeArrayFormula( rRange, rTokens );
449 
450  // create all table operations
451  for( const auto& [rRange, rModel] : maTableOperations )
452  finalizeTableOperation( rRange, rModel );
453 
454  // write default formatting of remaining row range
456 
457  addColXfStyles();
458 
460 
461  ScDocumentImport& rDocImport = getDocImport();
462  ScDocument& rDoc = rDocImport.getDoc();
463  StylesBuffer& rStyles = getStyles();
464  for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
465  {
466  SCCOL nScCol = static_cast< SCCOL >( rCol );
467 
468  // tdf#91567 Get pattern from the first row without AutoFilter
469  const ScPatternAttr* pDefPattern = nullptr;
470  bool bAutoFilter = true;
471  SCROW nScRow = 0;
472  while ( bAutoFilter && nScRow < rDoc.MaxRow() )
473  {
474  pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
475  if ( pDefPattern )
476  {
477  const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
478  bAutoFilter = pAttr->HasAutoFilter();
479  }
480  else
481  break;
482  nScRow++;
483  }
484  if ( !pDefPattern || nScRow == rDoc.MaxRow() )
485  pDefPattern = rDoc.GetDefPattern();
486 
487  Xf::AttrList aAttrs(pDefPattern);
488  for ( const auto& rRowStyle : rRowStyles )
489  {
490  Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
491 
492  if ( pXf )
493  pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow, rRowStyle.mnNumFmt.second );
494  }
495  if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
496  {
497  ScAttrEntry aEntry;
498  aEntry.nEndRow = rDoc.MaxRow();
499  aEntry.pPattern = pDefPattern;
500  rDoc.GetPool()->Put(*aEntry.pPattern);
501  aAttrs.maAttrs.push_back(aEntry);
502 
503  if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc))
504  aAttrs.mbLatinNumFmtOnly = false;
505  }
506 
507  ScDocumentImport::Attrs aAttrParam;
508  aAttrParam.mvData.swap(aAttrs.maAttrs);
509  aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
510 
511  rDocImport.setAttrEntries(getSheetIndex(), nScCol, std::move(aAttrParam));
512  }
513 
514  // merge all cached merged ranges and update right/bottom cell borders
515  for( const auto& rMergedRange : maMergedRanges )
516  applyCellMerging( rMergedRange.maRange );
517  for( const auto& rCenterFillRange : maCenterFillRanges )
518  applyCellMerging( rCenterFillRange.maRange );
519 }
520 
521 // private --------------------------------------------------------------------
522 
524  maRowRange( -1 ),
525  mnXfId( -1 )
526 {
527 }
528 
529 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
530 {
531  maRowRange = ValueRange( nRow );
532  mnXfId = nXfId;
533 }
534 
535 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
536 {
537  if( mnXfId == nXfId )
538  {
539  if( maRowRange.mnLast + 1 == nRow )
540  {
541  ++maRowRange.mnLast;
542  return true;
543  }
544  if( maRowRange.mnFirst == nRow + 1 )
545  {
546  --maRowRange.mnFirst;
547  return true;
548  }
549  }
550  return false;
551 }
552 
554  maRange( rRange ),
555  mnHorAlign( XML_TOKEN_INVALID )
556 {
557 }
558 
559 SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
560  maRange( rAddress, rAddress ),
561  mnHorAlign( nHorAlign )
562 {
563 }
564 
565 bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
566 {
567  if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
568  (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
569  {
570  maRange.aEnd.IncCol();
571  return true;
572  }
573  return false;
574 }
575 
576 void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
577 {
578  if( rTokens.hasElements() )
579  {
580  putFormulaTokens( rCellAddr, rTokens );
581  }
582 }
583 
584 
586 {
587  BinAddress aAddr(rAddr);
589  return aTokens;
590 }
591 
592 void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
593 {
594  Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
595  OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
596  if( xTokens.is() )
597  xTokens->setArrayTokens( rTokens );
598 }
599 
601 {
602  if (rModel.mbRef1Deleted)
603  return;
604 
605  if (rModel.maRef1.isEmpty())
606  return;
607 
608  if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
609  return;
610 
611  sal_Int16 nSheet = getSheetIndex();
612 
613  ScAddress aRef1( 0, 0, 0 );
614  if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
615  return;
616 
617  ScDocumentImport& rDoc = getDocImport();
618  ScTabOpParam aParam;
619 
620  ScRange aScRange(rRange);
621 
622  if (rModel.mb2dTable)
623  {
624  // Two-variable data table.
625  if (rModel.mbRef2Deleted)
626  return;
627 
628  if (rModel.maRef2.isEmpty())
629  return;
630 
631  ScAddress aRef2( 0, 0, 0 );
632  if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
633  return;
634 
635  aParam.meMode = ScTabOpParam::Both;
636 
637  aScRange.aStart.IncCol(-1);
638  aScRange.aStart.IncRow(-1);
639 
640  aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
641  aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
642 
643  // Ref1 is row input cell and Ref2 is column input cell.
644  aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
645  aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
646  rDoc.setTableOpCells(aScRange, aParam);
647 
648  return;
649  }
650 
651  // One-variable data table.
652 
653  if (rModel.mbRowTable)
654  {
655  // One-variable row input cell (horizontal).
656  aParam.meMode = ScTabOpParam::Row;
657  aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
658  aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
659  aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
660  aScRange.aStart.IncRow(-1);
661  rDoc.setTableOpCells(aScRange, aParam);
662  }
663  else
664  {
665  // One-variable column input cell (vertical).
666  aParam.meMode = ScTabOpParam::Column;
667  aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
668  aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
669  aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
670  aScRange.aStart.IncCol(-1);
671  rDoc.setTableOpCells(aScRange, aParam);
672  }
673 }
674 
676 {
677  if( rModel.mnXfId < 0 )
678  return;
679 
680  ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
681  ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
682  /* The xlsx sheet data contains row wise information.
683  * It is sufficient to check if the row range size is one
684  */
685  if (!rRangeList.empty() &&
686  *pLastRange == rModel.maCellAddr)
687  ; // do nothing - this probably bad data
688  else if (!rRangeList.empty() &&
689  pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
690  pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
691  pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
692  pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
693  {
694  pLastRange->aEnd.IncCol(); // Expand Column
695  }
696  else
697  {
698  rRangeList.push_back(ScRange(rModel.maCellAddr));
699  pLastRange = &rRangeList.back();
700  }
701 
702  if (rRangeList.size() > 1)
703  {
704  for (size_t i = rRangeList.size() - 1; i != 0; --i)
705  {
706  ScRange& rMergeRange = rRangeList[i - 1];
707  if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
708  break;
709 
710  /* Try to merge this with the previous range */
711  if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
712  pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
713  pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
714  {
715  rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
716  rRangeList.Remove(rRangeList.size() - 1);
717  break;
718  }
719  else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
720  break; // Un-necessary to check with any other rows
721  }
722  }
723  // update merged ranges for 'center across selection' and 'fill'
724  const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
725  if( !pXf )
726  return;
727 
728  sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
729  if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
730  {
731  /* start new merged range, if cell is not empty (#108781#),
732  or try to expand last range with empty cell */
733  if( rModel.mnCellType != XML_TOKEN_INVALID )
734  maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
735  else if( !maCenterFillRanges.empty() )
736  maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
737  }
738 }
739 
740 static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
741 {
742  SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
743  SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
744 
745  const SvxBoxItem* pFromItem =
746  rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
747  const SvxBoxItem* pToItem =
748  rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
749 
750  SvxBoxItem aNewItem( *pToItem );
751  aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
752  rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
753 }
754 
756 {
757  bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
758  bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
759 
760  const ScAddress& rStart = rRange.aStart;
761  const ScAddress& rEnd = rRange.aEnd;
762  ScDocument& rDoc = getScDocument();
763  // set correct right border
764  if( bMultiCol )
765  lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
766  // set correct lower border
767  if( bMultiRow )
768  lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
769  // do merge
770  if( bMultiCol || bMultiRow )
771  rDoc.DoMerge( getSheetIndex(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row() );
772 }
773 
774 } // namespace oox
775 
776 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void Set(const ScAddress &rAdr, bool bNewRelCol, bool bNewRelRow, bool bNewRelTab)
Definition: address.hxx:920
::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:499
ScDocumentImport & getDocImport()
constexpr TypedWhichId< SvxBoxItem > ATTR_BORDER(150)
SCROW Row() const
Definition: address.hxx:261
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.
const SfxItemSet & GetItemSet() const
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:500
::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:4788
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:6087
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.
void setAttrEntries(SCTAB nTab, SCCOL nCol, Attrs &&rAttrs)
Set an array of cell attributes to specified column.
const ScPatternAttr * pPattern
Definition: attarray.hxx:83
bool mbRef1Deleted
True = row oriented data table.
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:872
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:1142
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:270
void SetRow(SCROW nRowP)
Definition: address.hxx:274
const editeng::SvxBorderLine * GetLine(SvxBoxItemLine nLine) const
ScRefAddress aRefRowCell
Definition: paramisc.hxx:51
sal_Int32 mnXfId
Data type of the cell value.
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:4766
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:4745
bool empty() const
Definition: rangelst.hxx:88
void Remove(size_t nPos)
Definition: rangelst.cxx:1098
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:303
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...
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:299
XML_TOKEN_INVALID
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.
SC_DLLPUBLIC void DoMerge(SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bDeleteCaptions=true)
Definition: documen3.cxx:2068
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:266
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)
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:6082
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.
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.
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)
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
SCROW nEndRow
Definition: attarray.hxx:82
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.
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
std::vector< ScAttrEntry > maAttrs
void SetLine(const editeng::SvxBorderLine *pNew, SvxBoxItemLine nLine)
ScDocument & getDoc()