LibreOffice Module sc (master)  1
column2.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <column.hxx>
21 #include <docsh.hxx>
22 #include <scitems.hxx>
23 #include <formulacell.hxx>
24 #include <document.hxx>
25 #include <drwlayer.hxx>
26 #include <attarray.hxx>
27 #include <patattr.hxx>
28 #include <cellform.hxx>
29 #include <editutil.hxx>
30 #include <subtotal.hxx>
31 #include <markdata.hxx>
32 #include <fillinfo.hxx>
33 #include <segmenttree.hxx>
34 #include <docparam.hxx>
35 #include <cellvalue.hxx>
36 #include <tokenarray.hxx>
37 #include <formulagroup.hxx>
38 #include <listenercontext.hxx>
39 #include <mtvcellfunc.hxx>
40 #include <progress.hxx>
41 #include <scmatrix.hxx>
42 #include <rowheightcontext.hxx>
43 #include <tokenstringcontext.hxx>
44 
45 #include <editeng/eeitem.hxx>
46 #include <o3tl/safeint.hxx>
47 #include <o3tl/unit_conversion.hxx>
48 #include <svx/algitem.hxx>
49 #include <editeng/editobj.hxx>
50 #include <editeng/editstat.hxx>
52 #include <editeng/fhgtitem.hxx>
53 #include <svx/rotmodit.hxx>
54 #include <editeng/unolingu.hxx>
55 #include <editeng/justifyitem.hxx>
56 #include <svl/zforlist.hxx>
57 #include <svl/broadcast.hxx>
58 #include <vcl/outdev.hxx>
59 #include <formula/errorcodes.hxx>
60 #include <formula/vectortoken.hxx>
61 
62 #include <algorithm>
63 #include <limits>
64 #include <memory>
65 #include <numeric>
66 
67 #include <math.h>
68 
69 // factor from font size to optimal cell height (text width)
70 #define SC_ROT_BREAK_FACTOR 6
71 
72 static bool IsAmbiguousScript( SvtScriptType nScript )
73 {
74  //TODO: move to a header file
75  return ( nScript != SvtScriptType::LATIN &&
76  nScript != SvtScriptType::ASIAN &&
77  nScript != SvtScriptType::COMPLEX );
78 }
79 
80 // Data operations
81 
83  SCROW nRow, OutputDevice* pDev, double nPPTX, double nPPTY,
84  const Fraction& rZoomX, const Fraction& rZoomY,
85  bool bWidth, const ScNeededSizeOptions& rOptions,
86  const ScPatternAttr** ppPatternChange, bool bInPrintTwips ) const
87 {
88  // If bInPrintTwips is set, the size calculated should be in print twips,
89  // else it should be in pixels.
90 
91  // Switch unit to MapTwip instead ? (temporarily and then revert before exit).
92  if (bInPrintTwips)
93  assert(pDev->GetMapMode().GetMapUnit() == MapUnit::MapTwip);
94 
95  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
96  sc::CellStoreType::const_iterator it = aPos.first;
97  if (it == maCells.end() || it->type == sc::element_type_empty)
98  // Empty cell, or invalid row.
99  return 0;
100 
101  tools::Long nValue = 0;
102  ScRefCellValue aCell = GetCellValue(it, aPos.second);
103  double nPPT = bWidth ? nPPTX : nPPTY;
104 
105  auto conditionalScaleFunc = [bInPrintTwips](tools::Long nMeasure, double fScale) {
106  return bInPrintTwips ? nMeasure : static_cast<tools::Long>(nMeasure * fScale);
107  };
108 
109  const ScPatternAttr* pPattern = rOptions.pPattern;
110  if (!pPattern)
111  pPattern = pAttrArray->GetPattern( nRow );
112 
113  // merged?
114  // Do not merge in conditional formatting
115 
116  const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
117  const ScMergeFlagAttr* pFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
118 
119  if ( bWidth )
120  {
121  if ( pFlag->IsHorOverlapped() )
122  return 0;
123  if ( rOptions.bSkipMerged && pMerge->GetColMerge() > 1 )
124  return 0;
125  }
126  else
127  {
128  if ( pFlag->IsVerOverlapped() )
129  return 0;
130  if ( rOptions.bSkipMerged && pMerge->GetRowMerge() > 1 )
131  return 0;
132  }
133 
134  // conditional formatting
135  ScDocument& rDocument = GetDoc();
136  const SfxItemSet* pCondSet = rDocument.GetCondResult( nCol, nRow, nTab );
137 
138  //The pPattern may change in GetCondResult
139  if (aCell.meType == CELLTYPE_FORMULA)
140  {
141  pPattern = pAttrArray->GetPattern( nRow );
142  if (ppPatternChange)
143  *ppPatternChange = pPattern;
144  }
145  // line break?
146 
147  const SfxPoolItem* pCondItem;
148  SvxCellHorJustify eHorJust;
149  if (pCondSet &&
150  pCondSet->GetItemState(ATTR_HOR_JUSTIFY, true, &pCondItem) == SfxItemState::SET)
151  eHorJust = static_cast<const SvxHorJustifyItem*>(pCondItem)->GetValue();
152  else
153  eHorJust = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
154  bool bBreak;
155  if ( eHorJust == SvxCellHorJustify::Block )
156  bBreak = true;
157  else if ( pCondSet &&
158  pCondSet->GetItemState(ATTR_LINEBREAK, true, &pCondItem) == SfxItemState::SET)
159  bBreak = static_cast<const ScLineBreakCell*>(pCondItem)->GetValue();
160  else
161  bBreak = pPattern->GetItem(ATTR_LINEBREAK).GetValue();
162 
163  SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
164  sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet );
165 
166  // get "cell is value" flag
167  // Must be synchronized with ScOutputData::LayoutStrings()
168  bool bCellIsValue = (aCell.meType == CELLTYPE_VALUE);
169  if (aCell.meType == CELLTYPE_FORMULA)
170  {
171  ScFormulaCell* pFCell = aCell.mpFormula;
172  bCellIsValue = pFCell->IsRunning() || pFCell->IsValue();
173  }
174 
175  // #i111387#, tdf#121040: disable automatic line breaks for all number formats
176  if (bBreak && bCellIsValue && (pFormatter->GetType(nFormat) == SvNumFormatType::NUMBER))
177  {
178  // If a formula cell needs to be interpreted during aCell.hasNumeric()
179  // to determine the type, the pattern may get invalidated because the
180  // result may set a number format. In which case there's also the
181  // General format not set anymore...
182  bool bMayInvalidatePattern = (aCell.meType == CELLTYPE_FORMULA);
183  const ScPatternAttr* pOldPattern = pPattern;
184  bool bNumeric = aCell.hasNumeric();
185  if (bMayInvalidatePattern)
186  {
187  pPattern = pAttrArray->GetPattern( nRow );
188  if (ppPatternChange)
189  *ppPatternChange = pPattern; // XXX caller may have to check for change!
190  }
191  if (bNumeric)
192  {
193  if (!bMayInvalidatePattern || pPattern == pOldPattern)
194  bBreak = false;
195  else
196  {
197  nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet );
198  if (pFormatter->GetType(nFormat) == SvNumFormatType::NUMBER)
199  bBreak = false;
200  }
201  }
202  }
203 
204  // get other attributes from pattern and conditional formatting
205 
206  SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet );
207  bool bAsianVertical = ( eOrient == SvxCellOrientation::Stacked &&
208  pPattern->GetItem( ATTR_VERTICAL_ASIAN, pCondSet ).GetValue() );
209  if ( bAsianVertical )
210  bBreak = false;
211 
212  if ( bWidth && bBreak ) // after determining bAsianVertical (bBreak may be reset)
213  return 0;
214 
215  Degree100 nRotate(0);
217  if ( eOrient == SvxCellOrientation::Standard )
218  {
219  if (pCondSet &&
220  pCondSet->GetItemState(ATTR_ROTATE_VALUE, true, &pCondItem) == SfxItemState::SET)
221  nRotate = static_cast<const ScRotateValueItem*>(pCondItem)->GetValue();
222  else
223  nRotate = pPattern->GetItem(ATTR_ROTATE_VALUE).GetValue();
224  if ( nRotate )
225  {
226  if (pCondSet &&
227  pCondSet->GetItemState(ATTR_ROTATE_MODE, true, &pCondItem) == SfxItemState::SET)
228  eRotMode = static_cast<const SvxRotateModeItem*>(pCondItem)->GetValue();
229  else
230  eRotMode = pPattern->GetItem(ATTR_ROTATE_MODE).GetValue();
231 
232  if ( nRotate == 18000_deg100 )
233  eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
234  }
235  }
236 
237  if ( eHorJust == SvxCellHorJustify::Repeat )
238  {
239  // ignore orientation/rotation if "repeat" is active
240  eOrient = SvxCellOrientation::Standard;
241  nRotate = 0_deg100;
242  bAsianVertical = false;
243  }
244 
245  const SvxMarginItem* pMargin;
246  if (pCondSet &&
247  pCondSet->GetItemState(ATTR_MARGIN, true, &pCondItem) == SfxItemState::SET)
248  pMargin = static_cast<const SvxMarginItem*>(pCondItem);
249  else
250  pMargin = &pPattern->GetItem(ATTR_MARGIN);
251  sal_uInt16 nIndent = 0;
252  if ( eHorJust == SvxCellHorJustify::Left )
253  {
254  if (pCondSet &&
255  pCondSet->GetItemState(ATTR_INDENT, true, &pCondItem) == SfxItemState::SET)
256  nIndent = static_cast<const ScIndentItem*>(pCondItem)->GetValue();
257  else
258  nIndent = pPattern->GetItem(ATTR_INDENT).GetValue();
259  }
260 
261  SvtScriptType nScript = rDocument.GetScriptType(nCol, nRow, nTab);
262  if (nScript == SvtScriptType::NONE) nScript = ScGlobal::GetDefaultScriptType();
263 
264  // also call SetFont for edit cells, because bGetFont may be set only once
265  // bGetFont is set also if script type changes
266  if (rOptions.bGetFont)
267  {
268  Fraction aFontZoom = ( eOrient == SvxCellOrientation::Standard ) ? rZoomX : rZoomY;
269  vcl::Font aFont;
270  // font color doesn't matter here
271  pPattern->GetFont( aFont, SC_AUTOCOL_BLACK, pDev, &aFontZoom, pCondSet, nScript );
272  pDev->SetFont(aFont);
273  }
274 
275  bool bAddMargin = true;
276  CellType eCellType = aCell.meType;
277 
278  bool bEditEngine = (eCellType == CELLTYPE_EDIT ||
279  eOrient == SvxCellOrientation::Stacked ||
280  IsAmbiguousScript(nScript) ||
281  ((eCellType == CELLTYPE_FORMULA) && aCell.mpFormula->IsMultilineResult()));
282 
283  if (!bEditEngine) // direct output
284  {
285  const Color* pColor;
286  OUString aValStr;
288  aCell, nFormat, aValStr, &pColor, *pFormatter, rDocument, true, rOptions.bFormula);
289 
290  if (!aValStr.isEmpty())
291  {
292  // SetFont is moved up
293 
294  Size aSize( pDev->GetTextWidth( aValStr ), pDev->GetTextHeight() );
295  if ( eOrient != SvxCellOrientation::Standard )
296  {
297  tools::Long nTemp = aSize.Width();
298  aSize.setWidth( aSize.Height() );
299  aSize.setHeight( nTemp );
300  }
301  else if ( nRotate )
302  {
303  //TODO: take different X/Y scaling into consideration
304 
305  double nRealOrient = nRotate.get() * F_PI18000; // nRotate is in 1/100 Grad
306  double nCosAbs = fabs( cos( nRealOrient ) );
307  double nSinAbs = fabs( sin( nRealOrient ) );
308  tools::Long nHeight = static_cast<tools::Long>( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs );
309  tools::Long nWidth;
310  if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
311  nWidth = static_cast<tools::Long>( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs );
312  else if ( rOptions.bTotalSize )
313  {
314  nWidth = conditionalScaleFunc(rDocument.GetColWidth( nCol,nTab ), nPPT);
315  bAddMargin = false;
316  // only to the right:
317  //TODO: differ on direction up/down (only Text/whole height)
318  if ( pPattern->GetRotateDir( pCondSet ) == ScRotateDir::Right )
319  nWidth += static_cast<tools::Long>( rDocument.GetRowHeight( nRow,nTab ) *
320  (bInPrintTwips ? 1.0 : nPPT) * nCosAbs / nSinAbs );
321  }
322  else
323  nWidth = static_cast<tools::Long>( aSize.Height() / nSinAbs ); //TODO: limit?
324 
325  if ( bBreak && !rOptions.bTotalSize )
326  {
327  // limit size for line break
329  if ( nHeight > nCmp )
330  nHeight = nCmp;
331  }
332 
333  aSize = Size( nWidth, nHeight );
334  }
335  nValue = bWidth ? aSize.Width() : aSize.Height();
336 
337  if ( bAddMargin )
338  {
339  if (bWidth)
340  {
341  nValue += conditionalScaleFunc(pMargin->GetLeftMargin(), nPPT) +
342  conditionalScaleFunc(pMargin->GetRightMargin(), nPPT);
343  if ( nIndent )
344  nValue += conditionalScaleFunc(nIndent, nPPT);
345  }
346  else
347  nValue += conditionalScaleFunc(pMargin->GetTopMargin(), nPPT) +
348  conditionalScaleFunc(pMargin->GetBottomMargin(), nPPT);
349  }
350 
351  // linebreak done ?
352 
353  if ( bBreak && !bWidth )
354  {
355  // test with EditEngine the safety at 90%
356  // (due to rounding errors and because EditEngine formats partially differently)
357 
358  tools::Long nDocSize = conditionalScaleFunc((rDocument.GetColWidth( nCol,nTab ) -
359  pMargin->GetLeftMargin() - pMargin->GetRightMargin() -
360  nIndent), nPPTX);
361  nDocSize = (nDocSize * 9) / 10; // for safety
362  if ( aSize.Width() > nDocSize )
363  bEditEngine = true;
364  }
365  }
366  }
367 
368  if (bEditEngine)
369  {
370  // the font is not reset each time with !bEditEngine
371  vcl::Font aOldFont = pDev->GetFont();
372 
373  MapMode aHMMMode( MapUnit::Map100thMM, Point(), rZoomX, rZoomY );
374  MapMode aTwipMode(MapUnit::MapTwip, Point(), rZoomX, rZoomY);
375 
376  // save in document ?
377  std::unique_ptr<ScFieldEditEngine> pEngine = rDocument.CreateFieldEditEngine();
378 
379  pEngine->SetUpdateMode( false );
380  bool bTextWysiwyg = ( pDev->GetOutDevType() == OUTDEV_PRINTER );
381  EEControlBits nCtrl = pEngine->GetControlWord();
382  if ( bTextWysiwyg )
383  nCtrl |= EEControlBits::FORMAT100;
384  else
385  nCtrl &= ~EEControlBits::FORMAT100;
386  pEngine->SetControlWord( nCtrl );
387  MapMode aOld = pDev->GetMapMode();
388  pDev->SetMapMode( aHMMMode );
389  pEngine->SetRefDevice( pDev );
390  rDocument.ApplyAsianEditSettings( *pEngine );
391  std::unique_ptr<SfxItemSet> pSet(new SfxItemSet( pEngine->GetEmptyItemSet() ));
392  if ( ScStyleSheet* pPreviewStyle = rDocument.GetPreviewCellStyle( nCol, nRow, nTab ) )
393  {
394  std::unique_ptr<ScPatternAttr> pPreviewPattern(new ScPatternAttr( *pPattern ));
395  pPreviewPattern->SetStyleSheet(pPreviewStyle);
396  pPreviewPattern->FillEditItemSet( pSet.get(), pCondSet );
397  }
398  else
399  {
400  SfxItemSet* pFontSet = rDocument.GetPreviewFont( nCol, nRow, nTab );
401  pPattern->FillEditItemSet( pSet.get(), pFontSet ? pFontSet : pCondSet );
402  }
403 // no longer needed, are set with the text (is faster)
404 // pEngine->SetDefaults( pSet );
405 
406  if ( pSet->Get(EE_PARA_HYPHENATE).GetValue() ) {
407 
408  css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
409  pEngine->SetHyphenator( xXHyphenator );
410  }
411 
412  Size aPaper( 1000000, 1000000 );
413  if ( eOrient==SvxCellOrientation::Stacked && !bAsianVertical )
414  aPaper.setWidth( 1 );
415  else if (bBreak)
416  {
417  double fWidthFactor = bInPrintTwips ? 1.0 : nPPTX;
418  if ( bTextWysiwyg )
419  {
420  // if text is formatted for printer, don't use PixelToLogic,
421  // to ensure the exact same paper width (and same line breaks) as in
422  // ScEditUtil::GetEditArea, used for output.
423 
425  }
426 
427  // use original width for hidden columns:
428  tools::Long nDocWidth = static_cast<tools::Long>( rDocument.GetOriginalWidth(nCol,nTab) * fWidthFactor );
429  SCCOL nColMerge = pMerge->GetColMerge();
430  if (nColMerge > 1)
431  for (SCCOL nColAdd=1; nColAdd<nColMerge; nColAdd++)
432  nDocWidth += static_cast<tools::Long>( rDocument.GetColWidth(nCol+nColAdd,nTab) * fWidthFactor );
433  nDocWidth -= static_cast<tools::Long>( pMargin->GetLeftMargin() * fWidthFactor )
434  + static_cast<tools::Long>( pMargin->GetRightMargin() * fWidthFactor )
435  + 1; // output size is width-1 pixel (due to gridline)
436  if ( nIndent )
437  nDocWidth -= static_cast<tools::Long>( nIndent * fWidthFactor );
438 
439  // space for AutoFilter button: 20 * nZoom/100
440  constexpr tools::Long nFilterButtonWidthPix = 20; // Autofilter pixel width at 100% zoom.
441  if ( pFlag->HasAutoFilter() && !bTextWysiwyg )
442  nDocWidth -= bInPrintTwips ? o3tl::convert(nFilterButtonWidthPix, o3tl::Length::px,
444  : tools::Long(rZoomX * nFilterButtonWidthPix);
445 
446  aPaper.setWidth( nDocWidth );
447 
448  if ( !bTextWysiwyg )
449  {
450  aPaper = bInPrintTwips ?
451  OutputDevice::LogicToLogic(aPaper, aTwipMode, aHMMMode) :
452  pDev->PixelToLogic(aPaper, aHMMMode);
453  }
454  }
455  pEngine->SetPaperSize(aPaper);
456 
457  if (aCell.meType == CELLTYPE_EDIT)
458  {
459  pEngine->SetTextNewDefaults(*aCell.mpEditText, std::move(pSet));
460  }
461  else
462  {
463  const Color* pColor;
464  OUString aString;
466  aCell, nFormat, aString, &pColor, *pFormatter, rDocument, true,
467  rOptions.bFormula);
468 
469  if (!aString.isEmpty())
470  pEngine->SetTextNewDefaults(aString, std::move(pSet));
471  else
472  pEngine->SetDefaults(std::move(pSet));
473  }
474 
475  bool bEngineVertical = pEngine->IsVertical();
476  pEngine->SetVertical( bAsianVertical );
477  pEngine->SetUpdateMode( true );
478 
479  bool bEdWidth = bWidth;
480  if ( eOrient != SvxCellOrientation::Standard && eOrient != SvxCellOrientation::Stacked )
481  bEdWidth = !bEdWidth;
482  if ( nRotate )
483  {
484  //TODO: take different X/Y scaling into consideration
485 
486  Size aSize( pEngine->CalcTextWidth(), pEngine->GetTextHeight() );
487  double nRealOrient = nRotate.get() * F_PI18000; // nRotate is in 1/100 Grad
488  double nCosAbs = fabs( cos( nRealOrient ) );
489  double nSinAbs = fabs( sin( nRealOrient ) );
490  tools::Long nHeight = static_cast<tools::Long>( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs );
491  tools::Long nWidth;
492  if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
493  nWidth = static_cast<tools::Long>( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs );
494  else if ( rOptions.bTotalSize )
495  {
496  nWidth = conditionalScaleFunc(rDocument.GetColWidth( nCol,nTab ), nPPT);
497  bAddMargin = false;
498  if ( pPattern->GetRotateDir( pCondSet ) == ScRotateDir::Right )
499  nWidth += static_cast<tools::Long>( rDocument.GetRowHeight( nRow,nTab ) *
500  (bInPrintTwips ? 1.0 : nPPT) * nCosAbs / nSinAbs );
501  }
502  else
503  nWidth = static_cast<tools::Long>( aSize.Height() / nSinAbs ); //TODO: limit?
504  aSize = Size( nWidth, nHeight );
505 
506  Size aTextSize = bInPrintTwips ?
507  OutputDevice::LogicToLogic(aSize, aHMMMode, aTwipMode) :
508  pDev->LogicToPixel(aSize, aHMMMode);
509 
510  if ( bEdWidth )
511  nValue = aTextSize.Width();
512  else
513  {
514  nValue = aTextSize.Height();
515 
516  if ( bBreak && !rOptions.bTotalSize )
517  {
518  // limit size for line break
519  tools::Long nCmp = aOldFont.GetFontSize().Height() * SC_ROT_BREAK_FACTOR;
520  if ( nValue > nCmp )
521  nValue = nCmp;
522  }
523  }
524  }
525  else if ( bEdWidth )
526  {
527  if (bBreak)
528  nValue = 0;
529  else
530  {
531  Size aTextSize(pEngine->CalcTextWidth(), 0);
532  nValue = bInPrintTwips ?
533  OutputDevice::LogicToLogic(aTextSize, aHMMMode, aTwipMode).Width() :
534  pDev->LogicToPixel(aTextSize, aHMMMode).Width();
535  }
536  }
537  else // height
538  {
539  Size aTextSize(0, pEngine->GetTextHeight());
540  nValue = bInPrintTwips ?
541  OutputDevice::LogicToLogic(aTextSize, aHMMMode, aTwipMode).Height() :
542  pDev->LogicToPixel(aTextSize, aHMMMode).Height();
543 
544  // With non-100% zoom and several lines or paragraphs, don't shrink below the result with FORMAT100 set
545  if ( !bTextWysiwyg && ( rZoomY.GetNumerator() != 1 || rZoomY.GetDenominator() != 1 ) &&
546  ( pEngine->GetParagraphCount() > 1 || ( bBreak && pEngine->GetLineCount(0) > 1 ) ) )
547  {
548  pEngine->SetControlWord( nCtrl | EEControlBits::FORMAT100 );
549  pEngine->QuickFormatDoc( true );
550  aTextSize = Size(0, pEngine->GetTextHeight());
551  tools::Long nSecondValue = bInPrintTwips ?
552  OutputDevice::LogicToLogic(aTextSize, aHMMMode, aTwipMode).Height() :
553  pDev->LogicToPixel(aTextSize, aHMMMode).Height();
554  if ( nSecondValue > nValue )
555  nValue = nSecondValue;
556  }
557  }
558 
559  if ( nValue && bAddMargin )
560  {
561  if (bWidth)
562  {
563  nValue += conditionalScaleFunc(pMargin->GetLeftMargin(), nPPT) +
564  conditionalScaleFunc(pMargin->GetRightMargin(), nPPT);
565  if (nIndent)
566  nValue += conditionalScaleFunc(nIndent, nPPT);
567  }
568  else
569  {
570  nValue += conditionalScaleFunc(pMargin->GetTopMargin(), nPPT) +
571  conditionalScaleFunc(pMargin->GetBottomMargin(), nPPT);
572 
573  if ( bAsianVertical && pDev->GetOutDevType() != OUTDEV_PRINTER )
574  {
575  // add 1pt extra (default margin value) for line breaks with SetVertical
576  constexpr tools::Long nDefaultMarginInPoints = 1;
577  nValue += conditionalScaleFunc(
578  o3tl::convert(nDefaultMarginInPoints, o3tl::Length::pt, o3tl::Length::twip),
579  nPPT);
580  }
581  }
582  }
583 
584  // EditEngine is cached and re-used, so the old vertical flag must be restored
585  pEngine->SetVertical( bEngineVertical );
586 
587  rDocument.DisposeFieldEditEngine(pEngine);
588 
589  pDev->SetMapMode( aOld );
590  pDev->SetFont( aOldFont );
591  }
592 
593  if (bWidth)
594  {
595  // place for Autofilter Button
596  // 20 * nZoom/100
597  // Conditional formatting is not interesting here
598  constexpr tools::Long nFilterButtonWidthPix = 20; // Autofilter pixel width at 100% zoom.
599  ScMF nFlags = pPattern->GetItem(ATTR_MERGE_FLAG).GetValue();
600  if (nFlags & ScMF::Auto)
601  nValue += bInPrintTwips ? o3tl::convert(nFilterButtonWidthPix, o3tl::Length::px,
603  : tools::Long(rZoomX * nFilterButtonWidthPix);
604  }
605 
606  return nValue;
607 }
608 
609 namespace {
610 
611 class MaxStrLenFinder
612 {
613  ScDocument& mrDoc;
614  sal_uInt32 mnFormat;
615  OUString maMaxLenStr;
616  sal_Int32 mnMaxLen;
617 
618  void checkLength(const ScRefCellValue& rCell)
619  {
620  const Color* pColor;
621  OUString aValStr;
623  rCell, mnFormat, aValStr, &pColor, *mrDoc.GetFormatTable(), mrDoc);
624 
625  if (aValStr.getLength() > mnMaxLen)
626  {
627  mnMaxLen = aValStr.getLength();
628  maMaxLenStr = aValStr;
629  }
630  }
631 
632 public:
633  MaxStrLenFinder(ScDocument& rDoc, sal_uInt32 nFormat) :
634  mrDoc(rDoc), mnFormat(nFormat), mnMaxLen(0) {}
635 
636  void operator() (size_t /*nRow*/, double f)
637  {
638  ScRefCellValue aCell(f);
639  checkLength(aCell);
640  }
641 
642  void operator() (size_t /*nRow*/, const svl::SharedString& rSS)
643  {
644  if (rSS.getLength() > mnMaxLen)
645  {
646  mnMaxLen = rSS.getLength();
647  maMaxLenStr = rSS.getString();
648  }
649  }
650 
651  void operator() (size_t /*nRow*/, const EditTextObject* p)
652  {
653  ScRefCellValue aCell(p);
654  checkLength(aCell);
655  }
656 
657  void operator() (size_t /*nRow*/, const ScFormulaCell* p)
658  {
659  ScRefCellValue aCell(const_cast<ScFormulaCell*>(p));
660  checkLength(aCell);
661  }
662 
663  const OUString& getMaxLenStr() const { return maMaxLenStr; }
664 };
665 
666 }
667 
669  OutputDevice* pDev, double nPPTX, double nPPTY, const Fraction& rZoomX, const Fraction& rZoomY,
670  bool bFormula, sal_uInt16 nOldWidth, const ScMarkData* pMarkData, const ScColWidthParam* pParam) const
671 {
672  if (maCells.block_size() == 1 && maCells.begin()->type == sc::element_type_empty)
673  // All cells are empty.
674  return nOldWidth;
675 
676  sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
678  if (pMarkData && (pMarkData->IsMarked() || pMarkData->IsMultiMarked()))
679  {
680  aSpanSet.scan(*pMarkData, nTab, nCol);
681  aSpanSet.getSpans(aMarkedSpans);
682  }
683  else
684  // "Select" the entire column if no selection exists.
685  aMarkedSpans.emplace_back(0, GetDoc().MaxRow());
686 
687  sal_uInt16 nWidth = static_cast<sal_uInt16>(nOldWidth*nPPTX);
688  bool bFound = false;
689  ScDocument& rDocument = GetDoc();
690 
691  if ( pParam && pParam->mbSimpleText )
692  { // all the same except for number format
693  const ScPatternAttr* pPattern = GetPattern( 0 );
694  vcl::Font aFont;
695  // font color doesn't matter here
696  pPattern->GetFont( aFont, SC_AUTOCOL_BLACK, pDev, &rZoomX );
697  pDev->SetFont( aFont );
698  const SvxMarginItem* pMargin = &pPattern->GetItem(ATTR_MARGIN);
699  tools::Long nMargin = static_cast<tools::Long>( pMargin->GetLeftMargin() * nPPTX ) +
700  static_cast<tools::Long>( pMargin->GetRightMargin() * nPPTX );
701 
702  // Try to find the row that has the longest string, and measure the width of that string.
703  SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
704  sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
705  OUString aLongStr;
706  const Color* pColor;
707  if (pParam->mnMaxTextRow >= 0)
708  {
709  ScRefCellValue aCell = GetCellValue(pParam->mnMaxTextRow);
711  aCell, nFormat, aLongStr, &pColor, *pFormatter, rDocument);
712  }
713  else
714  {
715  // Go though all non-empty cells within selection.
716  MaxStrLenFinder aFunc(rDocument, nFormat);
717  sc::CellStoreType::const_iterator itPos = maCells.begin();
718  for (const auto& rMarkedSpan : aMarkedSpans)
719  itPos = sc::ParseAllNonEmpty(itPos, maCells, rMarkedSpan.mnRow1, rMarkedSpan.mnRow2, aFunc);
720 
721  aLongStr = aFunc.getMaxLenStr();
722  }
723 
724  if (!aLongStr.isEmpty())
725  {
726  nWidth = pDev->GetTextWidth(aLongStr) + static_cast<sal_uInt16>(nMargin);
727  bFound = true;
728  }
729  }
730  else
731  {
732  ScNeededSizeOptions aOptions;
733  aOptions.bFormula = bFormula;
734  const ScPatternAttr* pOldPattern = nullptr;
735 
736  // Go though all non-empty cells within selection.
737  sc::CellStoreType::const_iterator itPos = maCells.begin();
738  for (const auto& rMarkedSpan : aMarkedSpans)
739  {
740  SCROW nRow1 = rMarkedSpan.mnRow1, nRow2 = rMarkedSpan.mnRow2;
741  SCROW nRow = nRow1;
742  while (nRow <= nRow2)
743  {
744  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
745  itPos = aPos.first;
746  if (itPos->type == sc::element_type_empty)
747  {
748  // Skip empty cells.
749  nRow += itPos->size - aPos.second;
750  continue;
751  }
752 
753  for (size_t nOffset = aPos.second; nOffset < itPos->size; ++nOffset, ++nRow)
754  {
755  SvtScriptType nScript = rDocument.GetScriptType(nCol, nRow, nTab);
756  if (nScript == SvtScriptType::NONE)
757  nScript = ScGlobal::GetDefaultScriptType();
758 
759  const ScPatternAttr* pPattern = GetPattern(nRow);
760  aOptions.pPattern = pPattern;
761  aOptions.bGetFont = (pPattern != pOldPattern || nScript != SvtScriptType::NONE);
762  pOldPattern = pPattern;
763  sal_uInt16 nThis = static_cast<sal_uInt16>(GetNeededSize(
764  nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, true, aOptions, &pOldPattern));
765  if (nThis && (nThis > nWidth || !bFound))
766  {
767  nWidth = nThis;
768  bFound = true;
769  }
770  }
771  }
772  }
773  }
774 
775  if (bFound)
776  {
777  nWidth += 2;
778  sal_uInt16 nTwips = static_cast<sal_uInt16>(
779  std::min(nWidth / nPPTX, double(std::numeric_limits<sal_uInt16>::max())));
780  return nTwips;
781  }
782  else
783  return nOldWidth;
784 }
785 
786 static sal_uInt16 lcl_GetAttribHeight( const ScPatternAttr& rPattern, sal_uInt16 nFontHeightId )
787 {
788  const SvxFontHeightItem& rFontHeight =
789  static_cast<const SvxFontHeightItem&>(rPattern.GetItem(nFontHeightId));
790 
791  sal_uInt16 nHeight = rFontHeight.GetHeight();
792  nHeight *= 1.18;
793 
794  if ( rPattern.GetItem(ATTR_FONT_EMPHASISMARK).GetEmphasisMark() != FontEmphasisMark::NONE )
795  {
796  // add height for emphasis marks
797  //TODO: font metrics should be used instead
798  nHeight += nHeight / 4;
799  }
800 
801  const SvxMarginItem& rMargin = rPattern.GetItem(ATTR_MARGIN);
802 
803  nHeight += rMargin.GetTopMargin() + rMargin.GetBottomMargin();
804 
805  if (nHeight > STD_ROWHEIGHT_DIFF)
806  nHeight -= STD_ROWHEIGHT_DIFF;
807 
808  if (nHeight < ScGlobal::nStdRowHeight)
809  nHeight = ScGlobal::nStdRowHeight;
810 
811  return nHeight;
812 }
813 
814 // pHeight in Twips
815 // optimize nMinHeight, nMinStart : with nRow >= nMinStart is at least nMinHeight
816 // (is only evaluated with bStdAllowed)
817 
819  sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow, sal_uInt16 nMinHeight, SCROW nMinStart )
820 {
821  ScDocument& rDocument = GetDoc();
822  RowHeightsArray& rHeights = rCxt.getHeightArray();
823  ScAttrIterator aIter( pAttrArray.get(), nStartRow, nEndRow, rDocument.GetDefPattern() );
824 
825  SCROW nStart = -1;
826  SCROW nEnd = -1;
827  SCROW nEditPos = 0;
828  SCROW nNextEnd = 0;
829 
830  // with conditional formatting, always consider the individual cells
831 
832  const ScPatternAttr* pPattern = aIter.Next(nStart,nEnd);
833  while ( pPattern )
834  {
835  const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
836  const ScMergeFlagAttr* pFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
837  if ( pMerge->GetRowMerge() > 1 || pFlag->IsOverlapped() )
838  {
839  // do nothing - vertically with merged and overlapping,
840  // horizontally only with overlapped (invisible) -
841  // only one horizontal merged is always considered
842  }
843  else
844  {
845  bool bStdAllowed = (pPattern->GetCellOrientation() == SvxCellOrientation::Standard);
846  bool bStdOnly = false;
847  if (bStdAllowed)
848  {
849  bool bBreak = pPattern->GetItem(ATTR_LINEBREAK).GetValue() ||
850  (pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() ==
851  SvxCellHorJustify::Block);
852  bStdOnly = !bBreak;
853 
854  // conditional formatting: loop all cells
855  if (bStdOnly &&
856  !pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
857  {
858  bStdOnly = false;
859  }
860 
861  // rotated text: loop all cells
862  if ( bStdOnly && pPattern->GetItem(ATTR_ROTATE_VALUE).GetValue() )
863  bStdOnly = false;
864  }
865 
866  if (bStdOnly)
867  {
868  bool bHasEditCells = HasEditCells(nStart,nEnd,nEditPos);
869  // Call to HasEditCells() may change pattern due to
870  // calculation, => sync always.
871  // We don't know which row changed first, but as pPattern
872  // covered nStart to nEnd we can pick nStart. Worst case we
873  // have to repeat that for every row in range if every row
874  // changed.
875  pPattern = aIter.Resync( nStart, nStart, nEnd);
876  if (bHasEditCells && nEnd < nEditPos)
877  bHasEditCells = false; // run into that again
878  if (bHasEditCells) // includes mixed script types
879  {
880  if (nEditPos == nStart)
881  {
882  bStdOnly = false;
883  if (nEnd > nEditPos)
884  nNextEnd = nEnd;
885  nEnd = nEditPos; // calculate single
886  bStdAllowed = false; // will be computed in any case per cell
887  }
888  else
889  {
890  nNextEnd = nEnd;
891  nEnd = nEditPos - 1; // standard - part
892  }
893  }
894  }
895 
896  sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
897  aSpanSet.scan(*this, nStart, nEnd);
899  aSpanSet.getSpans(aSpans);
900 
901  if (bStdAllowed)
902  {
903  sal_uInt16 nLatHeight = 0;
904  sal_uInt16 nCjkHeight = 0;
905  sal_uInt16 nCtlHeight = 0;
906  sal_uInt16 nDefHeight;
908  if ( nDefScript == SvtScriptType::ASIAN )
909  nDefHeight = nCjkHeight = lcl_GetAttribHeight( *pPattern, ATTR_CJK_FONT_HEIGHT );
910  else if ( nDefScript == SvtScriptType::COMPLEX )
911  nDefHeight = nCtlHeight = lcl_GetAttribHeight( *pPattern, ATTR_CTL_FONT_HEIGHT );
912  else
913  nDefHeight = nLatHeight = lcl_GetAttribHeight( *pPattern, ATTR_FONT_HEIGHT );
914 
915  // if everything below is already larger, the loop doesn't have to
916  // be run again
917  SCROW nStdEnd = nEnd;
918  if ( nDefHeight <= nMinHeight && nStdEnd >= nMinStart )
919  nStdEnd = (nMinStart>0) ? nMinStart-1 : 0;
920 
921  if (nStart <= nStdEnd)
922  {
923  SCROW nRow = nStart;
924  for (;;)
925  {
926  size_t nIndex;
927  SCROW nRangeEnd;
928  sal_uInt16 nRangeHeight = rHeights.GetValue(nRow, nIndex, nRangeEnd);
929  if (nRangeHeight < nDefHeight)
930  rHeights.SetValue(nRow, std::min(nRangeEnd, nStdEnd), nDefHeight);
931  nRow = nRangeEnd + 1;
932  if (nRow > nStdEnd)
933  break;
934  }
935  }
936 
937  if ( bStdOnly )
938  {
939  // if cells are not handled individually below,
940  // check for cells with different script type
941  sc::CellTextAttrStoreType::iterator itAttr = maCellTextAttrs.begin();
942  sc::CellStoreType::iterator itCells = maCells.begin();
943  for (const auto& rSpan : aSpans)
944  {
945  for (SCROW nRow = rSpan.mnRow1; nRow <= rSpan.mnRow2; ++nRow)
946  {
947  SvtScriptType nScript = GetRangeScriptType(itAttr, nRow, nRow, itCells);
948  if (nScript == nDefScript)
949  continue;
950 
951  if ( nScript == SvtScriptType::ASIAN )
952  {
953  if ( nCjkHeight == 0 )
954  nCjkHeight = lcl_GetAttribHeight( *pPattern, ATTR_CJK_FONT_HEIGHT );
955  if (nCjkHeight > rHeights.GetValue(nRow))
956  rHeights.SetValue(nRow, nRow, nCjkHeight);
957  }
958  else if ( nScript == SvtScriptType::COMPLEX )
959  {
960  if ( nCtlHeight == 0 )
961  nCtlHeight = lcl_GetAttribHeight( *pPattern, ATTR_CTL_FONT_HEIGHT );
962  if (nCtlHeight > rHeights.GetValue(nRow))
963  rHeights.SetValue(nRow, nRow, nCtlHeight);
964  }
965  else
966  {
967  if ( nLatHeight == 0 )
968  nLatHeight = lcl_GetAttribHeight( *pPattern, ATTR_FONT_HEIGHT );
969  if (nLatHeight > rHeights.GetValue(nRow))
970  rHeights.SetValue(nRow, nRow, nLatHeight);
971  }
972  }
973  }
974  }
975  }
976 
977  if (!bStdOnly) // search covered cells
978  {
979  ScNeededSizeOptions aOptions;
980 
981  for (const auto& rSpan : aSpans)
982  {
983  for (SCROW nRow = rSpan.mnRow1; nRow <= rSpan.mnRow2; ++nRow)
984  {
985  // only calculate the cell height when it's used later (#37928#)
986 
987  if (rCxt.isForceAutoSize() || !(rDocument.GetRowFlags(nRow, nTab) & CRFlags::ManualSize) )
988  {
989  aOptions.pPattern = pPattern;
990  const ScPatternAttr* pOldPattern = pPattern;
991  sal_uInt16 nHeight = static_cast<sal_uInt16>(
992  std::min(
993  GetNeededSize( nRow, rCxt.getOutputDevice(), rCxt.getPPTX(), rCxt.getPPTY(),
994  rCxt.getZoomX(), rCxt.getZoomY(), false, aOptions,
995  &pPattern) / rCxt.getPPTY(),
996  double(std::numeric_limits<sal_uInt16>::max())));
997  if (nHeight > rHeights.GetValue(nRow))
998  rHeights.SetValue(nRow, nRow, nHeight);
999  // Pattern changed due to calculation? => sync.
1000  if (pPattern != pOldPattern)
1001  {
1002  pPattern = aIter.Resync( nRow, nStart, nEnd);
1003  nNextEnd = 0;
1004  }
1005  }
1006  }
1007  }
1008  }
1009  }
1010 
1011  if (nNextEnd > 0)
1012  {
1013  nStart = nEnd + 1;
1014  nEnd = nNextEnd;
1015  nNextEnd = 0;
1016  }
1017  else
1018  pPattern = aIter.Next(nStart,nEnd);
1019  }
1020 }
1021 
1022 bool ScColumn::GetNextSpellingCell(SCROW& nRow, bool bInSel, const ScMarkData& rData) const
1023 {
1024  ScDocument& rDocument = GetDoc();
1025  bool bStop = false;
1026  sc::CellStoreType::const_iterator it = maCells.position(nRow).first;
1027  mdds::mtv::element_t eType = it->type;
1028  if (!bInSel && it != maCells.end() && eType != sc::element_type_empty)
1029  {
1030  if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
1031  !(HasAttrib( nRow, nRow, HasAttrFlags::Protected) &&
1032  rDocument.IsTabProtected(nTab)) )
1033  return true;
1034  }
1035  while (!bStop)
1036  {
1037  if (bInSel)
1038  {
1039  nRow = rData.GetNextMarked(nCol, nRow, false);
1040  if (!rDocument.ValidRow(nRow))
1041  {
1042  nRow = GetDoc().MaxRow()+1;
1043  bStop = true;
1044  }
1045  else
1046  {
1047  it = maCells.position(it, nRow).first;
1048  eType = it->type;
1049  if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
1050  !(HasAttrib( nRow, nRow, HasAttrFlags::Protected) &&
1051  rDocument.IsTabProtected(nTab)) )
1052  return true;
1053  else
1054  nRow++;
1055  }
1056  }
1057  else if (GetNextDataPos(nRow))
1058  {
1059  it = maCells.position(it, nRow).first;
1060  eType = it->type;
1061  if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
1062  !(HasAttrib( nRow, nRow, HasAttrFlags::Protected) &&
1063  rDocument.IsTabProtected(nTab)) )
1064  return true;
1065  else
1066  nRow++;
1067  }
1068  else
1069  {
1070  nRow = GetDoc().MaxRow()+1;
1071  bStop = true;
1072  }
1073  }
1074  return false;
1075 }
1076 
1077 namespace {
1078 
1079 class StrEntries
1080 {
1081  sc::CellStoreType& mrCells;
1082 
1083 protected:
1084  struct StrEntry
1085  {
1086  SCROW mnRow;
1087  OUString maStr;
1088 
1089  StrEntry(SCROW nRow, const OUString& rStr) : mnRow(nRow), maStr(rStr) {}
1090  };
1091 
1092  std::vector<StrEntry> maStrEntries;
1093  ScDocument* mpDoc;
1094 
1095  StrEntries(sc::CellStoreType& rCells, ScDocument* pDoc) : mrCells(rCells), mpDoc(pDoc) {}
1096 
1097 public:
1098  void commitStrings()
1099  {
1100  svl::SharedStringPool& rPool = mpDoc->GetSharedStringPool();
1101  sc::CellStoreType::iterator it = mrCells.begin();
1102  for (const auto& rStrEntry : maStrEntries)
1103  it = mrCells.set(it, rStrEntry.mnRow, rPool.intern(rStrEntry.maStr));
1104  }
1105 };
1106 
1107 class RemoveEditAttribsHandler : public StrEntries
1108 {
1109  std::unique_ptr<ScFieldEditEngine> mpEngine;
1110 
1111 public:
1112  RemoveEditAttribsHandler(sc::CellStoreType& rCells, ScDocument* pDoc) : StrEntries(rCells, pDoc) {}
1113 
1114  void operator() (size_t nRow, EditTextObject*& pObj)
1115  {
1116  // For the test on hard formatting (ScEditAttrTester), are the defaults in the
1117  // EditEngine of no importance. When the tester would later recognise the same
1118  // attributes in default and hard formatting and has to remove them, the correct
1119  // defaults must be set in the EditEngine for each cell.
1120 
1121  // test for attributes
1122  if (!mpEngine)
1123  {
1124  mpEngine.reset(new ScFieldEditEngine(mpDoc, mpDoc->GetEditPool()));
1125  // EEControlBits::ONLINESPELLING if there are errors already
1126  mpEngine->SetControlWord(mpEngine->GetControlWord() | EEControlBits::ONLINESPELLING);
1127  mpDoc->ApplyAsianEditSettings(*mpEngine);
1128  }
1129  mpEngine->SetTextCurrentDefaults(*pObj);
1130  sal_Int32 nParCount = mpEngine->GetParagraphCount();
1131  for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
1132  {
1133  mpEngine->RemoveCharAttribs(nPar);
1134  const SfxItemSet& rOld = mpEngine->GetParaAttribs(nPar);
1135  if ( rOld.Count() )
1136  {
1137  SfxItemSet aNew( *rOld.GetPool(), rOld.GetRanges() ); // empty
1138  mpEngine->SetParaAttribs( nPar, aNew );
1139  }
1140  }
1141  // change URL field to text (not possible otherwise, thus pType=0)
1142  mpEngine->RemoveFields();
1143 
1144  bool bSpellErrors = mpEngine->HasOnlineSpellErrors();
1145  bool bNeedObject = bSpellErrors || nParCount>1; // keep errors/paragraphs
1146  // ScEditAttrTester is not needed anymore, arrays are gone
1147 
1148  if (bNeedObject) // remains edit cell
1149  {
1150  EEControlBits nCtrl = mpEngine->GetControlWord();
1151  EEControlBits nWantBig = bSpellErrors ? EEControlBits::ALLOWBIGOBJS : EEControlBits::NONE;
1152  if ( ( nCtrl & EEControlBits::ALLOWBIGOBJS ) != nWantBig )
1153  mpEngine->SetControlWord( (nCtrl & ~EEControlBits::ALLOWBIGOBJS) | nWantBig );
1154 
1155  // Overwrite the existing object.
1156  delete pObj;
1157  pObj = mpEngine->CreateTextObject().release();
1158  }
1159  else // create String
1160  {
1161  // Store the string replacement for later commits.
1162  OUString aText = ScEditUtil::GetSpaceDelimitedString(*mpEngine);
1163  maStrEntries.emplace_back(nRow, aText);
1164  }
1165  }
1166 };
1167 
1168 class TestTabRefAbsHandler
1169 {
1170  SCTAB mnTab;
1171  bool mbTestResult;
1172 public:
1173  explicit TestTabRefAbsHandler(SCTAB nTab) : mnTab(nTab), mbTestResult(false) {}
1174 
1175  void operator() (size_t /*nRow*/, const ScFormulaCell* pCell)
1176  {
1177  if (const_cast<ScFormulaCell*>(pCell)->TestTabRefAbs(mnTab))
1178  mbTestResult = true;
1179  }
1180 
1181  bool getTestResult() const { return mbTestResult; }
1182 };
1183 
1184 }
1185 
1186 void ScColumn::RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow )
1187 {
1188  RemoveEditAttribsHandler aFunc(maCells, &GetDoc());
1189  sc::ProcessEditText(maCells.begin(), maCells, nStartRow, nEndRow, aFunc);
1190  aFunc.commitStrings();
1191 }
1192 
1193 bool ScColumn::TestTabRefAbs(SCTAB nTable) const
1194 {
1195  TestTabRefAbsHandler aFunc(nTable);
1196  sc::ParseFormula(maCells, aFunc);
1197  return aFunc.getTestResult();
1198 }
1199 
1201 {
1202  return maCells.block_size() == 1 && maCells.begin()->type == sc::element_type_empty;
1203 }
1204 
1205 namespace {
1206 
1207 class CellCounter
1208 {
1209  size_t mnCount;
1210 public:
1211  CellCounter() : mnCount(0) {}
1212 
1213  void operator() (
1214  const sc::CellStoreType::value_type& node, size_t /*nOffset*/, size_t nDataSize)
1215  {
1216  if (node.type == sc::element_type_empty)
1217  return;
1218 
1219  mnCount += nDataSize;
1220  }
1221 
1222  size_t getCount() const { return mnCount; }
1223 };
1224 
1225 }
1226 
1227 SCSIZE ScColumn::VisibleCount( SCROW nStartRow, SCROW nEndRow ) const
1228 {
1229  CellCounter aFunc;
1230  sc::ParseBlock(maCells.begin(), maCells, aFunc, nStartRow, nEndRow);
1231  return aFunc.getCount();
1232 }
1233 
1235 {
1236  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
1237  sc::CellStoreType::const_iterator it = aPos.first;
1238  if (it == maCells.end())
1239  // Likely invalid row number.
1240  return false;
1241 
1242  return it->type != sc::element_type_empty;
1243 }
1244 
1246 {
1247  if (pAttrArray)
1248  return pAttrArray->IsEmpty();
1249  else
1250  return true;
1251 }
1252 
1253 bool ScColumn::IsEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
1254 {
1255  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
1256  sc::CellStoreType::const_iterator it = aPos.first;
1257  if (it == maCells.end())
1258  // Invalid row number.
1259  return false;
1260 
1261  if (it->type != sc::element_type_empty)
1262  // Non-empty cell at the start position.
1263  return false;
1264 
1265  // start position of next block which is not empty.
1266  SCROW nNextRow = nStartRow + it->size - aPos.second;
1267  return nEndRow < nNextRow;
1268 }
1269 
1270 bool ScColumn::IsNotesEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
1271 {
1272  std::pair<sc::CellNoteStoreType::const_iterator,size_t> aPos = maCellNotes.position(nStartRow);
1273  sc::CellNoteStoreType::const_iterator it = aPos.first;
1274  if (it == maCellNotes.end())
1275  // Invalid row number.
1276  return false;
1277 
1278  if (it->type != sc::element_type_empty)
1279  // Non-empty cell at the start position.
1280  return false;
1281 
1282  // start position of next block which is not empty.
1283  SCROW nNextRow = nStartRow + it->size - aPos.second;
1284  return nEndRow < nNextRow;
1285 }
1286 
1288 {
1289  // Given a range of rows, find a top or bottom empty segment. Skip the start row.
1290  switch (eDir)
1291  {
1292  case DIR_TOP:
1293  {
1294  // Determine the length of empty head segment.
1295  size_t nLength = nEndRow - nStartRow;
1296  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
1297  sc::CellStoreType::const_iterator it = aPos.first;
1298  if (it->type != sc::element_type_empty)
1299  // First row is already not empty.
1300  return 0;
1301 
1302  // length of this empty block minus the offset.
1303  size_t nThisLen = it->size - aPos.second;
1304  return std::min(nThisLen, nLength);
1305  }
1306  break;
1307  case DIR_BOTTOM:
1308  {
1309  // Determine the length of empty tail segment.
1310  size_t nLength = nEndRow - nStartRow;
1311  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nEndRow);
1312  sc::CellStoreType::const_iterator it = aPos.first;
1313  if (it->type != sc::element_type_empty)
1314  // end row is already not empty.
1315  return 0;
1316 
1317  // length of this empty block from the tip to the end row position.
1318  size_t nThisLen = aPos.second + 1;
1319  return std::min(nThisLen, nLength);
1320  }
1321  break;
1322  default:
1323  ;
1324  }
1325 
1326  return 0;
1327 }
1328 
1330 {
1331  if (IsEmptyData())
1332  return 0;
1333 
1334  sc::CellStoreType::const_iterator it = maCells.begin();
1335  if (it->type != sc::element_type_empty)
1336  return 0;
1337 
1338  return it->size;
1339 }
1340 
1342 {
1343  if (IsEmptyData())
1344  return 0;
1345 
1346  sc::CellStoreType::const_reverse_iterator it = maCells.rbegin();
1347  if (it->type != sc::element_type_empty)
1348  return GetDoc().MaxRow();
1349 
1350  return GetDoc().MaxRow() - static_cast<SCROW>(it->size);
1351 }
1352 
1353 SCROW ScColumn::GetLastDataPos( SCROW nLastRow, bool bConsiderCellNotes,
1354  bool bConsiderCellDrawObjects, bool bConsiderCellFormats ) const
1355 {
1356  sc::CellStoreType::const_position_type aPos = maCells.position(std::min(nLastRow,GetDoc().MaxRow()));
1357 
1358  if (bConsiderCellNotes && !IsNotesEmptyBlock(nLastRow, nLastRow))
1359  return nLastRow;
1360 
1361  if (bConsiderCellDrawObjects && !IsDrawObjectsEmptyBlock(nLastRow, nLastRow))
1362  return nLastRow;
1363 
1364  if (bConsiderCellFormats && HasVisibleAttrIn(nLastRow, nLastRow))
1365  return nLastRow;
1366 
1367  if (aPos.first->type != sc::element_type_empty)
1368  return nLastRow;
1369 
1370  if (aPos.first == maCells.begin())
1371  // This is the first block, and is empty.
1372  return 0;
1373 
1374  return static_cast<SCROW>(aPos.first->position - 1);
1375 }
1376 
1378 {
1379  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
1380  sc::CellStoreType::const_iterator it = aPos.first;
1381  if (it == maCells.end())
1382  return false;
1383 
1384  if (it->type == sc::element_type_empty)
1385  {
1386  if (it == maCells.begin())
1387  // No more previous non-empty cell.
1388  return false;
1389 
1390  rRow -= aPos.second + 1; // Last row position of the previous block.
1391  return true;
1392  }
1393 
1394  // This block is not empty.
1395  if (aPos.second)
1396  {
1397  // There are preceding cells in this block. Simply move back one cell.
1398  --rRow;
1399  return true;
1400  }
1401 
1402  // This is the first cell in a non-empty block. Move back to the previous block.
1403  if (it == maCells.begin())
1404  // No more preceding block.
1405  return false;
1406 
1407  --rRow; // Move to the last cell of the previous block.
1408  --it;
1409  if (it->type == sc::element_type_empty)
1410  {
1411  // This block is empty.
1412  if (it == maCells.begin())
1413  // No more preceding blocks.
1414  return false;
1415 
1416  // Skip the whole empty block segment.
1417  rRow -= it->size;
1418  }
1419 
1420  return true;
1421 }
1422 
1423 bool ScColumn::GetNextDataPos(SCROW& rRow) const // greater than rRow
1424 {
1425  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
1426  sc::CellStoreType::const_iterator it = aPos.first;
1427  if (it == maCells.end())
1428  return false;
1429 
1430  if (it->type == sc::element_type_empty)
1431  {
1432  // This block is empty. Skip ahead to the next block (if exists).
1433  rRow += it->size - aPos.second;
1434  ++it;
1435  if (it == maCells.end())
1436  // No more next block.
1437  return false;
1438 
1439  // Next block exists, and is non-empty.
1440  return true;
1441  }
1442 
1443  if (aPos.second < it->size - 1)
1444  {
1445  // There are still cells following the current position.
1446  ++rRow;
1447  return true;
1448  }
1449 
1450  // This is the last cell in the block. Move ahead to the next block.
1451  rRow += it->size - aPos.second; // First cell in the next block.
1452  ++it;
1453  if (it == maCells.end())
1454  // No more next block.
1455  return false;
1456 
1457  if (it->type == sc::element_type_empty)
1458  {
1459  // Next block is empty. Move to the next block.
1460  rRow += it->size;
1461  ++it;
1462  if (it == maCells.end())
1463  return false;
1464  }
1465 
1466  return true;
1467 }
1468 
1469 bool ScColumn::TrimEmptyBlocks(SCROW& rRowStart, SCROW& rRowEnd) const
1470 {
1471  assert(rRowStart <= rRowEnd);
1472  SCROW nRowStartNew = rRowStart, nRowEndNew = rRowEnd;
1473 
1474  // Trim down rRowStart first
1475  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRowStart);
1476  sc::CellStoreType::const_iterator it = aPos.first;
1477  if (it == maCells.end())
1478  return false;
1479 
1480  if (it->type == sc::element_type_empty)
1481  {
1482  // This block is empty. Skip ahead to the next block (if exists).
1483  nRowStartNew += it->size - aPos.second;
1484  if (nRowStartNew > rRowEnd)
1485  return false;
1486  ++it;
1487  if (it == maCells.end())
1488  // No more next block.
1489  return false;
1490  }
1491 
1492  // Trim up rRowEnd next
1493  aPos = maCells.position(rRowEnd);
1494  it = aPos.first;
1495  if (it == maCells.end())
1496  {
1497  rRowStart = nRowStartNew;
1498  return true; // Because trimming of rRowStart is ok
1499  }
1500 
1501  if (it->type == sc::element_type_empty)
1502  {
1503  // rRowEnd cannot be in the first block which is empty !
1504  assert(it != maCells.begin());
1505  // This block is empty. Skip to the previous block (it exists).
1506  nRowEndNew -= aPos.second + 1; // Last row position of the previous block.
1507  assert(nRowStartNew <= nRowEndNew);
1508  }
1509 
1510  rRowStart = nRowStartNew;
1511  rRowEnd = nRowEndNew;
1512  return true;
1513 }
1514 
1515 SCROW ScColumn::FindNextVisibleRow(SCROW nRow, bool bForward) const
1516 {
1517  if(bForward)
1518  {
1519  nRow++;
1520  SCROW nEndRow = 0;
1521  bool bHidden = GetDoc().RowHidden(nRow, nTab, nullptr, &nEndRow);
1522  if(bHidden)
1523  return std::min<SCROW>(GetDoc().MaxRow(), nEndRow + 1);
1524  else
1525  return nRow;
1526  }
1527  else
1528  {
1529  nRow--;
1530  SCROW nStartRow = GetDoc().MaxRow();
1531  bool bHidden = GetDoc().RowHidden(nRow, nTab, &nStartRow);
1532  if(bHidden)
1533  return std::max<SCROW>(0, nStartRow - 1);
1534  else
1535  return nRow;
1536  }
1537 }
1538 
1540  sc::CellStoreType::const_iterator& itPos, SCROW nRow, bool bForward) const
1541 {
1542  ScDocument& rDocument = GetDoc();
1543  if (bForward)
1544  {
1545  do
1546  {
1547  nRow++;
1548  SCROW nEndRow = 0;
1549  bool bHidden = rDocument.RowHidden(nRow, nTab, nullptr, &nEndRow);
1550  if (bHidden)
1551  {
1552  nRow = nEndRow + 1;
1553  if(nRow >= GetDoc().MaxRow())
1554  return GetDoc().MaxRow();
1555  }
1556 
1557  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
1558  itPos = aPos.first;
1559  if (itPos == maCells.end())
1560  // Invalid row.
1561  return GetDoc().MaxRow();
1562 
1563  if (itPos->type != sc::element_type_empty)
1564  return nRow;
1565 
1566  // Move to the last cell of the current empty block.
1567  nRow += itPos->size - aPos.second - 1;
1568  }
1569  while (nRow < GetDoc().MaxRow());
1570 
1571  return GetDoc().MaxRow();
1572  }
1573 
1574  do
1575  {
1576  nRow--;
1577  SCROW nStartRow = GetDoc().MaxRow();
1578  bool bHidden = rDocument.RowHidden(nRow, nTab, &nStartRow);
1579  if (bHidden)
1580  {
1581  nRow = nStartRow - 1;
1582  if(nRow <= 0)
1583  return 0;
1584  }
1585 
1586  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
1587  itPos = aPos.first;
1588  if (itPos == maCells.end())
1589  // Invalid row.
1590  return 0;
1591 
1592  if (itPos->type != sc::element_type_empty)
1593  return nRow;
1594 
1595  // Move to the first cell of the current empty block.
1596  nRow -= aPos.second;
1597  }
1598  while (nRow > 0);
1599 
1600  return 0;
1601 }
1602 
1604 {
1605  // Remove cached values. Given how often this function is called and how (not that) often
1606  // the cached values are used, it should be more efficient to just discard everything
1607  // instead of trying to figure out each time exactly what to discard.
1609 
1610  // TODO: Update column's "last updated" timestamp here.
1611 
1612 #if DEBUG_COLUMN_STORAGE
1613  if (maCells.size() != MAXROWCOUNT1)
1614  {
1615  cout << "ScColumn::CellStorageModified: Size of the cell array is incorrect." << endl;
1616  cout.flush();
1617  abort();
1618  }
1619 
1620  if (maCellTextAttrs.size() != MAXROWCOUNT1)
1621  {
1622  cout << "ScColumn::CellStorageModified: Size of the cell text attribute array is incorrect." << endl;
1623  cout.flush();
1624  abort();
1625  }
1626 
1627  if (maBroadcasters.size() != MAXROWCOUNT1)
1628  {
1629  cout << "ScColumn::CellStorageModified: Size of the broadcaster array is incorrect." << endl;
1630  cout.flush();
1631  abort();
1632  }
1633 
1634  // Make sure that these two containers are synchronized wrt empty segments.
1635  auto lIsEmptyType = [](const auto& rElement) { return rElement.type == sc::element_type_empty; };
1636  // Move to the first empty blocks.
1637  auto itCell = std::find_if(maCells.begin(), maCells.end(), lIsEmptyType);
1638  auto itAttr = std::find_if(maCellTextAttrs.begin(), maCellTextAttrs.end(), lIsEmptyType);
1639 
1640  while (itCell != maCells.end())
1641  {
1642  if (itCell->position != itAttr->position || itCell->size != itAttr->size)
1643  {
1644  cout << "ScColumn::CellStorageModified: Cell array and cell text attribute array are out of sync." << endl;
1645  cout << "-- cell array" << endl;
1646  maCells.dump_blocks(cout);
1647  cout << "-- attribute array" << endl;
1648  maCellTextAttrs.dump_blocks(cout);
1649  cout.flush();
1650  abort();
1651  }
1652 
1653  // Move to the next empty blocks.
1654  ++itCell;
1655  itCell = std::find_if(itCell, maCells.end(), lIsEmptyType);
1656 
1657  ++itAttr;
1658  itAttr = std::find_if(itAttr, maCellTextAttrs.end(), lIsEmptyType);
1659  }
1660 #endif
1661 }
1662 
1663 #if DUMP_COLUMN_STORAGE
1664 
1665 namespace {
1666 
1667 #define DUMP_FORMULA_RESULTS 0
1668 
1669 struct ColumnStorageDumper
1670 {
1671  const ScDocument& mrDoc;
1672 
1673  ColumnStorageDumper( const ScDocument& rDoc ) : mrDoc(rDoc) {}
1674 
1675  void operator() (const sc::CellStoreType::value_type& rNode) const
1676  {
1677  switch (rNode.type)
1678  {
1680  cout << " * numeric block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1681  break;
1683  cout << " * string block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1684  break;
1686  cout << " * edit-text block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1687  break;
1689  dumpFormulaBlock(rNode);
1690  break;
1692  cout << " * empty block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1693  break;
1694  default:
1695  cout << " * unknown block" << endl;
1696  }
1697  }
1698 
1699  void dumpFormulaBlock(const sc::CellStoreType::value_type& rNode) const
1700  {
1701  cout << " * formula block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1702  sc::formula_block::const_iterator it = sc::formula_block::begin(*rNode.data);
1703  sc::formula_block::const_iterator itEnd = sc::formula_block::end(*rNode.data);
1704 
1705  for (; it != itEnd; ++it)
1706  {
1707  const ScFormulaCell* pCell = *it;
1708  if (!pCell->IsShared())
1709  {
1710  cout << " * row " << pCell->aPos.Row() << " not shared" << endl;
1711  printFormula(pCell);
1712  printResult(pCell);
1713  continue;
1714  }
1715 
1716  if (pCell->GetSharedTopRow() != pCell->aPos.Row())
1717  {
1718  cout << " * row " << pCell->aPos.Row() << " shared with top row "
1719  << pCell->GetSharedTopRow() << " with length " << pCell->GetSharedLength()
1720  << endl;
1721  continue;
1722  }
1723 
1724  SCROW nLen = pCell->GetSharedLength();
1725  cout << " * group: start=" << pCell->aPos.Row() << ", length=" << nLen << endl;
1726  printFormula(pCell);
1727  printResult(pCell);
1728 
1729  if (nLen > 1)
1730  {
1731  for (SCROW i = 0; i < nLen-1; ++i, ++it)
1732  {
1733  pCell = *it;
1734  printResult(pCell);
1735  }
1736  }
1737  }
1738  }
1739 
1740  void printFormula(const ScFormulaCell* pCell) const
1741  {
1742  sc::TokenStringContext aCxt(mrDoc, mrDoc.GetGrammar());
1743  OUString aFormula = pCell->GetCode()->CreateString(aCxt, pCell->aPos);
1744  cout << " * formula: " << aFormula << endl;
1745  }
1746 
1747 #if DUMP_FORMULA_RESULTS
1748  void printResult(const ScFormulaCell* pCell) const
1749  {
1750  sc::FormulaResultValue aRes = pCell->GetResult();
1751  cout << " * result: ";
1752  switch (aRes.meType)
1753  {
1755  cout << aRes.mfValue << " (type: value)";
1756  break;
1758  cout << "'" << aRes.maString.getString() << "' (type: string)";
1759  break;
1761  cout << "error (" << static_cast<int>(aRes.mnError) << ")";
1762  break;
1764  cout << "invalid";
1765  break;
1766  }
1767 
1768  cout << endl;
1769  }
1770 #else
1771  void printResult(const ScFormulaCell*) const
1772  {
1773  (void) this; /* loplugin:staticmethods */
1774  }
1775 #endif
1776 };
1777 
1778 }
1779 
1780 void ScColumn::DumpColumnStorage() const
1781 {
1782  cout << "-- table: " << nTab << "; column: " << nCol << endl;
1783  std::for_each(maCells.begin(), maCells.end(), ColumnStorageDumper(GetDoc()));
1784  cout << "--" << endl;
1785 }
1786 #endif
1787 
1788 void ScColumn::CopyCellTextAttrsToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol) const
1789 {
1790  rDestCol.maCellTextAttrs.set_empty(nRow1, nRow2); // Empty the destination range first.
1791 
1792  sc::CellTextAttrStoreType::const_iterator itBlk = maCellTextAttrs.begin(), itBlkEnd = maCellTextAttrs.end();
1793 
1794  // Locate the top row position.
1795  size_t nBlockStart = 0, nRowPos = static_cast<size_t>(nRow1);
1796  itBlk = std::find_if(itBlk, itBlkEnd, [&nRowPos, &nBlockStart](const auto& rAttr) {
1797  return nBlockStart <= nRowPos && nRowPos < nBlockStart + rAttr.size; });
1798 
1799  if (itBlk == itBlkEnd)
1800  // Specified range not found. Bail out.
1801  return;
1802 
1803  size_t nBlockEnd;
1804  size_t nOffsetInBlock = nRowPos - nBlockStart;
1805 
1806  nRowPos = static_cast<size_t>(nRow2); // End row position.
1807 
1808  // Keep copying until we hit the end row position.
1809  sc::celltextattr_block::const_iterator itData, itDataEnd;
1810  for (; itBlk != itBlkEnd; ++itBlk, nBlockStart = nBlockEnd, nOffsetInBlock = 0)
1811  {
1812  nBlockEnd = nBlockStart + itBlk->size;
1813  if (!itBlk->data)
1814  {
1815  // Empty block.
1816  if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
1817  // This block contains the end row.
1818  rDestCol.maCellTextAttrs.set_empty(nBlockStart + nOffsetInBlock, nRowPos);
1819  else
1820  rDestCol.maCellTextAttrs.set_empty(nBlockStart + nOffsetInBlock, nBlockEnd-1);
1821 
1822  continue;
1823  }
1824 
1825  // Non-empty block.
1826  itData = sc::celltextattr_block::begin(*itBlk->data);
1827  itDataEnd = sc::celltextattr_block::end(*itBlk->data);
1828  std::advance(itData, nOffsetInBlock);
1829 
1830  if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
1831  {
1832  // This block contains the end row. Only copy partially.
1833  size_t nOffset = nRowPos - nBlockStart + 1;
1834  itDataEnd = sc::celltextattr_block::begin(*itBlk->data);
1835  std::advance(itDataEnd, nOffset);
1836 
1837  rDestCol.maCellTextAttrs.set(nBlockStart + nOffsetInBlock, itData, itDataEnd);
1838  break;
1839  }
1840 
1841  rDestCol.maCellTextAttrs.set(nBlockStart + nOffsetInBlock, itData, itDataEnd);
1842  }
1843 }
1844 
1845 namespace {
1846 
1847 class CopyCellNotesHandler
1848 {
1849  ScColumn& mrDestCol;
1850  sc::CellNoteStoreType& mrDestNotes;
1851  sc::CellNoteStoreType::iterator miPos;
1852  SCTAB mnSrcTab;
1853  SCCOL mnSrcCol;
1854  SCTAB mnDestTab;
1855  SCCOL mnDestCol;
1856  SCROW mnDestOffset;
1857  bool mbCloneCaption;
1858 
1859 public:
1860  CopyCellNotesHandler( const ScColumn& rSrcCol, ScColumn& rDestCol, SCROW nDestOffset, bool bCloneCaption ) :
1861  mrDestCol(rDestCol),
1862  mrDestNotes(rDestCol.GetCellNoteStore()),
1863  miPos(mrDestNotes.begin()),
1864  mnSrcTab(rSrcCol.GetTab()),
1865  mnSrcCol(rSrcCol.GetCol()),
1866  mnDestTab(rDestCol.GetTab()),
1867  mnDestCol(rDestCol.GetCol()),
1868  mnDestOffset(nDestOffset),
1869  mbCloneCaption(bCloneCaption) {}
1870 
1871  void operator() ( size_t nRow, const ScPostIt* p )
1872  {
1873  SCROW nDestRow = nRow + mnDestOffset;
1874  ScAddress aSrcPos(mnSrcCol, nRow, mnSrcTab);
1875  ScAddress aDestPos(mnDestCol, nDestRow, mnDestTab);
1876  miPos = mrDestNotes.set(miPos, nDestRow, p->Clone(aSrcPos, mrDestCol.GetDoc(), aDestPos, mbCloneCaption).release());
1877  // Notify our LOK clients also
1879  }
1880 };
1881 
1882 }
1883 
1885  SCROW nRow1, SCROW nRow2, ScColumn& rDestCol, bool bCloneCaption, SCROW nRowOffsetDest ) const
1886 {
1887  if (IsNotesEmptyBlock(nRow1, nRow2))
1888  // The column has no cell notes to copy between specified rows.
1889  return;
1890 
1891  ScDrawLayer *pDrawLayer = rDestCol.GetDoc().GetDrawLayer();
1892  bool bWasLocked = bool();
1893  if (pDrawLayer)
1894  {
1895  // Avoid O(n^2) by temporary locking SdrModel which disables broadcasting.
1896  // Each cell note adds undo listener, and all of them would be woken up in ScPostIt::CreateCaption.
1897  bWasLocked = pDrawLayer->isLocked();
1898  pDrawLayer->setLock(true);
1899  }
1900  CopyCellNotesHandler aFunc(*this, rDestCol, nRowOffsetDest, bCloneCaption);
1901  sc::ParseNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
1902  if (pDrawLayer)
1903  pDrawLayer->setLock(bWasLocked);
1904 }
1905 
1906 void ScColumn::DuplicateNotes(SCROW nStartRow, size_t nDataSize, ScColumn& rDestCol, sc::ColumnBlockPosition& maDestBlockPos,
1907  bool bCloneCaption, SCROW nRowOffsetDest ) const
1908 {
1909  CopyCellNotesToDocument(nStartRow, nStartRow + nDataSize -1, rDestCol, bCloneCaption, nRowOffsetDest);
1910  maDestBlockPos.miCellNotePos = rDestCol.maCellNotes.begin();
1911 }
1912 
1914 {
1915  return maBroadcasters.get<SvtBroadcaster*>(nRow);
1916 }
1917 
1919 {
1920  return maBroadcasters.get<SvtBroadcaster*>(nRow);
1921 }
1922 
1924 {
1925  rBlockPos.miBroadcasterPos =
1926  maBroadcasters.set_empty(rBlockPos.miBroadcasterPos, nRow1, nRow2);
1927 }
1928 
1930 {
1931  for (auto& rBroadcaster : maBroadcasters)
1932  {
1933  if (rBroadcaster.type == sc::element_type_broadcaster)
1934  {
1935  sc::broadcaster_block::iterator it = sc::broadcaster_block::begin(*rBroadcaster.data);
1936  sc::broadcaster_block::iterator itEnd = sc::broadcaster_block::end(*rBroadcaster.data);
1937  for (; it != itEnd; ++it)
1938  (*it)->PrepareForDestruction();
1939  }
1940  }
1941 }
1942 
1944 {
1945  return maCellNotes.get<ScPostIt*>(nRow);
1946 }
1947 
1949 {
1950  return maCellNotes.get<ScPostIt*>(nRow);
1951 }
1952 
1954 {
1955  sc::CellNoteStoreType::const_position_type aPos = maCellNotes.position(rBlockPos.miCellNotePos, nRow);
1956  rBlockPos.miCellNotePos = aPos.first;
1957 
1958  if (aPos.first->type != sc::element_type_cellnote)
1959  return nullptr;
1960 
1961  return sc::cellnote_block::at(*aPos.first->data, aPos.second);
1962 }
1963 
1965 {
1966  return const_cast<ScPostIt*>(const_cast<const ScColumn*>(this)->GetCellNote( rBlockPos, nRow ));
1967 }
1968 
1969 void ScColumn::SetCellNote(SCROW nRow, std::unique_ptr<ScPostIt> pNote)
1970 {
1971  //pNote->UpdateCaptionPos(ScAddress(nCol, nRow, nTab)); // TODO notes useful ? slow import with many notes
1972  maCellNotes.set(nRow, pNote.release());
1973 }
1974 
1975 namespace {
1976  class CellNoteHandler
1977  {
1978  const ScDocument* m_pDocument;
1979  const ScAddress m_aAddress; // 'incomplete' address consisting of tab, column
1980  const bool m_bForgetCaptionOwnership;
1981 
1982  public:
1983  CellNoteHandler(const ScDocument* pDocument, const ScAddress& rPos, bool bForgetCaptionOwnership) :
1984  m_pDocument(pDocument),
1985  m_aAddress(rPos),
1986  m_bForgetCaptionOwnership(bForgetCaptionOwnership) {}
1987 
1988  void operator() ( size_t nRow, ScPostIt* p )
1989  {
1990  if (m_bForgetCaptionOwnership)
1991  p->ForgetCaption();
1992 
1993  // Create a 'complete' address object
1994  ScAddress aAddr(m_aAddress);
1995  aAddr.SetRow(nRow);
1996  // Notify our LOK clients
1998  }
1999  };
2000 } // anonymous namespace
2001 
2002 void ScColumn::CellNotesDeleting(SCROW nRow1, SCROW nRow2, bool bForgetCaptionOwnership)
2003 {
2004  ScAddress aAddr(nCol, 0, nTab);
2005  CellNoteHandler aFunc(&GetDoc(), aAddr, bForgetCaptionOwnership);
2006  sc::ParseNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
2007 }
2008 
2009 void ScColumn::DeleteCellNotes( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2, bool bForgetCaptionOwnership )
2010 {
2011  CellNotesDeleting(nRow1, nRow2, bForgetCaptionOwnership);
2012 
2013  rBlockPos.miCellNotePos =
2014  maCellNotes.set_empty(rBlockPos.miCellNotePos, nRow1, nRow2);
2015 }
2016 
2018 {
2019  return std::any_of(maCellNotes.begin(), maCellNotes.end(),
2020  [](const auto& rCellNote) {
2021  // Having a cellnote block automatically means there is at least one cell note.
2022  return rCellNote.type == sc::element_type_cellnote; });
2023 }
2024 
2026 {
2027  // hypothesis : the column has cell notes (should be checked before)
2028  SCROW maxRow = 0;
2029  for (const auto& rCellNote : maCellNotes)
2030  {
2031  if (rCellNote.type == sc::element_type_cellnote)
2032  maxRow = rCellNote.position + rCellNote.size -1;
2033  }
2034  return maxRow;
2035 }
2037 {
2038  // hypothesis : the column has cell notes (should be checked before)
2039  SCROW minRow = 0;
2040  sc::CellNoteStoreType::const_iterator it = std::find_if(maCellNotes.begin(), maCellNotes.end(),
2041  [](const auto& rCellNote) { return rCellNote.type == sc::element_type_cellnote; });
2042  if (it != maCellNotes.end())
2043  minRow = it->position;
2044  return minRow;
2045 }
2046 
2047 sal_uInt16 ScColumn::GetTextWidth(SCROW nRow) const
2048 {
2049  return maCellTextAttrs.get<sc::CellTextAttr>(nRow).mnTextWidth;
2050 }
2051 
2052 void ScColumn::SetTextWidth(SCROW nRow, sal_uInt16 nWidth)
2053 {
2054  sc::CellTextAttrStoreType::position_type aPos = maCellTextAttrs.position(nRow);
2055  if (aPos.first->type != sc::element_type_celltextattr)
2056  return;
2057 
2058  // Set new value only when the slot is not empty.
2059  sc::celltextattr_block::at(*aPos.first->data, aPos.second).mnTextWidth = nWidth;
2061 }
2062 
2064 {
2065  if (!GetDoc().ValidRow(nRow) || maCellTextAttrs.is_empty(nRow))
2066  return SvtScriptType::NONE;
2067 
2068  return maCellTextAttrs.get<sc::CellTextAttr>(nRow).mnScriptType;
2069 }
2070 
2072  sc::CellTextAttrStoreType::iterator& itPos, SCROW nRow1, SCROW nRow2, const sc::CellStoreType::iterator& itrCells_ )
2073 {
2074  if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
2075  return SvtScriptType::NONE;
2076 
2077  SCROW nRow = nRow1;
2078  std::pair<sc::CellTextAttrStoreType::iterator,size_t> aRet =
2079  maCellTextAttrs.position(itPos, nRow1);
2080 
2081  itPos = aRet.first; // Track the position of cell text attribute array.
2082  sc::CellStoreType::iterator itrCells = itrCells_;
2083 
2084  SvtScriptType nScriptType = SvtScriptType::NONE;
2085  bool bUpdated = false;
2086  if (itPos->type == sc::element_type_celltextattr)
2087  {
2088  sc::celltextattr_block::iterator it = sc::celltextattr_block::begin(*itPos->data);
2089  sc::celltextattr_block::iterator itEnd = sc::celltextattr_block::end(*itPos->data);
2090  std::advance(it, aRet.second);
2091  for (; it != itEnd; ++it, ++nRow)
2092  {
2093  if (nRow > nRow2)
2094  return nScriptType;
2095 
2096  sc::CellTextAttr& rVal = *it;
2097  if (UpdateScriptType(rVal, nRow, itrCells))
2098  bUpdated = true;
2099  nScriptType |= rVal.mnScriptType;
2100  }
2101  }
2102  else
2103  {
2104  // Skip this whole block.
2105  nRow += itPos->size - aRet.second;
2106  }
2107 
2108  while (nRow <= nRow2)
2109  {
2110  ++itPos;
2111  if (itPos == maCellTextAttrs.end())
2112  return nScriptType;
2113 
2114  if (itPos->type != sc::element_type_celltextattr)
2115  {
2116  // Skip this whole block.
2117  nRow += itPos->size;
2118  continue;
2119  }
2120 
2121  sc::celltextattr_block::iterator it = sc::celltextattr_block::begin(*itPos->data);
2122  sc::celltextattr_block::iterator itEnd = sc::celltextattr_block::end(*itPos->data);
2123  for (; it != itEnd; ++it, ++nRow)
2124  {
2125  if (nRow > nRow2)
2126  return nScriptType;
2127 
2128  sc::CellTextAttr& rVal = *it;
2129  if (UpdateScriptType(rVal, nRow, itrCells))
2130  bUpdated = true;
2131 
2132  nScriptType |= rVal.mnScriptType;
2133  }
2134  }
2135 
2136  if (bUpdated)
2138 
2139  return nScriptType;
2140 }
2141 
2143 {
2144  if (!GetDoc().ValidRow(nRow))
2145  return;
2146 
2147  sc::CellTextAttrStoreType::position_type aPos = maCellTextAttrs.position(nRow);
2148  if (aPos.first->type != sc::element_type_celltextattr)
2149  // Set new value only when the slot is already set.
2150  return;
2151 
2152  sc::celltextattr_block::at(*aPos.first->data, aPos.second).mnScriptType = nType;
2154 }
2155 
2156 size_t ScColumn::GetFormulaHash( SCROW nRow ) const
2157 {
2158  const ScFormulaCell* pCell = FetchFormulaCell(nRow);
2159  return pCell ? pCell->GetHash() : 0;
2160 }
2161 
2163 {
2164  const ScFormulaCell* pCell = FetchFormulaCell(nRow);
2165  return pCell ? pCell->GetVectorState() : FormulaVectorUnknown;
2166 }
2167 
2169 {
2170  std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nRow);
2171  sc::CellStoreType::iterator it = aPos.first;
2172  if (it == maCells.end())
2173  // Invalid row. Return a null token.
2174  return formula::FormulaTokenRef();
2175 
2176  switch (it->type)
2177  {
2179  {
2180  double fVal = sc::numeric_block::at(*it->data, aPos.second);
2182  }
2184  {
2185  ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
2186  if (p->IsValue())
2188 
2190  }
2192  {
2193  const svl::SharedString& rSS = sc::string_block::at(*it->data, aPos.second);
2195  }
2197  {
2198  const EditTextObject* pText = sc::edittext_block::at(*it->data, aPos.second);
2199  OUString aStr = ScEditUtil::GetString(*pText, &GetDoc());
2200  svl::SharedString aSS( GetDoc().GetSharedStringPool().intern(aStr));
2202  }
2204  default:
2205  // Return a value of 0.0 in all the other cases.
2207  }
2208 }
2209 
2210 namespace {
2211 
2212 class ToMatrixHandler
2213 {
2214  ScMatrix& mrMat;
2215  SCCOL mnMatCol;
2216  SCROW mnTopRow;
2217  ScDocument* mpDoc;
2218  svl::SharedStringPool& mrStrPool;
2219 public:
2220  ToMatrixHandler(ScMatrix& rMat, SCCOL nMatCol, SCROW nTopRow, ScDocument* pDoc) :
2221  mrMat(rMat), mnMatCol(nMatCol), mnTopRow(nTopRow),
2222  mpDoc(pDoc), mrStrPool(pDoc->GetSharedStringPool()) {}
2223 
2224  void operator() (size_t nRow, double fVal)
2225  {
2226  mrMat.PutDouble(fVal, mnMatCol, nRow - mnTopRow);
2227  }
2228 
2229  void operator() (size_t nRow, const ScFormulaCell* p)
2230  {
2231  // Formula cell may need to re-calculate.
2232  ScFormulaCell& rCell = const_cast<ScFormulaCell&>(*p);
2233  if (rCell.IsValue())
2234  mrMat.PutDouble(rCell.GetValue(), mnMatCol, nRow - mnTopRow);
2235  else
2236  mrMat.PutString(rCell.GetString(), mnMatCol, nRow - mnTopRow);
2237  }
2238 
2239  void operator() (size_t nRow, const svl::SharedString& rSS)
2240  {
2241  mrMat.PutString(rSS, mnMatCol, nRow - mnTopRow);
2242  }
2243 
2244  void operator() (size_t nRow, const EditTextObject* pStr)
2245  {
2246  mrMat.PutString(mrStrPool.intern(ScEditUtil::GetString(*pStr, mpDoc)), mnMatCol, nRow - mnTopRow);
2247  }
2248 };
2249 
2250 }
2251 
2252 bool ScColumn::ResolveStaticReference( ScMatrix& rMat, SCCOL nMatCol, SCROW nRow1, SCROW nRow2 )
2253 {
2254  if (nRow1 > nRow2)
2255  return false;
2256 
2257  ToMatrixHandler aFunc(rMat, nMatCol, nRow1, &GetDoc());
2258  sc::ParseAllNonEmpty(maCells.begin(), maCells, nRow1, nRow2, aFunc);
2259  return true;
2260 }
2261 
2262 namespace {
2263 
2264 struct CellBucket
2265 {
2266  SCSIZE mnEmpValStart;
2267  SCSIZE mnNumValStart;
2268  SCSIZE mnStrValStart;
2269  SCSIZE mnEmpValCount;
2270  std::vector<double> maNumVals;
2271  std::vector<svl::SharedString> maStrVals;
2272 
2273  CellBucket() : mnEmpValStart(0), mnNumValStart(0), mnStrValStart(0), mnEmpValCount(0) {}
2274 
2275  void flush(ScMatrix& rMat, SCSIZE nCol)
2276  {
2277  if (mnEmpValCount)
2278  {
2279  rMat.PutEmptyResultVector(mnEmpValCount, nCol, mnEmpValStart);
2280  reset();
2281  }
2282  else if (!maNumVals.empty())
2283  {
2284  const double* p = maNumVals.data();
2285  rMat.PutDouble(p, maNumVals.size(), nCol, mnNumValStart);
2286  reset();
2287  }
2288  else if (!maStrVals.empty())
2289  {
2290  const svl::SharedString* p = maStrVals.data();
2291  rMat.PutString(p, maStrVals.size(), nCol, mnStrValStart);
2292  reset();
2293  }
2294  }
2295 
2296  void reset()
2297  {
2298  mnEmpValStart = mnNumValStart = mnStrValStart = 0;
2299  mnEmpValCount = 0;
2300  maNumVals.clear();
2301  maStrVals.clear();
2302  }
2303 };
2304 
2305 class FillMatrixHandler
2306 {
2307  ScMatrix& mrMat;
2308  size_t mnMatCol;
2309  size_t mnTopRow;
2310 
2311  ScDocument* mpDoc;
2312  svl::SharedStringPool& mrPool;
2313  svl::SharedStringPool* mpPool; // if matrix is not in the same document
2314 
2315 public:
2316  FillMatrixHandler(ScMatrix& rMat, size_t nMatCol, size_t nTopRow, ScDocument* pDoc, svl::SharedStringPool* pPool) :
2317  mrMat(rMat), mnMatCol(nMatCol), mnTopRow(nTopRow),
2318  mpDoc(pDoc), mrPool(pDoc->GetSharedStringPool()), mpPool(pPool) {}
2319 
2320  void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
2321  {
2322  size_t nMatRow = node.position + nOffset - mnTopRow;
2323 
2324  switch (node.type)
2325  {
2327  {
2328  const double* p = &sc::numeric_block::at(*node.data, nOffset);
2329  mrMat.PutDouble(p, nDataSize, mnMatCol, nMatRow);
2330  }
2331  break;
2333  {
2334  if (!mpPool)
2335  {
2336  const svl::SharedString* p = &sc::string_block::at(*node.data, nOffset);
2337  mrMat.PutString(p, nDataSize, mnMatCol, nMatRow);
2338  }
2339  else
2340  {
2341  std::vector<svl::SharedString> aStrings;
2342  aStrings.reserve(nDataSize);
2343  const svl::SharedString* p = &sc::string_block::at(*node.data, nOffset);
2344  for (size_t i = 0; i < nDataSize; ++i)
2345  {
2346  aStrings.push_back(mpPool->intern(p[i].getString()));
2347  }
2348  mrMat.PutString(aStrings.data(), aStrings.size(), mnMatCol, nMatRow);
2349  }
2350  }
2351  break;
2353  {
2354  std::vector<svl::SharedString> aSSs;
2355  aSSs.reserve(nDataSize);
2356  sc::edittext_block::const_iterator it = sc::edittext_block::begin(*node.data);
2357  std::advance(it, nOffset);
2358  sc::edittext_block::const_iterator itEnd = it;
2359  std::advance(itEnd, nDataSize);
2360  for (; it != itEnd; ++it)
2361  {
2362  OUString aStr = ScEditUtil::GetString(**it, mpDoc);
2363  if (!mpPool)
2364  aSSs.push_back(mrPool.intern(aStr));
2365  else
2366  aSSs.push_back(mpPool->intern(aStr));
2367  }
2368 
2369  const svl::SharedString* p = aSSs.data();
2370  mrMat.PutString(p, nDataSize, mnMatCol, nMatRow);
2371  }
2372  break;
2374  {
2375  CellBucket aBucket;
2376  sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
2377  std::advance(it, nOffset);
2378  sc::formula_block::const_iterator itEnd = it;
2379  std::advance(itEnd, nDataSize);
2380 
2381  size_t nPrevRow = 0, nThisRow = node.position + nOffset;
2382  for (; it != itEnd; ++it, nPrevRow = nThisRow, ++nThisRow)
2383  {
2384  ScFormulaCell& rCell = **it;
2385 
2386  if (rCell.IsEmpty())
2387  {
2388  if (aBucket.mnEmpValCount && nThisRow == nPrevRow + 1)
2389  {
2390  // Secondary empty results.
2391  ++aBucket.mnEmpValCount;
2392  }
2393  else
2394  {
2395  // First empty result.
2396  aBucket.flush(mrMat, mnMatCol);
2397  aBucket.mnEmpValStart = nThisRow - mnTopRow;
2398  ++aBucket.mnEmpValCount;
2399  }
2400  continue;
2401  }
2402 
2403  FormulaError nErr;
2404  double fVal;
2405  if (rCell.GetErrorOrValue(nErr, fVal))
2406  {
2407  if (nErr != FormulaError::NONE)
2408  fVal = CreateDoubleError(nErr);
2409 
2410  if (!aBucket.maNumVals.empty() && nThisRow == nPrevRow + 1)
2411  {
2412  // Secondary numbers.
2413  aBucket.maNumVals.push_back(fVal);
2414  }
2415  else
2416  {
2417  // First number.
2418  aBucket.flush(mrMat, mnMatCol);
2419  aBucket.mnNumValStart = nThisRow - mnTopRow;
2420  aBucket.maNumVals.push_back(fVal);
2421  }
2422  continue;
2423  }
2424 
2425  svl::SharedString aStr = rCell.GetString();
2426  if (mpPool)
2427  aStr = mpPool->intern(aStr.getString());
2428  if (!aBucket.maStrVals.empty() && nThisRow == nPrevRow + 1)
2429  {
2430  // Secondary strings.
2431  aBucket.maStrVals.push_back(aStr);
2432  }
2433  else
2434  {
2435  // First string.
2436  aBucket.flush(mrMat, mnMatCol);
2437  aBucket.mnStrValStart = nThisRow - mnTopRow;
2438  aBucket.maStrVals.push_back(aStr);
2439  }
2440  }
2441 
2442  aBucket.flush(mrMat, mnMatCol);
2443  }
2444  break;
2445  default:
2446  ;
2447  }
2448  }
2449 };
2450 
2451 }
2452 
2453 void ScColumn::FillMatrix( ScMatrix& rMat, size_t nMatCol, SCROW nRow1, SCROW nRow2, svl::SharedStringPool* pPool ) const
2454 {
2455  FillMatrixHandler aFunc(rMat, nMatCol, nRow1, &GetDoc(), pPool);
2456  sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
2457 }
2458 
2459 namespace {
2460 
2461 template<typename Blk>
2462 void getBlockIterators(
2463  const sc::CellStoreType::iterator& it, size_t& rLenRemain,
2464  typename Blk::iterator& rData, typename Blk::iterator& rDataEnd )
2465 {
2466  rData = Blk::begin(*it->data);
2467  if (rLenRemain >= it->size)
2468  {
2469  // Block is shorter than the remaining requested length.
2470  rDataEnd = Blk::end(*it->data);
2471  rLenRemain -= it->size;
2472  }
2473  else
2474  {
2475  rDataEnd = rData;
2476  std::advance(rDataEnd, rLenRemain);
2477  rLenRemain = 0;
2478  }
2479 }
2480 
2481 bool appendToBlock(
2483  size_t nPos, size_t nArrayLen, const sc::CellStoreType::iterator& _it, const sc::CellStoreType::iterator& itEnd )
2484 {
2485  svl::SharedStringPool& rPool = pDoc->GetSharedStringPool();
2486  size_t nLenRemain = nArrayLen - nPos;
2487  double fNan;
2488  rtl::math::setNan(&fNan);
2489 
2490  for (sc::CellStoreType::iterator it = _it; it != itEnd; ++it)
2491  {
2492  switch (it->type)
2493  {
2495  {
2496  sc::string_block::iterator itData, itDataEnd;
2497  getBlockIterators<sc::string_block>(it, nLenRemain, itData, itDataEnd);
2498  rCxt.ensureStrArray(rColArray, nArrayLen);
2499 
2500  for (; itData != itDataEnd; ++itData, ++nPos)
2501  (*rColArray.mpStrArray)[nPos] = itData->getData();
2502  }
2503  break;
2505  {
2506  sc::edittext_block::iterator itData, itDataEnd;
2507  getBlockIterators<sc::edittext_block>(it, nLenRemain, itData, itDataEnd);
2508  rCxt.ensureStrArray(rColArray, nArrayLen);
2509 
2510  for (; itData != itDataEnd; ++itData, ++nPos)
2511  {
2512  OUString aStr = ScEditUtil::GetString(**itData, pDoc);
2513  (*rColArray.mpStrArray)[nPos] = rPool.intern(aStr).getData();
2514  }
2515  }
2516  break;
2518  {
2519  sc::formula_block::iterator itData, itDataEnd;
2520  getBlockIterators<sc::formula_block>(it, nLenRemain, itData, itDataEnd);
2521 
2522  /* tdf#91416 setting progress in triggers a resize of the window
2523  and so ScTabView::DoResize and an InterpretVisible and
2524  InterpretDirtyCells which resets the mpFormulaGroupCxt that
2525  the current rCxt points to, which is bad, so disable progress
2526  during GetResult
2527  */
2529  bool bTempDisableProgress = pProgress && pProgress->Enabled();
2530  if (bTempDisableProgress)
2531  pProgress->Disable();
2532 
2533  for (; itData != itDataEnd; ++itData, ++nPos)
2534  {
2535  ScFormulaCell& rFC = **itData;
2536 
2537  sc::FormulaResultValue aRes = rFC.GetResult();
2538 
2539  if (aRes.meType == sc::FormulaResultValue::Invalid || aRes.mnError != FormulaError::NONE)
2540  {
2541  if (aRes.mnError == FormulaError::CircularReference)
2542  {
2543  // This cell needs to be recalculated on next visit.
2544  rFC.SetErrCode(FormulaError::NONE);
2545  rFC.SetDirtyVar();
2546  }
2547  return false;
2548  }
2549 
2551  {
2552  rCxt.ensureStrArray(rColArray, nArrayLen);
2553  (*rColArray.mpStrArray)[nPos] = aRes.maString.getData();
2554  }
2555  else
2556  {
2557  rCxt.ensureNumArray(rColArray, nArrayLen);
2558  (*rColArray.mpNumArray)[nPos] = aRes.mfValue;
2559  }
2560  }
2561 
2562  if (bTempDisableProgress)
2563  pProgress->Enable();
2564  }
2565  break;
2567  {
2568  if (nLenRemain > it->size)
2569  {
2570  nPos += it->size;
2571  nLenRemain -= it->size;
2572  }
2573  else
2574  {
2575  nPos = nArrayLen;
2576  nLenRemain = 0;
2577  }
2578  }
2579  break;
2581  {
2582  sc::numeric_block::iterator itData, itDataEnd;
2583  getBlockIterators<sc::numeric_block>(it, nLenRemain, itData, itDataEnd);
2584  rCxt.ensureNumArray(rColArray, nArrayLen);
2585 
2586  for (; itData != itDataEnd; ++itData, ++nPos)
2587  (*rColArray.mpNumArray)[nPos] = *itData;
2588  }
2589  break;
2590  default:
2591  return false;
2592  }
2593 
2594  if (!nLenRemain)
2595  return true;
2596  }
2597 
2598  return false;
2599 }
2600 
2601 void copyFirstStringBlock(
2602  ScDocument& rDoc, sc::FormulaGroupContext::StrArrayType& rArray, size_t nLen, const sc::CellStoreType::iterator& itBlk )
2603 {
2604  sc::FormulaGroupContext::StrArrayType::iterator itArray = rArray.begin();
2605 
2606  switch (itBlk->type)
2607  {
2609  {
2610  sc::string_block::iterator it = sc::string_block::begin(*itBlk->data);
2611  sc::string_block::iterator itEnd = it;
2612  std::advance(itEnd, nLen);
2613  for (; it != itEnd; ++it, ++itArray)
2614  *itArray = it->getData();
2615  }
2616  break;
2618  {
2619  sc::edittext_block::iterator it = sc::edittext_block::begin(*itBlk->data);
2620  sc::edittext_block::iterator itEnd = it;
2621  std::advance(itEnd, nLen);
2622 
2624  for (; it != itEnd; ++it, ++itArray)
2625  {
2626  EditTextObject* pText = *it;
2627  OUString aStr = ScEditUtil::GetString(*pText, &rDoc);
2628  *itArray = rPool.intern(aStr).getData();
2629  }
2630  }
2631  break;
2632  default:
2633  ;
2634  }
2635 }
2636 
2638 copyFirstFormulaBlock(
2639  sc::FormulaGroupContext& rCxt, const sc::CellStoreType::iterator& itBlk, size_t nArrayLen,
2640  SCTAB nTab, SCCOL nCol )
2641 {
2642  double fNan;
2643  rtl::math::setNan(&fNan);
2644 
2645  size_t nLen = std::min(itBlk->size, nArrayLen);
2646 
2647  sc::formula_block::iterator it = sc::formula_block::begin(*itBlk->data);
2648  sc::formula_block::iterator itEnd;
2649 
2650  sc::FormulaGroupContext::NumArrayType* pNumArray = nullptr;
2651  sc::FormulaGroupContext::StrArrayType* pStrArray = nullptr;
2652 
2653  itEnd = it;
2654  std::advance(itEnd, nLen);
2655  size_t nPos = 0;
2656  for (; it != itEnd; ++it, ++nPos)
2657  {
2658  ScFormulaCell& rFC = **it;
2659  sc::FormulaResultValue aRes = rFC.GetResult();
2660  if (aRes.meType == sc::FormulaResultValue::Invalid || aRes.mnError != FormulaError::NONE)
2661  {
2662  if (aRes.mnError == FormulaError::CircularReference)
2663  {
2664  // This cell needs to be recalculated on next visit.
2665  rFC.SetErrCode(FormulaError::NONE);
2666  rFC.SetDirtyVar();
2667  }
2668  return nullptr;
2669  }
2670 
2672  {
2673  if (!pNumArray)
2674  {
2675  rCxt.m_NumArrays.push_back(
2676  std::make_unique<sc::FormulaGroupContext::NumArrayType>(nArrayLen, fNan));
2677  pNumArray = rCxt.m_NumArrays.back().get();
2678  }
2679 
2680  (*pNumArray)[nPos] = aRes.mfValue;
2681  }
2682  else
2683  {
2684  if (!pStrArray)
2685  {
2686  rCxt.m_StrArrays.push_back(
2687  std::make_unique<sc::FormulaGroupContext::StrArrayType>(nArrayLen, nullptr));
2688  pStrArray = rCxt.m_StrArrays.back().get();
2689  }
2690 
2691  (*pStrArray)[nPos] = aRes.maString.getData();
2692  }
2693  }
2694 
2695  if (!pNumArray && !pStrArray)
2696  // At least one of these arrays should be allocated.
2697  return nullptr;
2698 
2699  return rCxt.setCachedColArray(nTab, nCol, pNumArray, pStrArray);
2700 }
2701 
2702 struct NonNullStringFinder
2703 {
2704  bool operator() (const rtl_uString* p) const { return p != nullptr; }
2705 };
2706 
2707 bool hasNonEmpty( const sc::FormulaGroupContext::StrArrayType& rArray, SCROW nRow1, SCROW nRow2 )
2708 {
2709  // The caller has to make sure the array is at least nRow2+1 long.
2710  sc::FormulaGroupContext::StrArrayType::const_iterator it = rArray.begin();
2711  std::advance(it, nRow1);
2712  sc::FormulaGroupContext::StrArrayType::const_iterator itEnd = it;
2713  std::advance(itEnd, nRow2-nRow1+1);
2714  return std::any_of(it, itEnd, NonNullStringFinder());
2715 }
2716 
2717 struct ProtectFormulaGroupContext
2718 {
2719  ProtectFormulaGroupContext( ScDocument* d )
2720  : doc( d ) { doc->BlockFormulaGroupContextDiscard( true ); }
2721  ~ProtectFormulaGroupContext()
2722  { doc->BlockFormulaGroupContextDiscard( false ); }
2723  ScDocument* doc;
2724 };
2725 
2726 }
2727 
2729 {
2730  if (nRow1 > nRow2)
2732 
2733  // See if the requested range is already cached.
2734  ScDocument& rDocument = GetDoc();
2735  sc::FormulaGroupContext& rCxt = *(rDocument.GetFormulaGroupContext());
2736  sc::FormulaGroupContext::ColArray* pColArray = rCxt.getCachedColArray(nTab, nCol, nRow2+1);
2737  if (pColArray)
2738  {
2739  const double* pNum = nullptr;
2740  if (pColArray->mpNumArray)
2741  pNum = &(*pColArray->mpNumArray)[nRow1];
2742 
2743  rtl_uString** pStr = nullptr;
2744  if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
2745  pStr = &(*pColArray->mpStrArray)[nRow1];
2746 
2747  return formula::VectorRefArray(pNum, pStr);
2748  }
2749 
2750  // ScColumn::CellStorageModified() simply discards the entire cache (FormulaGroupContext)
2751  // on any modification. However getting cell values may cause this to be called
2752  // if interpreting a cell results in a change to it (not just its result though).
2753  // So temporarily block the discarding.
2754  ProtectFormulaGroupContext protectContext(&GetDoc());
2755 
2756  double fNan;
2757  rtl::math::setNan(&fNan);
2758 
2759  // We need to fetch all cell values from row 0 to nRow2 for caching purposes.
2760  sc::CellStoreType::iterator itBlk = maCells.begin();
2761  switch (itBlk->type)
2762  {
2764  {
2765  if (o3tl::make_unsigned(nRow2) < itBlk->size)
2766  {
2767  // Requested range falls within the first block. No need to cache.
2768  const double* p = &sc::numeric_block::at(*itBlk->data, nRow1);
2769  return formula::VectorRefArray(p);
2770  }
2771 
2772  // Allocate a new array and copy the values to it.
2773  sc::numeric_block::const_iterator it = sc::numeric_block::begin(*itBlk->data);
2774  sc::numeric_block::const_iterator itEnd = sc::numeric_block::end(*itBlk->data);
2775  rCxt.m_NumArrays.push_back(
2776  std::make_unique<sc::FormulaGroupContext::NumArrayType>(it, itEnd));
2777  sc::FormulaGroupContext::NumArrayType& rArray = *rCxt.m_NumArrays.back();
2778  rArray.resize(nRow2+1, fNan); // allocate to the requested length.
2779 
2780  pColArray = rCxt.setCachedColArray(nTab, nCol, &rArray, nullptr);
2781  if (!pColArray)
2782  // Failed to insert a new cached column array.
2784 
2785  // Fill the remaining array with values from the following blocks.
2786  size_t nPos = itBlk->size;
2787  ++itBlk;
2788  if (!appendToBlock(&rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
2789  {
2790  rCxt.discardCachedColArray(nTab, nCol);
2792  }
2793 
2794  rtl_uString** pStr = nullptr;
2795  if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
2796  pStr = &(*pColArray->mpStrArray)[nRow1];
2797 
2798  return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], pStr);
2799  }
2800  break;
2803  {
2804  rCxt.m_StrArrays.push_back(
2805  std::make_unique<sc::FormulaGroupContext::StrArrayType>(nRow2+1, nullptr));
2806  sc::FormulaGroupContext::StrArrayType& rArray = *rCxt.m_StrArrays.back();
2807  pColArray = rCxt.setCachedColArray(nTab, nCol, nullptr, &rArray);
2808  if (!pColArray)
2809  // Failed to insert a new cached column array.
2810  return formula::VectorRefArray();
2811 
2812  if (o3tl::make_unsigned(nRow2) < itBlk->size)
2813  {
2814  // Requested range falls within the first block.
2815  copyFirstStringBlock(rDocument, rArray, nRow2+1, itBlk);
2816  return formula::VectorRefArray(&rArray[nRow1]);
2817  }
2818 
2819  copyFirstStringBlock(rDocument, rArray, itBlk->size, itBlk);
2820 
2821  // Fill the remaining array with values from the following blocks.
2822  size_t nPos = itBlk->size;
2823  ++itBlk;
2824  if (!appendToBlock(&rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
2825  {
2826  rCxt.discardCachedColArray(nTab, nCol);
2828  }
2829 
2830  assert(pColArray->mpStrArray);
2831 
2832  rtl_uString** pStr = nullptr;
2833  if (hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
2834  pStr = &(*pColArray->mpStrArray)[nRow1];
2835 
2836  if (pColArray->mpNumArray)
2837  return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], pStr);
2838  else
2839  return formula::VectorRefArray(pStr);
2840  }
2841  break;
2843  {
2844  if (o3tl::make_unsigned(nRow2) < itBlk->size)
2845  {
2846  // Requested length is within a single block, and the data is
2847  // not cached.
2848  pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol);
2849  if (!pColArray)
2850  // Failed to insert a new cached column array.
2852 
2853  const double* pNum = nullptr;
2854  rtl_uString** pStr = nullptr;
2855  if (pColArray->mpNumArray)
2856  pNum = &(*pColArray->mpNumArray)[nRow1];
2857  if (pColArray->mpStrArray)
2858  pStr = &(*pColArray->mpStrArray)[nRow1];
2859 
2860  return formula::VectorRefArray(pNum, pStr);
2861  }
2862 
2863  pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol);
2864  if (!pColArray)
2865  {
2866  // Failed to insert a new cached column array.
2868  }
2869 
2870  size_t nPos = itBlk->size;
2871  ++itBlk;
2872  if (!appendToBlock(&rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
2873  {
2874  rCxt.discardCachedColArray(nTab, nCol);
2876  }
2877 
2878  const double* pNum = nullptr;
2879  rtl_uString** pStr = nullptr;
2880  if (pColArray->mpNumArray)
2881  pNum = &(*pColArray->mpNumArray)[nRow1];
2882  if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
2883  pStr = &(*pColArray->mpStrArray)[nRow1];
2884 
2885  return formula::VectorRefArray(pNum, pStr);
2886  }
2887  break;
2889  {
2890  // Fill the whole length with NaN's.
2891  rCxt.m_NumArrays.push_back(
2892  std::make_unique<sc::FormulaGroupContext::NumArrayType>(nRow2+1, fNan));
2893  sc::FormulaGroupContext::NumArrayType& rArray = *rCxt.m_NumArrays.back();
2894  pColArray = rCxt.setCachedColArray(nTab, nCol, &rArray, nullptr);
2895  if (!pColArray)
2896  // Failed to insert a new cached column array.
2898 
2899  if (o3tl::make_unsigned(nRow2) < itBlk->size)
2900  return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]);
2901 
2902  // Fill the remaining array with values from the following blocks.
2903  size_t nPos = itBlk->size;
2904  ++itBlk;
2905  if (!appendToBlock(&rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
2906  {
2907  rCxt.discardCachedColArray(nTab, nCol);
2909  }
2910 
2911  if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
2912  return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], &(*pColArray->mpStrArray)[nRow1]);
2913  else
2914  return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]);
2915  }
2916  break;
2917  default:
2918  ;
2919  }
2920 
2922 }
2923 
2924 #ifdef DBG_UTIL
2925 static void assertNoInterpretNeededHelper( const sc::CellStoreType::value_type& node,
2926  size_t nOffset, size_t nDataSize )
2927 {
2928  switch (node.type)
2929  {
2931  {
2932  sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
2933  std::advance(it, nOffset);
2934  sc::formula_block::const_iterator itEnd = it;
2935  std::advance(itEnd, nDataSize);
2936  for (; it != itEnd; ++it)
2937  {
2938  const ScFormulaCell* pCell = *it;
2939  assert( !pCell->NeedsInterpret());
2940  }
2941  break;
2942  }
2943  }
2944 }
2946 {
2947  assert(nRow2 >= nRow1);
2949 }
2950 #endif
2951 
2952 void ScColumn::SetFormulaResults( SCROW nRow, const double* pResults, size_t nLen )
2953 {
2954  sc::CellStoreType::position_type aPos = maCells.position(nRow);
2955  sc::CellStoreType::iterator it = aPos.first;
2956  if (it->type != sc::element_type_formula)
2957  {
2958  // This is not a formula block.
2959  assert( false );
2960  return;
2961  }
2962 
2963  size_t nBlockLen = it->size - aPos.second;
2964  if (nBlockLen < nLen)
2965  // Result array is longer than the length of formula cells. Not good.
2966  return;
2967 
2968  sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
2969  std::advance(itCell, aPos.second);
2970 
2971  const double* pResEnd = pResults + nLen;
2972  for (; pResults != pResEnd; ++pResults, ++itCell)
2973  {
2974  ScFormulaCell& rCell = **itCell;
2975  FormulaError nErr = GetDoubleErrorValue(*pResults);
2976  if (nErr != FormulaError::NONE)
2977  rCell.SetResultError(nErr);
2978  else
2979  rCell.SetResultDouble(*pResults);
2980  rCell.ResetDirty();
2981  rCell.SetChanged(true);
2982  }
2983 }
2984 
2985 void ScColumn::CalculateInThread( ScInterpreterContext& rContext, SCROW nRow, size_t nLen, size_t nOffset,
2986  unsigned nThisThread, unsigned nThreadsTotal)
2987 {
2988  assert(GetDoc().IsThreadedGroupCalcInProgress());
2989 
2990  sc::CellStoreType::position_type aPos = maCells.position(nRow);
2991  sc::CellStoreType::iterator it = aPos.first;
2992  if (it->type != sc::element_type_formula)
2993  {
2994  // This is not a formula block.
2995  assert( false );
2996  return;
2997  }
2998 
2999  size_t nBlockLen = it->size - aPos.second;
3000  if (nBlockLen < nLen)
3001  // Length is longer than the length of formula cells. Not good.
3002  return;
3003 
3004  sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
3005  std::advance(itCell, aPos.second);
3006 
3007  for (size_t i = 0; i < nLen; ++i, ++itCell)
3008  {
3009  if (nThreadsTotal > 0 && ((i + nOffset) % nThreadsTotal) != nThisThread)
3010  continue;
3011 
3012  ScFormulaCell& rCell = **itCell;
3013  if (!rCell.NeedsInterpret())
3014  continue;
3015  // Here we don't call IncInterpretLevel() and DecInterpretLevel() as this call site is
3016  // always in a threaded calculation.
3017  rCell.InterpretTail(rContext, ScFormulaCell::SCITP_NORMAL);
3018  }
3019 }
3020 
3022 {
3023  sc::CellStoreType::position_type aPos = maCells.position(nRow);
3024  sc::CellStoreType::iterator it = aPos.first;
3025  if (it->type != sc::element_type_formula)
3026  {
3027  // This is not a formula block.
3028  assert( false );
3029  return;
3030  }
3031 
3032  size_t nBlockLen = it->size - aPos.second;
3033  if (nBlockLen < nLen)
3034  // Length is longer than the length of formula cells. Not good.
3035  return;
3036 
3037  sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
3038  std::advance(itCell, aPos.second);
3039 
3040  for (size_t i = 0; i < nLen; ++i, ++itCell)
3041  {
3042  ScFormulaCell& rCell = **itCell;
3043  rCell.HandleStuffAfterParallelCalculation(pInterpreter);
3044  }
3045 }
3046 
3047 void ScColumn::SetNumberFormat( SCROW nRow, sal_uInt32 nNumberFormat )
3048 {
3049  ApplyAttr(nRow, SfxUInt32Item(ATTR_VALUE_FORMAT, nNumberFormat));
3050 }
3051 
3052 ScFormulaCell * const * ScColumn::GetFormulaCellBlockAddress( SCROW nRow, size_t& rBlockSize ) const
3053 {
3054  if (!GetDoc().ValidRow(nRow))
3055  {
3056  rBlockSize = 0;
3057  return nullptr;
3058  }
3059 
3060  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
3061  sc::CellStoreType::const_iterator it = aPos.first;
3062  if (it == maCells.end())
3063  {
3064  rBlockSize = 0;
3065  return nullptr;
3066  }
3067 
3068  if (it->type != sc::element_type_formula)
3069  {
3070  // Not a formula cell.
3071  rBlockSize = 0;
3072  return nullptr;
3073  }
3074 
3075  rBlockSize = it->size;
3076  return &sc::formula_block::at(*it->data, aPos.second);
3077 }
3078 
3080 {
3081  size_t nBlockSize = 0;
3082  ScFormulaCell const * const * pp = GetFormulaCellBlockAddress( nRow, nBlockSize );
3083  return pp ? *pp : nullptr;
3084 }
3085 
3086 void ScColumn::FindDataAreaPos(SCROW& rRow, bool bDown) const
3087 {
3088  // If the cell is empty, find the next non-empty cell position. If the
3089  // cell is not empty, find the last non-empty cell position in the current
3090  // contiguous cell block.
3091 
3092  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
3093  sc::CellStoreType::const_iterator it = aPos.first;
3094  if (it == maCells.end())
3095  // Invalid row.
3096  return;
3097 
3098  if (it->type == sc::element_type_empty)
3099  {
3100  // Current cell is empty. Find the next non-empty cell.
3101  rRow = FindNextVisibleRowWithContent(it, rRow, bDown);
3102  return;
3103  }
3104 
3105  // Current cell is not empty.
3106  SCROW nNextRow = FindNextVisibleRow(rRow, bDown);
3107  aPos = maCells.position(it, nNextRow);
3108  it = aPos.first;
3109  if (it->type == sc::element_type_empty)
3110  {
3111  // Next visible cell is empty. Find the next non-empty cell.
3112  rRow = FindNextVisibleRowWithContent(it, nNextRow, bDown);
3113  return;
3114  }
3115 
3116  // Next visible cell is non-empty. Find the edge that's still visible.
3117  SCROW nLastRow = nNextRow;
3118  do
3119  {
3120  nNextRow = FindNextVisibleRow(nLastRow, bDown);
3121  if (nNextRow == nLastRow)
3122  break;
3123 
3124  aPos = maCells.position(it, nNextRow);
3125  it = aPos.first;
3126  if (it->type != sc::element_type_empty)
3127  nLastRow = nNextRow;
3128  }
3129  while (it->type != sc::element_type_empty);
3130 
3131  rRow = nLastRow;
3132 }
3133 
3134 bool ScColumn::HasDataAt(SCROW nRow, bool bConsiderCellNotes, bool bConsiderCellDrawObjects,
3135  bool bConsiderCellFormats) const
3136 {
3137  if (bConsiderCellNotes && !IsNotesEmptyBlock(nRow, nRow))
3138  return true;
3139 
3140  if (bConsiderCellDrawObjects && !IsDrawObjectsEmptyBlock(nRow, nRow))
3141  return true;
3142 
3143  if (bConsiderCellFormats && HasVisibleAttrIn(nRow, nRow))
3144  return true;
3145 
3146  return maCells.get_type(nRow) != sc::element_type_empty;
3147 }
3148 
3150  bool bConsiderCellNotes, bool bConsiderCellDrawObjects,
3151  bool bConsiderCellFormats) const
3152 {
3153  if (bConsiderCellNotes && !IsNotesEmptyBlock(nRow, nRow))
3154  return true;
3155 
3156  if (bConsiderCellDrawObjects && !IsDrawObjectsEmptyBlock(nRow, nRow))
3157  return true;
3158 
3159  if (bConsiderCellFormats && HasVisibleAttrIn(nRow, nRow))
3160  return true;
3161 
3162  std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
3163  if (aPos.first == maCells.end())
3164  return false;
3165  rBlockPos.miCellPos = aPos.first; // Store this for next call.
3166  return aPos.first->type != sc::element_type_empty;
3167 }
3168 
3170  bool bConsiderCellNotes, bool bConsiderCellDrawObjects, bool bConsiderCellFormats)
3171 {
3172  if (bConsiderCellNotes && !IsNotesEmptyBlock(nRow, nRow))
3173  return true;
3174 
3175  if (bConsiderCellDrawObjects && !IsDrawObjectsEmptyBlock(nRow, nRow))
3176  return true;
3177 
3178  if (bConsiderCellFormats && HasVisibleAttrIn(nRow, nRow))
3179  return true;
3180 
3181  std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
3182  if (aPos.first == maCells.end())
3183  return false;
3184  rBlockPos.miCellPos = aPos.first; // Store this for next call.
3185  return aPos.first->type != sc::element_type_empty;
3186 }
3187 
3188 bool ScColumn::IsAllAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW nEndRow ) const
3189 {
3190  if (pAttrArray && rCol.pAttrArray)
3191  return pAttrArray->IsAllEqual( *rCol.pAttrArray, nStartRow, nEndRow );
3192  else
3193  return !pAttrArray && !rCol.pAttrArray;
3194 }
3195 
3196 bool ScColumn::IsVisibleAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW nEndRow ) const
3197 {
3198  if (pAttrArray && rCol.pAttrArray)
3199  return pAttrArray->IsVisibleEqual( *rCol.pAttrArray, nStartRow, nEndRow );
3200  else
3201  return !pAttrArray && !rCol.pAttrArray;
3202 }
3203 
3204 bool ScColumn::GetFirstVisibleAttr( SCROW& rFirstRow ) const
3205 {
3206  if (pAttrArray)
3207  return pAttrArray->GetFirstVisibleAttr( rFirstRow );
3208  else
3209  return false;
3210 }
3211 
3212 bool ScColumn::GetLastVisibleAttr( SCROW& rLastRow ) const
3213 {
3214  if (pAttrArray)
3215  {
3216  // row of last cell is needed
3217  SCROW nLastData = GetLastDataPos(); // always including notes, 0 if none
3218 
3219  return pAttrArray->GetLastVisibleAttr( rLastRow, nLastData );
3220  }
3221  else
3222  return false;
3223 }
3224 
3225 bool ScColumn::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const
3226 {
3227  if (pAttrArray)
3228  return pAttrArray->HasVisibleAttrIn( nStartRow, nEndRow );
3229  else
3230  return false;
3231 }
3232 
3233 namespace {
3234 
3235 class FindUsedRowsHandler
3236 {
3237  typedef mdds::flat_segment_tree<SCROW,bool> UsedRowsType;
3238  UsedRowsType& mrUsed;
3239  UsedRowsType::const_iterator miUsed;
3240 public:
3241  explicit FindUsedRowsHandler(UsedRowsType& rUsed) : mrUsed(rUsed), miUsed(rUsed.begin()) {}
3242 
3243  void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
3244  {
3245  if (node.type == sc::element_type_empty)
3246  return;
3247 
3248  SCROW nRow1 = node.position + nOffset;
3249  SCROW nRow2 = nRow1 + nDataSize - 1;
3250  miUsed = mrUsed.insert(miUsed, nRow1, nRow2+1, true).first;
3251  }
3252 };
3253 
3254 }
3255 
3256 void ScColumn::FindUsed( SCROW nStartRow, SCROW nEndRow, mdds::flat_segment_tree<SCROW,bool>& rUsed ) const
3257 {
3258  FindUsedRowsHandler aFunc(rUsed);
3259  sc::ParseBlock(maCells.begin(), maCells, aFunc, nStartRow, nEndRow);
3260 }
3261 
3262 namespace {
3263 
3264 void startListening(
3265  sc::BroadcasterStoreType& rStore, sc::BroadcasterStoreType::iterator& itBlockPos, size_t nElemPos,
3266  SCROW nRow, SvtListener& rLst)
3267 {
3268  switch (itBlockPos->type)
3269  {
3271  {
3272  // Broadcaster already exists here.
3273  SvtBroadcaster* pBC = sc::broadcaster_block::at(*itBlockPos->data, nElemPos);
3274  rLst.StartListening(*pBC);
3275  }
3276  break;
3278  {
3279  // No broadcaster exists at this position yet.
3280  SvtBroadcaster* pBC = new SvtBroadcaster;
3281  rLst.StartListening(*pBC);
3282  itBlockPos = rStore.set(itBlockPos, nRow, pBC); // Store the block position for next iteration.
3283  }
3284  break;
3285  default:
3286 #if DEBUG_COLUMN_STORAGE
3287  cout << "ScColumn::StartListening: wrong block type encountered in the broadcaster storage." << endl;
3288  cout.flush();
3289  abort();
3290 #else
3291  ;
3292 #endif
3293  }
3294 }
3295 
3296 }
3297 
3299 {
3300  std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(nRow);
3301  startListening(maBroadcasters, aPos.first, aPos.second, nRow, rLst);
3302 }
3303 
3305 {
3306  SvtBroadcaster* pBC = GetBroadcaster(nRow);
3307  if (!pBC)
3308  return;
3309 
3310  rLst.EndListening(*pBC);
3311  if (!pBC->HasListeners())
3312  // There is no more listeners for this cell. Remove the broadcaster.
3313  maBroadcasters.set_empty(nRow, nRow);
3314 }
3315 
3317 {
3318  if (!GetDoc().ValidRow(rAddress.Row()))
3319  return;
3320 
3321  sc::ColumnBlockPosition* p = rCxt.getBlockPosition(rAddress.Tab(), rAddress.Col());
3322  if (!p)
3323  return;
3324 
3325  sc::BroadcasterStoreType::iterator& it = p->miBroadcasterPos;
3326  std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(it, rAddress.Row());
3327  it = aPos.first; // store the block position for next iteration.
3328  startListening(maBroadcasters, it, aPos.second, rAddress.Row(), rLst);
3329 }
3330 
3331 void ScColumn::EndListening( sc::EndListeningContext& rCxt, const ScAddress& rAddress, SvtListener& rListener )
3332 {
3333  sc::ColumnBlockPosition* p = rCxt.getBlockPosition(rAddress.Tab(), rAddress.Col());
3334  if (!p)
3335  return;
3336 
3337  sc::BroadcasterStoreType::iterator& it = p->miBroadcasterPos;
3338  std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(it, rAddress.Row());
3339  it = aPos.first; // store the block position for next iteration.
3340  if (it->type != sc::element_type_broadcaster)
3341  return;
3342 
3343  SvtBroadcaster* pBC = sc::broadcaster_block::at(*it->data, aPos.second);
3344  assert(pBC);
3345 
3346  rListener.EndListening(*pBC);
3347  if (!pBC->HasListeners())
3348  // There is no more listeners for this cell. Add it to the purge list for later purging.
3349  rCxt.addEmptyBroadcasterPosition(rAddress.Tab(), rAddress.Col(), rAddress.Row());
3350 }
3351 
3352 namespace {
3353 
3354 class CompileDBFormulaHandler
3355 {
3357 
3358 public:
3359  explicit CompileDBFormulaHandler( sc::CompileFormulaContext& rCxt ) :
3360  mrCxt(rCxt) {}
3361 
3362  void operator() (size_t, ScFormulaCell* p)
3363  {
3364  p->CompileDBFormula(mrCxt);
3365  }
3366 };
3367 
3368 struct CompileColRowNameFormulaHandler
3369 {
3371 public:
3372  explicit CompileColRowNameFormulaHandler( sc::CompileFormulaContext& rCxt ) : mrCxt(rCxt) {}
3373 
3374  void operator() (size_t, ScFormulaCell* p)
3375  {
3376  p->CompileColRowNameFormula(mrCxt);
3377  }
3378 };
3379 
3380 }
3381 
3383 {
3384  CompileDBFormulaHandler aFunc(rCxt);
3385  sc::ProcessFormula(maCells, aFunc);
3387 }
3388 
3390 {
3391  CompileColRowNameFormulaHandler aFunc(rCxt);
3392  sc::ProcessFormula(maCells, aFunc);
3394 }
3395 
3396 namespace {
3397 
3398 class UpdateSubTotalHandler
3399 {
3401 
3402  void update(double fVal, bool bVal)
3403  {
3404  if (mrData.getError())
3405  return;
3406 
3407  switch (mrData.getFunc())
3408  {
3409  case SUBTOTAL_FUNC_CNT2: // everything
3410  mrData.update( fVal);
3411  break;
3412  default: // only numeric values
3413  if (bVal)
3414  mrData.update( fVal);
3415  }
3416  }
3417 
3418 public:
3419  explicit UpdateSubTotalHandler(ScFunctionData& rData) : mrData(rData) {}
3420 
3421  void operator() (size_t /*nRow*/, double fVal)
3422  {
3423  update(fVal, true);
3424  }
3425 
3426  void operator() (size_t /*nRow*/, const svl::SharedString&)
3427  {
3428  update(0.0, false);
3429  }
3430 
3431  void operator() (size_t /*nRow*/, const EditTextObject*)
3432  {
3433  update(0.0, false);
3434  }
3435 
3436  void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
3437  {
3438  double fVal = 0.0;
3439  bool bVal = false;
3440  if (mrData.getFunc() != SUBTOTAL_FUNC_CNT2) // it doesn't interest us
3441  {
3442 
3443  if (pCell->GetErrCode() != FormulaError::NONE)
3444  {
3445  if (mrData.getFunc() != SUBTOTAL_FUNC_CNT) // simply remove from count
3446  mrData.setError();
3447  }
3448  else if (pCell->IsValue())
3449  {
3450  fVal = pCell->GetValue();
3451  bVal = true;
3452  }
3453  // otherwise text
3454  }
3455 
3456  update(fVal, bVal);
3457  }
3458 };
3459 
3460 }
3461 
3462 // multiple selections:
3464  const ScRangeList& rRanges, ScFunctionData& rData, const ScFlatBoolRowSegments& rHiddenRows )
3465 {
3466  sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
3467  aSpanSet.scan(rRanges, nTab, nCol); // mark all selected rows.
3468 
3469  if (aSpanSet.empty())
3470  return; // nothing to do, bail out
3471 
3472  // Exclude all hidden rows.
3474  SCROW nRow = 0;
3475  while (nRow <= GetDoc().MaxRow())
3476  {
3477  if (!rHiddenRows.getRangeData(nRow, aRange))
3478  break;
3479 
3480  if (aRange.mbValue)
3481  // Hidden range detected.
3482  aSpanSet.set(nRow, aRange.mnRow2, false);
3483 
3484  nRow = aRange.mnRow2 + 1;
3485  }
3486 
3488  aSpanSet.getSpans(aSpans);
3489 
3490  switch (rData.getFunc())
3491  {
3493  {
3494  // Simply count selected rows regardless of cell contents.
3495  for (const auto& rSpan : aSpans)
3496  rData.update( rSpan.mnRow2 - rSpan.mnRow1 + 1);
3497  }
3498  break;
3499  case SUBTOTAL_FUNC_CNT2:
3500  {
3501  // We need to parse all non-empty cells.
3502  sc::CellStoreType::const_iterator itCellPos = maCells.begin();
3503  UpdateSubTotalHandler aFunc(rData);
3504  for (const auto& rSpan : aSpans)
3505  {
3506  itCellPos = sc::ParseAllNonEmpty(
3507  itCellPos, maCells, rSpan.mnRow1, rSpan.mnRow2, aFunc);
3508  }
3509  }
3510  break;
3511  default:
3512  {
3513  // We need to parse only numeric values.
3514  sc::CellStoreType::const_iterator itCellPos = maCells.begin();
3515  UpdateSubTotalHandler aFunc(rData);
3516  for (const auto& rSpan : aSpans)
3517  {
3518  itCellPos = sc::ParseFormulaNumeric(
3519  itCellPos, maCells, rSpan.mnRow1, rSpan.mnRow2, aFunc);
3520  }
3521  }
3522  }
3523 }
3524 
3525 namespace {
3526 
3527 class WeightedCounter
3528 {
3530 public:
3531  WeightedCounter() : mnCount(0) {}
3532 
3533  void operator() (const sc::CellStoreType::value_type& node)
3534  {
3535  mnCount += getWeight(node);
3536  }
3537 
3538  static sal_uLong getWeight(const sc::CellStoreType::value_type& node)
3539  {
3540  switch (node.type)
3541  {
3544  return node.size;
3546  {
3547  // Each formula cell is worth its code length plus 5.
3548  return std::accumulate(sc::formula_block::begin(*node.data), sc::formula_block::end(*node.data), size_t(0),
3549  [](const size_t& rCount, const ScFormulaCell* p) { return rCount + 5 + p->GetCode()->GetCodeLen(); });
3550  }
3552  // each edit-text cell is worth 50.
3553  return node.size * 50;
3554  default:
3555  return 0;
3556  }
3557  }
3558 
3559  sal_uLong getCount() const { return mnCount; }
3560 };
3561 
3562 class WeightedCounterWithRows
3563 {
3564  const SCROW mnStartRow;
3565  const SCROW mnEndRow;
3566  sal_uLong mnCount;
3567 
3568 public:
3569  WeightedCounterWithRows(SCROW nStartRow, SCROW nEndRow)
3570  : mnStartRow(nStartRow)
3571  , mnEndRow(nEndRow)
3572  , mnCount(0)
3573  {
3574  }
3575 
3576  void operator() (const sc::CellStoreType::value_type& node)
3577  {
3578  const SCROW nRow1 = node.position;
3579  const SCROW nRow2 = nRow1 + 1;
3580 
3581  if ((nRow2 >= mnStartRow) && (nRow1 <= mnEndRow))
3582  {
3583  mnCount += WeightedCounter::getWeight(node);
3584  }
3585  }
3586 
3587  sal_uLong getCount() const { return mnCount; }
3588 };
3589 
3590 }
3591 
3593 {
3594  const WeightedCounter aFunc = std::for_each(maCells.begin(), maCells.end(),
3595  WeightedCounter());
3596  return aFunc.getCount();
3597 }
3598 
3600 {
3601  const WeightedCounterWithRows aFunc = std::for_each(maCells.begin(), maCells.end(),
3602  WeightedCounterWithRows(nStartRow, nEndRow));
3603  return aFunc.getCount();
3604 }
3605 
3606 namespace {
3607 
3608 class CodeCounter
3609 {
3610  size_t mnCount;
3611 public:
3612  CodeCounter() : mnCount(0) {}
3613 
3614  void operator() (size_t, const ScFormulaCell* p)
3615  {
3616  mnCount += p->GetCode()->GetCodeLen();
3617  }
3618 
3619  size_t getCount() const { return mnCount; }
3620 };
3621 
3622 }
3623 
3624 sal_uInt32 ScColumn::GetCodeCount() const
3625 {
3626  CodeCounter aFunc;
3627  sc::ParseFormula(maCells, aFunc);
3628  return aFunc.getCount();
3629 }
3630 
3632 {
3633  return pAttrArray ? pAttrArray->Count() : 0;
3634 }
3635 
3637 {
3638  return pAttrArray ? pAttrArray->Count( nRow1, nRow2 ) : 0;
3639 }
3640 
3642 {
3643  return pAttrArray && pAttrArray->Reserve( nReserve );
3644 }
3645 
3646 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
ScSubTotalFunc getFunc() const
Definition: subtotal.hxx:70
Matrix data type that can store values of mixed types.
Definition: scmatrix.hxx:112
SCROW GetCellNotesMinRow() const
Definition: column2.cxx:2036
SCROW GetCellNotesMaxRow() const
Definition: column2.cxx:2025
void CellStorageModified()
Called whenever the state of cell array gets modified i.e.
Definition: column2.cxx:1603
OutDevType GetOutDevType() const
SCROW GetNextMarked(SCCOL nCol, SCROW nRow, bool bUp) const
May return -1.
Definition: markdata.cxx:601
constexpr double nPPTY
SCCOL GetColMerge() const
Definition: attrib.hxx:68
SvxCellHorJustify
sal_uLong GetWeightedCount() const
Definition: column2.cxx:3592
void HandleStuffAfterParallelCalculation(ScInterpreter *pInterpreter)
SCSIZE GetEmptyLinesInBlock(SCROW nStartRow, SCROW nEndRow, ScDirection eDir) const
Definition: column2.cxx:1287
svl::SharedString maString
constexpr TypedWhichId< SvxEmphasisMarkItem > ATTR_FONT_EMPHASISMARK(121)
sal_Int32 nIndex
ScRotateDir GetRotateDir(const SfxItemSet *pCondSet) const
Definition: patattr.cxx:1327
OUString getString() const
SfxItemSet * GetPreviewFont()
Definition: document.hxx:1338
CellStoreType::const_iterator miCellPos
static ScProgress * GetInterpretProgress()
Definition: progress.hxx:65
SharedString intern(const OUString &rStr)
void SetTextWidth(SCROW nRow, sal_uInt16 nWidth)
Definition: column2.cxx:2052
bool isForceAutoSize() const
SC_DLLPUBLIC svl::SharedStringPool & GetSharedStringPool()
Definition: documen2.cxx:565
SC_DLLPUBLIC bool IsTabProtected(SCTAB nTab) const
Definition: documen3.cxx:1906
todo: It should be possible to have MarkArrays for each table, in order to enable "search all" across...
Definition: markdata.hxx:42
void CellNotesDeleting(SCROW nRow1, SCROW nRow2, bool bForgetCaptionOwnership)
Definition: column2.cxx:2002
SCSIZE VisibleCount(SCROW nStartRow, SCROW nEndRow) const
Definition: column2.cxx:1227
bool HasAttrib(SCROW nRow1, SCROW nRow2, HasAttrFlags nMask) const
Definition: column.cxx:303
std::unique_ptr< ScAttrArray > pAttrArray
Definition: column.hxx:130
ScDocument & GetDoc() const
Definition: column.hxx:183
const ScPatternAttr * GetPattern(SCROW nRow) const
Definition: column.cxx:370
bool NeedsInterpret() const
void PrepareBroadcastersForDestruction()
Definition: column2.cxx:1929
ScFormulaVectorState GetVectorState() const
SCROW Row() const
Definition: address.hxx:261
void setWidth(tools::Long nWidth)
SvxRotateMode
void ForgetCaption(bool bPreserveData=false)
Forgets the pointer to the note caption object.
Definition: postit.cxx:957
ScFormulaVectorState GetFormulaVectorState(SCROW nRow) const
Definition: column2.cxx:2162
void CalculateInThread(ScInterpreterContext &rContext, SCROW nRow, size_t nLen, size_t nOffset, unsigned nThisThread, unsigned nThreadsTotal)
Definition: column2.cxx:2985
ColArray * setCachedColArray(SCTAB nTab, SCCOL nCol, NumArrayType *pNumArray, StrArrayType *pStrArray)
Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
bool update()
bool HasVisibleAttrIn(SCROW nStartRow, SCROW nEndRow) const
Definition: column2.cxx:3225
constexpr TypedWhichId< SfxBoolItem > ATTR_VERTICAL_ASIAN(137)
void CompileDBFormula(sc::CompileFormulaContext &rCxt)
Definition: column2.cxx:3382
const mdds::mtv::element_t element_type_celltextattr
Definition: mtvelements.hxx:45
void PutDouble(double fVal, SCSIZE nC, SCSIZE nR)
Definition: scmatrix.cxx:2999
SCROW GetSharedTopRow() const
bool GetNextDataPos(SCROW &rRow) const
Definition: column2.cxx:1423
double getPPTX() const
SCTAB nTab
Definition: column.hxx:135
ScTokenArray * GetCode()
sal_uIntPtr sal_uLong
long Long
static void assertNoInterpretNeededHelper(const sc::CellStoreType::value_type &node, size_t nOffset, size_t nDataSize)
Definition: column2.cxx:2925
void SetCellNote(SCROW nRow, std::unique_ptr< ScPostIt > pNote)
Definition: column2.cxx:1969
OutputDevice * getOutputDevice()
bool IsOverlapped() const
Definition: attrib.hxx:101
#define STD_ROWHEIGHT_DIFF
Definition: global.hxx:105
void DuplicateNotes(SCROW nStartRow, size_t nDataSize, ScColumn &rDestCol, sc::ColumnBlockPosition &maDestBlockPos, bool bCloneCaption, SCROW nRowOffsetDest=0) const
Definition: column2.cxx:1906
constexpr TypedWhichId< SvxRotateModeItem > ATTR_ROTATE_MODE(136)
StoreT::const_iterator ParseBlock(const typename StoreT::const_iterator &itPos, const StoreT &rStore, Func &rFunc, typename StoreT::size_type nStart, typename StoreT::size_type nEnd)
Generic algorithm to parse blocks of multi_type_vector either partially or fully. ...
std::shared_ptr< sc::FormulaGroupContext > & GetFormulaGroupContext()
Definition: document.cxx:3516
CellStoreType::const_iterator ParseFormulaNumeric(const CellStoreType::const_iterator &itPos, const CellStoreType &rCells, SCROW nRow1, SCROW nRow2, Func &rFunc)
ColArray * getCachedColArray(SCTAB nTab, SCCOL nCol, size_t nSize)
keep track of longest array for each column.
SvNumFormatType GetType(sal_uInt32 nFIndex) const
SVX_ROTATE_MODE_STANDARD
Store position data for column array storage.
bool IsEmptyData() const
Definition: column2.cxx:1200
const MapMode & GetMapMode() const
sal_uInt16 GetCodeLen() const
CellStoreType::const_iterator ParseAllNonEmpty(const typename CellStoreType::const_iterator &itPos, const CellStoreType &rCells, SCROW nRow1, SCROW nRow2, Func &rFunc)
Definition: mtvcellfunc.hxx:95
bool HasVisibleDataAt(SCROW nRow) const
Definition: column2.cxx:1234
sal_Int16 GetRightMargin() const
void HandleStuffAfterParallelCalculation(SCROW nRow, size_t nLen, ScInterpreter *pInterpreter)
Definition: column2.cxx:3021
void addEmptyBroadcasterPosition(SCTAB nTab, SCCOL nCol, SCROW nRow)
constexpr TypedWhichId< ScLineBreakCell > ATTR_LINEBREAK(139)
This is very similar to ScCellValue, except that it references the original value instead of copying ...
Definition: cellvalue.hxx:103
formula::FormulaTokenRef ResolveStaticReference(SCROW nRow)
Definition: column2.cxx:2168
RowHeightsArray & getHeightArray()
SC_DLLPUBLIC formula::FormulaGrammar::Grammar GetGrammar() const
Definition: document.hxx:983
void ApplyAsianEditSettings(ScEditEngineDefaulter &rEngine)
Definition: documen9.cxx:673
bool empty() const
Whether there isn't any row tagged.
mdds::multi_type_vector< CellFunc, CellStoreEvent > CellStoreType
EEControlBits
bool IsEmptyAttr() const
Definition: column2.cxx:1245
void CompileColRowNameFormula(sc::CompileFormulaContext &rCxt)
Definition: column2.cxx:3389
void SetMapMode()
void PutString(const svl::SharedString &rStr, SCSIZE nC, SCSIZE nR)
Definition: scmatrix.cxx:3014
bool IsMultilineResult()
Determines whether or not the result string contains more than one paragraph.
bool ReservePatternCount(SCSIZE nReserve)
Definition: column2.cxx:3641
void ensureStrArray(ColArray &rColArray, size_t nArrayLen)
constexpr TypedWhichId< ScMergeAttr > ATTR_MERGE(144)
SC_DLLPUBLIC sal_uInt16 GetRowHeight(SCROW nRow, SCTAB nTab, bool bHiddenAsZero=true) const
Definition: document.cxx:4153
ColumnBlockPosition * getBlockPosition(SCTAB nTab, SCCOL nCol)
constexpr TypedWhichId< ScIndentItem > ATTR_INDENT(131)
const mdds::mtv::element_t element_type_cellnote
Definition: mtvelements.hxx:51
constexpr tools::Long Width() const
void ParseFormula(const CellStoreType &rStore, Func &rFunc)
Definition: mtvcellfunc.hxx:63
BroadcasterStoreType::iterator miBroadcasterPos
constexpr TypedWhichId< ScRotateValueItem > ATTR_ROTATE_VALUE(135)
void CompileDBFormula(sc::CompileFormulaContext &rCxt)
ObjectFormatterData & mrData
const mdds::mtv::element_t element_type_formula
Definition: mtvelements.hxx:49
bool EndListening(SvtBroadcaster &rBroadcaster)
const EditTextObject * mpEditText
Definition: cellvalue.hxx:109
Keep track of spans in a single column only.
SC_DLLPUBLIC sal_uInt16 GetOriginalWidth(SCCOL nCol, SCTAB nTab) const
Definition: document.cxx:4129
const ScPatternAttr * pPattern
Definition: column.hxx:101
ScRefCellValue GetCellValue(SCROW nRow) const
Definition: column.cxx:733
enumrange< T >::Iterator begin(enumrange< T >)
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:871
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:44
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
mdds::multi_type_vector< BCBlkFunc > BroadcasterStoreType
const vcl::Font & GetFont() const
Additional class containing cell annotation data.
Definition: postit.hxx:159
double GetValue()
bool IsMultiMarked() const
Definition: markdata.hxx:82
FormulaError GetErrCode()
static void LOKCommentNotify(LOKCommentNotificationType nType, const ScDocument *pDocument, const ScAddress &rPos, const ScPostIt *pNote)
Definition: docsh4.cxx:2481
void SetNumberFormat(SCROW nRow, sal_uInt32 nNumberFormat)
Definition: column2.cxx:3047
sc::CellStoreType maCells
Definition: column.hxx:128
bool isLocked() const
ScFormulaCell * mpFormula
Definition: cellvalue.hxx:110
SCTAB Tab() const
Definition: address.hxx:270
void DiscardFormulaGroupContext()
Definition: document.cxx:3525
SCROW GetLastDataPos() const
Definition: column2.cxx:1341
std::vector< double, DoubleAllocType > NumArrayType
FormulaError GetDoubleErrorValue(double fVal)
ScAddress aPos
constexpr auto convert(N n, sal_Int64 mul, sal_Int64 div)
const SfxPoolItem & GetItem(sal_uInt16 nWhichP) const
Definition: patattr.hxx:70
StrArrayStoreType m_StrArrays
manage life cycle of numeric arrays.
ScStyleSheet * GetPreviewCellStyle()
Definition: document.hxx:1342
static bool IsAmbiguousScript(SvtScriptType nScript)
Definition: column2.cxx:72
SvtBroadcaster * GetBroadcaster(SCROW nRow)
Definition: column2.cxx:1913
void discardCachedColArray(SCTAB nTab, SCCOL nCol)
DocumentType eType
constexpr double nPPTX
void PutEmptyResultVector(SCSIZE nCount, SCSIZE nC, SCSIZE nR)
Put a column vector of empty results, starting at row nR, must fit into dimensions.
Definition: scmatrix.cxx:3174
size_t GetFormulaHash(SCROW nRow) const
Definition: column2.cxx:2156
static SC_DLLPUBLIC sal_uInt16 nStdRowHeight
Definition: global.hxx:583
OUTDEV_PRINTER
SCROW GetSharedLength() const
bool IsShared() const
OUString CreateString(sc::TokenStringContext &rCxt, const ScAddress &rPos) const
Create a string representation of formula token array without modifying the internal state of the tok...
Definition: token.cxx:5096
SC_DLLPUBLIC ScDrawLayer * GetDrawLayer()
Definition: document.hxx:1057
ColumnBlockPosition * getBlockPosition(SCTAB nTab, SCCOL nCol)
SCCOL nCol
Definition: column.hxx:134
void getSpans(SpansType &rSpans) const
SC_DLLPUBLIC SvNumberFormatter * GetFormatTable() const
Definition: documen2.cxx:440
bool IsVerOverlapped() const
Definition: attrib.hxx:100
UNDERLYING_TYPE get() const
SvtScriptType
static void GetFont(vcl::Font &rFont, const SfxItemSet &rItemSet, ScAutoFontColorMode eAutoMode, const OutputDevice *pOutDev=nullptr, const Fraction *pScale=nullptr, const SfxItemSet *pCondSet=nullptr, SvtScriptType nScript=SvtScriptType::NONE, const Color *pBackConfigColor=nullptr, const Color *pTextConfigColor=nullptr)
Static helper function to fill a font object from the passed item set.
Definition: patattr.cxx:214
mdds::multi_type_vector< CNoteFunc > CellNoteStoreType
bool IsMarked() const
Definition: markdata.hxx:81
void FillMatrix(ScMatrix &rMat, size_t nMatCol, SCROW nRow1, SCROW nRow2, svl::SharedStringPool *pPool) const
Definition: column2.cxx:2453
constexpr TypedWhichId< SvxFontHeightItem > ATTR_CJK_FONT_HEIGHT(112)
void EndListening(SvtListener &rLst, SCROW nRow)
Definition: column2.cxx:3304
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
int i
const Fraction & getZoomY() const
void FindDataAreaPos(SCROW &rRow, bool bDown) const
Definition: column2.cxx:3086
void StartListening(SvtListener &rLst, SCROW nRow)
Definition: column2.cxx:3298
void DeleteBroadcasters(sc::ColumnBlockPosition &rBlockPos, SCROW nRow1, SCROW nRow2)
Definition: column2.cxx:1923
sal_Int32 getLength() const
bool TrimEmptyBlocks(SCROW &rRowStart, SCROW &rRowEnd) const
Definition: column2.cxx:1469
bool IsEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
Definition: column2.cxx:1253
size_t GetHash() const
sal_Int16 SCCOL
Definition: types.hxx:21
void RegroupFormulaCells(std::vector< ScAddress > *pGroupPos=nullptr)
Regroup formula cells for the entire column.
Definition: column3.cxx:3520
SvtScriptType mnScriptType
sc::CellTextAttrStoreType maCellTextAttrs
Definition: column.hxx:117
std::unique_ptr< ScPostIt > Clone(const ScAddress &rOwnPos, ScDocument &rDestDoc, const ScAddress &rDestPos, bool bCloneCaption) const
Clones this note and its caption object, if specified.
Definition: postit.cxx:877
static css::uno::Reference< css::linguistic2::XHyphenator > GetHyphenator()
ScFormulaCell *const * GetFormulaCellBlockAddress(SCROW nRow, size_t &rBlockSize) const
Definition: column2.cxx:3052
sal_uInt16 Count() const
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
const mdds::mtv::element_t element_type_empty
Definition: mtvelements.hxx:55
::boost::intrusive_ptr< FormulaToken > FormulaTokenRef
void set(SCROW nRow1, SCROW nRow2, bool bVal)
Context for creating string from an array of formula tokens, used in ScTokenArray::CreateString().
bool GetErrorOrValue(FormulaError &rErr, double &rVal)
void UpdateSelectionFunction(const ScRangeList &rRanges, ScFunctionData &rData, const ScFlatBoolRowSegments &rHiddenRows)
Definition: column2.cxx:3463
constexpr TypedWhichId< SfxUInt32Item > ATTR_VALUE_FORMAT(146)
const Size & GetFontSize() const
const mdds::mtv::element_t element_type_numeric
Mapped standard element types (for convenience).
Definition: mtvelements.hxx:54
void FindUsed(SCROW nStartRow, SCROW nEndRow, mdds::flat_segment_tree< SCROW, bool > &rUsed) const
Definition: column2.cxx:3256
size
sal_uInt32 GetNumberFormat(SvNumberFormatter *) const
Definition: patattr.cxx:1255
bool IsRunning() const
MapUnit GetMapUnit() const
tools::Long GetNeededSize(SCROW nRow, OutputDevice *pDev, double nPPTX, double nPPTY, const Fraction &rZoomX, const Fraction &rZoomY, bool bWidth, const ScNeededSizeOptions &rOptions, const ScPatternAttr **pPatternChange, bool bInPrintTwips=false) const
Definition: column2.cxx:82
CellStoreType::iterator ProcessFormula(const CellStoreType::iterator &it, CellStoreType &rStore, SCROW nRow1, SCROW nRow2, std::function< void(size_t, ScFormulaCell *)> aFuncElem)
Process formula cells found within specified row range.
Definition: mtvcellfunc.cxx:12
bool hasNumeric() const
Definition: cellvalue.cxx:622
std::size_t mnCount
bool StartListening(SvtBroadcaster &rBroadcaster)
void SetValue(A nPos, const D &rValue)
bool IsNotesEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
Definition: column2.cxx:1270
constexpr TypedWhichId< SvxFontHeightItem > ATTR_CTL_FONT_HEIGHT(117)
SC_DLLPUBLIC const SfxItemSet * GetCondResult(SCCOL nCol, SCROW nRow, SCTAB nTab, ScRefCellValue *pCell=nullptr) const
Definition: documen4.cxx:789
static SvxCellOrientation GetCellOrientation(const SfxItemSet &rItemSet, const SfxItemSet *pCondSet)
Definition: patattr.cxx:156
std::vector< rtl_uString * > StrArrayType
SfxItemPool * GetPool() const
double getPPTY() const
rtl_uString * getData()
void ApplyAttr(SCROW nRow, const SfxPoolItem &rAttr)
Definition: column.cxx:715
void setError()
Definition: subtotal.hxx:71
sal_Int16 GetBottomMargin() const
SCROW mnMaxTextRow
Definition: docparam.hxx:19
const svl::SharedString & GetString()
FormulaError
SCCOL Col() const
Definition: address.hxx:266
sal_uInt32 GetCodeCount() const
Definition: column2.cxx:3624
CellStoreType::iterator miCellPos
bool GetFirstVisibleAttr(SCROW &rFirstRow) const
Definition: column2.cxx:3204
void InterpretTail(ScInterpreterContext &, ScInterpretTailParameter)
void CopyCellNotesToDocument(SCROW nRow1, SCROW nRow2, ScColumn &rDestCol, bool bCloneCaption=true, SCROW nRowOffsetDest=0) const
Definition: column2.cxx:1884
bool GetLastVisibleAttr(SCROW &rLastRow) const
Definition: column2.cxx:3212
Point PixelToLogic(const Point &rDevicePt) const
Point LogicToPixel(const Point &rLogicPt) const
bool TestTabRefAbs(SCTAB nTable) const
Definition: column2.cxx:1193
bool IsHorOverlapped() const
Definition: attrib.hxx:99
const D & GetValue(A nPos) const
CellType meType
Definition: cellvalue.hxx:105
void GetOptimalHeight(sc::RowHeightContext &rCxt, SCROW nStartRow, SCROW nEndRow, sal_uInt16 nMinHeight, SCROW nMinStart)
Definition: column2.cxx:818
void DeleteCellNotes(sc::ColumnBlockPosition &rBlockPos, SCROW nRow1, SCROW nRow2, bool bForgetCaptionOwnership)
Definition: column2.cxx:2009
void setLock(bool bLock)
NumArrayStoreType m_NumArrays
constexpr TypedWhichId< ScMergeFlagAttr > ATTR_MERGE_FLAG(145)
sal_Int32 SCROW
Definition: types.hxx:17
To calculate a single subtotal function.
Definition: subtotal.hxx:60
ScPostIt * GetCellNote(SCROW nRow)
Definition: column2.cxx:1943
static SC_DLLPUBLIC SvtScriptType GetDefaultScriptType()
Definition: global.cxx:849
sc::BroadcasterStoreType maBroadcasters
Definition: column.hxx:123
double CreateDoubleError(FormulaError nErr)
bool ValidRow(SCROW nRow) const
Definition: document.hxx:874
ScMF
Definition: attrib.hxx:34
sal_uInt32 GetHeight() const
void AssertNoInterpretNeeded(SCROW nRow1, SCROW nRow2)
Definition: column2.cxx:2945
void SetFormulaResults(SCROW nRow, const double *pResults, size_t nLen)
Definition: column2.cxx:2952
bool IsVisibleAttrEqual(const ScColumn &rCol, SCROW nStartRow=0, SCROW nEndRow=MAXROW) const
Definition: column2.cxx:3196
sal_Int32 GetDenominator() const
sc::CellNoteStoreType maCellNotes
Definition: column.hxx:120
sal_uInt16 GetOptimalColWidth(OutputDevice *pDev, double nPPTX, double nPPTY, const Fraction &rZoomX, const Fraction &rZoomY, bool bFormula, sal_uInt16 nOldWidth, const ScMarkData *pMarkData, const ScColWidthParam *pParam) const
Definition: column2.cxx:668
SCROW GetFirstDataPos() const
Definition: column2.cxx:1329
constexpr tools::Long Height() const
void DumpColumnStorage() const
static sal_uInt16 lcl_GetAttribHeight(const ScPatternAttr &rPattern, sal_uInt16 nFontHeightId)
Definition: column2.cxx:786
SCROW FindNextVisibleRowWithContent(sc::CellStoreType::const_iterator &itPos, SCROW nRow, bool bForward) const
Definition: column2.cxx:1539
bool HasCellNotes() const
Definition: column2.cxx:2017
bool HasDataAt(SCROW nRow, bool bConsiderCellNotes=false, bool bConsiderCellDrawObjects=false, bool bConsiderCellFormats=false) const
Definition: column2.cxx:3134
SvtScriptType GetRangeScriptType(sc::CellTextAttrStoreType::iterator &itPos, SCROW nRow1, SCROW nRow2, const sc::CellStoreType::iterator &itr)
Get combined script types of the specified range.
Definition: column2.cxx:2071
void SetFont(const vcl::Font &rNewFont)
CellType
Definition: global.hxx:280
formula::VectorRefArray FetchVectorRefArray(SCROW nRow1, SCROW nRow2)
Definition: column2.cxx:2728
void CopyCellTextAttrsToDocument(SCROW nRow1, SCROW nRow2, ScColumn &rDestCol) const
Definition: column2.cxx:1788
std::unique_ptr< ScFieldEditEngine > CreateFieldEditEngine()
Definition: documen2.cxx:1119
const mdds::mtv::element_t element_type_edittext
Definition: mtvelements.hxx:48
void SetResultDouble(double n)
For import only: set a double result.
SvStream & endl(SvStream &rStr)
const mdds::mtv::element_t element_type_broadcaster
Custom element type IDs for multi_type_vector.
Definition: mtvelements.hxx:44
tools::Long GetTextHeight() const
SvtScriptType GetScriptType(SCROW nRow) const
Definition: column2.cxx:2063
void DisposeFieldEditEngine(std::unique_ptr< ScFieldEditEngine > &rpEditEngine)
Definition: documen2.cxx:1142
const Fraction & getZoomX() const
void RemoveEditAttribs(SCROW nStartRow, SCROW nEndRow)
Definition: column2.cxx:1186
CellNoteStoreType::iterator miCellNotePos
sal_Int16 GetLeftMargin() const
const ScFormulaCell * FetchFormulaCell(SCROW nRow) const
Definition: column2.cxx:3079
void update(double fNewVal)
Definition: subtotal.cxx:66
#define F_PI18000
SC_DLLPUBLIC SvtScriptType GetScriptType(SCCOL nCol, SCROW nRow, SCTAB nTab, const ScRefCellValue *pCell=nullptr)
Definition: documen6.cxx:132
tools::Long GetTextWidth(const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, vcl::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
constexpr TypedWhichId< SfxBoolItem > EE_PARA_HYPHENATE(EE_PARA_START+6)
constexpr TypedWhichId< SvxHorJustifyItem > ATTR_HOR_JUSTIFY(129)
constexpr TypedWhichId< ScCondFormatItem > ATTR_CONDITIONAL(154)
QPRO_FUNC_TYPE nType
Definition: qproform.cxx:400
std::vector< RowSpan > SpansType
sal_Int32 GetNumerator() const
SC_DLLPUBLIC ScPatternAttr * GetDefPattern() const
Definition: document.cxx:6043
sc::FormulaResultValue GetResult()
bool GetNextSpellingCell(SCROW &nRow, bool bInSel, const ScMarkData &rData) const
Definition: column2.cxx:1022
static OUString GetSpaceDelimitedString(const EditEngine &rEngine)
Retrieves string with paragraphs delimited by spaces.
Definition: editutil.cxx:97
CellNoteStoreType::const_iterator ParseNote(const CellNoteStoreType::const_iterator &itPos, const CellNoteStoreType &rStore, SCROW nStart, SCROW nEnd, Func &rFunc)
void SetResultError(FormulaError n)
bool HasListeners() const
SCROW FindNextVisibleRow(SCROW nRow, bool bForward) const
Definition: column2.cxx:1515
bool IsDrawObjectsEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
Definition: column.cxx:1954
SC_DLLPUBLIC SfxItemPool * GetEditPool() const
Definition: documen2.cxx:446
SC_DLLPUBLIC bool RowHidden(SCROW nRow, SCTAB nTab, SCROW *pFirstRow=nullptr, SCROW *pLastRow=nullptr) const
Definition: document.cxx:4421
void scan(const ScColumn &rColumn)
Scan an entire column and tag all non-empty cell positions.
void ensureNumArray(ColArray &rColArray, size_t nArrayLen)
void SetScriptType(SCROW nRow, SvtScriptType nType)
Definition: column2.cxx:2142
static SC_DLLPUBLIC OUString GetString(const EditTextObject &rEditText, const ScDocument *pDoc)
Retrieves string with paragraphs delimited by new lines (' ').
Definition: editutil.cxx:111
const sal_uInt16 * GetRanges() const
bool ValidRow(SCROW nRow, SCROW nMaxRow)
Definition: address.hxx:98
bool getError() const
Definition: subtotal.hxx:69
ScDirection
Definition: global.hxx:351
ScFormulaVectorState
When vectorization is enabled, we could potentially mass-calculate a series of formula token arrays i...
Definition: types.hxx:51
SCROW GetRowMerge() const
Definition: attrib.hxx:69
static void GetString(const ScRefCellValue &rCell, sal_uInt32 nFormat, OUString &rString, const Color **ppColor, SvNumberFormatter &rFormatter, const ScDocument &rDoc, bool bNullVals=true, bool bFormula=false, bool bUseStarFormat=false)
Definition: cellform.cxx:31
sal_Int32 nLength
sal_Int32 mnRow
bool HasAutoFilter() const
Definition: attrib.hxx:103
bool GetPrevDataPos(SCROW &rRow) const
Definition: column2.cxx:1377
SvxCellOrientation
bool getRangeData(SCROW nRow, RangeData &rData) const
#define SC_ROT_BREAK_FACTOR
Definition: column2.cxx:70
SC_DLLPUBLIC sal_uInt16 GetColWidth(SCCOL nCol, SCTAB nTab, bool bHiddenAsZero=true) const
Definition: document.cxx:4112
CellNoteStoreType::const_iterator miCellNotePos
sal_uInt16 GetTextWidth(SCROW nRow) const
Definition: column2.cxx:2047
void SetChanged(bool b)
void SetErrCode(FormulaError n)
const mdds::mtv::element_t element_type_string
Definition: mtvelements.hxx:47
constexpr TypedWhichId< SvxFontHeightItem > ATTR_FONT_HEIGHT(101)
aStr
bool IsAllAttrEqual(const ScColumn &rCol, SCROW nStartRow, SCROW nEndRow) const
Definition: column2.cxx:3188
bool HasEditCells(SCROW nStartRow, SCROW nEndRow, SCROW &rFirst)
Definition: column.cxx:3429
CellStoreType::iterator ProcessEditText(const CellStoreType::iterator &itPos, CellStoreType &rStore, SCROW nRow1, SCROW nRow2, Func &rFunc)
Definition: mtvcellfunc.hxx:55
sal_Int16 SCTAB
Definition: types.hxx:22
SC_DLLPUBLIC CRFlags GetRowFlags(SCROW nRow, SCTAB nTab) const
Definition: document.cxx:4328
constexpr TypedWhichId< SvxMarginItem > ATTR_MARGIN(143)
sal_Int16 nValue
bool UpdateScriptType(sc::CellTextAttr &rAttr, SCROW nRow, sc::CellStoreType::iterator &itr)
Definition: column3.cxx:761
void CompileColRowNameFormula(sc::CompileFormulaContext &rCxt)
void FillEditItemSet(SfxItemSet *pEditSet, const SfxItemSet *pCondSet=nullptr) const
Converts all Calc items contained in the own item set to edit engine items and puts them into pEditSe...
Definition: patattr.cxx:774
SCSIZE GetPatternCount() const
Definition: column2.cxx:3631
sal_Int16 GetTopMargin() const
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo