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