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