1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <scitems.hxx>
21 #include <editeng/justifyitem.hxx>
22 #include <unotools/textsearch.hxx>
23 #include <unotools/charclass.hxx>
24 
25 #include <patattr.hxx>
26 #include <table.hxx>
27 #include <document.hxx>
28 #include <drwlayer.hxx>
29 #include <olinetab.hxx>
30 #include <global.hxx>
31 #include <globstr.hrc>
32 #include <scresid.hxx>
33 #include <refupdat.hxx>
34 #include <markdata.hxx>
35 #include <progress.hxx>
36 #include <prnsave.hxx>
37 #include <tabprotection.hxx>
38 #include <sheetevents.hxx>
39 #include <segmenttree.hxx>
40 #include <dbdata.hxx>
41 #include <conditio.hxx>
42 #include <globalnames.hxx>
43 #include <cellvalue.hxx>
44 #include <scmatrix.hxx>
45 #include <refupdatecontext.hxx>
46 #include <rowheightcontext.hxx>
47 #include <compressedarray.hxx>
48 #include <vcl/svapp.hxx>
49 
50 #include <formula/vectortoken.hxx>
51 #include <token.hxx>
52 
53 #include <vector>
54 #include <memory>
55 
56 using ::std::vector;
57 
58 namespace {
59 
GetProgressBar(SCSIZE nCount,SCSIZE nTotalCount,ScProgress * pOuterProgress,const ScDocument * pDoc)60 ScProgress* GetProgressBar(
61     SCSIZE nCount, SCSIZE nTotalCount, ScProgress* pOuterProgress, const ScDocument* pDoc)
62 {
63     if (nTotalCount < 1000)
64     {
65         // if the total number of rows is less than 1000, don't even bother
66         // with the progress bar because drawing progress bar can be very
67         // expensive especially in GTK.
68         return nullptr;
69     }
70 
71     if (pOuterProgress)
72         return pOuterProgress;
73 
74     if (nCount > 1)
75         return new ScProgress(
76             pDoc->GetDocumentShell(), ScResId(STR_PROGRESS_HEIGHTING), nTotalCount, true);
77 
78     return nullptr;
79 }
80 
GetOptimalHeightsInColumn(sc::RowHeightContext & rCxt,ScColContainer & rCol,SCROW nStartRow,SCROW nEndRow,ScProgress * pProgress,sal_uLong nProgressStart)81 void GetOptimalHeightsInColumn(
82     sc::RowHeightContext& rCxt, ScColContainer& rCol, SCROW nStartRow, SCROW nEndRow,
83     ScProgress* pProgress, sal_uLong nProgressStart )
84 {
85     assert(nStartRow <= nEndRow);
86 
87     //  first, one time over the whole range
88     //  (with the last column in the hope that they most likely still are
89     //  on standard format)
90 
91 
92     rCol.back().GetOptimalHeight(rCxt, nStartRow, nEndRow, 0, 0);
93 
94     //  from there search for the standard height that is in use in the lower part
95 
96     ScFlatUInt16RowSegments& rHeights = rCxt.getHeightArray();
97     sal_uInt16 nMinHeight = rHeights.getValue(nEndRow);
98     SCSIZE nPos = nEndRow-1;
99     ScFlatUInt16RowSegments::RangeData aRangeData;
100     while ( nPos && rHeights.getRangeData(nPos-1, aRangeData) )
101     {
102         if (aRangeData.mnValue >= nMinHeight)
103             nPos = std::max<SCSIZE>(0, aRangeData.mnRow1);
104         else
105             break;
106     }
107 
108     const SCROW nMinStart = nPos;
109 
110     sal_uLong nWeightedCount = nProgressStart + rCol.back().GetWeightedCount(nStartRow, nEndRow);
111     const SCCOL maxCol = rCol.size() - 1; // last col done already above
112     for (SCCOL nCol=0; nCol<maxCol; nCol++)
113     {
114         rCol[nCol].GetOptimalHeight(rCxt, nStartRow, nEndRow, nMinHeight, nMinStart);
115 
116         if (pProgress)
117         {
118             nWeightedCount += rCol[nCol].GetWeightedCount(nStartRow, nEndRow);
119             pProgress->SetState( nWeightedCount );
120         }
121     }
122 }
123 
124 struct OptimalHeightsFuncObjBase
125 {
~OptimalHeightsFuncObjBase__anon1f1f9e450111::OptimalHeightsFuncObjBase126     virtual ~OptimalHeightsFuncObjBase() {}
127     virtual bool operator() (SCROW nStartRow, SCROW nEndRow, sal_uInt16 nHeight) = 0;
128 };
129 
130 struct SetRowHeightOnlyFunc : public OptimalHeightsFuncObjBase
131 {
132     ScTable* mpTab;
SetRowHeightOnlyFunc__anon1f1f9e450111::SetRowHeightOnlyFunc133     explicit SetRowHeightOnlyFunc(ScTable* pTab) :
134         mpTab(pTab)
135     {}
136 
operator ()__anon1f1f9e450111::SetRowHeightOnlyFunc137     virtual bool operator() (SCROW nStartRow, SCROW nEndRow, sal_uInt16 nHeight) override
138     {
139         mpTab->SetRowHeightOnly(nStartRow, nEndRow, nHeight);
140         return false;
141     }
142 };
143 
144 struct SetRowHeightRangeFunc : public OptimalHeightsFuncObjBase
145 {
146     ScTable* mpTab;
147     double const mnPPTY;
148 
SetRowHeightRangeFunc__anon1f1f9e450111::SetRowHeightRangeFunc149     SetRowHeightRangeFunc(ScTable* pTab, double nPPTY) :
150         mpTab(pTab),
151         mnPPTY(nPPTY)
152     {}
153 
operator ()__anon1f1f9e450111::SetRowHeightRangeFunc154     virtual bool operator() (SCROW nStartRow, SCROW nEndRow, sal_uInt16 nHeight) override
155     {
156         return mpTab->SetRowHeightRange(nStartRow, nEndRow, nHeight, mnPPTY);
157     }
158 };
159 
SetOptimalHeightsToRows(sc::RowHeightContext & rCxt,OptimalHeightsFuncObjBase & rFuncObj,ScBitMaskCompressedArray<SCROW,CRFlags> * pRowFlags,SCROW nStartRow,SCROW nEndRow)160 bool SetOptimalHeightsToRows(
161     sc::RowHeightContext& rCxt,
162     OptimalHeightsFuncObjBase& rFuncObj,
163     ScBitMaskCompressedArray<SCROW, CRFlags>* pRowFlags, SCROW nStartRow, SCROW nEndRow )
164 {
165     bool bChanged = false;
166     SCROW nRngStart = 0;
167     SCROW nRngEnd = 0;
168     sal_uInt16 nLast = 0;
169     sal_uInt16 nExtraHeight = rCxt.getExtraHeight();
170     for (SCSIZE i = nStartRow; i <= static_cast<SCSIZE>(nEndRow); i++)
171     {
172         size_t nIndex;
173         SCROW nRegionEndRow;
174         CRFlags nRowFlag = pRowFlags->GetValue( i, nIndex, nRegionEndRow );
175         if ( nRegionEndRow > nEndRow )
176             nRegionEndRow = nEndRow;
177         SCSIZE nMoreRows = nRegionEndRow - i;     // additional equal rows after first
178 
179         bool bAutoSize = !(nRowFlag & CRFlags::ManualSize);
180         if (bAutoSize || rCxt.isForceAutoSize())
181         {
182             if (nExtraHeight)
183             {
184                 if (bAutoSize)
185                     pRowFlags->SetValue( i, nRegionEndRow, nRowFlag | CRFlags::ManualSize);
186             }
187             else if (!bAutoSize)
188                 pRowFlags->SetValue( i, nRegionEndRow, nRowFlag & ~CRFlags::ManualSize);
189 
190             for (SCSIZE nInner = i; nInner <= i + nMoreRows; ++nInner)
191             {
192                 if (nLast)
193                 {
194                     ScFlatUInt16RowSegments::RangeData aRangeData;
195                     (void)rCxt.getHeightArray().getRangeData(nInner, aRangeData);
196                     if (aRangeData.mnValue + nExtraHeight == nLast)
197                     {
198                         nRngEnd = std::min<SCSIZE>(i + nMoreRows, aRangeData.mnRow2);
199                         nInner = aRangeData.mnRow2;
200                     }
201                     else
202                     {
203                         bChanged |= rFuncObj(nRngStart, nRngEnd, nLast);
204                         nLast = 0;
205                     }
206                 }
207                 if (!nLast)
208                 {
209                     nLast = rCxt.getHeightArray().getValue(nInner) + rCxt.getExtraHeight();
210                     nRngStart = nInner;
211                     nRngEnd = nInner;
212                 }
213             }
214         }
215         else
216         {
217             if (nLast)
218                 bChanged |= rFuncObj(nRngStart, nRngEnd, nLast);
219             nLast = 0;
220         }
221         i += nMoreRows;     // already handled - skip
222     }
223     if (nLast)
224         bChanged |= rFuncObj(nRngStart, nRngEnd, nLast);
225 
226     return bChanged;
227 }
228 
229 }
230 
ScTable(ScDocument * pDoc,SCTAB nNewTab,const OUString & rNewName,bool bColInfo,bool bRowInfo)231 ScTable::ScTable( ScDocument* pDoc, SCTAB nNewTab, const OUString& rNewName,
232                     bool bColInfo, bool bRowInfo ) :
233     aCol( INITIALCOLCOUNT ),
234     aName( rNewName ),
235     aCodeName( rNewName ),
236     nLinkRefreshDelay( 0 ),
237     nLinkMode( ScLinkMode::NONE ),
238     aPageStyle( ScResId(STR_STYLENAME_STANDARD) ),
239     nRepeatStartX( SCCOL_REPEAT_NONE ),
240     nRepeatEndX( SCCOL_REPEAT_NONE ),
241     nRepeatStartY( SCROW_REPEAT_NONE ),
242     nRepeatEndY( SCROW_REPEAT_NONE ),
243     mpRowHeights( static_cast<ScFlatUInt16RowSegments*>(nullptr) ),
244     mpHiddenCols(new ScFlatBoolColSegments),
245     mpHiddenRows(new ScFlatBoolRowSegments),
246     mpFilteredCols(new ScFlatBoolColSegments),
247     mpFilteredRows(new ScFlatBoolRowSegments),
248     nTableAreaX( 0 ),
249     nTableAreaY( 0 ),
250     nTab( nNewTab ),
251     pDocument( pDoc ),
252     pSortCollator( nullptr ),
253     nLockCount( 0 ),
254     aScenarioColor( COL_LIGHTGRAY ),
255     aTabBgColor( COL_AUTO ),
256     nScenarioFlags(ScScenarioFlags::NONE),
257     mpCondFormatList( new ScConditionalFormatList() ),
258     bScenario(false),
259     bLayoutRTL(false),
260     bLoadingRTL(false),
261     bPageSizeValid(false),
262     bTableAreaValid(false),
263     bVisible(true),
264     bStreamValid(false),
265     bPendingRowHeights(false),
266     bCalcNotification(false),
267     bGlobalKeepQuery(false),
268     bPrintEntireSheet(true),
269     bActiveScenario(false),
270     mbPageBreaksValid(false),
271     mbForceBreaks(false),
272     aDefaultColAttrArray(static_cast<SCCOL>(-1), nNewTab, pDoc, nullptr)
273 {
274     if (bColInfo)
275     {
276         mpColWidth.reset( new ScCompressedArray<SCCOL, sal_uInt16>( pDocument->MaxCol()+1, STD_COL_WIDTH ) );
277         mpColFlags.reset( new ScBitMaskCompressedArray<SCCOL, CRFlags>( pDocument->MaxCol()+1, CRFlags::NONE ) );
278     }
279 
280     if (bRowInfo)
281     {
282         mpRowHeights.reset(new ScFlatUInt16RowSegments(ScGlobal::nStdRowHeight));
283         pRowFlags.reset(new ScBitMaskCompressedArray<SCROW, CRFlags>( pDocument->MaxRow(), CRFlags::NONE));
284     }
285 
286     if ( pDocument->IsDocVisible() )
287     {
288         //  when a sheet is added to a visible document,
289         //  initialize its RTL flag from the system locale
290         bLayoutRTL = ScGlobal::IsSystemRTL();
291     }
292 
293     ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer();
294     if (pDrawLayer)
295     {
296         if ( pDrawLayer->ScAddPage( nTab ) )    // sal_False (not inserted) during Undo
297         {
298             pDrawLayer->ScRenamePage( nTab, aName );
299             sal_uLong const nx = sal_uLong(double(pDocument->MaxCol()+1) * STD_COL_WIDTH           * HMM_PER_TWIPS );
300             sal_uLong ny = static_cast<sal_uLong>(double(pDocument->MaxRow()+1) * ScGlobal::nStdRowHeight * HMM_PER_TWIPS );
301             pDrawLayer->SetPageSize( static_cast<sal_uInt16>(nTab), Size( nx, ny ), false );
302         }
303     }
304 
305     for (SCCOL k=0; k < aCol.size(); k++)
306         aCol[k].Init( k, nTab, pDocument, true );
307 }
308 
~ScTable()309 ScTable::~ScTable() COVERITY_NOEXCEPT_FALSE
310 {
311     if (!pDocument->IsInDtorClear())
312     {
313         for (SCCOL nCol = 0; nCol < (aCol.size() - 1); ++nCol)
314         {
315             aCol[nCol].FreeNotes();
316         }
317         //  In the dtor, don't delete the pages in the wrong order.
318         //  (or else nTab does not reflect the page number!)
319         //  In ScDocument::Clear is afterwards used from Clear at the Draw Layer to delete everything.
320 
321         ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer();
322         if (pDrawLayer)
323             pDrawLayer->ScRemovePage( nTab );
324     }
325 
326     pRowFlags.reset();
327     pSheetEvents.reset();
328     pOutlineTable.reset();
329     pSearchText.reset();
330     pRepeatColRange.reset();
331     pRepeatRowRange.reset();
332     pScenarioRanges.reset();
333     mpRangeName.reset();
334     pDBDataNoName.reset();
335     DestroySortCollator();
336 }
337 
GetHashCode() const338 sal_Int64 ScTable::GetHashCode() const
339 {
340     return sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(this));
341 }
342 
SetName(const OUString & rNewName)343 void ScTable::SetName( const OUString& rNewName )
344 {
345     aName = rNewName;
346     aUpperName.clear(); // invalidated if the name is changed
347 
348     // SetStreamValid is handled in ScDocument::RenameTab
349 }
350 
GetUpperName() const351 const OUString& ScTable::GetUpperName() const
352 {
353     if (aUpperName.isEmpty() && !aName.isEmpty())
354         aUpperName = ScGlobal::pCharClass->uppercase(aName);
355     return aUpperName;
356 }
357 
SetVisible(bool bVis)358 void ScTable::SetVisible( bool bVis )
359 {
360     if (bVisible != bVis)
361         SetStreamValid(false);
362 
363     bVisible = bVis;
364 }
365 
SetStreamValid(bool bSet,bool bIgnoreLock)366 void ScTable::SetStreamValid( bool bSet, bool bIgnoreLock )
367 {
368     if (!bStreamValid && !bSet)
369         return; // shortcut
370     if ( bIgnoreLock || !pDocument->IsStreamValidLocked() )
371         bStreamValid = bSet;
372 }
373 
SetPendingRowHeights(bool bSet)374 void ScTable::SetPendingRowHeights( bool bSet )
375 {
376     bPendingRowHeights = bSet;
377 }
378 
SetLayoutRTL(bool bSet)379 void ScTable::SetLayoutRTL( bool bSet )
380 {
381     bLayoutRTL = bSet;
382 }
383 
SetLoadingRTL(bool bSet)384 void ScTable::SetLoadingRTL( bool bSet )
385 {
386     bLoadingRTL = bSet;
387 }
388 
SetTabBgColor(const Color & rColor)389 void ScTable::SetTabBgColor(const Color& rColor)
390 {
391     if (aTabBgColor != rColor)
392     {
393         // The tab color has changed.  Set this table 'modified'.
394         aTabBgColor = rColor;
395         SetStreamValid(false);
396     }
397 }
398 
SetScenario(bool bFlag)399 void ScTable::SetScenario( bool bFlag )
400 {
401     bScenario = bFlag;
402 }
403 
SetLink(ScLinkMode nMode,const OUString & rDoc,const OUString & rFlt,const OUString & rOpt,const OUString & rTab,sal_uLong nRefreshDelay)404 void ScTable::SetLink( ScLinkMode nMode,
405                         const OUString& rDoc, const OUString& rFlt, const OUString& rOpt,
406                         const OUString& rTab, sal_uLong nRefreshDelay )
407 {
408     nLinkMode = nMode;
409     aLinkDoc = rDoc;        // File
410     aLinkFlt = rFlt;        // Filter
411     aLinkOpt = rOpt;        // Filter options
412     aLinkTab = rTab;        // Sheet name in source file
413     nLinkRefreshDelay = nRefreshDelay;  // refresh delay in seconds, 0==off
414 
415     SetStreamValid(false);
416 }
417 
GetOptimalColWidth(SCCOL nCol,OutputDevice * pDev,double nPPTX,double nPPTY,const Fraction & rZoomX,const Fraction & rZoomY,bool bFormula,const ScMarkData * pMarkData,const ScColWidthParam * pParam)418 sal_uInt16 ScTable::GetOptimalColWidth( SCCOL nCol, OutputDevice* pDev,
419                                     double nPPTX, double nPPTY,
420                                     const Fraction& rZoomX, const Fraction& rZoomY,
421                                     bool bFormula, const ScMarkData* pMarkData,
422                                     const ScColWidthParam* pParam )
423 {
424     if ( nCol >= aCol.size() )
425         return ( STD_COL_WIDTH - STD_EXTRA_WIDTH );
426 
427     return aCol[nCol].GetOptimalColWidth( pDev, nPPTX, nPPTY, rZoomX, rZoomY,
428         bFormula, STD_COL_WIDTH - STD_EXTRA_WIDTH, pMarkData, pParam );
429 }
430 
GetNeededSize(SCCOL nCol,SCROW nRow,OutputDevice * pDev,double nPPTX,double nPPTY,const Fraction & rZoomX,const Fraction & rZoomY,bool bWidth,bool bTotalSize)431 long ScTable::GetNeededSize( SCCOL nCol, SCROW nRow,
432                                 OutputDevice* pDev,
433                                 double nPPTX, double nPPTY,
434                                 const Fraction& rZoomX, const Fraction& rZoomY,
435                                 bool bWidth, bool bTotalSize )
436 {
437     if ( nCol >= aCol.size() )
438         return 0;
439 
440     ScNeededSizeOptions aOptions;
441     aOptions.bSkipMerged = false;       // count merged cells
442     aOptions.bTotalSize  = bTotalSize;
443 
444     return aCol[nCol].GetNeededSize
445         ( nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, bWidth, aOptions, nullptr );
446 }
447 
SetOptimalHeight(sc::RowHeightContext & rCxt,SCROW nStartRow,SCROW nEndRow,ScProgress * pOuterProgress,sal_uLong nProgressStart)448 bool ScTable::SetOptimalHeight(
449     sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow,
450     ScProgress* pOuterProgress, sal_uLong nProgressStart )
451 {
452     assert(nStartRow <= nEndRow);
453 
454     OSL_ENSURE( rCxt.getExtraHeight() == 0 || rCxt.isForceAutoSize(),
455         "automatic OptimalHeight with Extra" );
456 
457     if ( pDocument->IsAdjustHeightLocked() )
458     {
459         return false;
460     }
461 
462     SCSIZE  nCount = static_cast<SCSIZE>(nEndRow-nStartRow+1);
463 
464     ScProgress* pProgress = GetProgressBar(nCount, GetWeightedCount(), pOuterProgress, pDocument);
465 
466     GetOptimalHeightsInColumn(rCxt, aCol, nStartRow, nEndRow, pProgress, nProgressStart);
467 
468     rCxt.getHeightArray().enableTreeSearch(true);
469     SetRowHeightRangeFunc aFunc(this, rCxt.getPPTY());
470     bool bChanged = SetOptimalHeightsToRows(rCxt, aFunc, pRowFlags.get(), nStartRow, nEndRow);
471 
472     if ( pProgress != pOuterProgress )
473         delete pProgress;
474 
475     return bChanged;
476 }
477 
SetOptimalHeightOnly(sc::RowHeightContext & rCxt,SCROW nStartRow,SCROW nEndRow,ScProgress * pOuterProgress,sal_uLong nProgressStart)478 void ScTable::SetOptimalHeightOnly(
479     sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow,
480     ScProgress* pOuterProgress, sal_uLong nProgressStart )
481 {
482     OSL_ENSURE( rCxt.getExtraHeight() == 0 || rCxt.isForceAutoSize(),
483         "automatic OptimalHeight with Extra" );
484 
485     if ( pDocument->IsAdjustHeightLocked() )
486         return;
487 
488     SCSIZE  nCount = static_cast<SCSIZE>(nEndRow-nStartRow+1);
489 
490     ScProgress* pProgress = GetProgressBar(nCount, GetWeightedCount(), pOuterProgress, pDocument);
491 
492     GetOptimalHeightsInColumn(rCxt, aCol, nStartRow, nEndRow, pProgress, nProgressStart);
493 
494     SetRowHeightOnlyFunc aFunc(this);
495 
496     rCxt.getHeightArray().enableTreeSearch(true);
497     SetOptimalHeightsToRows(rCxt, aFunc, pRowFlags.get(), nStartRow, nEndRow);
498 
499     if ( pProgress != pOuterProgress )
500         delete pProgress;
501 }
502 
GetCellArea(SCCOL & rEndCol,SCROW & rEndRow) const503 bool ScTable::GetCellArea( SCCOL& rEndCol, SCROW& rEndRow ) const
504 {
505     bool bFound = false;
506     SCCOL nMaxX = 0;
507     SCROW nMaxY = 0;
508     for (SCCOL i=0; i<aCol.size(); i++)
509         {
510             if (!aCol[i].IsEmptyData())
511             {
512                 bFound = true;
513                 nMaxX = i;
514                 SCROW nRow = aCol[i].GetLastDataPos();
515                 if (nRow > nMaxY)
516                     nMaxY = nRow;
517             }
518             if ( aCol[i].HasCellNotes() )
519             {
520                 SCROW maxNoteRow = aCol[i].GetCellNotesMaxRow();
521                 if (maxNoteRow >= nMaxY)
522                 {
523                     bFound = true;
524                     nMaxY = maxNoteRow;
525                 }
526                 if (i>nMaxX)
527                 {
528                     bFound = true;
529                     nMaxX = i;
530                 }
531             }
532         }
533 
534     rEndCol = nMaxX;
535     rEndRow = nMaxY;
536     return bFound;
537 }
538 
GetTableArea(SCCOL & rEndCol,SCROW & rEndRow) const539 bool ScTable::GetTableArea( SCCOL& rEndCol, SCROW& rEndRow ) const
540 {
541     bool bRet = true;               //TODO: remember?
542     if (!bTableAreaValid)
543     {
544         bRet = GetPrintArea(nTableAreaX, nTableAreaY, true);
545         bTableAreaValid = true;
546     }
547     rEndCol = nTableAreaX;
548     rEndRow = nTableAreaY;
549     return bRet;
550 }
551 
552 const SCCOL SC_COLUMNS_STOP = 30;
553 
GetPrintArea(SCCOL & rEndCol,SCROW & rEndRow,bool bNotes) const554 bool ScTable::GetPrintArea( SCCOL& rEndCol, SCROW& rEndRow, bool bNotes ) const
555 {
556     bool bFound = false;
557     SCCOL nMaxX = 0;
558     SCROW nMaxY = 0;
559     SCCOL i;
560 
561     for (i=0; i<aCol.size(); i++)               // Test data
562         {
563             if (!aCol[i].IsEmptyData())
564             {
565                 bFound = true;
566                 if (i>nMaxX)
567                     nMaxX = i;
568                 SCROW nColY = aCol[i].GetLastDataPos();
569                 if (nColY > nMaxY)
570                     nMaxY = nColY;
571             }
572             if (bNotes && aCol[i].HasCellNotes() )
573             {
574                 SCROW maxNoteRow = aCol[i].GetCellNotesMaxRow();
575                 if (maxNoteRow >= nMaxY)
576                 {
577                     bFound = true;
578                     nMaxY = maxNoteRow;
579                 }
580                 if (i>nMaxX)
581                 {
582                     bFound = true;
583                     nMaxX = i;
584                 }
585             }
586         }
587 
588     SCCOL nMaxDataX = nMaxX;
589 
590     for (i=0; i<aCol.size(); i++)               // Test attribute
591     {
592         SCROW nLastRow;
593         if (aCol[i].GetLastVisibleAttr( nLastRow ))
594         {
595             bFound = true;
596             nMaxX = i;
597             if (nLastRow > nMaxY)
598                 nMaxY = nLastRow;
599         }
600     }
601 
602     if (nMaxX == pDocument->MaxCol())                    // omit attribute at the right
603     {
604         --nMaxX;
605         while ( nMaxX>0 && aCol[nMaxX].IsVisibleAttrEqual(aCol[nMaxX+1]) )
606             --nMaxX;
607     }
608 
609     if ( nMaxX < nMaxDataX )
610     {
611         nMaxX = nMaxDataX;
612     }
613     else if ( nMaxX > nMaxDataX )
614     {
615         SCCOL nAttrStartX = nMaxDataX + 1;
616         while ( nAttrStartX < (aCol.size()-1) )
617         {
618             SCCOL nAttrEndX = nAttrStartX;
619             while ( nAttrEndX < (aCol.size()-1) && aCol[nAttrStartX].IsVisibleAttrEqual(aCol[nAttrEndX+1]) )
620                 ++nAttrEndX;
621             if ( nAttrEndX + 1 - nAttrStartX >= SC_COLUMNS_STOP )
622             {
623                 // found equally-formatted columns behind data -> stop before these columns
624                 nMaxX = nAttrStartX - 1;
625 
626                 // also don't include default-formatted columns before that
627                 SCROW nDummyRow;
628                 while ( nMaxX > nMaxDataX && !aCol[nMaxX].GetLastVisibleAttr( nDummyRow ) )
629                     --nMaxX;
630                 break;
631             }
632             nAttrStartX = nAttrEndX + 1;
633         }
634     }
635 
636     rEndCol = nMaxX;
637     rEndRow = nMaxY;
638     return bFound;
639 }
640 
GetPrintAreaHor(SCROW nStartRow,SCROW nEndRow,SCCOL & rEndCol) const641 bool ScTable::GetPrintAreaHor( SCROW nStartRow, SCROW nEndRow,
642                                 SCCOL& rEndCol ) const
643 {
644     bool bFound = false;
645     SCCOL nMaxX = 0;
646     SCCOL i;
647 
648     for (i=0; i<aCol.size(); i++)               // Test attribute
649     {
650         if (aCol[i].HasVisibleAttrIn( nStartRow, nEndRow ))
651         {
652             bFound = true;
653             nMaxX = i;
654         }
655     }
656 
657     if (nMaxX == pDocument->MaxCol())                    // omit attribute at the right
658     {
659         --nMaxX;
660         while ( nMaxX>0 && aCol[nMaxX].IsVisibleAttrEqual(aCol[nMaxX+1], nStartRow, nEndRow) )
661             --nMaxX;
662     }
663 
664     for (i=0; i<aCol.size(); i++)               // test the data
665     {
666         if (!aCol[i].IsEmptyBlock( nStartRow, nEndRow ))        //TODO: bNotes ??????
667         {
668             bFound = true;
669             if (i>nMaxX)
670                 nMaxX = i;
671         }
672     }
673 
674     rEndCol = nMaxX;
675     return bFound;
676 }
677 
GetPrintAreaVer(SCCOL nStartCol,SCCOL nEndCol,SCROW & rEndRow,bool bNotes) const678 bool ScTable::GetPrintAreaVer( SCCOL nStartCol, SCCOL nEndCol,
679                                 SCROW& rEndRow, bool bNotes ) const
680 {
681     nStartCol = std::min<SCCOL>( nStartCol, aCol.size()-1 );
682     nEndCol   = std::min<SCCOL>( nEndCol,   aCol.size()-1 );
683     bool bFound = false;
684     SCROW nMaxY = 0;
685     SCCOL i;
686 
687     for (i=nStartCol; i<=nEndCol; i++)              // Test attribute
688     {
689         SCROW nLastRow;
690         if (aCol[i].GetLastVisibleAttr( nLastRow ))
691         {
692             bFound = true;
693             if (nLastRow > nMaxY)
694                 nMaxY = nLastRow;
695         }
696     }
697 
698     for (i=nStartCol; i<=nEndCol; i++)              // Test data
699     {
700         if (!aCol[i].IsEmptyData())
701         {
702             bFound = true;
703             SCROW nColY = aCol[i].GetLastDataPos();
704             if (nColY > nMaxY)
705                 nMaxY = nColY;
706         }
707         if (bNotes && aCol[i].HasCellNotes() )
708         {
709             SCROW maxNoteRow =aCol[i].GetCellNotesMaxRow();
710             if (maxNoteRow > nMaxY)
711             {
712                 bFound = true;
713                 nMaxY = maxNoteRow;
714             }
715         }
716     }
717 
718     rEndRow = nMaxY;
719     return bFound;
720 }
721 
GetDataStart(SCCOL & rStartCol,SCROW & rStartRow) const722 bool ScTable::GetDataStart( SCCOL& rStartCol, SCROW& rStartRow ) const
723 {
724     bool bFound = false;
725     SCCOL nMinX = aCol.size()-1;
726     SCROW nMinY = pDocument->MaxRow();
727     SCCOL i;
728 
729     for (i=0; i<aCol.size(); i++)                   // Test attribute
730     {
731         SCROW nFirstRow;
732         if (aCol[i].GetFirstVisibleAttr( nFirstRow ))
733         {
734             if (!bFound)
735                 nMinX = i;
736             bFound = true;
737             if (nFirstRow < nMinY)
738                 nMinY = nFirstRow;
739         }
740     }
741 
742     if (nMinX == 0)                                     // omit attribute at the right
743     {
744         if ( aCol.size() > 1 && aCol[0].IsVisibleAttrEqual(aCol[1]) )      // no single ones
745         {
746             ++nMinX;
747             while ( nMinX<(aCol.size()-1) && aCol[nMinX].IsVisibleAttrEqual(aCol[nMinX-1]) )
748                 ++nMinX;
749         }
750     }
751 
752     bool bDatFound = false;
753     for (i=0; i<aCol.size(); i++)                   // Test data
754     {
755         if (!aCol[i].IsEmptyData())
756         {
757             if (!bDatFound && i<nMinX)
758                 nMinX = i;
759             bFound = bDatFound = true;
760             SCROW nRow = aCol[i].GetFirstDataPos();
761             if (nRow < nMinY)
762                 nMinY = nRow;
763         }
764         if ( aCol[i].HasCellNotes() )
765         {
766             SCROW minNoteRow = aCol[i].GetCellNotesMinRow();
767             if (minNoteRow <= nMinY)
768             {
769                 bFound = true;
770                 nMinY = minNoteRow;
771             }
772             if (i<nMinX)
773             {
774                 bFound = true;
775                 nMinX = i;
776             }
777         }
778     }
779     rStartCol = nMinX;
780     rStartRow = nMinY;
781     return bFound;
782 }
783 
GetDataArea(SCCOL & rStartCol,SCROW & rStartRow,SCCOL & rEndCol,SCROW & rEndRow,bool bIncludeOld,bool bOnlyDown) const784 void ScTable::GetDataArea( SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow,
785                            bool bIncludeOld, bool bOnlyDown ) const
786 {
787     // return the smallest area containing at least all contiguous cells having data. This area
788     // is a square containing also empty cells. It may shrink or extend the area given as input
789     // Flags as modifiers:
790     //
791     //     bIncludeOld = true ensure that the returned area contains at least the initial area,
792     //                   independently of the emptiness of rows / columns (i.e. does not allow shrinking)
793     //     bOnlyDown = true means extend / shrink the inputted area only down, i.e modify only rEndRow
794 
795     rStartCol = std::min<SCCOL>( rStartCol, aCol.size()-1 );
796     rEndCol   = std::min<SCCOL>( rEndCol,   aCol.size()-1 );
797 
798     bool bLeft = false;
799     bool bRight  = false;
800     bool bTop = false;
801     bool bBottom = false;
802     bool bChanged = false;
803 
804     // We need to cache sc::ColumnBlockConstPosition per each column.
805     std::vector< sc::ColumnBlockConstPosition > blockPos( rEndCol + 1 );
806     for( SCCOL i = 0; i <= rEndCol; ++i )
807         aCol[ i ].InitBlockPosition( blockPos[ i ] );
808 
809     do
810     {
811         bChanged = false;
812 
813         if (!bOnlyDown)
814         {
815             SCROW nStart = rStartRow;
816             SCROW nEnd = rEndRow;
817             if (nStart>0) --nStart;
818             if (nEnd<pDocument->MaxRow()) ++nEnd;
819 
820             if (rEndCol < (aCol.size()-1))
821                 if (!aCol[rEndCol+1].IsEmptyBlock(nStart,nEnd))
822                 {
823                     assert( int( blockPos.size()) == rEndCol + 1 );
824                     ++rEndCol;
825                     blockPos.resize( blockPos.size() + 1 );
826                     aCol[ rEndCol ].InitBlockPosition( blockPos[ rEndCol ] );
827                     bChanged = true;
828                     bRight = true;
829                 }
830 
831             if (rStartCol > 0)
832                 if (!aCol[rStartCol-1].IsEmptyBlock(nStart,nEnd))
833                 {
834                     --rStartCol;
835                     bChanged = true;
836                     bLeft = true;
837                 }
838 
839             if (rStartRow > 0)
840             {
841                 SCROW nTest = rStartRow-1;
842                 bool needExtend = false;
843                 for ( SCCOL i = rStartCol; i<=rEndCol && !needExtend; i++)
844                     if (aCol[i].HasDataAt(blockPos[i], nTest))
845                         needExtend = true;
846                 if (needExtend)
847                 {
848                     --rStartRow;
849                     bChanged = true;
850                     bTop = true;
851                 }
852             }
853         }
854 
855         if (rEndRow < pDocument->MaxRow())
856         {
857             SCROW nTest = rEndRow+1;
858             bool needExtend = false;
859             for ( SCCOL i = rStartCol; i<=rEndCol && !needExtend; i++)
860                 if (aCol[i].HasDataAt(blockPos[ i ], nTest))
861                     needExtend = true;
862             if (needExtend)
863             {
864                 ++rEndRow;
865                 bChanged = true;
866                 bBottom = true;
867             }
868         }
869     }
870     while( bChanged );
871 
872     if ( !bIncludeOld && !bOnlyDown )
873     {
874         if ( !bLeft )
875             while ( rStartCol < rEndCol && rStartCol < (aCol.size()-1) && aCol[rStartCol].IsEmptyBlock(rStartRow,rEndRow) )
876                 ++rStartCol;
877 
878         if ( !bRight )
879             while ( rEndCol > 0 && rStartCol < rEndCol && aCol[rEndCol].IsEmptyBlock(rStartRow,rEndRow) )
880                 --rEndCol;
881 
882         if ( !bTop && rStartRow < pDocument->MaxRow() && rStartRow < rEndRow )
883         {
884             bool bShrink = true;
885             do
886             {
887                 for ( SCCOL i = rStartCol; i<=rEndCol && bShrink; i++)
888                     if (aCol[i].HasDataAt(rStartRow))
889                         bShrink = false;
890                 if (bShrink)
891                     ++rStartRow;
892             } while (bShrink && rStartRow < pDocument->MaxRow() && rStartRow < rEndRow);
893         }
894     }
895 
896     if ( !bIncludeOld )
897     {
898         if ( !bBottom && rEndRow > 0 && rStartRow < rEndRow )
899         {
900             SCROW nLastDataRow = GetLastDataRow( rStartCol, rEndCol, rEndRow);
901             if (nLastDataRow < rEndRow)
902                 rEndRow = std::max( rStartRow, nLastDataRow);
903         }
904     }
905 }
906 
GetDataAreaSubrange(ScRange & rRange) const907 bool ScTable::GetDataAreaSubrange( ScRange& rRange ) const
908 {
909     SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
910 
911     if ( nCol1 >= aCol.size() )
912         return false;
913 
914     nCol2 = std::min<SCCOL>( nCol2, aCol.size()-1 );
915 
916     SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
917 
918     SCCOL nFirstNonEmptyCol = -1, nLastNonEmptyCol = -1;
919     SCROW nRowStart = nRow2, nRowEnd = nRow1;
920 
921     for ( SCCOL nCol = nCol1; nCol <= nCol2; ++nCol )
922     {
923         SCROW nRowStartThis = nRow1, nRowEndThis = nRow2;
924         bool bTrimmed = aCol[nCol].TrimEmptyBlocks(nRowStartThis, nRowEndThis);
925         if ( bTrimmed )
926         {
927             if ( nFirstNonEmptyCol == -1 )
928                 nFirstNonEmptyCol = nCol;
929             nLastNonEmptyCol = nCol;
930 
931             nRowStart = std::min<SCROW>(nRowStart, nRowStartThis);
932             nRowEnd = std::max<SCROW>(nRowEnd, nRowEndThis);
933         }
934     }
935 
936     if ( nFirstNonEmptyCol == -1 )
937         return false;
938 
939     assert(nFirstNonEmptyCol <= nLastNonEmptyCol);
940     assert(nRowStart <= nRowEnd);
941 
942     rRange.aStart.Set(nFirstNonEmptyCol, nRowStart, rRange.aStart.Tab());
943     rRange.aEnd.Set(nLastNonEmptyCol, nRowEnd, rRange.aEnd.Tab());
944 
945     return true;
946 }
947 
ShrinkToUsedDataArea(bool & o_bShrunk,SCCOL & rStartCol,SCROW & rStartRow,SCCOL & rEndCol,SCROW & rEndRow,bool bColumnsOnly,bool bStickyTopRow,bool bStickyLeftCol,bool bConsiderCellNotes,bool bConsiderCellDrawObjects) const948 bool ScTable::ShrinkToUsedDataArea( bool& o_bShrunk, SCCOL& rStartCol, SCROW& rStartRow,
949         SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly, bool bStickyTopRow, bool bStickyLeftCol,
950         bool bConsiderCellNotes, bool bConsiderCellDrawObjects ) const
951 {
952     rStartCol = std::min<SCCOL>( rStartCol, aCol.size()-1 );
953     // check for rEndCol is done below.
954 
955     o_bShrunk = false;
956 
957     PutInOrder( rStartCol, rEndCol);
958     PutInOrder( rStartRow, rEndRow);
959     if (rStartCol < 0)
960     {
961         rStartCol = 0;
962         o_bShrunk = true;
963     }
964     if (rStartRow < 0)
965     {
966         rStartRow = 0;
967         o_bShrunk = true;
968     }
969     if (rEndCol >= aCol.size())
970     {
971         rEndCol = aCol.size()-1;
972         o_bShrunk = true;
973     }
974     if (rEndRow > pDocument->MaxRow())
975     {
976         rEndRow = pDocument->MaxRow();
977         o_bShrunk = true;
978     }
979 
980     while (rStartCol < rEndCol)
981     {
982         if (aCol[rEndCol].IsEmptyBlock( rStartRow, rEndRow))
983         {
984             if (bConsiderCellNotes && !aCol[rEndCol].IsNotesEmptyBlock( rStartRow, rEndRow ))
985                 break;
986 
987             if (bConsiderCellDrawObjects && !aCol[rEndCol].IsDrawObjectsEmptyBlock( rStartRow, rEndRow ))
988                 break;
989 
990             --rEndCol;
991             o_bShrunk = true;
992         }
993         else
994             break;  // while
995     }
996 
997     if (!bStickyLeftCol)
998     {
999         while (rStartCol < rEndCol)
1000         {
1001             if (aCol[rStartCol].IsEmptyBlock( rStartRow, rEndRow))
1002             {
1003                 if (bConsiderCellNotes && !aCol[rStartCol].IsNotesEmptyBlock( rStartRow, rEndRow ))
1004                     break;
1005 
1006                 if (bConsiderCellDrawObjects && !aCol[rStartCol].IsDrawObjectsEmptyBlock( rStartRow, rEndRow ))
1007                     break;
1008 
1009                 ++rStartCol;
1010                 o_bShrunk = true;
1011             }
1012             else
1013                 break;  // while
1014         }
1015     }
1016 
1017     if (!bColumnsOnly)
1018     {
1019         if (!bStickyTopRow)
1020         {
1021             while (rStartRow < rEndRow)
1022             {
1023                 bool bFound = false;
1024                 for (SCCOL i=rStartCol; i<=rEndCol && !bFound; i++)
1025                 {
1026                     if (aCol[i].HasDataAt( rStartRow, bConsiderCellNotes, bConsiderCellDrawObjects))
1027                         bFound = true;
1028                 }
1029                 if (!bFound)
1030                 {
1031                     ++rStartRow;
1032                     o_bShrunk = true;
1033                 }
1034                 else
1035                     break;  // while
1036             }
1037         }
1038 
1039         while (rStartRow < rEndRow)
1040         {
1041             SCROW nLastDataRow = GetLastDataRow( rStartCol, rEndCol, rEndRow,
1042                                                  bConsiderCellNotes, bConsiderCellDrawObjects);
1043             if (0 <= nLastDataRow && nLastDataRow < rEndRow)
1044             {
1045                 rEndRow = std::max( rStartRow, nLastDataRow);
1046                 o_bShrunk = true;
1047             }
1048             else
1049                 break;  // while
1050         }
1051     }
1052 
1053     return rStartCol != rEndCol || (bColumnsOnly ?
1054             !aCol[rStartCol].IsEmptyBlock( rStartRow, rEndRow) :
1055             (rStartRow != rEndRow ||
1056                 aCol[rStartCol].HasDataAt( rStartRow, bConsiderCellNotes, bConsiderCellDrawObjects)));
1057 }
1058 
GetLastDataRow(SCCOL nCol1,SCCOL nCol2,SCROW nLastRow,bool bConsiderCellNotes,bool bConsiderCellDrawObjects) const1059 SCROW ScTable::GetLastDataRow( SCCOL nCol1, SCCOL nCol2, SCROW nLastRow,
1060                                bool bConsiderCellNotes, bool bConsiderCellDrawObjects ) const
1061 {
1062     if ( !IsColValid( nCol1 ) || !ValidCol( nCol2 ) )
1063         return -1;
1064 
1065     nCol2 = std::min<SCCOL>( nCol2, aCol.size() - 1 );
1066 
1067     SCROW nNewLastRow = 0;
1068     for (SCCOL i = nCol1; i <= nCol2; ++i)
1069     {
1070         SCROW nThis = aCol[i].GetLastDataPos(nLastRow, bConsiderCellNotes, bConsiderCellDrawObjects);
1071         if (nNewLastRow < nThis)
1072             nNewLastRow = nThis;
1073     }
1074 
1075     return nNewLastRow;
1076 }
1077 
GetEmptyLinesInBlock(SCCOL nStartCol,SCROW nStartRow,SCCOL nEndCol,SCROW nEndRow,ScDirection eDir) const1078 SCSIZE ScTable::GetEmptyLinesInBlock( SCCOL nStartCol, SCROW nStartRow,
1079                                         SCCOL nEndCol, SCROW nEndRow, ScDirection eDir ) const
1080 {
1081     SCCOL nStartColOrig = nStartCol;
1082     SCCOL nEndColOrig   = nEndCol;
1083     nStartCol = std::min<SCCOL>( nStartCol, aCol.size()-1 );
1084     nEndCol   = std::min<SCCOL>( nEndCol,   aCol.size()-1 );
1085 
1086     // The region is not allocated and does not contain any data.
1087     if ( nStartColOrig != nStartCol )
1088         return ( ((eDir == DIR_BOTTOM) || (eDir == DIR_TOP)) ?
1089                  static_cast<SCSIZE>(nEndRow - nStartRow + 1) :
1090                  static_cast<SCSIZE>(nEndColOrig - nStartColOrig + 1) );
1091 
1092     SCSIZE nGapRight = static_cast<SCSIZE>(nEndColOrig - nEndCol);
1093     SCSIZE nCount = 0;
1094     SCCOL nCol;
1095     if ((eDir == DIR_BOTTOM) || (eDir == DIR_TOP))
1096     {
1097         nCount = static_cast<SCSIZE>(nEndRow - nStartRow + 1);
1098         for (nCol = nStartCol; nCol <= nEndCol; nCol++)
1099             nCount = std::min(nCount, aCol[nCol].GetEmptyLinesInBlock(nStartRow, nEndRow, eDir));
1100     }
1101     else if (eDir == DIR_RIGHT)
1102     {
1103         nCol = nEndCol;
1104         while ((nCol >= nStartCol) &&
1105                  aCol[nCol].IsEmptyBlock(nStartRow, nEndRow))
1106         {
1107             nCount++;
1108             nCol--;
1109         }
1110         nCount += nGapRight;
1111     }
1112     else
1113     {
1114         nCol = nStartCol;
1115         while ((nCol <= nEndCol) && aCol[nCol].IsEmptyBlock(nStartRow, nEndRow))
1116         {
1117             nCount++;
1118             nCol++;
1119         }
1120 
1121         // If the area between nStartCol and nEndCol are empty,
1122         // add the count of unallocated columns on the right.
1123         if ( nCol > nEndCol )
1124             nCount += nGapRight;
1125     }
1126     return nCount;
1127 }
1128 
IsEmptyLine(SCROW nRow,SCCOL nStartCol,SCCOL nEndCol) const1129 bool ScTable::IsEmptyLine( SCROW nRow, SCCOL nStartCol, SCCOL nEndCol ) const
1130 {
1131     // The range of columns are unallocated hence empty.
1132     if ( nStartCol >= aCol.size() )
1133         return true;
1134 
1135     nEndCol   = std::min<SCCOL>( nEndCol,   aCol.size()-1 );
1136 
1137     bool bFound = false;
1138     for (SCCOL i=nStartCol; i<=nEndCol && !bFound; i++)
1139         if (aCol[i].HasDataAt(nRow))
1140             bFound = true;
1141     return !bFound;
1142 }
1143 
LimitChartArea(SCCOL & rStartCol,SCROW & rStartRow,SCCOL & rEndCol,SCROW & rEndRow) const1144 void ScTable::LimitChartArea( SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow ) const
1145 {
1146     rStartCol = std::min<SCCOL>( rStartCol, aCol.size()-1 );
1147     rEndCol   = std::min<SCCOL>( rEndCol,   aCol.size()-1 );
1148 
1149     while ( rStartCol<rEndCol && aCol[rStartCol].IsEmptyBlock(rStartRow,rEndRow) )
1150         ++rStartCol;
1151 
1152     while ( rStartCol<rEndCol && aCol[rEndCol].IsEmptyBlock(rStartRow,rEndRow) )
1153         --rEndCol;
1154 
1155     while ( rStartRow<rEndRow && IsEmptyLine(rStartRow, rStartCol, rEndCol) )
1156         ++rStartRow;
1157 
1158     // Optimised loop for finding the bottom of the area, can be costly in large
1159     // spreadsheets.
1160     SCROW lastDataPos = 0;
1161     for (SCCOL i=rStartCol; i<=rEndCol; i++)
1162         lastDataPos = std::max(lastDataPos, aCol[i].GetLastDataPos());
1163     rEndRow = std::max( rStartRow, std::min(rEndRow, lastDataPos));
1164 }
1165 
FindNextVisibleCol(SCCOL nCol,bool bRight) const1166 SCCOL ScTable::FindNextVisibleCol( SCCOL nCol, bool bRight ) const
1167 {
1168     if(bRight)
1169     {
1170         nCol++;
1171         SCCOL nEnd = 0;
1172         bool bHidden = pDocument->ColHidden(nCol, nTab, nullptr, &nEnd);
1173         if(bHidden)
1174             nCol = nEnd +1;
1175 
1176         return std::min<SCCOL>(pDocument->MaxCol(), nCol);
1177     }
1178     else
1179     {
1180         nCol--;
1181         SCCOL nStart = pDocument->MaxCol();
1182         bool bHidden = pDocument->ColHidden(nCol, nTab, &nStart);
1183         if(bHidden)
1184             nCol = nStart - 1;
1185 
1186         return std::max<SCCOL>(0, nCol);
1187     }
1188 }
1189 
FindNextVisibleColWithContent(SCCOL nCol,bool bRight,SCROW nRow) const1190 SCCOL ScTable::FindNextVisibleColWithContent( SCCOL nCol, bool bRight, SCROW nRow ) const
1191 {
1192     const SCCOL nLastCol = aCol.size() - 1;
1193     if(bRight)
1194     {
1195         // If nCol is the last allocated column index, there won't be any content to its right.
1196         // To maintain the original return behaviour, return pDocument->MaxCol().
1197         if(nCol >= nLastCol)
1198             return pDocument->MaxCol();
1199 
1200         do
1201         {
1202             nCol++;
1203             SCCOL nEndCol = 0;
1204             bool bHidden = pDocument->ColHidden( nCol, nTab, nullptr, &nEndCol );
1205             if(bHidden)
1206             {
1207                 nCol = nEndCol +1;
1208                 // Can end search early as there is no data after nLastCol.
1209                 // For nCol == nLastCol, it may still have data so don't want to return pDocument->MaxCol().
1210                 if(nCol > nLastCol)
1211                     return pDocument->MaxCol();
1212             }
1213 
1214             if(aCol[nCol].HasVisibleDataAt(nRow))
1215                 return nCol;
1216         }
1217         while(nCol < nLastCol); // Stop search as soon as the last allocated column is searched.
1218 
1219         return pDocument->MaxCol();
1220     }
1221     else
1222     {
1223         // If nCol is in the unallocated range [nLastCol+1, pDocument->MaxCol()], then move it directly to nLastCol
1224         // as there is no data in the unallocated range. This also makes the search faster and avoids
1225         // the need for more range checks in the loop below.
1226         if ( nCol > nLastCol )
1227             nCol = nLastCol;
1228 
1229         if(nCol == 0)
1230             return 0;
1231 
1232         do
1233         {
1234             nCol--;
1235             SCCOL nStartCol = pDocument->MaxCol();
1236             bool bHidden = pDocument->ColHidden( nCol, nTab, &nStartCol );
1237             if(bHidden)
1238             {
1239                 nCol = nStartCol -1;
1240                 if(nCol <= 0)
1241                     return 0;
1242             }
1243 
1244             if(aCol[nCol].HasVisibleDataAt(nRow))
1245                 return nCol;
1246         }
1247         while(nCol > 0);
1248 
1249         return 0;
1250     }
1251 }
1252 
FindAreaPos(SCCOL & rCol,SCROW & rRow,ScMoveDirection eDirection) const1253 void ScTable::FindAreaPos( SCCOL& rCol, SCROW& rRow, ScMoveDirection eDirection ) const
1254 {
1255     const SCCOL nLastCol = aCol.size() - 1;
1256 
1257     if (eDirection == SC_MOVE_LEFT || eDirection == SC_MOVE_RIGHT)
1258     {
1259         SCCOL nNewCol = rCol;
1260         bool bThere = ( nNewCol <= nLastCol ) && aCol[nNewCol].HasVisibleDataAt(rRow);
1261         bool bRight = (eDirection == SC_MOVE_RIGHT);
1262         if (bThere)
1263         {
1264             if(nNewCol >= pDocument->MaxCol() && eDirection == SC_MOVE_RIGHT)
1265                 return;
1266             else if(nNewCol == 0 && eDirection == SC_MOVE_LEFT)
1267                 return;
1268 
1269             SCCOL nNextCol = FindNextVisibleCol( nNewCol, bRight );
1270 
1271             if( nNextCol <= nLastCol && aCol[nNextCol].HasVisibleDataAt(rRow) )
1272             {
1273                 bool bFound = false;
1274                 nNewCol = nNextCol;
1275                 do
1276                 {
1277                     nNextCol = FindNextVisibleCol( nNewCol, bRight );
1278                     if( nNextCol <= nLastCol && aCol[nNextCol].HasVisibleDataAt(rRow) )
1279                         nNewCol = nNextCol;
1280                     else
1281                         bFound = true;
1282                 }
1283                 while(!bFound && nNextCol > 0 && nNextCol < pDocument->MaxCol());
1284             }
1285             else
1286             {
1287                 nNewCol = FindNextVisibleColWithContent(nNewCol, bRight, rRow);
1288             }
1289         }
1290         else
1291         {
1292             nNewCol = FindNextVisibleColWithContent(nNewCol, bRight, rRow);
1293         }
1294 
1295         if (nNewCol<0)
1296             nNewCol=0;
1297         if (nNewCol>pDocument->MaxCol())
1298             nNewCol=pDocument->MaxCol();
1299         rCol = nNewCol;
1300     }
1301     else
1302     {
1303         if ( rCol <= nLastCol )
1304             aCol[rCol].FindDataAreaPos(rRow,eDirection == SC_MOVE_DOWN);
1305         else
1306         {
1307             // The cell (rCol, rRow) is equivalent to an empty cell (although not allocated).
1308             // Set rRow to 0 or pDocument->MaxRow() depending on eDirection to maintain the behaviour of
1309             // ScColumn::FindDataAreaPos() when the given column is empty.
1310             rRow = ( eDirection == SC_MOVE_DOWN ) ? pDocument->MaxRow() : 0;
1311         }
1312     }
1313 }
1314 
ValidNextPos(SCCOL nCol,SCROW nRow,const ScMarkData & rMark,bool bMarked,bool bUnprotected) const1315 bool ScTable::ValidNextPos( SCCOL nCol, SCROW nRow, const ScMarkData& rMark,
1316                                 bool bMarked, bool bUnprotected ) const
1317 {
1318     if (!ValidCol(nCol) || !ValidRow(nRow))
1319         return false;
1320 
1321     if (pDocument->HasAttrib(nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::Overlapped))
1322         // Skip an overlapped cell.
1323         return false;
1324 
1325     if (bMarked && !rMark.IsCellMarked(nCol,nRow))
1326         return false;
1327 
1328     /* TODO: for cursor movement *only* this should even take the protection
1329      * options (select locked, select unlocked) into account, see
1330      * ScTabView::SkipCursorHorizontal() and ScTabView::SkipCursorVertical(). */
1331     if (bUnprotected && pDocument->HasAttrib(nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::Protected))
1332         return false;
1333 
1334     if (bMarked || bUnprotected)        //TODO: also in other case ???
1335     {
1336         // Hidden cells must be skipped, as the cursor would end up on the next cell
1337         // even if it is protected or not marked.
1338         //TODO: control per Extra-Parameter, only for Cursor movement ???
1339 
1340         if (RowHidden(nRow))
1341             return false;
1342 
1343         if (ColHidden(nCol))
1344             return false;
1345     }
1346 
1347     return true;
1348 }
1349 
1350 // Skips the current cell if it is Hidden, Overlapped or Protected and Sheet is Protected
SkipRow(const SCCOL nCol,SCROW & rRow,const SCROW nMovY,const ScMarkData & rMark,const bool bUp,const SCROW nUsedY,const bool bMarked,const bool bSheetProtected) const1351 bool ScTable::SkipRow( const SCCOL nCol, SCROW& rRow, const SCROW nMovY,
1352         const ScMarkData& rMark, const bool bUp, const SCROW nUsedY,
1353         const bool bMarked, const bool bSheetProtected ) const
1354 {
1355     if ( !ValidRow( rRow ))
1356         return false;
1357 
1358     if (bSheetProtected && pDocument->HasAttrib( nCol, rRow, nTab, nCol, rRow, nTab, HasAttrFlags::Protected))
1359     {
1360         if ( rRow > nUsedY )
1361             rRow = (bUp ? nUsedY : pDocument->MaxRow() + nMovY);
1362         else
1363             rRow += nMovY;
1364 
1365         if (bMarked)
1366             rRow  = rMark.GetNextMarked( nCol, rRow, bUp );
1367 
1368         return true;
1369     }
1370     else
1371     {
1372         bool bRowHidden  = RowHidden( rRow );
1373         bool bOverlapped = pDocument->HasAttrib( nCol, rRow, nTab, nCol, rRow, nTab, HasAttrFlags::Overlapped );
1374 
1375         if ( bRowHidden || bOverlapped )
1376         {
1377             rRow += nMovY;
1378             if (bMarked)
1379                 rRow = rMark.GetNextMarked( nCol, rRow, bUp );
1380 
1381             return true;
1382         }
1383     }
1384 
1385     return false;
1386 }
1387 
GetNextPos(SCCOL & rCol,SCROW & rRow,SCCOL nMovX,SCROW nMovY,bool bMarked,bool bUnprotected,const ScMarkData & rMark,SCCOL nTabStartCol) const1388 void ScTable::GetNextPos( SCCOL& rCol, SCROW& rRow, SCCOL nMovX, SCROW nMovY,
1389         bool bMarked, bool bUnprotected, const ScMarkData& rMark, SCCOL nTabStartCol ) const
1390 {
1391     // Ensure bMarked is set only if there is a mark.
1392     assert( !bMarked || rMark.IsMarked() || rMark.IsMultiMarked());
1393 
1394     const bool bSheetProtected = IsProtected();
1395 
1396     if ( bUnprotected && !bSheetProtected )     // Is sheet really protected?
1397         bUnprotected = false;
1398 
1399     SCCOL nCol = rCol + nMovX;
1400     SCROW nRow = rRow + nMovY;
1401 
1402     SCCOL nStartCol, nEndCol;
1403     SCROW nStartRow, nEndRow;
1404     if (bMarked)
1405     {
1406         ScRange aRange( ScAddress::UNINITIALIZED);
1407         if (rMark.IsMarked())
1408             rMark.GetMarkArea( aRange);
1409         else if (rMark.IsMultiMarked())
1410             rMark.GetMultiMarkArea( aRange);
1411         else
1412         {
1413             // Covered by assert() above, but for NDEBUG build.
1414             if (ValidColRow(nCol,nRow))
1415             {
1416                 rCol = nCol;
1417                 rRow = nRow;
1418             }
1419             return;
1420         }
1421         nStartCol = aRange.aStart.Col();
1422         nStartRow = aRange.aStart.Row();
1423         nEndCol = aRange.aEnd.Col();
1424         nEndRow = aRange.aEnd.Row();
1425     }
1426     else if (bUnprotected)
1427     {
1428         nStartCol = 0;
1429         nStartRow = 0;
1430         nEndCol = rCol;
1431         nEndRow = rRow;
1432         pDocument->GetPrintArea( nTab, nEndCol, nEndRow, true );
1433         // Add some cols/rows to the print area (which is "content or
1434         // visually different from empty") to enable travelling through
1435         // protected forms with empty cells and no visual indicator.
1436         // 42 might be good enough and not too much...
1437         nEndCol = std::min<SCCOL>( nEndCol+42, pDocument->MaxCol());
1438         nEndRow = std::min<SCROW>( nEndRow+42, pDocument->MaxRow());
1439     }
1440     else
1441     {
1442         // Invalid values show up for instance for Tab, when nothing is
1443         // selected and not protected (left / right edge), then leave values
1444         // unchanged.
1445         if (ValidColRow(nCol,nRow))
1446         {
1447             rCol = nCol;
1448             rRow = nRow;
1449         }
1450 
1451         // Caller ensures actually moving nMovY to jump to prev/next row's
1452         // start col.
1453         if (nTabStartCol != SC_TABSTART_NONE)
1454             rCol = nTabStartCol;
1455 
1456         return;
1457     }
1458 
1459     if ( nMovY && (bMarked || bUnprotected))
1460     {
1461         do
1462         {
1463             const bool bUp = (nMovY < 0);
1464             const SCCOL nColAdd = (bUp ? -1 : 1);
1465 
1466             if (bMarked)
1467                 nRow = rMark.GetNextMarked( nCol, nRow, bUp );
1468 
1469             if (nTabStartCol != SC_TABSTART_NONE)
1470             {
1471                 /* NOTE: If current rCol < nTabStartCol when going down, there
1472                  * is no way to detect if the previous Tab wrapped around to
1473                  * the next row or if it was a Shift+Tab going backwards. The
1474                  * result after a wrap is an odd jump to the next row's
1475                  * nTabStartCol, which is logical though and always has been
1476                  * the case. Similar for rCol > nTabStartCol when going up.
1477                  * Related, it would be nice to limit advancing the position
1478                  * within bounds even if another wrap would occur, but again we
1479                  * can't tell if previously Tab or Shift+Tab was used, so we
1480                  * don't know if it would be nTabStartCol to nEndCol (for Tab)
1481                  * or nStartCol to nTabStartCol (for Shift+Tab). */
1482 
1483                 // Continue moving horizontally.
1484                 nMovX = nColAdd;
1485                 nCol = nTabStartCol;
1486                 break;  // do
1487             }
1488 
1489             while ( SkipRow( nCol, nRow, nMovY, rMark, bUp, nEndRow, bMarked, bSheetProtected ))
1490                 ;
1491 
1492             sal_uInt16 nWrap = 0;
1493             while ( nRow < nStartRow || nRow > nEndRow )
1494             {
1495                 nCol += nColAdd;
1496 
1497                 while (nStartCol <= nCol && nCol <= nEndCol && ValidCol(nCol) && ColHidden(nCol))
1498                     nCol += nColAdd;    //  skip hidden cols
1499 
1500                 if (nCol < nStartCol)
1501                 {
1502                     nCol = nEndCol;
1503 
1504                     if (++nWrap >= 2)
1505                         return;
1506                 }
1507                 else if (nCol > nEndCol)
1508                 {
1509                     nCol = nStartCol;
1510 
1511                     if (++nWrap >= 2)
1512                         return;
1513                 }
1514                 if (nRow < nStartRow)
1515                     nRow = nEndRow;
1516                 else if (nRow > nEndRow)
1517                     nRow = nStartRow;
1518 
1519                 if (bMarked)
1520                     nRow = rMark.GetNextMarked( nCol, nRow, bUp );
1521 
1522                 while ( SkipRow( nCol, nRow, nMovY, rMark, bUp, nEndRow, bMarked, bSheetProtected ))
1523                     ;
1524             }
1525         } while (false);
1526     }
1527 
1528     if ( nMovX && ( bMarked || bUnprotected ) )
1529     {
1530         // wrap initial skip counting:
1531         if (nCol < nStartCol)
1532         {
1533             nCol = nEndCol;
1534             --nRow;
1535             if (nRow < nStartRow)
1536                 nRow = nEndRow;
1537         }
1538         if (nCol > nEndCol)
1539         {
1540             nCol = nStartCol;
1541             ++nRow;
1542             if (nRow > nEndRow)
1543                 nRow = nStartRow;
1544         }
1545 
1546         if ( !ValidNextPos(nCol, nRow, rMark, bMarked, bUnprotected) )
1547         {
1548             const SCCOL nColCount = nEndCol - nStartCol + 1;
1549             std::unique_ptr<SCROW[]> pNextRows( new SCROW[nColCount]);
1550             const SCCOL nLastCol = aCol.size() - 1;
1551             const bool bUp = (nMovX < 0);   // Moving left also means moving up in rows.
1552             const SCROW nRowAdd = (bUp ? -1 : 1);
1553             sal_uInt16 nWrap = 0;
1554 
1555             if (bUp)
1556             {
1557                 for (SCCOL i = 0; i < nColCount; ++i)
1558                     pNextRows[i] = (i + nStartCol > nCol) ? (nRow + nRowAdd) : nRow;
1559             }
1560             else
1561             {
1562                 for (SCCOL i = 0; i < nColCount; ++i)
1563                     pNextRows[i] = (i + nStartCol < nCol) ? (nRow + nRowAdd) : nRow;
1564             }
1565             do
1566             {
1567                 SCROW nNextRow = pNextRows[nCol - nStartCol] + nRowAdd;
1568                 if ( bMarked )
1569                     nNextRow = rMark.GetNextMarked( nCol, nNextRow, bUp );
1570                 if ( bUnprotected )
1571                     nNextRow = ( nCol <= nLastCol ) ? aCol[nCol].GetNextUnprotected( nNextRow, bUp ) :
1572                         aDefaultColAttrArray.GetNextUnprotected( nNextRow, bUp );
1573                 pNextRows[nCol - nStartCol] = nNextRow;
1574 
1575                 if (bUp)
1576                 {
1577                     SCROW nMaxRow = nStartRow - 1;
1578                     for (SCCOL i = 0; i < nColCount; ++i)
1579                     {
1580                         if (pNextRows[i] >= nMaxRow)    // when two equal the right one
1581                         {
1582                             nMaxRow = pNextRows[i];
1583                             nCol = i + nStartCol;
1584                         }
1585                     }
1586                     nRow = nMaxRow;
1587 
1588                     if ( nRow < nStartRow )
1589                     {
1590                         if (++nWrap >= 2)
1591                             return;
1592                         nCol = nEndCol;
1593                         nRow = nEndRow;
1594                         for (SCCOL i = 0; i < nColCount; ++i)
1595                             pNextRows[i] = nEndRow;     // do it all over again
1596                     }
1597                 }
1598                 else
1599                 {
1600                     SCROW nMinRow = nEndRow + 1;
1601                     for (SCCOL i = 0; i < nColCount; ++i)
1602                     {
1603                         if (pNextRows[i] < nMinRow)     // when two equal the left one
1604                         {
1605                             nMinRow = pNextRows[i];
1606                             nCol = i + nStartCol;
1607                         }
1608                     }
1609                     nRow = nMinRow;
1610 
1611                     if ( nRow > nEndRow )
1612                     {
1613                         if (++nWrap >= 2)
1614                             return;
1615                         nCol = nStartCol;
1616                         nRow = nStartRow;
1617                         for (SCCOL i = 0; i < nColCount; ++i)
1618                             pNextRows[i] = nStartRow;   // do it all over again
1619                     }
1620                 }
1621             }
1622             while ( !ValidNextPos(nCol, nRow, rMark, bMarked, bUnprotected) );
1623         }
1624     }
1625 
1626     if (ValidColRow(nCol,nRow))
1627     {
1628         rCol = nCol;
1629         rRow = nRow;
1630     }
1631 }
1632 
GetNextMarkedCell(SCCOL & rCol,SCROW & rRow,const ScMarkData & rMark) const1633 bool ScTable::GetNextMarkedCell( SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark ) const
1634 {
1635     ++rRow;                 // next row
1636 
1637     while ( rCol < aCol.size() )
1638     {
1639         ScMarkArray aArray( rMark.GetMarkArray( rCol ) );
1640         while ( rRow <= pDocument->MaxRow() )
1641         {
1642             SCROW nStart = aArray.GetNextMarked( rRow, false );
1643             if ( nStart <= pDocument->MaxRow() )
1644             {
1645                 SCROW nEnd = aArray.GetMarkEnd( nStart, false );
1646 
1647                 const sc::CellStoreType& rCells = aCol[rCol].maCells;
1648                 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = rCells.position(nStart);
1649                 sc::CellStoreType::const_iterator it = aPos.first;
1650                 SCROW nTestRow = nStart;
1651                 if (it->type == sc::element_type_empty)
1652                 {
1653                     // Skip the empty block.
1654                     nTestRow += it->size - aPos.second;
1655                     ++it;
1656                     if (it == rCells.end())
1657                     {
1658                         // No more block.  Move on to the next column.
1659                         rRow = pDocument->MaxRow() + 1;
1660                         continue;
1661                     }
1662                 }
1663 
1664                 if (nTestRow <= nEnd)
1665                 {
1666                     // Cell found.
1667                     rRow = nTestRow;
1668                     return true;
1669                 }
1670 
1671                 rRow = nEnd + 1;                // Search for next selected range
1672             }
1673             else
1674                 rRow = pDocument->MaxRow() + 1;              // End of column
1675         }
1676         rRow = 0;
1677         ++rCol;                                 // test next column
1678     }
1679 
1680     // Though searched only the allocated columns, it is equivalent to a search till pDocument->MaxCol().
1681     rCol = pDocument->MaxCol() + 1;
1682     return false;                               // Through all columns
1683 }
1684 
UpdateDrawRef(UpdateRefMode eUpdateRefMode,SCCOL nCol1,SCROW nRow1,SCTAB nTab1,SCCOL nCol2,SCROW nRow2,SCTAB nTab2,SCCOL nDx,SCROW nDy,SCTAB nDz,bool bUpdateNoteCaptionPos)1685 void ScTable::UpdateDrawRef( UpdateRefMode eUpdateRefMode, SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
1686                                     SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
1687                                     SCCOL nDx, SCROW nDy, SCTAB nDz, bool bUpdateNoteCaptionPos )
1688 {
1689     if ( nTab >= nTab1 && nTab <= nTab2 && nDz == 0 )       // only within the table
1690     {
1691         ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer();
1692         if ( eUpdateRefMode != URM_COPY && pDrawLayer )
1693         {
1694             if ( eUpdateRefMode == URM_MOVE )
1695             {                                               // source range
1696                 nCol1 = sal::static_int_cast<SCCOL>( nCol1 - nDx );
1697                 nRow1 = sal::static_int_cast<SCROW>( nRow1 - nDy );
1698                 nCol2 = sal::static_int_cast<SCCOL>( nCol2 - nDx );
1699                 nRow2 = sal::static_int_cast<SCROW>( nRow2 - nDy );
1700             }
1701             pDrawLayer->MoveArea( nTab, nCol1,nRow1, nCol2,nRow2, nDx,nDy,
1702                                     (eUpdateRefMode == URM_INSDEL), bUpdateNoteCaptionPos );
1703         }
1704     }
1705 }
1706 
UpdateReference(sc::RefUpdateContext & rCxt,ScDocument * pUndoDoc,bool bIncludeDraw,bool bUpdateNoteCaptionPos)1707 void ScTable::UpdateReference(
1708     sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, bool bIncludeDraw, bool bUpdateNoteCaptionPos )
1709 {
1710     bool bUpdated = false;
1711     SCCOL i;
1712     SCCOL iMax;
1713     if (rCxt.meMode == URM_COPY )
1714     {
1715         i = rCxt.maRange.aStart.Col();
1716         iMax = rCxt.maRange.aEnd.Col();
1717     }
1718     else
1719     {
1720         i = 0;
1721         iMax = pDocument->MaxCol();
1722     }
1723 
1724     UpdateRefMode eUpdateRefMode = rCxt.meMode;
1725     SCCOL nDx = rCxt.mnColDelta;
1726     SCROW nDy = rCxt.mnRowDelta;
1727     SCTAB nDz = rCxt.mnTabDelta;
1728     SCCOL nCol1 = rCxt.maRange.aStart.Col(), nCol2 = rCxt.maRange.aEnd.Col();
1729     SCROW nRow1 = rCxt.maRange.aStart.Row(), nRow2 = rCxt.maRange.aEnd.Row();
1730     SCTAB nTab1 = rCxt.maRange.aStart.Tab(), nTab2 = rCxt.maRange.aEnd.Tab();
1731 
1732     // Named expressions need to be updated before formulas accessing them.
1733     if (mpRangeName)
1734         mpRangeName->UpdateReference(rCxt, nTab);
1735 
1736     for ( ; i<=iMax; i++)
1737         bUpdated |= CreateColumnIfNotExists(i).UpdateReference(rCxt, pUndoDoc);
1738 
1739     if ( bIncludeDraw )
1740         UpdateDrawRef( eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, nDx, nDy, nDz, bUpdateNoteCaptionPos );
1741 
1742     if ( nTab >= nTab1 && nTab <= nTab2 && nDz == 0 )       // print ranges: only within the table
1743     {
1744         SCTAB nSTab = nTab;
1745         SCTAB nETab = nTab;
1746         SCCOL nSCol = 0;
1747         SCROW nSRow = 0;
1748         SCCOL nECol = 0;
1749         SCROW nERow = 0;
1750         bool bRecalcPages = false;
1751 
1752         for ( auto& rPrintRange : aPrintRanges )
1753         {
1754             nSCol = rPrintRange.aStart.Col();
1755             nSRow = rPrintRange.aStart.Row();
1756             nECol = rPrintRange.aEnd.Col();
1757             nERow = rPrintRange.aEnd.Row();
1758 
1759             // do not try to modify sheet index of print range
1760             if ( ScRefUpdate::Update( pDocument, eUpdateRefMode,
1761                                       nCol1,nRow1,nTab, nCol2,nRow2,nTab,
1762                                       nDx,nDy,0,
1763                                       nSCol,nSRow,nSTab, nECol,nERow,nETab ) )
1764             {
1765                 rPrintRange = ScRange( nSCol, nSRow, 0, nECol, nERow, 0 );
1766                 bRecalcPages = true;
1767             }
1768         }
1769 
1770         if ( pRepeatColRange )
1771         {
1772             nSCol = pRepeatColRange->aStart.Col();
1773             nSRow = pRepeatColRange->aStart.Row();
1774             nECol = pRepeatColRange->aEnd.Col();
1775             nERow = pRepeatColRange->aEnd.Row();
1776 
1777             // do not try to modify sheet index of repeat range
1778             if ( ScRefUpdate::Update( pDocument, eUpdateRefMode,
1779                                       nCol1,nRow1,nTab, nCol2,nRow2,nTab,
1780                                       nDx,nDy,0,
1781                                       nSCol,nSRow,nSTab, nECol,nERow,nETab ) )
1782             {
1783                 *pRepeatColRange = ScRange( nSCol, nSRow, 0, nECol, nERow, 0 );
1784                 bRecalcPages = true;
1785                 nRepeatStartX = nSCol;  // for UpdatePageBreaks
1786                 nRepeatEndX = nECol;
1787             }
1788         }
1789 
1790         if ( pRepeatRowRange )
1791         {
1792             nSCol = pRepeatRowRange->aStart.Col();
1793             nSRow = pRepeatRowRange->aStart.Row();
1794             nECol = pRepeatRowRange->aEnd.Col();
1795             nERow = pRepeatRowRange->aEnd.Row();
1796 
1797             // do not try to modify sheet index of repeat range
1798             if ( ScRefUpdate::Update( pDocument, eUpdateRefMode,
1799                                       nCol1,nRow1,nTab, nCol2,nRow2,nTab,
1800                                       nDx,nDy,0,
1801                                       nSCol,nSRow,nSTab, nECol,nERow,nETab ) )
1802             {
1803                 *pRepeatRowRange = ScRange( nSCol, nSRow, 0, nECol, nERow, 0 );
1804                 bRecalcPages = true;
1805                 nRepeatStartY = nSRow;  // for UpdatePageBreaks
1806                 nRepeatEndY = nERow;
1807             }
1808         }
1809 
1810         //  updating print ranges is not necessary with multiple print ranges
1811         if ( bRecalcPages && GetPrintRangeCount() <= 1 )
1812         {
1813             UpdatePageBreaks(nullptr);
1814 
1815             pDocument->RepaintRange( ScRange(0,0,nTab,pDocument->MaxCol(),pDocument->MaxRow(),nTab) );
1816         }
1817     }
1818 
1819     if (bUpdated)
1820         SetStreamValid(false);
1821 
1822     if(mpCondFormatList)
1823         mpCondFormatList->UpdateReference(rCxt);
1824 
1825     if (pTabProtection)
1826         pTabProtection->updateReference( eUpdateRefMode, pDocument, rCxt.maRange, nDx, nDy, nDz);
1827 }
1828 
UpdateTranspose(const ScRange & rSource,const ScAddress & rDest,ScDocument * pUndoDoc)1829 void ScTable::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
1830                                     ScDocument* pUndoDoc )
1831 {
1832     for (auto const & rpCol : aCol)
1833         rpCol->UpdateTranspose( rSource, rDest, pUndoDoc );
1834 }
1835 
UpdateGrow(const ScRange & rArea,SCCOL nGrowX,SCROW nGrowY)1836 void ScTable::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
1837 {
1838     for (auto const & rpCol : aCol)
1839         rpCol->UpdateGrow( rArea, nGrowX, nGrowY );
1840 }
1841 
UpdateInsertTab(sc::RefUpdateInsertTabContext & rCxt)1842 void ScTable::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
1843 {
1844     // Store the old tab number in sc::UpdatedRangeNames for
1845     // ScTokenArray::AdjustReferenceOnInsertedTab() to check with
1846     // isNameModified()
1847     if (mpRangeName)
1848         mpRangeName->UpdateInsertTab(rCxt, nTab);
1849 
1850     if (nTab >= rCxt.mnInsertPos)
1851     {
1852         nTab += rCxt.mnSheets;
1853         if (pDBDataNoName)
1854             pDBDataNoName->UpdateMoveTab(nTab - 1 ,nTab);
1855     }
1856 
1857     if (mpCondFormatList)
1858         mpCondFormatList->UpdateInsertTab(rCxt);
1859 
1860     if (pTabProtection)
1861         pTabProtection->updateReference( URM_INSDEL, pDocument,
1862                 ScRange( 0, 0, rCxt.mnInsertPos, pDocument->MaxCol(), pDocument->MaxRow(), MAXTAB),
1863                 0, 0, rCxt.mnSheets);
1864 
1865     for (SCCOL i=0; i < aCol.size(); i++)
1866         aCol[i].UpdateInsertTab(rCxt);
1867 
1868     SetStreamValid(false);
1869 }
1870 
UpdateDeleteTab(sc::RefUpdateDeleteTabContext & rCxt)1871 void ScTable::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
1872 {
1873     // Store the old tab number in sc::UpdatedRangeNames for
1874     // ScTokenArray::AdjustReferenceOnDeletedTab() to check with
1875     // isNameModified()
1876     if (mpRangeName)
1877         mpRangeName->UpdateDeleteTab(rCxt, nTab);
1878 
1879     if (nTab > rCxt.mnDeletePos)
1880     {
1881         nTab -= rCxt.mnSheets;
1882         if (pDBDataNoName)
1883             pDBDataNoName->UpdateMoveTab(nTab + 1,nTab);
1884     }
1885 
1886     if (mpCondFormatList)
1887         mpCondFormatList->UpdateDeleteTab(rCxt);
1888 
1889     if (pTabProtection)
1890         pTabProtection->updateReference( URM_INSDEL, pDocument,
1891                 ScRange( 0, 0, rCxt.mnDeletePos, pDocument->MaxCol(), pDocument->MaxRow(), MAXTAB),
1892                 0, 0, -rCxt.mnSheets);
1893 
1894     for (SCCOL i = 0; i < aCol.size(); ++i)
1895         aCol[i].UpdateDeleteTab(rCxt);
1896 
1897     SetStreamValid(false);
1898 }
1899 
UpdateMoveTab(sc::RefUpdateMoveTabContext & rCxt,SCTAB nTabNo,ScProgress * pProgress)1900 void ScTable::UpdateMoveTab(
1901     sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo, ScProgress* pProgress )
1902 {
1903     nTab = nTabNo;
1904     if (mpRangeName)
1905         mpRangeName->UpdateMoveTab(rCxt, nTab);
1906 
1907     if (pDBDataNoName)
1908         pDBDataNoName->UpdateMoveTab(rCxt.mnOldPos, rCxt.mnNewPos);
1909 
1910     if(mpCondFormatList)
1911         mpCondFormatList->UpdateMoveTab(rCxt);
1912 
1913     if (pTabProtection)
1914         pTabProtection->updateReference( URM_REORDER, pDocument,
1915                 ScRange( 0, 0, rCxt.mnOldPos, pDocument->MaxCol(), pDocument->MaxRow(), MAXTAB),
1916                 0, 0, rCxt.mnNewPos - rCxt.mnOldPos);
1917 
1918     for ( SCCOL i=0; i < aCol.size(); i++ )
1919     {
1920         aCol[i].UpdateMoveTab(rCxt, nTabNo);
1921         if (pProgress)
1922             pProgress->SetState(pProgress->GetState() + aCol[i].GetCodeCount());
1923     }
1924 
1925     SetStreamValid(false);
1926 }
1927 
UpdateCompile(bool bForceIfNameInUse)1928 void ScTable::UpdateCompile( bool bForceIfNameInUse )
1929 {
1930     for (SCCOL i=0; i < aCol.size(); i++)
1931     {
1932         aCol[i].UpdateCompile( bForceIfNameInUse );
1933     }
1934 }
1935 
SetTabNo(SCTAB nNewTab)1936 void ScTable::SetTabNo(SCTAB nNewTab)
1937 {
1938     nTab = nNewTab;
1939     for (SCCOL i=0; i < aCol.size(); i++)
1940         aCol[i].SetTabNo(nNewTab);
1941 }
1942 
FindRangeNamesInUse(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,sc::UpdatedRangeNames & rIndexes) const1943 void ScTable::FindRangeNamesInUse(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1944                                sc::UpdatedRangeNames& rIndexes) const
1945 {
1946     for (SCCOL i = nCol1; i <= nCol2 && IsColValid( i ); i++)
1947         aCol[i].FindRangeNamesInUse(nRow1, nRow2, rIndexes);
1948 }
1949 
ExtendPrintArea(OutputDevice * pDev,SCCOL,SCROW nStartRow,SCCOL & rEndCol,SCROW nEndRow)1950 void ScTable::ExtendPrintArea( OutputDevice* pDev,
1951                     SCCOL /* nStartCol */, SCROW nStartRow, SCCOL& rEndCol, SCROW nEndRow )
1952 {
1953     if ( !mpColFlags || !pRowFlags )
1954     {
1955         OSL_FAIL("ExtendPrintArea: No ColInfo or RowInfo");
1956         return;
1957     }
1958 
1959     Point aPix1000 = pDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
1960     double nPPTX = aPix1000.X() / 1000.0;
1961     double nPPTY = aPix1000.Y() / 1000.0;
1962 
1963     // First, mark those columns that we need to skip i.e. hidden and empty columns.
1964 
1965     ScFlatBoolColSegments aSkipCols;
1966     aSkipCols.setFalse(0, pDocument->MaxCol());
1967     for (SCCOL i = 0; i <= pDocument->MaxCol(); ++i)
1968     {
1969         SCCOL nLastCol = i;
1970         if (ColHidden(i, nullptr, &nLastCol))
1971         {
1972             // Columns are hidden in this range.
1973             aSkipCols.setTrue(i, nLastCol);
1974         }
1975         else
1976         {
1977             // These columns are visible.  Check for empty columns.
1978             for (SCCOL j = i; j <= nLastCol; ++j)
1979             {
1980                 if ( j >= aCol.size() )
1981                 {
1982                     aSkipCols.setTrue( j, pDocument->MaxCol() );
1983                     break;
1984                 }
1985                 if (aCol[j].GetCellCount() == 0)
1986                     // empty
1987                     aSkipCols.setTrue(j,j);
1988             }
1989         }
1990         i = nLastCol;
1991     }
1992 
1993     ScFlatBoolColSegments::RangeData aColData;
1994     for (SCCOL nCol = rEndCol; nCol >= 0; --nCol)
1995     {
1996         if (!aSkipCols.getRangeData(nCol, aColData))
1997             // Failed to get the data.  This should never happen!
1998             return;
1999 
2000         if (aColData.mbValue)
2001         {
2002             // Skip these columns.
2003             nCol = aColData.mnCol1; // move toward 0.
2004             continue;
2005         }
2006 
2007         // These are visible and non-empty columns.
2008         for (SCCOL nDataCol = nCol; 0 <= nDataCol && nDataCol >= aColData.mnCol1; --nDataCol)
2009         {
2010             SCCOL nPrintCol = nDataCol;
2011             VisibleDataCellIterator aIter(pDocument, *mpHiddenRows, aCol[nDataCol]);
2012             ScRefCellValue aCell = aIter.reset(nStartRow);
2013             if (aCell.isEmpty())
2014                 // No visible cells found in this column.  Skip it.
2015                 continue;
2016 
2017             while (!aCell.isEmpty())
2018             {
2019                 SCCOL nNewCol = nDataCol;
2020                 SCROW nRow = aIter.getRow();
2021                 if (nRow > nEndRow)
2022                     // Went past the last row position.  Bail out.
2023                     break;
2024 
2025                 MaybeAddExtraColumn(nNewCol, nRow, pDev, nPPTX, nPPTY);
2026                 if (nNewCol > nPrintCol)
2027                     nPrintCol = nNewCol;
2028                 aCell = aIter.next();
2029             }
2030 
2031             if (nPrintCol > rEndCol)
2032                 // Make sure we don't shrink the print area.
2033                 rEndCol = nPrintCol;
2034         }
2035         nCol = aColData.mnCol1; // move toward 0.
2036     }
2037 }
2038 
MaybeAddExtraColumn(SCCOL & rCol,SCROW nRow,OutputDevice * pDev,double nPPTX,double nPPTY)2039 void ScTable::MaybeAddExtraColumn(SCCOL& rCol, SCROW nRow, OutputDevice* pDev, double nPPTX, double nPPTY)
2040 {
2041     // tdf#128873 we do not need to calculate text width (heavy operation)
2042     // when we for sure know that an additional column will not be added
2043     if (GetAllocatedColumnsCount() > rCol + 1)
2044     {
2045         ScRefCellValue aNextCell = aCol[rCol + 1].GetCellValue(nRow);
2046         if (!aNextCell.isEmpty())
2047         {
2048             // return rCol as is
2049             return;
2050         }
2051     }
2052 
2053     ScColumn& rColumn = aCol[rCol];
2054     ScRefCellValue aCell = rColumn.GetCellValue(nRow);
2055     if (!aCell.hasString())
2056         return;
2057 
2058     long nPixel = rColumn.GetTextWidth(nRow);
2059 
2060     // Width already calculated in Idle-Handler ?
2061     if ( TEXTWIDTH_DIRTY == nPixel )
2062     {
2063         ScNeededSizeOptions aOptions;
2064         aOptions.bTotalSize  = true;
2065         aOptions.bFormula    = false; //TODO: pass as parameter
2066         aOptions.bSkipMerged = false;
2067 
2068         Fraction aZoom(1,1);
2069         nPixel = rColumn.GetNeededSize(
2070             nRow, pDev, nPPTX, nPPTY, aZoom, aZoom, true, aOptions, nullptr );
2071 
2072         rColumn.SetTextWidth(nRow, static_cast<sal_uInt16>(nPixel));
2073     }
2074 
2075     long nTwips = static_cast<long>(nPixel / nPPTX);
2076     long nDocW = GetColWidth( rCol );
2077 
2078     long nMissing = nTwips - nDocW;
2079     if ( nMissing > 0 )
2080     {
2081         //  look at alignment
2082 
2083         const ScPatternAttr* pPattern = GetPattern( rCol, nRow );
2084         const SfxItemSet* pCondSet = pDocument->GetCondResult( rCol, nRow, nTab );
2085 
2086         SvxCellHorJustify eHorJust =
2087                         pPattern->GetItem( ATTR_HOR_JUSTIFY, pCondSet ).GetValue();
2088         if ( eHorJust == SvxCellHorJustify::Center )
2089             nMissing /= 2;                          // distributed into both directions
2090         else
2091         {
2092             // STANDARD is LEFT (only text is handled here)
2093             bool bRight = ( eHorJust == SvxCellHorJustify::Right );
2094             if ( IsLayoutRTL() )
2095                 bRight = !bRight;
2096             if ( bRight )
2097                 nMissing = 0;       // extended only to the left (logical)
2098         }
2099     }
2100 
2101     SCCOL nNewCol = rCol;
2102     while (nMissing > 0 && nNewCol < pDocument->MaxCol())
2103     {
2104         auto nNextCol = nNewCol + 1;
2105         bool bNextEmpty = true;
2106         if (GetAllocatedColumnsCount() > nNextCol)
2107         {
2108             ScRefCellValue aNextCell = aCol[nNextCol].GetCellValue(nRow);
2109             bNextEmpty = aNextCell.isEmpty();
2110         }
2111         if (!bNextEmpty)
2112         {
2113             // Cell content in a next column ends display of this string.
2114             nMissing = 0;
2115         }
2116         else
2117             nMissing -= GetColWidth(++nNewCol);
2118     }
2119     rCol = nNewCol;
2120 }
2121 
2122 namespace {
2123 
2124 class SetTableIndex
2125 {
2126     SCTAB const mnTab;
2127 public:
SetTableIndex(SCTAB nTab)2128     explicit SetTableIndex(SCTAB nTab) : mnTab(nTab) {}
2129 
operator ()(ScRange & rRange) const2130     void operator() (ScRange& rRange) const
2131     {
2132         rRange.aStart.SetTab(mnTab);
2133         rRange.aEnd.SetTab(mnTab);
2134     }
2135 };
2136 
2137 }
2138 
CopyPrintRange(const ScTable & rTable)2139 void ScTable::CopyPrintRange(const ScTable& rTable)
2140 {
2141     // The table index shouldn't be used when the print range is used, but
2142     // just in case set the correct table index.
2143 
2144     aPrintRanges = rTable.aPrintRanges;
2145     ::std::for_each(aPrintRanges.begin(), aPrintRanges.end(), SetTableIndex(nTab));
2146 
2147     bPrintEntireSheet = rTable.bPrintEntireSheet;
2148 
2149     pRepeatColRange.reset();
2150     if (rTable.pRepeatColRange)
2151     {
2152         pRepeatColRange.reset(new ScRange(*rTable.pRepeatColRange));
2153         pRepeatColRange->aStart.SetTab(nTab);
2154         pRepeatColRange->aEnd.SetTab(nTab);
2155     }
2156 
2157     pRepeatRowRange.reset();
2158     if (rTable.pRepeatRowRange)
2159     {
2160         pRepeatRowRange.reset(new ScRange(*rTable.pRepeatRowRange));
2161         pRepeatRowRange->aStart.SetTab(nTab);
2162         pRepeatRowRange->aEnd.SetTab(nTab);
2163     }
2164 }
2165 
SetRepeatColRange(std::unique_ptr<ScRange> pNew)2166 void ScTable::SetRepeatColRange( std::unique_ptr<ScRange> pNew )
2167 {
2168     pRepeatColRange = std::move(pNew);
2169 
2170     SetStreamValid(false);
2171 
2172     InvalidatePageBreaks();
2173 }
2174 
SetRepeatRowRange(std::unique_ptr<ScRange> pNew)2175 void ScTable::SetRepeatRowRange( std::unique_ptr<ScRange> pNew )
2176 {
2177     pRepeatRowRange = std::move(pNew);
2178 
2179     SetStreamValid(false);
2180 
2181     InvalidatePageBreaks();
2182 }
2183 
ClearPrintRanges()2184 void ScTable::ClearPrintRanges()
2185 {
2186     aPrintRanges.clear();
2187     bPrintEntireSheet = false;
2188 
2189     SetStreamValid(false);
2190 
2191     InvalidatePageBreaks();     // #i117952# forget page breaks for an old print range
2192 }
2193 
AddPrintRange(const ScRange & rNew)2194 void ScTable::AddPrintRange( const ScRange& rNew )
2195 {
2196     bPrintEntireSheet = false;
2197     if( aPrintRanges.size() < 0xFFFF )
2198         aPrintRanges.push_back( rNew );
2199 
2200     SetStreamValid(false);
2201 
2202     InvalidatePageBreaks();
2203 }
2204 
SetPrintEntireSheet()2205 void ScTable::SetPrintEntireSheet()
2206 {
2207     if( !IsPrintEntireSheet() )
2208     {
2209         ClearPrintRanges();
2210         bPrintEntireSheet = true;
2211     }
2212 }
2213 
GetPrintRange(sal_uInt16 nPos) const2214 const ScRange* ScTable::GetPrintRange(sal_uInt16 nPos) const
2215 {
2216     return (nPos < GetPrintRangeCount()) ? &aPrintRanges[ nPos ] : nullptr;
2217 }
2218 
FillPrintSaver(ScPrintSaverTab & rSaveTab) const2219 void ScTable::FillPrintSaver( ScPrintSaverTab& rSaveTab ) const
2220 {
2221     rSaveTab.SetAreas( aPrintRanges, bPrintEntireSheet );
2222     rSaveTab.SetRepeat( pRepeatColRange.get(), pRepeatRowRange.get() );
2223 }
2224 
RestorePrintRanges(const ScPrintSaverTab & rSaveTab)2225 void ScTable::RestorePrintRanges( const ScPrintSaverTab& rSaveTab )
2226 {
2227     aPrintRanges = rSaveTab.GetPrintRanges();
2228     bPrintEntireSheet = rSaveTab.IsEntireSheet();
2229     auto p = rSaveTab.GetRepeatCol();
2230     SetRepeatColRange( std::unique_ptr<ScRange>(p ? new ScRange(*p) : nullptr) );
2231     p = rSaveTab.GetRepeatRow();
2232     SetRepeatRowRange( std::unique_ptr<ScRange>(p ? new ScRange(*p) : nullptr) );
2233 
2234     InvalidatePageBreaks();     // #i117952# forget page breaks for an old print range
2235     UpdatePageBreaks(nullptr);
2236 }
2237 
VisibleDataCellIterator(const ScDocument * pDoc,ScFlatBoolRowSegments & rRowSegs,ScColumn & rColumn)2238 ScTable::VisibleDataCellIterator::VisibleDataCellIterator(const ScDocument* pDoc, ScFlatBoolRowSegments& rRowSegs, ScColumn& rColumn) :
2239     mpDocument(pDoc),
2240     mrRowSegs(rRowSegs),
2241     mrColumn(rColumn),
2242     mnCurRow(ROW_NOT_FOUND),
2243     mnUBound(ROW_NOT_FOUND)
2244 {
2245 }
2246 
~VisibleDataCellIterator()2247 ScTable::VisibleDataCellIterator::~VisibleDataCellIterator()
2248 {
2249 }
2250 
reset(SCROW nRow)2251 ScRefCellValue ScTable::VisibleDataCellIterator::reset(SCROW nRow)
2252 {
2253     if (nRow > mpDocument->MaxRow())
2254     {
2255         mnCurRow = ROW_NOT_FOUND;
2256         return ScRefCellValue();
2257     }
2258 
2259     ScFlatBoolRowSegments::RangeData aData;
2260     if (!mrRowSegs.getRangeData(nRow, aData))
2261     {
2262         mnCurRow = ROW_NOT_FOUND;
2263         return ScRefCellValue();
2264     }
2265 
2266     if (!aData.mbValue)
2267     {
2268         // specified row is visible.  Take it.
2269         mnCurRow = nRow;
2270         mnUBound = aData.mnRow2;
2271     }
2272     else
2273     {
2274         // specified row is not-visible.  The first visible row is the start of
2275         // the next segment.
2276         mnCurRow = aData.mnRow2 + 1;
2277         mnUBound = mnCurRow; // get range data on the next iteration.
2278         if (mnCurRow > mpDocument->MaxRow())
2279         {
2280             // Make sure the row doesn't exceed our current limit.
2281             mnCurRow = ROW_NOT_FOUND;
2282             return ScRefCellValue();
2283         }
2284     }
2285 
2286     maCell = mrColumn.GetCellValue(mnCurRow);
2287     if (!maCell.isEmpty())
2288         // First visible cell found.
2289         return maCell;
2290 
2291     // Find a first visible cell below this row (if any).
2292     return next();
2293 }
2294 
next()2295 ScRefCellValue ScTable::VisibleDataCellIterator::next()
2296 {
2297     if (mnCurRow == ROW_NOT_FOUND)
2298         return ScRefCellValue();
2299 
2300     while (mrColumn.GetNextDataPos(mnCurRow))
2301     {
2302         if (mnCurRow > mnUBound)
2303         {
2304             // We don't know the visibility of this row range.  Query it.
2305             ScFlatBoolRowSegments::RangeData aData;
2306             if (!mrRowSegs.getRangeData(mnCurRow, aData))
2307             {
2308                 mnCurRow = ROW_NOT_FOUND;
2309                 return ScRefCellValue();
2310             }
2311 
2312             if (aData.mbValue)
2313             {
2314                 // This row is invisible.  Skip to the last invisible row and
2315                 // try again.
2316                 mnCurRow = mnUBound = aData.mnRow2;
2317                 continue;
2318             }
2319 
2320             // This row is visible.
2321             mnUBound = aData.mnRow2;
2322         }
2323 
2324         maCell = mrColumn.GetCellValue(mnCurRow);
2325         if (!maCell.isEmpty())
2326             return maCell;
2327     }
2328 
2329     mnCurRow = ROW_NOT_FOUND;
2330     return ScRefCellValue();
2331 }
2332 
SetAnonymousDBData(std::unique_ptr<ScDBData> pDBData)2333 void ScTable::SetAnonymousDBData(std::unique_ptr<ScDBData> pDBData)
2334 {
2335     pDBDataNoName = std::move(pDBData);
2336 }
2337 
AddCondFormat(std::unique_ptr<ScConditionalFormat> pNew)2338 sal_uLong ScTable::AddCondFormat( std::unique_ptr<ScConditionalFormat> pNew )
2339 {
2340     if(!mpCondFormatList)
2341         mpCondFormatList.reset(new ScConditionalFormatList());
2342 
2343     sal_uInt32 nMax = mpCondFormatList->getMaxKey();
2344 
2345     pNew->SetKey(nMax+1);
2346     mpCondFormatList->InsertNew(std::move(pNew));
2347 
2348     return nMax + 1;
2349 }
2350 
GetScriptType(SCCOL nCol,SCROW nRow) const2351 SvtScriptType ScTable::GetScriptType( SCCOL nCol, SCROW nRow ) const
2352 {
2353     if ( !IsColValid( nCol ) )
2354         return SvtScriptType::NONE;
2355 
2356     return aCol[nCol].GetScriptType(nRow);
2357 }
2358 
SetScriptType(SCCOL nCol,SCROW nRow,SvtScriptType nType)2359 void ScTable::SetScriptType( SCCOL nCol, SCROW nRow, SvtScriptType nType )
2360 {
2361     if (!ValidCol(nCol))
2362         return;
2363 
2364     aCol[nCol].SetScriptType(nRow, nType);
2365 }
2366 
GetRangeScriptType(sc::ColumnBlockPosition & rBlockPos,SCCOL nCol,SCROW nRow1,SCROW nRow2)2367 SvtScriptType ScTable::GetRangeScriptType(
2368     sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow1, SCROW nRow2 )
2369 {
2370     if ( !IsColValid( nCol ) )
2371         return SvtScriptType::NONE;
2372 
2373     sc::CellStoreType::iterator itr = aCol[nCol].maCells.begin();
2374     return aCol[nCol].GetRangeScriptType(rBlockPos.miCellTextAttrPos, nRow1, nRow2, itr);
2375 }
2376 
GetFormulaHash(SCCOL nCol,SCROW nRow) const2377 size_t ScTable::GetFormulaHash( SCCOL nCol, SCROW nRow ) const
2378 {
2379     if ( !IsColValid( nCol ) )
2380         return 0;
2381 
2382     return aCol[nCol].GetFormulaHash(nRow);
2383 }
2384 
GetFormulaVectorState(SCCOL nCol,SCROW nRow) const2385 ScFormulaVectorState ScTable::GetFormulaVectorState( SCCOL nCol, SCROW nRow ) const
2386 {
2387     if ( !IsColValid( nCol ) )
2388         return FormulaVectorUnknown;
2389 
2390     return aCol[nCol].GetFormulaVectorState(nRow);
2391 }
2392 
ResolveStaticReference(SCCOL nCol,SCROW nRow)2393 formula::FormulaTokenRef ScTable::ResolveStaticReference( SCCOL nCol, SCROW nRow )
2394 {
2395     if ( !ValidCol( nCol ) || !ValidRow( nRow ) )
2396         return formula::FormulaTokenRef();
2397     if ( nCol >= aCol.size() )
2398         // Return a value of 0.0 if column not exists
2399         return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0));
2400     return aCol[nCol].ResolveStaticReference(nRow);
2401 }
2402 
ResolveStaticReference(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2)2403 formula::FormulaTokenRef ScTable::ResolveStaticReference( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
2404 {
2405     if (nCol2 < nCol1 || nRow2 < nRow1)
2406         return formula::FormulaTokenRef();
2407 
2408     if ( !ValidCol( nCol1 ) || !ValidCol( nCol2 ) || !ValidRow( nRow1 ) || !ValidRow( nRow2 ) )
2409         return formula::FormulaTokenRef();
2410 
2411     SCCOL nMaxCol;
2412     if ( nCol2 >= aCol.size() )
2413         nMaxCol = aCol.size() - 1;
2414     else
2415         nMaxCol = nCol2;
2416 
2417     ScMatrixRef pMat(new ScMatrix(nCol2-nCol1+1, nRow2-nRow1+1, 0.0));
2418     for (SCCOL nCol = nCol1; nCol <= nMaxCol; ++nCol)
2419     {
2420         if (!aCol[nCol].ResolveStaticReference(*pMat, nCol2-nCol1, nRow1, nRow2))
2421             // Column contains non-static cell. Failed.
2422             return formula::FormulaTokenRef();
2423     }
2424 
2425     return formula::FormulaTokenRef(new ScMatrixToken(pMat));
2426 }
2427 
FetchVectorRefArray(SCCOL nCol,SCROW nRow1,SCROW nRow2)2428 formula::VectorRefArray ScTable::FetchVectorRefArray( SCCOL nCol, SCROW nRow1, SCROW nRow2 )
2429 {
2430     if (nRow2 < nRow1)
2431         return formula::VectorRefArray();
2432 
2433     if ( !IsColValid( nCol ) || !ValidRow( nRow1 ) || !ValidRow( nRow2 ) )
2434         return formula::VectorRefArray();
2435 
2436     return aCol[nCol].FetchVectorRefArray(nRow1, nRow2);
2437 }
2438 
2439 #ifdef DBG_UTIL
AssertNoInterpretNeeded(SCCOL nCol,SCROW nRow1,SCROW nRow2)2440 void ScTable::AssertNoInterpretNeeded( SCCOL nCol, SCROW nRow1, SCROW nRow2 )
2441 {
2442     assert( nRow2 >= nRow1 );
2443     assert( IsColValid( nCol ) && ValidRow( nRow1 ) && ValidRow( nRow2 ) );
2444     return aCol[nCol].AssertNoInterpretNeeded(nRow1, nRow2);
2445 }
2446 #endif
2447 
HandleRefArrayForParallelism(SCCOL nCol,SCROW nRow1,SCROW nRow2,const ScFormulaCellGroupRef & mxGroup)2448 bool ScTable::HandleRefArrayForParallelism( SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScFormulaCellGroupRef& mxGroup )
2449 {
2450     if (nRow2 < nRow1)
2451         return false;
2452 
2453     if ( !IsColValid( nCol ) || !ValidRow( nRow1 ) || !ValidRow( nRow2 ) )
2454         return false;
2455 
2456     return aCol[nCol].HandleRefArrayForParallelism(nRow1, nRow2, mxGroup);
2457 }
2458 
GetRefCellValue(SCCOL nCol,SCROW nRow)2459 ScRefCellValue ScTable::GetRefCellValue( SCCOL nCol, SCROW nRow )
2460 {
2461     if ( !IsColRowValid( nCol, nRow ) )
2462         return ScRefCellValue();
2463 
2464     return aCol[nCol].GetCellValue(nRow);
2465 }
2466 
GetRefCellValue(SCCOL nCol,SCROW nRow,sc::ColumnBlockPosition & rBlockPos)2467 ScRefCellValue ScTable::GetRefCellValue( SCCOL nCol, SCROW nRow, sc::ColumnBlockPosition& rBlockPos )
2468 {
2469     if ( !IsColRowValid( nCol, nRow ) )
2470         return ScRefCellValue();
2471 
2472     return aCol[nCol].GetCellValue(rBlockPos, nRow);
2473 }
2474 
GetBroadcaster(SCCOL nCol,SCROW nRow)2475 SvtBroadcaster* ScTable::GetBroadcaster( SCCOL nCol, SCROW nRow )
2476 {
2477     if ( !IsColRowValid( nCol, nRow ) )
2478         return nullptr;
2479 
2480     return aCol[nCol].GetBroadcaster(nRow);
2481 }
2482 
DeleteBroadcasters(sc::ColumnBlockPosition & rBlockPos,SCCOL nCol,SCROW nRow1,SCROW nRow2)2483 void ScTable::DeleteBroadcasters(
2484     sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow1, SCROW nRow2 )
2485 {
2486     if ( !IsColValid( nCol ) )
2487         return;
2488 
2489     aCol[nCol].DeleteBroadcasters(rBlockPos, nRow1, nRow2);
2490 }
2491 
FillMatrix(ScMatrix & rMat,SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,svl::SharedStringPool * pPool) const2492 void ScTable::FillMatrix( ScMatrix& rMat, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, svl::SharedStringPool* pPool ) const
2493 {
2494     size_t nMatCol = 0;
2495     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol, ++nMatCol)
2496         CreateColumnIfNotExists(nCol).FillMatrix(rMat, nMatCol, nRow1, nRow2, pPool);
2497 }
2498 
InterpretDirtyCells(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2)2499 void ScTable::InterpretDirtyCells( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
2500 {
2501     nCol2 = ClampToAllocatedColumns(nCol2);
2502     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2503         aCol[nCol].InterpretDirtyCells(nRow1, nRow2);
2504 }
2505 
SetFormulaResults(SCCOL nCol,SCROW nRow,const double * pResults,size_t nLen)2506 void ScTable::SetFormulaResults( SCCOL nCol, SCROW nRow, const double* pResults, size_t nLen )
2507 {
2508     if (!ValidCol(nCol))
2509         return;
2510 
2511     aCol[nCol].SetFormulaResults(nRow, pResults, nLen);
2512 }
2513 
CalculateInColumnInThread(ScInterpreterContext & rContext,SCCOL nColStart,SCCOL nColEnd,SCROW nRowStart,SCROW nRowEnd,unsigned nThisThread,unsigned nThreadsTotal)2514 void ScTable::CalculateInColumnInThread( ScInterpreterContext& rContext,
2515                                          SCCOL nColStart, SCCOL nColEnd,
2516                                          SCROW nRowStart, SCROW nRowEnd,
2517                                          unsigned nThisThread, unsigned nThreadsTotal)
2518 {
2519     if (!ValidCol(nColStart) || !ValidCol(nColEnd))
2520         return;
2521 
2522     size_t nLen = nRowEnd - nRowStart + 1;
2523     size_t nOffset = 0;
2524     for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol)
2525     {
2526         aCol[nCurrCol].CalculateInThread( rContext, nRowStart, nLen, nOffset, nThisThread, nThreadsTotal );
2527         nOffset += nLen;
2528     }
2529 }
2530 
HandleStuffAfterParallelCalculation(SCCOL nColStart,SCCOL nColEnd,SCROW nRow,size_t nLen,ScInterpreter * pInterpreter)2531 void ScTable::HandleStuffAfterParallelCalculation( SCCOL nColStart, SCCOL nColEnd, SCROW nRow, size_t nLen,
2532                                                    ScInterpreter* pInterpreter)
2533 {
2534     assert(ValidCol(nColStart) && ValidCol(nColEnd));
2535 
2536     for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol)
2537         aCol[nCurrCol].HandleStuffAfterParallelCalculation( nRow, nLen, pInterpreter );
2538 }
2539 
2540 #if DUMP_COLUMN_STORAGE
DumpColumnStorage(SCCOL nCol) const2541 void ScTable::DumpColumnStorage( SCCOL nCol ) const
2542 {
2543     if ( !IsColValid( nCol ) )
2544         return;
2545 
2546     aCol[nCol].DumpColumnStorage();
2547 }
2548 #endif
2549 
GetBroadcaster(SCCOL nCol,SCROW nRow) const2550 const SvtBroadcaster* ScTable::GetBroadcaster( SCCOL nCol, SCROW nRow ) const
2551 {
2552     if ( !IsColRowValid( nCol, nRow ) )
2553         return nullptr;
2554 
2555     return aCol[nCol].GetBroadcaster(nRow);
2556 }
2557 
DeleteConditionalFormat(sal_uLong nIndex)2558 void ScTable::DeleteConditionalFormat( sal_uLong nIndex )
2559 {
2560     mpCondFormatList->erase(nIndex);
2561 }
2562 
SetCondFormList(ScConditionalFormatList * pNew)2563 void ScTable::SetCondFormList( ScConditionalFormatList* pNew )
2564 {
2565     mpCondFormatList.reset( pNew );
2566 }
2567 
GetCondFormList()2568 ScConditionalFormatList* ScTable::GetCondFormList()
2569 {
2570     if(!mpCondFormatList)
2571         mpCondFormatList.reset( new ScConditionalFormatList() );
2572 
2573     return mpCondFormatList.get();
2574 }
2575 
GetCondFormList() const2576 const ScConditionalFormatList* ScTable::GetCondFormList() const
2577 {
2578     return mpCondFormatList.get();
2579 }
2580 
GetColumnsRange(SCCOL nColBegin,SCCOL nColEnd) const2581 ScColumnsRange ScTable::GetColumnsRange(SCCOL nColBegin, SCCOL nColEnd) const
2582 {
2583     ScColContainer::ScColumnVector::const_iterator beginIter;
2584     ScColContainer::ScColumnVector::const_iterator endIter;
2585 
2586     // because the range is inclusive, some code will pass nColEnd<nColBegin to indicate an empty range
2587     if (nColEnd < nColBegin)
2588     {
2589         beginIter = aCol.end();
2590         endIter = aCol.end();
2591     }
2592     else if (nColBegin >= aCol.size())
2593     {
2594         beginIter = aCol.end();
2595         endIter = aCol.end();
2596     }
2597     else
2598     {
2599         // clamp end of range to available columns
2600         if (nColEnd >= aCol.size())
2601             nColEnd = aCol.size() - 1;
2602         beginIter = aCol.begin() + nColBegin;
2603         endIter = aCol.begin() + nColEnd + 1;
2604     }
2605     return ScColumnsRange(ScColumnsRange::Iterator(beginIter), ScColumnsRange::Iterator(endIter));
2606 }
2607 
2608 // out-of-line the cold part of the CreateColumnIfNotExists function
CreateColumnIfNotExistsImpl(const SCCOL nScCol) const2609 void ScTable::CreateColumnIfNotExistsImpl( const SCCOL nScCol ) const
2610 {
2611     // When doing multi-threaded load of, e.g. XLS files, we can hit this, which calls
2612     // into SfxItemPool::Put, in parallel with other code that calls into SfxItemPool::Put,
2613     // which is bad since that code is not thread-safe.
2614     SolarMutexGuard aGuard;
2615     const SCCOL aOldColSize = aCol.size();
2616     aCol.resize( static_cast< size_t >( nScCol + 1 ) );
2617     for (SCCOL i = aOldColSize; i <= nScCol; i++)
2618         aCol[i].Init( i, nTab, pDocument, false );
2619 }
2620 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2621