/* -*- 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/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { struct ColAttr { bool mbLatinNumFmtOnly; ColAttr() : mbLatinNumFmtOnly(false) {} }; struct TabAttr { std::vector maCols; }; } struct ScDocumentImportImpl { ScDocument& mrDoc; sc::StartListeningContext maListenCxt; std::vector maBlockPosSet; SvtScriptType mnDefaultScriptNumeric; std::vector maTabAttrs; explicit ScDocumentImportImpl(ScDocument& rDoc) : mrDoc(rDoc), maListenCxt(rDoc), mnDefaultScriptNumeric(SvtScriptType::UNKNOWN) {} static bool isValid( size_t nTab, size_t nCol ) { return (nTab <= size_t(MAXTAB) && nCol <= size_t(MAXCOL)); } ColAttr* getColAttr( size_t nTab, size_t nCol ) { if (!isValid(nTab, nCol)) return nullptr; if (nTab >= maTabAttrs.size()) maTabAttrs.resize(nTab+1); TabAttr& rTab = maTabAttrs[nTab]; if (nCol >= rTab.maCols.size()) rTab.maCols.resize(nCol+1); return &rTab.maCols[nCol]; } sc::ColumnBlockPosition* getBlockPosition( SCTAB nTab, SCCOL nCol ) { if (!isValid(nTab, nCol)) return nullptr; if (size_t(nTab) >= maBlockPosSet.size()) { for (SCTAB i = maBlockPosSet.size(); i <= nTab; ++i) maBlockPosSet.emplace_back(mrDoc, i); } sc::TableColumnBlockPositionSet& rTab = maBlockPosSet[nTab]; return rTab.getBlockPosition(nCol); } void initForSheets() { size_t n = mrDoc.GetTableCount(); for (size_t i = maBlockPosSet.size(); i < n; ++i) maBlockPosSet.emplace_back(mrDoc, i); if (maTabAttrs.size() < n) maTabAttrs.resize(n); } }; ScDocumentImport::Attrs::Attrs() : mbLatinNumFmtOnly(false) {} ScDocumentImport::Attrs::~Attrs() {} ScDocumentImport::ScDocumentImport(ScDocument& rDoc) : mpImpl(new ScDocumentImportImpl(rDoc)) {} ScDocumentImport::~ScDocumentImport() { } ScDocument& ScDocumentImport::getDoc() { return mpImpl->mrDoc; } const ScDocument& ScDocumentImport::getDoc() const { return mpImpl->mrDoc; } void ScDocumentImport::initForSheets() { mpImpl->initForSheets(); } void ScDocumentImport::setDefaultNumericScript(SvtScriptType nScript) { mpImpl->mnDefaultScriptNumeric = nScript; } void ScDocumentImport::setCellStyleToSheet(SCTAB nTab, const ScStyleSheet& rStyle) { ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab); if (!pTab) return; pTab->ApplyStyleArea(0, 0, MAXCOL, MAXROW, rStyle); } SCTAB ScDocumentImport::getSheetIndex(const OUString& rName) const { SCTAB nTab = -1; if (!mpImpl->mrDoc.GetTable(rName, nTab)) return -1; return nTab; } SCTAB ScDocumentImport::getSheetCount() const { return mpImpl->mrDoc.maTabs.size(); } bool ScDocumentImport::appendSheet(const OUString& rName) { SCTAB nTabCount = mpImpl->mrDoc.maTabs.size(); if (!ValidTab(nTabCount)) return false; mpImpl->mrDoc.maTabs.emplace_back(new ScTable(&mpImpl->mrDoc, nTabCount, rName)); return true; } void ScDocumentImport::setSheetName(SCTAB nTab, const OUString& rName) { mpImpl->mrDoc.SetTabNameOnLoad(nTab, rName); } void ScDocumentImport::setOriginDate(sal_uInt16 nYear, sal_uInt16 nMonth, sal_uInt16 nDay) { if (!mpImpl->mrDoc.pDocOptions) mpImpl->mrDoc.pDocOptions.reset( new ScDocOptions ); mpImpl->mrDoc.pDocOptions->SetDate(nDay, nMonth, nYear); } void ScDocumentImport::setAutoInput(const ScAddress& rPos, const OUString& rStr, const ScSetStringParam* pStringParam) { ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); if (!pTab) return; sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col()); if (!pBlockPos) return; // If ScSetStringParam was given, ScColumn::ParseString() shall take care // of checking. Ensure caller said so. assert(!pStringParam || pStringParam->mbCheckLinkFormula); ScCellValue aCell; pTab->aCol[rPos.Col()].ParseString( aCell, rPos.Row(), rPos.Tab(), rStr, mpImpl->mrDoc.GetAddressConvention(), pStringParam); sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells; switch (aCell.meType) { case CELLTYPE_STRING: // string is copied. pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), *aCell.mpString); break; case CELLTYPE_EDIT: // Cell takes the ownership of the text object. pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aCell.mpEditText); aCell.mpEditText = nullptr; break; case CELLTYPE_VALUE: pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aCell.mfValue); break; case CELLTYPE_FORMULA: if (!pStringParam) mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *aCell.mpFormula->GetCode()); // This formula cell instance is directly placed in the document without copying. pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aCell.mpFormula); aCell.mpFormula = nullptr; break; default: pBlockPos->miCellPos = rCells.set_empty(pBlockPos->miCellPos, rPos.Row(), rPos.Row()); } } void ScDocumentImport::setNumericCell(const ScAddress& rPos, double fVal) { ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); if (!pTab) return; sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col()); if (!pBlockPos) return; sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells; pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), fVal); } void ScDocumentImport::setStringCell(const ScAddress& rPos, const OUString& rStr) { ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); if (!pTab) return; sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col()); if (!pBlockPos) return; svl::SharedString aSS = mpImpl->mrDoc.GetSharedStringPool().intern(rStr); if (!aSS.getData()) return; sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells; pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aSS); } void ScDocumentImport::setEditCell(const ScAddress& rPos, std::unique_ptr pEditText) { ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); if (!pTab) return; sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col()); if (!pBlockPos) return; pEditText->NormalizeString(mpImpl->mrDoc.GetSharedStringPool()); sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells; pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), pEditText.release()); } void ScDocumentImport::setFormulaCell( const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar, const double* pResult ) { ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); if (!pTab) return; sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col()); if (!pBlockPos) return; std::unique_ptr pFC = std::make_unique(&mpImpl->mrDoc, rPos, rFormula, eGrammar); mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pFC->GetCode()); if (pResult) { // Set cached result to this formula cell. pFC->SetResultDouble(*pResult); } sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells; pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), pFC.release()); } void ScDocumentImport::setFormulaCell( const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar, const OUString& rResult ) { ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); if (!pTab) return; sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col()); if (!pBlockPos) return; std::unique_ptr pFC = std::make_unique(&mpImpl->mrDoc, rPos, rFormula, eGrammar); mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pFC->GetCode()); // Set cached result to this formula cell. pFC->SetHybridString(mpImpl->mrDoc.GetSharedStringPool().intern(rResult)); sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells; pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), pFC.release()); } void ScDocumentImport::setFormulaCell(const ScAddress& rPos, std::unique_ptr pArray) { ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); if (!pTab) return; sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col()); if (!pBlockPos) return; std::unique_ptr pFC = std::make_unique(&mpImpl->mrDoc, rPos, std::move(pArray)); mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pFC->GetCode()); sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells; pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), pFC.release()); } void ScDocumentImport::setFormulaCell(const ScAddress& rPos, ScFormulaCell* pCell) { ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); if (!pTab) return; sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col()); if (!pBlockPos) return; if (pCell) mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pCell->GetCode()); sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells; pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), pCell); } void ScDocumentImport::setMatrixCells( const ScRange& rRange, const ScTokenArray& rArray, formula::FormulaGrammar::Grammar eGram) { const ScAddress& rBasePos = rRange.aStart; ScTable* pTab = mpImpl->mrDoc.FetchTable(rBasePos.Tab()); if (!pTab) return; sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rBasePos.Tab(), rBasePos.Col()); if (!pBlockPos) return; if (utl::ConfigManager::IsFuzzing()) //just too slow return; sc::CellStoreType& rCells = pTab->aCol[rBasePos.Col()].maCells; // Set the master cell. ScFormulaCell* pCell = new ScFormulaCell(&mpImpl->mrDoc, rBasePos, rArray, eGram, ScMatrixMode::Formula); mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pCell->GetCode()); pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rBasePos.Row(), pCell); // Matrix formulas currently need re-calculation on import. pCell->SetMatColsRows( rRange.aEnd.Col()-rRange.aStart.Col()+1, rRange.aEnd.Row()-rRange.aStart.Row()+1); // Set the reference cells. ScSingleRefData aRefData; aRefData.InitFlags(); aRefData.SetColRel(true); aRefData.SetRowRel(true); aRefData.SetTabRel(true); aRefData.SetAddress(rBasePos, rBasePos); ScTokenArray aArr; // consists only of one single reference token. formula::FormulaToken* t = aArr.AddMatrixSingleReference(aRefData); ScAddress aPos = rBasePos; for (SCROW nRow = rRange.aStart.Row()+1; nRow <= rRange.aEnd.Row(); ++nRow) { // Token array must be cloned so that each formula cell receives its own copy. aPos.SetRow(nRow); // Reference in each cell must point to the origin cell relative to the current cell. aRefData.SetAddress(rBasePos, aPos); *t->GetSingleRef() = aRefData; std::unique_ptr pTokArr(aArr.Clone()); pCell = new ScFormulaCell(&mpImpl->mrDoc, aPos, *pTokArr, eGram, ScMatrixMode::Reference); pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, aPos.Row(), pCell); } for (SCCOL nCol = rRange.aStart.Col()+1; nCol <= rRange.aEnd.Col(); ++nCol) { pBlockPos = mpImpl->getBlockPosition(rBasePos.Tab(), nCol); if (!pBlockPos) return; sc::CellStoreType& rColCells = pTab->aCol[nCol].maCells; aPos.SetCol(nCol); for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow) { aPos.SetRow(nRow); aRefData.SetAddress(rBasePos, aPos); *t->GetSingleRef() = aRefData; std::unique_ptr pTokArr(aArr.Clone()); pCell = new ScFormulaCell(&mpImpl->mrDoc, aPos, *pTokArr, eGram, ScMatrixMode::Reference); pBlockPos->miCellPos = rColCells.set(pBlockPos->miCellPos, aPos.Row(), pCell); } } } void ScDocumentImport::setTableOpCells(const ScRange& rRange, const ScTabOpParam& rParam) { SCTAB nTab = rRange.aStart.Tab(); SCCOL nCol1 = rRange.aStart.Col(); SCROW nRow1 = rRange.aStart.Row(); SCCOL nCol2 = rRange.aEnd.Col(); SCROW nRow2 = rRange.aEnd.Row(); ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab); if (!pTab) return; ScDocument* pDoc = &mpImpl->mrDoc; ScRefAddress aRef; OUStringBuffer aFormulaBuf; aFormulaBuf.append('='); aFormulaBuf.append(ScCompiler::GetNativeSymbol(ocTableOp)); aFormulaBuf.append(ScCompiler::GetNativeSymbol(ocOpen)); OUString aSep = ScCompiler::GetNativeSymbol(ocSep); if (rParam.meMode == ScTabOpParam::Column) // column only { aRef.Set(rParam.aRefFormulaCell.GetAddress(), true, false, false); aFormulaBuf.append(aRef.GetRefString(pDoc, nTab)); aFormulaBuf.append(aSep); aFormulaBuf.append(rParam.aRefColCell.GetRefString(pDoc, nTab)); aFormulaBuf.append(aSep); aRef.Set(nCol1, nRow1, nTab, false, true, true); aFormulaBuf.append(aRef.GetRefString(pDoc, nTab)); nCol1++; nCol2 = std::min( nCol2, static_cast(rParam.aRefFormulaEnd.Col() - rParam.aRefFormulaCell.Col() + nCol1 + 1)); } else if (rParam.meMode == ScTabOpParam::Row) // row only { aRef.Set(rParam.aRefFormulaCell.GetAddress(), false, true, false); aFormulaBuf.append(aRef.GetRefString(pDoc, nTab)); aFormulaBuf.append(aSep); aFormulaBuf.append(rParam.aRefRowCell.GetRefString(pDoc, nTab)); aFormulaBuf.append(aSep); aRef.Set(nCol1, nRow1, nTab, true, false, true); aFormulaBuf.append(aRef.GetRefString(pDoc, nTab)); ++nRow1; nRow2 = std::min( nRow2, rParam.aRefFormulaEnd.Row() - rParam.aRefFormulaCell.Row() + nRow1 + 1); } else // both { aFormulaBuf.append(rParam.aRefFormulaCell.GetRefString(pDoc, nTab)); aFormulaBuf.append(aSep); aFormulaBuf.append(rParam.aRefColCell.GetRefString(pDoc, nTab)); aFormulaBuf.append(aSep); aRef.Set(nCol1, nRow1 + 1, nTab, false, true, true); aFormulaBuf.append(aRef.GetRefString(pDoc, nTab)); aFormulaBuf.append(aSep); aFormulaBuf.append(rParam.aRefRowCell.GetRefString(pDoc, nTab)); aFormulaBuf.append(aSep); aRef.Set(nCol1 + 1, nRow1, nTab, true, false, true); aFormulaBuf.append(aRef.GetRefString(pDoc, nTab)); ++nCol1; ++nRow1; } aFormulaBuf.append(ScCompiler::GetNativeSymbol(ocClose)); ScFormulaCell aRefCell( pDoc, ScAddress(nCol1, nRow1, nTab), aFormulaBuf.makeStringAndClear(), formula::FormulaGrammar::GRAM_NATIVE, ScMatrixMode::NONE); for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) { sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(nTab, nCol); if (!pBlockPos) // Something went horribly wrong. return; sc::CellStoreType& rColCells = pTab->aCol[nCol].maCells; for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) { ScAddress aPos(nCol, nRow, nTab); ScFormulaCell* pCell = new ScFormulaCell(aRefCell, *pDoc, aPos); pBlockPos->miCellPos = rColCells.set(pBlockPos->miCellPos, nRow, pCell); } } } void ScDocumentImport::setAttrEntries( SCTAB nTab, SCCOL nCol, Attrs&& rAttrs ) { ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab); if (!pTab) return; ScColumn* pCol = pTab->FetchColumn(nCol); if (!pCol) return; ColAttr* pColAttr = mpImpl->getColAttr(nTab, nCol); if (pColAttr) pColAttr->mbLatinNumFmtOnly = rAttrs.mbLatinNumFmtOnly; pCol->pAttrArray->SetAttrEntries(std::move(rAttrs.mvData)); } void ScDocumentImport::setRowsVisible(SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, bool bVisible) { if (!bVisible) { getDoc().ShowRows(nRowStart, nRowEnd, nTab, false); getDoc().SetDrawPageSize(nTab); getDoc().UpdatePageBreaks( nTab ); } else { assert(false); } } void ScDocumentImport::setMergedCells(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) { ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab); if (!pTab) return; pTab->SetMergedCells(nCol1, nRow1, nCol2, nRow2); } namespace { class CellStoreInitializer { // The pimpl pattern here is intentional. // // The problem with having the attributes in CellStoreInitializer // directly is that, as a functor, it might be copied around. In // that case miPos in _copied_ object points to maAttrs in the // original object, not in the copy. So later, deep in mdds, we end // up comparing iterators from different sequences. // // This could be solved by defining copy constructor and operator=, // but given the limited usage of the class, I think it is simpler // to let copies share the state. struct Impl { sc::CellTextAttrStoreType maAttrs; sc::CellTextAttrStoreType::iterator miPos; SvtScriptType const mnScriptNumeric; explicit Impl(const SvtScriptType nScriptNumeric) : maAttrs(MAXROWCOUNT), miPos(maAttrs.begin()), mnScriptNumeric(nScriptNumeric) {} }; ScDocumentImportImpl& mrDocImpl; SCTAB const mnTab; SCCOL const mnCol; public: CellStoreInitializer( ScDocumentImportImpl& rDocImpl, SCTAB nTab, SCCOL nCol ) : mrDocImpl(rDocImpl), mnTab(nTab), mnCol(nCol), mpImpl(new Impl(mrDocImpl.mnDefaultScriptNumeric)) {} std::shared_ptr mpImpl; void operator() (const sc::CellStoreType::value_type& node) { if (node.type == sc::element_type_empty) return; // Fill with default values for non-empty cell segments. sc::CellTextAttr aDefault; switch (node.type) { case sc::element_type_numeric: { aDefault.mnScriptType = mpImpl->mnScriptNumeric; const ColAttr* p = mrDocImpl.getColAttr(mnTab, mnCol); if (p && p->mbLatinNumFmtOnly) aDefault.mnScriptType = SvtScriptType::LATIN; } break; case sc::element_type_formula: { const ColAttr* p = mrDocImpl.getColAttr(mnTab, mnCol); if (p && p->mbLatinNumFmtOnly) { // We can assume latin script type if the block only // contains formula cells with numeric results. ScFormulaCell** pp = &sc::formula_block::at(*node.data, 0); ScFormulaCell** ppEnd = pp + node.size; bool bNumResOnly = true; for (; pp != ppEnd; ++pp) { const ScFormulaCell& rCell = **pp; if (!rCell.IsValueNoError()) { bNumResOnly = false; break; } } if (bNumResOnly) aDefault.mnScriptType = SvtScriptType::LATIN; } } break; default: ; } std::vector aDefaults(node.size, aDefault); mpImpl->miPos = mpImpl->maAttrs.set(mpImpl->miPos, node.position, aDefaults.begin(), aDefaults.end()); if (node.type == sc::element_type_formula) { // Have all formula cells start listening to the document. ScFormulaCell** pp = &sc::formula_block::at(*node.data, 0); ScFormulaCell** ppEnd = pp + node.size; for (; pp != ppEnd; ++pp) { ScFormulaCell& rFC = **pp; if (rFC.IsSharedTop()) { // Register formula cells as a group. sc::SharedFormulaUtil::startListeningAsGroup(mrDocImpl.maListenCxt, pp); pp += rFC.GetSharedLength() - 1; // Move to the last one in the group. } else rFC.StartListeningTo(mrDocImpl.maListenCxt); } } } void swap(sc::CellTextAttrStoreType& rAttrs) { mpImpl->maAttrs.swap(rAttrs); } }; } void ScDocumentImport::finalize() { // Populate the text width and script type arrays in all columns. Also // activate all formula cells. for (auto& rxTab : mpImpl->mrDoc.maTabs) { if (!rxTab) continue; ScTable& rTab = *rxTab; SCCOL nNumCols = rTab.aCol.size(); for (SCCOL nColIdx = 0; nColIdx < nNumCols; ++nColIdx) initColumn(rTab.aCol[nColIdx]); } mpImpl->mrDoc.finalizeOutlineImport(); } void ScDocumentImport::initColumn(ScColumn& rCol) { rCol.RegroupFormulaCells(); CellStoreInitializer aFunc(*mpImpl, rCol.nTab, rCol.nCol); std::for_each(rCol.maCells.begin(), rCol.maCells.end(), aFunc); aFunc.swap(rCol.maCellTextAttrs); rCol.CellStorageModified(); } namespace { class CellStoreAfterImportBroadcaster { public: CellStoreAfterImportBroadcaster() {} void operator() (const sc::CellStoreType::value_type& node) { if (node.type == sc::element_type_formula) { // Broadcast all formula cells marked for recalc. ScFormulaCell** pp = &sc::formula_block::at(*node.data, 0); ScFormulaCell** ppEnd = pp + node.size; for (; pp != ppEnd; ++pp) { if ((*pp)->GetCode()->IsRecalcModeMustAfterImport()) (*pp)->SetDirty(); } } } }; } void ScDocumentImport::broadcastRecalcAfterImport() { sc::AutoCalcSwitch aACSwitch( mpImpl->mrDoc, false); ScBulkBroadcast aBulkBroadcast( mpImpl->mrDoc.GetBASM(), SfxHintId::ScDataChanged); for (auto& rxTab : mpImpl->mrDoc.maTabs) { if (!rxTab) continue; ScTable& rTab = *rxTab; SCCOL nNumCols = rTab.aCol.size(); for (SCCOL nColIdx = 0; nColIdx < nNumCols; ++nColIdx) broadcastRecalcAfterImportColumn(rTab.aCol[nColIdx]); } } void ScDocumentImport::broadcastRecalcAfterImportColumn(ScColumn& rCol) { CellStoreAfterImportBroadcaster aFunc; std::for_each(rCol.maCells.begin(), rCol.maCells.end(), aFunc); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */