/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
ScProgress* GetProgressBar(
SCSIZE nCount, SCSIZE nTotalCount, ScProgress* pOuterProgress, const ScDocument* pDoc)
{ if (nTotalCount < 1000)
{ // if the total number of rows is less than 1000, don't even bother // with the progress bar because drawing progress bar can be very // expensive especially in GTK. return nullptr;
}
if (pOuterProgress) return pOuterProgress;
if (nCount > 1) returnnew ScProgress(
pDoc->GetDocumentShell(), ScResId(STR_PROGRESS_HEIGHTING), nTotalCount, true);
if ( rDocument.IsDocVisible() )
{ // when a sheet is added to a visible document, // initialize its RTL flag from the system locale
bLayoutRTL = ScGlobal::IsSystemRTL();
}
for (SCCOL k=0; k < aCol.size(); k++)
aCol[k].Init( k, nTab, rDocument, true );
}
ScTable::~ScTable() COVERITY_NOEXCEPT_FALSE
{ if (!rDocument.IsInDtorClear())
{ for (SCCOL nCol = 0; nCol < aCol.size(); ++nCol)
{
aCol[nCol].FreeNotes();
} // In the dtor, don't delete the pages in the wrong order. // (or else nTab does not reflect the page number!) // In ScDocument::Clear is afterwards used from Clear at the Draw Layer to delete everything.
ScDrawLayer* pDrawLayer = rDocument.GetDrawLayer(); if (pDrawLayer)
pDrawLayer->ScRemovePage( nTab );
}
void ScTable::SetTabBgColor(const Color& rColor)
{ if (aTabBgColor != rColor)
{ // The tab color has changed. Set this table 'modified'.
aTabBgColor = rColor;
SetStreamValid(false);
}
}
if ( rDocument.IsAdjustHeightLocked() )
{ returnfalse;
}
if (!rCxt.isForceAutoSize())
{ // Optimize - exit early if all rows have defined height: super expensive GetOptimalHeight
size_t nIndex;
SCROW nRow;
CRFlags nRowFlags = pRowFlags->GetValue(nStartRow, nIndex, nRow); // changes nIndex, nRow if (nRowFlags & CRFlags::ManualSize) // first block of rows is manual - are all the rest?
{ bool bAllRowsAreManualHeight = true; while (nRow < nEndRow)
{
nRowFlags = pRowFlags->GetNextValue(nIndex, nRow); if (!(nRowFlags & CRFlags::ManualSize))
{
bAllRowsAreManualHeight = false; break;
}
} if (bAllRowsAreManualHeight) returnfalse;
}
}
for (i=0; i<aCol.size(); i++) // Test data
{ if (bCalcHiddens || !rDocument.ColHidden(i, nTab))
{ if (!aCol[i].IsEmptyData())
{
bFound = true; if (i>nMaxX)
nMaxX = i;
SCROW nColY = aCol[i].GetLastDataPos(); if (nColY > nMaxY)
nMaxY = nColY;
} if (bNotes && aCol[i].HasCellNotes() )
{
SCROW maxNoteRow = aCol[i].GetCellNotesMaxRow(); if (maxNoteRow >= nMaxY)
{
bFound = true;
nMaxY = maxNoteRow;
} if (i>nMaxX)
{
bFound = true;
nMaxX = i;
}
} if (aCol[i].HasSparklines())
{
SCROW maxSparklineRow = aCol[i].GetSparklinesMaxRow(); if (maxSparklineRow >= nMaxY)
{
bFound = true;
nMaxY = maxSparklineRow;
} if (i > nMaxX)
{
bFound = true;
nMaxX = i;
}
}
}
}
SCCOL nMaxDataX = nMaxX;
for (i=0; i<aCol.size(); i++) // Test attribute
{ if (bCalcHiddens || !rDocument.ColHidden(i, nTab))
{
SCROW nLastRow; if (aCol[i].GetLastVisibleAttr( nLastRow, bSkipEmpty ))
{
bFound = true;
nMaxX = i; if (nLastRow > nMaxY)
nMaxY = nLastRow;
}
}
}
if (nMaxX == rDocument.MaxCol()) // omit attribute at the right
{
--nMaxX; while ( nMaxX>0 && aCol[nMaxX].IsVisibleAttrEqual(aCol[nMaxX+1], 0, rDocument.MaxRow()) )
--nMaxX;
}
if ( nMaxX < nMaxDataX )
{
nMaxX = nMaxDataX;
} elseif ( nMaxX > nMaxDataX )
{
SCCOL nAttrStartX = nMaxDataX + 1; while ( nAttrStartX < (aCol.size()-1) )
{
SCCOL nAttrEndX = nAttrStartX; while ( nAttrEndX < (aCol.size()-1) && aCol[nAttrStartX].IsVisibleAttrEqual(aCol[nAttrEndX+1], 0, rDocument.MaxRow()) )
++nAttrEndX; if ( nAttrEndX + 1 - nAttrStartX >= SC_COLUMNS_STOP )
{ // found equally-formatted columns behind data -> stop before these columns
nMaxX = nAttrStartX - 1;
// also don't include default-formatted columns before that
SCROW nDummyRow; while ( nMaxX > nMaxDataX && !aCol[nMaxX].GetLastVisibleAttr( nDummyRow, bSkipEmpty ) )
--nMaxX; break;
}
nAttrStartX = nAttrEndX + 1;
}
}
for (i=0; i<aCol.size(); i++) // Test attribute
{ if (aCol[i].HasVisibleAttrIn( nStartRow, nEndRow ))
{
bFound = true;
nMaxX = i;
}
}
if (nMaxX == rDocument.MaxCol()) // omit attribute at the right
{
--nMaxX; while ( nMaxX>0 && aCol[nMaxX].IsVisibleAttrEqual(aCol[nMaxX+1], nStartRow, nEndRow) )
--nMaxX;
}
for (i=0; i<aCol.size(); i++) // test the data
{ if (!aCol[i].IsEmptyData( nStartRow, nEndRow )) //TODO: bNotes ??????
{
bFound = true; if (i > nMaxX)
nMaxX = i;
} elseif (aCol[i].HasSparklines())
{ if (i > nMaxX)
{
bFound = true;
nMaxX = i;
}
}
}
for (i=0; i<aCol.size(); i++) // Test attribute
{
SCROW nFirstRow; if (aCol[i].GetFirstVisibleAttr( nFirstRow ))
{ if (!bFound)
nMinX = i;
bFound = true; if (nFirstRow < nMinY)
nMinY = nFirstRow;
}
}
if (nMinX == 0) // omit attribute at the right
{ if ( aCol.size() > 1 && aCol[0].IsVisibleAttrEqual(aCol[1], 0, rDocument.MaxRow())) // no single ones
{
++nMinX; while ( nMinX<(aCol.size()-1) && aCol[nMinX].IsVisibleAttrEqual(aCol[nMinX-1], 0, rDocument.MaxRow()))
++nMinX;
}
}
bool bDatFound = false; for (i=0; i<aCol.size(); i++) // Test data
{ if (!aCol[i].IsEmptyData())
{ if (!bDatFound && i<nMinX)
nMinX = i;
bFound = bDatFound = true;
SCROW nRow = aCol[i].GetFirstDataPos(); if (nRow < nMinY)
nMinY = nRow;
} if ( aCol[i].HasCellNotes() )
{
SCROW minNoteRow = aCol[i].GetCellNotesMinRow(); if (minNoteRow <= nMinY)
{
bFound = true;
nMinY = minNoteRow;
} if (i<nMinX)
{
bFound = true;
nMinX = i;
}
} if (aCol[i].HasSparklines())
{
SCROW minSparkline = aCol[i].GetSparklinesMinRow(); if (minSparkline <= nMinY)
{
bFound = true;
nMinY = minSparkline;
} if (i < nMinX)
{
bFound = true;
nMinX = i;
}
}
}
rStartCol = nMinX;
rStartRow = nMinY; return bFound;
}
void ScTable::GetDataArea( SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow, bool bIncludeOld, bool bOnlyDown ) const
{ // return the smallest area containing at least all contiguous cells having data. This area // is a square containing also empty cells. It may shrink or extend the area given as input // Flags as modifiers: // // bIncludeOld = true ensure that the returned area contains at least the initial area, // independently of the emptiness of rows / columns (i.e. does not allow shrinking) // bOnlyDown = true means extend / shrink the inputted area only down, i.e modify only rEndRow
// We need to cache sc::ColumnBlockConstPosition per each column.
std::vector< sc::ColumnBlockConstPosition > blockPos( rEndCol + 1 ); for( SCCOL i = 0; i <= rEndCol; ++i )
aCol[ i ].InitBlockPosition( blockPos[ i ] );
do
{
bChanged = false;
if (!bOnlyDown)
{
SCROW nStart = rStartRow;
SCROW nEnd = rEndRow; if (nStart>0) --nStart; if (nEnd<rDocument.MaxRow()) ++nEnd;
// The region is not allocated and does not contain any data. if ( nStartColOrig != nStartCol ) return ( ((eDir == DIR_BOTTOM) || (eDir == DIR_TOP)) ? static_cast<SCSIZE>(nEndRow - nStartRow + 1) : static_cast<SCSIZE>(nEndColOrig - nStartColOrig + 1) );
// If the area between nStartCol and nEndCol are empty, // add the count of unallocated columns on the right. if ( nCol > nEndCol )
nCount += nGapRight;
} return nCount;
}
bool ScTable::IsEmptyLine( SCROW nRow, SCCOL nStartCol, SCCOL nEndCol ) const
{ // The range of columns are unallocated hence empty. if ( nStartCol >= aCol.size() ) returntrue;
while ( rStartCol<rEndCol && aCol[rStartCol].IsEmptyData(rStartRow,rEndRow) )
++rStartCol;
while ( rStartCol<rEndCol && aCol[rEndCol].IsEmptyData(rStartRow,rEndRow) )
--rEndCol;
while ( rStartRow<rEndRow && IsEmptyLine(rStartRow, rStartCol, rEndCol) )
++rStartRow;
// Optimised loop for finding the bottom of the area, can be costly in large // spreadsheets.
SCROW lastDataPos = 0; for (SCCOL i=rStartCol; i<=rEndCol; i++)
lastDataPos = std::max(lastDataPos, aCol[i].GetLastDataPos()); // reduce EndRow to the last row with data
rEndRow = std::min(rEndRow, lastDataPos); // but make sure EndRow is >= StartRow
rEndRow = std::max(rStartRow, rEndRow);
}
SCCOL ScTable::FindNextVisibleColWithContent( SCCOL nCol, bool bRight, SCROW nRow ) const
{ const SCCOL nLastCol = aCol.size() - 1; if(bRight)
{ // If nCol is the last allocated column index, there won't be any content to its right. // To maintain the original return behaviour, return rDocument.MaxCol(). if(nCol >= nLastCol) return rDocument.MaxCol();
do
{
nCol++;
SCCOL nEndCol = 0; bool bHidden = rDocument.ColHidden( nCol, nTab, nullptr, &nEndCol ); if(bHidden)
{
nCol = nEndCol +1; // Can end search early as there is no data after nLastCol. // For nCol == nLastCol, it may still have data so don't want to return rDocument.MaxCol(). if(nCol > nLastCol) return rDocument.MaxCol();
}
if(aCol[nCol].HasVisibleDataAt(nRow)) return nCol;
} while(nCol < nLastCol); // Stop search as soon as the last allocated column is searched.
// If nCol is in the unallocated range [nLastCol+1, rDocument.MaxCol()], then move it directly after nLastCol // as there is no data in the unallocated range. This also makes the search faster and avoids // the need for more range checks in the loop below. if ( nCol > nLastCol )
nCol = nLastCol + 1;
if (nNewCol<0)
nNewCol=0; if (nNewCol>rDocument.MaxCol())
nNewCol=rDocument.MaxCol();
rCol = nNewCol;
} else
{ if ( rCol <= nLastCol )
aCol[rCol].FindDataAreaPos(rRow,eDirection == SC_MOVE_DOWN); else
{ // The cell (rCol, rRow) is equivalent to an empty cell (although not allocated). // Set rRow to 0 or rDocument.MaxRow() depending on eDirection to maintain the behaviour of // ScColumn::FindDataAreaPos() when the given column is empty.
rRow = ( eDirection == SC_MOVE_DOWN ) ? rDocument.MaxRow() : 0;
}
}
}
if (rDocument.HasAttrib(nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::Overlapped)) // Skip an overlapped cell. returnfalse;
if (bMarked && !rMark.IsCellMarked(nCol,nRow)) returnfalse;
/* TODO: for cursor movement *only* this should even take the protection * options (select locked, select unlocked) into account, see
* ScTabView::SkipCursorHorizontal() and ScTabView::SkipCursorVertical(). */ if (bUnprotected && rDocument.HasAttrib(nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::Protected)) returnfalse;
if (bMarked || bUnprotected) //TODO: also in other case ???
{ // Hidden cells must be skipped, as the cursor would end up on the next cell // even if it is protected or not marked. //TODO: control per Extra-Parameter, only for Cursor movement ???
if (RowHidden(nRow)) returnfalse;
if (ColHidden(nCol)) returnfalse;
}
returntrue;
}
// Skips the current cell if it is Hidden, Overlapped or Protected and Sheet is Protected bool ScTable::SkipRow( const SCCOL nCol, SCROW& rRow, const SCROW nMovY, const ScMarkData& rMark, constbool bUp, const SCROW nUsedY, constbool bMarked, constbool bSheetProtected ) const
{ if ( !ValidRow( rRow )) returnfalse;
SCCOL nStartCol, nEndCol;
SCROW nStartRow, nEndRow; if (bMarked)
{
ScRange aRange( ScAddress::UNINITIALIZED); if (rMark.IsMarked())
aRange = rMark.GetMarkArea(); elseif (rMark.IsMultiMarked())
aRange = rMark.GetMultiMarkArea(); else
{ // Covered by assert() above, but for NDEBUG build. if (ValidColRow(nCol,nRow))
{
rCol = nCol;
rRow = nRow;
} return;
}
nStartCol = aRange.aStart.Col();
nStartRow = aRange.aStart.Row();
nEndCol = aRange.aEnd.Col();
nEndRow = aRange.aEnd.Row();
} elseif (bUnprotected)
{
nStartCol = 0;
nStartRow = 0;
nEndCol = rCol;
nEndRow = rRow;
rDocument.GetPrintArea( nTab, nEndCol, nEndRow, true ); // Add some cols/rows to the print area (which is "content or // visually different from empty") to enable travelling through // protected forms with empty cells and no visual indicator. // 42 might be good enough and not too much...
nEndCol = std::min<SCCOL>( nEndCol+42, rDocument.MaxCol());
nEndRow = std::min<SCROW>( nEndRow+42, rDocument.MaxRow());
} else
{ // Invalid values show up for instance for Tab, when nothing is // selected and not protected (left / right edge), then leave values // unchanged. if (ValidColRow(nCol,nRow))
{
rCol = nCol;
rRow = nRow;
}
// Caller ensures actually moving nMovY to jump to prev/next row's // start col. if (nTabStartCol != SC_TABSTART_NONE)
rCol = nTabStartCol;
if (bMarked)
nRow = rMark.GetNextMarked( nCol, nRow, bUp );
if (nTabStartCol != SC_TABSTART_NONE)
{ /* NOTE: If current rCol < nTabStartCol when going down, there * is no way to detect if the previous Tab wrapped around to * the next row or if it was a Shift+Tab going backwards. The * result after a wrap is an odd jump to the next row's * nTabStartCol, which is logical though and always has been * the case. Similar for rCol > nTabStartCol when going up. * Related, it would be nice to limit advancing the position * within bounds even if another wrap would occur, but again we * can't tell if previously Tab or Shift+Tab was used, so we * don't know if it would be nTabStartCol to nEndCol (for Tab)
* or nStartCol to nTabStartCol (for Shift+Tab). */
if (++nWrap >= 2) return;
} if (nRow < nStartRow)
nRow = nEndRow; elseif (nRow > nEndRow)
nRow = nStartRow;
if (bMarked)
nRow = rMark.GetNextMarked( nCol, nRow, bUp );
while ( SkipRow( nCol, nRow, nMovY, rMark, bUp, nEndRow, bMarked, bSheetProtected ))
;
}
} while (false);
}
if ( nMovX && ( bMarked || bUnprotected ) )
{ // wrap initial skip counting: if (nCol < nStartCol)
{
nCol = nEndCol;
--nRow; if (nRow < nStartRow)
nRow = nEndRow;
} if (nCol > nEndCol)
{
nCol = nStartCol;
++nRow; if (nRow > nEndRow)
nRow = nStartRow;
}
if ( !ValidNextPos(nCol, nRow, rMark, bMarked, bUnprotected) )
{ const SCCOL nColCount = nEndCol - nStartCol + 1;
std::unique_ptr<SCROW[]> pNextRows( new SCROW[nColCount]); const SCCOL nLastCol = aCol.size() - 1; constbool bUp = (nMovX < 0); // Moving left also means moving up in rows. const SCROW nRowAdd = (bUp ? -1 : 1);
sal_uInt16 nWrap = 0;
if (bUp)
{ for (SCCOL i = 0; i < nColCount; ++i)
pNextRows[i] = (i + nStartCol > nCol) ? (nRow + nRowAdd) : nRow;
} else
{ for (SCCOL i = 0; i < nColCount; ++i)
pNextRows[i] = (i + nStartCol < nCol) ? (nRow + nRowAdd) : nRow;
} do
{
SCROW nNextRow = pNextRows[nCol - nStartCol] + nRowAdd; if ( bMarked )
nNextRow = rMark.GetNextMarked( nCol, nNextRow, bUp ); if ( bUnprotected )
nNextRow = ( nCol <= nLastCol ) ? aCol[nCol].GetNextUnprotected( nNextRow, bUp ) :
aDefaultColData.GetNextUnprotected( nNextRow, bUp );
pNextRows[nCol - nStartCol] = nNextRow;
if (bUp)
{
SCROW nMaxRow = nStartRow - 1; for (SCCOL i = 0; i < nColCount; ++i)
{ if (pNextRows[i] >= nMaxRow) // when two equal the right one
{
nMaxRow = pNextRows[i];
nCol = i + nStartCol;
}
}
nRow = nMaxRow;
if ( nRow < nStartRow )
{ if (++nWrap >= 2) return;
nCol = nEndCol;
nRow = nEndRow; for (SCCOL i = 0; i < nColCount; ++i)
pNextRows[i] = nEndRow; // do it all over again
}
} else
{
SCROW nMinRow = nEndRow + 1; for (SCCOL i = 0; i < nColCount; ++i)
{ if (pNextRows[i] < nMinRow) // when two equal the left one
{
nMinRow = pNextRows[i];
nCol = i + nStartCol;
}
}
nRow = nMinRow;
if ( nRow > nEndRow )
{ if (++nWrap >= 2) return;
nCol = nStartCol;
nRow = nStartRow; for (SCCOL i = 0; i < nColCount; ++i)
pNextRows[i] = nStartRow; // do it all over again
}
}
} while ( !ValidNextPos(nCol, nRow, rMark, bMarked, bUnprotected) );
}
}
rRow = nEnd + 1; // Search for next selected range
} else
rRow = rDocument.MaxRow() + 1; // End of column
}
rRow = 0;
++rCol; // test next column
}
// Though searched only the allocated columns, it is equivalent to a search till rDocument.MaxCol().
rCol = rDocument.MaxCol() + 1; returnfalse; // Through all columns
}
void ScTable::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
{ // Store the old tab number in sc::UpdatedRangeNames for // ScTokenArray::AdjustReferenceOnInsertedTab() to check with // isNameModified() if (mpRangeName)
mpRangeName->UpdateInsertTab(rCxt, nTab);
if (nTab >= rCxt.mnInsertPos)
{
nTab += rCxt.mnSheets; if (pDBDataNoName)
pDBDataNoName->UpdateMoveTab(nTab - 1 ,nTab);
}
if (mpCondFormatList)
mpCondFormatList->UpdateInsertTab(rCxt);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.