diff options
author | Kohei Yoshida <kohei.yoshida@gmail.com> | 2013-05-24 11:52:18 -0400 |
---|---|---|
committer | Kohei Yoshida <kohei.yoshida@gmail.com> | 2013-06-24 16:51:25 -0400 |
commit | c008dc483f8c6840803983e7e351cec6fdd32070 (patch) | |
tree | 7c88eeabde57ea4a3c1a760d1c02ea2fd37bd721 /sc/source/core/data/column3.cxx | |
parent | 75dec25730c88bdb8eb5e2a3f92689460fa89d29 (diff) |
Switch to using multi_type_vector for cell storage.
The old style cell storage is no more. Currently the code is buildable,
but crashes during unit test.
Change-Id: Ie688e22e95c7fb02b9e97b23df0fc1883a97945f
Diffstat (limited to 'sc/source/core/data/column3.cxx')
-rw-r--r-- | sc/source/core/data/column3.cxx | 3079 |
1 files changed, 1807 insertions, 1272 deletions
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx index f264aef5579d..021eba9bbf6d 100644 --- a/sc/source/core/data/column3.cxx +++ b/sc/source/core/data/column3.cxx @@ -42,6 +42,11 @@ #include "tokenarray.hxx" #include "stlalgorithm.hxx" #include "clipcontext.hxx" +#include "columnspanset.hxx" +#include "mtvcellfunc.hxx" +#include "scopetools.hxx" +#include "cellclonehandler.hxx" +#include "editutil.hxx" #include <com/sun/star/i18n/LocaleDataItem.hpp> @@ -53,6 +58,8 @@ #include <svl/zforlist.hxx> #include <svl/zformat.hxx> #include <svl/broadcast.hxx> +#include "editeng/editstat.hxx" + #include <cstdio> using ::com::sun::star::i18n::LocaleDataItem; @@ -66,6 +73,9 @@ namespace { void broadcastCells(ScDocument& rDoc, SCCOL nCol, SCROW nTab, const std::vector<SCROW>& rRows) { + if (rRows.empty()) + return; + // Broadcast the changes. ScHint aHint(SC_HINT_DATACHANGED, ScAddress(nCol, 0, nTab)); std::vector<SCROW>::const_iterator itRow = rRows.begin(), itRowEnd = rRows.end(); @@ -78,60 +88,48 @@ void broadcastCells(ScDocument& rDoc, SCCOL nCol, SCROW nTab, const std::vector< } -void ScColumn::Insert( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, ScBaseCell* pNewCell ) -{ - SetCell(rBlockPos, nRow, pNewCell); - PostSetCell(nRow, pNewCell); -} - -void ScColumn::Insert( SCROW nRow, ScBaseCell* pNewCell ) +void ScColumn::BroadcastCells( const std::vector<SCROW>& rRows ) { - SetCell(nRow, pNewCell); - PostSetCell(nRow, pNewCell); + broadcastCells(*pDocument, nCol, nTab, rRows); } +namespace { -void ScColumn::Insert( SCROW nRow, sal_uInt32 nNumberFormat, ScBaseCell* pCell ) -{ - Insert(nRow, pCell); - SetNumberFormat(nRow, nNumberFormat); -} - -void ScColumn::Append( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, ScBaseCell* pCell ) +class EndListeningHandler { - maItems.push_back(ColEntry()); - maItems.back().pCell = pCell; - maItems.back().nRow = nRow; + ScDocument* mpDoc; +public: + EndListeningHandler(ScDocument* pDoc) : mpDoc(pDoc) {} - rBlockPos.miCellTextAttrPos = - maCellTextAttrs.set(rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); + void operator() (size_t, ScFormulaCell* p) + { + p->EndListeningTo(mpDoc); + } +}; - CellStorageModified(); } -void ScColumn::Append( SCROW nRow, ScBaseCell* pCell ) +void ScColumn::EndFormulaListening( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2 ) { - maItems.push_back(ColEntry()); - maItems.back().pCell = pCell; - maItems.back().nRow = nRow; - - maCellTextAttrs.set<sc::CellTextAttr>(nRow, sc::CellTextAttr()); - CellStorageModified(); + EndListeningHandler aFunc(pDocument); + rBlockPos.miCellPos = + sc::ProcessFormula(rBlockPos.miCellPos, maCells, nRow1, nRow2, aFunc); } void ScColumn::Delete( SCROW nRow ) { - SCSIZE nIndex; - if (!Search(nRow, nIndex)) + std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nRow); + sc::CellStoreType::iterator it = aPos.first; + if (it == maCells.end()) return; - ScBaseCell* pCell = maItems[nIndex].pCell; - maItems.erase(maItems.begin() + nIndex); + if (it->type == sc::element_type_formula) + { + ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second); + p->EndListeningTo(pDocument); + } + maCells.set_empty(nRow, nRow); maCellTextAttrs.set_empty(nRow, nRow); - // Should we free memory here (delta)? It'll be slower! - if (pCell->GetCellType() == CELLTYPE_FORMULA) - static_cast<ScFormulaCell*>(pCell)->EndListeningTo(pDocument); - pCell->Delete(); pDocument->Broadcast( ScHint(SC_HINT_DATACHANGED, ScAddress(nCol, nRow, nTab))); @@ -139,140 +137,332 @@ void ScColumn::Delete( SCROW nRow ) CellStorageModified(); } - -void ScColumn::DeleteAtIndex( SCSIZE nIndex ) +void ScColumn::FreeAll() { - ScBaseCell* pCell = maItems[nIndex].pCell; - SCROW nRow = maItems[nIndex].nRow; - maItems.erase(maItems.begin() + nIndex); - if (pCell->GetCellType() == CELLTYPE_FORMULA) - static_cast<ScFormulaCell*>(pCell)->EndListeningTo(pDocument); - pCell->Delete(); - - pDocument->Broadcast( - ScHint(SC_HINT_DATACHANGED, ScAddress(nCol, nRow, nTab))); - - maCellTextAttrs.set_empty(nRow, nRow); + // Keep a logical empty range of 0-MAXROW at all times. + maCells.clear(); + maCells.resize(MAXROWCOUNT); + maCellTextAttrs.clear(); + maCellTextAttrs.resize(MAXROWCOUNT); CellStorageModified(); } +namespace { -void ScColumn::FreeAll() +/** + * Collect all formula cells for later mass-unregistration. Also tag row + * positions of all non-empty cells in the range. + */ +class DeleteRowsHandler { - for (SCSIZE i = 0; i < maItems.size(); i++) - maItems[i].pCell->Delete(); - maItems.clear(); + ScDocument& mrDoc; + std::vector<SCROW> maRows; + std::vector<ScFormulaCell*> maFormulaCells; +public: + DeleteRowsHandler(ScDocument& rDoc) : mrDoc(rDoc) {} - // Text width should keep a logical empty range of 0-MAXROW when the cell array is empty. - maCellTextAttrs.clear(); - maCellTextAttrs.resize(MAXROWCOUNT); - CellStorageModified(); -} + void operator() (size_t nRow, ScFormulaCell* pCell) + { + maFormulaCells.push_back(pCell); + maRows.push_back(nRow); + } + void operator() (mdds::mtv::element_t nType, size_t nTopRow, size_t nDataSize) + { + if (nType == sc::element_type_empty) + // Ignore empty cells. + return; -void ScColumn::DeleteRow( SCROW nStartRow, SCSIZE nSize ) + for (size_t i = 0; i < nDataSize; ++i) + // Tag all non-empty cells. + maRows.push_back(i + nTopRow); + } + + void endFormulas() + { + mrDoc.EndListeningFormulaCells(maFormulaCells); + } + + const std::vector<SCROW>& getNonEmptyRows() const + { + return maRows; + } +}; + +class ShiftFormulaPosHandler { - SCROW nEndRow = nStartRow + nSize - 1; +public: - pAttrArray->DeleteRow( nStartRow, nSize ); + void operator() (size_t nRow, ScFormulaCell* pCell) + { + pCell->aPos.SetRow(nRow); + } +}; - maBroadcasters.erase(nStartRow, nEndRow); - maBroadcasters.resize(MAXROWCOUNT); +class RangeBroadcaster +{ + ScDocument& mrDoc; + ScHint maHint; +public: + RangeBroadcaster(ScDocument& rDoc, SCTAB nTab, SCCOL nCol) : + mrDoc(rDoc), + maHint(SC_HINT_DATACHANGED, ScAddress(nCol, 0, nTab)) {} - if ( maItems.empty() ) - return ; + void operator() (const sc::SingleColumnSpanSet::Span& rSpan) + { + SCROW nRow1 = rSpan.mnRow1, nRow2 = rSpan.mnRow2; + maHint.GetAddress().SetRow(nRow1); + ScRange aRange(maHint.GetAddress()); + aRange.aEnd.SetRow(nRow2); + mrDoc.AreaBroadcastInRange(aRange, maHint); + } +}; - SCSIZE nFirstIndex; - Search( nStartRow, nFirstIndex ); - if ( nFirstIndex >= maItems.size() ) - return ; +} - sal_Bool bOldAutoCalc = pDocument->GetAutoCalc(); - pDocument->SetAutoCalc( false ); // Avoid calculating it multiple times +void ScColumn::DeleteRow( SCROW nStartRow, SCSIZE nSize ) +{ + pAttrArray->DeleteRow( nStartRow, nSize ); - bool bFound = false; - SCSIZE nStartIndex = 0; - SCSIZE nEndIndex = 0; - SCSIZE i; + SCROW nEndRow = nStartRow + nSize - 1; + maBroadcasters.erase(nStartRow, nEndRow); + maBroadcasters.resize(MAXROWCOUNT); - for ( i = nFirstIndex; i < maItems.size() && maItems[i].nRow <= nEndRow; i++ ) + // See if we have any cells that would get deleted or shifted by deletion. + std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nStartRow); + sc::CellStoreType::iterator itCell = aPos.first; + if (itCell->type == sc::element_type_empty) { - if (!bFound) + // 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()) { - nStartIndex = i; - bFound = true; + // No cells are affected by this deletion. Bail out. + CellStorageModified(); // broadcast array has been modified. + return; } - nEndIndex = i; } - if (bFound) + // Check if there are any cells below the end row that will get shifted. + bool bShiftCells = false; + aPos = maCells.position(itCell, nEndRow+1); + itCell = aPos.first; + if (itCell->type == sc::element_type_empty) { - std::vector<SCROW> aDeletedRows; - DeleteRange(nStartIndex, nEndIndex, IDF_CONTENTS, aDeletedRows); - broadcastCells(*pDocument, nCol, nTab, aDeletedRows); - - Search( nStartRow, i ); - if ( i >= maItems.size() ) - { - pDocument->SetAutoCalc( bOldAutoCalc ); - return ; - } + // 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 - i = nFirstIndex; + bShiftCells = true; + + sc::SingleColumnSpanSet aNonEmptySpans; + if (bShiftCells) + { + // Mark all non-empty cell positions below the end row. + sc::ColumnBlockConstPosition aBlockPos; + aBlockPos.miCellPos = itCell; + aNonEmptySpans.scan(aBlockPos, *this, nEndRow+1, MAXROW); + } - // There are cells below the deletion point. Shift their row positions. + sc::AutoCalcSwitch aACSwitch(*pDocument, false); - // Shift the text width array too (before the broadcast). + // Parse all non-empty cells in the range to pick up their row positions, + // and end all formula cells. + DeleteRowsHandler aDeleteRowsFunc(*pDocument); + sc::ProcessFormula(itCell, maCells, nStartRow, nEndRow, aDeleteRowsFunc, aDeleteRowsFunc); + aDeleteRowsFunc.endFormulas(); + + // Remove the cells. + maCells.erase(nStartRow, nEndRow); + maCells.resize(MAXROWCOUNT); + + // Shift the formula cell positions below the start row. + ShiftFormulaPosHandler aShiftFormulaFunc; + sc::ProcessFormula(maCells.begin(), maCells, nStartRow, MAXROW, aShiftFormulaFunc); + + // Single cell broadcasts on deleted cells. + BroadcastCells(aDeleteRowsFunc.getNonEmptyRows()); + + // Shift the text attribute array too (before the broadcast). maCellTextAttrs.erase(nStartRow, nEndRow); maCellTextAttrs.resize(MAXROWCOUNT); - ScAddress aAdr( nCol, 0, nTab ); - ScHint aHint(SC_HINT_DATACHANGED, aAdr); // only areas (ScBaseCell* == NULL) - ScAddress& rAddress = aHint.GetAddress(); - // for sparse occupation use single broadcasts, not ranges - bool bSingleBroadcasts = (((maItems.back().nRow - maItems[i].nRow) / - (maItems.size() - i)) > 1); - if ( bSingleBroadcasts ) + CellStorageModified(); + + if (!bShiftCells) + return; + + // Do area broadcast on the old non-empty cell ranges prior to the shift. + sc::SingleColumnSpanSet::SpansType aSpans; + aNonEmptySpans.getSpans(aSpans); + std::for_each(aSpans.begin(), aSpans.end(), RangeBroadcaster(*pDocument, nTab, nCol)); +} + +void ScColumn::CopyCellsInRangeToColumn( + sc::ColumnBlockConstPosition* pSrcColPos, + sc::ColumnBlockPosition* pDestColPos, sc::CellBlockCloneHandler& rHdl, SCROW nRow1, SCROW nRow2, ScColumn& rColumn ) const +{ + sc::ColumnBlockConstPosition aSrcColPos; + sc::ColumnBlockPosition aDestColPos; + + if (pSrcColPos) + aSrcColPos = *pSrcColPos; + else + InitBlockPosition(aSrcColPos); + + if (pDestColPos) + aDestColPos = *pDestColPos; + else + rColumn.InitBlockPosition(aDestColPos); + + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(aSrcColPos.miCellPos, nRow1); + sc::CellStoreType::const_iterator it = aPos.first; + aSrcColPos.miCellPos = aPos.first; + size_t nOffset = aPos.second; + size_t nDataSize = 0; + size_t nCurRow = nRow1; + + for (; it != maCells.end() && nCurRow <= static_cast<size_t>(nRow2); ++it, nOffset = 0, nCurRow += nDataSize) { - SCROW nLastBroadcast = MAXROW+1; - for ( ; i < maItems.size(); i++ ) + bool bLastBlock = false; + nDataSize = it->size - nOffset; + if (nCurRow + nDataSize - 1 > static_cast<size_t>(nRow2)) + { + // Truncate the block to copy to clipboard. + nDataSize = nRow2 - nCurRow + 1; + bLastBlock = true; + } + + bool bHasCells = true; + switch (it->type) { - SCROW nOldRow = maItems[i].nRow; - // Broadcast change in source - rAddress.SetRow( nOldRow ); - pDocument->AreaBroadcast( aHint ); - SCROW nNewRow = (maItems[i].nRow -= nSize); - // Broadcast change in target - if ( nLastBroadcast != nNewRow ) - { // Do not broadcast successive ones - rAddress.SetRow( nNewRow ); - pDocument->AreaBroadcast( aHint ); + case sc::element_type_numeric: + { + sc::numeric_block::const_iterator itData = sc::numeric_block::begin(*it->data); + std::advance(itData, nOffset); + sc::numeric_block::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + ScAddress aSrcPos(nCol, nCurRow, nTab); + ScAddress aDestPos(rColumn.nCol, nCurRow, rColumn.nTab); + rHdl.cloneDoubleBlock(aDestColPos.miCellPos, aSrcPos, aDestPos, itData, itDataEnd); + } + break; + case sc::element_type_string: + { + sc::string_block::const_iterator itData = sc::string_block::begin(*it->data); + std::advance(itData, nOffset); + sc::string_block::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + ScAddress aSrcPos(nCol, nCurRow, nTab); + ScAddress aDestPos(rColumn.nCol, nCurRow, rColumn.nTab); + rHdl.cloneStringBlock(aDestColPos.miCellPos, aSrcPos, aDestPos, itData, itDataEnd); + } + break; + case sc::element_type_edittext: + { + sc::edittext_block::const_iterator itData = sc::edittext_block::begin(*it->data); + std::advance(itData, nOffset); + sc::edittext_block::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + ScAddress aSrcPos(nCol, nCurRow, nTab); + ScAddress aDestPos(rColumn.nCol, nCurRow, rColumn.nTab); + rHdl.cloneEditTextBlock(aDestColPos.miCellPos, aSrcPos, aDestPos, itData, itDataEnd); + } + break; + case sc::element_type_formula: + { + sc::formula_block::const_iterator itData = sc::formula_block::begin(*it->data); + std::advance(itData, nOffset); + sc::formula_block::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + ScAddress aSrcPos(nCol, nCurRow, nTab); + ScAddress aDestPos(rColumn.nCol, nCurRow, rColumn.nTab); + rHdl.cloneFormulaBlock(aDestColPos.miCellPos, aSrcPos, aDestPos, itData, itDataEnd); } - nLastBroadcast = nOldRow; - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - ((ScFormulaCell*)pCell)->aPos.SetRow( nNewRow ); + break; + default: + bHasCells = false; } - } - else - { - rAddress.SetRow( maItems[i].nRow ); - ScRange aRange( rAddress ); - aRange.aEnd.SetRow( maItems.back().nRow ); - for ( ; i < maItems.size(); i++ ) + + if (bHasCells) { - SCROW nNewRow = (maItems[i].nRow -= nSize); - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - ((ScFormulaCell*)pCell)->aPos.SetRow( nNewRow ); + // Set default text attribute values for the new cells in the clip doc. + std::vector<sc::CellTextAttr> aTextAttrs(nDataSize, sc::CellTextAttr()); + aDestColPos.miCellTextAttrPos = rColumn.maCellTextAttrs.set( + aDestColPos.miCellTextAttrPos, nCurRow, aTextAttrs.begin(), aTextAttrs.end()); } - pDocument->AreaBroadcastInRange( aRange, aHint ); + + if (bLastBlock) + break; } - CellStorageModified(); - pDocument->SetAutoCalc( bOldAutoCalc ); + if (pSrcColPos) + { + // Save the positions for the next iteration. + *pSrcColPos = aSrcColPos; + } + + if (pDestColPos) + { + // Save the positions for the next iteration. + *pDestColPos = aDestColPos; + } + + rColumn.CellStorageModified(); +} + +sc::CellStoreType::iterator ScColumn::GetPositionToInsert( SCROW nRow ) +{ + return GetPositionToInsert(maCells.begin(), nRow); +} + +sc::CellStoreType::iterator ScColumn::GetPositionToInsert( const sc::CellStoreType::iterator& it, SCROW nRow ) +{ + // See if we are overwriting an existing formula cell. + std::pair<sc::CellStoreType::iterator,size_t> aRet = maCells.position(it, nRow); + sc::CellStoreType::iterator itRet = aRet.first; + if (itRet->type == sc::element_type_formula && !pDocument->IsClipOrUndo()) + { + ScFormulaCell* pCell = sc::formula_block::at(*itRet->data, aRet.second); + pCell->EndListeningTo(pDocument); + } + + return itRet; +} + +void ScColumn::ActivateNewFormulaCell( ScFormulaCell* pCell ) +{ + // 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 (!pDocument->IsClipOrUndo() && !pDocument->IsInsertingFromOtherDoc()) + { + pCell->StartListeningTo(pDocument); + if (!pDocument->IsCalcingAfterLoad()) + pCell->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 (pDocument->IsClipOrUndo() || pDocument->IsInsertingFromOtherDoc() || pDocument->IsCalcingAfterLoad()) + return; + + ScHint aHint(SC_HINT_DATACHANGED, ScAddress(nCol, nRow, nTab)); + pDocument->Broadcast(aHint); } void ScColumn::UpdateScriptType( sc::CellTextAttr& rAttr, SCROW nRow ) @@ -314,171 +504,92 @@ void ScColumn::UpdateScriptType( sc::CellTextAttr& rAttr, SCROW nRow ) namespace { -bool isDate(const ScDocument& rDoc, const ScColumn& rCol, SCROW nRow) +class DeleteAreaHandler { - sal_uLong nIndex = (sal_uLong)((SfxUInt32Item*)rCol.GetAttr(nRow, ATTR_VALUE_FORMAT))->GetValue(); - short nType = rDoc.GetFormatTable()->GetType(nIndex); - return (nType == NUMBERFORMAT_DATE) || (nType == NUMBERFORMAT_TIME) || (nType == NUMBERFORMAT_DATETIME); -} + ScDocument& mrDoc; + std::vector<ScFormulaCell*> maFormulaCells; + sc::SingleColumnSpanSet maDeleteRanges; -bool checkDeleteCellByFlag( - CellType eCellType, sal_uInt16 nDelFlag, const ScDocument& rDoc, const ScColumn& rCol, const ColEntry& rEntry) -{ - bool bDelete = false; + bool mbNumeric:1; + bool mbDateTime:1; + bool mbString:1; + bool mbFormula:1; - switch (eCellType) - { - case CELLTYPE_VALUE: - { - sal_uInt16 nValFlags = nDelFlag & (IDF_DATETIME|IDF_VALUE); - // delete values and dates? - bDelete = nValFlags == (IDF_DATETIME|IDF_VALUE); - // if not, decide according to cell number format - if (!bDelete && (nValFlags != 0)) - { - bool bIsDate = isDate(rDoc, rCol, rEntry.nRow); - bDelete = nValFlags == (bIsDate ? IDF_DATETIME : IDF_VALUE); - } - } - break; - case CELLTYPE_STRING: - case CELLTYPE_EDIT: - bDelete = (nDelFlag & IDF_STRING) != 0; - break; - case CELLTYPE_FORMULA: - bDelete = (nDelFlag & IDF_FORMULA) != 0; - break; - default:; // added to avoid warnings - } - - return bDelete; -} - -} - -void ScColumn::DeleteRange( - SCSIZE nStartIndex, SCSIZE nEndIndex, sal_uInt16 nDelFlag, std::vector<SCROW>& rDeletedRows ) -{ - /* If caller specifies to not remove the note caption objects, all cells - have to forget the pointers to them. This is used e.g. while undoing a - "paste cells" operation, which removes the caption objects later in - drawing undo. */ - - // cache all formula cells, they will be deleted at end of this function - std::vector<ScFormulaCell*> aDelCells; - aDelCells.reserve( nEndIndex - nStartIndex + 1 ); - - typedef mdds::flat_segment_tree<SCSIZE, bool> RemovedSegments_t; - RemovedSegments_t aRemovedSegments(nStartIndex, maItems.size(), false); - SCSIZE nFirst = nStartIndex; - - for ( SCSIZE nIdx = nStartIndex; nIdx <= nEndIndex; ++nIdx ) +public: + DeleteAreaHandler(ScDocument& rDoc, sal_uInt16 nDelFlag) : + mrDoc(rDoc), + mbNumeric(nDelFlag & IDF_VALUE), + mbDateTime(nDelFlag & IDF_DATETIME), + mbString(nDelFlag & IDF_STRING), + mbFormula(nDelFlag & IDF_FORMULA) {} + + void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize) { - if (((nDelFlag & IDF_CONTENTS) == IDF_CONTENTS)) + switch (node.type) { - // all content is to be deleted. - - ScBaseCell* pOldCell = maItems[ nIdx ].pCell; - rDeletedRows.push_back(maItems[nIdx].nRow); - - if (pOldCell->GetCellType() == CELLTYPE_FORMULA) + case sc::element_type_numeric: + if (!mbNumeric) + return; + break; + case sc::element_type_string: + case sc::element_type_edittext: + if (!mbString) + return; + break; + case sc::element_type_formula: { - // cache formula cell, will be deleted below - aDelCells.push_back( static_cast< ScFormulaCell* >( pOldCell ) ); - } - else - pOldCell->Delete(); + if (!mbFormula) + return; - continue; - } - - // delete some contents of the cells, or cells with broadcaster - bool bDelete = false; - ScBaseCell* pOldCell = maItems[nIdx].pCell; - CellType eCellType = pOldCell->GetCellType(); - if ((nDelFlag & IDF_CONTENTS) == IDF_CONTENTS) - // All cell types to be deleted. - bDelete = true; - else - { - // Decide whether to delete the cell object according to passed - // flags. - bDelete = checkDeleteCellByFlag(eCellType, nDelFlag, *pDocument, *this, maItems[nIdx]); - } + sc::formula_block::iterator it = sc::formula_block::begin(*node.data); + std::advance(it, nOffset); + sc::formula_block::iterator itEnd = it; + std::advance(itEnd, nDataSize); - if (bDelete) - { - // remove cell entry in cell item list - if (eCellType == CELLTYPE_FORMULA) - { - // Cache formula cells (will be deleted later), delete cell of other type. - aDelCells.push_back(static_cast<ScFormulaCell*>(pOldCell)); + for (; it != itEnd; ++it) + maFormulaCells.push_back(*it); } - else - pOldCell->Delete(); - - rDeletedRows.push_back(maItems[nIdx].nRow); + break; + case sc::element_type_empty: + default: + return; } - if (!bDelete) - { - // We just came to a non-deleted cell after a segment of - // deleted ones. So we need to remember the segment - // before moving on. - if (nFirst < nIdx) - aRemovedSegments.insert_back(nFirst, nIdx, true); - nFirst = nIdx + 1; - } + // Tag these cells for deletion. + SCROW nRow1 = node.position + nOffset; + SCROW nRow2 = nRow1 + nDataSize - 1; + maDeleteRanges.set(nRow1, nRow2, true); } - // there is a segment of deleted cells at the end - if (nFirst <= nEndIndex) - aRemovedSegments.insert_back(nFirst, nEndIndex + 1, true); + void endFormulas() { - // Remove segments from the column array, containing pDummyCell and - // formula cell pointers to be deleted. - - RemovedSegments_t::const_reverse_iterator it = aRemovedSegments.rbegin(); - RemovedSegments_t::const_reverse_iterator itEnd = aRemovedSegments.rend(); - - std::vector<ColEntry>::iterator itErase, itEraseEnd; - SCSIZE nEndSegment = it->first; // should equal maItems.size(). Non-inclusive. - // Skip the first node. - for (++it; it != itEnd; ++it) - { - if (!it->second) - { - // Don't remove this segment. - nEndSegment = it->first; - continue; - } - - // Remove this segment. - SCSIZE nStartSegment = it->first; - SCROW nStartRow = maItems[nStartSegment].nRow; - SCROW nEndRow = maItems[nEndSegment-1].nRow; + mrDoc.EndListeningFormulaCells(maFormulaCells); + } - itErase = maItems.begin(); - std::advance(itErase, nStartSegment); - itEraseEnd = maItems.begin(); - std::advance(itEraseEnd, nEndSegment); - maItems.erase(itErase, itEraseEnd); + const sc::SingleColumnSpanSet& getSpans() const + { + return maDeleteRanges; + } +}; - maCellTextAttrs.set_empty(nStartRow, nEndRow); +class EmptyCells +{ + sc::CellStoreType::iterator miPos; + sc::CellStoreType& mrCells; +public: + EmptyCells(sc::CellStoreType::iterator itPos, sc::CellStoreType& rCells) : + miPos(itPos), mrCells(rCells) {} - nEndSegment = nStartSegment; - } + void operator() (const sc::SingleColumnSpanSet::Span& rSpan) + { + miPos = mrCells.set_empty(miPos, rSpan.mnRow1, rSpan.mnRow2); } +}; - pDocument->EndListeningFormulaCells(aDelCells); - std::for_each(aDelCells.begin(), aDelCells.end(), ScDeleteObjectByPtr<ScFormulaCell>()); } void ScColumn::DeleteArea(SCROW nStartRow, SCROW nEndRow, sal_uInt16 nDelFlag) { - // FreeAll must not be called here due to Broadcasters - // Delete attribute at the end so that we can distinguish between numbers and dates - sal_uInt16 nContMask = IDF_CONTENTS; // IDF_NOCAPTIONS needs to be passed too, if IDF_NOTE is set if( nDelFlag & IDF_NOTE ) @@ -487,30 +598,17 @@ void ScColumn::DeleteArea(SCROW nStartRow, SCROW nEndRow, sal_uInt16 nDelFlag) std::vector<SCROW> aDeletedRows; - if ( !maItems.empty() && nContFlag) + if (!IsEmptyData() && nContFlag) { - if (nStartRow==0 && nEndRow==MAXROW) - { - DeleteRange(0, maItems.size()-1, nContFlag, aDeletedRows); - } - else - { - sal_Bool bFound=false; - SCSIZE nStartIndex = 0; - SCSIZE nEndIndex = 0; - for (SCSIZE i = 0; i < maItems.size(); i++) - if ((maItems[i].nRow >= nStartRow) && (maItems[i].nRow <= nEndRow)) - { - if (!bFound) - { - nStartIndex = i; - bFound = sal_True; - } - nEndIndex = i; - } - if (bFound) - DeleteRange(nStartIndex, nEndIndex, nContFlag, aDeletedRows); - } + DeleteAreaHandler aFunc(*pDocument, nDelFlag); + sc::CellStoreType::iterator itPos = maCells.position(nStartRow).first; + sc::ProcessBlock(itPos, maCells, aFunc, nStartRow, nEndRow); + aFunc.endFormulas(); // Have the formula cells stop listening. + aFunc.getSpans().getRows(aDeletedRows); + + sc::SingleColumnSpanSet::SpansType aSpans; + aFunc.getSpans().getSpans(aSpans); + std::for_each(aSpans.begin(), aSpans.end(), EmptyCells(itPos, maCells)); } if ( nDelFlag & IDF_EDITATTR ) @@ -527,78 +625,264 @@ void ScColumn::DeleteArea(SCROW nStartRow, SCROW nEndRow, sal_uInt16 nDelFlag) // Broadcast on only cells that were deleted; no point broadcasting on // cells that were already empty before the deletion. - broadcastCells(*pDocument, nCol, nTab, aDeletedRows); + BroadcastCells(aDeletedRows); } +bool ScColumn::InitBlockPosition( sc::ColumnBlockPosition& rBlockPos ) +{ + rBlockPos.miBroadcasterPos = maBroadcasters.begin(); + rBlockPos.miCellTextAttrPos = maCellTextAttrs.begin(); + rBlockPos.miCellPos = maCells.begin(); + return true; +} -ScFormulaCell* ScColumn::CreateRefCell( ScDocument* pDestDoc, const ScAddress& rDestPos, - SCSIZE nIndex, sal_uInt16 nFlags ) const +bool ScColumn::InitBlockPosition( sc::ColumnBlockConstPosition& rBlockPos ) const { - sal_uInt16 nContFlags = nFlags & IDF_CONTENTS; - if (!nContFlags) - return NULL; + rBlockPos.miBroadcasterPos = maBroadcasters.begin(); + rBlockPos.miCellTextAttrPos = maCellTextAttrs.begin(); + rBlockPos.miCellPos = maCells.begin(); + return true; +} + +namespace { + +class CopyAttrArrayByRange : std::unary_function<sc::SingleColumnSpanSet::Span, void> +{ + ScAttrArray& mrDestAttrArray; + ScAttrArray& mrSrcAttrArray; + long mnRowOffset; +public: + CopyAttrArrayByRange(ScAttrArray& rDestAttrArray, ScAttrArray& rSrcAttrArray, long nRowOffset) : + mrDestAttrArray(rDestAttrArray), mrSrcAttrArray(rSrcAttrArray), mnRowOffset(nRowOffset) {} + + void operator() (const sc::SingleColumnSpanSet::Span& rSpan) + { + mrDestAttrArray.CopyAreaSafe( + rSpan.mnRow1+mnRowOffset, rSpan.mnRow2+mnRowOffset, mnRowOffset, mrSrcAttrArray); + } +}; + +class CopyCellsFromClipHandler +{ + sc::CopyFromClipContext& mrCxt; + ScColumn& mrSrcCol; + ScColumn& mrDestCol; + SCTAB mnTab; + SCCOL mnCol; + long mnRowOffset; + sc::ColumnBlockPosition maDestBlockPos; + + bool isDateCell(SCROW nSrcRow) const + { + ScDocument* pSrcDoc = mrCxt.getClipDoc(); // clip document is the source. + sal_uLong nNumIndex = static_cast<const SfxUInt32Item*>(mrSrcCol.GetAttr(nSrcRow, ATTR_VALUE_FORMAT))->GetValue(); + short nType = pSrcDoc->GetFormatTable()->GetType(nNumIndex); + return (nType == NUMBERFORMAT_DATE) || (nType == NUMBERFORMAT_TIME) || (nType == NUMBERFORMAT_DATETIME); + } + + void insertRefCell(SCROW nSrcRow, SCROW nDestRow) + { + ScAddress aSrcPos(mnCol, nSrcRow, mnTab); + ScAddress aDestPos(mnCol, nDestRow, mnTab); + ScSingleRefData aRef; + aRef.InitAddress(aSrcPos); + aRef.SetFlag3D(true); + + ScTokenArray aArr; + aArr.AddSingleReference(aRef); - // Test whether the Cell should be copied - // Also do this for IDF_CONTENTS, due to Notes/Broadcasters - sal_Bool bMatch = false; - ScBaseCell* pCell = maItems[nIndex].pCell; - CellType eCellType = pCell->GetCellType(); - switch ( eCellType ) + mrDestCol.SetFormulaCell( + maDestBlockPos, nDestRow, new ScFormulaCell(&mrDestCol.GetDoc(), aDestPos, &aArr)); + } + +public: + CopyCellsFromClipHandler(sc::CopyFromClipContext& rCxt, ScColumn& rSrcCol, ScColumn& rDestCol, SCTAB nDestTab, SCCOL nDestCol, long nRowOffset) : + mrCxt(rCxt), + mrSrcCol(rSrcCol), + mrDestCol(rDestCol), + mnTab(nDestTab), + mnCol(nDestCol), + mnRowOffset(nRowOffset) { - case CELLTYPE_VALUE: + sc::ColumnBlockPosition* p = mrCxt.getBlockPosition(nDestTab, nDestCol); + if (p) + maDestBlockPos = *p; + else + mrDestCol.InitBlockPosition(maDestBlockPos); + } + + void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize) + { + if (node.type == sc::element_type_empty) + return; + + SCROW nSrcRow1 = node.position + nOffset; + + sal_uInt16 nFlags = mrCxt.getInsertFlag(); + bool bNumeric = (nFlags & IDF_VALUE) != 0; + bool bDateTime = (nFlags & IDF_DATETIME) != 0; + bool bString = (nFlags & IDF_STRING) != 0; + bool bBoolean = (nFlags & IDF_SPECIAL_BOOLEAN) != 0; + bool bFormula = (nFlags & IDF_FORMULA) != 0; + + bool bAsLink = mrCxt.isAsLink(); + + switch (node.type) + { + case sc::element_type_numeric: { - sal_uInt16 nValFlags = nFlags & (IDF_DATETIME|IDF_VALUE); + // 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 = isDateCell(nSrcRow) ? bDateTime : bNumeric; + if (!bCopy) + continue; - if ( nValFlags == (IDF_DATETIME|IDF_VALUE) ) - bMatch = sal_True; - else if ( nValFlags ) + if (bAsLink) + insertRefCell(nSrcRow, nSrcRow + mnRowOffset); + else + mrDestCol.SetValue(maDestBlockPos, nSrcRow + mnRowOffset, *it); + } + } + break; + case sc::element_type_string: + { + if (!bString) + return; + + 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) { - sal_uLong nNumIndex = (sal_uLong)((SfxUInt32Item*)GetAttr( - maItems[nIndex].nRow, ATTR_VALUE_FORMAT ))->GetValue(); - short nTyp = pDocument->GetFormatTable()->GetType(nNumIndex); - if ((nTyp == NUMBERFORMAT_DATE) || (nTyp == NUMBERFORMAT_TIME) || (nTyp == NUMBERFORMAT_DATETIME)) - bMatch = ((nFlags & IDF_DATETIME) != 0); + if (bAsLink) + insertRefCell(nSrcRow, nSrcRow + mnRowOffset); else - bMatch = ((nFlags & IDF_VALUE) != 0); + mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, *it); } } break; - case CELLTYPE_STRING: - case CELLTYPE_EDIT: bMatch = ((nFlags & IDF_STRING) != 0); break; - case CELLTYPE_FORMULA: bMatch = ((nFlags & IDF_FORMULA) != 0); break; - default: - { - // added to avoid warnings - } - } - if (!bMatch) - return NULL; - - - // Insert Reference - ScSingleRefData aRef; - aRef.nCol = nCol; - aRef.nRow = maItems[nIndex].nRow; - aRef.nTab = nTab; - aRef.InitFlags(); // -> Everything absolute - aRef.SetFlag3D(true); - - // 3D (false) and TabRel (true), if the final Position is at the same Table? - // The target position is not yet known for TransposeClip! + case sc::element_type_edittext: + { + if (!bString) + return; + + 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) + { - aRef.CalcRelFromAbs( rDestPos ); + 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 = const_cast<ScFormulaCell&>(**it); + bool bForceFormula = false; + if (bBoolean) + { + // See if the formula consists of =TRUE() or =FALSE(). + ScTokenArray* pCode = rSrcCell.GetCode(); + if (pCode && pCode->GetLen() == 1) + { + const formula::FormulaToken* p = pCode->First(); + if (p->GetOpCode() == ocTrue || p->GetOpCode() == ocFalse) + // This is a boolean formula. + bForceFormula = true; + } + } - ScTokenArray aArr; - aArr.AddSingleReference( aRef ); + 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)); + } + } + else if (bNumeric || bDateTime || bString) + { + // Always just copy the original row to the Undo Documen; + // do not create Value/string cells from formulas - return new ScFormulaCell( pDestDoc, rDestPos, &aArr ); -} + sal_uInt16 nErr = rSrcCell.GetErrCode(); + if (nErr) + { + // 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); + } + } + } + else if (rSrcCell.IsValue()) + { + bool bCopy = isDateCell(nSrcRow) ? bDateTime : bNumeric; + if (!bCopy) + continue; + + if (bAsLink) + insertRefCell(nSrcRow, nSrcRow + mnRowOffset); + else + mrDestCol.SetValue(maDestBlockPos, nSrcRow + mnRowOffset, rSrcCell.GetValue()); + } + else if (bString) + { + OUString aStr = rSrcCell.GetString(); + if (aStr.isEmpty()) + // do not clone empty string + continue; + + if (bAsLink) + insertRefCell(nSrcRow, nSrcRow + mnRowOffset); + else if (rSrcCell.IsMultilineResult()) + { + // Clone as an edit text object. + ScFieldEditEngine& rEngine = mrDestCol.GetDoc().GetEditEngine(); + rEngine.SetText(aStr); + mrDestCol.SetEditText(maDestBlockPos, nSrcRow + mnRowOffset, rEngine.CreateTextObject()); + } + else + mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, aStr); + } + } + } + } + break; + default: + ; + } + } +}; -bool ScColumn::InitBlockPosition( sc::ColumnBlockPosition& rBlockPos ) -{ - rBlockPos.miBroadcasterPos = maBroadcasters.begin(); - rBlockPos.miCellTextAttrPos = maCellTextAttrs.begin(); - return true; } // rColumn = source @@ -611,29 +895,13 @@ void ScColumn::CopyFromClip( { if (rCxt.isSkipAttrForEmptyCells()) { - // copy only attributes for non-empty cells - // (notes are not counted as non-empty here, to match the content behavior) - - SCSIZE nStartIndex; - rColumn.Search( nRow1-nDy, nStartIndex ); - while ( nStartIndex < rColumn.maItems.size() && rColumn.maItems[nStartIndex].nRow <= nRow2-nDy ) - { - SCSIZE nEndIndex = nStartIndex; - SCROW nStartRow = rColumn.maItems[nStartIndex].nRow; - SCROW nEndRow = nStartRow; - - // find consecutive non-empty cells - while ( nEndRow < nRow2-nDy && - nEndIndex+1 < rColumn.maItems.size() && - rColumn.maItems[nEndIndex+1].nRow == nEndRow+1 ) - { - ++nEndIndex; - ++nEndRow; - } - - rColumn.pAttrArray->CopyAreaSafe( nStartRow+nDy, nEndRow+nDy, nDy, *pAttrArray ); - nStartIndex = nEndIndex + 1; - } + // copy only attributes for non-empty cells between nRow1-nDy and nRow2-nDy. + sc::SingleColumnSpanSet aSpanSet; + 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 ); @@ -647,8 +915,6 @@ void ScColumn::CopyFromClip( // IDF_ALL must always contain more flags when compared to "Insert contents" as // contents can be selected one by one! - ReserveSize(maItems.size() + static_cast<SCSIZE>(nRow2-nRow1+1)); - ScAddress aDestPos( nCol, 0, nTab ); // Adapt Row // Create reference (Source Position) @@ -667,167 +933,19 @@ void ScColumn::CopyFromClip( aRef.CalcRelFromAbs( aDestPos ); ScTokenArray aArr; aArr.AddSingleReference( aRef ); - Insert( nDestRow, new ScFormulaCell( pDocument, aDestPos, &aArr ) ); + SetFormulaCell(nDestRow, new ScFormulaCell(pDocument, aDestPos, &aArr)); } return; } - SCSIZE nColCount = rColumn.maItems.size(); - - // ignore IDF_FORMULA - "all contents but no formulas" results in the same number of cells - if ((rCxt.getInsertFlag() & ( IDF_CONTENTS & ~IDF_FORMULA )) == ( IDF_CONTENTS & ~IDF_FORMULA ) && nRow2-nRow1 >= 64) - { - //! Always do the Resize from the outside, where the number of repetitions is known - //! (then it can be removed here) - - ReserveSize(maItems.size() + nColCount); - } - - sal_Bool bAtEnd = false; - for (SCSIZE i = 0; i < nColCount && !bAtEnd; i++) - { - SCsROW nDestRow = rColumn.maItems[i].nRow + nDy; - if ( nDestRow > (SCsROW) nRow2 ) - bAtEnd = sal_True; - else if ( nDestRow >= (SCsROW) nRow1 ) - { - // rows at the beginning may be skipped if filtered rows are left out, - // nDestRow may be negative then - - ScAddress aDestPos( nCol, (SCROW)nDestRow, nTab ); - - ScBaseCell* pNewCell = rCxt.isAsLink() ? - rColumn.CreateRefCell(pDocument, aDestPos, i, rCxt.getInsertFlag()) : - rColumn.CloneCell(i, rCxt.getInsertFlag(), *pDocument, aDestPos); - if (pNewCell) - { - sc::ColumnBlockPosition* p = rCxt.getBlockPosition(nTab, nCol); - if (p) - Insert(*p, aDestPos.Row(), pNewCell); - else - Insert(aDestPos.Row(), pNewCell); - } - } - } -} - - -namespace { - -/** - * Helper for ScColumn::CloneCell - * Decide whether to clone a value cell depending on clone flags and number format. - */ -bool lclCanCloneValue( ScDocument& rDoc, const ScColumn& rCol, SCROW nRow, bool bCloneValue, bool bCloneDateTime ) -{ - // values and dates, or nothing to be cloned -> not needed to check number format - if( bCloneValue == bCloneDateTime ) - return bCloneValue; - - // check number format of value cell - sal_uLong nNumIndex = (sal_uLong)((SfxUInt32Item*)rCol.GetAttr( nRow, ATTR_VALUE_FORMAT ))->GetValue(); - short nTyp = rDoc.GetFormatTable()->GetType( nNumIndex ); - bool bIsDateTime = (nTyp == NUMBERFORMAT_DATE) || (nTyp == NUMBERFORMAT_TIME) || (nTyp == NUMBERFORMAT_DATETIME); - return bIsDateTime ? bCloneDateTime : bCloneValue; -} - -} // namespace - - -ScBaseCell* ScColumn::CloneCell( - SCSIZE nIndex, sal_uInt16 nFlags, ScDocument& rDestDoc, const ScAddress& rDestPos) const -{ - bool bCloneValue = (nFlags & IDF_VALUE) != 0; - bool bCloneDateTime = (nFlags & IDF_DATETIME) != 0; - bool bCloneString = (nFlags & IDF_STRING) != 0; - bool bCloneSpecialBoolean = (nFlags & IDF_SPECIAL_BOOLEAN) != 0; - bool bCloneFormula = (nFlags & IDF_FORMULA) != 0; - bool bForceFormula = false; - - ScBaseCell* pNew = 0; - ScBaseCell& rSource = *maItems[nIndex].pCell; - switch (rSource.GetCellType()) - { - case CELLTYPE_STRING: - case CELLTYPE_EDIT: - // note will be cloned below - if (bCloneString) - pNew = rSource.Clone( rDestDoc, rDestPos ); - break; - - case CELLTYPE_VALUE: - // note will be cloned below - if (lclCanCloneValue( *pDocument, *this, maItems[nIndex].nRow, bCloneValue, bCloneDateTime )) - pNew = rSource.Clone( rDestDoc, rDestPos ); - break; - - case CELLTYPE_FORMULA: - if ( bCloneSpecialBoolean ) - { - ScFormulaCell& rForm = (ScFormulaCell&)rSource; - OUStringBuffer aBuf; - // FIXME: do we have a localisation issue here? - rForm.GetFormula( aBuf ); - OUString aVal( aBuf.makeStringAndClear() ); - if ( aVal == "=TRUE()" || aVal == "=FALSE()" ) - bForceFormula = true; - } - if (bForceFormula || bCloneFormula) - { - // note will be cloned below - pNew = rSource.Clone( rDestDoc, rDestPos ); - } - else if ( (bCloneValue || bCloneDateTime || bCloneString) && !rDestDoc.IsUndo() ) - { - // Always just copy the original row to the Undo Documen; - // do not create Value/string cells from formulas - ScFormulaCell& rForm = (ScFormulaCell&)rSource; - sal_uInt16 nErr = rForm.GetErrCode(); - if ( nErr ) - { - // error codes are cloned with values - if (bCloneValue) - { - ScFormulaCell* pErrCell = new ScFormulaCell( &rDestDoc, rDestPos ); - pErrCell->SetErrCode( nErr ); - pNew = pErrCell; - } - } - else if (rForm.IsValue()) - { - if (lclCanCloneValue( *pDocument, *this, maItems[nIndex].nRow, bCloneValue, bCloneDateTime )) - { - double nVal = rForm.GetValue(); - pNew = new ScValueCell(nVal); - } - } - else if (bCloneString) - { - String aString = rForm.GetString(); - // do not clone empty string - if (aString.Len() > 0) - { - if ( rForm.IsMultilineResult() ) - { - pNew = new ScEditCell( aString, &rDestDoc ); - } - else - { - pNew = new ScStringCell( aString ); - } - } - } - } - break; - - default: OSL_FAIL( "ScColumn::CloneCell - unknown cell type" ); - } + // nRow1 to nRow2 is for destination (this) column. Subtract nDy to get the source range. - return pNew; + // 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); + sc::ParseBlock(rColumn.maCells.begin(), rColumn.maCells, aFunc, nRow1-nDy, nRow2-nDy); } - void ScColumn::MixMarked( sc::MixDocContext& rCxt, const ScMarkData& rMark, sal_uInt16 nFunction, bool bSkipEmpty, const ScColumn& rSrcCol ) @@ -867,7 +985,7 @@ bool lcl_DoFunction( double& rVal1, double nVal2, sal_uInt16 nFunction ) return bOk; } -void lcl_AddCode( ScTokenArray& rArr, ScFormulaCell* pCell ) +void lcl_AddCode( ScTokenArray& rArr, const ScFormulaCell* pCell ) { rArr.AddOpCode(ocOpen); @@ -885,138 +1003,167 @@ void lcl_AddCode( ScTokenArray& rArr, ScFormulaCell* pCell ) rArr.AddOpCode(ocClose); } -struct FindRemovedCell : std::unary_function<ColEntry, bool> +class MixDataHandler { - bool operator() (const ColEntry& rEntry) const - { - return rEntry.pCell == NULL; - } -}; - -} + ScColumn& mrDestColumn; -void ScColumn::MixData( - sc::MixDocContext& rCxt, SCROW nRow1, SCROW nRow2, sal_uInt16 nFunction, - bool bSkipEmpty, const ScColumn& rSrcCol ) -{ - SCSIZE nSrcCount = rSrcCol.maItems.size(); + sc::CellStoreType& mrDestCells; + sc::CellStoreType::iterator miDestPos; - sc::ColumnBlockPosition* p = rCxt.getBlockPosition(nTab, nCol); + sc::CellTextAttrStoreType& mrDestAttrs; - SCSIZE nIndex; - Search( nRow1, nIndex ); + sc::CellStoreType maNewCells; + sc::CellStoreType::iterator miNewCellsPos; - SCSIZE nSrcIndex = 0, nDestIndex = 0; - rSrcCol.Search( nRow1, nSrcIndex ); // See if data is at the beginning + size_t mnRowOffset; + sal_uInt16 mnFunction; - SCROW nNextThis = MAXROW+1; - if ( nIndex < maItems.size() ) - nNextThis = maItems[nIndex].nRow; - SCROW nNextSrc = MAXROW+1; - if ( nSrcIndex < nSrcCount ) - nNextSrc = rSrcCol.maItems[nSrcIndex].nRow; + bool mbSkipEmpty; - bool bDeferredDelete = false; - while ( nNextThis <= nRow2 || nNextSrc <= nRow2 ) +public: + MixDataHandler( + sc::ColumnBlockPosition* pBlockPos, + ScColumn& rDestColumn, + sc::CellStoreType& rDestCells, + sc::CellTextAttrStoreType& rDestAttrs, + SCROW nRow1, SCROW nRow2, + sal_uInt16 nFunction, bool bSkipEmpty) : + mrDestColumn(rDestColumn), + mrDestCells(rDestCells), + mrDestAttrs(rDestAttrs), + maNewCells(0, nRow2 - nRow1 + 1), + miNewCellsPos(maNewCells.begin()), + mnRowOffset(nRow1), + mnFunction(nFunction), + mbSkipEmpty(bSkipEmpty) { - SCROW nRow = std::min( nNextThis, nNextSrc ); + if (pBlockPos) + miDestPos = pBlockPos->miCellPos; + else + miDestPos = mrDestCells.begin(); + } - ScBaseCell* pSrc = NULL; - ScBaseCell* pDest = NULL; - ScBaseCell* pNew = NULL; - bool bDelete = false; + void operator() (size_t nRow, double f) + { + std::pair<sc::CellStoreType::iterator, size_t> aPos = mrDestCells.position(miDestPos, nRow); + miDestPos = aPos.first; + switch (miDestPos->type) + { + case sc::element_type_numeric: + { + // Both src and dest are of numeric type. + bool bOk = lcl_DoFunction(f, sc::numeric_block::at(*miDestPos->data, aPos.second), mnFunction); - if ( nSrcIndex < nSrcCount && nNextSrc == nRow ) - pSrc = rSrcCol.maItems[nSrcIndex].pCell; + if (bOk) + miNewCellsPos = maNewCells.set(miNewCellsPos, nRow-mnRowOffset, f); + else + { + ScFormulaCell* pFC = + new ScFormulaCell( + &mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab())); - if ( nIndex < maItems.size() && nNextThis == nRow ) - { - pDest = maItems[nIndex].pCell; - nDestIndex = nIndex; - } + pFC->SetErrCode(errNoValue); + miNewCellsPos = maNewCells.set(miNewCellsPos, nRow-mnRowOffset, pFC); + } + } + break; + case sc::element_type_formula: + { + // Combination of value and at least one formula -> Create formula + ScTokenArray aArr; - OSL_ENSURE( pSrc || pDest, "What happened?" ); + // First row + aArr.AddDouble(f); - CellType eSrcType = pSrc ? pSrc->GetCellType() : CELLTYPE_NONE; - CellType eDestType = pDest ? pDest->GetCellType() : CELLTYPE_NONE; + // Operator + OpCode eOp = ocAdd; + switch (mnFunction) + { + case PASTE_ADD: eOp = ocAdd; break; + case PASTE_SUB: eOp = ocSub; break; + case PASTE_MUL: eOp = ocMul; break; + case PASTE_DIV: eOp = ocDiv; break; + } + aArr.AddOpCode(eOp); // Function - bool bSrcEmpty = (eSrcType == CELLTYPE_NONE); - bool bDestEmpty = (eDestType == CELLTYPE_NONE); + // Second row + ScFormulaCell* pDest = sc::formula_block::at(*miDestPos->data, aPos.second); + lcl_AddCode(aArr, pDest); - if ( bSkipEmpty && bDestEmpty ) // Restore original row - { - if ( pSrc ) // Did we have a row here? + 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: { - pNew = pSrc->Clone( *pDocument ); + // Destination cell is not a number. Just take the source cell. + miNewCellsPos = maNewCells.set(miNewCellsPos, nRow-mnRowOffset, f); } + break; + default: + ; } - else if ( nFunction ) // Really provide calculation function - { - double nVal1; - double nVal2; - if ( eSrcType == CELLTYPE_VALUE ) - nVal1 = ((ScValueCell*)pSrc)->GetValue(); - else - nVal1 = 0.0; - if ( eDestType == CELLTYPE_VALUE ) - nVal2 = ((ScValueCell*)pDest)->GetValue(); - else - nVal2 = 0.0; + } - // Empty row is treated as a value - sal_Bool bSrcVal = ( bSrcEmpty || eSrcType == CELLTYPE_VALUE ); - sal_Bool bDestVal = ( bDestEmpty || eDestType == CELLTYPE_VALUE ); + void operator() (size_t nRow, const OUString& rStr) + { + miNewCellsPos = maNewCells.set(miNewCellsPos, nRow-mnRowOffset, rStr); + } - sal_Bool bSrcText = ( eSrcType == CELLTYPE_STRING || - eSrcType == CELLTYPE_EDIT ); - sal_Bool bDestText = ( eDestType == CELLTYPE_STRING || - eDestType == CELLTYPE_EDIT ); + void operator() (size_t nRow, const EditTextObject* p) + { + miNewCellsPos = maNewCells.set(miNewCellsPos, nRow-mnRowOffset, p->Clone()); + } - // Else we only have formulas ... - if ( bSrcEmpty && bDestEmpty ) - { - // Both empty -> do nothing - } - else if ( bSrcVal && bDestVal ) + void operator() (size_t nRow, const ScFormulaCell* p) + { + std::pair<sc::CellStoreType::iterator, size_t> aPos = mrDestCells.position(miDestPos, nRow); + miDestPos = aPos.first; + switch (miDestPos->type) + { + case sc::element_type_numeric: { - // Insterted new value or both have overflown - sal_Bool bOk = lcl_DoFunction( nVal1, nVal2, nFunction ); + // Source is formula, and dest is value. + ScTokenArray aArr; - if (bOk) - pNew = new ScValueCell( nVal1 ); - else + // First row + lcl_AddCode(aArr, p); + + // Operator + OpCode eOp = ocAdd; + switch (mnFunction) { - ScFormulaCell* pFC = new ScFormulaCell( pDocument, - ScAddress( nCol, nRow, nTab ) ); - pFC->SetErrCode( errNoValue ); - //! oder NOVALUE, dann auch in consoli, - //! sonst in Interpreter::GetCellValue die Abfrage auf errNoValue raus - //! (dann geht Stringzelle+Wertzelle nicht mehr) - pNew = pFC; + case PASTE_ADD: eOp = ocAdd; break; + case PASTE_SUB: eOp = ocSub; break; + case PASTE_MUL: eOp = ocMul; break; + case PASTE_DIV: eOp = ocDiv; break; } + aArr.AddOpCode(eOp); // Function + + // Second row + aArr.AddDouble(sc::numeric_block::at(*miDestPos->data, aPos.second)); + + miNewCellsPos = maNewCells.set( + miNewCellsPos, nRow-mnRowOffset, + new ScFormulaCell( + &mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab()), &aArr)); } - else if ( bSrcText || bDestText ) - { - // We do no not calculate with texts - alwyas "old" cell, thus pSrc - if (pSrc) - pNew = pSrc->Clone( *pDocument ); - else if (pDest) - bDelete = sal_True; - } - else + break; + case sc::element_type_formula: { - // Combination of value and at least one formula -> Create formula + // Both are formulas. ScTokenArray aArr; // First row - if ( eSrcType == CELLTYPE_FORMULA ) - lcl_AddCode( aArr, (ScFormulaCell*)pSrc ); - else - aArr.AddDouble( nVal1 ); + lcl_AddCode(aArr, p); // Operator OpCode eOp = ocAdd; - switch ( nFunction ) + switch (mnFunction) { case PASTE_ADD: eOp = ocAdd; break; case PASTE_SUB: eOp = ocSub; break; @@ -1026,72 +1173,177 @@ void ScColumn::MixData( aArr.AddOpCode(eOp); // Function // Second row - if ( eDestType == CELLTYPE_FORMULA ) - lcl_AddCode( aArr, (ScFormulaCell*)pDest ); - else - aArr.AddDouble( nVal2 ); + ScFormulaCell* pDest = sc::formula_block::at(*miDestPos->data, aPos.second); + lcl_AddCode(aArr, pDest); - pNew = new ScFormulaCell( pDocument, ScAddress( nCol, nRow, nTab ), &aArr ); + 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. + */ + void operator() (mdds::mtv::element_t, size_t nTopRow, size_t nDataSize) + { + if (mbSkipEmpty) + return; - if ( pNew || bDelete ) // New result? + // Source cells are empty. Treat them as if they have a value of 0.0. + for (size_t i = 0; i < nDataSize; ++i) { - if (pDest && !pNew) // Old cell present? + size_t nDestRow = nTopRow + i; + std::pair<sc::CellStoreType::iterator, size_t> aPos = mrDestCells.position(miDestPos, nDestRow); + miDestPos = aPos.first; + switch (miDestPos->type) { - // Delete the destination cell because the cell was originally - // empty. Don't erase its slot in the cell array yet. - OSL_ASSERT(pDest == maItems[nDestIndex].pCell); - maItems[nDestIndex].pCell = NULL; + case sc::element_type_numeric: + case sc::element_type_string: + case sc::element_type_edittext: + // Dont' do anything. + break; + case sc::element_type_formula: + { + ScTokenArray aArr; - if (pDest->GetCellType() == CELLTYPE_FORMULA) - static_cast<ScFormulaCell*>(pDest)->EndListeningTo(pDocument); - pDest->Delete(); + // First row + ScFormulaCell* pSrc = sc::formula_block::at(*miDestPos->data, aPos.second); + lcl_AddCode( aArr, pSrc); - bDeferredDelete = true; + // Operator + OpCode eOp = ocAdd; + switch (mnFunction) + { + case PASTE_ADD: eOp = ocAdd; break; + case PASTE_SUB: eOp = ocSub; break; + case PASTE_MUL: eOp = ocMul; break; + case PASTE_DIV: eOp = ocDiv; break; + } + + aArr.AddOpCode(eOp); // Function + aArr.AddDouble(0.0); + + miNewCellsPos = maNewCells.set( + miNewCellsPos, nDestRow-mnRowOffset, + new ScFormulaCell( + &mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nDestRow, mrDestColumn.GetTab()), &aArr)); + } + break; + default: + ; } - if (pNew) + } + } + + /** + * Set the new cells to the destination (this) column. + */ + void commit(sc::ColumnBlockPosition* pDestBlockPos) + { + sc::ColumnBlockPosition aDestBlockPos; + if (pDestBlockPos) + aDestBlockPos = *pDestBlockPos; + else + mrDestColumn.InitBlockPosition(aDestBlockPos); + + // Stop all formula cells in the destination range first. + sc::ColumnBlockPosition aCopy = aDestBlockPos; + mrDestColumn.EndFormulaListening(aCopy, mnRowOffset, mnRowOffset + maNewCells.size() - 1); + + // Move the new cells to the destination range. + sc::CellStoreType::iterator& itDestPos = aDestBlockPos.miCellPos; + sc::CellTextAttrStoreType::iterator& itDestAttrPos = aDestBlockPos.miCellTextAttrPos; + + sc::CellStoreType::iterator it = maNewCells.begin(), itEnd = maNewCells.end(); + for (; it != itEnd; ++it) + { + bool bHasContent = true; + size_t nDestRow = mnRowOffset + it->position; + + switch (it->type) { - if (p) - Insert(*p, nRow, pNew); - else - Insert(nRow, pNew); // Insert new one + case sc::element_type_numeric: + { + sc::numeric_block::iterator itData = sc::numeric_block::begin(*it->data); + sc::numeric_block::iterator itDataEnd = sc::numeric_block::end(*it->data); + itDestPos = mrDestCells.set(itDestPos, nDestRow, itData, itDataEnd); + } + break; + case sc::element_type_string: + { + sc::string_block::iterator itData = sc::string_block::begin(*it->data); + sc::string_block::iterator itDataEnd = sc::string_block::end(*it->data); + itDestPos = mrDestCells.set(itDestPos, nDestRow, itData, itDataEnd); + } + break; + case sc::element_type_edittext: + { + sc::edittext_block::iterator itData = sc::edittext_block::begin(*it->data); + sc::edittext_block::iterator itDataEnd = sc::edittext_block::end(*it->data); + itDestPos = mrDestCells.set(itDestPos, nDestRow, itData, itDataEnd); + } + case sc::element_type_formula: + { + sc::formula_block::iterator itData = sc::formula_block::begin(*it->data); + sc::formula_block::iterator itDataEnd = sc::formula_block::end(*it->data); + itDestPos = mrDestCells.set(itDestPos, nDestRow, itData, itDataEnd); + } + break; + case sc::element_type_empty: + { + itDestPos = mrDestCells.set_empty(itDestPos, nDestRow, nDestRow+it->size-1); + bHasContent = false; + } + break; + default: + ; } - Search( nRow, nIndex ); // Everything could have moved - if (pNew) - nNextThis = nRow; // nIndex points right at nRow now + if (bHasContent) + { + std::vector<sc::CellTextAttr> aAttrs(it->size, sc::CellTextAttr()); + itDestAttrPos = mrDestAttrs.set(itDestAttrPos, nDestRow, aAttrs.begin(), aAttrs.end()); + } else - nNextThis = ( nIndex < maItems.size() ) ? maItems[nIndex].nRow : MAXROW+1; + itDestAttrPos = mrDestAttrs.set_empty(itDestAttrPos, nDestRow, nDestRow+it->size-1); } - if ( nNextThis == nRow ) - { - ++nIndex; - nNextThis = ( nIndex < maItems.size() ) ? maItems[nIndex].nRow : MAXROW+1; - } - if ( nNextSrc == nRow ) - { - ++nSrcIndex; - nNextSrc = ( nSrcIndex < nSrcCount ) ? - rSrcCol.maItems[nSrcIndex].nRow : - MAXROW+1; - } + maNewCells.release(); + + if (pDestBlockPos) + *pDestBlockPos = aDestBlockPos; } +}; - if (bDeferredDelete) - { - // Erase all the slots in the cell array where the deleted cells - // previously occupied. - std::vector<ColEntry>::iterator it = - std::remove_if(maItems.begin(), maItems.end(), FindRemovedCell()); +} - maItems.erase(it, maItems.end()); +void ScColumn::MixData( + sc::MixDocContext& rCxt, SCROW nRow1, SCROW nRow2, sal_uInt16 nFunction, + bool bSkipEmpty, const ScColumn& rSrcCol ) +{ + // destination (this column) block position. - // Reset the cell text attriute array to keep it in sync again. - ResetCellTextAttrs(); - } + sc::ColumnBlockPosition* p = rCxt.getBlockPosition(nTab, nCol); + MixDataHandler aFunc(p, *this, maCells, maCellTextAttrs, nRow1, nRow2, nFunction, bSkipEmpty); + sc::ParseAll(rSrcCol.maCells.begin(), rSrcCol.maCells, nRow1, nRow2, aFunc, aFunc); + + aFunc.commit(p); } @@ -1100,90 +1352,68 @@ ScAttrIterator* ScColumn::CreateAttrIterator( SCROW nStartRow, SCROW nEndRow ) c return new ScAttrIterator( pAttrArray, nStartRow, nEndRow ); } +namespace { -void ScColumn::StartAllListeners() +class StartAllListenersHandler { - if (maItems.empty()) - return; + ScDocument* mpDoc; +public: + StartAllListenersHandler(ScDocument* pDoc) : mpDoc(pDoc) {} - for (SCSIZE i = 0; i < maItems.size(); i++) + void operator() (size_t, ScFormulaCell* p) { - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - { - SCROW nRow = maItems[i].nRow; - ((ScFormulaCell*)pCell)->StartListeningTo( pDocument ); - if ( nRow != maItems[i].nRow ) - Search( nRow, i ); // Insert Listener? - } + p->StartListeningTo(mpDoc); } -} - +}; -void ScColumn::StartNeededListeners() +class StartNeededListenerHandler { - if (maItems.empty()) - return; + ScDocument* mpDoc; +public: + StartNeededListenerHandler(ScDocument* pDoc) : mpDoc(pDoc) {} - for (SCSIZE i = 0; i < maItems.size(); i++) + void operator() (size_t, ScFormulaCell* p) { - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - { - ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); - if (pFCell->NeedsListening()) - { - SCROW nRow = maItems[i].nRow; - pFCell->StartListeningTo( pDocument ); - if ( nRow != maItems[i].nRow ) - Search( nRow, i ); // Insert Listener? - } - } + if (p->NeedsListening()) + p->StartListeningTo(mpDoc); } -} +}; +} -void ScColumn::BroadcastInArea( SCROW nRow1, SCROW nRow2 ) +void ScColumn::StartAllListeners() { - if (maItems.empty()) - return; + StartAllListenersHandler aFunc(pDocument); + sc::ProcessFormula(maCells, aFunc); +} - SCROW nRow; - SCSIZE nIndex; - if (!Search(nRow1, nIndex)) - return; - while ( nIndex < maItems.size() && (nRow = maItems[nIndex].nRow) <= nRow2 ) - { - ScBaseCell* pCell = maItems[nIndex].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - ((ScFormulaCell*)pCell)->SetDirty(); - else - pDocument->Broadcast( ScHint(SC_HINT_DATACHANGED, - ScAddress(nCol, nRow, nTab))); - nIndex++; - } +void ScColumn::StartNeededListeners() +{ + StartNeededListenerHandler aFunc(pDocument); + sc::ProcessFormula(maCells, aFunc); } +namespace { -void ScColumn::StartListeningInArea( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 ) +class StartListeningInAreaHandler { - if (maItems.empty()) - return; + sc::StartListeningContext& mrCxt; +public: + StartListeningInAreaHandler(sc::StartListeningContext& rCxt) : mrCxt(rCxt) {} - SCROW nRow; - SCSIZE nIndex; - Search( nRow1, nIndex ); - while ( nIndex < maItems.size() && (nRow = maItems[nIndex].nRow) <= nRow2 ) + void operator() (size_t /*nRow*/, ScFormulaCell* p) { - ScBaseCell* pCell = maItems[nIndex].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - ((ScFormulaCell*)pCell)->StartListeningTo(rCxt); - if ( nRow != maItems[nIndex].nRow ) - Search( nRow, nIndex ); // Inserted via Listening - - ++nIndex; + p->StartListeningTo(mrCxt); } +}; + +} + +void ScColumn::StartListeningInArea( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 ) +{ + StartListeningInAreaHandler aFunc(rCxt); + sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc); } @@ -1198,8 +1428,7 @@ bool ScColumn::SetString( SCROW nRow, SCTAB nTabP, const String& rString, if (!ValidRow(nRow)) return false; - ScBaseCell* pNewCell = NULL; - sal_Bool bIsLoading = false; + ScCellValue aNewCell; if (rString.Len() > 0) { ScSetStringParam aParam; @@ -1211,33 +1440,24 @@ bool ScColumn::SetString( SCROW nRow, SCTAB nTabP, const String& rString, sal_Unicode cFirstChar; if (!aParam.mpNumFormatter) aParam.mpNumFormatter = pDocument->GetFormatTable(); - SfxObjectShell* pDocSh = pDocument->GetDocumentShell(); - if ( pDocSh ) - bIsLoading = pDocSh->IsLoading(); - // IsLoading for ConvertFrom import - if ( !bIsLoading ) - { - nIndex = nOldIndex = GetNumberFormat( nRow ); - if ( rString.Len() > 1 - && aParam.mpNumFormatter->GetType(nIndex) != NUMBERFORMAT_TEXT ) - cFirstChar = rString.GetChar(0); - else - cFirstChar = 0; // Text - } - else - { // There are not applied formats when importing during ConvertFrom + + nIndex = nOldIndex = GetNumberFormat( nRow ); + if ( rString.Len() > 1 + && aParam.mpNumFormatter->GetType(nIndex) != NUMBERFORMAT_TEXT ) cFirstChar = rString.GetChar(0); - } + else + cFirstChar = 0; // Text if ( cFirstChar == '=' ) { if ( rString.Len() == 1 ) // = Text - pNewCell = new ScStringCell( rString ); + aNewCell.set(rString); else // = Formula - pNewCell = new ScFormulaCell( pDocument, - ScAddress( nCol, nRow, nTabP ), rString, - formula::FormulaGrammar::mergeToGrammar( formula::FormulaGrammar::GRAM_DEFAULT, - eConv), MM_NONE ); + aNewCell.set( + new ScFormulaCell( + pDocument, ScAddress(nCol, nRow, nTabP), rString, + formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_DEFAULT, eConv), + MM_NONE)); } else if ( cFirstChar == '\'') // 'Text { @@ -1251,53 +1471,18 @@ bool ScColumn::SetString( SCROW nRow, SCTAB nTabP, const String& rString, bNumeric = aParam.mpNumFormatter->IsNumberFormat(aTest, nIndex, fTest); if (bNumeric) // This is a number. Strip out the first char. - pNewCell = new ScStringCell(aTest); + aNewCell.set(aTest); } if (!bNumeric) // This is normal text. Take it as-is. - pNewCell = new ScStringCell(rString); + aNewCell.set(rString); } else { double nVal; - sal_Bool bIsText = false; - if ( bIsLoading ) - { - if ( !maItems.empty() ) - { - String aStr; - SCSIZE i = maItems.size(); - SCSIZE nStop = (i >= 3 ? i - 3 : 0); - // Compare the last lines and see whether same String - // and IsNumberFormat can be made obsolete - do - { - i--; - ScBaseCell* pCell = maItems[i].pCell; - switch ( pCell->GetCellType() ) - { - case CELLTYPE_STRING : - aStr = ((ScStringCell*)pCell)->GetString(); - if ( rString == aStr ) - bIsText = true; - break; - default: - if ( i == maItems.size() - 1 ) - i = 0; - // Probably whole column and no String - } - } while ( i && i > nStop && !bIsText ); - } - // Prefill nIndex for IsNumberFormat - if ( !bIsText ) - nIndex = nOldIndex = aParam.mpNumFormatter->GetStandardIndex(); - } do { - if (bIsText) - break; - if (aParam.mbDetectNumberFormat) { if (!aParam.mpNumFormatter->IsNumberFormat(rString, nIndex, nVal)) @@ -1311,7 +1496,7 @@ bool ScColumn::SetString( SCROW nRow, SCTAB nTabP, const String& rString, nIndex = aParam.mpNumFormatter->GetFormatForLanguageIfBuiltIn( nIndex, pOldFormat->GetLanguage() ); } - pNewCell = new ScValueCell( nVal ); + aNewCell.set(nVal); if ( nIndex != nOldIndex) { // #i22345# New behavior: Apply the detected number format only if @@ -1365,12 +1550,12 @@ bool ScColumn::SetString( SCROW nRow, SCTAB nTabP, const String& rString, if (!ScStringUtil::parseSimpleNumber(rString, dsep, gsep, nVal)) break; - pNewCell = new ScValueCell(nVal); + aNewCell.set(nVal); } } while (false); - if (!pNewCell) + if (aNewCell.meType == CELLTYPE_NONE) { if (aParam.meSetTextNumFormat != ScSetStringParam::Never && aParam.mpNumFormatter->IsNumberFormat(rString, nIndex, nVal)) { @@ -1382,52 +1567,12 @@ bool ScColumn::SetString( SCROW nRow, SCTAB nTabP, const String& rString, ApplyPattern(nRow, aNewAttrs); } - pNewCell = new ScStringCell(rString); + aNewCell.set(rString); } } } - if ( bIsLoading && (maItems.empty() || nRow > maItems.back().nRow) ) - { // Save search and build up Listener without a detour via Insert - // Broadcast comes after Loading - if ( pNewCell ) - Append( nRow, pNewCell ); - } - else - { - SCSIZE i; - if (Search(nRow, i)) - { - ScBaseCell* pOldCell = maItems[i].pCell; - if (pNewCell) - { - if ( pOldCell->GetCellType() == CELLTYPE_FORMULA ) - static_cast<ScFormulaCell*>(pOldCell)->EndListeningTo(pDocument); - - pOldCell->Delete(); - maItems[i].pCell = pNewCell; // Replace - maCellTextAttrs.set<sc::CellTextAttr>(nRow, sc::CellTextAttr()); - CellStorageModified(); - - if ( pNewCell->GetCellType() == CELLTYPE_FORMULA ) - { - static_cast<ScFormulaCell*>(pNewCell)->StartListeningTo(pDocument); - ((ScFormulaCell*)pNewCell)->SetDirty(); - } - else - pDocument->Broadcast( - ScHint(SC_HINT_DATACHANGED, ScAddress(nCol, nRow, nTabP))); - } - else - { - DeleteAtIndex(i); // Delete and Broadcast - } - } - else if (pNewCell) - { - Insert(nRow, pNewCell); // Re-insert and Broadcast - } - } + aNewCell.release(*this, nRow); // Do not set Formats and Formulas here anymore! // These are queried during output @@ -1437,114 +1582,361 @@ bool ScColumn::SetString( SCROW nRow, SCTAB nTabP, const String& rString, void ScColumn::SetEditText( SCROW nRow, EditTextObject* pEditText ) { - Insert(nRow, new ScEditCell(pEditText, pDocument)); + sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + maCells.set(it, nRow, pEditText); + maCellTextAttrs.set(nRow, sc::CellTextAttr()); + CellStorageModified(); + + BroadcastNewCell(nRow); +} + +void ScColumn::SetEditText( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, EditTextObject* pEditText ) +{ + rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow); + rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, pEditText); + rBlockPos.miCellTextAttrPos = maCellTextAttrs.set( + rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); + CellStorageModified(); + + BroadcastNewCell(nRow); +} + +void ScColumn::SetEditText( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, const EditTextObject& rEditText ) +{ + if (pDocument->GetEditPool() == rEditText.GetPool()) + { + SetEditText(rBlockPos, nRow, rEditText.Clone()); + return; + } + + //! another "spool" + // Sadly there is no other way to change the Pool than to + // "spool" the Object through a corresponding Engine + EditEngine& rEngine = pDocument->GetEditEngine(); + if (!rEditText.HasOnlineSpellErrors()) + { + rEngine.SetText(rEditText); + SetEditText(rBlockPos, nRow, rEngine.CreateTextObject()); + return; + } + + sal_uLong nControl = rEngine.GetControlWord(); + const sal_uLong nSpellControl = EE_CNTRL_ONLINESPELLING | EE_CNTRL_ALLOWBIGOBJS; + bool bNewControl = (nControl & nSpellControl) != nSpellControl; + if (bNewControl) + rEngine.SetControlWord(nControl | nSpellControl); + rEngine.SetText(rEditText); + EditTextObject* pData = rEngine.CreateTextObject(); + if (bNewControl) + rEngine.SetControlWord(nControl); + + SetEditText(rBlockPos, nRow, pData); } void ScColumn::SetEditText( SCROW nRow, const EditTextObject& rEditText, const SfxItemPool* pEditPool ) { - Insert(nRow, new ScEditCell(rEditText, pDocument, pEditPool)); + if (pEditPool && pDocument->GetEditPool() == pEditPool) + { + SetEditText(nRow, rEditText.Clone()); + return; + } + + //! another "spool" + // Sadly there is no other way to change the Pool than to + // "spool" the Object through a corresponding Engine + EditEngine& rEngine = pDocument->GetEditEngine(); + if (!rEditText.HasOnlineSpellErrors()) + { + rEngine.SetText(rEditText); + SetEditText(nRow, rEngine.CreateTextObject()); + return; + } + + sal_uLong nControl = rEngine.GetControlWord(); + const sal_uLong nSpellControl = EE_CNTRL_ONLINESPELLING | EE_CNTRL_ALLOWBIGOBJS; + bool bNewControl = (nControl & nSpellControl) != nSpellControl; + if (bNewControl) + rEngine.SetControlWord(nControl | nSpellControl); + rEngine.SetText(rEditText); + EditTextObject* pData = rEngine.CreateTextObject(); + if (bNewControl) + rEngine.SetControlWord(nControl); + + SetEditText(nRow, pData); } void ScColumn::SetFormula( SCROW nRow, const ScTokenArray& rArray, formula::FormulaGrammar::Grammar eGram ) { ScAddress aPos(nCol, nRow, nTab); + + sc::CellStoreType::iterator it = GetPositionToInsert(nRow); ScFormulaCell* pCell = new ScFormulaCell(pDocument, aPos, &rArray, eGram); - sal_uInt32 nCellFormat = GetNumberFormat( nRow ); + sal_uInt32 nCellFormat = GetNumberFormat(nRow); if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0) pCell->SetNeedNumberFormat(true); - Insert(nRow, pCell); + maCells.set(it, nRow, pCell); + maCellTextAttrs.set(nRow, sc::CellTextAttr()); + CellStorageModified(); + + ActivateNewFormulaCell(pCell); } void ScColumn::SetFormula( SCROW nRow, const OUString& rFormula, formula::FormulaGrammar::Grammar eGram ) { ScAddress aPos(nCol, nRow, nTab); - ScFormulaCell* pCell = new ScFormulaCell(pDocument, aPos, rFormula, eGram); - sal_uInt32 nCellFormat = GetNumberFormat( nRow ); + sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + ScFormulaCell* pCell = new ScFormulaCell(pDocument, aPos, rFormula, eGram); + sal_uInt32 nCellFormat = GetNumberFormat(nRow); if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0) pCell->SetNeedNumberFormat(true); - Insert(nRow, pCell); + maCells.set(it, nRow, pCell); + maCellTextAttrs.set(nRow, sc::CellTextAttr()); + CellStorageModified(); + + ActivateNewFormulaCell(pCell); } void ScColumn::SetFormulaCell( SCROW nRow, ScFormulaCell* pCell ) { - Insert(nRow, pCell); + sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + maCells.set(it, nRow, pCell); + maCellTextAttrs.set(nRow, sc::CellTextAttr()); + CellStorageModified(); + + ActivateNewFormulaCell(pCell); } -void ScColumn::GetFilterEntries(SCROW nStartRow, SCROW nEndRow, std::vector<ScTypedStrData>& rStrings, bool& rHasDates) +void ScColumn::SetFormulaCell( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, ScFormulaCell* pCell ) { - bool bHasDates = false; - SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); - OUString aString; - SCSIZE nIndex; + rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow); + rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, pCell); + rBlockPos.miCellTextAttrPos = maCellTextAttrs.set( + rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); + CellStorageModified(); - Search( nStartRow, nIndex ); + ActivateNewFormulaCell(pCell); +} - for (; nIndex < maItems.size(); ++nIndex) - { - SCROW nRow = maItems[nIndex].nRow; - if (nRow > nEndRow) - break; +namespace { - ScBaseCell* pCell = maItems[nIndex].pCell; - ScRefCellValue aCell; - aCell.assign(*maItems[nIndex].pCell); - sal_uLong nFormat = GetNumberFormat( nRow ); +class FilterEntriesHandler +{ + ScColumn& mrColumn; + std::vector<ScTypedStrData>& mrStrings; + bool mbHasDates; - ScCellFormat::GetInputString(aCell, nFormat, aString, *pFormatter); + void processCell(SCROW nRow, ScRefCellValue& rCell) + { + SvNumberFormatter* pFormatter = mrColumn.GetDoc().GetFormatTable(); + OUString aStr; + sal_uLong nFormat = mrColumn.GetNumberFormat(nRow); + ScCellFormat::GetInputString(rCell, nFormat, aStr, *pFormatter); - if ( pDocument->HasStringData( nCol, nRow, nTab ) ) + if (rCell.hasString()) { - rStrings.push_back(ScTypedStrData(aString)); - continue; + mrStrings.push_back(ScTypedStrData(aStr)); + return; } - double nValue = 0.0; + double fVal = 0.0; - switch ( pCell->GetCellType() ) + switch (rCell.meType) { case CELLTYPE_VALUE: - nValue = ((ScValueCell*)pCell)->GetValue(); + fVal = rCell.mfValue; break; case CELLTYPE_FORMULA: { - ScFormulaCell* pFC = static_cast<ScFormulaCell*>(pCell); + ScFormulaCell* pFC = rCell.mpFormula; sal_uInt16 nErr = pFC->GetErrCode(); if (nErr) { // Error cell is evaluated as string (for now). - String aErr = ScGlobal::GetErrorString(nErr); - if (aErr.Len()) + OUString aErr = ScGlobal::GetErrorString(nErr); + if (!aErr.isEmpty()) { - rStrings.push_back(ScTypedStrData(aErr)); - continue; + mrStrings.push_back(ScTypedStrData(aErr)); + return; } } else - nValue = pFC->GetValue(); + fVal = pFC->GetValue(); } break; default: ; } - if (pFormatter) + short nType = pFormatter->GetType(nFormat); + if ((nType & NUMBERFORMAT_DATE) && !(nType & NUMBERFORMAT_TIME)) + { + // special case for date values. Disregard the time + // element if the number format is of date type. + fVal = rtl::math::approxFloor(fVal); + mbHasDates = true; + } + + mrStrings.push_back(ScTypedStrData(aStr, fVal, ScTypedStrData::Value)); + } + +public: + FilterEntriesHandler(ScColumn& rColumn, std::vector<ScTypedStrData>& rStrings) : + mrColumn(rColumn), mrStrings(rStrings), mbHasDates(false) {} + + void operator() (size_t nRow, double fVal) + { + ScRefCellValue aCell(fVal); + processCell(nRow, aCell); + } + + void operator() (size_t nRow, const OUString& rStr) + { + ScRefCellValue aCell(&rStr); + processCell(nRow, aCell); + } + + void operator() (size_t nRow, const EditTextObject* p) + { + ScRefCellValue aCell(p); + processCell(nRow, aCell); + } + + void operator() (size_t nRow, const ScFormulaCell* p) + { + ScRefCellValue aCell(const_cast<ScFormulaCell*>(p)); + processCell(nRow, aCell); + } + + bool hasDates() const { return mbHasDates; } +}; + +} + +void ScColumn::GetFilterEntries(SCROW nStartRow, SCROW nEndRow, std::vector<ScTypedStrData>& rStrings, bool& rHasDates) +{ + FilterEntriesHandler aFunc(*this, rStrings); + sc::ParseAllNonEmpty(maCells.begin(), maCells, nStartRow, nEndRow, aFunc); + rHasDates = aFunc.hasDates(); +} + +namespace { + +/** + * Iterate over only string and edit-text cells. + */ +class StrCellIterator +{ + typedef std::pair<sc::CellStoreType::const_iterator,size_t> PosType; + const sc::CellStoreType& mrCells; + PosType maPos; + sc::CellStoreType::const_iterator miBeg; + sc::CellStoreType::const_iterator miEnd; +public: + StrCellIterator(const sc::CellStoreType& rCells, SCROW nStart) : + mrCells(rCells), maPos(rCells.position(nStart)), miBeg(rCells.begin()), miEnd(rCells.end()) {} + + bool valid() const { return (maPos.first != miEnd); } + + bool has() const + { + return (maPos.first->type == sc::element_type_string || maPos.first->type == sc::element_type_edittext); + } + + bool prev() + { + if (!has()) + { + // Not in a string block. Move back until we hit a string block. + while (!has()) + { + if (maPos.first == miBeg) + return false; + + --maPos.first; // move to the preceding block. + maPos.second = maPos.first->size - 1; // last cell in the block. + } + return true; + } + + // 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) + return false; + + // Move to the last cell of the previous block. + --maPos.first; + maPos.second = maPos.first->size - 1; + if (has()) + break; + } + } + return true; + } + + bool next() + { + if (!has()) { - short nType = pFormatter->GetType(nFormat); - if ((nType & NUMBERFORMAT_DATE) && !(nType & NUMBERFORMAT_TIME)) + // Not in a string block. Move forward until we hit a string block. + while (!has()) { - // special case for date values. Disregard the time - // element if the number format is of date type. - nValue = ::rtl::math::approxFloor(nValue); - bHasDates = true; + ++maPos.first; + if (maPos.first == miEnd) + return false; + + maPos.second = 0; // First cell in this block. } + return true; } - rStrings.push_back(ScTypedStrData(aString, nValue, ScTypedStrData::Value)); + // 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) + return false; + + maPos.second = 0; + if (has()) + break; + } + } + return true; } - rHasDates = bHasDates; + OUString get() const + { + switch (maPos.first->type) + { + case sc::element_type_string: + return sc::string_block::at(*maPos.first->data, maPos.second); + case sc::element_type_edittext: + { + const EditTextObject* p = sc::edittext_block::at(*maPos.first->data, maPos.second); + return ScEditUtil::GetString(*p); + } + default: + ; + } + return OUString(); + } +}; + } // @@ -1556,87 +1948,132 @@ void ScColumn::GetFilterEntries(SCROW nStartRow, SCROW nEndRow, std::vector<ScTy #define DATENT_MAX 200 #define DATENT_SEARCH 2000 - -bool ScColumn::GetDataEntries(SCROW nStartRow, std::set<ScTypedStrData>& rStrings, bool bLimit) +bool ScColumn::GetDataEntries( + SCROW nStartRow, std::set<ScTypedStrData>& rStrings, bool bLimit ) const { - sal_Bool bFound = false; - SCSIZE nThisIndex; - sal_Bool bThisUsed = Search( nStartRow, nThisIndex ); - String aString; - sal_uInt16 nCells = 0; + // 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. - // The limitation to neighbouring cells (without gaps) is not wanted anymore - // (Featurecommission for 5.1), search upwards/downwards instead so that - // nearby cell are cought at least first. - // TODO: Compare distances of cell numbers? Performance?? + StrCellIterator aItrUp(maCells, nStartRow); + StrCellIterator aItrDown(maCells, nStartRow+1); - SCSIZE nUpIndex = nThisIndex; // Points after the row - SCSIZE nDownIndex = nThisIndex; // Points to the row - if (bThisUsed) - ++nDownIndex; // Skip starting row + bool bMoveUp = aItrUp.valid(); + if (!bMoveUp) + // Current cell is invalid. + return false; + + // Skip the start position cell. + bMoveUp = aItrUp.prev(); // Find the previous string cell position. - while ( nUpIndex || nDownIndex < maItems.size() ) + bool bMoveDown = aItrDown.valid(); + if (bMoveDown && !aItrDown.has()) + bMoveDown = aItrDown.next(); // Find the next string cell position. + + bool bFound = false; + size_t nCellsSearched = 0; + while (bMoveUp || bMoveDown) { - if ( nUpIndex ) // Up + if (bMoveUp) { - ScBaseCell* pCell = maItems[nUpIndex-1].pCell; - CellType eType = pCell->GetCellType(); - if (eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT) // Only Strings are of interest + // Get the current string and move up. + OUString aStr = aItrUp.get(); + if (!aStr.isEmpty()) { - if (eType == CELLTYPE_STRING) - aString = ((ScStringCell*)pCell)->GetString(); - else - aString = ((ScEditCell*)pCell)->GetString(); - - bool bInserted = rStrings.insert(ScTypedStrData(aString)).second; + bool bInserted = rStrings.insert(ScTypedStrData(aStr)).second; if (bInserted && bLimit && rStrings.size() >= DATENT_MAX) - break; // Maximum reached + return true; // Maximum reached bFound = true; - - if ( bLimit ) - if (++nCells >= DATENT_SEARCH) - break; // Searched enough } - --nUpIndex; + + if (bLimit && ++nCellsSearched >= DATENT_SEARCH) + return bFound; // max search cell count reached. + + bMoveUp = aItrUp.prev(); } - if ( nDownIndex < maItems.size() ) // Down + if (bMoveDown) { - ScBaseCell* pCell = maItems[nDownIndex].pCell; - CellType eType = pCell->GetCellType(); - if (eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT) // Only Strings are of interest + // Get the current string and move down. + OUString aStr = aItrDown.get(); + if (!aStr.isEmpty()) { - if (eType == CELLTYPE_STRING) - aString = ((ScStringCell*)pCell)->GetString(); - else - aString = ((ScEditCell*)pCell)->GetString(); - - bool bInserted = rStrings.insert(ScTypedStrData(aString)).second; + bool bInserted = rStrings.insert(ScTypedStrData(aStr)).second; if (bInserted && bLimit && rStrings.size() >= DATENT_MAX) - break; // Maximum reached + return true; // Maximum reached bFound = true; - - if ( bLimit ) - if (++nCells >= DATENT_SEARCH) - break; // Searched enough } - ++nDownIndex; + + if (bLimit && ++nCellsSearched >= DATENT_SEARCH) + return bFound; // max search cell count reached. + + bMoveDown = aItrDown.next(); } } return bFound; } -#undef DATENT_MAX -#undef DATENT_SEARCH +namespace { + +class FormulaToValueHandler +{ + struct Entry + { + SCROW mnRow; + ScCellValue maValue; + + Entry(SCROW nRow, double f) : mnRow(nRow), maValue(f) {} + Entry(SCROW nRow, const OUString& rStr) : mnRow(nRow), maValue(rStr) {} + }; + typedef std::vector<Entry> EntriesType; + EntriesType maEntries; + +public: + + void operator() (size_t nRow, const ScFormulaCell* p) + { + ScFormulaCell* p2 = const_cast<ScFormulaCell*>(p); + if (p2->IsValue()) + maEntries.push_back(Entry(nRow, p2->GetValue())); + else + maEntries.push_back(Entry(nRow, p2->GetString())); + } + + void commitCells(ScColumn& rColumn) + { + sc::ColumnBlockPosition aBlockPos; + rColumn.InitBlockPosition(aBlockPos); + + EntriesType::iterator it = maEntries.begin(), itEnd = maEntries.end(); + for (; it != itEnd; ++it) + { + Entry& r = *it; + switch (r.maValue.meType) + { + case CELLTYPE_VALUE: + rColumn.SetValue(aBlockPos, r.mnRow, r.maValue.mfValue, false); + break; + case CELLTYPE_STRING: + rColumn.SetRawString(aBlockPos, r.mnRow, *r.maValue.mpString, false); + default: + ; + } + } + } +}; + +} void ScColumn::RemoveProtected( SCROW nStartRow, SCROW nEndRow ) { + FormulaToValueHandler aFunc; + sc::CellStoreType::const_iterator itPos = maCells.begin(); + ScAttrIterator aAttrIter( pAttrArray, nStartRow, nEndRow ); SCROW nTop = -1; SCROW nBottom = -1; - SCSIZE nIndex; const ScPatternAttr* pPattern = aAttrIter.Next( nTop, nBottom ); while (pPattern) { @@ -1645,186 +2082,193 @@ void ScColumn::RemoveProtected( SCROW nStartRow, SCROW nEndRow ) DeleteArea( nTop, nBottom, IDF_CONTENTS ); else if ( pAttr->GetHideFormula() ) { - Search( nTop, nIndex ); - while ( nIndex<maItems.size() && maItems[nIndex].nRow<=nBottom ) - { - if ( maItems[nIndex].pCell->GetCellType() == CELLTYPE_FORMULA ) - { - ScFormulaCell* pFormula = (ScFormulaCell*)maItems[nIndex].pCell; - if (pFormula->IsValue()) - { - double nVal = pFormula->GetValue(); - maItems[nIndex].pCell = new ScValueCell( nVal ); - } - else - { - String aString = pFormula->GetString(); - maItems[nIndex].pCell = new ScStringCell( aString ); - } - delete pFormula; - - SetTextWidth(maItems[nIndex].nRow, TEXTWIDTH_DIRTY); - CellStorageModified(); - } - ++nIndex; - } + // Replace all formula cells between nTop and nBottom with raw value cells. + itPos = sc::ParseFormula(itPos, maCells, nTop, nBottom, aFunc); } pPattern = aAttrIter.Next( nTop, nBottom ); } + + aFunc.commitCells(*this); } void ScColumn::SetError( SCROW nRow, const sal_uInt16 nError) { - if (ValidRow(nRow)) - { - ScFormulaCell* pCell = new ScFormulaCell - ( pDocument, ScAddress( nCol, nRow, nTab ) ); - pCell->SetErrCode( nError ); - Insert( nRow, pCell ); - } + if (!ValidRow(nRow)) + return; + + ScFormulaCell* pCell = new ScFormulaCell(pDocument, ScAddress(nCol, nRow, nTab)); + pCell->SetErrCode(nError); + + sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + maCells.set(it, nRow, pCell); + maCellTextAttrs.set(nRow, sc::CellTextAttr()); + CellStorageModified(); + + ActivateNewFormulaCell(pCell); } +void ScColumn::SetRawString( SCROW nRow, const OUString& rStr, bool bBroadcast ) +{ + if (!ValidRow(nRow)) + return; -void ScColumn::SetValue( SCROW nRow, const double& rVal) + sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + maCells.set(it, nRow, rStr); + maCellTextAttrs.set(nRow, sc::CellTextAttr()); + CellStorageModified(); + + if (bBroadcast) + BroadcastNewCell(nRow); +} + +void ScColumn::SetRawString( + sc::ColumnBlockPosition& rBlockPos, SCROW nRow, const OUString& rStr, bool bBroadcast ) { - if (ValidRow(nRow)) - { - ScBaseCell* pCell = new ScValueCell(rVal); - Insert( nRow, pCell ); - } + if (!ValidRow(nRow)) + return; + + rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow); + rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, rStr); + rBlockPos.miCellTextAttrPos = maCellTextAttrs.set( + rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); + CellStorageModified(); + + if (bBroadcast) + BroadcastNewCell(nRow); +} + +void ScColumn::SetValue( SCROW nRow, double fVal ) +{ + if (!ValidRow(nRow)) + return; + + sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + maCells.set(it, nRow, fVal); + maCellTextAttrs.set(nRow, sc::CellTextAttr()); + CellStorageModified(); + + BroadcastNewCell(nRow); } +void ScColumn::SetValue( + sc::ColumnBlockPosition& rBlockPos, SCROW nRow, double fVal, bool bBroadcast ) +{ + if (!ValidRow(nRow)) + return; + + rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow); + rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, fVal); + rBlockPos.miCellTextAttrPos = maCellTextAttrs.set( + rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); + CellStorageModified(); + + if (bBroadcast) + BroadcastNewCell(nRow); +} void ScColumn::GetString( SCROW nRow, OUString& rString ) const { - SCSIZE nIndex; - Color* pColor; - if (Search(nRow, nIndex)) - { - ScRefCellValue aCell; - aCell.assign(*maItems[nIndex].pCell); + ScRefCellValue aCell = GetCellValue(nRow); - // ugly hack for ordering problem with GetNumberFormat and missing inherited formats - if(aCell.meType == CELLTYPE_FORMULA) - aCell.mpFormula->MaybeInterpret(); + // ugly hack for ordering problem with GetNumberFormat and missing inherited formats + if (aCell.meType == CELLTYPE_FORMULA) + aCell.mpFormula->MaybeInterpret(); - sal_uLong nFormat = GetNumberFormat( nRow ); - ScCellFormat::GetString(aCell, nFormat, rString, &pColor, *(pDocument->GetFormatTable())); - } - else - rString = EMPTY_OUSTRING; + sal_uLong nFormat = GetNumberFormat(nRow); + Color* pColor = NULL; + ScCellFormat::GetString(aCell, nFormat, rString, &pColor, *(pDocument->GetFormatTable())); } const OUString* ScColumn::GetStringCell( SCROW nRow ) const { - SCSIZE nIndex; - if (!Search(nRow, nIndex)) + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it == maCells.end()) return NULL; - const ScBaseCell* pCell = maItems[nIndex].pCell; - if (pCell->GetCellType() != CELLTYPE_STRING) + if (it->type != sc::element_type_string) return NULL; - return static_cast<const ScStringCell*>(pCell)->GetStringPtr(); + return &sc::string_block::at(*it->data, aPos.second); } double* ScColumn::GetValueCell( SCROW nRow ) { - SCSIZE nIndex; - if (!Search(nRow, nIndex)) + std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nRow); + sc::CellStoreType::iterator it = aPos.first; + if (it == maCells.end()) return NULL; - ScBaseCell* pCell = maItems[nIndex].pCell; - if (pCell->GetCellType() != CELLTYPE_VALUE) + if (it->type != sc::element_type_numeric) return NULL; - return static_cast<ScValueCell*>(pCell)->GetValuePtr(); + return &sc::numeric_block::at(*it->data, aPos.second); } void ScColumn::GetInputString( SCROW nRow, OUString& rString ) const { - SCSIZE nIndex; - if (Search(nRow, nIndex)) - { - ScRefCellValue aCell; - aCell.assign(*maItems[nIndex].pCell); - sal_uLong nFormat = GetNumberFormat( nRow ); - ScCellFormat::GetInputString(aCell, nFormat, rString, *(pDocument->GetFormatTable())); - } - else - rString = OUString(); + ScRefCellValue aCell = GetCellValue(nRow); + sal_uLong nFormat = GetNumberFormat(nRow); + ScCellFormat::GetInputString(aCell, nFormat, rString, *(pDocument->GetFormatTable())); } - double ScColumn::GetValue( SCROW nRow ) const { - SCSIZE nIndex; - if (Search(nRow, nIndex)) + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); + sc::CellStoreType::const_iterator it = aPos.first; + switch (it->type) { - ScBaseCell* pCell = maItems[nIndex].pCell; - switch (pCell->GetCellType()) + case sc::element_type_numeric: + return sc::numeric_block::at(*it->data, aPos.second); + case sc::element_type_formula: { - case CELLTYPE_VALUE: - return ((ScValueCell*)pCell)->GetValue(); - - case CELLTYPE_FORMULA: - { - if (((ScFormulaCell*)pCell)->IsValue()) - return ((ScFormulaCell*)pCell)->GetValue(); - else - return 0.0; - } - - default: - return 0.0; + const ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second); + ScFormulaCell* p2 = const_cast<ScFormulaCell*>(p); + return p2->IsValue() ? p2->GetValue() : 0.0; } + default: + ; } + return 0.0; } const EditTextObject* ScColumn::GetEditText( SCROW nRow ) const { - SCSIZE nIndex; - if (!Search(nRow, nIndex)) + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it == maCells.end()) return NULL; - const ScBaseCell* pCell = maItems[nIndex].pCell; - if (pCell->GetCellType() != CELLTYPE_EDIT) + if (it->type != sc::element_type_edittext) return NULL; - const ScEditCell* pEditCell = static_cast<const ScEditCell*>(pCell); - return pEditCell->GetData(); + return sc::edittext_block::at(*it->data, aPos.second); } void ScColumn::RemoveEditTextCharAttribs( SCROW nRow, const ScPatternAttr& rAttr ) { - SCSIZE nIndex; - if (!Search(nRow, nIndex)) + std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nRow); + sc::CellStoreType::iterator it = aPos.first; + if (it == maCells.end()) return; - ScBaseCell* pCell = maItems[nIndex].pCell; - if (pCell->GetCellType() != CELLTYPE_EDIT) + if (it->type != sc::element_type_edittext) return; - ScEditCell* pEditCell = static_cast<ScEditCell*>(pCell); - pEditCell->RemoveCharAttribs(rAttr); + EditTextObject* p = sc::edittext_block::at(*it->data, aPos.second); + ScEditUtil::RemoveCharAttribs(*p, rAttr); } void ScColumn::GetFormula( SCROW nRow, OUString& rFormula ) const { - SCSIZE nIndex; - if (Search(nRow, nIndex)) - { - ScBaseCell* pCell = maItems[nIndex].pCell; - if (pCell->GetCellType() == CELLTYPE_FORMULA) - ((ScFormulaCell*)pCell)->GetFormula( rFormula ); - else - rFormula = OUString(); - } + const ScFormulaCell* p = FetchFormulaCell(nRow); + if (p) + p->GetFormula(rFormula); else - rFormula = OUString(); + rFormula = EMPTY_OUSTRING; } const ScTokenArray* ScColumn::GetFormulaTokens( SCROW nRow ) const @@ -1848,44 +2292,105 @@ ScFormulaCell* ScColumn::GetFormulaCell( SCROW nRow ) CellType ScColumn::GetCellType( SCROW nRow ) const { - SCSIZE nIndex; - if (Search(nRow, nIndex)) - return maItems[nIndex].pCell->GetCellType(); + switch (maCells.get_type(nRow)) + { + case sc::element_type_numeric: + return CELLTYPE_VALUE; + case sc::element_type_string: + return CELLTYPE_STRING; + case sc::element_type_edittext: + return CELLTYPE_EDIT; + case sc::element_type_formula: + return CELLTYPE_FORMULA; + default: + ; + } return CELLTYPE_NONE; } +namespace { + +/** + * Count the number of all non-empty cells. + */ +class CellCounter +{ + size_t mnCount; +public: + CellCounter() : mnCount(0) {} + + void operator() (const sc::CellStoreType::value_type& node) + { + if (node.type == sc::element_type_empty) + return; + + mnCount += node.size; + } + + size_t getCount() const { return mnCount; } +}; + +} + SCSIZE ScColumn::GetCellCount() const { - return maItems.size(); + CellCounter aFunc; + std::for_each(maCells.begin(), maCells.end(), aFunc); + return aFunc.getCount(); } sal_uInt16 ScColumn::GetErrCode( SCROW nRow ) const { - SCSIZE nIndex; - if (Search(nRow, nIndex)) - { - ScBaseCell* pCell = maItems[nIndex].pCell; - if (pCell->GetCellType() == CELLTYPE_FORMULA) - return ((ScFormulaCell*)pCell)->GetErrCode(); - } - return 0; + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it == maCells.end()) + return 0; + + if (it->type != sc::element_type_formula) + return 0; + + const ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second); + return const_cast<ScFormulaCell*>(p)->GetErrCode(); } bool ScColumn::HasStringData( SCROW nRow ) const { - SCSIZE nIndex; - if (Search(nRow, nIndex)) - return (maItems[nIndex].pCell)->HasStringData(); + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); + switch (aPos.first->type) + { + case sc::element_type_string: + case sc::element_type_edittext: + return true; + case sc::element_type_formula: + { + const ScFormulaCell* p = sc::formula_block::at(*aPos.first->data, aPos.second); + return !const_cast<ScFormulaCell*>(p)->IsValue(); + } + default: + ; + } + return false; } bool ScColumn::HasValueData( SCROW nRow ) const { - SCSIZE nIndex; - if (Search(nRow, nIndex)) - return (maItems[nIndex].pCell)->HasValueData(); + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); + switch (aPos.first->type) + { + case sc::element_type_numeric: + return true; + case sc::element_type_formula: + { + const ScFormulaCell* p = sc::formula_block::at(*aPos.first->data, aPos.second); + return const_cast<ScFormulaCell*>(p)->IsValue(); + } + default: + ; + } + return false; } @@ -1894,267 +2399,297 @@ bool ScColumn::HasValueData( SCROW nRow ) const */ bool ScColumn::HasStringCells( SCROW nStartRow, SCROW nEndRow ) const { - if ( !maItems.empty() ) + 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) { - SCSIZE nIndex; - Search( nStartRow, nIndex ); - while ( nIndex < maItems.size() && maItems[nIndex].nRow <= nEndRow ) - { - CellType eType = maItems[nIndex].pCell->GetCellType(); - if ( eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT ) - return sal_True; - ++nIndex; - } + if (it->type == sc::element_type_string || it->type == sc::element_type_edittext) + return true; + + nRow += it->size - nOffset; + nOffset = 0; } + return false; } +namespace { -sal_Int32 ScColumn::GetMaxStringLen( SCROW nRowStart, SCROW nRowEnd, CharSet eCharSet ) const +class MaxStringLenHandler { - sal_Int32 nStringLen = 0; - if ( !maItems.empty() ) + sal_Int32 mnMaxLen; + const ScColumn& mrColumn; + SvNumberFormatter* mpFormatter; + CharSet meCharSet; + bool mbOctetEncoding; + + void processCell(size_t nRow, ScRefCellValue& rCell) { + Color* pColor; OUString aString; - OString aOString; - bool bIsOctetTextEncoding = rtl_isOctetTextEncoding( eCharSet); - SvNumberFormatter* pNumFmt = pDocument->GetFormatTable(); - SCSIZE nIndex; - SCROW nRow; - Search( nRowStart, nIndex ); - while ( nIndex < maItems.size() && (nRow = maItems[nIndex].nRow) <= nRowEnd ) + sal_uInt32 nFormat = static_cast<const SfxUInt32Item*>(mrColumn.GetAttr(nRow, ATTR_VALUE_FORMAT))->GetValue(); + ScCellFormat::GetString(rCell, nFormat, aString, &pColor, *mpFormatter); + sal_Int32 nLen = 0; + if (mbOctetEncoding) { - ScRefCellValue aCell; - aCell.assign(*maItems[nIndex].pCell); - Color* pColor; - sal_uLong nFormat = (sal_uLong) ((SfxUInt32Item*) GetAttr( - nRow, ATTR_VALUE_FORMAT ))->GetValue(); - ScCellFormat::GetString(aCell, nFormat, aString, &pColor, *pNumFmt); - sal_Int32 nLen; - if (bIsOctetTextEncoding) + OString aOString; + if (!aString.convertToString(&aOString, meCharSet, + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | + RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)) { - if (!aString.convertToString( &aOString, eCharSet, - 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(); + // 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. } - else - nLen = aString.getLength() * sizeof(sal_Unicode); - if ( nStringLen < nLen) - nStringLen = nLen; - nIndex++; + nLen = aOString.getLength(); } + else + nLen = aString.getLength() * sizeof(sal_Unicode); + + if (mnMaxLen < nLen) + mnMaxLen = nLen; + } + +public: + MaxStringLenHandler(const ScColumn& rColumn, CharSet eCharSet) : + mnMaxLen(0), + mrColumn(rColumn), + mpFormatter(rColumn.GetDoc().GetFormatTable()), + meCharSet(eCharSet), + mbOctetEncoding(rtl_isOctetTextEncoding(eCharSet)) + { + } + + void operator() (size_t nRow, double fVal) + { + ScRefCellValue aCell(fVal); + processCell(nRow, aCell); } - return nStringLen; + + void operator() (size_t nRow, const OUString& rStr) + { + ScRefCellValue aCell(&rStr); + processCell(nRow, aCell); + } + + void operator() (size_t nRow, const EditTextObject* p) + { + ScRefCellValue aCell(p); + processCell(nRow, aCell); + } + + void operator() (size_t nRow, const ScFormulaCell* p) + { + ScRefCellValue aCell(const_cast<ScFormulaCell*>(p)); + processCell(nRow, aCell); + } + + sal_Int32 getMaxLen() const { return mnMaxLen; } +}; + } +sal_Int32 ScColumn::GetMaxStringLen( SCROW nRowStart, SCROW nRowEnd, CharSet eCharSet ) const +{ + MaxStringLenHandler aFunc(*this, eCharSet); + sc::ParseAllNonEmpty(maCells.begin(), maCells, nRowStart, nRowEnd, aFunc); + return aFunc.getMaxLen(); +} -xub_StrLen ScColumn::GetMaxNumberStringLen( - sal_uInt16& nPrecision, SCROW nRowStart, SCROW nRowEnd ) const +namespace { + +class MaxNumStringLenHandler { - xub_StrLen nStringLen = 0; - nPrecision = pDocument->GetDocOptions().GetStdPrecision(); - if ( nPrecision == SvNumberFormatter::UNLIMITED_PRECISION ) - // In case of unlimited precision, use 2 instead. - nPrecision = 2; + const ScColumn& mrColumn; + SvNumberFormatter* mpFormatter; + sal_Int32 mnMaxLen; + sal_uInt16 mnPrecision; - if ( !maItems.empty() ) + void processCell(size_t nRow, ScRefCellValue& rCell) { + if (rCell.meType == CELLTYPE_FORMULA && !rCell.mpFormula->IsValue()) + return; + OUString aString; - SvNumberFormatter* pNumFmt = pDocument->GetFormatTable(); - SCSIZE nIndex; - SCROW nRow; - Search( nRowStart, nIndex ); - while ( nIndex < maItems.size() && (nRow = maItems[nIndex].nRow) <= nRowEnd ) + sal_uInt32 nFormat = static_cast<const SfxUInt32Item*>( + mrColumn.GetAttr(nRow, ATTR_VALUE_FORMAT))->GetValue(); + ScCellFormat::GetInputString(rCell, nFormat, aString, *mpFormatter); + sal_Int32 nLen = aString.getLength(); + if (nLen <= 0) + // Ignore empty string. + return; + + if (nFormat) { - ScRefCellValue aCell; - aCell.assign(*maItems[nIndex].pCell); - CellType eType = aCell.meType; - if ( eType == CELLTYPE_VALUE || (eType == CELLTYPE_FORMULA - && aCell.mpFormula->IsValue()) ) + const SvNumberformat* pEntry = mpFormatter->GetEntry(nFormat); + sal_uInt16 nPrec; + if (pEntry) { - sal_uLong nFormat = (sal_uLong) ((SfxUInt32Item*) GetAttr( - nRow, ATTR_VALUE_FORMAT ))->GetValue(); - ScCellFormat::GetInputString(aCell, nFormat, aString, *pNumFmt); - xub_StrLen nLen = aString.getLength(); - if ( nLen ) - { - if ( nFormat ) - { - const SvNumberformat* pEntry = pNumFmt->GetEntry( nFormat ); - sal_uInt16 nPrec; - if (pEntry) - { - bool bThousand, bNegRed; - sal_uInt16 nLeading; - pEntry->GetFormatSpecialInfo(bThousand, bNegRed, nPrec, nLeading); - } - else - nPrec = pNumFmt->GetFormatPrecision( nFormat ); + bool bThousand, bNegRed; + sal_uInt16 nLeading; + pEntry->GetFormatSpecialInfo(bThousand, bNegRed, nPrec, nLeading); + } + else + nPrec = mpFormatter->GetFormatPrecision(nFormat); - if ( nPrec != SvNumberFormatter::UNLIMITED_PRECISION && nPrec > nPrecision ) - nPrecision = nPrec; - } - if ( nPrecision ) - { // less than nPrecision in string => widen it - // more => shorten it - String aSep = pNumFmt->GetFormatDecimalSep( nFormat ); - sal_Int32 nTmp = aString.indexOf( aSep ); - if ( nTmp == -1 ) - nLen += nPrecision + aSep.Len(); - else - { - nTmp = aString.getLength() - (nTmp + aSep.Len()); - if ( nTmp != nPrecision ) - nLen += nPrecision - nTmp; - // nPrecision > nTmp : nLen + Diff - // nPrecision < nTmp : nLen - Diff - } - } - if ( nStringLen < nLen ) - nStringLen = nLen; - } + if (nPrec != SvNumberFormatter::UNLIMITED_PRECISION && nPrec > mnPrecision) + mnPrecision = nPrec; + } + + if (mnPrecision) + { // less than mnPrecision in string => widen it + // more => shorten it + OUString aSep = mpFormatter->GetFormatDecimalSep(nFormat); + 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 } - nIndex++; } + + if (mnMaxLen < nLen) + mnMaxLen = nLen; + } + +public: + MaxNumStringLenHandler(const ScColumn& rColumn, sal_uInt16 nPrecision) : + mrColumn(rColumn), mpFormatter(rColumn.GetDoc().GetFormatTable()), + mnMaxLen(0), mnPrecision(nPrecision) + { } - return nStringLen; + + void operator() (size_t nRow, double fVal) + { + ScRefCellValue aCell(fVal); + processCell(nRow, aCell); + } + + void operator() (size_t nRow, const ScFormulaCell* p) + { + ScRefCellValue aCell(const_cast<ScFormulaCell*>(p)); + processCell(nRow, aCell); + } + + sal_Int32 getMaxLen() const { return mnMaxLen; } + + sal_uInt16 getPrecision() const { return mnPrecision; } +}; + +} + +xub_StrLen ScColumn::GetMaxNumberStringLen( + sal_uInt16& nPrecision, SCROW nRowStart, SCROW nRowEnd ) const +{ + nPrecision = pDocument->GetDocOptions().GetStdPrecision(); + if ( nPrecision == SvNumberFormatter::UNLIMITED_PRECISION ) + // In case of unlimited precision, use 2 instead. + nPrecision = 2; + + MaxNumStringLenHandler aFunc(*this, nPrecision); + sc::ParseFormulaNumeric(maCells.begin(), maCells, nRowStart, nRowEnd, aFunc); + nPrecision = aFunc.getPrecision(); + return aFunc.getMaxLen(); } namespace { -struct CellGroupSetter : std::unary_function<ColEntry, void> +class CellGroupSetter { ScFormulaCellGroupRef mxGroup; public: CellGroupSetter(const ScFormulaCellGroupRef& xGroup) : mxGroup(xGroup) {} - void operator() (ColEntry& rEntry) + void operator() (size_t, ScFormulaCell* pCell) { - if (rEntry.pCell && rEntry.pCell->GetCellType() == CELLTYPE_FORMULA) - static_cast<ScFormulaCell*>(rEntry.pCell)->SetCellGroup(mxGroup); + pCell->SetCellGroup(mxGroup); } }; -} - -// Very[!] slow way to look for and merge contiguous runs -// of similar formulae into a formulagroup -void ScColumn::RebuildFormulaGroups() +class GroupFormulaCells { - if ( maItems.empty() || !mbDirtyGroups ) - return; + std::vector<ScFormulaCellGroupRef>& mrFnGroups; + ScFormulaCellGroupRef mxNone; - // clear double groups - std::for_each(maDoubles.begin(), maDoubles.end(), ScDeleteObjectByPtr<ColDoubleEntry>()); - maDoubles.clear(); - - // clear previous groups - ScFormulaCellGroupRef xNone; - std::for_each(maItems.begin(), maItems.end(), CellGroupSetter(xNone)); - maFnGroups.clear(); +public: + GroupFormulaCells(std::vector<ScFormulaCellGroupRef>& rFnGroups) : mrFnGroups(rFnGroups) {} - // re-build groups - ColDoubleEntry *pLastDouble = NULL; - for (size_t i = 1; i < maItems.size(); i++) + void operator() (sc::CellStoreType::value_type& node) { - ColEntry &rCur = maItems[ i ]; - ColEntry &rPrev = maItems[ i - 1 ]; - if ( ( rPrev.nRow != rCur.nRow - 1 ) || // not contiguous - !rCur.pCell || !rPrev.pCell || // paranoia - rCur.pCell->GetCellType() != rPrev.pCell->GetCellType() ) // same type - { - // Non-contiguous cell detected. Break the series. - pLastDouble = NULL; - continue; - } + if (node.type != sc::element_type_formula) + // We are only interested in formula cells. + return; - // collate doubles - if ( rCur.pCell->GetCellType() == CELLTYPE_VALUE ) + ScFormulaCell* pPrev = NULL; + ScFormulaCell* pCur = NULL; + size_t nRow = node.position; // start row position. + + sc::formula_block::iterator it = sc::formula_block::begin(*node.data); + sc::formula_block::iterator itEnd = sc::formula_block::end(*node.data); + for (; it != itEnd; ++it, ++nRow, pPrev = pCur) { - if ( !pLastDouble ) + pCur = *it; + if (!pPrev) + continue; + + ScFormulaCell::CompareState eCompState = pPrev->CompareByTokenArray(pCur); + if (eCompState == ScFormulaCell::NotEqual) { - pLastDouble = new ColDoubleEntry(); - pLastDouble->mnStart = rPrev.nRow; - pLastDouble->maData.push_back( - static_cast< ScValueCell * >( rPrev.pCell )->GetValue() ); - maDoubles.push_back( pLastDouble ); + // different formula tokens. + pCur->SetCellGroup(mxNone); + continue; } - pLastDouble->maData.push_back( - static_cast< ScValueCell * >( rCur.pCell )->GetValue() ); - continue; - } - if ( rCur.pCell->GetCellType() != CELLTYPE_FORMULA ) - continue; + // Formula tokens equal those of the previous formula cell. + ScFormulaCellGroupRef xGroup = pPrev->GetCellGroup(); + if (!xGroup) + { + // create a new group ... + xGroup.reset(new ScFormulaCellGroup); + xGroup->mnStart = nRow - 1; + xGroup->mbInvariant = (eCompState == ScFormulaCell::EqualInvariant); + xGroup->mnLength = 2; - // see if these formula tokens are identical. - ScFormulaCell *pCur = static_cast< ScFormulaCell *>( rCur.pCell ); - ScFormulaCell *pPrev = static_cast< ScFormulaCell *>( rPrev.pCell ); + mrFnGroups.push_back(xGroup); - ScFormulaCell::CompareState eCompState = pPrev->CompareByTokenArray(pCur); - if (eCompState == ScFormulaCell::NotEqual) - { - // different formula tokens. - pCur->SetCellGroup( xNone ); - continue; + pCur->SetCellGroup(xGroup); + pPrev->SetCellGroup(xGroup); + } + else + { + // existing group. extend its length. + pCur->SetCellGroup(xGroup); + ++xGroup->mnLength; + } } + } +}; - ScFormulaCellGroupRef xGroup = pPrev->GetCellGroup(); - if (!xGroup) - { - // create a new group ... - xGroup.reset(new ScFormulaCellGroup); - xGroup->mnStart = rPrev.nRow; - xGroup->mbInvariant = eCompState == ScFormulaCell::EqualInvariant; - xGroup->mnLength = 2; - - maFnGroups.push_back( xGroup ); +} - pCur->SetCellGroup( xGroup ); - pPrev->SetCellGroup( xGroup ); - } - else - { - // existing group. extend its length. - pCur->SetCellGroup( xGroup ); - xGroup->mnLength++; - } - } +// Very[!] slow way to look for and merge contiguous runs +// of similar formulae into a formulagroup +void ScColumn::RebuildFormulaGroups() +{ + if (!mbDirtyGroups) + return; -#if OSL_DEBUG_LEVEL > 0 - if ( maDoubles.size() + maFnGroups.size() > 0 ) - { - OUString aStr; - fprintf( stderr, "column %2d has %2d double span(s): ", (int)nCol, (int)maDoubles.size() ); - for (std::vector< ColDoubleEntry *>::iterator it = maDoubles.begin(); - it != maDoubles.end(); ++it ) - { - ScRange aDoubleRange( nCol, (*it)->mnStart, nTab, - nCol, (*it)->mnStart + (*it)->maData.size() - 1, nTab ); - aDoubleRange.Format( aStr, SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW, pDocument ); - fprintf( stderr, "%s, ", OUStringToOString( aStr, RTL_TEXTENCODING_UTF8 ).getStr() ); - } - fprintf( stderr, "\n" ); + // clear previous formula groups. + ScFormulaCellGroupRef xNone; + CellGroupSetter aFunc(xNone); + sc::ProcessFormula(maCells, aFunc); + maFnGroups.clear(); - fprintf( stderr, "column %2d has %2d formula span(s): ", (int)nCol, (int)maFnGroups.size() ); - for (std::vector< ScFormulaCellGroupRef>::iterator it = maFnGroups.begin(); - it != maFnGroups.end(); ++it ) - { - ScRange aDoubleRange( nCol, (*it)->mnStart, nTab, - nCol, (*it)->mnStart + (*it)->mnLength - 1, nTab ); - aDoubleRange.Format( aStr, SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW, pDocument ); - fprintf( stderr, "%s, ", OUStringToOString( aStr, RTL_TEXTENCODING_UTF8 ).getStr() ); - } - fprintf( stderr, "\n" ); - } -#endif + // re-build formula groups. + std::for_each(maCells.begin(), maCells.end(), GroupFormulaCells(maFnGroups)); mbDirtyGroups = false; } |