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