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