Move token-cache for doubles to ScInterpreterContext...
[LibreOffice.git] / sc / source / core / data / column2.cxx
blobfea8d080dd133d969ff407af7eb5de407345a2cf
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <column.hxx>
21 #include <docsh.hxx>
22 #include <scitems.hxx>
23 #include <formulacell.hxx>
24 #include <document.hxx>
25 #include <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>
32 #include <brdcst.hxx>
33 #include <editutil.hxx>
34 #include <subtotal.hxx>
35 #include <markdata.hxx>
36 #include <compiler.hxx>
37 #include <dbdata.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>
72 #include <algorithm>
73 #include <memory>
75 #include <math.h>
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 );
88 // Data operations
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.
100 return 0;
102 long nValue = 0;
103 ScRefCellValue aCell = GetCellValue(it, aPos.second);
104 double nPPT = bWidth ? nPPTX : nPPTY;
106 const ScPatternAttr* pPattern = rOptions.pPattern;
107 if (!pPattern)
108 pPattern = pAttrArray->GetPattern( nRow );
110 // merged?
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));
116 if ( bWidth )
118 if ( pFlag->IsHorOverlapped() )
119 return 0;
120 if ( rOptions.bSkipMerged && pMerge->GetColMerge() > 1 )
121 return 0;
123 else
125 if ( pFlag->IsVerOverlapped() )
126 return 0;
127 if ( rOptions.bSkipMerged && pMerge->GetRowMerge() > 1 )
128 return 0;
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 );
138 if (ppPatternChange)
139 *ppPatternChange = pPattern;
141 // line break?
143 const SfxPoolItem* pCondItem;
144 SvxCellHorJustify eHorJust;
145 if (pCondSet &&
146 pCondSet->GetItemState(ATTR_HOR_JUSTIFY, true, &pCondItem) == SfxItemState::SET)
147 eHorJust = static_cast<const SvxHorJustifyItem*>(pCondItem)->GetValue();
148 else
149 eHorJust = static_cast<const SvxHorJustifyItem&>(
150 pPattern->GetItem( ATTR_HOR_JUSTIFY )).GetValue();
151 bool bBreak;
152 if ( eHorJust == SvxCellHorJustify::Block )
153 bBreak = true;
154 else if ( pCondSet &&
155 pCondSet->GetItemState(ATTR_LINEBREAK, true, &pCondItem) == SfxItemState::SET)
156 bBreak = static_cast<const SfxBoolItem*>(pCondItem)->GetValue();
157 else
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 );
175 if (ppPatternChange)
176 *ppPatternChange = pPattern; // XXX caller may have to check for change!
178 if (bNumeric)
180 if (!bMayInvalidatePattern || pPattern == pOldPattern)
181 bBreak = false;
182 else
184 nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet );
185 if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
186 bBreak = false;
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 )
197 bBreak = false;
199 if ( bWidth && bBreak ) // after determining bAsianVertical (bBreak may be reset)
200 return 0;
202 long nRotate = 0;
203 SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD;
204 if ( eOrient == SvxCellOrientation::Standard )
206 if (pCondSet &&
207 pCondSet->GetItemState(ATTR_ROTATE_VALUE, true, &pCondItem) == SfxItemState::SET)
208 nRotate = static_cast<const SfxInt32Item*>(pCondItem)->GetValue();
209 else
210 nRotate =static_cast<const SfxInt32Item&>(pPattern->GetItem(ATTR_ROTATE_VALUE)).GetValue();
211 if ( nRotate )
213 if (pCondSet &&
214 pCondSet->GetItemState(ATTR_ROTATE_MODE, true, &pCondItem) == SfxItemState::SET)
215 eRotMode = static_cast<const SvxRotateModeItem*>(pCondItem)->GetValue();
216 else
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;
229 nRotate = 0;
230 bAsianVertical = false;
233 const SvxMarginItem* pMargin;
234 if (pCondSet &&
235 pCondSet->GetItemState(ATTR_MARGIN, true, &pCondItem) == SfxItemState::SET)
236 pMargin = static_cast<const SvxMarginItem*>(pCondItem);
237 else
238 pMargin = static_cast<const SvxMarginItem*>(&pPattern->GetItem(ATTR_MARGIN));
239 sal_uInt16 nIndent = 0;
240 if ( eHorJust == SvxCellHorJustify::Left )
242 if (pCondSet &&
243 pCondSet->GetItemState(ATTR_INDENT, true, &pCondItem) == SfxItemState::SET)
244 nIndent = static_cast<const SfxUInt16Item*>(pCondItem)->GetValue();
245 else
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;
257 vcl::Font aFont;
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
273 Color* pColor;
274 OUString aValStr;
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;
289 else if ( nRotate )
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 );
297 long nWidth;
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 );
303 bAddMargin = false;
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 );
310 else
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 )
318 nHeight = nCmp;
321 aSize = Size( nWidth, nHeight );
323 nValue = bWidth ? aSize.Width() : aSize.Height();
325 if ( bAddMargin )
327 if (bWidth)
329 nValue += (long) ( pMargin->GetLeftMargin() * nPPT ) +
330 (long) ( pMargin->GetRightMargin() * nPPT );
331 if ( nIndent )
332 nValue += (long) ( nIndent * nPPT );
334 else
335 nValue += (long) ( pMargin->GetTopMargin() * nPPT ) +
336 (long) ( pMargin->GetBottomMargin() * nPPT );
339 // linebreak done ?
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() -
348 nIndent )
349 * nPPT );
350 nDocPixel = (nDocPixel * 9) / 10; // for safety
351 if ( aSize.Width() > nDocPixel )
352 bEditEngine = true;
357 if (bEditEngine)
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();
370 if ( bTextWysiwyg )
371 nCtrl |= EEControlBits::FORMAT100;
372 else
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 );
386 else
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 )
402 aPaper.Width() = 1;
403 else if (bBreak)
405 double fWidthFactor = nPPTX;
406 if ( bTextWysiwyg )
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();
418 if (nColMerge > 1)
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)
424 if ( nIndent )
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;
433 if ( !bTextWysiwyg )
434 aPaper = pDev->PixelToLogic( aPaper, aHMMMode );
436 pEngine->SetPaperSize(aPaper);
438 if (aCell.meType == CELLTYPE_EDIT)
440 pEngine->SetTextNewDefaults(*aCell.mpEditText, pSet);
442 else
444 Color* pColor;
445 OUString aString;
446 ScCellFormat::GetString(
447 aCell, nFormat, aString, &pColor, *pFormatter, pDocument, true,
448 rOptions.bFormula);
450 if (!aString.isEmpty())
451 pEngine->SetTextNewDefaults(aString, pSet);
452 else
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;
463 if ( nRotate )
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 );
472 long nWidth;
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 );
478 bAddMargin = false;
479 if ( pPattern->GetRotateDir( pCondSet ) == ScRotateDir::Right )
480 nWidth += (long)( pDocument->GetRowHeight( nRow,nTab ) *
481 nPPT * nCosAbs / nSinAbs );
483 else
484 nWidth = (long)( aSize.Height() / nSinAbs ); //TODO: limit?
485 aSize = Size( nWidth, nHeight );
487 Size aPixSize = pDev->LogicToPixel( aSize, aHMMMode );
488 if ( bEdWidth )
489 nValue = aPixSize.Width();
490 else
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;
498 if ( nValue > nCmp )
499 nValue = nCmp;
503 else if ( bEdWidth )
505 if (bBreak)
506 nValue = 0;
507 else
508 nValue = pDev->LogicToPixel(Size( pEngine->CalcTextWidth(), 0 ),
509 aHMMMode).Width();
511 else // height
513 nValue = pDev->LogicToPixel(Size( 0, pEngine->GetTextHeight() ),
514 aHMMMode).Height();
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 )
530 if (bWidth)
532 nValue += (long) ( pMargin->GetLeftMargin() * nPPT ) +
533 (long) ( pMargin->GetRightMargin() * nPPT );
534 if (nIndent)
535 nValue += (long) ( nIndent * nPPT );
537 else
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 );
559 if (bWidth)
561 // place for Autofilter Button
562 // 20 * nZoom/100
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);
569 return nValue;
572 namespace {
574 class MaxStrLenFinder
576 ScDocument& mrDoc;
577 sal_uInt32 mnFormat;
578 OUString maMaxLenStr;
579 sal_Int32 mnMaxLen;
581 void checkLength(ScRefCellValue& rCell)
583 Color* pColor;
584 OUString aValStr;
585 ScCellFormat::GetString(
586 rCell, mnFormat, aValStr, &pColor, *mrDoc.GetFormatTable(), &mrDoc);
588 if (aValStr.getLength() > mnMaxLen)
590 mnMaxLen = aValStr.getLength();
591 maMaxLenStr = aValStr;
595 public:
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);
602 checkLength(aCell);
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);
617 checkLength(aCell);
620 void operator() (size_t /*nRow*/, const ScFormulaCell* p)
622 ScRefCellValue aCell(const_cast<ScFormulaCell*>(p));
623 checkLength(aCell);
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.
637 return nOldWidth;
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);
646 else
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);
651 bool bFound = false;
653 if ( pParam && pParam->mbSimpleText )
654 { // all the same except for number format
655 const ScPatternAttr* pPattern = GetPattern( 0 );
656 vcl::Font aFont;
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 );
667 OUString aLongStr;
668 Color* pColor;
669 if (pParam->mnMaxTextRow >= 0)
671 ScRefCellValue aCell = GetCellValue(pParam->mnMaxTextRow);
672 ScCellFormat::GetString(
673 aCell, nFormat, aLongStr, &pColor, *pFormatter, pDocument);
675 else
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);
690 bFound = true;
693 else
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;
705 SCROW nRow = nRow1;
706 while (nRow <= nRow2)
708 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
709 itPos = aPos.first;
710 if (itPos->type == sc::element_type_empty)
712 // Skip empty cells.
713 nRow += itPos->size - aPos.second;
714 continue;
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);
729 if (nThis)
731 if (nThis > nWidth || !bFound)
733 nWidth = nThis;
734 bFound = true;
742 if (bFound)
744 nWidth += 2;
745 sal_uInt16 nTwips = (sal_uInt16) (nWidth / nPPTX);
746 return nTwips;
748 else
749 return nOldWidth;
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();
758 nHeight *= 1.18;
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;
779 return nHeight;
782 // pHeight in Twips
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() );
792 SCROW nStart = -1;
793 SCROW nEnd = -1;
794 SCROW nEditPos = 0;
795 SCROW nNextEnd = 0;
797 // with conditional formatting, always consider the individual cells
799 const ScPatternAttr* pPattern = aIter.Next(nStart,nEnd);
800 while ( pPattern )
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
810 else
812 bool bStdAllowed = (pPattern->GetCellOrientation() == SvxCellOrientation::Standard);
813 bool bStdOnly = false;
814 if (bStdAllowed)
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);
820 bStdOnly = !bBreak;
822 // conditional formatting: loop all cells
823 if (bStdOnly &&
824 !static_cast<const ScCondFormatItem&>(pPattern->GetItem(
825 ATTR_CONDITIONAL)).GetCondFormatData().empty())
827 bStdOnly = false;
830 // rotated text: loop all cells
831 if ( bStdOnly && static_cast<const SfxInt32Item&>(pPattern->
832 GetItem(ATTR_ROTATE_VALUE)).GetValue() )
833 bStdOnly = false;
836 if (bStdOnly)
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
844 // changed.
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)
852 bStdOnly = false;
853 if (nEnd > nEditPos)
854 nNextEnd = nEnd;
855 nEnd = nEditPos; // calculate single
856 bStdAllowed = false; // will be computed in any case per cell
858 else
860 nNextEnd = nEnd;
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);
871 if (bStdAllowed)
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 );
882 else
883 nDefHeight = nLatHeight = lcl_GetAttribHeight( *pPattern, ATTR_FONT_HEIGHT );
885 // if everything below is already larger, the loop doesn't have to
886 // be run again
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; });
894 if ( bStdOnly )
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)
907 continue;
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);
923 else
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);
960 nNextEnd = 0;
968 if (nNextEnd > 0)
970 nStart = nEnd + 1;
971 nEnd = nNextEnd;
972 nNextEnd = 0;
974 else
975 pPattern = aIter.Next(nStart,nEnd);
979 bool ScColumn::GetNextSpellingCell(SCROW& nRow, bool bInSel, const ScMarkData& rData) const
981 bool bStop = false;
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)) )
989 return true;
991 while (!bStop)
993 if (bInSel)
995 nRow = rData.GetNextMarked(nCol, nRow, false);
996 if (!ValidRow(nRow))
998 nRow = MAXROW+1;
999 bStop = true;
1001 else
1003 it = maCells.position(it, nRow).first;
1004 eType = it->type;
1005 if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
1006 !(HasAttrib( nRow, nRow, HasAttrFlags::Protected) &&
1007 pDocument->IsTabProtected(nTab)) )
1008 return true;
1009 else
1010 nRow++;
1013 else if (GetNextDataPos(nRow))
1015 it = maCells.position(it, nRow).first;
1016 eType = it->type;
1017 if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
1018 !(HasAttrib( nRow, nRow, HasAttrFlags::Protected) &&
1019 pDocument->IsTabProtected(nTab)) )
1020 return true;
1021 else
1022 nRow++;
1024 else
1026 nRow = MAXROW+1;
1027 bStop = true;
1030 return false;
1033 namespace {
1035 class StrEntries
1037 sc::CellStoreType& mrCells;
1039 protected:
1040 struct StrEntry
1042 SCROW mnRow;
1043 OUString maStr;
1045 StrEntry(SCROW nRow, const OUString& rStr) : mnRow(nRow), maStr(rStr) {}
1048 std::vector<StrEntry> maStrEntries;
1049 ScDocument* mpDoc;
1051 StrEntries(sc::CellStoreType& rCells, ScDocument* pDoc) : mrCells(rCells), mpDoc(pDoc) {}
1053 public:
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;
1068 public:
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
1079 if (!mpEngine)
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);
1092 if ( rOld.Count() )
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.
1113 delete pObj;
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
1127 SCTAB mnTab;
1128 bool mbTestResult;
1129 public:
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;
1162 namespace {
1164 class CellCounter
1166 size_t mnCount;
1167 public:
1168 CellCounter() : mnCount(0) {}
1170 void operator() (
1171 const sc::CellStoreType::value_type& node, size_t /*nOffset*/, size_t nDataSize)
1173 if (node.type == sc::element_type_empty)
1174 return;
1176 mnCount += nDataSize;
1179 size_t getCount() const { return mnCount; }
1184 SCSIZE ScColumn::VisibleCount( SCROW nStartRow, SCROW nEndRow ) const
1186 CellCounter aFunc;
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.
1197 return false;
1199 return it->type != sc::element_type_empty;
1202 bool ScColumn::IsEmptyAttr() const
1204 if (pAttrArray)
1205 return pAttrArray->IsEmpty();
1206 else
1207 return true;
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.
1216 return false;
1218 if (it->type != sc::element_type_empty)
1219 // Non-empty cell at the start position.
1220 return false;
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.
1233 return false;
1235 if (it->type != sc::element_type_empty)
1236 // Non-empty cell at the start position.
1237 return false;
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.
1247 switch (eDir)
1249 case DIR_TOP:
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.
1257 return 0;
1259 // length of this empty block minus the offset.
1260 size_t nThisLen = it->size - aPos.second;
1261 return std::min(nThisLen, nLength);
1263 break;
1264 case DIR_BOTTOM:
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.
1272 return 0;
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);
1278 break;
1279 default:
1283 return 0;
1286 SCROW ScColumn::GetFirstDataPos() const
1288 if (IsEmptyData())
1289 return 0;
1291 sc::CellStoreType::const_iterator it = maCells.begin();
1292 if (it->type != sc::element_type_empty)
1293 return 0;
1295 return it->size;
1298 SCROW ScColumn::GetLastDataPos() const
1300 if (IsEmptyData())
1301 return 0;
1303 sc::CellStoreType::const_reverse_iterator it = maCells.rbegin();
1304 if (it->type != sc::element_type_empty)
1305 return MAXROW;
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)
1314 return nLastRow;
1316 if (aPos.first == maCells.begin())
1317 // This is the first block, and is empty.
1318 return 0;
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())
1328 return false;
1330 if (it->type == sc::element_type_empty)
1332 if (it == maCells.begin())
1333 // No more previous non-empty cell.
1334 return false;
1336 rRow -= aPos.second + 1; // Last row position of the previous block.
1337 return true;
1340 // This block is not empty.
1341 if (aPos.second)
1343 // There are preceding cells in this block. Simply move back one cell.
1344 --rRow;
1345 return true;
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.
1351 return false;
1353 --rRow; // Move to the last cell of the previous block.
1354 --it;
1355 if (it->type == sc::element_type_empty)
1357 // This block is empty.
1358 if (it == maCells.begin())
1359 // No more preceding blocks.
1360 return false;
1362 // Skip the whole empty block segment.
1363 rRow -= it->size;
1366 return true;
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())
1374 return false;
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;
1380 ++it;
1381 if (it == maCells.end())
1382 // No more next block.
1383 return false;
1385 // Next block exists, and is non-empty.
1386 return true;
1389 if (aPos.second < it->size - 1)
1391 // There are still cells following the current position.
1392 ++rRow;
1393 return true;
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.
1398 ++it;
1399 if (it == maCells.end())
1400 // No more next block.
1401 return false;
1403 if (it->type == sc::element_type_empty)
1405 // Next block is empty. Move to the next block.
1406 rRow += it->size;
1407 ++it;
1408 if (it == maCells.end())
1409 return false;
1412 return true;
1415 SCROW ScColumn::FindNextVisibleRow(SCROW nRow, bool bForward) const
1417 if(bForward)
1419 nRow++;
1420 SCROW nEndRow = 0;
1421 bool bHidden = pDocument->RowHidden(nRow, nTab, nullptr, &nEndRow);
1422 if(bHidden)
1423 return std::min<SCROW>(MAXROW, nEndRow + 1);
1424 else
1425 return nRow;
1427 else
1429 nRow--;
1430 SCROW nStartRow = MAXROW;
1431 bool bHidden = pDocument->RowHidden(nRow, nTab, &nStartRow);
1432 if(bHidden)
1433 return std::max<SCROW>(0, nStartRow - 1);
1434 else
1435 return nRow;
1439 SCROW ScColumn::FindNextVisibleRowWithContent(
1440 sc::CellStoreType::const_iterator& itPos, SCROW nRow, bool bForward) const
1442 if (bForward)
1446 nRow++;
1447 SCROW nEndRow = 0;
1448 bool bHidden = pDocument->RowHidden(nRow, nTab, nullptr, &nEndRow);
1449 if (bHidden)
1451 nRow = nEndRow + 1;
1452 if(nRow >= MAXROW)
1453 return MAXROW;
1456 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
1457 itPos = aPos.first;
1458 if (itPos == maCells.end())
1459 // Invalid row.
1460 return MAXROW;
1462 if (itPos->type != sc::element_type_empty)
1463 return nRow;
1465 // Move to the last cell of the current empty block.
1466 nRow += itPos->size - aPos.second - 1;
1468 while (nRow < MAXROW);
1470 return MAXROW;
1475 nRow--;
1476 SCROW nStartRow = MAXROW;
1477 bool bHidden = pDocument->RowHidden(nRow, nTab, &nStartRow);
1478 if (bHidden)
1480 nRow = nStartRow - 1;
1481 if(nRow <= 0)
1482 return 0;
1485 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
1486 itPos = aPos.first;
1487 if (itPos == maCells.end())
1488 // Invalid row.
1489 return 0;
1491 if (itPos->type != sc::element_type_empty)
1492 return nRow;
1494 // Move to the first cell of the current empty block.
1495 nRow -= aPos.second;
1497 while (nRow > 0);
1499 return 0;
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;
1510 cout.flush();
1511 abort();
1514 if (maCellTextAttrs.size() != MAXROWCOUNT)
1516 cout << "ScColumn::CellStorageModified: Size of the cell text attribute array is incorrect." << endl;
1517 cout.flush();
1518 abort();
1521 if (maBroadcasters.size() != MAXROWCOUNT)
1523 cout << "ScColumn::CellStorageModified: Size of the broadcaster array is incorrect." << endl;
1524 cout.flush();
1525 abort();
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)
1534 ++itCell;
1536 while (itAttr != maCellTextAttrs.end() && itAttr->type != sc::element_type_empty)
1537 ++itAttr;
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);
1548 cout.flush();
1549 abort();
1552 // Move to the next empty blocks.
1553 ++itCell;
1554 while (itCell != maCells.end() && itCell->type != sc::element_type_empty)
1555 ++itCell;
1557 ++itAttr;
1558 while (itAttr != maCellTextAttrs.end() && itAttr->type != sc::element_type_empty)
1559 ++itAttr;
1561 #else
1562 (void) this; // Avoid "this member function can be declared static [loplugin:staticmethods]"
1563 #endif
1566 #if DUMP_COLUMN_STORAGE
1568 namespace {
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
1580 switch (rNode.type)
1582 case sc::element_type_numeric:
1583 cout << " * numeric block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1584 break;
1585 case sc::element_type_string:
1586 cout << " * string block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1587 break;
1588 case sc::element_type_edittext:
1589 cout << " * edit-text block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1590 break;
1591 case sc::element_type_formula:
1592 dumpFormulaBlock(rNode);
1593 break;
1594 case sc::element_type_empty:
1595 cout << " * empty block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
1596 break;
1597 default:
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);
1615 printResult(pCell);
1616 continue;
1619 if (pCell->GetSharedTopRow() != pCell->aPos.Row())
1621 cout << " * row " << pCell->aPos.Row() << " shared with top row "
1622 << pCell->GetSharedTopRow() << " with length " << pCell->GetSharedLength()
1623 << endl;
1624 continue;
1627 SCROW nLen = pCell->GetSharedLength();
1628 cout << " * group: start=" << pCell->aPos.Row() << ", length=" << nLen << endl;
1629 printFormula(pCell);
1630 printResult(pCell);
1632 if (nLen > 1)
1634 for (SCROW i = 0; i < nLen-1; ++i, ++it)
1636 pCell = *it;
1637 printResult(pCell);
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)";
1659 break;
1660 case sc::FormulaResultValue::String:
1661 cout << "'" << aRes.maString.getString() << "' (type: string)";
1662 break;
1663 case sc::FormulaResultValue::Error:
1664 cout << "error (" << static_cast<int>(aRes.mnError) << ")";
1665 break;
1666 case sc::FormulaResultValue::Invalid:
1667 cout << "invalid";
1668 break;
1671 cout << endl;
1673 #else
1674 void printResult(const ScFormulaCell*) const
1676 (void) this; /* loplugin:staticmethods */
1678 #endif
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;
1689 #endif
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)
1705 // Found.
1706 nOffsetInBlock = nRowPos - nBlockStart;
1707 break;
1711 if (itBlk == itBlkEnd)
1712 // Specified range not found. Bail out.
1713 return;
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;
1722 if (!itBlk->data)
1724 // Empty block.
1725 if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
1726 // This block contains the end row.
1727 rDestCol.maCellTextAttrs.set_empty(nBlockStart + nOffsetInBlock, nRowPos);
1728 else
1729 rDestCol.maCellTextAttrs.set_empty(nBlockStart + nOffsetInBlock, nBlockEnd-1);
1731 continue;
1734 // Non-empty block.
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);
1747 break;
1750 rDestCol.maCellTextAttrs.set(nBlockStart + nOffsetInBlock, itData, itDataEnd);
1754 namespace {
1756 class CopyCellNotesHandler
1758 ScColumn& mrDestCol;
1759 sc::CellNoteStoreType& mrDestNotes;
1760 sc::CellNoteStoreType::iterator miPos;
1761 SCTAB mnSrcTab;
1762 SCCOL mnSrcCol;
1763 SCTAB mnDestTab;
1764 SCCOL mnDestCol;
1765 SCROW mnDestOffset; /// Add this to the source row position to get the destination row.
1766 bool mbCloneCaption;
1768 public:
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.
1798 return;
1800 ScDrawLayer *pDrawLayer = rDestCol.GetDoc().GetDrawLayer();
1801 bool bWasLocked = bool();
1802 if (pDrawLayer)
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);
1811 if (pDrawLayer)
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)
1869 return nullptr;
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);
1880 namespace {
1881 class CellNoteHandler
1883 const ScDocument* m_pDocument;
1884 const ScAddress m_aAddress; // 'incomplete' address consisting of tab, column
1885 const bool m_bForgetCaptionOwnership;
1887 public:
1888 CellNoteHandler(const ScDocument* pDocument, const ScAddress& rPos, bool bForgetCaptionOwnership) :
1889 m_pDocument(pDocument),
1890 m_aAddress(rPos),
1891 m_bForgetCaptionOwnership(bForgetCaptionOwnership) {}
1893 void operator() ( size_t nRow, ScPostIt* p )
1895 if (m_bForgetCaptionOwnership)
1896 p->ForgetCaption();
1898 // Create a 'complete' address object
1899 ScAddress aAddr(m_aAddress);
1900 aAddr.SetRow(nRow);
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.
1929 return true;
1931 return false;
1934 SCROW ScColumn::GetCellNotesMaxRow() const
1936 // hypothesis : the column has cell notes (should be checked before)
1937 SCROW maxRow = 0;
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;
1944 return maxRow;
1946 SCROW ScColumn::GetCellNotesMinRow() const
1948 // hypothesis : the column has cell notes (should be checked before)
1949 SCROW minRow = 0;
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)
1956 bFound = true;
1957 minRow = it->position;
1960 return minRow;
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)
1972 return;
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;
1993 SCROW nRow = nRow1;
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)
2008 if (nRow > nRow2)
2009 return nScriptType;
2011 sc::CellTextAttr& rVal = *it;
2012 if (UpdateScriptType(rVal, nRow, itrCells))
2013 bUpdated = true;
2014 nScriptType |= rVal.mnScriptType;
2017 else
2019 // Skip this whole block.
2020 nRow += itPos->size - aRet.second;
2023 while (nRow <= nRow2)
2025 ++itPos;
2026 if (itPos == maCellTextAttrs.end())
2027 return nScriptType;
2029 if (itPos->type != sc::element_type_celltextattr)
2031 // Skip this whole block.
2032 nRow += itPos->size;
2033 continue;
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)
2040 if (nRow > nRow2)
2041 return nScriptType;
2043 sc::CellTextAttr& rVal = *it;
2044 if (UpdateScriptType(rVal, nRow, itrCells))
2045 bUpdated = true;
2047 nScriptType |= rVal.mnScriptType;
2051 if (bUpdated)
2052 CellStorageModified();
2054 return nScriptType;
2057 void ScColumn::SetScriptType( SCROW nRow, SvtScriptType nType )
2059 if (!ValidRow(nRow))
2060 return;
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.
2065 return;
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();
2091 switch (it->type)
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);
2101 if (p->IsValue())
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:
2119 default:
2120 // Return a value of 0.0 in all the other cases.
2121 return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0));
2125 namespace {
2127 class ToMatrixHandler
2129 ScMatrix& mrMat;
2130 SCCOL mnMatCol;
2131 SCROW mnTopRow;
2132 ScDocument* mpDoc;
2133 svl::SharedStringPool& mrStrPool;
2134 public:
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);
2150 else
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 )
2169 if (nRow1 > nRow2)
2170 return false;
2172 ToMatrixHandler aFunc(rMat, nMatCol, nRow1, pDocument);
2173 sc::ParseAllNonEmpty(maCells.begin(), maCells, nRow1, nRow2, aFunc);
2174 return true;
2177 namespace {
2179 struct CellBucket
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)
2192 if (mnEmpValCount)
2194 rMat.PutEmptyResultVector(mnEmpValCount, nCol, mnEmpValStart);
2195 reset();
2197 else if (!maNumVals.empty())
2199 const double* p = &maNumVals[0];
2200 rMat.PutDouble(p, maNumVals.size(), nCol, mnNumValStart);
2201 reset();
2203 else if (!maStrVals.empty())
2205 const svl::SharedString* p = &maStrVals[0];
2206 rMat.PutString(p, maStrVals.size(), nCol, mnStrValStart);
2207 reset();
2211 void reset()
2213 mnEmpValStart = mnNumValStart = mnStrValStart = 0;
2214 mnEmpValCount = 0;
2215 maNumVals.clear();
2216 maStrVals.clear();
2220 class FillMatrixHandler
2222 ScMatrix& mrMat;
2223 size_t mnMatCol;
2224 size_t mnTopRow;
2226 ScDocument* mpDoc;
2227 svl::SharedStringPool& mrPool;
2228 svl::SharedStringPool* mpPool; // if matrix is not in the same document
2230 public:
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;
2239 switch (node.type)
2241 case sc::element_type_numeric:
2243 const double* p = &sc::numeric_block::at(*node.data, nOffset);
2244 mrMat.PutDouble(p, nDataSize, mnMatCol, nMatRow);
2246 break;
2247 case sc::element_type_string:
2249 if (!mpPool)
2251 const svl::SharedString* p = &sc::string_block::at(*node.data, nOffset);
2252 mrMat.PutString(p, nDataSize, mnMatCol, nMatRow);
2254 else
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);
2266 break;
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);
2278 if (!mpPool)
2279 aSSs.push_back(mrPool.intern(aStr));
2280 else
2281 aSSs.push_back(mpPool->intern(aStr));
2284 const svl::SharedString* p = &aSSs[0];
2285 mrMat.PutString(p, nDataSize, mnMatCol, nMatRow);
2287 break;
2288 case sc::element_type_formula:
2290 CellBucket aBucket;
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;
2308 else
2310 // First empty result.
2311 aBucket.flush(mrMat, mnMatCol);
2312 aBucket.mnEmpValStart = nThisRow - mnTopRow;
2313 ++aBucket.mnEmpValCount;
2315 continue;
2318 FormulaError nErr;
2319 double fVal;
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);
2330 else
2332 // First number.
2333 aBucket.flush(mrMat, mnMatCol);
2334 aBucket.mnNumValStart = nThisRow - mnTopRow;
2335 aBucket.maNumVals.push_back(fVal);
2337 continue;
2340 svl::SharedString aStr = rCell.GetString();
2341 if (mpPool)
2342 aStr = mpPool->intern(aStr.getString());
2343 if (!aBucket.maStrVals.empty() && nThisRow == nPrevRow + 1)
2345 // Secondary strings.
2346 aBucket.maStrVals.push_back(aStr);
2348 else
2350 // First string.
2351 aBucket.flush(mrMat, mnMatCol);
2352 aBucket.mnStrValStart = nThisRow - mnTopRow;
2353 aBucket.maStrVals.push_back(aStr);
2357 aBucket.flush(mrMat, mnMatCol);
2359 break;
2360 default:
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);
2374 namespace {
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;
2388 else
2390 rDataEnd = rData;
2391 std::advance(rDataEnd, rLenRemain);
2392 rLenRemain = 0;
2396 bool appendToBlock(
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;
2402 double fNan;
2403 rtl::math::setNan(&fNan);
2405 for (sc::CellStoreType::iterator it = _it; it != itEnd; ++it)
2407 switch (it->type)
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();
2418 break;
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();
2431 break;
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
2441 during GetResult
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);
2460 rFC.SetDirtyVar();
2462 return false;
2465 if (aRes.meType == sc::FormulaResultValue::String)
2467 rCxt.ensureStrArray(rColArray, nArrayLen);
2468 (*rColArray.mpStrArray)[nPos] = aRes.maString.getData();
2470 else
2472 rCxt.ensureNumArray(rColArray, nArrayLen);
2473 (*rColArray.mpNumArray)[nPos] = aRes.mfValue;
2477 if (bTempDisableProgress)
2478 pProgress->Enable();
2480 break;
2481 case sc::element_type_empty:
2483 if (nLenRemain > it->size)
2485 nPos += it->size;
2486 nLenRemain -= it->size;
2488 else
2490 nPos = nArrayLen;
2491 nLenRemain = 0;
2494 break;
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;
2504 break;
2505 default:
2506 return false;
2509 if (!nLenRemain)
2510 return true;
2513 return false;
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();
2531 break;
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();
2546 break;
2547 default:
2552 sc::FormulaGroupContext::ColArray*
2553 copyFirstFormulaBlock(
2554 sc::FormulaGroupContext& rCxt, const sc::CellStoreType::iterator& itBlk, size_t nArrayLen,
2555 SCTAB nTab, SCCOL nCol )
2557 double fNan;
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;
2568 itEnd = it;
2569 std::advance(itEnd, nLen);
2570 size_t nPos = 0;
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);
2581 rFC.SetDirtyVar();
2583 return nullptr;
2586 if (aRes.meType == sc::FormulaResultValue::Value)
2588 if (!pNumArray)
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;
2597 else
2599 if (!pStrArray)
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.
2612 return nullptr;
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 )
2636 if (nRow1 > 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);
2642 if (pColArray)
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);
2655 double fNan;
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);
2680 if (!pColArray)
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;
2686 ++itBlk;
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);
2696 break;
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);
2704 if (!pColArray)
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;
2719 ++itBlk;
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);
2731 else
2732 return formula::VectorRefArray(pStr);
2734 break;
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
2740 // not cached.
2741 pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol);
2742 if (!pColArray)
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);
2757 if (!pColArray)
2758 // Failed to insert a new cached column array.
2759 return formula::VectorRefArray(formula::VectorRefArray::Invalid);
2761 size_t nPos = itBlk->size;
2762 ++itBlk;
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);
2775 break;
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);
2783 if (!pColArray)
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;
2792 ++itBlk;
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]);
2798 else
2799 return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]);
2801 break;
2802 default:
2806 return formula::VectorRefArray(formula::VectorRefArray::Invalid);
2809 bool ScColumn::HandleRefArrayForParallelism( SCROW nRow1, SCROW nRow2 )
2811 if (nRow1 > nRow2)
2812 return false;
2814 for (auto i = nRow1; i <= nRow2; ++i)
2816 auto aCell = GetCellValue(i);
2817 if (aCell.meType == CELLTYPE_FORMULA)
2818 aCell.mpFormula->MaybeInterpret();
2821 return true;
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.
2830 return;
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.
2835 return;
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);
2847 else
2848 rCell.SetResultDouble(*pResults);
2849 rCell.ResetDirty();
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.
2860 return;
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.
2865 return;
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());
2875 rCell.ResetDirty();
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.
2888 return;
2890 size_t nBlockLen = it->size - aPos.second;
2891 if (nBlockLen < nLen)
2892 // Length is longer than the length of formula cells. Not good.
2893 return;
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)
2901 continue;
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.
2916 return;
2918 size_t nBlockLen = it->size - aPos.second;
2919 if (nBlockLen < nLen)
2920 // Length is longer than the length of formula cells. Not good.
2921 return;
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))
2942 rBlockSize = 0;
2943 return nullptr;
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())
2950 rBlockSize = 0;
2951 return nullptr;
2954 if (it->type != sc::element_type_formula)
2956 // Not a formula cell.
2957 rBlockSize = 0;
2958 return nullptr;
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())
2981 // Invalid row.
2982 return;
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);
2988 return;
2991 // Current cell is not empty.
2992 SCROW nNextRow = FindNextVisibleRow(rRow, bDown);
2993 aPos = maCells.position(it, nNextRow);
2994 it = aPos.first;
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);
2999 return;
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)
3008 break;
3010 aPos = maCells.position(it, nNextRow);
3011 it = aPos.first;
3012 if (it->type != sc::element_type_empty)
3013 nLastRow = nNextRow;
3015 while (it->type != sc::element_type_empty);
3017 rRow = nLastRow;
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 );
3029 else
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 );
3037 else
3038 return !pAttrArray && !rCol.pAttrArray;
3041 bool ScColumn::GetFirstVisibleAttr( SCROW& rFirstRow ) const
3043 if (pAttrArray)
3044 return pAttrArray->GetFirstVisibleAttr( rFirstRow );
3045 else
3046 return false;
3049 bool ScColumn::GetLastVisibleAttr( SCROW& rLastRow ) const
3051 if (pAttrArray)
3053 // row of last cell is needed
3054 SCROW nLastData = GetLastDataPos(); // always including notes, 0 if none
3056 return pAttrArray->GetLastVisibleAttr( rLastRow, nLastData );
3058 else
3059 return false;
3062 bool ScColumn::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const
3064 if (pAttrArray)
3065 return pAttrArray->HasVisibleAttrIn( nStartRow, nEndRow );
3066 else
3067 return false;
3070 namespace {
3072 class FindUsedRowsHandler
3074 typedef mdds::flat_segment_tree<SCROW,bool> UsedRowsType;
3075 UsedRowsType& mrUsed;
3076 UsedRowsType::const_iterator miUsed;
3077 public:
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)
3083 return;
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);
3099 namespace {
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);
3113 break;
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.
3121 break;
3122 default:
3123 #if DEBUG_COLUMN_STORAGE
3124 cout << "ScColumn::StartListening: wrong block type encountered in the broadcaster storage." << endl;
3125 cout.flush();
3126 abort();
3127 #else
3129 #endif
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);
3144 if (!pBC)
3145 return;
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()))
3156 return;
3158 sc::ColumnBlockPosition* p = rCxt.getBlockPosition(rAddress.Tab(), rAddress.Col());
3159 if (!p)
3160 return;
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());
3171 if (!p)
3172 return;
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)
3178 return;
3180 SvtBroadcaster* pBC = sc::broadcaster_block::at(*it->data, aPos.second);
3181 assert(pBC);
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());
3189 namespace {
3191 class CompileDBFormulaHandler
3193 sc::CompileFormulaContext& mrCxt;
3195 public:
3196 explicit CompileDBFormulaHandler( sc::CompileFormulaContext& rCxt ) :
3197 mrCxt(rCxt) {}
3199 void operator() (size_t, ScFormulaCell* p)
3201 p->CompileDBFormula(mrCxt);
3205 struct CompileColRowNameFormulaHandler
3207 sc::CompileFormulaContext& mrCxt;
3208 public:
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();
3233 namespace {
3235 class UpdateSubTotalHandler
3237 ScFunctionData& mrData;
3239 void update(double fVal, bool bVal)
3241 if (mrData.bError)
3242 return;
3244 switch (mrData.eFunc)
3246 case SUBTOTAL_FUNC_SUM:
3247 case SUBTOTAL_FUNC_AVE:
3249 if (!bVal)
3250 return;
3252 ++mrData.nCount;
3253 if (!SubTotal::SafePlus(mrData.nVal, fVal))
3254 mrData.bError = true;
3256 break;
3257 case SUBTOTAL_FUNC_CNT: // only the value
3259 if (!bVal)
3260 return;
3262 ++mrData.nCount;
3264 break;
3265 case SUBTOTAL_FUNC_CNT2: // everything
3266 ++mrData.nCount;
3267 break;
3268 case SUBTOTAL_FUNC_MAX:
3270 if (!bVal)
3271 return;
3273 if (++mrData.nCount == 1 || fVal > mrData.nVal)
3274 mrData.nVal = fVal;
3276 break;
3277 case SUBTOTAL_FUNC_MIN:
3279 if (!bVal)
3280 return;
3282 if (++mrData.nCount == 1 || fVal < mrData.nVal)
3283 mrData.nVal = fVal;
3285 break;
3286 default:
3288 // added to avoid warnings
3293 public:
3294 explicit UpdateSubTotalHandler(ScFunctionData& rData) : mrData(rData) {}
3296 void operator() (size_t /*nRow*/, double fVal)
3298 update(fVal, true);
3301 void operator() (size_t /*nRow*/, const svl::SharedString&)
3303 update(0.0, false);
3306 void operator() (size_t /*nRow*/, const EditTextObject*)
3308 update(0.0, false);
3311 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
3313 double fVal = 0.0;
3314 bool bVal = false;
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();
3326 bVal = true;
3328 // otherwise text
3331 update(fVal, bVal);
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;
3349 SCROW nRow = 0;
3350 while (nRow <= MAXROW)
3352 if (!rHiddenRows.getRangeData(nRow, aRange))
3353 break;
3355 if (aRange.mbValue)
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;
3375 break;
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);
3387 break;
3388 default:
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);
3402 namespace {
3404 class WeightedCounter
3406 size_t mnCount;
3407 public:
3408 WeightedCounter() : mnCount(0) {}
3410 void operator() (const sc::CellStoreType::value_type& node)
3412 switch (node.type)
3414 case sc::element_type_numeric:
3415 case sc::element_type_string:
3416 mnCount += node.size;
3417 break;
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();
3429 break;
3430 case sc::element_type_edittext:
3431 // each edit-text cell is worth 50.
3432 mnCount += node.size * 50;
3433 break;
3434 default:
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();
3451 namespace {
3453 class CodeCounter
3455 size_t mnCount;
3456 public:
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
3471 CodeCounter aFunc;
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: */