Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / source / core / data / column2.cxx
blobe0e5505da9aaaadd4af307d83681907cffd09b60
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.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <column.hxx>
21 #include <docsh.hxx>
22 #include <scitems.hxx>
23 #include <formulacell.hxx>
24 #include <document.hxx>
25 #include <drwlayer.hxx>
26 #include <attarray.hxx>
27 #include <patattr.hxx>
28 #include <cellform.hxx>
29 #include <editutil.hxx>
30 #include <subtotal.hxx>
31 #include <markdata.hxx>
32 #include <fillinfo.hxx>
33 #include <segmenttree.hxx>
34 #include <docparam.hxx>
35 #include <cellvalue.hxx>
36 #include <tokenarray.hxx>
37 #include <formulagroup.hxx>
38 #include <listenercontext.hxx>
39 #include <mtvcellfunc.hxx>
40 #include <progress.hxx>
41 #include <scmatrix.hxx>
42 #include <rowheightcontext.hxx>
43 #include <tokenstringcontext.hxx>
44 #include <sortparam.hxx>
45 #include <SparklineGroup.hxx>
46 #include <SparklineList.hxx>
48 #include <editeng/eeitem.hxx>
49 #include <o3tl/safeint.hxx>
50 #include <o3tl/unit_conversion.hxx>
51 #include <svx/algitem.hxx>
52 #include <editeng/editobj.hxx>
53 #include <editeng/editstat.hxx>
54 #include <editeng/emphasismarkitem.hxx>
55 #include <editeng/fhgtitem.hxx>
56 #include <svx/rotmodit.hxx>
57 #include <editeng/unolingu.hxx>
58 #include <editeng/justifyitem.hxx>
59 #include <svl/numformat.hxx>
60 #include <svl/zforlist.hxx>
61 #include <svl/broadcast.hxx>
62 #include <utility>
63 #include <vcl/outdev.hxx>
64 #include <formula/errorcodes.hxx>
65 #include <formula/vectortoken.hxx>
67 #include <algorithm>
68 #include <limits>
69 #include <memory>
70 #include <numeric>
72 // factor from font size to optimal cell height (text width)
73 #define SC_ROT_BREAK_FACTOR 6
75 static bool IsAmbiguousScript( SvtScriptType nScript )
77 //TODO: move to a header file
78 return ( nScript != SvtScriptType::LATIN &&
79 nScript != SvtScriptType::ASIAN &&
80 nScript != SvtScriptType::COMPLEX );
83 // Data operations
85 tools::Long ScColumn::GetNeededSize(
86 SCROW nRow, OutputDevice* pDev, double nPPTX, double nPPTY,
87 const Fraction& rZoomX, const Fraction& rZoomY,
88 bool bWidth, const ScNeededSizeOptions& rOptions,
89 const ScPatternAttr** ppPatternChange, bool bInPrintTwips ) const
91 // If bInPrintTwips is set, the size calculated should be in print twips,
92 // else it should be in pixels.
94 // Switch unit to MapTwip instead ? (temporarily and then revert before exit).
95 if (bInPrintTwips)
96 assert(pDev->GetMapMode().GetMapUnit() == MapUnit::MapTwip);
98 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
99 sc::CellStoreType::const_iterator it = aPos.first;
100 if (it == maCells.end() || it->type == sc::element_type_empty)
101 // Empty cell, or invalid row.
102 return 0;
104 tools::Long nValue = 0;
105 ScRefCellValue aCell = GetCellValue(it, aPos.second);
106 double nPPT = bWidth ? nPPTX : nPPTY;
108 auto conditionalScaleFunc = [bInPrintTwips](tools::Long nMeasure, double fScale) {
109 return bInPrintTwips ? nMeasure : static_cast<tools::Long>(nMeasure * fScale);
112 const ScPatternAttr* pPattern = rOptions.aPattern.getScPatternAttr();
113 if (!pPattern)
114 pPattern = pAttrArray->GetPattern( nRow );
116 // merged?
117 // Do not merge in conditional formatting
119 const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
120 const ScMergeFlagAttr* pFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
122 if ( bWidth )
124 if ( pFlag->IsHorOverlapped() )
125 return 0;
126 if ( rOptions.bSkipMerged && pMerge->GetColMerge() > 1 )
127 return 0;
129 else
131 if ( pFlag->IsVerOverlapped() )
132 return 0;
133 if ( rOptions.bSkipMerged && pMerge->GetRowMerge() > 1 )
134 return 0;
137 // conditional formatting
138 ScDocument& rDocument = GetDoc();
139 const SfxItemSet* pCondSet = rDocument.GetCondResult( nCol, nRow, nTab );
141 //The pPattern may change in GetCondResult
142 if (aCell.getType() == CELLTYPE_FORMULA)
144 pPattern = pAttrArray->GetPattern( nRow );
145 if (ppPatternChange)
146 *ppPatternChange = pPattern;
148 // line break?
150 const SvxHorJustifyItem* pCondItem;
151 SvxCellHorJustify eHorJust;
152 if (pCondSet && (pCondItem = pCondSet->GetItemIfSet(ATTR_HOR_JUSTIFY)) )
153 eHorJust = pCondItem->GetValue();
154 else
155 eHorJust = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
156 bool bBreak;
157 const ScLineBreakCell* pLineBreakCell;
158 if ( eHorJust == SvxCellHorJustify::Block )
159 bBreak = true;
160 else if ( pCondSet && (pLineBreakCell = pCondSet->GetItemIfSet(ATTR_LINEBREAK)) )
161 bBreak = pLineBreakCell->GetValue();
162 else
163 bBreak = pPattern->GetItem(ATTR_LINEBREAK).GetValue();
165 ScInterpreterContext& rContext = rDocument.GetNonThreadedContext();
166 sal_uInt32 nFormat = pPattern->GetNumberFormat( rContext, pCondSet );
168 // get "cell is value" flag
169 // Must be synchronized with ScOutputData::LayoutStrings()
170 bool bCellIsValue = (aCell.getType() == CELLTYPE_VALUE);
171 if (aCell.getType() == CELLTYPE_FORMULA)
173 ScFormulaCell* pFCell = aCell.getFormula();
174 bCellIsValue = pFCell->IsRunning();
175 if (!bCellIsValue)
177 bCellIsValue = pFCell->IsValue();
178 if (bCellIsValue)
180 // the pattern may change in IsValue()
181 pPattern = pAttrArray->GetPattern( nRow );
182 if (ppPatternChange)
183 *ppPatternChange = pPattern;
188 // #i111387#, tdf#121040: disable automatic line breaks for all number formats
189 if (bBreak && bCellIsValue && (rContext.NFGetType(nFormat) == SvNumFormatType::NUMBER))
191 // If a formula cell needs to be interpreted during aCell.hasNumeric()
192 // to determine the type, the pattern may get invalidated because the
193 // result may set a number format. In which case there's also the
194 // General format not set anymore...
195 bool bMayInvalidatePattern = (aCell.getType() == CELLTYPE_FORMULA);
196 const CellAttributeHolder aOldPattern(pPattern);
197 bool bNumeric = aCell.hasNumeric();
198 if (bMayInvalidatePattern)
200 pPattern = pAttrArray->GetPattern( nRow );
201 if (ppPatternChange)
202 *ppPatternChange = pPattern; // XXX caller may have to check for change!
204 if (bNumeric)
206 if (!bMayInvalidatePattern || ScPatternAttr::areSame(pPattern, aOldPattern.getScPatternAttr()))
207 bBreak = false;
208 else
210 nFormat = pPattern->GetNumberFormat( rContext, pCondSet );
211 if (rContext.NFGetType(nFormat) == SvNumFormatType::NUMBER)
212 bBreak = false;
217 // get other attributes from pattern and conditional formatting
219 SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet );
220 bool bAsianVertical = ( eOrient == SvxCellOrientation::Stacked &&
221 pPattern->GetItem( ATTR_VERTICAL_ASIAN, pCondSet ).GetValue() );
222 if ( bAsianVertical )
223 bBreak = false;
225 if ( bWidth && bBreak ) // after determining bAsianVertical (bBreak may be reset)
226 return 0;
228 Degree100 nRotate(0);
229 SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD;
230 if ( eOrient == SvxCellOrientation::Standard )
232 const ScRotateValueItem* pRotateValueItem;
233 if (pCondSet &&
234 (pRotateValueItem = pCondSet->GetItemIfSet(ATTR_ROTATE_VALUE)) )
235 nRotate = pRotateValueItem->GetValue();
236 else
237 nRotate = pPattern->GetItem(ATTR_ROTATE_VALUE).GetValue();
238 if ( nRotate )
240 const SvxRotateModeItem* pRotateModeItem;
241 if (pCondSet &&
242 (pRotateModeItem = pCondSet->GetItemIfSet(ATTR_ROTATE_MODE)) )
243 eRotMode = pRotateModeItem->GetValue();
244 else
245 eRotMode = pPattern->GetItem(ATTR_ROTATE_MODE).GetValue();
247 if ( nRotate == 18000_deg100 )
248 eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
252 if ( eHorJust == SvxCellHorJustify::Repeat )
254 // ignore orientation/rotation if "repeat" is active
255 eOrient = SvxCellOrientation::Standard;
256 nRotate = 0_deg100;
257 bAsianVertical = false;
260 const SvxMarginItem* pMargin;
261 if (pCondSet &&
262 (pMargin = pCondSet->GetItemIfSet(ATTR_MARGIN)) )
264 else
265 pMargin = &pPattern->GetItem(ATTR_MARGIN);
266 sal_uInt16 nIndent = 0;
267 if ( eHorJust == SvxCellHorJustify::Left )
269 const ScIndentItem* pIndentItem;
270 if (pCondSet &&
271 (pIndentItem = pCondSet->GetItemIfSet(ATTR_INDENT)) )
272 nIndent = pIndentItem->GetValue();
273 else
274 nIndent = pPattern->GetItem(ATTR_INDENT).GetValue();
277 SvtScriptType nScript = rDocument.GetScriptType(nCol, nRow, nTab);
278 if (nScript == SvtScriptType::NONE) nScript = ScGlobal::GetDefaultScriptType();
280 // also call SetFont for edit cells, because bGetFont may be set only once
281 // bGetFont is set also if script type changes
282 if (rOptions.bGetFont)
284 Fraction aFontZoom = ( eOrient == SvxCellOrientation::Standard ) ? rZoomX : rZoomY;
285 vcl::Font aFont;
286 aFont.SetKerning(FontKerning::NONE); // like ScDrawStringsVars::SetPattern
287 // font color doesn't matter here
288 pPattern->fillFontOnly(aFont, pDev, &aFontZoom, pCondSet, nScript);
289 pDev->SetFont(aFont);
292 bool bAddMargin = true;
293 CellType eCellType = aCell.getType();
295 bool bEditEngine = (eCellType == CELLTYPE_EDIT ||
296 eOrient == SvxCellOrientation::Stacked ||
297 IsAmbiguousScript(nScript) ||
298 ((eCellType == CELLTYPE_FORMULA) && aCell.getFormula()->IsMultilineResult()));
300 if (!bEditEngine) // direct output
302 const Color* pColor;
303 OUString aValStr = ScCellFormat::GetString(
304 aCell, nFormat, &pColor, &rContext, rDocument, true, rOptions.bFormula);
306 if (!aValStr.isEmpty())
308 // SetFont is moved up
310 tools::Long nWidth = 0;
311 if ( eOrient != SvxCellOrientation::Standard )
313 nWidth = pDev->GetTextHeight();
315 else if ( nRotate )
317 //TODO: take different X/Y scaling into consideration
319 // avoid calling the expensive GetTextWidth when not needed
320 auto TextWidth = [&, w = std::optional<tools::Long>()]() mutable
322 if (!w)
323 w = pDev->GetTextWidth(aValStr);
324 return *w;
326 auto TextHeight = [&, h = std::optional<tools::Long>()]() mutable
328 if (!h)
329 h = pDev->GetTextHeight();
330 return *h;
332 double nRealOrient = toRadians(nRotate);
333 double nCosAbs = fabs( cos( nRealOrient ) );
334 double nSinAbs = fabs( sin( nRealOrient ) );
335 if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
336 nWidth = static_cast<tools::Long>( TextWidth() * nCosAbs + TextHeight() * nSinAbs );
337 else if ( rOptions.bTotalSize )
339 nWidth = conditionalScaleFunc(rDocument.GetColWidth( nCol,nTab ), nPPT);
340 bAddMargin = false;
341 // only to the right:
342 //TODO: differ on direction up/down (only Text/whole height)
343 if ( pPattern->GetRotateDir( pCondSet ) == ScRotateDir::Right )
344 nWidth += static_cast<tools::Long>( rDocument.GetRowHeight( nRow,nTab ) *
345 (bInPrintTwips ? 1.0 : nPPT) * nCosAbs / nSinAbs );
347 else
348 nWidth = static_cast<tools::Long>( TextHeight() / nSinAbs ); //TODO: limit?
350 if (bWidth)
351 nValue = nWidth;
352 else
354 tools::Long nHeight = static_cast<tools::Long>( TextHeight() * nCosAbs + TextWidth() * nSinAbs );
355 if ( bBreak && !rOptions.bTotalSize )
357 // limit size for line break
358 tools::Long nCmp = pDev->GetFont().GetFontSize().Height() * SC_ROT_BREAK_FACTOR;
359 if ( nHeight > nCmp )
360 nHeight = nCmp;
362 nValue = nHeight;
365 else if (bBreak && !bWidth)
366 nWidth = pDev->GetTextWidth(aValStr);
367 else
368 // in the common case (height), avoid calling the expensive GetTextWidth
369 nValue = bWidth ? pDev->GetTextWidth( aValStr ) : pDev->GetTextHeight();
371 if ( bAddMargin )
373 if (bWidth)
375 nValue += conditionalScaleFunc(pMargin->GetLeftMargin(), nPPT) +
376 conditionalScaleFunc(pMargin->GetRightMargin(), nPPT);
377 if ( nIndent )
378 nValue += conditionalScaleFunc(nIndent, nPPT);
380 else
381 nValue += conditionalScaleFunc(pMargin->GetTopMargin(), nPPT) +
382 conditionalScaleFunc(pMargin->GetBottomMargin(), nPPT);
385 // linebreak done ?
387 if ( bBreak && !bWidth )
389 // test with EditEngine the safety at 90%
390 // (due to rounding errors and because EditEngine formats partially differently)
392 tools::Long nDocSize = conditionalScaleFunc((rDocument.GetColWidth( nCol,nTab ) -
393 pMargin->GetLeftMargin() - pMargin->GetRightMargin() -
394 nIndent), nPPTX);
395 nDocSize = (nDocSize * 9) / 10; // for safety
396 if (nWidth > nDocSize)
397 bEditEngine = true;
402 if (bEditEngine)
404 // the font is not reset each time with !bEditEngine
405 vcl::Font aOldFont = pDev->GetFont();
407 MapMode aHMMMode( MapUnit::Map100thMM, Point(), rZoomX, rZoomY );
409 // save in document ?
410 std::unique_ptr<ScFieldEditEngine> pEngine = rDocument.CreateFieldEditEngine();
412 const bool bPrevUpdateLayout = pEngine->SetUpdateLayout( false );
413 bool bTextWysiwyg = ( pDev->GetOutDevType() == OUTDEV_PRINTER );
414 MapMode aOld = pDev->GetMapMode();
415 pDev->SetMapMode( aHMMMode );
416 pEngine->SetRefDevice( pDev );
417 rDocument.ApplyAsianEditSettings( *pEngine );
418 auto pSet = std::make_unique<SfxItemSet>(pEngine->GetEmptyItemSet());
419 if ( ScStyleSheet* pPreviewStyle = rDocument.GetPreviewCellStyle( nCol, nRow, nTab ) )
421 ScPatternAttr aPreviewPattern( *pPattern );
422 aPreviewPattern.SetStyleSheet(pPreviewStyle);
423 aPreviewPattern.FillEditItemSet(pSet.get(), pCondSet);
425 else
427 SfxItemSet* pFontSet = rDocument.GetPreviewFont( nCol, nRow, nTab );
428 pPattern->FillEditItemSet(pSet.get(), pFontSet ? pFontSet : pCondSet);
430 // no longer needed, are set with the text (is faster)
431 // pEngine->SetDefaults( pSet );
433 if ( pSet->Get(EE_PARA_HYPHENATE).GetValue() ) {
435 css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
436 pEngine->SetHyphenator( xXHyphenator );
439 Size aPaper( 1000000, 1000000 );
440 if ( eOrient==SvxCellOrientation::Stacked && !bAsianVertical )
441 aPaper.setWidth( 1 );
442 else if (bBreak)
444 double fWidthFactor = bInPrintTwips ? 1.0 : nPPTX;
445 if ( bTextWysiwyg )
447 // if text is formatted for printer, don't use PixelToLogic,
448 // to ensure the exact same paper width (and same line breaks) as in
449 // ScEditUtil::GetEditArea, used for output.
451 fWidthFactor = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100);
454 // use original width for hidden columns:
455 tools::Long nDocWidth = static_cast<tools::Long>( rDocument.GetOriginalWidth(nCol,nTab) * fWidthFactor );
456 SCCOL nColMerge = pMerge->GetColMerge();
457 if (nColMerge > 1)
458 for (SCCOL nColAdd=1; nColAdd<nColMerge; nColAdd++)
459 nDocWidth += static_cast<tools::Long>( rDocument.GetColWidth(nCol+nColAdd,nTab) * fWidthFactor );
460 nDocWidth -= static_cast<tools::Long>( pMargin->GetLeftMargin() * fWidthFactor )
461 + static_cast<tools::Long>( pMargin->GetRightMargin() * fWidthFactor )
462 + 1; // output size is width-1 pixel (due to gridline)
463 if ( nIndent )
464 nDocWidth -= static_cast<tools::Long>( nIndent * fWidthFactor );
466 // space for AutoFilter button: 20 * nZoom/100
467 constexpr tools::Long nFilterButtonWidthPix = 20; // Autofilter pixel width at 100% zoom.
468 if ( pFlag->HasAutoFilter() && !bTextWysiwyg )
469 nDocWidth -= bInPrintTwips ? o3tl::convert(nFilterButtonWidthPix, o3tl::Length::px,
470 o3tl::Length::twip)
471 : tools::Long(rZoomX * nFilterButtonWidthPix);
473 aPaper.setWidth( nDocWidth );
475 if ( !bTextWysiwyg )
477 aPaper = bInPrintTwips ?
478 o3tl::convert(aPaper, o3tl::Length::twip, o3tl::Length::mm100) :
479 pDev->PixelToLogic(aPaper, aHMMMode);
482 pEngine->SetPaperSize(aPaper);
484 if (aCell.getType() == CELLTYPE_EDIT)
486 pEngine->SetTextNewDefaults(*aCell.getEditText(), std::move(pSet));
488 else
490 const Color* pColor;
491 OUString aString = ScCellFormat::GetString(
492 aCell, nFormat, &pColor, &rContext, rDocument, true,
493 rOptions.bFormula);
495 if (!aString.isEmpty())
496 pEngine->SetTextNewDefaults(aString, std::move(pSet));
497 else
498 pEngine->SetDefaults(std::move(pSet));
501 bool bEngineVertical = pEngine->IsEffectivelyVertical();
502 pEngine->SetVertical( bAsianVertical );
503 pEngine->SetUpdateLayout( bPrevUpdateLayout );
505 bool bEdWidth = bWidth;
506 if ( eOrient != SvxCellOrientation::Standard && eOrient != SvxCellOrientation::Stacked )
507 bEdWidth = !bEdWidth;
508 if ( nRotate )
510 //TODO: take different X/Y scaling into consideration
512 Size aSize( pEngine->CalcTextWidth(), pEngine->GetTextHeight() );
513 double nRealOrient = toRadians(nRotate);
514 double nCosAbs = fabs( cos( nRealOrient ) );
515 double nSinAbs = fabs( sin( nRealOrient ) );
516 tools::Long nHeight = static_cast<tools::Long>( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs );
517 tools::Long nWidth;
518 if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
519 nWidth = static_cast<tools::Long>( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs );
520 else if ( rOptions.bTotalSize )
522 nWidth = conditionalScaleFunc(rDocument.GetColWidth( nCol,nTab ), nPPT);
523 bAddMargin = false;
524 if ( pPattern->GetRotateDir( pCondSet ) == ScRotateDir::Right )
525 nWidth += static_cast<tools::Long>( rDocument.GetRowHeight( nRow,nTab ) *
526 (bInPrintTwips ? 1.0 : nPPT) * nCosAbs / nSinAbs );
528 else
529 nWidth = static_cast<tools::Long>( aSize.Height() / nSinAbs ); //TODO: limit?
530 aSize = Size( nWidth, nHeight );
532 Size aTextSize = bInPrintTwips ?
533 o3tl::toTwips(aSize, o3tl::Length::mm100) :
534 pDev->LogicToPixel(aSize, aHMMMode);
536 if ( bEdWidth )
537 nValue = aTextSize.Width();
538 else
540 nValue = aTextSize.Height();
542 if ( bBreak && !rOptions.bTotalSize )
544 // limit size for line break
545 tools::Long nCmp = aOldFont.GetFontSize().Height() * SC_ROT_BREAK_FACTOR;
546 if ( nValue > nCmp )
547 nValue = nCmp;
551 else if ( bEdWidth )
553 if (bBreak)
554 nValue = 0;
555 else
557 sal_uInt32 aTextSize(pEngine->CalcTextWidth());
558 nValue = bInPrintTwips ?
559 o3tl::toTwips(aTextSize, o3tl::Length::mm100) :
560 pDev->LogicToPixel(Size(aTextSize, 0), aHMMMode).Width();
563 else // height
565 sal_uInt32 aTextSize(pEngine->GetTextHeight());
566 nValue = bInPrintTwips ?
567 o3tl::toTwips(aTextSize, o3tl::Length::mm100) :
568 pDev->LogicToPixel(Size(0, aTextSize), aHMMMode).Height();
571 if ( nValue && bAddMargin )
573 if (bWidth)
575 nValue += conditionalScaleFunc(pMargin->GetLeftMargin(), nPPT) +
576 conditionalScaleFunc(pMargin->GetRightMargin(), nPPT);
577 if (nIndent)
578 nValue += conditionalScaleFunc(nIndent, nPPT);
580 else
582 nValue += conditionalScaleFunc(pMargin->GetTopMargin(), nPPT) +
583 conditionalScaleFunc(pMargin->GetBottomMargin(), nPPT);
585 if ( bAsianVertical && pDev->GetOutDevType() != OUTDEV_PRINTER )
587 // add 1pt extra (default margin value) for line breaks with SetVertical
588 constexpr tools::Long nDefaultMarginInPoints = 1;
589 nValue += conditionalScaleFunc(
590 o3tl::convert(nDefaultMarginInPoints, o3tl::Length::pt, o3tl::Length::twip),
591 nPPT);
596 // EditEngine is cached and re-used, so the old vertical flag must be restored
597 pEngine->SetVertical( bEngineVertical );
599 rDocument.DisposeFieldEditEngine(pEngine);
601 pDev->SetMapMode( aOld );
602 pDev->SetFont( aOldFont );
605 if (bWidth)
607 // place for Autofilter Button
608 // 20 * nZoom/100
609 // Conditional formatting is not interesting here
610 constexpr tools::Long nFilterButtonWidthPix = 20; // Autofilter pixel width at 100% zoom.
611 ScMF nFlags = pPattern->GetItem(ATTR_MERGE_FLAG).GetValue();
612 if (nFlags & ScMF::Auto)
613 nValue += bInPrintTwips ? o3tl::convert(nFilterButtonWidthPix, o3tl::Length::px,
614 o3tl::Length::twip)
615 : tools::Long(rZoomX * nFilterButtonWidthPix);
618 return nValue;
621 namespace {
623 class MaxStrLenFinder
625 ScDocument& mrDoc;
626 sal_uInt32 mnFormat;
627 OUString maMaxLenStr;
628 sal_Int32 mnMaxLen;
630 // tdf#59820 - search for the longest substring in a multiline string
631 void checkLineBreak(const OUString& aStrVal)
633 sal_Int32 nFromIndex = 0;
634 sal_Int32 nToIndex = aStrVal.indexOf('\n', nFromIndex);
635 // if there is no line break, just take the length of the entire string
636 if (nToIndex == -1)
638 mnMaxLen = aStrVal.getLength();
639 maMaxLenStr = aStrVal;
641 else
643 sal_Int32 nMaxLen = 0;
644 // search for the longest substring in the multiline string
645 while (nToIndex != -1)
647 if (nMaxLen < nToIndex - nFromIndex)
649 nMaxLen = nToIndex - nFromIndex;
651 nFromIndex = nToIndex + 1;
652 nToIndex = aStrVal.indexOf('\n', nFromIndex);
654 // take into consideration the last part of multiline string
655 nToIndex = aStrVal.getLength() - nFromIndex;
656 if (nMaxLen < nToIndex)
658 nMaxLen = nToIndex;
660 // assign new maximum including its substring
661 if (mnMaxLen < nMaxLen)
663 mnMaxLen = nMaxLen;
664 maMaxLenStr = aStrVal.subView(nFromIndex);
669 void checkLength(const ScRefCellValue& rCell)
671 const Color* pColor;
672 OUString aValStr = ScCellFormat::GetString(
673 rCell, mnFormat, &pColor, nullptr, mrDoc);
675 if (aValStr.getLength() <= mnMaxLen)
676 return;
678 switch (rCell.getType())
680 case CELLTYPE_NONE:
681 case CELLTYPE_VALUE:
682 mnMaxLen = aValStr.getLength();
683 maMaxLenStr = aValStr;
684 break;
685 case CELLTYPE_EDIT:
686 case CELLTYPE_STRING:
687 case CELLTYPE_FORMULA:
688 default:
689 checkLineBreak(aValStr);
693 public:
694 MaxStrLenFinder(ScDocument& rDoc, sal_uInt32 nFormat) :
695 mrDoc(rDoc), mnFormat(nFormat), mnMaxLen(0) {}
697 void operator() (size_t /*nRow*/, double f)
699 ScRefCellValue aCell(f);
700 checkLength(aCell);
703 void operator() (size_t /*nRow*/, const svl::SharedString& rSS)
705 if (rSS.getLength() > mnMaxLen)
707 checkLineBreak(rSS.getString());
711 void operator() (size_t /*nRow*/, const EditTextObject* p)
713 ScRefCellValue aCell(p);
714 checkLength(aCell);
717 void operator() (size_t /*nRow*/, const ScFormulaCell* p)
719 ScRefCellValue aCell(const_cast<ScFormulaCell*>(p));
720 checkLength(aCell);
723 const OUString& getMaxLenStr() const { return maMaxLenStr; }
728 sal_uInt16 ScColumn::GetOptimalColWidth(
729 OutputDevice* pDev, double nPPTX, double nPPTY, const Fraction& rZoomX, const Fraction& rZoomY,
730 bool bFormula, sal_uInt16 nOldWidth, const ScMarkData* pMarkData, const ScColWidthParam* pParam) const
732 if (maCells.block_size() == 1 && maCells.begin()->type == sc::element_type_empty)
733 // All cells are empty.
734 return nOldWidth;
736 sc::SingleColumnSpanSet::SpansType aMarkedSpans;
737 if (pMarkData && (pMarkData->IsMarked() || pMarkData->IsMultiMarked()))
739 sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
740 aSpanSet.scan(*pMarkData, nTab, nCol);
741 aSpanSet.getSpans(aMarkedSpans);
743 else
744 // "Select" the entire column if no selection exists.
745 aMarkedSpans.emplace_back(0, GetDoc().MaxRow());
747 sal_uInt16 nWidth = static_cast<sal_uInt16>(nOldWidth*nPPTX);
748 bool bFound = false;
749 ScDocument& rDocument = GetDoc();
751 if ( pParam && pParam->mbSimpleText )
752 { // all the same except for number format
753 SCROW nRow = 0;
754 const ScPatternAttr* pPattern = GetPattern( nRow );
755 vcl::Font aFont;
756 aFont.SetKerning(FontKerning::NONE); // like ScDrawStringsVars::SetPattern
757 // font color doesn't matter here
758 pPattern->fillFontOnly(aFont, pDev, &rZoomX);
759 pDev->SetFont(aFont);
760 const SvxMarginItem* pMargin = &pPattern->GetItem(ATTR_MARGIN);
761 tools::Long nMargin = static_cast<tools::Long>( pMargin->GetLeftMargin() * nPPTX ) +
762 static_cast<tools::Long>( pMargin->GetRightMargin() * nPPTX );
764 // Try to find the row that has the longest string, and measure the width of that string.
765 ScInterpreterContext& rContext = rDocument.GetNonThreadedContext();
766 sal_uInt32 nFormat = pPattern->GetNumberFormat(rContext);
767 while ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && nRow <= 2)
769 // This is often used with CSV import or other data having a header
770 // row; if there is no specific format set try next row for actual
771 // data format.
772 // Or again in case there was a leading sep=";" row or two header
773 // rows..
774 const ScPatternAttr* pNextPattern = GetPattern( ++nRow );
775 if (!ScPatternAttr::areSame(pNextPattern, pPattern))
776 nFormat = pNextPattern->GetNumberFormat(rContext);
778 OUString aLongStr;
779 const Color* pColor;
780 if (pParam->mnMaxTextRow >= 0)
782 ScRefCellValue aCell = GetCellValue(pParam->mnMaxTextRow);
783 aLongStr = ScCellFormat::GetString(
784 aCell, nFormat, &pColor, &rContext, rDocument);
786 else
788 // Go though all non-empty cells within selection.
789 MaxStrLenFinder aFunc(rDocument, nFormat);
790 sc::CellStoreType::const_iterator itPos = maCells.begin();
791 for (const auto& rMarkedSpan : aMarkedSpans)
792 itPos = sc::ParseAllNonEmpty(itPos, maCells, rMarkedSpan.mnRow1, rMarkedSpan.mnRow2, aFunc);
794 aLongStr = aFunc.getMaxLenStr();
797 if (!aLongStr.isEmpty())
799 nWidth = pDev->GetTextWidth(aLongStr) + static_cast<sal_uInt16>(nMargin);
800 bFound = true;
803 else
805 ScNeededSizeOptions aOptions;
806 aOptions.bFormula = bFormula;
807 const ScPatternAttr* pOldPattern = nullptr;
809 // Go though all non-empty cells within selection.
810 sc::CellStoreType::const_iterator itPos = maCells.begin();
811 // coverity[auto_causes_copy] This trivial copy is intentional
812 for (auto [ nRow, nRow2 ] : aMarkedSpans)
814 while (nRow <= nRow2)
816 size_t nOffset;
817 std::tie(itPos, nOffset) = maCells.position(itPos, nRow);
818 if (itPos->type == sc::element_type_empty)
820 // Skip empty cells.
821 nRow += itPos->size - nOffset;
822 continue;
825 for (; nOffset < itPos->size && nRow <= nRow2; ++nOffset, ++nRow)
827 SvtScriptType nScript = rDocument.GetScriptType(nCol, nRow, nTab);
828 if (nScript == SvtScriptType::NONE)
829 nScript = ScGlobal::GetDefaultScriptType();
831 const ScPatternAttr* pPattern = GetPattern(nRow);
832 aOptions.bGetFont = (!ScPatternAttr::areSame(pPattern, pOldPattern) || nScript != SvtScriptType::NONE);
833 aOptions.aPattern.setScPatternAttr(pPattern);
834 pOldPattern = aOptions.aPattern.getScPatternAttr();
835 sal_uInt16 nThis = static_cast<sal_uInt16>(GetNeededSize(
836 nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, true, aOptions, &pOldPattern));
837 if (nThis && (nThis > nWidth || !bFound))
839 nWidth = nThis;
840 bFound = true;
847 if (bFound)
849 nWidth += 2;
850 sal_uInt16 nTwips = static_cast<sal_uInt16>(
851 std::min(nWidth / nPPTX, std::numeric_limits<sal_uInt16>::max() / 2.0));
852 return nTwips;
854 else
855 return nOldWidth;
858 static sal_uInt16 lcl_GetAttribHeight(const ScPatternAttr& rPattern, sal_uInt16 nFontHeightId,
859 sal_uInt16 nMinHeight)
861 const SvxFontHeightItem& rFontHeight =
862 static_cast<const SvxFontHeightItem&>(rPattern.GetItem(nFontHeightId));
864 sal_uInt16 nHeight = rFontHeight.GetHeight();
865 nHeight *= 1.18;
867 if ( rPattern.GetItem(ATTR_FONT_EMPHASISMARK).GetEmphasisMark() != FontEmphasisMark::NONE )
869 // add height for emphasis marks
870 //TODO: font metrics should be used instead
871 nHeight += nHeight / 4;
874 const SvxMarginItem& rMargin = rPattern.GetItem(ATTR_MARGIN);
876 nHeight += rMargin.GetTopMargin() + rMargin.GetBottomMargin();
878 if (nHeight > STD_ROWHEIGHT_DIFF)
879 nHeight -= STD_ROWHEIGHT_DIFF;
881 if (nHeight < nMinHeight)
882 nHeight = nMinHeight;
884 return nHeight;
887 // pHeight in Twips
888 // optimize nMinHeight, nMinStart : with nRow >= nMinStart is at least nMinHeight
889 // (is only evaluated with bStdAllowed)
891 void ScColumn::GetOptimalHeight(
892 sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow, sal_uInt16 nMinHeight, SCROW nMinStart )
894 ScDocument& rDocument = GetDoc();
895 RowHeightsArray& rHeights = rCxt.getHeightArray();
896 ScAttrIterator aIter( pAttrArray.get(), nStartRow, nEndRow, &rDocument.getCellAttributeHelper().getDefaultCellAttribute() );
898 SCROW nStart = -1;
899 SCROW nEnd = -1;
900 SCROW nEditPos = 0;
901 SCROW nNextEnd = 0;
903 // with conditional formatting, always consider the individual cells
905 const ScPatternAttr* pPattern = aIter.Next(nStart,nEnd);
906 const sal_uInt16 nOptimalMinRowHeight = GetDoc().GetSheetOptimalMinRowHeight(nTab);
907 while ( pPattern )
909 const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
910 const ScMergeFlagAttr* pFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
911 if ( pMerge->GetRowMerge() > 1 || pFlag->IsOverlapped() )
913 // do nothing - vertically with merged and overlapping,
914 // horizontally only with overlapped (invisible) -
915 // only one horizontal merged is always considered
917 else
919 bool bStdAllowed = (pPattern->GetCellOrientation() == SvxCellOrientation::Standard);
920 bool bStdOnly = false;
921 if (bStdAllowed)
923 bool bBreak = pPattern->GetItem(ATTR_LINEBREAK).GetValue() ||
924 (pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() ==
925 SvxCellHorJustify::Block);
926 bStdOnly = !bBreak;
928 // conditional formatting: loop all cells
929 if (bStdOnly &&
930 !pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
932 bStdOnly = false;
935 // rotated text: loop all cells
936 if ( bStdOnly && pPattern->GetItem(ATTR_ROTATE_VALUE).GetValue() )
937 bStdOnly = false;
940 if (bStdOnly)
942 bool bHasEditCells = HasEditCells(nStart,nEnd,nEditPos);
943 // Call to HasEditCells() may change pattern due to
944 // calculation, => sync always.
945 // We don't know which row changed first, but as pPattern
946 // covered nStart to nEnd we can pick nStart. Worst case we
947 // have to repeat that for every row in range if every row
948 // changed.
949 pPattern = aIter.Resync( nStart, nStart, nEnd);
950 if (bHasEditCells && nEnd < nEditPos)
951 bHasEditCells = false; // run into that again
952 if (bHasEditCells) // includes mixed script types
954 if (nEditPos == nStart)
956 bStdOnly = false;
957 if (nEnd > nEditPos)
958 nNextEnd = nEnd;
959 nEnd = nEditPos; // calculate single
960 bStdAllowed = false; // will be computed in any case per cell
962 else
964 nNextEnd = nEnd;
965 nEnd = nEditPos - 1; // standard - part
970 sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
971 aSpanSet.scan(*this, nStart, nEnd);
972 sc::SingleColumnSpanSet::SpansType aSpans;
973 aSpanSet.getSpans(aSpans);
975 if (bStdAllowed)
977 sal_uInt16 nLatHeight = 0;
978 sal_uInt16 nCjkHeight = 0;
979 sal_uInt16 nCtlHeight = 0;
980 sal_uInt16 nDefHeight;
981 SvtScriptType nDefScript = ScGlobal::GetDefaultScriptType();
982 if ( nDefScript == SvtScriptType::ASIAN )
983 nDefHeight = nCjkHeight = lcl_GetAttribHeight(*pPattern, ATTR_CJK_FONT_HEIGHT,
984 nOptimalMinRowHeight);
985 else if ( nDefScript == SvtScriptType::COMPLEX )
986 nDefHeight = nCtlHeight = lcl_GetAttribHeight(*pPattern, ATTR_CTL_FONT_HEIGHT,
987 nOptimalMinRowHeight);
988 else
989 nDefHeight = nLatHeight = lcl_GetAttribHeight(*pPattern, ATTR_FONT_HEIGHT,
990 nOptimalMinRowHeight);
992 // if everything below is already larger, the loop doesn't have to
993 // be run again
994 SCROW nStdEnd = nEnd;
995 if ( nDefHeight <= nMinHeight && nStdEnd >= nMinStart )
996 nStdEnd = (nMinStart>0) ? nMinStart-1 : 0;
998 if (nStart <= nStdEnd)
1000 SCROW nRow = nStart;
1001 for (;;)
1003 size_t nIndex;
1004 SCROW nRangeEnd;
1005 sal_uInt16 nRangeHeight = rHeights.GetValue(nRow, nIndex, nRangeEnd);
1006 if (nRangeHeight < nDefHeight)
1007 rHeights.SetValue(nRow, std::min(nRangeEnd, nStdEnd), nDefHeight);
1008 nRow = nRangeEnd + 1;
1009 if (nRow > nStdEnd)
1010 break;
1014 if ( bStdOnly )
1016 // if cells are not handled individually below,
1017 // check for cells with different script type
1018 sc::CellTextAttrStoreType::iterator itAttr = maCellTextAttrs.begin();
1019 sc::CellStoreType::iterator itCells = maCells.begin();
1020 for (const auto& rSpan : aSpans)
1022 for (SCROW nRow = rSpan.mnRow1; nRow <= rSpan.mnRow2; ++nRow)
1024 SvtScriptType nScript = GetRangeScriptType(itAttr, nRow, nRow, itCells);
1025 if (nScript == nDefScript)
1026 continue;
1028 if ( nScript == SvtScriptType::ASIAN )
1030 if ( nCjkHeight == 0 )
1031 nCjkHeight = lcl_GetAttribHeight(*pPattern,
1032 ATTR_CJK_FONT_HEIGHT,
1033 nOptimalMinRowHeight);
1034 if (nCjkHeight > rHeights.GetValue(nRow))
1035 rHeights.SetValue(nRow, nRow, nCjkHeight);
1037 else if ( nScript == SvtScriptType::COMPLEX )
1039 if ( nCtlHeight == 0 )
1040 nCtlHeight = lcl_GetAttribHeight(*pPattern,
1041 ATTR_CTL_FONT_HEIGHT,
1042 nOptimalMinRowHeight);
1043 if (nCtlHeight > rHeights.GetValue(nRow))
1044 rHeights.SetValue(nRow, nRow, nCtlHeight);
1046 else
1048 if ( nLatHeight == 0 )
1049 nLatHeight = lcl_GetAttribHeight(*pPattern, ATTR_FONT_HEIGHT,
1050 nOptimalMinRowHeight);
1051 if (nLatHeight > rHeights.GetValue(nRow))
1052 rHeights.SetValue(nRow, nRow, nLatHeight);
1059 if (!bStdOnly) // search covered cells
1061 ScNeededSizeOptions aOptions;
1062 CellAttributeHolder aOldPattern;
1064 for (const auto& rSpan : aSpans)
1066 for (SCROW nRow = rSpan.mnRow1; nRow <= rSpan.mnRow2; ++nRow)
1068 // only calculate the cell height when it's used later (#37928#)
1070 if (rCxt.isForceAutoSize() || !(rDocument.GetRowFlags(nRow, nTab) & CRFlags::ManualSize) )
1072 aOptions.aPattern.setScPatternAttr(pPattern);
1073 aOldPattern.setScPatternAttr(aOptions.aPattern.getScPatternAttr());
1074 sal_uInt16 nHeight = static_cast<sal_uInt16>(
1075 std::min(
1076 GetNeededSize( nRow, rCxt.getOutputDevice(), rCxt.getPPTX(), rCxt.getPPTY(),
1077 rCxt.getZoomX(), rCxt.getZoomY(), false, aOptions,
1078 &pPattern) / rCxt.getPPTY(),
1079 double(std::numeric_limits<sal_uInt16>::max())));
1080 if (nHeight > rHeights.GetValue(nRow))
1081 rHeights.SetValue(nRow, nRow, nHeight);
1083 // Pattern changed due to calculation? => sync.
1084 if (!ScPatternAttr::areSame(pPattern, aOldPattern.getScPatternAttr()))
1086 pPattern = aIter.Resync( nRow, nStart, nEnd);
1087 nNextEnd = 0;
1095 if (nNextEnd > 0)
1097 nStart = nEnd + 1;
1098 nEnd = nNextEnd;
1099 nNextEnd = 0;
1101 else
1102 pPattern = aIter.Next(nStart,nEnd);
1106 bool ScColumn::GetNextSpellingCell(SCROW& nRow, bool bInSel, const ScMarkData& rData) const
1108 ScDocument& rDocument = GetDoc();
1109 sc::CellStoreType::const_iterator it = maCells.position(nRow).first;
1110 mdds::mtv::element_t eType = it->type;
1111 if (!bInSel && it != maCells.end() && eType != sc::element_type_empty)
1113 if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
1114 !(HasAttrib( nRow, nRow, HasAttrFlags::Protected) &&
1115 rDocument.IsTabProtected(nTab)) )
1116 return true;
1118 if (bInSel)
1120 SCROW lastDataPos = GetLastDataPos();
1121 for (;;)
1123 nRow = rData.GetNextMarked(nCol, nRow, false);
1124 if (!rDocument.ValidRow(nRow) || nRow > lastDataPos )
1126 nRow = GetDoc().MaxRow()+1;
1127 return false;
1129 else
1131 it = maCells.position(it, nRow).first;
1132 eType = it->type;
1133 if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
1134 !(HasAttrib( nRow, nRow, HasAttrFlags::Protected) &&
1135 rDocument.IsTabProtected(nTab)) )
1136 return true;
1137 else
1138 nRow++;
1142 else
1144 while (GetNextDataPos(nRow))
1146 it = maCells.position(it, nRow).first;
1147 eType = it->type;
1148 if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
1149 !(HasAttrib( nRow, nRow, HasAttrFlags::Protected) &&
1150 rDocument.IsTabProtected(nTab)) )
1151 return true;
1152 else
1153 nRow++;
1155 nRow = GetDoc().MaxRow()+1;
1156 return false;
1160 namespace {
1162 class StrEntries
1164 sc::CellStoreType& mrCells;
1166 protected:
1167 struct StrEntry
1169 SCROW mnRow;
1170 OUString maStr;
1172 StrEntry(SCROW nRow, OUString aStr) : mnRow(nRow), maStr(std::move(aStr)) {}
1175 std::vector<StrEntry> maStrEntries;
1176 ScDocument* mpDoc;
1178 StrEntries(sc::CellStoreType& rCells, ScDocument* pDoc) : mrCells(rCells), mpDoc(pDoc) {}
1180 public:
1181 void commitStrings()
1183 svl::SharedStringPool& rPool = mpDoc->GetSharedStringPool();
1184 sc::CellStoreType::iterator it = mrCells.begin();
1185 for (const auto& rStrEntry : maStrEntries)
1186 it = mrCells.set(it, rStrEntry.mnRow, rPool.intern(rStrEntry.maStr));
1190 class RemoveEditAttribsHandler : public StrEntries
1192 std::unique_ptr<ScFieldEditEngine> mpEngine;
1194 public:
1195 RemoveEditAttribsHandler(sc::CellStoreType& rCells, ScDocument* pDoc) : StrEntries(rCells, pDoc) {}
1197 void operator() (size_t nRow, EditTextObject*& pObj)
1199 // For the test on hard formatting (ScEditAttrTester), are the defaults in the
1200 // EditEngine of no importance. When the tester would later recognise the same
1201 // attributes in default and hard formatting and has to remove them, the correct
1202 // defaults must be set in the EditEngine for each cell.
1204 // test for attributes
1205 if (!mpEngine)
1207 mpEngine.reset(new ScFieldEditEngine(mpDoc, mpDoc->GetEditPool()));
1208 // EEControlBits::ONLINESPELLING if there are errors already
1209 mpEngine->SetControlWord(mpEngine->GetControlWord() | EEControlBits::ONLINESPELLING);
1210 mpDoc->ApplyAsianEditSettings(*mpEngine);
1212 mpEngine->SetTextCurrentDefaults(*pObj);
1213 sal_Int32 nParCount = mpEngine->GetParagraphCount();
1214 for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
1216 mpEngine->RemoveCharAttribs(nPar);
1217 const SfxItemSet& rOld = mpEngine->GetParaAttribs(nPar);
1218 if ( rOld.Count() )
1220 SfxItemSet aNew( *rOld.GetPool(), rOld.GetRanges() ); // empty
1221 mpEngine->SetParaAttribs( nPar, aNew );
1224 // change URL field to text (not possible otherwise, thus pType=0)
1225 mpEngine->RemoveFields();
1227 bool bSpellErrors = mpEngine->HasOnlineSpellErrors();
1228 bool bNeedObject = bSpellErrors || nParCount>1; // keep errors/paragraphs
1229 // ScEditAttrTester is not needed anymore, arrays are gone
1231 if (bNeedObject) // remains edit cell
1233 EEControlBits nCtrl = mpEngine->GetControlWord();
1234 EEControlBits nWantBig = bSpellErrors ? EEControlBits::ALLOWBIGOBJS : EEControlBits::NONE;
1235 if ( ( nCtrl & EEControlBits::ALLOWBIGOBJS ) != nWantBig )
1236 mpEngine->SetControlWord( (nCtrl & ~EEControlBits::ALLOWBIGOBJS) | nWantBig );
1238 // Overwrite the existing object.
1239 delete pObj;
1240 pObj = mpEngine->CreateTextObject().release();
1242 else // create String
1244 // Store the string replacement for later commits.
1245 OUString aText = ScEditUtil::GetSpaceDelimitedString(*mpEngine);
1246 maStrEntries.emplace_back(nRow, aText);
1251 class TestTabRefAbsHandler
1253 SCTAB mnTab;
1254 bool mbTestResult;
1255 public:
1256 explicit TestTabRefAbsHandler(SCTAB nTab) : mnTab(nTab), mbTestResult(false) {}
1258 void operator() (size_t /*nRow*/, const ScFormulaCell* pCell)
1260 if (const_cast<ScFormulaCell*>(pCell)->TestTabRefAbs(mnTab))
1261 mbTestResult = true;
1264 bool getTestResult() const { return mbTestResult; }
1269 void ScColumn::RemoveEditAttribs( sc::ColumnBlockPosition& rBlockPos, SCROW nStartRow, SCROW nEndRow )
1271 RemoveEditAttribsHandler aFunc(maCells, &GetDoc());
1273 rBlockPos.miCellPos = sc::ProcessEditText(
1274 rBlockPos.miCellPos, maCells, nStartRow, nEndRow, aFunc);
1276 aFunc.commitStrings();
1279 bool ScColumn::TestTabRefAbs(SCTAB nTable) const
1281 TestTabRefAbsHandler aFunc(nTable);
1282 sc::ParseFormula(maCells, aFunc);
1283 return aFunc.getTestResult();
1286 bool ScColumn::IsEmptyData() const
1288 return maCells.block_size() == 1 && maCells.begin()->type == sc::element_type_empty;
1291 namespace {
1293 class CellCounter
1295 size_t mnCount;
1296 public:
1297 CellCounter() : mnCount(0) {}
1299 void operator() (
1300 const sc::CellStoreType::value_type& node, size_t /*nOffset*/, size_t nDataSize)
1302 if (node.type == sc::element_type_empty)
1303 return;
1305 mnCount += nDataSize;
1308 size_t getCount() const { return mnCount; }
1313 SCSIZE ScColumn::VisibleCount( SCROW nStartRow, SCROW nEndRow ) const
1315 CellCounter aFunc;
1316 sc::ParseBlock(maCells.begin(), maCells, aFunc, nStartRow, nEndRow);
1317 return aFunc.getCount();
1320 bool ScColumn::HasVisibleDataAt(SCROW nRow) const
1322 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
1323 sc::CellStoreType::const_iterator it = aPos.first;
1324 if (it == maCells.end())
1325 // Likely invalid row number.
1326 return false;
1328 return it->type != sc::element_type_empty;
1331 bool ScColumn::IsEmptyData(SCROW nStartRow, SCROW nEndRow) const
1333 // simple case
1334 if (maCells.block_size() == 1 && maCells.begin()->type == sc::element_type_empty)
1335 return true;
1337 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
1338 sc::CellStoreType::const_iterator it = aPos.first;
1339 if (it == maCells.end())
1340 // Invalid row number.
1341 return false;
1343 if (it->type != sc::element_type_empty)
1344 // Non-empty cell at the start position.
1345 return false;
1347 // start position of next block which is not empty.
1348 SCROW nNextRow = nStartRow + it->size - aPos.second;
1349 return nEndRow < nNextRow;
1352 bool ScColumn::IsNotesEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
1354 std::pair<sc::CellNoteStoreType::const_iterator,size_t> aPos = maCellNotes.position(nStartRow);
1355 sc::CellNoteStoreType::const_iterator it = aPos.first;
1356 if (it == maCellNotes.end())
1357 // Invalid row number.
1358 return false;
1360 if (it->type != sc::element_type_empty)
1361 // Non-empty cell at the start position.
1362 return false;
1364 // start position of next block which is not empty.
1365 SCROW nNextRow = nStartRow + it->size - aPos.second;
1366 return nEndRow < nNextRow;
1369 SCSIZE ScColumn::GetEmptyLinesInBlock( SCROW nStartRow, SCROW nEndRow, ScDirection eDir ) const
1371 // Given a range of rows, find a top or bottom empty segment.
1372 switch (eDir)
1374 case DIR_TOP:
1376 // Determine the length of empty head segment.
1377 size_t nLength = nEndRow - nStartRow + 1;
1378 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
1379 sc::CellStoreType::const_iterator it = aPos.first;
1380 if (it->type != sc::element_type_empty)
1381 // First row is already not empty.
1382 return 0;
1384 // length of this empty block minus the offset.
1385 size_t nThisLen = it->size - aPos.second;
1386 return std::min(nThisLen, nLength);
1388 break;
1389 case DIR_BOTTOM:
1391 // Determine the length of empty tail segment.
1392 size_t nLength = nEndRow - nStartRow + 1;
1393 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nEndRow);
1394 sc::CellStoreType::const_iterator it = aPos.first;
1395 if (it->type != sc::element_type_empty)
1396 // end row is already not empty.
1397 return 0;
1399 // length of this empty block from the tip to the end row position.
1400 size_t nThisLen = aPos.second + 1;
1401 return std::min(nThisLen, nLength);
1403 break;
1404 default:
1408 return 0;
1411 SCROW ScColumn::GetFirstDataPos() const
1413 if (IsEmptyData())
1414 return 0;
1416 sc::CellStoreType::const_iterator it = maCells.begin();
1417 if (it->type != sc::element_type_empty)
1418 return 0;
1420 return it->size;
1423 SCROW ScColumn::GetLastDataPos() const
1425 if (IsEmptyData())
1426 return 0;
1428 sc::CellStoreType::const_reverse_iterator it = maCells.rbegin();
1429 if (it->type != sc::element_type_empty)
1430 return GetDoc().MaxRow();
1432 return GetDoc().MaxRow() - static_cast<SCROW>(it->size);
1435 SCROW ScColumn::GetLastDataPos( SCROW nLastRow, ScDataAreaExtras* pDataAreaExtras ) const
1437 nLastRow = std::min( nLastRow, GetDoc().MaxRow());
1439 if (pDataAreaExtras && pDataAreaExtras->mnEndRow < nLastRow)
1441 // Check in order of likeliness.
1442 if ( (pDataAreaExtras->mbCellFormats && HasVisibleAttrIn(nLastRow, nLastRow)) ||
1443 (pDataAreaExtras->mbCellNotes && !IsNotesEmptyBlock(nLastRow, nLastRow)) ||
1444 (pDataAreaExtras->mbCellDrawObjects && !IsDrawObjectsEmptyBlock(nLastRow, nLastRow)))
1445 pDataAreaExtras->mnEndRow = nLastRow;
1448 sc::CellStoreType::const_position_type aPos = maCells.position(nLastRow);
1450 if (aPos.first->type != sc::element_type_empty)
1451 return nLastRow;
1453 if (aPos.first == maCells.begin())
1454 // This is the first block, and is empty.
1455 return 0;
1457 return static_cast<SCROW>(aPos.first->position - 1);
1460 bool ScColumn::GetPrevDataPos(SCROW& rRow) const
1462 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
1463 sc::CellStoreType::const_iterator it = aPos.first;
1464 if (it == maCells.end())
1465 return false;
1467 if (it->type == sc::element_type_empty)
1469 if (it == maCells.begin())
1470 // No more previous non-empty cell.
1471 return false;
1473 rRow -= aPos.second + 1; // Last row position of the previous block.
1474 return true;
1477 // This block is not empty.
1478 if (aPos.second)
1480 // There are preceding cells in this block. Simply move back one cell.
1481 --rRow;
1482 return true;
1485 // This is the first cell in a non-empty block. Move back to the previous block.
1486 if (it == maCells.begin())
1487 // No more preceding block.
1488 return false;
1490 --rRow; // Move to the last cell of the previous block.
1491 --it;
1492 if (it->type == sc::element_type_empty)
1494 // This block is empty.
1495 if (it == maCells.begin())
1496 // No more preceding blocks.
1497 return false;
1499 // Skip the whole empty block segment.
1500 rRow -= it->size;
1503 return true;
1506 bool ScColumn::GetNextDataPos(SCROW& rRow) const // greater than rRow
1508 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
1509 sc::CellStoreType::const_iterator it = aPos.first;
1510 if (it == maCells.end())
1511 return false;
1513 if (it->type == sc::element_type_empty)
1515 // This block is empty. Skip ahead to the next block (if exists).
1516 rRow += it->size - aPos.second;
1517 ++it;
1518 if (it == maCells.end())
1519 // No more next block.
1520 return false;
1522 // Next block exists, and is non-empty.
1523 return true;
1526 if (aPos.second < it->size - 1)
1528 // There are still cells following the current position.
1529 ++rRow;
1530 return true;
1533 // This is the last cell in the block. Move ahead to the next block.
1534 rRow += it->size - aPos.second; // First cell in the next block.
1535 ++it;
1536 if (it == maCells.end())
1537 // No more next block.
1538 return false;
1540 if (it->type == sc::element_type_empty)
1542 // Next block is empty. Move to the next block.
1543 rRow += it->size;
1544 ++it;
1545 if (it == maCells.end())
1546 return false;
1549 return true;
1552 bool ScColumn::TrimEmptyBlocks(SCROW& rRowStart, SCROW& rRowEnd) const
1554 assert(rRowStart <= rRowEnd);
1555 SCROW nRowStartNew = rRowStart, nRowEndNew = rRowEnd;
1557 // Trim down rRowStart first
1558 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRowStart);
1559 sc::CellStoreType::const_iterator it = aPos.first;
1560 if (it == maCells.end())
1561 return false;
1563 if (it->type == sc::element_type_empty)
1565 // This block is empty. Skip ahead to the next block (if exists).
1566 nRowStartNew += it->size - aPos.second;
1567 if (nRowStartNew > rRowEnd)
1568 return false;
1569 ++it;
1570 if (it == maCells.end())
1571 // No more next block.
1572 return false;
1575 // Trim up rRowEnd next
1576 aPos = maCells.position(rRowEnd);
1577 it = aPos.first;
1578 if (it == maCells.end())
1580 rRowStart = nRowStartNew;
1581 return true; // Because trimming of rRowStart is ok
1584 if (it->type == sc::element_type_empty)
1586 // rRowEnd cannot be in the first block which is empty !
1587 assert(it != maCells.begin());
1588 // This block is empty. Skip to the previous block (it exists).
1589 nRowEndNew -= aPos.second + 1; // Last row position of the previous block.
1590 assert(nRowStartNew <= nRowEndNew);
1593 rRowStart = nRowStartNew;
1594 rRowEnd = nRowEndNew;
1595 return true;
1598 SCROW ScColumn::FindNextVisibleRow(SCROW nRow, bool bForward) const
1600 if(bForward)
1602 nRow++;
1603 SCROW nEndRow = 0;
1604 bool bHidden = GetDoc().RowHidden(nRow, nTab, nullptr, &nEndRow);
1605 if(bHidden)
1606 return std::min<SCROW>(GetDoc().MaxRow(), nEndRow + 1);
1607 else
1608 return nRow;
1610 else
1612 nRow--;
1613 SCROW nStartRow = GetDoc().MaxRow();
1614 bool bHidden = GetDoc().RowHidden(nRow, nTab, &nStartRow);
1615 if(bHidden)
1616 return std::max<SCROW>(0, nStartRow - 1);
1617 else
1618 return nRow;
1622 SCROW ScColumn::FindNextVisibleRowWithContent(
1623 sc::CellStoreType::const_iterator& itPos, SCROW nRow, bool bForward) const
1625 ScDocument& rDocument = GetDoc();
1626 if (bForward)
1630 nRow++;
1631 SCROW nEndRow = 0;
1632 bool bHidden = rDocument.RowHidden(nRow, nTab, nullptr, &nEndRow);
1633 if (bHidden)
1635 nRow = nEndRow + 1;
1636 if(nRow >= GetDoc().MaxRow())
1637 return GetDoc().MaxRow();
1640 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
1641 itPos = aPos.first;
1642 if (itPos == maCells.end())
1643 // Invalid row.
1644 return GetDoc().MaxRow();
1646 if (itPos->type != sc::element_type_empty)
1647 return nRow;
1649 // Move to the last cell of the current empty block.
1650 nRow += itPos->size - aPos.second - 1;
1652 while (nRow < GetDoc().MaxRow());
1654 return GetDoc().MaxRow();
1659 nRow--;
1660 SCROW nStartRow = GetDoc().MaxRow();
1661 bool bHidden = rDocument.RowHidden(nRow, nTab, &nStartRow);
1662 if (bHidden)
1664 nRow = nStartRow - 1;
1665 if(nRow <= 0)
1666 return 0;
1669 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
1670 itPos = aPos.first;
1671 if (itPos == maCells.end())
1672 // Invalid row.
1673 return 0;
1675 if (itPos->type != sc::element_type_empty)
1676 return nRow;
1678 // Move to the first cell of the current empty block.
1679 nRow -= aPos.second;
1681 while (nRow > 0);
1683 return 0;
1686 void ScColumn::CellStorageModified()
1688 // Remove cached values. Given how often this function is called and how (not that) often
1689 // the cached values are used, it should be more efficient to just discard everything
1690 // instead of trying to figure out each time exactly what to discard.
1691 GetDoc().DiscardFormulaGroupContext();
1693 // TODO: Update column's "last updated" timestamp here.
1695 assert(sal::static_int_cast<SCROW>(maCells.size()) == GetDoc().GetMaxRowCount()
1696 && "Size of the cell array is incorrect." );
1698 assert(sal::static_int_cast<SCROW>(maCellTextAttrs.size()) == GetDoc().GetMaxRowCount()
1699 && "Size of the cell text attribute array is incorrect.");
1701 assert(sal::static_int_cast<SCROW>(maBroadcasters.size()) == GetDoc().GetMaxRowCount()
1702 && "Size of the broadcaster array is incorrect.");
1704 #if DEBUG_COLUMN_STORAGE
1705 // Make sure that these two containers are synchronized wrt empty segments.
1706 auto lIsEmptyType = [](const auto& rElement) { return rElement.type == sc::element_type_empty; };
1707 // Move to the first empty blocks.
1708 auto itCell = std::find_if(maCells.begin(), maCells.end(), lIsEmptyType);
1709 auto itAttr = std::find_if(maCellTextAttrs.begin(), maCellTextAttrs.end(), lIsEmptyType);
1711 while (itCell != maCells.end())
1713 if (itCell->position != itAttr->position || itCell->size != itAttr->size)
1715 cout << "ScColumn::CellStorageModified: Cell array and cell text attribute array are out of sync." << endl;
1716 cout << "-- cell array" << endl;
1717 maCells.dump_blocks(cout);
1718 cout << "-- attribute array" << endl;
1719 maCellTextAttrs.dump_blocks(cout);
1720 cout.flush();
1721 abort();
1724 // Move to the next empty blocks.
1725 ++itCell;
1726 itCell = std::find_if(itCell, maCells.end(), lIsEmptyType);
1728 ++itAttr;
1729 itAttr = std::find_if(itAttr, maCellTextAttrs.end(), lIsEmptyType);
1731 #endif
1734 #if DUMP_COLUMN_STORAGE
1736 namespace {
1738 #define DUMP_FORMULA_RESULTS 0
1740 struct ColumnStorageDumper
1742 const ScDocument& mrDoc;
1744 ColumnStorageDumper( const ScDocument& rDoc ) : mrDoc(rDoc) {}
1746 void operator() (const sc::CellStoreType::value_type& rNode) const
1748 switch (rNode.type)
1750 case sc::element_type_numeric:
1751 cout << " * numeric block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1752 break;
1753 case sc::element_type_string:
1754 cout << " * string block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1755 break;
1756 case sc::element_type_edittext:
1757 cout << " * edit-text block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1758 break;
1759 case sc::element_type_formula:
1760 dumpFormulaBlock(rNode);
1761 break;
1762 case sc::element_type_empty:
1763 cout << " * empty block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1764 break;
1765 default:
1766 cout << " * unknown block" << endl;
1770 void dumpFormulaBlock(const sc::CellStoreType::value_type& rNode) const
1772 cout << " * formula block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1773 sc::formula_block::const_iterator it = sc::formula_block::begin(*rNode.data);
1774 sc::formula_block::const_iterator itEnd = sc::formula_block::end(*rNode.data);
1776 for (; it != itEnd; ++it)
1778 const ScFormulaCell* pCell = *it;
1779 if (!pCell->IsShared())
1781 cout << " * row " << pCell->aPos.Row() << " not shared" << endl;
1782 printFormula(pCell);
1783 printResult(pCell);
1784 continue;
1787 if (pCell->GetSharedTopRow() != pCell->aPos.Row())
1789 cout << " * row " << pCell->aPos.Row() << " shared with top row "
1790 << pCell->GetSharedTopRow() << " with length " << pCell->GetSharedLength()
1791 << endl;
1792 continue;
1795 SCROW nLen = pCell->GetSharedLength();
1796 cout << " * group: start=" << pCell->aPos.Row() << ", length=" << nLen << endl;
1797 printFormula(pCell);
1798 printResult(pCell);
1800 if (nLen > 1)
1802 for (SCROW i = 0; i < nLen-1; ++i, ++it)
1804 pCell = *it;
1805 printResult(pCell);
1811 void printFormula(const ScFormulaCell* pCell) const
1813 sc::TokenStringContext aCxt(mrDoc, mrDoc.GetGrammar());
1814 OUString aFormula = pCell->GetCode()->CreateString(aCxt, pCell->aPos);
1815 cout << " * formula: " << aFormula << endl;
1818 #if DUMP_FORMULA_RESULTS
1819 void printResult(const ScFormulaCell* pCell) const
1821 sc::FormulaResultValue aRes = pCell->GetResult();
1822 cout << " * result: ";
1823 switch (aRes.meType)
1825 case sc::FormulaResultValue::Value:
1826 cout << aRes.mfValue << " (type: value)";
1827 break;
1828 case sc::FormulaResultValue::String:
1829 cout << "'" << aRes.maString.getString() << "' (type: string)";
1830 break;
1831 case sc::FormulaResultValue::Error:
1832 cout << "error (" << static_cast<int>(aRes.mnError) << ")";
1833 break;
1834 case sc::FormulaResultValue::Invalid:
1835 cout << "invalid";
1836 break;
1839 cout << endl;
1841 #else
1842 void printResult(const ScFormulaCell*) const
1844 (void) this; /* loplugin:staticmethods */
1846 #endif
1851 void ScColumn::DumpColumnStorage() const
1853 cout << "-- table: " << nTab << "; column: " << nCol << endl;
1854 std::for_each(maCells.begin(), maCells.end(), ColumnStorageDumper(GetDoc()));
1855 cout << "--" << endl;
1857 #endif
1859 void ScColumn::CopyCellTextAttrsToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol) const
1861 rDestCol.maCellTextAttrs.set_empty(nRow1, nRow2); // Empty the destination range first.
1863 sc::CellTextAttrStoreType::const_iterator itBlk = maCellTextAttrs.begin(), itBlkEnd = maCellTextAttrs.end();
1865 // Locate the top row position.
1866 size_t nBlockStart = 0, nRowPos = static_cast<size_t>(nRow1);
1867 itBlk = std::find_if(itBlk, itBlkEnd, [&nRowPos, &nBlockStart](const auto& rAttr) {
1868 return nBlockStart <= nRowPos && nRowPos < nBlockStart + rAttr.size; });
1870 if (itBlk == itBlkEnd)
1871 // Specified range not found. Bail out.
1872 return;
1874 size_t nBlockEnd;
1875 size_t nOffsetInBlock = nRowPos - nBlockStart;
1877 nRowPos = static_cast<size_t>(nRow2); // End row position.
1879 // Keep copying until we hit the end row position.
1880 sc::celltextattr_block::const_iterator itData, itDataEnd;
1881 for (; itBlk != itBlkEnd; ++itBlk, nBlockStart = nBlockEnd, nOffsetInBlock = 0)
1883 nBlockEnd = nBlockStart + itBlk->size;
1884 if (!itBlk->data)
1886 // Empty block.
1887 if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
1888 // This block contains the end row.
1889 rDestCol.maCellTextAttrs.set_empty(nBlockStart + nOffsetInBlock, nRowPos);
1890 else
1891 rDestCol.maCellTextAttrs.set_empty(nBlockStart + nOffsetInBlock, nBlockEnd-1);
1893 continue;
1896 // Non-empty block.
1897 itData = sc::celltextattr_block::begin(*itBlk->data);
1898 itDataEnd = sc::celltextattr_block::end(*itBlk->data);
1899 std::advance(itData, nOffsetInBlock);
1901 if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
1903 // This block contains the end row. Only copy partially.
1904 size_t nOffset = nRowPos - nBlockStart + 1;
1905 itDataEnd = sc::celltextattr_block::begin(*itBlk->data);
1906 std::advance(itDataEnd, nOffset);
1908 rDestCol.maCellTextAttrs.set(nBlockStart + nOffsetInBlock, itData, itDataEnd);
1909 break;
1912 rDestCol.maCellTextAttrs.set(nBlockStart + nOffsetInBlock, itData, itDataEnd);
1916 namespace {
1918 class CopyCellNotesHandler
1920 ScColumn& mrDestCol;
1921 sc::CellNoteStoreType& mrDestNotes;
1922 sc::CellNoteStoreType::iterator miPos;
1923 SCTAB mnSrcTab;
1924 SCCOL mnSrcCol;
1925 SCTAB mnDestTab;
1926 SCCOL mnDestCol;
1927 SCROW mnDestOffset; /// Add this to the source row position to get the destination row.
1928 bool mbCloneCaption;
1930 public:
1931 CopyCellNotesHandler( const ScColumn& rSrcCol, ScColumn& rDestCol, SCROW nDestOffset, bool bCloneCaption ) :
1932 mrDestCol(rDestCol),
1933 mrDestNotes(rDestCol.GetCellNoteStore()),
1934 miPos(mrDestNotes.begin()),
1935 mnSrcTab(rSrcCol.GetTab()),
1936 mnSrcCol(rSrcCol.GetCol()),
1937 mnDestTab(rDestCol.GetTab()),
1938 mnDestCol(rDestCol.GetCol()),
1939 mnDestOffset(nDestOffset),
1940 mbCloneCaption(bCloneCaption) {}
1942 void operator() ( size_t nRow, const ScPostIt* p )
1944 SCROW nDestRow = nRow + mnDestOffset;
1945 ScAddress aSrcPos(mnSrcCol, nRow, mnSrcTab);
1946 ScAddress aDestPos(mnDestCol, nDestRow, mnDestTab);
1947 ScPostIt* pNew = p->Clone(aSrcPos, mrDestCol.GetDoc(), aDestPos, mbCloneCaption).release();
1948 miPos = mrDestNotes.set(miPos, nDestRow, pNew);
1949 // Notify our LOK clients also
1950 ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Add, mrDestCol.GetDoc(), aDestPos, pNew);
1956 void ScColumn::CopyCellNotesToDocument(
1957 SCROW nRow1, SCROW nRow2, ScColumn& rDestCol, bool bCloneCaption, SCROW nRowOffsetDest ) const
1959 if (IsNotesEmptyBlock(nRow1, nRow2))
1960 // The column has no cell notes to copy between specified rows.
1961 return;
1963 ScDrawLayer *pDrawLayer = rDestCol.GetDoc().GetDrawLayer();
1964 bool bWasLocked = bool();
1965 if (pDrawLayer)
1967 // Avoid O(n^2) by temporary locking SdrModel which disables broadcasting.
1968 // Each cell note adds undo listener, and all of them would be woken up in ScPostIt::CreateCaption.
1969 bWasLocked = pDrawLayer->isLocked();
1970 pDrawLayer->setLock(true);
1972 CopyCellNotesHandler aFunc(*this, rDestCol, nRowOffsetDest, bCloneCaption);
1973 sc::ParseNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
1974 if (pDrawLayer)
1975 pDrawLayer->setLock(bWasLocked);
1978 void ScColumn::DuplicateNotes(SCROW nStartRow, size_t nDataSize, ScColumn& rDestCol, sc::ColumnBlockPosition& maDestBlockPos,
1979 bool bCloneCaption, SCROW nRowOffsetDest ) const
1981 CopyCellNotesToDocument(nStartRow, nStartRow + nDataSize -1, rDestCol, bCloneCaption, nRowOffsetDest);
1982 maDestBlockPos.miCellNotePos = rDestCol.maCellNotes.begin();
1985 SvtBroadcaster* ScColumn::GetBroadcaster(SCROW nRow)
1987 return maBroadcasters.get<SvtBroadcaster*>(nRow);
1990 const SvtBroadcaster* ScColumn::GetBroadcaster(SCROW nRow) const
1992 return maBroadcasters.get<SvtBroadcaster*>(nRow);
1995 void ScColumn::DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2 )
1997 rBlockPos.miBroadcasterPos =
1998 maBroadcasters.set_empty(rBlockPos.miBroadcasterPos, nRow1, nRow2);
2001 void ScColumn::PrepareBroadcastersForDestruction()
2003 for (auto& rBroadcaster : maBroadcasters)
2005 if (rBroadcaster.type == sc::element_type_broadcaster)
2007 sc::broadcaster_block::iterator it = sc::broadcaster_block::begin(*rBroadcaster.data);
2008 sc::broadcaster_block::iterator itEnd = sc::broadcaster_block::end(*rBroadcaster.data);
2009 for (; it != itEnd; ++it)
2010 (*it)->PrepareForDestruction();
2015 namespace
2017 struct BroadcasterNoListenersPredicate
2019 bool operator()( size_t, const SvtBroadcaster* broadcaster )
2021 return !broadcaster->HasListeners();
2027 void ScColumn::DeleteEmptyBroadcasters()
2029 if(!mbEmptyBroadcastersPending)
2030 return;
2031 // Clean up after ScDocument::EnableDelayDeletingBroadcasters().
2032 BroadcasterNoListenersPredicate predicate;
2033 sc::SetElementsToEmpty1<sc::broadcaster_block>( maBroadcasters, predicate );
2034 mbEmptyBroadcastersPending = false;
2037 // Sparklines
2039 namespace
2042 class DeletingSparklinesHandler
2044 ScDocument& m_rDocument;
2045 SCTAB m_nTab;
2047 public:
2048 DeletingSparklinesHandler(ScDocument& rDocument, SCTAB nTab)
2049 : m_rDocument(rDocument)
2050 , m_nTab(nTab)
2053 void operator() (size_t /*nRow*/, const sc::SparklineCell* pCell)
2055 auto* pList = m_rDocument.GetSparklineList(m_nTab);
2056 pList->removeSparkline(pCell->getSparkline());
2060 } // end anonymous ns
2062 sc::SparklineCell* ScColumn::GetSparklineCell(SCROW nRow)
2064 return maSparklines.get<sc::SparklineCell*>(nRow);
2067 void ScColumn::CreateSparklineCell(SCROW nRow, std::shared_ptr<sc::Sparkline> const& pSparkline)
2069 auto* pList = GetDoc().GetSparklineList(GetTab());
2070 pList->addSparkline(pSparkline);
2071 maSparklines.set(nRow, new sc::SparklineCell(pSparkline));
2074 void ScColumn::DeleteSparklineCells(sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2)
2076 DeletingSparklinesHandler aFunction(GetDoc(), nTab);
2077 sc::ParseSparkline(maSparklines.begin(), maSparklines, nRow1, nRow2, aFunction);
2079 rBlockPos.miSparklinePos = maSparklines.set_empty(rBlockPos.miSparklinePos, nRow1, nRow2);
2082 bool ScColumn::DeleteSparkline(SCROW nRow)
2084 if (!GetDoc().ValidRow(nRow))
2085 return false;
2087 DeletingSparklinesHandler aFunction(GetDoc(), nTab);
2088 sc::ParseSparkline(maSparklines.begin(), maSparklines, nRow, nRow, aFunction);
2090 maSparklines.set_empty(nRow, nRow);
2091 return true;
2094 bool ScColumn::IsSparklinesEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
2096 std::pair<sc::SparklineStoreType::const_iterator,size_t> aPos = maSparklines.position(nStartRow);
2097 sc::SparklineStoreType::const_iterator it = aPos.first;
2098 if (it == maSparklines.end())
2099 return false;
2101 if (it->type != sc::element_type_empty)
2102 return false;
2104 // start position of next block which is not empty.
2105 SCROW nNextRow = nStartRow + it->size - aPos.second;
2106 return nEndRow < nNextRow;
2109 namespace
2112 class CopySparklinesHandler
2114 ScColumn& mrDestColumn;
2115 sc::SparklineStoreType& mrDestSparkline;
2116 sc::SparklineStoreType::iterator miDestPosition;
2117 SCROW mnDestOffset;
2119 public:
2120 CopySparklinesHandler(ScColumn& rDestColumn, SCROW nDestOffset)
2121 : mrDestColumn(rDestColumn)
2122 , mrDestSparkline(mrDestColumn.GetSparklineStore())
2123 , miDestPosition(mrDestSparkline.begin())
2124 , mnDestOffset(nDestOffset)
2127 void operator() (size_t nRow, const sc::SparklineCell* pCell)
2129 SCROW nDestRow = nRow + mnDestOffset;
2131 auto const& pSparkline = pCell->getSparkline();
2132 auto const& pGroup = pCell->getSparklineGroup();
2134 auto& rDestDoc = mrDestColumn.GetDoc();
2135 auto pDestinationGroup = rDestDoc.SearchSparklineGroup(pGroup->getID());
2136 if (!pDestinationGroup)
2137 pDestinationGroup = std::make_shared<sc::SparklineGroup>(*pGroup); // Copy the group
2138 auto pNewSparkline = std::make_shared<sc::Sparkline>(mrDestColumn.GetCol(), nDestRow, pDestinationGroup);
2139 pNewSparkline->setInputRange(pSparkline->getInputRange());
2141 auto* pList = rDestDoc.GetSparklineList(mrDestColumn.GetTab());
2142 pList->addSparkline(pNewSparkline);
2144 miDestPosition = mrDestSparkline.set(miDestPosition, nDestRow, new sc::SparklineCell(std::move(pNewSparkline)));
2150 void ScColumn::CopyCellSparklinesToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol, SCROW nRowOffsetDest) const
2152 if (IsSparklinesEmptyBlock(nRow1, nRow2))
2153 // The column has no cell sparklines to copy between specified rows.
2154 return;
2156 CopySparklinesHandler aFunctor(rDestCol, nRowOffsetDest);
2157 sc::ParseSparkline(maSparklines.begin(), maSparklines, nRow1, nRow2, aFunctor);
2160 void ScColumn::DuplicateSparklines(SCROW nStartRow, size_t nDataSize, ScColumn& rDestCol,
2161 sc::ColumnBlockPosition& rDestBlockPos, SCROW nRowOffsetDest) const
2163 CopyCellSparklinesToDocument(nStartRow, nStartRow + nDataSize - 1, rDestCol, nRowOffsetDest);
2164 rDestBlockPos.miSparklinePos = rDestCol.maSparklines.begin();
2167 bool ScColumn::HasSparklines() const
2169 if (maSparklines.block_size() == 1 && maSparklines.begin()->type == sc::element_type_empty)
2170 return false; // all elements are empty
2171 return true; // otherwise some must be sparklines
2174 SCROW ScColumn::GetSparklinesMaxRow() const
2176 SCROW maxRow = 0;
2177 for (const auto& rSparkline : maSparklines)
2179 if (rSparkline.type == sc::element_type_sparkline)
2180 maxRow = rSparkline.position + rSparkline.size - 1;
2182 return maxRow;
2185 SCROW ScColumn::GetSparklinesMinRow() const
2187 SCROW minRow = 0;
2188 sc::SparklineStoreType::const_iterator it = std::find_if(maSparklines.begin(), maSparklines.end(),
2189 [](const auto& rSparkline)
2191 return rSparkline.type == sc::element_type_sparkline;
2193 if (it != maSparklines.end())
2194 minRow = it->position;
2195 return minRow;
2198 // Notes
2200 ScPostIt* ScColumn::GetCellNote(SCROW nRow)
2202 return maCellNotes.get<ScPostIt*>(nRow);
2205 const ScPostIt* ScColumn::GetCellNote(SCROW nRow) const
2207 return maCellNotes.get<ScPostIt*>(nRow);
2210 const ScPostIt* ScColumn::GetCellNote( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow ) const
2212 sc::CellNoteStoreType::const_position_type aPos = maCellNotes.position(rBlockPos.miCellNotePos, nRow);
2213 rBlockPos.miCellNotePos = aPos.first;
2215 if (aPos.first->type != sc::element_type_cellnote)
2216 return nullptr;
2218 return sc::cellnote_block::at(*aPos.first->data, aPos.second);
2221 ScPostIt* ScColumn::GetCellNote( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow )
2223 return const_cast<ScPostIt*>(const_cast<const ScColumn*>(this)->GetCellNote( rBlockPos, nRow ));
2226 void ScColumn::SetCellNote(SCROW nRow, std::unique_ptr<ScPostIt> pNote)
2228 //pNote->UpdateCaptionPos(ScAddress(nCol, nRow, nTab)); // TODO notes useful ? slow import with many notes
2229 maCellNotes.set(nRow, pNote.release());
2232 namespace {
2233 class CellNoteHandler
2235 const ScDocument& m_rDocument;
2236 const ScAddress m_aAddress; // 'incomplete' address consisting of tab, column
2237 const bool m_bForgetCaptionOwnership;
2239 public:
2240 CellNoteHandler(const ScDocument& rDocument, const ScAddress& rPos, bool bForgetCaptionOwnership) :
2241 m_rDocument(rDocument),
2242 m_aAddress(rPos),
2243 m_bForgetCaptionOwnership(bForgetCaptionOwnership) {}
2245 void operator() ( size_t nRow, ScPostIt* p )
2247 if (m_bForgetCaptionOwnership)
2248 p->ForgetCaption();
2250 // Create a 'complete' address object
2251 ScAddress aAddr(m_aAddress);
2252 aAddr.SetRow(nRow);
2253 // Notify our LOK clients
2254 ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Remove, m_rDocument, aAddr, p);
2257 } // anonymous namespace
2259 void ScColumn::CellNotesDeleting(SCROW nRow1, SCROW nRow2, bool bForgetCaptionOwnership)
2261 ScAddress aAddr(nCol, 0, nTab);
2262 CellNoteHandler aFunc(GetDoc(), aAddr, bForgetCaptionOwnership);
2263 sc::ParseNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
2266 void ScColumn::DeleteCellNotes( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2, bool bForgetCaptionOwnership )
2268 CellNotesDeleting(nRow1, nRow2, bForgetCaptionOwnership);
2270 rBlockPos.miCellNotePos =
2271 maCellNotes.set_empty(rBlockPos.miCellNotePos, nRow1, nRow2);
2274 bool ScColumn::HasCellNotes() const
2276 return mnBlkCountCellNotes != 0;
2279 SCROW ScColumn::GetCellNotesMaxRow() const
2281 // hypothesis : the column has cell notes (should be checked before)
2282 SCROW maxRow = 0;
2283 for (const auto& rCellNote : maCellNotes)
2285 if (rCellNote.type == sc::element_type_cellnote)
2286 maxRow = rCellNote.position + rCellNote.size -1;
2288 return maxRow;
2290 SCROW ScColumn::GetCellNotesMinRow() const
2292 // hypothesis : the column has cell notes (should be checked before)
2293 SCROW minRow = 0;
2294 sc::CellNoteStoreType::const_iterator it = std::find_if(maCellNotes.begin(), maCellNotes.end(),
2295 [](const auto& rCellNote) { return rCellNote.type == sc::element_type_cellnote; });
2296 if (it != maCellNotes.end())
2297 minRow = it->position;
2298 return minRow;
2301 sal_uInt16 ScColumn::GetTextWidth(SCROW nRow) const
2303 return maCellTextAttrs.get<sc::CellTextAttr>(nRow).mnTextWidth;
2306 void ScColumn::SetTextWidth(SCROW nRow, sal_uInt16 nWidth)
2308 sc::CellTextAttrStoreType::position_type aPos = maCellTextAttrs.position(nRow);
2309 if (aPos.first->type != sc::element_type_celltextattr)
2310 return;
2312 // Set new value only when the slot is not empty.
2313 sc::celltextattr_block::at(*aPos.first->data, aPos.second).mnTextWidth = nWidth;
2314 CellStorageModified();
2317 SvtScriptType ScColumn::GetScriptType( SCROW nRow ) const
2319 if (!GetDoc().ValidRow(nRow) || maCellTextAttrs.is_empty(nRow))
2320 return SvtScriptType::NONE;
2322 return maCellTextAttrs.get<sc::CellTextAttr>(nRow).mnScriptType;
2325 SvtScriptType ScColumn::GetRangeScriptType(
2326 sc::CellTextAttrStoreType::iterator& itPos, SCROW nRow1, SCROW nRow2, const sc::CellStoreType::iterator& itrCells_ )
2328 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
2329 return SvtScriptType::NONE;
2331 SCROW nRow = nRow1;
2332 std::pair<sc::CellTextAttrStoreType::iterator,size_t> aRet =
2333 maCellTextAttrs.position(itPos, nRow1);
2335 itPos = aRet.first; // Track the position of cell text attribute array.
2336 sc::CellStoreType::iterator itrCells = itrCells_;
2338 SvtScriptType nScriptType = SvtScriptType::NONE;
2339 bool bUpdated = false;
2340 if (itPos->type == sc::element_type_celltextattr)
2342 sc::celltextattr_block::iterator it = sc::celltextattr_block::begin(*itPos->data);
2343 sc::celltextattr_block::iterator itEnd = sc::celltextattr_block::end(*itPos->data);
2344 std::advance(it, aRet.second);
2345 for (; it != itEnd; ++it, ++nRow)
2347 if (nRow > nRow2)
2348 return nScriptType;
2350 sc::CellTextAttr& rVal = *it;
2351 if (UpdateScriptType(rVal, nRow, itrCells))
2352 bUpdated = true;
2353 nScriptType |= rVal.mnScriptType;
2356 else
2358 // Skip this whole block.
2359 nRow += itPos->size - aRet.second;
2362 while (nRow <= nRow2)
2364 ++itPos;
2365 if (itPos == maCellTextAttrs.end())
2366 return nScriptType;
2368 if (itPos->type != sc::element_type_celltextattr)
2370 // Skip this whole block.
2371 nRow += itPos->size;
2372 continue;
2375 sc::celltextattr_block::iterator it = sc::celltextattr_block::begin(*itPos->data);
2376 sc::celltextattr_block::iterator itEnd = sc::celltextattr_block::end(*itPos->data);
2377 for (; it != itEnd; ++it, ++nRow)
2379 if (nRow > nRow2)
2380 return nScriptType;
2382 sc::CellTextAttr& rVal = *it;
2383 if (UpdateScriptType(rVal, nRow, itrCells))
2384 bUpdated = true;
2386 nScriptType |= rVal.mnScriptType;
2390 if (bUpdated)
2391 CellStorageModified();
2393 return nScriptType;
2396 void ScColumn::SetScriptType( SCROW nRow, SvtScriptType nType )
2398 if (!GetDoc().ValidRow(nRow))
2399 return;
2401 sc::CellTextAttrStoreType::position_type aPos = maCellTextAttrs.position(nRow);
2402 if (aPos.first->type != sc::element_type_celltextattr)
2403 // Set new value only when the slot is already set.
2404 return;
2406 sc::celltextattr_block::at(*aPos.first->data, aPos.second).mnScriptType = nType;
2407 CellStorageModified();
2410 formula::FormulaTokenRef ScColumn::ResolveStaticReference( SCROW nRow )
2412 std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nRow);
2413 sc::CellStoreType::iterator it = aPos.first;
2414 if (it == maCells.end())
2415 // Invalid row. Return a null token.
2416 return formula::FormulaTokenRef();
2418 switch (it->type)
2420 case sc::element_type_numeric:
2422 double fVal = sc::numeric_block::at(*it->data, aPos.second);
2423 return formula::FormulaTokenRef(new formula::FormulaDoubleToken(fVal));
2425 case sc::element_type_formula:
2427 ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
2428 if (p->IsValue())
2429 return formula::FormulaTokenRef(new formula::FormulaDoubleToken(p->GetValue()));
2431 return formula::FormulaTokenRef(new formula::FormulaStringToken(p->GetString()));
2433 case sc::element_type_string:
2435 const svl::SharedString& rSS = sc::string_block::at(*it->data, aPos.second);
2436 return formula::FormulaTokenRef(new formula::FormulaStringToken(rSS));
2438 case sc::element_type_edittext:
2440 const EditTextObject* pText = sc::edittext_block::at(*it->data, aPos.second);
2441 OUString aStr = ScEditUtil::GetString(*pText, &GetDoc());
2442 svl::SharedString aSS( GetDoc().GetSharedStringPool().intern(aStr));
2443 return formula::FormulaTokenRef(new formula::FormulaStringToken(std::move(aSS)));
2445 case sc::element_type_empty:
2446 default:
2447 // Return a value of 0.0 in all the other cases.
2448 return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0));
2452 namespace {
2454 class ToMatrixHandler
2456 ScMatrix& mrMat;
2457 SCCOL mnMatCol;
2458 SCROW mnTopRow;
2459 ScDocument* mpDoc;
2460 svl::SharedStringPool& mrStrPool;
2461 public:
2462 ToMatrixHandler(ScMatrix& rMat, SCCOL nMatCol, SCROW nTopRow, ScDocument* pDoc) :
2463 mrMat(rMat), mnMatCol(nMatCol), mnTopRow(nTopRow),
2464 mpDoc(pDoc), mrStrPool(pDoc->GetSharedStringPool()) {}
2466 void operator() (size_t nRow, double fVal)
2468 mrMat.PutDouble(fVal, mnMatCol, nRow - mnTopRow);
2471 void operator() (size_t nRow, const ScFormulaCell* p)
2473 // Formula cell may need to re-calculate.
2474 ScFormulaCell& rCell = const_cast<ScFormulaCell&>(*p);
2475 if (rCell.IsValue())
2476 mrMat.PutDouble(rCell.GetValue(), mnMatCol, nRow - mnTopRow);
2477 else
2478 mrMat.PutString(rCell.GetString(), mnMatCol, nRow - mnTopRow);
2481 void operator() (size_t nRow, const svl::SharedString& rSS)
2483 mrMat.PutString(rSS, mnMatCol, nRow - mnTopRow);
2486 void operator() (size_t nRow, const EditTextObject* pStr)
2488 mrMat.PutString(mrStrPool.intern(ScEditUtil::GetString(*pStr, mpDoc)), mnMatCol, nRow - mnTopRow);
2494 bool ScColumn::ResolveStaticReference( ScMatrix& rMat, SCCOL nMatCol, SCROW nRow1, SCROW nRow2 )
2496 if (nRow1 > nRow2)
2497 return false;
2499 ToMatrixHandler aFunc(rMat, nMatCol, nRow1, &GetDoc());
2500 sc::ParseAllNonEmpty(maCells.begin(), maCells, nRow1, nRow2, aFunc);
2501 return true;
2504 namespace {
2506 struct CellBucket
2508 SCSIZE mnEmpValStart;
2509 SCSIZE mnNumValStart;
2510 SCSIZE mnStrValStart;
2511 SCSIZE mnEmpValCount;
2512 std::vector<double> maNumVals;
2513 std::vector<svl::SharedString> maStrVals;
2515 CellBucket() : mnEmpValStart(0), mnNumValStart(0), mnStrValStart(0), mnEmpValCount(0) {}
2517 void flush(ScMatrix& rMat, SCSIZE nCol)
2519 if (mnEmpValCount)
2521 rMat.PutEmptyResultVector(mnEmpValCount, nCol, mnEmpValStart);
2522 reset();
2524 else if (!maNumVals.empty())
2526 const double* p = maNumVals.data();
2527 rMat.PutDouble(p, maNumVals.size(), nCol, mnNumValStart);
2528 reset();
2530 else if (!maStrVals.empty())
2532 const svl::SharedString* p = maStrVals.data();
2533 rMat.PutString(p, maStrVals.size(), nCol, mnStrValStart);
2534 reset();
2538 void reset()
2540 mnEmpValStart = mnNumValStart = mnStrValStart = 0;
2541 mnEmpValCount = 0;
2542 maNumVals.clear();
2543 maStrVals.clear();
2547 class FillMatrixHandler
2549 ScMatrix& mrMat;
2550 size_t mnMatCol;
2551 size_t mnTopRow;
2553 ScDocument* mpDoc;
2554 svl::SharedStringPool& mrPool;
2555 svl::SharedStringPool* mpPool; // if matrix is not in the same document
2557 public:
2558 FillMatrixHandler(ScMatrix& rMat, size_t nMatCol, size_t nTopRow, ScDocument* pDoc, svl::SharedStringPool* pPool) :
2559 mrMat(rMat), mnMatCol(nMatCol), mnTopRow(nTopRow),
2560 mpDoc(pDoc), mrPool(pDoc->GetSharedStringPool()), mpPool(pPool) {}
2562 void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
2564 size_t nMatRow = node.position + nOffset - mnTopRow;
2566 switch (node.type)
2568 case sc::element_type_numeric:
2570 const double* p = &sc::numeric_block::at(*node.data, nOffset);
2571 mrMat.PutDouble(p, nDataSize, mnMatCol, nMatRow);
2573 break;
2574 case sc::element_type_string:
2576 if (!mpPool)
2578 const svl::SharedString* p = &sc::string_block::at(*node.data, nOffset);
2579 mrMat.PutString(p, nDataSize, mnMatCol, nMatRow);
2581 else
2583 std::vector<svl::SharedString> aStrings;
2584 aStrings.reserve(nDataSize);
2585 const svl::SharedString* p = &sc::string_block::at(*node.data, nOffset);
2586 for (size_t i = 0; i < nDataSize; ++i)
2588 aStrings.push_back(mpPool->intern(p[i].getString()));
2590 mrMat.PutString(aStrings.data(), aStrings.size(), mnMatCol, nMatRow);
2593 break;
2594 case sc::element_type_edittext:
2596 std::vector<svl::SharedString> aSSs;
2597 aSSs.reserve(nDataSize);
2598 sc::edittext_block::const_iterator it = sc::edittext_block::begin(*node.data);
2599 std::advance(it, nOffset);
2600 sc::edittext_block::const_iterator itEnd = it;
2601 std::advance(itEnd, nDataSize);
2602 for (; it != itEnd; ++it)
2604 OUString aStr = ScEditUtil::GetString(**it, mpDoc);
2605 if (!mpPool)
2606 aSSs.push_back(mrPool.intern(aStr));
2607 else
2608 aSSs.push_back(mpPool->intern(aStr));
2611 const svl::SharedString* p = aSSs.data();
2612 mrMat.PutString(p, nDataSize, mnMatCol, nMatRow);
2614 break;
2615 case sc::element_type_formula:
2617 CellBucket aBucket;
2618 sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
2619 std::advance(it, nOffset);
2620 sc::formula_block::const_iterator itEnd = it;
2621 std::advance(itEnd, nDataSize);
2623 size_t nPrevRow = 0, nThisRow = node.position + nOffset;
2624 for (; it != itEnd; ++it, nPrevRow = nThisRow, ++nThisRow)
2626 ScFormulaCell& rCell = **it;
2628 if (rCell.IsEmpty())
2630 if (aBucket.mnEmpValCount && nThisRow == nPrevRow + 1)
2632 // Secondary empty results.
2633 ++aBucket.mnEmpValCount;
2635 else
2637 // First empty result.
2638 aBucket.flush(mrMat, mnMatCol);
2639 aBucket.mnEmpValStart = nThisRow - mnTopRow;
2640 ++aBucket.mnEmpValCount;
2642 continue;
2645 FormulaError nErr;
2646 double fVal;
2647 if (rCell.GetErrorOrValue(nErr, fVal))
2649 if (nErr != FormulaError::NONE)
2650 fVal = CreateDoubleError(nErr);
2652 if (!aBucket.maNumVals.empty() && nThisRow == nPrevRow + 1)
2654 // Secondary numbers.
2655 aBucket.maNumVals.push_back(fVal);
2657 else
2659 // First number.
2660 aBucket.flush(mrMat, mnMatCol);
2661 aBucket.mnNumValStart = nThisRow - mnTopRow;
2662 aBucket.maNumVals.push_back(fVal);
2664 continue;
2667 svl::SharedString aStr = rCell.GetString();
2668 if (mpPool)
2669 aStr = mpPool->intern(aStr.getString());
2670 if (!aBucket.maStrVals.empty() && nThisRow == nPrevRow + 1)
2672 // Secondary strings.
2673 aBucket.maStrVals.push_back(aStr);
2675 else
2677 // First string.
2678 aBucket.flush(mrMat, mnMatCol);
2679 aBucket.mnStrValStart = nThisRow - mnTopRow;
2680 aBucket.maStrVals.push_back(aStr);
2684 aBucket.flush(mrMat, mnMatCol);
2686 break;
2687 default:
2695 void ScColumn::FillMatrix( ScMatrix& rMat, size_t nMatCol, SCROW nRow1, SCROW nRow2, svl::SharedStringPool* pPool ) const
2697 FillMatrixHandler aFunc(rMat, nMatCol, nRow1, &GetDoc(), pPool);
2698 sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
2701 namespace {
2703 template<typename Blk>
2704 void getBlockIterators(
2705 const sc::CellStoreType::iterator& it, size_t& rLenRemain,
2706 typename Blk::iterator& rData, typename Blk::iterator& rDataEnd )
2708 rData = Blk::begin(*it->data);
2709 if (rLenRemain >= it->size)
2711 // Block is shorter than the remaining requested length.
2712 rDataEnd = Blk::end(*it->data);
2713 rLenRemain -= it->size;
2715 else
2717 rDataEnd = rData;
2718 std::advance(rDataEnd, rLenRemain);
2719 rLenRemain = 0;
2723 bool appendToBlock(
2724 ScDocument* pDoc, sc::FormulaGroupContext& rCxt, sc::FormulaGroupContext::ColArray& rColArray,
2725 size_t nPos, size_t nArrayLen, const sc::CellStoreType::iterator& _it, const sc::CellStoreType::iterator& itEnd )
2727 svl::SharedStringPool& rPool = pDoc->GetSharedStringPool();
2728 size_t nLenRemain = nArrayLen - nPos;
2730 for (sc::CellStoreType::iterator it = _it; it != itEnd; ++it)
2732 switch (it->type)
2734 case sc::element_type_string:
2736 sc::string_block::iterator itData, itDataEnd;
2737 getBlockIterators<sc::string_block>(it, nLenRemain, itData, itDataEnd);
2738 rCxt.ensureStrArray(rColArray, nArrayLen);
2740 for (; itData != itDataEnd; ++itData, ++nPos)
2741 (*rColArray.mpStrArray)[nPos] = itData->getData();
2743 break;
2744 case sc::element_type_edittext:
2746 sc::edittext_block::iterator itData, itDataEnd;
2747 getBlockIterators<sc::edittext_block>(it, nLenRemain, itData, itDataEnd);
2748 rCxt.ensureStrArray(rColArray, nArrayLen);
2750 for (; itData != itDataEnd; ++itData, ++nPos)
2752 OUString aStr = ScEditUtil::GetString(**itData, pDoc);
2753 (*rColArray.mpStrArray)[nPos] = rPool.intern(aStr).getData();
2756 break;
2757 case sc::element_type_formula:
2759 sc::formula_block::iterator itData, itDataEnd;
2760 getBlockIterators<sc::formula_block>(it, nLenRemain, itData, itDataEnd);
2762 /* tdf#91416 setting progress in triggers a resize of the window
2763 and so ScTabView::DoResize and an InterpretVisible and
2764 InterpretDirtyCells which resets the mpFormulaGroupCxt that
2765 the current rCxt points to, which is bad, so disable progress
2766 during GetResult
2768 ScProgress *pProgress = ScProgress::GetInterpretProgress();
2769 bool bTempDisableProgress = pProgress && pProgress->Enabled();
2770 if (bTempDisableProgress)
2771 pProgress->Disable();
2773 for (; itData != itDataEnd; ++itData, ++nPos)
2775 ScFormulaCell& rFC = **itData;
2777 sc::FormulaResultValue aRes = rFC.GetResult();
2779 if (aRes.meType == sc::FormulaResultValue::Invalid || aRes.mnError != FormulaError::NONE)
2781 if (aRes.mnError == FormulaError::CircularReference)
2783 // This cell needs to be recalculated on next visit.
2784 rFC.SetErrCode(FormulaError::NONE);
2785 rFC.SetDirtyVar();
2787 return false;
2790 if (aRes.meType == sc::FormulaResultValue::String)
2792 rCxt.ensureStrArray(rColArray, nArrayLen);
2793 (*rColArray.mpStrArray)[nPos] = aRes.maString.getData();
2795 else
2797 rCxt.ensureNumArray(rColArray, nArrayLen);
2798 (*rColArray.mpNumArray)[nPos] = aRes.mfValue;
2802 if (bTempDisableProgress)
2803 pProgress->Enable();
2805 break;
2806 case sc::element_type_empty:
2808 if (nLenRemain > it->size)
2810 nPos += it->size;
2811 nLenRemain -= it->size;
2813 else
2815 nPos = nArrayLen;
2816 nLenRemain = 0;
2819 break;
2820 case sc::element_type_numeric:
2822 sc::numeric_block::iterator itData, itDataEnd;
2823 getBlockIterators<sc::numeric_block>(it, nLenRemain, itData, itDataEnd);
2824 rCxt.ensureNumArray(rColArray, nArrayLen);
2826 for (; itData != itDataEnd; ++itData, ++nPos)
2827 (*rColArray.mpNumArray)[nPos] = *itData;
2829 break;
2830 default:
2831 return false;
2834 if (!nLenRemain)
2835 return true;
2838 return false;
2841 void copyFirstStringBlock(
2842 ScDocument& rDoc, sc::FormulaGroupContext::StrArrayType& rArray, size_t nLen, const sc::CellStoreType::iterator& itBlk )
2844 sc::FormulaGroupContext::StrArrayType::iterator itArray = rArray.begin();
2846 switch (itBlk->type)
2848 case sc::element_type_string:
2850 sc::string_block::iterator it = sc::string_block::begin(*itBlk->data);
2851 sc::string_block::iterator itEnd = it;
2852 std::advance(itEnd, nLen);
2853 for (; it != itEnd; ++it, ++itArray)
2854 *itArray = it->getData();
2856 break;
2857 case sc::element_type_edittext:
2859 sc::edittext_block::iterator it = sc::edittext_block::begin(*itBlk->data);
2860 sc::edittext_block::iterator itEnd = it;
2861 std::advance(itEnd, nLen);
2863 svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
2864 for (; it != itEnd; ++it, ++itArray)
2866 EditTextObject* pText = *it;
2867 OUString aStr = ScEditUtil::GetString(*pText, &rDoc);
2868 *itArray = rPool.intern(aStr).getData();
2871 break;
2872 default:
2877 sc::FormulaGroupContext::ColArray*
2878 copyFirstFormulaBlock(
2879 sc::FormulaGroupContext& rCxt, const sc::CellStoreType::iterator& itBlk, size_t nArrayLen,
2880 SCTAB nTab, SCCOL nCol )
2882 size_t nLen = std::min(itBlk->size, nArrayLen);
2884 sc::formula_block::iterator it = sc::formula_block::begin(*itBlk->data);
2885 sc::formula_block::iterator itEnd;
2887 sc::FormulaGroupContext::NumArrayType* pNumArray = nullptr;
2888 sc::FormulaGroupContext::StrArrayType* pStrArray = nullptr;
2890 itEnd = it;
2891 std::advance(itEnd, nLen);
2892 size_t nPos = 0;
2893 for (; it != itEnd; ++it, ++nPos)
2895 ScFormulaCell& rFC = **it;
2896 sc::FormulaResultValue aRes = rFC.GetResult();
2897 if (aRes.meType == sc::FormulaResultValue::Invalid || aRes.mnError != FormulaError::NONE)
2899 if (aRes.mnError == FormulaError::CircularReference)
2901 // This cell needs to be recalculated on next visit.
2902 rFC.SetErrCode(FormulaError::NONE);
2903 rFC.SetDirtyVar();
2905 return nullptr;
2908 if (aRes.meType == sc::FormulaResultValue::Value)
2910 if (!pNumArray)
2912 rCxt.m_NumArrays.push_back(
2913 std::make_unique<sc::FormulaGroupContext::NumArrayType>(nArrayLen,
2914 std::numeric_limits<double>::quiet_NaN()));
2915 pNumArray = rCxt.m_NumArrays.back().get();
2918 (*pNumArray)[nPos] = aRes.mfValue;
2920 else
2922 if (!pStrArray)
2924 rCxt.m_StrArrays.push_back(
2925 std::make_unique<sc::FormulaGroupContext::StrArrayType>(nArrayLen, nullptr));
2926 pStrArray = rCxt.m_StrArrays.back().get();
2929 (*pStrArray)[nPos] = aRes.maString.getData();
2933 if (!pNumArray && !pStrArray)
2934 // At least one of these arrays should be allocated.
2935 return nullptr;
2937 return rCxt.setCachedColArray(nTab, nCol, pNumArray, pStrArray);
2940 struct NonNullStringFinder
2942 bool operator() (const rtl_uString* p) const { return p != nullptr; }
2945 bool hasNonEmpty( const sc::FormulaGroupContext::StrArrayType& rArray, SCROW nRow1, SCROW nRow2 )
2947 // The caller has to make sure the array is at least nRow2+1 long.
2948 sc::FormulaGroupContext::StrArrayType::const_iterator it = rArray.begin();
2949 std::advance(it, nRow1);
2950 sc::FormulaGroupContext::StrArrayType::const_iterator itEnd = it;
2951 std::advance(itEnd, nRow2-nRow1+1);
2952 return std::any_of(it, itEnd, NonNullStringFinder());
2955 struct ProtectFormulaGroupContext
2957 ProtectFormulaGroupContext( ScDocument* d )
2958 : doc( d ) { doc->BlockFormulaGroupContextDiscard( true ); }
2959 ~ProtectFormulaGroupContext()
2960 { doc->BlockFormulaGroupContextDiscard( false ); }
2961 ScDocument* doc;
2966 formula::VectorRefArray ScColumn::FetchVectorRefArray( SCROW nRow1, SCROW nRow2 )
2968 if (nRow1 > nRow2)
2969 return formula::VectorRefArray(formula::VectorRefArray::Invalid);
2971 // See if the requested range is already cached.
2972 ScDocument& rDocument = GetDoc();
2973 sc::FormulaGroupContext& rCxt = *(rDocument.GetFormulaGroupContext());
2974 sc::FormulaGroupContext::ColArray* pColArray = rCxt.getCachedColArray(nTab, nCol, nRow2+1);
2975 if (pColArray)
2977 const double* pNum = nullptr;
2978 if (pColArray->mpNumArray)
2979 pNum = &(*pColArray->mpNumArray)[nRow1];
2981 rtl_uString** pStr = nullptr;
2982 if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
2983 pStr = &(*pColArray->mpStrArray)[nRow1];
2985 return formula::VectorRefArray(pNum, pStr);
2988 // ScColumn::CellStorageModified() simply discards the entire cache (FormulaGroupContext)
2989 // on any modification. However getting cell values may cause this to be called
2990 // if interpreting a cell results in a change to it (not just its result though).
2991 // So temporarily block the discarding.
2992 ProtectFormulaGroupContext protectContext(&GetDoc());
2994 // We need to fetch all cell values from row 0 to nRow2 for caching purposes.
2995 sc::CellStoreType::iterator itBlk = maCells.begin();
2996 switch (itBlk->type)
2998 case sc::element_type_numeric:
3000 if (o3tl::make_unsigned(nRow2) < itBlk->size)
3002 // Requested range falls within the first block. No need to cache.
3003 const double* p = &sc::numeric_block::at(*itBlk->data, nRow1);
3004 return formula::VectorRefArray(p);
3007 // Allocate a new array and copy the values to it.
3008 sc::numeric_block::const_iterator it = sc::numeric_block::begin(*itBlk->data);
3009 sc::numeric_block::const_iterator itEnd = sc::numeric_block::end(*itBlk->data);
3010 rCxt.m_NumArrays.push_back(
3011 std::make_unique<sc::FormulaGroupContext::NumArrayType>(it, itEnd));
3012 sc::FormulaGroupContext::NumArrayType& rArray = *rCxt.m_NumArrays.back();
3013 // allocate to the requested length.
3014 rArray.resize(nRow2+1, std::numeric_limits<double>::quiet_NaN());
3016 pColArray = rCxt.setCachedColArray(nTab, nCol, &rArray, nullptr);
3017 if (!pColArray)
3018 // Failed to insert a new cached column array.
3019 return formula::VectorRefArray(formula::VectorRefArray::Invalid);
3021 // Fill the remaining array with values from the following blocks.
3022 size_t nPos = itBlk->size;
3023 ++itBlk;
3024 if (!appendToBlock(&rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
3026 rCxt.discardCachedColArray(nTab, nCol);
3027 return formula::VectorRefArray(formula::VectorRefArray::Invalid);
3030 rtl_uString** pStr = nullptr;
3031 if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
3032 pStr = &(*pColArray->mpStrArray)[nRow1];
3034 return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], pStr);
3036 break;
3037 case sc::element_type_string:
3038 case sc::element_type_edittext:
3040 rCxt.m_StrArrays.push_back(
3041 std::make_unique<sc::FormulaGroupContext::StrArrayType>(nRow2+1, nullptr));
3042 sc::FormulaGroupContext::StrArrayType& rArray = *rCxt.m_StrArrays.back();
3043 pColArray = rCxt.setCachedColArray(nTab, nCol, nullptr, &rArray);
3044 if (!pColArray)
3045 // Failed to insert a new cached column array.
3046 return formula::VectorRefArray();
3048 if (o3tl::make_unsigned(nRow2) < itBlk->size)
3050 // Requested range falls within the first block.
3051 copyFirstStringBlock(rDocument, rArray, nRow2+1, itBlk);
3052 return formula::VectorRefArray(&rArray[nRow1]);
3055 copyFirstStringBlock(rDocument, rArray, itBlk->size, itBlk);
3057 // Fill the remaining array with values from the following blocks.
3058 size_t nPos = itBlk->size;
3059 ++itBlk;
3060 if (!appendToBlock(&rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
3062 rCxt.discardCachedColArray(nTab, nCol);
3063 return formula::VectorRefArray(formula::VectorRefArray::Invalid);
3066 assert(pColArray->mpStrArray);
3068 rtl_uString** pStr = nullptr;
3069 if (hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
3070 pStr = &(*pColArray->mpStrArray)[nRow1];
3072 if (pColArray->mpNumArray)
3073 return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], pStr);
3074 else
3075 return formula::VectorRefArray(pStr);
3077 break;
3078 case sc::element_type_formula:
3080 if (o3tl::make_unsigned(nRow2) < itBlk->size)
3082 // Requested length is within a single block, and the data is
3083 // not cached.
3084 pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol);
3085 if (!pColArray)
3086 // Failed to insert a new cached column array.
3087 return formula::VectorRefArray(formula::VectorRefArray::Invalid);
3089 const double* pNum = nullptr;
3090 rtl_uString** pStr = nullptr;
3091 if (pColArray->mpNumArray)
3092 pNum = &(*pColArray->mpNumArray)[nRow1];
3093 if (pColArray->mpStrArray)
3094 pStr = &(*pColArray->mpStrArray)[nRow1];
3096 return formula::VectorRefArray(pNum, pStr);
3099 pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol);
3100 if (!pColArray)
3102 // Failed to insert a new cached column array.
3103 return formula::VectorRefArray(formula::VectorRefArray::Invalid);
3106 size_t nPos = itBlk->size;
3107 ++itBlk;
3108 if (!appendToBlock(&rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
3110 rCxt.discardCachedColArray(nTab, nCol);
3111 return formula::VectorRefArray(formula::VectorRefArray::Invalid);
3114 const double* pNum = nullptr;
3115 rtl_uString** pStr = nullptr;
3116 if (pColArray->mpNumArray)
3117 pNum = &(*pColArray->mpNumArray)[nRow1];
3118 if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
3119 pStr = &(*pColArray->mpStrArray)[nRow1];
3121 return formula::VectorRefArray(pNum, pStr);
3123 break;
3124 case sc::element_type_empty:
3126 // Fill the whole length with NaN's.
3127 rCxt.m_NumArrays.push_back(
3128 std::make_unique<sc::FormulaGroupContext::NumArrayType>(nRow2+1,
3129 std::numeric_limits<double>::quiet_NaN()));
3130 sc::FormulaGroupContext::NumArrayType& rArray = *rCxt.m_NumArrays.back();
3131 pColArray = rCxt.setCachedColArray(nTab, nCol, &rArray, nullptr);
3132 if (!pColArray)
3133 // Failed to insert a new cached column array.
3134 return formula::VectorRefArray(formula::VectorRefArray::Invalid);
3136 if (o3tl::make_unsigned(nRow2) < itBlk->size)
3137 return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]);
3139 // Fill the remaining array with values from the following blocks.
3140 size_t nPos = itBlk->size;
3141 ++itBlk;
3142 if (!appendToBlock(&rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
3144 rCxt.discardCachedColArray(nTab, nCol);
3145 return formula::VectorRefArray(formula::VectorRefArray::Invalid);
3148 if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
3149 return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], &(*pColArray->mpStrArray)[nRow1]);
3150 else
3151 return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]);
3153 break;
3154 default:
3158 return formula::VectorRefArray(formula::VectorRefArray::Invalid);
3161 #ifdef DBG_UTIL
3162 static void assertNoInterpretNeededHelper( const sc::CellStoreType::value_type& node,
3163 size_t nOffset, size_t nDataSize )
3165 switch (node.type)
3167 case sc::element_type_formula:
3169 sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
3170 std::advance(it, nOffset);
3171 sc::formula_block::const_iterator itEnd = it;
3172 std::advance(itEnd, nDataSize);
3173 for (; it != itEnd; ++it)
3175 const ScFormulaCell* pCell = *it;
3176 assert( !pCell->NeedsInterpret());
3178 break;
3182 void ScColumn::AssertNoInterpretNeeded( SCROW nRow1, SCROW nRow2 )
3184 assert(nRow2 >= nRow1);
3185 sc::ParseBlock( maCells.begin(), maCells, assertNoInterpretNeededHelper, 0, nRow2 );
3187 #endif
3189 void ScColumn::SetFormulaResults( SCROW nRow, const double* pResults, size_t nLen )
3191 sc::CellStoreType::position_type aPos = maCells.position(nRow);
3192 sc::CellStoreType::iterator it = aPos.first;
3193 if (it->type != sc::element_type_formula)
3195 // This is not a formula block.
3196 assert( false );
3197 return;
3200 size_t nBlockLen = it->size - aPos.second;
3201 if (nBlockLen < nLen)
3203 // Result array is longer than the length of formula cells. Not good.
3204 assert( false );
3205 return;
3208 sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
3209 std::advance(itCell, aPos.second);
3211 const double* pResEnd = pResults + nLen;
3212 for (; pResults != pResEnd; ++pResults, ++itCell)
3214 ScFormulaCell& rCell = **itCell;
3215 FormulaError nErr = GetDoubleErrorValue(*pResults);
3216 if (nErr != FormulaError::NONE)
3217 rCell.SetResultError(nErr);
3218 else
3219 rCell.SetResultDouble(*pResults);
3220 rCell.ResetDirty();
3221 rCell.SetChanged(true);
3225 void ScColumn::CalculateInThread( ScInterpreterContext& rContext, SCROW nRow, size_t nLen, size_t nOffset,
3226 unsigned nThisThread, unsigned nThreadsTotal)
3228 assert(GetDoc().IsThreadedGroupCalcInProgress());
3230 sc::CellStoreType::position_type aPos = maCells.position(nRow);
3231 sc::CellStoreType::iterator it = aPos.first;
3232 if (it->type != sc::element_type_formula)
3234 // This is not a formula block.
3235 assert( false );
3236 return;
3239 size_t nBlockLen = it->size - aPos.second;
3240 if (nBlockLen < nLen)
3242 // Length is longer than the length of formula cells. Not good.
3243 assert( false );
3244 return;
3247 sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
3248 std::advance(itCell, aPos.second);
3250 for (size_t i = 0; i < nLen; ++i, ++itCell)
3252 if (nThreadsTotal > 0 && ((i + nOffset) % nThreadsTotal) != nThisThread)
3253 continue;
3255 ScFormulaCell& rCell = **itCell;
3256 if (!rCell.NeedsInterpret())
3257 continue;
3258 // Here we don't call IncInterpretLevel() and DecInterpretLevel() as this call site is
3259 // always in a threaded calculation.
3260 rCell.InterpretTail(rContext, ScFormulaCell::SCITP_NORMAL);
3264 void ScColumn::HandleStuffAfterParallelCalculation( SCROW nRow, size_t nLen, ScInterpreter* pInterpreter )
3266 sc::CellStoreType::position_type aPos = maCells.position(nRow);
3267 sc::CellStoreType::iterator it = aPos.first;
3268 if (it->type != sc::element_type_formula)
3270 // This is not a formula block.
3271 assert( false );
3272 return;
3275 size_t nBlockLen = it->size - aPos.second;
3276 if (nBlockLen < nLen)
3278 // Length is longer than the length of formula cells. Not good.
3279 assert( false );
3280 return;
3283 sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
3284 std::advance(itCell, aPos.second);
3286 for (size_t i = 0; i < nLen; ++i, ++itCell)
3288 ScFormulaCell& rCell = **itCell;
3289 rCell.HandleStuffAfterParallelCalculation(pInterpreter);
3293 void ScColumn::SetNumberFormat( SCROW nRow, sal_uInt32 nNumberFormat )
3295 ApplyAttr(nRow, SfxUInt32Item(ATTR_VALUE_FORMAT, nNumberFormat));
3298 ScFormulaCell * const * ScColumn::GetFormulaCellBlockAddress( SCROW nRow, size_t& rBlockSize ) const
3300 if (!GetDoc().ValidRow(nRow))
3302 rBlockSize = 0;
3303 return nullptr;
3306 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
3307 sc::CellStoreType::const_iterator it = aPos.first;
3308 if (it == maCells.end())
3310 rBlockSize = 0;
3311 return nullptr;
3314 if (it->type != sc::element_type_formula)
3316 // Not a formula cell.
3317 rBlockSize = 0;
3318 return nullptr;
3321 rBlockSize = it->size;
3322 return &sc::formula_block::at(*it->data, aPos.second);
3325 const ScFormulaCell* ScColumn::FetchFormulaCell( SCROW nRow ) const
3327 size_t nBlockSize = 0;
3328 ScFormulaCell const * const * pp = GetFormulaCellBlockAddress( nRow, nBlockSize );
3329 return pp ? *pp : nullptr;
3332 void ScColumn::FindDataAreaPos(SCROW& rRow, bool bDown) const
3334 // If the cell is empty, find the next non-empty cell position. If the
3335 // cell is not empty, find the last non-empty cell position in the current
3336 // contiguous cell block.
3338 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
3339 sc::CellStoreType::const_iterator it = aPos.first;
3340 if (it == maCells.end())
3341 // Invalid row.
3342 return;
3344 if (it->type == sc::element_type_empty)
3346 // Current cell is empty. Find the next non-empty cell.
3347 rRow = FindNextVisibleRowWithContent(it, rRow, bDown);
3348 return;
3351 // Current cell is not empty.
3352 SCROW nNextRow = FindNextVisibleRow(rRow, bDown);
3353 aPos = maCells.position(it, nNextRow);
3354 it = aPos.first;
3355 if (it->type == sc::element_type_empty)
3357 // Next visible cell is empty. Find the next non-empty cell.
3358 rRow = FindNextVisibleRowWithContent(it, nNextRow, bDown);
3359 return;
3362 // Next visible cell is non-empty. Find the edge that's still visible.
3363 SCROW nLastRow = nNextRow;
3366 nNextRow = FindNextVisibleRow(nLastRow, bDown);
3367 if (nNextRow == nLastRow)
3368 break;
3370 aPos = maCells.position(it, nNextRow);
3371 it = aPos.first;
3372 if (it->type != sc::element_type_empty)
3373 nLastRow = nNextRow;
3375 while (it->type != sc::element_type_empty);
3377 rRow = nLastRow;
3380 bool ScColumn::HasDataAt(SCROW nRow, ScDataAreaExtras* pDataAreaExtras ) const
3382 if (pDataAreaExtras)
3383 GetDataExtrasAt( nRow, *pDataAreaExtras);
3385 return maCells.get_type(nRow) != sc::element_type_empty;
3388 bool ScColumn::HasDataAt( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow,
3389 ScDataAreaExtras* pDataAreaExtras ) const
3391 if (pDataAreaExtras)
3392 GetDataExtrasAt( nRow, *pDataAreaExtras);
3394 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
3395 if (aPos.first == maCells.end())
3396 return false;
3397 rBlockPos.miCellPos = aPos.first; // Store this for next call.
3398 return aPos.first->type != sc::element_type_empty;
3401 bool ScColumn::HasDataAt( sc::ColumnBlockPosition& rBlockPos, SCROW nRow,
3402 ScDataAreaExtras* pDataAreaExtras )
3404 if (pDataAreaExtras)
3405 GetDataExtrasAt( nRow, *pDataAreaExtras);
3407 std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
3408 if (aPos.first == maCells.end())
3409 return false;
3410 rBlockPos.miCellPos = aPos.first; // Store this for next call.
3411 return aPos.first->type != sc::element_type_empty;
3414 void ScColumn::GetDataExtrasAt( SCROW nRow, ScDataAreaExtras& rDataAreaExtras ) const
3416 if (rDataAreaExtras.mnStartRow <= nRow && nRow <= rDataAreaExtras.mnEndRow)
3417 return;
3419 // Check in order of likeliness.
3420 if ( (rDataAreaExtras.mbCellFormats && HasVisibleAttrIn(nRow, nRow)) ||
3421 (rDataAreaExtras.mbCellNotes && !IsNotesEmptyBlock(nRow, nRow)) ||
3422 (rDataAreaExtras.mbCellDrawObjects && !IsDrawObjectsEmptyBlock(nRow, nRow)))
3424 if (rDataAreaExtras.mnStartRow > nRow)
3425 rDataAreaExtras.mnStartRow = nRow;
3426 if (rDataAreaExtras.mnEndRow < nRow)
3427 rDataAreaExtras.mnEndRow = nRow;
3431 namespace {
3433 class FindUsedRowsHandler
3435 typedef mdds::flat_segment_tree<SCROW,bool> UsedRowsType;
3436 UsedRowsType& mrUsed;
3437 UsedRowsType::const_iterator miUsed;
3438 public:
3439 explicit FindUsedRowsHandler(UsedRowsType& rUsed) : mrUsed(rUsed), miUsed(rUsed.begin()) {}
3441 void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
3443 if (node.type == sc::element_type_empty)
3444 return;
3446 SCROW nRow1 = node.position + nOffset;
3447 SCROW nRow2 = nRow1 + nDataSize - 1;
3448 miUsed = mrUsed.insert(miUsed, nRow1, nRow2+1, true).first;
3454 void ScColumn::FindUsed( SCROW nStartRow, SCROW nEndRow, mdds::flat_segment_tree<SCROW,bool>& rUsed ) const
3456 FindUsedRowsHandler aFunc(rUsed);
3457 sc::ParseBlock(maCells.begin(), maCells, aFunc, nStartRow, nEndRow);
3460 namespace {
3462 void startListening(
3463 sc::BroadcasterStoreType& rStore, sc::BroadcasterStoreType::iterator& itBlockPos, size_t nElemPos,
3464 SCROW nRow, SvtListener& rLst)
3466 switch (itBlockPos->type)
3468 case sc::element_type_broadcaster:
3470 // Broadcaster already exists here.
3471 SvtBroadcaster* pBC = sc::broadcaster_block::at(*itBlockPos->data, nElemPos);
3472 rLst.StartListening(*pBC);
3474 break;
3475 case mdds::mtv::element_type_empty:
3477 // No broadcaster exists at this position yet.
3478 SvtBroadcaster* pBC = new SvtBroadcaster;
3479 rLst.StartListening(*pBC);
3480 itBlockPos = rStore.set(itBlockPos, nRow, pBC); // Store the block position for next iteration.
3482 break;
3483 default:
3484 assert(false && "wrong block type encountered in the broadcaster storage.");
3490 void ScColumn::StartListening( SvtListener& rLst, SCROW nRow )
3492 std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(nRow);
3493 startListening(maBroadcasters, aPos.first, aPos.second, nRow, rLst);
3496 void ScColumn::EndListening( SvtListener& rLst, SCROW nRow )
3498 SvtBroadcaster* pBC = GetBroadcaster(nRow);
3499 if (!pBC)
3500 return;
3502 rLst.EndListening(*pBC);
3503 if (!pBC->HasListeners())
3504 { // There is no more listeners for this cell. Remove the broadcaster.
3505 if(GetDoc().IsDelayedDeletingBroadcasters())
3506 mbEmptyBroadcastersPending = true;
3507 else
3508 maBroadcasters.set_empty(nRow, nRow);
3512 void ScColumn::StartListening( sc::StartListeningContext& rCxt, const ScAddress& rAddress, SvtListener& rLst )
3514 if (!GetDoc().ValidRow(rAddress.Row()))
3515 return;
3517 sc::ColumnBlockPosition* p = rCxt.getBlockPosition(rAddress.Tab(), rAddress.Col());
3518 if (!p)
3519 return;
3521 sc::BroadcasterStoreType::iterator& it = p->miBroadcasterPos;
3522 std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(it, rAddress.Row());
3523 it = aPos.first; // store the block position for next iteration.
3524 startListening(maBroadcasters, it, aPos.second, rAddress.Row(), rLst);
3527 void ScColumn::EndListening( sc::EndListeningContext& rCxt, const ScAddress& rAddress, SvtListener& rListener )
3529 sc::ColumnBlockPosition* p = rCxt.getBlockPosition(rAddress.Tab(), rAddress.Col());
3530 if (!p)
3531 return;
3533 sc::BroadcasterStoreType::iterator& it = p->miBroadcasterPos;
3534 std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(it, rAddress.Row());
3535 it = aPos.first; // store the block position for next iteration.
3536 if (it->type != sc::element_type_broadcaster)
3537 return;
3539 SvtBroadcaster* pBC = sc::broadcaster_block::at(*it->data, aPos.second);
3540 assert(pBC);
3542 rListener.EndListening(*pBC);
3543 if (!pBC->HasListeners())
3544 // There is no more listeners for this cell. Add it to the purge list for later purging.
3545 rCxt.addEmptyBroadcasterPosition(rAddress.Tab(), rAddress.Col(), rAddress.Row());
3548 namespace {
3550 class CompileDBFormulaHandler
3552 sc::CompileFormulaContext& mrCxt;
3554 public:
3555 explicit CompileDBFormulaHandler( sc::CompileFormulaContext& rCxt ) :
3556 mrCxt(rCxt) {}
3558 void operator() (size_t, ScFormulaCell* p)
3560 p->CompileDBFormula(mrCxt);
3564 struct CompileColRowNameFormulaHandler
3566 sc::CompileFormulaContext& mrCxt;
3567 public:
3568 explicit CompileColRowNameFormulaHandler( sc::CompileFormulaContext& rCxt ) : mrCxt(rCxt) {}
3570 void operator() (size_t, ScFormulaCell* p)
3572 p->CompileColRowNameFormula(mrCxt);
3578 void ScColumn::CompileDBFormula( sc::CompileFormulaContext& rCxt )
3580 CompileDBFormulaHandler aFunc(rCxt);
3581 sc::ProcessFormula(maCells, aFunc);
3582 RegroupFormulaCells();
3585 void ScColumn::CompileColRowNameFormula( sc::CompileFormulaContext& rCxt )
3587 CompileColRowNameFormulaHandler aFunc(rCxt);
3588 sc::ProcessFormula(maCells, aFunc);
3589 RegroupFormulaCells();
3592 namespace {
3594 class UpdateSubTotalHandler
3596 ScFunctionData& mrData;
3598 void update(double fVal, bool bVal)
3600 if (mrData.getError())
3601 return;
3603 switch (mrData.getFunc())
3605 case SUBTOTAL_FUNC_CNT2: // everything
3606 mrData.update( fVal);
3607 break;
3608 default: // only numeric values
3609 if (bVal)
3610 mrData.update( fVal);
3614 public:
3615 explicit UpdateSubTotalHandler(ScFunctionData& rData) : mrData(rData) {}
3617 void operator() (size_t /*nRow*/, double fVal)
3619 update(fVal, true);
3622 void operator() (size_t /*nRow*/, const svl::SharedString&)
3624 update(0.0, false);
3627 void operator() (size_t /*nRow*/, const EditTextObject*)
3629 update(0.0, false);
3632 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
3634 double fVal = 0.0;
3635 bool bVal = false;
3636 if (mrData.getFunc() != SUBTOTAL_FUNC_CNT2) // it doesn't interest us
3639 if (pCell->GetErrCode() != FormulaError::NONE)
3641 if (mrData.getFunc() != SUBTOTAL_FUNC_CNT) // simply remove from count
3642 mrData.setError();
3644 else if (pCell->IsValue())
3646 fVal = pCell->GetValue();
3647 bVal = true;
3649 // otherwise text
3652 update(fVal, bVal);
3658 // multiple selections:
3659 void ScColumn::UpdateSelectionFunction(
3660 const ScRangeList& rRanges, ScFunctionData& rData, const ScFlatBoolRowSegments& rHiddenRows )
3662 sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
3663 aSpanSet.scan(rRanges, nTab, nCol); // mark all selected rows.
3665 if (aSpanSet.empty())
3666 return; // nothing to do, bail out
3668 // Exclude all hidden rows.
3669 ScFlatBoolRowSegments::RangeData aRange;
3670 SCROW nRow = 0;
3671 while (nRow <= GetDoc().MaxRow())
3673 if (!rHiddenRows.getRangeData(nRow, aRange))
3674 break;
3676 if (aRange.mbValue)
3677 // Hidden range detected.
3678 aSpanSet.set(nRow, aRange.mnRow2, false);
3680 nRow = aRange.mnRow2 + 1;
3683 sc::SingleColumnSpanSet::SpansType aSpans;
3684 aSpanSet.getSpans(aSpans);
3686 switch (rData.getFunc())
3688 case SUBTOTAL_FUNC_SELECTION_COUNT:
3690 // Simply count selected rows regardless of cell contents.
3691 for (const auto& rSpan : aSpans)
3692 rData.update( rSpan.mnRow2 - rSpan.mnRow1 + 1);
3694 break;
3695 case SUBTOTAL_FUNC_CNT2:
3697 // We need to parse all non-empty cells.
3698 sc::CellStoreType::const_iterator itCellPos = maCells.begin();
3699 UpdateSubTotalHandler aFunc(rData);
3700 for (const auto& rSpan : aSpans)
3702 itCellPos = sc::ParseAllNonEmpty(
3703 itCellPos, maCells, rSpan.mnRow1, rSpan.mnRow2, aFunc);
3706 break;
3707 default:
3709 // We need to parse only numeric values.
3710 sc::CellStoreType::const_iterator itCellPos = maCells.begin();
3711 UpdateSubTotalHandler aFunc(rData);
3712 for (const auto& rSpan : aSpans)
3714 itCellPos = sc::ParseFormulaNumeric(
3715 itCellPos, maCells, rSpan.mnRow1, rSpan.mnRow2, aFunc);
3721 namespace {
3723 class WeightedCounter
3725 size_t mnCount;
3726 public:
3727 WeightedCounter() : mnCount(0) {}
3729 void operator() (const sc::CellStoreType::value_type& node)
3731 mnCount += getWeight(node);
3734 static size_t getWeight(const sc::CellStoreType::value_type& node)
3736 switch (node.type)
3738 case sc::element_type_numeric:
3739 case sc::element_type_string:
3740 return node.size;
3741 case sc::element_type_formula:
3743 // Each formula cell is worth its code length plus 5.
3744 return std::accumulate(sc::formula_block::begin(*node.data), sc::formula_block::end(*node.data), size_t(0),
3745 [](const size_t& rCount, const ScFormulaCell* p) { return rCount + 5 + p->GetCode()->GetCodeLen(); });
3747 case sc::element_type_edittext:
3748 // each edit-text cell is worth 50.
3749 return node.size * 50;
3750 default:
3751 return 0;
3755 size_t getCount() const { return mnCount; }
3758 class WeightedCounterWithRows
3760 const SCROW mnStartRow;
3761 const SCROW mnEndRow;
3762 size_t mnCount;
3764 public:
3765 WeightedCounterWithRows(SCROW nStartRow, SCROW nEndRow)
3766 : mnStartRow(nStartRow)
3767 , mnEndRow(nEndRow)
3768 , mnCount(0)
3772 void operator() (const sc::CellStoreType::value_type& node)
3774 const SCROW nRow1 = node.position;
3775 const SCROW nRow2 = nRow1 + 1;
3777 if ((nRow2 >= mnStartRow) && (nRow1 <= mnEndRow))
3779 mnCount += WeightedCounter::getWeight(node);
3783 size_t getCount() const { return mnCount; }
3788 sal_uInt64 ScColumn::GetWeightedCount() const
3790 const WeightedCounter aFunc = std::for_each(maCells.begin(), maCells.end(),
3791 WeightedCounter());
3792 return aFunc.getCount();
3795 sal_uInt64 ScColumn::GetWeightedCount(SCROW nStartRow, SCROW nEndRow) const
3797 const WeightedCounterWithRows aFunc = std::for_each(maCells.begin(), maCells.end(),
3798 WeightedCounterWithRows(nStartRow, nEndRow));
3799 return aFunc.getCount();
3802 namespace {
3804 class CodeCounter
3806 sal_uInt64 mnCount;
3807 public:
3808 CodeCounter() : mnCount(0) {}
3810 void operator() (size_t, const ScFormulaCell* p)
3812 mnCount += p->GetCode()->GetCodeLen();
3815 sal_uInt64 getCount() const { return mnCount; }
3820 sal_uInt64 ScColumn::GetCodeCount() const
3822 CodeCounter aFunc;
3823 sc::ParseFormula(maCells, aFunc);
3824 return aFunc.getCount();
3827 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */