/* -*- 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 .
*/
class CellInterpreterBase
{ protected: void Interpret(ScFormulaCell* p)
{ // Interpret() takes a range in a formula group, so group those together. if( !groupCells.empty() && p->GetCellGroup() == groupCells.back()->GetCellGroup()
&& p->aPos.Row() == groupCells.back()->aPos.Row() + 1 )
{
assert( p->aPos.Tab() == groupCells.back()->aPos.Tab()
&& p->aPos.Col() == groupCells.back()->aPos.Col());
groupCells.push_back(p); // Extend range. return;
}
flushPending(); if( !p->GetCellGroup())
{
p->Interpret(); return;
}
groupCells.push_back(p);
}
~CellInterpreterBase()
{
suppress_fun_call_w_exception(flushPending());
} private: void flushPending()
{ if(groupCells.empty()) return;
SCROW firstRow = groupCells.front()->GetCellGroup()->mpTopCell->aPos.Row(); if(!groupCells.front()->Interpret(
groupCells.front()->aPos.Row() - firstRow, groupCells.back()->aPos.Row() - firstRow))
{ // Interpret() will try to group-interpret the given cell range if possible, but if that // is not possible, it will interpret just the given cell. So if group-interpreting // wasn't possible, interpret them one by one. for(ScFormulaCell* cell : groupCells)
cell->Interpret();
}
groupCells.clear();
}
std::vector<ScFormulaCell*> groupCells;
};
class DirtyCellInterpreter : public CellInterpreterBase
{ public: voidoperator() (size_t, ScFormulaCell* p)
{ if(p->GetDirty())
Interpret(p);
}
};
class NeedsInterpretCellInterpreter : public CellInterpreterBase
{ public: voidoperator() (size_t, ScFormulaCell* p)
{ if(p->NeedsInterpret())
{
Interpret(p); // In some cases such as circular dependencies Interpret() // will not reset the dirty flag, check that in order to tell // the caller that the cell range may trigger Interpret() again. if(p->NeedsInterpret())
allInterpreted = false;
}
} bool allInterpreted = true;
};
auto maxRowCount = GetDoc().GetMaxRowCount(); // Keep a logical empty range of 0-rDoc.MaxRow() at all times.
maCells.clear();
maCells.resize(maxRowCount);
maCellTextAttrs.clear();
maCellTextAttrs.resize(maxRowCount);
maCellNotes.clear();
maCellNotes.resize(maxRowCount);
maSparklines.clear();
maSparklines.resize(maxRowCount);
CellStorageModified();
}
// See if we have any cells that would get deleted or shifted by deletion.
sc::CellStoreType::position_type aPos = maCells.position(nStartRow);
sc::CellStoreType::iterator itCell = aPos.first; if (itCell->type == sc::element_type_empty)
{ // This is an empty block. If this is the last block, then there is no cells to delete or shift.
sc::CellStoreType::iterator itTest = itCell;
++itTest; if (itTest == maCells.end())
{ // No cells are affected by this deletion. Bail out.
CellStorageModified(); // broadcast array has been modified. return;
}
}
// Check if there are any cells below the end row that will get shifted. bool bShiftCells = false; if (nEndRow < GetDoc().MaxRow()) //only makes sense to do this if there *is* a row after the end row
{
aPos = maCells.position(itCell, nEndRow+1);
itCell = aPos.first; if (itCell->type == sc::element_type_empty)
{ // This block is empty. See if there is any block that follows.
sc::CellStoreType::iterator itTest = itCell;
++itTest; if (itTest != maCells.end()) // Non-empty block follows -> cells that will get shifted.
bShiftCells = true;
} else
bShiftCells = true;
}
sc::SingleColumnSpanSet aNonEmptySpans(GetDoc().GetSheetLimits()); if (bShiftCells)
{ // Mark all non-empty cell positions below the end row.
sc::ColumnBlockConstPosition aBlockPos;
aBlockPos.miCellPos = itCell;
aNonEmptySpans.scan(aBlockPos, *this, nEndRow+1, GetDoc().MaxRow());
}
sc::AutoCalcSwitch aACSwitch(GetDoc(), false);
// Remove the cells.
maCells.erase(nStartRow, nEndRow);
maCells.resize(GetDoc().GetMaxRowCount());
// Get the position again after the container change.
aPos = maCells.position(nStartRow);
// Shift the formula cell positions below the start row.
ShiftFormulaPosHandler aShiftFormulaFunc;
sc::ProcessFormula(aPos.first, maCells, nStartRow, GetDoc().MaxRow(), aShiftFormulaFunc);
// Shift the text attribute array too (before the broadcast).
maCellTextAttrs.erase(nStartRow, nEndRow);
maCellTextAttrs.resize(GetDoc().GetMaxRowCount());
void ScColumn::JoinNewFormulaCell( const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell )
{ // Check the previous row position for possible grouping. if (aPos.first->type == sc::element_type_formula && aPos.second > 0)
{
ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1);
sc::CellStoreType::position_type aPosPrev = aPos;
--aPosPrev.second;
sc::SharedFormulaUtil::joinFormulaCells(aPosPrev, rPrev, rCell);
}
// Check the next row position for possible grouping. if (aPos.first->type == sc::element_type_formula && aPos.second+1 < aPos.first->size)
{
ScFormulaCell& rNext = *sc::formula_block::at(*aPos.first->data, aPos.second+1);
sc::SharedFormulaUtil::joinFormulaCells(aPos, rCell, rNext);
}
}
void ScColumn::DetachFormulaCell( const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell, std::vector<SCROW>& rNewSharedRows )
{ if (!GetDoc().IsClipOrUndo())
{ #if USE_FORMULA_GROUP_LISTENER if (rCell.IsShared() && rCell.GetSharedLength() > 1)
{ // Record new spans (shared or remaining single) that will result // from unsharing to reestablish listeners. // Same cases as in unshareFormulaCell(). // XXX NOTE: this is not part of unshareFormulaCell() because that // is called in other contexts as well, for which passing and // determining the rows vector would be superfluous. If that was // needed, move it there. const SCROW nSharedTopRow = rCell.GetSharedTopRow(); const SCROW nSharedLength = rCell.GetSharedLength(); if (rCell.aPos.Row() == nSharedTopRow)
{ // Top cell. // Next row will be new shared top or single cell.
rNewSharedRows.push_back( nSharedTopRow + 1);
rNewSharedRows.push_back( nSharedTopRow + nSharedLength - 1);
} elseif (rCell.aPos.Row() == nSharedTopRow + nSharedLength - 1)
{ // Bottom cell. // Current shared top row will be new shared top again or // single cell.
rNewSharedRows.push_back( nSharedTopRow);
rNewSharedRows.push_back( rCell.aPos.Row() - 1);
} else
{ // Some mid cell. // Current shared top row will be new shared top again or // single cell, plus a new shared top below or single cell.
rNewSharedRows.push_back( nSharedTopRow);
rNewSharedRows.push_back( rCell.aPos.Row() - 1);
rNewSharedRows.push_back( rCell.aPos.Row() + 1);
rNewSharedRows.push_back( nSharedTopRow + nSharedLength - 1);
}
} #endif
// Have the dying formula cell stop listening. // If in a shared formula group this ends the group listening.
rCell.EndListeningTo(GetDoc());
}
sc::SharedFormulaUtil::joinFormulaCellAbove(aPos); if (GetDoc().ValidRow(nRow2+1))
{
aPos = maCells.position(it, nRow2+1);
sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
}
if (GetDoc().IsClipOrUndo()) return;
// Need to process (start listening) entire shared formula groups, not just // a slice thereof. bool bEnlargedDown = false;
aPos = maCells.position(nRow1);
it = aPos.first; if (it->type == sc::element_type_formula)
{
ScFormulaCell& rCell = *sc::formula_block::at(*it->data, aPos.second); if (rCell.IsShared())
{
nRow1 = std::min( nRow1, rCell.GetSharedTopRow());
SCROW nCellLen = rCell.GetSharedLength(); // coverity[ tainted_data_return : FALSE ] version 2023.12.2 constauto nEndRow = rCell.GetSharedTopRow() + nCellLen; if (nRow2 < nEndRow)
{
nRow2 = nEndRow - 1;
bEnlargedDown = true; // Same end row is also enlarged, i.e. doesn't need to be // checked for another group.
}
}
} if (!bEnlargedDown)
{
aPos = maCells.position(it, nRow2);
it = aPos.first; if (it->type == sc::element_type_formula)
{
ScFormulaCell& rCell = *sc::formula_block::at(*it->data, aPos.second); if (rCell.IsShared())
{
nRow2 = std::max( nRow2, rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1);
}
}
}
// Split formula grouping at the top and bottom boundaries.
sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, &rCxt); if (GetDoc().ValidRow(nRow2+1))
{
aPos = maCells.position(it, nRow2+1);
sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, &rCxt);
}
void ScColumn::AttachNewFormulaCell( const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell, const std::vector<SCROW>& rNewSharedRows, bool bJoin, sc::StartListeningType eListenType )
{ if (bJoin) // See if this new formula cell can join an existing shared formula group.
JoinNewFormulaCell(aPos, rCell);
// When we insert from the Clipboard we still have wrong (old) References! // First they are rewired in CopyBlockFromClip via UpdateReference and the // we call StartListeningFromClip and BroadcastFromClip. // If we insert into the Clipboard/andoDoc, we do not use a Broadcast. // After Import we call CalcAfterLoad and in there Listening.
ScDocument& rDocument = GetDoc(); if (rDocument.IsClipOrUndo() || rDocument.IsInsertingFromOtherDoc()) return;
switch (eListenType)
{ case sc::ConvertToGroupListening:
{ constauto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(rDocument);
sc::StartListeningContext aStartCxt(rDocument, pPosSet);
sc::EndListeningContext aEndCxt(rDocument, pPosSet);
SCROW nStartRow, nEndRow;
nStartRow = nEndRow = aPos.first->position + aPos.second; for (const SCROW nR : rNewSharedRows)
{ if (nStartRow > nR)
nStartRow = nR; if (nEndRow < nR)
nEndRow = nR;
}
StartListeningFormulaCells(aStartCxt, aEndCxt, nStartRow, nEndRow);
} break; case sc::SingleCellListening:
rCell.StartListeningTo(rDocument);
StartListeningUnshared( rNewSharedRows); break; case sc::NoListening: default: if (!rNewSharedRows.empty())
{
assert(rNewSharedRows.size() == 2 || rNewSharedRows.size() == 4); // Calling SetNeedsListeningGroup() with a top row sets it to // all affected formula cells of that group. const ScFormulaCell* pFC = GetFormulaCell( rNewSharedRows[0]);
assert(pFC); // that *is* supposed to be a top row if (pFC && !pFC->NeedsListening())
SetNeedsListeningGroup( rNewSharedRows[0]); if (rNewSharedRows.size() > 2)
{
pFC = GetFormulaCell( rNewSharedRows[2]);
assert(pFC); // that *is* supposed to be a top row if (pFC && !pFC->NeedsListening())
SetNeedsListeningGroup( rNewSharedRows[2]);
}
} break;
}
if (!rDocument.IsCalcingAfterLoad())
rCell.SetDirty();
}
void ScColumn::AttachNewFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength,
std::vector<SCROW>& rNewSharedRows )
{ // Make sure the whole length consists of formula cells. if (aPos.first->type != sc::element_type_formula) return;
if (aPos.first->size < aPos.second + nLength) // Block is shorter than specified length. return;
// Join the top and bottom cells only.
ScFormulaCell* pCell1 = sc::formula_block::at(*aPos.first->data, aPos.second);
JoinNewFormulaCell(aPos, *pCell1);
ScDocument& rDocument = GetDoc(); if (rDocument.IsClipOrUndo() || rDocument.IsInsertingFromOtherDoc()) return;
constbool bShared = pCell1->IsShared() || pCell2->IsShared(); if (bShared)
{ const SCROW nTopRow = (pCell1->IsShared() ? pCell1->GetSharedTopRow() : pCell1->aPos.Row()); const SCROW nBotRow = (pCell2->IsShared() ?
pCell2->GetSharedTopRow() + pCell2->GetSharedLength() - 1 : pCell2->aPos.Row()); if (rNewSharedRows.empty())
{
rNewSharedRows.push_back( nTopRow);
rNewSharedRows.push_back( nBotRow);
} elseif (rNewSharedRows.size() == 2)
{ // Combine into one span. if (rNewSharedRows[0] > nTopRow)
rNewSharedRows[0] = nTopRow; if (rNewSharedRows[1] < nBotRow)
rNewSharedRows[1] = nBotRow;
} elseif (rNewSharedRows.size() == 4)
{ // Merge into one span. // The original two spans are ordered from top to bottom.
std::vector<SCROW> aRows { std::min( rNewSharedRows[0], nTopRow), std::max( rNewSharedRows[3], nBotRow) };
rNewSharedRows.swap( aRows);
} else
{
assert(!"rNewSharedRows?");
}
}
StartListeningUnshared( rNewSharedRows);
sc::StartListeningContext aCxt(rDocument);
ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second);
ScFormulaCell** ppEnd = pp + nLength; for (; pp != ppEnd; ++pp)
{ if (!bShared)
(*pp)->StartListeningTo(aCxt); if (!rDocument.IsCalcingAfterLoad())
(*pp)->SetDirty();
}
}
void ScColumn::BroadcastNewCell( SCROW nRow )
{ // When we insert from the Clipboard we still have wrong (old) References! // First they are rewired in CopyBlockFromClip via UpdateReference and the // we call StartListeningFromClip and BroadcastFromClip. // If we insert into the Clipboard/andoDoc, we do not use a Broadcast. // After Import we call CalcAfterLoad and in there Listening. if (GetDoc().IsClipOrUndo() || GetDoc().IsInsertingFromOtherDoc() || GetDoc().IsCalcingAfterLoad()) return;
// Script type not yet determined. Determine the real script // type, and store it. const ScPatternAttr* pPattern = GetPattern(nRow); if (!pPattern) returnfalse;
voidoperator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
{ switch (node.type)
{ case sc::element_type_numeric: // Numeric type target datetime and number, thus we have a dedicated function if (!mbNumeric && !mbDateTime) return;
// If numeric and datetime selected, delete full range if (mbNumeric && mbDateTime) break;
deleteNumeric(node, nOffset, nDataSize); return; case sc::element_type_string: case sc::element_type_edittext: if (!mbString) return; break; case sc::element_type_formula:
{ if (!mbFormula) return;
sc::formula_block::iterator it = sc::formula_block::begin(*node.data) + nOffset;
sc::formula_block::iterator itEnd = it + nDataSize;
maFormulaCells.insert(maFormulaCells.end(), it, itEnd);
} break; case sc::element_type_empty: default: return;
}
// Tag these cells for deletion.
SCROW nRow1 = node.position + nOffset;
SCROW nRow2 = nRow1 + nDataSize - 1;
maDeleteRanges.set(nRow1, nRow2, true);
}
void deleteNumberOrDateTime(SCROW nRow1, SCROW nRow2, bool dateTime)
{ if (!dateTime && !mbNumeric) // numeric flag must be selected return; if (dateTime && !mbDateTime) // datetime flag must be selected return;
maDeleteRanges.set(nRow1, nRow2, true);
}
/** * Query the formula ranges that may have stopped listening, accounting for * the formula groups.
*/
std::vector<std::pair<SCROW, SCROW>> getFormulaRanges()
{
std::vector<std::pair<SCROW, SCROW>> aRet;
// First, split formula grouping at the top and bottom boundaries // before emptying the cells.
sc::CellStoreType::position_type aPos = rCells.position(mrPos.miCellPos, rSpan.mnRow1);
splitFormulaGrouping(aPos);
aPos = rCells.position(aPos.first, rSpan.mnRow2);
splitFormulaGrouping(aPos);
// Determine which cells to delete based on the deletion flags.
DeleteAreaHandler aFunc(GetDoc(), nDelFlag, *this);
sc::CellStoreType::iterator itPos = maCells.position(rBlockPos.miCellPos, nRow1).first;
sc::ProcessBlock(itPos, maCells, aFunc, nRow1, nRow2);
xResult->aFormulaRanges = aFunc.getFormulaRanges();
aFunc.endFormulas(); // Have the formula cells stop listening.
// Get the deletion spans.
sc::SingleColumnSpanSet::SpansType aSpans;
aFunc.getSpans().getSpans(aSpans);
// Delete the cells for real. // tdf#139820: Deleting in reverse order is more efficient.
std::for_each(aSpans.rbegin(), aSpans.rend(), EmptyCells(rBlockPos, *this));
CellStorageModified();
aFunc.getSpans().swap(xResult->aDeletedRows);
return xResult;
}
void ScColumn::DeleteArea(
SCROW nStartRow, SCROW nEndRow, InsertDeleteFlags nDelFlag, bool bBroadcast,
sc::ColumnSpanSet* pBroadcastSpans )
{
InsertDeleteFlags nContMask = InsertDeleteFlags::CONTENTS; // InsertDeleteFlags::NOCAPTIONS needs to be passed too, if InsertDeleteFlags::NOTE is set if( nDelFlag & InsertDeleteFlags::NOTE )
nContMask |= InsertDeleteFlags::NOCAPTIONS;
InsertDeleteFlags nContFlag = nDelFlag & nContMask;
// Delete attributes just now if ((nDelFlag & InsertDeleteFlags::ATTRIB) == InsertDeleteFlags::ATTRIB)
pAttrArray->DeleteArea( nStartRow, nEndRow ); elseif ((nDelFlag & InsertDeleteFlags::HARDATTR) == InsertDeleteFlags::HARDATTR)
pAttrArray->DeleteHardAttr( nStartRow, nEndRow );
if (xResult && bBroadcast)
{ // Broadcast on only cells that were deleted; no point broadcasting on // cells that were already empty before the deletion.
std::vector<SCROW> aRows;
xResult->aDeletedRows.getRows(aRows);
BroadcastCells(aRows, SfxHintId::ScDataChanged);
}
}
if (node.type == sc::element_type_empty)
{ if (bCopyCellNotes && !mrCxt.isSkipEmptyCells())
{ bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
duplicateNotes(nSrcRow1, nDataSize, bCloneCaption );
} if (bCopySparklines) // If there is a sparkline is it empty?
{
duplicateSparklines(nSrcRow1, nDataSize);
} return;
}
switch (node.type)
{ case sc::element_type_numeric:
{ // We need to copy numeric cells individually because of date type check.
sc::numeric_block::const_iterator it = sc::numeric_block::begin(*node.data);
std::advance(it, nOffset);
sc::numeric_block::const_iterator itEnd = it;
std::advance(itEnd, nDataSize); for (SCROW nSrcRow = nSrcRow1; it != itEnd; ++it, ++nSrcRow)
{ bool bCopy = mrCxt.isDateCell(mrSrcCol, nSrcRow) ? bDateTime : bNumeric; if (!bCopy) continue;
if (bAsLink)
insertRefCell(nSrcRow, nSrcRow + mnRowOffset); else
mrDestCol.SetValue(maDestBlockPos, nSrcRow + mnRowOffset, *it);
}
} break; case sc::element_type_string:
{ if (!bString) break;
sc::string_block::const_iterator it = sc::string_block::begin(*node.data);
std::advance(it, nOffset);
sc::string_block::const_iterator itEnd = it;
std::advance(itEnd, nDataSize); for (SCROW nSrcRow = nSrcRow1; it != itEnd; ++it, ++nSrcRow)
{ if (bAsLink)
insertRefCell(nSrcRow, nSrcRow + mnRowOffset); elseif (mpSharedStringPool)
{ // Re-intern the string if source is a different document.
svl::SharedString aInterned = mpSharedStringPool->intern( (*it).getString());
mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, aInterned);
} else
mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, *it);
}
} break; case sc::element_type_edittext:
{ if (!bString) break;
sc::edittext_block::const_iterator it = sc::edittext_block::begin(*node.data);
std::advance(it, nOffset);
sc::edittext_block::const_iterator itEnd = it;
std::advance(itEnd, nDataSize); for (SCROW nSrcRow = nSrcRow1; it != itEnd; ++it, ++nSrcRow)
{
if (bAsLink)
insertRefCell(nSrcRow, nSrcRow + mnRowOffset); else
mrDestCol.SetEditText(maDestBlockPos, nSrcRow + mnRowOffset, **it);
}
} break; case sc::element_type_formula:
{
sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
std::advance(it, nOffset);
sc::formula_block::const_iterator itEnd = it;
std::advance(itEnd, nDataSize); for (SCROW nSrcRow = nSrcRow1; it != itEnd; ++it, ++nSrcRow)
{
ScFormulaCell& rSrcCell = **it; bool bForceFormula = false; if (bBoolean)
{ // See if the formula consists of =TRUE() or =FALSE(). const ScTokenArray* pCode = rSrcCell.GetCode(); if (pCode && pCode->GetLen() == 1)
{ const formula::FormulaToken* p = pCode->FirstToken(); if (p->GetOpCode() == ocTrue || p->GetOpCode() == ocFalse) // This is a boolean formula.
bForceFormula = true;
}
}
ScAddress aDestPos(mnCol, nSrcRow + mnRowOffset, mnTab); if (bFormula || bForceFormula)
{ if (bAsLink)
insertRefCell(nSrcRow, nSrcRow + mnRowOffset); else
{
mrDestCol.SetFormulaCell(
maDestBlockPos, nSrcRow + mnRowOffset, new ScFormulaCell(rSrcCell, mrDestCol.GetDoc(), aDestPos),
sc::SingleCellListening,
rSrcCell.NeedsNumberFormat());
}
} elseif (bNumeric || bDateTime || bString)
{ // Always just copy the original row to the Undo Document; // do not create Value/string cells from formulas
FormulaError nErr = rSrcCell.GetErrCode(); if (nErr != FormulaError::NONE)
{ // error codes are cloned with values if (bNumeric)
{ if (bAsLink)
insertRefCell(nSrcRow, nSrcRow + mnRowOffset); else
{
ScFormulaCell* pErrCell = new ScFormulaCell(mrDestCol.GetDoc(), aDestPos);
pErrCell->SetErrCode(nErr);
mrDestCol.SetFormulaCell(
maDestBlockPos, nSrcRow + mnRowOffset, pErrCell);
}
}
} elseif (rSrcCell.IsEmptyDisplayedAsString())
{ // Empty stays empty and doesn't become 0. continue;
} elseif (rSrcCell.IsValue())
{ bool bCopy = mrCxt.isDateCell(mrSrcCol, nSrcRow) ? bDateTime : bNumeric; if (!bCopy) continue;
if (bAsLink)
insertRefCell(nSrcRow, nSrcRow + mnRowOffset); else
mrDestCol.SetValue(maDestBlockPos, nSrcRow + mnRowOffset, rSrcCell.GetValue());
} elseif (bString)
{
svl::SharedString aStr = rSrcCell.GetString(); if (aStr.isEmpty()) // do not clone empty string continue;
if (bAsLink)
insertRefCell(nSrcRow, nSrcRow + mnRowOffset); elseif (rSrcCell.IsMultilineResult())
{ // Clone as an edit text object.
ScFieldEditEngine& rEngine = mrDestCol.GetDoc().GetEditEngine();
rEngine.SetTextCurrentDefaults(aStr.getString());
mrDestCol.SetEditText(maDestBlockPos, nSrcRow + mnRowOffset, rEngine.CreateTextObject());
} elseif (mpSharedStringPool)
{ // Re-intern the string if source is a different document.
svl::SharedString aInterned = mpSharedStringPool->intern( aStr.getString());
mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, aInterned);
} else
{
mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, aStr);
}
}
}
}
} break; default:
;
} if (bCopyCellNotes)
{ bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
duplicateNotes(nSrcRow1, nDataSize, bCloneCaption );
} if (bCopySparklines)
{
duplicateSparklines(nSrcRow1, nDataSize);
}
}
};
class CopyTextAttrsFromClipHandler
{
sc::CellTextAttrStoreType& mrAttrs;
size_t mnDelta;
sc::ColumnBlockPosition maDestBlockPos;
sc::ColumnBlockPosition* mpDestBlockPos; // to save it for next iteration.
~CopyTextAttrsFromClipHandler()
{ if (mpDestBlockPos) // Don't forget to save this to the context!
mpDestBlockPos->miCellTextAttrPos = maDestBlockPos.miCellTextAttrPos;
}
if ((rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE)
{ if (rCxt.isSkipEmptyCells())
{ // copy only attributes for non-empty cells between nRow1-nDy and nRow2-nDy.
sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
aSpanSet.scan(rColumn, nRow1-nDy, nRow2-nDy);
sc::SingleColumnSpanSet::SpansType aSpans;
aSpanSet.getSpans(aSpans);
std::for_each(
aSpans.begin(), aSpans.end(), CopyAttrArrayByRange(*rColumn.pAttrArray, *pAttrArray, nDy));
} else
rColumn.pAttrArray->CopyAreaSafe( nRow1, nRow2, nDy, *pAttrArray );
} if ((rCxt.getInsertFlag() & InsertDeleteFlags::CONTENTS) == InsertDeleteFlags::NONE) return;
ScDocument& rDocument = GetDoc(); if (rCxt.isAsLink() && rCxt.getInsertFlag() == InsertDeleteFlags::ALL)
{ // We also reference empty cells for "ALL" // InsertDeleteFlags::ALL must always contain more flags when compared to "Insert contents" as // contents can be selected one by one!
// Don't forget to copy the cell text attributes.
CopyTextAttrsFromClipHandler aFunc(rCxt, maCellTextAttrs, *this, nTab, nCol, nDy);
sc::ParseBlock(rColumn.maCellTextAttrs.begin(), rColumn.maCellTextAttrs, aFunc, nRow1-nDy, nRow2-nDy);
return;
}
// Compare the ScDocumentPool* to determine if we are copying within the // same document. If not, re-intern shared strings.
svl::SharedStringPool* pSharedStringPool = (rColumn.GetDoc().GetPool() != rDocument.GetPool()) ?
&rDocument.GetSharedStringPool() : nullptr;
// nRow1 to nRow2 is for destination (this) column. Subtract nDy to get the source range. // Copy all cells in the source column (rColumn) from nRow1-nDy to nRow2-nDy to this column.
{
CopyCellsFromClipHandler aFunc(rCxt, rColumn, *this, nTab, nCol, nDy, pSharedStringPool);
sc::ParseBlock(rColumn.maCells.begin(), rColumn.maCells, aFunc, nRow1-nDy, nRow2-nDy);
}
{ // Don't forget to copy the cell text attributes.
CopyTextAttrsFromClipHandler aFunc(rCxt, maCellTextAttrs, *this, nTab, nCol, nDy);
sc::ParseBlock(rColumn.maCellTextAttrs.begin(), rColumn.maCellTextAttrs, aFunc, nRow1-nDy, nRow2-nDy);
}
}
// Result in rVal1 bool lcl_DoFunction( double& rVal1, double nVal2, ScPasteFunc nFunction )
{ bool bOk = false; switch (nFunction)
{ case ScPasteFunc::ADD:
bOk = SubTotal::SafePlus( rVal1, nVal2 ); break; case ScPasteFunc::SUB:
nVal2 = -nVal2; // FIXME: Can we do this always without error?
bOk = SubTotal::SafePlus( rVal1, nVal2 ); break; case ScPasteFunc::MUL:
bOk = SubTotal::SafeMult( rVal1, nVal2 ); break; case ScPasteFunc::DIV:
bOk = SubTotal::SafeDiv( rVal1, nVal2 ); break; default: break;
} return bOk;
}
// Both src and dest are of numeric type.
doFunction(nRow, f, fSrcVal);
} break; case sc::element_type_formula:
{ // Combination of value and at least one formula -> Create formula
ScTokenArray aArr(mrDestColumn.GetDoc());
// First row
aArr.AddDouble(f);
// Operator
OpCode eOp = ocAdd; switch (mnFunction)
{ case ScPasteFunc::ADD: eOp = ocAdd; break; case ScPasteFunc::SUB: eOp = ocSub; break; case ScPasteFunc::MUL: eOp = ocMul; break; case ScPasteFunc::DIV: eOp = ocDiv; break; default: break;
}
aArr.AddOpCode(eOp); // Function
// Second row
ScFormulaCell* pDest = sc::formula_block::at(*aPos.first->data, aPos.second);
lcl_AddCode(aArr, pDest);
miNewCellsPos = maNewCells.set(
miNewCellsPos, nRow-mnRowOffset, new ScFormulaCell(
mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab()), aArr));
} break; case sc::element_type_string: case sc::element_type_edittext:
{ // Destination cell is not a number. Just take the source cell.
miNewCellsPos = maNewCells.set(miNewCellsPos, nRow-mnRowOffset, f);
} break; default:
;
}
}
voidoperator() (size_t nRow, const ScFormulaCell* p)
{
sc::CellStoreType::position_type aPos = mrDestColumn.GetCellStore().position(mrBlockPos.miCellPos, nRow);
mrBlockPos.miCellPos = aPos.first; switch (aPos.first->type)
{ case sc::element_type_numeric:
{ // Source is formula, and dest is value.
ScTokenArray aArr(mrDestColumn.GetDoc());
// First row
lcl_AddCode(aArr, p);
// Operator
OpCode eOp = ocAdd; switch (mnFunction)
{ case ScPasteFunc::ADD: eOp = ocAdd; break; case ScPasteFunc::SUB: eOp = ocSub; break; case ScPasteFunc::MUL: eOp = ocMul; break; case ScPasteFunc::DIV: eOp = ocDiv; break; default: break;
}
aArr.AddOpCode(eOp); // Function
// Second row
aArr.AddDouble(sc::numeric_block::at(*aPos.first->data, aPos.second));
miNewCellsPos = maNewCells.set(
miNewCellsPos, nRow-mnRowOffset, new ScFormulaCell(
mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab()), aArr));
} break; case sc::element_type_formula:
{ // Both are formulas.
ScTokenArray aArr(mrDestColumn.GetDoc());
// First row
lcl_AddCode(aArr, p);
// Operator
OpCode eOp = ocAdd; switch (mnFunction)
{ case ScPasteFunc::ADD: eOp = ocAdd; break; case ScPasteFunc::SUB: eOp = ocSub; break; case ScPasteFunc::MUL: eOp = ocMul; break; case ScPasteFunc::DIV: eOp = ocDiv; break; default: break;
}
aArr.AddOpCode(eOp); // Function
// Second row
ScFormulaCell* pDest = sc::formula_block::at(*aPos.first->data, aPos.second);
lcl_AddCode(aArr, pDest);
miNewCellsPos = maNewCells.set(
miNewCellsPos, nRow-mnRowOffset, new ScFormulaCell(
mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab()), aArr));
} break; case sc::element_type_string: case sc::element_type_edittext: case sc::element_type_empty:
{ // Destination cell is not a number. Just take the source cell.
ScAddress aDestPos(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab());
miNewCellsPos = maNewCells.set(
miNewCellsPos, nRow-mnRowOffset, new ScFormulaCell(*p, mrDestColumn.GetDoc(), aDestPos));
} break; default:
;
}
}
/** * Empty cell series in the source (clip) document.
*/ voidoperator() (mdds::mtv::element_t, size_t nTopRow, size_t nDataSize)
{ if (mbSkipEmpty) return;
// Source cells are empty. Treat them as if they have a value of 0.0. for (size_t i = 0; i < nDataSize; ++i)
{
size_t nDestRow = nTopRow + i;
sc::CellStoreType::position_type aPos = mrDestColumn.GetCellStore().position(mrBlockPos.miCellPos, nDestRow);
mrBlockPos.miCellPos = aPos.first; switch (aPos.first->type)
{ case sc::element_type_numeric:
{ double fVal2 = sc::numeric_block::at(*aPos.first->data, aPos.second);
doFunction(nDestRow, 0.0, fVal2);
} break; case sc::element_type_string:
{ const svl::SharedString& aVal = sc::string_block::at(*aPos.first->data, aPos.second);
miNewCellsPos = maNewCells.set(
miNewCellsPos, nDestRow-mnRowOffset, aVal);
} break; case sc::element_type_edittext:
{
EditTextObject* pObj = sc::edittext_block::at(*aPos.first->data, aPos.second);
miNewCellsPos = maNewCells.set(
miNewCellsPos, nDestRow-mnRowOffset, pObj->Clone().release());
} break; case sc::element_type_formula:
{
ScTokenArray aArr(mrDestColumn.GetDoc());
/** * Set the new cells to the destination (this) column.
*/ void commit()
{
sc::CellStoreType& rDestCells = mrDestColumn.GetCellStore();
// Stop all formula cells in the destination range first.
sc::CellStoreType::position_type aPos = rDestCells.position(mrBlockPos.miCellPos, mnRowOffset);
mrDestColumn.DetachFormulaCells(aPos, maNewCells.size(), nullptr);
// Move the new cells to the destination range.
sc::CellStoreType::iterator& itDestPos = mrBlockPos.miCellPos;
sc::CellTextAttrStoreType::iterator& itDestAttrPos = mrBlockPos.miCellTextAttrPos;
// Group new formula cells before inserting them.
sc::SharedFormulaUtil::groupFormulaCells(itData, itDataEnd);
// Insert the formula cells to the column.
itDestPos = rDestCells.set(itDestPos, nDestRow, itData, itDataEnd);
// Merge with the previous formula group (if any).
aPos = rDestCells.position(itDestPos, nDestRow);
sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
// Merge with the next formula group (if any).
size_t nNextRow = nDestRow + rNewCell.size; if (mrDestColumn.GetDoc().ValidRow(nNextRow))
{
aPos = rDestCells.position(aPos.first, nNextRow);
sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
}
// Start listening on cells to get them updated by changes of referenced cells
std::vector<SCROW> aNewSharedRows;
aPos = rDestCells.position(itDestPos, nDestRow);
size_t nFormulaCells = std::distance(itData, itDataEnd);
mrDestColumn.AttachNewFormulaCells(aPos, nFormulaCells, aNewSharedRows);
} break; case sc::element_type_empty:
{
itDestPos = rDestCells.set_empty(itDestPos, nDestRow, nDestRow+rNewCell.size-1);
bHasContent = false;
} break; default:
;
}
for (; pp != ppEnd; ++pp)
{
ScFormulaCell& rFC = **pp; if (!mbAllListeners && !rFC.NeedsListening()) continue;
if (rFC.IsSharedTop())
{
sc::SharedFormulaUtil::startListeningAsGroup(*mpCxt, pp);
pp += rFC.GetSharedLength() - 1; // Move to the last cell in the group.
} else
rFC.StartListeningTo(*mpCxt);
}
}
};
if ( cFirstChar == '=' )
{ if ( rString.getLength() == 1 ) // = Text
{
rCell.set(rPool.intern(rString));
} elseif (aParam.meSetTextNumFormat == ScSetStringParam::Always)
{ // Set the cell format type to Text.
applyTextNumFormat(*this, nRow, aParam.mpNumFormatter);
rCell.set(rPool.intern(rString));
} else// = Formula
{
ScFormulaCell* pFormulaCell = new ScFormulaCell(
GetDoc(), ScAddress(nCol, nRow, nTabP), rString,
formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_DEFAULT, eConv),
ScMatrixMode::NONE); if (aParam.mbCheckLinkFormula)
GetDoc().CheckLinkFormulaNeedingCheck( *pFormulaCell->GetCode());
rCell.set( pFormulaCell);
}
} elseif ( cFirstChar == '\'') // 'Text
{ if (aParam.mbHandleApostrophe)
{ // Cell format is not 'Text', and the first char is an apostrophe. // Strip it and set text content. // NOTE: this corresponds with sc/source/ui/view/tabvwsha.cxx // ScTabViewShell::UpdateInputHandler() prepending an apostrophe if // necessary.
rCell.set(rPool.intern(rString.copy(1)));
} else
{ // This is normal text. Take it as-is.
rCell.set(rPool.intern(rString));
}
} else
{ double nVal;
do
{ if (aParam.mbDetectNumberFormat)
{ // Editing a date prefers the format's locale's edit date // format's date acceptance patterns and YMD order. /* TODO: this could be determined already far above when * starting to edit a date "cell" and passed down. A problem * could also be if a new date was typed over or written by a * macro assuming the current locale if that conflicts somehow.
* You can't have everything. See tdf#116579 and tdf#125109. */ if (eNumFormatType == SvNumFormatType::ALL)
eNumFormatType = aParam.mpNumFormatter->GetType(nIndex); bool bForceFormatDate = (eNumFormatType == SvNumFormatType::DATE
|| eNumFormatType == SvNumFormatType::DATETIME); const SvNumberformat* pOldFormat = nullptr;
NfEvalDateFormat eEvalDateFormat = NF_EVALDATEFORMAT_INTL_FORMAT; if (bForceFormatDate)
{
ScRefCellValue aCell = GetCellValue(nRow); if (aCell.getType() == CELLTYPE_VALUE)
{ // Only for an actual date (serial number), not an // arbitrary string or formula or empty cell. // Also ensure the edit date format's pattern is used, // not the display format's.
pOldFormat = aParam.mpNumFormatter->GetEntry( nOldIndex); if (!pOldFormat)
bForceFormatDate = false; else
{
nIndex = aParam.mpNumFormatter->GetEditFormat(
aCell.getValue(), nOldIndex, eNumFormatType, pOldFormat);
eEvalDateFormat = aParam.mpNumFormatter->GetEvalDateFormat();
aParam.mpNumFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT_INTL);
}
} else
{
bForceFormatDate = false;
}
}
if (bForceFormatDate)
aParam.mpNumFormatter->SetEvalDateFormat( eEvalDateFormat);
if (!bIsNumberFormat) break;
// If we have bForceFormatDate, the pOldFormat was/is of // nOldIndex date(+time) type already, if detected type is // compatible keep the original format. if (bForceFormatDate && SvNumberFormatter::IsCompatible(
eNumFormatType, aParam.mpNumFormatter->GetType( nIndex)))
{
nIndex = nOldIndex;
} else
{ // convert back to the original language if a built-in format was detected if (!pOldFormat)
pOldFormat = aParam.mpNumFormatter->GetEntry( nOldIndex ); if (pOldFormat)
nIndex = aParam.mpNumFormatter->GetFormatForLanguageIfBuiltIn(
nIndex, pOldFormat->GetLanguage());
}
rCell.set(nVal); if ( nIndex != nOldIndex)
{ // #i22345# New behavior: Apply the detected number format only if // the old one was the default number, date, time or boolean format. // Exception: If the new format is boolean, always apply it.
bool bOverwrite = false; if ( pOldFormat )
{
SvNumFormatType nOldType = pOldFormat->GetMaskedType(); if ( nOldType == SvNumFormatType::NUMBER || nOldType == SvNumFormatType::DATE ||
nOldType == SvNumFormatType::TIME || nOldType == SvNumFormatType::LOGICAL )
{ if ( nOldIndex == aParam.mpNumFormatter->GetStandardFormat(
nOldType, pOldFormat->GetLanguage() ) )
{
bOverwrite = true; // default of these types can be overwritten
}
}
} if ( !bOverwrite && aParam.mpNumFormatter->GetType( nIndex ) == SvNumFormatType::LOGICAL )
{
bOverwrite = true; // overwrite anything if boolean was detected
}
if ( bOverwrite )
{
ApplyAttr( nRow, SfxUInt32Item( ATTR_VALUE_FORMAT,
nIndex) );
bNumFmtSet = true;
}
}
} elseif (aParam.meSetTextNumFormat == ScSetStringParam::Never ||
aParam.meSetTextNumFormat == ScSetStringParam::SpecialNumberOnly)
{ // Only check if the string is a regular number. const LocaleDataWrapper* pLocale = aParam.mpNumFormatter->GetLocaleData(); if (!pLocale) break;
if (!ScStringUtil::parseSimpleNumber(rString, dsep, gsep, dsepa, nVal, aParam.mbDetectScientificNumberFormat)) break;
rCell.set(nVal);
}
} while (false);
if (rCell.getType() == CELLTYPE_NONE)
{ // If we reach here with ScSetStringParam::SpecialNumberOnly it // means a simple number was not detected above, so test for // special numbers. In any case ScSetStringParam::Always does not // mean always, but only always for content that could be any // numeric. if ((aParam.meSetTextNumFormat == ScSetStringParam::Always ||
aParam.meSetTextNumFormat == ScSetStringParam::SpecialNumberOnly) &&
aParam.mpNumFormatter->IsNumberFormat(rString, nIndex, nVal))
{ // Set the cell format type to Text.
applyTextNumFormat(*this, nRow, aParam.mpNumFormatter);
}
rCell.set(rPool.intern(rString));
}
}
return bNumFmtSet;
}
/** * Returns true if the cell format was set as well
*/ bool ScColumn::SetString( SCROW nRow, SCTAB nTabP, const OUString& rString,
formula::FormulaGrammar::AddressConvention eConv, const ScSetStringParam* pParam )
{ if (!GetDoc().ValidRow(nRow)) returnfalse;
// rats, yet another "spool" // Sadly there is no other way to change the Pool than to // "spool" the Object through a corresponding Engine
EditEngine& rEngine = GetDoc().GetEditEngine();
rEngine.SetText(rEditText);
SetEditText(rBlockPos, nRow, rEngine.CreateTextObject());
}
// rats, yet another "spool" // Sadly there is no other way to change the Pool than to // "spool" the Object through a corresponding Engine
EditEngine& rEngine = GetDoc().GetEditEngine();
rEngine.SetText(rEditText);
SetEditText(nRow, rEngine.CreateTextObject());
}
// Reget position_type as the type may have changed to formula, block and // block size changed, ...
aPos = maCells.position(nRow);
AttachNewFormulaCells(aPos, rCells.size(), aNewSharedRows);
returntrue;
}
svl::SharedString ScColumn::GetSharedString( SCROW nRow ) const
{
sc::CellStoreType::const_position_type aPos = maCells.position(nRow); switch (aPos.first->type)
{ case sc::element_type_string: return sc::string_block::at(*aPos.first->data, aPos.second); case sc::element_type_edittext:
{ const EditTextObject* pObj = sc::edittext_block::at(*aPos.first->data, aPos.second);
std::vector<svl::SharedString> aSSs = pObj->GetSharedStrings(); if (aSSs.size() != 1) // We don't handle multiline content for now. return svl::SharedString();
switch (rCell.getType())
{ case CELLTYPE_VALUE:
fVal = rCell.getDouble(); break;
case CELLTYPE_FORMULA:
{
ScFormulaCell* pFC = rCell.getFormula();
FormulaError nErr = pFC->GetErrCode(); if (nErr != FormulaError::NONE)
{ // Error cell is evaluated as string (for now).
OUString aErr = ScGlobal::GetErrorString(nErr); if (!aErr.isEmpty())
{
mrFilterEntries.push_back(ScTypedStrData(std::move(aErr), 0.0, 0.0, ScTypedStrData::Standard, false, mbFilteredRow)); return;
}
} else
fVal = pFC->GetValue();
} break; default:
;
}
SvNumFormatType nType = rContext.NFGetType(nFormat); bool bDate = false; if ((nType & SvNumFormatType::DATE) && !(nType & SvNumFormatType::TIME))
{ // special case for date values. Disregard the time // element if the number format is of date type.
fVal = rtl::math::approxFloor(fVal);
mrFilterEntries.mbHasDates = true;
bDate = true; // Convert string representation to ISO 8601 date to eliminate // locale dependent behaviour later when filtering for dates.
sal_uInt32 nIndex = rContext.NFGetFormatIndex( NF_DATE_DIN_YYYYMMDD);
aStr = rContext.NFGetInputLineString( fVal, nIndex);
} elseif (nType == SvNumFormatType::DATETIME)
{ // special case for datetime values. // Convert string representation to ISO 8601 (with blank instead of T) datetime // to eliminate locale dependent behaviour later when filtering for datetimes.
sal_uInt32 nIndex = rContext.NFGetFormatIndex(NF_DATETIME_ISO_YYYYMMDD_HHMMSS);
aStr = rContext.NFGetInputLineString(fVal, nIndex);
} // store the formatted/rounded value for filtering if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0 && !bDate)
mrFilterEntries.push_back(ScTypedStrData(std::move(aStr), fVal, rColumn.GetDoc().RoundValueAsShown(fVal, nFormat), ScTypedStrData::Value, bDate, mbFilteredRow)); else
mrFilterEntries.push_back(ScTypedStrData(std::move(aStr), fVal, fVal, ScTypedStrData::Value, bDate, mbFilteredRow));
}
ScAddress aCell(GetCol(), 0, GetTab());
ScDocument& rDoc = GetDoc();
ScConditionalFormatList* pCondFormList = rDoc.GetCondFormList(aCell.Tab()); // cache the output of GetCondResult const ScPatternAttr* pPrevPattern = nullptr;
ScRefCellValue aPrevCellValue;
Color aPrevPatternColor;
Color aPrevInsertColor;
ScConditionalFormat* pCondFormat = nullptr; const ScCondFormatIndexes* pCondFormats = nullptr;
Color aBackgroundBrushColor;
SCROW nPatternStartRow = -1;
SCROW nPatternEndRow = -1; while (nRow1 <= nRow2)
{
aCell.SetRow(nRow1);
Color aBackColor; bool bCondBackColor = false;
if (nRow1 <= nPatternEndRow)
; // then the previous value of pPattern and pCondFormat and pCondFormats and aBackgroundBrushColor is still valid else
{ if (const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(nPatternStartRow, nPatternEndRow, nRow1))
{
pCondFormats = &pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData();
sal_uInt32 nIndex = 0; if(!pCondFormats->empty())
nIndex = (*pCondFormats)[0]; if (nIndex)
{
assert(pCondFormList);
pCondFormat = pCondFormList->GetFormat( nIndex );
} else
pCondFormat = nullptr;
aBackgroundBrushColor = pPattern->GetItem(ATTR_BACKGROUND).GetColor();
if (!pCondFormats->empty())
{ // Speed up processing when dealing with runs of identical cells. This avoids // an expensive GetCondResult call.
ScRefCellValue aCellValue = GetCellValue(nRow1); if (pPrevPattern == pPattern && aCellValue == aPrevCellValue)
{
aBackColor = aPrevPatternColor;
bCondBackColor = true;
} else
{ const SfxItemSet* pCondSet = rDoc.GetCondResult(GetCol(), nRow1, GetTab()); const SvxBrushItem* pCondBrush = &pPattern->GetItem(ATTR_BACKGROUND, pCondSet);
aBackColor = pCondBrush->GetColor();
bCondBackColor = true;
aPrevCellValue = aCellValue;
pPrevPattern = pPattern;
aPrevPatternColor = aBackColor;
}
}
}
}
if (pCondFormat)
{ for (size_t nFormat = 0; nFormat < pCondFormat->size(); nFormat++)
{ auto aEntry = pCondFormat->GetEntry(nFormat); if (aEntry->GetType() == ScFormatEntry::Type::Colorscale)
{ const ScColorScaleFormat* pColFormat = static_cast<const ScColorScaleFormat*>(aEntry);
std::optional<Color> oColor = pColFormat->GetColor(aCell); if (oColor)
{
aBackColor = *oColor;
bCondBackColor = true;
}
}
}
}
if (!bCondBackColor)
{
aBackColor = aBackgroundBrushColor;
}
bool prev()
{ if (!has())
{ // Not in a string block. Move back until we hit a string block. while (!has())
{ if (maPos.first == miBeg) returnfalse;
--maPos.first; // move to the preceding block.
maPos.second = maPos.first->size - 1; // last cell in the block.
} returntrue;
}
// We are in a string block. if (maPos.second > 0)
{ // Move back one cell in the same block.
--maPos.second;
} else
{ // Move back to the preceding string block. while (true)
{ if (maPos.first == miBeg) returnfalse;
// Move to the last cell of the previous block.
--maPos.first;
maPos.second = maPos.first->size - 1; if (has()) break;
}
} returntrue;
}
bool next()
{ if (!has())
{ // Not in a string block. Move forward until we hit a string block. while (!has())
{
++maPos.first; if (maPos.first == miEnd) returnfalse;
maPos.second = 0; // First cell in this block.
} returntrue;
}
// We are in a string block.
++maPos.second; if (maPos.second >= maPos.first->size)
{ // Move to the next string block. while (true)
{
++maPos.first; if (maPos.first == miEnd) returnfalse;
maPos.second = 0; if (has()) break;
}
} returntrue;
}
// GetDataEntries - Strings from continuous Section around nRow bool ScColumn::GetDataEntries(
SCROW nStartRow, std::set<ScTypedStrData>& rStrings) const
{ // Start at the specified row position, and collect all string values // going upward and downward directions in parallel. The start position // cell must be skipped.
bool bMoveUp = aItrUp.valid(); if (!bMoveUp) // Current cell is invalid. returnfalse;
// Skip the start position cell.
bMoveUp = aItrUp.prev(); // Find the previous string cell position.
bool bMoveDown = aItrDown.valid(); if (bMoveDown && !aItrDown.has())
bMoveDown = aItrDown.next(); // Find the next string cell position.
bool bFound = false; while (bMoveUp)
{ // Get the current string and move up.
OUString aStr = aItrUp.get(); if (!aStr.isEmpty())
{ if (rStrings.insert(ScTypedStrData(std::move(aStr))).second)
bFound = true;
}
bMoveUp = aItrUp.prev();
}
while (bMoveDown)
{ // Get the current string and move down.
OUString aStr = aItrDown.get(); if (!aStr.isEmpty())
{ if (rStrings.insert(ScTypedStrData(std::move(aStr))).second)
bFound = true;
}
bMoveDown = aItrDown.next();
}
return bFound;
}
namespace {
class FormulaToValueHandler
{ struct Entry
{
SCROW mnRow;
ScCellValue maValue;
/** * Return true if there is a string or editcell in the range
*/ bool ScColumn::HasStringCells( SCROW nStartRow, SCROW nEndRow ) const
{
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
sc::CellStoreType::const_iterator it = aPos.first;
size_t nOffset = aPos.second;
SCROW nRow = nStartRow; for (; it != maCells.end() && nRow <= nEndRow; ++it)
{ if (it->type == sc::element_type_string || it->type == sc::element_type_edittext) returntrue;
void processCell(size_t nRow, const ScRefCellValue& rCell)
{ const Color* pColor;
sal_uInt32 nFormat = mrColumn.GetAttr(nRow, ATTR_VALUE_FORMAT).GetValue();
OUString aString = ScCellFormat::GetString(rCell, nFormat, &pColor, nullptr, mrColumn.GetDoc());
sal_Int32 nLen = 0; if (mbOctetEncoding)
{
OString aOString; if (!aString.convertToString(&aOString, meCharSet,
RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))
{ // TODO: anything? this is used by the dBase export filter // that throws an error anyway, but in case of another // context we might want to indicate a conversion error // early.
}
nLen = aOString.getLength();
} else
nLen = aString.getLength() * sizeof(sal_Unicode);
void processCell(size_t nRow, ScRefCellValue& rCell)
{
sal_uInt16 nCellPrecision = mnMaxGeneralPrecision; if (rCell.getType() == CELLTYPE_FORMULA)
{ if (!rCell.getFormula()->IsValue()) return;
// Limit unformatted formula cell precision to precision // encountered so far, if any, otherwise we'd end up with 15 just // because of =1/3 ... If no precision yet then arbitrarily limit // to a maximum of 4 unless a maximum general precision is set. if (mnPrecision)
nCellPrecision = mnPrecision; else
nCellPrecision = (mnMaxGeneralPrecision >= 15) ? 4 : mnMaxGeneralPrecision;
}
if (mnPrecision)
{ // less than mnPrecision in string => widen it // more => shorten it
sal_Int32 nTmp = aString.indexOf(aSep); if ( nTmp == -1 )
nLen += mnPrecision + aSep.getLength(); else
{
nTmp = aString.getLength() - (nTmp + aSep.getLength()); if (nTmp != mnPrecision)
nLen += mnPrecision - nTmp; // nPrecision > nTmp : nLen + Diff // nPrecision < nTmp : nLen - Diff
}
}
// Enlarge for sign if necessary. Bear in mind that // GetMaxNumberStringLen() is for determining dBase decimal field width // and precision where the overall field width must include the sign. // Fitting -1 into "#.##" (width 4, 2 decimals) does not work. if (mbHaveSigned && fVal >= 0.0)
++nLen;
if (mnMaxLen < nLen)
mnMaxLen = nLen;
}
public:
MaxNumStringLenHandler(const ScColumn& rColumn, sal_uInt16 nMaxGeneralPrecision) :
mrColumn(rColumn),
mnMaxLen(0), mnPrecision(0), mnMaxGeneralPrecision(nMaxGeneralPrecision),
mbHaveSigned(false)
{ // Limit the decimals passed to doubleToUString(). // Also, the dBaseIII maximum precision is 15. if (mnMaxGeneralPrecision > 15)
mnMaxGeneralPrecision = 15;
}
sc::formula_block::iterator it = sc::formula_block::begin(*node.data);
sc::formula_block::iterator itEnd = sc::formula_block::end(*node.data);
// This block should never be empty.
ScFormulaCell* pPrev = *it;
ScFormulaCellGroupRef xPrevGrp = pPrev->GetCellGroup(); if (xPrevGrp)
{ // Move to the cell after the last cell of the current group.
std::advance(it, xPrevGrp->mnLength);
nRow += xPrevGrp->mnLength;
} else
{
++it;
++nRow;
}
ScFormulaCell::CompareState eCompState = pPrev->CompareByTokenArray(*pCur); if (eCompState == ScFormulaCell::NotEqual)
{ // different formula tokens. if (xCurGrp)
{ // Move to the cell after the last cell of the current group. if (xCurGrp->mnLength > std::distance(it, itEnd)) throw css::lang::IllegalArgumentException();
std::advance(it, xCurGrp->mnLength);
nRow += xCurGrp->mnLength;
} else
{
++it;
++nRow;
}
continue;
}
// Formula tokens equal those of the previous formula cell or cell group. if (xPrevGrp)
{ // Previous cell is a group. if (xCurGrp)
{ // The current cell is a group. Merge these two groups.
xPrevGrp->mnLength += xCurGrp->mnLength;
pCur->SetCellGroup(xPrevGrp);
sc::formula_block::iterator itGrpEnd = it; if (xCurGrp->mnLength > std::distance(itGrpEnd, itEnd)) throw css::lang::IllegalArgumentException();
std::advance(itGrpEnd, xCurGrp->mnLength); for (++it; it != itGrpEnd; ++it)
{
ScFormulaCell* pCell = *it;
pCell->SetCellGroup(xPrevGrp);
}
nRow += xCurGrp->mnLength;
} else
{ // Add this cell to the previous group.
pCur->SetCellGroup(xPrevGrp);
++xPrevGrp->mnLength;
++nRow;
++it;
}
} elseif (xCurGrp)
{ // Previous cell is a regular cell and current cell is a group.
nRow += xCurGrp->mnLength; if (xCurGrp->mnLength > std::distance(it, itEnd)) throw css::lang::IllegalArgumentException();
std::advance(it, xCurGrp->mnLength);
pPrev->SetCellGroup(xCurGrp);
xCurGrp->mpTopCell = pPrev;
++xCurGrp->mnLength;
xPrevGrp = xCurGrp;
} else
{ // Both previous and current cells are regular cells.
assert(pPrev->aPos.Row() == static_cast<SCROW>(nRow - 1)); // silence set-but-unused warning for non-dbg build
(void) nRow;
xPrevGrp = pPrev->CreateCellGroup(2, eCompState == ScFormulaCell::EqualInvariant);
pCur->SetCellGroup(xPrevGrp);
++nRow;
++it;
}
if (mpGroupPos)
mpGroupPos->push_back(pCur->aPos);
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.