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