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