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