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