/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * 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 .
*/
tools::Long ScColumn::GetNeededSize(
SCROW nRow, OutputDevice* pDev, double nPPTX, double nPPTY, const Fraction& rZoomX, const Fraction& rZoomY, bool bWidth, const ScNeededSizeOptions& rOptions, const ScPatternAttr** ppPatternChange, bool bInPrintTwips ) const
{ // If bInPrintTwips is set, the size calculated should be in print twips, // else it should be in pixels.
// Switch unit to MapTwip instead ? (temporarily and then revert before exit). if (bInPrintTwips)
assert(pDev->GetMapMode().GetMapUnit() == MapUnit::MapTwip);
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
sc::CellStoreType::const_iterator it = aPos.first; if (it == maCells.end() || it->type == sc::element_type_empty) // Empty cell, or invalid row. return 0;
//The pPattern may change in GetCondResult if (aCell.getType() == CELLTYPE_FORMULA)
{
pPattern = pAttrArray->GetPattern( nRow ); if (ppPatternChange)
*ppPatternChange = pPattern;
} // line break?
// get "cell is value" flag // Must be synchronized with ScOutputData::LayoutStrings() bool bCellIsValue = (aCell.getType() == CELLTYPE_VALUE); if (aCell.getType() == CELLTYPE_FORMULA)
{
ScFormulaCell* pFCell = aCell.getFormula();
bCellIsValue = pFCell->IsRunning(); if (!bCellIsValue)
{
bCellIsValue = pFCell->IsValue(); if (bCellIsValue)
{ // the pattern may change in IsValue()
pPattern = pAttrArray->GetPattern( nRow ); if (ppPatternChange)
*ppPatternChange = pPattern;
}
}
}
// #i111387#, tdf#121040: disable automatic line breaks for all number formats if (bBreak && bCellIsValue && (rContext.NFGetType(nFormat) == SvNumFormatType::NUMBER))
{ // If a formula cell needs to be interpreted during aCell.hasNumeric() // to determine the type, the pattern may get invalidated because the // result may set a number format. In which case there's also the // General format not set anymore... bool bMayInvalidatePattern = (aCell.getType() == CELLTYPE_FORMULA); const CellAttributeHolder aOldPattern(pPattern); bool bNumeric = aCell.hasNumeric(); if (bMayInvalidatePattern)
{
pPattern = pAttrArray->GetPattern( nRow ); if (ppPatternChange)
*ppPatternChange = pPattern; // XXX caller may have to check for change!
} if (bNumeric)
{ if (!bMayInvalidatePattern || ScPatternAttr::areSame(pPattern, aOldPattern.getScPatternAttr()))
bBreak = false; else
{
nFormat = pPattern->GetNumberFormat( rContext, pCondSet ); if (rContext.NFGetType(nFormat) == SvNumFormatType::NUMBER)
bBreak = false;
}
}
}
// get other attributes from pattern and conditional formatting
// also call SetFont for edit cells, because bGetFont may be set only once // bGetFont is set also if script type changes if (rOptions.bGetFont)
{
Fraction aFontZoom = ( eOrient == SvxCellOrientation::Standard ) ? rZoomX : rZoomY;
vcl::Font aFont;
aFont.SetKerning(FontKerning::NONE); // like ScDrawStringsVars::SetPattern // font color doesn't matter here
pPattern->fillFontOnly(aFont, pDev, &aFontZoom, pCondSet, nScript);
pDev->SetFont(aFont);
}
Size aPaper( 1000000, 1000000 ); if ( eOrient==SvxCellOrientation::Stacked && !bAsianVertical )
aPaper.setWidth( 1 ); elseif (bBreak)
{ double fWidthFactor = bInPrintTwips ? 1.0 : nPPTX; if ( bTextWysiwyg )
{ // if text is formatted for printer, don't use PixelToLogic, // to ensure the exact same paper width (and same line breaks) as in // ScEditUtil::GetEditArea, used for output.
// tdf#59820 - search for the longest substring in a multiline string void checkLineBreak(const OUString& aStrVal)
{
sal_Int32 nFromIndex = 0;
sal_Int32 nToIndex = aStrVal.indexOf('\n', nFromIndex); // if there is no line break, just take the length of the entire string if (nToIndex == -1)
{
mnMaxLen = aStrVal.getLength();
maMaxLenStr = aStrVal;
} else
{
sal_Int32 nMaxLen = 0; // search for the longest substring in the multiline string while (nToIndex != -1)
{ if (nMaxLen < nToIndex - nFromIndex)
{
nMaxLen = nToIndex - nFromIndex;
}
nFromIndex = nToIndex + 1;
nToIndex = aStrVal.indexOf('\n', nFromIndex);
} // take into consideration the last part of multiline string
nToIndex = aStrVal.getLength() - nFromIndex; if (nMaxLen < nToIndex)
{
nMaxLen = nToIndex;
} // assign new maximum including its substring if (mnMaxLen < nMaxLen)
{
mnMaxLen = nMaxLen;
maMaxLenStr = aStrVal.subView(nFromIndex);
}
}
}
switch (rCell.getType())
{ case CELLTYPE_NONE: case CELLTYPE_VALUE:
mnMaxLen = aValStr.getLength();
maMaxLenStr = aValStr; break; case CELLTYPE_EDIT: case CELLTYPE_STRING: case CELLTYPE_FORMULA: default:
checkLineBreak(aValStr);
}
}
if ( pParam && pParam->mbSimpleText )
{ // all the same except for number format
SCROW nRow = 0; const ScPatternAttr* pPattern = GetPattern( nRow );
vcl::Font aFont;
aFont.SetKerning(FontKerning::NONE); // like ScDrawStringsVars::SetPattern // font color doesn't matter here
pPattern->fillFontOnly(aFont, pDev, &rZoomX);
pDev->SetFont(aFont); const SvxMarginItem* pMargin = &pPattern->GetItem(ATTR_MARGIN);
tools::Long nMargin = static_cast<tools::Long>( pMargin->GetLeftMargin() * nPPTX ) + static_cast<tools::Long>( pMargin->GetRightMargin() * nPPTX );
// Try to find the row that has the longest string, and measure the width of that string.
ScInterpreterContext& rContext = rDocument.GetNonThreadedContext();
sal_uInt32 nFormat = pPattern->GetNumberFormat(rContext); while ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && nRow <= 2)
{ // This is often used with CSV import or other data having a header // row; if there is no specific format set try next row for actual // data format. // Or again in case there was a leading sep=";" row or two header // rows.. const ScPatternAttr* pNextPattern = GetPattern( ++nRow ); if (!ScPatternAttr::areSame(pNextPattern, pPattern))
nFormat = pNextPattern->GetNumberFormat(rContext);
}
OUString aLongStr; const Color* pColor; if (pParam->mnMaxTextRow >= 0)
{
ScRefCellValue aCell = GetCellValue(pParam->mnMaxTextRow);
aLongStr = ScCellFormat::GetString(
aCell, nFormat, &pColor, &rContext, rDocument);
} else
{ // Go though all non-empty cells within selection.
MaxStrLenFinder aFunc(rDocument, nFormat);
sc::CellStoreType::const_iterator itPos = maCells.begin(); for (constauto& rMarkedSpan : aMarkedSpans)
itPos = sc::ParseAllNonEmpty(itPos, maCells, rMarkedSpan.mnRow1, rMarkedSpan.mnRow2, aFunc);
if ( rPattern.GetItem(ATTR_FONT_EMPHASISMARK).GetEmphasisMark() != FontEmphasisMark::NONE )
{ // add height for emphasis marks //TODO: font metrics should be used instead
nHeight += nHeight / 4;
}
// with conditional formatting, always consider the individual cells
const ScPatternAttr* pPattern = aIter.Next(nStart,nEnd); const sal_uInt16 nOptimalMinRowHeight = GetDoc().GetSheetOptimalMinRowHeight(nTab); while ( pPattern )
{ const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE); const ScMergeFlagAttr* pFlag = &pPattern->GetItem(ATTR_MERGE_FLAG); if ( pMerge->GetRowMerge() > 1 || pFlag->IsOverlapped() )
{ // do nothing - vertically with merged and overlapping, // horizontally only with overlapped (invisible) - // only one horizontal merged is always considered
} else
{ bool bStdAllowed = (pPattern->GetCellOrientation() == SvxCellOrientation::Standard); bool bStdOnly = false; if (bStdAllowed)
{ bool bBreak = pPattern->GetItem(ATTR_LINEBREAK).GetValue() ||
(pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() ==
SvxCellHorJustify::Block);
bStdOnly = !bBreak;
// conditional formatting: loop all cells if (bStdOnly &&
!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
{
bStdOnly = false;
}
// rotated text: loop all cells if ( bStdOnly && pPattern->GetItem(ATTR_ROTATE_VALUE).GetValue() )
bStdOnly = false;
}
if (bStdOnly)
{ bool bHasEditCells = HasEditCells(nStart,nEnd,nEditPos); // Call to HasEditCells() may change pattern due to // calculation, => sync always. // We don't know which row changed first, but as pPattern // covered nStart to nEnd we can pick nStart. Worst case we // have to repeat that for every row in range if every row // changed.
pPattern = aIter.Resync( nStart, nStart, nEnd); if (bHasEditCells && nEnd < nEditPos)
bHasEditCells = false; // run into that again if (bHasEditCells) // includes mixed script types
{ if (nEditPos == nStart)
{
bStdOnly = false; if (nEnd > nEditPos)
nNextEnd = nEnd;
nEnd = nEditPos; // calculate single
bStdAllowed = false; // will be computed in any case per cell
} else
{
nNextEnd = nEnd;
nEnd = nEditPos - 1; // standard - part
}
}
}
// if everything below is already larger, the loop doesn't have to // be run again
SCROW nStdEnd = nEnd; if ( nDefHeight <= nMinHeight && nStdEnd >= nMinStart )
nStdEnd = (nMinStart>0) ? nMinStart-1 : 0;
for (constauto& rSpan : aSpans)
{ for (SCROW nRow = rSpan.mnRow1; nRow <= rSpan.mnRow2; ++nRow)
{ // only calculate the cell height when it's used later (#37928#)
voidoperator() (size_t nRow, EditTextObject*& pObj)
{ // For the test on hard formatting (ScEditAttrTester), are the defaults in the // EditEngine of no importance. When the tester would later recognise the same // attributes in default and hard formatting and has to remove them, the correct // defaults must be set in the EditEngine for each cell.
// test for attributes if (!mpEngine)
{
mpEngine.reset(new ScFieldEditEngine(&mrDoc, mrDoc.GetEditPool())); // EEControlBits::ONLINESPELLING if there are errors already
mpEngine->SetControlWord(mpEngine->GetControlWord() | EEControlBits::ONLINESPELLING);
mrDoc.ApplyAsianEditSettings(*mpEngine);
}
mpEngine->SetTextCurrentDefaults(*pObj);
sal_Int32 nParCount = mpEngine->GetParagraphCount(); for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
{
mpEngine->RemoveCharAttribs(nPar); const SfxItemSet& rOld = mpEngine->GetParaAttribs(nPar); if ( rOld.Count() )
{
SfxItemSet aNew( *rOld.GetPool(), rOld.GetRanges() ); // empty
mpEngine->SetParaAttribs( nPar, aNew );
}
} // change URL field to text (not possible otherwise, thus pType=0)
mpEngine->RemoveFields();
bool bSpellErrors = mpEngine->HasOnlineSpellErrors(); bool bNeedObject = bSpellErrors || nParCount>1; // keep errors/paragraphs // ScEditAttrTester is not needed anymore, arrays are gone
if (it->type != sc::element_type_empty) // Non-empty cell at the start position. returnfalse;
// start position of next block which is not empty.
SCROW nNextRow = nStartRow + it->size - aPos.second; return nEndRow < nNextRow;
}
SCSIZE ScColumn::GetEmptyLinesInBlock( SCROW nStartRow, SCROW nEndRow, ScDirection eDir ) const
{ // Given a range of rows, find a top or bottom empty segment. switch (eDir)
{ case DIR_TOP:
{ // Determine the length of empty head segment.
size_t nLength = nEndRow - nStartRow + 1;
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
sc::CellStoreType::const_iterator it = aPos.first; if (it->type != sc::element_type_empty) // First row is already not empty. return 0;
// length of this empty block minus the offset.
size_t nThisLen = it->size - aPos.second; return std::min(nThisLen, nLength);
} break; case DIR_BOTTOM:
{ // Determine the length of empty tail segment.
size_t nLength = nEndRow - nStartRow + 1;
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nEndRow);
sc::CellStoreType::const_iterator it = aPos.first; if (it->type != sc::element_type_empty) // end row is already not empty. return 0;
// length of this empty block from the tip to the end row position.
size_t nThisLen = aPos.second + 1; return std::min(nThisLen, nLength);
} break; default:
;
}
return 0;
}
SCROW ScColumn::GetFirstDataPos() const
{ if (IsEmptyData()) return 0;
sc::CellStoreType::const_iterator it = maCells.begin(); if (it->type != sc::element_type_empty) return 0;
return it->size;
}
SCROW ScColumn::GetLastDataPos() const
{ if (IsEmptyData()) return 0;
sc::CellStoreType::const_reverse_iterator it = maCells.rbegin(); if (it->type != sc::element_type_empty) return GetDoc().MaxRow();
bool ScColumn::GetPrevDataPos(SCROW& rRow) const
{
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
sc::CellStoreType::const_iterator it = aPos.first; if (it == maCells.end()) returnfalse;
if (it->type == sc::element_type_empty)
{ if (it == maCells.begin()) // No more previous non-empty cell. returnfalse;
rRow -= aPos.second + 1; // Last row position of the previous block. returntrue;
}
// This block is not empty. if (aPos.second)
{ // There are preceding cells in this block. Simply move back one cell.
--rRow; returntrue;
}
// This is the first cell in a non-empty block. Move back to the previous block. if (it == maCells.begin()) // No more preceding block. returnfalse;
--rRow; // Move to the last cell of the previous block.
--it; if (it->type == sc::element_type_empty)
{ // This block is empty. if (it == maCells.begin()) // No more preceding blocks. returnfalse;
bool ScColumn::GetNextDataPos(SCROW& rRow) const// greater than rRow
{
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
sc::CellStoreType::const_iterator it = aPos.first; if (it == maCells.end()) returnfalse;
if (it->type == sc::element_type_empty)
{ // This block is empty. Skip ahead to the next block (if exists).
rRow += it->size - aPos.second;
++it; if (it == maCells.end()) // No more next block. returnfalse;
// Next block exists, and is non-empty. returntrue;
}
if (aPos.second < it->size - 1)
{ // There are still cells following the current position.
++rRow; returntrue;
}
// This is the last cell in the block. Move ahead to the next block.
rRow += it->size - aPos.second; // First cell in the next block.
++it; if (it == maCells.end()) // No more next block. returnfalse;
if (it->type == sc::element_type_empty)
{ // Next block is empty. Move to the next block.
rRow += it->size;
++it; if (it == maCells.end()) returnfalse;
}
// Trim down rRowStart first
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRowStart);
sc::CellStoreType::const_iterator it = aPos.first; if (it == maCells.end()) returnfalse;
if (it->type == sc::element_type_empty)
{ // This block is empty. Skip ahead to the next block (if exists).
nRowStartNew += it->size - aPos.second; if (nRowStartNew > rRowEnd) returnfalse;
++it; if (it == maCells.end()) // No more next block. returnfalse;
}
// Trim up rRowEnd next
aPos = maCells.position(rRowEnd);
it = aPos.first; if (it == maCells.end())
{
rRowStart = nRowStartNew; returntrue; // Because trimming of rRowStart is ok
}
if (it->type == sc::element_type_empty)
{ // rRowEnd cannot be in the first block which is empty !
assert(it != maCells.begin()); // This block is empty. Skip to the previous block (it exists).
nRowEndNew -= aPos.second + 1; // Last row position of the previous block.
assert(nRowStartNew <= nRowEndNew);
}
if (itPos->type != sc::element_type_empty) return nRow;
// Move to the first cell of the current empty block.
nRow -= aPos.second;
} while (nRow > 0);
return 0;
}
void ScColumn::CellStorageModified()
{ // Remove cached values. Given how often this function is called and how (not that) often // the cached values are used, it should be more efficient to just discard everything // instead of trying to figure out each time exactly what to discard.
GetDoc().DiscardFormulaGroupContext();
assert(sal::static_int_cast<SCROW>(maCells.size()) == GetDoc().GetMaxRowCount()
&& "Size of the cell array is incorrect." );
assert(sal::static_int_cast<SCROW>(maCellTextAttrs.size()) == GetDoc().GetMaxRowCount()
&& "Size of the cell text attribute array is incorrect.");
assert(sal::static_int_cast<SCROW>(maBroadcasters.size()) == GetDoc().GetMaxRowCount()
&& "Size of the broadcaster array is incorrect.");
#if DEBUG_COLUMN_STORAGE // Make sure that these two containers are synchronized wrt empty segments. auto lIsEmptyType = [](constauto& rElement) { return rElement.type == sc::element_type_empty; }; // Move to the first empty blocks. auto itCell = std::find_if(maCells.begin(), maCells.end(), lIsEmptyType); auto itAttr = std::find_if(maCellTextAttrs.begin(), maCellTextAttrs.end(), lIsEmptyType);
while (itCell != maCells.end())
{ if (itCell->position != itAttr->position || itCell->size != itAttr->size)
{
cout << "ScColumn::CellStorageModified: Cell array and cell text attribute array are out of sync." << endl;
cout << "-- cell array" << endl;
maCells.dump_blocks(cout);
cout << "-- attribute array" << endl;
maCellTextAttrs.dump_blocks(cout);
cout.flush();
abort();
}
// Move to the next empty blocks.
++itCell;
itCell = std::find_if(itCell, maCells.end(), lIsEmptyType);
class CopyCellNotesHandler
{
ScColumn& mrDestCol;
sc::CellNoteStoreType& mrDestNotes;
sc::CellNoteStoreType::iterator miPos;
SCTAB mnSrcTab;
SCCOL mnSrcCol;
SCTAB mnDestTab;
SCCOL mnDestCol;
SCROW mnDestOffset; /// Add this to the source row position to get the destination row. bool mbCloneCaption;
void ScColumn::CopyCellNotesToDocument(
SCROW nRow1, SCROW nRow2, ScColumn& rDestCol, bool bCloneCaption, SCROW nRowOffsetDest ) const
{ if (IsNotesEmptyBlock(nRow1, nRow2)) // The column has no cell notes to copy between specified rows. return;
ScDrawLayer *pDrawLayer = rDestCol.GetDoc().GetDrawLayer(); bool bWasLocked = bool(); if (pDrawLayer)
{ // Avoid O(n^2) by temporary locking SdrModel which disables broadcasting. // Each cell note adds undo listener, and all of them would be woken up in ScPostIt::CreateCaption.
bWasLocked = pDrawLayer->isLocked();
pDrawLayer->setLock(true);
}
CopyCellNotesHandler aFunc(*this, rDestCol, nRowOffsetDest, bCloneCaption);
sc::ParseNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc); if (pDrawLayer)
pDrawLayer->setLock(bWasLocked);
}
auto& rDestDoc = mrDestColumn.GetDoc(); auto pDestinationGroup = rDestDoc.SearchSparklineGroup(pGroup->getID()); if (!pDestinationGroup)
pDestinationGroup = std::make_shared<sc::SparklineGroup>(*pGroup); // Copy the group auto pNewSparkline = std::make_shared<sc::Sparkline>(mrDestColumn.GetCol(), nDestRow, pDestinationGroup);
pNewSparkline->setInputRange(pSparkline->getInputRange());
miDestPosition = mrDestSparkline.set(miDestPosition, nDestRow, new sc::SparklineCell(std::move(pNewSparkline)));
}
};
}
void ScColumn::CopyCellSparklinesToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol, SCROW nRowOffsetDest) const
{ if (IsSparklinesEmptyBlock(nRow1, nRow2)) // The column has no cell sparklines to copy between specified rows. return;
bool ScColumn::HasSparklines() const
{ if (maSparklines.block_size() == 1 && maSparklines.begin()->type == sc::element_type_empty) returnfalse; // all elements are empty returntrue; // otherwise some must be sparklines
}
// Set new value only when the slot is not empty.
sc::celltextattr_block::at(*aPos.first->data, aPos.second).mnTextWidth = nWidth;
CellStorageModified();
}
sc::CellTextAttrStoreType::position_type aPos = maCellTextAttrs.position(nRow); if (aPos.first->type != sc::element_type_celltextattr) // Set new value only when the slot is already set. return;
/* tdf#91416 setting progress in triggers a resize of the window and so ScTabView::DoResize and an InterpretVisible and InterpretDirtyCells which resets the mpFormulaGroupCxt that the current rCxt points to, which is bad, so disable progress during GetResult
*/
ScProgress *pProgress = ScProgress::GetInterpretProgress(); bool bTempDisableProgress = pProgress && pProgress->Enabled(); if (bTempDisableProgress)
pProgress->Disable();
if (aRes.meType == sc::FormulaResultValue::Invalid || aRes.mnError != FormulaError::NONE)
{ if (aRes.mnError == FormulaError::CircularReference)
{ // This cell needs to be recalculated on next visit.
rFC.SetErrCode(FormulaError::NONE);
rFC.SetDirtyVar();
} returnfalse;
}
bool hasNonEmpty( const sc::FormulaGroupContext::StrArrayType& rArray, SCROW nRow1, SCROW nRow2 )
{ // The caller has to make sure the array is at least nRow2+1 long.
sc::FormulaGroupContext::StrArrayType::const_iterator it = rArray.begin();
std::advance(it, nRow1);
sc::FormulaGroupContext::StrArrayType::const_iterator itEnd = it;
std::advance(itEnd, nRow2-nRow1+1); return std::any_of(it, itEnd, NonNullStringFinder());
}
// See if the requested range is already cached.
ScDocument& rDocument = GetDoc();
sc::FormulaGroupContext& rCxt = *(rDocument.GetFormulaGroupContext());
sc::FormulaGroupContext::ColArray* pColArray = rCxt.getCachedColArray(nTab, nCol, nRow2+1); if (pColArray)
{ constdouble* pNum = nullptr; if (pColArray->mpNumArray)
pNum = &(*pColArray->mpNumArray)[nRow1];
// ScColumn::CellStorageModified() simply discards the entire cache (FormulaGroupContext) // on any modification. However getting cell values may cause this to be called // if interpreting a cell results in a change to it (not just its result though). // So temporarily block the discarding.
ProtectFormulaGroupContext protectContext(&GetDoc());
// We need to fetch all cell values from row 0 to nRow2 for caching purposes.
sc::CellStoreType::iterator itBlk = maCells.begin(); switch (itBlk->type)
{ case sc::element_type_numeric:
{ if (o3tl::make_unsigned(nRow2) < itBlk->size)
{ // Requested range falls within the first block. No need to cache. constdouble* p = &sc::numeric_block::at(*itBlk->data, nRow1); return formula::VectorRefArray(p);
}
// Allocate a new array and copy the values to it.
sc::numeric_block::const_iterator it = sc::numeric_block::begin(*itBlk->data);
sc::numeric_block::const_iterator itEnd = sc::numeric_block::end(*itBlk->data);
rCxt.m_NumArrays.push_back(
std::make_unique<sc::FormulaGroupContext::NumArrayType>(it, itEnd));
sc::FormulaGroupContext::NumArrayType& rArray = *rCxt.m_NumArrays.back(); // allocate to the requested length.
rArray.resize(nRow2+1, std::numeric_limits<double>::quiet_NaN());
pColArray = rCxt.setCachedColArray(nTab, nCol, &rArray, nullptr); if (!pColArray) // Failed to insert a new cached column array. return formula::VectorRefArray(formula::VectorRefArray::Invalid);
// Fill the remaining array with values from the following blocks.
size_t nPos = itBlk->size;
++itBlk; if (!appendToBlock(rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
{
rCxt.discardCachedColArray(nTab, nCol); return formula::VectorRefArray(formula::VectorRefArray::Invalid);
}
return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], pStr);
} break; case sc::element_type_string: case sc::element_type_edittext:
{
rCxt.m_StrArrays.push_back(
std::make_unique<sc::FormulaGroupContext::StrArrayType>(nRow2+1, nullptr));
sc::FormulaGroupContext::StrArrayType& rArray = *rCxt.m_StrArrays.back();
pColArray = rCxt.setCachedColArray(nTab, nCol, nullptr, &rArray); if (!pColArray) // Failed to insert a new cached column array. return formula::VectorRefArray();
if (o3tl::make_unsigned(nRow2) < itBlk->size)
{ // Requested range falls within the first block.
copyFirstStringBlock(rDocument, rArray, nRow2+1, itBlk); return formula::VectorRefArray(&rArray[nRow1]);
}
// Fill the remaining array with values from the following blocks.
size_t nPos = itBlk->size;
++itBlk; if (!appendToBlock(rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
{
rCxt.discardCachedColArray(nTab, nCol); return formula::VectorRefArray(formula::VectorRefArray::Invalid);
}
if (pColArray->mpNumArray) return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], pStr); else return formula::VectorRefArray(pStr);
} break; case sc::element_type_formula:
{ if (o3tl::make_unsigned(nRow2) < itBlk->size)
{ // Requested length is within a single block, and the data is // not cached.
pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol); if (!pColArray) // Failed to insert a new cached column array. return formula::VectorRefArray(formula::VectorRefArray::Invalid);
constdouble* pNum = nullptr;
rtl_uString** pStr = nullptr; if (pColArray->mpNumArray)
pNum = &(*pColArray->mpNumArray)[nRow1]; if (pColArray->mpStrArray)
pStr = &(*pColArray->mpStrArray)[nRow1];
return formula::VectorRefArray(pNum, pStr);
}
pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol); if (!pColArray)
{ // Failed to insert a new cached column array. return formula::VectorRefArray(formula::VectorRefArray::Invalid);
}
return formula::VectorRefArray(pNum, pStr);
} break; case sc::element_type_empty:
{ // Fill the whole length with NaN's.
rCxt.m_NumArrays.push_back(
std::make_unique<sc::FormulaGroupContext::NumArrayType>(nRow2+1,
std::numeric_limits<double>::quiet_NaN()));
sc::FormulaGroupContext::NumArrayType& rArray = *rCxt.m_NumArrays.back();
pColArray = rCxt.setCachedColArray(nTab, nCol, &rArray, nullptr); if (!pColArray) // Failed to insert a new cached column array. return formula::VectorRefArray(formula::VectorRefArray::Invalid);
if (o3tl::make_unsigned(nRow2) < itBlk->size) return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]);
// Fill the remaining array with values from the following blocks.
size_t nPos = itBlk->size;
++itBlk; if (!appendToBlock(rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
{
rCxt.discardCachedColArray(nTab, nCol); return formula::VectorRefArray(formula::VectorRefArray::Invalid);
}
void ScColumn::SetFormulaResults( SCROW nRow, constdouble* pResults, size_t nLen )
{
sc::CellStoreType::position_type aPos = maCells.position(nRow);
sc::CellStoreType::iterator it = aPos.first; if (it->type != sc::element_type_formula)
{ // This is not a formula block.
assert( false ); return;
}
size_t nBlockLen = it->size - aPos.second; if (nBlockLen < nLen)
{ // Result array is longer than the length of formula cells. Not good.
assert( false ); return;
}
sc::CellStoreType::position_type aPos = maCells.position(nRow);
sc::CellStoreType::iterator it = aPos.first; if (it->type != sc::element_type_formula)
{ // This is not a formula block.
assert( false ); return;
}
size_t nBlockLen = it->size - aPos.second; if (nBlockLen < nLen)
{ // Length is longer than the length of formula cells. Not good.
assert( false ); return;
}
for (size_t i = 0; i < nLen; ++i, ++itCell)
{ if (nThreadsTotal > 0 && ((i + nOffset) % nThreadsTotal) != nThisThread) continue;
ScFormulaCell& rCell = **itCell; if (!rCell.NeedsInterpret()) continue; // Here we don't call IncInterpretLevel() and DecInterpretLevel() as this call site is // always in a threaded calculation.
rCell.InterpretTail(rContext, ScFormulaCell::SCITP_NORMAL);
}
}
void ScColumn::HandleStuffAfterParallelCalculation( SCROW nRow, size_t nLen, ScInterpreter* pInterpreter )
{
sc::CellStoreType::position_type aPos = maCells.position(nRow);
sc::CellStoreType::iterator it = aPos.first; if (it->type != sc::element_type_formula)
{ // This is not a formula block.
assert( false ); return;
}
size_t nBlockLen = it->size - aPos.second; if (nBlockLen < nLen)
{ // Length is longer than the length of formula cells. Not good.
assert( false ); return;
}
void ScColumn::FindDataAreaPos(SCROW& rRow, bool bDown) const
{ // If the cell is empty, find the next non-empty cell position. If the // cell is not empty, find the last non-empty cell position in the current // contiguous cell block.
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
sc::CellStoreType::const_iterator it = aPos.first; if (it == maCells.end()) // Invalid row. return;
if (it->type == sc::element_type_empty)
{ // Current cell is empty. Find the next non-empty cell.
rRow = FindNextVisibleRowWithContent(it, rRow, bDown); return;
}
// Current cell is not empty.
SCROW nNextRow = FindNextVisibleRow(rRow, bDown);
aPos = maCells.position(it, nNextRow);
it = aPos.first; if (it->type == sc::element_type_empty)
{ // Next visible cell is empty. Find the next non-empty cell.
rRow = FindNextVisibleRowWithContent(it, nNextRow, bDown); return;
}
// Next visible cell is non-empty. Find the edge that's still visible.
SCROW nLastRow = nNextRow; do
{
nNextRow = FindNextVisibleRow(nLastRow, bDown); if (nNextRow == nLastRow) break;
aPos = maCells.position(it, nNextRow);
it = aPos.first; if (it->type != sc::element_type_empty)
nLastRow = nNextRow;
} while (it->type != sc::element_type_empty);
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow); if (aPos.first == maCells.end()) returnfalse;
rBlockPos.miCellPos = aPos.first; // Store this for next call. return aPos.first->type != sc::element_type_empty;
}
std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow); if (aPos.first == maCells.end()) returnfalse;
rBlockPos.miCellPos = aPos.first; // Store this for next call. return aPos.first->type != sc::element_type_empty;
}
rLst.EndListening(*pBC); if (!pBC->HasListeners())
{ // There is no more listeners for this cell. Remove the broadcaster. if(GetDoc().IsDelayedDeletingBroadcasters())
mbEmptyBroadcastersPending = true; else
maBroadcasters.set_empty(nRow, nRow);
}
}
sc::ColumnBlockPosition* p = rCxt.getBlockPosition(rAddress.Tab(), rAddress.Col()); if (!p) return;
sc::BroadcasterStoreType::iterator& it = p->miBroadcasterPos;
std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(it, rAddress.Row());
it = aPos.first; // store the block position for next iteration.
startListening(maBroadcasters, it, aPos.second, rAddress.Row(), rLst);
}
void ScColumn::EndListening( sc::EndListeningContext& rCxt, const ScAddress& rAddress, SvtListener& rListener )
{
sc::ColumnBlockPosition* p = rCxt.getBlockPosition(rAddress.Tab(), rAddress.Col()); if (!p) return;
sc::BroadcasterStoreType::iterator& it = p->miBroadcasterPos;
std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(it, rAddress.Row());
it = aPos.first; // store the block position for next iteration. if (it->type != sc::element_type_broadcaster) return;
rListener.EndListening(*pBC); if (!pBC->HasListeners()) // There is no more listeners for this cell. Add it to the purge list for later purging.
rCxt.addEmptyBroadcasterPosition(rAddress.Tab(), rAddress.Col(), rAddress.Row());
}
namespace {
class CompileDBFormulaHandler
{
sc::CompileFormulaContext& mrCxt;
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.