1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 .
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>
63 #include <vcl/outdev.hxx>
64 #include <formula/errorcodes.hxx>
65 #include <formula/vectortoken.hxx>
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
);
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).
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.
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();
114 pPattern
= pAttrArray
->GetPattern( nRow
);
117 // Do not merge in conditional formatting
119 const ScMergeAttr
* pMerge
= &pPattern
->GetItem(ATTR_MERGE
);
120 const ScMergeFlagAttr
* pFlag
= &pPattern
->GetItem(ATTR_MERGE_FLAG
);
124 if ( pFlag
->IsHorOverlapped() )
126 if ( rOptions
.bSkipMerged
&& pMerge
->GetColMerge() > 1 )
131 if ( pFlag
->IsVerOverlapped() )
133 if ( rOptions
.bSkipMerged
&& pMerge
->GetRowMerge() > 1 )
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
);
146 *ppPatternChange
= pPattern
;
150 const SvxHorJustifyItem
* pCondItem
;
151 SvxCellHorJustify eHorJust
;
152 if (pCondSet
&& (pCondItem
= pCondSet
->GetItemIfSet(ATTR_HOR_JUSTIFY
)) )
153 eHorJust
= pCondItem
->GetValue();
155 eHorJust
= pPattern
->GetItem( ATTR_HOR_JUSTIFY
).GetValue();
157 const ScLineBreakCell
* pLineBreakCell
;
158 if ( eHorJust
== SvxCellHorJustify::Block
)
160 else if ( pCondSet
&& (pLineBreakCell
= pCondSet
->GetItemIfSet(ATTR_LINEBREAK
)) )
161 bBreak
= pLineBreakCell
->GetValue();
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();
177 bCellIsValue
= pFCell
->IsValue();
180 // the pattern may change in IsValue()
181 pPattern
= pAttrArray
->GetPattern( nRow
);
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
);
202 *ppPatternChange
= pPattern
; // XXX caller may have to check for change!
206 if (!bMayInvalidatePattern
|| ScPatternAttr::areSame(pPattern
, aOldPattern
.getScPatternAttr()))
210 nFormat
= pPattern
->GetNumberFormat( rContext
, pCondSet
);
211 if (rContext
.NFGetType(nFormat
) == SvNumFormatType::NUMBER
)
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
)
225 if ( bWidth
&& bBreak
) // after determining bAsianVertical (bBreak may be reset)
228 Degree100
nRotate(0);
229 SvxRotateMode eRotMode
= SVX_ROTATE_MODE_STANDARD
;
230 if ( eOrient
== SvxCellOrientation::Standard
)
232 const ScRotateValueItem
* pRotateValueItem
;
234 (pRotateValueItem
= pCondSet
->GetItemIfSet(ATTR_ROTATE_VALUE
)) )
235 nRotate
= pRotateValueItem
->GetValue();
237 nRotate
= pPattern
->GetItem(ATTR_ROTATE_VALUE
).GetValue();
240 const SvxRotateModeItem
* pRotateModeItem
;
242 (pRotateModeItem
= pCondSet
->GetItemIfSet(ATTR_ROTATE_MODE
)) )
243 eRotMode
= pRotateModeItem
->GetValue();
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
;
257 bAsianVertical
= false;
260 const SvxMarginItem
* pMargin
;
262 (pMargin
= pCondSet
->GetItemIfSet(ATTR_MARGIN
)) )
265 pMargin
= &pPattern
->GetItem(ATTR_MARGIN
);
266 sal_uInt16 nIndent
= 0;
267 if ( eHorJust
== SvxCellHorJustify::Left
)
269 const ScIndentItem
* pIndentItem
;
271 (pIndentItem
= pCondSet
->GetItemIfSet(ATTR_INDENT
)) )
272 nIndent
= pIndentItem
->GetValue();
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
;
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
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();
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
323 w
= pDev
->GetTextWidth(aValStr
);
326 auto TextHeight
= [&, h
= std::optional
<tools::Long
>()]() mutable
329 h
= pDev
->GetTextHeight();
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
);
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
);
348 nWidth
= static_cast<tools::Long
>( TextHeight() / nSinAbs
); //TODO: limit?
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
)
365 else if (bBreak
&& !bWidth
)
366 nWidth
= pDev
->GetTextWidth(aValStr
);
368 // in the common case (height), avoid calling the expensive GetTextWidth
369 nValue
= bWidth
? pDev
->GetTextWidth( aValStr
) : pDev
->GetTextHeight();
375 nValue
+= conditionalScaleFunc(pMargin
->GetLeftMargin(), nPPT
) +
376 conditionalScaleFunc(pMargin
->GetRightMargin(), nPPT
);
378 nValue
+= conditionalScaleFunc(nIndent
, nPPT
);
381 nValue
+= conditionalScaleFunc(pMargin
->GetTopMargin(), nPPT
) +
382 conditionalScaleFunc(pMargin
->GetBottomMargin(), nPPT
);
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() -
395 nDocSize
= (nDocSize
* 9) / 10; // for safety
396 if (nWidth
> nDocSize
)
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
);
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 );
444 double fWidthFactor
= bInPrintTwips
? 1.0 : nPPTX
;
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();
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)
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
,
471 : tools::Long(rZoomX
* nFilterButtonWidthPix
);
473 aPaper
.setWidth( nDocWidth
);
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
));
491 OUString aString
= ScCellFormat::GetString(
492 aCell
, nFormat
, &pColor
, &rContext
, rDocument
, true,
495 if (!aString
.isEmpty())
496 pEngine
->SetTextNewDefaults(aString
, std::move(pSet
));
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
;
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
);
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
);
524 if ( pPattern
->GetRotateDir( pCondSet
) == ScRotateDir::Right
)
525 nWidth
+= static_cast<tools::Long
>( rDocument
.GetRowHeight( nRow
,nTab
) *
526 (bInPrintTwips
? 1.0 : nPPT
) * nCosAbs
/ nSinAbs
);
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
);
537 nValue
= aTextSize
.Width();
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
;
557 sal_uInt32
aTextSize(pEngine
->CalcTextWidth());
558 nValue
= bInPrintTwips
?
559 o3tl::toTwips(aTextSize
, o3tl::Length::mm100
) :
560 pDev
->LogicToPixel(Size(aTextSize
, 0), aHMMMode
).Width();
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
)
575 nValue
+= conditionalScaleFunc(pMargin
->GetLeftMargin(), nPPT
) +
576 conditionalScaleFunc(pMargin
->GetRightMargin(), nPPT
);
578 nValue
+= conditionalScaleFunc(nIndent
, nPPT
);
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
),
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
);
607 // place for Autofilter Button
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
,
615 : tools::Long(rZoomX
* nFilterButtonWidthPix
);
623 class MaxStrLenFinder
627 OUString maMaxLenStr
;
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
638 mnMaxLen
= aStrVal
.getLength();
639 maMaxLenStr
= aStrVal
;
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
)
660 // assign new maximum including its substring
661 if (mnMaxLen
< nMaxLen
)
664 maMaxLenStr
= aStrVal
.subView(nFromIndex
);
669 void checkLength(const ScRefCellValue
& rCell
)
672 OUString aValStr
= ScCellFormat::GetString(
673 rCell
, mnFormat
, &pColor
, nullptr, mrDoc
);
675 if (aValStr
.getLength() <= mnMaxLen
)
678 switch (rCell
.getType())
682 mnMaxLen
= aValStr
.getLength();
683 maMaxLenStr
= aValStr
;
686 case CELLTYPE_STRING
:
687 case CELLTYPE_FORMULA
:
689 checkLineBreak(aValStr
);
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
);
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
);
717 void operator() (size_t /*nRow*/, const ScFormulaCell
* p
)
719 ScRefCellValue
aCell(const_cast<ScFormulaCell
*>(p
));
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.
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
);
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
);
749 ScDocument
& rDocument
= GetDoc();
751 if ( pParam
&& pParam
->mbSimpleText
)
752 { // all the same except for number format
754 const ScPatternAttr
* pPattern
= GetPattern( nRow
);
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
772 // Or again in case there was a leading sep=";" row or two header
774 const ScPatternAttr
* pNextPattern
= GetPattern( ++nRow
);
775 if (!ScPatternAttr::areSame(pNextPattern
, pPattern
))
776 nFormat
= pNextPattern
->GetNumberFormat(rContext
);
780 if (pParam
->mnMaxTextRow
>= 0)
782 ScRefCellValue aCell
= GetCellValue(pParam
->mnMaxTextRow
);
783 aLongStr
= ScCellFormat::GetString(
784 aCell
, nFormat
, &pColor
, &rContext
, rDocument
);
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
);
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
)
817 std::tie(itPos
, nOffset
) = maCells
.position(itPos
, nRow
);
818 if (itPos
->type
== sc::element_type_empty
)
821 nRow
+= itPos
->size
- nOffset
;
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
))
850 sal_uInt16 nTwips
= static_cast<sal_uInt16
>(
851 std::min(nWidth
/ nPPTX
, std::numeric_limits
<sal_uInt16
>::max() / 2.0));
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();
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
;
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() );
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
);
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
919 bool bStdAllowed
= (pPattern
->GetCellOrientation() == SvxCellOrientation::Standard
);
920 bool bStdOnly
= false;
923 bool bBreak
= pPattern
->GetItem(ATTR_LINEBREAK
).GetValue() ||
924 (pPattern
->GetItem( ATTR_HOR_JUSTIFY
).GetValue() ==
925 SvxCellHorJustify::Block
);
928 // conditional formatting: loop all cells
930 !pPattern
->GetItem(ATTR_CONDITIONAL
).GetCondFormatData().empty())
935 // rotated text: loop all cells
936 if ( bStdOnly
&& pPattern
->GetItem(ATTR_ROTATE_VALUE
).GetValue() )
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
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
)
959 nEnd
= nEditPos
; // calculate single
960 bStdAllowed
= false; // will be computed in any case per cell
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
);
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
);
989 nDefHeight
= nLatHeight
= lcl_GetAttribHeight(*pPattern
, ATTR_FONT_HEIGHT
,
990 nOptimalMinRowHeight
);
992 // if everything below is already larger, the loop doesn't have to
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
;
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;
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
)
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
);
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
>(
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
);
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
)) )
1120 SCROW lastDataPos
= GetLastDataPos();
1123 nRow
= rData
.GetNextMarked(nCol
, nRow
, false);
1124 if (!rDocument
.ValidRow(nRow
) || nRow
> lastDataPos
)
1126 nRow
= GetDoc().MaxRow()+1;
1131 it
= maCells
.position(it
, nRow
).first
;
1133 if ( (eType
== sc::element_type_string
|| eType
== sc::element_type_edittext
) &&
1134 !(HasAttrib( nRow
, nRow
, HasAttrFlags::Protected
) &&
1135 rDocument
.IsTabProtected(nTab
)) )
1144 while (GetNextDataPos(nRow
))
1146 it
= maCells
.position(it
, nRow
).first
;
1148 if ( (eType
== sc::element_type_string
|| eType
== sc::element_type_edittext
) &&
1149 !(HasAttrib( nRow
, nRow
, HasAttrFlags::Protected
) &&
1150 rDocument
.IsTabProtected(nTab
)) )
1155 nRow
= GetDoc().MaxRow()+1;
1164 sc::CellStoreType
& mrCells
;
1172 StrEntry(SCROW nRow
, OUString aStr
) : mnRow(nRow
), maStr(std::move(aStr
)) {}
1175 std::vector
<StrEntry
> maStrEntries
;
1178 StrEntries(sc::CellStoreType
& rCells
, ScDocument
* pDoc
) : mrCells(rCells
), mpDoc(pDoc
) {}
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
;
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
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
);
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.
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
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
;
1297 CellCounter() : mnCount(0) {}
1300 const sc::CellStoreType::value_type
& node
, size_t /*nOffset*/, size_t nDataSize
)
1302 if (node
.type
== sc::element_type_empty
)
1305 mnCount
+= nDataSize
;
1308 size_t getCount() const { return mnCount
; }
1313 SCSIZE
ScColumn::VisibleCount( SCROW nStartRow
, SCROW nEndRow
) const
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.
1328 return it
->type
!= sc::element_type_empty
;
1331 bool ScColumn::IsEmptyData(SCROW nStartRow
, SCROW nEndRow
) const
1334 if (maCells
.block_size() == 1 && maCells
.begin()->type
== sc::element_type_empty
)
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.
1343 if (it
->type
!= sc::element_type_empty
)
1344 // Non-empty cell at the start position.
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.
1360 if (it
->type
!= sc::element_type_empty
)
1361 // Non-empty cell at the start position.
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.
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.
1384 // length of this empty block minus the offset.
1385 size_t nThisLen
= it
->size
- aPos
.second
;
1386 return std::min(nThisLen
, nLength
);
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.
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
);
1411 SCROW
ScColumn::GetFirstDataPos() const
1416 sc::CellStoreType::const_iterator it
= maCells
.begin();
1417 if (it
->type
!= sc::element_type_empty
)
1423 SCROW
ScColumn::GetLastDataPos() const
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
)
1453 if (aPos
.first
== maCells
.begin())
1454 // This is the first block, and is empty.
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())
1467 if (it
->type
== sc::element_type_empty
)
1469 if (it
== maCells
.begin())
1470 // No more previous non-empty cell.
1473 rRow
-= aPos
.second
+ 1; // Last row position of the previous block.
1477 // This block is not empty.
1480 // There are preceding cells in this block. Simply move back one cell.
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.
1490 --rRow
; // Move to the last cell of the previous block.
1492 if (it
->type
== sc::element_type_empty
)
1494 // This block is empty.
1495 if (it
== maCells
.begin())
1496 // No more preceding blocks.
1499 // Skip the whole empty block segment.
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())
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
;
1518 if (it
== maCells
.end())
1519 // No more next block.
1522 // Next block exists, and is non-empty.
1526 if (aPos
.second
< it
->size
- 1)
1528 // There are still cells following the current position.
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.
1536 if (it
== maCells
.end())
1537 // No more next block.
1540 if (it
->type
== sc::element_type_empty
)
1542 // Next block is empty. Move to the next block.
1545 if (it
== maCells
.end())
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())
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
)
1570 if (it
== maCells
.end())
1571 // No more next block.
1575 // Trim up rRowEnd next
1576 aPos
= maCells
.position(rRowEnd
);
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
;
1598 SCROW
ScColumn::FindNextVisibleRow(SCROW nRow
, bool bForward
) const
1604 bool bHidden
= GetDoc().RowHidden(nRow
, nTab
, nullptr, &nEndRow
);
1606 return std::min
<SCROW
>(GetDoc().MaxRow(), nEndRow
+ 1);
1613 SCROW nStartRow
= GetDoc().MaxRow();
1614 bool bHidden
= GetDoc().RowHidden(nRow
, nTab
, &nStartRow
);
1616 return std::max
<SCROW
>(0, nStartRow
- 1);
1622 SCROW
ScColumn::FindNextVisibleRowWithContent(
1623 sc::CellStoreType::const_iterator
& itPos
, SCROW nRow
, bool bForward
) const
1625 ScDocument
& rDocument
= GetDoc();
1632 bool bHidden
= rDocument
.RowHidden(nRow
, nTab
, nullptr, &nEndRow
);
1636 if(nRow
>= GetDoc().MaxRow())
1637 return GetDoc().MaxRow();
1640 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(itPos
, nRow
);
1642 if (itPos
== maCells
.end())
1644 return GetDoc().MaxRow();
1646 if (itPos
->type
!= sc::element_type_empty
)
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();
1660 SCROW nStartRow
= GetDoc().MaxRow();
1661 bool bHidden
= rDocument
.RowHidden(nRow
, nTab
, &nStartRow
);
1664 nRow
= nStartRow
- 1;
1669 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(itPos
, nRow
);
1671 if (itPos
== maCells
.end())
1675 if (itPos
->type
!= sc::element_type_empty
)
1678 // Move to the first cell of the current empty block.
1679 nRow
-= aPos
.second
;
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
);
1724 // Move to the next empty blocks.
1726 itCell
= std::find_if(itCell
, maCells
.end(), lIsEmptyType
);
1729 itAttr
= std::find_if(itAttr
, maCellTextAttrs
.end(), lIsEmptyType
);
1734 #if DUMP_COLUMN_STORAGE
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
1750 case sc::element_type_numeric
:
1751 cout
<< " * numeric block (pos=" << rNode
.position
<< ", length=" << rNode
.size
<< ")" << endl
;
1753 case sc::element_type_string
:
1754 cout
<< " * string block (pos=" << rNode
.position
<< ", length=" << rNode
.size
<< ")" << endl
;
1756 case sc::element_type_edittext
:
1757 cout
<< " * edit-text block (pos=" << rNode
.position
<< ", length=" << rNode
.size
<< ")" << endl
;
1759 case sc::element_type_formula
:
1760 dumpFormulaBlock(rNode
);
1762 case sc::element_type_empty
:
1763 cout
<< " * empty block (pos=" << rNode
.position
<< ", length=" << rNode
.size
<< ")" << endl
;
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
);
1787 if (pCell
->GetSharedTopRow() != pCell
->aPos
.Row())
1789 cout
<< " * row " << pCell
->aPos
.Row() << " shared with top row "
1790 << pCell
->GetSharedTopRow() << " with length " << pCell
->GetSharedLength()
1795 SCROW nLen
= pCell
->GetSharedLength();
1796 cout
<< " * group: start=" << pCell
->aPos
.Row() << ", length=" << nLen
<< endl
;
1797 printFormula(pCell
);
1802 for (SCROW i
= 0; i
< nLen
-1; ++i
, ++it
)
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)";
1828 case sc::FormulaResultValue::String
:
1829 cout
<< "'" << aRes
.maString
.getString() << "' (type: string)";
1831 case sc::FormulaResultValue::Error
:
1832 cout
<< "error (" << static_cast<int>(aRes
.mnError
) << ")";
1834 case sc::FormulaResultValue::Invalid
:
1842 void printResult(const ScFormulaCell
*) const
1844 (void) this; /* loplugin:staticmethods */
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
;
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.
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
;
1887 if (nBlockStart
<= nRowPos
&& nRowPos
< nBlockEnd
)
1888 // This block contains the end row.
1889 rDestCol
.maCellTextAttrs
.set_empty(nBlockStart
+ nOffsetInBlock
, nRowPos
);
1891 rDestCol
.maCellTextAttrs
.set_empty(nBlockStart
+ nOffsetInBlock
, nBlockEnd
-1);
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
);
1912 rDestCol
.maCellTextAttrs
.set(nBlockStart
+ nOffsetInBlock
, itData
, itDataEnd
);
1918 class CopyCellNotesHandler
1920 ScColumn
& mrDestCol
;
1921 sc::CellNoteStoreType
& mrDestNotes
;
1922 sc::CellNoteStoreType::iterator miPos
;
1927 SCROW mnDestOffset
; /// Add this to the source row position to get the destination row.
1928 bool mbCloneCaption
;
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.
1963 ScDrawLayer
*pDrawLayer
= rDestCol
.GetDoc().GetDrawLayer();
1964 bool bWasLocked
= bool();
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
);
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();
2017 struct BroadcasterNoListenersPredicate
2019 bool operator()( size_t, const SvtBroadcaster
* broadcaster
)
2021 return !broadcaster
->HasListeners();
2027 void ScColumn::DeleteEmptyBroadcasters()
2029 if(!mbEmptyBroadcastersPending
)
2031 // Clean up after ScDocument::EnableDelayDeletingBroadcasters().
2032 BroadcasterNoListenersPredicate predicate
;
2033 sc::SetElementsToEmpty1
<sc::broadcaster_block
>( maBroadcasters
, predicate
);
2034 mbEmptyBroadcastersPending
= false;
2042 class DeletingSparklinesHandler
2044 ScDocument
& m_rDocument
;
2048 DeletingSparklinesHandler(ScDocument
& rDocument
, SCTAB nTab
)
2049 : m_rDocument(rDocument
)
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
))
2087 DeletingSparklinesHandler
aFunction(GetDoc(), nTab
);
2088 sc::ParseSparkline(maSparklines
.begin(), maSparklines
, nRow
, nRow
, aFunction
);
2090 maSparklines
.set_empty(nRow
, nRow
);
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())
2101 if (it
->type
!= sc::element_type_empty
)
2104 // start position of next block which is not empty.
2105 SCROW nNextRow
= nStartRow
+ it
->size
- aPos
.second
;
2106 return nEndRow
< nNextRow
;
2112 class CopySparklinesHandler
2114 ScColumn
& mrDestColumn
;
2115 sc::SparklineStoreType
& mrDestSparkline
;
2116 sc::SparklineStoreType::iterator miDestPosition
;
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.
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
2177 for (const auto& rSparkline
: maSparklines
)
2179 if (rSparkline
.type
== sc::element_type_sparkline
)
2180 maxRow
= rSparkline
.position
+ rSparkline
.size
- 1;
2185 SCROW
ScColumn::GetSparklinesMinRow() const
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
;
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
)
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());
2233 class CellNoteHandler
2235 const ScDocument
& m_rDocument
;
2236 const ScAddress m_aAddress
; // 'incomplete' address consisting of tab, column
2237 const bool m_bForgetCaptionOwnership
;
2240 CellNoteHandler(const ScDocument
& rDocument
, const ScAddress
& rPos
, bool bForgetCaptionOwnership
) :
2241 m_rDocument(rDocument
),
2243 m_bForgetCaptionOwnership(bForgetCaptionOwnership
) {}
2245 void operator() ( size_t nRow
, ScPostIt
* p
)
2247 if (m_bForgetCaptionOwnership
)
2250 // Create a 'complete' address object
2251 ScAddress
aAddr(m_aAddress
);
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)
2283 for (const auto& rCellNote
: maCellNotes
)
2285 if (rCellNote
.type
== sc::element_type_cellnote
)
2286 maxRow
= rCellNote
.position
+ rCellNote
.size
-1;
2290 SCROW
ScColumn::GetCellNotesMinRow() const
2292 // hypothesis : the column has cell notes (should be checked before)
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
;
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
)
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
;
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
)
2350 sc::CellTextAttr
& rVal
= *it
;
2351 if (UpdateScriptType(rVal
, nRow
, itrCells
))
2353 nScriptType
|= rVal
.mnScriptType
;
2358 // Skip this whole block.
2359 nRow
+= itPos
->size
- aRet
.second
;
2362 while (nRow
<= nRow2
)
2365 if (itPos
== maCellTextAttrs
.end())
2368 if (itPos
->type
!= sc::element_type_celltextattr
)
2370 // Skip this whole block.
2371 nRow
+= itPos
->size
;
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
)
2382 sc::CellTextAttr
& rVal
= *it
;
2383 if (UpdateScriptType(rVal
, nRow
, itrCells
))
2386 nScriptType
|= rVal
.mnScriptType
;
2391 CellStorageModified();
2396 void ScColumn::SetScriptType( SCROW nRow
, SvtScriptType nType
)
2398 if (!GetDoc().ValidRow(nRow
))
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.
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();
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
);
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
:
2447 // Return a value of 0.0 in all the other cases.
2448 return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0));
2454 class ToMatrixHandler
2460 svl::SharedStringPool
& mrStrPool
;
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
);
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
)
2499 ToMatrixHandler
aFunc(rMat
, nMatCol
, nRow1
, &GetDoc());
2500 sc::ParseAllNonEmpty(maCells
.begin(), maCells
, nRow1
, nRow2
, aFunc
);
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
)
2521 rMat
.PutEmptyResultVector(mnEmpValCount
, nCol
, mnEmpValStart
);
2524 else if (!maNumVals
.empty())
2526 const double* p
= maNumVals
.data();
2527 rMat
.PutDouble(p
, maNumVals
.size(), nCol
, mnNumValStart
);
2530 else if (!maStrVals
.empty())
2532 const svl::SharedString
* p
= maStrVals
.data();
2533 rMat
.PutString(p
, maStrVals
.size(), nCol
, mnStrValStart
);
2540 mnEmpValStart
= mnNumValStart
= mnStrValStart
= 0;
2547 class FillMatrixHandler
2554 svl::SharedStringPool
& mrPool
;
2555 svl::SharedStringPool
* mpPool
; // if matrix is not in the same document
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
;
2568 case sc::element_type_numeric
:
2570 const double* p
= &sc::numeric_block::at(*node
.data
, nOffset
);
2571 mrMat
.PutDouble(p
, nDataSize
, mnMatCol
, nMatRow
);
2574 case sc::element_type_string
:
2578 const svl::SharedString
* p
= &sc::string_block::at(*node
.data
, nOffset
);
2579 mrMat
.PutString(p
, nDataSize
, mnMatCol
, nMatRow
);
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
);
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
);
2606 aSSs
.push_back(mrPool
.intern(aStr
));
2608 aSSs
.push_back(mpPool
->intern(aStr
));
2611 const svl::SharedString
* p
= aSSs
.data();
2612 mrMat
.PutString(p
, nDataSize
, mnMatCol
, nMatRow
);
2615 case sc::element_type_formula
:
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
;
2637 // First empty result.
2638 aBucket
.flush(mrMat
, mnMatCol
);
2639 aBucket
.mnEmpValStart
= nThisRow
- mnTopRow
;
2640 ++aBucket
.mnEmpValCount
;
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
);
2660 aBucket
.flush(mrMat
, mnMatCol
);
2661 aBucket
.mnNumValStart
= nThisRow
- mnTopRow
;
2662 aBucket
.maNumVals
.push_back(fVal
);
2667 svl::SharedString aStr
= rCell
.GetString();
2669 aStr
= mpPool
->intern(aStr
.getString());
2670 if (!aBucket
.maStrVals
.empty() && nThisRow
== nPrevRow
+ 1)
2672 // Secondary strings.
2673 aBucket
.maStrVals
.push_back(aStr
);
2678 aBucket
.flush(mrMat
, mnMatCol
);
2679 aBucket
.mnStrValStart
= nThisRow
- mnTopRow
;
2680 aBucket
.maStrVals
.push_back(aStr
);
2684 aBucket
.flush(mrMat
, mnMatCol
);
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
);
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
;
2718 std::advance(rDataEnd
, rLenRemain
);
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
)
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();
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();
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
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
);
2790 if (aRes
.meType
== sc::FormulaResultValue::String
)
2792 rCxt
.ensureStrArray(rColArray
, nArrayLen
);
2793 (*rColArray
.mpStrArray
)[nPos
] = aRes
.maString
.getData();
2797 rCxt
.ensureNumArray(rColArray
, nArrayLen
);
2798 (*rColArray
.mpNumArray
)[nPos
] = aRes
.mfValue
;
2802 if (bTempDisableProgress
)
2803 pProgress
->Enable();
2806 case sc::element_type_empty
:
2808 if (nLenRemain
> it
->size
)
2811 nLenRemain
-= it
->size
;
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
;
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();
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();
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;
2891 std::advance(itEnd
, nLen
);
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
);
2908 if (aRes
.meType
== sc::FormulaResultValue::Value
)
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
;
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.
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 ); }
2966 formula::VectorRefArray
ScColumn::FetchVectorRefArray( SCROW nRow1
, SCROW 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);
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);
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
;
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
);
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
);
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
;
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
);
3075 return formula::VectorRefArray(pStr
);
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
3084 pColArray
= copyFirstFormulaBlock(rCxt
, itBlk
, nRow2
+1, nTab
, nCol
);
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
);
3102 // Failed to insert a new cached column array.
3103 return formula::VectorRefArray(formula::VectorRefArray::Invalid
);
3106 size_t nPos
= itBlk
->size
;
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
);
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);
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
;
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
]);
3151 return formula::VectorRefArray(&(*pColArray
->mpNumArray
)[nRow1
]);
3158 return formula::VectorRefArray(formula::VectorRefArray::Invalid
);
3162 static void assertNoInterpretNeededHelper( const sc::CellStoreType::value_type
& node
,
3163 size_t nOffset
, size_t nDataSize
)
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());
3182 void ScColumn::AssertNoInterpretNeeded( SCROW nRow1
, SCROW nRow2
)
3184 assert(nRow2
>= nRow1
);
3185 sc::ParseBlock( maCells
.begin(), maCells
, assertNoInterpretNeededHelper
, 0, nRow2
);
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.
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.
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
);
3219 rCell
.SetResultDouble(*pResults
);
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.
3239 size_t nBlockLen
= it
->size
- aPos
.second
;
3240 if (nBlockLen
< nLen
)
3242 // Length is longer than the length of formula cells. Not good.
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
)
3255 ScFormulaCell
& rCell
= **itCell
;
3256 if (!rCell
.NeedsInterpret())
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.
3275 size_t nBlockLen
= it
->size
- aPos
.second
;
3276 if (nBlockLen
< nLen
)
3278 // Length is longer than the length of formula cells. Not good.
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
))
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())
3314 if (it
->type
!= sc::element_type_formula
)
3316 // Not a formula cell.
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())
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
);
3351 // Current cell is not empty.
3352 SCROW nNextRow
= FindNextVisibleRow(rRow
, bDown
);
3353 aPos
= maCells
.position(it
, nNextRow
);
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
);
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
)
3370 aPos
= maCells
.position(it
, nNextRow
);
3372 if (it
->type
!= sc::element_type_empty
)
3373 nLastRow
= nNextRow
;
3375 while (it
->type
!= sc::element_type_empty
);
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())
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())
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
)
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
;
3433 class FindUsedRowsHandler
3435 typedef mdds::flat_segment_tree
<SCROW
,bool> UsedRowsType
;
3436 UsedRowsType
& mrUsed
;
3437 UsedRowsType::const_iterator miUsed
;
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
)
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
);
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
);
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.
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
);
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;
3508 maBroadcasters
.set_empty(nRow
, nRow
);
3512 void ScColumn::StartListening( sc::StartListeningContext
& rCxt
, const ScAddress
& rAddress
, SvtListener
& rLst
)
3514 if (!GetDoc().ValidRow(rAddress
.Row()))
3517 sc::ColumnBlockPosition
* p
= rCxt
.getBlockPosition(rAddress
.Tab(), rAddress
.Col());
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());
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
)
3539 SvtBroadcaster
* pBC
= sc::broadcaster_block::at(*it
->data
, aPos
.second
);
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());
3550 class CompileDBFormulaHandler
3552 sc::CompileFormulaContext
& mrCxt
;
3555 explicit CompileDBFormulaHandler( sc::CompileFormulaContext
& rCxt
) :
3558 void operator() (size_t, ScFormulaCell
* p
)
3560 p
->CompileDBFormula(mrCxt
);
3564 struct CompileColRowNameFormulaHandler
3566 sc::CompileFormulaContext
& mrCxt
;
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();
3594 class UpdateSubTotalHandler
3596 ScFunctionData
& mrData
;
3598 void update(double fVal
, bool bVal
)
3600 if (mrData
.getError())
3603 switch (mrData
.getFunc())
3605 case SUBTOTAL_FUNC_CNT2
: // everything
3606 mrData
.update( fVal
);
3608 default: // only numeric values
3610 mrData
.update( fVal
);
3615 explicit UpdateSubTotalHandler(ScFunctionData
& rData
) : mrData(rData
) {}
3617 void operator() (size_t /*nRow*/, double fVal
)
3622 void operator() (size_t /*nRow*/, const svl::SharedString
&)
3627 void operator() (size_t /*nRow*/, const EditTextObject
*)
3632 void operator() (size_t /*nRow*/, ScFormulaCell
* pCell
)
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
3644 else if (pCell
->IsValue())
3646 fVal
= pCell
->GetValue();
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
;
3671 while (nRow
<= GetDoc().MaxRow())
3673 if (!rHiddenRows
.getRangeData(nRow
, aRange
))
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);
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
);
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
);
3723 class WeightedCounter
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
)
3738 case sc::element_type_numeric
:
3739 case sc::element_type_string
:
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;
3755 size_t getCount() const { return mnCount
; }
3758 class WeightedCounterWithRows
3760 const SCROW mnStartRow
;
3761 const SCROW mnEndRow
;
3765 WeightedCounterWithRows(SCROW nStartRow
, SCROW nEndRow
)
3766 : mnStartRow(nStartRow
)
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(),
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();
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
3823 sc::ParseFormula(maCells
, aFunc
);
3824 return aFunc
.getCount();
3827 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */