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