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 <docpool.hxx>
26 #include <drwlayer.hxx>
27 #include <attarray.hxx>
28 #include <patattr.hxx>
29 #include <cellform.hxx>
30 #include <stlsheet.hxx>
31 #include <rechead.hxx>
33 #include <editutil.hxx>
34 #include <subtotal.hxx>
35 #include <markdata.hxx>
36 #include <compiler.hxx>
38 #include <fillinfo.hxx>
39 #include <segmenttree.hxx>
40 #include <docparam.hxx>
41 #include <cellvalue.hxx>
42 #include <tokenarray.hxx>
43 #include <globalnames.hxx>
44 #include <formulagroup.hxx>
45 #include <listenercontext.hxx>
46 #include <mtvcellfunc.hxx>
47 #include <progress.hxx>
48 #include <scmatrix.hxx>
49 #include <rowheightcontext.hxx>
50 #include <tokenstringcontext.hxx>
52 #include <editeng/eeitem.hxx>
54 #include <svx/algitem.hxx>
55 #include <editeng/editobj.hxx>
56 #include <editeng/editstat.hxx>
57 #include <editeng/emphasismarkitem.hxx>
58 #include <editeng/fhgtitem.hxx>
59 #include <editeng/forbiddencharacterstable.hxx>
60 #include <svx/rotmodit.hxx>
61 #include <editeng/scripttypeitem.hxx>
62 #include <editeng/unolingu.hxx>
63 #include <editeng/justifyitem.hxx>
64 #include <svl/zforlist.hxx>
65 #include <svl/broadcast.hxx>
66 #include <vcl/outdev.hxx>
67 #include <formula/errorcodes.hxx>
68 #include <formula/vectortoken.hxx>
70 #include <o3tl/make_unique.hxx>
77 // factor from font size to optimal cell height (text width)
78 #define SC_ROT_BREAK_FACTOR 6
80 inline bool IsAmbiguousScript( SvtScriptType nScript
)
82 //TODO: move to a header file
83 return ( nScript
!= SvtScriptType::LATIN
&&
84 nScript
!= SvtScriptType::ASIAN
&&
85 nScript
!= SvtScriptType::COMPLEX
);
90 long ScColumn::GetNeededSize(
91 SCROW nRow
, OutputDevice
* pDev
, double nPPTX
, double nPPTY
,
92 const Fraction
& rZoomX
, const Fraction
& rZoomY
,
93 bool bWidth
, const ScNeededSizeOptions
& rOptions
,
94 const ScPatternAttr
** ppPatternChange
) const
96 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(nRow
);
97 sc::CellStoreType::const_iterator it
= aPos
.first
;
98 if (it
== maCells
.end() || it
->type
== sc::element_type_empty
)
99 // Empty cell, or invalid row.
103 ScRefCellValue aCell
= GetCellValue(it
, aPos
.second
);
104 double nPPT
= bWidth
? nPPTX
: nPPTY
;
106 const ScPatternAttr
* pPattern
= rOptions
.pPattern
;
108 pPattern
= pAttrArray
->GetPattern( nRow
);
111 // Do not merge in conditional formatting
113 const ScMergeAttr
* pMerge
= static_cast<const ScMergeAttr
*>(&pPattern
->GetItem(ATTR_MERGE
));
114 const ScMergeFlagAttr
* pFlag
= static_cast<const ScMergeFlagAttr
*>(&pPattern
->GetItem(ATTR_MERGE_FLAG
));
118 if ( pFlag
->IsHorOverlapped() )
120 if ( rOptions
.bSkipMerged
&& pMerge
->GetColMerge() > 1 )
125 if ( pFlag
->IsVerOverlapped() )
127 if ( rOptions
.bSkipMerged
&& pMerge
->GetRowMerge() > 1 )
131 // conditional formatting
132 const SfxItemSet
* pCondSet
= pDocument
->GetCondResult( nCol
, nRow
, nTab
);
134 //The pPattern may change in GetCondResult
135 if (aCell
.meType
== CELLTYPE_FORMULA
)
137 pPattern
= pAttrArray
->GetPattern( nRow
);
139 *ppPatternChange
= pPattern
;
143 const SfxPoolItem
* pCondItem
;
144 SvxCellHorJustify eHorJust
;
146 pCondSet
->GetItemState(ATTR_HOR_JUSTIFY
, true, &pCondItem
) == SfxItemState::SET
)
147 eHorJust
= static_cast<const SvxHorJustifyItem
*>(pCondItem
)->GetValue();
149 eHorJust
= static_cast<const SvxHorJustifyItem
&>(
150 pPattern
->GetItem( ATTR_HOR_JUSTIFY
)).GetValue();
152 if ( eHorJust
== SvxCellHorJustify::Block
)
154 else if ( pCondSet
&&
155 pCondSet
->GetItemState(ATTR_LINEBREAK
, true, &pCondItem
) == SfxItemState::SET
)
156 bBreak
= static_cast<const SfxBoolItem
*>(pCondItem
)->GetValue();
158 bBreak
= static_cast<const SfxBoolItem
&>(pPattern
->GetItem(ATTR_LINEBREAK
)).GetValue();
160 SvNumberFormatter
* pFormatter
= pDocument
->GetFormatTable();
161 sal_uLong nFormat
= pPattern
->GetNumberFormat( pFormatter
, pCondSet
);
162 // #i111387# disable automatic line breaks only for "General" number format
163 if (bBreak
&& ( nFormat
% SV_COUNTRY_LANGUAGE_OFFSET
) == 0 )
165 // If a formula cell needs to be interpreted during aCell.hasNumeric()
166 // to determine the type, the pattern may get invalidated because the
167 // result may set a number format. In which case there's also the
168 // General format not set anymore..
169 bool bMayInvalidatePattern
= (aCell
.meType
== CELLTYPE_FORMULA
);
170 const ScPatternAttr
* pOldPattern
= pPattern
;
171 bool bNumeric
= aCell
.hasNumeric();
172 if (bMayInvalidatePattern
)
174 pPattern
= pAttrArray
->GetPattern( nRow
);
176 *ppPatternChange
= pPattern
; // XXX caller may have to check for change!
180 if (!bMayInvalidatePattern
|| pPattern
== pOldPattern
)
184 nFormat
= pPattern
->GetNumberFormat( pFormatter
, pCondSet
);
185 if ((nFormat
% SV_COUNTRY_LANGUAGE_OFFSET
) == 0)
191 // get other attributes from pattern and conditional formatting
193 SvxCellOrientation eOrient
= pPattern
->GetCellOrientation( pCondSet
);
194 bool bAsianVertical
= ( eOrient
== SvxCellOrientation::Stacked
&&
195 static_cast<const SfxBoolItem
&>(pPattern
->GetItem( ATTR_VERTICAL_ASIAN
, pCondSet
)).GetValue() );
196 if ( bAsianVertical
)
199 if ( bWidth
&& bBreak
) // after determining bAsianVertical (bBreak may be reset)
203 SvxRotateMode eRotMode
= SVX_ROTATE_MODE_STANDARD
;
204 if ( eOrient
== SvxCellOrientation::Standard
)
207 pCondSet
->GetItemState(ATTR_ROTATE_VALUE
, true, &pCondItem
) == SfxItemState::SET
)
208 nRotate
= static_cast<const SfxInt32Item
*>(pCondItem
)->GetValue();
210 nRotate
=static_cast<const SfxInt32Item
&>(pPattern
->GetItem(ATTR_ROTATE_VALUE
)).GetValue();
214 pCondSet
->GetItemState(ATTR_ROTATE_MODE
, true, &pCondItem
) == SfxItemState::SET
)
215 eRotMode
= static_cast<const SvxRotateModeItem
*>(pCondItem
)->GetValue();
217 eRotMode
= static_cast<const SvxRotateModeItem
&>(
218 pPattern
->GetItem(ATTR_ROTATE_MODE
)).GetValue();
220 if ( nRotate
== 18000 )
221 eRotMode
= SVX_ROTATE_MODE_STANDARD
; // no overflow
225 if ( eHorJust
== SvxCellHorJustify::Repeat
)
227 // ignore orientation/rotation if "repeat" is active
228 eOrient
= SvxCellOrientation::Standard
;
230 bAsianVertical
= false;
233 const SvxMarginItem
* pMargin
;
235 pCondSet
->GetItemState(ATTR_MARGIN
, true, &pCondItem
) == SfxItemState::SET
)
236 pMargin
= static_cast<const SvxMarginItem
*>(pCondItem
);
238 pMargin
= static_cast<const SvxMarginItem
*>(&pPattern
->GetItem(ATTR_MARGIN
));
239 sal_uInt16 nIndent
= 0;
240 if ( eHorJust
== SvxCellHorJustify::Left
)
243 pCondSet
->GetItemState(ATTR_INDENT
, true, &pCondItem
) == SfxItemState::SET
)
244 nIndent
= static_cast<const SfxUInt16Item
*>(pCondItem
)->GetValue();
246 nIndent
= static_cast<const SfxUInt16Item
&>(pPattern
->GetItem(ATTR_INDENT
)).GetValue();
249 SvtScriptType nScript
= pDocument
->GetScriptType(nCol
, nRow
, nTab
);
250 if (nScript
== SvtScriptType::NONE
) nScript
= ScGlobal::GetDefaultScriptType();
252 // also call SetFont for edit cells, because bGetFont may be set only once
253 // bGetFont is set also if script type changes
254 if (rOptions
.bGetFont
)
256 Fraction aFontZoom
= ( eOrient
== SvxCellOrientation::Standard
) ? rZoomX
: rZoomY
;
258 // font color doesn't matter here
259 pPattern
->GetFont( aFont
, SC_AUTOCOL_BLACK
, pDev
, &aFontZoom
, pCondSet
, nScript
);
260 pDev
->SetFont(aFont
);
263 bool bAddMargin
= true;
264 CellType eCellType
= aCell
.meType
;
266 bool bEditEngine
= (eCellType
== CELLTYPE_EDIT
||
267 eOrient
== SvxCellOrientation::Stacked
||
268 IsAmbiguousScript(nScript
) ||
269 ((eCellType
== CELLTYPE_FORMULA
) && aCell
.mpFormula
->IsMultilineResult()));
271 if (!bEditEngine
) // direct output
275 ScCellFormat::GetString(
276 aCell
, nFormat
, aValStr
, &pColor
, *pFormatter
, pDocument
, true, rOptions
.bFormula
);
278 if (!aValStr
.isEmpty())
280 // SetFont is moved up
282 Size
aSize( pDev
->GetTextWidth( aValStr
), pDev
->GetTextHeight() );
283 if ( eOrient
!= SvxCellOrientation::Standard
)
285 long nTemp
= aSize
.Width();
286 aSize
.Width() = aSize
.Height();
287 aSize
.Height() = nTemp
;
291 //TODO: take different X/Y scaling into consideration
293 double nRealOrient
= nRotate
* F_PI18000
; // nRotate is in 1/100 Grad
294 double nCosAbs
= fabs( cos( nRealOrient
) );
295 double nSinAbs
= fabs( sin( nRealOrient
) );
296 long nHeight
= (long)( aSize
.Height() * nCosAbs
+ aSize
.Width() * nSinAbs
);
298 if ( eRotMode
== SVX_ROTATE_MODE_STANDARD
)
299 nWidth
= (long)( aSize
.Width() * nCosAbs
+ aSize
.Height() * nSinAbs
);
300 else if ( rOptions
.bTotalSize
)
302 nWidth
= (long) ( pDocument
->GetColWidth( nCol
,nTab
) * nPPT
);
304 // only to the right:
305 //TODO: differ on direction up/down (only Text/whole height)
306 if ( pPattern
->GetRotateDir( pCondSet
) == ScRotateDir::Right
)
307 nWidth
+= (long)( pDocument
->GetRowHeight( nRow
,nTab
) *
308 nPPT
* nCosAbs
/ nSinAbs
);
311 nWidth
= (long)( aSize
.Height() / nSinAbs
); //TODO: limit?
313 if ( bBreak
&& !rOptions
.bTotalSize
)
315 // limit size for line break
316 long nCmp
= pDev
->GetFont().GetFontSize().Height() * SC_ROT_BREAK_FACTOR
;
317 if ( nHeight
> nCmp
)
321 aSize
= Size( nWidth
, nHeight
);
323 nValue
= bWidth
? aSize
.Width() : aSize
.Height();
329 nValue
+= (long) ( pMargin
->GetLeftMargin() * nPPT
) +
330 (long) ( pMargin
->GetRightMargin() * nPPT
);
332 nValue
+= (long) ( nIndent
* nPPT
);
335 nValue
+= (long) ( pMargin
->GetTopMargin() * nPPT
) +
336 (long) ( pMargin
->GetBottomMargin() * nPPT
);
341 if ( bBreak
&& !bWidth
)
343 // test with EditEngine the safety at 90%
344 // (due to rounding errors and because EditEngine formats partially differently)
346 long nDocPixel
= (long) ( ( pDocument
->GetColWidth( nCol
,nTab
) -
347 pMargin
->GetLeftMargin() - pMargin
->GetRightMargin() -
350 nDocPixel
= (nDocPixel
* 9) / 10; // for safety
351 if ( aSize
.Width() > nDocPixel
)
359 // the font is not reset each time with !bEditEngine
360 vcl::Font aOldFont
= pDev
->GetFont();
362 MapMode
aHMMMode( MapUnit::Map100thMM
, Point(), rZoomX
, rZoomY
);
364 // save in document ?
365 ScFieldEditEngine
* pEngine
= pDocument
->CreateFieldEditEngine();
367 pEngine
->SetUpdateMode( false );
368 bool bTextWysiwyg
= ( pDev
->GetOutDevType() == OUTDEV_PRINTER
);
369 EEControlBits nCtrl
= pEngine
->GetControlWord();
371 nCtrl
|= EEControlBits::FORMAT100
;
373 nCtrl
&= ~EEControlBits::FORMAT100
;
374 pEngine
->SetControlWord( nCtrl
);
375 MapMode aOld
= pDev
->GetMapMode();
376 pDev
->SetMapMode( aHMMMode
);
377 pEngine
->SetRefDevice( pDev
);
378 pDocument
->ApplyAsianEditSettings( *pEngine
);
379 SfxItemSet
* pSet
= new SfxItemSet( pEngine
->GetEmptyItemSet() );
380 if ( ScStyleSheet
* pPreviewStyle
= pDocument
->GetPreviewCellStyle( nCol
, nRow
, nTab
) )
382 std::unique_ptr
<ScPatternAttr
> pPreviewPattern(new ScPatternAttr( *pPattern
));
383 pPreviewPattern
->SetStyleSheet(pPreviewStyle
);
384 pPreviewPattern
->FillEditItemSet( pSet
, pCondSet
);
388 SfxItemSet
* pFontSet
= pDocument
->GetPreviewFont( nCol
, nRow
, nTab
);
389 pPattern
->FillEditItemSet( pSet
, pFontSet
? pFontSet
: pCondSet
);
391 // no longer needed, are set with the text (is faster)
392 // pEngine->SetDefaults( pSet );
394 if ( pSet
->Get(EE_PARA_HYPHENATE
).GetValue() ) {
396 css::uno::Reference
<css::linguistic2::XHyphenator
> xXHyphenator( LinguMgr::GetHyphenator() );
397 pEngine
->SetHyphenator( xXHyphenator
);
400 Size aPaper
= Size( 1000000, 1000000 );
401 if ( eOrient
==SvxCellOrientation::Stacked
&& !bAsianVertical
)
405 double fWidthFactor
= nPPTX
;
408 // if text is formatted for printer, don't use PixelToLogic,
409 // to ensure the exact same paper width (and same line breaks) as in
410 // ScEditUtil::GetEditArea, used for output.
412 fWidthFactor
= HMM_PER_TWIPS
;
415 // use original width for hidden columns:
416 long nDocWidth
= (long) ( pDocument
->GetOriginalWidth(nCol
,nTab
) * fWidthFactor
);
417 SCCOL nColMerge
= pMerge
->GetColMerge();
419 for (SCCOL nColAdd
=1; nColAdd
<nColMerge
; nColAdd
++)
420 nDocWidth
+= (long) ( pDocument
->GetColWidth(nCol
+nColAdd
,nTab
) * fWidthFactor
);
421 nDocWidth
-= (long) ( pMargin
->GetLeftMargin() * fWidthFactor
)
422 + (long) ( pMargin
->GetRightMargin() * fWidthFactor
)
423 + 1; // output size is width-1 pixel (due to gridline)
425 nDocWidth
-= (long) ( nIndent
* fWidthFactor
);
427 // space for AutoFilter button: 20 * nZoom/100
428 if ( pFlag
->HasAutoFilter() && !bTextWysiwyg
)
429 nDocWidth
-= long(rZoomX
*20);
431 aPaper
.Width() = nDocWidth
;
434 aPaper
= pDev
->PixelToLogic( aPaper
, aHMMMode
);
436 pEngine
->SetPaperSize(aPaper
);
438 if (aCell
.meType
== CELLTYPE_EDIT
)
440 pEngine
->SetTextNewDefaults(*aCell
.mpEditText
, pSet
);
446 ScCellFormat::GetString(
447 aCell
, nFormat
, aString
, &pColor
, *pFormatter
, pDocument
, true,
450 if (!aString
.isEmpty())
451 pEngine
->SetTextNewDefaults(aString
, pSet
);
453 pEngine
->SetDefaults(pSet
);
456 bool bEngineVertical
= pEngine
->IsVertical();
457 pEngine
->SetVertical( bAsianVertical
);
458 pEngine
->SetUpdateMode( true );
460 bool bEdWidth
= bWidth
;
461 if ( eOrient
!= SvxCellOrientation::Standard
&& eOrient
!= SvxCellOrientation::Stacked
)
462 bEdWidth
= !bEdWidth
;
465 //TODO: take different X/Y scaling into consideration
467 Size
aSize( pEngine
->CalcTextWidth(), pEngine
->GetTextHeight() );
468 double nRealOrient
= nRotate
* F_PI18000
; // nRotate is in 1/100 Grad
469 double nCosAbs
= fabs( cos( nRealOrient
) );
470 double nSinAbs
= fabs( sin( nRealOrient
) );
471 long nHeight
= (long)( aSize
.Height() * nCosAbs
+ aSize
.Width() * nSinAbs
);
473 if ( eRotMode
== SVX_ROTATE_MODE_STANDARD
)
474 nWidth
= (long)( aSize
.Width() * nCosAbs
+ aSize
.Height() * nSinAbs
);
475 else if ( rOptions
.bTotalSize
)
477 nWidth
= (long) ( pDocument
->GetColWidth( nCol
,nTab
) * nPPT
);
479 if ( pPattern
->GetRotateDir( pCondSet
) == ScRotateDir::Right
)
480 nWidth
+= (long)( pDocument
->GetRowHeight( nRow
,nTab
) *
481 nPPT
* nCosAbs
/ nSinAbs
);
484 nWidth
= (long)( aSize
.Height() / nSinAbs
); //TODO: limit?
485 aSize
= Size( nWidth
, nHeight
);
487 Size aPixSize
= pDev
->LogicToPixel( aSize
, aHMMMode
);
489 nValue
= aPixSize
.Width();
492 nValue
= aPixSize
.Height();
494 if ( bBreak
&& !rOptions
.bTotalSize
)
496 // limit size for line break
497 long nCmp
= aOldFont
.GetFontSize().Height() * SC_ROT_BREAK_FACTOR
;
508 nValue
= pDev
->LogicToPixel(Size( pEngine
->CalcTextWidth(), 0 ),
513 nValue
= pDev
->LogicToPixel(Size( 0, pEngine
->GetTextHeight() ),
516 // With non-100% zoom and several lines or paragraphs, don't shrink below the result with FORMAT100 set
517 if ( !bTextWysiwyg
&& ( rZoomY
.GetNumerator() != 1 || rZoomY
.GetDenominator() != 1 ) &&
518 ( pEngine
->GetParagraphCount() > 1 || ( bBreak
&& pEngine
->GetLineCount(0) > 1 ) ) )
520 pEngine
->SetControlWord( nCtrl
| EEControlBits::FORMAT100
);
521 pEngine
->QuickFormatDoc( true );
522 long nSecondValue
= pDev
->LogicToPixel(Size( 0, pEngine
->GetTextHeight() ), aHMMMode
).Height();
523 if ( nSecondValue
> nValue
)
524 nValue
= nSecondValue
;
528 if ( nValue
&& bAddMargin
)
532 nValue
+= (long) ( pMargin
->GetLeftMargin() * nPPT
) +
533 (long) ( pMargin
->GetRightMargin() * nPPT
);
535 nValue
+= (long) ( nIndent
* nPPT
);
539 nValue
+= (long) ( pMargin
->GetTopMargin() * nPPT
) +
540 (long) ( pMargin
->GetBottomMargin() * nPPT
);
542 if ( bAsianVertical
&& pDev
->GetOutDevType() != OUTDEV_PRINTER
)
544 // add 1pt extra (default margin value) for line breaks with SetVertical
545 nValue
+= (long) ( 20 * nPPT
);
550 // EditEngine is cached and re-used, so the old vertical flag must be restored
551 pEngine
->SetVertical( bEngineVertical
);
553 pDocument
->DisposeFieldEditEngine(pEngine
);
555 pDev
->SetMapMode( aOld
);
556 pDev
->SetFont( aOldFont
);
561 // place for Autofilter Button
563 // Conditional formatting is not interesting here
565 ScMF nFlags
= static_cast<const ScMergeFlagAttr
&>(pPattern
->GetItem(ATTR_MERGE_FLAG
)).GetValue();
566 if (nFlags
& ScMF::Auto
)
567 nValue
+= long(rZoomX
*20);
574 class MaxStrLenFinder
578 OUString maMaxLenStr
;
581 void checkLength(ScRefCellValue
& rCell
)
585 ScCellFormat::GetString(
586 rCell
, mnFormat
, aValStr
, &pColor
, *mrDoc
.GetFormatTable(), &mrDoc
);
588 if (aValStr
.getLength() > mnMaxLen
)
590 mnMaxLen
= aValStr
.getLength();
591 maMaxLenStr
= aValStr
;
596 MaxStrLenFinder(ScDocument
& rDoc
, sal_uInt32 nFormat
) :
597 mrDoc(rDoc
), mnFormat(nFormat
), mnMaxLen(0) {}
599 void operator() (size_t /*nRow*/, double f
)
601 ScRefCellValue
aCell(f
);
605 void operator() (size_t /*nRow*/, const svl::SharedString
& rSS
)
607 if (rSS
.getLength() > mnMaxLen
)
609 mnMaxLen
= rSS
.getLength();
610 maMaxLenStr
= rSS
.getString();
614 void operator() (size_t /*nRow*/, const EditTextObject
* p
)
616 ScRefCellValue
aCell(p
);
620 void operator() (size_t /*nRow*/, const ScFormulaCell
* p
)
622 ScRefCellValue
aCell(const_cast<ScFormulaCell
*>(p
));
626 const OUString
& getMaxLenStr() const { return maMaxLenStr
; }
631 sal_uInt16
ScColumn::GetOptimalColWidth(
632 OutputDevice
* pDev
, double nPPTX
, double nPPTY
, const Fraction
& rZoomX
, const Fraction
& rZoomY
,
633 bool bFormula
, sal_uInt16 nOldWidth
, const ScMarkData
* pMarkData
, const ScColWidthParam
* pParam
) const
635 if (maCells
.block_size() == 1 && maCells
.begin()->type
== sc::element_type_empty
)
636 // All cells are empty.
639 sc::SingleColumnSpanSet aSpanSet
;
640 sc::SingleColumnSpanSet::SpansType aMarkedSpans
;
641 if (pMarkData
&& (pMarkData
->IsMarked() || pMarkData
->IsMultiMarked()))
643 aSpanSet
.scan(*pMarkData
, nTab
, nCol
);
644 aSpanSet
.getSpans(aMarkedSpans
);
647 // "Select" the entire column if no selection exists.
648 aMarkedSpans
.emplace_back(0, MAXROW
);
650 sal_uInt16 nWidth
= static_cast<sal_uInt16
>(nOldWidth
*nPPTX
);
653 if ( pParam
&& pParam
->mbSimpleText
)
654 { // all the same except for number format
655 const ScPatternAttr
* pPattern
= GetPattern( 0 );
657 // font color doesn't matter here
658 pPattern
->GetFont( aFont
, SC_AUTOCOL_BLACK
, pDev
, &rZoomX
);
659 pDev
->SetFont( aFont
);
660 const SvxMarginItem
* pMargin
= static_cast<const SvxMarginItem
*>(&pPattern
->GetItem(ATTR_MARGIN
));
661 long nMargin
= (long) ( pMargin
->GetLeftMargin() * nPPTX
) +
662 (long) ( pMargin
->GetRightMargin() * nPPTX
);
664 // Try to find the row that has the longest string, and measure the width of that string.
665 SvNumberFormatter
* pFormatter
= pDocument
->GetFormatTable();
666 sal_uInt32 nFormat
= pPattern
->GetNumberFormat( pFormatter
);
669 if (pParam
->mnMaxTextRow
>= 0)
671 ScRefCellValue aCell
= GetCellValue(pParam
->mnMaxTextRow
);
672 ScCellFormat::GetString(
673 aCell
, nFormat
, aLongStr
, &pColor
, *pFormatter
, pDocument
);
677 // Go though all non-empty cells within selection.
678 MaxStrLenFinder
aFunc(*pDocument
, nFormat
);
679 sc::CellStoreType::const_iterator itPos
= maCells
.begin();
680 sc::SingleColumnSpanSet::SpansType::const_iterator it
= aMarkedSpans
.begin(), itEnd
= aMarkedSpans
.end();
681 for (; it
!= itEnd
; ++it
)
682 itPos
= sc::ParseAllNonEmpty(itPos
, maCells
, it
->mnRow1
, it
->mnRow2
, aFunc
);
684 aLongStr
= aFunc
.getMaxLenStr();
687 if (!aLongStr
.isEmpty())
689 nWidth
= pDev
->GetTextWidth(aLongStr
) + static_cast<sal_uInt16
>(nMargin
);
695 ScNeededSizeOptions aOptions
;
696 aOptions
.bFormula
= bFormula
;
697 const ScPatternAttr
* pOldPattern
= nullptr;
699 // Go though all non-empty cells within selection.
700 sc::CellStoreType::const_iterator itPos
= maCells
.begin();
701 sc::SingleColumnSpanSet::SpansType::const_iterator it
= aMarkedSpans
.begin(), itEnd
= aMarkedSpans
.end();
702 for (; it
!= itEnd
; ++it
)
704 SCROW nRow1
= it
->mnRow1
, nRow2
= it
->mnRow2
;
706 while (nRow
<= nRow2
)
708 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(itPos
, nRow
);
710 if (itPos
->type
== sc::element_type_empty
)
713 nRow
+= itPos
->size
- aPos
.second
;
717 for (size_t nOffset
= aPos
.second
; nOffset
< itPos
->size
; ++nOffset
, ++nRow
)
719 SvtScriptType nScript
= pDocument
->GetScriptType(nCol
, nRow
, nTab
);
720 if (nScript
== SvtScriptType::NONE
)
721 nScript
= ScGlobal::GetDefaultScriptType();
723 const ScPatternAttr
* pPattern
= GetPattern(nRow
);
724 aOptions
.pPattern
= pPattern
;
725 aOptions
.bGetFont
= (pPattern
!= pOldPattern
|| nScript
!= SvtScriptType::NONE
);
726 pOldPattern
= pPattern
;
727 sal_uInt16 nThis
= (sal_uInt16
) GetNeededSize(
728 nRow
, pDev
, nPPTX
, nPPTY
, rZoomX
, rZoomY
, true, aOptions
, &pOldPattern
);
731 if (nThis
> nWidth
|| !bFound
)
745 sal_uInt16 nTwips
= (sal_uInt16
) (nWidth
/ nPPTX
);
752 static sal_uInt16
lcl_GetAttribHeight( const ScPatternAttr
& rPattern
, sal_uInt16 nFontHeightId
)
754 const SvxFontHeightItem
& rFontHeight
=
755 static_cast<const SvxFontHeightItem
&>(rPattern
.GetItem(nFontHeightId
));
757 sal_uInt16 nHeight
= rFontHeight
.GetHeight();
760 if ( static_cast<const SvxEmphasisMarkItem
&>(rPattern
.
761 GetItem(ATTR_FONT_EMPHASISMARK
)).GetEmphasisMark() != FontEmphasisMark::NONE
)
763 // add height for emphasis marks
764 //TODO: font metrics should be used instead
765 nHeight
+= nHeight
/ 4;
768 const SvxMarginItem
& rMargin
=
769 static_cast<const SvxMarginItem
&>(rPattern
.GetItem(ATTR_MARGIN
));
771 nHeight
+= rMargin
.GetTopMargin() + rMargin
.GetBottomMargin();
773 if (nHeight
> STD_ROWHEIGHT_DIFF
)
774 nHeight
-= STD_ROWHEIGHT_DIFF
;
776 if (nHeight
< ScGlobal::nStdRowHeight
)
777 nHeight
= ScGlobal::nStdRowHeight
;
783 // optimize nMinHeight, nMinStart : with nRow >= nMinStart is at least nMinHeight
784 // (is only evaluated with bStdAllowed)
786 void ScColumn::GetOptimalHeight(
787 sc::RowHeightContext
& rCxt
, SCROW nStartRow
, SCROW nEndRow
, sal_uInt16 nMinHeight
, SCROW nMinStart
)
789 ScFlatUInt16RowSegments
& rHeights
= rCxt
.getHeightArray();
790 ScAttrIterator
aIter( pAttrArray
, nStartRow
, nEndRow
, pDocument
->GetDefPattern() );
797 // with conditional formatting, always consider the individual cells
799 const ScPatternAttr
* pPattern
= aIter
.Next(nStart
,nEnd
);
802 const ScMergeAttr
* pMerge
= static_cast<const ScMergeAttr
*>(&pPattern
->GetItem(ATTR_MERGE
));
803 const ScMergeFlagAttr
* pFlag
= static_cast<const ScMergeFlagAttr
*>(&pPattern
->GetItem(ATTR_MERGE_FLAG
));
804 if ( pMerge
->GetRowMerge() > 1 || pFlag
->IsOverlapped() )
806 // do nothing - vertically with merged and overlapping,
807 // horizontally only with overlapped (invisible) -
808 // only one horizontal merged is always considered
812 bool bStdAllowed
= (pPattern
->GetCellOrientation() == SvxCellOrientation::Standard
);
813 bool bStdOnly
= false;
816 bool bBreak
= static_cast<const SfxBoolItem
&>(pPattern
->GetItem(ATTR_LINEBREAK
)).GetValue() ||
817 (static_cast<const SvxHorJustifyItem
&>(pPattern
->
818 GetItem( ATTR_HOR_JUSTIFY
)).GetValue() ==
819 SvxCellHorJustify::Block
);
822 // conditional formatting: loop all cells
824 !static_cast<const ScCondFormatItem
&>(pPattern
->GetItem(
825 ATTR_CONDITIONAL
)).GetCondFormatData().empty())
830 // rotated text: loop all cells
831 if ( bStdOnly
&& static_cast<const SfxInt32Item
&>(pPattern
->
832 GetItem(ATTR_ROTATE_VALUE
)).GetValue() )
838 bool bHasEditCells
= HasEditCells(nStart
,nEnd
,nEditPos
);
839 // Call to HasEditCells() may change pattern due to
840 // calculation, => sync always.
841 // We don't know which row changed first, but as pPattern
842 // covered nStart to nEnd we can pick nStart. Worst case we
843 // have to repeat that for every row in range if every row
845 pPattern
= aIter
.Resync( nStart
, nStart
, nEnd
);
846 if (bHasEditCells
&& nEnd
< nEditPos
)
847 bHasEditCells
= false; // run into that again
848 if (bHasEditCells
) // includes mixed script types
850 if (nEditPos
== nStart
)
855 nEnd
= nEditPos
; // calculate single
856 bStdAllowed
= false; // will be computed in any case per cell
861 nEnd
= nEditPos
- 1; // standard - part
866 sc::SingleColumnSpanSet aSpanSet
;
867 aSpanSet
.scan(*this, nStart
, nEnd
);
868 sc::SingleColumnSpanSet::SpansType aSpans
;
869 aSpanSet
.getSpans(aSpans
);
873 sal_uInt16 nLatHeight
= 0;
874 sal_uInt16 nCjkHeight
= 0;
875 sal_uInt16 nCtlHeight
= 0;
876 sal_uInt16 nDefHeight
;
877 SvtScriptType nDefScript
= ScGlobal::GetDefaultScriptType();
878 if ( nDefScript
== SvtScriptType::ASIAN
)
879 nDefHeight
= nCjkHeight
= lcl_GetAttribHeight( *pPattern
, ATTR_CJK_FONT_HEIGHT
);
880 else if ( nDefScript
== SvtScriptType::COMPLEX
)
881 nDefHeight
= nCtlHeight
= lcl_GetAttribHeight( *pPattern
, ATTR_CTL_FONT_HEIGHT
);
883 nDefHeight
= nLatHeight
= lcl_GetAttribHeight( *pPattern
, ATTR_FONT_HEIGHT
);
885 // if everything below is already larger, the loop doesn't have to
887 SCROW nStdEnd
= nEnd
;
888 if ( nDefHeight
<= nMinHeight
&& nStdEnd
>= nMinStart
)
889 nStdEnd
= (nMinStart
>0) ? nMinStart
-1 : 0;
891 if (nStart
<= nStdEnd
)
892 rHeights
.setValueIf(nStart
, nStdEnd
, nDefHeight
, [=](sal_uInt16 nRowHeight
){ return nDefHeight
> nRowHeight
; });
896 // if cells are not handled individually below,
897 // check for cells with different script type
898 sc::CellTextAttrStoreType::iterator itAttr
= maCellTextAttrs
.begin();
899 sc::SingleColumnSpanSet::SpansType::const_iterator it
= aSpans
.begin(), itEnd
= aSpans
.end();
900 sc::CellStoreType::iterator itCells
= maCells
.begin();
901 for (; it
!= itEnd
; ++it
)
903 for (SCROW nRow
= it
->mnRow1
; nRow
<= it
->mnRow2
; ++nRow
)
905 SvtScriptType nScript
= GetRangeScriptType(itAttr
, nRow
, nRow
, itCells
);
906 if (nScript
== nDefScript
)
909 if ( nScript
== SvtScriptType::ASIAN
)
911 if ( nCjkHeight
== 0 )
912 nCjkHeight
= lcl_GetAttribHeight( *pPattern
, ATTR_CJK_FONT_HEIGHT
);
913 if (nCjkHeight
> rHeights
.getValue(nRow
))
914 rHeights
.setValue(nRow
, nRow
, nCjkHeight
);
916 else if ( nScript
== SvtScriptType::COMPLEX
)
918 if ( nCtlHeight
== 0 )
919 nCtlHeight
= lcl_GetAttribHeight( *pPattern
, ATTR_CTL_FONT_HEIGHT
);
920 if (nCtlHeight
> rHeights
.getValue(nRow
))
921 rHeights
.setValue(nRow
, nRow
, nCtlHeight
);
925 if ( nLatHeight
== 0 )
926 nLatHeight
= lcl_GetAttribHeight( *pPattern
, ATTR_FONT_HEIGHT
);
927 if (nLatHeight
> rHeights
.getValue(nRow
))
928 rHeights
.setValue(nRow
, nRow
, nLatHeight
);
935 if (!bStdOnly
) // search covered cells
937 ScNeededSizeOptions aOptions
;
939 sc::SingleColumnSpanSet::SpansType::const_iterator it
= aSpans
.begin(), itEnd
= aSpans
.end();
940 for (; it
!= itEnd
; ++it
)
942 for (SCROW nRow
= it
->mnRow1
; nRow
<= it
->mnRow2
; ++nRow
)
944 // only calculate the cell height when it's used later (#37928#)
946 if (rCxt
.isForceAutoSize() || !(pDocument
->GetRowFlags(nRow
, nTab
) & CRFlags::ManualSize
) )
948 aOptions
.pPattern
= pPattern
;
949 const ScPatternAttr
* pOldPattern
= pPattern
;
950 sal_uInt16 nHeight
= (sal_uInt16
)
951 ( GetNeededSize( nRow
, rCxt
.getOutputDevice(), rCxt
.getPPTX(), rCxt
.getPPTY(),
952 rCxt
.getZoomX(), rCxt
.getZoomY(), false, aOptions
,
953 &pPattern
) / rCxt
.getPPTY() );
954 if (nHeight
> rHeights
.getValue(nRow
))
955 rHeights
.setValue(nRow
, nRow
, nHeight
);
956 // Pattern changed due to calculation? => sync.
957 if (pPattern
!= pOldPattern
)
959 pPattern
= aIter
.Resync( nRow
, nStart
, nEnd
);
975 pPattern
= aIter
.Next(nStart
,nEnd
);
979 bool ScColumn::GetNextSpellingCell(SCROW
& nRow
, bool bInSel
, const ScMarkData
& rData
) const
982 sc::CellStoreType::const_iterator it
= maCells
.position(nRow
).first
;
983 mdds::mtv::element_t eType
= it
->type
;
984 if (!bInSel
&& it
!= maCells
.end() && eType
!= sc::element_type_empty
)
986 if ( (eType
== sc::element_type_string
|| eType
== sc::element_type_edittext
) &&
987 !(HasAttrib( nRow
, nRow
, HasAttrFlags::Protected
) &&
988 pDocument
->IsTabProtected(nTab
)) )
995 nRow
= rData
.GetNextMarked(nCol
, nRow
, false);
1003 it
= maCells
.position(it
, nRow
).first
;
1005 if ( (eType
== sc::element_type_string
|| eType
== sc::element_type_edittext
) &&
1006 !(HasAttrib( nRow
, nRow
, HasAttrFlags::Protected
) &&
1007 pDocument
->IsTabProtected(nTab
)) )
1013 else if (GetNextDataPos(nRow
))
1015 it
= maCells
.position(it
, nRow
).first
;
1017 if ( (eType
== sc::element_type_string
|| eType
== sc::element_type_edittext
) &&
1018 !(HasAttrib( nRow
, nRow
, HasAttrFlags::Protected
) &&
1019 pDocument
->IsTabProtected(nTab
)) )
1037 sc::CellStoreType
& mrCells
;
1045 StrEntry(SCROW nRow
, const OUString
& rStr
) : mnRow(nRow
), maStr(rStr
) {}
1048 std::vector
<StrEntry
> maStrEntries
;
1051 StrEntries(sc::CellStoreType
& rCells
, ScDocument
* pDoc
) : mrCells(rCells
), mpDoc(pDoc
) {}
1054 void commitStrings()
1056 svl::SharedStringPool
& rPool
= mpDoc
->GetSharedStringPool();
1057 sc::CellStoreType::iterator it
= mrCells
.begin();
1058 std::vector
<StrEntry
>::iterator itStr
= maStrEntries
.begin(), itStrEnd
= maStrEntries
.end();
1059 for (; itStr
!= itStrEnd
; ++itStr
)
1060 it
= mrCells
.set(it
, itStr
->mnRow
, rPool
.intern(itStr
->maStr
));
1064 class RemoveEditAttribsHandler
: public StrEntries
1066 std::unique_ptr
<ScFieldEditEngine
> mpEngine
;
1069 RemoveEditAttribsHandler(sc::CellStoreType
& rCells
, ScDocument
* pDoc
) : StrEntries(rCells
, pDoc
) {}
1071 void operator() (size_t nRow
, EditTextObject
*& pObj
)
1073 // For the test on hard formatting (ScEditAttrTester), are the defaults in the
1074 // EditEngine of no importance. When the tester would later recognise the same
1075 // attributes in default and hard formatting and has to remove them, the correct
1076 // defaults must be set in the EditEngine for each cell.
1078 // test for attributes
1081 mpEngine
.reset(new ScFieldEditEngine(mpDoc
, mpDoc
->GetEditPool()));
1082 // EEControlBits::ONLINESPELLING if there are errors already
1083 mpEngine
->SetControlWord(mpEngine
->GetControlWord() | EEControlBits::ONLINESPELLING
);
1084 mpDoc
->ApplyAsianEditSettings(*mpEngine
);
1086 mpEngine
->SetText(*pObj
);
1087 sal_Int32 nParCount
= mpEngine
->GetParagraphCount();
1088 for (sal_Int32 nPar
=0; nPar
<nParCount
; nPar
++)
1090 mpEngine
->RemoveCharAttribs(nPar
);
1091 const SfxItemSet
& rOld
= mpEngine
->GetParaAttribs(nPar
);
1094 SfxItemSet
aNew( *rOld
.GetPool(), rOld
.GetRanges() ); // empty
1095 mpEngine
->SetParaAttribs( nPar
, aNew
);
1098 // change URL field to text (not possible otherwise, thus pType=0)
1099 mpEngine
->RemoveFields();
1101 bool bSpellErrors
= mpEngine
->HasOnlineSpellErrors();
1102 bool bNeedObject
= bSpellErrors
|| nParCount
>1; // keep errors/paragraphs
1103 // ScEditAttrTester is not needed anymore, arrays are gone
1105 if (bNeedObject
) // remains edit cell
1107 EEControlBits nCtrl
= mpEngine
->GetControlWord();
1108 EEControlBits nWantBig
= bSpellErrors
? EEControlBits::ALLOWBIGOBJS
: EEControlBits::NONE
;
1109 if ( ( nCtrl
& EEControlBits::ALLOWBIGOBJS
) != nWantBig
)
1110 mpEngine
->SetControlWord( (nCtrl
& ~EEControlBits::ALLOWBIGOBJS
) | nWantBig
);
1112 // Overwrite the existing object.
1114 pObj
= mpEngine
->CreateTextObject();
1116 else // create String
1118 // Store the string replacement for later commits.
1119 OUString aText
= ScEditUtil::GetSpaceDelimitedString(*mpEngine
);
1120 maStrEntries
.emplace_back(nRow
, aText
);
1125 class TestTabRefAbsHandler
1130 explicit TestTabRefAbsHandler(SCTAB nTab
) : mnTab(nTab
), mbTestResult(false) {}
1132 void operator() (size_t /*nRow*/, const ScFormulaCell
* pCell
)
1134 if (const_cast<ScFormulaCell
*>(pCell
)->TestTabRefAbs(mnTab
))
1135 mbTestResult
= true;
1138 bool getTestResult() const { return mbTestResult
; }
1143 void ScColumn::RemoveEditAttribs( SCROW nStartRow
, SCROW nEndRow
)
1145 RemoveEditAttribsHandler
aFunc(maCells
, pDocument
);
1146 sc::ProcessEditText(maCells
.begin(), maCells
, nStartRow
, nEndRow
, aFunc
);
1147 aFunc
.commitStrings();
1150 bool ScColumn::TestTabRefAbs(SCTAB nTable
) const
1152 TestTabRefAbsHandler
aFunc(nTable
);
1153 sc::ParseFormula(maCells
, aFunc
);
1154 return aFunc
.getTestResult();
1157 bool ScColumn::IsEmptyData() const
1159 return maCells
.block_size() == 1 && maCells
.begin()->type
== sc::element_type_empty
;
1168 CellCounter() : mnCount(0) {}
1171 const sc::CellStoreType::value_type
& node
, size_t /*nOffset*/, size_t nDataSize
)
1173 if (node
.type
== sc::element_type_empty
)
1176 mnCount
+= nDataSize
;
1179 size_t getCount() const { return mnCount
; }
1184 SCSIZE
ScColumn::VisibleCount( SCROW nStartRow
, SCROW nEndRow
) const
1187 sc::ParseBlock(maCells
.begin(), maCells
, aFunc
, nStartRow
, nEndRow
);
1188 return aFunc
.getCount();
1191 bool ScColumn::HasVisibleDataAt(SCROW nRow
) const
1193 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(nRow
);
1194 sc::CellStoreType::const_iterator it
= aPos
.first
;
1195 if (it
== maCells
.end())
1196 // Likely invalid row number.
1199 return it
->type
!= sc::element_type_empty
;
1202 bool ScColumn::IsEmptyAttr() const
1205 return pAttrArray
->IsEmpty();
1210 bool ScColumn::IsEmptyBlock(SCROW nStartRow
, SCROW nEndRow
) const
1212 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(nStartRow
);
1213 sc::CellStoreType::const_iterator it
= aPos
.first
;
1214 if (it
== maCells
.end())
1215 // Invalid row number.
1218 if (it
->type
!= sc::element_type_empty
)
1219 // Non-empty cell at the start position.
1222 // start position of next block which is not empty.
1223 SCROW nNextRow
= nStartRow
+ it
->size
- aPos
.second
;
1224 return nEndRow
< nNextRow
;
1227 bool ScColumn::IsNotesEmptyBlock(SCROW nStartRow
, SCROW nEndRow
) const
1229 std::pair
<sc::CellNoteStoreType::const_iterator
,size_t> aPos
= maCellNotes
.position(nStartRow
);
1230 sc::CellNoteStoreType::const_iterator it
= aPos
.first
;
1231 if (it
== maCellNotes
.end())
1232 // Invalid row number.
1235 if (it
->type
!= sc::element_type_empty
)
1236 // Non-empty cell at the start position.
1239 // start position of next block which is not empty.
1240 SCROW nNextRow
= nStartRow
+ it
->size
- aPos
.second
;
1241 return nEndRow
< nNextRow
;
1244 SCSIZE
ScColumn::GetEmptyLinesInBlock( SCROW nStartRow
, SCROW nEndRow
, ScDirection eDir
) const
1246 // Given a range of rows, find a top or bottom empty segment. Skip the start row.
1251 // Determine the length of empty head segment.
1252 size_t nLength
= nEndRow
- nStartRow
;
1253 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(nStartRow
);
1254 sc::CellStoreType::const_iterator it
= aPos
.first
;
1255 if (it
->type
!= sc::element_type_empty
)
1256 // First row is already not empty.
1259 // length of this empty block minus the offset.
1260 size_t nThisLen
= it
->size
- aPos
.second
;
1261 return std::min(nThisLen
, nLength
);
1266 // Determine the length of empty tail segment.
1267 size_t nLength
= nEndRow
- nStartRow
;
1268 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(nEndRow
);
1269 sc::CellStoreType::const_iterator it
= aPos
.first
;
1270 if (it
->type
!= sc::element_type_empty
)
1271 // end row is already not empty.
1274 // length of this empty block from the tip to the end row position.
1275 size_t nThisLen
= aPos
.second
+ 1;
1276 return std::min(nThisLen
, nLength
);
1286 SCROW
ScColumn::GetFirstDataPos() const
1291 sc::CellStoreType::const_iterator it
= maCells
.begin();
1292 if (it
->type
!= sc::element_type_empty
)
1298 SCROW
ScColumn::GetLastDataPos() const
1303 sc::CellStoreType::const_reverse_iterator it
= maCells
.rbegin();
1304 if (it
->type
!= sc::element_type_empty
)
1307 return MAXROW
- static_cast<SCROW
>(it
->size
);
1310 SCROW
ScColumn::GetLastDataPos( SCROW nLastRow
) const
1312 sc::CellStoreType::const_position_type aPos
= maCells
.position(nLastRow
);
1313 if (aPos
.first
->type
!= sc::element_type_empty
)
1316 if (aPos
.first
== maCells
.begin())
1317 // This is the first block, and is empty.
1320 return static_cast<SCROW
>(aPos
.first
->position
- 1);
1323 bool ScColumn::GetPrevDataPos(SCROW
& rRow
) const
1325 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(rRow
);
1326 sc::CellStoreType::const_iterator it
= aPos
.first
;
1327 if (it
== maCells
.end())
1330 if (it
->type
== sc::element_type_empty
)
1332 if (it
== maCells
.begin())
1333 // No more previous non-empty cell.
1336 rRow
-= aPos
.second
+ 1; // Last row position of the previous block.
1340 // This block is not empty.
1343 // There are preceding cells in this block. Simply move back one cell.
1348 // This is the first cell in an non-empty block. Move back to the previous block.
1349 if (it
== maCells
.begin())
1350 // No more preceding block.
1353 --rRow
; // Move to the last cell of the previous block.
1355 if (it
->type
== sc::element_type_empty
)
1357 // This block is empty.
1358 if (it
== maCells
.begin())
1359 // No more preceding blocks.
1362 // Skip the whole empty block segment.
1369 bool ScColumn::GetNextDataPos(SCROW
& rRow
) const // greater than rRow
1371 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(rRow
);
1372 sc::CellStoreType::const_iterator it
= aPos
.first
;
1373 if (it
== maCells
.end())
1376 if (it
->type
== sc::element_type_empty
)
1378 // This block is empty. Skip ahead to the next block (if exists).
1379 rRow
+= it
->size
- aPos
.second
;
1381 if (it
== maCells
.end())
1382 // No more next block.
1385 // Next block exists, and is non-empty.
1389 if (aPos
.second
< it
->size
- 1)
1391 // There are still cells following the current position.
1396 // This is the last cell in the block. Move ahead to the next block.
1397 rRow
+= it
->size
- aPos
.second
; // First cell in the next block.
1399 if (it
== maCells
.end())
1400 // No more next block.
1403 if (it
->type
== sc::element_type_empty
)
1405 // Next block is empty. Move to the next block.
1408 if (it
== maCells
.end())
1415 SCROW
ScColumn::FindNextVisibleRow(SCROW nRow
, bool bForward
) const
1421 bool bHidden
= pDocument
->RowHidden(nRow
, nTab
, nullptr, &nEndRow
);
1423 return std::min
<SCROW
>(MAXROW
, nEndRow
+ 1);
1430 SCROW nStartRow
= MAXROW
;
1431 bool bHidden
= pDocument
->RowHidden(nRow
, nTab
, &nStartRow
);
1433 return std::max
<SCROW
>(0, nStartRow
- 1);
1439 SCROW
ScColumn::FindNextVisibleRowWithContent(
1440 sc::CellStoreType::const_iterator
& itPos
, SCROW nRow
, bool bForward
) const
1448 bool bHidden
= pDocument
->RowHidden(nRow
, nTab
, nullptr, &nEndRow
);
1456 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(itPos
, nRow
);
1458 if (itPos
== maCells
.end())
1462 if (itPos
->type
!= sc::element_type_empty
)
1465 // Move to the last cell of the current empty block.
1466 nRow
+= itPos
->size
- aPos
.second
- 1;
1468 while (nRow
< MAXROW
);
1476 SCROW nStartRow
= MAXROW
;
1477 bool bHidden
= pDocument
->RowHidden(nRow
, nTab
, &nStartRow
);
1480 nRow
= nStartRow
- 1;
1485 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(itPos
, nRow
);
1487 if (itPos
== maCells
.end())
1491 if (itPos
->type
!= sc::element_type_empty
)
1494 // Move to the first cell of the current empty block.
1495 nRow
-= aPos
.second
;
1502 void ScColumn::CellStorageModified()
1504 // TODO: Update column's "last updated" timestamp here.
1506 #if DEBUG_COLUMN_STORAGE
1507 if (maCells
.size() != MAXROWCOUNT
)
1509 cout
<< "ScColumn::CellStorageModified: Size of the cell array is incorrect." << endl
;
1514 if (maCellTextAttrs
.size() != MAXROWCOUNT
)
1516 cout
<< "ScColumn::CellStorageModified: Size of the cell text attribute array is incorrect." << endl
;
1521 if (maBroadcasters
.size() != MAXROWCOUNT
)
1523 cout
<< "ScColumn::CellStorageModified: Size of the broadcaster array is incorrect." << endl
;
1528 // Make sure that these two containers are synchronized wrt empty segments.
1529 sc::CellStoreType::const_iterator itCell
= maCells
.begin();
1530 sc::CellTextAttrStoreType::const_iterator itAttr
= maCellTextAttrs
.begin();
1532 // Move to the first empty blocks.
1533 while (itCell
!= maCells
.end() && itCell
->type
!= sc::element_type_empty
)
1536 while (itAttr
!= maCellTextAttrs
.end() && itAttr
->type
!= sc::element_type_empty
)
1539 while (itCell
!= maCells
.end())
1541 if (itCell
->position
!= itAttr
->position
|| itCell
->size
!= itAttr
->size
)
1543 cout
<< "ScColumn::CellStorageModified: Cell array and cell text attribute array are out of sync." << endl
;
1544 cout
<< "-- cell array" << endl
;
1545 maCells
.dump_blocks(cout
);
1546 cout
<< "-- attribute array" << endl
;
1547 maCellTextAttrs
.dump_blocks(cout
);
1552 // Move to the next empty blocks.
1554 while (itCell
!= maCells
.end() && itCell
->type
!= sc::element_type_empty
)
1558 while (itAttr
!= maCellTextAttrs
.end() && itAttr
->type
!= sc::element_type_empty
)
1562 (void) this; // Avoid "this member function can be declared static [loplugin:staticmethods]"
1566 #if DUMP_COLUMN_STORAGE
1570 #define DUMP_FORMULA_RESULTS 0
1572 struct ColumnStorageDumper
1574 const ScDocument
* mpDoc
;
1576 ColumnStorageDumper( const ScDocument
* pDoc
) : mpDoc(pDoc
) {}
1578 void operator() (const sc::CellStoreType::value_type
& rNode
) const
1582 case sc::element_type_numeric
:
1583 cout
<< " * numeric block (pos=" << rNode
.position
<< ", length=" << rNode
.size
<< ")" << endl
;
1585 case sc::element_type_string
:
1586 cout
<< " * string block (pos=" << rNode
.position
<< ", length=" << rNode
.size
<< ")" << endl
;
1588 case sc::element_type_edittext
:
1589 cout
<< " * edit-text block (pos=" << rNode
.position
<< ", length=" << rNode
.size
<< ")" << endl
;
1591 case sc::element_type_formula
:
1592 dumpFormulaBlock(rNode
);
1594 case sc::element_type_empty
:
1595 cout
<< " * empty block (pos=" << rNode
.position
<< ", length=" << rNode
.size
<< ")" << endl
;
1598 cout
<< " * unknown block" << endl
;
1602 void dumpFormulaBlock(const sc::CellStoreType::value_type
& rNode
) const
1604 cout
<< " * formula block (pos=" << rNode
.position
<< ", length=" << rNode
.size
<< ")" << endl
;
1605 sc::formula_block::const_iterator it
= sc::formula_block::begin(*rNode
.data
);
1606 sc::formula_block::const_iterator itEnd
= sc::formula_block::end(*rNode
.data
);
1608 for (; it
!= itEnd
; ++it
)
1610 const ScFormulaCell
* pCell
= *it
;
1611 if (!pCell
->IsShared())
1613 cout
<< " * row " << pCell
->aPos
.Row() << " not shared" << endl
;
1614 printFormula(pCell
);
1619 if (pCell
->GetSharedTopRow() != pCell
->aPos
.Row())
1621 cout
<< " * row " << pCell
->aPos
.Row() << " shared with top row "
1622 << pCell
->GetSharedTopRow() << " with length " << pCell
->GetSharedLength()
1627 SCROW nLen
= pCell
->GetSharedLength();
1628 cout
<< " * group: start=" << pCell
->aPos
.Row() << ", length=" << nLen
<< endl
;
1629 printFormula(pCell
);
1634 for (SCROW i
= 0; i
< nLen
-1; ++i
, ++it
)
1643 void printFormula(const ScFormulaCell
* pCell
) const
1645 sc::TokenStringContext
aCxt(mpDoc
, mpDoc
->GetGrammar());
1646 OUString aFormula
= pCell
->GetCode()->CreateString(aCxt
, pCell
->aPos
);
1647 cout
<< " * formula: " << aFormula
<< endl
;
1650 #if DUMP_FORMULA_RESULTS
1651 void printResult(const ScFormulaCell
* pCell
) const
1653 sc::FormulaResultValue aRes
= pCell
->GetResult();
1654 cout
<< " * result: ";
1655 switch (aRes
.meType
)
1657 case sc::FormulaResultValue::Value
:
1658 cout
<< aRes
.mfValue
<< " (type: value)";
1660 case sc::FormulaResultValue::String
:
1661 cout
<< "'" << aRes
.maString
.getString() << "' (type: string)";
1663 case sc::FormulaResultValue::Error
:
1664 cout
<< "error (" << static_cast<int>(aRes
.mnError
) << ")";
1666 case sc::FormulaResultValue::Invalid
:
1674 void printResult(const ScFormulaCell
*) const
1676 (void) this; /* loplugin:staticmethods */
1683 void ScColumn::DumpColumnStorage() const
1685 cout
<< "-- table: " << nTab
<< "; column: " << nCol
<< endl
;
1686 std::for_each(maCells
.begin(), maCells
.end(), ColumnStorageDumper(pDocument
));
1687 cout
<< "--" << endl
;
1691 void ScColumn::CopyCellTextAttrsToDocument(SCROW nRow1
, SCROW nRow2
, ScColumn
& rDestCol
) const
1693 rDestCol
.maCellTextAttrs
.set_empty(nRow1
, nRow2
); // Empty the destination range first.
1695 sc::CellTextAttrStoreType::const_iterator itBlk
= maCellTextAttrs
.begin(), itBlkEnd
= maCellTextAttrs
.end();
1697 // Locate the top row position.
1698 size_t nOffsetInBlock
= 0;
1699 size_t nBlockStart
= 0, nBlockEnd
= 0, nRowPos
= static_cast<size_t>(nRow1
);
1700 for (; itBlk
!= itBlkEnd
; ++itBlk
)
1702 nBlockEnd
= nBlockStart
+ itBlk
->size
;
1703 if (nBlockStart
<= nRowPos
&& nRowPos
< nBlockEnd
)
1706 nOffsetInBlock
= nRowPos
- nBlockStart
;
1711 if (itBlk
== itBlkEnd
)
1712 // Specified range not found. Bail out.
1715 nRowPos
= static_cast<size_t>(nRow2
); // End row position.
1717 // Keep copying until we hit the end row position.
1718 sc::celltextattr_block::const_iterator itData
, itDataEnd
;
1719 for (; itBlk
!= itBlkEnd
; ++itBlk
, nBlockStart
= nBlockEnd
, nOffsetInBlock
= 0)
1721 nBlockEnd
= nBlockStart
+ itBlk
->size
;
1725 if (nBlockStart
<= nRowPos
&& nRowPos
< nBlockEnd
)
1726 // This block contains the end row.
1727 rDestCol
.maCellTextAttrs
.set_empty(nBlockStart
+ nOffsetInBlock
, nRowPos
);
1729 rDestCol
.maCellTextAttrs
.set_empty(nBlockStart
+ nOffsetInBlock
, nBlockEnd
-1);
1735 itData
= sc::celltextattr_block::begin(*itBlk
->data
);
1736 itDataEnd
= sc::celltextattr_block::end(*itBlk
->data
);
1737 std::advance(itData
, nOffsetInBlock
);
1739 if (nBlockStart
<= nRowPos
&& nRowPos
< nBlockEnd
)
1741 // This block contains the end row. Only copy partially.
1742 size_t nOffset
= nRowPos
- nBlockStart
+ 1;
1743 itDataEnd
= sc::celltextattr_block::begin(*itBlk
->data
);
1744 std::advance(itDataEnd
, nOffset
);
1746 rDestCol
.maCellTextAttrs
.set(nBlockStart
+ nOffsetInBlock
, itData
, itDataEnd
);
1750 rDestCol
.maCellTextAttrs
.set(nBlockStart
+ nOffsetInBlock
, itData
, itDataEnd
);
1756 class CopyCellNotesHandler
1758 ScColumn
& mrDestCol
;
1759 sc::CellNoteStoreType
& mrDestNotes
;
1760 sc::CellNoteStoreType::iterator miPos
;
1765 SCROW mnDestOffset
; /// Add this to the source row position to get the destination row.
1766 bool mbCloneCaption
;
1769 CopyCellNotesHandler( const ScColumn
& rSrcCol
, ScColumn
& rDestCol
, SCROW nDestOffset
, bool bCloneCaption
) :
1770 mrDestCol(rDestCol
),
1771 mrDestNotes(rDestCol
.GetCellNoteStore()),
1772 miPos(mrDestNotes
.begin()),
1773 mnSrcTab(rSrcCol
.GetTab()),
1774 mnSrcCol(rSrcCol
.GetCol()),
1775 mnDestTab(rDestCol
.GetTab()),
1776 mnDestCol(rDestCol
.GetCol()),
1777 mnDestOffset(nDestOffset
),
1778 mbCloneCaption(bCloneCaption
) {}
1780 void operator() ( size_t nRow
, const ScPostIt
* p
)
1782 SCROW nDestRow
= nRow
+ mnDestOffset
;
1783 ScAddress
aSrcPos(mnSrcCol
, nRow
, mnSrcTab
);
1784 ScAddress
aDestPos(mnDestCol
, nDestRow
, mnDestTab
);
1785 miPos
= mrDestNotes
.set(miPos
, nDestRow
, p
->Clone(aSrcPos
, mrDestCol
.GetDoc(), aDestPos
, mbCloneCaption
));
1786 // Notify our LOK clients also
1787 ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Add
, &mrDestCol
.GetDoc(), aDestPos
, p
);
1793 void ScColumn::CopyCellNotesToDocument(
1794 SCROW nRow1
, SCROW nRow2
, ScColumn
& rDestCol
, bool bCloneCaption
, SCROW nRowOffsetDest
) const
1796 if (IsNotesEmptyBlock(nRow1
, nRow2
))
1797 // The column has no cell notes to copy between specified rows.
1800 ScDrawLayer
*pDrawLayer
= rDestCol
.GetDoc().GetDrawLayer();
1801 bool bWasLocked
= bool();
1804 // Avoid O(n^2) by temporary locking SdrModel which disables broadcasting.
1805 // Each cell note adds undo listener, and all of them would be woken up in ScPostIt::CreateCaption.
1806 bWasLocked
= pDrawLayer
->isLocked();
1807 pDrawLayer
->setLock(true);
1809 CopyCellNotesHandler
aFunc(*this, rDestCol
, nRowOffsetDest
, bCloneCaption
);
1810 sc::ParseNote(maCellNotes
.begin(), maCellNotes
, nRow1
, nRow2
, aFunc
);
1812 pDrawLayer
->setLock(bWasLocked
);
1815 void ScColumn::DuplicateNotes(SCROW nStartRow
, size_t nDataSize
, ScColumn
& rDestCol
, sc::ColumnBlockPosition
& maDestBlockPos
,
1816 bool bCloneCaption
, SCROW nRowOffsetDest
) const
1818 CopyCellNotesToDocument(nStartRow
, nStartRow
+ nDataSize
-1, rDestCol
, bCloneCaption
, nRowOffsetDest
);
1819 maDestBlockPos
.miCellNotePos
= rDestCol
.maCellNotes
.begin();
1822 SvtBroadcaster
* ScColumn::GetBroadcaster(SCROW nRow
)
1824 return maBroadcasters
.get
<SvtBroadcaster
*>(nRow
);
1827 const SvtBroadcaster
* ScColumn::GetBroadcaster(SCROW nRow
) const
1829 return maBroadcasters
.get
<SvtBroadcaster
*>(nRow
);
1832 void ScColumn::DeleteBroadcasters( sc::ColumnBlockPosition
& rBlockPos
, SCROW nRow1
, SCROW nRow2
)
1834 rBlockPos
.miBroadcasterPos
=
1835 maBroadcasters
.set_empty(rBlockPos
.miBroadcasterPos
, nRow1
, nRow2
);
1838 void ScColumn::PrepareBroadcastersForDestruction()
1840 sc::BroadcasterStoreType::iterator itPos
= maBroadcasters
.begin(), itPosEnd
= maBroadcasters
.end();
1841 for (; itPos
!= itPosEnd
; ++itPos
)
1843 if (itPos
->type
== sc::element_type_broadcaster
)
1845 sc::broadcaster_block::iterator it
= sc::broadcaster_block::begin(*itPos
->data
);
1846 sc::broadcaster_block::iterator itEnd
= sc::broadcaster_block::end(*itPos
->data
);
1847 for (; it
!= itEnd
; ++it
)
1848 (*it
)->PrepareForDestruction();
1853 ScPostIt
* ScColumn::GetCellNote(SCROW nRow
)
1855 return maCellNotes
.get
<ScPostIt
*>(nRow
);
1858 const ScPostIt
* ScColumn::GetCellNote(SCROW nRow
) const
1860 return maCellNotes
.get
<ScPostIt
*>(nRow
);
1863 const ScPostIt
* ScColumn::GetCellNote( sc::ColumnBlockConstPosition
& rBlockPos
, SCROW nRow
) const
1865 sc::CellNoteStoreType::const_position_type aPos
= maCellNotes
.position(rBlockPos
.miCellNotePos
, nRow
);
1866 rBlockPos
.miCellNotePos
= aPos
.first
;
1868 if (aPos
.first
->type
!= sc::element_type_cellnote
)
1871 return sc::cellnote_block::at(*aPos
.first
->data
, aPos
.second
);
1874 void ScColumn::SetCellNote(SCROW nRow
, ScPostIt
* pNote
)
1876 //pNote->UpdateCaptionPos(ScAddress(nCol, nRow, nTab)); // TODO notes useful ? slow import with many notes
1877 maCellNotes
.set(nRow
, pNote
);
1881 class CellNoteHandler
1883 const ScDocument
* m_pDocument
;
1884 const ScAddress m_aAddress
; // 'incomplete' address consisting of tab, column
1885 const bool m_bForgetCaptionOwnership
;
1888 CellNoteHandler(const ScDocument
* pDocument
, const ScAddress
& rPos
, bool bForgetCaptionOwnership
) :
1889 m_pDocument(pDocument
),
1891 m_bForgetCaptionOwnership(bForgetCaptionOwnership
) {}
1893 void operator() ( size_t nRow
, ScPostIt
* p
)
1895 if (m_bForgetCaptionOwnership
)
1898 // Create a 'complete' address object
1899 ScAddress
aAddr(m_aAddress
);
1901 // Notify our LOK clients
1902 ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Remove
, m_pDocument
, aAddr
, p
);
1905 } // anonymous namespace
1907 void ScColumn::CellNotesDeleting(SCROW nRow1
, SCROW nRow2
, bool bForgetCaptionOwnership
)
1909 ScAddress
aAddr(nCol
, 0, nTab
);
1910 CellNoteHandler
aFunc(pDocument
, aAddr
, bForgetCaptionOwnership
);
1911 sc::ParseNote(maCellNotes
.begin(), maCellNotes
, nRow1
, nRow2
, aFunc
);
1914 void ScColumn::DeleteCellNotes( sc::ColumnBlockPosition
& rBlockPos
, SCROW nRow1
, SCROW nRow2
, bool bForgetCaptionOwnership
)
1916 CellNotesDeleting(nRow1
, nRow2
, bForgetCaptionOwnership
);
1918 rBlockPos
.miCellNotePos
=
1919 maCellNotes
.set_empty(rBlockPos
.miCellNotePos
, nRow1
, nRow2
);
1922 bool ScColumn::HasCellNotes() const
1924 sc::CellNoteStoreType::const_iterator it
= maCellNotes
.begin(), itEnd
= maCellNotes
.end();
1925 for (; it
!= itEnd
; ++it
)
1927 if (it
->type
== sc::element_type_cellnote
)
1928 // Having a cellnote block automatically means there is at least one cell note.
1934 SCROW
ScColumn::GetCellNotesMaxRow() const
1936 // hypothesis : the column has cell notes (should be checked before)
1938 sc::CellNoteStoreType::const_iterator it
= maCellNotes
.begin(), itEnd
= maCellNotes
.end();
1939 for (; it
!= itEnd
; ++it
)
1941 if (it
->type
== sc::element_type_cellnote
)
1942 maxRow
= it
->position
+ it
->size
-1;
1946 SCROW
ScColumn::GetCellNotesMinRow() const
1948 // hypothesis : the column has cell notes (should be checked before)
1950 bool bFound
= false;
1951 sc::CellNoteStoreType::const_iterator it
= maCellNotes
.begin(), itEnd
= maCellNotes
.end();
1952 for (; it
!= itEnd
&& !bFound
; ++it
)
1954 if (it
->type
== sc::element_type_cellnote
)
1957 minRow
= it
->position
;
1963 sal_uInt16
ScColumn::GetTextWidth(SCROW nRow
) const
1965 return maCellTextAttrs
.get
<sc::CellTextAttr
>(nRow
).mnTextWidth
;
1968 void ScColumn::SetTextWidth(SCROW nRow
, sal_uInt16 nWidth
)
1970 sc::CellTextAttrStoreType::position_type aPos
= maCellTextAttrs
.position(nRow
);
1971 if (aPos
.first
->type
!= sc::element_type_celltextattr
)
1974 // Set new value only when the slot is not empty.
1975 sc::celltextattr_block::at(*aPos
.first
->data
, aPos
.second
).mnTextWidth
= nWidth
;
1976 CellStorageModified();
1979 SvtScriptType
ScColumn::GetScriptType( SCROW nRow
) const
1981 if (!ValidRow(nRow
) || maCellTextAttrs
.is_empty(nRow
))
1982 return SvtScriptType::NONE
;
1984 return maCellTextAttrs
.get
<sc::CellTextAttr
>(nRow
).mnScriptType
;
1987 SvtScriptType
ScColumn::GetRangeScriptType(
1988 sc::CellTextAttrStoreType::iterator
& itPos
, SCROW nRow1
, SCROW nRow2
, const sc::CellStoreType::iterator
& itrCells
)
1990 if (!ValidRow(nRow1
) || !ValidRow(nRow2
) || nRow1
> nRow2
)
1991 return SvtScriptType::NONE
;
1994 std::pair
<sc::CellTextAttrStoreType::iterator
,size_t> aRet
=
1995 maCellTextAttrs
.position(itPos
, nRow1
);
1997 itPos
= aRet
.first
; // Track the position of cell text attribute array.
1999 SvtScriptType nScriptType
= SvtScriptType::NONE
;
2000 bool bUpdated
= false;
2001 if (itPos
->type
== sc::element_type_celltextattr
)
2003 sc::celltextattr_block::iterator it
= sc::celltextattr_block::begin(*itPos
->data
);
2004 sc::celltextattr_block::iterator itEnd
= sc::celltextattr_block::end(*itPos
->data
);
2005 std::advance(it
, aRet
.second
);
2006 for (; it
!= itEnd
; ++it
, ++nRow
)
2011 sc::CellTextAttr
& rVal
= *it
;
2012 if (UpdateScriptType(rVal
, nRow
, itrCells
))
2014 nScriptType
|= rVal
.mnScriptType
;
2019 // Skip this whole block.
2020 nRow
+= itPos
->size
- aRet
.second
;
2023 while (nRow
<= nRow2
)
2026 if (itPos
== maCellTextAttrs
.end())
2029 if (itPos
->type
!= sc::element_type_celltextattr
)
2031 // Skip this whole block.
2032 nRow
+= itPos
->size
;
2036 sc::celltextattr_block::iterator it
= sc::celltextattr_block::begin(*itPos
->data
);
2037 sc::celltextattr_block::iterator itEnd
= sc::celltextattr_block::end(*itPos
->data
);
2038 for (; it
!= itEnd
; ++it
, ++nRow
)
2043 sc::CellTextAttr
& rVal
= *it
;
2044 if (UpdateScriptType(rVal
, nRow
, itrCells
))
2047 nScriptType
|= rVal
.mnScriptType
;
2052 CellStorageModified();
2057 void ScColumn::SetScriptType( SCROW nRow
, SvtScriptType nType
)
2059 if (!ValidRow(nRow
))
2062 sc::CellTextAttrStoreType::position_type aPos
= maCellTextAttrs
.position(nRow
);
2063 if (aPos
.first
->type
!= sc::element_type_celltextattr
)
2064 // Set new value only when the slot is already set.
2067 sc::celltextattr_block::at(*aPos
.first
->data
, aPos
.second
).mnScriptType
= nType
;
2068 CellStorageModified();
2071 size_t ScColumn::GetFormulaHash( SCROW nRow
) const
2073 const ScFormulaCell
* pCell
= FetchFormulaCell(nRow
);
2074 return pCell
? pCell
->GetHash() : 0;
2077 ScFormulaVectorState
ScColumn::GetFormulaVectorState( SCROW nRow
) const
2079 const ScFormulaCell
* pCell
= FetchFormulaCell(nRow
);
2080 return pCell
? pCell
->GetVectorState() : FormulaVectorUnknown
;
2083 formula::FormulaTokenRef
ScColumn::ResolveStaticReference( SCROW nRow
)
2085 std::pair
<sc::CellStoreType::iterator
,size_t> aPos
= maCells
.position(nRow
);
2086 sc::CellStoreType::iterator it
= aPos
.first
;
2087 if (it
== maCells
.end())
2088 // Invalid row. Return a null token.
2089 return formula::FormulaTokenRef();
2093 case sc::element_type_numeric
:
2095 double fVal
= sc::numeric_block::at(*it
->data
, aPos
.second
);
2096 return formula::FormulaTokenRef(new formula::FormulaDoubleToken(fVal
));
2098 case sc::element_type_formula
:
2100 ScFormulaCell
* p
= sc::formula_block::at(*it
->data
, aPos
.second
);
2102 return formula::FormulaTokenRef(new formula::FormulaDoubleToken(p
->GetValue()));
2104 return formula::FormulaTokenRef(new formula::FormulaStringToken(p
->GetString()));
2106 case sc::element_type_string
:
2108 const svl::SharedString
& rSS
= sc::string_block::at(*it
->data
, aPos
.second
);
2109 return formula::FormulaTokenRef(new formula::FormulaStringToken(rSS
));
2111 case sc::element_type_edittext
:
2113 const EditTextObject
* pText
= sc::edittext_block::at(*it
->data
, aPos
.second
);
2114 OUString aStr
= ScEditUtil::GetString(*pText
, pDocument
);
2115 svl::SharedString
aSS( pDocument
->GetSharedStringPool().intern(aStr
));
2116 return formula::FormulaTokenRef(new formula::FormulaStringToken(aSS
));
2118 case sc::element_type_empty
:
2120 // Return a value of 0.0 in all the other cases.
2121 return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0));
2127 class ToMatrixHandler
2133 svl::SharedStringPool
& mrStrPool
;
2135 ToMatrixHandler(ScMatrix
& rMat
, SCCOL nMatCol
, SCROW nTopRow
, ScDocument
* pDoc
) :
2136 mrMat(rMat
), mnMatCol(nMatCol
), mnTopRow(nTopRow
),
2137 mpDoc(pDoc
), mrStrPool(pDoc
->GetSharedStringPool()) {}
2139 void operator() (size_t nRow
, double fVal
)
2141 mrMat
.PutDouble(fVal
, mnMatCol
, nRow
- mnTopRow
);
2144 void operator() (size_t nRow
, const ScFormulaCell
* p
)
2146 // Formula cell may need to re-calculate.
2147 ScFormulaCell
& rCell
= const_cast<ScFormulaCell
&>(*p
);
2148 if (rCell
.IsValue())
2149 mrMat
.PutDouble(rCell
.GetValue(), mnMatCol
, nRow
- mnTopRow
);
2151 mrMat
.PutString(rCell
.GetString(), mnMatCol
, nRow
- mnTopRow
);
2154 void operator() (size_t nRow
, const svl::SharedString
& rSS
)
2156 mrMat
.PutString(rSS
, mnMatCol
, nRow
- mnTopRow
);
2159 void operator() (size_t nRow
, const EditTextObject
* pStr
)
2161 mrMat
.PutString(mrStrPool
.intern(ScEditUtil::GetString(*pStr
, mpDoc
)), mnMatCol
, nRow
- mnTopRow
);
2167 bool ScColumn::ResolveStaticReference( ScMatrix
& rMat
, SCCOL nMatCol
, SCROW nRow1
, SCROW nRow2
)
2172 ToMatrixHandler
aFunc(rMat
, nMatCol
, nRow1
, pDocument
);
2173 sc::ParseAllNonEmpty(maCells
.begin(), maCells
, nRow1
, nRow2
, aFunc
);
2181 SCSIZE mnEmpValStart
;
2182 SCSIZE mnNumValStart
;
2183 SCSIZE mnStrValStart
;
2184 SCSIZE mnEmpValCount
;
2185 std::vector
<double> maNumVals
;
2186 std::vector
<svl::SharedString
> maStrVals
;
2188 CellBucket() : mnEmpValStart(0), mnNumValStart(0), mnStrValStart(0), mnEmpValCount(0) {}
2190 void flush(ScMatrix
& rMat
, SCSIZE nCol
)
2194 rMat
.PutEmptyResultVector(mnEmpValCount
, nCol
, mnEmpValStart
);
2197 else if (!maNumVals
.empty())
2199 const double* p
= &maNumVals
[0];
2200 rMat
.PutDouble(p
, maNumVals
.size(), nCol
, mnNumValStart
);
2203 else if (!maStrVals
.empty())
2205 const svl::SharedString
* p
= &maStrVals
[0];
2206 rMat
.PutString(p
, maStrVals
.size(), nCol
, mnStrValStart
);
2213 mnEmpValStart
= mnNumValStart
= mnStrValStart
= 0;
2220 class FillMatrixHandler
2227 svl::SharedStringPool
& mrPool
;
2228 svl::SharedStringPool
* mpPool
; // if matrix is not in the same document
2231 FillMatrixHandler(ScMatrix
& rMat
, size_t nMatCol
, size_t nTopRow
, ScDocument
* pDoc
, svl::SharedStringPool
* pPool
) :
2232 mrMat(rMat
), mnMatCol(nMatCol
), mnTopRow(nTopRow
),
2233 mpDoc(pDoc
), mrPool(pDoc
->GetSharedStringPool()), mpPool(pPool
) {}
2235 void operator() (const sc::CellStoreType::value_type
& node
, size_t nOffset
, size_t nDataSize
)
2237 size_t nMatRow
= node
.position
+ nOffset
- mnTopRow
;
2241 case sc::element_type_numeric
:
2243 const double* p
= &sc::numeric_block::at(*node
.data
, nOffset
);
2244 mrMat
.PutDouble(p
, nDataSize
, mnMatCol
, nMatRow
);
2247 case sc::element_type_string
:
2251 const svl::SharedString
* p
= &sc::string_block::at(*node
.data
, nOffset
);
2252 mrMat
.PutString(p
, nDataSize
, mnMatCol
, nMatRow
);
2256 std::vector
<svl::SharedString
> aStrings
;
2257 aStrings
.reserve(nDataSize
);
2258 const svl::SharedString
* p
= &sc::string_block::at(*node
.data
, nOffset
);
2259 for (size_t i
= 0; i
< nDataSize
; ++i
)
2261 aStrings
.push_back(mpPool
->intern(p
[i
].getString()));
2263 mrMat
.PutString(aStrings
.data(), aStrings
.size(), mnMatCol
, nMatRow
);
2267 case sc::element_type_edittext
:
2269 std::vector
<svl::SharedString
> aSSs
;
2270 aSSs
.reserve(nDataSize
);
2271 sc::edittext_block::const_iterator it
= sc::edittext_block::begin(*node
.data
);
2272 std::advance(it
, nOffset
);
2273 sc::edittext_block::const_iterator itEnd
= it
;
2274 std::advance(itEnd
, nDataSize
);
2275 for (; it
!= itEnd
; ++it
)
2277 OUString aStr
= ScEditUtil::GetString(**it
, mpDoc
);
2279 aSSs
.push_back(mrPool
.intern(aStr
));
2281 aSSs
.push_back(mpPool
->intern(aStr
));
2284 const svl::SharedString
* p
= &aSSs
[0];
2285 mrMat
.PutString(p
, nDataSize
, mnMatCol
, nMatRow
);
2288 case sc::element_type_formula
:
2291 sc::formula_block::const_iterator it
= sc::formula_block::begin(*node
.data
);
2292 std::advance(it
, nOffset
);
2293 sc::formula_block::const_iterator itEnd
= it
;
2294 std::advance(itEnd
, nDataSize
);
2296 size_t nPrevRow
= 0, nThisRow
= node
.position
+ nOffset
;
2297 for (; it
!= itEnd
; ++it
, nPrevRow
= nThisRow
, ++nThisRow
)
2299 ScFormulaCell
& rCell
= **it
;
2301 if (rCell
.IsEmpty())
2303 if (aBucket
.mnEmpValCount
&& nThisRow
== nPrevRow
+ 1)
2305 // Secondary empty results.
2306 ++aBucket
.mnEmpValCount
;
2310 // First empty result.
2311 aBucket
.flush(mrMat
, mnMatCol
);
2312 aBucket
.mnEmpValStart
= nThisRow
- mnTopRow
;
2313 ++aBucket
.mnEmpValCount
;
2320 if (rCell
.GetErrorOrValue(nErr
, fVal
))
2322 if (nErr
!= FormulaError::NONE
)
2323 fVal
= CreateDoubleError(nErr
);
2325 if (!aBucket
.maNumVals
.empty() && nThisRow
== nPrevRow
+ 1)
2327 // Secondary numbers.
2328 aBucket
.maNumVals
.push_back(fVal
);
2333 aBucket
.flush(mrMat
, mnMatCol
);
2334 aBucket
.mnNumValStart
= nThisRow
- mnTopRow
;
2335 aBucket
.maNumVals
.push_back(fVal
);
2340 svl::SharedString aStr
= rCell
.GetString();
2342 aStr
= mpPool
->intern(aStr
.getString());
2343 if (!aBucket
.maStrVals
.empty() && nThisRow
== nPrevRow
+ 1)
2345 // Secondary strings.
2346 aBucket
.maStrVals
.push_back(aStr
);
2351 aBucket
.flush(mrMat
, mnMatCol
);
2352 aBucket
.mnStrValStart
= nThisRow
- mnTopRow
;
2353 aBucket
.maStrVals
.push_back(aStr
);
2357 aBucket
.flush(mrMat
, mnMatCol
);
2368 void ScColumn::FillMatrix( ScMatrix
& rMat
, size_t nMatCol
, SCROW nRow1
, SCROW nRow2
, svl::SharedStringPool
* pPool
) const
2370 FillMatrixHandler
aFunc(rMat
, nMatCol
, nRow1
, pDocument
, pPool
);
2371 sc::ParseBlock(maCells
.begin(), maCells
, aFunc
, nRow1
, nRow2
);
2376 template<typename Blk
>
2377 void getBlockIterators(
2378 const sc::CellStoreType::iterator
& it
, size_t& rLenRemain
,
2379 typename
Blk::iterator
& rData
, typename
Blk::iterator
& rDataEnd
)
2381 rData
= Blk::begin(*it
->data
);
2382 if (rLenRemain
>= it
->size
)
2384 // Block is shorter than the remaining requested length.
2385 rDataEnd
= Blk::end(*it
->data
);
2386 rLenRemain
-= it
->size
;
2391 std::advance(rDataEnd
, rLenRemain
);
2397 ScDocument
* pDoc
, sc::FormulaGroupContext
& rCxt
, sc::FormulaGroupContext::ColArray
& rColArray
,
2398 size_t nPos
, size_t nArrayLen
, const sc::CellStoreType::iterator
& _it
, const sc::CellStoreType::iterator
& itEnd
)
2400 svl::SharedStringPool
& rPool
= pDoc
->GetSharedStringPool();
2401 size_t nLenRemain
= nArrayLen
- nPos
;
2403 rtl::math::setNan(&fNan
);
2405 for (sc::CellStoreType::iterator it
= _it
; it
!= itEnd
; ++it
)
2409 case sc::element_type_string
:
2411 sc::string_block::iterator itData
, itDataEnd
;
2412 getBlockIterators
<sc::string_block
>(it
, nLenRemain
, itData
, itDataEnd
);
2413 rCxt
.ensureStrArray(rColArray
, nArrayLen
);
2415 for (; itData
!= itDataEnd
; ++itData
, ++nPos
)
2416 (*rColArray
.mpStrArray
)[nPos
] = itData
->getData();
2419 case sc::element_type_edittext
:
2421 sc::edittext_block::iterator itData
, itDataEnd
;
2422 getBlockIterators
<sc::edittext_block
>(it
, nLenRemain
, itData
, itDataEnd
);
2423 rCxt
.ensureStrArray(rColArray
, nArrayLen
);
2425 for (; itData
!= itDataEnd
; ++itData
, ++nPos
)
2427 OUString aStr
= ScEditUtil::GetString(**itData
, pDoc
);
2428 (*rColArray
.mpStrArray
)[nPos
] = rPool
.intern(aStr
).getData();
2432 case sc::element_type_formula
:
2434 sc::formula_block::iterator itData
, itDataEnd
;
2435 getBlockIterators
<sc::formula_block
>(it
, nLenRemain
, itData
, itDataEnd
);
2437 /* tdf#91416 setting progress in triggers a resize of the window
2438 and so ScTabView::DoResize and an InterpretVisible and
2439 InterpretDirtyCells which resets the mpFormulaGroupCxt that
2440 the current rCxt points to, which is bad, so disable progress
2443 ScProgress
*pProgress
= ScProgress::GetInterpretProgress();
2444 bool bTempDisableProgress
= pProgress
&& pProgress
->Enabled();
2445 if (bTempDisableProgress
)
2446 pProgress
->Disable();
2448 for (; itData
!= itDataEnd
; ++itData
, ++nPos
)
2450 ScFormulaCell
& rFC
= **itData
;
2452 sc::FormulaResultValue aRes
= rFC
.GetResult();
2454 if (aRes
.meType
== sc::FormulaResultValue::Invalid
|| aRes
.mnError
!= FormulaError::NONE
)
2456 if (aRes
.mnError
== FormulaError::CircularReference
)
2458 // This cell needs to be recalculated on next visit.
2459 rFC
.SetErrCode(FormulaError::NONE
);
2465 if (aRes
.meType
== sc::FormulaResultValue::String
)
2467 rCxt
.ensureStrArray(rColArray
, nArrayLen
);
2468 (*rColArray
.mpStrArray
)[nPos
] = aRes
.maString
.getData();
2472 rCxt
.ensureNumArray(rColArray
, nArrayLen
);
2473 (*rColArray
.mpNumArray
)[nPos
] = aRes
.mfValue
;
2477 if (bTempDisableProgress
)
2478 pProgress
->Enable();
2481 case sc::element_type_empty
:
2483 if (nLenRemain
> it
->size
)
2486 nLenRemain
-= it
->size
;
2495 case sc::element_type_numeric
:
2497 sc::numeric_block::iterator itData
, itDataEnd
;
2498 getBlockIterators
<sc::numeric_block
>(it
, nLenRemain
, itData
, itDataEnd
);
2499 rCxt
.ensureNumArray(rColArray
, nArrayLen
);
2501 for (; itData
!= itDataEnd
; ++itData
, ++nPos
)
2502 (*rColArray
.mpNumArray
)[nPos
] = *itData
;
2516 void copyFirstStringBlock(
2517 ScDocument
& rDoc
, sc::FormulaGroupContext::StrArrayType
& rArray
, size_t nLen
, const sc::CellStoreType::iterator
& itBlk
)
2519 sc::FormulaGroupContext::StrArrayType::iterator itArray
= rArray
.begin();
2521 switch (itBlk
->type
)
2523 case sc::element_type_string
:
2525 sc::string_block::iterator it
= sc::string_block::begin(*itBlk
->data
);
2526 sc::string_block::iterator itEnd
= it
;
2527 std::advance(itEnd
, nLen
);
2528 for (; it
!= itEnd
; ++it
, ++itArray
)
2529 *itArray
= it
->getData();
2532 case sc::element_type_edittext
:
2534 sc::edittext_block::iterator it
= sc::edittext_block::begin(*itBlk
->data
);
2535 sc::edittext_block::iterator itEnd
= it
;
2536 std::advance(itEnd
, nLen
);
2538 svl::SharedStringPool
& rPool
= rDoc
.GetSharedStringPool();
2539 for (; it
!= itEnd
; ++it
, ++itArray
)
2541 EditTextObject
* pText
= *it
;
2542 OUString aStr
= ScEditUtil::GetString(*pText
, &rDoc
);
2543 *itArray
= rPool
.intern(aStr
).getData();
2552 sc::FormulaGroupContext::ColArray
*
2553 copyFirstFormulaBlock(
2554 sc::FormulaGroupContext
& rCxt
, const sc::CellStoreType::iterator
& itBlk
, size_t nArrayLen
,
2555 SCTAB nTab
, SCCOL nCol
)
2558 rtl::math::setNan(&fNan
);
2560 size_t nLen
= std::min(itBlk
->size
, nArrayLen
);
2562 sc::formula_block::iterator it
= sc::formula_block::begin(*itBlk
->data
);
2563 sc::formula_block::iterator itEnd
;
2565 sc::FormulaGroupContext::NumArrayType
* pNumArray
= nullptr;
2566 sc::FormulaGroupContext::StrArrayType
* pStrArray
= nullptr;
2569 std::advance(itEnd
, nLen
);
2571 for (; it
!= itEnd
; ++it
, ++nPos
)
2573 ScFormulaCell
& rFC
= **it
;
2574 sc::FormulaResultValue aRes
= rFC
.GetResult();
2575 if (aRes
.meType
== sc::FormulaResultValue::Invalid
|| aRes
.mnError
!= FormulaError::NONE
)
2577 if (aRes
.mnError
== FormulaError::CircularReference
)
2579 // This cell needs to be recalculated on next visit.
2580 rFC
.SetErrCode(FormulaError::NONE
);
2586 if (aRes
.meType
== sc::FormulaResultValue::Value
)
2590 rCxt
.m_NumArrays
.push_back(
2591 o3tl::make_unique
<sc::FormulaGroupContext::NumArrayType
>(nArrayLen
, fNan
));
2592 pNumArray
= rCxt
.m_NumArrays
.back().get();
2595 (*pNumArray
)[nPos
] = aRes
.mfValue
;
2601 rCxt
.m_StrArrays
.push_back(
2602 o3tl::make_unique
<sc::FormulaGroupContext::StrArrayType
>(nArrayLen
, nullptr));
2603 pStrArray
= rCxt
.m_StrArrays
.back().get();
2606 (*pStrArray
)[nPos
] = aRes
.maString
.getData();
2610 if (!pNumArray
&& !pStrArray
)
2611 // At least one of these arrays should be allocated.
2614 return rCxt
.setCachedColArray(nTab
, nCol
, pNumArray
, pStrArray
);
2617 struct NonNullStringFinder
2619 bool operator() (const rtl_uString
* p
) const { return p
!= nullptr; }
2622 bool hasNonEmpty( const sc::FormulaGroupContext::StrArrayType
& rArray
, SCROW nRow1
, SCROW nRow2
)
2624 // The caller has to make sure the array is at least nRow2+1 long.
2625 sc::FormulaGroupContext::StrArrayType::const_iterator it
= rArray
.begin();
2626 std::advance(it
, nRow1
);
2627 sc::FormulaGroupContext::StrArrayType::const_iterator itEnd
= it
;
2628 std::advance(itEnd
, nRow2
-nRow1
+1);
2629 return std::any_of(it
, itEnd
, NonNullStringFinder());
2634 formula::VectorRefArray
ScColumn::FetchVectorRefArray( SCROW nRow1
, SCROW nRow2
)
2637 return formula::VectorRefArray(formula::VectorRefArray::Invalid
);
2639 // See if the requested range is already cached.
2640 sc::FormulaGroupContext
& rCxt
= *(pDocument
->GetFormulaGroupContext());
2641 sc::FormulaGroupContext::ColArray
* pColArray
= rCxt
.getCachedColArray(nTab
, nCol
, nRow2
+1);
2644 const double* pNum
= nullptr;
2645 if (pColArray
->mpNumArray
)
2646 pNum
= &(*pColArray
->mpNumArray
)[nRow1
];
2648 rtl_uString
** pStr
= nullptr;
2649 if (pColArray
->mpStrArray
&& hasNonEmpty(*pColArray
->mpStrArray
, nRow1
, nRow2
))
2650 pStr
= &(*pColArray
->mpStrArray
)[nRow1
];
2652 return formula::VectorRefArray(pNum
, pStr
);
2656 rtl::math::setNan(&fNan
);
2658 // We need to fetch all cell values from row 0 to nRow2 for caching purposes.
2659 sc::CellStoreType::iterator itBlk
= maCells
.begin();
2660 switch (itBlk
->type
)
2662 case sc::element_type_numeric
:
2664 if (static_cast<size_t>(nRow2
) < itBlk
->size
)
2666 // Requested range falls within the first block. No need to cache.
2667 const double* p
= &sc::numeric_block::at(*itBlk
->data
, nRow1
);
2668 return formula::VectorRefArray(p
);
2671 // Allocate a new array and copy the values to it.
2672 sc::numeric_block::const_iterator it
= sc::numeric_block::begin(*itBlk
->data
);
2673 sc::numeric_block::const_iterator itEnd
= sc::numeric_block::end(*itBlk
->data
);
2674 rCxt
.m_NumArrays
.push_back(
2675 o3tl::make_unique
<sc::FormulaGroupContext::NumArrayType
>(it
, itEnd
));
2676 sc::FormulaGroupContext::NumArrayType
& rArray
= *rCxt
.m_NumArrays
.back();
2677 rArray
.resize(nRow2
+1, fNan
); // allocate to the requested length.
2679 pColArray
= rCxt
.setCachedColArray(nTab
, nCol
, &rArray
, nullptr);
2681 // Failed to insert a new cached column array.
2682 return formula::VectorRefArray(formula::VectorRefArray::Invalid
);
2684 // Fill the remaining array with values from the following blocks.
2685 size_t nPos
= itBlk
->size
;
2687 if (!appendToBlock(pDocument
, rCxt
, *pColArray
, nPos
, nRow2
+1, itBlk
, maCells
.end()))
2688 return formula::VectorRefArray(formula::VectorRefArray::Invalid
);
2690 rtl_uString
** pStr
= nullptr;
2691 if (pColArray
->mpStrArray
&& hasNonEmpty(*pColArray
->mpStrArray
, nRow1
, nRow2
))
2692 pStr
= &(*pColArray
->mpStrArray
)[nRow1
];
2694 return formula::VectorRefArray(&(*pColArray
->mpNumArray
)[nRow1
], pStr
);
2697 case sc::element_type_string
:
2698 case sc::element_type_edittext
:
2700 rCxt
.m_StrArrays
.push_back(
2701 o3tl::make_unique
<sc::FormulaGroupContext::StrArrayType
>(nRow2
+1, nullptr));
2702 sc::FormulaGroupContext::StrArrayType
& rArray
= *rCxt
.m_StrArrays
.back();
2703 pColArray
= rCxt
.setCachedColArray(nTab
, nCol
, nullptr, &rArray
);
2705 // Failed to insert a new cached column array.
2706 return formula::VectorRefArray();
2708 if (static_cast<size_t>(nRow2
) < itBlk
->size
)
2710 // Requested range falls within the first block.
2711 copyFirstStringBlock(*pDocument
, rArray
, nRow2
+1, itBlk
);
2712 return formula::VectorRefArray(&rArray
[nRow1
]);
2715 copyFirstStringBlock(*pDocument
, rArray
, itBlk
->size
, itBlk
);
2717 // Fill the remaining array with values from the following blocks.
2718 size_t nPos
= itBlk
->size
;
2720 if (!appendToBlock(pDocument
, rCxt
, *pColArray
, nPos
, nRow2
+1, itBlk
, maCells
.end()))
2721 return formula::VectorRefArray(formula::VectorRefArray::Invalid
);
2723 assert(pColArray
->mpStrArray
);
2725 rtl_uString
** pStr
= nullptr;
2726 if (hasNonEmpty(*pColArray
->mpStrArray
, nRow1
, nRow2
))
2727 pStr
= &(*pColArray
->mpStrArray
)[nRow1
];
2729 if (pColArray
->mpNumArray
)
2730 return formula::VectorRefArray(&(*pColArray
->mpNumArray
)[nRow1
], pStr
);
2732 return formula::VectorRefArray(pStr
);
2735 case sc::element_type_formula
:
2737 if (static_cast<size_t>(nRow2
) < itBlk
->size
)
2739 // Requested length is within a single block, and the data is
2741 pColArray
= copyFirstFormulaBlock(rCxt
, itBlk
, nRow2
+1, nTab
, nCol
);
2743 // Failed to insert a new cached column array.
2744 return formula::VectorRefArray(formula::VectorRefArray::Invalid
);
2746 const double* pNum
= nullptr;
2747 rtl_uString
** pStr
= nullptr;
2748 if (pColArray
->mpNumArray
)
2749 pNum
= &(*pColArray
->mpNumArray
)[nRow1
];
2750 if (pColArray
->mpStrArray
)
2751 pStr
= &(*pColArray
->mpStrArray
)[nRow1
];
2753 return formula::VectorRefArray(pNum
, pStr
);
2756 pColArray
= copyFirstFormulaBlock(rCxt
, itBlk
, nRow2
+1, nTab
, nCol
);
2758 // Failed to insert a new cached column array.
2759 return formula::VectorRefArray(formula::VectorRefArray::Invalid
);
2761 size_t nPos
= itBlk
->size
;
2763 if (!appendToBlock(pDocument
, rCxt
, *pColArray
, nPos
, nRow2
+1, itBlk
, maCells
.end()))
2764 return formula::VectorRefArray(formula::VectorRefArray::Invalid
);
2766 const double* pNum
= nullptr;
2767 rtl_uString
** pStr
= nullptr;
2768 if (pColArray
->mpNumArray
)
2769 pNum
= &(*pColArray
->mpNumArray
)[nRow1
];
2770 if (pColArray
->mpStrArray
&& hasNonEmpty(*pColArray
->mpStrArray
, nRow1
, nRow2
))
2771 pStr
= &(*pColArray
->mpStrArray
)[nRow1
];
2773 return formula::VectorRefArray(pNum
, pStr
);
2776 case sc::element_type_empty
:
2778 // Fill the whole length with NaN's.
2779 rCxt
.m_NumArrays
.push_back(
2780 o3tl::make_unique
<sc::FormulaGroupContext::NumArrayType
>(nRow2
+1, fNan
));
2781 sc::FormulaGroupContext::NumArrayType
& rArray
= *rCxt
.m_NumArrays
.back();
2782 pColArray
= rCxt
.setCachedColArray(nTab
, nCol
, &rArray
, nullptr);
2784 // Failed to insert a new cached column array.
2785 return formula::VectorRefArray(formula::VectorRefArray::Invalid
);
2787 if (static_cast<size_t>(nRow2
) < itBlk
->size
)
2788 return formula::VectorRefArray(&(*pColArray
->mpNumArray
)[nRow1
]);
2790 // Fill the remaining array with values from the following blocks.
2791 size_t nPos
= itBlk
->size
;
2793 if (!appendToBlock(pDocument
, rCxt
, *pColArray
, nPos
, nRow2
+1, itBlk
, maCells
.end()))
2794 return formula::VectorRefArray(formula::VectorRefArray::Invalid
);
2796 if (pColArray
->mpStrArray
&& hasNonEmpty(*pColArray
->mpStrArray
, nRow1
, nRow2
))
2797 return formula::VectorRefArray(&(*pColArray
->mpNumArray
)[nRow1
], &(*pColArray
->mpStrArray
)[nRow1
]);
2799 return formula::VectorRefArray(&(*pColArray
->mpNumArray
)[nRow1
]);
2806 return formula::VectorRefArray(formula::VectorRefArray::Invalid
);
2809 bool ScColumn::HandleRefArrayForParallelism( SCROW nRow1
, SCROW nRow2
)
2814 for (auto i
= nRow1
; i
<= nRow2
; ++i
)
2816 auto aCell
= GetCellValue(i
);
2817 if (aCell
.meType
== CELLTYPE_FORMULA
)
2818 aCell
.mpFormula
->MaybeInterpret();
2824 void ScColumn::SetFormulaResults( SCROW nRow
, const double* pResults
, size_t nLen
)
2826 sc::CellStoreType::position_type aPos
= maCells
.position(nRow
);
2827 sc::CellStoreType::iterator it
= aPos
.first
;
2828 if (it
->type
!= sc::element_type_formula
)
2829 // This is not a formula block.
2832 size_t nBlockLen
= it
->size
- aPos
.second
;
2833 if (nBlockLen
< nLen
)
2834 // Result array is longer than the length of formula cells. Not good.
2837 sc::formula_block::iterator itCell
= sc::formula_block::begin(*it
->data
);
2838 std::advance(itCell
, aPos
.second
);
2840 const double* pResEnd
= pResults
+ nLen
;
2841 for (; pResults
!= pResEnd
; ++pResults
, ++itCell
)
2843 ScFormulaCell
& rCell
= **itCell
;
2844 FormulaError nErr
= GetDoubleErrorValue(*pResults
);
2845 if (nErr
!= FormulaError::NONE
)
2846 rCell
.SetResultError(nErr
);
2848 rCell
.SetResultDouble(*pResults
);
2850 rCell
.SetChanged(true);
2854 void ScColumn::SetFormulaResults( SCROW nRow
, const formula::FormulaConstTokenRef
* pResults
, size_t nLen
)
2856 sc::CellStoreType::position_type aPos
= maCells
.position(nRow
);
2857 sc::CellStoreType::iterator it
= aPos
.first
;
2858 if (it
->type
!= sc::element_type_formula
)
2859 // This is not a formula block.
2862 size_t nBlockLen
= it
->size
- aPos
.second
;
2863 if (nBlockLen
< nLen
)
2864 // Result array is longer than the length of formula cells. Not good.
2867 sc::formula_block::iterator itCell
= sc::formula_block::begin(*it
->data
);
2868 std::advance(itCell
, aPos
.second
);
2870 const formula::FormulaConstTokenRef
* pResEnd
= pResults
+ nLen
;
2871 for (; pResults
!= pResEnd
; ++pResults
, ++itCell
)
2873 ScFormulaCell
& rCell
= **itCell
;
2874 rCell
.SetResultToken(pResults
->get());
2876 rCell
.SetChanged(true);
2880 void ScColumn::CalculateInThread( ScInterpreterContext
& rContext
, SCROW nRow
, size_t nLen
, unsigned nThisThread
, unsigned nThreadsTotal
)
2882 assert(pDocument
->mbThreadedGroupCalcInProgress
);
2884 sc::CellStoreType::position_type aPos
= maCells
.position(nRow
);
2885 sc::CellStoreType::iterator it
= aPos
.first
;
2886 if (it
->type
!= sc::element_type_formula
)
2887 // This is not a formula block.
2890 size_t nBlockLen
= it
->size
- aPos
.second
;
2891 if (nBlockLen
< nLen
)
2892 // Length is longer than the length of formula cells. Not good.
2895 sc::formula_block::iterator itCell
= sc::formula_block::begin(*it
->data
);
2896 std::advance(itCell
, aPos
.second
);
2898 for (size_t i
= 0; i
< nLen
; ++i
, ++itCell
)
2900 if (nThreadsTotal
> 0 && (i
% nThreadsTotal
) != nThisThread
)
2903 ScFormulaCell
& rCell
= **itCell
;
2904 // Here we don't call IncInterpretLevel() and DecInterpretLevel() as this call site is
2905 // always in a threaded calculation.
2906 rCell
.InterpretTail(rContext
, ScFormulaCell::SCITP_NORMAL
);
2910 void ScColumn::HandleStuffAfterParallelCalculation( SCROW nRow
, size_t nLen
)
2912 sc::CellStoreType::position_type aPos
= maCells
.position(nRow
);
2913 sc::CellStoreType::iterator it
= aPos
.first
;
2914 if (it
->type
!= sc::element_type_formula
)
2915 // This is not a formula block.
2918 size_t nBlockLen
= it
->size
- aPos
.second
;
2919 if (nBlockLen
< nLen
)
2920 // Length is longer than the length of formula cells. Not good.
2923 sc::formula_block::iterator itCell
= sc::formula_block::begin(*it
->data
);
2924 std::advance(itCell
, aPos
.second
);
2926 for (size_t i
= 0; i
< nLen
; ++i
, ++itCell
)
2928 ScFormulaCell
& rCell
= **itCell
;
2929 rCell
.HandleStuffAfterParallelCalculation();
2933 void ScColumn::SetNumberFormat( SCROW nRow
, sal_uInt32 nNumberFormat
)
2935 ApplyAttr(nRow
, SfxUInt32Item(ATTR_VALUE_FORMAT
, nNumberFormat
));
2938 ScFormulaCell
* const * ScColumn::GetFormulaCellBlockAddress( SCROW nRow
, size_t& rBlockSize
) const
2940 if (!ValidRow(nRow
))
2946 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(nRow
);
2947 sc::CellStoreType::const_iterator it
= aPos
.first
;
2948 if (it
== maCells
.end())
2954 if (it
->type
!= sc::element_type_formula
)
2956 // Not a formula cell.
2961 rBlockSize
= it
->size
;
2962 return &sc::formula_block::at(*it
->data
, aPos
.second
);
2965 const ScFormulaCell
* ScColumn::FetchFormulaCell( SCROW nRow
) const
2967 size_t nBlockSize
= 0;
2968 ScFormulaCell
const * const * pp
= GetFormulaCellBlockAddress( nRow
, nBlockSize
);
2969 return pp
? *pp
: nullptr;
2972 void ScColumn::FindDataAreaPos(SCROW
& rRow
, bool bDown
) const
2974 // If the cell is empty, find the next non-empty cell position. If the
2975 // cell is not empty, find the last non-empty cell position in the current
2976 // contiguous cell block.
2978 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= maCells
.position(rRow
);
2979 sc::CellStoreType::const_iterator it
= aPos
.first
;
2980 if (it
== maCells
.end())
2984 if (it
->type
== sc::element_type_empty
)
2986 // Current cell is empty. Find the next non-empty cell.
2987 rRow
= FindNextVisibleRowWithContent(it
, rRow
, bDown
);
2991 // Current cell is not empty.
2992 SCROW nNextRow
= FindNextVisibleRow(rRow
, bDown
);
2993 aPos
= maCells
.position(it
, nNextRow
);
2995 if (it
->type
== sc::element_type_empty
)
2997 // Next visible cell is empty. Find the next non-empty cell.
2998 rRow
= FindNextVisibleRowWithContent(it
, nNextRow
, bDown
);
3002 // Next visible cell is non-empty. Find the edge that's still visible.
3003 SCROW nLastRow
= nNextRow
;
3006 nNextRow
= FindNextVisibleRow(nLastRow
, bDown
);
3007 if (nNextRow
== nLastRow
)
3010 aPos
= maCells
.position(it
, nNextRow
);
3012 if (it
->type
!= sc::element_type_empty
)
3013 nLastRow
= nNextRow
;
3015 while (it
->type
!= sc::element_type_empty
);
3020 bool ScColumn::HasDataAt(SCROW nRow
) const
3022 return maCells
.get_type(nRow
) != sc::element_type_empty
;
3025 bool ScColumn::IsAllAttrEqual( const ScColumn
& rCol
, SCROW nStartRow
, SCROW nEndRow
) const
3027 if (pAttrArray
&& rCol
.pAttrArray
)
3028 return pAttrArray
->IsAllEqual( *rCol
.pAttrArray
, nStartRow
, nEndRow
);
3030 return !pAttrArray
&& !rCol
.pAttrArray
;
3033 bool ScColumn::IsVisibleAttrEqual( const ScColumn
& rCol
, SCROW nStartRow
, SCROW nEndRow
) const
3035 if (pAttrArray
&& rCol
.pAttrArray
)
3036 return pAttrArray
->IsVisibleEqual( *rCol
.pAttrArray
, nStartRow
, nEndRow
);
3038 return !pAttrArray
&& !rCol
.pAttrArray
;
3041 bool ScColumn::GetFirstVisibleAttr( SCROW
& rFirstRow
) const
3044 return pAttrArray
->GetFirstVisibleAttr( rFirstRow
);
3049 bool ScColumn::GetLastVisibleAttr( SCROW
& rLastRow
) const
3053 // row of last cell is needed
3054 SCROW nLastData
= GetLastDataPos(); // always including notes, 0 if none
3056 return pAttrArray
->GetLastVisibleAttr( rLastRow
, nLastData
);
3062 bool ScColumn::HasVisibleAttrIn( SCROW nStartRow
, SCROW nEndRow
) const
3065 return pAttrArray
->HasVisibleAttrIn( nStartRow
, nEndRow
);
3072 class FindUsedRowsHandler
3074 typedef mdds::flat_segment_tree
<SCROW
,bool> UsedRowsType
;
3075 UsedRowsType
& mrUsed
;
3076 UsedRowsType::const_iterator miUsed
;
3078 explicit FindUsedRowsHandler(UsedRowsType
& rUsed
) : mrUsed(rUsed
), miUsed(rUsed
.begin()) {}
3080 void operator() (const sc::CellStoreType::value_type
& node
, size_t nOffset
, size_t nDataSize
)
3082 if (node
.type
== sc::element_type_empty
)
3085 SCROW nRow1
= node
.position
+ nOffset
;
3086 SCROW nRow2
= nRow1
+ nDataSize
- 1;
3087 miUsed
= mrUsed
.insert(miUsed
, nRow1
, nRow2
+1, true).first
;
3093 void ScColumn::FindUsed( SCROW nStartRow
, SCROW nEndRow
, mdds::flat_segment_tree
<SCROW
,bool>& rUsed
) const
3095 FindUsedRowsHandler
aFunc(rUsed
);
3096 sc::ParseBlock(maCells
.begin(), maCells
, aFunc
, nStartRow
, nEndRow
);
3101 void startListening(
3102 sc::BroadcasterStoreType
& rStore
, sc::BroadcasterStoreType::iterator
& itBlockPos
, size_t nElemPos
,
3103 SCROW nRow
, SvtListener
& rLst
)
3105 switch (itBlockPos
->type
)
3107 case sc::element_type_broadcaster
:
3109 // Broadcaster already exists here.
3110 SvtBroadcaster
* pBC
= sc::broadcaster_block::at(*itBlockPos
->data
, nElemPos
);
3111 rLst
.StartListening(*pBC
);
3114 case mdds::mtv::element_type_empty
:
3116 // No broadcaster exists at this position yet.
3117 SvtBroadcaster
* pBC
= new SvtBroadcaster
;
3118 rLst
.StartListening(*pBC
);
3119 itBlockPos
= rStore
.set(itBlockPos
, nRow
, pBC
); // Store the block position for next iteration.
3123 #if DEBUG_COLUMN_STORAGE
3124 cout
<< "ScColumn::StartListening: wrong block type encountered in the broadcaster storage." << endl
;
3135 void ScColumn::StartListening( SvtListener
& rLst
, SCROW nRow
)
3137 std::pair
<sc::BroadcasterStoreType::iterator
,size_t> aPos
= maBroadcasters
.position(nRow
);
3138 startListening(maBroadcasters
, aPos
.first
, aPos
.second
, nRow
, rLst
);
3141 void ScColumn::EndListening( SvtListener
& rLst
, SCROW nRow
)
3143 SvtBroadcaster
* pBC
= GetBroadcaster(nRow
);
3147 rLst
.EndListening(*pBC
);
3148 if (!pBC
->HasListeners())
3149 // There is no more listeners for this cell. Remove the broadcaster.
3150 maBroadcasters
.set_empty(nRow
, nRow
);
3153 void ScColumn::StartListening( sc::StartListeningContext
& rCxt
, const ScAddress
& rAddress
, SvtListener
& rLst
)
3155 if (!ValidRow(rAddress
.Row()))
3158 sc::ColumnBlockPosition
* p
= rCxt
.getBlockPosition(rAddress
.Tab(), rAddress
.Col());
3162 sc::BroadcasterStoreType::iterator
& it
= p
->miBroadcasterPos
;
3163 std::pair
<sc::BroadcasterStoreType::iterator
,size_t> aPos
= maBroadcasters
.position(it
, rAddress
.Row());
3164 it
= aPos
.first
; // store the block position for next iteration.
3165 startListening(maBroadcasters
, it
, aPos
.second
, rAddress
.Row(), rLst
);
3168 void ScColumn::EndListening( sc::EndListeningContext
& rCxt
, const ScAddress
& rAddress
, SvtListener
& rListener
)
3170 sc::ColumnBlockPosition
* p
= rCxt
.getBlockPosition(rAddress
.Tab(), rAddress
.Col());
3174 sc::BroadcasterStoreType::iterator
& it
= p
->miBroadcasterPos
;
3175 std::pair
<sc::BroadcasterStoreType::iterator
,size_t> aPos
= maBroadcasters
.position(it
, rAddress
.Row());
3176 it
= aPos
.first
; // store the block position for next iteration.
3177 if (it
->type
!= sc::element_type_broadcaster
)
3180 SvtBroadcaster
* pBC
= sc::broadcaster_block::at(*it
->data
, aPos
.second
);
3183 rListener
.EndListening(*pBC
);
3184 if (!pBC
->HasListeners())
3185 // There is no more listeners for this cell. Add it to the purge list for later purging.
3186 rCxt
.addEmptyBroadcasterPosition(rAddress
.Tab(), rAddress
.Col(), rAddress
.Row());
3191 class CompileDBFormulaHandler
3193 sc::CompileFormulaContext
& mrCxt
;
3196 explicit CompileDBFormulaHandler( sc::CompileFormulaContext
& rCxt
) :
3199 void operator() (size_t, ScFormulaCell
* p
)
3201 p
->CompileDBFormula(mrCxt
);
3205 struct CompileColRowNameFormulaHandler
3207 sc::CompileFormulaContext
& mrCxt
;
3209 explicit CompileColRowNameFormulaHandler( sc::CompileFormulaContext
& rCxt
) : mrCxt(rCxt
) {}
3211 void operator() (size_t, ScFormulaCell
* p
)
3213 p
->CompileColRowNameFormula(mrCxt
);
3219 void ScColumn::CompileDBFormula( sc::CompileFormulaContext
& rCxt
)
3221 CompileDBFormulaHandler
aFunc(rCxt
);
3222 sc::ProcessFormula(maCells
, aFunc
);
3223 RegroupFormulaCells();
3226 void ScColumn::CompileColRowNameFormula( sc::CompileFormulaContext
& rCxt
)
3228 CompileColRowNameFormulaHandler
aFunc(rCxt
);
3229 sc::ProcessFormula(maCells
, aFunc
);
3230 RegroupFormulaCells();
3235 class UpdateSubTotalHandler
3237 ScFunctionData
& mrData
;
3239 void update(double fVal
, bool bVal
)
3244 switch (mrData
.eFunc
)
3246 case SUBTOTAL_FUNC_SUM
:
3247 case SUBTOTAL_FUNC_AVE
:
3253 if (!SubTotal::SafePlus(mrData
.nVal
, fVal
))
3254 mrData
.bError
= true;
3257 case SUBTOTAL_FUNC_CNT
: // only the value
3265 case SUBTOTAL_FUNC_CNT2
: // everything
3268 case SUBTOTAL_FUNC_MAX
:
3273 if (++mrData
.nCount
== 1 || fVal
> mrData
.nVal
)
3277 case SUBTOTAL_FUNC_MIN
:
3282 if (++mrData
.nCount
== 1 || fVal
< mrData
.nVal
)
3288 // added to avoid warnings
3294 explicit UpdateSubTotalHandler(ScFunctionData
& rData
) : mrData(rData
) {}
3296 void operator() (size_t /*nRow*/, double fVal
)
3301 void operator() (size_t /*nRow*/, const svl::SharedString
&)
3306 void operator() (size_t /*nRow*/, const EditTextObject
*)
3311 void operator() (size_t /*nRow*/, ScFormulaCell
* pCell
)
3315 if (mrData
.eFunc
!= SUBTOTAL_FUNC_CNT2
) // it doesn't interest us
3318 if (pCell
->GetErrCode() != FormulaError::NONE
)
3320 if (mrData
.eFunc
!= SUBTOTAL_FUNC_CNT
) // simply remove from count
3321 mrData
.bError
= true;
3323 else if (pCell
->IsValue())
3325 fVal
= pCell
->GetValue();
3337 // multiple selections:
3338 void ScColumn::UpdateSelectionFunction(
3339 const ScRangeList
& rRanges
, ScFunctionData
& rData
, const ScFlatBoolRowSegments
& rHiddenRows
)
3341 sc::SingleColumnSpanSet aSpanSet
;
3342 aSpanSet
.scan(rRanges
, nTab
, nCol
); // mark all selected rows.
3344 if (aSpanSet
.empty())
3345 return; // nothing to do, bail out
3347 // Exclude all hidden rows.
3348 ScFlatBoolRowSegments::RangeData aRange
;
3350 while (nRow
<= MAXROW
)
3352 if (!rHiddenRows
.getRangeData(nRow
, aRange
))
3356 // Hidden range detected.
3357 aSpanSet
.set(nRow
, aRange
.mnRow2
, false);
3359 nRow
= aRange
.mnRow2
+ 1;
3362 sc::SingleColumnSpanSet::SpansType aSpans
;
3363 aSpanSet
.getSpans(aSpans
);
3365 sc::SingleColumnSpanSet::SpansType::const_iterator it
= aSpans
.begin(), itEnd
= aSpans
.end();
3367 switch (rData
.eFunc
)
3369 case SUBTOTAL_FUNC_SELECTION_COUNT
:
3371 // Simply count selected rows regardless of cell contents.
3372 for (; it
!= itEnd
; ++it
)
3373 rData
.nCount
+= it
->mnRow2
- it
->mnRow1
+ 1;
3376 case SUBTOTAL_FUNC_CNT2
:
3378 // We need to parse all non-empty cells.
3379 sc::CellStoreType::const_iterator itCellPos
= maCells
.begin();
3380 UpdateSubTotalHandler
aFunc(rData
);
3381 for (; it
!= itEnd
; ++it
)
3383 itCellPos
= sc::ParseAllNonEmpty(
3384 itCellPos
, maCells
, it
->mnRow1
, it
->mnRow2
, aFunc
);
3390 // We need to parse only numeric values.
3391 sc::CellStoreType::const_iterator itCellPos
= maCells
.begin();
3392 UpdateSubTotalHandler
aFunc(rData
);
3393 for (; it
!= itEnd
; ++it
)
3395 itCellPos
= sc::ParseFormulaNumeric(
3396 itCellPos
, maCells
, it
->mnRow1
, it
->mnRow2
, aFunc
);
3404 class WeightedCounter
3408 WeightedCounter() : mnCount(0) {}
3410 void operator() (const sc::CellStoreType::value_type
& node
)
3414 case sc::element_type_numeric
:
3415 case sc::element_type_string
:
3416 mnCount
+= node
.size
;
3418 case sc::element_type_formula
:
3420 // Each formula cell is worth its code length plus 5.
3421 sc::formula_block::const_iterator it
= sc::formula_block::begin(*node
.data
);
3422 sc::formula_block::const_iterator itEnd
= sc::formula_block::end(*node
.data
);
3423 for (; it
!= itEnd
; ++it
)
3425 const ScFormulaCell
* p
= *it
;
3426 mnCount
+= 5 + p
->GetCode()->GetCodeLen();
3430 case sc::element_type_edittext
:
3431 // each edit-text cell is worth 50.
3432 mnCount
+= node
.size
* 50;
3439 size_t getCount() const { return mnCount
; }
3444 sal_uInt32
ScColumn::GetWeightedCount() const
3446 WeightedCounter aFunc
;
3447 std::for_each(maCells
.begin(), maCells
.end(), aFunc
);
3448 return aFunc
.getCount();
3457 CodeCounter() : mnCount(0) {}
3459 void operator() (size_t, const ScFormulaCell
* p
)
3461 mnCount
+= p
->GetCode()->GetCodeLen();
3464 size_t getCount() const { return mnCount
; }
3469 sal_uInt32
ScColumn::GetCodeCount() const
3472 sc::ParseFormula(maCells
, aFunc
);
3473 return aFunc
.getCount();
3476 SCSIZE
ScColumn::GetPatternCount() const
3478 return pAttrArray
? pAttrArray
->Count() : 0;
3481 SCSIZE
ScColumn::GetPatternCount( SCROW nRow1
, SCROW nRow2
) const
3483 return pAttrArray
? pAttrArray
->Count( nRow1
, nRow2
) : 0;
3486 bool ScColumn::ReservePatternCount( SCSIZE nReserve
)
3488 return pAttrArray
&& pAttrArray
->Reserve( nReserve
);
3491 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */