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) && (maColSpans.count( nRow ) == 0) )
105  {
106  maColSpans[ nRow ] = rColSpans.getRanges();
107  mnCurrRow = nRow;
108  }
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 
349 void SheetDataBuffer::addColXfStyle( sal_Int32 nXfId, sal_Int32 nFormatId, const ScRange& rAddress, bool bProcessRowRange )
350 {
351  RowRangeStyle aStyleRows;
352  aStyleRows.mnNumFmt.first = nXfId;
353  aStyleRows.mnNumFmt.second = nFormatId;
354  aStyleRows.mnStartRow = rAddress.aStart.Row();
355  aStyleRows.mnEndRow = rAddress.aEnd.Row();
356  for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol )
357  {
358  if ( !bProcessRowRange )
359  maStylesPerColumn[ nCol ].insert( aStyleRows );
360  else
361  {
362  RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
363  // Reset row range for each column
364  aStyleRows.mnStartRow = rAddress.aStart.Row();
365  aStyleRows.mnEndRow = rAddress.aEnd.Row();
366 
367  // If aStyleRows includes rows already allocated to a style
368  // in rRowStyles, then we need to split it into parts.
369  // ( to occupy only rows that have no style definition)
370 
371  // Start iterating at the first element that is not completely before aStyleRows
372  RowStyles::iterator rows_it = rRowStyles.lower_bound(aStyleRows);
373  RowStyles::iterator rows_end = rRowStyles.end();
374  bool bAddRange = true;
375  for ( ; rows_it != rows_end; ++rows_it )
376  {
377  const RowRangeStyle& r = *rows_it;
378 
379  // Add the part of aStyleRows that does not overlap with r
380  if ( aStyleRows.mnStartRow < r.mnStartRow )
381  {
382  RowRangeStyle aSplit = aStyleRows;
383  aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, r.mnStartRow - 1);
384  // Insert with hint that aSplit comes directly before the current position
385  rRowStyles.insert( rows_it, aSplit );
386  }
387 
388  // Done if no part of aStyleRows extends beyond r
389  if ( aStyleRows.mnEndRow <= r.mnEndRow )
390  {
391  bAddRange = false;
392  break;
393  }
394 
395  // Cut off the part aStyleRows that was handled above
396  aStyleRows.mnStartRow = r.mnEndRow + 1;
397  }
398  if ( bAddRange )
399  rRowStyles.insert( aStyleRows );
400  }
401  }
402 }
403 
405 {
406  // create all array formulas
407  for( const auto& [rRange, rTokens] : maArrayFormulas )
408  finalizeArrayFormula( rRange, rTokens );
409 
410  // create all table operations
411  for( const auto& [rRange, rModel] : maTableOperations )
412  finalizeTableOperation( rRange, rModel );
413 
414  // write default formatting of remaining row range
416 
417  std::map< FormatKeyPair, ScRangeList > rangeStyleListMap;
418  for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists )
419  {
420  addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList );
421  }
422  // gather all ranges that have the same style and apply them in bulk
423  for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap )
424  {
425  for (size_t i = 0, nSize = rRanges.size(); i < nSize; ++i)
426  addColXfStyle( rFormatKeyPair.first, rFormatKeyPair.second, rRanges[i]);
427  }
428 
429  for ( const auto& [rXfId, rRowRangeList] : maXfIdRowRangeList )
430  {
431  if ( rXfId == -1 ) // it's a dud skip it
432  continue;
433  AddressConverter& rAddrConv = getAddressConverter();
434  // get all row ranges for id
435  for ( const auto& rRange : rRowRangeList )
436  {
437  ScRange aRange( 0, rRange.mnFirst, getSheetIndex(),
438  rAddrConv.getMaxApiAddress().Col(), rRange.mnLast, getSheetIndex() );
439 
440  addColXfStyle( rXfId, -1, aRange, true );
441  }
442  }
443 
444  ScDocumentImport& rDocImport = getDocImport();
445  ScDocument& rDoc = rDocImport.getDoc();
446  StylesBuffer& rStyles = getStyles();
447  for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
448  {
449  SCCOL nScCol = static_cast< SCCOL >( rCol );
450 
451  // tdf#91567 Get pattern from the first row without AutoFilter
452  const ScPatternAttr* pDefPattern = nullptr;
453  bool bAutoFilter = true;
454  SCROW nScRow = 0;
455  while ( bAutoFilter && nScRow < rDoc.MaxRow() )
456  {
457  pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
458  if ( pDefPattern )
459  {
460  const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
461  bAutoFilter = pAttr->HasAutoFilter();
462  }
463  else
464  break;
465  nScRow++;
466  }
467  if ( !pDefPattern || nScRow == rDoc.MaxRow() )
468  pDefPattern = rDoc.GetDefPattern();
469 
470  Xf::AttrList aAttrs(pDefPattern);
471  for ( const auto& rRowStyle : rRowStyles )
472  {
473  Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
474 
475  if ( pXf )
476  pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow, rRowStyle.mnNumFmt.second );
477  }
478  if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
479  {
480  ScAttrEntry aEntry;
481  aEntry.nEndRow = rDoc.MaxRow();
482  aEntry.pPattern = pDefPattern;
483  rDoc.GetPool()->Put(*aEntry.pPattern);
484  aAttrs.maAttrs.push_back(aEntry);
485 
486  if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc))
487  aAttrs.mbLatinNumFmtOnly = false;
488  }
489 
490  ScDocumentImport::Attrs aAttrParam;
491  aAttrParam.mvData.swap(aAttrs.maAttrs);
492  aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
493 
494  rDocImport.setAttrEntries(getSheetIndex(), nScCol, std::move(aAttrParam));
495  }
496 
497  // merge all cached merged ranges and update right/bottom cell borders
498  for( const auto& rMergedRange : maMergedRanges )
499  applyCellMerging( rMergedRange.maRange );
500  for( const auto& rCenterFillRange : maCenterFillRanges )
501  applyCellMerging( rCenterFillRange.maRange );
502 }
503 
504 // private --------------------------------------------------------------------
505 
507  maRowRange( -1 ),
508  mnXfId( -1 )
509 {
510 }
511 
512 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
513 {
514  maRowRange = ValueRange( nRow );
515  mnXfId = nXfId;
516 }
517 
518 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
519 {
520  if( mnXfId == nXfId )
521  {
522  if( maRowRange.mnLast + 1 == nRow )
523  {
524  ++maRowRange.mnLast;
525  return true;
526  }
527  if( maRowRange.mnFirst == nRow + 1 )
528  {
529  --maRowRange.mnFirst;
530  return true;
531  }
532  }
533  return false;
534 }
535 
537  maRange( rRange ),
538  mnHorAlign( XML_TOKEN_INVALID )
539 {
540 }
541 
542 SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
543  maRange( rAddress, rAddress ),
544  mnHorAlign( nHorAlign )
545 {
546 }
547 
548 bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
549 {
550  if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
551  (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
552  {
553  maRange.aEnd.IncCol();
554  return true;
555  }
556  return false;
557 }
558 
559 void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
560 {
561  if( rTokens.hasElements() )
562  {
563  putFormulaTokens( rCellAddr, rTokens );
564  }
565 }
566 
567 
569 {
570  BinAddress aAddr(rAddr);
572  return aTokens;
573 }
574 
575 void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
576 {
577  Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
578  OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
579  if( xTokens.is() )
580  xTokens->setArrayTokens( rTokens );
581 }
582 
584 {
585  if (rModel.mbRef1Deleted)
586  return;
587 
588  if (rModel.maRef1.isEmpty())
589  return;
590 
591  if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
592  return;
593 
594  sal_Int16 nSheet = getSheetIndex();
595 
596  ScAddress aRef1( 0, 0, 0 );
597  if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
598  return;
599 
600  ScDocumentImport& rDoc = getDocImport();
601  ScTabOpParam aParam;
602 
603  ScRange aScRange(rRange);
604 
605  if (rModel.mb2dTable)
606  {
607  // Two-variable data table.
608  if (rModel.mbRef2Deleted)
609  return;
610 
611  if (rModel.maRef2.isEmpty())
612  return;
613 
614  ScAddress aRef2( 0, 0, 0 );
615  if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
616  return;
617 
618  aParam.meMode = ScTabOpParam::Both;
619 
620  aScRange.aStart.IncCol(-1);
621  aScRange.aStart.IncRow(-1);
622 
623  aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
624  aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
625 
626  // Ref1 is row input cell and Ref2 is column input cell.
627  aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
628  aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
629  rDoc.setTableOpCells(aScRange, aParam);
630 
631  return;
632  }
633 
634  // One-variable data table.
635 
636  if (rModel.mbRowTable)
637  {
638  // One-variable row input cell (horizontal).
639  aParam.meMode = ScTabOpParam::Row;
640  aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
641  aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
642  aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
643  aScRange.aStart.IncRow(-1);
644  rDoc.setTableOpCells(aScRange, aParam);
645  }
646  else
647  {
648  // One-variable column input cell (vertical).
649  aParam.meMode = ScTabOpParam::Column;
650  aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
651  aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
652  aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
653  aScRange.aStart.IncCol(-1);
654  rDoc.setTableOpCells(aScRange, aParam);
655  }
656 }
657 
659 {
660  if( rModel.mnXfId < 0 )
661  return;
662 
663  ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
664  ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
665  /* The xlsx sheet data contains row wise information.
666  * It is sufficient to check if the row range size is one
667  */
668  if (!rRangeList.empty() &&
669  *pLastRange == rModel.maCellAddr)
670  ; // do nothing - this probably bad data
671  else if (!rRangeList.empty() &&
672  pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
673  pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
674  pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
675  pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
676  {
677  pLastRange->aEnd.IncCol(); // Expand Column
678  }
679  else
680  {
681  rRangeList.push_back(ScRange(rModel.maCellAddr));
682  pLastRange = &rRangeList.back();
683  }
684 
685  if (rRangeList.size() > 1)
686  {
687  for (size_t i = rRangeList.size() - 1; i != 0; --i)
688  {
689  ScRange& rMergeRange = rRangeList[i - 1];
690  if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
691  break;
692 
693  /* Try to merge this with the previous range */
694  if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
695  pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
696  pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
697  {
698  rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
699  rRangeList.Remove(rRangeList.size() - 1);
700  break;
701  }
702  else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
703  break; // Un-necessary to check with any other rows
704  }
705  }
706  // update merged ranges for 'center across selection' and 'fill'
707  const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
708  if( !pXf )
709  return;
710 
711  sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
712  if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
713  {
714  /* start new merged range, if cell is not empty (#108781#),
715  or try to expand last range with empty cell */
716  if( rModel.mnCellType != XML_TOKEN_INVALID )
717  maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
718  else if( !maCenterFillRanges.empty() )
719  maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
720  }
721 }
722 
723 static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
724 {
725  SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
726  SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
727 
728  const SvxBoxItem* pFromItem =
729  rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
730  const SvxBoxItem* pToItem =
731  rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
732 
733  SvxBoxItem aNewItem( *pToItem );
734  aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
735  rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
736 }
737 
739 {
740  bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
741  bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
742 
743  const ScAddress& rStart = rRange.aStart;
744  const ScAddress& rEnd = rRange.aEnd;
745  ScDocument& rDoc = getScDocument();
746  // set correct right border
747  if( bMultiCol )
748  lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
749  // set correct lower border
750  if( bMultiRow )
751  lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
752  // do merge
753  if( bMultiCol || bMultiRow )
754  rDoc.DoMerge( getSheetIndex(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row() );
755 }
756 
757 } // namespace oox
758 
759 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void addColXfStyle(sal_Int32 nXfId, sal_Int32 nFormatId, const ScRange &rAddress, bool bProcessRowRange=false)
void Set(const ScAddress &rAdr, bool bNewRelCol, bool bNewRelRow, bool bNewRelTab)
Definition: address.hxx:921
::std::map< BinAddress, ApiTokenSequence > maSharedFormulas
All table operations in the sheet.
sal_Int32 mnCurrRow
Buffered column spans, mapped by row index.
sal_Int32 nIndex
ScAddress aStart
Definition: address.hxx:500
ScDocumentImport & getDocImport()
constexpr TypedWhichId< SvxBoxItem > ATTR_BORDER(150)
SCROW Row() const
Definition: address.hxx:262
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:501
::std::pair< sal_Int32, sal_Int32 > XfIdNumFmtKey
bool tryExpand(sal_Int32 nRow, sal_Int32 nXfId)
SC_DLLPUBLIC void ApplyAttr(SCCOL nCol, SCROW nRow, SCTAB nTab, const SfxPoolItem &rAttr)
Definition: document.cxx:4757
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:6061
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:261
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:873
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 push_back(const ScRange &rRange)
Definition: rangelst.cxx:1144
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.
::std::set< RowRangeStyle, StyleRowRangeComp > RowStyles
SCTAB Tab() const
Definition: address.hxx:271
void SetRow(SCROW nRowP)
Definition: address.hxx:275
const editeng::SvxBorderLine * GetLine(SvxBoxItemLine nLine) const
ScRefAddress aRefRowCell
Definition: paramisc.hxx:52
sal_Int32 mnXfId
Data type of the cell value.
std::pair< sal_Int32, sal_Int32 > FormatKeyPair
SC_DLLPUBLIC const ScPatternAttr * GetPattern(SCCOL nCol, SCROW nRow, SCTAB nTab) const
Definition: document.cxx:4735
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:4714
bool empty() const
Definition: rangelst.hxx:89
void Remove(size_t nPos)
Definition: rangelst.cxx:1100
ScAddress maSharedFmlaAddr
Maps shared formula base address to defined name token index.
static bool parseDateTime(css::util::DateTime &rDateTime, const OUString &rString)
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:46
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:304
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:22
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:50
size_t size() const
Definition: rangelst.hxx:90
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:300
XML_TOKEN_INVALID
const Alignment & getAlignment() const
Returns the alignment data of this style.
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:2052
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:95
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:267
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:18
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:53
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:6056
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:33
#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:51
void finalizeImport()
Final processing after the sheet has been imported.
void setNumericCell(const ScAddress &rPos, double fVal)
bool HasAutoFilter() const
Definition: attrib.hxx:104
void createSharedFormula(const ScAddress &rRange, const ApiTokenSequence &rTokens)
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:23
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()