diff options
Diffstat (limited to 'sc/source/core/data/documentimport.cxx')
-rw-r--r-- | sc/source/core/data/documentimport.cxx | 859 |
1 files changed, 859 insertions, 0 deletions
diff --git a/sc/source/core/data/documentimport.cxx b/sc/source/core/data/documentimport.cxx new file mode 100644 index 000000000..7ce2d0a00 --- /dev/null +++ b/sc/source/core/data/documentimport.cxx @@ -0,0 +1,859 @@ +/* -*- 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 <documentimport.hxx> +#include <document.hxx> +#include <table.hxx> +#include <column.hxx> +#include <formulacell.hxx> +#include <docoptio.hxx> +#include <mtvelements.hxx> +#include <tokenarray.hxx> +#include <stringutil.hxx> +#include <compiler.hxx> +#include <paramisc.hxx> +#include <listenercontext.hxx> +#include <attarray.hxx> +#include <sharedformula.hxx> +#include <bcaslot.hxx> +#include <scopetools.hxx> +#include <numformat.hxx> + +#include <o3tl/safeint.hxx> +#include <svl/sharedstringpool.hxx> +#include <svl/languageoptions.hxx> +#include <unotools/configmgr.hxx> +#include <unordered_map> + +namespace { + +struct ColAttr +{ + bool mbLatinNumFmtOnly; + + ColAttr() : mbLatinNumFmtOnly(false) {} +}; + +struct TabAttr +{ + std::vector<ColAttr> maCols; +}; + +} + +struct ScDocumentImportImpl +{ + ScDocument& mrDoc; + sc::StartListeningContext maListenCxt; + std::vector<sc::TableColumnBlockPositionSet> maBlockPosSet; + SvtScriptType mnDefaultScriptNumeric; + bool mbFuzzing; + std::vector<TabAttr> maTabAttrs; + std::unordered_map<sal_uInt32, bool> maIsLatinScriptMap; + + explicit ScDocumentImportImpl(ScDocument& rDoc) : + mrDoc(rDoc), + maListenCxt(rDoc), + mnDefaultScriptNumeric(SvtScriptType::UNKNOWN), + mbFuzzing(utl::ConfigManager::IsFuzzing()) + {} + + bool isValid( size_t nTab, size_t nCol ) + { + return (nTab <= o3tl::make_unsigned(MAXTAB) && nCol <= o3tl::make_unsigned(mrDoc.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 (o3tl::make_unsigned(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 invalidateBlockPositionSet(SCTAB nTab) + { + if (o3tl::make_unsigned(nTab) >= maBlockPosSet.size()) + return; + + sc::TableColumnBlockPositionSet& rTab = maBlockPosSet[nTab]; + rTab.invalidate(); + } + + 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, getDoc().MaxCol(), getDoc().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::invalidateBlockPositionSet(SCTAB nTab) +{ + mpImpl->invalidateBlockPositionSet(nTab); +} + +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<EditTextObject> 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<ScFormulaCell> pFC = + std::make_unique<ScFormulaCell>(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<ScFormulaCell> pFC = + std::make_unique<ScFormulaCell>(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<ScTokenArray> 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<ScFormulaCell> pFC = + std::make_unique<ScFormulaCell>(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; + + sc::CellStoreType::position_type aPos = rCells.position(rPos.Row()); + if (aPos.first != rCells.end() && aPos.first->type == sc::element_type_formula) + { + ScFormulaCell* p = sc::formula_block::at(*aPos.first->data, aPos.second); + sc::SharedFormulaUtil::unshareFormulaCell(aPos, *p); + } + + 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(mpImpl->mrDoc.GetSheetLimits(), rBasePos, rBasePos); + + ScTokenArray aArr(mpImpl->mrDoc); // 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(mpImpl->mrDoc.GetSheetLimits(), rBasePos, aPos); + *t->GetSingleRef() = aRefData; + ScTokenArray aTokArr(aArr.CloneValue()); + pCell = new ScFormulaCell(mpImpl->mrDoc, aPos, aTokArr, 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(mpImpl->mrDoc.GetSheetLimits(), rBasePos, aPos); + *t->GetSingleRef() = aRefData; + ScTokenArray aTokArr(aArr.CloneValue()); + pCell = new ScFormulaCell(mpImpl->mrDoc, aPos, aTokArr, 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& rDoc = 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(rDoc, nTab)); + aFormulaBuf.append(aSep); + aFormulaBuf.append(rParam.aRefColCell.GetRefString(rDoc, nTab)); + aFormulaBuf.append(aSep); + aRef.Set(nCol1, nRow1, nTab, false, true, true); + aFormulaBuf.append(aRef.GetRefString(rDoc, nTab)); + nCol1++; + nCol2 = std::min( nCol2, static_cast<SCCOL>(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(rDoc, nTab)); + aFormulaBuf.append(aSep); + aFormulaBuf.append(rParam.aRefRowCell.GetRefString(rDoc, nTab)); + aFormulaBuf.append(aSep); + aRef.Set(nCol1, nRow1, nTab, true, false, true); + aFormulaBuf.append(aRef.GetRefString(rDoc, nTab)); + ++nRow1; + nRow2 = std::min( + nRow2, rParam.aRefFormulaEnd.Row() - rParam.aRefFormulaCell.Row() + nRow1 + 1); + } + else // both + { + aFormulaBuf.append(rParam.aRefFormulaCell.GetRefString(rDoc, nTab)); + aFormulaBuf.append(aSep); + aFormulaBuf.append(rParam.aRefColCell.GetRefString(rDoc, nTab)); + aFormulaBuf.append(aSep); + aRef.Set(nCol1, nRow1 + 1, nTab, false, true, true); + aFormulaBuf.append(aRef.GetRefString(rDoc, nTab)); + aFormulaBuf.append(aSep); + aFormulaBuf.append(rParam.aRefRowCell.GetRefString(rDoc, nTab)); + aFormulaBuf.append(aSep); + aRef.Set(nCol1 + 1, nRow1, nTab, true, false, true); + aFormulaBuf.append(aRef.GetRefString(rDoc, nTab)); + ++nCol1; + ++nRow1; + } + + aFormulaBuf.append(ScCompiler::GetNativeSymbol(ocClose)); + + ScFormulaCell aRefCell( + rDoc, 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, rDoc, aPos); + pBlockPos->miCellPos = + rColCells.set(pBlockPos->miCellPos, nRow, pCell); + } + } +} + +void ScDocumentImport::fillDownCells(const ScAddress& rPos, SCROW nFillSize) +{ + 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; + ScRefCellValue aRefCell = pTab->aCol[rPos.Col()].GetCellValue(*pBlockPos, rPos.Row()); + + switch (aRefCell.meType) + { + case CELLTYPE_VALUE: + { + std::vector<double> aCopied(nFillSize, aRefCell.mfValue); + pBlockPos->miCellPos = rCells.set( + pBlockPos->miCellPos, rPos.Row()+1, aCopied.begin(), aCopied.end()); + break; + } + case CELLTYPE_STRING: + { + std::vector<svl::SharedString> aCopied(nFillSize, *aRefCell.mpString); + pBlockPos->miCellPos = rCells.set( + pBlockPos->miCellPos, rPos.Row()+1, aCopied.begin(), aCopied.end()); + break; + } + default: + break; + } +} + +void ScDocumentImport::setAttrEntries( SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, Attrs&& rAttrs ) +{ + ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab); + if (!pTab) + return; + + for(SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol ) + { + ColAttr* pColAttr = mpImpl->getColAttr(nTab, nCol); + if (pColAttr) + pColAttr->mbLatinNumFmtOnly = rAttrs.mbLatinNumFmtOnly; + } + + pTab->SetAttrEntries( nColStart, nColEnd, 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 mnScriptNumeric; + + explicit Impl(const ScSheetLimits& rSheetLimits, const SvtScriptType nScriptNumeric) + : maAttrs(rSheetLimits.GetMaxRowCount()), miPos(maAttrs.begin()), mnScriptNumeric(nScriptNumeric) + {} + }; + + ScDocumentImportImpl& mrDocImpl; + SCTAB mnTab; + SCCOL mnCol; + +public: + CellStoreInitializer( ScDocumentImportImpl& rDocImpl, SCTAB nTab, SCCOL nCol ) : + mrDocImpl(rDocImpl), + mnTab(nTab), + mnCol(nCol), + mpImpl(std::make_shared<Impl>(rDocImpl.mrDoc.GetSheetLimits(), mrDocImpl.mnDefaultScriptNumeric)) + {} + + std::shared_ptr<Impl> 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<sc::CellTextAttr> aDefaults(node.size, aDefault); + mpImpl->miPos = mpImpl->maAttrs.set(mpImpl->miPos, node.position, aDefaults.begin(), aDefaults.end()); + + if (node.type != sc::element_type_formula) + return; + + if (mrDocImpl.mbFuzzing) // skip listening when fuzzing + return; + + // 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); +} + + +bool ScDocumentImport::isLatinScript(const ScPatternAttr& rPatAttr) +{ + SvNumberFormatter* pFormatter = mpImpl->mrDoc.GetFormatTable(); + sal_uInt32 nKey = rPatAttr.GetNumberFormat(pFormatter); + return isLatinScript(nKey); +} + +bool ScDocumentImport::isLatinScript(sal_uInt32 nFormat) +{ + auto it = mpImpl->maIsLatinScriptMap.find(nFormat); + if (it != mpImpl->maIsLatinScriptMap.end()) + return it->second; + bool b = sc::NumFmtUtil::isLatinScript(nFormat, mpImpl->mrDoc); + mpImpl->maIsLatinScriptMap.emplace(nFormat, b); + return b; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |