xref: /core/sc/source/core/data/table2.cxx (revision 8cad1620)
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 <algorithm>
21 #include <memory>
22 #include <table.hxx>
23 #include <patattr.hxx>
24 #include <docpool.hxx>
25 #include <formulacell.hxx>
26 #include <document.hxx>
27 #include <drwlayer.hxx>
28 #include <olinetab.hxx>
29 #include <stlpool.hxx>
30 #include <attarray.hxx>
31 #include <markdata.hxx>
32 #include <dociter.hxx>
33 #include <conditio.hxx>
34 #include <chartlis.hxx>
35 #include <fillinfo.hxx>
36 #include <bcaslot.hxx>
37 #include <postit.hxx>
38 #include <sheetevents.hxx>
39 #include <segmenttree.hxx>
40 #include <dbdata.hxx>
41 #include <tokenarray.hxx>
42 #include <clipcontext.hxx>
43 #include <types.hxx>
44 #include <editutil.hxx>
45 #include <mtvcellfunc.hxx>
46 #include <refupdatecontext.hxx>
47 #include <scopetools.hxx>
48 #include <tabprotection.hxx>
49 #include <columnspanset.hxx>
50 #include <rowheightcontext.hxx>
51 #include <listenercontext.hxx>
52 #include <compressedarray.hxx>
53 #include <brdcst.hxx>
54 #include <refdata.hxx>
55 #include <docsh.hxx>
56 
57 #include <scitems.hxx>
58 #include <editeng/boxitem.hxx>
59 #include <editeng/editobj.hxx>
60 #include <o3tl/safeint.hxx>
61 #include <svl/poolcach.hxx>
62 #include <unotools/charclass.hxx>
63 #include <math.h>
64 
65 namespace {
66 
67 class ColumnRegroupFormulaCells
68 {
69     ScColContainer& mrCols;
70     std::vector<ScAddress>* mpGroupPos;
71 
72 public:
73     ColumnRegroupFormulaCells( ScColContainer& rCols, std::vector<ScAddress>* pGroupPos ) :
74         mrCols(rCols), mpGroupPos(pGroupPos) {}
75 
76     void operator() (SCCOL nCol)
77     {
78         mrCols[nCol].RegroupFormulaCells(mpGroupPos);
79     }
80 };
81 
82 }
83 
84 sal_uInt16 ScTable::GetTextWidth(SCCOL nCol, SCROW nRow) const
85 {
86     return aCol[nCol].GetTextWidth(nRow);
87 }
88 
89 bool ScTable::SetOutlineTable( const ScOutlineTable* pNewOutline )
90 {
91     sal_uInt16 nOldSizeX = 0;
92     sal_uInt16 nOldSizeY = 0;
93     sal_uInt16 nNewSizeX = 0;
94     sal_uInt16 nNewSizeY = 0;
95 
96     if (pOutlineTable)
97     {
98         nOldSizeX = pOutlineTable->GetColArray().GetDepth();
99         nOldSizeY = pOutlineTable->GetRowArray().GetDepth();
100         pOutlineTable.reset();
101     }
102 
103     if (pNewOutline)
104     {
105         pOutlineTable.reset(new ScOutlineTable( *pNewOutline ));
106         nNewSizeX = pOutlineTable->GetColArray().GetDepth();
107         nNewSizeY = pOutlineTable->GetRowArray().GetDepth();
108     }
109 
110     return ( nNewSizeX != nOldSizeX || nNewSizeY != nOldSizeY );        // changed size?
111 }
112 
113 void ScTable::StartOutlineTable()
114 {
115     if (!pOutlineTable)
116         pOutlineTable.reset(new ScOutlineTable);
117 }
118 
119 void ScTable::SetSheetEvents( std::unique_ptr<ScSheetEvents> pNew )
120 {
121     pSheetEvents = std::move(pNew);
122 
123     SetCalcNotification( false );       // discard notifications before the events were set
124 
125     SetStreamValid(false);
126 }
127 
128 void ScTable::SetCalcNotification( bool bSet )
129 {
130     bCalcNotification = bSet;
131 }
132 
133 bool ScTable::TestInsertRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize ) const
134 {
135     bool bTest = true;
136 
137     if ( nStartCol==0 && nEndCol==pDocument->MaxCol() && pOutlineTable )
138         bTest = pOutlineTable->TestInsertRow(nSize);
139 
140     for (SCCOL i=nStartCol; (i<=nEndCol) && bTest; i++)
141         bTest = CreateColumnIfNotExists(i).TestInsertRow(nStartRow, nSize);
142 
143     return bTest;
144 }
145 
146 void ScTable::InsertRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize )
147 {
148     if (nStartCol==0 && nEndCol==pDocument->MaxCol())
149     {
150         if (mpRowHeights && pRowFlags)
151         {
152             mpRowHeights->insertSegment(nStartRow, nSize);
153             CRFlags nNewFlags = pRowFlags->Insert( nStartRow, nSize);
154             // only copy manual size flag, clear all others
155             if (nNewFlags != CRFlags::NONE && (nNewFlags != CRFlags::ManualSize))
156                 pRowFlags->SetValue( nStartRow, nStartRow + nSize - 1,
157                         nNewFlags & CRFlags::ManualSize);
158         }
159 
160         if (pOutlineTable)
161             pOutlineTable->InsertRow( nStartRow, nSize );
162 
163         mpFilteredRows->insertSegment(nStartRow, nSize);
164         mpHiddenRows->insertSegment(nStartRow, nSize);
165 
166         if (!maRowManualBreaks.empty())
167         {
168             // Copy all breaks up to nStartRow (non-inclusive).
169             ::std::set<SCROW>::iterator itr1 = maRowManualBreaks.lower_bound(nStartRow);
170             ::std::set<SCROW> aNewBreaks(maRowManualBreaks.begin(), itr1);
171 
172             // Copy all breaks from nStartRow (inclusive) to the last element,
173             // but add nSize to each value.
174             ::std::set<SCROW>::iterator itr2 = maRowManualBreaks.end();
175             for (; itr1 != itr2; ++itr1)
176                 aNewBreaks.insert(static_cast<SCROW>(*itr1 + nSize));
177 
178             maRowManualBreaks.swap(aNewBreaks);
179         }
180     }
181 
182     for (SCCOL j=nStartCol; j<=nEndCol; j++)
183         aCol[j].InsertRow( nStartRow, nSize );
184 
185     mpCondFormatList->InsertRow(nTab, nStartCol, nEndCol, nStartRow, nSize);
186 
187     InvalidatePageBreaks();
188 
189     // TODO: In the future we may want to check if the table has been
190     // really modified before setting the stream invalid.
191     SetStreamValid(false);
192 }
193 
194 void ScTable::DeleteRow(
195     const sc::ColumnSet& rRegroupCols, SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize,
196     bool* pUndoOutline, std::vector<ScAddress>* pGroupPos )
197 {
198     if (nStartCol==0 && nEndCol==pDocument->MaxCol())
199     {
200         if (pRowFlags)
201             pRowFlags->Remove( nStartRow, nSize);
202 
203         if (mpRowHeights)
204             mpRowHeights->removeSegment(nStartRow, nStartRow+nSize);
205 
206         if (pOutlineTable)
207             if (pOutlineTable->DeleteRow( nStartRow, nSize ))
208                 if (pUndoOutline)
209                     *pUndoOutline = true;
210 
211         mpFilteredRows->removeSegment(nStartRow, nStartRow+nSize);
212         mpHiddenRows->removeSegment(nStartRow, nStartRow+nSize);
213 
214         if (!maRowManualBreaks.empty())
215         {
216             // Erase all manual breaks between nStartRow and nStartRow + nSize - 1 (inclusive).
217             std::set<SCROW>::iterator itr1 = maRowManualBreaks.lower_bound(nStartRow);
218             std::set<SCROW>::iterator itr2 = maRowManualBreaks.upper_bound(static_cast<SCROW>(nStartRow + nSize - 1));
219             maRowManualBreaks.erase(itr1, itr2);
220 
221             // Copy all breaks from the 1st element up to nStartRow to the new container.
222             itr1 = maRowManualBreaks.lower_bound(nStartRow);
223             ::std::set<SCROW> aNewBreaks(maRowManualBreaks.begin(), itr1);
224 
225             // Copy all breaks from nStartRow to the last element, but subtract each value by nSize.
226             itr2 = maRowManualBreaks.end();
227             for (; itr1 != itr2; ++itr1)
228                 aNewBreaks.insert(static_cast<SCROW>(*itr1 - nSize));
229 
230             maRowManualBreaks.swap(aNewBreaks);
231         }
232     }
233 
234     {   // scope for bulk broadcast
235         ScBulkBroadcast aBulkBroadcast( pDocument->GetBASM(), SfxHintId::ScDataChanged);
236         for (SCCOL j=nStartCol; j<=nEndCol; j++)
237             aCol[j].DeleteRow(nStartRow, nSize, pGroupPos);
238     }
239 
240     std::vector<SCCOL> aRegroupCols;
241     rRegroupCols.getColumns(nTab, aRegroupCols);
242     std::for_each(
243         aRegroupCols.begin(), aRegroupCols.end(), ColumnRegroupFormulaCells(aCol, pGroupPos));
244 
245     InvalidatePageBreaks();
246 
247     // TODO: In the future we may want to check if the table has been
248     // really modified before setting the stream invalid.
249     SetStreamValid(false);
250 }
251 
252 bool ScTable::TestInsertCol( SCROW nStartRow, SCROW nEndRow, SCSIZE nSize ) const
253 {
254     if ( nSize > o3tl::make_unsigned(pDocument->MaxCol()) )
255         return false;
256 
257     if ( nStartRow==0 && nEndRow==pDocument->MaxRow() && pOutlineTable
258         && ! pOutlineTable->TestInsertCol(nSize) )
259             return false;
260 
261     auto range = GetColumnsRange( pDocument->MaxCol() - static_cast<SCCOL>(nSize) + 1, pDocument->MaxCol() );
262     for (auto it = range.rbegin(); it != range.rend(); ++it )
263         if (! aCol[*it].TestInsertCol(nStartRow, nEndRow))
264             return false;
265 
266     return true;
267 }
268 
269 void ScTable::InsertCol(
270     const sc::ColumnSet& rRegroupCols, SCCOL nStartCol, SCROW nStartRow, SCROW nEndRow, SCSIZE nSize )
271 {
272     if (nStartRow==0 && nEndRow==pDocument->MaxRow())
273     {
274         if (mpColWidth && mpColFlags)
275         {
276             mpColWidth->InsertPreservingSize(nStartCol, nSize, STD_COL_WIDTH);
277             // The inserted columns have the same widths as the columns, which were selected for insert.
278             for (SCSIZE i=0; i < std::min(pDocument->MaxCol()-nSize-nStartCol, nSize); ++i)
279                 mpColWidth->SetValue(nStartCol + i, mpColWidth->GetValue(nStartCol+i+nSize));
280             mpColFlags->InsertPreservingSize(nStartCol, nSize, CRFlags::NONE);
281         }
282         if (pOutlineTable)
283             pOutlineTable->InsertCol( nStartCol, nSize );
284 
285         mpHiddenCols->insertSegment(nStartCol, static_cast<SCCOL>(nSize));
286         mpFilteredCols->insertSegment(nStartCol, static_cast<SCCOL>(nSize));
287 
288         if (!maColManualBreaks.empty())
289         {
290             // Copy all breaks up to nStartCol (non-inclusive).
291             ::std::set<SCCOL>::iterator itr1 = maColManualBreaks.lower_bound(nStartCol);
292             ::std::set<SCCOL> aNewBreaks(maColManualBreaks.begin(), itr1);
293 
294             // Copy all breaks from nStartCol (inclusive) to the last element,
295             // but add nSize to each value.
296             ::std::set<SCCOL>::iterator itr2 = maColManualBreaks.end();
297             for (; itr1 != itr2; ++itr1)
298                 aNewBreaks.insert(static_cast<SCCOL>(*itr1 + nSize));
299 
300             maColManualBreaks.swap(aNewBreaks);
301         }
302     }
303 
304     if ((nStartRow == 0) && (nEndRow == pDocument->MaxRow()))
305     {
306         for (SCSIZE i=0; i < nSize; i++)
307             for (SCCOL nCol = aCol.size() - 1; nCol > nStartCol; nCol--)
308                 aCol[nCol].SwapCol(aCol[nCol-1]);
309     }
310     else
311     {
312         for (SCSIZE i=0; static_cast<SCCOL>(i+nSize)+nStartCol < aCol.size(); i++)
313             aCol[aCol.size() - 1 - nSize - i].MoveTo(nStartRow, nEndRow, aCol[aCol.size() - 1 - i]);
314     }
315 
316     std::vector<SCCOL> aRegroupCols;
317     rRegroupCols.getColumns(nTab, aRegroupCols);
318     std::for_each(aRegroupCols.begin(), aRegroupCols.end(), ColumnRegroupFormulaCells(aCol, nullptr));
319 
320     if (nStartCol>0)                        // copy old attributes
321     {
322         sal_uInt16 nWhichArray[2];
323         nWhichArray[0] = ATTR_MERGE;
324         nWhichArray[1] = 0;
325 
326         sc::CopyToDocContext aCxt(*pDocument);
327         for (SCSIZE i=0; i<nSize; i++)
328         {
329             aCol[nStartCol-1].CopyToColumn(aCxt, nStartRow, nEndRow, InsertDeleteFlags::ATTRIB,
330                                                 false, aCol[nStartCol+i] );
331             aCol[nStartCol+i].RemoveFlags( nStartRow, nEndRow,
332                                                 ScMF::Hor | ScMF::Ver | ScMF::Auto );
333             aCol[nStartCol+i].ClearItems( nStartRow, nEndRow, nWhichArray );
334         }
335     }
336 
337     mpCondFormatList->InsertCol(nTab, nStartRow, nEndRow, nStartCol, nSize);
338 
339     InvalidatePageBreaks();
340 
341     // TODO: In the future we may want to check if the table has been
342     // really modified before setting the stream invalid.
343     SetStreamValid(false);
344 }
345 
346 void ScTable::DeleteCol(
347     const sc::ColumnSet& rRegroupCols, SCCOL nStartCol, SCROW nStartRow, SCROW nEndRow, SCSIZE nSize, bool* pUndoOutline )
348 {
349     if (nStartRow==0 && nEndRow==pDocument->MaxRow())
350     {
351         if (mpColWidth && mpColFlags)
352         {
353             assert( nStartCol + nSize <= o3tl::make_unsigned(pDocument->MaxCol()+1) );    // moving 0 if ==pDocument->MaxCol()+1 is correct
354             mpColWidth->RemovePreservingSize(nStartCol, nSize, STD_COL_WIDTH);
355             mpColFlags->RemovePreservingSize(nStartCol, nSize, CRFlags::NONE);
356         }
357         if (pOutlineTable)
358             if (pOutlineTable->DeleteCol( nStartCol, nSize ))
359                 if (pUndoOutline)
360                     *pUndoOutline = true;
361 
362         SCCOL nRmSize = nStartCol + static_cast<SCCOL>(nSize);
363         mpHiddenCols->removeSegment(nStartCol, nRmSize);
364         mpFilteredCols->removeSegment(nStartCol, nRmSize);
365 
366         if (!maColManualBreaks.empty())
367         {
368             // Erase all manual breaks between nStartCol and nStartCol + nSize - 1 (inclusive).
369             std::set<SCCOL>::iterator itr1 = maColManualBreaks.lower_bound(nStartCol);
370             std::set<SCCOL>::iterator itr2 = maColManualBreaks.upper_bound(static_cast<SCCOL>(nStartCol + nSize - 1));
371             maColManualBreaks.erase(itr1, itr2);
372 
373             // Copy all breaks from the 1st element up to nStartCol to the new container.
374             itr1 = maColManualBreaks.lower_bound(nStartCol);
375             ::std::set<SCCOL> aNewBreaks(maColManualBreaks.begin(), itr1);
376 
377             // Copy all breaks from nStartCol to the last element, but subtract each value by nSize.
378             itr2 = maColManualBreaks.end();
379             for (; itr1 != itr2; ++itr1)
380                 aNewBreaks.insert(static_cast<SCCOL>(*itr1 - nSize));
381 
382             maColManualBreaks.swap(aNewBreaks);
383         }
384     }
385 
386     for (SCSIZE i = 0; i < nSize; i++)
387         aCol[nStartCol + i].DeleteArea(nStartRow, nEndRow, InsertDeleteFlags::ALL, false);
388 
389     if ((nStartRow == 0) && (nEndRow == pDocument->MaxRow()))
390     {
391         for (SCSIZE i=0; i < nSize; i++)
392             for (SCCOL nCol = nStartCol; nCol < aCol.size() - 1; nCol++)
393                 aCol[nCol].SwapCol(aCol[nCol+1]);
394     }
395     else
396     {
397         for (SCSIZE i=0; static_cast<SCCOL>(i+nSize)+nStartCol < aCol.size(); i++)
398             aCol[nStartCol + nSize + i].MoveTo(nStartRow, nEndRow, aCol[nStartCol + i]);
399     }
400 
401     std::vector<SCCOL> aRegroupCols;
402     rRegroupCols.getColumns(nTab, aRegroupCols);
403     std::for_each(aRegroupCols.begin(), aRegroupCols.end(), ColumnRegroupFormulaCells(aCol, nullptr));
404 
405     InvalidatePageBreaks();
406 
407     // TODO: In the future we may want to check if the table has been
408     // really modified before setting the stream invalid.
409     SetStreamValid(false);
410 }
411 
412 void ScTable::DeleteArea(
413     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, InsertDeleteFlags nDelFlag,
414     bool bBroadcast, sc::ColumnSpanSet* pBroadcastSpans )
415 {
416     if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1;
417     if (nRow2 > pDocument->MaxRow()) nRow2 = pDocument->MaxRow();
418     if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
419     {
420         {   // scope for bulk broadcast
421             ScBulkBroadcast aBulkBroadcast( pDocument->GetBASM(), SfxHintId::ScDataChanged);
422             for (SCCOL i = nCol1; i <= nCol2; i++)
423                 aCol[i].DeleteArea(nRow1, nRow2, nDelFlag, bBroadcast, pBroadcastSpans);
424         }
425 
426             // Do not set protected cell in a protected table
427 
428         if ( IsProtected() && (nDelFlag & InsertDeleteFlags::ATTRIB) )
429         {
430             ScPatternAttr aPattern(pDocument->GetPool());
431             aPattern.GetItemSet().Put( ScProtectionAttr( false ) );
432             ApplyPatternArea( nCol1, nRow1, nCol2, nRow2, aPattern );
433         }
434 
435         if( nDelFlag & InsertDeleteFlags::ATTRIB )
436             mpCondFormatList->DeleteArea( nCol1, nRow1, nCol2, nRow2 );
437     }
438 
439     // TODO: In the future we may want to check if the table has been
440     // really modified before setting the stream invalid.
441     SetStreamValid(false);
442 }
443 
444 void ScTable::DeleteSelection( InsertDeleteFlags nDelFlag, const ScMarkData& rMark, bool bBroadcast )
445 {
446     {   // scope for bulk broadcast
447         ScBulkBroadcast aBulkBroadcast( pDocument->GetBASM(), SfxHintId::ScDataChanged);
448         for (SCCOL i=0; i < aCol.size(); i++)
449             aCol[i].DeleteSelection(nDelFlag, rMark, bBroadcast);
450     }
451 
452     ScRangeList aRangeList;
453     rMark.FillRangeListWithMarks(&aRangeList, false);
454 
455     for (size_t i = 0; i < aRangeList.size(); ++i)
456     {
457         const ScRange & rRange = aRangeList[i];
458 
459         if((nDelFlag & InsertDeleteFlags::ATTRIB) && rRange.aStart.Tab() == nTab)
460             mpCondFormatList->DeleteArea( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row() );
461     }
462 
463         // Do not set protected cell in a protected sheet
464 
465     if ( IsProtected() && (nDelFlag & InsertDeleteFlags::ATTRIB) )
466     {
467         ScDocumentPool* pPool = pDocument->GetPool();
468         SfxItemSet aSet( *pPool, svl::Items<ATTR_PATTERN_START, ATTR_PATTERN_END>{} );
469         aSet.Put( ScProtectionAttr( false ) );
470         SfxItemPoolCache aCache( pPool, &aSet );
471         ApplySelectionCache( &aCache, rMark );
472     }
473 
474     // TODO: In the future we may want to check if the table has been
475     // really modified before setting the stream invalid.
476     SetStreamValid(false);
477 }
478 
479 // pTable = Clipboard
480 void ScTable::CopyToClip(
481     sc::CopyToClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
482     ScTable* pTable )
483 {
484     if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
485         return;
486 
487     //  copy content
488     //local range names need to be copied first for formula cells
489     if (!pTable->mpRangeName && mpRangeName)
490         pTable->mpRangeName.reset( new ScRangeName(*mpRangeName) );
491 
492     nCol2 = ClampToAllocatedColumns(nCol2);
493 
494     for ( SCCOL i = nCol1; i <= nCol2; i++)
495         aCol[i].CopyToClip(rCxt, nRow1, nRow2, pTable->CreateColumnIfNotExists(i));  // notes are handled at column level
496 
497     //  copy widths/heights, and only "hidden", "filtered" and "manual" flags
498     //  also for all preceding columns/rows, to have valid positions for drawing objects
499 
500     if (mpColWidth && pTable->mpColWidth)
501         pTable->mpColWidth->CopyFrom(*mpColWidth, 0, nCol2);
502 
503     pTable->CopyColHidden(*this, 0, nCol2);
504     pTable->CopyColFiltered(*this, 0, nCol2);
505     if (pDBDataNoName)
506         pTable->SetAnonymousDBData(std::unique_ptr<ScDBData>(new ScDBData(*pDBDataNoName)));
507 
508     if (pRowFlags && pTable->pRowFlags && mpRowHeights && pTable->mpRowHeights)
509     {
510         pTable->pRowFlags->CopyFromAnded( *pRowFlags, 0, nRow2, CRFlags::ManualSize);
511         pTable->CopyRowHeight(*this, 0, nRow2, 0);
512     }
513 
514     pTable->CopyRowHidden(*this, 0, nRow2);
515     pTable->CopyRowFiltered(*this, 0, nRow2);
516 
517     // If necessary replace formulas with values
518 
519     if ( IsProtected() )
520         for (SCCOL i = nCol1; i <= nCol2; i++)
521             pTable->aCol[i].RemoveProtected(nRow1, nRow2);
522 
523     pTable->mpCondFormatList.reset(new ScConditionalFormatList(pTable->pDocument, *mpCondFormatList));
524 }
525 
526 void ScTable::CopyToClip(
527     sc::CopyToClipContext& rCxt, const ScRangeList& rRanges, ScTable* pTable )
528 {
529     for ( size_t i = 0, nListSize = rRanges.size(); i < nListSize; ++i )
530     {
531         const ScRange & r = rRanges[ i ];
532         CopyToClip( rCxt, r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), pTable);
533     }
534 }
535 
536 void ScTable::CopyStaticToDocument(
537     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const SvNumberFormatterMergeMap& rMap, ScTable* pDestTab )
538 {
539     if (nCol1 > nCol2 || nRow1 > nRow2)
540         return;
541 
542     const SCCOL nFirstUnallocated = std::clamp<SCCOL>(GetAllocatedColumnsCount(), nCol1, nCol2 + 1);
543     if (nFirstUnallocated > nCol1)
544         pDestTab->CreateColumnIfNotExists(nFirstUnallocated - 1);
545 
546     for (SCCOL i = nCol1; i < nFirstUnallocated; ++i)
547     {
548         ScColumn& rSrcCol = aCol[i];
549         ScColumn& rDestCol = pDestTab->aCol[i];
550         rSrcCol.CopyStaticToDocument(nRow1, nRow2, rMap, rDestCol);
551     }
552 
553     // Maybe copy this table's default attrs to dest not limiting to already allocated in dest?
554     const SCCOL nLastInDest = std::min<SCCOL>(pDestTab->GetAllocatedColumnsCount() - 1, nCol2);
555     for (SCCOL i = nFirstUnallocated; i <= nLastInDest; ++i)
556     {
557         ScColumn& rDestCol = pDestTab->aCol[i];
558         rDestCol.maCellTextAttrs.set_empty(nRow1, nRow2);
559         rDestCol.maCells.set_empty(nRow1, nRow2);
560         for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
561         {
562             sal_uInt32 nNumFmt = aDefaultColAttrArray.GetPattern(nRow)->GetNumberFormat(
563                 pDocument->GetNonThreadedContext().GetFormatTable());
564             SvNumberFormatterMergeMap::const_iterator itNum = rMap.find(nNumFmt);
565             if (itNum != rMap.end())
566                 nNumFmt = itNum->second;
567 
568             rDestCol.SetNumberFormat(nRow, nNumFmt);
569         }
570         rDestCol.CellStorageModified();
571     }
572 }
573 
574 void ScTable::CopyCellToDocument(SCCOL nSrcCol, SCROW nSrcRow, SCCOL nDestCol, SCROW nDestRow, ScTable& rDestTab )
575 {
576     if (!ValidColRow(nSrcCol, nSrcRow) || !ValidColRow(nDestCol, nDestRow))
577         return;
578 
579     if (nSrcCol >= GetAllocatedColumnsCount())
580     {
581         if (nDestCol < rDestTab.GetAllocatedColumnsCount())
582         {
583             ScColumn& rDestCol = rDestTab.aCol[nDestCol];
584             rDestCol.maCells.set_empty(nDestRow, nDestRow);
585             rDestCol.maCellTextAttrs.set_empty(nDestRow, nDestRow);
586             rDestCol.maCellNotes.set_empty(nDestRow, nDestRow);
587             rDestCol.CellStorageModified();
588         }
589         return;
590     }
591 
592     ScColumn& rSrcCol = aCol[nSrcCol];
593     ScColumn& rDestCol = rDestTab.CreateColumnIfNotExists(nDestCol);
594     rSrcCol.CopyCellToDocument(nSrcRow, nDestRow, rDestCol);
595 }
596 
597 namespace {
598 
599 bool CheckAndDeduplicateCondFormat(ScDocument* pDocument, ScConditionalFormat* pOldFormat, const ScConditionalFormat* pNewFormat, SCTAB nTab)
600 {
601     if (!pOldFormat)
602         return false;
603 
604     if (pOldFormat->EqualEntries(*pNewFormat, true))
605     {
606         const ScRangeList& rNewRangeList = pNewFormat->GetRange();
607         ScRangeList& rDstRangeList = pOldFormat->GetRangeList();
608         for (size_t i = 0; i < rNewRangeList.size(); ++i)
609         {
610             rDstRangeList.Join(rNewRangeList[i]);
611         }
612         pDocument->AddCondFormatData(rNewRangeList, nTab, pOldFormat->GetKey());
613         return true;
614     }
615 
616     return false;
617 }
618 
619 }
620 
621 void ScTable::CopyConditionalFormat( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
622         SCCOL nDx, SCROW nDy, const ScTable* pTable)
623 {
624     ScRange aOldRange( nCol1 - nDx, nRow1 - nDy, pTable->nTab, nCol2 - nDx, nRow2 - nDy, pTable->nTab);
625     ScRange aNewRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
626     bool bSameDoc = pDocument->GetStyleSheetPool() == pTable->pDocument->GetStyleSheetPool();
627 
628     for(const auto& rxCondFormat : *pTable->mpCondFormatList)
629     {
630         const ScRangeList& rCondFormatRange = rxCondFormat->GetRange();
631         if(!rCondFormatRange.Intersects( aOldRange ))
632             continue;
633 
634         ScRangeList aIntersectedRange = rCondFormatRange.GetIntersectedRange(aOldRange);
635         std::unique_ptr<ScConditionalFormat> pNewFormat = rxCondFormat->Clone(pDocument);
636 
637         pNewFormat->SetRange(aIntersectedRange);
638         sc::RefUpdateContext aRefCxt(*pDocument);
639         aRefCxt.meMode = URM_COPY;
640         aRefCxt.maRange = aNewRange;
641         aRefCxt.mnColDelta = nDx;
642         aRefCxt.mnRowDelta = nDy;
643         aRefCxt.mnTabDelta = nTab - pTable->nTab;
644         pNewFormat->UpdateReference(aRefCxt, true);
645 
646         if (bSameDoc && pTable->nTab == nTab && CheckAndDeduplicateCondFormat(pDocument, mpCondFormatList->GetFormat(rxCondFormat->GetKey()), pNewFormat.get(), nTab))
647         {
648             continue;
649         }
650         sal_uLong nMax = 0;
651         bool bDuplicate = false;
652         for(const auto& rxCond : *mpCondFormatList)
653         {
654             // Check if there is the same format in the destination
655             // If there is, then simply expand its range
656             if (CheckAndDeduplicateCondFormat(pDocument, rxCond.get(), pNewFormat.get(), nTab))
657             {
658                 bDuplicate = true;
659                 break;
660             }
661 
662             if (rxCond->GetKey() > nMax)
663                 nMax = rxCond->GetKey();
664         }
665         // Do not add duplicate entries
666         if (bDuplicate)
667         {
668             continue;
669         }
670 
671         pNewFormat->SetKey(nMax + 1);
672         auto pNewFormatTmp = pNewFormat.get();
673         mpCondFormatList->InsertNew(std::move(pNewFormat));
674 
675         if(!bSameDoc)
676         {
677             for(size_t i = 0, n = pNewFormatTmp->size();
678                     i < n; ++i)
679             {
680                 OUString aStyleName;
681                 const ScFormatEntry* pEntry = pNewFormatTmp->GetEntry(i);
682                 if(pEntry->GetType() == ScFormatEntry::Type::Condition ||
683                    pEntry->GetType() == ScFormatEntry::Type::ExtCondition)
684                     aStyleName = static_cast<const ScCondFormatEntry*>(pEntry)->GetStyle();
685                 else if(pEntry->GetType() == ScFormatEntry::Type::Date)
686                     aStyleName = static_cast<const ScCondDateFormatEntry*>(pEntry)->GetStyleName();
687 
688                 if(!aStyleName.isEmpty())
689                 {
690                     if(pDocument->GetStyleSheetPool()->Find(aStyleName, SfxStyleFamily::Para))
691                         continue;
692 
693                     pDocument->GetStyleSheetPool()->CopyStyleFrom(
694                             pTable->pDocument->GetStyleSheetPool(), aStyleName, SfxStyleFamily::Para );
695                 }
696             }
697         }
698 
699         pDocument->AddCondFormatData( pNewFormatTmp->GetRange(), nTab, pNewFormatTmp->GetKey() );
700     }
701 }
702 
703 bool ScTable::InitColumnBlockPosition( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol )
704 {
705     if (!ValidCol(nCol))
706         return false;
707 
708     CreateColumnIfNotExists(nCol).InitBlockPosition(rBlockPos);
709     return true;
710 }
711 
712 // pTable is source
713 
714 void ScTable::CopyFromClip(
715     sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
716     SCCOL nDx, SCROW nDy, ScTable* pTable )
717 {
718     if (nCol2 > pDocument->MaxCol())
719         nCol2 = pDocument->MaxCol();
720     if (nRow2 > pDocument->MaxRow())
721         nRow2 = pDocument->MaxRow();
722 
723     if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
724     {
725         CreateColumnIfNotExists(nCol2);
726         for ( SCCOL i = nCol1; i <= nCol2; i++)
727         {
728             pTable->CreateColumnIfNotExists(i - nDx);
729             aCol[i].CopyFromClip(rCxt, nRow1, nRow2, nDy, pTable->aCol[i - nDx]); // notes are handles at column level
730         }
731 
732         if (rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB)
733         {
734             // make sure that there are no old references to the cond formats
735             sal_uInt16 nWhichArray[2];
736             nWhichArray[0] = ATTR_CONDITIONAL;
737             nWhichArray[1] = 0;
738             for ( SCCOL i = nCol1; i <= nCol2; ++i)
739                 aCol[i].ClearItems(nRow1, nRow2, nWhichArray);
740         }
741 
742         if ((rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE)
743         {
744             if (nRow1==0 && nRow2==pDocument->MaxRow() && mpColWidth && pTable->mpColWidth)
745                 mpColWidth->CopyFrom(*pTable->mpColWidth, nCol1, nCol2, nCol1 - nDx);
746 
747             if (nCol1==0 && nCol2==pDocument->MaxCol() && mpRowHeights && pTable->mpRowHeights &&
748                                              pRowFlags && pTable->pRowFlags)
749             {
750                 CopyRowHeight(*pTable, nRow1, nRow2, -nDy);
751                 // Must copy CRFlags::ManualSize bit too, otherwise pRowHeight doesn't make sense
752                 for (SCROW j=nRow1; j<=nRow2; j++)
753                 {
754                     if ( pTable->pRowFlags->GetValue(j-nDy) & CRFlags::ManualSize )
755                         pRowFlags->OrValue( j, CRFlags::ManualSize);
756                     else
757                         pRowFlags->AndValue( j, ~CRFlags::ManualSize);
758                 }
759             }
760 
761             // Do not set protected cell in a protected sheet
762             if (IsProtected() && (rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB))
763             {
764                 ScPatternAttr aPattern(pDocument->GetPool());
765                 aPattern.GetItemSet().Put( ScProtectionAttr( false ) );
766                 ApplyPatternArea( nCol1, nRow1, nCol2, nRow2, aPattern );
767             }
768 
769             // create deep copies for conditional formatting
770             CopyConditionalFormat( nCol1, nRow1, nCol2, nRow2, nDx, nDy, pTable);
771         }
772     }
773 }
774 
775 void ScTable::MixData(
776     sc::MixDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
777     ScPasteFunc nFunction, bool bSkipEmpty, const ScTable* pSrcTab )
778 {
779     for (SCCOL i=nCol1; i<=nCol2; i++)
780         aCol[i].MixData(rCxt, nRow1, nRow2, nFunction, bSkipEmpty, pSrcTab->aCol[i]);
781 }
782 
783 // Selection form this document
784 void ScTable::MixMarked(
785     sc::MixDocContext& rCxt, const ScMarkData& rMark, ScPasteFunc nFunction,
786     bool bSkipEmpty, const ScTable* pSrcTab )
787 {
788     for (SCCOL i=0; i < aCol.size(); i++)
789         aCol[i].MixMarked(rCxt, rMark, nFunction, bSkipEmpty, pSrcTab->aCol[i]);
790 }
791 
792 namespace {
793 
794 class TransClipHandler
795 {
796     ScTable& mrClipTab;
797     SCTAB mnSrcTab;
798     SCCOL mnSrcCol;
799     size_t mnTopRow;
800     SCROW mnTransRow;
801     bool mbAsLink;
802     bool mbWasCut;
803 
804     ScAddress getDestPos(size_t nRow) const
805     {
806         return ScAddress(static_cast<SCCOL>(nRow-mnTopRow), mnTransRow, mrClipTab.GetTab());
807     }
808 
809     ScFormulaCell* createRefCell(size_t nSrcRow, const ScAddress& rDestPos) const
810     {
811         ScAddress aSrcPos(mnSrcCol, nSrcRow, mnSrcTab);
812         ScSingleRefData aRef;
813         aRef.InitAddress(aSrcPos); // Absolute reference.
814         aRef.SetFlag3D(true);
815 
816         ScTokenArray aArr(&mrClipTab.GetDoc());
817         aArr.AddSingleReference(aRef);
818         return new ScFormulaCell(&mrClipTab.GetDoc(), rDestPos, aArr);
819     }
820 
821     void setLink(size_t nRow)
822     {
823         SCCOL nTransCol = nRow - mnTopRow;
824         mrClipTab.SetFormulaCell(
825             nTransCol, mnTransRow, createRefCell(nRow, getDestPos(nRow)));
826     }
827 
828 public:
829     TransClipHandler(ScTable& rClipTab, SCTAB nSrcTab, SCCOL nSrcCol, size_t nTopRow, SCROW nTransRow, bool bAsLink, bool bWasCut) :
830         mrClipTab(rClipTab), mnSrcTab(nSrcTab), mnSrcCol(nSrcCol),
831         mnTopRow(nTopRow), mnTransRow(nTransRow), mbAsLink(bAsLink), mbWasCut(bWasCut) {}
832 
833     void operator() (size_t nRow, double fVal)
834     {
835         if (mbAsLink)
836         {
837             setLink(nRow);
838             return;
839         }
840 
841         SCCOL nTransCol = nRow - mnTopRow;
842         mrClipTab.SetValue(nTransCol, mnTransRow, fVal);
843     }
844 
845     void operator() (size_t nRow, const svl::SharedString& rStr)
846     {
847         if (mbAsLink)
848         {
849             setLink(nRow);
850             return;
851         }
852 
853         SCCOL nTransCol = nRow - mnTopRow;
854         mrClipTab.SetRawString(nTransCol, mnTransRow, rStr);
855     }
856 
857     void operator() (size_t nRow, const EditTextObject* p)
858     {
859         if (mbAsLink)
860         {
861             setLink(nRow);
862             return;
863         }
864 
865         SCCOL nTransCol = nRow - mnTopRow;
866         mrClipTab.SetEditText(nTransCol, mnTransRow, ScEditUtil::Clone(*p, mrClipTab.GetDoc()));
867     }
868 
869     void operator() (size_t nRow, const ScFormulaCell* p)
870     {
871         if (mbAsLink)
872         {
873             setLink(nRow);
874             return;
875         }
876 
877         ScFormulaCell* pNew = new ScFormulaCell(
878             *p, mrClipTab.GetDoc(), getDestPos(nRow), ScCloneFlags::StartListening);
879 
880         //  rotate reference
881         //  for Cut, the references are later adjusted through UpdateTranspose
882 
883         if (!mbWasCut)
884             pNew->TransposeReference();
885 
886         SCCOL nTransCol = nRow - mnTopRow;
887         mrClipTab.SetFormulaCell(nTransCol, mnTransRow, pNew);
888     }
889 };
890 
891 }
892 
893 void ScTable::TransposeClip( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
894                                 ScTable* pTransClip, InsertDeleteFlags nFlags, bool bAsLink )
895 {
896     bool bWasCut = pDocument->IsCutMode();
897 
898     ScDocument* pDestDoc = pTransClip->pDocument;
899 
900     for (SCCOL nCol=nCol1; nCol<=nCol2; nCol++)
901     {
902         SCROW nRow;
903         if ( bAsLink && nFlags == InsertDeleteFlags::ALL )
904         {
905             //  with InsertDeleteFlags::ALL, also create links (formulas) for empty cells
906 
907             for ( nRow=nRow1; nRow<=nRow2; nRow++ )
908             {
909                 //  create simple formula, as in ScColumn::CreateRefCell
910 
911                 ScAddress aDestPos( static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), pTransClip->nTab );
912                 ScSingleRefData aRef;
913                 aRef.InitAddress(ScAddress(nCol,nRow,nTab));
914                 aRef.SetFlag3D(true);
915                 ScTokenArray aArr(pDestDoc);
916                 aArr.AddSingleReference( aRef );
917 
918                 pTransClip->SetFormulaCell(
919                     static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1),
920                     new ScFormulaCell(pDestDoc, aDestPos, aArr));
921             }
922         }
923         else
924         {
925             TransClipHandler aFunc(*pTransClip, nTab, nCol, nRow1, static_cast<SCROW>(nCol-nCol1), bAsLink, bWasCut);
926             const sc::CellStoreType& rCells = aCol[nCol].maCells;
927             sc::ParseAllNonEmpty(rCells.begin(), rCells, nRow1, nRow2, aFunc);
928         }
929 
930         //  Attribute
931 
932         SCROW nAttrRow1 = {}; // spurious -Werror=maybe-uninitialized
933         SCROW nAttrRow2 = {}; // spurious -Werror=maybe-uninitialized
934         const ScPatternAttr* pPattern;
935         std::unique_ptr<ScAttrIterator> pAttrIter(aCol[nCol].CreateAttrIterator( nRow1, nRow2 ));
936         while ( (pPattern = pAttrIter->Next( nAttrRow1, nAttrRow2 )) != nullptr )
937         {
938             if ( !IsDefaultItem( pPattern ) )
939             {
940                 const SfxItemSet& rSet = pPattern->GetItemSet();
941                 if ( rSet.GetItemState( ATTR_MERGE, false ) == SfxItemState::DEFAULT &&
942                      rSet.GetItemState( ATTR_MERGE_FLAG, false ) == SfxItemState::DEFAULT &&
943                      rSet.GetItemState( ATTR_BORDER, false ) == SfxItemState::DEFAULT )
944                 {
945                     // no borders or merge items involved - use pattern as-is
946                     for (nRow = nAttrRow1; nRow<=nAttrRow2; nRow++)
947                         pTransClip->SetPattern( static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), *pPattern );
948                 }
949                 else
950                 {
951                     // transpose borders and merge values, remove merge flags (refreshed after pasting)
952                     ScPatternAttr aNewPattern( *pPattern );
953                     SfxItemSet& rNewSet = aNewPattern.GetItemSet();
954 
955                     const SvxBoxItem& rOldBox = rSet.Get(ATTR_BORDER);
956                     if ( rOldBox.GetTop() || rOldBox.GetBottom() || rOldBox.GetLeft() || rOldBox.GetRight() )
957                     {
958                         SvxBoxItem aNew( ATTR_BORDER );
959                         aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::TOP ), SvxBoxItemLine::LEFT );
960                         aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::LEFT ), SvxBoxItemLine::TOP );
961                         aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::BOTTOM ), SvxBoxItemLine::RIGHT );
962                         aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::RIGHT ), SvxBoxItemLine::BOTTOM );
963                         aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::TOP ), SvxBoxItemLine::LEFT );
964                         aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::LEFT ), SvxBoxItemLine::TOP );
965                         aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::BOTTOM ), SvxBoxItemLine::RIGHT );
966                         aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::RIGHT ), SvxBoxItemLine::BOTTOM );
967                         rNewSet.Put( aNew );
968                     }
969 
970                     const ScMergeAttr& rOldMerge = rSet.Get(ATTR_MERGE);
971                     if (rOldMerge.IsMerged())
972                         rNewSet.Put( ScMergeAttr( std::min(
973                                         static_cast<SCCOL>(rOldMerge.GetRowMerge()),
974                                         static_cast<SCCOL>(pDocument->MaxCol()+1 - (nAttrRow2-nRow1))),
975                                     std::min(
976                                         static_cast<SCROW>(rOldMerge.GetColMerge()),
977                                         static_cast<SCROW>(pDocument->MaxRow()+1 - (nCol-nCol1)))));
978                     const ScMergeFlagAttr& rOldFlag = rSet.Get(ATTR_MERGE_FLAG);
979                     if (rOldFlag.IsOverlapped())
980                     {
981                         ScMF nNewFlags = rOldFlag.GetValue() & ~ScMF( ScMF::Hor | ScMF::Ver );
982                         if ( nNewFlags != ScMF::NONE )
983                             rNewSet.Put( ScMergeFlagAttr( nNewFlags ) );
984                         else
985                             rNewSet.ClearItem( ATTR_MERGE_FLAG );
986                     }
987 
988                     for (nRow = nAttrRow1; nRow<=nAttrRow2; nRow++)
989                         pTransClip->SetPattern( static_cast<SCCOL>(nRow-nRow1),
990                                 static_cast<SCROW>(nCol-nCol1), aNewPattern);
991                 }
992             }
993         }
994 
995         // Cell Notes - fdo#68381 paste cell notes on Transpose
996         if ( pDocument->HasColNotes(nCol, nTab) )
997             TransposeColNotes(pTransClip, nCol1, nCol, nRow1, nRow2);
998     }
999 }
1000 
1001 void ScTable::TransposeColNotes(ScTable* pTransClip, SCCOL nCol1, SCCOL nCol, SCROW nRow1, SCROW nRow2)
1002 {
1003     sc::CellNoteStoreType::const_iterator itBlk = aCol[nCol].maCellNotes.begin(), itBlkEnd = aCol[nCol].maCellNotes.end();
1004 
1005     // Locate the top row position.
1006     size_t nOffsetInBlock = 0;
1007     size_t nBlockStart = 0, nBlockEnd = 0, nRowPos = static_cast<size_t>(nRow1);
1008     for (; itBlk != itBlkEnd; ++itBlk, nBlockStart = nBlockEnd)
1009     {
1010         nBlockEnd = nBlockStart + itBlk->size;
1011         if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
1012         {
1013             // Found.
1014             nOffsetInBlock = nRowPos - nBlockStart;
1015             break;
1016         }
1017     }
1018 
1019     if (itBlk != itBlkEnd)
1020         // Specified range found
1021     {
1022         nRowPos = static_cast<size_t>(nRow2); // End row position.
1023 
1024         // Keep processing until we hit the end row position.
1025         sc::cellnote_block::const_iterator itData, itDataEnd;
1026         for (; itBlk != itBlkEnd; ++itBlk, nBlockStart = nBlockEnd, nOffsetInBlock = 0)
1027         {
1028             nBlockEnd = nBlockStart + itBlk->size;
1029 
1030             if (itBlk->data)
1031             {
1032                 itData = sc::cellnote_block::begin(*itBlk->data);
1033                 std::advance(itData, nOffsetInBlock);
1034 
1035                 if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
1036                 {
1037                     // This block contains the end row. Only process partially.
1038                     size_t nOffsetEnd = nRowPos - nBlockStart + 1;
1039                     itDataEnd = sc::cellnote_block::begin(*itBlk->data);
1040                     std::advance(itDataEnd, nOffsetEnd);
1041                     size_t curRow = nBlockStart + nOffsetInBlock;
1042                     for (; itData != itDataEnd; ++itData, ++curRow)
1043                     {
1044                         ScAddress aDestPos( static_cast<SCCOL>(curRow-nRow1), static_cast<SCROW>(nCol-nCol1), pTransClip->nTab );
1045                         pTransClip->pDocument->ReleaseNote(aDestPos);
1046                         ScPostIt* pNote = *itData;
1047                         if (pNote)
1048                         {
1049                             std::unique_ptr<ScPostIt> pClonedNote = pNote->Clone( ScAddress(nCol, curRow, nTab), *pTransClip->pDocument, aDestPos, true );
1050                             pTransClip->pDocument->SetNote(aDestPos, std::move(pClonedNote));
1051                         }
1052                     }
1053                     break; // we reached the last valid block
1054                 }
1055                 else
1056                 {
1057                     itDataEnd = sc::cellnote_block::end(*itBlk->data);
1058                     size_t curRow = nBlockStart + nOffsetInBlock;
1059                     for (; itData != itDataEnd; ++itData, ++curRow)
1060                     {
1061                         ScAddress aDestPos( static_cast<SCCOL>(curRow-nRow1), static_cast<SCROW>(nCol-nCol1), pTransClip->nTab );
1062                         pTransClip->pDocument->ReleaseNote(aDestPos);
1063                         ScPostIt* pNote = *itData;
1064                         if (pNote)
1065                         {
1066                             std::unique_ptr<ScPostIt> pClonedNote = pNote->Clone( ScAddress(nCol, curRow, nTab), *pTransClip->pDocument, aDestPos, true );
1067                             pTransClip->pDocument->SetNote(aDestPos, std::move(pClonedNote));
1068                         }
1069                     }
1070                 }
1071             }
1072             else
1073             {
1074                 size_t curRow;
1075                 for ( curRow = nBlockStart + nOffsetInBlock; curRow <= nBlockEnd && curRow <= nRowPos; ++curRow)
1076                 {
1077                     ScAddress aDestPos( static_cast<SCCOL>(curRow-nRow1), static_cast<SCROW>(nCol-nCol1), pTransClip->nTab );
1078                     pTransClip->pDocument->ReleaseNote(aDestPos);
1079                 }
1080                 if (curRow == nRowPos)
1081                     break;
1082             }
1083         }
1084     }
1085 }
1086 
1087 ScColumn* ScTable::FetchColumn( SCCOL nCol )
1088 {
1089     if (!ValidCol(nCol))
1090         return nullptr;
1091 
1092     return &CreateColumnIfNotExists(nCol);
1093 }
1094 
1095 const ScColumn* ScTable::FetchColumn( SCCOL nCol ) const
1096 {
1097     if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
1098         return nullptr;
1099 
1100     return &aCol[nCol];
1101 }
1102 
1103 void ScTable::StartListeners( sc::StartListeningContext& rCxt, bool bAll )
1104 {
1105     std::shared_ptr<const sc::ColumnSet> pColSet = rCxt.getColumnSet();
1106     if (!pColSet)
1107     {
1108         for (SCCOL i=0; i < aCol.size(); i++)
1109             aCol[i].StartListeners(rCxt, bAll);
1110     }
1111     else if (pColSet->hasTab( nTab))
1112     {
1113         std::vector<SCCOL> aColumns;
1114         pColSet->getColumns( nTab, aColumns);
1115         for (auto i : aColumns)
1116         {
1117             if (0 <= i && i < aCol.size())
1118                 aCol[i].StartListeners(rCxt, bAll);
1119         }
1120     }
1121 }
1122 
1123 void ScTable::AttachFormulaCells(
1124     sc::StartListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
1125 {
1126     nCol2 = ClampToAllocatedColumns(nCol2);
1127     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1128         aCol[nCol].AttachFormulaCells(rCxt, nRow1, nRow2);
1129 }
1130 
1131 void ScTable::DetachFormulaCells(
1132     sc::EndListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
1133 {
1134     nCol2 = ClampToAllocatedColumns(nCol2);
1135     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1136         aCol[nCol].DetachFormulaCells(rCxt, nRow1, nRow2, nullptr);
1137 }
1138 
1139 void ScTable::SetDirtyFromClip(
1140     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans )
1141 {
1142     if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1;
1143     if (nCol2 > pDocument->MaxCol()) nCol2 = pDocument->MaxCol();
1144     if (nRow2 > pDocument->MaxRow()) nRow2 = pDocument->MaxRow();
1145     if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
1146         for (SCCOL i = nCol1; i <= nCol2; i++)
1147             aCol[i].SetDirtyFromClip(nRow1, nRow2, rBroadcastSpans);
1148 }
1149 
1150 void ScTable::StartListeningFormulaCells(
1151     sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
1152     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
1153 {
1154     if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1;
1155     if (nCol2 > pDocument->MaxCol()) nCol2 = pDocument->MaxCol();
1156     if (nRow2 > pDocument->MaxRow()) nRow2 = pDocument->MaxRow();
1157     if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
1158         for (SCCOL i = nCol1; i <= nCol2; i++)
1159             aCol[i].StartListeningFormulaCells(rStartCxt, rEndCxt, nRow1, nRow2);
1160 }
1161 
1162 void ScTable::CopyToTable(
1163     sc::CopyToDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1164     InsertDeleteFlags nFlags, bool bMarked, ScTable* pDestTab, const ScMarkData* pMarkData,
1165     bool bAsLink, bool bColRowFlags, bool bGlobalNamesToLocal, bool bCopyCaptions )
1166 {
1167     if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
1168         return;
1169 
1170     bool bIsUndoDoc = pDestTab->pDocument->IsUndo();
1171 
1172     if (bIsUndoDoc && (nFlags & InsertDeleteFlags::CONTENTS))
1173     {
1174         // Copying formulas may create sheet-local named expressions on the
1175         // destination sheet. Add existing to Undo first.
1176         pDestTab->SetRangeName( std::unique_ptr<ScRangeName>( new ScRangeName( *GetRangeName())));
1177     }
1178 
1179     if (nFlags != InsertDeleteFlags::NONE)
1180     {
1181         InsertDeleteFlags nTempFlags( nFlags &
1182                 ~InsertDeleteFlags( InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES));
1183         // tdf#102364 - in some pathological cases CopyToTable() replacing cells with new cells
1184         // can lead to repetitive splitting and rejoining of the same formula group, which can get
1185         // quadratically expensive with large groups. So do the grouping just once at the end.
1186         sc::DelayFormulaGroupingSwitch delayGrouping( *pDestTab->pDocument, true );
1187         for (SCCOL i = nCol1; i <= ClampToAllocatedColumns(nCol2); i++)
1188             aCol[i].CopyToColumn(rCxt, nRow1, nRow2, bIsUndoDoc ? nFlags : nTempFlags, bMarked,
1189                                  pDestTab->CreateColumnIfNotExists(i), pMarkData, bAsLink, bGlobalNamesToLocal);
1190     }
1191 
1192     if (!bColRowFlags)      // Column widths/Row heights/Flags
1193         return;
1194 
1195     if(bIsUndoDoc && (nFlags & InsertDeleteFlags::ATTRIB))
1196     {
1197         pDestTab->mpCondFormatList.reset(new ScConditionalFormatList(pDestTab->pDocument, *mpCondFormatList));
1198     }
1199 
1200     if (pDBDataNoName)
1201     {
1202         std::unique_ptr<ScDBData> pNewDBData(new ScDBData(*pDBDataNoName));
1203         SCCOL aCol1, aCol2;
1204         SCROW aRow1, aRow2;
1205         SCTAB aTab;
1206         pNewDBData->GetArea(aTab, aCol1, aRow1, aCol2, aRow2);
1207         pNewDBData->MoveTo(pDestTab->nTab, aCol1, aRow1, aCol2, aRow2);
1208         pDestTab->SetAnonymousDBData(std::move(pNewDBData));
1209     }
1210     //  Charts have to be adjusted when hide/show
1211     ScChartListenerCollection* pCharts = pDestTab->pDocument->GetChartListenerCollection();
1212 
1213     bool bFlagChange = false;
1214 
1215     bool bWidth  = (nRow1==0 && nRow2==pDocument->MaxRow() && mpColWidth && pDestTab->mpColWidth);
1216     bool bHeight = (nCol1==0 && nCol2==pDocument->MaxCol() && mpRowHeights && pDestTab->mpRowHeights);
1217 
1218     if (bWidth || bHeight)
1219     {
1220         if (bWidth)
1221         {
1222             auto destTabColWidthIt = pDestTab->mpColWidth->begin() + nCol1;
1223             auto thisTabColWidthIt = mpColWidth->begin() + nCol1;
1224             pDestTab->mpColWidth->CopyFrom(*mpColWidth, nCol1, nCol2);
1225             pDestTab->mpColFlags->CopyFrom(*mpColFlags, nCol1, nCol2);
1226             for (SCCOL i = nCol1; i <= nCol2; ++i)
1227             {
1228                 bool bThisHidden = ColHidden(i);
1229                 bool bHiddenChange = (pDestTab->ColHidden(i) != bThisHidden);
1230                 bool bChange = bHiddenChange || (*destTabColWidthIt != *thisTabColWidthIt);
1231                 pDestTab->SetColHidden(i, i, bThisHidden);
1232                 //TODO: collect changes?
1233                 if (bHiddenChange && pCharts)
1234                     pCharts->SetRangeDirty(ScRange( i, 0, nTab, i, pDocument->MaxRow(), nTab ));
1235 
1236                 if (bChange)
1237                     bFlagChange = true;
1238 
1239                 ++destTabColWidthIt;
1240                 ++thisTabColWidthIt;
1241             }
1242             pDestTab->SetColManualBreaks( maColManualBreaks);
1243         }
1244 
1245         if (bHeight)
1246         {
1247             bool bChange = pDestTab->GetRowHeight(nRow1, nRow2) != GetRowHeight(nRow1, nRow2);
1248 
1249             if (bChange)
1250                 bFlagChange = true;
1251 
1252             pDestTab->CopyRowHeight(*this, nRow1, nRow2, 0);
1253             pDestTab->pRowFlags->CopyFrom(*pRowFlags, nRow1, nRow2);
1254 
1255             // Hidden flags.
1256             for (SCROW i = nRow1; i <= nRow2; ++i)
1257             {
1258                 SCROW nLastRow;
1259                 bool bHidden = RowHidden(i, nullptr, &nLastRow);
1260                 if (nLastRow >= nRow2)
1261                     // the last row shouldn't exceed the upper bound the caller specified.
1262                     nLastRow = nRow2;
1263 
1264                 bool bHiddenChanged = pDestTab->SetRowHidden(i, nLastRow, bHidden);
1265                 if (bHiddenChanged && pCharts)
1266                     // Hidden flags differ.
1267                     pCharts->SetRangeDirty(ScRange(0, i, nTab, pDocument->MaxCol(), nLastRow, nTab));
1268 
1269                 if (bHiddenChanged)
1270                     bFlagChange = true;
1271 
1272                 // Jump to the last row of the identical flag segment.
1273                 i = nLastRow;
1274             }
1275 
1276             // Filtered flags.
1277             for (SCROW i = nRow1; i <= nRow2; ++i)
1278             {
1279                 SCROW nLastRow;
1280                 bool bFiltered = RowFiltered(i, nullptr, &nLastRow);
1281                 if (nLastRow >= nRow2)
1282                     // the last row shouldn't exceed the upper bound the caller specified.
1283                     nLastRow = nRow2;
1284                 pDestTab->SetRowFiltered(i, nLastRow, bFiltered);
1285                 i = nLastRow;
1286             }
1287             pDestTab->SetRowManualBreaks( maRowManualBreaks);
1288         }
1289     }
1290 
1291     if (bFlagChange)
1292         pDestTab->InvalidatePageBreaks();
1293 
1294     if(nFlags & InsertDeleteFlags::ATTRIB)
1295     {
1296         pDestTab->mpCondFormatList->DeleteArea(nCol1, nRow1, nCol2, nRow2);
1297         pDestTab->CopyConditionalFormat(nCol1, nRow1, nCol2, nRow2, 0, 0, this);
1298     }
1299 
1300     if(nFlags & InsertDeleteFlags::OUTLINE) // also only when bColRowFlags
1301         pDestTab->SetOutlineTable( pOutlineTable.get() );
1302 
1303     if (!bIsUndoDoc && bCopyCaptions && (nFlags & (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES)))
1304     {
1305         bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
1306         CopyCaptionsToTable( nCol1, nRow1, nCol2, nRow2, pDestTab, bCloneCaption);
1307     }
1308 }
1309 
1310 void ScTable::CopyCaptionsToTable( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScTable* pDestTab,
1311         bool bCloneCaption )
1312 {
1313     if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
1314         return;
1315 
1316     nCol2 = ClampToAllocatedColumns(nCol2);
1317     for (SCCOL i = nCol1; i <= nCol2; i++)
1318     {
1319         aCol[i].CopyCellNotesToDocument(nRow1, nRow2, pDestTab->CreateColumnIfNotExists(i), bCloneCaption);
1320         pDestTab->aCol[i].UpdateNoteCaptions(nRow1, nRow2);
1321     }
1322 }
1323 
1324 void ScTable::UndoToTable(
1325     sc::CopyToDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1326     InsertDeleteFlags nFlags, bool bMarked, ScTable* pDestTab )
1327 {
1328     if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
1329     {
1330         bool bWidth  = (nRow1==0 && nRow2==pDocument->MaxRow() && mpColWidth && pDestTab->mpColWidth);
1331         bool bHeight = (nCol1==0 && nCol2==pDocument->MaxCol() && mpRowHeights && pDestTab->mpRowHeights);
1332 
1333         if ((nFlags & InsertDeleteFlags::CONTENTS) && mpRangeName)
1334         {
1335             // Undo sheet-local named expressions created during copying
1336             // formulas. If mpRangeName is not set then the Undo wasn't even
1337             // set to an empty ScRangeName map so don't "undo" that.
1338             pDestTab->SetRangeName( std::unique_ptr<ScRangeName>( new ScRangeName( *GetRangeName())));
1339             if (!pDestTab->pDocument->IsClipOrUndo())
1340             {
1341                 ScDocShell* pDocSh = static_cast<ScDocShell*>(pDestTab->pDocument->GetDocumentShell());
1342                 if (pDocSh)
1343                     pDocSh->SetAreasChangedNeedBroadcast();
1344             }
1345 
1346         }
1347 
1348         for ( SCCOL i = 0; i < aCol.size(); i++)
1349         {
1350             if ( i >= nCol1 && i <= nCol2 )
1351                 aCol[i].UndoToColumn(rCxt, nRow1, nRow2, nFlags, bMarked, pDestTab->aCol[i]);
1352             else
1353                 aCol[i].CopyToColumn(rCxt, 0, pDocument->MaxRow(), InsertDeleteFlags::FORMULA, false, pDestTab->aCol[i]);
1354         }
1355 
1356         if (nFlags & InsertDeleteFlags::ATTRIB)
1357             pDestTab->mpCondFormatList.reset(new ScConditionalFormatList(pDestTab->pDocument, *mpCondFormatList));
1358 
1359         if (bWidth||bHeight)
1360         {
1361             if (bWidth)
1362             {
1363                 pDestTab->mpColWidth->CopyFrom(*mpColWidth, nCol1, nCol2);
1364                 pDestTab->SetColManualBreaks( maColManualBreaks);
1365             }
1366             if (bHeight)
1367             {
1368                 pDestTab->CopyRowHeight(*this, nRow1, nRow2, 0);
1369                 pDestTab->SetRowManualBreaks( maRowManualBreaks);
1370             }
1371         }
1372     }
1373 }
1374 
1375 void ScTable::CopyUpdated( const ScTable* pPosTab, ScTable* pDestTab ) const
1376 {
1377     pPosTab->CreateColumnIfNotExists(aCol.size()-1);
1378     pDestTab->CreateColumnIfNotExists(aCol.size()-1);
1379     for (SCCOL i=0; i < aCol.size(); i++)
1380         aCol[i].CopyUpdated( pPosTab->aCol[i], pDestTab->aCol[i] );
1381 }
1382 
1383 void ScTable::InvalidateTableArea()
1384 {
1385     bTableAreaValid = false;
1386 }
1387 
1388 void ScTable::InvalidatePageBreaks()
1389 {
1390     mbPageBreaksValid = false;
1391 }
1392 
1393 void ScTable::CopyScenarioTo( const ScTable* pDestTab ) const
1394 {
1395     OSL_ENSURE( bScenario, "bScenario == FALSE" );
1396 
1397     for (SCCOL i=0; i < aCol.size(); i++)
1398         aCol[i].CopyScenarioTo( pDestTab->CreateColumnIfNotExists(i) );
1399 }
1400 
1401 void ScTable::CopyScenarioFrom( const ScTable* pSrcTab )
1402 {
1403     OSL_ENSURE( bScenario, "bScenario == FALSE" );
1404 
1405     SCCOL nEndCol = pSrcTab->aCol.size();
1406     CreateColumnIfNotExists(nEndCol);
1407     for (SCCOL i=0; i < nEndCol; i++)
1408         aCol[i].CopyScenarioFrom( pSrcTab->aCol[i] );
1409 }
1410 
1411 void ScTable::MarkScenarioIn( ScMarkData& rDestMark, ScScenarioFlags nNeededBits ) const
1412 {
1413     OSL_ENSURE( bScenario, "bScenario == FALSE" );
1414 
1415     if ( ( nScenarioFlags & nNeededBits ) != nNeededBits )  // Are all Bits set?
1416         return;
1417 
1418     for (SCCOL i=0; i < aCol.size(); i++)
1419         aCol[i].MarkScenarioIn( rDestMark );
1420 }
1421 
1422 bool ScTable::HasScenarioRange( const ScRange& rRange ) const
1423 {
1424     OSL_ENSURE( bScenario, "bScenario == FALSE" );
1425 
1426     ScRange aTabRange = rRange;
1427     aTabRange.aStart.SetTab( nTab );
1428     aTabRange.aEnd.SetTab( nTab );
1429 
1430     const ScRangeList* pList = GetScenarioRanges();
1431 
1432     if (pList)
1433     {
1434         for ( size_t j = 0, n = pList->size(); j < n; j++ )
1435         {
1436             const ScRange & rR = (*pList)[j];
1437             if ( rR.Intersects( aTabRange ) )
1438                 return true;
1439         }
1440     }
1441 
1442     return false;
1443 }
1444 
1445 void ScTable::InvalidateScenarioRanges()
1446 {
1447     pScenarioRanges.reset();
1448 }
1449 
1450 const ScRangeList* ScTable::GetScenarioRanges() const
1451 {
1452     OSL_ENSURE( bScenario, "bScenario == FALSE" );
1453 
1454     if (!pScenarioRanges)
1455     {
1456         const_cast<ScTable*>(this)->pScenarioRanges.reset(new ScRangeList);
1457         ScMarkData aMark(pDocument->GetSheetLimits());
1458         MarkScenarioIn( aMark, ScScenarioFlags::NONE );     // always
1459         aMark.FillRangeListWithMarks( pScenarioRanges.get(), false );
1460     }
1461     return pScenarioRanges.get();
1462 }
1463 
1464 bool ScTable::TestCopyScenarioTo( const ScTable* pDestTab ) const
1465 {
1466     OSL_ENSURE( bScenario, "bScenario == FALSE" );
1467 
1468     if (!pDestTab->IsProtected())
1469         return true;
1470 
1471     bool bOk = true;
1472     for (SCCOL i=0; i < aCol.size() && bOk; i++)
1473         bOk = aCol[i].TestCopyScenarioTo( pDestTab->aCol[i] );
1474     return bOk;
1475 }
1476 
1477 bool ScTable::SetString( SCCOL nCol, SCROW nRow, SCTAB nTabP, const OUString& rString,
1478                          const ScSetStringParam * pParam )
1479 {
1480     if (!ValidColRow(nCol,nRow))
1481     {
1482         return false;
1483     }
1484 
1485     return CreateColumnIfNotExists(nCol).SetString(
1486         nRow, nTabP, rString, pDocument->GetAddressConvention(), pParam);
1487 }
1488 
1489 bool ScTable::SetEditText( SCCOL nCol, SCROW nRow, std::unique_ptr<EditTextObject> pEditText )
1490 {
1491     if (!ValidColRow(nCol, nRow))
1492     {
1493         return false;
1494     }
1495 
1496     CreateColumnIfNotExists(nCol).SetEditText(nRow, std::move(pEditText));
1497     return true;
1498 }
1499 
1500 void ScTable::SetEditText( SCCOL nCol, SCROW nRow, const EditTextObject& rEditText, const SfxItemPool* pEditPool )
1501 {
1502     if (!ValidColRow(nCol, nRow))
1503         return;
1504 
1505     CreateColumnIfNotExists(nCol).SetEditText(nRow, rEditText, pEditPool);
1506 }
1507 
1508 SCROW ScTable::GetFirstEditTextRow( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const
1509 {
1510     if (!ValidCol(nCol1) || !ValidCol(nCol2) || nCol2 < nCol1)
1511         return -1;
1512 
1513     if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow2 < nRow1)
1514         return -1;
1515 
1516     nCol2 = ClampToAllocatedColumns(nCol2);
1517     SCROW nFirst = pDocument->MaxRow()+1;
1518     for (SCCOL i = nCol1; i <= nCol2; ++i)
1519     {
1520         const ScColumn& rCol = aCol[i];
1521         SCROW nThisFirst = -1;
1522         if (const_cast<ScColumn&>(rCol).HasEditCells(nRow1, nRow2, nThisFirst))
1523         {
1524             if (nThisFirst == nRow1)
1525                 return nRow1;
1526 
1527             if (nThisFirst < nFirst)
1528                 nFirst = nThisFirst;
1529         }
1530     }
1531 
1532     return nFirst == (pDocument->MaxRow()+1) ? -1 : nFirst;
1533 }
1534 
1535 void ScTable::SetEmptyCell( SCCOL nCol, SCROW nRow )
1536 {
1537     if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
1538         return;
1539 
1540     aCol[nCol].Delete(nRow);
1541 }
1542 
1543 void ScTable::SetFormula(
1544     SCCOL nCol, SCROW nRow, const ScTokenArray& rArray, formula::FormulaGrammar::Grammar eGram )
1545 {
1546     if (!ValidColRow(nCol, nRow))
1547         return;
1548 
1549     CreateColumnIfNotExists(nCol).SetFormula(nRow, rArray, eGram);
1550 }
1551 
1552 void ScTable::SetFormula(
1553     SCCOL nCol, SCROW nRow, const OUString& rFormula, formula::FormulaGrammar::Grammar eGram )
1554 {
1555     if (!ValidColRow(nCol, nRow))
1556         return;
1557 
1558     CreateColumnIfNotExists(nCol).SetFormula(nRow, rFormula, eGram);
1559 }
1560 
1561 ScFormulaCell* ScTable::SetFormulaCell( SCCOL nCol, SCROW nRow, ScFormulaCell* pCell )
1562 {
1563     if (!ValidColRow(nCol, nRow))
1564     {
1565         delete pCell;
1566         return nullptr;
1567     }
1568 
1569     return CreateColumnIfNotExists(nCol).SetFormulaCell(nRow, pCell, sc::ConvertToGroupListening);
1570 }
1571 
1572 bool ScTable::SetFormulaCells( SCCOL nCol, SCROW nRow, std::vector<ScFormulaCell*>& rCells )
1573 {
1574     if (!ValidCol(nCol))
1575         return false;
1576 
1577     return CreateColumnIfNotExists(nCol).SetFormulaCells(nRow, rCells);
1578 }
1579 
1580 svl::SharedString ScTable::GetSharedString( SCCOL nCol, SCROW nRow ) const
1581 {
1582     if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
1583         return svl::SharedString();
1584 
1585     return aCol[nCol].GetSharedString(nRow);
1586 }
1587 
1588 void ScTable::SetValue( SCCOL nCol, SCROW nRow, const double& rVal )
1589 {
1590     if (ValidColRow(nCol, nRow))
1591         CreateColumnIfNotExists(nCol).SetValue(nRow, rVal);
1592 }
1593 
1594 void ScTable::SetRawString( SCCOL nCol, SCROW nRow, const svl::SharedString& rStr )
1595 {
1596     if (ValidColRow(nCol, nRow))
1597         CreateColumnIfNotExists(nCol).SetRawString(nRow, rStr);
1598 }
1599 
1600 void ScTable::GetString( SCCOL nCol, SCROW nRow, OUString& rString, const ScInterpreterContext* pContext ) const
1601 {
1602     if (ValidColRow(nCol,nRow) && nCol < GetAllocatedColumnsCount())
1603         aCol[nCol].GetString( nRow, rString, pContext );
1604     else
1605         rString.clear();
1606 }
1607 
1608 double* ScTable::GetValueCell( SCCOL nCol, SCROW nRow )
1609 {
1610     if (!ValidColRow(nCol, nRow))
1611         return nullptr;
1612 
1613     return CreateColumnIfNotExists(nCol).GetValueCell(nRow);
1614 }
1615 
1616 void ScTable::GetInputString( SCCOL nCol, SCROW nRow, OUString& rString ) const
1617 {
1618     if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
1619         aCol[nCol].GetInputString( nRow, rString );
1620     else
1621         rString.clear();
1622 }
1623 
1624 double ScTable::GetValue( SCCOL nCol, SCROW nRow ) const
1625 {
1626     if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
1627         return aCol[nCol].GetValue( nRow );
1628     return 0.0;
1629 }
1630 
1631 const EditTextObject* ScTable::GetEditText( SCCOL nCol, SCROW nRow ) const
1632 {
1633     if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
1634         return nullptr;
1635 
1636     return aCol[nCol].GetEditText(nRow);
1637 }
1638 
1639 void ScTable::RemoveEditTextCharAttribs( SCCOL nCol, SCROW nRow, const ScPatternAttr& rAttr )
1640 {
1641     if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
1642         return;
1643 
1644     return aCol[nCol].RemoveEditTextCharAttribs(nRow, rAttr);
1645 }
1646 
1647 void ScTable::GetFormula( SCCOL nCol, SCROW nRow, OUString& rFormula ) const
1648 {
1649     if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
1650         aCol[nCol].GetFormula( nRow, rFormula );
1651     else
1652         rFormula.clear();
1653 }
1654 
1655 const ScFormulaCell* ScTable::GetFormulaCell( SCCOL nCol, SCROW nRow ) const
1656 {
1657     if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
1658         return nullptr;
1659 
1660     return aCol[nCol].GetFormulaCell(nRow);
1661 }
1662 
1663 ScFormulaCell* ScTable::GetFormulaCell( SCCOL nCol, SCROW nRow )
1664 {
1665     if (!ValidColRow(nCol, nRow))
1666         return nullptr;
1667     return CreateColumnIfNotExists(nCol).GetFormulaCell(nRow);
1668 }
1669 
1670 std::unique_ptr<ScPostIt> ScTable::ReleaseNote( SCCOL nCol, SCROW nRow )
1671 {
1672     if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
1673         return nullptr;
1674 
1675     return aCol[nCol].ReleaseNote(nRow);
1676 }
1677 
1678 size_t ScTable::GetNoteCount( SCCOL nCol ) const
1679 {
1680     if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
1681         return 0;
1682 
1683     return aCol[nCol].GetNoteCount();
1684 }
1685 
1686 SCROW ScTable::GetNotePosition( SCCOL nCol, size_t nIndex ) const
1687 {
1688     if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
1689         return -1;
1690 
1691     return aCol[nCol].GetNotePosition(nIndex);
1692 }
1693 
1694 void ScTable::CreateAllNoteCaptions()
1695 {
1696     for (SCCOL i = 0; i < aCol.size(); ++i)
1697         aCol[i].CreateAllNoteCaptions();
1698 }
1699 
1700 void ScTable::ForgetNoteCaptions( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bPreserveData )
1701 {
1702     if (!ValidCol(nCol1) || !ValidCol(nCol2))
1703         return;
1704     if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1;
1705     for (SCCOL i = nCol1; i <= nCol2; ++i)
1706         aCol[i].ForgetNoteCaptions(nRow1, nRow2, bPreserveData);
1707 }
1708 
1709 void ScTable::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const
1710 {
1711     for (SCCOL nCol = 0; nCol < aCol.size(); ++nCol)
1712         aCol[nCol].GetAllNoteEntries(rNotes);
1713 }
1714 
1715 void ScTable::GetNotesInRange( const ScRange& rRange, std::vector<sc::NoteEntry>& rNotes ) const
1716 {
1717     SCROW nStartRow = rRange.aStart.Row();
1718     SCROW nEndRow = rRange.aEnd.Row();
1719     SCCOL nEndCol = ClampToAllocatedColumns(rRange.aEnd.Col());
1720     for (SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; ++nCol)
1721     {
1722         aCol[nCol].GetNotesInRange(nStartRow, nEndRow, rNotes);
1723     }
1724 }
1725 
1726 CommentCaptionState ScTable::GetAllNoteCaptionsState(const ScRange& rRange, std::vector<sc::NoteEntry>& rNotes )
1727 {
1728     SCROW nStartRow = rRange.aStart.Row();
1729     SCROW nEndRow = rRange.aEnd.Row();
1730     bool bIsFirstNoteShownState = true; // because of error: -Werror=maybe-uninitialized
1731     bool bFirstControl = true;
1732 
1733     ScTable* pTab = pDocument->FetchTable(nTab);
1734     assert(pTab);
1735     const SCCOL nEndCol = pTab->ClampToAllocatedColumns(rRange.aEnd.Col());
1736     for (SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; ++nCol)
1737     {
1738         if (bFirstControl && pDocument->HasColNotes(nCol, nTab)) // detect status of first note caption
1739         {
1740             aCol[nCol].GetNotesInRange(nStartRow, nEndRow, rNotes);
1741             bIsFirstNoteShownState = rNotes.begin()->mpNote->IsCaptionShown();
1742             bFirstControl = false;
1743         }
1744 
1745         if (pDocument->HasColNotes(nCol, nTab))
1746         {
1747             aCol[nCol].GetNotesInRange(nStartRow, nEndRow, rNotes);
1748 
1749             bool bIsMixedState = std::any_of(rNotes.begin(), rNotes.end(), [bIsFirstNoteShownState](const sc::NoteEntry& rNote) {
1750                 // compare the first note caption with others
1751                 return bIsFirstNoteShownState != rNote.mpNote->IsCaptionShown(); });
1752             if (bIsMixedState)
1753                 return CommentCaptionState::MIXED;
1754         }
1755     }
1756     return bIsFirstNoteShownState ? CommentCaptionState::ALLSHOWN : CommentCaptionState::ALLHIDDEN;
1757 }
1758 
1759 void ScTable::GetUnprotectedCells( ScRangeList& rRangeList ) const
1760 {
1761     for (auto const & pCol : aCol)
1762         pCol->GetUnprotectedCells(0, pDocument->MaxRow(), rRangeList);
1763 }
1764 
1765 bool ScTable::ContainsNotesInRange( const ScRange& rRange ) const
1766 {
1767     SCROW nStartRow = rRange.aStart.Row();
1768     SCROW nEndRow = rRange.aEnd.Row();
1769     SCCOL nEndCol = ClampToAllocatedColumns(rRange.aEnd.Col());
1770     for (SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; ++nCol)
1771     {
1772         bool bContainsNote = !aCol[nCol].IsNotesEmptyBlock(nStartRow, nEndRow);
1773         if(bContainsNote)
1774             return true;
1775     }
1776 
1777     return false;
1778 }
1779 
1780 CellType ScTable::GetCellType( SCCOL nCol, SCROW nRow ) const
1781 {
1782     if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
1783         return aCol[nCol].GetCellType( nRow );
1784     return CELLTYPE_NONE;
1785 }
1786 
1787 ScRefCellValue ScTable::GetCellValue( SCCOL nCol, SCROW nRow ) const
1788 {
1789     if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
1790         return ScRefCellValue();
1791 
1792     return aCol[nCol].GetCellValue(nRow);
1793 }
1794 
1795 void ScTable::GetFirstDataPos(SCCOL& rCol, SCROW& rRow) const
1796 {
1797     rCol = 0;
1798     rRow = pDocument->MaxRow()+1;
1799     while (rCol < (aCol.size() - 1) && aCol[rCol].IsEmptyData() )
1800         ++rCol;
1801     SCCOL nCol = rCol;
1802     while (nCol < aCol.size() && rRow > 0)
1803     {
1804         if (!aCol[nCol].IsEmptyData())
1805             rRow = ::std::min( rRow, aCol[nCol].GetFirstDataPos());
1806         ++nCol;
1807     }
1808 }
1809 
1810 void ScTable::GetLastDataPos(SCCOL& rCol, SCROW& rRow) const
1811 {
1812     rCol = aCol.size() - 1;
1813     rRow = 0;
1814     while (aCol[rCol].IsEmptyData() && (rCol > 0))
1815         rCol--;
1816     SCCOL nCol = rCol;
1817     while (nCol >= 0 && rRow < pDocument->MaxRow())
1818         rRow = ::std::max( rRow, aCol[nCol--].GetLastDataPos());
1819 }
1820 
1821 bool ScTable::HasData( SCCOL nCol, SCROW nRow ) const
1822 {
1823     if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
1824         return aCol[nCol].HasDataAt( nRow );
1825     else
1826         return false;
1827 }
1828 
1829 bool ScTable::HasStringData( SCCOL nCol, SCROW nRow ) const
1830 {
1831     if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
1832         return aCol[nCol].HasStringData( nRow );
1833     else
1834         return false;
1835 }
1836 
1837 bool ScTable::HasValueData( SCCOL nCol, SCROW nRow ) const
1838 {
1839     if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
1840         return aCol[nCol].HasValueData( nRow );
1841     else
1842         return false;
1843 }
1844 
1845 bool ScTable::HasStringCells( SCCOL nStartCol, SCROW nStartRow,
1846                                 SCCOL nEndCol, SCROW nEndRow ) const
1847 {
1848     if (ValidCol(nEndCol))
1849     {
1850         nEndCol = ClampToAllocatedColumns(nEndCol);
1851         for (SCCOL nCol = nStartCol; nCol <= nEndCol; nCol++)
1852             if (aCol[nCol].HasStringCells(nStartRow, nEndRow))
1853                 return true;
1854     }
1855 
1856     return false;
1857 }
1858 
1859 void ScTable::SetDirtyVar()
1860 {
1861     for (SCCOL i=0; i < aCol.size(); i++)
1862         aCol[i].SetDirtyVar();
1863 }
1864 
1865 void ScTable::CheckVectorizationState()
1866 {
1867     sc::AutoCalcSwitch aACSwitch(*pDocument, false);
1868 
1869     for (SCCOL i = 0; i < aCol.size(); i++)
1870         aCol[i].CheckVectorizationState();
1871 }
1872 
1873 void ScTable::SetAllFormulasDirty( const sc::SetFormulaDirtyContext& rCxt )
1874 {
1875     sc::AutoCalcSwitch aACSwitch(*pDocument, false);
1876 
1877     for (SCCOL i=0; i < aCol.size(); i++)
1878         aCol[i].SetAllFormulasDirty(rCxt);
1879 }
1880 
1881 void ScTable::SetDirty( const ScRange& rRange, ScColumn::BroadcastMode eMode )
1882 {
1883     bool bOldAutoCalc = pDocument->GetAutoCalc();
1884     pDocument->SetAutoCalc( false );    // avoid multiple recalculations
1885     SCCOL nCol2 = rRange.aEnd.Col();
1886     nCol2 = ClampToAllocatedColumns(nCol2);
1887     for (SCCOL i=rRange.aStart.Col(); i<=nCol2; i++)
1888         aCol[i].SetDirty(rRange.aStart.Row(), rRange.aEnd.Row(), eMode);
1889     pDocument->SetAutoCalc( bOldAutoCalc );
1890 }
1891 
1892 void ScTable::SetTableOpDirty( const ScRange& rRange )
1893 {
1894     bool bOldAutoCalc = pDocument->GetAutoCalc();
1895     pDocument->SetAutoCalc( false );    // no multiple recalculation
1896     const SCCOL nCol2 = ClampToAllocatedColumns(rRange.aEnd.Col());
1897     for (SCCOL i=rRange.aStart.Col(); i<=nCol2; i++)
1898         aCol[i].SetTableOpDirty( rRange );
1899     pDocument->SetAutoCalc( bOldAutoCalc );
1900 }
1901 
1902 void ScTable::SetDirtyAfterLoad()
1903 {
1904     bool bOldAutoCalc = pDocument->GetAutoCalc();
1905     pDocument->SetAutoCalc( false );    // avoid multiple recalculations
1906     for (SCCOL i=0; i < aCol.size(); i++)
1907         aCol[i].SetDirtyAfterLoad();
1908     pDocument->SetAutoCalc( bOldAutoCalc );
1909 }
1910 
1911 void ScTable::SetDirtyIfPostponed()
1912 {
1913     bool bOldAutoCalc = pDocument->GetAutoCalc();
1914     pDocument->SetAutoCalc( false );    // avoid multiple recalculations
1915     for (SCCOL i=0; i < aCol.size(); i++)
1916         aCol[i].SetDirtyIfPostponed();
1917     pDocument->SetAutoCalc( bOldAutoCalc );
1918 }
1919 
1920 void ScTable::BroadcastRecalcOnRefMove()
1921 {
1922     sc::AutoCalcSwitch aSwitch(*pDocument, false);
1923     for (SCCOL i = 0; i < aCol.size(); ++i)
1924         aCol[i].BroadcastRecalcOnRefMove();
1925 }
1926 
1927 bool ScTable::BroadcastBroadcasters( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScHint& rHint )
1928 {
1929     bool bBroadcasted = false;
1930     sc::AutoCalcSwitch aSwitch(*pDocument, false);
1931     rHint.GetAddress().SetTab(nTab);
1932     nCol2 = ClampToAllocatedColumns(nCol2);
1933     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1934         bBroadcasted |= aCol[nCol].BroadcastBroadcasters( nRow1, nRow2, rHint);
1935     return bBroadcasted;
1936 }
1937 
1938 void ScTable::SetLoadingMedium(bool bLoading)
1939 {
1940     mpRowHeights->enableTreeSearch(!bLoading);
1941 }
1942 
1943 void ScTable::CalcAll()
1944 {
1945     for (SCCOL i=0; i < aCol.size(); i++)
1946         aCol[i].CalcAll();
1947 
1948     mpCondFormatList->CalcAll();
1949 }
1950 
1951 void ScTable::CompileAll( sc::CompileFormulaContext& rCxt )
1952 {
1953     for (SCCOL i = 0; i < aCol.size(); ++i)
1954         aCol[i].CompileAll(rCxt);
1955 
1956     if(mpCondFormatList)
1957         mpCondFormatList->CompileAll();
1958 }
1959 
1960 void ScTable::CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rProgress )
1961 {
1962     if (mpRangeName)
1963         mpRangeName->CompileUnresolvedXML(rCxt);
1964 
1965     for (SCCOL i=0; i < aCol.size(); i++)
1966     {
1967         aCol[i].CompileXML(rCxt, rProgress);
1968     }
1969 
1970     if(mpCondFormatList)
1971         mpCondFormatList->CompileXML();
1972 }
1973 
1974 bool ScTable::CompileErrorCells( sc::CompileFormulaContext& rCxt, FormulaError nErrCode )
1975 {
1976     bool bCompiled = false;
1977     for (SCCOL i = 0; i < aCol.size(); ++i)
1978     {
1979         if (aCol[i].CompileErrorCells(rCxt, nErrCode))
1980             bCompiled = true;
1981     }
1982 
1983     return bCompiled;
1984 }
1985 
1986 void ScTable::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening )
1987 {
1988     for (SCCOL i = 0; i < aCol.size(); ++i)
1989         aCol[i].CalcAfterLoad(rCxt, bStartListening);
1990 }
1991 
1992 void ScTable::ResetChanged( const ScRange& rRange )
1993 {
1994     SCCOL nStartCol = rRange.aStart.Col();
1995     SCROW nStartRow = rRange.aStart.Row();
1996     SCCOL nEndCol = ClampToAllocatedColumns(rRange.aEnd.Col());
1997     SCROW nEndRow = rRange.aEnd.Row();
1998 
1999     for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
2000         aCol[nCol].ResetChanged(nStartRow, nEndRow);
2001 }
2002 
2003 //  Attribute
2004 
2005 const SfxPoolItem* ScTable::GetAttr( SCCOL nCol, SCROW nRow, sal_uInt16 nWhich ) const
2006 {
2007     if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
2008         return &aCol[nCol].GetAttr( nRow, nWhich );
2009     else
2010         return nullptr;
2011 }
2012 
2013 sal_uInt32 ScTable::GetNumberFormat( const ScInterpreterContext& rContext, const ScAddress& rPos ) const
2014 {
2015     if (ValidColRow(rPos.Col(), rPos.Row()))
2016     {
2017         if (rPos.Col() < GetAllocatedColumnsCount())
2018             return aCol[rPos.Col()].GetNumberFormat(rContext, rPos.Row());
2019         return aDefaultColAttrArray.GetPattern(rPos.Row())
2020             ->GetNumberFormat(rContext.GetFormatTable());
2021     }
2022     return 0;
2023 }
2024 
2025 sal_uInt32 ScTable::GetNumberFormat( SCCOL nCol, SCROW nRow ) const
2026 {
2027     if (ValidColRow(nCol,nRow))
2028         return CreateColumnIfNotExists(nCol).GetNumberFormat(pDocument->GetNonThreadedContext(), nRow);
2029     else
2030         return 0;
2031 }
2032 
2033 sal_uInt32 ScTable::GetNumberFormat( SCCOL nCol, SCROW nStartRow, SCROW nEndRow ) const
2034 {
2035     if (!ValidCol(nCol) || !ValidRow(nStartRow) || !ValidRow(nEndRow))
2036         return 0;
2037 
2038     return CreateColumnIfNotExists(nCol).GetNumberFormat(nStartRow, nEndRow);
2039 }
2040 
2041 void ScTable::SetNumberFormat( SCCOL nCol, SCROW nRow, sal_uInt32 nNumberFormat )
2042 {
2043     if (!ValidColRow(nCol, nRow))
2044         return;
2045 
2046     CreateColumnIfNotExists(nCol).SetNumberFormat(nRow, nNumberFormat);
2047 }
2048 
2049 const ScPatternAttr* ScTable::GetPattern( SCCOL nCol, SCROW nRow ) const
2050 {
2051     if (ValidColRow(nCol,nRow))
2052         return CreateColumnIfNotExists(nCol).GetPattern( nRow );
2053     else
2054     {
2055         OSL_FAIL("wrong column or row");
2056         return pDocument->GetDefPattern();      // for safety
2057     }
2058 }
2059 
2060 const ScPatternAttr* ScTable::GetMostUsedPattern( SCCOL nCol, SCROW nStartRow, SCROW nEndRow ) const
2061 {
2062     if ( ValidColRow( nCol, nStartRow ) && ValidRow( nEndRow ) && (nStartRow <= nEndRow)
2063         && nCol < GetAllocatedColumnsCount())
2064         return aCol[nCol].GetMostUsedPattern( nStartRow, nEndRow );
2065     else
2066         return nullptr;
2067 }
2068 
2069 bool ScTable::HasAttrib( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, HasAttrFlags nMask ) const
2070 {
2071     if ( nCol1 >= aCol.size() )
2072          return false;
2073     if ( nCol2 >= aCol.size() )
2074          nCol2 = aCol.size() - 1; // Rows above range, doesn't contains flags
2075 
2076     bool bFound = false;
2077     for (SCCOL i=nCol1; i<=nCol2 && !bFound; i++)
2078         bFound |= aCol[i].HasAttrib( nRow1, nRow2, nMask );
2079     return bFound;
2080 }
2081 
2082 bool ScTable::HasAttribSelection( const ScMarkData& rMark, HasAttrFlags nMask ) const
2083 {
2084     std::vector<sc::ColRowSpan> aSpans = rMark.GetMarkedColSpans();
2085 
2086     for (const sc::ColRowSpan & aSpan : aSpans)
2087     {
2088         for (SCCOLROW j = aSpan.mnStart; j <= aSpan.mnEnd; ++j)
2089         {
2090             if (aCol[j].HasAttribSelection(rMark, nMask))
2091               return true;
2092         }
2093     }
2094     return false;
2095 }
2096 
2097 bool ScTable::ExtendMerge( SCCOL nStartCol, SCROW nStartRow,
2098                            SCCOL& rEndCol, SCROW& rEndRow,
2099                            bool bRefresh )
2100 {
2101     if (!(ValidCol(nStartCol) && ValidCol(rEndCol)))
2102     {
2103         OSL_FAIL("ScTable::ExtendMerge: invalid column number");
2104         return false;
2105     }
2106     if ( nStartCol >= aCol.size() )
2107     {
2108         OSL_FAIL("ScTable::ExtendMerge: invalid nStartCol");
2109         return false;
2110     }
2111     bool bFound = false;
2112     SCCOL nOldEndX = std::min( rEndCol, static_cast<SCCOL>(aCol.size()-1) );
2113     SCROW nOldEndY = rEndRow;
2114     for (SCCOL i=nStartCol; i<=nOldEndX; i++)
2115         bFound |= aCol[i].ExtendMerge( i, nStartRow, nOldEndY, rEndCol, rEndRow, bRefresh );
2116     return bFound;
2117 }
2118 
2119 void ScTable::SetMergedCells( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
2120 {
2121     ScMergeAttr aAttr(nCol2-nCol1+1, nRow2-nRow1+1);
2122     ApplyAttr(nCol1, nRow1, aAttr);
2123 
2124     if (nCol1 < nCol2)
2125         ApplyFlags(nCol1+1, nRow1, nCol2, nRow2, ScMF::Hor);
2126 
2127     if (nRow1 < nRow2)
2128         ApplyFlags(nCol1, nRow1+1, nCol1, nRow2, ScMF::Ver);
2129 
2130     if (nCol1 < nCol2 && nRow1 < nRow2)
2131         ApplyFlags(nCol1+1, nRow1+1, nCol2, nRow2, ScMF::Hor | ScMF::Ver);
2132 }
2133 
2134 bool ScTable::IsBlockEmpty( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bIgnoreNotes ) const
2135 {
2136     if (!(ValidCol(nCol1) && ValidCol(nCol2)))
2137     {
2138         OSL_FAIL("ScTable::IsBlockEmpty: invalid column number");
2139         return false;
2140     }
2141     nCol2 = ClampToAllocatedColumns(nCol2);
2142     bool bEmpty = true;
2143     for (SCCOL i=nCol1; i<=nCol2 && bEmpty; i++)
2144     {
2145         bEmpty = aCol[i].IsEmptyBlock( nRow1, nRow2 );
2146         if (!bIgnoreNotes && bEmpty)
2147         {
2148             bEmpty = aCol[i].IsNotesEmptyBlock(nRow1, nRow2);
2149         }
2150     }
2151     return bEmpty;
2152 }
2153 
2154 SCSIZE ScTable::FillMaxRot( RowInfo* pRowInfo, SCSIZE nArrCount, SCCOL nX1, SCCOL nX2,
2155                             SCCOL nCol, SCROW nAttrRow1, SCROW nAttrRow2, SCSIZE nArrY,
2156                             const ScPatternAttr* pPattern, const SfxItemSet* pCondSet )
2157 {
2158     //  Return value = new nArrY
2159 
2160     ScRotateDir nRotDir = pPattern->GetRotateDir( pCondSet );
2161     if ( nRotDir != ScRotateDir::NONE )
2162     {
2163         bool bHit = true;
2164         if ( nCol+1 < nX1 )                             // column to the left
2165             bHit = ( nRotDir != ScRotateDir::Left );
2166         else if ( nCol > nX2+1 )                        // column to the right
2167             bHit = ( nRotDir != ScRotateDir::Right );      // ScRotateDir::Standard may now also be extended to the left
2168 
2169         if ( bHit )
2170         {
2171             double nFactor = 0.0;
2172             if ( nCol > nX2+1 )
2173             {
2174                 long nRotVal = pPattern->
2175                         GetItem( ATTR_ROTATE_VALUE, pCondSet ).GetValue();
2176                 double nRealOrient = nRotVal * F_PI18000;   // 1/100 degree
2177                 double nCos = cos( nRealOrient );
2178                 double nSin = sin( nRealOrient );
2179                 //TODO: limit !!!
2180                 //TODO: additional factor for varying PPT X/Y !!!
2181 
2182                 // for ScRotateDir::Left this gives a negative value,
2183                 // if the mode is considered
2184                 nFactor = -fabs( nCos / nSin );
2185             }
2186 
2187             for ( SCROW nRow = nAttrRow1; nRow <= nAttrRow2; nRow++ )
2188             {
2189                 if (!RowHidden(nRow))
2190                 {
2191                     bool bHitOne = true;
2192                     if ( nCol > nX2+1 )
2193                     {
2194                         // Does the rotated cell extend into the visible range?
2195 
2196                         SCCOL nTouchedCol = nCol;
2197                         long nWidth = static_cast<long>(mpRowHeights->getValue(nRow) * nFactor);
2198                         OSL_ENSURE(nWidth <= 0, "Wrong direction");
2199                         while ( nWidth < 0 && nTouchedCol > 0 )
2200                         {
2201                             --nTouchedCol;
2202                             nWidth += GetColWidth( nTouchedCol );
2203                         }
2204                         if ( nTouchedCol > nX2 )
2205                             bHitOne = false;
2206                     }
2207 
2208                     if (bHitOne)
2209                     {
2210                         while ( nArrY<nArrCount && pRowInfo[nArrY].nRowNo < nRow )
2211                             ++nArrY;
2212                         if ( nArrY<nArrCount && pRowInfo[nArrY].nRowNo == nRow )
2213                             pRowInfo[nArrY].nRotMaxCol = nCol;
2214                     }
2215                 }
2216             }
2217         }
2218     }
2219 
2220     return nArrY;
2221 }
2222 
2223 void ScTable::FindMaxRotCol( RowInfo* pRowInfo, SCSIZE nArrCount, SCCOL nX1, SCCOL nX2 )
2224 {
2225     if ( !mpColWidth || !mpRowHeights || !mpColFlags || !pRowFlags )
2226     {
2227         OSL_FAIL( "Row/column info missing" );
2228         return;
2229     }
2230 
2231     //  nRotMaxCol is initialized to SC_ROTMAX_NONE, nRowNo is already set
2232 
2233     SCROW nY1 = pRowInfo[0].nRowNo;
2234     SCROW nY2 = pRowInfo[nArrCount-1].nRowNo;
2235 
2236     for (SCCOL nCol : GetColumnsRange(0, pDocument->MaxCol()))
2237     {
2238         if (!ColHidden(nCol))
2239         {
2240             SCSIZE nArrY = 0;
2241             ScDocAttrIterator aIter( pDocument, nTab, nCol, nY1, nCol, nY2 );
2242             SCCOL nAttrCol;
2243             SCROW nAttrRow1, nAttrRow2;
2244             const ScPatternAttr* pPattern = aIter.GetNext( nAttrCol, nAttrRow1, nAttrRow2 );
2245             while ( pPattern )
2246             {
2247                 const SfxPoolItem* pCondItem;
2248                 if ( pPattern->GetItemSet().GetItemState( ATTR_CONDITIONAL, true, &pCondItem )
2249                         == SfxItemState::SET )
2250                 {
2251                     //  Run through all formats, so that each cell does not have to be
2252                     //  handled individually
2253 
2254                     const ScCondFormatIndexes& rCondFormatData = static_cast<const ScCondFormatItem*>(pCondItem)->GetCondFormatData();
2255                     ScStyleSheetPool* pStylePool = pDocument->GetStyleSheetPool();
2256                     if (mpCondFormatList && pStylePool && !rCondFormatData.empty())
2257                     {
2258                         for(const auto& rItem : rCondFormatData)
2259                         {
2260                             const ScConditionalFormat* pFormat = mpCondFormatList->GetFormat(rItem);
2261                             if ( pFormat )
2262                             {
2263                                 size_t nEntryCount = pFormat->size();
2264                                 for (size_t nEntry=0; nEntry<nEntryCount; nEntry++)
2265                                 {
2266                                     const ScFormatEntry* pEntry = pFormat->GetEntry(nEntry);
2267                                     if(pEntry->GetType() != ScFormatEntry::Type::Condition &&
2268                                        pEntry->GetType() != ScFormatEntry::Type::ExtCondition)
2269                                         continue;
2270 
2271                                     OUString  aStyleName = static_cast<const ScCondFormatEntry*>(pEntry)->GetStyle();
2272                                     if (!aStyleName.isEmpty())
2273                                     {
2274                                         SfxStyleSheetBase* pStyleSheet =
2275                                             pStylePool->Find( aStyleName, SfxStyleFamily::Para );
2276                                         if ( pStyleSheet )
2277                                         {
2278                                             FillMaxRot( pRowInfo, nArrCount, nX1, nX2,
2279                                                     nCol, nAttrRow1, nAttrRow2,
2280                                                     nArrY, pPattern, &pStyleSheet->GetItemSet() );
2281                                             //  not changing nArrY
2282                                         }
2283                                     }
2284                                 }
2285                             }
2286                         }
2287                     }
2288                 }
2289 
2290                 nArrY = FillMaxRot( pRowInfo, nArrCount, nX1, nX2,
2291                                     nCol, nAttrRow1, nAttrRow2,
2292                                     nArrY, pPattern, nullptr );
2293 
2294                 pPattern = aIter.GetNext( nAttrCol, nAttrRow1, nAttrRow2 );
2295             }
2296         }
2297     }
2298 }
2299 
2300 bool ScTable::HasBlockMatrixFragment( const SCCOL nCol1, SCROW nRow1, const SCCOL nCol2, SCROW nRow2,
2301         bool bNoMatrixAtAll ) const
2302 {
2303     using namespace sc;
2304 
2305     if ( !IsColValid( nCol1 ) )
2306         return false;
2307 
2308     const SCCOL nMaxCol2 = std::min<SCCOL>( nCol2, aCol.size() - 1 );
2309 
2310     MatrixEdge nEdges = MatrixEdge::Nothing;
2311 
2312     if ( nCol1 == nMaxCol2 )
2313     {   // left and right column
2314         const MatrixEdge n = MatrixEdge::Left | MatrixEdge::Right;
2315         nEdges = aCol[nCol1].GetBlockMatrixEdges( nRow1, nRow2, n, bNoMatrixAtAll );
2316         if ((nEdges != MatrixEdge::Nothing) && (((nEdges & n)!=n) || (nEdges & (MatrixEdge::Inside|MatrixEdge::Open))))
2317             return true;        // left or right edge is missing or open
2318     }
2319     else
2320     {   // left column
2321         nEdges = aCol[nCol1].GetBlockMatrixEdges(nRow1, nRow2, MatrixEdge::Left, bNoMatrixAtAll);
2322         if ((nEdges != MatrixEdge::Nothing) && ((!(nEdges & MatrixEdge::Left)) || (nEdges & (MatrixEdge::Inside|MatrixEdge::Open))))
2323             return true;        // left edge missing or open
2324         // right column
2325         nEdges = aCol[nMaxCol2].GetBlockMatrixEdges(nRow1, nRow2, MatrixEdge::Right, bNoMatrixAtAll);
2326         if ((nEdges != MatrixEdge::Nothing) && ((!(nEdges & MatrixEdge::Right)) || (nEdges & (MatrixEdge::Inside|MatrixEdge::Open))))
2327             return true;        // right edge is missing or open
2328     }
2329 
2330     if (bNoMatrixAtAll)
2331     {
2332         for (SCCOL i=nCol1; i<=nMaxCol2; i++)
2333         {
2334             nEdges = aCol[i].GetBlockMatrixEdges( nRow1, nRow2, MatrixEdge::Nothing, bNoMatrixAtAll);
2335             if (nEdges != MatrixEdge::Nothing
2336                     && (nEdges != (MatrixEdge::Top | MatrixEdge::Left | MatrixEdge::Bottom | MatrixEdge::Right)))
2337                 return true;
2338         }
2339     }
2340     else if ( nRow1 == nRow2 )
2341     {   // Row on top and on bottom
2342         bool bOpen = false;
2343         const MatrixEdge n = MatrixEdge::Bottom | MatrixEdge::Top;
2344         for ( SCCOL i=nCol1; i<=nMaxCol2; i++)
2345         {
2346             nEdges = aCol[i].GetBlockMatrixEdges( nRow1, nRow1, n, bNoMatrixAtAll );
2347             if (nEdges != MatrixEdge::Nothing)
2348             {
2349                 if ( (nEdges & n) != n )
2350                     return true;        // Top or bottom edge missing
2351                 if (nEdges & MatrixEdge::Left)
2352                     bOpen = true;       // left edge open, continue
2353                 else if ( !bOpen )
2354                     return true;        // Something exist that has not been opened
2355                 if (nEdges & MatrixEdge::Right)
2356                     bOpen = false;      // Close right edge
2357             }
2358         }
2359         if ( bOpen )
2360             return true;
2361     }
2362     else
2363     {
2364         int j;
2365         MatrixEdge n;
2366         SCROW nR;
2367         // first top row, then bottom row
2368         for ( j=0, n = MatrixEdge::Top,    nR=nRow1; j<2;
2369               j++, n = MatrixEdge::Bottom, nR=nRow2)
2370         {
2371             bool bOpen = false;
2372             for ( SCCOL i=nCol1; i<=nMaxCol2; i++)
2373             {
2374                 nEdges = aCol[i].GetBlockMatrixEdges( nR, nR, n, bNoMatrixAtAll );
2375                 if ( nEdges != MatrixEdge::Nothing)
2376                 {
2377                     // in top row no top edge respectively
2378                     // in bottom row no bottom edge
2379                     if ( (nEdges & n) != n )
2380                         return true;
2381                     if (nEdges & MatrixEdge::Left)
2382                         bOpen = true;       // open left edge, continue
2383                     else if ( !bOpen )
2384                         return true;        // Something exist that has not been opened
2385                     if (nEdges & MatrixEdge::Right)
2386                         bOpen = false;      // Close right edge
2387                 }
2388             }
2389             if ( bOpen )
2390                 return true;
2391         }
2392     }
2393     return false;
2394 }
2395 
2396 bool ScTable::HasSelectionMatrixFragment( const ScMarkData& rMark ) const
2397 {
2398     std::vector<sc::ColRowSpan> aSpans = rMark.GetMarkedColSpans();
2399 
2400     for (const sc::ColRowSpan & aSpan : aSpans)
2401     {
2402         SCCOL nEndCol = ClampToAllocatedColumns(aSpan.mnEnd);
2403         for ( SCCOLROW j=aSpan.mnStart; j<=nEndCol; j++ )
2404         {
2405             if ( aCol[j].HasSelectionMatrixFragment(rMark) )
2406                 return true;
2407         }
2408     }
2409     return false;
2410 }
2411 
2412 bool ScTable::IsBlockEditable( SCCOL nCol1, SCROW nRow1, SCCOL nCol2,
2413             SCROW nRow2, bool* pOnlyNotBecauseOfMatrix /* = NULL */,
2414             bool bNoMatrixAtAll ) const
2415 {
2416     if ( !ValidColRow( nCol2, nRow2 ) )
2417     {
2418         SAL_WARN("sc", "IsBlockEditable: invalid column or row " << nCol2 << " " << nRow2);
2419         if (pOnlyNotBecauseOfMatrix)
2420             *pOnlyNotBecauseOfMatrix = false;
2421         return false;
2422     }
2423     nCol1 = ClampToAllocatedColumns(nCol1);
2424     nCol2 = ClampToAllocatedColumns(nCol2);
2425 
2426     bool bIsEditable = true;
2427     if ( nLockCount )
2428         bIsEditable = false;
2429     else if ( IsProtected() && !pDocument->IsScenario(nTab) )
2430     {
2431         bIsEditable = !HasAttrib( nCol1, nRow1, nCol2, nRow2, HasAttrFlags::Protected );
2432         if (!bIsEditable)
2433         {
2434             // An enhanced protection permission may override the attribute.
2435             if (pTabProtection)
2436                 bIsEditable = pTabProtection->isBlockEditable( ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab));
2437         }
2438         if (bIsEditable)
2439         {
2440             // If Sheet is protected and cells are not protected then
2441             // check the active scenario protect flag if this range is
2442             // on the active scenario range. Note the 'copy back' must also
2443             // be set to apply protection.
2444             sal_uInt16 nScenTab = nTab+1;
2445             while(pDocument->IsScenario(nScenTab))
2446             {
2447                 ScRange aEditRange(nCol1, nRow1, nScenTab, nCol2, nRow2, nScenTab);
2448                 if(pDocument->IsActiveScenario(nScenTab) && pDocument->HasScenarioRange(nScenTab, aEditRange))
2449                 {
2450                     ScScenarioFlags nFlags;
2451                     pDocument->GetScenarioFlags(nScenTab,nFlags);
2452                     bIsEditable = !((nFlags & ScScenarioFlags::Protected) && (nFlags & ScScenarioFlags::TwoWay));
2453                     break;
2454                 }
2455                 nScenTab++;
2456             }
2457         }
2458     }
2459     else if (pDocument->IsScenario(nTab))
2460     {
2461         // Determine if the preceding sheet is protected
2462         SCTAB nActualTab = nTab;
2463         do
2464         {
2465             nActualTab--;
2466         }
2467         while(pDocument->IsScenario(nActualTab));
2468 
2469         if(pDocument->IsTabProtected(nActualTab))
2470         {
2471             ScRange aEditRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab);
2472             if(pDocument->HasScenarioRange(nTab, aEditRange))
2473             {
2474                 ScScenarioFlags nFlags;
2475                 pDocument->GetScenarioFlags(nTab,nFlags);
2476                 bIsEditable = !(nFlags & ScScenarioFlags::Protected);
2477             }
2478         }
2479     }
2480     if ( bIsEditable )
2481     {
2482         if (HasBlockMatrixFragment( nCol1, nRow1, nCol2, nRow2, bNoMatrixAtAll))
2483         {
2484             bIsEditable = false;
2485             if ( pOnlyNotBecauseOfMatrix )
2486                 *pOnlyNotBecauseOfMatrix = true;
2487         }
2488         else if ( pOnlyNotBecauseOfMatrix )
2489             *pOnlyNotBecauseOfMatrix = false;
2490     }
2491     else if ( pOnlyNotBecauseOfMatrix )
2492         *pOnlyNotBecauseOfMatrix = false;
2493     return bIsEditable;
2494 }
2495 
2496 bool ScTable::IsSelectionEditable( const ScMarkData& rMark,
2497             bool* pOnlyNotBecauseOfMatrix /* = NULL */ ) const
2498 {
2499     bool bIsEditable = true;
2500     if ( nLockCount )
2501         bIsEditable = false;
2502     else if ( IsProtected() && !pDocument->IsScenario(nTab) )
2503     {
2504         ScRangeList aRanges;
2505         rMark.FillRangeListWithMarks( &aRanges, false );
2506         bIsEditable = !HasAttribSelection( rMark, HasAttrFlags::Protected );
2507         if (!bIsEditable)
2508         {
2509             // An enhanced protection permission may override the attribute.
2510             if (pTabProtection)
2511                 bIsEditable = pTabProtection->isSelectionEditable( aRanges);
2512         }
2513         if (bIsEditable)
2514         {
2515             // If Sheet is protected and cells are not protected then
2516             // check the active scenario protect flag if this area is
2517             // in the active scenario range.
2518             SCTAB nScenTab = nTab+1;
2519             while(pDocument->IsScenario(nScenTab) && bIsEditable)
2520             {
2521                 if(pDocument->IsActiveScenario(nScenTab))
2522                 {
2523                     for (size_t i=0, nRange = aRanges.size(); (i < nRange) && bIsEditable; i++ )
2524                     {
2525                         const ScRange & rRange = aRanges[ i ];
2526                         if(pDocument->HasScenarioRange(nScenTab, rRange))
2527                         {
2528                             ScScenarioFlags nFlags;
2529                             pDocument->GetScenarioFlags(nScenTab,nFlags);
2530                             bIsEditable = !((nFlags & ScScenarioFlags::Protected) && (nFlags & ScScenarioFlags::TwoWay));
2531                         }
2532                     }
2533                 }
2534                 nScenTab++;
2535             }
2536         }
2537     }
2538     else if (pDocument->IsScenario(nTab))
2539     {
2540         // Determine if the preceding sheet is protected
2541         SCTAB nActualTab = nTab;
2542         do
2543         {
2544             nActualTab--;
2545         }
2546         while(pDocument->IsScenario(nActualTab));
2547 
2548         if(pDocument->IsTabProtected(nActualTab))
2549         {
2550             ScRangeList aRanges;
2551             rMark.FillRangeListWithMarks( &aRanges, false );
2552             for (size_t i = 0, nRange = aRanges.size(); (i < nRange) && bIsEditable; i++)
2553             {
2554                 const ScRange & rRange = aRanges[ i ];
2555                 if(pDocument->HasScenarioRange(nTab, rRange))
2556                 {
2557                     ScScenarioFlags nFlags;
2558                     pDocument->GetScenarioFlags(nTab,nFlags);
2559                     bIsEditable = !(nFlags & ScScenarioFlags::Protected);
2560                 }
2561             }
2562         }
2563     }
2564     if ( bIsEditable )
2565     {
2566         if ( HasSelectionMatrixFragment( rMark ) )
2567         {
2568             bIsEditable = false;
2569             if ( pOnlyNotBecauseOfMatrix )
2570                 *pOnlyNotBecauseOfMatrix = true;
2571         }
2572         else if ( pOnlyNotBecauseOfMatrix )
2573             *pOnlyNotBecauseOfMatrix = false;
2574     }
2575     else if ( pOnlyNotBecauseOfMatrix )
2576         *pOnlyNotBecauseOfMatrix = false;
2577     return bIsEditable;
2578 }
2579 
2580 void ScTable::LockTable()
2581 {
2582     ++nLockCount;
2583 }
2584 
2585 void ScTable::UnlockTable()
2586 {
2587     if (nLockCount)
2588         --nLockCount;
2589     else
2590     {
2591         OSL_FAIL("UnlockTable without LockTable");
2592     }
2593 }
2594 
2595 void ScTable::MergeSelectionPattern( ScMergePatternState& rState, const ScMarkData& rMark, bool bDeep ) const
2596 {
2597     std::vector<sc::ColRowSpan> aSpans = rMark.GetMarkedColSpans();
2598 
2599     for (const sc::ColRowSpan & rSpan : aSpans)
2600     {
2601         SCCOL nEnd = ClampToAllocatedColumns(rSpan.mnEnd);
2602         for (SCCOLROW i = rSpan.mnStart; i <= nEnd; ++i)
2603         {
2604             aCol[i].MergeSelectionPattern( rState, rMark, bDeep );
2605         }
2606     }
2607 }
2608 
2609 void ScTable::MergePatternArea( ScMergePatternState& rState, SCCOL nCol1, SCROW nRow1,
2610                                                     SCCOL nCol2, SCROW nRow2, bool bDeep ) const
2611 {
2612     nCol2 = ClampToAllocatedColumns(nCol2);
2613     for (SCCOL i=nCol1; i<=nCol2; i++)
2614         aCol[i].MergePatternArea( rState, nRow1, nRow2, bDeep );
2615 }
2616 
2617 void ScTable::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner, ScLineFlags& rFlags,
2618                     SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const
2619 {
2620     if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow))
2621     {
2622         PutInOrder(nStartCol, nEndCol);
2623         PutInOrder(nStartRow, nEndRow);
2624         nEndCol = ClampToAllocatedColumns(nEndCol);
2625         for (SCCOL i=nStartCol; i<=nEndCol; i++)
2626             aCol[i].MergeBlockFrame( pLineOuter, pLineInner, rFlags,
2627                                     nStartRow, nEndRow, (i==nStartCol), nEndCol-i );
2628     }
2629 }
2630 
2631 void ScTable::ApplyBlockFrame(const SvxBoxItem& rLineOuter, const SvxBoxInfoItem* pLineInner,
2632                               SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow)
2633 {
2634     if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow))
2635     {
2636         PutInOrder(nStartCol, nEndCol);
2637         PutInOrder(nStartRow, nEndRow);
2638         nEndCol = ClampToAllocatedColumns(nEndCol);
2639         for (SCCOL i=nStartCol; i<=nEndCol; i++)
2640             aCol[i].ApplyBlockFrame(rLineOuter, pLineInner,
2641                                     nStartRow, nEndRow, (i==nStartCol), nEndCol-i);
2642     }
2643 }
2644 
2645 void ScTable::ApplyPattern( SCCOL nCol, SCROW nRow, const ScPatternAttr& rAttr )
2646 {
2647     if (ValidColRow(nCol,nRow))
2648         CreateColumnIfNotExists(nCol).ApplyPattern( nRow, rAttr );
2649 }
2650 
2651 void ScTable::ApplyPatternArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
2652                                      const ScPatternAttr& rAttr, ScEditDataArray* pDataArray,
2653                                      bool* const pIsChanged )
2654 {
2655     if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow))
2656     {
2657         PutInOrder(nStartCol, nEndCol);
2658         PutInOrder(nStartRow, nEndRow);
2659         for (SCCOL i = nStartCol; i <= nEndCol; i++)
2660             CreateColumnIfNotExists(i).ApplyPatternArea(nStartRow, nEndRow, rAttr, pDataArray, pIsChanged);
2661     }
2662 }
2663 
2664 void ScTable::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
2665         const ScPatternAttr& rPattern, SvNumFormatType nNewType )
2666 {
2667     SCCOL nEndCol = rRange.aEnd.Col();
2668     for ( SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; nCol++ )
2669     {
2670         aCol[nCol].ApplyPatternIfNumberformatIncompatible( rRange, rPattern, nNewType );
2671     }
2672 }
2673 
2674 void ScTable::AddCondFormatData( const ScRangeList& rRangeList, sal_uInt32 nIndex )
2675 {
2676     size_t n = rRangeList.size();
2677     for(size_t i = 0; i < n; ++i)
2678     {
2679         const ScRange & rRange = rRangeList[i];
2680         SCCOL nColStart = rRange.aStart.Col();
2681         SCCOL nColEnd = rRange.aEnd.Col();
2682         SCROW nRowStart = rRange.aStart.Row();
2683         SCROW nRowEnd = rRange.aEnd.Row();
2684         for(SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol)
2685         {
2686             CreateColumnIfNotExists(nCol).AddCondFormat(nRowStart, nRowEnd, nIndex);
2687         }
2688     }
2689 }
2690 
2691 void ScTable::RemoveCondFormatData( const ScRangeList& rRangeList, sal_uInt32 nIndex )
2692 {
2693     size_t n = rRangeList.size();
2694     for(size_t i = 0; i < n; ++i)
2695     {
2696         const ScRange & rRange = rRangeList[i];
2697         SCCOL nColStart = rRange.aStart.Col();
2698         SCCOL nColEnd = ClampToAllocatedColumns(rRange.aEnd.Col());
2699         SCROW nRowStart = rRange.aStart.Row();
2700         SCROW nRowEnd = rRange.aEnd.Row();
2701         for(SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol)
2702         {
2703             aCol[nCol].RemoveCondFormat(nRowStart, nRowEnd, nIndex);
2704         }
2705     }
2706 }
2707 
2708 void  ScTable::SetPatternAreaCondFormat( SCCOL nCol, SCROW nStartRow, SCROW nEndRow,
2709         const ScPatternAttr& rAttr, const ScCondFormatIndexes& rCondFormatIndexes )
2710 {
2711     aCol[nCol].SetPatternArea( nStartRow, nEndRow, rAttr);
2712 
2713     for (const auto& rIndex : rCondFormatIndexes)
2714     {
2715         ScConditionalFormat* pCondFormat = mpCondFormatList->GetFormat(rIndex);
2716         if (pCondFormat)
2717         {
2718             ScRangeList aRange = pCondFormat->GetRange();
2719             aRange.Join( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab));
2720             pCondFormat->SetRange(aRange);
2721         }
2722     }
2723 }
2724 
2725 void ScTable::ApplyStyle( SCCOL nCol, SCROW nRow, const ScStyleSheet* rStyle )
2726 {
2727     if (ValidColRow(nCol,nRow))
2728         // If column not exists then we need to create it
2729         CreateColumnIfNotExists( nCol ).ApplyStyle( nRow, rStyle );
2730 }
2731 
2732 void ScTable::ApplyStyleArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, const ScStyleSheet& rStyle )
2733 {
2734     if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow))
2735     {
2736         PutInOrder(nStartCol, nEndCol);
2737         PutInOrder(nStartRow, nEndRow);
2738         if ( nEndCol == pDocument->MaxCol() )
2739         {
2740             if ( nStartCol < aCol.size() )
2741             {
2742                 // If we would like set all columns to specific style, then change only default style for not existing columns
2743                 nEndCol = aCol.size() - 1;
2744                 for (SCCOL i = nStartCol; i <= nEndCol; i++)
2745                     aCol[i].ApplyStyleArea(nStartRow, nEndRow, rStyle);
2746                 aDefaultColAttrArray.ApplyStyleArea(nStartRow, nEndRow, rStyle );
2747             }
2748             else
2749             {
2750                 CreateColumnIfNotExists( nStartCol - 1 );
2751                 aDefaultColAttrArray.ApplyStyleArea(nStartRow, nEndRow, rStyle );
2752             }
2753         }
2754         else
2755         {
2756             CreateColumnIfNotExists( nEndCol );
2757             for (SCCOL i = nStartCol; i <= nEndCol; i++)
2758                 aCol[i].ApplyStyleArea(nStartRow, nEndRow, rStyle);
2759         }
2760     }
2761 }
2762 
2763 void ScTable::ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark)
2764 {
2765     for (SCCOL i=0; i < aCol.size(); i++)
2766         aCol[i].ApplySelectionStyle( rStyle, rMark );
2767 }
2768 
2769 void ScTable::ApplySelectionLineStyle( const ScMarkData& rMark,
2770                             const ::editeng::SvxBorderLine* pLine, bool bColorOnly )
2771 {
2772     if ( bColorOnly && !pLine )
2773         return;
2774 
2775     for (SCCOL i=0; i < aCol.size(); i++)
2776         aCol[i].ApplySelectionLineStyle( rMark, pLine, bColorOnly );
2777 }
2778 
2779 const ScStyleSheet* ScTable::GetStyle( SCCOL nCol, SCROW nRow ) const
2780 {
2781     if ( !ValidColRow( nCol, nRow ) )
2782         return nullptr;
2783     if ( nCol < aCol.size() )
2784         return aCol[nCol].GetStyle( nRow );
2785     else
2786         return aDefaultColAttrArray.GetPattern( nRow )->GetStyleSheet();
2787 }
2788 
2789 const ScStyleSheet* ScTable::GetSelectionStyle( const ScMarkData& rMark, bool& rFound ) const
2790 {
2791     rFound = false;
2792 
2793     bool    bEqual = true;
2794     bool    bColFound;
2795 
2796     const ScStyleSheet* pStyle = nullptr;
2797     const ScStyleSheet* pNewStyle;
2798 
2799     for (SCCOL i=0; i < aCol.size() && bEqual; i++)
2800         if (rMark.HasMultiMarks(i))
2801         {
2802             pNewStyle = aCol[i].GetSelectionStyle( rMark, bColFound );
2803             if (bColFound)
2804             {
2805                 rFound = true;
2806                 if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
2807                     bEqual = false;
2808                 pStyle = pNewStyle;
2809             }
2810         }
2811 
2812     return bEqual ? pStyle : nullptr;
2813 }
2814 
2815 const ScStyleSheet* ScTable::GetAreaStyle( bool& rFound, SCCOL nCol1, SCROW nRow1,
2816                                            SCCOL nCol2, SCROW nRow2 ) const
2817 {
2818     rFound = false;
2819 
2820     bool    bEqual = true;
2821     bool    bColFound;
2822 
2823     const ScStyleSheet* pStyle = nullptr;
2824     const ScStyleSheet* pNewStyle;
2825     nCol2 = ClampToAllocatedColumns(nCol2);
2826     for (SCCOL i=nCol1; i<=nCol2 && bEqual; i++)
2827     {
2828         pNewStyle = aCol[i].GetAreaStyle(bColFound, nRow1, nRow2);
2829         if (bColFound)
2830         {
2831             rFound = true;
2832             if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
2833                 bEqual = false;
2834             pStyle = pNewStyle;
2835         }
2836     }
2837 
2838     return bEqual ? pStyle : nullptr;
2839 }
2840 
2841 bool ScTable::IsStyleSheetUsed( const ScStyleSheet& rStyle ) const
2842 {
2843     bool bIsUsed = false;
2844 
2845     for ( SCCOL i=0; i < aCol.size(); i++ )
2846     {
2847         if ( aCol[i].IsStyleSheetUsed( rStyle ) )
2848         {
2849             bIsUsed = true;
2850         }
2851     }
2852 
2853     return bIsUsed;
2854 }
2855 
2856 void ScTable::StyleSheetChanged( const SfxStyleSheetBase* pStyleSheet, bool bRemoved,
2857                                 OutputDevice* pDev,
2858                                 double nPPTX, double nPPTY,
2859                                 const Fraction& rZoomX, const Fraction& rZoomY )
2860 {
2861     ScFlatBoolRowSegments aUsedRows(pDocument->MaxRow());
2862     for (SCCOL i = 0; i < aCol.size(); ++i)
2863         aCol[i].FindStyleSheet(pStyleSheet, aUsedRows, bRemoved);
2864 
2865     sc::RowHeightContext aCxt(pDocument->MaxRow(), nPPTX, nPPTY, rZoomX, rZoomY, pDev);
2866     SCROW nRow = 0;
2867     while (nRow <= pDocument->MaxRow())
2868     {
2869         ScFlatBoolRowSegments::RangeData aData;
2870         if (!aUsedRows.getRangeData(nRow, aData))
2871             // search failed!
2872             return;
2873 
2874         SCROW nEndRow = aData.mnRow2;
2875         if (aData.mbValue)
2876             SetOptimalHeight(aCxt, nRow, nEndRow);
2877 
2878         nRow = nEndRow + 1;
2879     }
2880 }
2881 
2882 bool ScTable::ApplyFlags( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
2883                           ScMF nFlags )
2884 {
2885     bool bChanged = false;
2886     if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow))
2887         for (SCCOL i = nStartCol; i <= nEndCol; i++)
2888             bChanged |= CreateColumnIfNotExists(i).ApplyFlags(nStartRow, nEndRow, nFlags);
2889     return bChanged;
2890 }
2891 
2892 bool ScTable::RemoveFlags( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
2893                            ScMF nFlags )
2894 {
2895     if (!ValidColRow(nStartCol, nStartRow) || !ValidColRow(nEndCol, nEndRow))
2896         return false;
2897     bool bChanged = false;
2898     nEndCol = ClampToAllocatedColumns(nEndCol);
2899     for (SCCOL i = nStartCol; i <= nEndCol; i++)
2900         bChanged |= aCol[i].RemoveFlags(nStartRow, nEndRow, nFlags);
2901     return bChanged;
2902 }
2903 
2904 void ScTable::SetPattern( const ScAddress& rPos, const ScPatternAttr& rAttr )
2905 {
2906     if (ValidColRow(rPos.Col(),rPos.Row()))
2907         aCol[rPos.Col()].SetPattern( rPos.Row(), rAttr );
2908 }
2909 
2910 const ScPatternAttr* ScTable::SetPattern( SCCOL nCol, SCROW nRow, std::unique_ptr<ScPatternAttr> pAttr )
2911 {
2912     if (ValidColRow(nCol,nRow))
2913         return aCol[nCol].SetPattern( nRow, std::move(pAttr) );
2914     return nullptr;
2915 }
2916 
2917 void ScTable::SetPattern( SCCOL nCol, SCROW nRow, const ScPatternAttr& rAttr )
2918 {
2919     if (ValidColRow(nCol,nRow))
2920         aCol[nCol].SetPattern( nRow, rAttr );
2921 }
2922 
2923 void ScTable::ApplyAttr( SCCOL nCol, SCROW nRow, const SfxPoolItem& rAttr )
2924 {
2925     if (ValidColRow(nCol,nRow))
2926         CreateColumnIfNotExists(nCol).ApplyAttr( nRow, rAttr );
2927 }
2928 
2929 void ScTable::ApplySelectionCache( SfxItemPoolCache* pCache, const ScMarkData& rMark,
2930                                    ScEditDataArray* pDataArray, bool* const pIsChanged )
2931 {
2932     for (SCCOL i=0; i < aCol.size(); i++)
2933         aCol[i].ApplySelectionCache( pCache, rMark, pDataArray, pIsChanged );
2934 }
2935 
2936 void ScTable::ChangeSelectionIndent( bool bIncrement, const ScMarkData& rMark )
2937 {
2938     for (SCCOL i=0; i < aCol.size(); i++)
2939         aCol[i].ChangeSelectionIndent( bIncrement, rMark );
2940 }
2941 
2942 void ScTable::ClearSelectionItems( const sal_uInt16* pWhich, const ScMarkData& rMark )
2943 {
2944     for (SCCOL i=0; i < aCol.size(); i++)
2945         aCol[i].ClearSelectionItems( pWhich, rMark );
2946 }
2947 
2948 //  Column widths / Row heights
2949 
2950 void ScTable::SetColWidth( SCCOL nCol, sal_uInt16 nNewWidth )
2951 {
2952     if (ValidCol(nCol) && mpColWidth)
2953     {
2954         if (!nNewWidth)
2955         {
2956             nNewWidth = STD_COL_WIDTH;
2957         }
2958 
2959         if ( nNewWidth != mpColWidth->GetValue(nCol) )
2960         {
2961             mpColWidth->SetValue(nCol, nNewWidth);
2962             InvalidatePageBreaks();
2963         }
2964     }
2965     else
2966     {
2967         OSL_FAIL("Invalid column number or no widths");
2968     }
2969 }
2970 
2971 void ScTable::SetColWidthOnly( SCCOL nCol, sal_uInt16 nNewWidth )
2972 {
2973     if (!ValidCol(nCol) || !mpColWidth)
2974         return;
2975 
2976     if (!nNewWidth)
2977         nNewWidth = STD_COL_WIDTH;
2978 
2979     if (nNewWidth != mpColWidth->GetValue(nCol))
2980         mpColWidth->SetValue(nCol, nNewWidth);
2981 }
2982 
2983 void ScTable::SetRowHeight( SCROW nRow, sal_uInt16 nNewHeight )
2984 {
2985     if (ValidRow(nRow) && mpRowHeights)
2986     {
2987         if (!nNewHeight)
2988         {
2989             OSL_FAIL("SetRowHeight: Row height zero");
2990             nNewHeight = ScGlobal::nStdRowHeight;
2991         }
2992 
2993         sal_uInt16 nOldHeight = mpRowHeights->getValue(nRow);
2994         if ( nNewHeight != nOldHeight )
2995         {
2996             mpRowHeights->setValue(nRow, nRow, nNewHeight);
2997             InvalidatePageBreaks();
2998         }
2999     }
3000     else
3001     {
3002         OSL_FAIL("Invalid row number or no heights");
3003     }
3004 }
3005 
3006 namespace {
3007 
3008 /**
3009  * Check if the new pixel size is different from the old size between
3010  * specified ranges.
3011  */
3012 bool lcl_pixelSizeChanged(
3013     ScFlatUInt16RowSegments& rRowHeights, SCROW nStartRow, SCROW nEndRow,
3014     sal_uInt16 nNewHeight, double nPPTY)
3015 {
3016     long nNewPix = static_cast<long>(nNewHeight * nPPTY);
3017 
3018     ScFlatUInt16RowSegments::ForwardIterator aFwdIter(rRowHeights);
3019     for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
3020     {
3021         sal_uInt16 nHeight;
3022         if (!aFwdIter.getValue(nRow, nHeight))
3023             break;
3024 
3025         if (nHeight != nNewHeight)
3026         {
3027             bool bChanged = (nNewPix != static_cast<long>(nHeight * nPPTY));
3028             if (bChanged)
3029                 return true;
3030         }
3031 
3032         // Skip ahead to the last position of the current range.
3033         nRow = aFwdIter.getLastPos();
3034     }
3035     return false;
3036 }
3037 
3038 }
3039 
3040 bool ScTable::SetRowHeightRange( SCROW nStartRow, SCROW nEndRow, sal_uInt16 nNewHeight,
3041                                     double nPPTY )
3042 {
3043     bool bChanged = false;
3044     if (ValidRow(nStartRow) && ValidRow(nEndRow) && mpRowHeights)
3045     {
3046         if (!nNewHeight)
3047         {
3048             OSL_FAIL("SetRowHeight: Row height zero");
3049             nNewHeight = ScGlobal::nStdRowHeight;
3050         }
3051 
3052         bool bSingle = false;   // true = process every row for its own
3053         ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer();
3054         if (pDrawLayer)
3055             if (pDrawLayer->HasObjectsInRows( nTab, nStartRow, nEndRow ))
3056                 bSingle = true;
3057 
3058         if (bSingle)
3059         {
3060             ScFlatUInt16RowSegments::RangeData aData;
3061             if (mpRowHeights->getRangeData(nStartRow, aData) &&
3062                 nNewHeight == aData.mnValue && nEndRow <= aData.mnRow2)
3063             {
3064                 bSingle = false;    // no difference in this range
3065             }
3066         }
3067 
3068         if (!bSingle || nEndRow - nStartRow < 20)
3069         {
3070             bChanged = lcl_pixelSizeChanged(*mpRowHeights, nStartRow, nEndRow, nNewHeight, nPPTY);
3071             mpRowHeights->setValue(nStartRow, nEndRow, nNewHeight);
3072         }
3073         else
3074         {
3075             SCROW nMid = (nStartRow + nEndRow) / 2;
3076             if (SetRowHeightRange(nStartRow, nMid, nNewHeight, 1.0))
3077                 bChanged = true;
3078             if (SetRowHeightRange(nMid + 1, nEndRow, nNewHeight, 1.0))
3079                 bChanged = true;
3080         }
3081 
3082         if (bChanged)
3083             InvalidatePageBreaks();
3084     }
3085     else
3086     {
3087         OSL_FAIL("Invalid row number or no heights");
3088     }
3089 
3090     return bChanged;
3091 }
3092 
3093 void ScTable::SetRowHeightOnly( SCROW nStartRow, SCROW nEndRow, sal_uInt16 nNewHeight )
3094 {
3095     if (!ValidRow(nStartRow) || !ValidRow(nEndRow) || !mpRowHeights)
3096         return;
3097 
3098     if (!nNewHeight)
3099         nNewHeight = ScGlobal::nStdRowHeight;
3100 
3101     mpRowHeights->setValue(nStartRow, nEndRow, nNewHeight);
3102 }
3103 
3104 void ScTable::SetManualHeight( SCROW nStartRow, SCROW nEndRow, bool bManual )
3105 {
3106     if (ValidRow(nStartRow) && ValidRow(nEndRow) && pRowFlags)
3107     {
3108         if (bManual)
3109             pRowFlags->OrValue( nStartRow, nEndRow, CRFlags::ManualSize);
3110         else
3111             pRowFlags->AndValue( nStartRow, nEndRow, ~CRFlags::ManualSize);
3112     }
3113     else
3114     {
3115         OSL_FAIL("Invalid row number or no column flags");
3116     }
3117 }
3118 
3119 sal_uInt16 ScTable::GetColWidth( SCCOL nCol, bool bHiddenAsZero ) const
3120 {
3121     OSL_ENSURE(ValidCol(nCol),"wrong column number");
3122 
3123     if (ValidCol(nCol) && mpColFlags && mpColWidth)
3124     {
3125         if (bHiddenAsZero && ColHidden(nCol))
3126             return 0;
3127         else
3128             return mpColWidth->GetValue(nCol);
3129     }
3130     else
3131         return sal_uInt16(STD_COL_WIDTH);
3132 }
3133 
3134 sal_uLong ScTable::GetColWidth( SCCOL nStartCol, SCCOL nEndCol ) const
3135 {
3136     if (!ValidCol(nStartCol) || !ValidCol(nEndCol) || nStartCol > nEndCol)
3137         return 0;
3138 
3139     sal_uLong nW = 0;
3140     bool bHidden = false;
3141     SCCOL nLastHiddenCol = -1;
3142     auto colWidthIt = mpColWidth->begin() + nStartCol;
3143     for (SCCOL nCol = nStartCol; nCol <= nEndCol; (++nCol <= nEndCol) ? ++colWidthIt : (void)false)
3144     {
3145         if (nCol > nLastHiddenCol)
3146             bHidden = ColHidden(nCol, nullptr, &nLastHiddenCol);
3147 
3148         if (bHidden)
3149             continue;
3150 
3151         nW += *colWidthIt;
3152     }
3153     return nW;
3154 }
3155 
3156 sal_uInt16 ScTable::GetOriginalWidth( SCCOL nCol ) const        // always the set value
3157 {
3158     OSL_ENSURE(ValidCol(nCol),"wrong column number");
3159 
3160     if (ValidCol(nCol) && mpColWidth)
3161         return mpColWidth->GetValue(nCol);
3162     else
3163         return sal_uInt16(STD_COL_WIDTH);
3164 }
3165 
3166 sal_uInt16 ScTable::GetCommonWidth( SCCOL nEndCol ) const
3167 {
3168     //  get the width that is used in the largest continuous column range (up to nEndCol)
3169 
3170     if ( !ValidCol(nEndCol) )
3171     {
3172         OSL_FAIL("wrong column");
3173         nEndCol = pDocument->MaxCol();
3174     }
3175 
3176     sal_uInt16 nMaxWidth = 0;
3177     sal_uInt16 nMaxCount = 0;
3178     SCCOL nRangeStart = 0;
3179     while ( nRangeStart <= nEndCol )
3180     {
3181         //  skip hidden columns
3182         while ( nRangeStart <= nEndCol && ColHidden(nRangeStart) )
3183             ++nRangeStart;
3184         if ( nRangeStart <= nEndCol )
3185         {
3186             sal_uInt16 nThisCount = 0;
3187             auto colWidthIt = mpColWidth->begin() + nRangeStart;
3188             sal_uInt16 nThisWidth = *colWidthIt;
3189             SCCOL nRangeEnd = nRangeStart;
3190             while ( nRangeEnd <= nEndCol && *colWidthIt == nThisWidth )
3191             {
3192                 ++nThisCount;
3193                 ++nRangeEnd;
3194                 ++colWidthIt;
3195 
3196                 //  skip hidden columns
3197                 while ( nRangeEnd <= nEndCol && ColHidden(nRangeEnd) )
3198                 {
3199                     ++nRangeEnd;
3200                     ++colWidthIt;
3201                 }
3202             }
3203 
3204             if ( nThisCount > nMaxCount )
3205             {
3206                 nMaxCount = nThisCount;
3207                 nMaxWidth = nThisWidth;
3208             }
3209 
3210             nRangeStart = nRangeEnd;        // next range
3211         }
3212     }
3213 
3214     return nMaxWidth;
3215 }
3216 
3217 sal_uInt16 ScTable::GetRowHeight( SCROW nRow, SCROW* pStartRow, SCROW* pEndRow, bool bHiddenAsZero ) const
3218 {
3219     SAL_WARN_IF(!ValidRow(nRow), "sc", "Invalid row number " << nRow);
3220 
3221     if (ValidRow(nRow) && mpRowHeights)
3222     {
3223         if (bHiddenAsZero && RowHidden( nRow, pStartRow, pEndRow))
3224             return 0;
3225         else
3226         {
3227             ScFlatUInt16RowSegments::RangeData aData;
3228             if (!mpRowHeights->getRangeData(nRow, aData))
3229             {
3230                 if (pStartRow)
3231                     *pStartRow = nRow;
3232                 if (pEndRow)
3233                     *pEndRow = nRow;
3234                 // TODO: What should we return in case the search fails?
3235                 return 0;
3236             }
3237 
3238             // If bHiddenAsZero, pStartRow and pEndRow were initialized to
3239             // boundaries of a non-hidden segment. Assume that the previous and
3240             // next segment are hidden then and limit the current height
3241             // segment.
3242             if (pStartRow)
3243                 *pStartRow = (bHiddenAsZero ? std::max( *pStartRow, aData.mnRow1) : aData.mnRow1);
3244             if (pEndRow)
3245                 *pEndRow = (bHiddenAsZero ? std::min( *pEndRow, aData.mnRow2) : aData.mnRow2);
3246             return aData.mnValue;
3247         }
3248     }
3249     else
3250     {
3251         if (pStartRow)
3252             *pStartRow = nRow;
3253         if (pEndRow)
3254             *pEndRow = nRow;
3255         return ScGlobal::nStdRowHeight;
3256     }
3257 }
3258 
3259 sal_uLong ScTable::GetRowHeight( SCROW nStartRow, SCROW nEndRow, bool bHiddenAsZero ) const
3260 {
3261     OSL_ENSURE(ValidRow(nStartRow) && ValidRow(nEndRow),"wrong row number");
3262 
3263     if (ValidRow(nStartRow) && ValidRow(nEndRow) && mpRowHeights)
3264     {
3265         sal_uLong nHeight = 0;
3266         SCROW nRow = nStartRow;
3267         while (nRow <= nEndRow)
3268         {
3269             SCROW nLastRow = -1;
3270             if (!( ( RowHidden(nRow, nullptr, &nLastRow) ) && bHiddenAsZero ) )
3271             {
3272                 if (nLastRow > nEndRow)
3273                     nLastRow = nEndRow;
3274                 nHeight += mpRowHeights->getSumValue(nRow, nLastRow);
3275             }
3276             nRow = nLastRow + 1;
3277         }
3278         return nHeight;
3279     }
3280     else
3281         return (nEndRow - nStartRow + 1) * static_cast<sal_uLong>(ScGlobal::nStdRowHeight);
3282 }
3283 
3284 sal_uLong ScTable::GetScaledRowHeight( SCROW nStartRow, SCROW nEndRow, double fScale, const sal_uLong* pnMaxHeight ) const
3285 {
3286     OSL_ENSURE(ValidRow(nStartRow) && ValidRow(nEndRow),"wrong row number");
3287 
3288     if (ValidRow(nStartRow) && ValidRow(nEndRow) && mpRowHeights)
3289     {
3290         sal_uLong nHeight = 0;
3291         SCROW nRow = nStartRow;
3292         while (nRow <= nEndRow)
3293         {
3294             SCROW nLastRow = -1;
3295             if (!RowHidden(nRow, nullptr, &nLastRow))
3296             {
3297                 if (nLastRow > nEndRow)
3298                     nLastRow = nEndRow;
3299 
3300                 // #i117315# can't use getSumValue, because individual values must be rounded
3301                 while (nRow <= nLastRow)
3302                 {
3303                     ScFlatUInt16RowSegments::RangeData aData;
3304                     if (!mpRowHeights->getRangeData(nRow, aData))
3305                         return nHeight;   // shouldn't happen
3306 
3307                     SCROW nSegmentEnd = std::min( nLastRow, aData.mnRow2 );
3308 
3309                     // round-down a single height value, multiply resulting (pixel) values
3310                     const sal_uLong nOneHeight = static_cast<sal_uLong>( aData.mnValue * fScale );
3311                     // sometimes scaling results in zero height
3312                     if (nOneHeight)
3313                     {
3314                         SCROW nRowsInSegment = nSegmentEnd + 1 - nRow;
3315                         if (pnMaxHeight)
3316                         {
3317                             nRowsInSegment = std::min(nRowsInSegment, static_cast<SCROW>(*pnMaxHeight / nOneHeight + 1));
3318                             nHeight += nOneHeight * nRowsInSegment;
3319                             if (nHeight > *pnMaxHeight)
3320                                 return nHeight;
3321                         }
3322                         else
3323                             nHeight += nOneHeight * nRowsInSegment;
3324                     }
3325 
3326                     nRow = nSegmentEnd + 1;
3327                 }
3328             }
3329             nRow = nLastRow + 1;
3330         }
3331         return nHeight;
3332     }
3333     else
3334     {
3335         const sal_uLong nOneHeight = static_cast<sal_uLong>(ScGlobal::nStdRowHeight * fScale);
3336         SCROW nRowsInSegment = nEndRow - nStartRow + 1;
3337         if (pnMaxHeight)
3338         {
3339             nRowsInSegment = std::min(nRowsInSegment, static_cast<SCROW>(*pnMaxHeight / nOneHeight + 1));
3340             return nOneHeight * nRowsInSegment;
3341         }
3342         else
3343             return static_cast<sal_uLong>(nRowsInSegment * nOneHeight);
3344     }
3345 }
3346 
3347 sal_uInt16 ScTable::GetOriginalHeight( SCROW nRow ) const       // non-0 even if hidden
3348 {
3349     OSL_ENSURE(ValidRow(nRow),"wrong row number");
3350 
3351     if (ValidRow(nRow) && mpRowHeights)
3352         return mpRowHeights->getValue(nRow);
3353     else
3354         return ScGlobal::nStdRowHeight;
3355 }
3356 
3357 //  Column/Row -Flags
3358 
3359 SCROW ScTable::GetHiddenRowCount( SCROW nRow ) const
3360 {
3361     if (!ValidRow(nRow))
3362         return 0;
3363 
3364     SCROW nLastRow = -1;
3365     if (!RowHidden(nRow, nullptr, &nLastRow) || !ValidRow(nLastRow))
3366         return 0;
3367 
3368     return nLastRow - nRow + 1;
3369 }
3370 
3371 //TODO: combine ShowRows / DBShowRows
3372 
3373 void ScTable::ShowCol(SCCOL nCol, bool bShow)
3374 {
3375     if (ValidCol(nCol))
3376     {
3377         bool bWasVis = !ColHidden(nCol);
3378         if (bWasVis != bShow)
3379         {
3380             SetColHidden(nCol, nCol, !bShow);
3381 
3382             ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection();
3383             if ( pCharts )
3384                 pCharts->SetRangeDirty(ScRange( nCol, 0, nTab, nCol, pDocument->MaxRow(), nTab ));
3385         }
3386     }
3387     else
3388     {
3389         OSL_FAIL("Invalid column number or no flags");
3390     }
3391 }
3392 
3393 void ScTable::ShowRow(SCROW nRow, bool bShow)
3394 {
3395     if (ValidRow(nRow) && pRowFlags)
3396     {
3397         bool bWasVis = !RowHidden(nRow);
3398         if (bWasVis != bShow)
3399         {
3400             SetRowHidden(nRow, nRow, !bShow);
3401             if (bShow)
3402                 SetRowFiltered(nRow, nRow, false);
3403             ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection();
3404             if ( pCharts )
3405                 pCharts->SetRangeDirty(ScRange( 0, nRow, nTab, pDocument->MaxCol(), nRow, nTab ));
3406 
3407             InvalidatePageBreaks();
3408         }
3409     }
3410     else
3411     {
3412         OSL_FAIL("Invalid row number or no flags");
3413     }
3414 }
3415 
3416 void ScTable::DBShowRow(SCROW nRow, bool bShow)
3417 {
3418     if (ValidRow(nRow) && pRowFlags)
3419     {
3420         //  Always set filter flag; unchanged when Hidden
3421         bool bChanged = SetRowHidden(nRow, nRow, !bShow);
3422         SetRowFiltered(nRow, nRow, !bShow);
3423 
3424         if (bChanged)
3425         {
3426             ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection();
3427             if ( pCharts )
3428                 pCharts->SetRangeDirty(ScRange( 0, nRow, nTab, pDocument->MaxCol(), nRow, nTab ));
3429 
3430             if (pOutlineTable)
3431                 UpdateOutlineRow( nRow, nRow, bShow );
3432 
3433             InvalidatePageBreaks();
3434         }
3435     }
3436     else
3437     {
3438         OSL_FAIL("Invalid row number or no flags");
3439     }
3440 }
3441 
3442 void ScTable::DBShowRows(SCROW nRow1, SCROW nRow2, bool bShow)
3443 {
3444     SCROW nStartRow = nRow1;
3445     while (nStartRow <= nRow2)
3446     {
3447         SCROW nEndRow = -1;
3448         bool bWasVis = !RowHiddenLeaf(nStartRow, nullptr, &nEndRow);
3449         if (nEndRow > nRow2)
3450             nEndRow = nRow2;
3451 
3452         bool bChanged = ( bWasVis != bShow );
3453 
3454         SetRowHidden(nStartRow, nEndRow, !bShow);
3455         SetRowFiltered(nStartRow, nEndRow, !bShow);
3456 
3457         if ( bChanged )
3458         {
3459             ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection();
3460             if ( pCharts )
3461                 pCharts->SetRangeDirty(ScRange( 0, nStartRow, nTab, pDocument->MaxCol(), nEndRow, nTab ));
3462         }
3463 
3464         nStartRow = nEndRow + 1;
3465     }
3466 
3467     //  #i12341# For Show/Hide rows, the outlines are updated separately from the outside.
3468     //  For filtering, the changes aren't visible to the caller, so UpdateOutlineRow has
3469     //  to be done here.
3470     if (pOutlineTable)
3471         UpdateOutlineRow( nRow1, nRow2, bShow );
3472 }
3473 
3474 void ScTable::ShowRows(SCROW nRow1, SCROW nRow2, bool bShow)
3475 {
3476     SCROW nStartRow = nRow1;
3477 
3478     // #i116164# if there are no drawing objects within the row range, a single HeightChanged call is enough
3479     ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer();
3480     bool bHasObjects = pDrawLayer && pDrawLayer->HasObjectsInRows( nTab, nRow1, nRow2 );
3481 
3482     while (nStartRow <= nRow2)
3483     {
3484         SCROW nEndRow = -1;
3485         bool bWasVis = !RowHiddenLeaf(nStartRow, nullptr, &nEndRow);
3486         if (nEndRow > nRow2)
3487             nEndRow = nRow2;
3488 
3489         bool bChanged = ( bWasVis != bShow );
3490 
3491         SetRowHidden(nStartRow, nEndRow, !bShow);
3492         if (bShow)
3493             SetRowFiltered(nStartRow, nEndRow, false);
3494 
3495         if ( bChanged )
3496         {
3497             ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection();
3498             if ( pCharts )
3499                 pCharts->SetRangeDirty(ScRange( 0, nStartRow, nTab, pDocument->MaxCol(), nEndRow, nTab ));
3500 
3501             InvalidatePageBreaks();
3502         }
3503 
3504         nStartRow = nEndRow + 1;
3505     }
3506 
3507     if ( !bHasObjects )
3508     {
3509         // #i116164# set the flags for the whole range at once
3510         SetRowHidden(nRow1, nRow2, !bShow);
3511         if (bShow)
3512             SetRowFiltered(nRow1, nRow2, false);
3513     }
3514 }
3515 
3516 bool ScTable::IsDataFiltered(SCCOL nColStart, SCROW nRowStart, SCCOL nColEnd, SCROW nRowEnd) const
3517 {
3518     for (SCROW i = nRowStart; i <= nRowEnd; ++i)
3519     {
3520         if (RowHidden(i))
3521             return true;
3522     }
3523     for (SCCOL i = nColStart; i <= nColEnd; ++i)
3524     {
3525         if (ColHidden(i))
3526             return true;
3527     }
3528     return false;
3529 }
3530 
3531 bool ScTable::IsDataFiltered(const ScRange& rRange) const
3532 {
3533     return IsDataFiltered(rRange.aStart.Col(), rRange.aStart.Row(),
3534                 rRange.aEnd.Col(), rRange.aEnd.Row());
3535 }
3536 
3537 void ScTable::SetRowFlags( SCROW nRow, CRFlags nNewFlags )
3538 {
3539     if (ValidRow(nRow) && pRowFlags)
3540         pRowFlags->SetValue( nRow, nNewFlags);
3541     else
3542     {
3543         OSL_FAIL("Invalid row number or no flags");
3544     }
3545 }
3546 
3547 void ScTable::SetRowFlags( SCROW nStartRow, SCROW nEndRow, CRFlags nNewFlags )
3548 {
3549     if (ValidRow(nStartRow) && ValidRow(nEndRow) && pRowFlags)
3550         pRowFlags->SetValue( nStartRow, nEndRow, nNewFlags);
3551     else
3552     {
3553         OSL_FAIL("Invalid row number(s) or no flags");
3554     }
3555 }
3556 
3557 CRFlags ScTable::GetColFlags( SCCOL nCol ) const
3558 {
3559     if (ValidCol(nCol) && mpColFlags)
3560         return mpColFlags->GetValue(nCol);
3561     else
3562         return CRFlags::NONE;
3563 }
3564 
3565 CRFlags ScTable::GetRowFlags( SCROW nRow ) const
3566 {
3567     if (ValidRow(nRow) && pRowFlags)
3568         return pRowFlags->GetValue(nRow);
3569     else
3570         return CRFlags::NONE;
3571 }
3572 
3573 SCROW ScTable::GetLastFlaggedRow() const
3574 {
3575     SCROW nLastFound = 0;
3576     if (pRowFlags)
3577     {
3578         SCROW nRow = pRowFlags->GetLastAnyBitAccess( CRFlags::All );
3579         if (ValidRow(nRow))
3580             nLastFound = nRow;
3581     }
3582 
3583     if (!maRowManualBreaks.empty())
3584         nLastFound = ::std::max(nLastFound, *maRowManualBreaks.rbegin());
3585 
3586     if (mpHiddenRows)
3587     {
3588         SCROW nRow = mpHiddenRows->findLastTrue();
3589         if (ValidRow(nRow))
3590             nLastFound = ::std::max(nLastFound, nRow);
3591     }
3592 
3593     if (mpFilteredRows)
3594     {
3595         SCROW nRow = mpFilteredRows->findLastTrue();
3596         if (ValidRow(nRow))
3597             nLastFound = ::std::max(nLastFound, nRow);
3598     }
3599 
3600     return nLastFound;
3601 }
3602 
3603 SCCOL ScTable::GetLastChangedCol() const
3604 {
3605     if ( !mpColFlags )
3606         return 0;
3607 
3608     SCCOL nLastFound = 0;
3609     const auto nColSize = aCol.size();
3610     auto colWidthIt = mpColWidth->begin() + 1;
3611     for (SCCOL nCol = 1; nCol < nColSize; (++nCol < nColSize) ? ++colWidthIt : (void)false)
3612         if ((mpColFlags->GetValue(nCol) & CRFlags::All) || (*colWidthIt != STD_COL_WIDTH))
3613             nLastFound = nCol;
3614 
3615     return nLastFound;
3616 }
3617 
3618 SCROW ScTable::GetLastChangedRow() const
3619 {
3620     if ( !pRowFlags )
3621         return 0;
3622 
3623     SCROW nLastFlags = GetLastFlaggedRow();
3624 
3625     // Find the last row position where the height is NOT the standard row
3626     // height.
3627     // KOHEI: Test this to make sure it does what it's supposed to.
3628     SCROW nLastHeight = mpRowHeights->findLastTrue(ScGlobal::nStdRowHeight);
3629     if (!ValidRow(nLastHeight))
3630         nLastHeight = 0;
3631 
3632     return std::max( nLastFlags, nLastHeight);
3633 }
3634 
3635 bool ScTable::UpdateOutlineCol( SCCOL nStartCol, SCCOL nEndCol, bool bShow )
3636 {
3637     if (pOutlineTable && mpColFlags)
3638     {
3639         return pOutlineTable->GetColArray().ManualAction( nStartCol, nEndCol, bShow, *this, true );
3640     }
3641     else
3642         return false;
3643 }
3644 
3645 bool ScTable::UpdateOutlineRow( SCROW nStartRow, SCROW nEndRow, bool bShow )
3646 {
3647     if (pOutlineTable && pRowFlags)
3648         return pOutlineTable->GetRowArray().ManualAction( nStartRow, nEndRow, bShow, *this, false );
3649     else
3650         return false;
3651 }
3652 
3653 void ScTable::ExtendHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 )
3654 {
3655     // Column-wise expansion
3656 
3657     while (rX1 > 0 && ColHidden(rX1-1))
3658         --rX1;
3659 
3660     while (rX2 < pDocument->MaxCol() && ColHidden(rX2+1))
3661         ++rX2;
3662 
3663     // Row-wise expansion
3664 
3665     if (rY1 > 0)
3666     {
3667         ScFlatBoolRowSegments::RangeData aData;
3668         if (mpHiddenRows->getRangeData(rY1-1, aData) && aData.mbValue)
3669         {
3670             SCROW nStartRow = aData.mnRow1;
3671             if (ValidRow(nStartRow))
3672                 rY1 = nStartRow;
3673         }
3674     }
3675     if (rY2 < pDocument->MaxRow())
3676     {
3677         SCROW nEndRow = -1;
3678         if (RowHidden(rY2+1, nullptr, &nEndRow) && ValidRow(nEndRow))
3679             rY2 = nEndRow;
3680     }
3681 }
3682 
3683 void ScTable::StripHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 )
3684 {
3685     while ( rX2>rX1 && ColHidden(rX2) )
3686         --rX2;
3687     while ( rX2>rX1 && ColHidden(rX1) )
3688         ++rX1;
3689 
3690     if (rY1 < rY2)
3691     {
3692         ScFlatBoolRowSegments::RangeData aData;
3693         if (mpHiddenRows->getRangeData(rY2, aData) && aData.mbValue)
3694         {
3695             SCROW nStartRow = aData.mnRow1;
3696             if (ValidRow(nStartRow) && nStartRow >= rY1)
3697                 rY2 = nStartRow;
3698         }
3699     }
3700 
3701     if (rY1 < rY2)
3702     {
3703         SCROW nEndRow = -1;
3704         if (RowHidden(rY1, nullptr, &nEndRow) && ValidRow(nEndRow) && nEndRow <= rY2)
3705             rY1 = nEndRow;
3706     }
3707 }
3708 
3709 //  Auto-Outline
3710 
3711 template< typename T >
3712 static short DiffSign( T a, T b )
3713 {
3714     return (a<b) ? -1 :
3715             (a>b) ? 1 : 0;
3716 }
3717 
3718 namespace {
3719 
3720 class OutlineArrayFinder
3721 {
3722     ScRange maRef;
3723     SCCOL mnCol;
3724     SCTAB mnTab;
3725     ScOutlineArray* mpArray;
3726     bool mbSizeChanged;
3727 
3728 public:
3729     OutlineArrayFinder(const ScRange& rRef, SCCOL nCol, SCTAB nTab, ScOutlineArray* pArray, bool bSizeChanged) :
3730         maRef(rRef), mnCol(nCol), mnTab(nTab), mpArray(pArray),
3731         mbSizeChanged(bSizeChanged) {}
3732 
3733     bool operator() (size_t nRow, const ScFormulaCell* pCell)
3734     {
3735         SCROW nRow2 = static_cast<SCROW>(nRow);
3736 
3737         if (!pCell->HasRefListExpressibleAsOneReference(maRef))
3738             return false;
3739 
3740         if (maRef.aStart.Row() != nRow2 || maRef.aEnd.Row() != nRow2 ||
3741             maRef.aStart.Tab() != mnTab || maRef.aEnd.Tab() != mnTab)
3742             return false;
3743 
3744         if (DiffSign(maRef.aStart.Col(), mnCol) != DiffSign(maRef.aEnd.Col(), mnCol))
3745             return false;
3746 
3747         return mpArray->Insert(maRef.aStart.Col(), maRef.aEnd.Col(), mbSizeChanged);
3748     }
3749 };
3750 
3751 }
3752 
3753 void ScTable::DoAutoOutline( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow )
3754 {
3755     typedef mdds::flat_segment_tree<SCROW, bool> UsedRowsType;
3756 
3757     bool bSizeChanged = false;
3758 
3759     SCCOL nCol;
3760     SCROW nRow;
3761     bool bFound;
3762     ScRange aRef;
3763 
3764     nEndCol = ClampToAllocatedColumns(nEndCol);
3765 
3766     StartOutlineTable();
3767 
3768                             // Rows
3769 
3770     UsedRowsType aUsed(0, pDocument->MaxRow()+1, false);
3771     for (nCol=nStartCol; nCol<=nEndCol; nCol++)
3772         aCol[nCol].FindUsed(nStartRow, nEndRow, aUsed);
3773     aUsed.build_tree();
3774 
3775     ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
3776     for (nRow=nStartRow; nRow<=nEndRow; nRow++)
3777     {
3778         bool bUsed = false;
3779         SCROW nLastRow = nRow;
3780         aUsed.search_tree(nRow, bUsed, nullptr, &nLastRow);
3781         if (!bUsed)
3782         {
3783             nRow = nLastRow;
3784             continue;
3785         }
3786 
3787         bFound = false;
3788         for (nCol=nStartCol; nCol<=nEndCol && !bFound; nCol++)
3789         {
3790             ScRefCellValue aCell = aCol[nCol].GetCellValue(nRow);
3791 
3792             if (aCell.meType != CELLTYPE_FORMULA)
3793                 continue;
3794 
3795             if (!aCell.mpFormula->HasRefListExpressibleAsOneReference(aRef))
3796                 continue;
3797 
3798             if ( aRef.aStart.Col() == nCol && aRef.aEnd.Col() == nCol &&
3799                  aRef.aStart.Tab() == nTab && aRef.aEnd.Tab() == nTab &&
3800                  DiffSign( aRef.aStart.Row(), nRow ) ==
3801                     DiffSign( aRef.aEnd.Row(), nRow ) )
3802             {
3803                 if (rRowArray.Insert( aRef.aStart.Row(), aRef.aEnd.Row(), bSizeChanged ))
3804                 {
3805                     bFound = true;
3806                 }
3807             }
3808         }
3809     }
3810 
3811     // Column
3812     ScOutlineArray& rColArray = pOutlineTable->GetColArray();
3813     for (nCol=nStartCol; nCol<=nEndCol; nCol++)
3814     {
3815         if (aCol[nCol].IsEmptyData())
3816             continue;
3817 
3818         OutlineArrayFinder aFunc(aRef, nCol, nTab, &rColArray, bSizeChanged);
3819         sc::FindFormula(aCol[nCol].maCells, nStartRow, nEndRow, aFunc);
3820     }
3821 }
3822 
3823                                     //  CopyData - for Query in other range
3824 
3825 void ScTable::CopyData( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
3826                             SCCOL nDestCol, SCROW nDestRow, SCTAB nDestTab )
3827 {
3828     //TODO: if used for multiple rows, optimize after columns!
3829 
3830     ScAddress aSrc( nStartCol, nStartRow, nTab );
3831     ScAddress aDest( nDestCol, nDestRow, nDestTab );
3832     ScRange aRange( aSrc, aDest );
3833     bool bThisTab = ( nDestTab == nTab );
3834     SCROW nDestY = nDestRow;
3835     for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
3836     {
3837         aSrc.SetRow( nRow );
3838         aDest.SetRow( nDestY );
3839         SCCOL nDestX = nDestCol;
3840         for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
3841         {
3842             aSrc.SetCol( nCol );
3843             aDest.SetCol( nDestX );
3844             ScCellValue aCell;
3845             aCell.assign(*pDocument, ScAddress(nCol, nRow, nTab));
3846 
3847             if (aCell.meType == CELLTYPE_FORMULA)
3848             {
3849                 sc::RefUpdateContext aCxt(*pDocument);
3850                 aCxt.meMode = URM_COPY;
3851                 aCxt.maRange = aRange;
3852                 aCxt.mnColDelta = nDestCol - nStartCol;
3853                 aCxt.mnRowDelta = nDestRow - nStartRow;
3854                 aCxt.mnTabDelta = nDestTab - nTab;
3855                 aCell.mpFormula->UpdateReference(aCxt);
3856                 aCell.mpFormula->aPos = aDest;
3857             }
3858 
3859             if (bThisTab)
3860             {
3861                 aCell.release(aCol[nDestX], nDestY);
3862                 SetPattern( nDestX, nDestY, *GetPattern( nCol, nRow ) );
3863             }
3864             else
3865             {
3866                 aCell.release(*pDocument, aDest);
3867                 pDocument->SetPattern( aDest, *GetPattern( nCol, nRow ) );
3868             }
3869 
3870             ++nDestX;
3871         }
3872         ++nDestY;
3873     }
3874 }
3875 
3876 bool ScTable::RefVisible(const ScFormulaCell* pCell)
3877 {
3878     ScRange aRef;
3879 
3880     if (pCell->HasOneReference(aRef))
3881     {
3882         if (aRef.aStart.Col()==aRef.aEnd.Col() && aRef.aStart.Tab()==aRef.aEnd.Tab())
3883         {
3884             SCROW nEndRow;
3885             if (!RowFiltered(aRef.aStart.Row(), nullptr, &nEndRow))
3886                 // row not filtered.
3887                 nEndRow = ::std::numeric_limits<SCROW>::max();
3888 
3889             if (!ValidRow(nEndRow) || nEndRow < aRef.aEnd.Row())
3890                 return true;    // at least partly visible
3891             return false;       // completely invisible
3892         }
3893     }
3894 
3895     return true;                        // somehow different
3896 }
3897 
3898 void ScTable::GetUpperCellString(SCCOL nCol, SCROW nRow, OUString& rStr)
3899 {
3900     GetInputString(nCol, nRow, rStr);
3901     rStr = ScGlobal::getCharClassPtr()->uppercase(rStr.trim());
3902 }
3903 
3904 // Calculate the size of the sheet and set the size on DrawPage
3905 
3906 void ScTable::SetDrawPageSize(bool bResetStreamValid, bool bUpdateNoteCaptionPos)
3907 {
3908     ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer();
3909     if( pDrawLayer )
3910     {
3911         double fValX = GetColOffset( pDocument->MaxCol() + 1 ) * HMM_PER_TWIPS;
3912         double fValY = GetRowOffset( pDocument->MaxRow() + 1 ) * HMM_PER_TWIPS;
3913         const long nMax = ::std::numeric_limits<long>::max();
3914         // #i113884# Avoid int32 overflow with possible negative results than can cause bad effects.
3915         // If the draw page size is smaller than all rows, only the bottom of the sheet is affected.
3916         long x = ( fValX > static_cast<double>(nMax) ) ? nMax : static_cast<long>(fValX);
3917         long y = ( fValY > static_cast<double>(nMax) ) ? nMax : static_cast<long>(fValY);
3918 
3919         if ( IsLayoutRTL() )        // IsNegativePage
3920             x = -x;
3921 
3922         pDrawLayer->SetPageSize( static_cast<sal_uInt16>(nTab), Size( x, y ), bUpdateNoteCaptionPos );
3923     }
3924 
3925     // #i102616# actions that modify the draw page size count as sheet modification
3926     // (exception: InitDrawLayer)
3927     if (bResetStreamValid)
3928         SetStreamValid(false);
3929 }
3930 
3931 void ScTable::SetRangeName(std::unique_ptr<ScRangeName> pNew)
3932 {
3933     mpRangeName = std::move(pNew);
3934 
3935     //fdo#39792: mark stream as invalid, otherwise new ScRangeName will not be written to file
3936     SetStreamValid(false);
3937 }
3938 
3939 ScRangeName* ScTable::GetRangeName() const
3940 {
3941     if (!mpRangeName)
3942         mpRangeName.reset(new ScRangeName);
3943     return mpRangeName.get();
3944 }
3945 
3946 sal_uLong ScTable::GetRowOffset( SCROW nRow, bool bHiddenAsZero ) const
3947 {
3948     sal_uLong n = 0;
3949     if ( mpHiddenRows && mpRowHeights )
3950     {
3951         if (nRow == 0)
3952             return 0;
3953         else if (nRow == 1)
3954             return GetRowHeight(0, nullptr, nullptr, bHiddenAsZero );
3955 
3956         n = GetTotalRowHeight(0, nRow-1, bHiddenAsZero);
3957 #if OSL_DEBUG_LEVEL > 0
3958         if (n == ::std::numeric_limits<unsigned long>::max())
3959             OSL_FAIL("ScTable::GetRowOffset: row heights overflow");
3960 #endif
3961     }
3962     else
3963     {
3964         OSL_FAIL("GetRowOffset: Data missing");
3965     }
3966     return n;
3967 }
3968 
3969 SCROW ScTable::GetRowForHeight(sal_uLong nHeight) const
3970 {
3971     sal_uLong nSum = 0;
3972 
3973     ScFlatBoolRowSegments::RangeData aData;
3974 
3975     ScFlatUInt16RowSegments::RangeData aRowHeightRange;
3976     aRowHeightRange.mnRow2 = -1;
3977     aRowHeightRange.mnValue = 0; // silence MSVC C4701
3978 
3979     for (SCROW nRow = 0; nRow <= pDocument->MaxRow(); ++nRow)
3980     {
3981         if (!mpHiddenRows->getRangeData(nRow, aData))
3982             // Failed to fetch the range data for whatever reason.
3983             break;
3984 
3985         if (aData.mbValue)
3986         {
3987             // This row is hidden.  Skip ahead all hidden rows.
3988             nRow = aData.mnRow2;
3989             continue;
3990         }
3991 
3992         if (aRowHeightRange.mnRow2 < nRow)
3993         {
3994             if (!mpRowHeights->getRangeData(nRow, aRowHeightRange))
3995                 // Failed to fetch the range data for whatever reason.
3996                 break;
3997         }
3998 
3999         nSum += aRowHeightRange.mnValue;
4000 
4001         if (nSum > nHeight)
4002         {
4003             if (nRow >= pDocument->MaxRow())
4004                 return pDocument->MaxRow();
4005 
4006             // Find the next visible row.
4007             ++nRow;
4008 
4009             if (!mpHiddenRows->getRangeData(nRow, aData))
4010                 // Failed to fetch the range data for whatever reason.
4011                 break;
4012 
4013             if (aData.mbValue)
4014                 // These rows are hidden.
4015                 nRow = aData.mnRow2 + 1;
4016 
4017             return nRow <= pDocument->MaxRow() ? nRow : pDocument->MaxRow();
4018         }
4019     }
4020     return -1;
4021 }
4022 
4023 sal_uLong ScTable::GetColOffset( SCCOL nCol, bool bHiddenAsZero ) const
4024 {
4025     sal_uLong n = 0;
4026     if ( mpColWidth )
4027     {
4028         auto colWidthIt = mpColWidth->begin();
4029         for (SCCOL i = 0; i < nCol; (++i < nCol) ? ++colWidthIt : (void)false)
4030             if (!( bHiddenAsZero && ColHidden(i) ))
4031                 n += *colWidthIt;
4032     }
4033     else
4034     {
4035         OSL_FAIL("GetColumnOffset: Data missing");
4036     }
4037     return n;
4038 }
4039 
4040 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4041