diff options
Diffstat (limited to 'sc/source/ui/docshell/externalrefmgr.cxx')
-rw-r--r-- | sc/source/ui/docshell/externalrefmgr.cxx | 340 |
1 files changed, 231 insertions, 109 deletions
diff --git a/sc/source/ui/docshell/externalrefmgr.cxx b/sc/source/ui/docshell/externalrefmgr.cxx index 90b570327876..65065ab74cb8 100644 --- a/sc/source/ui/docshell/externalrefmgr.cxx +++ b/sc/source/ui/docshell/externalrefmgr.cxx @@ -580,9 +580,9 @@ ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData( const ScAddress& s = rRange.aStart; const ScAddress& e = rRange.aEnd; - SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); - SCCOL nCol1 = s.Col(), nCol2 = e.Col(); - SCROW nRow1 = s.Row(), nRow2 = e.Row(); + const SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); + const SCCOL nCol1 = s.Col(), nCol2 = e.Col(); + const SCROW nRow1 = s.Row(), nRow2 = e.Row(); // Make sure I have all the tables cached. size_t nTabFirstId = itrTabId->second; @@ -616,58 +616,120 @@ ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData( return TokenArrayRef(); } - ScMatrixRef xMat = new ScFullMatrix( - static_cast<SCSIZE>(nDataCol2-nDataCol1+1), static_cast<SCSIZE>(nDataRow2-nDataRow1+1)); + SCSIZE nMatrixColumns = static_cast<SCSIZE>(nDataCol2-nDataCol1+1); + SCSIZE nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1); + ScMatrixRef xMat = new ScFullMatrix( nMatrixColumns, nMatrixRows); - // Only fill non-empty cells, for better performance. + // Needed in shrink and fill. vector<SCROW> aRows; pTab->getAllRows(aRows, nDataRow1, nDataRow2); - for (vector<SCROW>::const_iterator itr = aRows.begin(), itrEnd = aRows.end(); itr != itrEnd; ++itr) + bool bFill = true; + + // Check if size could be allocated and if not skip the fill, there's + // one error element instead. But retry first with the actual data area + // if that is smaller than the original range, which works for most + // functions just not some that operate/compare with the original size + // and expect empty values in non-data areas. + // Restrict this though to ranges of entire columns or rows, other + // ranges might be on purpose. (Other special cases to handle?) + /* TODO: sparse matrix could help */ + SCSIZE nMatCols, nMatRows; + xMat->GetDimensions( nMatCols, nMatRows); + if (nMatCols != nMatrixColumns || nMatRows != nMatrixRows) { - SCROW nRow = *itr; - vector<SCCOL> aCols; - pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2); - for (vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end(); itrCol != itrColEnd; ++itrCol) + bFill = false; + if (aRows.empty()) { - SCCOL nCol = *itrCol; - TokenRef pToken = pTab->getCell(nCol, nRow); - if (!pToken) - // This should never happen! - return TokenArrayRef(); - - SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1; - switch (pToken->GetType()) + // There's no data at all. Set the one matrix element to empty + // for column-repeated and row-repeated access. + xMat->PutEmpty(0,0); + } + else if ((nCol1 == 0 && nCol2 == MAXCOL) || (nRow1 == 0 && nRow2 == MAXROW)) + { + nDataRow1 = aRows.front(); + nDataRow2 = aRows.back(); + SCCOL nMinCol = std::numeric_limits<SCCOL>::max(); + SCCOL nMaxCol = std::numeric_limits<SCCOL>::min(); + for (vector<SCROW>::const_iterator itr = aRows.begin(), itrEnd = aRows.end(); itr != itrEnd; ++itr) + { + vector<SCCOL> aCols; + pTab->getAllCols(*itr, aCols, nDataCol1, nDataCol2); + if (!aCols.empty()) + { + nMinCol = std::min( nMinCol, aCols.front()); + nMaxCol = std::max( nMaxCol, aCols.back()); + } + } + + if (nMinCol <= nMaxCol && ((static_cast<SCSIZE>(nMaxCol-nMinCol+1) < nMatrixColumns) || + (static_cast<SCSIZE>(nDataRow2-nDataRow1+1) < nMatrixRows))) { - case svDouble: - xMat->PutDouble(pToken->GetDouble(), nC, nR); - break; - case svString: - xMat->PutString(pToken->GetString(), nC, nR); - break; - default: - ; + nMatrixColumns = static_cast<SCSIZE>(nMaxCol-nMinCol+1); + nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1); + xMat = new ScFullMatrix( nMatrixColumns, nMatrixRows); + xMat->GetDimensions( nMatCols, nMatRows); + if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows) + { + nDataCol1 = nMinCol; + nDataCol2 = nMaxCol; + bFill = true; + } } } } - if (!bFirstTab) - pArray->AddOpCode(ocSep); + if (bFill) + { + // Only fill non-empty cells, for better performance. + for (vector<SCROW>::const_iterator itr = aRows.begin(), itrEnd = aRows.end(); itr != itrEnd; ++itr) + { + SCROW nRow = *itr; + vector<SCCOL> aCols; + pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2); + for (vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end(); itrCol != itrColEnd; ++itrCol) + { + SCCOL nCol = *itrCol; + TokenRef pToken = pTab->getCell(nCol, nRow); + if (!pToken) + // This should never happen! + return TokenArrayRef(); + + SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1; + switch (pToken->GetType()) + { + case svDouble: + xMat->PutDouble(pToken->GetDouble(), nC, nR); + break; + case svString: + xMat->PutString(pToken->GetString(), nC, nR); + break; + default: + ; + } + } + } - ScMatrixToken aToken(xMat); - if (!pArray) - pArray.reset(new ScTokenArray); - pArray->AddToken(aToken); + if (!bFirstTab) + pArray->AddOpCode(ocSep); - bFirstTab = false; + ScMatrixToken aToken(xMat); + if (!pArray) + pArray.reset(new ScTokenArray); + pArray->AddToken(aToken); - if (!pNewRange) - pNewRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); - else - pNewRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); + bFirstTab = false; + + if (!pNewRange) + pNewRange.reset(new ScRange(nDataCol1, nDataRow1, nTab, nDataCol2, nDataRow2, nTab)); + else + pNewRange->ExtendTo(ScRange(nDataCol1, nDataRow1, nTab, nDataCol2, nDataRow2, nTab)); + } } - if (pNewRange) + rDoc.maRangeArrays.insert( RangeArrayMap::value_type(aCacheRange, pArray)); + if (pNewRange && *pNewRange != aCacheRange) rDoc.maRangeArrays.insert( RangeArrayMap::value_type(*pNewRange, pArray)); + return pArray; } @@ -791,37 +853,56 @@ void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRa pTabData.reset(new Table); const ScMatrixRef& pMat = itrData->mpRangeData; - for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) + SCSIZE nMatCols, nMatRows; + pMat->GetDimensions( nMatCols, nMatRows); + if (nMatCols > static_cast<SCSIZE>(nCol2 - nCol1) && nMatRows > static_cast<SCSIZE>(nRow2 - nRow1)) { - const SCSIZE nR = nRow - nRow1; - for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) { - const SCSIZE nC = nCol - nCol1; + const SCSIZE nR = nRow - nRow1; + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + { + const SCSIZE nC = nCol - nCol1; - ScMatrixValue value = pMat->Get(nC, nR); + ScMatrixValue value = pMat->Get(nC, nR); - TokenRef pToken; + TokenRef pToken; - switch (value.nType) { - case SC_MATVAL_VALUE: - case SC_MATVAL_BOOLEAN: - pToken.reset(new formula::FormulaDoubleToken(value.fVal)); - break; - case SC_MATVAL_STRING: - pToken.reset(new formula::FormulaStringToken(value.aStr)); - break; - default: - // Don't cache empty cells. - break; - } + switch (value.nType) { + case SC_MATVAL_VALUE: + case SC_MATVAL_BOOLEAN: + pToken.reset(new formula::FormulaDoubleToken(value.fVal)); + break; + case SC_MATVAL_STRING: + pToken.reset(new formula::FormulaStringToken(value.aStr)); + break; + default: + // Don't cache empty cells. + break; + } - if (pToken) - // Don't mark this cell 'cached' here, for better performance. - pTabData->setCell(nCol, nRow, pToken, 0, false); + if (pToken) + // Don't mark this cell 'cached' here, for better performance. + pTabData->setCell(nCol, nRow, pToken, 0, false); + } + } + // Mark the whole range 'cached'. + pTabData->setCachedCellRange(nCol1, nRow1, nCol2, nRow2); + } + else + { + // This may happen due to a matrix not been allocated earlier, in + // which case it should have exactly one error element. + SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix size mismatch"); + if (nMatCols != 1 || nMatRows != 1) + SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - not a one element matrix"); + else + { + sal_uInt16 nErr = GetDoubleErrorValue( pMat->GetDouble(0,0)); + SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix error value is " << nErr << + (nErr ? ", ok" : ", not ok")); } } - // Mark the whole range 'cached'. - pTabData->setCachedCellRange(nCol1, nRow1, nCol2, nRow2); } size_t nTabLastId = nTabFirstId + rRange.aEnd.Tab() - rRange.aStart.Tab(); @@ -1467,9 +1548,9 @@ static std::unique_ptr<ScTokenArray> convertToTokenArray( ScAddress& s = rRange.aStart; ScAddress& e = rRange.aEnd; - SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); - SCCOL nCol1 = s.Col(), nCol2 = e.Col(); - SCROW nRow1 = s.Row(), nRow2 = e.Row(); + const SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); + const SCCOL nCol1 = s.Col(), nCol2 = e.Col(); + const SCROW nRow1 = s.Row(), nRow2 = e.Row(); if (nTab2 != nTab1) // For now, we don't support multi-sheet ranges intentionally because @@ -1502,62 +1583,103 @@ static std::unique_ptr<ScTokenArray> convertToTokenArray( else pUsedRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); - ScMatrixRef xMat = new ScFullMatrix( - static_cast<SCSIZE>(nCol2-nCol1+1), static_cast<SCSIZE>(nRow2-nRow1+1)); - - ColumnBatch<svl::SharedString> aStringBatch(pHostDoc, pSrcDoc, CELLTYPE_STRING, CELLTYPE_EDIT); - ColumnBatch<double> aDoubleBatch(pHostDoc, pSrcDoc, CELLTYPE_VALUE, CELLTYPE_VALUE); + SCSIZE nMatrixColumns = static_cast<SCSIZE>(nCol2-nCol1+1); + SCSIZE nMatrixRows = static_cast<SCSIZE>(nRow2-nRow1+1); + ScMatrixRef xMat = new ScFullMatrix( nMatrixColumns, nMatrixRows); + + bool bFill = true; + SCCOL nEffectiveCol1 = nCol1; + SCROW nEffectiveRow1 = nRow1; + + // Check if size could be allocated and if not skip the fill, there's + // one error element instead. But retry first with the actual data area + // if that is smaller than the original range, which works for most + // functions just not some that operate/compare with the original size + // and expect empty values in non-data areas. + // Restrict this though to ranges of entire columns or rows, other + // ranges might be on purpose. (Other special cases to handle?) + /* TODO: sparse matrix could help */ + SCSIZE nMatCols, nMatRows; + xMat->GetDimensions( nMatCols, nMatRows); + if (nMatCols != nMatrixColumns || nMatRows != nMatrixRows) + { + bFill = false; + if ((nCol1 == 0 && nCol2 == MAXCOL) || (nRow1 == 0 && nRow2 == MAXROW)) + { + if ((static_cast<SCSIZE>(nDataCol2-nDataCol1+1) < nMatrixColumns) || + (static_cast<SCSIZE>(nDataRow2-nDataRow1+1) < nMatrixRows)) + { + nMatrixColumns = static_cast<SCSIZE>(nDataCol2-nDataCol1+1); + nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1); + xMat = new ScFullMatrix( nMatrixColumns, nMatrixRows); + xMat->GetDimensions( nMatCols, nMatRows); + if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows) + { + nEffectiveCol1 = nDataCol1; + nEffectiveRow1 = nDataRow1; + bFill = true; + } + } + } + } - for (SCCOL nCol = nDataCol1; nCol <= nDataCol2; ++nCol) + if (bFill) { - const SCSIZE nC = nCol - nCol1; - for (SCROW nRow = nDataRow1; nRow <= nDataRow2; ++nRow) + ColumnBatch<svl::SharedString> aStringBatch(pHostDoc, pSrcDoc, CELLTYPE_STRING, CELLTYPE_EDIT); + ColumnBatch<double> aDoubleBatch(pHostDoc, pSrcDoc, CELLTYPE_VALUE, CELLTYPE_VALUE); + + for (SCCOL nCol = nDataCol1; nCol <= nDataCol2; ++nCol) { - const SCSIZE nR = nRow - nRow1; + const SCSIZE nC = nCol - nEffectiveCol1; + for (SCROW nRow = nDataRow1; nRow <= nDataRow2; ++nRow) + { + const SCSIZE nR = nRow - nEffectiveRow1; - ScRefCellValue aCell(*pSrcDoc, ScAddress(nCol, nRow, nTab)); + ScRefCellValue aCell(*pSrcDoc, ScAddress(nCol, nRow, nTab)); - aStringBatch.update(aCell, nC, nR, xMat); - aDoubleBatch.update(aCell, nC, nR, xMat); + aStringBatch.update(aCell, nC, nR, xMat); + aDoubleBatch.update(aCell, nC, nR, xMat); - if (aCell.hasEmptyValue()) - // Skip empty cells. Matrix's default values are empty elements. - continue; + if (aCell.hasEmptyValue()) + // Skip empty cells. Matrix's default values are empty elements. + continue; - switch (aCell.meType) - { - case CELLTYPE_FORMULA: + switch (aCell.meType) { - ScFormulaCell* pFCell = aCell.mpFormula; - sal_uInt16 nError = pFCell->GetErrCode(); - if (nError) - xMat->PutDouble( CreateDoubleError( nError), nC, nR); - else if (pFCell->IsValue()) - { - double fVal = pFCell->GetValue(); - xMat->PutDouble(fVal, nC, nR); - } - else - { - svl::SharedString aStr = pFCell->GetString(); - aStr = pHostDoc->GetSharedStringPool().intern(aStr.getString()); - xMat->PutString(aStr, nC, nR); - } + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFCell = aCell.mpFormula; + sal_uInt16 nError = pFCell->GetErrCode(); + if (nError) + xMat->PutDouble( CreateDoubleError( nError), nC, nR); + else if (pFCell->IsValue()) + { + double fVal = pFCell->GetValue(); + xMat->PutDouble(fVal, nC, nR); + } + else + { + svl::SharedString aStr = pFCell->GetString(); + aStr = pHostDoc->GetSharedStringPool().intern(aStr.getString()); + xMat->PutString(aStr, nC, nR); + } + } + break; + // These are handled in batch: + case CELLTYPE_VALUE: + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + break; + default: + OSL_FAIL("attempted to convert an unknown cell type."); } - break; - // These are handled in batch: - case CELLTYPE_VALUE: - case CELLTYPE_STRING: - case CELLTYPE_EDIT: - break; - default: - OSL_FAIL("attempted to convert an unknown cell type."); } - } - aStringBatch.flush(nC, xMat); - aDoubleBatch.flush(nC, xMat); + aStringBatch.flush(nC, xMat); + aDoubleBatch.flush(nC, xMat); + } } + if (!bFirstTab) pArray->AddOpCode(ocSep); |