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 
10 #include <table.hxx>
11 #include <clipcontext.hxx>
12 #include <document.hxx>
13 #include <clipparam.hxx>
14 #include <segmenttree.hxx>
15 #include <sharedformula.hxx>
16 #include <cellvalues.hxx>
17 #include <olinetab.hxx>
18 #include <tabprotection.hxx>
19 #include <columniterator.hxx>
20 #include <drwlayer.hxx>
21 #include <compressedarray.hxx>
22 
23 #include <osl/diagnose.h>
24 #include <sal/log.hxx>
25 #include <tools/stream.hxx>
26 
IsMerged(SCCOL nCol,SCROW nRow) const27 bool ScTable::IsMerged( SCCOL nCol, SCROW nRow ) const
28 {
29     if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount() )
30         return false;
31 
32     return aCol[nCol].IsMerged(nRow);
33 }
34 
HasMultipleDataCells(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2) const35 sc::MultiDataCellState ScTable::HasMultipleDataCells( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const
36 {
37     if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
38         return sc::MultiDataCellState();
39 
40     if (nCol1 > nCol2 || nRow1 > nRow2)
41         // invalid range.
42         return sc::MultiDataCellState();
43 
44     if (aCol.empty())
45         return sc::MultiDataCellState(sc::MultiDataCellState::Empty);
46 
47     auto setFirstCell = []( sc::MultiDataCellState& rRet, SCCOL nCurCol, SCROW nCurRow )
48     {
49         if (rRet.mnCol1 < 0)
50         {
51             // First cell not yet set.  Set it.
52             rRet.mnCol1 = nCurCol;
53             rRet.mnRow1 = nCurRow;
54         }
55     };
56 
57     SCCOL nMaxCol = aCol.size()-1;
58     bool bHasOne = false;
59     sc::MultiDataCellState aRet(sc::MultiDataCellState::Empty);
60 
61     for (SCCOL nCol = nCol1; nCol <= nCol2 && nCol <= nMaxCol; ++nCol)
62     {
63         SCROW nFirstDataRow = -1;
64         switch (aCol[nCol].HasDataCellsInRange(nRow1, nRow2, &nFirstDataRow))
65         {
66             case sc::MultiDataCellState::HasOneCell:
67             {
68                 setFirstCell(aRet, nCol, nFirstDataRow);
69 
70                 if (bHasOne)
71                 {
72                     // We've already found one data cell in another column.
73                     aRet.meState = sc::MultiDataCellState::HasMultipleCells;
74                     return aRet;
75                 }
76                 bHasOne = true;
77                 break;
78             }
79             case sc::MultiDataCellState::HasMultipleCells:
80             {
81                 setFirstCell(aRet, nCol, nFirstDataRow);
82 
83                 aRet.meState = sc::MultiDataCellState::HasMultipleCells;
84                 return aRet;
85             }
86             case sc::MultiDataCellState::Empty:
87             default:
88                 ;
89         }
90     }
91 
92     if (bHasOne)
93         aRet.meState = sc::MultiDataCellState::HasOneCell;
94 
95     return aRet;
96 }
97 
DeleteBeforeCopyFromClip(sc::CopyFromClipContext & rCxt,const ScTable & rClipTab,sc::ColumnSpanSet & rBroadcastSpans)98 void ScTable::DeleteBeforeCopyFromClip(
99     sc::CopyFromClipContext& rCxt, const ScTable& rClipTab, sc::ColumnSpanSet& rBroadcastSpans )
100 {
101     sc::CopyFromClipContext::Range aRange = rCxt.getDestRange();
102     if (!ValidCol(aRange.mnCol1) || !ValidCol(aRange.mnCol2))
103         return;
104 
105     // Pass some stuff to the columns via context.
106     rCxt.setTableProtected(IsProtected());
107     rCxt.setCondFormatList(mpCondFormatList.get());
108 
109     ScRange aClipRange = rCxt.getClipDoc()->GetClipParam().getWholeRange();
110     SCCOL nClipCol = aClipRange.aStart.Col();
111     {
112         const SCCOL nMaxCol2 = std::min<SCCOL>( aRange.mnCol2, aCol.size() - 1 );
113         for (SCCOL nCol = aRange.mnCol1; nCol <= nMaxCol2; ++nCol, ++nClipCol)
114         {
115             if (nClipCol > aClipRange.aEnd.Col())
116                 nClipCol = aClipRange.aStart.Col(); // loop through columns.
117 
118             const ScColumn& rClipCol = rClipTab.aCol[nClipCol];
119             aCol[nCol].DeleteBeforeCopyFromClip(rCxt, rClipCol, rBroadcastSpans);
120         }
121     }
122 
123     SetStreamValid(false);
124 }
125 
CopyOneCellFromClip(sc::CopyFromClipContext & rCxt,const SCCOL nCol1,const SCROW nRow1,const SCCOL nCol2,const SCROW nRow2,const SCROW nSrcRow,const ScTable * pSrcTab)126 void ScTable::CopyOneCellFromClip(
127     sc::CopyFromClipContext& rCxt, const SCCOL nCol1, const SCROW nRow1, const SCCOL nCol2, const SCROW nRow2, const SCROW nSrcRow, const ScTable* pSrcTab )
128 {
129     ScRange aSrcRange = rCxt.getClipDoc()->GetClipParam().getWholeRange();
130     SCCOL nSrcColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1;
131 
132     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
133     {
134         SCCOL nColOffset = nCol - nCol1;
135         nColOffset = nColOffset % nSrcColSize;
136         assert(nColOffset >= 0);
137         CreateColumnIfNotExists(nCol).CopyOneCellFromClip(rCxt, nRow1, nRow2, nColOffset);
138 
139         if (rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB)
140         {
141             for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
142                 CopyConditionalFormat(nCol, nRow, nCol, nRow, nCol - aSrcRange.aStart.Col() - nColOffset,
143                         nRow - nSrcRow, pSrcTab);
144         }
145     }
146 
147     if (nCol1 == 0 && nCol2 == rDocument.MaxCol() && mpRowHeights)
148     {
149         mpRowHeights->setValue(nRow1, nRow2, pSrcTab->GetOriginalHeight(nSrcRow));
150 
151         if (pRowFlags && pSrcTab->pRowFlags) {
152            if (pSrcTab->pRowFlags->GetValue(nSrcRow) & CRFlags::ManualSize)
153                pRowFlags->OrValue(nRow1, CRFlags::ManualSize);
154            else
155                pRowFlags->AndValue(nRow1, ~CRFlags::ManualSize);
156         }
157     }
158 
159     // Copy graphics over too
160     bool bCopyGraphics
161         = (rCxt.getInsertFlag() & InsertDeleteFlags::OBJECTS) != InsertDeleteFlags::NONE;
162     if (!(bCopyGraphics && rCxt.getClipDoc()->mpDrawLayer))
163         return;
164 
165     ScDrawLayer* pDrawLayer = GetDoc().GetDrawLayer();
166     OSL_ENSURE(pDrawLayer, "No drawing layer");
167     if (pDrawLayer)
168     {
169         const ScAddress aSrcStartPos
170             = rCxt.getClipDoc()->GetClipParam().getWholeRange().aStart;
171         const ScAddress aSrcEndPos = rCxt.getClipDoc()->GetClipParam().getWholeRange().aEnd;
172         tools::Rectangle aSourceRect = rCxt.getClipDoc()->GetMMRect(
173             aSrcStartPos.Col(), aSrcStartPos.Row(), aSrcEndPos.Col(), aSrcEndPos.Row(),
174             aSrcStartPos.Tab());
175         tools::Rectangle aDestRect = GetDoc().GetMMRect(nCol1, nRow1, nCol2, nRow2, nTab);
176         pDrawLayer->CopyFromClip(rCxt.getClipDoc()->mpDrawLayer.get(), aSrcStartPos.Tab(),
177                                  aSourceRect, ScAddress(nCol1, nRow1, nTab), aDestRect);
178     }
179 }
180 
SetValues(const SCCOL nCol,const SCROW nRow,const std::vector<double> & rVals)181 void ScTable::SetValues( const SCCOL nCol, const SCROW nRow, const std::vector<double>& rVals )
182 {
183     if (!ValidCol(nCol))
184         return;
185 
186     CreateColumnIfNotExists(nCol).SetValues(nRow, rVals);
187 }
188 
TransferCellValuesTo(const SCCOL nCol,SCROW nRow,size_t nLen,sc::CellValues & rDest)189 void ScTable::TransferCellValuesTo( const SCCOL nCol, SCROW nRow, size_t nLen, sc::CellValues& rDest )
190 {
191     if (!ValidCol(nCol))
192         return;
193 
194     CreateColumnIfNotExists(nCol).TransferCellValuesTo(nRow, nLen, rDest);
195 }
196 
CopyCellValuesFrom(const SCCOL nCol,SCROW nRow,const sc::CellValues & rSrc)197 void ScTable::CopyCellValuesFrom( const SCCOL nCol, SCROW nRow, const sc::CellValues& rSrc )
198 {
199     if (!ValidCol(nCol))
200         return;
201 
202     CreateColumnIfNotExists(nCol).CopyCellValuesFrom(nRow, rSrc);
203 }
204 
ConvertFormulaToValue(sc::EndListeningContext & rCxt,const SCCOL nCol1,const SCROW nRow1,const SCCOL nCol2,const SCROW nRow2,sc::TableValues * pUndo)205 void ScTable::ConvertFormulaToValue(
206     sc::EndListeningContext& rCxt, const SCCOL nCol1, const SCROW nRow1, const SCCOL nCol2, const SCROW nRow2,
207     sc::TableValues* pUndo )
208 {
209     if (!ValidCol(nCol1) || !ValidCol(nCol2) || nCol1 > nCol2)
210         return;
211 
212     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
213          CreateColumnIfNotExists(nCol).ConvertFormulaToValue(rCxt, nRow1, nRow2, pUndo);
214 }
215 
SwapNonEmpty(sc::TableValues & rValues,sc::StartListeningContext & rStartCxt,sc::EndListeningContext & rEndCxt)216 void ScTable::SwapNonEmpty(
217     sc::TableValues& rValues, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt )
218 {
219     const ScRange& rRange = rValues.getRange();
220     assert(rRange.IsValid());
221     for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
222         CreateColumnIfNotExists(nCol).SwapNonEmpty(rValues, rStartCxt, rEndCxt);
223 }
224 
PreprocessRangeNameUpdate(sc::EndListeningContext & rEndListenCxt,sc::CompileFormulaContext & rCompileCxt)225 void ScTable::PreprocessRangeNameUpdate(
226     sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
227 {
228     for (SCCOL i = 0; i < aCol.size(); ++i)
229         aCol[i].PreprocessRangeNameUpdate(rEndListenCxt, rCompileCxt);
230 }
231 
PreprocessDBDataUpdate(sc::EndListeningContext & rEndListenCxt,sc::CompileFormulaContext & rCompileCxt)232 void ScTable::PreprocessDBDataUpdate(
233     sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
234 {
235     for (SCCOL i = 0; i < aCol.size(); ++i)
236         aCol[i].PreprocessDBDataUpdate(rEndListenCxt, rCompileCxt);
237 }
238 
CompileHybridFormula(sc::StartListeningContext & rStartListenCxt,sc::CompileFormulaContext & rCompileCxt)239 void ScTable::CompileHybridFormula(
240     sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt )
241 {
242     for (SCCOL i = 0; i < aCol.size(); ++i)
243         aCol[i].CompileHybridFormula(rStartListenCxt, rCompileCxt);
244 }
245 
UpdateScriptTypes(const SCCOL nCol1,SCROW nRow1,const SCCOL nCol2,SCROW nRow2)246 void ScTable::UpdateScriptTypes( const SCCOL nCol1, SCROW nRow1, const SCCOL nCol2, SCROW nRow2 )
247 {
248     if (!IsColValid(nCol1) || !ValidCol(nCol2) || nCol1 > nCol2)
249         return;
250 
251     const SCCOL nMaxCol2 = std::min<SCCOL>( nCol2, aCol.size() - 1 );
252 
253     for (SCCOL nCol = nCol1; nCol <= nMaxCol2; ++nCol)
254         aCol[nCol].UpdateScriptTypes(nRow1, nRow2);
255 }
256 
HasUniformRowHeight(SCROW nRow1,SCROW nRow2) const257 bool ScTable::HasUniformRowHeight( SCROW nRow1, SCROW nRow2 ) const
258 {
259     if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow1 > nRow2)
260         return false;
261 
262     ScFlatUInt16RowSegments::RangeData aData;
263     if (!mpRowHeights->getRangeData(nRow1, aData))
264         // Search failed.
265         return false;
266 
267     return nRow2 <= aData.mnRow2;
268 }
269 
SplitFormulaGroups(SCCOL nCol,std::vector<SCROW> & rRows)270 void ScTable::SplitFormulaGroups( SCCOL nCol, std::vector<SCROW>& rRows )
271 {
272     if (!IsColValid(nCol))
273         return;
274 
275     sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), aCol[nCol].maCells, rRows);
276 }
277 
UnshareFormulaCells(SCCOL nCol,std::vector<SCROW> & rRows)278 void ScTable::UnshareFormulaCells( SCCOL nCol, std::vector<SCROW>& rRows )
279 {
280     if (!IsColValid(nCol))
281         return;
282 
283     sc::SharedFormulaUtil::unshareFormulaCells(rDocument, aCol[nCol].maCells, rRows);
284 }
285 
RegroupFormulaCells(SCCOL nCol)286 void ScTable::RegroupFormulaCells( SCCOL nCol )
287 {
288     if (!IsColValid(nCol))
289         return;
290 
291     aCol[nCol].RegroupFormulaCells();
292 }
293 
HasFormulaCell(const SCCOL nCol1,SCROW nRow1,const SCCOL nCol2,SCROW nRow2) const294 bool ScTable::HasFormulaCell( const SCCOL nCol1, SCROW nRow1, const SCCOL nCol2, SCROW nRow2 ) const
295 {
296     if (nCol2 < nCol1 || !IsColValid(nCol1) || !ValidCol(nCol2))
297         return false;
298 
299     const SCCOL nMaxCol2 = std::min<SCCOL>( nCol2, aCol.size() - 1 );
300 
301     for (SCCOL nCol = nCol1; nCol <= nMaxCol2; ++nCol)
302         if (aCol[nCol].HasFormulaCell(nRow1, nRow2))
303             return true;
304 
305     return false;
306 }
307 
EndListeningIntersectedGroup(sc::EndListeningContext & rCxt,SCCOL nCol,SCROW nRow,std::vector<ScAddress> * pGroupPos)308 void ScTable::EndListeningIntersectedGroup(
309     sc::EndListeningContext& rCxt, SCCOL nCol, SCROW nRow, std::vector<ScAddress>* pGroupPos )
310 {
311     if (!IsColValid(nCol))
312         return;
313 
314     aCol[nCol].EndListeningIntersectedGroup(rCxt, nRow, pGroupPos);
315 }
316 
EndListeningIntersectedGroups(sc::EndListeningContext & rCxt,const SCCOL nCol1,SCROW nRow1,const SCCOL nCol2,SCROW nRow2,std::vector<ScAddress> * pGroupPos)317 void ScTable::EndListeningIntersectedGroups(
318     sc::EndListeningContext& rCxt, const SCCOL nCol1, SCROW nRow1, const SCCOL nCol2, SCROW nRow2,
319     std::vector<ScAddress>* pGroupPos )
320 {
321     if (nCol2 < nCol1 || !IsColValid(nCol1) || !ValidCol(nCol2))
322         return;
323 
324     for (SCCOL nCol : GetColumnsRange(nCol1, nCol2))
325         aCol[nCol].EndListeningIntersectedGroups(rCxt, nRow1, nRow2, pGroupPos);
326 }
327 
EndListeningGroup(sc::EndListeningContext & rCxt,const SCCOL nCol,SCROW nRow)328 void ScTable::EndListeningGroup( sc::EndListeningContext& rCxt, const SCCOL nCol, SCROW nRow )
329 {
330     if (!IsColValid(nCol))
331         return;
332 
333     aCol[nCol].EndListeningGroup(rCxt, nRow);
334 }
335 
SetNeedsListeningGroup(SCCOL nCol,SCROW nRow)336 void ScTable::SetNeedsListeningGroup( SCCOL nCol, SCROW nRow )
337 {
338     if (!ValidCol(nCol))
339         return;
340 
341     CreateColumnIfNotExists(nCol).SetNeedsListeningGroup(nRow);
342 }
343 
IsEditActionAllowed(sc::ColRowEditAction eAction,SCCOLROW nStart,SCCOLROW nEnd) const344 bool ScTable::IsEditActionAllowed(
345     sc::ColRowEditAction eAction, SCCOLROW nStart, SCCOLROW nEnd ) const
346 {
347     if (!IsProtected())
348     {
349         SCCOL nCol1 = 0, nCol2 = aCol.size() - 1;
350         SCROW nRow1 = 0, nRow2 = rDocument.MaxRow();
351 
352         switch (eAction)
353         {
354             case sc::ColRowEditAction::InsertColumnsBefore:
355             case sc::ColRowEditAction::InsertColumnsAfter:
356             case sc::ColRowEditAction::DeleteColumns:
357             {
358                 nCol1 = nStart;
359                 nCol2 = nEnd;
360                 break;
361             }
362             case sc::ColRowEditAction::InsertRowsBefore:
363             case sc::ColRowEditAction::InsertRowsAfter:
364             case sc::ColRowEditAction::DeleteRows:
365             {
366                 nRow1 = nStart;
367                 nRow2 = nEnd;
368                 break;
369             }
370             default:
371                 ;
372         }
373 
374         return IsBlockEditable(nCol1, nRow1, nCol2, nRow2, nullptr);
375     }
376 
377     if (IsScenario())
378         // TODO: I don't even know what this scenario thingie is. Perhaps we
379         // should check it against the scenario ranges?
380         return false;
381 
382     assert(pTabProtection);
383 
384     switch (eAction)
385     {
386         case sc::ColRowEditAction::InsertColumnsBefore:
387         case sc::ColRowEditAction::InsertColumnsAfter:
388         {
389             // TODO: improve the matrix range handling for the insert-before action.
390             if (HasBlockMatrixFragment(nStart, 0, nEnd, rDocument.MaxRow()))
391                 return false;
392 
393             return pTabProtection->isOptionEnabled(ScTableProtection::INSERT_COLUMNS);
394         }
395         case sc::ColRowEditAction::InsertRowsBefore:
396         case sc::ColRowEditAction::InsertRowsAfter:
397         {
398             // TODO: improve the matrix range handling for the insert-before action.
399             if (HasBlockMatrixFragment(0, nStart, rDocument.MaxCol(), nEnd))
400                 return false;
401 
402             return pTabProtection->isOptionEnabled(ScTableProtection::INSERT_ROWS);
403         }
404         case sc::ColRowEditAction::DeleteColumns:
405         {
406             if (!pTabProtection->isOptionEnabled(ScTableProtection::DELETE_COLUMNS))
407                 return false;
408 
409             return !HasAttrib(nStart, 0, nEnd, rDocument.MaxRow(), HasAttrFlags::Protected);
410         }
411         case sc::ColRowEditAction::DeleteRows:
412         {
413             if (!pTabProtection->isOptionEnabled(ScTableProtection::DELETE_ROWS))
414                 return false;
415 
416             return !HasAttrib(0, nStart, rDocument.MaxCol(), nEnd, HasAttrFlags::Protected);
417         }
418         default:
419             ;
420     }
421 
422     return false;
423 }
424 
GetColumnIterator(SCCOL nCol,SCROW nRow1,SCROW nRow2) const425 std::unique_ptr<sc::ColumnIterator> ScTable::GetColumnIterator( SCCOL nCol, SCROW nRow1, SCROW nRow2 ) const
426 {
427     if (!ValidCol(nCol))
428         return std::unique_ptr<sc::ColumnIterator>();
429 
430     return CreateColumnIfNotExists(nCol).GetColumnIterator(nRow1, nRow2);
431 }
432 
EnsureFormulaCellResults(const SCCOL nCol1,SCROW nRow1,const SCCOL nCol2,SCROW nRow2,bool bSkipRunning)433 bool ScTable::EnsureFormulaCellResults( const SCCOL nCol1, SCROW nRow1, const SCCOL nCol2, SCROW nRow2, bool bSkipRunning )
434 {
435     if (nCol2 < nCol1 || !IsColValid(nCol1) || !ValidCol(nCol2))
436         return false;
437 
438     const SCCOL nMaxCol2 = std::min<SCCOL>( nCol2, aCol.size() - 1 );
439 
440     bool bAnyDirty = false;
441 
442     for (SCCOL nCol = nCol1; nCol <= nMaxCol2; ++nCol)
443     {
444         bool bRet = aCol[nCol].EnsureFormulaCellResults(nRow1, nRow2, bSkipRunning);
445         bAnyDirty = bAnyDirty || bRet;
446     }
447 
448     return bAnyDirty;
449 }
450 
finalizeOutlineImport()451 void ScTable::finalizeOutlineImport()
452 {
453     if (pOutlineTable && pRowFlags)
454     {
455         pOutlineTable->GetRowArray().finalizeImport(*this);
456     }
457 }
458 
StoreToCache(SvStream & rStrm) const459 void ScTable::StoreToCache(SvStream& rStrm) const
460 {
461     SCCOL nStartCol = 0;
462     SCCOL nEndCol = rDocument.MaxCol();
463     SCROW nStartRow = 0;
464     SCROW nEndRow = rDocument.MaxRow();
465 
466     GetDataArea(nStartCol, nStartRow, nEndCol, nEndRow, false, false);
467 
468     rStrm.WriteUInt64(nEndCol + 1);
469     for (SCCOL nCol = 0; nCol <= nEndCol; ++nCol)
470     {
471         aCol[nCol].StoreToCache(rStrm);
472     }
473 }
474 
RestoreFromCache(SvStream & rStrm)475 void ScTable::RestoreFromCache(SvStream& rStrm)
476 {
477     sal_uInt64 nCols = 0;
478     rStrm.ReadUInt64(nCols);
479     for (SCCOL nCol = 0; nCol < static_cast<SCCOL>(nCols); ++nCol)
480     {
481         aCol[nCol].RestoreFromCache(rStrm);
482     }
483 }
484 
dumpSheetGeomData(bool bColumns,SheetGeomType eGeomType)485 OString ScTable::dumpSheetGeomData(bool bColumns, SheetGeomType eGeomType)
486 {
487     switch (eGeomType)
488     {
489         case SheetGeomType::SIZES:
490             // returns a non-empty space separated list of spans with trailing space.
491             // The format of the span is <size of any row/col in the span in print twips>:<last row/col of the span>
492             // Example (for columns with three spans if MAXCOL is 1023):   "1280:3 1049:50 1280:1023"
493             return dumpColumnRowSizes(bColumns);
494         case SheetGeomType::HIDDEN:
495             // returns a non-empty space separated list of spans with trailing space.
496             // The format of the span is:
497             // 1) First span:         <1 (span is hidden) / 0 (not hidden)>:<last row/col of the span>
498             // 2) Rest of the spans:  <last row/col of the span>
499             // The hidden state of the spans after the first can be inferred from the first span's flag as no adjacent
500             // spans can have the same state by definition of span.
501             return dumpHiddenFiltered(bColumns, /*bHidden*/ true);
502         case SheetGeomType::FILTERED:
503             // has exactly the same format as 'hidden'.
504             return dumpHiddenFiltered(bColumns, /*bHidden*/ false);
505         case SheetGeomType::GROUPS:
506             // returns a space separated list of 'levels' with trailing space.
507             // A 'level' is a comma separated list of groups(outline entries) with trailing comma.
508             // format of a group is:
509             // <start row/col of group>:<number of rows/cols in the group>:<1/0(group is hidden?)>:<1/0(control is visible?)>
510             return dumpColumnRowGroups(bColumns);
511         default:
512             ;
513     }
514 
515     return "";
516 }
517 
dumpColumnRowSizes(bool bColumns)518 OString ScTable::dumpColumnRowSizes(bool bColumns)
519 {
520     // If the data-structures are not available, just report that all
521     // rows/cols have the default sizes.
522     static const OString aDefaultForCols
523         = OString::number(STD_COL_WIDTH) + ":" + OString::number(MAXCOL) + " ";
524     static const OString aDefaultForRows
525         = OString::number(ScGlobal::nStdRowHeight) + ":" + OString::number(MAXROW) + " ";
526 
527     // ScCompressedArray is a template class and we don't want to impose
528     // the restriction that its value type should be string serializable,
529     // instead just operate on the specialized object.
530     typedef ScCompressedArray<SCCOL, sal_uInt16> ColWidthsType;
531     auto dumpColWidths = [](const ColWidthsType& rWidths) -> OString {
532         OString aOutput;
533         OString aSegment;
534         SCCOL nStartCol = 0;
535         const SCCOL nMaxCol = std::min(rWidths.GetLastPos(), MAXCOL);
536         size_t nDummy = 0;
537         while (nStartCol <= nMaxCol)
538         {
539             SCCOL nEndCol;
540             sal_uInt16 nWidth = rWidths.GetValue(nStartCol, nDummy, nEndCol);
541             // The last span nEndCol is always MAXCOL+1 for some reason, and we don't want that.
542             if (nEndCol > nMaxCol)
543                 nEndCol = nMaxCol;
544             aSegment = OString::number(nWidth) + ":" + OString::number(nEndCol) + " ";
545             aOutput += aSegment;
546             nStartCol = nEndCol + 1;
547         }
548 
549         return aOutput;
550     };
551 
552     if (bColumns)
553         return mpColWidth ? dumpColWidths(*mpColWidth) : aDefaultForCols;
554 
555     return mpRowHeights ? mpRowHeights->dumpAsString() : aDefaultForRows;
556 }
557 
dumpHiddenFiltered(bool bColumns,bool bHidden)558 OString ScTable::dumpHiddenFiltered(bool bColumns, bool bHidden)
559 {
560     // defaults to no hidden/filtered row/cols.
561     static const OString aDefaultForCols = "0:" + OString::number(MAXCOL) + " ";
562     static const OString aDefaultForRows = "0:" + OString::number(MAXROW) + " ";
563 
564     if (bHidden)
565     {
566         if (bColumns)
567             return mpHiddenCols ? mpHiddenCols->dumpAsString() : aDefaultForCols;
568 
569         return mpHiddenRows ? mpHiddenRows->dumpAsString() : aDefaultForRows;
570     }
571 
572     if (bColumns)
573         return mpFilteredCols ? mpFilteredCols->dumpAsString() : aDefaultForCols;
574 
575     return mpFilteredRows ? mpFilteredRows->dumpAsString() : aDefaultForRows;
576 }
577 
dumpColumnRowGroups(bool bColumns) const578 OString ScTable::dumpColumnRowGroups(bool bColumns) const
579 {
580     if (!pOutlineTable)
581         return "";
582 
583     if (bColumns)
584         return pOutlineTable->GetColArray().dumpAsString();
585 
586     return pOutlineTable->GetRowArray().dumpAsString();
587 }
588 
GetLOKFreezeCol() const589 SCCOL ScTable::GetLOKFreezeCol() const
590 {
591     return maLOKFreezeCell.Col();
592 }
593 
GetLOKFreezeRow() const594 SCROW ScTable::GetLOKFreezeRow() const
595 {
596     return maLOKFreezeCell.Row();
597 }
598 
SetLOKFreezeCol(SCCOL nFreezeCol)599 bool ScTable::SetLOKFreezeCol(SCCOL nFreezeCol)
600 {
601     if (!ValidCol(nFreezeCol))
602     {
603         SAL_WARN("sc.core", "ScTable::SetLOKFreezeCol : invalid nFreezeCol = " << nFreezeCol);
604         return false;
605     }
606 
607     if (maLOKFreezeCell.Col() != nFreezeCol)
608     {
609         maLOKFreezeCell.SetCol(nFreezeCol);
610         return true;
611     }
612 
613     return false;
614 }
615 
SetLOKFreezeRow(SCROW nFreezeRow)616 bool ScTable::SetLOKFreezeRow(SCROW nFreezeRow)
617 {
618     if (!ValidRow(nFreezeRow))
619     {
620         SAL_WARN("sc.core", "ScTable::SetLOKFreezeRow : invalid nFreezeRow = " << nFreezeRow);
621         return false;
622     }
623 
624     if (maLOKFreezeCell.Row() != nFreezeRow)
625     {
626         maLOKFreezeCell.SetRow(nFreezeRow);
627         return true;
628     }
629 
630     return false;
631 }
632 
633 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
634