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 | |
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
53 files changed, 7885 insertions, 5566 deletions
diff --git a/editeng/source/editeng/editobj.cxx b/editeng/source/editeng/editobj.cxx index 2dd86b9c3f71..efb5eb19e924 100644 --- a/editeng/source/editeng/editobj.cxx +++ b/editeng/source/editeng/editobj.cxx @@ -303,6 +303,11 @@ editeng::FieldUpdater EditTextObject::GetFieldUpdater() return mpImpl->GetFieldUpdater(); } +const SfxItemPool* EditTextObject::GetPool() const +{ + return mpImpl->GetPool(); +} + sal_uInt16 EditTextObject::GetUserType() const { return mpImpl->GetUserType(); diff --git a/include/editeng/editobj.hxx b/include/editeng/editobj.hxx index 6713e50ccab8..83f647533b98 100644 --- a/include/editeng/editobj.hxx +++ b/include/editeng/editobj.hxx @@ -71,6 +71,7 @@ public: EditTextObject( const EditTextObject& r ); virtual ~EditTextObject(); + const SfxItemPool* GetPool() const; sal_uInt16 GetUserType() const; // For OutlinerMode, it can however not save in compatible format void SetUserType( sal_uInt16 n ); diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk index 2ba1225927e0..d5495b345116 100644 --- a/sc/Library_sc.mk +++ b/sc/Library_sc.mk @@ -102,6 +102,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\ sc/source/core/data/bigrange \ sc/source/core/data/cell \ sc/source/core/data/cell2 \ + sc/source/core/data/cellclonehandler \ sc/source/core/data/cellvalue \ sc/source/core/data/clipcontext \ sc/source/core/data/clipparam \ @@ -249,6 +250,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\ sc/source/core/tool/reftokenhelper \ sc/source/core/tool/refupdat \ sc/source/core/tool/scmatrix \ + sc/source/core/tool/scopetools \ sc/source/core/tool/simplerangelist \ sc/source/core/tool/stringutil \ sc/source/core/tool/subtotal \ diff --git a/sc/inc/cellclonehandler.hxx b/sc/inc/cellclonehandler.hxx new file mode 100644 index 000000000000..a08383c49024 --- /dev/null +++ b/sc/inc/cellclonehandler.hxx @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef SC_CELLCLONEHANDLER_HXX +#define SC_CELLCLONEHANDLER_HXX + +#include "rtl/ustring.hxx" +#include "address.hxx" +#include "mtvelements.hxx" + +class ScDocument; +class EditTextObject; +class ScFormulaCell; + +namespace sc { + +class CellBlockCloneHandler +{ + ScDocument& mrSrcDoc; + ScDocument& mrDestDoc; + CellStoreType& mrDestCellStore; + +protected: + ScDocument& getSrcDoc(); + ScDocument& getDestDoc(); + const ScDocument& getDestDoc() const; + CellStoreType& getDestCellStore(); + +public: + CellBlockCloneHandler( + ScDocument& rSrcDoc, ScDocument& rDestDoc, CellStoreType& rDestCellStore); + virtual ~CellBlockCloneHandler(); + + virtual void cloneDoubleBlock( + CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + const numeric_block::const_iterator& itBegin, const numeric_block::const_iterator& itEnd); + + virtual void cloneStringBlock( + CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + const string_block::const_iterator& itBegin, const string_block::const_iterator& itEnd); + + virtual void cloneEditTextBlock( + CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + const edittext_block::const_iterator& itBegin, const edittext_block::const_iterator& itEnd); + + virtual void cloneFormulaBlock( + CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + const formula_block::const_iterator& itBegin, const formula_block::const_iterator& itEnd); +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/inc/cellvalue.hxx b/sc/inc/cellvalue.hxx index fb7c17573fb8..b1be68d7f977 100644 --- a/sc/inc/cellvalue.hxx +++ b/sc/inc/cellvalue.hxx @@ -7,15 +7,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef __SC_CELLVALUE_HXX__ -#define __SC_CELLVALUE_HXX__ +#ifndef SC_CELLVALUE_HXX +#define SC_CELLVALUE_HXX #include "global.hxx" +#include "mtvelements.hxx" class ScDocument; class ScFormulaCell; class EditTextObject; -class ScBaseCell; +class ScColumn; /** * Store arbitrary cell value of any kind. It only stores cell value and @@ -42,6 +43,12 @@ struct SC_DLLPUBLIC ScCellValue void clear(); + void set( double fValue ); + void set( const OUString& rStr ); + void set( const EditTextObject& rEditText ); + void set( const ScFormulaCell& rFormula ); + void set( ScFormulaCell* pFormula ); + /** * Take cell value from specified position in specified document. */ @@ -50,11 +57,6 @@ struct SC_DLLPUBLIC ScCellValue void assign( const ScCellValue& rOther, ScDocument& rDestDoc, int nCloneFlags = SC_CLONECELL_DEFAULT ); /** - * TODO: Remove this later. - */ - void assign( const ScBaseCell& rCell ); - - /** * Set cell value at specified position in specified document. */ void commit( ScDocument& rDoc, const ScAddress& rPos ) const; @@ -66,6 +68,8 @@ struct SC_DLLPUBLIC ScCellValue */ void release( ScDocument& rDoc, const ScAddress& rPos ); + void release( ScColumn& rColumn, SCROW nRow ); + bool hasString() const; bool hasNumeric() const; @@ -110,16 +114,15 @@ struct SC_DLLPUBLIC ScRefCellValue */ void assign( ScDocument& rDoc, const ScAddress& rPos ); - /** - * TODO: Remove this later. - */ - void assign( ScBaseCell& rCell ); + void assign( const sc::CellStoreType::const_iterator& itPos, size_t nOffset ); /** * Set cell value at specified position in specified document. */ void commit( ScDocument& rDoc, const ScAddress& rPos ) const; + void commit( ScColumn& rColumn, SCROW nRow ) const; + bool hasString() const; bool hasNumeric() const; diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx index e2c6b452dc4c..c0b9c35fd7e2 100644 --- a/sc/inc/column.hxx +++ b/sc/inc/column.hxx @@ -32,6 +32,7 @@ #include <vector> #include <boost/intrusive_ptr.hpp> +#include <mdds/flat_segment_tree.hpp> namespace editeng { class SvxBorderLine; } @@ -44,6 +45,8 @@ namespace sc { class CopyToDocContext; class MixDocContext; struct ColumnBlockPosition; + class CellBlockCloneHandler; + class SingleColumnSpanSet; } class Fraction; @@ -58,7 +61,6 @@ class SvxBoxItem; class ScAttrIterator; class ScAttrArray; struct ScAttrEntry; -class ScBaseCell; class ScDocument; class ScEditDataArray; class ScFormulaCell; @@ -77,6 +79,7 @@ struct ScColWidthParam; class ScColumnTextWidthIterator; struct ScFormulaCellGroup; struct ScRefCellValue; +struct ScCellValue; class ScDocumentImport; struct ScNeededSizeOptions @@ -90,28 +93,6 @@ struct ScNeededSizeOptions ScNeededSizeOptions(); }; -struct ColEntry -{ - SCROW nRow; - ScBaseCell* pCell; - - struct Less : std::binary_function<ColEntry, ColEntry, bool> - { - bool operator() (const ColEntry& r1, const ColEntry& r2) const; - }; -}; - -struct ColDoubleEntry -{ - SCROW mnStart; - std::vector<double> maData; - - struct LessByPtr : std::binary_function<ColDoubleEntry*, ColDoubleEntry*, bool> - { - bool operator() (const ColDoubleEntry* p1, const ColDoubleEntry* p2) const; - }; -}; - class ScColumn { // Empty values correspond with empty cells. All non-empty cell positions @@ -130,10 +111,6 @@ class ScColumn SCCOL nCol; SCTAB nTab; - std::vector<ColEntry> maItems; - - // temporary until we switch to mdds container - std::vector<ColDoubleEntry *> maDoubles; std::vector<ScFormulaCellGroupRef> maFnGroups; ScAttrArray* pAttrArray; @@ -141,43 +118,39 @@ class ScColumn bool mbDirtyGroups; /// formula groups are dirty. friend class ScDocument; // for FillInfo +friend class ScTable; friend class ScDocumentIterator; friend class ScValueIterator; friend class ScHorizontalValueIterator; friend class ScDBQueryDataIterator; -friend class ScColumnIterator; friend class ScQueryCellIterator; -friend class ScMarkedDataIter; friend class ScCellIterator; friend class ScHorizontalCellIterator; friend class ScHorizontalAttrIterator; friend class ScColumnTextWidthIterator; friend class ScDocumentImport; +friend class sc::SingleColumnSpanSet; ScColumn(const ScColumn&); // disabled ScColumn& operator= (const ScColumn&); // disabled - std::vector<ColEntry>::iterator Search( SCROW nRow ); - std::vector<ColEntry>::const_iterator Search( SCROW nRow ) const; - public: ScColumn(); ~ScColumn(); void Init(SCCOL nNewCol, SCTAB nNewTab, ScDocument* pDoc); - bool Search( SCROW nRow, SCSIZE& nIndex ) const; - ScBaseCell* GetCell( SCROW nRow ) const; + ScDocument& GetDoc(); + const ScDocument& GetDoc() const; + SCTAB GetTab() const { return nTab; } + SCCOL GetCol() const { return nCol; } + ScRefCellValue GetCellValue( SCROW nRow ) const; - void Insert( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, ScBaseCell* pCell ); - void Insert( SCROW nRow, ScBaseCell* pCell ); - void Insert( SCROW nRow, sal_uInt32 nFormatIndex, ScBaseCell* pCell ); - void Append( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, ScBaseCell* pCell ); - void Append( SCROW nRow, ScBaseCell* pCell ); + ScRefCellValue GetCellValue( sc::CellStoreType::const_iterator& itPos, SCROW nRow ) const; + ScRefCellValue GetCellValue( sc::CellStoreType::const_iterator& itPos, size_t nOffset ) const; + void Delete( SCROW nRow ); - void DeleteAtIndex( SCSIZE nIndex ); void FreeAll(); - void ReserveSize( SCSIZE nSize ); void SwapRow( SCROW nRow1, SCROW nRow2 ); void SwapCell( SCROW nRow, ScColumn& rCol); void RebuildFormulaGroups(); @@ -188,7 +161,6 @@ public: SCCOL& rPaintCol, SCROW& rPaintRow, bool bRefresh ); - bool IsEmptyVisData() const; // without Broadcaster bool IsEmptyData() const; bool IsEmptyAttr() const; bool IsEmpty() const; @@ -200,12 +172,10 @@ public: bool HasVisibleDataAt(SCROW nRow) const; SCROW GetFirstDataPos() const; SCROW GetLastDataPos() const; - SCROW GetLastVisDataPos() const; // without Broadcaster - SCROW GetFirstVisDataPos() const; bool GetPrevDataPos(SCROW& rRow) const; bool GetNextDataPos(SCROW& rRow) const; void FindDataAreaPos(SCROW& rRow, bool bDown) const; // (without Broadcaster) - void FindUsed( SCROW nStartRow, SCROW nEndRow, bool* pUsed ) const; + void FindUsed( SCROW nStartRow, SCROW nEndRow, mdds::flat_segment_tree<SCROW, bool>& rUsed ) const; SCSIZE VisibleCount( SCROW nStartRow, SCROW nEndRow ) const; sal_uInt16 GetBlockMatrixEdges( SCROW nRow1, SCROW nRow2, sal_uInt16 nMask ) const; @@ -219,7 +189,7 @@ public: bool IsAllAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW nEndRow ) const; bool TestInsertCol( SCROW nStartRow, SCROW nEndRow) const; - bool TestInsertRow( SCSIZE nSize ) const; + bool TestInsertRow( SCROW nStartRow, SCSIZE nSize ) const; void InsertRow( SCROW nStartRow, SCSIZE nSize ); void DeleteRow( SCROW nStartRow, SCSIZE nSize ); void DeleteArea(SCROW nStartRow, SCROW nEndRow, sal_uInt16 nDelFlag ); @@ -228,11 +198,11 @@ public: void CopyStaticToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol); void CopyCellToDocument( SCROW nSrcRow, SCROW nDestRow, ScColumn& rDestCol ); bool InitBlockPosition( sc::ColumnBlockPosition& rBlockPos ); + bool InitBlockPosition( sc::ColumnBlockConstPosition& rBlockPos ) const; void CopyFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, long nDy, ScColumn& rColumn ); void StartListeningInArea( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 ); - void BroadcastInArea( SCROW nRow1, SCROW nRow2 ); void RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow ); @@ -244,21 +214,15 @@ public: sc::MixDocContext& rCxt, SCROW nRow1, SCROW nRow2, sal_uInt16 nFunction, bool bSkipEmpty, const ScColumn& rSrcCol ); - ScFormulaCell* CreateRefCell( ScDocument* pDestDoc, const ScAddress& rDestPos, - SCSIZE nIndex, sal_uInt16 nFlags ) const; - ScAttrIterator* CreateAttrIterator( SCROW nStartRow, SCROW nEndRow ) const; - - SCCOL GetCol() const { return nCol; } - // UpdateSelectionFunction: multi-select void UpdateSelectionFunction( const ScMarkData& rMark, ScFunctionData& rData, ScFlatBoolRowSegments& rHiddenRows, - bool bDoExclude, SCROW nExStartRow, SCROW nExEndRow ) const; + bool bDoExclude, SCROW nExStartRow, SCROW nExEndRow ); void UpdateAreaFunction( - ScFunctionData& rData, ScFlatBoolRowSegments& rHiddenRows, SCROW nStartRow, SCROW nEndRow) const; + ScFunctionData& rData, ScFlatBoolRowSegments& rHiddenRows, SCROW nStartRow, SCROW nEndRow); void CopyToColumn( sc::CopyToDocContext& rCxt, SCROW nRow1, SCROW nRow2, sal_uInt16 nFlags, bool bMarked, @@ -286,12 +250,18 @@ public: ScSetStringParam* pParam = NULL ); void SetEditText( SCROW nRow, EditTextObject* pEditText ); + void SetEditText( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, EditTextObject* pEditText ); + void SetEditText( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, const EditTextObject& rEditText ); void SetEditText( SCROW nRow, const EditTextObject& rEditText, const SfxItemPool* pEditPool ); void SetFormula( SCROW nRow, const ScTokenArray& rArray, formula::FormulaGrammar::Grammar eGram ); void SetFormula( SCROW nRow, const OUString& rFormula, formula::FormulaGrammar::Grammar eGram ); void SetFormulaCell( SCROW nRow, ScFormulaCell* pCell ); + void SetFormulaCell( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, ScFormulaCell* pCell ); - void SetValue( SCROW nRow, const double& rVal); + void SetRawString( SCROW nRow, const OUString& rStr, bool bBroadcast = true ); + void SetRawString( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, const OUString& rStr, bool bBroadcast = true ); + void SetValue( SCROW nRow, double fVal ); + void SetValue( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, double fVal, bool bBroadcast = true ); void SetError( SCROW nRow, const sal_uInt16 nError); void GetString( SCROW nRow, OUString& rString ) const; @@ -318,7 +288,7 @@ public: bool IsFormulaDirty( SCROW nRow ) const; void SetDirty(); - void SetDirty( const ScRange& ); + void SetDirty( SCROW nRow1, SCROW nRow2 ); void SetDirtyVar(); void SetDirtyAfterLoad(); void SetTableOpDirty( const ScRange& ); @@ -428,7 +398,7 @@ public: SCsROW GetNextUnprotected( SCROW nRow, bool bUp ) const; void GetFilterEntries(SCROW nStartRow, SCROW nEndRow, std::vector<ScTypedStrData>& rStrings, bool& rHasDates); - bool GetDataEntries(SCROW nRow, std::set<ScTypedStrData>& rStrings, bool bLimit); + bool GetDataEntries( SCROW nRow, std::set<ScTypedStrData>& rStrings, bool bLimit ) const; void UpdateInsertTabAbs(SCTAB nNewPos); bool TestTabRefAbs(SCTAB nTable) const; @@ -458,6 +428,11 @@ public: void SetTextWidth(SCROW nRow, sal_uInt16 nWidth); sal_uInt8 GetScriptType( SCROW nRow ) const; + + /** + * Get combined script types of the specified range. This method may + * update script types on demand if they have not been determined. + */ sal_uInt8 GetRangeScriptType( sc::CellTextAttrStoreType::iterator& itPos, SCROW nRow1, SCROW nRow2 ); void SetScriptType( SCROW nRow, sal_uInt8 nType ); @@ -469,8 +444,6 @@ public: bool ResolveStaticReference( ScMatrix& rMat, SCCOL nMatCol, SCROW nRow1, SCROW nRow2 ); const double* FetchDoubleArray( sc::FormulaGroupContext& rCxt, SCROW nRow1, SCROW nRow2 ) const; - ScRefCellValue GetRefCellValue( SCROW ); - void SetNumberFormat( SCROW nRow, sal_uInt32 nNumberFormat ); SvtBroadcaster* GetBroadcaster( SCROW nRow ); @@ -478,17 +451,25 @@ public: void DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2 ); bool HasBroadcaster() const; + void BroadcastCells( const std::vector<SCROW>& rRows ); + void EndFormulaListening( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2 ); + private: - void UpdateScriptType( sc::CellTextAttr& rAttr, SCROW nRow ); - void DeleteRange( - SCSIZE nStartIndex, SCSIZE nEndIndex, sal_uInt16 nDelFlag, std::vector<SCROW>& rDeletedRows ); + void CopyCellsInRangeToColumn( + sc::ColumnBlockConstPosition* rSrcColPos, sc::ColumnBlockPosition* pDestColPos, + sc::CellBlockCloneHandler& Hdl, SCROW nRow1, SCROW nRow2, ScColumn& rColumn ) const; - const ScFormulaCell* FetchFormulaCell( SCROW nRow ) const; + sc::CellStoreType::iterator GetPositionToInsert( SCROW nRow ); + sc::CellStoreType::iterator GetPositionToInsert( const sc::CellStoreType::iterator& it, SCROW nRow ); + void ActivateNewFormulaCell( ScFormulaCell* pCell ); + void BroadcastNewCell( SCROW nRow ); + void UpdateScriptType( sc::CellTextAttr& rAttr, SCROW nRow ); - ScBaseCell* CloneCell(SCSIZE nIndex, sal_uInt16 nFlags, ScDocument& rDestDoc, const ScAddress& rDestPos) const; + const ScFormulaCell* FetchFormulaCell( SCROW nRow ) const; - SCROW FindNextVisibleRowWithContent(SCROW nRow, bool bForward) const; + SCROW FindNextVisibleRowWithContent( + sc::CellStoreType::const_iterator& itPos, SCROW nRow, bool bForward) const; SCROW FindNextVisibleRow(SCROW nRow, bool bForward) const; /** @@ -501,52 +482,21 @@ private: void CopyCellTextAttrsToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol) const; - void SetCell( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, ScBaseCell* pNewCell ); - void SetCell( SCROW nRow, ScBaseCell* pNewCell ); - void PostSetCell( SCROW nRow, ScBaseCell* pNewCell ); - /** * Clear and re-populate the cell text attribute array from the non-empty * cells stored in the cell array. */ void ResetCellTextAttrs(); -}; + void SwapCellTextAttrs( SCROW nRow1, SCROW nRow2 ); -class ScColumnIterator // walk through all data of a area/range -{ - const ScColumn* pColumn; - SCSIZE nPos; - SCROW nTop; - SCROW nBottom; -public: - ScColumnIterator( const ScColumn* pCol, SCROW nStart=0, SCROW nEnd=MAXROW ); - ~ScColumnIterator(); - - bool Next( SCROW& rRow, ScBaseCell*& rpCell ); - SCSIZE GetIndex() const; -}; - - -class ScMarkedDataIter // walk through data in a selected area/range -{ - const ScColumn* pColumn; - SCSIZE nPos; - ScMarkArrayIter* pMarkIter; - SCROW nTop; - SCROW nBottom; - bool bNext; - bool bAll; - -public: - ScMarkedDataIter( const ScColumn* pCol, const ScMarkData* pMarkData, - bool bAllIfNone = false ); - ~ScMarkedDataIter(); - - bool Next( SCSIZE& rIndex ); + /** + * Retrieve the cell value and set that slot empty. The ownership of that + * cell value moves to the returned cell value object. + */ + void ReleaseCellValue( sc::CellStoreType::iterator& itPos, SCROW nRow, ScCellValue& rVal ); }; - #endif diff --git a/sc/inc/columnspanset.hxx b/sc/inc/columnspanset.hxx index ab248285cfd8..55c3f57507e9 100644 --- a/sc/inc/columnspanset.hxx +++ b/sc/inc/columnspanset.hxx @@ -16,8 +16,13 @@ #include <mdds/flat_segment_tree.hpp> #include <boost/noncopyable.hpp> +class ScColumn; +class ScMarkData; + namespace sc { +struct ColumnBlockConstPosition; + /** * Structure that stores segments of boolean flags per column, and perform * custom action on those segments. @@ -49,6 +54,55 @@ public: void executeFromTop(Action& ac) const; }; +/** + * Keep track of spans in a single column only. + */ +class SingleColumnSpanSet +{ +public: + typedef mdds::flat_segment_tree<SCROW, bool> ColumnSpansType; + + struct Span + { + SCROW mnRow1; + SCROW mnRow2; + + Span(SCROW nRow1, SCROW nRow2) : mnRow1(nRow1), mnRow2(nRow2) {} + }; + + typedef std::vector<Span> SpansType; + + SingleColumnSpanSet(); + + /** + * Scan an entire column and tag all non-empty cell positions. + */ + void scan(const ScColumn& rColumn); + + /** + * Scan a column between specified range, and tag all non-empty cell + * positions. + */ + void scan(const ScColumn& rColumn, SCROW nStart, SCROW nEnd); + + void scan( + ColumnBlockConstPosition& rBlockPos, const ScColumn& rColumn, SCROW nStart, SCROW nEnd); + + /** + * Scan all marked data and tag all marked segments in specified column. + */ + void scan(const ScMarkData& rMark, SCTAB nTab, SCCOL nCol); + + void set(SCROW nRow1, SCROW nRow2, bool bVal); + + void getRows(std::vector<SCROW> &rRows) const; + + void getSpans(SpansType& rSpans) const; + +private: + ColumnSpansType maSpans; +}; + } #endif diff --git a/sc/inc/dociter.hxx b/sc/inc/dociter.hxx index cfffb7ef2f56..87d17b261f90 100644 --- a/sc/inc/dociter.hxx +++ b/sc/inc/dociter.hxx @@ -25,6 +25,7 @@ #include "global.hxx" #include "scdllapi.h" #include "cellvalue.hxx" +#include "mtvelements.hxx" #include <memory> @@ -34,7 +35,6 @@ #include <boost/scoped_ptr.hpp> class ScDocument; -class ScBaseCell; class ScPatternAttr; class ScAttrArray; class ScAttrIterator; @@ -47,84 +47,52 @@ struct ScDBQueryParamInternal; struct ScDBQueryParamMatrix; class ScFormulaCell; -class ScDocumentIterator // walk through all non-empty cells -{ -private: - ScDocument* pDoc; - SCTAB nStartTab; - SCTAB nEndTab; - - const ScPatternAttr* pDefPattern; - - SCCOL nCol; - SCROW nRow; - SCTAB nTab; - ScBaseCell* pCell; - const ScPatternAttr* pPattern; - - - SCSIZE nColPos; - SCSIZE nAttrPos; - - bool GetThis(); - bool GetThisCol(); - -public: - ScDocumentIterator( ScDocument* pDocument, SCTAB nStartTable, SCTAB nEndTable ); - ~ScDocumentIterator(); - - bool GetFirst(); - bool GetNext(); - - ScCellValue GetCellValue() const; - const ScPatternAttr* GetPattern(); - void GetPos( SCCOL& rCol, SCROW& rRow, SCTAB& rTab ); -}; - class ScValueIterator // walk through all values in an area { -private: - double fNextValue; + typedef std::pair<sc::CellStoreType::const_iterator, size_t> PositionType; + ScDocument* pDoc; const ScAttrArray* pAttrArray; sal_uLong nNumFormat; // for CalcAsShown sal_uLong nNumFmtIndex; - SCCOL nStartCol; - SCROW nStartRow; - SCTAB nStartTab; - SCCOL nEndCol; - SCROW nEndRow; - SCTAB nEndTab; - SCCOL nCol; - SCROW nRow; - SCTAB nTab; - SCSIZE nColRow; - SCROW nNextRow; + ScAddress maStartPos; + ScAddress maEndPos; + SCCOL mnCol; + SCTAB mnTab; SCROW nAttrEndRow; short nNumFmtType; - bool bNumValid; - bool bSubTotal; - bool bNextValid; - bool bCalcAsShown; - bool bTextAsZero; + bool bNumValid:1; + bool bSubTotal:1; + bool bCalcAsShown:1; + bool bTextAsZero:1; + + const sc::CellStoreType* mpCells; + PositionType maCurPos; + + SCROW GetRow() const; + void IncBlock(); + void IncPos(); + void SetPos(size_t nPos); + + /** + * See if the cell at the current position is a non-empty cell. If not, + * move to the next non-empty cell position. + */ + bool GetThis( double& rValue, sal_uInt16& rErr ); - bool GetThis(double& rValue, sal_uInt16& rErr); public: - ScValueIterator(ScDocument* pDocument, - const ScRange& rRange, bool bSTotal = false, - bool bTextAsZero = false ); - void GetCurNumFmtInfo( short& nType, sal_uLong& nIndex ); + ScValueIterator( + ScDocument* pDocument, const ScRange& rRange, bool bSTotal = false, + bool bTextAsZero = false ); + + void GetCurNumFmtInfo( short& nType, sal_uLong& nIndex ); + /// Does NOT reset rValue if no value found! - bool GetFirst(double& rValue, sal_uInt16& rErr); + bool GetFirst( double& rValue, sal_uInt16& rErr ); + /// Does NOT reset rValue if no value found! - bool GetNext(double& rValue, sal_uInt16& rErr) - { - return bNextValid ? ( bNextValid = false, rValue = fNextValue, - rErr = 0, nRow = nNextRow, - ++nColRow, bNumValid = false, true ) - : ( ++nRow, GetThis(rValue, rErr) ); - } + bool GetNext( double& rValue, sal_uInt16& rErr ); }; class ScDBQueryDataIterator @@ -141,11 +109,9 @@ public: }; private: - static SCROW GetRowByColEntryIndex(ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCSIZE nColRow); - static ScBaseCell* GetCellByColEntryIndex(ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCSIZE nColRow); + static const sc::CellStoreType* GetColumnCellStore(ScDocument& rDoc, SCTAB nTab, SCCOL nCol); static const ScAttrArray* GetAttrArrayByCol(ScDocument& rDoc, SCTAB nTab, SCCOL nCol); - static bool IsQueryValid(ScDocument& rDoc, const ScQueryParam& rParam, SCTAB nTab, SCROW nRow, ScBaseCell* pCell); - static SCSIZE SearchColEntryIndex(ScDocument& rDoc, SCTAB nTab, SCROW nRow, SCCOL nCol); + static bool IsQueryValid(ScDocument& rDoc, const ScQueryParam& rParam, SCTAB nTab, SCROW nRow, ScRefCellValue& rCell); class DataAccess { @@ -161,6 +127,7 @@ private: class DataAccessInternal : public DataAccess { + typedef std::pair<sc::CellStoreType::const_iterator,size_t> PositionType; public: DataAccessInternal(const ScDBQueryDataIterator* pParent, ScDBQueryParamInternal* pParam, ScDocument* pDoc); virtual ~DataAccessInternal(); @@ -169,6 +136,12 @@ private: virtual bool getNext(Value& rValue); private: + void incBlock(); + void incPos(); + void setPos(size_t nPos); + + const sc::CellStoreType* mpCells; + PositionType maCurPos; ScDBQueryParamInternal* mpParam; ScDocument* mpDoc; const ScAttrArray* pAttrArray; @@ -176,7 +149,6 @@ private: sal_uLong nNumFmtIndex; SCCOL nCol; SCROW nRow; - SCSIZE nColRow; SCROW nAttrEndRow; SCTAB nTab; short nNumFmtType; @@ -212,18 +184,31 @@ public: bool GetNext(Value& rValue); }; -class ScCellIterator // walk through all cells in an area -{ // for SubTotal no hidden and no sub-total lines -private: +/** + * Walk through all cells in an area. For SubTotal no hidden and no + * sub-total lines. + **/ +class ScCellIterator +{ + typedef std::pair<sc::CellStoreType::const_iterator, size_t> PositionType; + ScDocument* mpDoc; ScAddress maStartPos; ScAddress maEndPos; ScAddress maCurPos; + + PositionType maCurColPos; SCSIZE mnIndex; bool mbSubTotal; ScRefCellValue maCurCell; + void incBlock(); + void incPos(); + void setPos(size_t nPos); + + const ScColumn* getColumn() const; + void init(); bool getCurrent(); @@ -269,7 +254,9 @@ class ScQueryCellIterator // walk through all non-empty cells in an ar nTestEqualConditionFulfilled = nTestEqualConditionEnabled | nTestEqualConditionMatched }; -private: + typedef std::pair<sc::CellStoreType::const_iterator, size_t> PositionType; + PositionType maCurPos; + boost::scoped_ptr<ScQueryParam> mpParam; ScDocument* pDoc; const ScAttrArray* pAttrArray; @@ -277,14 +264,17 @@ private: SCTAB nTab; SCCOL nCol; SCROW nRow; - SCSIZE nColRow; SCROW nAttrEndRow; sal_uInt8 nStopOnMismatch; sal_uInt8 nTestEqualCondition; bool bAdvanceQuery; bool bIgnoreMismatchOnLeadingStrings; - ScBaseCell* GetThis(); + /** Initialize position for new column. */ + void InitPos(); + void IncPos(); + void IncBlock(); + bool GetThis(); /* Only works if no regular expression is involved, only searches for rows in one column, and only the first @@ -295,15 +285,15 @@ private: GetThis() and GetNext() afterwards. Introduced for FindEqualOrSortedLastInRange() */ - ScBaseCell* BinarySearch(); + bool BinarySearch(); public: ScQueryCellIterator(ScDocument* pDocument, SCTAB nTable, const ScQueryParam& aParam, bool bMod = true); // for bMod = FALSE the QueryParam has to be filled // (bIsString) - ScBaseCell* GetFirst(); - ScBaseCell* GetNext(); + bool GetFirst(); + bool GetNext(); SCCOL GetCol() { return nCol; } SCROW GetRow() { return nRow; } @@ -420,27 +410,36 @@ public: class ScHorizontalCellIterator // walk through all non empty cells in an area { // row by row -private: + typedef std::pair<sc::CellStoreType::const_iterator,size_t> PositionType; + + struct ColParam + { + sc::CellStoreType::const_iterator maPos; + sc::CellStoreType::const_iterator maEnd; + }; + + std::vector<ColParam> maColPositions; + ScDocument* pDoc; - SCTAB nTab; + SCTAB mnTab; SCCOL nStartCol; SCCOL nEndCol; SCROW nStartRow; SCROW nEndRow; SCROW* pNextRows; SCSIZE* pNextIndices; - SCCOL nCol; - SCROW nRow; + SCCOL mnCol; + SCROW mnRow; ScRefCellValue maCurCell; bool bMore; public: - ScHorizontalCellIterator(ScDocument* pDocument, SCTAB nTable, - SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2); - ~ScHorizontalCellIterator(); + ScHorizontalCellIterator(ScDocument* pDocument, SCTAB nTable, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2); + ~ScHorizontalCellIterator(); ScRefCellValue* GetNext( SCCOL& rCol, SCROW& rRow ); - bool ReturnNext( SCCOL& rCol, SCROW& rRow ); + bool GetPos( SCCOL& rCol, SCROW& rRow ); /// Set a(nother) sheet and (re)init. void SetTab( SCTAB nTab ); diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index 11a18e9ba24e..64f66e991e13 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -83,7 +83,6 @@ class XColorList; struct ScAttrEntry; class ScAutoFormatData; -class ScBaseCell; class ScBroadcastAreaSlotMachine; class ScChangeViewSettings; class ScChartCollection; @@ -212,7 +211,6 @@ const sal_uInt8 SC_DDE_IGNOREMODE = 255; /// For usage in FindDdeLink() class ScDocument { -friend class ScDocumentIterator; friend class ScValueIterator; friend class ScHorizontalValueIterator; friend class ScDBQueryDataIterator; @@ -1616,8 +1614,6 @@ public: SCCOL nCol, SCROW nRow, SCTAB nTab, ScMarkData& rMark, bool bIsUndo); - void DoColResize( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCSIZE nAdd ); - void InvalidateTextWidth( const OUString& rStyleName ); void InvalidateTextWidth( SCTAB nTab ); void InvalidateTextWidth( const ScAddress* pAdrFrom, const ScAddress* pAdrTo, bool bNumFormatChanged ); @@ -1700,6 +1696,11 @@ public: SC_DLLPUBLIC ScMacroManager* GetMacroManager(); + /** + * See if specified column has any non-empty cells. + */ + bool IsEmptyData( SCTAB nTab, SCCOL nCol ) const; + private: ScDocument(const ScDocument& r); // disabled with no definition @@ -2011,9 +2012,6 @@ private: // CLOOK-Impl-methods bool HasPartOfMerged( const ScRange& rRange ); - void PutCell( const ScAddress&, ScBaseCell* pCell, bool bForceTab = false ); - void PutCell(SCCOL nCol, SCROW nRow, SCTAB nTab, ScBaseCell* pCell, sal_uLong nFormatIndex, bool bForceTab = false ); - ScRefCellValue GetRefCellValue( const ScAddress& rPos ); std::map< SCTAB, ScSortParam > mSheetSortParams; diff --git a/sc/inc/documentimport.hxx b/sc/inc/documentimport.hxx index afd954bf93d5..9fddf2b86282 100644 --- a/sc/inc/documentimport.hxx +++ b/sc/inc/documentimport.hxx @@ -20,7 +20,6 @@ class ScDocument; class ScAddress; class ScTokenArray; -class ScBaseCell; struct ScDocumentImportImpl; /** @@ -62,9 +61,6 @@ public: void setFormulaCell(const ScAddress& rPos, const ScTokenArray& rArray); void finalize(); - -private: - void insertCell(const ScAddress& rPos, ScBaseCell* pCell); }; #endif diff --git a/sc/inc/editutil.hxx b/sc/inc/editutil.hxx index a4c7af899749..10bec821b00a 100644 --- a/sc/inc/editutil.hxx +++ b/sc/inc/editutil.hxx @@ -66,6 +66,8 @@ public: static void RemoveCharAttribs( EditTextObject& rEditText, const ScPatternAttr& rAttr ); + static EditTextObject* Clone( const EditTextObject& rSrc, ScDocument& rDestDoc ); + public: ScEditUtil( ScDocument* pDocument, SCCOL nX, SCROW nY, SCTAB nZ, const Point& rScrPosPixel, diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx index 747d67f977d6..10a5b585de4d 100644 --- a/sc/inc/formulacell.hxx +++ b/sc/inc/formulacell.hxx @@ -212,7 +212,7 @@ public: const ScMatrix* GetMatrix(); bool GetMatrixOrigin( ScAddress& rPos ) const; void GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows ); - sal_uInt16 GetMatrixEdge( ScAddress& rOrgPos ); + sal_uInt16 GetMatrixEdge( ScAddress& rOrgPos ) const; sal_uInt16 GetErrCode(); // interpret first if necessary sal_uInt16 GetRawError(); // don't interpret, just return code or result error sal_uInt8 GetMatrixFlag() const { return cMatrixFlag; } diff --git a/sc/inc/markdata.hxx b/sc/inc/markdata.hxx index ad7a4b1c828c..75937edde3e3 100644 --- a/sc/inc/markdata.hxx +++ b/sc/inc/markdata.hxx @@ -96,6 +96,8 @@ public: void FillRangeListWithMarks( ScRangeList* pList, bool bClear ) const; void ExtendRangeListTables( ScRangeList* pList ) const; + ScRangeList GetMarkedRanges() const; + void MarkFromRangeList( const ScRangeList& rList, bool bReset ); SCCOLROW GetMarkColumnRanges( SCCOLROW* pRanges ); diff --git a/sc/inc/mtvcellfunc.hxx b/sc/inc/mtvcellfunc.hxx new file mode 100644 index 000000000000..9ce3c12ea8cc --- /dev/null +++ b/sc/inc/mtvcellfunc.hxx @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef SC_MTVCELLFUNC_HXX +#define SC_MTVCELLFUNC_HXX + +#include "mtvelements.hxx" +#include "mtvfunctions.hxx" + +namespace sc { + +template<typename _Func> +void ProcessFormula(CellStoreType& rStore, _Func& rFunc) +{ + FuncElseNoOp<size_t> aElse; + ProcessElements1<CellStoreType, formula_block, _Func, FuncElseNoOp<size_t> >(rStore, rFunc, aElse); +} + +template<typename _FuncElem> +typename CellStoreType::iterator +ProcessFormula( + const CellStoreType::iterator& it, CellStoreType& rStore, SCROW nRow1, SCROW nRow2, _FuncElem& rFuncElem) +{ + FuncElseNoOp<size_t> aElse; + return ProcessElements1< + CellStoreType, formula_block, _FuncElem, FuncElseNoOp<size_t> >(it, rStore, nRow1, nRow2, rFuncElem, aElse); +} + +template<typename _FuncElem, typename _FuncElse> +typename CellStoreType::iterator +ProcessFormula( + const CellStoreType::iterator& it, CellStoreType& rStore, SCROW nRow1, SCROW nRow2, _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + return ProcessElements1< + CellStoreType, formula_block, _FuncElem, _FuncElse>(it, rStore, nRow1, nRow2, rFuncElem, rFuncElse); +} + +template<typename _Func> +typename CellStoreType::iterator +ProcessFormulaNumeric( + const CellStoreType::iterator& itPos, CellStoreType& rStore, SCROW nRow1, SCROW nRow2, _Func& rFunc) +{ + FuncElseNoOp<size_t> aElse; + return ProcessElements2< + CellStoreType, numeric_block, formula_block, _Func, FuncElseNoOp<size_t> >( + itPos, rStore, nRow1, nRow2, rFunc, aElse); +} + +template<typename _Func> +void ProcessEditText(CellStoreType& rStore, _Func& rFunc) +{ + FuncElseNoOp<size_t> aElse; + ProcessElements1<CellStoreType, edittext_block, _Func, FuncElseNoOp<size_t> >(rStore, rFunc, aElse); +} + +template<typename _Func> +CellStoreType::iterator +ProcessEditText(const CellStoreType::iterator& itPos, CellStoreType& rStore, SCROW nRow1, SCROW nRow2, _Func& rFunc) +{ + FuncElseNoOp<size_t> aElse; + return ProcessElements1<CellStoreType, edittext_block, _Func, FuncElseNoOp<size_t> >( + itPos, rStore, nRow1, nRow2, rFunc, aElse); +} + +template<typename _Func> +void ParseFormula( + const CellStoreType& rStore, _Func& rFunc) +{ + FuncElseNoOp<size_t> aElse; + ParseElements1<CellStoreType, formula_block, _Func, FuncElseNoOp<size_t> >(rStore, rFunc, aElse); +} + +template<typename _Func> +typename CellStoreType::const_iterator +ParseFormula( + const CellStoreType::const_iterator& itPos, const CellStoreType& rStore, + SCROW nStart, SCROW nEnd, _Func& rFunc) +{ + FuncElseNoOp<size_t> aElse; + return ParseElements1<CellStoreType, formula_block, _Func, FuncElseNoOp<size_t> >( + itPos, rStore, nStart, nEnd, rFunc, aElse); +} + +template<typename _FuncElem, typename _FuncElse> +typename CellStoreType::const_iterator +ParseAll( + const typename CellStoreType::const_iterator& itPos, const CellStoreType& rCells, + SCROW nRow1, SCROW nRow2, _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + return ParseElements4<CellStoreType, + numeric_block, string_block, edittext_block, formula_block, + _FuncElem, _FuncElse>( + itPos, rCells, nRow1, nRow2, rFuncElem, rFuncElse); +} + +template<typename _Func> +typename CellStoreType::const_iterator +ParseAllNonEmpty( + const typename CellStoreType::const_iterator& itPos, const CellStoreType& rCells, + SCROW nRow1, SCROW nRow2, _Func& rFunc) +{ + FuncElseNoOp<size_t> aElse; + return ParseElements4<CellStoreType, + numeric_block, string_block, edittext_block, formula_block, + _Func, FuncElseNoOp<size_t> >( + itPos, rCells, nRow1, nRow2, rFunc, aElse); +} + +template<typename _Func> +typename CellStoreType::const_iterator +ParseFormulaNumeric( + const CellStoreType::const_iterator& itPos, const CellStoreType& rCells, + SCROW nRow1, SCROW nRow2, _Func& rFunc) +{ + FuncElseNoOp<size_t> aElse; + return ParseElements2<CellStoreType, + numeric_block, formula_block, _Func, FuncElseNoOp<size_t> >( + itPos, rCells, nRow1, nRow2, rFunc, aElse); +} + +template<typename _Func> +void ProcessFormulaEditText(CellStoreType& rStore, _Func& rFunc) +{ + FuncElseNoOp<size_t> aElse; + ProcessElements2<CellStoreType, edittext_block, formula_block, _Func, FuncElseNoOp<size_t> >(rStore, rFunc, aElse); +} + +template<typename _Func> +std::pair<CellStoreType::const_iterator, size_t> +FindFormula(const CellStoreType& rStore, SCROW nRow1, SCROW nRow2, _Func& rFunc) +{ + typedef std::pair<size_t,bool> ElseRetType; + FuncElseNoOp<size_t, ElseRetType> aElse; + return FindElement1<CellStoreType, formula_block, _Func, FuncElseNoOp<size_t, ElseRetType> >(rStore, nRow1, nRow2, rFunc, aElse); +} + +template<typename _Func> +std::pair<CellStoreType::const_iterator, size_t> +FindFormulaEditText(const CellStoreType& rStore, SCROW nRow1, SCROW nRow2, _Func& rFunc) +{ + return FindElement2<CellStoreType, edittext_block, formula_block, _Func, _Func>(rStore, nRow1, nRow2, rFunc, rFunc); +} + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/inc/mtvelements.hxx b/sc/inc/mtvelements.hxx index 941c1a24a618..0ebf8be9635f 100644 --- a/sc/inc/mtvelements.hxx +++ b/sc/inc/mtvelements.hxx @@ -54,7 +54,9 @@ const mdds::mtv::element_t element_type_string = mdds::mtv::element_type_user_st const mdds::mtv::element_t element_type_edittext = mdds::mtv::element_type_user_start + 3; const mdds::mtv::element_t element_type_formula = mdds::mtv::element_type_user_start + 4; +// Mapped standard element types (for convenience). const mdds::mtv::element_t element_type_numeric = mdds::mtv::element_type_numeric; +const mdds::mtv::element_t element_type_empty = mdds::mtv::element_type_empty; // Custom element blocks. @@ -64,6 +66,9 @@ typedef mdds::mtv::default_element_block<element_type_string, rtl::OUString> str typedef mdds::mtv::noncopyable_managed_element_block<element_type_edittext, EditTextObject> edittext_block; typedef mdds::mtv::noncopyable_managed_element_block<element_type_formula, ScFormulaCell> formula_block; +// Mapped standard element blocks (for convenience). +typedef mdds::mtv::numeric_element_block numeric_block; + // This needs to be in the same namespace as CellTextAttr. MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(CellTextAttr, element_type_celltextattr, CellTextAttr(), celltextattr_block) @@ -83,18 +88,15 @@ MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(OUString, sc::element_type_string, OUString(), namespace sc { // Broadcaster storage container -typedef mdds::mtv::custom_block_func1<sc::element_type_broadcaster, sc::broadcaster_block> BCBlkFunc; +typedef mdds::mtv::custom_block_func1<sc::broadcaster_block> BCBlkFunc; typedef mdds::multi_type_vector<BCBlkFunc> BroadcasterStoreType; // Cell text attribute container. -typedef mdds::mtv::custom_block_func1<sc::element_type_celltextattr, sc::celltextattr_block> CTAttrFunc; +typedef mdds::mtv::custom_block_func1<sc::celltextattr_block> CTAttrFunc; typedef mdds::multi_type_vector<CTAttrFunc> CellTextAttrStoreType; // Cell container -typedef mdds::mtv::custom_block_func3< - sc::element_type_string, sc::string_block, - sc::element_type_edittext, sc::edittext_block, - sc::element_type_formula, sc::formula_block> CellFunc; +typedef mdds::mtv::custom_block_func3<sc::string_block, sc::edittext_block, sc::formula_block> CellFunc; typedef mdds::multi_type_vector<CellFunc> CellStoreType; /** @@ -104,6 +106,18 @@ struct ColumnBlockPosition { BroadcasterStoreType::iterator miBroadcasterPos; CellTextAttrStoreType::iterator miCellTextAttrPos; + CellStoreType::iterator miCellPos; + + ColumnBlockPosition& operator= (const ColumnBlockPosition& r); +}; + +struct ColumnBlockConstPosition +{ + BroadcasterStoreType::const_iterator miBroadcasterPos; + CellTextAttrStoreType::const_iterator miCellTextAttrPos; + CellStoreType::const_iterator miCellPos; + + ColumnBlockConstPosition& operator= (const ColumnBlockConstPosition& r); }; class ColumnBlockPositionSet @@ -125,3 +139,4 @@ public: #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ + diff --git a/sc/inc/mtvfunctions.hxx b/sc/inc/mtvfunctions.hxx new file mode 100644 index 000000000000..03cb55ab4548 --- /dev/null +++ b/sc/inc/mtvfunctions.hxx @@ -0,0 +1,786 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef SC_MTVFUNCTIONS_HXX +#define SC_MTVFUNCTIONS_HXX + +#include <cstdlib> +#include <mdds/multi_type_vector_types.hpp> + +namespace sc { + +template<typename _SizeT, typename _Ret = bool> +struct FuncElseNoOp +{ + _Ret operator() (mdds::mtv::element_t, _SizeT, _SizeT) const + { + return _Ret(); + } +}; + +/** + * Generic algorithm to parse blocks of multi_type_vector either partially + * or fully. + */ +template<typename _StoreT, typename _Func> +typename _StoreT::const_iterator +ParseBlock( + const typename _StoreT::const_iterator& itPos, const _StoreT& rStore, _Func& rFunc, + typename _StoreT::size_type nStart, typename _StoreT::size_type nEnd) +{ + typedef std::pair<typename _StoreT::const_iterator, typename _StoreT::size_type> PositionType; + + PositionType aPos = rStore.position(itPos, nStart); + typename _StoreT::const_iterator it = aPos.first; + typename _StoreT::size_type nOffset = aPos.second; + typename _StoreT::size_type nDataSize = 0; + typename _StoreT::size_type nTopRow = nStart; + + for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize) + { + bool bLastBlock = false; + nDataSize = it->size - nOffset; + if (nTopRow + nDataSize - 1 > nEnd) + { + // Truncate the block. + nDataSize = nEnd - nTopRow + 1; + bLastBlock = true; + } + + rFunc(*it, nOffset, nDataSize); + + if (bLastBlock) + break; + } + + return it; +} + +/** + * Non-const variant of the above function. TODO: Find a way to merge these + * two in an elegant way. + */ +template<typename _StoreT, typename _Func> +typename _StoreT::iterator +ProcessBlock(const typename _StoreT::iterator& itPos, _StoreT& rStore, _Func& rFunc, typename _StoreT::size_type nStart, typename _StoreT::size_type nEnd) +{ + typedef std::pair<typename _StoreT::iterator, typename _StoreT::size_type> PositionType; + + PositionType aPos = rStore.position(itPos, nStart); + typename _StoreT::iterator it = aPos.first; + typename _StoreT::size_type nOffset = aPos.second; + typename _StoreT::size_type nDataSize = 0; + typename _StoreT::size_type nCurRow = nStart; + + for (; it != rStore.end() && nCurRow <= nEnd; ++it, nOffset = 0, nCurRow += nDataSize) + { + bool bLastBlock = false; + nDataSize = it->size - nOffset; + if (nCurRow + nDataSize - 1 > nEnd) + { + // Truncate the block. + nDataSize = nEnd - nCurRow + 1; + bLastBlock = true; + } + + rFunc(*it, nOffset, nDataSize); + + if (bLastBlock) + break; + } + + return it; +} + +template<typename _StoreT, typename _BlkT, typename _FuncElem, typename _FuncElse> +void ParseElements1(const _StoreT& rStore, _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typename _StoreT::size_type nTopRow = 0, nDataSize = 0; + typename _StoreT::const_iterator it = rStore.begin(), itEnd = rStore.end(); + for (; it != itEnd; ++it, nTopRow += nDataSize) + { + nDataSize = it->size; + if (it->type != _BlkT::block_type) + { + rFuncElse(it->type, nTopRow, nDataSize); + continue; + } + + typename _BlkT::const_iterator itf = _BlkT::begin(*it->data); + typename _BlkT::const_iterator itfEnd = _BlkT::end(*it->data); + typename _StoreT::size_type nRow = nTopRow; + for (; itf != itfEnd; ++itf, ++nRow) + rFuncElem(nRow, *itf); + } +} + +template<typename _StoreT, typename _BlkT, typename _FuncElem, typename _FuncElse> +typename _StoreT::const_iterator +ParseElements1( + const typename _StoreT::const_iterator& itPos, const _StoreT& rStore, + typename _StoreT::size_type nStart, typename _StoreT::size_type nEnd, + _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typedef std::pair<typename _StoreT::const_iterator, typename _StoreT::size_type> PositionType; + + PositionType aPos = rStore.position(itPos, nStart); + typename _StoreT::const_iterator it = aPos.first; + typename _StoreT::size_type nOffset = aPos.second; + typename _StoreT::size_type nDataSize = 0; + typename _StoreT::size_type nTopRow = nStart; + + for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize) + { + bool bLastBlock = false; + nDataSize = it->size - nOffset; + if (nTopRow + nDataSize - 1 > nEnd) + { + // Truncate the block. + nDataSize = nEnd - nTopRow + 1; + bLastBlock = true; + } + + if (it->type == _BlkT::block_type) + { + typename _BlkT::const_iterator itf = _BlkT::begin(*it->data); + std::advance(itf, nOffset); + typename _BlkT::const_iterator itfEnd = itf; + std::advance(itfEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itf != itfEnd; ++itf, ++nRow) + rFuncElem(nRow, *itf); + } + else + rFuncElse(it->type, nTopRow, nDataSize); + + + if (bLastBlock) + break; + } + + return it; +}; + +template<typename _StoreT, typename _Blk1, typename _Blk2, typename _FuncElem, typename _FuncElse> +typename _StoreT::const_iterator +ParseElements2( + const typename _StoreT::const_iterator& itPos, const _StoreT& rStore, typename _StoreT::size_type nStart, typename _StoreT::size_type nEnd, + _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typedef std::pair<typename _StoreT::const_iterator, typename _StoreT::size_type> PositionType; + + PositionType aPos = rStore.position(itPos, nStart); + typename _StoreT::const_iterator it = aPos.first; + typename _StoreT::size_type nOffset = aPos.second; + typename _StoreT::size_type nDataSize = 0; + typename _StoreT::size_type nTopRow = nStart; + + for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize) + { + bool bLastBlock = false; + nDataSize = it->size - nOffset; + if (nTopRow + nDataSize - 1 > nEnd) + { + // Truncate the block. + nDataSize = nEnd - nTopRow + 1; + bLastBlock = true; + } + + switch (it->type) + { + case _Blk1::block_type: + { + typename _Blk1::const_iterator itData = _Blk1::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk1::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk2::block_type: + { + typename _Blk2::const_iterator itData = _Blk2::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk2::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + default: + rFuncElse(it->type, nTopRow, nDataSize); + } + + if (bLastBlock) + break; + } + + return it; +} + +template<typename _StoreT, typename _Blk1, typename _Blk2, typename _Blk3, typename _Blk4, typename _FuncElem, typename _FuncElse> +typename _StoreT::const_iterator +ParseElements4( + const typename _StoreT::const_iterator& itPos, const _StoreT& rStore, typename _StoreT::size_type nStart, typename _StoreT::size_type nEnd, + _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typedef std::pair<typename _StoreT::const_iterator, typename _StoreT::size_type> PositionType; + + PositionType aPos = rStore.position(itPos, nStart); + typename _StoreT::const_iterator it = aPos.first; + typename _StoreT::size_type nOffset = aPos.second; + typename _StoreT::size_type nDataSize = 0; + typename _StoreT::size_type nTopRow = nStart; + + for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize) + { + bool bLastBlock = false; + nDataSize = it->size - nOffset; + if (nTopRow + nDataSize - 1 > nEnd) + { + // Truncate the block. + nDataSize = nEnd - nTopRow + 1; + bLastBlock = true; + } + + switch (it->type) + { + case _Blk1::block_type: + { + typename _Blk1::const_iterator itData = _Blk1::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk1::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk2::block_type: + { + typename _Blk2::const_iterator itData = _Blk2::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk2::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk3::block_type: + { + typename _Blk3::const_iterator itData = _Blk3::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk3::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk4::block_type: + { + typename _Blk4::const_iterator itData = _Blk4::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk4::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + default: + rFuncElse(it->type, nTopRow, nDataSize); + } + + if (bLastBlock) + break; + } + + return it; +} + +template<typename _StoreT, typename _BlkT, typename _FuncElem, typename _FuncElse> +void ProcessElements1(_StoreT& rStore, _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typename _StoreT::size_type nTopRow = 0, nDataSize = 0; + typename _StoreT::iterator it = rStore.begin(), itEnd = rStore.end(); + for (; it != itEnd; ++it, nTopRow += nDataSize) + { + nDataSize = it->size; + if (it->type != _BlkT::block_type) + { + rFuncElse(it->type, nTopRow, nDataSize); + continue; + } + + typename _BlkT::iterator itf = _BlkT::begin(*it->data); + typename _BlkT::iterator itfEnd = _BlkT::end(*it->data); + typename _StoreT::size_type nRow = nTopRow; + for (; itf != itfEnd; ++itf, ++nRow) + rFuncElem(nRow, *itf); + } +} + +/** + * This variant specifies start and end positions. + */ +template<typename _StoreT, typename _BlkT, typename _FuncElem, typename _FuncElse> +typename _StoreT::iterator +ProcessElements1( + const typename _StoreT::iterator& itPos, _StoreT& rStore, + typename _StoreT::size_type nStart, typename _StoreT::size_type nEnd, + _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typedef std::pair<typename _StoreT::iterator, typename _StoreT::size_type> PositionType; + + PositionType aPos = rStore.position(itPos, nStart); + typename _StoreT::iterator it = aPos.first; + typename _StoreT::size_type nOffset = aPos.second; + typename _StoreT::size_type nDataSize = 0; + typename _StoreT::size_type nTopRow = nStart; + + for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize) + { + bool bLastBlock = false; + nDataSize = it->size - nOffset; + if (nTopRow + nDataSize - 1 > nEnd) + { + // Truncate the block. + nDataSize = nEnd - nTopRow + 1; + bLastBlock = true; + } + + if (it->type == _BlkT::block_type) + { + typename _BlkT::iterator itf = _BlkT::begin(*it->data); + std::advance(itf, nOffset); + typename _BlkT::iterator itfEnd = itf; + std::advance(itfEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itf != itfEnd; ++itf, ++nRow) + rFuncElem(nRow, *itf); + } + else + rFuncElse(it->type, nTopRow, nDataSize); + + if (bLastBlock) + break; + } + + return it; +}; + +template<typename _StoreT, typename _Blk1, typename _Blk2, typename _FuncElem, typename _FuncElse> +void ProcessElements2(_StoreT& rStore, _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typename _StoreT::size_type nTopRow = 0, nDataSize = 0; + typename _StoreT::iterator it = rStore.begin(), itEnd = rStore.end(); + for (; it != itEnd; ++it, nTopRow += nDataSize) + { + nDataSize = it->size; + switch (it->type) + { + case _Blk1::block_type: + { + typename _Blk1::iterator itData = _Blk1::begin(*it->data); + typename _Blk1::iterator itDataEnd = _Blk1::end(*it->data); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk2::block_type: + { + typename _Blk2::iterator itData = _Blk2::begin(*it->data); + typename _Blk2::iterator itDataEnd = _Blk2::end(*it->data); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + default: + rFuncElse(it->type, nTopRow, nDataSize); + } + } +} + +template<typename _StoreT, typename _Blk1, typename _Blk2, typename _FuncElem, typename _FuncElse> +typename _StoreT::iterator +ProcessElements2( + const typename _StoreT::iterator& itPos, _StoreT& rStore, + typename _StoreT::size_type nStart, typename _StoreT::size_type nEnd, + _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typedef std::pair<typename _StoreT::iterator, typename _StoreT::size_type> PositionType; + + PositionType aPos = rStore.position(itPos, nStart); + typename _StoreT::iterator it = aPos.first; + typename _StoreT::size_type nOffset = aPos.second; + typename _StoreT::size_type nDataSize = 0; + typename _StoreT::size_type nTopRow = nStart; + + for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize) + { + bool bLastBlock = false; + nDataSize = it->size - nOffset; + if (nTopRow + nDataSize - 1 > nEnd) + { + // Truncate the block. + nDataSize = nEnd - nTopRow + 1; + bLastBlock = true; + } + + switch (it->type) + { + case _Blk1::block_type: + { + typename _Blk1::iterator itData = _Blk1::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk1::iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk2::block_type: + { + typename _Blk2::iterator itData = _Blk2::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk2::iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + default: + rFuncElse(it->type, nTopRow, nDataSize); + } + + if (bLastBlock) + break; + } + + return it; +} + +template<typename _StoreT, typename _Blk1, typename _Blk2, typename _Blk3, typename _FuncElem, typename _FuncElse> +void ProcessElements3(_StoreT& rStore, _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typename _StoreT::size_type nTopRow = 0, nDataSize = 0; + typename _StoreT::iterator it = rStore.begin(), itEnd = rStore.end(); + for (; it != itEnd; ++it, nTopRow += nDataSize) + { + nDataSize = it->size; + switch (it->type) + { + case _Blk1::block_type: + { + typename _Blk1::iterator itData = _Blk1::begin(*it->data); + typename _Blk1::iterator itDataEnd = _Blk1::end(*it->data); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk2::block_type: + { + typename _Blk2::iterator itData = _Blk2::begin(*it->data); + typename _Blk2::iterator itDataEnd = _Blk2::end(*it->data); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk3::block_type: + { + typename _Blk3::iterator itData = _Blk3::begin(*it->data); + typename _Blk3::iterator itDataEnd = _Blk3::end(*it->data); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + default: + rFuncElse(it->type, nTopRow, nDataSize); + } + } +} + +template<typename _StoreT, typename _Blk1, typename _Blk2, typename _Blk3, typename _Blk4, typename _FuncElem, typename _FuncElse> +void ProcessElements4(_StoreT& rStore, _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typename _StoreT::size_type nTopRow = 0, nDataSize = 0; + typename _StoreT::iterator it = rStore.begin(), itEnd = rStore.end(); + for (; it != itEnd; ++it, nTopRow += nDataSize) + { + nDataSize = it->size; + switch (it->type) + { + case _Blk1::block_type: + { + typename _Blk1::iterator itData = _Blk1::begin(*it->data); + typename _Blk1::iterator itDataEnd = _Blk1::end(*it->data); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk2::block_type: + { + typename _Blk2::iterator itData = _Blk2::begin(*it->data); + typename _Blk2::iterator itDataEnd = _Blk2::end(*it->data); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk3::block_type: + { + typename _Blk3::iterator itData = _Blk3::begin(*it->data); + typename _Blk3::iterator itDataEnd = _Blk3::end(*it->data); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk4::block_type: + { + typename _Blk4::iterator itData = _Blk4::begin(*it->data); + typename _Blk4::iterator itDataEnd = _Blk4::end(*it->data); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + default: + rFuncElse(it->type, nTopRow, nDataSize); + } + } +} + +template<typename _StoreT, typename _Blk1, typename _Blk2, typename _Blk3, typename _Blk4, typename _FuncElem, typename _FuncElse> +typename _StoreT::iterator +ProcessElements4( + const typename _StoreT::iterator& itPos, _StoreT& rStore, + typename _StoreT::size_type nStart, typename _StoreT::size_type nEnd, + _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typedef std::pair<typename _StoreT::iterator, typename _StoreT::size_type> PositionType; + + PositionType aPos = rStore.position(itPos, nStart); + typename _StoreT::iterator it = aPos.first; + typename _StoreT::size_type nOffset = aPos.second; + typename _StoreT::size_type nDataSize = 0; + typename _StoreT::size_type nTopRow = nStart; + + for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize) + { + bool bLastBlock = false; + nDataSize = it->size - nOffset; + if (nTopRow + nDataSize - 1 > nEnd) + { + // Truncate the block. + nDataSize = nEnd - nTopRow + 1; + bLastBlock = true; + } + + switch (it->type) + { + case _Blk1::block_type: + { + typename _Blk1::iterator itData = _Blk1::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk1::iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk2::block_type: + { + typename _Blk2::iterator itData = _Blk2::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk2::iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk3::block_type: + { + typename _Blk3::iterator itData = _Blk3::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk3::iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + case _Blk4::block_type: + { + typename _Blk4::iterator itData = _Blk4::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk4::iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + rFuncElem(nRow, *itData); + } + break; + default: + rFuncElse(it->type, nTopRow, nDataSize); + } + + if (bLastBlock) + break; + } + + return it; +} + +template<typename _StoreT, typename _Blk1, typename _FuncElem, typename _FuncElse> +std::pair<typename _StoreT::const_iterator, typename _StoreT::size_type> +FindElement1( + const _StoreT& rStore, typename _StoreT::size_type nStart, typename _StoreT::size_type nEnd, + _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typedef std::pair<typename _StoreT::const_iterator, typename _StoreT::size_type> PositionType; + typedef std::pair<typename _StoreT::size_type, bool> ElseRetType; + + PositionType aPos = rStore.position(nStart); + typename _StoreT::const_iterator it = aPos.first; + typename _StoreT::size_type nOffset = aPos.second; + typename _StoreT::size_type nDataSize = 0; + typename _StoreT::size_type nTopRow = nStart; + + for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize) + { + bool bLastBlock = false; + nDataSize = it->size - nOffset; + if (nTopRow + nDataSize - 1 > nEnd) + { + // Truncate the block. + nDataSize = nEnd - nTopRow + 1; + bLastBlock = true; + } + + switch (it->type) + { + case _Blk1::block_type: + { + typename _Blk1::const_iterator itData = _Blk1::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk1::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + { + if (rFuncElem(nRow, *itData)) + return PositionType(it, nRow-nTopRow); + } + } + break; + default: + { + ElseRetType aRet = rFuncElse(it->type, nTopRow, nDataSize); + if (aRet.second) + return PositionType(it, aRet.first); + } + } + + if (bLastBlock) + break; + } + + return PositionType(rStore.end(), 0); +} + +template<typename _StoreT, typename _Blk1, typename _Blk2, typename _FuncElem, typename _FuncElse> +std::pair<typename _StoreT::const_iterator, typename _StoreT::size_type> +FindElement2( + const _StoreT& rStore, typename _StoreT::size_type nStart, typename _StoreT::size_type nEnd, + _FuncElem& rFuncElem, _FuncElse& rFuncElse) +{ + typedef std::pair<typename _StoreT::const_iterator, typename _StoreT::size_type> PositionType; + typedef std::pair<typename _StoreT::size_type, bool> ElseRetType; + + PositionType aPos = rStore.position(nStart); + typename _StoreT::const_iterator it = aPos.first; + typename _StoreT::size_type nOffset = aPos.second; + typename _StoreT::size_type nDataSize = 0; + typename _StoreT::size_type nTopRow = nStart; + + for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize) + { + bool bLastBlock = false; + nDataSize = it->size - nOffset; + if (nTopRow + nDataSize - 1 > nEnd) + { + // Truncate the block. + nDataSize = nEnd - nTopRow + 1; + bLastBlock = true; + } + + switch (it->type) + { + case _Blk1::block_type: + { + typename _Blk1::const_iterator itData = _Blk1::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk1::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + { + if (rFuncElem(nRow, *itData)) + return PositionType(it, nRow-nTopRow); + } + } + break; + case _Blk2::block_type: + { + typename _Blk2::const_iterator itData = _Blk2::begin(*it->data); + std::advance(itData, nOffset); + typename _Blk2::const_iterator itDataEnd = itData; + std::advance(itDataEnd, nDataSize); + typename _StoreT::size_type nRow = nTopRow; + for (; itData != itDataEnd; ++itData, ++nRow) + if (rFuncElem(nRow, *itData)) + return PositionType(it, nRow-nTopRow); + } + break; + default: + { + ElseRetType aRet = rFuncElse(it->type, nTopRow, nDataSize); + if (aRet.second) + return PositionType(it, aRet.first); + } + } + + if (bLastBlock) + break; + } + + return PositionType(rStore.end(), 0); +} + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/inc/scopetools.hxx b/sc/inc/scopetools.hxx new file mode 100644 index 000000000000..590ccbf0f2ce --- /dev/null +++ b/sc/inc/scopetools.hxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef SC_SCOPETOOLS_HXX +#define SC_SCOPETOOLS_HXX + +class ScDocument; + +namespace sc { + +/** + * Temporarily switch on/off auto calculation mode. + */ +class AutoCalcSwitch +{ + ScDocument& mrDoc; + bool mbOldValue; +public: + AutoCalcSwitch(ScDocument& rDoc, bool bAutoCalc); + ~AutoCalcSwitch(); +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx index b62fa1e8cf56..c5c02862e4cc 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -30,6 +30,7 @@ #include "compressedarray.hxx" #include "postit.hxx" #include "types.hxx" +#include "cellvalue.hxx" #include "formula/types.hxx" #include <set> @@ -65,7 +66,6 @@ class SvxBoxItem; class SvxSearchItem; class ScAutoFormatData; -class ScBaseCell; class ScDocument; class ScEditDataArray; class ScFormulaCell; @@ -191,7 +191,6 @@ private: bool mbPageBreaksValid:1; friend class ScDocument; // for FillInfo -friend class ScDocumentIterator; friend class ScValueIterator; friend class ScHorizontalValueIterator; friend class ScDBQueryDataIterator; @@ -209,6 +208,10 @@ public: bool bColInfo = true, bool bRowInfo = true ); ~ScTable(); + ScDocument& GetDoc(); + const ScDocument& GetDoc() const; + SCTAB GetTab() const { return nTab; } + ScOutlineTable* GetOutlineTable() { return pOutlineTable; } SCSIZE GetCellCount(SCCOL nCol) const; @@ -310,10 +313,6 @@ public: bool IsBlockEmpty( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bIgnoreNotes = false ) const; - void PutCell( const ScAddress&, ScBaseCell* pCell ); - void PutCell( SCCOL nCol, SCROW nRow, ScBaseCell* pCell ); - void PutCell(SCCOL nCol, SCROW nRow, sal_uLong nFormatIndex, ScBaseCell* pCell); - bool SetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const String& rString, ScSetStringParam* pParam = NULL ); @@ -331,6 +330,7 @@ public: void SetValue( SCCOL nCol, SCROW nRow, const double& rVal ); void SetError( SCCOL nCol, SCROW nRow, sal_uInt16 nError); + void SetRawString( SCCOL nCol, SCROW nRow, const OUString& rStr ); void GetString( SCCOL nCol, SCROW nRow, OUString& rString ) const; const OUString* GetStringCell( SCCOL nCol, SCROW nRow ) const; double* GetValueCell( SCCOL nCol, SCROW nRow ); @@ -356,13 +356,7 @@ public: CELLTYPE_NONE; } CellType GetCellType( SCCOL nCol, SCROW nRow ) const; - ScBaseCell* GetCell( const ScAddress& rPos ) const - { - return ValidColRow(rPos.Col(),rPos.Row()) ? - aCol[rPos.Col()].GetCell( rPos.Row() ) : - NULL; - } - ScBaseCell* GetCell( SCCOL nCol, SCROW nRow ) const; + ScRefCellValue GetCellValue( SCCOL nCol, SCROW nRow ) const; void GetFirstDataPos(SCCOL& rCol, SCROW& rRow) const; void GetLastDataPos(SCCOL& rCol, SCROW& rRow) const; @@ -372,7 +366,7 @@ public: @param bForced True = always create all captions, false = skip when Undo is disabled. */ void InitializeNoteCaptions( bool bForced = false ); - bool TestInsertRow( SCCOL nStartCol, SCCOL nEndCol, SCSIZE nSize ) const; + bool TestInsertRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize ) const; void InsertRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize ); void DeleteRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize, bool* pUndoOutline = NULL ); @@ -480,6 +474,8 @@ public: 0; } + bool IsEmptyData( SCCOL nCol ) const; + void ResetChanged( const ScRange& rRange ); void SetDirty(); @@ -793,8 +789,8 @@ public: void ExtendHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 ); void Sort(const ScSortParam& rSortParam, bool bKeepQuery, ScProgress* pProgress); - bool ValidQuery( - SCROW nRow, const ScQueryParam& rQueryParam, ScBaseCell* pCell = NULL, + bool ValidQuery( + SCROW nRow, const ScQueryParam& rQueryParam, ScRefCellValue* pCell = NULL, bool* pbTestEqualCondition = NULL); void TopTenQuery( ScQueryParam& ); SCSIZE Query(ScQueryParam& rQueryParam, bool bKeepSub); @@ -807,9 +803,6 @@ public: bool HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const; bool HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const; - void DoColResize( SCCOL nCol1, SCCOL nCol2, SCSIZE nAdd ); - - sal_Int32 GetMaxStringLen( SCCOL nCol, SCROW nRowStart, SCROW nRowEnd, CharSet eCharSet ) const; xub_StrLen GetMaxNumberStringLen( sal_uInt16& nPrecision, @@ -918,9 +911,10 @@ private: void DecoladeRow( ScSortInfoArray*, SCROW nRow1, SCROW nRow2 ); void SwapCol(SCCOL nCol1, SCCOL nCol2); void SwapRow(SCROW nRow1, SCROW nRow2); - short CompareCell( sal_uInt16 nSort, - ScBaseCell* pCell1, SCCOL nCell1Col, SCROW nCell1Row, - ScBaseCell* pCell2, SCCOL nCell2Col, SCROW nCell2Row ) const; + short CompareCell( + sal_uInt16 nSort, + ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row, + ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const; short Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const; short Compare( ScSortInfoArray*, SCCOLROW nIndex1, SCCOLROW nIndex2) const; ScSortInfoArray* CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 ); @@ -1000,14 +994,14 @@ private: * * @return First visible data cell if found, or NULL otherwise. */ - ScBaseCell* reset(SCROW nRow); + ScRefCellValue reset(SCROW nRow); /** * Find the next visible data cell position. * * @return Next visible data cell if found, or NULL otherwise. */ - ScBaseCell* next(); + ScRefCellValue next(); /** * Get the current row position. @@ -1020,7 +1014,7 @@ private: private: ScFlatBoolRowSegments& mrRowSegs; ScColumn& mrColumn; - ScBaseCell* mpCell; + ScRefCellValue maCell; SCROW mnCurRow; SCROW mnUBound; }; diff --git a/sc/inc/types.hxx b/sc/inc/types.hxx index 9654271c4887..629ab720f728 100644 --- a/sc/inc/types.hxx +++ b/sc/inc/types.hxx @@ -7,8 +7,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef __SC_TYPES_HXX__ -#define __SC_TYPES_HXX__ +#ifndef SC_TYPES_HXX +#define SC_TYPES_HXX #include <boost/intrusive_ptr.hpp> @@ -36,6 +36,18 @@ enum ScFormulaVectorState FormulaVectorUnknown }; +namespace sc { + +const sal_uInt16 MatrixEdgeNothing = 0; +const sal_uInt16 MatrixEdgeInside = 1; +const sal_uInt16 MatrixEdgeBottom = 2; +const sal_uInt16 MatrixEdgeLeft = 4; +const sal_uInt16 MatrixEdgeTop = 8; +const sal_uInt16 MatrixEdgeRight = 16; +const sal_uInt16 MatrixEdgeOpen = 32; + +} + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx index f41185e0c2bb..28436dfad105 100644 --- a/sc/qa/unit/ucalc.cxx +++ b/sc/qa/unit/ucalc.cxx @@ -52,6 +52,7 @@ #include "conditio.hxx" #include "globstr.hrc" #include "tokenarray.hxx" +#include "scopetools.hxx" #include "formula/IFunctionDescription.hxx" @@ -417,25 +418,6 @@ ScRange insertRangeData(ScDocument* pDoc, const ScAddress& rPos, const char* aDa } /** - * Temporarily switch on/off auto calculation mode. - */ -class AutoCalcSwitch -{ - ScDocument* mpDoc; - bool mbOldValue; -public: - AutoCalcSwitch(ScDocument* pDoc, bool bAutoCalc) : mpDoc(pDoc), mbOldValue(pDoc->GetAutoCalc()) - { - mpDoc->SetAutoCalc(bAutoCalc); - } - - ~AutoCalcSwitch() - { - mpDoc->SetAutoCalc(mbOldValue); - } -}; - -/** * Temporarily set formula grammar. */ class FormulaGrammarSwitch @@ -1800,7 +1782,7 @@ void Test::testFormulaDepTracking() { CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo")); - AutoCalcSwitch aACSwitch(m_pDoc, true); // turn on auto calculation. + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation. // B2 listens on D2. m_pDoc->SetString(1, 1, 0, "=D2"); @@ -1908,7 +1890,7 @@ void Test::testFormulaDepTracking2() { CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo")); - AutoCalcSwitch aACSwitch(m_pDoc, true); // turn on auto calculation. + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation. double val = 2.0; m_pDoc->SetValue(0, 0, 0, val); @@ -2018,7 +2000,7 @@ void Test::testCellBroadcaster() { CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo")); - AutoCalcSwitch aACSwitch(m_pDoc, true); // turn on auto calculation. + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation. m_pDoc->SetString(ScAddress(1,0,0), "=A1"); // B1 depends on A1. double val = m_pDoc->GetValue(ScAddress(1,0,0)); // A1 is empty, so the result should be 0. CPPUNIT_ASSERT_EQUAL(0.0, val); @@ -3059,7 +3041,7 @@ void Test::testPivotTableFilters() CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess); } - AutoCalcSwitch aACSwitch(m_pDoc, true); // turn on auto calculation. + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation. ScAddress aFormulaAddr = aOutRange.aEnd; aFormulaAddr.IncRow(2); diff --git a/sc/source/core/data/cell2.cxx b/sc/source/core/data/cell2.cxx index 8345e488f980..0d46036bf07c 100644 --- a/sc/source/core/data/cell2.cxx +++ b/sc/source/core/data/cell2.cxx @@ -135,39 +135,22 @@ void ScEditCell::UpdateFields(SCTAB nTab) aUpdater.updateTableFields(nTab); } -void ScEditCell::SetTextObject( const EditTextObject* pObject, - const SfxItemPool* pFromPool ) +void ScEditCell::SetTextObject( + const EditTextObject* pObject, const SfxItemPool* pFromPool ) { - if ( pObject ) + if (!pObject) { - if ( pFromPool && mpDoc->GetEditPool() == pFromPool ) - mpData = pObject->Clone(); - else - { //! another "spool" - // Sadly there is no other way to change the Pool than to - // "spool" the Object through a corresponding Engine - EditEngine& rEngine = mpDoc->GetEditEngine(); - if ( pObject->HasOnlineSpellErrors() ) - { - 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( *pObject ); - mpData = rEngine.CreateTextObject(); - if ( bNewControl ) - rEngine.SetControlWord( nControl ); - } - else - { - rEngine.SetText( *pObject ); - mpData = rEngine.CreateTextObject(); - } - } - } - else mpData = NULL; + return; + } + + if ( pFromPool && mpDoc->GetEditPool() == pFromPool ) + { + mpData = pObject->Clone(); + return; + } + + mpData = ScEditUtil::Clone(*pObject, *mpDoc); } ScEditDataArray::ScEditDataArray() diff --git a/sc/source/core/data/cellclonehandler.cxx b/sc/source/core/data/cellclonehandler.cxx new file mode 100644 index 000000000000..1f1d75ccf802 --- /dev/null +++ b/sc/source/core/data/cellclonehandler.cxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "cellclonehandler.hxx" +#include "editutil.hxx" +#include "document.hxx" + +namespace sc { + +CellBlockCloneHandler::CellBlockCloneHandler( + ScDocument& rSrcDoc, ScDocument& rDestDoc, CellStoreType& rDestCellStore) : + mrSrcDoc(rSrcDoc), mrDestDoc(rDestDoc), mrDestCellStore(rDestCellStore) {} + +CellBlockCloneHandler::~CellBlockCloneHandler() {} + +ScDocument& CellBlockCloneHandler::getSrcDoc() +{ + return mrSrcDoc; +} + +ScDocument& CellBlockCloneHandler::getDestDoc() +{ + return mrDestDoc; +} + +const ScDocument& CellBlockCloneHandler::getDestDoc() const +{ + return mrDestDoc; +} + +CellStoreType& CellBlockCloneHandler::getDestCellStore() +{ + return mrDestCellStore; +} + +void CellBlockCloneHandler::cloneDoubleBlock( + CellStoreType::iterator& itPos, const ScAddress& /*rSrcPos*/, const ScAddress& rDestPos, + const numeric_block::const_iterator& itBegin, const numeric_block::const_iterator& itEnd) +{ + itPos = mrDestCellStore.set(itPos, rDestPos.Row(), itBegin, itEnd); +} + +void CellBlockCloneHandler::cloneStringBlock( + CellStoreType::iterator& itPos, const ScAddress& /*rSrcPos*/, const ScAddress& rDestPos, + const string_block::const_iterator& itBegin, const string_block::const_iterator& itEnd) +{ + itPos = mrDestCellStore.set(itPos, rDestPos.Row(), itBegin, itEnd); +} + +void CellBlockCloneHandler::cloneEditTextBlock( + CellStoreType::iterator& itPos, const ScAddress& /*rSrcPos*/, const ScAddress& rDestPos, + const edittext_block::const_iterator& itBegin, const edittext_block::const_iterator& itEnd) +{ + std::vector<EditTextObject*> aCloned; + aCloned.reserve(std::distance(itBegin, itEnd)); + for (edittext_block::const_iterator it = itBegin; it != itEnd; ++it) + aCloned.push_back(ScEditUtil::Clone(**it, getDestDoc())); + + itPos = getDestCellStore().set(itPos, rDestPos.Row(), aCloned.begin(), aCloned.end()); +} + +void CellBlockCloneHandler::cloneFormulaBlock( + CellStoreType::iterator& itPos, const ScAddress& /*rSrcPos*/, const ScAddress& rDestPos, + const formula_block::const_iterator& itBegin, const formula_block::const_iterator& itEnd) +{ + std::vector<ScFormulaCell*> aCloned; + aCloned.reserve(std::distance(itBegin, itEnd)); + ScAddress aDestPos = rDestPos; + for (formula_block::const_iterator it = itBegin; it != itEnd; ++it, aDestPos.IncRow()) + { + const ScFormulaCell& rOld = **it; + if (rOld.GetDirty() && getSrcDoc().GetAutoCalc()) + const_cast<ScFormulaCell&>(rOld).Interpret(); + + aCloned.push_back(new ScFormulaCell(rOld, getDestDoc(), aDestPos)); + } + + itPos = getDestCellStore().set(itPos, rDestPos.Row(), aCloned.begin(), aCloned.end()); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/core/data/cellvalue.cxx b/sc/source/core/data/cellvalue.cxx index 75362ba7bfc2..169d2f7ef6d7 100644 --- a/sc/source/core/data/cellvalue.cxx +++ b/sc/source/core/data/cellvalue.cxx @@ -9,6 +9,7 @@ #include "cellvalue.hxx" #include "document.hxx" +#include "column.hxx" #include "cell.hxx" #include "formulacell.hxx" #include "editeng/editobj.hxx" @@ -182,6 +183,41 @@ void ScCellValue::clear() mfValue = 0.0; } +void ScCellValue::set( double fValue ) +{ + clear(); + meType = CELLTYPE_VALUE; + mfValue = fValue; +} + +void ScCellValue::set( const OUString& rStr ) +{ + clear(); + meType = CELLTYPE_STRING; + mpString = new OUString(rStr); +} + +void ScCellValue::set( const EditTextObject& rEditText ) +{ + clear(); + meType = CELLTYPE_EDIT; + mpEditText = rEditText.Clone(); +} + +void ScCellValue::set( const ScFormulaCell& rFormula ) +{ + clear(); + meType = CELLTYPE_FORMULA; + mpFormula = rFormula.Clone(); +} + +void ScCellValue::set( ScFormulaCell* pFormula ) +{ + clear(); + meType = CELLTYPE_FORMULA; + mpFormula = pFormula; +} + void ScCellValue::assign( const ScDocument& rDoc, const ScAddress& rPos ) { clear(); @@ -255,85 +291,87 @@ void ScCellValue::assign( const ScCellValue& rOther, ScDocument& rDestDoc, int n } } -void ScCellValue::assign( const ScBaseCell& rCell ) +void ScCellValue::commit( ScDocument& rDoc, const ScAddress& rPos ) const { - clear(); - - meType = rCell.GetCellType(); switch (meType) { case CELLTYPE_STRING: - mpString = new OUString(static_cast<const ScStringCell&>(rCell).GetString()); - break; - case CELLTYPE_EDIT: { - const EditTextObject* p = static_cast<const ScEditCell&>(rCell).GetData(); - if (p) - mpEditText = p->Clone(); + ScSetStringParam aParam; + aParam.setTextInput(); + rDoc.SetString(rPos, *mpString, &aParam); } break; + case CELLTYPE_EDIT: + rDoc.SetEditText(rPos, mpEditText->Clone()); + break; case CELLTYPE_VALUE: - mfValue = static_cast<const ScValueCell&>(rCell).GetValue(); + rDoc.SetValue(rPos, mfValue); break; case CELLTYPE_FORMULA: - mpFormula = static_cast<const ScFormulaCell&>(rCell).Clone(); + rDoc.SetFormulaCell(rPos, mpFormula->Clone()); break; default: - meType = CELLTYPE_NONE; // reset to empty. + rDoc.SetEmptyCell(rPos); } } -void ScCellValue::commit( ScDocument& rDoc, const ScAddress& rPos ) const +void ScCellValue::release( ScDocument& rDoc, const ScAddress& rPos ) { switch (meType) { case CELLTYPE_STRING: { + // Currently, string cannot be placed without copying. ScSetStringParam aParam; aParam.setTextInput(); rDoc.SetString(rPos, *mpString, &aParam); + delete mpString; } break; case CELLTYPE_EDIT: - rDoc.SetEditText(rPos, mpEditText->Clone()); + // Cell takes the ownership of the text object. + rDoc.SetEditText(rPos, mpEditText); break; case CELLTYPE_VALUE: rDoc.SetValue(rPos, mfValue); break; case CELLTYPE_FORMULA: - rDoc.SetFormulaCell(rPos, mpFormula->Clone()); + // This formula cell instance is directly placed in the document without copying. + rDoc.SetFormulaCell(rPos, mpFormula); break; default: rDoc.SetEmptyCell(rPos); } + + meType = CELLTYPE_NONE; + mfValue = 0.0; } -void ScCellValue::release( ScDocument& rDoc, const ScAddress& rPos ) +void ScCellValue::release( ScColumn& rColumn, SCROW nRow ) { switch (meType) { case CELLTYPE_STRING: { // Currently, string cannot be placed without copying. - ScSetStringParam aParam; - aParam.setTextInput(); - rDoc.SetString(rPos, *mpString, &aParam); + rColumn.SetRawString(nRow, *mpString); delete mpString; } break; case CELLTYPE_EDIT: // Cell takes the ownership of the text object. - rDoc.SetEditText(rPos, mpEditText); + rColumn.SetEditText(nRow, mpEditText); break; case CELLTYPE_VALUE: - rDoc.SetValue(rPos, mfValue); + rColumn.SetValue(nRow, mfValue); break; case CELLTYPE_FORMULA: // This formula cell instance is directly placed in the document without copying. - rDoc.SetFormulaCell(rPos, mpFormula); + rColumn.SetFormulaCell(nRow, mpFormula); break; default: - rDoc.SetEmptyCell(rPos); + rColumn.Delete(nRow); } meType = CELLTYPE_NONE; @@ -403,52 +441,81 @@ void ScRefCellValue::assign( ScDocument& rDoc, const ScAddress& rPos ) *this = rDoc.GetRefCellValue(rPos); } -void ScRefCellValue::assign( ScBaseCell& rCell ) +void ScRefCellValue::assign( const sc::CellStoreType::const_iterator& itPos, size_t nOffset ) { - clear(); + switch (itPos->type) + { + case sc::element_type_numeric: + // Numeric cell + mfValue = sc::numeric_block::at(*itPos->data, nOffset); + meType = CELLTYPE_VALUE; + break; + case sc::element_type_string: + // String cell + mpString = &sc::string_block::at(*itPos->data, nOffset); + meType = CELLTYPE_STRING; + break; + case sc::element_type_edittext: + // Edit cell + mpEditText = sc::edittext_block::at(*itPos->data, nOffset); + meType = CELLTYPE_EDIT; + break; + case sc::element_type_formula: + // Formula cell + mpFormula = sc::formula_block::at(*itPos->data, nOffset); + meType = CELLTYPE_FORMULA; + break; + default: + clear(); + } +} - meType = rCell.GetCellType(); +void ScRefCellValue::commit( ScDocument& rDoc, const ScAddress& rPos ) const +{ switch (meType) { case CELLTYPE_STRING: - mpString = static_cast<const ScStringCell&>(rCell).GetStringPtr(); + { + ScSetStringParam aParam; + aParam.setTextInput(); + rDoc.SetString(rPos, *mpString, &aParam); + } break; case CELLTYPE_EDIT: - mpEditText = static_cast<const ScEditCell&>(rCell).GetData(); + rDoc.SetEditText(rPos, ScEditUtil::Clone(*mpEditText, rDoc)); break; case CELLTYPE_VALUE: - mfValue = static_cast<const ScValueCell&>(rCell).GetValue(); + rDoc.SetValue(rPos, mfValue); break; case CELLTYPE_FORMULA: - mpFormula = static_cast<ScFormulaCell*>(&rCell); + rDoc.SetFormulaCell(rPos, new ScFormulaCell(*mpFormula, rDoc, rPos)); break; default: - meType = CELLTYPE_NONE; // reset to empty. + rDoc.SetEmptyCell(rPos); } } -void ScRefCellValue::commit( ScDocument& rDoc, const ScAddress& rPos ) const +void ScRefCellValue::commit( ScColumn& rColumn, SCROW nRow ) const { switch (meType) { case CELLTYPE_STRING: - { - ScSetStringParam aParam; - aParam.setTextInput(); - rDoc.SetString(rPos, *mpString, &aParam); - } + rColumn.SetRawString(nRow, *mpString); break; case CELLTYPE_EDIT: - rDoc.SetEditText(rPos, mpEditText->Clone()); + rColumn.SetEditText(nRow, ScEditUtil::Clone(*mpEditText, rColumn.GetDoc())); break; case CELLTYPE_VALUE: - rDoc.SetValue(rPos, mfValue); + rColumn.SetValue(nRow, mfValue); break; case CELLTYPE_FORMULA: - rDoc.SetFormulaCell(rPos, mpFormula->Clone()); + { + ScAddress aDestPos(rColumn.GetCol(), nRow, rColumn.GetTab()); + rColumn.SetFormulaCell(nRow, new ScFormulaCell(*mpFormula, rColumn.GetDoc(), aDestPos)); + } break; default: - rDoc.SetEmptyCell(rPos); + rColumn.Delete(nRow); } } diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx index 878255296911..1ac6471e9f74 100644 --- a/sc/source/core/data/column.cxx +++ b/sc/source/core/data/column.cxx @@ -35,28 +35,26 @@ #include "tokenarray.hxx" #include "cellform.hxx" #include "clipcontext.hxx" +#include "types.hxx" +#include "editutil.hxx" +#include "cellclonehandler.hxx" +#include "mtvcellfunc.hxx" +#include "columnspanset.hxx" +#include "scopetools.hxx" #include <svl/poolcach.hxx> #include <svl/zforlist.hxx> #include <editeng/scripttypeitem.hxx> +#include "editeng/fieldupdater.hxx" #include <cstring> #include <map> #include <cstdio> +#include <boost/scoped_ptr.hpp> using ::editeng::SvxBorderLine; using namespace formula; -bool ColEntry::Less::operator() (const ColEntry& r1, const ColEntry& r2) const -{ - return r1.nRow < r2.nRow; -} - -bool ColDoubleEntry::LessByPtr::operator() (const ColDoubleEntry* p1, const ColDoubleEntry* p2) const -{ - return p1->mnStart < p2->mnStart; -} - namespace { inline bool IsAmbiguousScriptNonZero( sal_uInt8 nScript ) @@ -75,25 +73,10 @@ ScNeededSizeOptions::ScNeededSizeOptions() : { } -std::vector<ColEntry>::iterator ScColumn::Search( SCROW nRow ) -{ - // Find first cell whose position is equal or greater than nRow. - ColEntry aBound; - aBound.nRow = nRow; - return std::lower_bound(maItems.begin(), maItems.end(), aBound, ColEntry::Less()); -} - -std::vector<ColEntry>::const_iterator ScColumn::Search( SCROW nRow ) const -{ - // Find first cell whose position is equal or greater than nRow. - ColEntry aBound; - aBound.nRow = nRow; - return std::lower_bound(maItems.begin(), maItems.end(), aBound, ColEntry::Less()); -} - ScColumn::ScColumn() : maCellTextAttrs(MAXROWCOUNT), maBroadcasters(MAXROWCOUNT), + maCells(MAXROWCOUNT), nCol( 0 ), pAttrArray( NULL ), pDocument( NULL ), @@ -126,135 +109,191 @@ SCsROW ScColumn::GetNextUnprotected( SCROW nRow, bool bUp ) const sal_uInt16 ScColumn::GetBlockMatrixEdges( SCROW nRow1, SCROW nRow2, sal_uInt16 nMask ) const { - // nothing:0, inside:1, bottom:2, left:4, top:8, right:16, open:32 - if ( maItems.empty() ) + using namespace sc; + + if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow1 > nRow2) return 0; - if ( nRow1 == nRow2 ) + + ScAddress aOrigin(ScAddress::INITIALIZE_INVALID); + + if (nRow1 == nRow2) { - SCSIZE nIndex; - if ( Search( nRow1, nIndex ) ) - { - ScBaseCell* pCell = maItems[nIndex].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA - && ((ScFormulaCell*)pCell)->GetMatrixFlag() ) - { - ScAddress aOrg( ScAddress::INITIALIZE_INVALID ); - return ((ScFormulaCell*)pCell)->GetMatrixEdge( aOrg ); - } - } - return 0; + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1); + if (aPos.first->type != sc::element_type_formula) + return 0; + + const ScFormulaCell* pCell = sc::formula_block::at(*aPos.first->data, aPos.second); + if (!pCell->GetMatrixFlag()) + return 0; + + return pCell->GetMatrixEdge(aOrigin); } - else + + bool bOpen = false; + sal_uInt16 nEdges = 0; + + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1); + sc::CellStoreType::const_iterator it = aPos.first; + size_t nOffset = aPos.second; + SCROW nRow = nRow1; + for (;it != maCells.end() && nRow <= nRow2; ++it, nOffset = 0) { - ScAddress aOrg( ScAddress::INITIALIZE_INVALID ); - bool bOpen = false; - sal_uInt16 nEdges = 0; - SCSIZE nIndex; - Search( nRow1, nIndex ); - while ( nIndex < maItems.size() && maItems[nIndex].nRow <= nRow2 ) + if (it->type != sc::element_type_formula) { - ScBaseCell* pCell = maItems[nIndex].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA - && ((ScFormulaCell*)pCell)->GetMatrixFlag() ) - { - nEdges = ((ScFormulaCell*)pCell)->GetMatrixEdge( aOrg ); - if ( nEdges ) - { - if ( nEdges & 8 ) - bOpen = true; // top edge opens, keep on looking - else if ( !bOpen ) - return nEdges | 32; // there's something that wasn't opened - else if ( nEdges & 1 ) - return nEdges; // inside - // (nMask & 16 and (4 and not 16)) or - // (nMask & 4 and (16 and not 4)) - if ( ((nMask & 16) && (nEdges & 4) && !(nEdges & 16)) - || ((nMask & 4) && (nEdges & 16) && !(nEdges & 4)) ) - return nEdges; // only left/right edge - if ( nEdges & 2 ) - bOpen = false; // bottom edge closes - } - } - nIndex++; + // Skip this block. + nRow += it->size - nOffset; + continue; + } + + size_t nRowsToRead = nRow2 - nRow + 1; + size_t nEnd = std::min(it->size, nRowsToRead); + sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data); + std::advance(itCell, nOffset); + for (size_t i = nOffset; i < nEnd; ++itCell, ++i) + { + // Loop inside the formula block. + const ScFormulaCell* pCell = *itCell; + if (!pCell->GetMatrixFlag()) + continue; + + nEdges = pCell->GetMatrixEdge(aOrigin); + if (!nEdges) + continue; + + if (nEdges & MatrixEdgeTop) + bOpen = true; // top edge opens, keep on looking + else if (!bOpen) + return nEdges | MatrixEdgeOpen; // there's something that wasn't opened + else if (nEdges & MatrixEdgeInside) + return nEdges; // inside + // (nMask & 16 and (4 and not 16)) or + // (nMask & 4 and (16 and not 4)) + if (((nMask & MatrixEdgeRight) && (nEdges & MatrixEdgeLeft) && !(nEdges & MatrixEdgeRight)) || + ((nMask & MatrixEdgeLeft) && (nEdges & MatrixEdgeRight) && !(nEdges & MatrixEdgeLeft))) + return nEdges; // only left/right edge + + if (nEdges & MatrixEdgeBottom) + bOpen = false; // bottom edge closes } - if ( bOpen ) - nEdges |= 32; // not closed, matrix continues - return nEdges; + + nRow += nEnd; } + if (bOpen) + nEdges |= MatrixEdgeOpen; // not closed, matrix continues + + return nEdges; } bool ScColumn::HasSelectionMatrixFragment(const ScMarkData& rMark) const { - if ( rMark.IsMultiMarked() ) + using namespace sc; + + if (!rMark.IsMultiMarked()) + return false; + + ScAddress aOrigin(ScAddress::INITIALIZE_INVALID); + ScAddress aCurOrigin = aOrigin; + + bool bOpen = false; + ScRangeList aRanges = rMark.GetMarkedRanges(); + for (size_t i = 0, n = aRanges.size(); i < n; ++i) { - bool bFound = false; + const ScRange& r = *aRanges[i]; + if (nTab < r.aStart.Tab() || r.aEnd.Tab() < nTab) + continue; - ScAddress aOrg( ScAddress::INITIALIZE_INVALID ); - ScAddress aCurOrg( ScAddress::INITIALIZE_INVALID ); - SCROW nTop, nBottom; - ScMarkArrayIter aMarkIter( rMark.GetArray()+nCol ); - while ( !bFound && aMarkIter.Next( nTop, nBottom ) ) + if (nCol < r.aStart.Col() || r.aEnd.Col() < nCol) + continue; + + SCROW nTop = r.aStart.Row(), nBottom = r.aEnd.Row(); + SCROW nRow = nTop; + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); + sc::CellStoreType::const_iterator it = aPos.first; + size_t nOffset = aPos.second; + + for (;it != maCells.end() && nRow <= nBottom; ++it, nOffset = 0) { - bool bOpen = false; - sal_uInt16 nEdges; - SCSIZE nIndex; - Search( nTop, nIndex ); - while ( !bFound && nIndex < maItems.size() && maItems[nIndex].nRow <= nBottom ) + if (it->type != sc::element_type_formula) + { + // Skip this block. + nRow += it->size - nOffset; + continue; + } + + // This is a formula cell block. + size_t nRowsToRead = nBottom - nRow + 1; + size_t nEnd = std::min(it->size, nRowsToRead); + sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data); + std::advance(itCell, nOffset); + for (size_t j = nOffset; j < nEnd; ++itCell, ++j) { - ScBaseCell* pCell = maItems[nIndex].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA - && ((ScFormulaCell*)pCell)->GetMatrixFlag() ) + // Loop inside the formula block. + const ScFormulaCell* pCell = *itCell; + if (!pCell->GetMatrixFlag()) + // cell is not a part of a matrix. + continue; + + sal_uInt16 nEdges = pCell->GetMatrixEdge(aOrigin); + if (!nEdges) + continue; + + bool bFound = false; + + if (nEdges & MatrixEdgeTop) + bOpen = true; // top edge opens, keep on looking + else if (!bOpen) + return true; // there's something that wasn't opened + else if (nEdges & MatrixEdgeInside) + bFound = true; // inside, all selected? + + if ((((nEdges & MatrixEdgeLeft) | MatrixEdgeRight) ^ ((nEdges & MatrixEdgeRight) | MatrixEdgeLeft))) + // either left or right, but not both. + bFound = true; // only left/right edge, all selected? + + if (nEdges & MatrixEdgeBottom) + bOpen = false; // bottom edge closes + + if (bFound) { - nEdges = ((ScFormulaCell*)pCell)->GetMatrixEdge( aOrg ); - if ( nEdges ) - { - if ( nEdges & 8 ) - bOpen = true; // top edge opens, keep on looking - else if ( !bOpen ) - return true; // there's something that wasn't opened - else if ( nEdges & 1 ) - bFound = true; // inside, all selected? - // (4 and not 16) or (16 and not 4) - if ( (((nEdges & 4) | 16) ^ ((nEdges & 16) | 4)) ) - bFound = true; // only left/right edge, all selected? - if ( nEdges & 2 ) - bOpen = false; // bottom edge closes - - if ( bFound ) - { // all selected? - if ( aCurOrg != aOrg ) - { // new matrix to check? - aCurOrg = aOrg; - ScFormulaCell* pFCell; - if ( ((ScFormulaCell*)pCell)->GetMatrixFlag() - == MM_REFERENCE ) - pFCell = pDocument->GetFormulaCell(aOrg); - else - pFCell = (ScFormulaCell*)pCell; - SCCOL nC; - SCROW nR; - pFCell->GetMatColsRows( nC, nR ); - ScRange aRange( aOrg, ScAddress( - aOrg.Col() + nC - 1, aOrg.Row() + nR - 1, - aOrg.Tab() ) ); - if ( rMark.IsAllMarked( aRange ) ) - bFound = false; - } - else - bFound = false; // done already - } + // Check if the matrix is inside the selection in its entirety. + // + // TODO: It's more efficient to skip the matrix range if + // it's within selection, to avoid checking it again and + // again. + + if (aCurOrigin != aOrigin) + { // new matrix to check? + aCurOrigin = aOrigin; + const ScFormulaCell* pFCell; + if (pCell->GetMatrixFlag() == MM_REFERENCE) + pFCell = pDocument->GetFormulaCell(aOrigin); + else + pFCell = pCell; + + SCCOL nC; + SCROW nR; + pFCell->GetMatColsRows(nC, nR); + ScRange aRange(aOrigin, ScAddress(aOrigin.Col()+nC-1, aOrigin.Row()+nR-1, aOrigin.Tab())); + if (rMark.IsAllMarked(aRange)) + bFound = false; } + else + bFound = false; // done already } - nIndex++; + + if (bFound) + return true; } - if ( bOpen ) - return true; + + nRow += nEnd; } - return bFound; } - else - return false; + + if (bOpen) + return true; + + return false; } @@ -707,385 +746,484 @@ void ScColumn::ApplyAttr( SCROW nRow, const SfxPoolItem& rAttr ) delete pTemp; } -bool ScColumn::Search( SCROW nRow, SCSIZE& nIndex ) const +ScDocument& ScColumn::GetDoc() { - if ( maItems.empty() ) - { - nIndex = 0; - return false; - } - SCROW nMinRow = maItems[0].nRow; - if ( nRow <= nMinRow ) - { - nIndex = 0; - return nRow == nMinRow; - } - SCROW nMaxRow = maItems.back().nRow; - if ( nRow >= nMaxRow ) - { - if ( nRow == nMaxRow ) - { - nIndex = maItems.size() - 1; - return true; - } - else - { - nIndex = maItems.size(); - return false; - } - } + return *pDocument; +} - long nOldLo, nOldHi; - long nLo = nOldLo = 0; - long nHi = nOldHi = std::min(static_cast<long>(maItems.size())-1, static_cast<long>(nRow) ); - long i = 0; - bool bFound = false; - // quite continuous distribution? => interpolating search - bool bInterpol = (static_cast<SCSIZE>(nMaxRow - nMinRow) < maItems.size() * 2); - SCROW nR; +const ScDocument& ScColumn::GetDoc() const +{ + return *pDocument; +} - while ( !bFound && nLo <= nHi ) - { - if ( !bInterpol || nHi - nLo < 3 ) - i = (nLo+nHi) / 2; // no effort, no division by zero - else - { // interpolating search - long nLoRow = maItems[nLo].nRow; // no unsigned underflow upon substraction - i = nLo + (long)((long)(nRow - nLoRow) * (nHi - nLo) - / (maItems[nHi].nRow - nLoRow)); - if ( i < 0 || static_cast<SCSIZE>(i) >= maItems.size() ) - { // oops ... - i = (nLo+nHi) / 2; - bInterpol = false; - } - } - nR = maItems[i].nRow; - if ( nR < nRow ) - { - nLo = i+1; - if ( bInterpol ) - { - if ( nLo <= nOldLo ) - bInterpol = false; - else - nOldLo = nLo; - } - } - else - { - if ( nR > nRow ) - { - nHi = i-1; - if ( bInterpol ) - { - if ( nHi >= nOldHi ) - bInterpol = false; - else - nOldHi = nHi; - } - } - else - bFound = true; - } - } - if (bFound) - nIndex = static_cast<SCSIZE>(i); - else - nIndex = static_cast<SCSIZE>(nLo); // rear index - return bFound; +ScRefCellValue ScColumn::GetCellValue( SCROW nRow ) const +{ + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); + if (aPos.first == maCells.end()) + return ScRefCellValue(); + + return GetCellValue(aPos.first, aPos.second); } -ScBaseCell* ScColumn::GetCell( SCROW nRow ) const +ScRefCellValue ScColumn::GetCellValue( sc::CellStoreType::const_iterator& itPos, SCROW nRow ) const { - SCSIZE nIndex; - if (Search(nRow, nIndex)) - return maItems[nIndex].pCell; - return NULL; + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow); + itPos = aPos.first; + if (aPos.first == maCells.end()) + return ScRefCellValue(); + + return GetCellValue(itPos, aPos.second); } -ScRefCellValue ScColumn::GetCellValue( SCROW nRow ) const +ScRefCellValue ScColumn::GetCellValue( sc::CellStoreType::const_iterator& itPos, size_t nOffset ) const { - ScRefCellValue aVal; - SCSIZE nIndex; - if (Search(nRow, nIndex)) - aVal.assign(*maItems[nIndex].pCell); + ScRefCellValue aVal; // Defaults to empty cell. + switch (itPos->type) + { + case sc::element_type_numeric: + // Numeric cell + aVal.mfValue = sc::numeric_block::at(*itPos->data, nOffset); + aVal.meType = CELLTYPE_VALUE; + break; + case sc::element_type_string: + // String cell + aVal.mpString = &sc::string_block::at(*itPos->data, nOffset); + aVal.meType = CELLTYPE_STRING; + break; + case sc::element_type_edittext: + // Edit cell + aVal.mpEditText = sc::edittext_block::at(*itPos->data, nOffset); + aVal.meType = CELLTYPE_EDIT; + break; + case sc::element_type_formula: + // Formula cell + aVal.mpFormula = sc::formula_block::at(*itPos->data, nOffset); + aVal.meType = CELLTYPE_FORMULA; + break; + default: + ; + } return aVal; } -void ScColumn::ReserveSize( SCSIZE nSize ) +void ScColumn::ReleaseCellValue( sc::CellStoreType::iterator& itPos, SCROW nRow, ScCellValue& rVal ) +{ + std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(itPos, nRow); + itPos = aPos.first; // Store it for the next iteration. + if (aPos.first == maCells.end()) + return; + + switch (itPos->type) + { + case sc::element_type_numeric: + // Numeric cell + itPos = maCells.release(itPos, nRow, rVal.mfValue); + rVal.meType = CELLTYPE_VALUE; + break; + case sc::element_type_string: + { + // Make a copy until we implement shared strings... + OUString aStr; + itPos = maCells.release(itPos, nRow, aStr); + rVal.mpString = new OUString(aStr); + rVal.meType = CELLTYPE_STRING; + } + break; + case sc::element_type_edittext: + itPos = maCells.release(itPos, nRow, rVal.mpEditText); + rVal.meType = CELLTYPE_EDIT; + break; + case sc::element_type_formula: + itPos = maCells.release(itPos, nRow, rVal.mpFormula); + rVal.meType = CELLTYPE_FORMULA; + break; + default: + ; + } +} + +namespace { + +ScFormulaCell* cloneFormulaCell(ScDocument* pDoc, const ScAddress& rNewPos, ScFormulaCell& rOldCell) { - if (nSize > sal::static_int_cast<SCSIZE>(MAXROWCOUNT)) - nSize = MAXROWCOUNT; - if (nSize < maItems.size()) - nSize = maItems.size(); + ScFormulaCell* pNew = new ScFormulaCell(rOldCell, *pDoc, rNewPos, SC_CLONECELL_ADJUST3DREL); + rOldCell.EndListeningTo(pDoc); + pNew->StartListeningTo(pDoc); + pNew->SetDirty(); + return pNew; +} - maItems.reserve(nSize); } -// SwapRow for sorting void ScColumn::SwapRow(SCROW nRow1, SCROW nRow2) { + typedef std::pair<sc::CellStoreType::iterator,size_t> CellPosType; + if (nRow1 == nRow2) // Nothing to swap. return; - /* Simple swap of cell pointers does not work if broadcasters exist (crash - if cell broadcasts directly or indirectly to itself). While swapping - the cells, broadcasters have to remain at old positions! */ + // Ensure that nRow1 < nRow2. + if (nRow2 < nRow1) + std::swap(nRow1, nRow2); - ScBaseCell* pCell1 = 0; - SCSIZE nIndex1; - if ( Search( nRow1, nIndex1 ) ) - pCell1 = maItems[nIndex1].pCell; + // Broadcasters (if exist) should NOT be swapped. - ScBaseCell* pCell2 = 0; - SCSIZE nIndex2; - if ( Search( nRow2, nIndex2 ) ) - pCell2 = maItems[nIndex2].pCell; - - // no cells found, nothing to do - if ( !pCell1 && !pCell2 ) - return ; - - // swap variables if first cell is empty, to save some code below - if ( !pCell1 ) - { - ::std::swap( nRow1, nRow2 ); - ::std::swap( nIndex1, nIndex2 ); - ::std::swap( pCell1, pCell2 ); - } - - // from here: first cell (pCell1, nIndex1) exists always + CellPosType aPos1 = maCells.position(nRow1); + if (aPos1.first == maCells.end()) + return; - ScAddress aPos1( nCol, nRow1, nTab ); - ScAddress aPos2( nCol, nRow2, nTab ); + CellPosType aPos2 = maCells.position(aPos1.first, nRow2); + if (aPos2.first == maCells.end()) + return; - CellType eType1 = pCell1->GetCellType(); - CellType eType2 = pCell2 ? pCell2->GetCellType() : CELLTYPE_NONE; + std::vector<SCROW> aRows; + aRows.reserve(2); + aRows.push_back(nRow1); + aRows.push_back(nRow2); - ScFormulaCell* pFmlaCell1 = (eType1 == CELLTYPE_FORMULA) ? static_cast< ScFormulaCell* >( pCell1 ) : 0; - ScFormulaCell* pFmlaCell2 = (eType2 == CELLTYPE_FORMULA) ? static_cast< ScFormulaCell* >( pCell2 ) : 0; + sc::CellStoreType::iterator it1 = aPos1.first, it2 = aPos2.first; - // simple swap if no formula cells present - if ( !pFmlaCell1 && !pFmlaCell2 ) + if (it1->type == it2->type) { - if ( pCell2 ) + // Both positions are of the same type. Do a simple value swap. + switch (it1->type) { - /* Both cells exist, no formula cells involved, a simple swap can - be performed (but keep broadcasters and notes at old position). */ - maItems[nIndex1].pCell = pCell2; - maItems[nIndex2].pCell = pCell1; - - // Swap text width values and script types. - sc::CellTextAttr aVal1 = maCellTextAttrs.get<sc::CellTextAttr>(nRow1); - sc::CellTextAttr aVal2 = maCellTextAttrs.get<sc::CellTextAttr>(nRow2); - maCellTextAttrs.set(nRow1, aVal2); - maCellTextAttrs.set(nRow2, aVal1); - - CellStorageModified(); + case sc::element_type_empty: + // Both are empty. Nothing to swap. + return; + case sc::element_type_numeric: + std::swap( + sc::numeric_block::at(*it1->data, aPos1.second), + sc::numeric_block::at(*it2->data, aPos2.second)); + break; + case sc::element_type_string: + std::swap( + sc::string_block::at(*it1->data, aPos1.second), + sc::string_block::at(*it2->data, aPos2.second)); + break; + case sc::element_type_edittext: + std::swap( + sc::edittext_block::at(*it1->data, aPos1.second), + sc::edittext_block::at(*it2->data, aPos2.second)); + break; + case sc::element_type_formula: + { + // Swapping of formula cells involve adjustment of references wrt their positions. + sc::formula_block::iterator itf1 = sc::formula_block::begin(*it1->data); + sc::formula_block::iterator itf2 = sc::formula_block::begin(*it2->data); + std::advance(itf1, aPos1.second); + std::advance(itf2, aPos2.second); + + // TODO: Find out a way to adjust references without cloning new instances. + boost::scoped_ptr<ScFormulaCell> pOld1(*itf1); + boost::scoped_ptr<ScFormulaCell> pOld2(*itf2); + ScFormulaCell* pNew1 = cloneFormulaCell(pDocument, ScAddress(nCol, nRow1, nTab), *pOld2); + ScFormulaCell* pNew2 = cloneFormulaCell(pDocument, ScAddress(nCol, nRow2, nTab), *pOld1); + *itf1 = pNew1; + *itf2 = pNew2; + } + break; + default: + ; } - else - { - // Only cell 1 exists; cell 2 is empty. Move cell 1 from to row - // 2. - // remove ColEntry at old position - maItems.erase( maItems.begin() + nIndex1 ); - maCellTextAttrs.set_empty(nRow1, nRow1); + SwapCellTextAttrs(nRow1, nRow2); + CellStorageModified(); + BroadcastCells(aRows); + return; + } + + // The two cells are of different types. - // Empty text width at the cell 1 position. For now, we don't - // transfer the old value to the cell 2 position since Insert() is - // quite complicated. - CellStorageModified(); + sc::CellStoreType::const_iterator cit = it1; + ScRefCellValue aCell1 = GetCellValue(cit, nRow1); + cit = it2; + ScRefCellValue aCell2 = GetCellValue(cit, nRow2); - // insert ColEntry at new position. - Insert( nRow2, pCell1 ); + if (aCell1.meType == CELLTYPE_NONE) + { + // cell 1 is empty and cell 2 is not. + switch (aCell2.meType) + { + case CELLTYPE_VALUE: + it1 = maCells.set(it1, nRow1, aCell2.mfValue); // it2 becomes invalid. + maCells.set_empty(it1, nRow2, nRow2); + break; + case CELLTYPE_STRING: + it1 = maCells.set(it1, nRow1, *aCell2.mpString); + maCells.set_empty(it1, nRow2, nRow2); + break; + case CELLTYPE_EDIT: + { + it1 = maCells.set(it1, nRow1, aCell2.mpEditText); + EditTextObject* p; + maCells.release(it1, nRow2, p); + } + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell* pNew = cloneFormulaCell(pDocument, ScAddress(nCol, nRow1, nTab), *aCell2.mpFormula); + it1 = maCells.set(it1, nRow1, pNew); + maCells.set_empty(it1, nRow2, nRow2); // original formula cell gets deleted. + } + break; + default: + ; } + SwapCellTextAttrs(nRow1, nRow2); + CellStorageModified(); + BroadcastCells(aRows); return; } - // from here: at least one of the cells is a formula cell + if (aCell2.meType == CELLTYPE_NONE) + { + // cell 1 is not empty and cell 2 is empty. + switch (aCell1.meType) + { + case CELLTYPE_VALUE: + // Value is copied in Cell1. + it1 = maCells.set_empty(it1, nRow1, nRow1); + maCells.set(it1, nRow2, aCell1.mfValue); + break; + case CELLTYPE_STRING: + { + OUString aStr = *aCell1.mpString; // make a copy. + it1 = maCells.set_empty(it1, nRow1, nRow1); // original string is gone. + maCells.set(it1, nRow2, aStr); + } + break; + case CELLTYPE_EDIT: + { + EditTextObject* p; + it1 = maCells.release(it1, nRow1, p); + maCells.set(it1, nRow2, p); + } + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell* pNew = cloneFormulaCell(pDocument, ScAddress(nCol, nRow2, nTab), *aCell1.mpFormula); + it1 = maCells.set_empty(it1, nRow1, nRow1); // original formula cell is gone. + maCells.set(it1, nRow2, pNew); + } + break; + default: + ; + } - /* Never move any array formulas. Disabling sort if parts of array - formulas are contained is done at UI. */ - if ( (pFmlaCell1 && (pFmlaCell1->GetMatrixFlag() != 0)) || (pFmlaCell2 && (pFmlaCell2->GetMatrixFlag() != 0)) ) + SwapCellTextAttrs(nRow1, nRow2); + CellStorageModified(); + BroadcastCells(aRows); return; + } - // do not swap, if formulas are equal - if ( pFmlaCell1 && pFmlaCell2 ) + // Neither cells are empty, and they are of different types. + switch (aCell1.meType) { - ScTokenArray* pCode1 = pFmlaCell1->GetCode(); - ScTokenArray* pCode2 = pFmlaCell2->GetCode(); - - if (pCode1->GetLen() == pCode2->GetLen()) // not-UPN + case CELLTYPE_VALUE: { - bool bEqual = true; - sal_uInt16 nLen = pCode1->GetLen(); - FormulaToken** ppToken1 = pCode1->GetArray(); - FormulaToken** ppToken2 = pCode2->GetArray(); - for (sal_uInt16 i=0; i<nLen; i++) + switch (aCell2.meType) { - if ( !ppToken1[i]->TextEqual(*(ppToken2[i])) || - ppToken1[i]->Is3DRef() || ppToken2[i]->Is3DRef() ) + case CELLTYPE_STRING: + it1 = maCells.set(it1, nRow1, *aCell2.mpString); + break; + case CELLTYPE_EDIT: + { + it1 = maCells.set(it1, nRow1, aCell2.mpEditText); + EditTextObject* p; + it1 = maCells.release(it1, nRow2, p); + } + break; + case CELLTYPE_FORMULA: { - bEqual = false; - break; + ScFormulaCell* pNew = cloneFormulaCell(pDocument, ScAddress(nCol, nRow1, nTab), *aCell2.mpFormula); + it1 = maCells.set(it1, nRow1, pNew); + // The old formula cell will get overwritten below. } + break; + default: + ; } - // do not swap formula cells with equal formulas - if (bEqual) + maCells.set(it1, nRow2, aCell1.mfValue); + } + break; + case CELLTYPE_STRING: + { + OUString aStr = *aCell1.mpString; // make a copy. + switch (aCell2.meType) { - return; + case CELLTYPE_VALUE: + it1 = maCells.set(it1, nRow1, aCell2.mfValue); + break; + case CELLTYPE_EDIT: + { + it1 = maCells.set(it1, nRow1, aCell2.mpEditText); + EditTextObject* p; + it1 = maCells.release(it1, nRow2, p); // prevent it being overwritten. + } + break; + case CELLTYPE_FORMULA: + { + // cell 1 - string, cell 2 - formula + ScFormulaCell* pNew = cloneFormulaCell(pDocument, ScAddress(nCol, nRow1, nTab), *aCell2.mpFormula); + it1 = maCells.set(it1, nRow1, pNew); + // Old formula cell will get overwritten below. + } + break; + default: + ; } + + maCells.set(it1, nRow2, aStr); } - } + break; + case CELLTYPE_EDIT: + { + EditTextObject* p; + it1 = maCells.release(it1, nRow1, p); - /* Create clone of pCell1 at position of pCell2 (pCell1 exists always, see - variable swapping above).*/ - ScBaseCell* pNew2 = pCell1->Clone( *pDocument, aPos2, SC_CLONECELL_ADJUST3DREL ); + switch (aCell2.meType) + { + case CELLTYPE_VALUE: + it1 = maCells.set(it1, nRow1, aCell2.mfValue); + break; + case CELLTYPE_STRING: + it1 = maCells.set(it1, nRow1, *aCell2.mpString); + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell* pNew = cloneFormulaCell(pDocument, ScAddress(nCol, nRow1, nTab), *aCell2.mpFormula); + it1 = maCells.set(it1, nRow1, pNew); + // Old formula cell will get overwritten below. + } + break; + default: + ; + } - /* Create clone of pCell2 at position of pCell1. Do not clone the note, - but move pointer of old note to new cell. */ - ScBaseCell* pNew1 = 0; - if ( pCell2 ) - { - pNew1 = pCell2->Clone( *pDocument, aPos1, SC_CLONECELL_ADJUST3DREL ); - } + maCells.set(it1, nRow2, aCell1.mpEditText); + } + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell* pNew = cloneFormulaCell(pDocument, ScAddress(nCol, nRow2, nTab), *aCell1.mpFormula); + switch (aCell2.meType) + { + case CELLTYPE_VALUE: + it1 = maCells.set(it1, nRow1, aCell2.mfValue); + break; + case CELLTYPE_STRING: + it1 = maCells.set(it1, nRow1, *aCell2.mpString); + break; + case CELLTYPE_EDIT: + { + it1 = maCells.set(it1, nRow1, aCell2.mpEditText); + EditTextObject* p; + it1 = maCells.release(it1, nRow2, p); + } + break; + default: + ; + } - /* Insert the new cells. Old cell has to be deleted, if there is no new - cell (call to Insert deletes old cell by itself). */ - if ( !pNew1 ) - Delete( nRow1 ); // deletes pCell1 - else - Insert( nRow1, pNew1 ); // deletes pCell1, inserts pNew1 + maCells.set(it1, nRow2, pNew); + } + break; + default: + ; + } - if ( pCell2 && !pNew2 ) - Delete( nRow2 ); // deletes pCell2 - else if ( pNew2 ) - Insert( nRow2, pNew2 ); // deletes pCell2 (if existing), inserts pNew2 + SwapCellTextAttrs(nRow1, nRow2); + CellStorageModified(); + BroadcastCells(aRows); } +namespace { -void ScColumn::SwapCell( SCROW nRow, ScColumn& rCol) +/** + * Adjust references in formula cell with respect to column-wise relocation. + */ +void updateRefInFormulaCell( ScFormulaCell& rCell, SCCOL nCol, SCTAB nTab, SCCOL nColDiff ) { - ScBaseCell* pCell1 = 0; - SCSIZE nIndex1; - if ( Search( nRow, nIndex1 ) ) - pCell1 = maItems[nIndex1].pCell; - - ScBaseCell* pCell2 = 0; - SCSIZE nIndex2; - if ( rCol.Search( nRow, nIndex2 ) ) - pCell2 = rCol.maItems[nIndex2].pCell; - - // reverse call if own cell is missing (ensures own existing cell in following code) - if( !pCell1 ) - { - if( pCell2 ) - rCol.SwapCell( nRow, *this ); - return; - } - - // from here: own cell (pCell1, nIndex1) exists always + ScRange aRange(ScAddress(nCol, 0, nTab), ScAddress(nCol, MAXROW, nTab)); + rCell.aPos.SetCol(nCol); + rCell.UpdateReference(URM_MOVE, aRange, nColDiff, 0, 0); +} - ScFormulaCell* pFmlaCell1 = (pCell1->GetCellType() == CELLTYPE_FORMULA) ? static_cast< ScFormulaCell* >( pCell1 ) : 0; - ScFormulaCell* pFmlaCell2 = (pCell2 && (pCell2->GetCellType() == CELLTYPE_FORMULA)) ? static_cast< ScFormulaCell* >( pCell2 ) : 0; +} - if ( pCell2 ) - { - // Both cell 1 and cell 2 exist. Swap them. +void ScColumn::SwapCell( SCROW nRow, ScColumn& rCol) +{ + ScFormulaCell* pCell1 = maCells.get<ScFormulaCell*>(nRow); + ScFormulaCell* pCell2 = rCol.maCells.get<ScFormulaCell*>(nRow); + if (pCell1) + updateRefInFormulaCell(*pCell1, rCol.nCol, nTab, rCol.nCol - nCol); + if (pCell2) + updateRefInFormulaCell(*pCell2, nCol, nTab, nCol - rCol.nCol); - maItems[nIndex1].pCell = pCell2; - rCol.maItems[nIndex2].pCell = pCell1; + maCells.swap(nRow, nRow, rCol.maCells, nRow); + maCellTextAttrs.swap(nRow, nRow, rCol.maCellTextAttrs, nRow); - // update references - SCsCOL dx = rCol.nCol - nCol; - if ( pFmlaCell1 ) - { - ScRange aRange( ScAddress( rCol.nCol, 0, nTab ), - ScAddress( rCol.nCol, MAXROW, nTab ) ); - pFmlaCell1->aPos.SetCol( rCol.nCol ); - pFmlaCell1->UpdateReference(URM_MOVE, aRange, dx, 0, 0); - } - if ( pFmlaCell2 ) - { - ScRange aRange( ScAddress( nCol, 0, nTab ), - ScAddress( nCol, MAXROW, nTab ) ); - pFmlaCell2->aPos.SetCol( nCol ); - pFmlaCell2->UpdateReference(URM_MOVE, aRange, -dx, 0, 0); - } + CellStorageModified(); + rCol.CellStorageModified(); +} - // Swap text width values and script types. - sc::CellTextAttr aVal1 = maCellTextAttrs.get<sc::CellTextAttr>(nRow); - sc::CellTextAttr aVal2 = rCol.maCellTextAttrs.get<sc::CellTextAttr>(nRow); - maCellTextAttrs.set(nRow, aVal2); - rCol.maCellTextAttrs.set(nRow, aVal1); - CellStorageModified(); - rCol.CellStorageModified(); - } - else - { - // Cell 1 exists but cell 2 isn't. - maItems.erase(maItems.begin() + nIndex1); +bool ScColumn::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const +{ + if (IsEmpty()) + return true; - // update references - SCsCOL dx = rCol.nCol - nCol; - if ( pFmlaCell1 ) - { - ScRange aRange( ScAddress( rCol.nCol, 0, nTab ), - ScAddress( rCol.nCol, MAXROW, nTab ) ); - pFmlaCell1->aPos.SetCol( rCol.nCol ); - pFmlaCell1->UpdateReference(URM_MOVE, aRange, dx, 0, 0); - } + // Return false if we have any non-empty cells between nStartRow and nEndRow inclusive. + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it->type != sc::element_type_empty) + return false; - maCellTextAttrs.set_empty(nRow, nRow); - CellStorageModified(); + // Get the length of the remaining empty segment. + size_t nLen = it->size - aPos.second; + SCROW nNextNonEmptyRow = nStartRow + nLen; + if (nNextNonEmptyRow <= nEndRow) + return false; - // We don't transfer the text width to the destination column because - // of Insert()'s complexity. + // AttrArray only looks for merged cells - // insert - rCol.Insert(nRow, pCell1); - } + return pAttrArray ? pAttrArray->TestInsertCol(nStartRow, nEndRow) : true; } -bool ScColumn::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const +bool ScColumn::TestInsertRow( SCROW nStartRow, SCSIZE nSize ) const { - if (!IsEmpty()) + // AttrArray only looks for merged cells { - bool bTest = true; - if ( !maItems.empty() ) - for (SCSIZE i=0; (i<maItems.size()) && bTest; i++) - bTest = (maItems[i].nRow < nStartRow) || (maItems[i].nRow > nEndRow); - - // AttrArray only looks for merged cells - - if ((bTest) && (pAttrArray)) - bTest = pAttrArray->TestInsertCol(nStartRow, nEndRow); + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it->type == sc::element_type_empty && maCells.block_size() == 1) + // The entire cell array is empty. + return pAttrArray->TestInsertRow(nSize); + } - //! consider the removed Attribute at Undo + // See if there would be any non-empty cell that gets pushed out. - return bTest; - } - else - return true; -} + // Find the position of the last non-empty cell below nStartRow. + size_t nLastNonEmptyRow = MAXROW; + sc::CellStoreType::const_reverse_iterator it = maCells.rbegin(), itEnd = maCells.rend(); + if (it->type == sc::element_type_empty) + nLastNonEmptyRow -= it->size; + if (nLastNonEmptyRow < static_cast<size_t>(nStartRow)) + // No cells would get pushed out. + return pAttrArray->TestInsertRow(nSize); -bool ScColumn::TestInsertRow( SCSIZE nSize ) const -{ - // AttrArray only looks for merged cells + if (nLastNonEmptyRow + nSize > static_cast<size_t>(MAXROW)) + // At least one cell would get pushed out. Not good. + return false; - if ( !maItems.empty() ) - return ( nSize <= sal::static_int_cast<SCSIZE>(MAXROW) && - maItems[maItems.size()-1].nRow <= MAXROW-(SCROW)nSize && pAttrArray->TestInsertRow( nSize ) ); - else - return pAttrArray->TestInsertRow( nSize ); + return pAttrArray->TestInsertRow(nSize); } @@ -1096,93 +1234,64 @@ void ScColumn::InsertRow( SCROW nStartRow, SCSIZE nSize ) maBroadcasters.insert_empty(nStartRow, nSize); maBroadcasters.resize(MAXROWCOUNT); - if ( maItems.empty() ) - return; + maCellTextAttrs.insert_empty(nStartRow, nSize); + maCellTextAttrs.resize(MAXROWCOUNT); - SCSIZE i; - Search( nStartRow, i ); - if ( i >= maItems.size() ) - return ; + maCells.insert_empty(nStartRow, nSize); + maCells.resize(MAXROWCOUNT); bool bOldAutoCalc = pDocument->GetAutoCalc(); pDocument->SetAutoCalc( false ); // avoid recalculations - SCSIZE nNewCount = maItems.size(); - bool bCountChanged = false; - 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 ) - { - SCROW nLastBroadcast = MAXROW+1; - for ( ; i < maItems.size(); i++) - { - SCROW nOldRow = maItems[i].nRow; - // Change source broadcaster - if ( nLastBroadcast != nOldRow ) - { // Do not broadcast a direct sequence twice - rAddress.SetRow( nOldRow ); - pDocument->AreaBroadcast( aHint ); - } - SCROW nNewRow = (maItems[i].nRow += nSize); - // Change target broadcaster - rAddress.SetRow( nNewRow ); - pDocument->AreaBroadcast( aHint ); - nLastBroadcast = nNewRow; - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - ((ScFormulaCell*)pCell)->aPos.SetRow( nNewRow ); - if ( nNewRow > MAXROW && !bCountChanged ) - { - nNewCount = i; - bCountChanged = true; - } - } - } - else + // Get the position of the first affected cell. + std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nStartRow+nSize); + sc::CellStoreType::iterator it = aPos.first; + + // Update the positions of all affected formula cells. + if (it->type == sc::element_type_formula) { - rAddress.SetRow( maItems[i].nRow ); - ScRange aRange( rAddress ); - for ( ; i < maItems.size(); i++) + sc::formula_block::iterator itf = sc::formula_block::begin(*it->data); + sc::formula_block::iterator itfEnd = sc::formula_block::end(*it->data); + std::advance(itf, aPos.second); + for (; itf != itfEnd; ++itf) { - SCROW nNewRow = (maItems[i].nRow += nSize); - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - ((ScFormulaCell*)pCell)->aPos.SetRow( nNewRow ); - if ( nNewRow > MAXROW && !bCountChanged ) - { - nNewCount = i; - bCountChanged = true; - aRange.aEnd.SetRow( MAXROW ); - } + ScFormulaCell* pCell = *itf; + pCell->aPos.IncRow(nSize); } - if ( !bCountChanged ) - aRange.aEnd.SetRow( maItems.back().nRow ); - pDocument->AreaBroadcastInRange( aRange, aHint ); } - if (bCountChanged) + for (++it; it != maCells.end(); ++it) { - // Some cells in the lower part of the cell array have been pushed out - // beyond MAXROW. Delete them. - std::vector<ColEntry>::iterator itBeg = maItems.begin(); - std::advance(itBeg, nNewCount); - for (std::vector<ColEntry>::iterator it = itBeg; it != maItems.end(); ++it) - it->pCell->Delete(); + if (it->type != sc::element_type_formula) + continue; - maItems.erase(itBeg, maItems.end()); + sc::formula_block::iterator itf = sc::formula_block::begin(*it->data); + sc::formula_block::iterator itfEnd = sc::formula_block::end(*it->data); + for (; itf != itfEnd; ++itf) + { + ScFormulaCell* pCell = *itf; + pCell->aPos.IncRow(nSize); + } } + CellStorageModified(); + pDocument->SetAutoCalc( bOldAutoCalc ); - maCellTextAttrs.insert_empty(nStartRow, nSize); - maCellTextAttrs.resize(MAXROWCOUNT); - CellStorageModified(); + // We *probably* don't need to broadcast here since the parent call seems + // to take care of it. } +namespace { + +class CopyToClipHandler : public sc::CellBlockCloneHandler +{ +public: + CopyToClipHandler(ScDocument& rSrcDoc, ScDocument& rDestDoc, sc::CellStoreType& rDestCellStore) : + sc::CellBlockCloneHandler(rSrcDoc, rDestDoc, rDestCellStore) {} +}; + +} void ScColumn::CopyToClip( sc::CopyToClipContext& rCxt, SCROW nRow1, SCROW nRow2, ScColumn& rColumn ) const @@ -1190,181 +1299,410 @@ void ScColumn::CopyToClip( pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray, rCxt.isKeepScenarioFlags() ? (SC_MF_ALL & ~SC_MF_SCENARIO) : SC_MF_ALL ); - SCSIZE i; - SCSIZE nBlockCount = 0; - SCSIZE nStartIndex = 0, nEndIndex = 0; - for (i = 0; i < maItems.size(); i++) - if ((maItems[i].nRow >= nRow1) && (maItems[i].nRow <= nRow2)) - { - if (!nBlockCount) - nStartIndex = i; - nEndIndex = i; - ++nBlockCount; + CopyToClipHandler aHdl(*pDocument, *rColumn.pDocument, rColumn.maCells); + CopyCellsInRangeToColumn(NULL, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol), aHdl, nRow1, nRow2, rColumn); +} + +void ScColumn::CopyStaticToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol) +{ + if (nRow1 > nRow2) + return; + + sc::ColumnBlockPosition aDestPos; + CopyCellTextAttrsToDocument(nRow1, nRow2, rDestCol); + + // First, clear the destination column for the specified row range. + rDestCol.maCells.set_empty(nRow1, nRow2); - // put interpreted cells in the clipboard in order to create other formats (text, graphics, ...) + aDestPos.miCellPos = rDestCol.maCells.begin(); - if ( maItems[i].pCell->GetCellType() == CELLTYPE_FORMULA ) + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1); + sc::CellStoreType::const_iterator it = 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) + { + 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; + } + + switch (it->type) + { + 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); + aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, itData, itDataEnd); + } + break; + case sc::element_type_string: { - ScFormulaCell* pFCell = (ScFormulaCell*) maItems[i].pCell; - if (pFCell->GetDirty() && pDocument->GetAutoCalc()) - pFCell->Interpret(); + 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); + aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, 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); + + // Convert to simple strings. + std::vector<OUString> aConverted; + aConverted.reserve(nDataSize); + for (; itData != itDataEnd; ++itData) + { + const EditTextObject& rObj = **itData; + aConverted.push_back(ScEditUtil::GetString(rObj)); + } + aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, aConverted.begin(), aConverted.end()); + } + 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); + + // Interpret and convert to raw values. + for (SCROW i = 0; itData != itDataEnd; ++itData, ++i) + { + SCROW nRow = nCurRow + i; + + ScFormulaCell& rFC = const_cast<ScFormulaCell&>(**itData); + if (rFC.GetDirty() && pDocument->GetAutoCalc()) + rFC.Interpret(); + + if (rFC.GetErrCode()) + // Skip cells with error. + break; + + if (rFC.IsValue()) + aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nRow, rFC.GetValue()); + else + aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nRow, rFC.GetString()); + } + } + break; + default: + ; } - if (nBlockCount) - { - rColumn.ReserveSize(rColumn.GetCellCount() + nBlockCount); - ScAddress aOwnPos( nCol, 0, nTab ); - ScAddress aDestPos( rColumn.nCol, 0, rColumn.nTab ); - for (i = nStartIndex; i <= nEndIndex; i++) + if (bLastBlock) + break; + } +} + +void ScColumn::CopyCellToDocument( SCROW nSrcRow, SCROW nDestRow, ScColumn& rDestCol ) +{ + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nSrcRow); + sc::CellStoreType::const_iterator it = aPos.first; + bool bSet = true; + switch (it->type) + { + case sc::element_type_numeric: + rDestCol.maCells.set(nDestRow, sc::numeric_block::at(*it->data, aPos.second)); + break; + case sc::element_type_string: + rDestCol.maCells.set(nDestRow, sc::string_block::at(*it->data, aPos.second)); + break; + case sc::element_type_edittext: { - aOwnPos.SetRow( maItems[i].nRow ); - aDestPos.SetRow( maItems[i].nRow ); - ScBaseCell* pNewCell = maItems[i].pCell->Clone( *rColumn.pDocument, aDestPos, SC_CLONECELL_DEFAULT ); - sc::ColumnBlockPosition* p = rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol); - if (p) - rColumn.Append(*p, aDestPos.Row(), pNewCell); + EditTextObject* p = sc::edittext_block::at(*it->data, aPos.second); + if (pDocument == rDestCol.pDocument) + rDestCol.maCells.set(nDestRow, p->Clone()); else - rColumn.Append(aDestPos.Row(), pNewCell); + rDestCol.maCells.set(nDestRow, ScEditUtil::Clone(*p, *rDestCol.pDocument)); } + break; + case sc::element_type_formula: + { + ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second); + if (p->GetDirty() && pDocument->GetAutoCalc()) + p->Interpret(); + + ScAddress aDestPos = p->aPos; + aDestPos.SetRow(nDestRow); + ScFormulaCell* pNew = new ScFormulaCell(*p, *rDestCol.pDocument, aDestPos); + rDestCol.maCells.set(nDestRow, pNew); + } + break; + case sc::element_type_empty: + default: + // empty + rDestCol.maCells.set_empty(nDestRow, nDestRow); + bSet = false; } + + if (bSet) + rDestCol.maCellTextAttrs.set(nDestRow, maCellTextAttrs.get<sc::CellTextAttr>(nSrcRow)); + else + rDestCol.maCellTextAttrs.set_empty(nDestRow, nDestRow); } namespace { -class FindInRows : std::unary_function<ColEntry, bool> +bool canCopyValue(const ScDocument& rDoc, const ScAddress& rPos, sal_uInt16 nFlags) { - SCROW mnRow1; - SCROW mnRow2; -public: - FindInRows(SCROW nRow1, SCROW nRow2) : mnRow1(nRow1), mnRow2(nRow2) {} - bool operator() (const ColEntry& rEntry) - { - return mnRow1 <= rEntry.nRow && rEntry.nRow <= mnRow2; - } -}; + sal_uInt32 nNumIndex = static_cast<const SfxUInt32Item*>(rDoc.GetAttr(rPos, ATTR_VALUE_FORMAT))->GetValue(); + short nType = rDoc.GetFormatTable()->GetType(nNumIndex); + if ((nType == NUMBERFORMAT_DATE) || (nType == NUMBERFORMAT_TIME) || (nType == NUMBERFORMAT_DATETIME)) + return ((nFlags & IDF_DATETIME) != 0); -class FindAboveRow : std::unary_function<ColEntry, bool> + return ((nFlags & IDF_VALUE) != 0); +} + +class CopyAsLinkHandler : public sc::CellBlockCloneHandler { - SCROW mnRow; -public: - FindAboveRow(SCROW nRow) : mnRow(nRow) {} - bool operator() (const ColEntry& rEntry) + sal_uInt16 mnCopyFlags; + std::vector<ScFormulaCell*> maCellBuffer; + + ScFormulaCell* createRefCell(const ScAddress& rSrcPos, const ScAddress& rDestPos) { - return mnRow < rEntry.nRow; + ScSingleRefData aRef; + aRef.InitAddress(rSrcPos); // Absolute reference. + aRef.SetFlag3D(true); + + ScTokenArray aArr; + aArr.AddSingleReference(aRef); + return new ScFormulaCell(&getDestDoc(), rDestPos, &aArr); } -}; -struct DeleteCell : std::unary_function<ColEntry, void> -{ - void operator() (ColEntry& rEntry) + template<typename _DataBlock> + void createRefBlock( + sc::CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + const typename _DataBlock::const_iterator& itBegin, const typename _DataBlock::const_iterator& itEnd) { - rEntry.pCell->Delete(); + maCellBuffer.clear(); + maCellBuffer.reserve(std::distance(itBegin, itEnd)); + ScAddress aSrcPos = rSrcPos; + ScAddress aDestPos = rDestPos; + for (typename _DataBlock::const_iterator it = itBegin; it != itEnd; ++it, aSrcPos.IncRow(), aDestPos.IncRow()) + maCellBuffer.push_back(createRefCell(aSrcPos, aDestPos)); + + itPos = getDestCellStore().set(itPos, rDestPos.Row(), maCellBuffer.begin(), maCellBuffer.end()); } -}; -} +public: + CopyAsLinkHandler( + ScDocument& rSrcDoc, ScDocument& rDestDoc, sc::CellStoreType& rDestCellStore, sal_uInt16 nCopyFlags) : + sc::CellBlockCloneHandler(rSrcDoc, rDestDoc, rDestCellStore), + mnCopyFlags(nCopyFlags) {} -void ScColumn::CopyStaticToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol) -{ - if (nRow1 > nRow2) - return; + virtual void cloneDoubleBlock( + sc::CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + const sc::numeric_block::const_iterator& itBegin, const sc::numeric_block::const_iterator& itEnd) + { + if ((mnCopyFlags & (IDF_DATETIME|IDF_VALUE)) == 0) + return; - CopyCellTextAttrsToDocument(nRow1, nRow2, rDestCol); + ScAddress aSrcPos = rSrcPos; + ScAddress aDestPos = rDestPos; + for (sc::numeric_block::const_iterator it = itBegin; it != itEnd; ++it, aSrcPos.IncRow(), aDestPos.IncRow()) + { + if (!canCopyValue(getSrcDoc(), aSrcPos, mnCopyFlags)) + continue; - // First, clear the destination column for the row range specified. - std::vector<ColEntry>::iterator it, itEnd; + itPos = getDestCellStore().set(itPos, aDestPos.Row(), createRefCell(aSrcPos, aDestPos)); + } + + } - it = std::find_if(rDestCol.maItems.begin(), rDestCol.maItems.end(), FindInRows(nRow1, nRow2)); - if (it != rDestCol.maItems.end()) + virtual void cloneStringBlock( + sc::CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + const sc::string_block::const_iterator& itBegin, const sc::string_block::const_iterator& itEnd) { - itEnd = std::find_if(it, rDestCol.maItems.end(), FindAboveRow(nRow2)); - std::for_each(it, itEnd, DeleteCell()); - rDestCol.maItems.erase(it, itEnd); + if (!(mnCopyFlags & IDF_STRING)) + return; + + createRefBlock<sc::string_block>(itPos, rSrcPos, rDestPos, itBegin, itEnd); } - // Determine the range of cells in the original column that need to be copied. - it = std::find_if(maItems.begin(), maItems.end(), FindInRows(nRow1, nRow2)); - if (it == maItems.end()) - // Nothing to copy. - return; + virtual void cloneEditTextBlock( + sc::CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + const sc::edittext_block::const_iterator& itBegin, const sc::edittext_block::const_iterator& itEnd) + { + if (!(mnCopyFlags & IDF_STRING)) + return; + + createRefBlock<sc::edittext_block>(itPos, rSrcPos, rDestPos, itBegin, itEnd); + } - itEnd = std::find_if(it, maItems.end(), FindAboveRow(nRow2)); + virtual void cloneFormulaBlock( + sc::CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + const sc::formula_block::const_iterator& itBegin, const sc::formula_block::const_iterator& itEnd) + { + if (!(mnCopyFlags & IDF_FORMULA)) + return; - // Clone and staticize all cells that need to be copied. - std::vector<ColEntry> aCopied; - aCopied.reserve(std::distance(it, itEnd)); - for (; it != itEnd; ++it) + createRefBlock<sc::formula_block>(itPos, rSrcPos, rDestPos, itBegin, itEnd); + } +}; + +class CopyByCloneHandler : public sc::CellBlockCloneHandler +{ + sal_uInt16 mnCopyFlags; + + void cloneFormulaCell( + sc::CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + ScFormulaCell& rSrcCell) { - ColEntry aEntry; - aEntry.nRow = it->nRow; - switch (it->pCell->GetCellType()) + bool bCloneValue = (mnCopyFlags & IDF_VALUE) != 0; + bool bCloneDateTime = (mnCopyFlags & IDF_DATETIME) != 0; + bool bCloneString = (mnCopyFlags & IDF_STRING) != 0; + bool bCloneSpecialBoolean = (mnCopyFlags & IDF_SPECIAL_BOOLEAN) != 0; + bool bCloneFormula = (mnCopyFlags & IDF_FORMULA) != 0; + + bool bForceFormula = false; + + if (bCloneSpecialBoolean) { - case CELLTYPE_VALUE: + // See if the formula consists of =TRUE() or =FALSE(). + ScTokenArray* pCode = rSrcCell.GetCode(); + if (pCode && pCode->GetLen() == 1) { - const ScValueCell& rCell = static_cast<const ScValueCell&>(*it->pCell); - aEntry.pCell = new ScValueCell(rCell.GetValue()); - aCopied.push_back(aEntry); + const formula::FormulaToken* p = pCode->First(); + if (p->GetOpCode() == ocTrue || p->GetOpCode() == ocFalse) + // This is a boolean formula. + bForceFormula = true; } - break; - case CELLTYPE_STRING: + } + + if (bForceFormula || bCloneFormula) + { + // Clone as formula cell. + itPos = getDestCellStore().set( + itPos, rDestPos.Row(), new ScFormulaCell(rSrcCell, getDestDoc(), rDestPos)); + + return; + } + + if (getDestDoc().IsUndo()) + return; + + if (bCloneValue) + { + sal_uInt16 nErr = rSrcCell.GetErrCode(); + if (nErr) { - const ScStringCell& rCell = static_cast<const ScStringCell&>(*it->pCell); - aEntry.pCell = new ScStringCell(rCell.GetString()); - aCopied.push_back(aEntry); + // error codes are cloned with values + ScFormulaCell* pErrCell = new ScFormulaCell(&getDestDoc(), rDestPos); + pErrCell->SetErrCode(nErr); + itPos = getDestCellStore().set( + itPos, rDestPos.Row(), new ScFormulaCell(rSrcCell, getDestDoc(), rDestPos)); + return; } - break; - case CELLTYPE_EDIT: + } + + if (bCloneValue || bCloneDateTime) + { + if (rSrcCell.IsValue()) { - // Convert to a simple string cell. - const ScEditCell& rCell = static_cast<const ScEditCell&>(*it->pCell); - aEntry.pCell = new ScStringCell(rCell.GetString()); - aCopied.push_back(aEntry); + if (canCopyValue(getSrcDoc(), rSrcPos, mnCopyFlags)) + itPos = getDestCellStore().set(itPos, rDestPos.Row(), rSrcCell.GetValue()); + + return; } - break; - case CELLTYPE_FORMULA: + } + + if (bCloneString) + { + OUString aStr = rSrcCell.GetString(); + if (aStr.isEmpty()) + // Don't create empty string cells. + return; + + if (rSrcCell.IsMultilineResult()) { - ScFormulaCell& rCell = static_cast<ScFormulaCell&>(*it->pCell); - if (rCell.GetDirty() && pDocument->GetAutoCalc()) - rCell.Interpret(); - - if (rCell.GetErrCode()) - // Skip cells with error. - break; - - if (rCell.IsValue()) - aEntry.pCell = new ScValueCell(rCell.GetValue()); - else - aEntry.pCell = new ScStringCell(rCell.GetString()); - aCopied.push_back(aEntry); + // Clone as an edit text object. + EditEngine& rEngine = getDestDoc().GetEditEngine(); + rEngine.SetText(aStr); + itPos = getDestCellStore().set(itPos, rDestPos.Row(), rEngine.CreateTextObject()); } - break; - default: - ; // ignore the rest. + else + itPos = getDestCellStore().set(itPos, rDestPos.Row(), aStr); } } - // Insert the cells into destination column. At this point the - // destination column shouldn't have any cells within the specified range. - it = std::find_if(rDestCol.maItems.begin(), rDestCol.maItems.end(), FindAboveRow(nRow2)); - rDestCol.maItems.insert(it, aCopied.begin(), aCopied.end()); -} +public: + CopyByCloneHandler(ScDocument& rSrcDoc, ScDocument& rDestDoc, sc::CellStoreType& rDestCellStore, sal_uInt16 nCopyFlags) : + sc::CellBlockCloneHandler(rSrcDoc,rDestDoc,rDestCellStore), + mnCopyFlags(nCopyFlags) + { + } -void ScColumn::CopyCellToDocument( SCROW nSrcRow, SCROW nDestRow, ScColumn& rDestCol ) -{ - SCSIZE nIndex; - if (!Search(nSrcRow, nIndex)) + virtual void cloneDoubleBlock( + sc::CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + const sc::numeric_block::const_iterator& itBegin, const sc::numeric_block::const_iterator& itEnd) { - // Source cell is empty. Remove the destination cell if one exists. - rDestCol.Delete(nDestRow); - return; + if ((mnCopyFlags & (IDF_DATETIME|IDF_VALUE)) == 0) + return; + + ScAddress aSrcPos = rSrcPos; + ScAddress aDestPos = rDestPos; + for (sc::numeric_block::const_iterator it = itBegin; it != itEnd; ++it, aSrcPos.IncRow(), aDestPos.IncRow()) + { + if (!canCopyValue(getSrcDoc(), aSrcPos, mnCopyFlags)) + continue; + + itPos = getDestCellStore().set(itPos, aDestPos.Row(), *it); + } } - ScBaseCell* pDestCell = - maItems[nSrcRow].pCell->Clone( - *rDestCol.pDocument, ScAddress(rDestCol.nCol, nDestRow, rDestCol.nTab)); + virtual void cloneStringBlock( + sc::CellStoreType::iterator& itPos, const ScAddress& /*rSrcPos*/, const ScAddress& rDestPos, + const sc::string_block::const_iterator& itBegin, const sc::string_block::const_iterator& itEnd) + { + if (!(mnCopyFlags & IDF_STRING)) + return; + + ScAddress aDestPos = rDestPos; + for (sc::string_block::const_iterator it = itBegin; it != itEnd; ++it, aDestPos.IncRow()) + { + const OUString& rStr = *it; + if (rStr.isEmpty()) + // String cell with empty value is used to special-case cell value removal. + itPos = getDestCellStore().set_empty(itPos, aDestPos.Row(), aDestPos.Row()); + else + itPos = getDestCellStore().set(itPos, aDestPos.Row(), rStr); + } + } + + virtual void cloneEditTextBlock( + sc::CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + const sc::edittext_block::const_iterator& itBegin, const sc::edittext_block::const_iterator& itEnd) + { + if (!(mnCopyFlags & IDF_STRING)) + return; + + sc::CellBlockCloneHandler::cloneEditTextBlock(itPos, rSrcPos, rDestPos, itBegin, itEnd); + } + + virtual void cloneFormulaBlock( + sc::CellStoreType::iterator& itPos, const ScAddress& rSrcPos, const ScAddress& rDestPos, + const sc::formula_block::const_iterator& itBegin, const sc::formula_block::const_iterator& itEnd) + { + ScAddress aSrcPos = rSrcPos; + ScAddress aDestPos = rDestPos; + for (sc::formula_block::const_iterator it = itBegin; it != itEnd; ++it, aSrcPos.IncRow(), aDestPos.IncRow()) + cloneFormulaCell(itPos, aSrcPos, aDestPos, const_cast<ScFormulaCell&>(**it)); + } +}; - rDestCol.Insert(nDestRow, pDestCell); } void ScColumn::CopyToColumn( @@ -1416,60 +1754,14 @@ void ScColumn::CopyToColumn( if ((nFlags & IDF_CONTENTS) != 0) { - SCSIZE i; - SCSIZE nBlockCount = 0; - SCSIZE nStartIndex = 0, nEndIndex = 0; - for (i = 0; i < maItems.size(); i++) - if ((maItems[i].nRow >= nRow1) && (maItems[i].nRow <= nRow2)) - { - if (!nBlockCount) - nStartIndex = i; - nEndIndex = i; - ++nBlockCount; - } + boost::scoped_ptr<sc::CellBlockCloneHandler> pHdl(NULL); - if (nBlockCount) - { - rColumn.ReserveSize(rColumn.GetCellCount() + nBlockCount); - ScAddress aDestPos( rColumn.nCol, 0, rColumn.nTab ); - for (i = nStartIndex; i <= nEndIndex; i++) - { - aDestPos.SetRow( maItems[i].nRow ); - ScBaseCell* pNew = bAsLink ? - CreateRefCell( rColumn.pDocument, aDestPos, i, nFlags ) : - CloneCell( i, nFlags, *rColumn.pDocument, aDestPos ); + if (bAsLink) + pHdl.reset(new CopyAsLinkHandler(*pDocument, *rColumn.pDocument, rColumn.maCells, nFlags)); + else + pHdl.reset(new CopyByCloneHandler(*pDocument, *rColumn.pDocument, rColumn.maCells, nFlags)); - if (pNew) - { - // Special case to allow removing of cell instances. A - // string cell with empty content is used to indicate an - // empty cell. - sc::ColumnBlockPosition* p = rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol); - if (pNew->GetCellType() == CELLTYPE_STRING) - { - OUString aStr = static_cast<ScStringCell*>(pNew)->GetString(); - if (aStr.isEmpty()) - // A string cell with empty string. Delete the cell itself. - rColumn.Delete(maItems[i].nRow); - else - { - // non-empty string cell - if (p) - rColumn.Insert(*p, maItems[i].nRow, pNew); - else - rColumn.Insert(maItems[i].nRow, pNew); - } - } - else - { - if (p) - rColumn.Insert(*p, maItems[i].nRow, pNew); - else - rColumn.Insert(maItems[i].nRow, pNew); - } - } - } - } + CopyCellsInRangeToColumn(NULL, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol), *pHdl, nRow1, nRow2, rColumn); } } @@ -1490,23 +1782,26 @@ void ScColumn::UndoToColumn( void ScColumn::CopyUpdated( const ScColumn& rPosCol, ScColumn& rDestCol ) const { - ScDocument& rDestDoc = *rDestCol.pDocument; - ScAddress aOwnPos( nCol, 0, nTab ); - ScAddress aDestPos( rDestCol.nCol, 0, rDestCol.nTab ); + // Copy cells from this column to the destination column only for those + // rows that are present in the position column. - SCSIZE nPosCount = rPosCol.maItems.size(); - for (SCSIZE nPosIndex = 0; nPosIndex < nPosCount; nPosIndex++) - { - aOwnPos.SetRow( rPosCol.maItems[nPosIndex].nRow ); - aDestPos.SetRow( aOwnPos.Row() ); - SCSIZE nThisIndex; - if ( Search( aDestPos.Row(), nThisIndex ) ) - { - ScBaseCell* pNew = maItems[nThisIndex].pCell->Clone( rDestDoc, aDestPos ); - rDestCol.Insert( aDestPos.Row(), pNew ); - } - } + sc::CellBlockCloneHandler aHdl(*pDocument, *rDestCol.pDocument, rDestCol.maCells); + sc::ColumnBlockConstPosition aSrcPos; + sc::ColumnBlockPosition aDestPos; + InitBlockPosition(aSrcPos); + rDestCol.InitBlockPosition(aDestPos); + + // First, mark all the non-empty cell ranges from the position column. + sc::SingleColumnSpanSet aRangeSet; + aRangeSet.scan(rPosCol); + // Now, copy cells from this column to the destination column for those + // marked row ranges. + sc::SingleColumnSpanSet::SpansType aRanges; + aRangeSet.getSpans(aRanges); + sc::SingleColumnSpanSet::SpansType::const_iterator it = aRanges.begin(), itEnd = aRanges.end(); + for (; it != itEnd; ++it) + CopyCellsInRangeToColumn(&aSrcPos, &aDestPos, aHdl, it->mnRow1, it->mnRow2, rDestCol); } @@ -1610,10 +1905,31 @@ void ScColumn::MarkScenarioIn( ScMarkData& rDestMark ) const } } +namespace { + +void resetColumnPosition(sc::CellStoreType& rCells, SCCOL nCol) +{ + sc::CellStoreType::iterator it = rCells.begin(), itEnd = rCells.end(); + for (; it != itEnd; ++it) + { + if (it->type != sc::element_type_formula) + continue; + + sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data); + sc::formula_block::iterator itCellEnd = sc::formula_block::end(*it->data); + for (; itCell != itCellEnd; ++itCell) + { + ScFormulaCell& rCell = **itCell; + rCell.aPos.SetCol(nCol); + } + } +} + +} void ScColumn::SwapCol(ScColumn& rCol) { - maItems.swap(rCol.maItems); + maCells.swap(rCol.maCells); maCellTextAttrs.swap(rCol.maCellTextAttrs); CellStorageModified(); @@ -1629,518 +1945,403 @@ void ScColumn::SwapCol(ScColumn& rCol) std::swap(mbDirtyGroups, rCol.mbDirtyGroups); - SCSIZE i; - for (i = 0; i < maItems.size(); i++) - { - ScFormulaCell* pCell = (ScFormulaCell*) maItems[i].pCell; - if( pCell->GetCellType() == CELLTYPE_FORMULA) - pCell->aPos.SetCol(nCol); - } - for (i = 0; i < rCol.maItems.size(); i++) - { - ScFormulaCell* pCell = (ScFormulaCell*) rCol.maItems[i].pCell; - if( pCell->GetCellType() == CELLTYPE_FORMULA) - pCell->aPos.SetCol(rCol.nCol); - } + // Reset column positions in formula cells. + resetColumnPosition(maCells, nCol); + resetColumnPosition(rCol.maCells, rCol.nCol); } void ScColumn::MoveTo(SCROW nStartRow, SCROW nEndRow, ScColumn& rCol) { pAttrArray->MoveTo(nStartRow, nEndRow, *rCol.pAttrArray); + // Mark the non-empty cells within the specified range, for later broadcasting. + sc::SingleColumnSpanSet aNonEmpties; + aNonEmpties.scan(*this, nStartRow, nEndRow); + sc::SingleColumnSpanSet::SpansType aRanges; + aNonEmpties.getSpans(aRanges); + // Move the broadcasters to the destination column. maBroadcasters.transfer(nStartRow, nEndRow, rCol.maBroadcasters, nStartRow); + maCells.transfer(nStartRow, nEndRow, rCol.maCells, nStartRow); + maCellTextAttrs.transfer(nStartRow, nEndRow, rCol.maCellTextAttrs, nStartRow); - if (maItems.empty()) - // No cells to move. - return; + CellStorageModified(); + rCol.CellStorageModified(); - ::std::vector<SCROW> aRows; - SCSIZE i; - Search( nStartRow, i); // i points to start row or position thereafter - SCSIZE nStartPos = i; - // First, copy the cell instances to the new column. - for ( ; i < maItems.size() && maItems[i].nRow <= nEndRow; ++i) + // Broadcast on moved ranges. Area-broadcast only. + ScHint aHint(SC_HINT_DATACHANGED, ScAddress(nCol, 0, nTab)); + ScAddress& rPos = aHint.GetAddress(); + sc::SingleColumnSpanSet::SpansType::const_iterator itRange = aRanges.begin(), itRangeEnd = aRanges.end(); + for (; itRange != itRangeEnd; ++itRange) { - SCROW nRow = maItems[i].nRow; - aRows.push_back( nRow); - rCol.Insert( nRow, maItems[i].pCell); + for (SCROW nRow = itRange->mnRow1; nRow <= itRange->mnRow2; ++nRow) + { + rPos.SetRow(nRow); + pDocument->AreaBroadcast(aHint); + } } +} + +namespace { - SCSIZE nStopPos = i; - if (nStartPos < nStopPos) +class UpdateRefHandler +{ +protected: + ScRange maRange; + SCCOL mnDx; + SCROW mnDy; + SCTAB mnDz; + UpdateRefMode meMode; + ScDocument* mpUndoDoc; + bool mbUpdated; + +public: + UpdateRefHandler(const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz, UpdateRefMode eMode, ScDocument* pUndoDoc) : + maRange(rRange), mnDx(nDx), mnDy(nDy), mnDz(nDz), meMode(eMode), mpUndoDoc(pUndoDoc), mbUpdated(false) {} + + bool isUpdated() const { return mbUpdated; } + + void operator() (sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize) { - // At least one cell gets copied to the destination column. - maCellTextAttrs.set_empty(nStartRow, nEndRow); - CellStorageModified(); + if (node.type != sc::element_type_formula) + return; - // Create list of ranges of cell entry positions. - typedef ::std::pair<SCSIZE,SCSIZE> PosPair; - typedef ::std::vector<PosPair> EntryPosPairs; - EntryPosPairs aEntries; - { - bool bFirst = true; - nStopPos = 0; - for (::std::vector<SCROW>::const_iterator it( aRows.begin()); - it != aRows.end() && nStopPos < maItems.size(); ++it, - ++nStopPos) - { - if (!bFirst && *it != maItems[nStopPos].nRow) - { - aEntries.push_back( PosPair(nStartPos, nStopPos)); - bFirst = true; - } - if (bFirst && Search( *it, nStartPos)) - { - bFirst = false; - nStopPos = nStartPos; - } - } - if (!bFirst && nStartPos < nStopPos) - aEntries.push_back( PosPair(nStartPos, nStopPos)); - } - // Broadcast changes - ScAddress aAdr( nCol, 0, nTab ); - ScHint aHint(SC_HINT_DATACHANGED, aAdr); // areas only - ScAddress& rAddress = aHint.GetAddress(); - - // must iterate backwards, because indexes of following cells become invalid - for (EntryPosPairs::reverse_iterator it( aEntries.rbegin()); - it != aEntries.rend(); ++it) - { - nStartPos = (*it).first; - nStopPos = (*it).second; - for (i=nStartPos; i<nStopPos; ++i) - { - rAddress.SetRow( maItems[i].nRow ); - pDocument->AreaBroadcast( aHint ); - } - // Erase the slots containing pointers to the dummy cell instance. - maItems.erase(maItems.begin() + nStartPos, maItems.begin() + nStopPos); - } + 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); + + size_t nRow = node.position + nOffset; + for (; it != itEnd; ++it, ++nRow) + updateReference(**it, static_cast<SCROW>(nRow)); } + + virtual void updateReference(ScFormulaCell& rCell, SCROW nRow) = 0; +}; + +class UpdateRefOnCopy : public UpdateRefHandler +{ +public: + UpdateRefOnCopy(const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz, ScDocument* pUndoDoc) : + UpdateRefHandler(rRange, nDx, nDy, nDz, URM_COPY, pUndoDoc) {} + + virtual void updateReference(ScFormulaCell& rCell, SCROW /*nRow*/) + { + mbUpdated |= rCell.UpdateReference(meMode, maRange, mnDx, mnDy, mnDz, mpUndoDoc); + } +}; + +class UpdateRefOnNonCopy : public UpdateRefHandler +{ + SCCOL mnCol; + SCROW mnTab; +public: + UpdateRefOnNonCopy(SCCOL nCol, SCTAB nTab, const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz, UpdateRefMode eMode, ScDocument* pUndoDoc) : + UpdateRefHandler(rRange, nDx, nDy, nDz, eMode, pUndoDoc), + mnCol(nCol), mnTab(nTab) {} + + virtual void updateReference(ScFormulaCell& rCell, SCROW nRow) + { + ScAddress aUndoPos(mnCol, nRow, mnTab); + mbUpdated |= rCell.UpdateReference(meMode, maRange, mnDx, mnDy, mnDz, mpUndoDoc, &aUndoPos); + } +}; + } bool ScColumn::UpdateReference( UpdateRefMode eUpdateRefMode, SCCOL nCol1, SCROW nRow1, SCTAB nTab1, SCCOL nCol2, SCROW nRow2, SCTAB nTab2, SCsCOL nDx, SCsROW nDy, SCsTAB nDz, ScDocument* pUndoDoc ) { - bool bUpdated = false; - if ( !maItems.empty() ) + ScRange aRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + + if (eUpdateRefMode == URM_COPY) { - ScRange aRange( ScAddress( nCol1, nRow1, nTab1 ), - ScAddress( nCol2, nRow2, nTab2 ) ); - if ( eUpdateRefMode == URM_COPY && nRow1 == nRow2 ) - { // e.g. put a single cell in the clipboard - SCSIZE nIndex; - if ( Search( nRow1, nIndex ) ) - { - ScFormulaCell* pCell = (ScFormulaCell*) maItems[nIndex].pCell; - if( pCell->GetCellType() == CELLTYPE_FORMULA) - bUpdated |= pCell->UpdateReference( - eUpdateRefMode, aRange, nDx, nDy, nDz, pUndoDoc ); - } - } - else - { - // For performance reasons two loop bodies instead of - // testing for update mode in each iteration. - // Anyways, this is still a bottleneck on large arrays with few - // formulas cells. - if ( eUpdateRefMode == URM_COPY ) - { - SCSIZE i; - Search( nRow1, i ); - for ( ; i < maItems.size(); i++ ) - { - SCROW nRow = maItems[i].nRow; - if ( nRow > nRow2 ) - break; - ScBaseCell* pCell = maItems[i].pCell; - if( pCell->GetCellType() == CELLTYPE_FORMULA) - { - bUpdated |= ((ScFormulaCell*)pCell)->UpdateReference( - eUpdateRefMode, aRange, nDx, nDy, nDz, pUndoDoc ); - if ( nRow != maItems[i].nRow ) - Search( nRow, i ); // Listener removed/inserted? - } - } - } - else - { - SCSIZE i = 0; - for ( ; i < maItems.size(); i++ ) - { - ScBaseCell* pCell = maItems[i].pCell; - if( pCell->GetCellType() == CELLTYPE_FORMULA) - { - SCROW nRow = maItems[i].nRow; - // When deleting rows on several sheets, the formula's position may be updated with the first call, - // so the undo position must be passed from here. - ScAddress aUndoPos( nCol, nRow, nTab ); - bUpdated |= ((ScFormulaCell*)pCell)->UpdateReference( - eUpdateRefMode, aRange, nDx, nDy, nDz, pUndoDoc, &aUndoPos ); - if ( nRow != maItems[i].nRow ) - Search( nRow, i ); // Listener removed/inserted? - } - } - } - } + UpdateRefOnCopy aHandler(aRange, nDx, nDy, nDz, pUndoDoc); + sc::ProcessBlock(maCells.begin(), maCells, aHandler, nRow1, nRow2); + return aHandler.isUpdated(); } - return bUpdated; + + UpdateRefOnNonCopy aHandler(nCol, nTab, aRange, nDx, nDy, nDz, eUpdateRefMode, pUndoDoc); + sc::ProcessBlock(maCells.begin(), maCells, aHandler, nRow1, nRow2); + return aHandler.isUpdated(); } +namespace { -void ScColumn::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest, - ScDocument* pUndoDoc ) +class UpdateTransHandler { - if ( !maItems.empty() ) - for (SCSIZE i=0; i<maItems.size(); i++) - { - ScBaseCell* pCell = maItems[i].pCell; - if (pCell->GetCellType() == CELLTYPE_FORMULA) - { - SCROW nRow = maItems[i].nRow; - ((ScFormulaCell*)pCell)->UpdateTranspose( rSource, rDest, pUndoDoc ); - if ( nRow != maItems[i].nRow ) - Search( nRow, i ); // Listener deleted/inserted? - } - } -} + ScRange maSource; + ScAddress maDest; + ScDocument* mpUndoDoc; +public: + UpdateTransHandler(const ScRange& rSource, const ScAddress& rDest, ScDocument* pUndoDoc) : + maSource(rSource), maDest(rDest), mpUndoDoc(pUndoDoc) {} + void operator() (size_t, ScFormulaCell* pCell) + { + pCell->UpdateTranspose(maSource, maDest, mpUndoDoc); + } +}; -void ScColumn::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) +class UpdateGrowHandler { - if ( !maItems.empty() ) - for (SCSIZE i=0; i<maItems.size(); i++) - { - ScBaseCell* pCell = maItems[i].pCell; - if (pCell->GetCellType() == CELLTYPE_FORMULA) - { - SCROW nRow = maItems[i].nRow; - ((ScFormulaCell*)pCell)->UpdateGrow( rArea, nGrowX, nGrowY ); - if ( nRow != maItems[i].nRow ) - Search( nRow, i ); // Listener deleted/inserted? - } - } -} + ScRange maArea; + SCCOL mnGrowX; + SCROW mnGrowY; +public: + UpdateGrowHandler(const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY) : + maArea(rArea), mnGrowX(nGrowX), mnGrowY(nGrowY) {} + void operator() (size_t, ScFormulaCell* pCell) + { + pCell->UpdateGrow(maArea, mnGrowX, mnGrowY); + } +}; -void ScColumn::UpdateInsertTab(SCTAB nInsPos, SCTAB nNewSheets) +class InsertTabUpdater { - if (nTab >= nInsPos) + sc::CellTextAttrStoreType& mrTextAttrs; + sc::CellTextAttrStoreType::iterator miAttrPos; + SCTAB mnTab; + SCTAB mnInsPos; + SCTAB mnNewSheets; +public: + InsertTabUpdater(sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab, SCTAB nInsPos, SCTAB nNewSheets) : + mrTextAttrs(rTextAttrs), + miAttrPos(rTextAttrs.begin()), + mnTab(nTab), + mnInsPos(nInsPos), + mnNewSheets(nNewSheets) {} + + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) { - nTab += nNewSheets; - pAttrArray->SetTab(nTab); + pCell->UpdateInsertTab(mnInsPos, mnNewSheets); } - UpdateInsertTabOnlyCells(nInsPos, nNewSheets); -} + void operator() (size_t nRow, EditTextObject* pCell) + { + editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater(); + aUpdater.updateTableFields(mnTab); + miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr()); + } +}; -void ScColumn::UpdateInsertTabOnlyCells(SCTAB nInsPos, SCTAB nNewSheets) +class DeleteTabUpdater { - if (maItems.empty()) - return; + sc::CellTextAttrStoreType& mrTextAttrs; + sc::CellTextAttrStoreType::iterator miAttrPos; + SCTAB mnDelPos; + SCTAB mnSheets; + SCTAB mnTab; + bool mbIsMove; +public: + DeleteTabUpdater(sc::CellTextAttrStoreType& rTextAttrs, SCTAB nDelPos, SCTAB nSheets, SCTAB nTab, bool bIsMove) : + mrTextAttrs(rTextAttrs), + miAttrPos(rTextAttrs.begin()), + mnDelPos(nDelPos), + mnSheets(nSheets), + mnTab(nTab), + mbIsMove(bIsMove) {} - for (size_t i = 0; i < maItems.size(); ++i) + void operator() (size_t, ScFormulaCell* pCell) { - switch (maItems[i].pCell->GetCellType()) - { - case CELLTYPE_FORMULA: - { - SCROW nRow = maItems[i].nRow; - ScFormulaCell* p = static_cast<ScFormulaCell*>(maItems[i].pCell); - p->UpdateInsertTab(nInsPos, nNewSheets); - if (nRow != maItems[i].nRow) - Search(nRow, i); // Listener deleted/inserted? - } - break; - case CELLTYPE_EDIT: - { - ScEditCell* p = static_cast<ScEditCell*>(maItems[i].pCell); - p->UpdateFields(nTab); - SetTextWidth(maItems[i].nRow, TEXTWIDTH_DIRTY); - } - break; - default: - ; - } + pCell->UpdateDeleteTab(mnDelPos, mbIsMove, mnSheets); } -} -void ScColumn::UpdateInsertTabAbs(SCTAB nNewPos) + void operator() (size_t nRow, EditTextObject* pCell) + { + editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater(); + aUpdater.updateTableFields(mnTab); + miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr()); + } +}; + +class InsertAbsTabUpdater { - if (maItems.empty()) - return; + sc::CellTextAttrStoreType& mrTextAttrs; + sc::CellTextAttrStoreType::iterator miAttrPos; + SCTAB mnTab; + SCTAB mnNewPos; +public: + InsertAbsTabUpdater(sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab, SCTAB nNewPos) : + mrTextAttrs(rTextAttrs), + miAttrPos(rTextAttrs.begin()), + mnTab(nTab), + mnNewPos(nNewPos) {} - for (size_t i = 0; i < maItems.size(); ++i) + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) { - switch (maItems[i].pCell->GetCellType()) - { - case CELLTYPE_FORMULA: - { - SCROW nRow = maItems[i].nRow; - ScFormulaCell* p = static_cast<ScFormulaCell*>(maItems[i].pCell); - p->UpdateInsertTabAbs(nNewPos); - if (nRow != maItems[i].nRow) - Search(nRow, i); // Listener deleted/inserted? - } - break; - case CELLTYPE_EDIT: - { - ScEditCell* p = static_cast<ScEditCell*>(maItems[i].pCell); - p->UpdateFields(nTab); - SetTextWidth(maItems[i].nRow, TEXTWIDTH_DIRTY); - } - break; - default: - ; - } + pCell->UpdateInsertTabAbs(mnNewPos); } -} -void ScColumn::UpdateDeleteTab(SCTAB nDelPos, bool bIsMove, ScColumn* pRefUndo, SCTAB nSheets) -{ - if (nTab > nDelPos) + void operator() (size_t nRow, EditTextObject* pCell) { - nTab -= nSheets; - pAttrArray->SetTab(nTab); + editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater(); + aUpdater.updateTableFields(mnTab); + miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr()); } +}; - if (maItems.empty()) - return; +class MoveTabUpdater +{ + sc::CellTextAttrStoreType& mrTextAttrs; + sc::CellTextAttrStoreType::iterator miAttrPos; + SCTAB mnTab; + SCTAB mnOldPos; + SCTAB mnNewPos; +public: + MoveTabUpdater(sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab, SCTAB nOldPos, SCTAB nNewPos) : + mrTextAttrs(rTextAttrs), + miAttrPos(rTextAttrs.begin()), + mnTab(nTab), + mnOldPos(nOldPos), + mnNewPos(nNewPos) {} - for (size_t i = 0; i < maItems.size(); ++i) + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) { - switch (maItems[i].pCell->GetCellType()) - { - case CELLTYPE_FORMULA: - { - SCROW nRow = maItems[i].nRow; - ScFormulaCell* pOld = static_cast<ScFormulaCell*>(maItems[i].pCell); + pCell->UpdateMoveTab(mnOldPos, mnNewPos, mnTab); + } - /* Do not copy cell note to the undo document. Undo will copy - back the formula cell while keeping the original note. */ - ScBaseCell* pSave = pRefUndo ? pOld->Clone( *pDocument ) : 0; + void operator() (size_t nRow, EditTextObject* pCell) + { + editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater(); + aUpdater.updateTableFields(mnTab); + miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr()); + } +}; - bool bChanged = pOld->UpdateDeleteTab(nDelPos, bIsMove, nSheets); - if ( nRow != maItems[i].nRow ) - Search( nRow, i ); // Listener deleted/inserted? +class UpdateCompileHandler +{ + bool mbForceIfNameInUse; +public: + UpdateCompileHandler(bool bForceIfNameInUse) : mbForceIfNameInUse(bForceIfNameInUse) {} - if (pRefUndo) - { - if (bChanged) - pRefUndo->Insert( nRow, pSave ); - else if(pSave) - pSave->Delete(); - } - } - break; - case CELLTYPE_EDIT: - { - ScEditCell* p = static_cast<ScEditCell*>(maItems[i].pCell); - p->UpdateFields(nTab); - SetTextWidth(maItems[i].nRow, TEXTWIDTH_DIRTY); - } - break; - default: - ; - } + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) + { + pCell->UpdateCompile(mbForceIfNameInUse); } -} +}; -void ScColumn::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo ) +class TabNoSetter { - nTab = nTabNo; - pAttrArray->SetTab( nTabNo ); - if (maItems.empty()) - return; + SCTAB mnTab; +public: + TabNoSetter(SCTAB nTab) : mnTab(nTab) {} - for (size_t i = 0; i < maItems.size(); ++i) + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) { - switch (maItems[i].pCell->GetCellType()) - { - case CELLTYPE_FORMULA: - { - ScFormulaCell* p = static_cast<ScFormulaCell*>(maItems[i].pCell); - SCROW nRow = maItems[i].nRow; - p->UpdateMoveTab(nOldPos, nNewPos, nTabNo); - if (nRow != maItems[i].nRow) - Search(nRow, i); // Listener deleted/inserted? - } - break; - case CELLTYPE_EDIT: - { - ScEditCell* p = static_cast<ScEditCell*>(maItems[i].pCell); - p->UpdateFields(nTab); - SetTextWidth(maItems[i].nRow, TEXTWIDTH_DIRTY); - } - break; - default: - ; - } + pCell->aPos.SetTab(mnTab); } -} - +}; -void ScColumn::UpdateCompile( bool bForceIfNameInUse ) +class UsedRangeNameFinder { - if ( !maItems.empty() ) + std::set<sal_uInt16>& mrIndexes; +public: + UsedRangeNameFinder(std::set<sal_uInt16>& rIndexes) : mrIndexes(rIndexes) {} + + void operator() (size_t /*nRow*/, const ScFormulaCell* pCell) { - for (SCSIZE i = 0; i < maItems.size(); i++) - { - ScFormulaCell* p = (ScFormulaCell*) maItems[i].pCell; - if( p->GetCellType() == CELLTYPE_FORMULA ) - { - SCROW nRow = maItems[i].nRow; - p->UpdateCompile( bForceIfNameInUse ); - if ( nRow != maItems[i].nRow ) - Search( nRow, i ); // Listener deleted/inserted? - } - } + pCell->FindRangeNamesInUse(mrIndexes); } -} - +}; -void ScColumn::SetTabNo(SCTAB nNewTab) +struct SetDirtyVarHandler { - nTab = nNewTab; - pAttrArray->SetTab( nNewTab ); - if ( !maItems.empty() ) - for (SCSIZE i = 0; i < maItems.size(); i++) - { - ScFormulaCell* p = (ScFormulaCell*) maItems[i].pCell; - if( p->GetCellType() == CELLTYPE_FORMULA ) - p->aPos.SetTab( nNewTab ); - } -} + void operator() (size_t /*nRow*/, ScFormulaCell* p) + { + p->SetDirtyVar(); + } +}; -void ScColumn::FindRangeNamesInUse(SCROW nRow1, SCROW nRow2, std::set<sal_uInt16>& rIndexes) const +class SetDirtyHandler { - if ( !maItems.empty() ) - for (SCSIZE i = 0; i < maItems.size(); i++) - if ((maItems[i].nRow >= nRow1) && - (maItems[i].nRow <= nRow2) && - (maItems[i].pCell->GetCellType() == CELLTYPE_FORMULA)) - ((ScFormulaCell*)maItems[i].pCell)->FindRangeNamesInUse(rIndexes); -} + ScDocument& mrDoc; +public: + SetDirtyHandler(ScDocument& rDoc) : mrDoc(rDoc) {} -void ScColumn::SetDirtyVar() -{ - for (SCSIZE i=0; i<maItems.size(); i++) + void operator() (size_t /*nRow*/, ScFormulaCell* p) { - ScFormulaCell* p = (ScFormulaCell*) maItems[i].pCell; - if( p->GetCellType() == CELLTYPE_FORMULA ) - p->SetDirtyVar(); + p->SetDirtyVar(); + if (!mrDoc.IsInFormulaTree(p)) + mrDoc.PutInFormulaTree(p); } -} +}; -bool ScColumn::IsFormulaDirty( SCROW nRow ) const +class SetDirtyOnRangeHandler { - if (!ValidRow(nRow)) - return false; + sc::SingleColumnSpanSet maValueRanges; + ScColumn& mrColumn; +public: + SetDirtyOnRangeHandler(ScColumn& rColumn) : mrColumn(rColumn) {} - SCSIZE nIndex; - if (!Search(nRow, nIndex)) - // No cell at this row position. - return false; + void operator() (size_t /*nRow*/, ScFormulaCell* p) + { + p->SetDirty(); + } - const ScBaseCell* pCell = maItems[nIndex].pCell; - if (pCell->GetCellType() != CELLTYPE_FORMULA) - // Not even a formula cell. - return false; + void operator() (mdds::mtv::element_t type, size_t nTopRow, size_t nDataSize) + { + if (type == sc::element_type_empty) + // Ignore empty blocks. + return; - return static_cast<const ScFormulaCell*>(pCell)->GetDirty(); -} + // Non-formula cells. + SCROW nRow1 = nTopRow; + SCROW nRow2 = nTopRow + nDataSize - 1; + maValueRanges.set(nRow1, nRow2, true); + } -void ScColumn::SetDirty() -{ - // is only done documentwide, no FormulaTracking - bool bOldAutoCalc = pDocument->GetAutoCalc(); - pDocument->SetAutoCalc( false ); // no multiple recalculation - for (SCSIZE i=0; i<maItems.size(); i++) + void broadcast() { - ScFormulaCell* p = (ScFormulaCell*) maItems[i].pCell; - if( p->GetCellType() == CELLTYPE_FORMULA ) - { - p->SetDirtyVar(); - if ( !pDocument->IsInFormulaTree( p ) ) - pDocument->PutInFormulaTree( p ); - } + std::vector<SCROW> aRows; + maValueRanges.getRows(aRows); + mrColumn.BroadcastCells(aRows); } - pDocument->SetAutoCalc( bOldAutoCalc ); -} +}; +class SetTableOpDirtyOnRangeHandler +{ + sc::SingleColumnSpanSet maValueRanges; + ScColumn& mrColumn; +public: + SetTableOpDirtyOnRangeHandler(ScColumn& rColumn) : mrColumn(rColumn) {} -void ScColumn::SetDirty( const ScRange& rRange ) -{ // broadcasts everything within the range, with FormulaTracking - if ( maItems.empty() ) - return ; - bool bOldAutoCalc = pDocument->GetAutoCalc(); - pDocument->SetAutoCalc( false ); // no multiple recalculation - SCROW nRow2 = rRange.aEnd.Row(); - ScAddress aPos( nCol, 0, nTab ); - ScHint aHint(SC_HINT_DATACHANGED, aPos); - SCROW nRow; - SCSIZE nIndex; - Search( rRange.aStart.Row(), 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)->SetDirty(); - else - { - aHint.GetAddress().SetRow( nRow ); - pDocument->Broadcast( aHint ); - } - nIndex++; + p->SetTableOpDirty(); } - pDocument->SetAutoCalc( bOldAutoCalc ); -} - -void ScColumn::SetTableOpDirty( const ScRange& rRange ) -{ - if ( maItems.empty() ) - return ; - bool bOldAutoCalc = pDocument->GetAutoCalc(); - pDocument->SetAutoCalc( false ); // no multiple recalculation - SCROW nRow2 = rRange.aEnd.Row(); - ScAddress aPos( nCol, 0, nTab ); - ScHint aHint(SC_HINT_TABLEOPDIRTY, aPos); - SCROW nRow; - SCSIZE nIndex; - Search( rRange.aStart.Row(), nIndex ); - while ( nIndex < maItems.size() && (nRow = maItems[nIndex].nRow) <= nRow2 ) + void operator() (mdds::mtv::element_t type, size_t nTopRow, size_t nDataSize) { - ScBaseCell* pCell = maItems[nIndex].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - ((ScFormulaCell*)pCell)->SetTableOpDirty(); - else - { - aHint.GetAddress().SetRow( nRow ); - pDocument->Broadcast( aHint ); - } - nIndex++; + if (type == sc::element_type_empty) + // Ignore empty blocks. + return; + + // Non-formula cells. + SCROW nRow1 = nTopRow; + SCROW nRow2 = nTopRow + nDataSize - 1; + maValueRanges.set(nRow1, nRow2, true); } - pDocument->SetAutoCalc( bOldAutoCalc ); -} + void broadcast() + { + std::vector<SCROW> aRows; + maValueRanges.getRows(aRows); + mrColumn.BroadcastCells(aRows); + } +}; -void ScColumn::SetDirtyAfterLoad() +struct SetDirtyAfterLoadHandler { - bool bOldAutoCalc = pDocument->GetAutoCalc(); - pDocument->SetAutoCalc( false ); // no multiple recalculation - for (SCSIZE i=0; i<maItems.size(); i++) + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) { - ScFormulaCell* p = (ScFormulaCell*) maItems[i].pCell; #if 1 // Simply set dirty and append to FormulaTree, without broadcasting, // which is a magnitude faster. This is used to calculate the entire // document, e.g. when loading alien file formats. - if ( p->GetCellType() == CELLTYPE_FORMULA ) - p->SetDirtyAfterLoad(); + pCell->SetDirtyAfterLoad(); #else /* This was used with the binary file format that stored results, where only * newly compiled and volatile functions and their dependents had to be @@ -2153,205 +2354,357 @@ void ScColumn::SetDirtyAfterLoad() // If the cell was alsready dirty because of CalcAfterLoad, // FormulaTracking has to take place. - if ( p->GetCellType() == CELLTYPE_FORMULA && p->GetDirty() ) - p->SetDirty(); + if (pCell->GetDirty()) + pCell->SetDirty(); #endif } - pDocument->SetAutoCalc( bOldAutoCalc ); -} - +}; -void ScColumn::SetRelNameDirty() +struct SetRelNameDirtyHandler { - bool bOldAutoCalc = pDocument->GetAutoCalc(); - pDocument->SetAutoCalc( false ); // no multiple recalculation - for (SCSIZE i=0; i<maItems.size(); i++) + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) { - ScFormulaCell* p = (ScFormulaCell*) maItems[i].pCell; - if( p->GetCellType() == CELLTYPE_FORMULA && p->HasRelNameReference() ) - p->SetDirty(); + if (pCell->HasRelNameReference()) + pCell->SetDirty(); } - pDocument->SetAutoCalc( bOldAutoCalc ); -} - +}; -void ScColumn::CalcAll() +struct CalcAllHandler { - if ( !maItems.empty() ) - for (SCSIZE i=0; i<maItems.size(); i++) - { - ScBaseCell* pCell = maItems[i].pCell; - if (pCell->GetCellType() == CELLTYPE_FORMULA) - { - ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) + { #if OSL_DEBUG_LEVEL > 1 - // after F9 ctrl-F9: check the calculation for each FormulaTree - double nOldVal, nNewVal; - nOldVal = pFCell->GetValue(); + // after F9 ctrl-F9: check the calculation for each FormulaTree + double nOldVal, nNewVal; + nOldVal = pCell->GetValue(); #endif - pFCell->Interpret(); + pCell->Interpret(); #if OSL_DEBUG_LEVEL > 1 - if ( pFCell->GetCode()->IsRecalcModeNormal() ) - nNewVal = pFCell->GetValue(); - else - nNewVal = nOldVal; // random(), jetzt() etc. - OSL_ENSURE( nOldVal==nNewVal, "CalcAll: nOldVal != nNewVal" ); -#endif - } - } -} + if (pCell->GetCode()->IsRecalcModeNormal()) + nNewVal = pCell->GetValue(); + else + nNewVal = nOldVal; // random(), jetzt() etc. + OSL_ENSURE(nOldVal == nNewVal, "CalcAll: nOldVal != nNewVal"); +#endif + } +}; -void ScColumn::CompileAll() +struct CompileAllHandler { - if ( !maItems.empty() ) + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) { - for (SCSIZE i = 0; i < maItems.size(); i++) - { - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - { - SCROW nRow = maItems[i].nRow; - // for unconditional compilation - // bCompile=true and pCode->nError=0 - ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); - pFCell->GetCode()->SetCodeError( 0 ); - pFCell->SetCompile( true ); - pFCell->CompileTokenArray(); - if ( nRow != maItems[i].nRow ) - Search( nRow, i ); // Listener deleted/inserted? - } - } + // for unconditional compilation + // bCompile=true and pCode->nError=0 + pCell->GetCode()->SetCodeError(0); + pCell->SetCompile(true); + pCell->CompileTokenArray(); } -} - +}; -void ScColumn::CompileXML( ScProgress& rProgress ) +class CompileXMLHandler { - if ( !maItems.empty() ) - for (SCSIZE i = 0; i < maItems.size(); i++) - { - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - { - SCROW nRow = maItems[i].nRow; - ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); - sal_uInt32 nCellFormat = GetNumberFormat( nRow ); - if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0) - pFCell->SetNeedNumberFormat(false); - else - pFCell->SetDirty(true); - - pFCell->CompileXML( rProgress ); - if ( nRow != maItems[i].nRow ) - Search( nRow, i ); // Listener deleted/inserted? - } - } -} + ScProgress& mrProgress; +public: + CompileXMLHandler(ScProgress& rProgress) : mrProgress(rProgress) {} -bool ScColumn::CompileErrorCells(sal_uInt16 nErrCode) + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) + { + pCell->CompileXML(mrProgress); + } +}; + +class CompileErrorCellsHandler { - if (maItems.empty()) - return false; + sal_uInt16 mnErrCode; + FormulaGrammar::Grammar meGram; + bool mbCompiled; +public: + CompileErrorCellsHandler(sal_uInt16 nErrCode, FormulaGrammar::Grammar eGram) : + mnErrCode(nErrCode), meGram(eGram), mbCompiled(false) {} - bool bCompiled = false; - std::vector<ColEntry>::iterator it = maItems.begin(), itEnd = maItems.end(); - for (; it != itEnd; ++it) + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) { - ScBaseCell* pCell = it->pCell; - if (pCell->GetCellType() != CELLTYPE_FORMULA) - // Not a formula cell. Skip it. - continue; - - ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); - sal_uInt16 nCurError = pFCell->GetRawError(); + sal_uInt16 nCurError = pCell->GetRawError(); if (!nCurError) // It's not an error cell. Skip it. - continue; + return; - if (nErrCode && nCurError != nErrCode) + if (mnErrCode && nCurError != mnErrCode) // Error code is specified, and it doesn't match. Skip it. - continue; + return; - pFCell->GetCode()->SetCodeError(0); + pCell->GetCode()->SetCodeError(0); OUStringBuffer aBuf; - pFCell->GetFormula(aBuf, pDocument->GetGrammar()); - pFCell->Compile(aBuf.makeStringAndClear(), false, pDocument->GetGrammar()); + pCell->GetFormula(aBuf, meGram); + pCell->Compile(aBuf.makeStringAndClear(), false, meGram); - bCompiled = true; + mbCompiled = true; } - return bCompiled; -} + bool isCompiled() const { return mbCompiled; } +}; -void ScColumn::CalcAfterLoad() +struct CalcAfterLoadHandler { - if ( !maItems.empty() ) - for (SCSIZE i = 0; i < maItems.size(); i++) - { - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - ((ScFormulaCell*)pCell)->CalcAfterLoad(); - } -} + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) + { + pCell->CalcAfterLoad(); + } +}; +struct ResetChangedHandler +{ + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) + { + pCell->SetChanged(false); + } +}; -void ScColumn::ResetChanged( SCROW nStartRow, SCROW nEndRow ) +/** + * Ambiguous script type counts as edit cell. + */ +class FindEditCellsHandler { - if ( !maItems.empty() ) + ScColumn& mrColumn; + sc::CellTextAttrStoreType& mrAttrs; + sc::CellTextAttrStoreType::iterator miAttrPos; + +public: + FindEditCellsHandler(ScColumn& rColumn, sc::CellTextAttrStoreType& rAttrs) : + mrColumn(rColumn), mrAttrs(rAttrs), miAttrPos(rAttrs.begin()) {} + + bool operator() (size_t, const EditTextObject*) { - SCSIZE nIndex; - Search(nStartRow,nIndex); - while (nIndex<maItems.size() && maItems[nIndex].nRow <= nEndRow) + return true; + } + + bool operator() (size_t nRow, const ScFormulaCell* p) + { + sal_uInt8 nScriptType = mrColumn.GetRangeScriptType(miAttrPos, nRow, nRow); + if (IsAmbiguousScriptNonZero(nScriptType)) + return true; + + return const_cast<ScFormulaCell*>(p)->IsMultilineResult(); + } + + std::pair<size_t,bool> operator() (mdds::mtv::element_t type, size_t nTopRow, size_t nDataSize) + { + typedef std::pair<size_t,bool> RetType; + + if (type == sc::element_type_empty) + return RetType(0, false); + + for (size_t i = 0; i < nDataSize; ++i) { - ScBaseCell* pCell = maItems[nIndex].pCell; - if (pCell->GetCellType() == CELLTYPE_FORMULA) - ((ScFormulaCell*)pCell)->SetChanged(false); - ++nIndex; + SCROW nRow = nTopRow + i; + sal_uInt8 nScriptType = mrColumn.GetRangeScriptType(miAttrPos, nRow, nRow); + if (IsAmbiguousScriptNonZero(nScriptType)) + // Return the offset from the first row. + return RetType(i, true); } + + return RetType(0, false); } +}; + } +void ScColumn::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest, + ScDocument* pUndoDoc ) +{ + UpdateTransHandler aFunc(rSource, rDest, pUndoDoc); + sc::ProcessFormula(maCells, aFunc); +} -bool ScColumn::HasEditCells(SCROW nStartRow, SCROW nEndRow, SCROW& rFirst) + +void ScColumn::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) { - // used in GetOptimalHeight - ambiguous script type counts as edit cell + UpdateGrowHandler aFunc(rArea, nGrowX, nGrowY); + sc::ProcessFormula(maCells, aFunc); +} - std::vector<ColEntry>::const_iterator itCell = Search(nStartRow); - std::vector<ColEntry>::const_iterator itCellEnd = maItems.end(); - if (itCell == itCellEnd) - return false; - sc::CellTextAttrStoreType::iterator itAttrPos = maCellTextAttrs.begin(); - for (; itCell != itCellEnd && itCell->nRow <= nEndRow; ++itCell) +void ScColumn::UpdateInsertTab(SCTAB nInsPos, SCTAB nNewSheets) +{ + if (nTab >= nInsPos) { - ScBaseCell* pCell = itCell->pCell; - SCROW nRow = itCell->nRow; - CellType eCellType = pCell->GetCellType(); + nTab += nNewSheets; + pAttrArray->SetTab(nTab); + } - // See if this is a real edit cell. - if (eCellType == CELLTYPE_EDIT) - { - rFirst = nRow; - return true; - } + UpdateInsertTabOnlyCells(nInsPos, nNewSheets); +} - sal_uInt8 nScriptType = GetRangeScriptType(itAttrPos, nRow, nRow); - if (IsAmbiguousScriptNonZero(nScriptType)) - { - rFirst = nRow; - return true; - } +void ScColumn::UpdateInsertTabOnlyCells(SCTAB nInsPos, SCTAB nNewSheets) +{ + InsertTabUpdater aFunc(maCellTextAttrs, nTab, nInsPos, nNewSheets); + sc::ProcessFormulaEditText(maCells, aFunc); +} - // Lastly, see if this is a formula cell with multi-line result. - if (eCellType == CELLTYPE_FORMULA && static_cast<ScFormulaCell*>(pCell)->IsMultilineResult()) - { - rFirst = nRow; - return true; - } +void ScColumn::UpdateDeleteTab(SCTAB nDelPos, bool bIsMove, ScColumn* /*pRefUndo*/, SCTAB nSheets) +{ + if (nTab > nDelPos) + { + nTab -= nSheets; + pAttrArray->SetTab(nTab); } - return false; + DeleteTabUpdater aFunc(maCellTextAttrs, nDelPos, nSheets, nTab, bIsMove); + sc::ProcessFormulaEditText(maCells, aFunc); +} + +void ScColumn::UpdateInsertTabAbs(SCTAB nNewPos) +{ + InsertAbsTabUpdater aFunc(maCellTextAttrs, nTab, nNewPos); + sc::ProcessFormulaEditText(maCells, aFunc); +} + +void ScColumn::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo ) +{ + nTab = nTabNo; + pAttrArray->SetTab( nTabNo ); + + MoveTabUpdater aFunc(maCellTextAttrs, nTab, nOldPos, nNewPos); + sc::ProcessFormulaEditText(maCells, aFunc); +} + + +void ScColumn::UpdateCompile( bool bForceIfNameInUse ) +{ + UpdateCompileHandler aFunc(bForceIfNameInUse); + sc::ProcessFormula(maCells, aFunc); +} + + +void ScColumn::SetTabNo(SCTAB nNewTab) +{ + nTab = nNewTab; + pAttrArray->SetTab( nNewTab ); + + TabNoSetter aFunc(nTab); + sc::ProcessFormula(maCells, aFunc); +} + +void ScColumn::FindRangeNamesInUse(SCROW nRow1, SCROW nRow2, std::set<sal_uInt16>& rIndexes) const +{ + UsedRangeNameFinder aFunc(rIndexes); + sc::ParseFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc); +} + +void ScColumn::SetDirtyVar() +{ + SetDirtyVarHandler aFunc; + sc::ProcessFormula(maCells, aFunc); +} + +bool ScColumn::IsFormulaDirty( SCROW nRow ) const +{ + if (!ValidRow(nRow)) + return false; + + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it->type != sc::element_type_formula) + // This is not a formula cell block. + return false; + + const ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second); + return p->GetDirty(); +} + +void ScColumn::SetDirty() +{ + // is only done documentwide, no FormulaTracking + sc::AutoCalcSwitch aSwitch(*pDocument, false); + SetDirtyHandler aFunc(*pDocument); + sc::ProcessFormula(maCells, aFunc); +} + +void ScColumn::SetDirty( SCROW nRow1, SCROW nRow2 ) +{ + // broadcasts everything within the range, with FormulaTracking + sc::AutoCalcSwitch aSwitch(*pDocument, false); + + SetDirtyOnRangeHandler aHdl(*this); + sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl, aHdl); + aHdl.broadcast(); +} + +void ScColumn::SetTableOpDirty( const ScRange& rRange ) +{ + sc::AutoCalcSwitch aSwitch(*pDocument, false); + + SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row(); + SetTableOpDirtyOnRangeHandler aHdl(*this); + sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl, aHdl); + aHdl.broadcast(); +} + +void ScColumn::SetDirtyAfterLoad() +{ + sc::AutoCalcSwitch aSwitch(*pDocument, false); + SetDirtyAfterLoadHandler aFunc; + sc::ProcessFormula(maCells, aFunc); +} + +void ScColumn::SetRelNameDirty() +{ + sc::AutoCalcSwitch aSwitch(*pDocument, false); + SetRelNameDirtyHandler aFunc; + sc::ProcessFormula(maCells, aFunc); +} + +void ScColumn::CalcAll() +{ + CalcAllHandler aFunc; + sc::ProcessFormula(maCells, aFunc); +} + +void ScColumn::CompileAll() +{ + CompileAllHandler aFunc; + sc::ProcessFormula(maCells, aFunc); +} + +void ScColumn::CompileXML( ScProgress& rProgress ) +{ + CompileXMLHandler aFunc(rProgress); + sc::ProcessFormula(maCells, aFunc); +} + +bool ScColumn::CompileErrorCells(sal_uInt16 nErrCode) +{ + CompileErrorCellsHandler aHdl(nErrCode, pDocument->GetGrammar()); + sc::ProcessFormula(maCells, aHdl); + return aHdl.isCompiled(); +} + +void ScColumn::CalcAfterLoad() +{ + CalcAfterLoadHandler aFunc; + sc::ProcessFormula(maCells, aFunc); +} + +void ScColumn::ResetChanged( SCROW nStartRow, SCROW nEndRow ) +{ + ResetChangedHandler aFunc; + sc::ProcessFormula(maCells.begin(), maCells, nStartRow, nEndRow, aFunc); +} + +bool ScColumn::HasEditCells(SCROW nStartRow, SCROW nEndRow, SCROW& rFirst) +{ + // used in GetOptimalHeight - ambiguous script type counts as edit cell + + FindEditCellsHandler aFunc(*this, maCellTextAttrs); + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = + sc::FindFormulaEditText(maCells, nStartRow, nEndRow, aFunc); + + if (aPos.first == maCells.end()) + return false; + + rFirst = aPos.first->position + aPos.second; + return true; } diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx index 7915efd69071..3044ca3e039c 100644 --- a/sc/source/core/data/column2.cxx +++ b/sc/source/core/data/column2.cxx @@ -42,6 +42,7 @@ #include "globalnames.hxx" #include "formulagroup.hxx" #include "listenercontext.hxx" +#include "mtvcellfunc.hxx" #include <math.h> @@ -100,455 +101,519 @@ long ScColumn::GetNeededSize( const Fraction& rZoomX, const Fraction& rZoomY, bool bWidth, const ScNeededSizeOptions& rOptions ) const { - long nValue=0; - SCSIZE nIndex; + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it == maCells.end() || it->type == sc::element_type_empty) + // Empty cell, or invalid row. + return 0; + + long nValue = 0; + ScRefCellValue aCell = GetCellValue(it, aPos.second); double nPPT = bWidth ? nPPTX : nPPTY; - if (Search(nRow,nIndex)) - { - ScRefCellValue aCell; - aCell.assign(*maItems[nIndex].pCell); - const ScPatternAttr* pPattern = rOptions.pPattern; - if (!pPattern) - pPattern = pAttrArray->GetPattern( nRow ); + const ScPatternAttr* pPattern = rOptions.pPattern; + if (!pPattern) + pPattern = pAttrArray->GetPattern( nRow ); - // merged? - // Do not merge in conditional formatting + // merged? + // Do not merge in conditional formatting - const ScMergeAttr* pMerge = (const ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE); - const ScMergeFlagAttr* pFlag = (const ScMergeFlagAttr*)&pPattern->GetItem(ATTR_MERGE_FLAG); + const ScMergeAttr* pMerge = (const ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE); + const ScMergeFlagAttr* pFlag = (const ScMergeFlagAttr*)&pPattern->GetItem(ATTR_MERGE_FLAG); - if ( bWidth ) - { - if ( pFlag->IsHorOverlapped() ) - return 0; - if ( rOptions.bSkipMerged && pMerge->GetColMerge() > 1 ) - return 0; - } - else - { - if ( pFlag->IsVerOverlapped() ) - return 0; - if ( rOptions.bSkipMerged && pMerge->GetRowMerge() > 1 ) - return 0; - } + if ( bWidth ) + { + if ( pFlag->IsHorOverlapped() ) + return 0; + if ( rOptions.bSkipMerged && pMerge->GetColMerge() > 1 ) + return 0; + } + else + { + if ( pFlag->IsVerOverlapped() ) + return 0; + if ( rOptions.bSkipMerged && pMerge->GetRowMerge() > 1 ) + return 0; + } - // conditional formatting - const SfxItemSet* pCondSet = pDocument->GetCondResult( nCol, nRow, nTab ); + // conditional formatting + const SfxItemSet* pCondSet = pDocument->GetCondResult( nCol, nRow, nTab ); - // line break? + // line break? - const SfxPoolItem* pCondItem; - SvxCellHorJustify eHorJust; - if (pCondSet && - pCondSet->GetItemState(ATTR_HOR_JUSTIFY, true, &pCondItem) == SFX_ITEM_SET) - eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem*)pCondItem)->GetValue(); - else - eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem&) - pPattern->GetItem( ATTR_HOR_JUSTIFY )).GetValue(); - bool bBreak; - if ( eHorJust == SVX_HOR_JUSTIFY_BLOCK ) - bBreak = true; - else if ( pCondSet && - pCondSet->GetItemState(ATTR_LINEBREAK, true, &pCondItem) == SFX_ITEM_SET) - bBreak = ((const SfxBoolItem*)pCondItem)->GetValue(); - else - bBreak = ((const SfxBoolItem&)pPattern->GetItem(ATTR_LINEBREAK)).GetValue(); + const SfxPoolItem* pCondItem; + SvxCellHorJustify eHorJust; + if (pCondSet && + pCondSet->GetItemState(ATTR_HOR_JUSTIFY, true, &pCondItem) == SFX_ITEM_SET) + eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem*)pCondItem)->GetValue(); + else + eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem&) + pPattern->GetItem( ATTR_HOR_JUSTIFY )).GetValue(); + bool bBreak; + if ( eHorJust == SVX_HOR_JUSTIFY_BLOCK ) + bBreak = true; + else if ( pCondSet && + pCondSet->GetItemState(ATTR_LINEBREAK, true, &pCondItem) == SFX_ITEM_SET) + bBreak = ((const SfxBoolItem*)pCondItem)->GetValue(); + else + bBreak = ((const SfxBoolItem&)pPattern->GetItem(ATTR_LINEBREAK)).GetValue(); - SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); - sal_uLong nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet ); - // #i111387# disable automatic line breaks only for "General" number format - if (bBreak && aCell.hasNumeric() && ( nFormat % SV_COUNTRY_LANGUAGE_OFFSET ) == 0 ) - { - bBreak = false; - } + SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); + sal_uLong nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet ); + // #i111387# disable automatic line breaks only for "General" number format + if (bBreak && aCell.hasNumeric() && ( nFormat % SV_COUNTRY_LANGUAGE_OFFSET ) == 0 ) + { + bBreak = false; + } - // get other attributes from pattern and conditional formatting + // get other attributes from pattern and conditional formatting - SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet ); - bool bAsianVertical = ( eOrient == SVX_ORIENTATION_STACKED && - ((const SfxBoolItem&)pPattern->GetItem( ATTR_VERTICAL_ASIAN, pCondSet )).GetValue() ); - if ( bAsianVertical ) - bBreak = false; + SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet ); + bool bAsianVertical = ( eOrient == SVX_ORIENTATION_STACKED && + ((const SfxBoolItem&)pPattern->GetItem( ATTR_VERTICAL_ASIAN, pCondSet )).GetValue() ); + if ( bAsianVertical ) + bBreak = false; - if ( bWidth && bBreak ) // after determining bAsianVertical (bBreak may be reset) - return 0; + if ( bWidth && bBreak ) // after determining bAsianVertical (bBreak may be reset) + return 0; - long nRotate = 0; - SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD; - if ( eOrient == SVX_ORIENTATION_STANDARD ) + long nRotate = 0; + SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD; + if ( eOrient == SVX_ORIENTATION_STANDARD ) + { + if (pCondSet && + pCondSet->GetItemState(ATTR_ROTATE_VALUE, true, &pCondItem) == SFX_ITEM_SET) + nRotate = ((const SfxInt32Item*)pCondItem)->GetValue(); + else + nRotate = ((const SfxInt32Item&)pPattern->GetItem(ATTR_ROTATE_VALUE)).GetValue(); + if ( nRotate ) { if (pCondSet && - pCondSet->GetItemState(ATTR_ROTATE_VALUE, true, &pCondItem) == SFX_ITEM_SET) - nRotate = ((const SfxInt32Item*)pCondItem)->GetValue(); + pCondSet->GetItemState(ATTR_ROTATE_MODE, true, &pCondItem) == SFX_ITEM_SET) + eRotMode = (SvxRotateMode)((const SvxRotateModeItem*)pCondItem)->GetValue(); else - nRotate = ((const SfxInt32Item&)pPattern->GetItem(ATTR_ROTATE_VALUE)).GetValue(); - if ( nRotate ) - { - if (pCondSet && - pCondSet->GetItemState(ATTR_ROTATE_MODE, true, &pCondItem) == SFX_ITEM_SET) - eRotMode = (SvxRotateMode)((const SvxRotateModeItem*)pCondItem)->GetValue(); - else - eRotMode = (SvxRotateMode)((const SvxRotateModeItem&) - pPattern->GetItem(ATTR_ROTATE_MODE)).GetValue(); + eRotMode = (SvxRotateMode)((const SvxRotateModeItem&) + pPattern->GetItem(ATTR_ROTATE_MODE)).GetValue(); - if ( nRotate == 18000 ) - eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow - } + if ( nRotate == 18000 ) + eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow } + } - if ( eHorJust == SVX_HOR_JUSTIFY_REPEAT ) - { - // ignore orientation/rotation if "repeat" is active - eOrient = SVX_ORIENTATION_STANDARD; - nRotate = 0; - bAsianVertical = false; - } + if ( eHorJust == SVX_HOR_JUSTIFY_REPEAT ) + { + // ignore orientation/rotation if "repeat" is active + eOrient = SVX_ORIENTATION_STANDARD; + nRotate = 0; + bAsianVertical = false; + } - const SvxMarginItem* pMargin; + const SvxMarginItem* pMargin; + if (pCondSet && + pCondSet->GetItemState(ATTR_MARGIN, true, &pCondItem) == SFX_ITEM_SET) + pMargin = (const SvxMarginItem*) pCondItem; + else + pMargin = (const SvxMarginItem*) &pPattern->GetItem(ATTR_MARGIN); + sal_uInt16 nIndent = 0; + if ( eHorJust == SVX_HOR_JUSTIFY_LEFT ) + { if (pCondSet && - pCondSet->GetItemState(ATTR_MARGIN, true, &pCondItem) == SFX_ITEM_SET) - pMargin = (const SvxMarginItem*) pCondItem; + pCondSet->GetItemState(ATTR_INDENT, true, &pCondItem) == SFX_ITEM_SET) + nIndent = ((const SfxUInt16Item*)pCondItem)->GetValue(); else - pMargin = (const SvxMarginItem*) &pPattern->GetItem(ATTR_MARGIN); - sal_uInt16 nIndent = 0; - if ( eHorJust == SVX_HOR_JUSTIFY_LEFT ) - { - if (pCondSet && - pCondSet->GetItemState(ATTR_INDENT, true, &pCondItem) == SFX_ITEM_SET) - nIndent = ((const SfxUInt16Item*)pCondItem)->GetValue(); - else - nIndent = ((const SfxUInt16Item&)pPattern->GetItem(ATTR_INDENT)).GetValue(); - } + nIndent = ((const SfxUInt16Item&)pPattern->GetItem(ATTR_INDENT)).GetValue(); + } - sal_uInt8 nScript = pDocument->GetScriptType(nCol, nRow, nTab); - if (nScript == 0) nScript = ScGlobal::GetDefaultScriptType(); + sal_uInt8 nScript = pDocument->GetScriptType(nCol, nRow, nTab); + if (nScript == 0) nScript = ScGlobal::GetDefaultScriptType(); - // also call SetFont for edit cells, because bGetFont may be set only once - // bGetFont is set also if script type changes - if (rOptions.bGetFont) - { - Fraction aFontZoom = ( eOrient == SVX_ORIENTATION_STANDARD ) ? rZoomX : rZoomY; - Font aFont; - // font color doesn't matter here - pPattern->GetFont( aFont, SC_AUTOCOL_BLACK, pDev, &aFontZoom, pCondSet, nScript ); - pDev->SetFont(aFont); - } + // also call SetFont for edit cells, because bGetFont may be set only once + // bGetFont is set also if script type changes + if (rOptions.bGetFont) + { + Fraction aFontZoom = ( eOrient == SVX_ORIENTATION_STANDARD ) ? rZoomX : rZoomY; + Font aFont; + // font color doesn't matter here + pPattern->GetFont( aFont, SC_AUTOCOL_BLACK, pDev, &aFontZoom, pCondSet, nScript ); + pDev->SetFont(aFont); + } + + bool bAddMargin = true; + CellType eCellType = aCell.meType; - bool bAddMargin = true; - CellType eCellType = aCell.meType; + bool bEditEngine = (eCellType == CELLTYPE_EDIT || + eOrient == SVX_ORIENTATION_STACKED || + IsAmbiguousScript(nScript) || + ((eCellType == CELLTYPE_FORMULA) && aCell.mpFormula->IsMultilineResult())); - bool bEditEngine = (eCellType == CELLTYPE_EDIT || - eOrient == SVX_ORIENTATION_STACKED || - IsAmbiguousScript(nScript) || - ((eCellType == CELLTYPE_FORMULA) && aCell.mpFormula->IsMultilineResult())); + if (!bEditEngine) // direct output + { + Color* pColor; + OUString aValStr; + ScCellFormat::GetString( + aCell, nFormat, aValStr, &pColor, *pFormatter, true, rOptions.bFormula, ftCheck); - if (!bEditEngine) // direct output + if (!aValStr.isEmpty()) { - Color* pColor; - OUString aValStr; - ScCellFormat::GetString( - aCell, nFormat, aValStr, &pColor, *pFormatter, true, rOptions.bFormula, ftCheck); + // SetFont is moved up - if (!aValStr.isEmpty()) + Size aSize( pDev->GetTextWidth( aValStr ), pDev->GetTextHeight() ); + if ( eOrient != SVX_ORIENTATION_STANDARD ) { - // SetFont is moved up + long nTemp = aSize.Width(); + aSize.Width() = aSize.Height(); + aSize.Height() = nTemp; + } + else if ( nRotate ) + { + //! take different X/Y scaling into consideration - Size aSize( pDev->GetTextWidth( aValStr ), pDev->GetTextHeight() ); - if ( eOrient != SVX_ORIENTATION_STANDARD ) - { - long nTemp = aSize.Width(); - aSize.Width() = aSize.Height(); - aSize.Height() = nTemp; - } - else if ( nRotate ) + double nRealOrient = nRotate * F_PI18000; // nRotate is in 1/100 Grad + double nCosAbs = fabs( cos( nRealOrient ) ); + double nSinAbs = fabs( sin( nRealOrient ) ); + long nHeight = (long)( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs ); + long nWidth; + if ( eRotMode == SVX_ROTATE_MODE_STANDARD ) + nWidth = (long)( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs ); + else if ( rOptions.bTotalSize ) { - //! take different X/Y scaling into consideration - - double nRealOrient = nRotate * F_PI18000; // nRotate is in 1/100 Grad - double nCosAbs = fabs( cos( nRealOrient ) ); - double nSinAbs = fabs( sin( nRealOrient ) ); - long nHeight = (long)( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs ); - long nWidth; - if ( eRotMode == SVX_ROTATE_MODE_STANDARD ) - nWidth = (long)( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs ); - else if ( rOptions.bTotalSize ) - { - nWidth = (long) ( pDocument->GetColWidth( nCol,nTab ) * nPPT ); - bAddMargin = false; - // only to the right: - //! differ on direction up/down (only Text/whole height) - if ( pPattern->GetRotateDir( pCondSet ) == SC_ROTDIR_RIGHT ) - nWidth += (long)( pDocument->GetRowHeight( nRow,nTab ) * - nPPT * nCosAbs / nSinAbs ); - } - else - nWidth = (long)( aSize.Height() / nSinAbs ); //! limit? - - if ( bBreak && !rOptions.bTotalSize ) - { - // limit size for line break - long nCmp = pDev->GetFont().GetSize().Height() * SC_ROT_BREAK_FACTOR; - if ( nHeight > nCmp ) - nHeight = nCmp; - } - - aSize = Size( nWidth, nHeight ); + nWidth = (long) ( pDocument->GetColWidth( nCol,nTab ) * nPPT ); + bAddMargin = false; + // only to the right: + //! differ on direction up/down (only Text/whole height) + if ( pPattern->GetRotateDir( pCondSet ) == SC_ROTDIR_RIGHT ) + nWidth += (long)( pDocument->GetRowHeight( nRow,nTab ) * + nPPT * nCosAbs / nSinAbs ); } - nValue = bWidth ? aSize.Width() : aSize.Height(); + else + nWidth = (long)( aSize.Height() / nSinAbs ); //! limit? - if ( bAddMargin ) + if ( bBreak && !rOptions.bTotalSize ) { - if (bWidth) - { - nValue += (long) ( pMargin->GetLeftMargin() * nPPT ) + - (long) ( pMargin->GetRightMargin() * nPPT ); - if ( nIndent ) - nValue += (long) ( nIndent * nPPT ); - } - else - nValue += (long) ( pMargin->GetTopMargin() * nPPT ) + - (long) ( pMargin->GetBottomMargin() * nPPT ); + // limit size for line break + long nCmp = pDev->GetFont().GetSize().Height() * SC_ROT_BREAK_FACTOR; + if ( nHeight > nCmp ) + nHeight = nCmp; } - // linebreak done ? + aSize = Size( nWidth, nHeight ); + } + nValue = bWidth ? aSize.Width() : aSize.Height(); - if ( bBreak && !bWidth ) + if ( bAddMargin ) + { + if (bWidth) { - // test with EditEngine the safety at 90% - // (due to rounding errors and because EditEngine formats partially differently) - - long nDocPixel = (long) ( ( pDocument->GetColWidth( nCol,nTab ) - - pMargin->GetLeftMargin() - pMargin->GetRightMargin() - - nIndent ) - * nPPT ); - nDocPixel = (nDocPixel * 9) / 10; // for safety - if ( aSize.Width() > nDocPixel ) - bEditEngine = true; + nValue += (long) ( pMargin->GetLeftMargin() * nPPT ) + + (long) ( pMargin->GetRightMargin() * nPPT ); + if ( nIndent ) + nValue += (long) ( nIndent * nPPT ); } + else + nValue += (long) ( pMargin->GetTopMargin() * nPPT ) + + (long) ( pMargin->GetBottomMargin() * nPPT ); + } + + // linebreak done ? + + if ( bBreak && !bWidth ) + { + // test with EditEngine the safety at 90% + // (due to rounding errors and because EditEngine formats partially differently) + + long nDocPixel = (long) ( ( pDocument->GetColWidth( nCol,nTab ) - + pMargin->GetLeftMargin() - pMargin->GetRightMargin() - + nIndent ) + * nPPT ); + nDocPixel = (nDocPixel * 9) / 10; // for safety + if ( aSize.Width() > nDocPixel ) + bEditEngine = true; } } + } - if (bEditEngine) - { - // the font is not reset each time with !bEditEngine - Font aOldFont = pDev->GetFont(); + if (bEditEngine) + { + // the font is not reset each time with !bEditEngine + Font aOldFont = pDev->GetFont(); - MapMode aHMMMode( MAP_100TH_MM, Point(), rZoomX, rZoomY ); + MapMode aHMMMode( MAP_100TH_MM, Point(), rZoomX, rZoomY ); - // save in document ? - ScFieldEditEngine* pEngine = pDocument->CreateFieldEditEngine(); + // save in document ? + ScFieldEditEngine* pEngine = pDocument->CreateFieldEditEngine(); - pEngine->SetUpdateMode( false ); - bool bTextWysiwyg = ( pDev->GetOutDevType() == OUTDEV_PRINTER ); - sal_uLong nCtrl = pEngine->GetControlWord(); - if ( bTextWysiwyg ) - nCtrl |= EE_CNTRL_FORMAT100; - else - nCtrl &= ~EE_CNTRL_FORMAT100; - pEngine->SetControlWord( nCtrl ); - MapMode aOld = pDev->GetMapMode(); - pDev->SetMapMode( aHMMMode ); - pEngine->SetRefDevice( pDev ); - pDocument->ApplyAsianEditSettings( *pEngine ); - SfxItemSet* pSet = new SfxItemSet( pEngine->GetEmptyItemSet() ); - pPattern->FillEditItemSet( pSet, pCondSet ); + pEngine->SetUpdateMode( false ); + bool bTextWysiwyg = ( pDev->GetOutDevType() == OUTDEV_PRINTER ); + sal_uLong nCtrl = pEngine->GetControlWord(); + if ( bTextWysiwyg ) + nCtrl |= EE_CNTRL_FORMAT100; + else + nCtrl &= ~EE_CNTRL_FORMAT100; + pEngine->SetControlWord( nCtrl ); + MapMode aOld = pDev->GetMapMode(); + pDev->SetMapMode( aHMMMode ); + pEngine->SetRefDevice( pDev ); + pDocument->ApplyAsianEditSettings( *pEngine ); + SfxItemSet* pSet = new SfxItemSet( pEngine->GetEmptyItemSet() ); + pPattern->FillEditItemSet( pSet, pCondSet ); // no longer needed, are setted with the text (is faster) // pEngine->SetDefaults( pSet ); - if ( ((const SfxBoolItem&)pSet->Get(EE_PARA_HYPHENATE)).GetValue() ) { + if ( ((const SfxBoolItem&)pSet->Get(EE_PARA_HYPHENATE)).GetValue() ) { - com::sun::star::uno::Reference<com::sun::star::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() ); - pEngine->SetHyphenator( xXHyphenator ); - } + com::sun::star::uno::Reference<com::sun::star::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() ); + pEngine->SetHyphenator( xXHyphenator ); + } - Size aPaper = Size( 1000000, 1000000 ); - if ( eOrient==SVX_ORIENTATION_STACKED && !bAsianVertical ) - aPaper.Width() = 1; - else if (bBreak) + Size aPaper = Size( 1000000, 1000000 ); + if ( eOrient==SVX_ORIENTATION_STACKED && !bAsianVertical ) + aPaper.Width() = 1; + else if (bBreak) + { + double fWidthFactor = nPPTX; + if ( bTextWysiwyg ) { - double fWidthFactor = nPPTX; - if ( bTextWysiwyg ) - { - // if text is formatted for printer, don't use PixelToLogic, - // to ensure the exact same paper width (and same line breaks) as in - // ScEditUtil::GetEditArea, used for output. + // if text is formatted for printer, don't use PixelToLogic, + // to ensure the exact same paper width (and same line breaks) as in + // ScEditUtil::GetEditArea, used for output. - fWidthFactor = HMM_PER_TWIPS; - } - - // use original width for hidden columns: - long nDocWidth = (long) ( pDocument->GetOriginalWidth(nCol,nTab) * fWidthFactor ); - SCCOL nColMerge = pMerge->GetColMerge(); - if (nColMerge > 1) - for (SCCOL nColAdd=1; nColAdd<nColMerge; nColAdd++) - nDocWidth += (long) ( pDocument->GetColWidth(nCol+nColAdd,nTab) * fWidthFactor ); - nDocWidth -= (long) ( pMargin->GetLeftMargin() * fWidthFactor ) - + (long) ( pMargin->GetRightMargin() * fWidthFactor ) - + 1; // output size is width-1 pixel (due to gridline) - if ( nIndent ) - nDocWidth -= (long) ( nIndent * fWidthFactor ); - - // space for AutoFilter button: 20 * nZoom/100 - if ( pFlag->HasAutoFilter() && !bTextWysiwyg ) - nDocWidth -= (rZoomX.GetNumerator()*20)/rZoomX.GetDenominator(); - - aPaper.Width() = nDocWidth; - - if ( !bTextWysiwyg ) - aPaper = pDev->PixelToLogic( aPaper, aHMMMode ); + fWidthFactor = HMM_PER_TWIPS; } - pEngine->SetPaperSize(aPaper); - if (aCell.meType == CELLTYPE_EDIT) - { - pEngine->SetTextNewDefaults(*aCell.mpEditText, pSet); - } + // use original width for hidden columns: + long nDocWidth = (long) ( pDocument->GetOriginalWidth(nCol,nTab) * fWidthFactor ); + SCCOL nColMerge = pMerge->GetColMerge(); + if (nColMerge > 1) + for (SCCOL nColAdd=1; nColAdd<nColMerge; nColAdd++) + nDocWidth += (long) ( pDocument->GetColWidth(nCol+nColAdd,nTab) * fWidthFactor ); + nDocWidth -= (long) ( pMargin->GetLeftMargin() * fWidthFactor ) + + (long) ( pMargin->GetRightMargin() * fWidthFactor ) + + 1; // output size is width-1 pixel (due to gridline) + if ( nIndent ) + nDocWidth -= (long) ( nIndent * fWidthFactor ); + + // space for AutoFilter button: 20 * nZoom/100 + if ( pFlag->HasAutoFilter() && !bTextWysiwyg ) + nDocWidth -= (rZoomX.GetNumerator()*20)/rZoomX.GetDenominator(); + + aPaper.Width() = nDocWidth; + + if ( !bTextWysiwyg ) + aPaper = pDev->PixelToLogic( aPaper, aHMMMode ); + } + pEngine->SetPaperSize(aPaper); + + if (aCell.meType == CELLTYPE_EDIT) + { + pEngine->SetTextNewDefaults(*aCell.mpEditText, pSet); + } + else + { + Color* pColor; + OUString aString; + ScCellFormat::GetString( + aCell, nFormat, aString, &pColor, *pFormatter, true, + rOptions.bFormula, ftCheck); + + if (!aString.isEmpty()) + pEngine->SetTextNewDefaults(aString, pSet); else + pEngine->SetDefaults(pSet); + } + + bool bEngineVertical = pEngine->IsVertical(); + pEngine->SetVertical( bAsianVertical ); + pEngine->SetUpdateMode( true ); + + bool bEdWidth = bWidth; + if ( eOrient != SVX_ORIENTATION_STANDARD && eOrient != SVX_ORIENTATION_STACKED ) + bEdWidth = !bEdWidth; + if ( nRotate ) + { + //! take different X/Y scaling into consideration + + Size aSize( pEngine->CalcTextWidth(), pEngine->GetTextHeight() ); + double nRealOrient = nRotate * F_PI18000; // nRotate is in 1/100 Grad + double nCosAbs = fabs( cos( nRealOrient ) ); + double nSinAbs = fabs( sin( nRealOrient ) ); + long nHeight = (long)( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs ); + long nWidth; + if ( eRotMode == SVX_ROTATE_MODE_STANDARD ) + nWidth = (long)( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs ); + else if ( rOptions.bTotalSize ) { - Color* pColor; - OUString aString; - ScCellFormat::GetString( - aCell, nFormat, aString, &pColor, *pFormatter, true, - rOptions.bFormula, ftCheck); - - if (!aString.isEmpty()) - pEngine->SetTextNewDefaults(aString, pSet); - else - pEngine->SetDefaults(pSet); + nWidth = (long) ( pDocument->GetColWidth( nCol,nTab ) * nPPT ); + bAddMargin = false; + if ( pPattern->GetRotateDir( pCondSet ) == SC_ROTDIR_RIGHT ) + nWidth += (long)( pDocument->GetRowHeight( nRow,nTab ) * + nPPT * nCosAbs / nSinAbs ); } + else + nWidth = (long)( aSize.Height() / nSinAbs ); //! limit? + aSize = Size( nWidth, nHeight ); - bool bEngineVertical = pEngine->IsVertical(); - pEngine->SetVertical( bAsianVertical ); - pEngine->SetUpdateMode( true ); - - bool bEdWidth = bWidth; - if ( eOrient != SVX_ORIENTATION_STANDARD && eOrient != SVX_ORIENTATION_STACKED ) - bEdWidth = !bEdWidth; - if ( nRotate ) + Size aPixSize = pDev->LogicToPixel( aSize, aHMMMode ); + if ( bEdWidth ) + nValue = aPixSize.Width(); + else { - //! take different X/Y scaling into consideration + nValue = aPixSize.Height(); - Size aSize( pEngine->CalcTextWidth(), pEngine->GetTextHeight() ); - double nRealOrient = nRotate * F_PI18000; // nRotate is in 1/100 Grad - double nCosAbs = fabs( cos( nRealOrient ) ); - double nSinAbs = fabs( sin( nRealOrient ) ); - long nHeight = (long)( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs ); - long nWidth; - if ( eRotMode == SVX_ROTATE_MODE_STANDARD ) - nWidth = (long)( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs ); - else if ( rOptions.bTotalSize ) + if ( bBreak && !rOptions.bTotalSize ) { - nWidth = (long) ( pDocument->GetColWidth( nCol,nTab ) * nPPT ); - bAddMargin = false; - if ( pPattern->GetRotateDir( pCondSet ) == SC_ROTDIR_RIGHT ) - nWidth += (long)( pDocument->GetRowHeight( nRow,nTab ) * - nPPT * nCosAbs / nSinAbs ); + // limit size for line break + long nCmp = aOldFont.GetSize().Height() * SC_ROT_BREAK_FACTOR; + if ( nValue > nCmp ) + nValue = nCmp; } - else - nWidth = (long)( aSize.Height() / nSinAbs ); //! limit? - aSize = Size( nWidth, nHeight ); - - Size aPixSize = pDev->LogicToPixel( aSize, aHMMMode ); - if ( bEdWidth ) - nValue = aPixSize.Width(); - else - { - nValue = aPixSize.Height(); + } + } + else if ( bEdWidth ) + { + if (bBreak) + nValue = 0; + else + nValue = pDev->LogicToPixel(Size( pEngine->CalcTextWidth(), 0 ), + aHMMMode).Width(); + } + else // height + { + nValue = pDev->LogicToPixel(Size( 0, pEngine->GetTextHeight() ), + aHMMMode).Height(); - if ( bBreak && !rOptions.bTotalSize ) - { - // limit size for line break - long nCmp = aOldFont.GetSize().Height() * SC_ROT_BREAK_FACTOR; - if ( nValue > nCmp ) - nValue = nCmp; - } - } + // With non-100% zoom and several lines or paragraphs, don't shrink below the result with FORMAT100 set + if ( !bTextWysiwyg && ( rZoomY.GetNumerator() != 1 || rZoomY.GetDenominator() != 1 ) && + ( pEngine->GetParagraphCount() > 1 || ( bBreak && pEngine->GetLineCount(0) > 1 ) ) ) + { + pEngine->SetControlWord( nCtrl | EE_CNTRL_FORMAT100 ); + pEngine->QuickFormatDoc( sal_True ); + long nSecondValue = pDev->LogicToPixel(Size( 0, pEngine->GetTextHeight() ), aHMMMode).Height(); + if ( nSecondValue > nValue ) + nValue = nSecondValue; } - else if ( bEdWidth ) + } + + if ( nValue && bAddMargin ) + { + if (bWidth) { - if (bBreak) - nValue = 0; - else - nValue = pDev->LogicToPixel(Size( pEngine->CalcTextWidth(), 0 ), - aHMMMode).Width(); + nValue += (long) ( pMargin->GetLeftMargin() * nPPT ) + + (long) ( pMargin->GetRightMargin() * nPPT ); + if (nIndent) + nValue += (long) ( nIndent * nPPT ); } - else // height + else { - nValue = pDev->LogicToPixel(Size( 0, pEngine->GetTextHeight() ), - aHMMMode).Height(); + nValue += (long) ( pMargin->GetTopMargin() * nPPT ) + + (long) ( pMargin->GetBottomMargin() * nPPT ); - // With non-100% zoom and several lines or paragraphs, don't shrink below the result with FORMAT100 set - if ( !bTextWysiwyg && ( rZoomY.GetNumerator() != 1 || rZoomY.GetDenominator() != 1 ) && - ( pEngine->GetParagraphCount() > 1 || ( bBreak && pEngine->GetLineCount(0) > 1 ) ) ) + if ( bAsianVertical && pDev->GetOutDevType() != OUTDEV_PRINTER ) { - pEngine->SetControlWord( nCtrl | EE_CNTRL_FORMAT100 ); - pEngine->QuickFormatDoc( sal_True ); - long nSecondValue = pDev->LogicToPixel(Size( 0, pEngine->GetTextHeight() ), aHMMMode).Height(); - if ( nSecondValue > nValue ) - nValue = nSecondValue; + // add 1pt extra (default margin value) for line breaks with SetVertical + nValue += (long) ( 20 * nPPT ); } } + } - if ( nValue && bAddMargin ) - { - if (bWidth) - { - nValue += (long) ( pMargin->GetLeftMargin() * nPPT ) + - (long) ( pMargin->GetRightMargin() * nPPT ); - if (nIndent) - nValue += (long) ( nIndent * nPPT ); - } - else - { - nValue += (long) ( pMargin->GetTopMargin() * nPPT ) + - (long) ( pMargin->GetBottomMargin() * nPPT ); + // EditEngine is cached and re-used, so the old vertical flag must be restored + pEngine->SetVertical( bEngineVertical ); - if ( bAsianVertical && pDev->GetOutDevType() != OUTDEV_PRINTER ) - { - // add 1pt extra (default margin value) for line breaks with SetVertical - nValue += (long) ( 20 * nPPT ); - } - } - } + pDocument->DisposeFieldEditEngine(pEngine); - // EditEngine is cached and re-used, so the old vertical flag must be restored - pEngine->SetVertical( bEngineVertical ); + pDev->SetMapMode( aOld ); + pDev->SetFont( aOldFont ); + } - pDocument->DisposeFieldEditEngine(pEngine); + if (bWidth) + { + // place for Autofilter Button + // 20 * nZoom/100 + // Conditional formatting is not interesting here - pDev->SetMapMode( aOld ); - pDev->SetFont( aOldFont ); - } + sal_Int16 nFlags = ((const ScMergeFlagAttr&)pPattern->GetItem(ATTR_MERGE_FLAG)).GetValue(); + if (nFlags & SC_MF_AUTO) + nValue += (rZoomX.GetNumerator()*20)/rZoomX.GetDenominator(); + } + return nValue; +} - if (bWidth) - { - // place for Autofilter Button - // 20 * nZoom/100 - // Conditional formatting is not interesting here +namespace { + +class MaxStrLenFinder +{ + ScDocument& mrDoc; + sal_uInt32 mnFormat; + OUString maMaxLenStr; + sal_Int32 mnMaxLen; - sal_Int16 nFlags = ((const ScMergeFlagAttr&)pPattern->GetItem(ATTR_MERGE_FLAG)).GetValue(); - if (nFlags & SC_MF_AUTO) - nValue += (rZoomX.GetNumerator()*20)/rZoomX.GetDenominator(); + void checkLength(ScRefCellValue& rCell) + { + Color* pColor; + OUString aValStr; + ScCellFormat::GetString( + rCell, mnFormat, aValStr, &pColor, *mrDoc.GetFormatTable(), true, false, ftCheck); + + if (aValStr.getLength() > mnMaxLen) + { + mnMaxLen = aValStr.getLength(); + maMaxLenStr = aValStr; } } - return nValue; -} +public: + MaxStrLenFinder(ScDocument& rDoc, sal_uInt32 nFormat) : + mrDoc(rDoc), mnFormat(nFormat), mnMaxLen(0) {} + + void operator() (size_t /*nRow*/, double f) + { + ScRefCellValue aCell(f); + checkLength(aCell); + } + + void operator() (size_t /*nRow*/, const OUString& rStr) + { + ScRefCellValue aCell(&rStr); + checkLength(aCell); + } + + void operator() (size_t /*nRow*/, const EditTextObject* p) + { + ScRefCellValue aCell(p); + checkLength(aCell); + } + + void operator() (size_t /*nRow*/, const ScFormulaCell* p) + { + ScRefCellValue aCell(const_cast<ScFormulaCell*>(p)); + checkLength(aCell); + } +}; + +} sal_uInt16 ScColumn::GetOptimalColWidth( OutputDevice* pDev, double nPPTX, double nPPTY, const Fraction& rZoomX, const Fraction& rZoomY, bool bFormula, sal_uInt16 nOldWidth, const ScMarkData* pMarkData, const ScColWidthParam* pParam) const { - if ( maItems.empty() ) + if (maCells.block_size() == 1 && maCells.begin()->type == sc::element_type_empty) + // All cells are empty. return nOldWidth; - sal_uInt16 nWidth = (sal_uInt16) (nOldWidth * nPPTX); - bool bFound = false; + sc::SingleColumnSpanSet aSpanSet; + sc::SingleColumnSpanSet::SpansType aMarkedSpans; + if (pMarkData) + { + aSpanSet.scan(*pMarkData, nTab, nCol); + aSpanSet.getSpans(aMarkedSpans); + } + else + // "Select" the entire column if no selection exists. + aMarkedSpans.push_back(sc::SingleColumnSpanSet::Span(0, MAXROW)); + + sal_uInt16 nWidth = static_cast<sal_uInt16>(nOldWidth*nPPTX); + bool bFound = false; - SCSIZE nIndex; - ScMarkedDataIter aDataIter(this, pMarkData, true); if ( pParam && pParam->mbSimpleText ) { // all the same except for number format const ScPatternAttr* pPattern = GetPattern( 0 ); @@ -573,25 +638,12 @@ sal_uInt16 ScColumn::GetOptimalColWidth( } else { - xub_StrLen nLongLen = 0; - while (aDataIter.Next(nIndex)) - { - if (nIndex >= maItems.size()) - // Out-of-bound reached. No need to keep going. - break; - - ScRefCellValue aCell; - aCell.assign(*maItems[nIndex].pCell); - OUString aValStr; - ScCellFormat::GetString( - aCell, nFormat, aValStr, &pColor, *pFormatter, true, false, ftCheck); - - if (aValStr.getLength() > nLongLen) - { - nLongLen = aValStr.getLength(); - aLongStr = aValStr; - } - } + // Go though all non-empty cells within selection. + MaxStrLenFinder aFunc(*pDocument, nFormat); + sc::CellStoreType::const_iterator itPos = maCells.begin(); + sc::SingleColumnSpanSet::SpansType::const_iterator it = aMarkedSpans.begin(), itEnd = aMarkedSpans.end(); + for (; it != itEnd; ++it) + itPos = sc::ParseAllNonEmpty(itPos, maCells, it->mnRow1, it->mnRow2, aFunc); } if (!aLongStr.isEmpty()) @@ -607,25 +659,44 @@ sal_uInt16 ScColumn::GetOptimalColWidth( const ScPatternAttr* pOldPattern = NULL; sal_uInt8 nOldScript = 0; - while (aDataIter.Next( nIndex )) + // Go though all non-empty cells within selection. + sc::CellStoreType::const_iterator itPos = maCells.begin(); + sc::SingleColumnSpanSet::SpansType::const_iterator it = aMarkedSpans.begin(), itEnd = aMarkedSpans.end(); + for (; it != itEnd; ++it) { - SCROW nRow = maItems[nIndex].nRow; - - sal_uInt8 nScript = pDocument->GetScriptType(nCol, nRow, nTab); - if (nScript == 0) nScript = ScGlobal::GetDefaultScriptType(); - - const ScPatternAttr* pPattern = GetPattern( nRow ); - aOptions.pPattern = pPattern; - aOptions.bGetFont = (pPattern != pOldPattern || nScript != nOldScript); - sal_uInt16 nThis = (sal_uInt16) GetNeededSize( nRow, pDev, nPPTX, nPPTY, - rZoomX, rZoomY, true, aOptions ); - pOldPattern = pPattern; - if (nThis) + SCROW nRow1 = it->mnRow1, nRow2 = it->mnRow2; + SCROW nRow = nRow1; + while (nRow <= nRow2) { - if (nThis>nWidth || !bFound) + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow); + itPos = aPos.first; + if (itPos->type == sc::element_type_empty) { - nWidth = nThis; - bFound = true; + // Skip empty cells. + nRow += itPos->size - aPos.second; + continue; + } + + for (size_t nOffset = aPos.second; nOffset < itPos->size; ++nOffset, ++nRow) + { + sal_uInt8 nScript = pDocument->GetScriptType(nCol, nRow, nTab); + if (nScript == 0) + nScript = ScGlobal::GetDefaultScriptType(); + + const ScPatternAttr* pPattern = GetPattern(nRow); + aOptions.pPattern = pPattern; + aOptions.bGetFont = (pPattern != pOldPattern || nScript != nOldScript); + sal_uInt16 nThis = (sal_uInt16) GetNeededSize( + nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, true, aOptions); + pOldPattern = pPattern; + if (nThis) + { + if (nThis > nWidth || !bFound) + { + nWidth = nThis; + bFound = true; + } + } } } } @@ -704,7 +775,6 @@ void ScColumn::GetOptimalHeight( } else { - SCROW nRow = 0; bool bStdAllowed = (pPattern->GetCellOrientation() == SVX_ORIENTATION_STANDARD); bool bStdOnly = false; if (bStdAllowed) @@ -747,6 +817,11 @@ void ScColumn::GetOptimalHeight( } } + sc::SingleColumnSpanSet aSpanSet; + aSpanSet.scan(*this, nStart, nEnd); + sc::SingleColumnSpanSet::SpansType aSpans; + aSpanSet.getSpans(aSpans); + if (bStdAllowed) { sal_uInt16 nLatHeight = 0; @@ -767,7 +842,7 @@ void ScColumn::GetOptimalHeight( if ( nDefHeight <= nMinHeight && nStdEnd >= nMinStart ) nStdEnd = (nMinStart>0) ? nMinStart-1 : 0; - for (nRow=nStart; nRow<=nStdEnd; nRow++) + for (SCROW nRow = nStart; nRow <= nStdEnd; ++nRow) if (nDefHeight > pHeight[nRow-nStartRow]) pHeight[nRow-nStartRow] = nDefHeight; @@ -775,15 +850,16 @@ void ScColumn::GetOptimalHeight( { // if cells are not handled individually below, // check for cells with different script type - - SCSIZE nIndex; - Search(nStart,nIndex); sc::CellTextAttrStoreType::iterator itAttr = maCellTextAttrs.begin(); - while ( nIndex < maItems.size() && (nRow=maItems[nIndex].nRow) <= nEnd ) + sc::SingleColumnSpanSet::SpansType::const_iterator it = aSpans.begin(), itEnd = aSpans.end(); + for (; it != itEnd; ++it) { - sal_uInt8 nScript = GetRangeScriptType(itAttr, nRow, nRow); - if ( nScript != nDefScript ) + for (SCROW nRow = it->mnRow1; nRow <= it->mnRow2; ++nRow) { + sal_uInt8 nScript = GetRangeScriptType(itAttr, nRow, nRow); + if (nScript == nDefScript) + continue; + if ( nScript == SCRIPTTYPE_ASIAN ) { if ( nCjkHeight == 0 ) @@ -806,7 +882,6 @@ void ScColumn::GetOptimalHeight( pHeight[nRow-nStartRow] = nLatHeight; } } - ++nIndex; } } } @@ -815,22 +890,23 @@ void ScColumn::GetOptimalHeight( { ScNeededSizeOptions aOptions; - SCSIZE nIndex; - Search(nStart,nIndex); - while ( (nIndex < maItems.size()) ? ((nRow=maItems[nIndex].nRow) <= nEnd) : false ) + sc::SingleColumnSpanSet::SpansType::const_iterator it = aSpans.begin(), itEnd = aSpans.end(); + for (; it != itEnd; ++it) { - // only calculate the cell height when it's used later (#37928#) - - if ( bShrink || !(pDocument->GetRowFlags(nRow, nTab) & CR_MANUALSIZE) ) + for (SCROW nRow = it->mnRow1; nRow <= it->mnRow2; ++nRow) { - aOptions.pPattern = pPattern; - sal_uInt16 nHeight = (sal_uInt16) - ( GetNeededSize( nRow, pDev, nPPTX, nPPTY, - rZoomX, rZoomY, false, aOptions ) / nPPTY ); - if (nHeight > pHeight[nRow-nStartRow]) - pHeight[nRow-nStartRow] = nHeight; + // only calculate the cell height when it's used later (#37928#) + + if ( bShrink || !(pDocument->GetRowFlags(nRow, nTab) & CR_MANUALSIZE) ) + { + aOptions.pPattern = pPattern; + sal_uInt16 nHeight = (sal_uInt16) + ( GetNeededSize( nRow, pDev, nPPTX, nPPTY, + rZoomX, rZoomY, false, aOptions ) / nPPTY ); + if (nHeight > pHeight[nRow-nStartRow]) + pHeight[nRow-nStartRow] = nHeight; + } } - ++nIndex; } } } @@ -849,15 +925,14 @@ void ScColumn::GetOptimalHeight( bool ScColumn::GetNextSpellingCell(SCROW& nRow, bool bInSel, const ScMarkData& rData) const { bool bStop = false; - CellType eCellType; - SCSIZE nIndex; - if (!bInSel && Search(nRow, nIndex)) + sc::CellStoreType::const_iterator it = maCells.position(nRow).first; + mdds::mtv::element_t eType = it->type; + if (!bInSel && it != maCells.end() && eType != sc::element_type_empty) { - eCellType = GetCellType(nRow); - if ( (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT) && + if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) && !(HasAttrib( nRow, nRow, HASATTR_PROTECTED) && pDocument->IsTabProtected(nTab)) ) - return true; + return true; } while (!bStop) { @@ -871,22 +946,24 @@ bool ScColumn::GetNextSpellingCell(SCROW& nRow, bool bInSel, const ScMarkData& r } else { - eCellType = GetCellType(nRow); - if ( (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT) && + it = maCells.position(it, nRow).first; + eType = it->type; + if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) && !(HasAttrib( nRow, nRow, HASATTR_PROTECTED) && pDocument->IsTabProtected(nTab)) ) - return true; + return true; else nRow++; } } else if (GetNextDataPos(nRow)) { - eCellType = GetCellType(nRow); - if ( (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT) && + it = maCells.position(it, nRow).first; + eType = it->type; + if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) && !(HasAttrib( nRow, nRow, HASATTR_PROTECTED) && pDocument->IsTabProtected(nTab)) ) - return true; + return true; else nRow++; } @@ -899,271 +976,218 @@ bool ScColumn::GetNextSpellingCell(SCROW& nRow, bool bInSel, const ScMarkData& r return false; } -// ========================================================================================= +namespace { -void ScColumn::RemoveAutoSpellObj() +class StrEntries { - boost::scoped_ptr<ScTabEditEngine> pEngine; - - for (SCSIZE i=0; i<maItems.size(); i++) - if ( maItems[i].pCell->GetCellType() == CELLTYPE_EDIT ) - { - ScEditCell* pOldCell = (ScEditCell*) maItems[i].pCell; - const EditTextObject* pData = pOldCell->GetData(); - // no query on HasOnlineSpellErrors, this makes it also work after loading - - // For the test on hard formatting (ScEditAttrTester), are the defaults in the - // EditEngine of no importance. When the tester would later recognise the same - // attributes in default and hard formatting and has to remove them, the correct - // defaults must be set in the EditEngine for each cell. - - // test for attributes - if ( !pEngine ) - pEngine.reset(new ScTabEditEngine(pDocument)); - pEngine->SetText( *pData ); - ScEditAttrTester aTester(pEngine.get()); - if ( aTester.NeedsObject() ) // only remove spelling errors - { - pOldCell->SetData(pEngine->CreateTextObject()); - } - else // create a string - { - OUString aText = ScEditUtil::GetSpaceDelimitedString(*pEngine); - ScBaseCell* pNewCell = new ScStringCell( aText ); - maItems[i].pCell = pNewCell; - delete pOldCell; - } - } -} + sc::CellStoreType& mrCells; -void ScColumn::RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow ) -{ - boost::scoped_ptr<ScFieldEditEngine> pEngine; +protected: + struct StrEntry + { + SCROW mnRow; + OUString maStr; - SCSIZE i; - Search( nStartRow, i ); - for (; i<maItems.size() && maItems[i].nRow <= nEndRow; i++) - if ( maItems[i].pCell->GetCellType() == CELLTYPE_EDIT ) - { - ScEditCell* pOldCell = (ScEditCell*) maItems[i].pCell; - const EditTextObject* pData = pOldCell->GetData(); + StrEntry(SCROW nRow, const OUString& rStr) : mnRow(nRow), maStr(rStr) {} + }; - // For the test on hard formatting (ScEditAttrTester), are the defaults in the - // EditEngine of no importance. When the tester would later recognise the same - // attributes in default and hard formatting and has to remove them, the correct - // defaults must be set in the EditEngine for each cell. + std::vector<StrEntry> maStrEntries; - // test for attributes - if ( !pEngine ) - { - pEngine.reset(new ScFieldEditEngine(pDocument, pDocument->GetEditPool())); - // EE_CNTRL_ONLINESPELLING if there are errors already - pEngine->SetControlWord( pEngine->GetControlWord() | EE_CNTRL_ONLINESPELLING ); - pDocument->ApplyAsianEditSettings( *pEngine ); - } - pEngine->SetText( *pData ); - sal_Int32 nParCount = pEngine->GetParagraphCount(); - for (sal_Int32 nPar=0; nPar<nParCount; nPar++) - { - pEngine->QuickRemoveCharAttribs( nPar ); - const SfxItemSet& rOld = pEngine->GetParaAttribs( nPar ); - if ( rOld.Count() ) - { - SfxItemSet aNew( *rOld.GetPool(), rOld.GetRanges() ); // empty - pEngine->SetParaAttribs( nPar, aNew ); - } - } - // change URL field to text (not possible otherwise, thus pType=0) - pEngine->RemoveFields( true ); + StrEntries(sc::CellStoreType& rCells) : mrCells(rCells) {} - bool bSpellErrors = pEngine->HasOnlineSpellErrors(); - bool bNeedObject = bSpellErrors || nParCount>1; // keep errors/paragraphs - // ScEditAttrTester is not needed anymore, arrays are gone +public: + void commitStrings() + { + sc::CellStoreType::iterator it = mrCells.begin(); + std::vector<StrEntry>::iterator itStr = maStrEntries.begin(), itStrEnd = maStrEntries.end(); + for (; itStr != itStrEnd; ++itStr) + it = mrCells.set(it, itStr->mnRow, itStr->maStr); + } +}; - if ( bNeedObject ) // remains edit cell - { - sal_uInt32 nCtrl = pEngine->GetControlWord(); - sal_uInt32 nWantBig = bSpellErrors ? EE_CNTRL_ALLOWBIGOBJS : 0; - if ( ( nCtrl & EE_CNTRL_ALLOWBIGOBJS ) != nWantBig ) - pEngine->SetControlWord( (nCtrl & ~EE_CNTRL_ALLOWBIGOBJS) | nWantBig ); - pOldCell->SetData(pEngine->CreateTextObject()); - } - else // create String - { - String aText = ScEditUtil::GetSpaceDelimitedString( *pEngine ); - ScBaseCell* pNewCell = new ScStringCell( aText ); - maItems[i].pCell = pNewCell; - delete pOldCell; - } - } -} +class RemoveAutoSpellObjHandler : public StrEntries +{ + ScDocument* mpDoc; + boost::scoped_ptr<ScTabEditEngine> mpEngine; -// ========================================================================================= +public: + RemoveAutoSpellObjHandler(sc::CellStoreType& rCells, ScDocument* pDoc) : StrEntries(rCells), mpDoc(pDoc) {} -bool ScColumn::TestTabRefAbs(SCTAB nTable) const -{ - bool bRet = false; - if ( !maItems.empty() ) - for (SCSIZE i = 0; i < maItems.size(); i++) - if ( maItems[i].pCell->GetCellType() == CELLTYPE_FORMULA ) - if (((ScFormulaCell*)maItems[i].pCell)->TestTabRefAbs(nTable)) - bRet = true; - return bRet; -} + void operator() (size_t nRow, EditTextObject*& pObj) + { + // no query on HasOnlineSpellErrors, this makes it also work after loading -// ========================================================================================= + // For the test on hard formatting (ScEditAttrTester), are the defaults in the + // EditEngine of no importance. When the tester would later recognise the same + // attributes in default and hard formatting and has to remove them, the correct + // defaults must be set in the EditEngine for each cell. -ScColumnIterator::ScColumnIterator( const ScColumn* pCol, SCROW nStart, SCROW nEnd ) : - pColumn( pCol ), - nTop( nStart ), - nBottom( nEnd ) -{ - pColumn->Search( nTop, nPos ); -} + // test for attributes + if (!mpEngine) + mpEngine.reset(new ScTabEditEngine(mpDoc)); -ScColumnIterator::~ScColumnIterator() -{ -} + mpEngine->SetText(*pObj); -bool ScColumnIterator::Next( SCROW& rRow, ScBaseCell*& rpCell ) -{ - if ( nPos < pColumn->maItems.size() ) - { - rRow = pColumn->maItems[nPos].nRow; - if ( rRow <= nBottom ) + ScEditAttrTester aTester(mpEngine.get()); + if (aTester.NeedsObject()) // only remove spelling errors { - rpCell = pColumn->maItems[nPos].pCell; - ++nPos; - return true; + // Overwrite the existing object. + delete pObj; + pObj = mpEngine->CreateTextObject(); + } + else + { + // Store the string replacement for later commits. + OUString aText = ScEditUtil::GetSpaceDelimitedString(*mpEngine); + maStrEntries.push_back(StrEntry(nRow, aText)); } } +}; - rRow = 0; - rpCell = NULL; - return false; -} - -SCSIZE ScColumnIterator::GetIndex() const // Index of the last cell asked -{ - return nPos - 1; // next time the position is incremented -} - -// ----------------------------------------------------------------------------------------- - -ScMarkedDataIter::ScMarkedDataIter( const ScColumn* pCol, const ScMarkData* pMarkData, - bool bAllIfNone ) : - pColumn( pCol ), - pMarkIter( NULL ), - bNext( true ), - bAll( bAllIfNone ) +class RemoveEditAttribsHandler : public StrEntries { - if (pMarkData && pMarkData->IsMultiMarked()) - pMarkIter = new ScMarkArrayIter( pMarkData->GetArray() + pCol->GetCol() ); -} + ScDocument* mpDoc; + boost::scoped_ptr<ScFieldEditEngine> mpEngine; -ScMarkedDataIter::~ScMarkedDataIter() -{ - delete pMarkIter; -} +public: + RemoveEditAttribsHandler(sc::CellStoreType& rCells, ScDocument* pDoc) : StrEntries(rCells), mpDoc(pDoc) {} -bool ScMarkedDataIter::Next( SCSIZE& rIndex ) -{ - bool bFound = false; - do + void operator() (size_t nRow, EditTextObject*& pObj) { - if (bNext) - { - if (!pMarkIter || !pMarkIter->Next( nTop, nBottom )) + // For the test on hard formatting (ScEditAttrTester), are the defaults in the + // EditEngine of no importance. When the tester would later recognise the same + // attributes in default and hard formatting and has to remove them, the correct + // defaults must be set in the EditEngine for each cell. + + // test for attributes + if (!mpEngine) + { + mpEngine.reset(new ScFieldEditEngine(mpDoc, mpDoc->GetEditPool())); + // EE_CNTRL_ONLINESPELLING if there are errors already + mpEngine->SetControlWord(mpEngine->GetControlWord() | EE_CNTRL_ONLINESPELLING); + mpDoc->ApplyAsianEditSettings(*mpEngine); + } + mpEngine->SetText(*pObj); + sal_Int32 nParCount = mpEngine->GetParagraphCount(); + for (sal_Int32 nPar=0; nPar<nParCount; nPar++) + { + mpEngine->QuickRemoveCharAttribs(nPar); + const SfxItemSet& rOld = mpEngine->GetParaAttribs(nPar); + if ( rOld.Count() ) { - if (bAll) // complete column - { - nTop = 0; - nBottom = MAXROW; - } - else - return false; + SfxItemSet aNew( *rOld.GetPool(), rOld.GetRanges() ); // empty + mpEngine->SetParaAttribs( nPar, aNew ); } - pColumn->Search( nTop, nPos ); - bNext = false; - bAll = false; // only the first time } + // change URL field to text (not possible otherwise, thus pType=0) + mpEngine->RemoveFields(true); - if ( nPos >= pColumn->maItems.size() ) - return false; + bool bSpellErrors = mpEngine->HasOnlineSpellErrors(); + bool bNeedObject = bSpellErrors || nParCount>1; // keep errors/paragraphs + // ScEditAttrTester is not needed anymore, arrays are gone - if ( pColumn->maItems[nPos].nRow <= nBottom ) - bFound = true; - else - bNext = true; + if (bNeedObject) // remains edit cell + { + sal_uInt32 nCtrl = mpEngine->GetControlWord(); + sal_uInt32 nWantBig = bSpellErrors ? EE_CNTRL_ALLOWBIGOBJS : 0; + if ( ( nCtrl & EE_CNTRL_ALLOWBIGOBJS ) != nWantBig ) + mpEngine->SetControlWord( (nCtrl & ~EE_CNTRL_ALLOWBIGOBJS) | nWantBig ); + + // Overwrite the existing object. + delete pObj; + pObj = mpEngine->CreateTextObject(); + } + else // create String + { + // Store the string replacement for later commits. + OUString aText = ScEditUtil::GetSpaceDelimitedString(*mpEngine); + maStrEntries.push_back(StrEntry(nRow, aText)); + } } - while (!bFound); +}; + +class TestTabRefAbsHandler +{ + SCTAB mnTab; + bool mbTestResult; +public: + TestTabRefAbsHandler(SCTAB nTab) : mnTab(nTab), mbTestResult(false) {} + + void operator() (size_t /*nRow*/, const ScFormulaCell* pCell) + { + if (const_cast<ScFormulaCell*>(pCell)->TestTabRefAbs(mnTab)) + mbTestResult = true; + } + + bool getTestResult() const { return mbTestResult; } +}; - rIndex = nPos++; - return true; } +void ScColumn::RemoveAutoSpellObj() +{ + RemoveAutoSpellObjHandler aFunc(maCells, pDocument); + sc::ProcessEditText(maCells, aFunc); + aFunc.commitStrings(); +} -//------------ +void ScColumn::RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow ) +{ + RemoveEditAttribsHandler aFunc(maCells, pDocument); + sc::ProcessEditText(maCells.begin(), maCells, nStartRow, nEndRow, aFunc); + aFunc.commitStrings(); +} -bool ScColumn::IsEmptyData() const +bool ScColumn::TestTabRefAbs(SCTAB nTable) const { - return (maItems.empty()); + TestTabRefAbsHandler aFunc(nTable); + sc::ParseFormula(maCells, aFunc); + return aFunc.getTestResult(); } -bool ScColumn::IsEmptyVisData() const +bool ScColumn::IsEmptyData() const { - return maItems.empty(); + return maCells.block_size() == 1 && maCells.begin()->type == sc::element_type_empty; } -SCSIZE ScColumn::VisibleCount( SCROW nStartRow, SCROW nEndRow ) const +namespace { + +class CellCounter { - // Notes are not counted + size_t mnCount; +public: + CellCounter() : mnCount(0) {} - SCSIZE nVisCount = 0; - SCSIZE nIndex; - Search( nStartRow, nIndex ); - while ( nIndex < maItems.size() && maItems[nIndex].nRow <= nEndRow ) + void operator() ( + const sc::CellStoreType::value_type& node, size_t /*nOffset*/, size_t nDataSize) { - if ( maItems[nIndex].nRow >= nStartRow ) - { - ++nVisCount; - } - ++nIndex; + if (node.type == sc::element_type_empty) + return; + + mnCount += nDataSize; } - return nVisCount; -} -SCROW ScColumn::GetLastVisDataPos() const -{ - if (maItems.empty()) - return 0; + size_t getCount() const { return mnCount; } +}; - return maItems.back().nRow; } -SCROW ScColumn::GetFirstVisDataPos() const +SCSIZE ScColumn::VisibleCount( SCROW nStartRow, SCROW nEndRow ) const { - SCROW nRet = 0; - if ( !maItems.empty() ) - { - SCSIZE i; - bool bFound = false; - for (i=0; i<maItems.size() && !bFound; i++) - { - bFound = true; - nRet = maItems[i].nRow; - } - } - return nRet; + CellCounter aFunc; + sc::ParseBlock(maCells.begin(), maCells, aFunc, nStartRow, nEndRow); + return aFunc.getCount(); } bool ScColumn::HasVisibleDataAt(SCROW nRow) const { - std::vector<ColEntry>::const_iterator it = Search(nRow); - if (it == maItems.end()) + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it == maCells.end()) + // Likely invalid row number. return false; - return it->nRow == nRow; + return it->type != sc::element_type_empty; } bool ScColumn::IsEmptyAttr() const @@ -1181,100 +1205,174 @@ bool ScColumn::IsEmpty() const bool ScColumn::IsEmptyBlock(SCROW nStartRow, SCROW nEndRow) const { - if ( maItems.empty() ) - return true; + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it == maCells.end()) + // Invalid row number. + return false; - std::vector<ColEntry>::const_iterator it = Search(nStartRow); - if (it == maItems.end()) - // All non-empty cells are before nStartRow. - return true; + if (it->type != sc::element_type_empty) + // Non-empty cell at the start position. + return false; - return (it->nRow > nEndRow); + // start position of next block which is not empty. + SCROW nNextRow = nStartRow + it->size - aPos.second; + return nEndRow < nNextRow; } SCSIZE ScColumn::GetEmptyLinesInBlock( SCROW nStartRow, SCROW nEndRow, ScDirection eDir ) const { - SCSIZE nLines = 0; - SCSIZE i; - if ( !maItems.empty() ) + // Given a range of rows, find a top or bottom empty segment. + switch (eDir) { - bool bFound = false; - if (eDir == DIR_BOTTOM) - { - i = maItems.size(); - while (!bFound && (i > 0)) - { - i--; - if ( maItems[i].nRow < nStartRow ) - break; - bFound = maItems[i].nRow <= nEndRow; - } - if (bFound) - nLines = static_cast<SCSIZE>(nEndRow - maItems[i].nRow); - else - nLines = static_cast<SCSIZE>(nEndRow - nStartRow); + case DIR_TOP: + { + // Determine the length of empty head segment. + size_t nLength = nEndRow - nStartRow + 1; + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it->type != sc::element_type_empty) + // First row is already not empty. + return 0; + + // length of this empty block minus the offset. + size_t nThisLen = it->size - aPos.second; + return std::min(nThisLen, nLength); } - else if (eDir == DIR_TOP) - { - i = 0; - while (!bFound && (i < maItems.size())) - { - if ( maItems[i].nRow > nEndRow ) - break; - bFound = maItems[i].nRow >= nStartRow; - i++; - } - if (bFound) - nLines = static_cast<SCSIZE>(maItems[i-1].nRow - nStartRow); - else - nLines = static_cast<SCSIZE>(nEndRow - nStartRow); + break; + case DIR_BOTTOM: + { + // Determine the length empty tail segment. + size_t nLength = nEndRow - nStartRow + 1; + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nEndRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it->type != sc::element_type_empty) + // end row is already not empty. + return 0; + + // length of this empty block from the tip to the end row position. + size_t nThisLen = aPos.second; + return std::min(nThisLen, nLength); } + break; + default: + ; } - else - nLines = static_cast<SCSIZE>(nEndRow - nStartRow); - return nLines; + + return 0; } SCROW ScColumn::GetFirstDataPos() const { - if ( !maItems.empty() ) - return maItems[0].nRow; - else + if (IsEmptyData()) return 0; + + sc::CellStoreType::const_iterator it = maCells.begin(); + if (it->type != sc::element_type_empty) + return 0; + + return it->size; } SCROW ScColumn::GetLastDataPos() const { - if ( !maItems.empty() ) - return maItems.back().nRow; - else + if (IsEmptyData()) return 0; + + sc::CellStoreType::const_reverse_iterator it = maCells.rbegin(); + if (it->type != sc::element_type_empty) + return MAXROW; + + return MAXROW - static_cast<SCROW>(it->size); } bool ScColumn::GetPrevDataPos(SCROW& rRow) const { - bool bFound = false; - SCSIZE i = maItems.size(); - while (!bFound && (i > 0)) + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it == maCells.end()) + return false; + + if (it->type == sc::element_type_empty) + { + if (it == maCells.begin()) + // No more previous non-empty cell. + return false; + + rRow -= aPos.second + 1; // Last row position of the previous block. + return true; + } + + // This block is not empty. + if (aPos.second) { - --i; - bFound = (maItems[i].nRow < rRow); - if (bFound) - rRow = maItems[i].nRow; + // There are preceding cells in this block. Simply move back one cell. + --rRow; + return true; } - return bFound; + + // This is the first cell in an non-empty block. Move back to the previous block. + if (it == maCells.begin()) + // No more preceding block. + return false; + + --rRow; // Move to the last cell of the previous block. + --it; + if (it->type == sc::element_type_empty) + { + // This block is empty. + if (it == maCells.begin()) + // No more preceding blocks. + return false; + + // Skip the whole empty block segment. + rRow -= it->size; + } + + return true; } bool ScColumn::GetNextDataPos(SCROW& rRow) const // greater than rRow { - SCSIZE nIndex; - if (Search( rRow, nIndex )) - ++nIndex; // next cell + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it == maCells.end()) + return false; + + if (it->type == sc::element_type_empty) + { + // This block is empty. Skip ahead to the next block (if exists). + rRow += it->size - aPos.second; + ++it; + if (it == maCells.end()) + // No more next block. + return false; + } + + if (aPos.second < it->size - 1) + { + // There are still cells following the current position. + ++rRow; + return true; + } + + // This is the last cell in the block. Move ahead to the next block. + rRow += it->size - aPos.second; // First cell in the next block. + ++it; + if (it == maCells.end()) + // No more next block. + return false; + + if (it->type == sc::element_type_empty) + { + // Next block is empty. Move to the next block. + rRow += it->size; + ++it; + if (it == maCells.end()) + return false; + } - bool bMore = ( nIndex < maItems.size() ); - if ( bMore ) - rRow = maItems[nIndex].nRow; - return bMore; + return true; } SCROW ScColumn::FindNextVisibleRow(SCROW nRow, bool bForward) const @@ -1301,103 +1399,123 @@ SCROW ScColumn::FindNextVisibleRow(SCROW nRow, bool bForward) const } } -SCROW ScColumn::FindNextVisibleRowWithContent(SCROW nRow, bool bForward) const +SCROW ScColumn::FindNextVisibleRowWithContent( + sc::CellStoreType::const_iterator& itPos, SCROW nRow, bool bForward) const { - if(bForward) + if (bForward) { do { nRow++; SCROW nEndRow = 0; bool bHidden = pDocument->RowHidden(nRow, nTab, NULL, &nEndRow); - if(bHidden) + if (bHidden) { nRow = nEndRow + 1; if(nRow >= MAXROW) return MAXROW; } - SCSIZE nIndex; - bool bThere = Search( nRow, nIndex ); - if (bThere) - return nRow; - else if((bThere ? nIndex+1 : nIndex) >= maItems.size()) + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow); + itPos = aPos.first; + if (itPos == maCells.end()) + // Invalid row. return MAXROW; - else - { - if(bThere) - nRow = maItems[nIndex+1].nRow - 1; - else - nRow = maItems[nIndex].nRow - 1; - } + + if (itPos->type != sc::element_type_empty) + return nRow; + + // Move to the last cell of the current empty block. + nRow += itPos->size - aPos.second - 1; } - while(nRow < MAXROW); + while (nRow < MAXROW); return MAXROW; } - else + + do { - do + nRow--; + SCROW nStartRow = MAXROW; + bool bHidden = pDocument->RowHidden(nRow, nTab, &nStartRow, NULL); + if (bHidden) { - nRow--; - SCROW nStartRow = MAXROW; - bool bHidden = pDocument->RowHidden(nRow, nTab, &nStartRow, NULL); - if(bHidden) - { - nRow = nStartRow - 1; - if(nRow <= 0) - return 0; - } - - SCSIZE nIndex; - bool bThere = Search( nRow, nIndex ); - if (bThere) - return nRow; - else if(nIndex == 0) + nRow = nStartRow - 1; + if(nRow <= 0) return 0; - else - nRow = maItems[nIndex-1].nRow + 1; } - while(nRow > 0); - return 0; + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow); + itPos = aPos.first; + if (itPos == maCells.end()) + // Invalid row. + return 0; + + if (itPos->type != sc::element_type_empty) + return nRow; + + // Move to the first cell of the current empty block. + nRow -= aPos.second; } + while (nRow > 0); + + return 0; } void ScColumn::CellStorageModified() { mbDirtyGroups = true; + #if DEBUG_COLUMN_STORAGE - if (maItems.empty()) + if (maCells.size() != MAXROWCOUNT) { - if (maCellTextAttrs.empty()) - { - cout << "ScColumn::CellStorageModified: Text width array is empty, but shouldn't." << endl; - cout.flush(); - abort(); - } + cout << "ScColumn::CellStorageModified: Size of the cell array is incorrect." << endl; + cout.flush(); + abort(); + } - if (maCellTextAttrs.block_size() != 1 || maCellTextAttrs.begin()->type != mdds::mtv::element_type_empty) - { - cout << "ScColumn::CellStorageModified: When the cell array is empty, the cell text attribute array should consist of one empty block." << endl; - cout.flush(); - abort(); - } + if (maCellTextAttrs.size() != MAXROWCOUNT) + { + cout << "ScColumn::CellStorageModified: Size of the cell text attribute array is incorrect." << endl; + cout.flush(); + abort(); + } - return; + if (maBroadcasters.size() != MAXROWCOUNT) + { + cout << "ScColumn::CellStorageModified: Size of the broadcaster array is incorrect." << endl; + cout.flush(); + abort(); } - ScColumnTextWidthIterator aIter(*this, 0, MAXROW); - for (; aIter.hasCell(); aIter.next()) + // Make sure that these two containers are synchronized wrt empty segments. + sc::CellStoreType::const_iterator itCell = maCells.begin(); + sc::CellTextAttrStoreType::const_iterator itAttr = maCellTextAttrs.begin(); + + // Move to the first empty blocks. + while (itCell != maCells.end() && itCell->type != sc::element_type_empty) + ++itCell; + + while (itAttr != maCellTextAttrs.end() && itAttr->type != sc::element_type_empty) + ++itAttr; + + while (itCell != maCells.end()) { - SCROW nRow = aIter.getPos(); - ScBaseCell* pCell = GetCell(nRow); - if (!pCell) + if (itCell->position != itAttr->position || itCell->size != itAttr->size) { - cout << "ScColumn::CellStorageModified: Cell and text width storages are out of sync!" << endl; + cout << "ScColumn::CellStorageModified: Cell array and cell text attribute array are out of sync." << endl; cout.flush(); abort(); } + + // Move to the next empty blocks. + ++itCell; + while (itCell != maCells.end() && itCell->type != sc::element_type_empty) + ++itCell; + + ++itAttr; + while (itAttr != maCellTextAttrs.end() && itAttr->type != sc::element_type_empty) + ++itAttr; } #endif } @@ -1465,142 +1583,87 @@ void ScColumn::CopyCellTextAttrsToDocument(SCROW nRow1, SCROW nRow2, ScColumn& r } } -void ScColumn::SetCell( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, ScBaseCell* pNewCell ) +namespace { + +class CellTextAttrInitializer { - if(pNewCell->GetCellType() == CELLTYPE_FORMULA) + sc::CellTextAttrStoreType maAttrs; + sc::CellTextAttrStoreType::iterator miPos; +public: + CellTextAttrInitializer() : maAttrs(MAXROWCOUNT), miPos(maAttrs.begin()) {} + + void operator() (const sc::CellStoreType::value_type& node) { - ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pNewCell); - sal_uInt32 nCellFormat = GetNumberFormat( nRow ); - if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0) - pFCell->SetNeedNumberFormat(true); + if (node.type == sc::element_type_empty) + return; + // Fill with default values for non-empty cell segments. + std::vector<sc::CellTextAttr> aDefaults(node.size); + miPos = maAttrs.set(miPos, node.position, aDefaults.begin(), aDefaults.end()); } - bool bIsAppended = false; - if ( !maItems.empty() ) + void swap(sc::CellTextAttrStoreType& rAttrs) { - if (maItems.back().nRow < nRow) - { - Append(rBlockPos, nRow, pNewCell); - bIsAppended = true; - } + maAttrs.swap(rAttrs); } - if (!bIsAppended) - { - SCSIZE nIndex; - if (Search(nRow, nIndex)) - { - ScBaseCell* pOldCell = maItems[nIndex].pCell; - if ( pOldCell->GetCellType() == CELLTYPE_FORMULA && !pDocument->IsClipOrUndo() ) - static_cast<ScFormulaCell*>(pOldCell)->EndListeningTo( pDocument ); - pOldCell->Delete(); - maItems[nIndex].pCell = pNewCell; - } - else - { - maItems.insert(maItems.begin() + nIndex, ColEntry()); - maItems[nIndex].pCell = pNewCell; - maItems[nIndex].nRow = nRow; - } +}; - rBlockPos.miCellTextAttrPos = - maCellTextAttrs.set(rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); +} - CellStorageModified(); - } +void ScColumn::ResetCellTextAttrs() +{ + CellTextAttrInitializer aFunc; + std::for_each(maCells.begin(), maCells.end(), aFunc); + aFunc.swap(maCellTextAttrs); } -void ScColumn::SetCell( SCROW nRow, ScBaseCell* pNewCell ) +void ScColumn::SwapCellTextAttrs( SCROW nRow1, SCROW nRow2 ) { - if(pNewCell->GetCellType() == CELLTYPE_FORMULA) - { - ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pNewCell); - sal_uInt32 nCellFormat = GetNumberFormat( nRow ); - if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0) - pFCell->SetNeedNumberFormat(true); + typedef std::pair<sc::CellTextAttrStoreType::iterator,size_t> PosType; - } + if (nRow1 == nRow2) + return; - bool bIsAppended = false; - if ( !maItems.empty() ) - { - if (maItems.back().nRow < nRow) - { - Append(nRow, pNewCell); - bIsAppended = true; - } - } - if (!bIsAppended) - { - SCSIZE nIndex; - if (Search(nRow, nIndex)) - { - ScBaseCell* pOldCell = maItems[nIndex].pCell; - if ( pOldCell->GetCellType() == CELLTYPE_FORMULA && !pDocument->IsClipOrUndo() ) - static_cast<ScFormulaCell*>(pOldCell)->EndListeningTo( pDocument ); - pOldCell->Delete(); - maItems[nIndex].pCell = pNewCell; - } - else - { - maItems.insert(maItems.begin() + nIndex, ColEntry()); - maItems[nIndex].pCell = pNewCell; - maItems[nIndex].nRow = nRow; - } + if (nRow1 > nRow2) + std::swap(nRow1, nRow2); - maCellTextAttrs.set(nRow, sc::CellTextAttr()); - CellStorageModified(); - } -} + PosType aPos1 = maCellTextAttrs.position(nRow1); + if (aPos1.first == maCellTextAttrs.end()) + return; -void ScColumn::PostSetCell( SCROW nRow, ScBaseCell* pNewCell ) -{ - // 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()) ) - { - CellType eCellType = pNewCell->GetCellType(); - if (eCellType == CELLTYPE_FORMULA) - static_cast<ScFormulaCell*>(pNewCell)->StartListeningTo(pDocument); + PosType aPos2 = maCellTextAttrs.position(aPos1.first, nRow2); + if (aPos2.first == maCellTextAttrs.end()) + return; - if (!pDocument->IsCalcingAfterLoad()) - { - if ( eCellType == CELLTYPE_FORMULA ) - ((ScFormulaCell*)pNewCell)->SetDirty(); - else - pDocument->Broadcast( - ScHint(SC_HINT_DATACHANGED, ScAddress(nCol, nRow, nTab))); - } - } -} + sc::CellTextAttrStoreType::iterator it1 = aPos1.first, it2 = aPos2.first; + if (it1->type == it2->type) + { + if (it1->type == sc::element_type_empty) + // Both are empty. Nothing to swap. + return; -namespace { + // Both are non-empty. Simply swap their values. + std::swap( + sc::celltextattr_block::at(*it1->data, aPos1.second), + sc::celltextattr_block::at(*it2->data, aPos2.second)); -class SetEmptyAttr : std::unary_function<ColEntry, void> -{ - sc::CellTextAttrStoreType& mrAttrStore; - sc::CellTextAttrStoreType::iterator miPos; -public: - SetEmptyAttr(sc::CellTextAttrStoreType& rAttrStore) : - mrAttrStore(rAttrStore), miPos(rAttrStore.begin()) {} + return; + } - void operator() (const ColEntry& rEntry) + // One is empty while the other isn't. + if (it1->type == sc::element_type_empty) { - miPos = mrAttrStore.set(miPos, rEntry.nRow, sc::CellTextAttr()); + // row 1 is empty while row 2 is non-empty. + const sc::CellTextAttr& rVal2 = sc::celltextattr_block::at(*it2->data, aPos2.second); + it1 = maCellTextAttrs.set(it1, nRow1, rVal2); + maCellTextAttrs.set_empty(it1, nRow2, nRow2); + return; } -}; - -} -void ScColumn::ResetCellTextAttrs() -{ - maCellTextAttrs.clear(); - maCellTextAttrs.resize(MAXROWCOUNT); - - std::for_each(maItems.begin(), maItems.end(), SetEmptyAttr(maCellTextAttrs)); + // row 1 is non-empty while row 2 is empty. + sc::CellTextAttr aVal1 = sc::celltextattr_block::at(*it1->data, aPos1.second); // make a copy. + it1 = maCellTextAttrs.set_empty(it1, nRow1, nRow1); + maCellTextAttrs.set(it1, nRow2, aVal1); } SvtBroadcaster* ScColumn::GetBroadcaster(SCROW nRow) @@ -1751,73 +1814,78 @@ ScFormulaVectorState ScColumn::GetFormulaVectorState( SCROW nRow ) const formula::FormulaTokenRef ScColumn::ResolveStaticReference( SCROW nRow ) { - std::vector<ColEntry>::iterator it = Search(nRow); - std::vector<ColEntry>::iterator itEnd = maItems.end(); - - if (it == itEnd || it->nRow != nRow) - { - // Empty cell. - return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0)); - } + std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nRow); + sc::CellStoreType::iterator it = aPos.first; + if (it == maCells.end()) + // Invalid row. Return a null token. + return formula::FormulaTokenRef(); - ScBaseCell* pCell = it->pCell; - switch (pCell->GetCellType()) + switch (it->type) { - case CELLTYPE_VALUE: + case sc::element_type_numeric: { - ScValueCell* pVC = static_cast<ScValueCell*>(pCell); - return formula::FormulaTokenRef(new formula::FormulaDoubleToken(pVC->GetValue())); + double fVal = sc::numeric_block::at(*it->data, aPos.second); + return formula::FormulaTokenRef(new formula::FormulaDoubleToken(fVal)); } - case CELLTYPE_FORMULA: + case sc::element_type_formula: { - ScFormulaCell* pFC = static_cast<ScFormulaCell*>(pCell); - if (pFC->GetDirty()) - // Dirty formula cell is not considered static. Return null token. + ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second); + if (p->GetDirty()) + // Dirty formula cell is not considered static (for now). + // Return null token. Later we will switch to dynamically + // interpreting all dirty formula cells. return formula::FormulaTokenRef(); - return formula::FormulaTokenRef(new formula::FormulaDoubleToken(pFC->GetResultDouble())); + return formula::FormulaTokenRef(new formula::FormulaDoubleToken(p->GetResultDouble())); } + case sc::element_type_empty: default: - ; + // Return a value of 0.0 in all the other cases. + return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0)); } - - return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0)); } -bool ScColumn::ResolveStaticReference( ScMatrix& rMat, SCCOL nMatCol, SCROW nRow1, SCROW nRow2 ) +namespace { + +class ToMatrixHandler { - if (nRow1 > nRow2) - return false; + ScMatrix& mrMat; + SCCOL mnMatCol; + SCROW mnTopRow; + bool mbSuccess; +public: + ToMatrixHandler(ScMatrix& rMat, SCCOL nMatCol, SCROW nTopRow) : + mrMat(rMat), mnMatCol(nMatCol), mnTopRow(nTopRow), mbSuccess(true) {} - std::vector<ColEntry>::iterator it = Search(nRow1); - std::vector<ColEntry>::iterator itEnd = maItems.end(); + void operator() (size_t nRow, double fVal) + { + mrMat.PutDouble(fVal, mnMatCol, nRow - mnTopRow); + } - for (; it != itEnd && it->nRow <= nRow2; ++it) + void operator() (size_t nRow, ScFormulaCell* p) { - switch (it->pCell->GetCellType()) + if (p->GetDirty()) { - case CELLTYPE_VALUE: - { - ScValueCell* pVC = static_cast<ScValueCell*>(it->pCell); - rMat.PutDouble(pVC->GetValue(), nMatCol, it->nRow - nRow1); - } - break; - case CELLTYPE_FORMULA: - { - ScFormulaCell* pFC = static_cast<ScFormulaCell*>(it->pCell); - if (pFC->GetDirty()) - // Dirty formula cell is not considered static. Return null token. - return false; - - rMat.PutDouble(pFC->GetResultDouble(), nMatCol, it->nRow - nRow1); - } - break; - default: - ; + mbSuccess = false; + return; } + + mrMat.PutDouble(p->GetResultDouble(), mnMatCol, nRow - mnTopRow); } - return true; + bool isSuccess() const { return mbSuccess; } +}; + +} + +bool ScColumn::ResolveStaticReference( ScMatrix& rMat, SCCOL nMatCol, SCROW nRow1, SCROW nRow2 ) +{ + if (nRow1 > nRow2) + return false; + + ToMatrixHandler aFunc(rMat, nMatCol, nRow1); + sc::ProcessFormulaNumeric(maCells.begin(), maCells, nRow1, nRow2, aFunc); + return aFunc.isSuccess(); } const double* ScColumn::FetchDoubleArray( sc::FormulaGroupContext& /*rCxt*/, SCROW nRow1, SCROW nRow2 ) const @@ -1826,59 +1894,18 @@ const double* ScColumn::FetchDoubleArray( sc::FormulaGroupContext& /*rCxt*/, SCR if (nRow1 > nRow2) return NULL; - std::vector<ColDoubleEntry*>::const_iterator it = maDoubles.begin(), itEnd = maDoubles.end(); - size_t nOffset = 0; - for (; it != itEnd; ++it) - { - const ColDoubleEntry& rEntry = **it; - SCROW nRowStart = rEntry.mnStart; - SCROW nRowEnd = nRowStart + rEntry.maData.size() - 1; - if (nRowStart <= nRow1 && nRow2 <= nRowEnd) - { - // Found it. - nOffset = nRow1 - nRowStart; - break; - } - } - - if (it == itEnd) - { - // Array not found. + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1); + if (aPos.first->type != sc::element_type_numeric) + // This is not a numeric cell block. return NULL; - } - const ColDoubleEntry& rEntry = **it; - return &rEntry.maData[0] + nOffset; -} - -ScRefCellValue ScColumn::GetRefCellValue( SCROW nRow ) -{ - ScRefCellValue aCell; // start empty - SCSIZE nIndex; - if (!Search(nRow, nIndex)) - return aCell; - - ScBaseCell* pCell = maItems[nIndex].pCell; - aCell.meType = pCell->GetCellType(); - switch (aCell.meType) - { - case CELLTYPE_STRING: - aCell.mpString = static_cast<const ScStringCell*>(pCell)->GetStringPtr(); - break; - case CELLTYPE_EDIT: - aCell.mpEditText = static_cast<const ScEditCell*>(pCell)->GetData(); - break; - case CELLTYPE_VALUE: - aCell.mfValue = static_cast<const ScValueCell*>(pCell)->GetValue(); - break; - case CELLTYPE_FORMULA: - aCell.mpFormula = static_cast<ScFormulaCell*>(pCell); - break; - default: - aCell.meType = CELLTYPE_NONE; // reset to empty. + size_t nLen = aPos.first->size - aPos.second; + if (static_cast<SCROW>(nLen) < nRow2 - nRow1 + 1) + // Array shorter than requested. + return NULL; } - return aCell; + return &sc::numeric_block::at(*aPos.first->data, aPos.second); } void ScColumn::SetNumberFormat( SCROW nRow, sal_uInt32 nNumberFormat ) @@ -1896,71 +1923,66 @@ const ScFormulaCell* ScColumn::FetchFormulaCell( SCROW nRow ) const if (!ValidRow(nRow)) return NULL; - SCSIZE nIndex; - if (!Search(nRow, nIndex)) - // cell not found. + 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_FORMULA) + if (it->type != sc::element_type_formula) // Not a formula cell. return NULL; - return static_cast<const ScFormulaCell*>(pCell); + return sc::formula_block::at(*it->data, aPos.second); } void ScColumn::FindDataAreaPos(SCROW& rRow, bool bDown) const { - // check if we are in a data area - SCSIZE nIndex; - bool bThere = Search(rRow, nIndex); + // If the cell is empty, find the next non-empty cell position. If the + // cell is not empty, find the last non-empty cell position in the current + // contiguous cell block. - size_t nLastIndex = maItems.size() - 1; - if (bThere) - { - SCROW nNextRow = FindNextVisibleRow(rRow, bDown); - SCSIZE nNewIndex; - bool bNextThere = Search(nNextRow, nNewIndex); + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow); + sc::CellStoreType::const_iterator it = aPos.first; + if (it == maCells.end()) + // Invalid row. + return; - if(bNextThere) - { - SCROW nLastRow; - nLastRow = nNextRow; - do - { - nNextRow = FindNextVisibleRow(nLastRow, bDown); - bNextThere = Search(nNextRow, nNewIndex); - if (!bNextThere) - bNextThere = false; - else - nLastRow = nNextRow; - } - while(bNextThere && nNewIndex < nLastIndex && nNewIndex > 0); + if (it->type == sc::element_type_empty) + { + // Current cell is empty. Find the next non-empty cell. + rRow = FindNextVisibleRowWithContent(it, rRow, bDown); + return; + } - rRow = nLastRow; - } - else - { - rRow = FindNextVisibleRowWithContent(nNextRow, bDown); - } + // Current cell is not empty. + SCROW nNextRow = FindNextVisibleRow(rRow, bDown); + aPos = maCells.position(it, nNextRow); + it = aPos.first; + if (it->type == sc::element_type_empty) + { + // Next visible cell is empty. Find the next non-empty cell. + rRow = FindNextVisibleRowWithContent(it, nNextRow, bDown); + return; } - else + + // Next visible cell is non-empty. Find the edge that's still visible. + SCROW nLastRow = nNextRow; + do { - rRow = FindNextVisibleRowWithContent(rRow, bDown); + nNextRow = FindNextVisibleRow(nLastRow, bDown); + aPos = maCells.position(it, nNextRow); + it = aPos.first; + if (it->type != sc::element_type_empty) + nLastRow = nNextRow; } + while (it->type != sc::element_type_empty); + + rRow = nLastRow; } bool ScColumn::HasDataAt(SCROW nRow) const { - // are only visible cells interesting ? - //! then HasVisibleDataAt out - - SCSIZE nIndex; - if (Search(nRow, nIndex)) - return true; - - return false; - + return maCells.get_type(nRow) != sc::element_type_empty; } bool ScColumn::IsAllAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW nEndRow ) const @@ -1992,7 +2014,7 @@ bool ScColumn::GetLastVisibleAttr( SCROW& rLastRow, bool bFullFormattedArea ) co if (pAttrArray) { // row of last cell is needed - SCROW nLastData = GetLastVisDataPos(); // always including notes, 0 if none + SCROW nLastData = GetLastDataPos(); // always including notes, 0 if none return pAttrArray->GetLastVisibleAttr( rLastRow, nLastData, bFullFormattedArea ); } @@ -2008,16 +2030,33 @@ bool ScColumn::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const return false; } -void ScColumn::FindUsed( SCROW nStartRow, SCROW nEndRow, bool* pUsed ) const +namespace { + +class FindUsedRowsHandler { - SCROW nRow = 0; - SCSIZE nIndex; - Search( nStartRow, nIndex ); - while ( (nIndex < maItems.size()) ? ((nRow=maItems[nIndex].nRow) <= nEndRow) : false ) + typedef mdds::flat_segment_tree<SCROW,bool> UsedRowsType; + UsedRowsType& mrUsed; + UsedRowsType::const_iterator miUsed; +public: + FindUsedRowsHandler(UsedRowsType& rUsed) : mrUsed(rUsed), miUsed(rUsed.begin()) {} + + void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize) { - pUsed[nRow-nStartRow] = true; - ++nIndex; + if (node.type == sc::element_type_empty) + return; + + SCROW nRow1 = node.position + nOffset; + SCROW nRow2 = nRow1 + nDataSize - 1; + miUsed = mrUsed.insert(miUsed, nRow1, nRow2+1, true).first; } +}; + +} + +void ScColumn::FindUsed( SCROW nStartRow, SCROW nEndRow, mdds::flat_segment_tree<SCROW,bool>& rUsed ) const +{ + FindUsedRowsHandler aFunc(rUsed); + sc::ParseBlock(maCells.begin(), maCells, aFunc, nStartRow, nEndRow); } namespace { @@ -2133,141 +2172,219 @@ void ScColumn::EndListening( sc::EndListeningContext& rCxt, SCROW nRow, SvtListe rCxt.addEmptyBroadcasterPosition(nTab, nCol, nRow); } +namespace { + +struct CompileDBFormulaHandler +{ + void operator() (size_t, ScFormulaCell* p) + { + p->CompileDBFormula(); + } +}; + +class CompileDBFormula2Handler +{ + bool mbCreateFormulaString; + +public: + CompileDBFormula2Handler(bool bCreateFormulaString) : + mbCreateFormulaString(bCreateFormulaString) {} + + void operator() (size_t, ScFormulaCell* p) + { + p->CompileDBFormula(mbCreateFormulaString); + } +}; + +class CompileNameFormulaHandler +{ + bool mbCreateFormulaString; + +public: + CompileNameFormulaHandler(bool bCreateFormulaString) : + mbCreateFormulaString(bCreateFormulaString) {} + + void operator() (size_t, ScFormulaCell* p) + { + p->CompileNameFormula(mbCreateFormulaString); + } +}; + +struct CompileColRowNameFormulaHandler +{ + void operator() (size_t, ScFormulaCell* p) + { + p->CompileColRowNameFormula(); + } +}; + +} + void ScColumn::CompileDBFormula() { - if ( !maItems.empty() ) - for (SCSIZE i = 0; i < maItems.size(); i++) - { - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - ((ScFormulaCell*) pCell)->CompileDBFormula(); - } + CompileDBFormulaHandler aFunc; + sc::ProcessFormula(maCells, aFunc); } void ScColumn::CompileDBFormula( bool bCreateFormulaString ) { - if ( !maItems.empty() ) - for (SCSIZE i = 0; i < maItems.size(); i++) - { - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - ((ScFormulaCell*) pCell)->CompileDBFormula( bCreateFormulaString ); - } + CompileDBFormula2Handler aFunc(bCreateFormulaString); + sc::ProcessFormula(maCells, aFunc); } void ScColumn::CompileNameFormula( bool bCreateFormulaString ) { - if ( !maItems.empty() ) - for (SCSIZE i = 0; i < maItems.size(); i++) - { - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - ((ScFormulaCell*) pCell)->CompileNameFormula( bCreateFormulaString ); - } + CompileNameFormulaHandler aFunc(bCreateFormulaString); + sc::ProcessFormula(maCells, aFunc); } void ScColumn::CompileColRowNameFormula() { - if ( !maItems.empty() ) - for (SCSIZE i = 0; i < maItems.size(); i++) - { - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - ((ScFormulaCell*) pCell)->CompileColRowNameFormula(); - } + CompileColRowNameFormulaHandler aFunc; + sc::ProcessFormula(maCells, aFunc); } -static void lcl_UpdateSubTotal( ScFunctionData& rData, const ScBaseCell* pCell ) +namespace { + +class UpdateSubTotalHandler { - double nValue = 0.0; - bool bVal = false; - bool bCell = true; - switch (pCell->GetCellType()) - { - case CELLTYPE_VALUE: - nValue = ((ScValueCell*)pCell)->GetValue(); - bVal = true; - break; - case CELLTYPE_FORMULA: - { - if ( rData.eFunc != SUBTOTAL_FUNC_CNT2 ) // it doesn't interest us - { - ScFormulaCell* pFC = (ScFormulaCell*)pCell; - if ( pFC->GetErrCode() ) - { - if ( rData.eFunc != SUBTOTAL_FUNC_CNT ) // simply remove from count - rData.bError = true; - } - else if (pFC->IsValue()) - { - nValue = pFC->GetValue(); - bVal = true; - } - // otherwise text - } - } - break; - default: - { - // added to avoid warnings - } - } + ScFunctionData& mrData; + ScFlatBoolRowSegments& mrHiddenRows; - if (!rData.bError) + void update(double fVal, bool bVal) { - switch (rData.eFunc) + if (!mrData.bError) + return; + + switch (mrData.eFunc) { case SUBTOTAL_FUNC_SUM: case SUBTOTAL_FUNC_AVE: - if (bVal) - { - ++rData.nCount; - if (!SubTotal::SafePlus( rData.nVal, nValue )) - rData.bError = true; - } - break; + { + if (!bVal) + return; + + ++mrData.nCount; + if (!SubTotal::SafePlus(mrData.nVal, fVal)) + mrData.bError = true; + } + break; case SUBTOTAL_FUNC_CNT: // only the value - if (bVal) - ++rData.nCount; - break; + { + if (!bVal) + return; + + ++mrData.nCount; + } + break; case SUBTOTAL_FUNC_CNT2: // everything - if (bCell) - ++rData.nCount; - break; + ++mrData.nCount; + break; case SUBTOTAL_FUNC_MAX: - if (bVal) - if (++rData.nCount == 1 || nValue > rData.nVal ) - rData.nVal = nValue; - break; + { + if (!bVal) + return; + + if (++mrData.nCount == 1 || fVal > mrData.nVal) + mrData.nVal = fVal; + } + break; case SUBTOTAL_FUNC_MIN: - if (bVal) - if (++rData.nCount == 1 || nValue < rData.nVal ) - rData.nVal = nValue; - break; + { + if (!bVal) + return; + + if (++mrData.nCount == 1 || fVal < mrData.nVal) + mrData.nVal = fVal; + } + break; default: { // added to avoid warnings } } } + +public: + UpdateSubTotalHandler(ScFunctionData& rData, ScFlatBoolRowSegments& rHiddenRows) : + mrData(rData), mrHiddenRows(rHiddenRows) {} + + void operator() (size_t nRow, double fVal) + { + if (mrHiddenRows.getValue(nRow)) + return; + + update(fVal, true); + } + + void operator() (size_t nRow, ScFormulaCell* pCell) + { + if (mrHiddenRows.getValue(nRow)) + return; + + double fVal = 0.0; + bool bVal = false; + if (mrData.eFunc != SUBTOTAL_FUNC_CNT2) // it doesn't interest us + { + + if (pCell->GetErrCode()) + { + if (mrData.eFunc != SUBTOTAL_FUNC_CNT) // simply remove from count + mrData.bError = true; + } + else if (pCell->IsValue()) + { + fVal = pCell->GetValue(); + bVal = true; + } + // otherwise text + } + + update(fVal, bVal); + } + + void operator() (mdds::mtv::element_t eType, size_t nTopRow, size_t nDataSize) + { + if (eType == sc::element_type_empty) + return; + + for (size_t i = 0; i < nDataSize; ++i) + { + size_t nRow = nTopRow + i; + if (mrHiddenRows.getValue(nRow)) + continue; + + update(0.0, false); + } + } +}; + } // multiple selections: void ScColumn::UpdateSelectionFunction( const ScMarkData& rMark, ScFunctionData& rData, ScFlatBoolRowSegments& rHiddenRows, - bool bDoExclude, SCROW nExStartRow, SCROW nExEndRow) const + bool bDoExclude, SCROW nExStartRow, SCROW nExEndRow) { if ( rData.eFunc != SUBTOTAL_FUNC_SELECTION_COUNT ) { - SCSIZE nIndex; - ScMarkedDataIter aDataIter(this, &rMark, false); - while (aDataIter.Next( nIndex )) + sc::SingleColumnSpanSet aSpanSet; + aSpanSet.scan(rMark, nTab, nCol); + if (bDoExclude) + { + aSpanSet.set(0, nExStartRow, false); + aSpanSet.set(nExEndRow+1, MAXROWCOUNT, false); + } + + sc::SingleColumnSpanSet::SpansType aSpans; + aSpanSet.getSpans(aSpans); + UpdateSubTotalHandler aFunc(rData, rHiddenRows); + sc::CellStoreType::iterator itCellPos = maCells.begin(); + sc::SingleColumnSpanSet::SpansType::const_iterator it = aSpans.begin(), itEnd = aSpans.end(); + for (; it != itEnd; ++it) { - SCROW nRow = maItems[nIndex].nRow; - bool bRowHidden = rHiddenRows.getValue(nRow); - if ( !bRowHidden ) - if ( !bDoExclude || nRow < nExStartRow || nRow > nExEndRow ) - lcl_UpdateSubTotal( rData, maItems[nIndex].pCell ); + itCellPos = sc::ProcessFormulaNumeric( + itCellPos, maCells, it->mnRow1, it->mnRow2, aFunc); } } else @@ -2314,21 +2431,13 @@ void ScColumn::UpdateSelectionFunction( // with bNoMarked ignore the multiple selections void ScColumn::UpdateAreaFunction( - ScFunctionData& rData, ScFlatBoolRowSegments& rHiddenRows, SCROW nStartRow, SCROW nEndRow) const + ScFunctionData& rData, ScFlatBoolRowSegments& rHiddenRows, SCROW nStartRow, SCROW nEndRow) { if ( rData.eFunc != SUBTOTAL_FUNC_SELECTION_COUNT ) { - SCSIZE nIndex; - Search( nStartRow, nIndex ); - while ( nIndex<maItems.size() && maItems[nIndex].nRow<=nEndRow ) - { - SCROW nRow = maItems[nIndex].nRow; - bool bRowHidden = rHiddenRows.getValue(nRow); - if ( !bRowHidden ) - if ( rData.eFunc != SUBTOTAL_FUNC_SELECTION_COUNT ) - lcl_UpdateSubTotal( rData, maItems[nIndex].pCell ); - ++nIndex; - } + UpdateSubTotalHandler aFunc(rData, rHiddenRows); + sc::ProcessFormulaNumeric( + maCells.begin(), maCells, nStartRow, nEndRow, aFunc); } else { @@ -2350,49 +2459,78 @@ void ScColumn::UpdateAreaFunction( } } -sal_uInt32 ScColumn::GetWeightedCount() const -{ - sal_uInt32 nTotal = 0; +namespace { - // Notes are not counted +class WeightedCounter +{ + size_t mnCount; +public: + WeightedCounter() : mnCount(0) {} - for (SCSIZE i=0; i<maItems.size(); i++) + void operator() (const sc::CellStoreType::value_type& node) { - ScBaseCell* pCell = maItems[i].pCell; - switch ( pCell->GetCellType() ) + switch (node.type) { - case CELLTYPE_VALUE: - case CELLTYPE_STRING: - ++nTotal; - break; - case CELLTYPE_FORMULA: - nTotal += 5 + ((ScFormulaCell*)pCell)->GetCode()->GetCodeLen(); - break; - case CELLTYPE_EDIT: - nTotal += 50; - break; - default: + case sc::element_type_numeric: + case sc::element_type_string: + mnCount += node.size; + break; + case sc::element_type_formula: { - // added to avoid warnings + // Each formula cell is worth its code length plus 5. + sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data); + sc::formula_block::const_iterator itEnd = sc::formula_block::end(*node.data); + for (; it != itEnd; ++it) + { + const ScFormulaCell* p = *it; + mnCount += 5 + p->GetCode()->GetCodeLen(); + } } + break; + case sc::element_type_edittext: + // each edit-text cell is worth 50. + mnCount += node.size * 50; + break; + default: + ; } } - return nTotal; + size_t getCount() const { return mnCount; } +}; + } -sal_uInt32 ScColumn::GetCodeCount() const +sal_uInt32 ScColumn::GetWeightedCount() const { - sal_uInt32 nCodeCount = 0; + WeightedCounter aFunc; + std::for_each(maCells.begin(), maCells.end(), aFunc); + return aFunc.getCount(); +} + +namespace { + +class CodeCounter +{ + size_t mnCount; +public: + CodeCounter() : mnCount(0) {} - for (SCSIZE i=0; i<maItems.size(); i++) + void operator() (size_t, const ScFormulaCell* p) { - ScBaseCell* pCell = maItems[i].pCell; - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - nCodeCount += ((ScFormulaCell*)pCell)->GetCode()->GetCodeLen(); + mnCount += p->GetCode()->GetCodeLen(); } - return nCodeCount; + size_t getCount() const { return mnCount; } +}; + +} + +sal_uInt32 ScColumn::GetCodeCount() const +{ + CodeCounter aFunc; + sc::ParseFormula(maCells, aFunc); + return aFunc.getCount(); } 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; } diff --git a/sc/source/core/data/columnspanset.cxx b/sc/source/core/data/columnspanset.cxx index 9b63dc75f728..8bfb7fc6fa79 100644 --- a/sc/source/core/data/columnspanset.cxx +++ b/sc/source/core/data/columnspanset.cxx @@ -9,6 +9,10 @@ #include "columnspanset.hxx" #include "stlalgorithm.hxx" +#include "column.hxx" +#include "mtvfunctions.hxx" +#include "markdata.hxx" +#include "rangelst.hxx" #include <algorithm> @@ -98,6 +102,119 @@ void ColumnSpanSet::executeFromTop(Action& ac) const } } +namespace { + +class Scanner +{ + SingleColumnSpanSet::ColumnSpansType& mrRanges; +public: + Scanner(SingleColumnSpanSet::ColumnSpansType& rRanges) : mrRanges(rRanges) {} + + void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize) + { + if (node.type == sc::element_type_empty) + return; + + size_t nRow = node.position + nOffset; + size_t nEndRow = nRow + nDataSize; // Last row of current block plus 1 + mrRanges.insert_back(nRow, nEndRow, true); + } +}; + +} + +SingleColumnSpanSet::SingleColumnSpanSet() : maSpans(0, MAXROWCOUNT, false) {} + +void SingleColumnSpanSet::scan(const ScColumn& rColumn) +{ + const CellStoreType& rCells = rColumn.maCells; + sc::CellStoreType::const_iterator it = rCells.begin(), itEnd = rCells.end(); + SCROW nCurRow = 0; + for (;it != itEnd; ++it) + { + SCROW nEndRow = nCurRow + it->size; // Last row of current block plus 1. + if (it->type != sc::element_type_empty) + maSpans.insert_back(nCurRow, nEndRow, true); + + nCurRow = nEndRow; + } +} + +void SingleColumnSpanSet::scan(const ScColumn& rColumn, SCROW nStart, SCROW nEnd) +{ + const CellStoreType& rCells = rColumn.maCells; + Scanner aScanner(maSpans); + sc::ParseBlock(rCells.begin(), rCells, aScanner, nStart, nEnd); +} + +void SingleColumnSpanSet::scan( + ColumnBlockConstPosition& rBlockPos, const ScColumn& rColumn, SCROW nStart, SCROW nEnd) +{ + const CellStoreType& rCells = rColumn.maCells; + Scanner aScanner(maSpans); + rBlockPos.miCellPos = sc::ParseBlock(rBlockPos.miCellPos, rCells, aScanner, nStart, nEnd); +} + +void SingleColumnSpanSet::scan(const ScMarkData& rMark, SCTAB nTab, SCCOL nCol) +{ + if (!rMark.GetTableSelect(nTab)) + // This table is not selected. Nothing to scan. + return; + + ScRangeList aRanges = rMark.GetMarkedRanges(); + for (size_t i = 0, n = aRanges.size(); i < n; ++i) + { + const ScRange* p = aRanges[i]; + if (nCol < p->aStart.Col() || p->aEnd.Col() < nCol) + // This column is not in this range. Skip it. + continue; + + maSpans.insert_back(p->aStart.Row(), p->aEnd.Row()+1, true); + } +} + +void SingleColumnSpanSet::set(SCROW nRow1, SCROW nRow2, bool bVal) +{ + maSpans.insert_back(nRow1, nRow2+1, bVal); +} + +void SingleColumnSpanSet::getRows(std::vector<SCROW> &rRows) const +{ + std::vector<SCROW> aRows; + + SpansType aRanges; + getSpans(aRanges); + SpansType::const_iterator it = aRanges.begin(), itEnd = aRanges.end(); + for (; it != itEnd; ++it) + { + for (SCROW nRow = it->mnRow1; nRow <= it->mnRow2; ++nRow) + aRows.push_back(nRow); + } + + rRows.swap(aRows); +} + +void SingleColumnSpanSet::getSpans(SpansType& rSpans) const +{ + SpansType aSpans; + ColumnSpansType::const_iterator it = maSpans.begin(), itEnd = maSpans.end(); + SCROW nLastRow = it->first; + bool bLastVal = it->second; + for (++it; it != itEnd; ++it) + { + SCROW nThisRow = it->first; + bool bThisVal = it->second; + + if (bLastVal) + aSpans.push_back(Span(nLastRow, nThisRow-1)); + + nLastRow = nThisRow; + bLastVal = bThisVal; + } + + rSpans.swap(aSpans); +} + } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/core/data/dociter.cxx b/sc/source/core/data/dociter.cxx index 8f6d1c2eaa2f..b3dab8d331e4 100644 --- a/sc/source/core/data/dociter.cxx +++ b/sc/source/core/data/dociter.cxx @@ -50,177 +50,39 @@ using ::std::set; // STATIC DATA ----------------------------------------------------------- -namespace -{ - void lcl_uppercase(OUString& rStr) - { - rStr = ScGlobal::pCharClass->uppercase(rStr.trim()); - } -} - -ScDocumentIterator::ScDocumentIterator( ScDocument* pDocument, - SCTAB nStartTable, SCTAB nEndTable ) : - pDoc( pDocument ), - nStartTab( nStartTable ), - nEndTab( nEndTable ) -{ - SCTAB nDocMaxTab = pDoc->GetTableCount() - 1; - PutInOrder( nStartTab, nEndTab ); - if (!ValidTab(nStartTab) || nStartTab > nDocMaxTab ) nStartTab = nDocMaxTab; - if (!ValidTab(nEndTab) || nEndTab > nDocMaxTab ) nEndTab = nDocMaxTab; - - pDefPattern = pDoc->GetDefPattern(); - - nCol = 0; - nRow = 0; - nTab = nStartTab; - - nColPos = 0; - nAttrPos = 0; -} - -ScDocumentIterator::~ScDocumentIterator() -{ -} - -bool ScDocumentIterator::GetThisCol() -{ - ScTable* pTab = NULL; - while ( nTab < pDoc->GetTableCount() && (pTab = pDoc->maTabs[nTab]) == NULL ) - { - if ( nTab == nEndTab ) - { - nCol = MAXCOL; - nRow = MAXROW; - return false; - } - ++nTab; - } - if (pTab == NULL) - { - OSL_FAIL("no table in document?"); - return false; - } - ScColumn* pCol = &pTab->aCol[nCol]; - const ScAttrArray* pAtt = pCol->pAttrArray; - - bool bFound = false; - do - { - SCROW nColRow; - SCROW nAttrEnd; - - do - { - nAttrEnd = pAtt->pData[nAttrPos].nRow; - if (nAttrEnd < nRow) - ++nAttrPos; - } - while (nAttrEnd < nRow); - - do - { - nColRow = (nColPos < pCol->maItems.size()) ? pCol->maItems[nColPos].nRow : MAXROW+1; - if (nColRow < nRow) - ++nColPos; - } - while (nColRow < nRow); - - if (nColRow == nRow) - { - bFound = true; - pCell = pCol->maItems[nColPos].pCell; - pPattern = pAtt->pData[nAttrPos].pPattern; - } - else if ( pAtt->pData[nAttrPos].pPattern != pDefPattern ) - { - bFound = true; - pCell = NULL; - pPattern = pAtt->pData[nAttrPos].pPattern; - } - else - { - nRow = std::min( (SCROW)nColRow, (SCROW)(nAttrEnd+1) ); - } - } - while (!bFound && nRow <= MAXROW); - - return bFound; -} - -bool ScDocumentIterator::GetThis() -{ - bool bEnd = false; - bool bSuccess = false; - - while ( !bSuccess && !bEnd ) - { - if ( nRow > MAXROW ) - bSuccess = false; - else - bSuccess = GetThisCol(); - - if ( !bSuccess ) - { - ++nCol; - if (nCol > MAXCOL) - { - nCol = 0; - ++nTab; - if (nTab > nEndTab) - bEnd = true; - } - nRow = 0; - nColPos = 0; - nAttrPos = 0; - } - } - - return !bEnd; -} +namespace { -bool ScDocumentIterator::GetFirst() +void upperCase(OUString& rStr) { - nCol = 0; - nTab = nStartTab; - - nRow = 0; - nColPos = 0; - nAttrPos = 0; - - return GetThis(); + rStr = ScGlobal::pCharClass->uppercase(rStr.trim()); } -bool ScDocumentIterator::GetNext() +template<typename _Iter> +void incBlock(std::pair<_Iter, size_t>& rPos) { - ++nRow; - - return GetThis(); + // Move to the next block. + ++rPos.first; + rPos.second = 0; } -ScCellValue ScDocumentIterator::GetCellValue() const +template<typename _Iter> +void incPos(std::pair<_Iter, size_t>& rPos) { - ScCellValue aRet; - if (pCell) - aRet.assign(*pCell); - return aRet; + if (rPos.second + 1 < rPos.first->size) + // Increment within the block. + ++rPos.second; + else + incBlock(rPos); } -const ScPatternAttr* ScDocumentIterator::GetPattern() +template<typename _Iter> +size_t toLogicalPos(const std::pair<_Iter, size_t>& rPos) { - return pPattern; + return rPos.first->position + rPos.second; } -void ScDocumentIterator::GetPos( SCCOL& rCol, SCROW& rRow, SCTAB& rTab ) -{ - rCol = nCol; - rRow = nRow; - rTab = nTab; } - -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ void ScAttrArray_IterGetNumberFormat( sal_uLong& nFormat, const ScAttrArray*& rpArr, SCROW& nAttrEndRow, const ScAttrArray* pNewArr, SCROW nRow, ScDocument* pDoc ) @@ -246,197 +108,202 @@ ScValueIterator::ScValueIterator( ScDocument* pDocument, const ScRange& rRange, bool bSTotal, bool bTextZero ) : pDoc( pDocument ), nNumFmtIndex(0), - nStartCol( rRange.aStart.Col() ), - nStartRow( rRange.aStart.Row() ), - nStartTab( rRange.aStart.Tab() ), - nEndCol( rRange.aEnd.Col() ), - nEndRow( rRange.aEnd.Row() ), - nEndTab( rRange.aEnd.Tab() ), + maStartPos(rRange.aStart), + maEndPos(rRange.aEnd), nNumFmtType( NUMBERFORMAT_UNDEFINED ), bNumValid( false ), bSubTotal(bSTotal), - bNextValid( false ), bCalcAsShown( pDocument->GetDocOptions().IsCalcAsShown() ), bTextAsZero( bTextZero ) { SCTAB nDocMaxTab = pDocument->GetTableCount() - 1; - PutInOrder( nStartCol, nEndCol); - PutInOrder( nStartRow, nEndRow); - PutInOrder( nStartTab, nEndTab ); - - if (!ValidCol(nStartCol)) nStartCol = MAXCOL; - if (!ValidCol(nEndCol)) nEndCol = MAXCOL; - if (!ValidRow(nStartRow)) nStartRow = MAXROW; - if (!ValidRow(nEndRow)) nEndRow = MAXROW; - if (!ValidTab(nStartTab) || nStartTab > nDocMaxTab) nStartTab = nDocMaxTab; - if (!ValidTab(nEndTab) || nEndTab > nDocMaxTab) nEndTab = nDocMaxTab; - - nCol = nStartCol; - nRow = nStartRow; - nTab = nStartTab; + if (!ValidCol(maStartPos.Col())) maStartPos.SetCol(MAXCOL); + if (!ValidCol(maEndPos.Col())) maEndPos.SetCol(MAXCOL); + if (!ValidRow(maStartPos.Row())) maStartPos.SetRow(MAXROW); + if (!ValidRow(maEndPos.Row())) maEndPos.SetRow(MAXROW); + if (!ValidTab(maStartPos.Tab()) || maStartPos.Tab() > nDocMaxTab) maStartPos.SetTab(nDocMaxTab); + if (!ValidTab(maEndPos.Tab()) || maEndPos.Tab() > nDocMaxTab) maEndPos.SetTab(nDocMaxTab); - nColRow = 0; // Initialized in GetFirst nNumFormat = 0; // Initialized in GetNumberFormat pAttrArray = 0; nAttrEndRow = 0; } +SCROW ScValueIterator::GetRow() const +{ + // Position of the head of the current block + offset within the block + // equals the logical element position. + return maCurPos.first->position + maCurPos.second; +} + +void ScValueIterator::IncBlock() +{ + ++maCurPos.first; + maCurPos.second = 0; +} + +void ScValueIterator::IncPos() +{ + if (maCurPos.second + 1 < maCurPos.first->size) + // Move within the same block. + ++maCurPos.second; + else + // Move to the next block. + IncBlock(); +} + +void ScValueIterator::SetPos(size_t nPos) +{ + maCurPos = mpCells->position(maCurPos.first, nPos); +} + bool ScValueIterator::GetThis(double& rValue, sal_uInt16& rErr) { - if (nTab >= pDoc->GetTableCount()) - { - OSL_FAIL("try to access out of index, FIX IT"); - } - ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol]; - for (;;) + while (true) { - if ( nRow > nEndRow ) + bool bNextColumn = maCurPos.first == mpCells->end(); + if (!bNextColumn) + { + if (GetRow() > maEndPos.Row()) + bNextColumn = true; + } + + ScColumn* pCol = NULL; + if (bNextColumn) { - nRow = nStartRow; + // Find the next available column. do { - nCol++; - if ( nCol > nEndCol ) + ++mnCol; + if (mnCol > maEndPos.Col()) { - nCol = nStartCol; - nTab++; - if ( nTab > nEndTab ) + mnCol = maStartPos.Col(); + ++mnTab; + if (mnTab > maEndPos.Tab()) { rErr = 0; return false; // Over and out } } - pCol = &(pDoc->maTabs[nTab])->aCol[nCol]; - } while ( pCol->maItems.empty() ); - pCol->Search( nRow, nColRow ); + pCol = &(pDoc->maTabs[mnTab])->aCol[mnCol]; + } + while (pCol->IsEmptyData()); + + mpCells = &pCol->maCells; + maCurPos = mpCells->position(maStartPos.Row()); } - while (( nColRow < pCol->maItems.size() ) && ( pCol->maItems[nColRow].nRow < nRow )) - nColRow++; + SCROW nCurRow = GetRow(); + SCROW nLastRow; + if (bSubTotal && pDoc->maTabs[mnTab]->RowFiltered(nCurRow, NULL, &nLastRow)) + { + // Skip all filtered rows for subtotal mode. + SetPos(nLastRow+1); + continue; + } - if ( nColRow < pCol->maItems.size() && pCol->maItems[nColRow].nRow <= nEndRow ) + switch (maCurPos.first->type) { - nRow = pCol->maItems[nColRow].nRow + 1; - if ( !bSubTotal || !pDoc->maTabs[nTab]->RowFiltered( nRow-1 ) ) + case sc::element_type_numeric: { - ScBaseCell* pCell = pCol->maItems[nColRow].pCell; - ++nColRow; - switch (pCell->GetCellType()) + bNumValid = false; + rValue = sc::numeric_block::at(*maCurPos.first->data, maCurPos.second); + rErr = 0; + if (bCalcAsShown) { - case CELLTYPE_VALUE: - { - bNumValid = false; - rValue = ((ScValueCell*)pCell)->GetValue(); - rErr = 0; - --nRow; - if ( bCalcAsShown ) - { - ScAttrArray_IterGetNumberFormat( nNumFormat, pAttrArray, - nAttrEndRow, pCol->pAttrArray, nRow, pDoc ); - rValue = pDoc->RoundValueAsShown( rValue, nNumFormat ); - } - /* - * If there's another Value Cell in the same column, - * which is also within the Area, we retrieve it's value - */ - if ( nColRow < pCol->maItems.size() && - pCol->maItems[nColRow].nRow <= nEndRow && - pCol->maItems[nColRow].pCell->GetCellType() == CELLTYPE_VALUE && - !bSubTotal ) - { - fNextValue = ((ScValueCell*)pCol->maItems[nColRow].pCell)->GetValue(); - nNextRow = pCol->maItems[nColRow].nRow; - bNextValid = true; - if ( bCalcAsShown ) - { - ScAttrArray_IterGetNumberFormat( nNumFormat, pAttrArray, - nAttrEndRow, pCol->pAttrArray, nNextRow, pDoc ); - fNextValue = pDoc->RoundValueAsShown( fNextValue, nNumFormat ); - } - } - - return true; // Found it! - } - - case CELLTYPE_FORMULA: - { - if (!bSubTotal || !((ScFormulaCell*)pCell)->IsSubTotal()) - { - rErr = ((ScFormulaCell*)pCell)->GetErrCode(); - if ( rErr || ((ScFormulaCell*)pCell)->IsValue() ) - { - rValue = ((ScFormulaCell*)pCell)->GetValue(); - nRow--; - bNumValid = false; - return true; // Found it! - } - else if ( bTextAsZero ) - { - rValue = 0.0; - nRow--; - bNumValid = false; - return true; - } - } - } + ScAttrArray_IterGetNumberFormat(nNumFormat, pAttrArray, + nAttrEndRow, pCol->pAttrArray, nCurRow, pDoc); + rValue = pDoc->RoundValueAsShown(rValue, nNumFormat); + } + return true; // Found it! + } + break; + case sc::element_type_formula: + { + ScFormulaCell* pCell = sc::formula_block::at(*maCurPos.first->data, maCurPos.second); + if (bSubTotal && pCell->IsSubTotal()) + { + // Skip subtotal formula cells. + IncPos(); break; + } - case CELLTYPE_STRING : - case CELLTYPE_EDIT : - { - if ( bTextAsZero ) - { - rErr = 0; - rValue = 0.0; - nNumFmtType = NUMBERFORMAT_NUMBER; - nNumFmtIndex = 0; - bNumValid = true; - --nRow; - return true; - } - } - break; - default: - { - // added to avoid warnings - } + rErr = pCell->GetErrCode(); + if (rErr || pCell->IsValue()) + { + rValue = pCell->GetValue(); + bNumValid = false; + return true; // Found it! + } + else if (bTextAsZero) + { + rValue = 0.0; + bNumValid = false; + return true; + } + IncPos(); + } + break; + case sc::element_type_string : + case sc::element_type_edittext : + { + if (bTextAsZero) + { + rErr = 0; + rValue = 0.0; + nNumFmtType = NUMBERFORMAT_NUMBER; + nNumFmtIndex = 0; + bNumValid = true; + return true; } + IncBlock(); } + break; + case sc::element_type_empty: + default: + // Skip the whole block. + IncBlock(); } - else - nRow = nEndRow + 1; // Next column } } void ScValueIterator::GetCurNumFmtInfo( short& nType, sal_uLong& nIndex ) { - if (!bNumValid && nTab < pDoc->GetTableCount()) + if (!bNumValid && mnTab < pDoc->GetTableCount()) { - const ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol]; - nNumFmtIndex = pCol->GetNumberFormat( nRow ); + SCROW nCurRow = GetRow(); + const ScColumn* pCol = &(pDoc->maTabs[mnTab])->aCol[mnCol]; + nNumFmtIndex = pCol->GetNumberFormat(nCurRow); nNumFmtType = pDoc->GetFormatTable()->GetType( nNumFmtIndex ); bNumValid = true; } + nType = nNumFmtType; nIndex = nNumFmtIndex; } bool ScValueIterator::GetFirst(double& rValue, sal_uInt16& rErr) { - nCol = nStartCol; - nRow = nStartRow; - nTab = nStartTab; + mnCol = maStartPos.Col(); + mnTab = maStartPos.Tab(); - if (nTab >= pDoc->GetTableCount()) - OSL_FAIL("try to access index out of bounds, FIX IT"); - ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol]; - pCol->Search( nRow, nColRow ); + ScTable* pTab = pDoc->FetchTable(mnTab); + if (pTab) + return false; nNumFormat = 0; // Initialized in GetNumberFormat pAttrArray = 0; nAttrEndRow = 0; + mpCells = &pTab->aCol[maStartPos.Col()].maCells; + maCurPos = mpCells->position(maStartPos.Row()); + return GetThis(rValue, rErr); +} + +bool ScValueIterator::GetNext(double& rValue, sal_uInt16& rErr) +{ + IncPos(); return GetThis(rValue, rErr); } @@ -451,20 +318,13 @@ ScDBQueryDataIterator::DataAccess::~DataAccess() { } -SCROW ScDBQueryDataIterator::GetRowByColEntryIndex(ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCSIZE nColRow) +const sc::CellStoreType* ScDBQueryDataIterator::GetColumnCellStore(ScDocument& rDoc, SCTAB nTab, SCCOL nCol) { - if (nTab >= rDoc.GetTableCount()) - OSL_FAIL("try to access index out of bounds, FIX IT"); - ScColumn* pCol = &rDoc.maTabs[nTab]->aCol[nCol]; - return pCol->maItems[nColRow].nRow; -} + ScTable* pTab = rDoc.FetchTable(nTab); + if (!pTab) + return NULL; -ScBaseCell* ScDBQueryDataIterator::GetCellByColEntryIndex(ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCSIZE nColRow) -{ - if (nTab >= rDoc.GetTableCount()) - OSL_FAIL("try to access index out of bounds, FIX IT"); - ScColumn* pCol = &rDoc.maTabs[nTab]->aCol[nCol]; - return pCol->maItems[nColRow].pCell; + return &pTab->aCol[nCol].maCells; } const ScAttrArray* ScDBQueryDataIterator::GetAttrArrayByCol(ScDocument& rDoc, SCTAB nTab, SCCOL nCol) @@ -475,27 +335,19 @@ const ScAttrArray* ScDBQueryDataIterator::GetAttrArrayByCol(ScDocument& rDoc, SC return pCol->pAttrArray; } -bool ScDBQueryDataIterator::IsQueryValid(ScDocument& rDoc, const ScQueryParam& rParam, SCTAB nTab, SCROW nRow, ScBaseCell* pCell) -{ - if (nTab >= rDoc.GetTableCount()) - OSL_FAIL("try to access index out of bounds, FIX IT"); - return rDoc.maTabs[nTab]->ValidQuery(nRow, rParam, pCell); -} - -SCSIZE ScDBQueryDataIterator::SearchColEntryIndex(ScDocument& rDoc, SCTAB nTab, SCROW nRow, SCCOL nCol) +bool ScDBQueryDataIterator::IsQueryValid( + ScDocument& rDoc, const ScQueryParam& rParam, SCTAB nTab, SCROW nRow, ScRefCellValue& rCell) { if (nTab >= rDoc.GetTableCount()) OSL_FAIL("try to access index out of bounds, FIX IT"); - ScColumn* pCol = &rDoc.maTabs[nTab]->aCol[nCol]; - SCSIZE nColRow; - pCol->Search(nRow, nColRow); - return nColRow; + return rDoc.maTabs[nTab]->ValidQuery(nRow, rParam, &rCell); } // ---------------------------------------------------------------------------- ScDBQueryDataIterator::DataAccessInternal::DataAccessInternal(const ScDBQueryDataIterator* pParent, ScDBQueryParamInternal* pParam, ScDocument* pDoc) : DataAccess(pParent), + mpCells(NULL), mpParam(pParam), mpDoc(pDoc), bCalcAsShown( pDoc->GetDocOptions().IsCalcAsShown() ) @@ -503,8 +355,6 @@ ScDBQueryDataIterator::DataAccessInternal::DataAccessInternal(const ScDBQueryDat nCol = mpParam->mnField; nRow = mpParam->nRow1; nTab = mpParam->nTab; - - nColRow = 0; // Initialized in GetFirst SCSIZE i; SCSIZE nCount = mpParam->GetEntryCount(); for (i=0; (i<nCount) && (mpParam->GetEntry(i).bDoQuery); i++) @@ -529,108 +379,96 @@ ScDBQueryDataIterator::DataAccessInternal::~DataAccessInternal() bool ScDBQueryDataIterator::DataAccessInternal::getCurrent(Value& rValue) { - SCCOLROW nFirstQueryField = mpParam->GetEntry(0).nField; - for ( ;; ) + // Start with the current row position, and find the first row position + // that satisfies the query. + + // TODO: The following line nFirstQueryField is supposed to be used some + // way. Find out how it's supposed to be used and fix this bug. + + // SCCOLROW nFirstQueryField = mpParam->GetEntry(0).nField; + while (true) { - if (nRow > mpParam->nRow2) + if (maCurPos.first == mpCells->end() || nRow > mpParam->nRow2) { // Bottom of the range reached. Bail out. rValue.mnError = 0; return false; } - SCSIZE nCellCount = mpDoc->GetCellCount(nTab, nCol); - if (!nCellCount) - // No cells found in this column. Bail out. - return false; - - SCROW nThisRow = ScDBQueryDataIterator::GetRowByColEntryIndex(*mpDoc, nTab, nCol, nColRow); - while ( (nColRow < nCellCount) && (nThisRow < nRow) ) + if (maCurPos.first->type == sc::element_type_empty) { - ++nColRow; - if(nColRow < nCellCount) - nThisRow = ScDBQueryDataIterator::GetRowByColEntryIndex(*mpDoc, nTab, nCol, nColRow); + // Skip the whole empty block. + incBlock(); + continue; } - if ( nColRow < nCellCount && nThisRow <= mpParam->nRow2 ) - { - nRow = nThisRow; - ScBaseCell* pCell = NULL; - if (nCol == static_cast<SCCOL>(nFirstQueryField)) - pCell = ScDBQueryDataIterator::GetCellByColEntryIndex(*mpDoc, nTab, nCol, nColRow); + ScRefCellValue aCell; + aCell.assign(maCurPos.first, maCurPos.second); - if (ScDBQueryDataIterator::IsQueryValid(*mpDoc, *mpParam, nTab, nRow, pCell)) + if (ScDBQueryDataIterator::IsQueryValid(*mpDoc, *mpParam, nTab, nRow, aCell)) + { + switch (aCell.meType) { - // #i109812# get cell here if it wasn't done above - if (nCol != static_cast<SCCOL>(nFirstQueryField)) - pCell = ScDBQueryDataIterator::GetCellByColEntryIndex(*mpDoc, nTab, nCol, nColRow); - - switch (pCell ? pCell->GetCellType() : CELLTYPE_NONE) + case CELLTYPE_VALUE: { - case CELLTYPE_VALUE: - { - rValue.mfValue = ((ScValueCell*)pCell)->GetValue(); - rValue.mbIsNumber = true; - if ( bCalcAsShown ) - { - const ScAttrArray* pNewAttrArray = - ScDBQueryDataIterator::GetAttrArrayByCol(*mpDoc, nTab, nCol); - ScAttrArray_IterGetNumberFormat( nNumFormat, pAttrArray, - nAttrEndRow, pNewAttrArray, nRow, mpDoc ); - rValue.mfValue = mpDoc->RoundValueAsShown( rValue.mfValue, nNumFormat ); - } - nNumFmtType = NUMBERFORMAT_NUMBER; - nNumFmtIndex = 0; - rValue.mnError = 0; - return true; // Found it! - } + rValue.mfValue = aCell.mfValue; + rValue.mbIsNumber = true; + if ( bCalcAsShown ) + { + const ScAttrArray* pNewAttrArray = + ScDBQueryDataIterator::GetAttrArrayByCol(*mpDoc, nTab, nCol); + ScAttrArray_IterGetNumberFormat( nNumFormat, pAttrArray, + nAttrEndRow, pNewAttrArray, nRow, mpDoc ); + rValue.mfValue = mpDoc->RoundValueAsShown( rValue.mfValue, nNumFormat ); + } + nNumFmtType = NUMBERFORMAT_NUMBER; + nNumFmtIndex = 0; + rValue.mnError = 0; + return true; // Found it! + } - case CELLTYPE_FORMULA: - { - if (((ScFormulaCell*)pCell)->IsValue()) - { - rValue.mfValue = ((ScFormulaCell*)pCell)->GetValue(); - rValue.mbIsNumber = true; - mpDoc->GetNumberFormatInfo( nNumFmtType, - nNumFmtIndex, ScAddress(nCol, nRow, nTab)); - rValue.mnError = ((ScFormulaCell*)pCell)->GetErrCode(); - return true; // Found it! - } - else if(mpParam->mbSkipString) - nRow++; - else - { - rValue.maString = static_cast<ScFormulaCell*>(pCell)->GetString(); - rValue.mfValue = 0.0; - rValue.mnError = static_cast<ScFormulaCell*>(pCell)->GetErrCode(); - rValue.mbIsNumber = false; - return true; - } - } - break; - case CELLTYPE_STRING: - case CELLTYPE_EDIT: - if (mpParam->mbSkipString) - ++nRow; - else - { - rValue.maString = pCell->GetStringData(); - rValue.mfValue = 0.0; - rValue.mnError = 0; - rValue.mbIsNumber = false; - return true; - } - break; - default: - nRow++; - break; + case CELLTYPE_FORMULA: + { + if (aCell.mpFormula->IsValue()) + { + rValue.mfValue = aCell.mpFormula->GetValue(); + rValue.mbIsNumber = true; + mpDoc->GetNumberFormatInfo( + nNumFmtType, nNumFmtIndex, ScAddress(nCol, nRow, nTab)); + rValue.mnError = aCell.mpFormula->GetErrCode(); + return true; // Found it! + } + else if(mpParam->mbSkipString) + incPos(); + else + { + rValue.maString = aCell.mpFormula->GetString(); + rValue.mfValue = 0.0; + rValue.mnError = aCell.mpFormula->GetErrCode(); + rValue.mbIsNumber = false; + return true; + } } + break; + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + if (mpParam->mbSkipString) + incPos(); + else + { + rValue.maString = aCell.getString(); + rValue.mfValue = 0.0; + rValue.mnError = 0; + rValue.mbIsNumber = false; + return true; + } + break; + default: + incPos(); } - else - nRow++; } else - nRow = mpParam->nRow2 + 1; // Next column + incPos(); } // statement unreachable } @@ -638,18 +476,52 @@ bool ScDBQueryDataIterator::DataAccessInternal::getCurrent(Value& rValue) bool ScDBQueryDataIterator::DataAccessInternal::getFirst(Value& rValue) { if (mpParam->bHasHeader) - nRow++; + ++nRow; - nColRow = ScDBQueryDataIterator::SearchColEntryIndex(*mpDoc, nTab, nRow, nCol); + mpCells = ScDBQueryDataIterator::GetColumnCellStore(*mpDoc, nTab, nCol); + if (!mpCells) + return false; + + maCurPos = mpCells->position(nRow); return getCurrent(rValue); } bool ScDBQueryDataIterator::DataAccessInternal::getNext(Value& rValue) { - ++nRow; + if (!mpCells || maCurPos.first == mpCells->end()) + return false; + + incPos(); return getCurrent(rValue); } +void ScDBQueryDataIterator::DataAccessInternal::incBlock() +{ + ++maCurPos.first; + maCurPos.second = 0; + + nRow = maCurPos.first->position; +} + +void ScDBQueryDataIterator::DataAccessInternal::incPos() +{ + if (maCurPos.second + 1 < maCurPos.first->size) + { + // Move within the same block. + ++maCurPos.second; + ++nRow; + } + else + // Move to the next block. + incBlock(); +} + +void ScDBQueryDataIterator::DataAccessInternal::setPos(size_t nPos) +{ + maCurPos = mpCells->position(maCurPos.first, nPos); + nRow = nPos; +} + // ---------------------------------------------------------------------------- ScDBQueryDataIterator::DataAccessMatrix::DataAccessMatrix(const ScDBQueryDataIterator* pParent, ScDBQueryParamMatrix* pParam) : @@ -812,9 +684,9 @@ bool ScDBQueryDataIterator::DataAccessMatrix::isValidQuery(SCROW nRow, const ScM { // Equality check first. OUString aMatStr = rMat.GetString(nField, nRow); - lcl_uppercase(aMatStr); + upperCase(aMatStr); OUString aQueryStr = rEntry.GetQueryItem().maString; - lcl_uppercase(aQueryStr); + upperCase(aQueryStr); bool bDone = false; switch (rEntry.eOp) { @@ -922,12 +794,43 @@ ScCellIterator::ScCellIterator( ScDocument* pDoc, const ScRange& rRange, bool bS mpDoc(pDoc), maStartPos(rRange.aStart), maEndPos(rRange.aEnd), - mnIndex(0), mbSubTotal(bSTotal) { init(); } +void ScCellIterator::incBlock() +{ + ++maCurColPos.first; + maCurColPos.second = 0; + + maCurPos.SetRow(maCurColPos.first->position); +} + +void ScCellIterator::incPos() +{ + if (maCurColPos.second + 1 < maCurColPos.first->size) + { + // Move within the same block. + ++maCurColPos.second; + maCurPos.IncRow(); + } + else + // Move to the next block. + incBlock(); +} + +void ScCellIterator::setPos(size_t nPos) +{ + maCurColPos = getColumn()->maCells.position(maCurColPos.first, nPos); + maCurPos.SetRow(nPos); +} + +const ScColumn* ScCellIterator::getColumn() const +{ + return &mpDoc->maTabs[maCurPos.Tab()]->aCol[maCurPos.Col()]; +} + void ScCellIterator::init() { SCTAB nDocMaxTab = mpDoc->GetTableCount() - 1; @@ -959,11 +862,20 @@ void ScCellIterator::init() bool ScCellIterator::getCurrent() { - ScColumn* pCol = &(mpDoc->maTabs[maCurPos.Tab()])->aCol[maCurPos.Col()]; + const ScColumn* pCol = getColumn(); + while (true) { - if (maCurPos.Row() > maEndPos.Row()) + bool bNextColumn = maCurColPos.first == pCol->maCells.end(); + if (!bNextColumn) { + if (maCurPos.Row() > maEndPos.Row()) + bNextColumn = true; + } + + if (bNextColumn) + { + // Move to the next column. maCurPos.SetRow(maStartPos.Row()); do { @@ -978,57 +890,39 @@ bool ScCellIterator::getCurrent() return false; // Over and out } } - pCol = &(mpDoc->maTabs[maCurPos.Tab()])->aCol[maCurPos.Col()]; - } while ( pCol->maItems.empty() ); - pCol->Search(maCurPos.Row(), mnIndex); - } + pCol = getColumn(); + } + while (pCol->IsEmptyData()); - while ( (mnIndex < pCol->maItems.size()) && (pCol->maItems[mnIndex].nRow < maCurPos.Row()) ) - ++mnIndex; + maCurColPos = pCol->maCells.position(maCurPos.Row()); + } - if (mnIndex < pCol->maItems.size() && pCol->maItems[mnIndex].nRow <= maEndPos.Row()) + if (maCurColPos.first->type == sc::element_type_empty) { - maCurPos.SetRow(pCol->maItems[mnIndex].nRow); - if (!mbSubTotal || !mpDoc->maTabs[maCurPos.Tab()]->RowFiltered(maCurPos.Row())) - { - ScBaseCell* pCell = pCol->maItems[mnIndex].pCell; - - if ( mbSubTotal && pCell->GetCellType() == CELLTYPE_FORMULA - && ((ScFormulaCell*)pCell)->IsSubTotal() ) - maCurPos.IncRow(); // Don't subtotal rows - else - { - // Found it! - maCurCell.meType = pCell->GetCellType(); - switch (maCurCell.meType) - { - case CELLTYPE_VALUE: - maCurCell.mfValue = static_cast<const ScValueCell*>(pCell)->GetValue(); - break; - case CELLTYPE_STRING: - maCurCell.mpString = static_cast<const ScStringCell*>(pCell)->GetStringPtr(); - break; - case CELLTYPE_EDIT: - maCurCell.mpEditText = static_cast<const ScEditCell*>(pCell)->GetData(); - break; - case CELLTYPE_FORMULA: - maCurCell.mpFormula = static_cast<ScFormulaCell*>(pCell); - break; - default: - maCurCell.meType = CELLTYPE_NONE; - } + incBlock(); + continue; + } - if (maCurCell.meType != CELLTYPE_NONE) - return true; + SCROW nLastRow; + if (mbSubTotal && pCol->GetDoc().maTabs[maCurPos.Tab()]->RowFiltered(maCurPos.Row(), NULL, &nLastRow)) + { + // Skip all filtered rows for subtotal mode. + setPos(nLastRow+1); + continue; + } - maCurPos.IncRow(); - } + if (maCurColPos.first->type == sc::element_type_formula) + { + const ScFormulaCell* pCell = sc::formula_block::at(*maCurColPos.first->data, maCurColPos.second); + if (pCell->IsSubTotal()) + { + // Skip subtotal formula cells. + incPos(); + continue; } - else - maCurPos.IncRow(); } - else - maCurPos.SetRow(maEndPos.Row() + 1); // Next column + + maCurCell.assign(maCurColPos.first, maCurColPos.second); } return false; } @@ -1133,8 +1027,9 @@ bool ScCellIterator::first() return false; maCurPos = maStartPos; - ScColumn* pCol = &(mpDoc->maTabs[maCurPos.Tab()])->aCol[maCurPos.Col()]; - pCol->Search(maCurPos.Row(), mnIndex); + const ScColumn* pCol = getColumn(); + + maCurColPos = pCol->maCells.position(maCurPos.Row()); return getCurrent(); } @@ -1158,7 +1053,6 @@ ScQueryCellIterator::ScQueryCellIterator(ScDocument* pDocument, SCTAB nTable, { nCol = mpParam->nCol1; nRow = mpParam->nRow1; - nColRow = 0; // Initialized in GetFirst SCSIZE i; if (bMod) // Or else it's already inserted { @@ -1178,11 +1072,40 @@ ScQueryCellIterator::ScQueryCellIterator(ScDocument* pDocument, SCTAB nTable, nAttrEndRow = 0; } -ScBaseCell* ScQueryCellIterator::GetThis() +void ScQueryCellIterator::InitPos() +{ + nRow = mpParam->nRow1; + if (mpParam->bHasHeader && mpParam->bByRow) + ++nRow; + ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol]; + maCurPos = pCol->maCells.position(nRow); +} + +void ScQueryCellIterator::IncPos() +{ + if (maCurPos.second + 1 < maCurPos.first->size) + { + // Move within the same block. + ++maCurPos.second; + ++nRow; + } + else + // Move to the next block. + IncBlock(); +} + +void ScQueryCellIterator::IncBlock() +{ + ++maCurPos.first; + maCurPos.second = 0; + + nRow = maCurPos.first->position; +} + +bool ScQueryCellIterator::GetThis() { if (nTab >= pDoc->GetTableCount()) OSL_FAIL("try to access index out of bounds, FIX IT"); - ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol]; const ScQueryEntry& rEntry = mpParam->GetEntry(0); const ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); @@ -1193,13 +1116,19 @@ ScBaseCell* ScQueryCellIterator::GetThis() !mpParam->bHasHeader && rItem.meType == ScQueryEntry::ByString && ((mpParam->bByRow && nRow == mpParam->nRow1) || (!mpParam->bByRow && nCol == mpParam->nCol1)); - for ( ;; ) + + ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol]; + while (true) { - if ( nRow > mpParam->nRow2 ) + bool bNextColumn = maCurPos.first == pCol->maCells.end(); + if (!bNextColumn) + { + if (nRow > mpParam->nRow2) + bNextColumn = true; + } + + if (bNextColumn) { - nRow = mpParam->nRow1; - if (mpParam->bHasHeader && mpParam->bByRow) - nRow++; do { if ( ++nCol > mpParam->nCol2 ) @@ -1210,89 +1139,87 @@ ScBaseCell* ScQueryCellIterator::GetThis() nFirstQueryField = rEntry.nField; } pCol = &(pDoc->maTabs[nTab])->aCol[nCol]; - } while ( pCol->maItems.empty() ); - pCol->Search( nRow, nColRow ); + } + while (pCol->IsEmptyData()); + + InitPos(); + bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings && !mpParam->bHasHeader && rItem.meType == ScQueryEntry::ByString && mpParam->bByRow; } - while ( nColRow < pCol->maItems.size() && pCol->maItems[nColRow].nRow < nRow ) - nColRow++; + if (maCurPos.first->type == sc::element_type_empty) + { + IncBlock(); + continue; + } + + ScRefCellValue aCell; + aCell.assign(maCurPos.first, maCurPos.second); - if ( nColRow < pCol->maItems.size() && - (nRow = pCol->maItems[nColRow].nRow) <= mpParam->nRow2 ) + if (bAllStringIgnore && aCell.hasString()) + IncPos(); + else { - ScBaseCell* pCell = pCol->maItems[nColRow].pCell; - if (bAllStringIgnore && pCell->HasStringData()) - ++nRow; - else + bool bTestEqualCondition = false; + if ( pDoc->maTabs[nTab]->ValidQuery( nRow, *mpParam, + (nCol == static_cast<SCCOL>(nFirstQueryField) ? &aCell : NULL), + (nTestEqualCondition ? &bTestEqualCondition : NULL) ) ) + { + if ( nTestEqualCondition && bTestEqualCondition ) + nTestEqualCondition |= nTestEqualConditionMatched; + return !aCell.isEmpty(); // Found it! + } + else if ( nStopOnMismatch ) { - bool bTestEqualCondition = false; - if ( (pDoc->maTabs[nTab])->ValidQuery( nRow, *mpParam, - (nCol == static_cast<SCCOL>(nFirstQueryField) ? pCell : NULL), - (nTestEqualCondition ? &bTestEqualCondition : NULL) ) ) + // Yes, even a mismatch may have a fulfilled equal + // condition if regular expressions were involved and + // SC_LESS_EQUAL or SC_GREATER_EQUAL were queried. + if ( nTestEqualCondition && bTestEqualCondition ) { - if ( nTestEqualCondition && bTestEqualCondition ) - nTestEqualCondition |= nTestEqualConditionMatched; - return pCell; // Found it! + nTestEqualCondition |= nTestEqualConditionMatched; + nStopOnMismatch |= nStopOnMismatchOccurred; + return false; } - else if ( nStopOnMismatch ) + bool bStop; + if (bFirstStringIgnore) { - // Yes, even a mismatch may have a fulfilled equal - // condition if regular expressions were involved and - // SC_LESS_EQUAL or SC_GREATER_EQUAL were queried. - if ( nTestEqualCondition && bTestEqualCondition ) + if (aCell.hasString()) { - nTestEqualCondition |= nTestEqualConditionMatched; - nStopOnMismatch |= nStopOnMismatchOccurred; - return NULL; - } - bool bStop; - if (bFirstStringIgnore) - { - if (pCell->HasStringData()) - { - ++nRow; - bStop = false; - } - else - bStop = true; + IncPos(); + bStop = false; } else bStop = true; - if (bStop) - { - nStopOnMismatch |= nStopOnMismatchOccurred; - return NULL; - } } else - nRow++; + bStop = true; + if (bStop) + { + nStopOnMismatch |= nStopOnMismatchOccurred; + return false; + } } + else + IncPos(); } - else - nRow = mpParam->nRow2 + 1; // Next column bFirstStringIgnore = false; } } -ScBaseCell* ScQueryCellIterator::GetFirst() +bool ScQueryCellIterator::GetFirst() { if (nTab >= pDoc->GetTableCount()) OSL_FAIL("try to access index out of bounds, FIX IT"); nCol = mpParam->nCol1; - nRow = mpParam->nRow1; - if (mpParam->bHasHeader) - nRow++; - ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol]; - pCol->Search( nRow, nColRow ); + InitPos(); return GetThis(); } -ScBaseCell* ScQueryCellIterator::GetNext() +bool ScQueryCellIterator::GetNext() { - ++nRow; + IncPos(); if ( nStopOnMismatch ) nStopOnMismatch = nStopOnMismatchEnabled; if ( nTestEqualCondition ) @@ -1336,24 +1263,25 @@ bool ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL& nFoundCol, if (bBinary ? (BinarySearch() ? GetThis() : 0) : GetFirst()) { // First equal entry or last smaller than (greater than) entry. - SCSIZE nColRowSave; - ScBaseCell* pNext = 0; + PositionType aPosSave; + bool bNext = false; do { nFoundCol = GetCol(); nFoundRow = GetRow(); - nColRowSave = nColRow; - } while ( !IsEqualConditionFulfilled() && (pNext = GetNext()) != NULL ); + aPosSave = maCurPos; + } + while ( !IsEqualConditionFulfilled() && (bNext = GetNext())); // There may be no pNext but equal condition fulfilled if regular // expressions are involved. Keep the found entry and proceed. - if (!pNext && !IsEqualConditionFulfilled()) + if (!bNext && !IsEqualConditionFulfilled()) { // Step back to last in range and adjust position markers for // GetNumberFormat() or similar. nCol = nFoundCol; nRow = nFoundRow; - nColRow = nColRowSave; + maCurPos = aPosSave; } } if ( IsEqualConditionFulfilled() ) @@ -1380,20 +1308,20 @@ bool ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL& nFoundCol, else break; // for } - SCSIZE nColRowSave; + PositionType aPosSave; bIgnoreMismatchOnLeadingStrings = false; SetTestEqualCondition( false ); do { nFoundCol = GetCol(); nFoundRow = GetRow(); - nColRowSave = nColRow; + aPosSave = maCurPos; } while (GetNext()); // Step back conditions are the same as above nCol = nFoundCol; nRow = nFoundRow; - nColRow = nColRowSave; + maCurPos = aPosSave; return true; } if ( (bSearchForEqualAfterMismatch || mpParam->bRegExp) && @@ -1429,35 +1357,37 @@ bool ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL& nFoundCol, // Last of a consecutive area, avoid searching the entire parameter // range as it is a real performance bottleneck in case of regular // expressions. - SCSIZE nColRowSave; + PositionType aPosSave; do { nFoundCol = GetCol(); nFoundRow = GetRow(); - nColRowSave = nColRow; + aPosSave = maCurPos; SetStopOnMismatch( true ); } while (GetNext()); nCol = nFoundCol; nRow = nFoundRow; - nColRow = nColRowSave; + maCurPos = aPosSave; } } return (nFoundCol <= MAXCOL) && (nFoundRow <= MAXROW); } -ScBaseCell* ScQueryCellIterator::BinarySearch() +bool ScQueryCellIterator::BinarySearch() { + // TODO: This will be extremely slow with mdds::multi_type_vector. + if (nTab >= pDoc->GetTableCount()) OSL_FAIL("try to access index out of bounds, FIX IT"); nCol = mpParam->nCol1; ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol]; - if (!pCol->maItems.size()) - return 0; + if (pCol->IsEmptyData()) + return false; + PositionType aHiPos, aLoPos; ScRefCellValue aCell; - ScBaseCell* pCell; - SCSIZE nHi, nLo; + CollatorWrapper* pCollator = (mpParam->bCaseSens ? ScGlobal::GetCaseCollator() : ScGlobal::GetCollator()); SvNumberFormatter& rFormatter = *(pDoc->GetFormatTable()); @@ -1472,41 +1402,59 @@ ScBaseCell* ScQueryCellIterator::BinarySearch() nRow = mpParam->nRow1; if (mpParam->bHasHeader) nRow++; - if (pCol->Search( nRow, nLo ) && bFirstStringIgnore && - pCol->maItems[nLo].pCell->HasStringData()) + + aLoPos = pCol->maCells.position(nRow); + if (bFirstStringIgnore && aLoPos.first->type == sc::element_type_string) { OUString aCellStr; - sal_uLong nFormat = pCol->GetNumberFormat( pCol->maItems[nLo].nRow); - aCell.assign(*pCol->maItems[nLo].pCell); + sal_uLong nFormat = pCol->GetNumberFormat(toLogicalPos(aLoPos)); + aCell.assign(aLoPos.first, aLoPos.second); ScCellFormat::GetInputString(aCell, nFormat, aCellStr, rFormatter); sal_Int32 nTmp = pCollator->compareString(aCellStr, rEntry.GetQueryItem().maString); if ((rEntry.eOp == SC_LESS_EQUAL && nTmp > 0) || (rEntry.eOp == SC_GREATER_EQUAL && nTmp < 0) || (rEntry.eOp == SC_EQUAL && nTmp != 0)) - ++nLo; + { + // Skip the first string value at low point. + incPos(aLoPos); + } + } + + aHiPos = pCol->maCells.position(mpParam->nRow2); + if (bAllStringIgnore) + { + // Skip all string cells, but never go past the high point. + if (aLoPos.first->type == sc::element_type_string) + { + if (aLoPos.first == pCol->maCells.end()) + // This is the last block. Move to the last element in this block. + aLoPos.second = aLoPos.first->size - 1; + else + // Move to the next block. + incBlock(aLoPos); + } + + if (toLogicalPos(aLoPos) > toLogicalPos(aHiPos)) + // Never go past the high point. + aLoPos = aHiPos; } - if (!pCol->Search( mpParam->nRow2, nHi ) && nHi>0) - --nHi; - while (bAllStringIgnore && nLo <= nHi && nLo < pCol->maItems.size() && - pCol->maItems[nLo].pCell->HasStringData()) - ++nLo; // Bookkeeping values for breaking up the binary search in case the data // range isn't strictly sorted. - SCSIZE nLastInRange = nLo; - SCSIZE nFirstLastInRange = nLastInRange; + PositionType aLastInRange = aLoPos; + PositionType aFirstLastInRange = aLastInRange; double fLastInRangeValue = bLessEqual ? -(::std::numeric_limits<double>::max()) : ::std::numeric_limits<double>::max(); - String aLastInRangeString; + OUString aLastInRangeString; if (!bLessEqual) - aLastInRangeString.Assign( sal_Unicode(0xFFFF)); - if (nLastInRange < pCol->maItems.size()) + aLastInRangeString = OUString(sal_Unicode(0xFFFF)); + if (aLastInRange.first != pCol->maCells.end()) { - aCell.assign(*pCol->maItems[nLastInRange].pCell); + aCell.assign(aLastInRange.first, aLastInRange.second); if (aCell.hasString()) { - sal_uLong nFormat = pCol->GetNumberFormat( pCol->maItems[nLastInRange].nRow); + sal_uLong nFormat = pCol->GetNumberFormat(toLogicalPos(aLastInRange)); OUString aStr; ScCellFormat::GetInputString(aCell, nFormat, aStr, rFormatter); aLastInRangeString = aStr; @@ -1532,20 +1480,22 @@ ScBaseCell* ScQueryCellIterator::BinarySearch() sal_Int32 nRes = 0; bool bFound = false; bool bDone = false; - while (nLo <= nHi && !bDone) + size_t nLogicalLow = toLogicalPos(aLoPos), nLogicalHigh = toLogicalPos(aHiPos); + while (nLogicalLow <= nLogicalHigh && !bDone) { - SCSIZE nMid = (nLo+nHi)/2; - SCSIZE i = nMid; - if (i > nHi) + size_t nMid = (nLogicalLow+nLogicalHigh)/2; + size_t i = nMid; + if (i > nLogicalHigh) { if (nMid > 0) - nHi = nMid - 1; + nLogicalHigh = nMid - 1; else bDone = true; continue; // while } - bool bStr = pCol->maItems[i].pCell->HasStringData(); + PositionType aPos = pCol->maCells.position(i); + bool bStr = aPos.first->type == sc::element_type_string; nRes = 0; // compares are content<query:-1, content>query:1 @@ -1553,14 +1503,14 @@ ScBaseCell* ScQueryCellIterator::BinarySearch() if (!bStr && !bByString) { double nCellVal; - pCell = pCol->maItems[i].pCell; - switch ( pCell->GetCellType() ) + aCell.assign(aPos.first, aPos.second); + switch (aCell.meType) { case CELLTYPE_VALUE : - nCellVal = static_cast<ScValueCell*>(pCell)->GetValue(); + nCellVal = aCell.mfValue; break; case CELLTYPE_FORMULA : - nCellVal = static_cast<ScFormulaCell*>(pCell)->GetValue(); + nCellVal = aCell.mpFormula->GetValue(); break; default: nCellVal = 0.0; @@ -1574,12 +1524,12 @@ ScBaseCell* ScQueryCellIterator::BinarySearch() if (fLastInRangeValue < nCellVal) { fLastInRangeValue = nCellVal; - nLastInRange = i; + aLastInRange = aPos; } else if (fLastInRangeValue > nCellVal) { // not strictly sorted, continue with GetThis() - nLastInRange = nFirstLastInRange; + aLastInRange = aFirstLastInRange; bDone = true; } } @@ -1593,12 +1543,12 @@ ScBaseCell* ScQueryCellIterator::BinarySearch() if (fLastInRangeValue > nCellVal) { fLastInRangeValue = nCellVal; - nLastInRange = i; + aLastInRange = aPos; } else if (fLastInRangeValue < nCellVal) { // not strictly sorted, continue with GetThis() - nLastInRange = nFirstLastInRange; + aLastInRange = aFirstLastInRange; bDone = true; } } @@ -1607,8 +1557,8 @@ ScBaseCell* ScQueryCellIterator::BinarySearch() else if (bStr && bByString) { OUString aCellStr; - sal_uLong nFormat = pCol->GetNumberFormat( pCol->maItems[i].nRow); - aCell.assign(*pCol->maItems[i].pCell); + sal_uLong nFormat = pCol->GetNumberFormat(i); + aCell.assign(aPos.first, aPos.second); ScCellFormat::GetInputString(aCell, nFormat, aCellStr, rFormatter); nRes = pCollator->compareString(aCellStr, rEntry.GetQueryItem().maString); @@ -1619,12 +1569,12 @@ ScBaseCell* ScQueryCellIterator::BinarySearch() if (nTmp < 0) { aLastInRangeString = aCellStr; - nLastInRange = i; + aLastInRange = aPos; } else if (nTmp > 0) { // not strictly sorted, continue with GetThis() - nLastInRange = nFirstLastInRange; + aLastInRange = aFirstLastInRange; bDone = true; } } @@ -1635,12 +1585,12 @@ ScBaseCell* ScQueryCellIterator::BinarySearch() if (nTmp > 0) { aLastInRangeString = aCellStr; - nLastInRange = i; + aLastInRange = aPos; } else if (nTmp < 0) { // not strictly sorted, continue with GetThis() - nLastInRange = nFirstLastInRange; + aLastInRange = aFirstLastInRange; bDone = true; } } @@ -1649,22 +1599,22 @@ ScBaseCell* ScQueryCellIterator::BinarySearch() { nRes = -1; // numeric < string if (bLessEqual) - nLastInRange = i; + aLastInRange = aPos; } else // if (bStr && !bByString) { nRes = 1; // string > numeric if (!bLessEqual) - nLastInRange = i; + aLastInRange = aPos; } if (nRes < 0) { if (bLessEqual) - nLo = nMid + 1; + nLogicalLow = nMid + 1; else // assumed to be SC_GREATER_EQUAL { if (nMid > 0) - nHi = nMid - 1; + nLogicalHigh = nMid - 1; else bDone = true; } @@ -1674,16 +1624,16 @@ ScBaseCell* ScQueryCellIterator::BinarySearch() if (bLessEqual) { if (nMid > 0) - nHi = nMid - 1; + nLogicalHigh = nMid - 1; else bDone = true; } else // assumed to be SC_GREATER_EQUAL - nLo = nMid + 1; + nLogicalLow = nMid + 1; } else { - nLo = i; + aLoPos = aPos; bDone = bFound = true; } } @@ -1697,21 +1647,24 @@ ScBaseCell* ScQueryCellIterator::BinarySearch() // Else, in case no exact match was found, we step back for a // subsequent GetThis() to find the last in range. Effectively this is // --nLo with nLastInRange == nLo-1. Both conditions combined yield: - nLo = nLastInRange; + aLoPos = aLastInRange; } - if (nLo < pCol->maItems.size() && pCol->maItems[nLo].nRow <= mpParam->nRow2) + + if (aLoPos.first != pCol->maCells.end() && toLogicalPos(aLoPos) <= static_cast<size_t>(mpParam->nRow2)) { - nRow = pCol->maItems[nLo].nRow; - pCell = pCol->maItems[nLo].pCell; - nColRow = nLo; + nRow = toLogicalPos(aLoPos); + maCurPos = aLoPos; + return true; } else { nRow = mpParam->nRow2 + 1; - pCell = 0; - nColRow = pCol->maItems.size() - 1; + // Set current position to the last possible row. + maCurPos.first = pCol->maCells.end(); + --maCurPos.first; + maCurPos.second = maCurPos.first->size - 1; + return false; } - return pCell; } @@ -1719,23 +1672,24 @@ ScBaseCell* ScQueryCellIterator::BinarySearch() ScHorizontalCellIterator::ScHorizontalCellIterator(ScDocument* pDocument, SCTAB nTable, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) : + maColPositions(nCol2-nCol2+1), pDoc( pDocument ), - nTab( nTable ), + mnTab( nTable ), nStartCol( nCol1 ), nEndCol( nCol2 ), nStartRow( nRow1 ), nEndRow( nRow2 ), - nCol( nCol1 ), - nRow( nRow1 ), + mnCol( nCol1 ), + mnRow( nRow1 ), bMore( true ) { - if (nTab >= pDoc->GetTableCount()) + if (mnTab >= pDoc->GetTableCount()) OSL_FAIL("try to access index out of bounds, FIX IT"); pNextRows = new SCROW[ nCol2-nCol1+1 ]; pNextIndices = new SCSIZE[ nCol2-nCol1+1 ]; - SetTab( nTab ); + SetTab( mnTab ); } ScHorizontalCellIterator::~ScHorizontalCellIterator() @@ -1746,101 +1700,103 @@ ScHorizontalCellIterator::~ScHorizontalCellIterator() void ScHorizontalCellIterator::SetTab( SCTAB nTabP ) { - nTab = nTabP; - nRow = nStartRow; - nCol = nStartCol; + mnTab = nTabP; + mnRow = nStartRow; + mnCol = nStartCol; bMore = true; - for (SCCOL i=nStartCol; i<=nEndCol; i++) + // Set the start position in each column. + for (SCCOL i = nStartCol; i <= nEndCol; ++i) { - ScColumn* pCol = &pDoc->maTabs[nTab]->aCol[i]; - - SCSIZE nIndex; - pCol->Search( nStartRow, nIndex ); - if ( nIndex < pCol->maItems.size() ) - { - pNextRows[i-nStartCol] = pCol->maItems[nIndex].nRow; - pNextIndices[i-nStartCol] = nIndex; - } - else - { - pNextRows[i-nStartCol] = MAXROWCOUNT; // Nothing found - pNextIndices[i-nStartCol] = MAXROWCOUNT; - } + ScColumn* pCol = &pDoc->maTabs[mnTab]->aCol[i]; + maColPositions[i-nStartCol].maPos = pCol->maCells.position(nStartRow).first; + maColPositions[i-nStartCol].maEnd = pCol->maCells.end(); } - if (pNextRows[0] != nStartRow) + if (maColPositions[0].maPos->type == sc::element_type_empty) + // Skip to the first non-empty cell. Advance(); } ScRefCellValue* ScHorizontalCellIterator::GetNext( SCCOL& rCol, SCROW& rRow ) { - if ( bMore ) - { - rCol = nCol; - rRow = nRow; - - ScColumn* pCol = &pDoc->maTabs[nTab]->aCol[nCol]; - SCSIZE nIndex = pNextIndices[nCol-nStartCol]; - OSL_ENSURE( nIndex < pCol->maItems.size(), "ScHorizontalCellIterator::GetNext: nIndex out of range" ); - ScBaseCell* pCell = pCol->maItems[nIndex].pCell; - if ( ++nIndex < pCol->maItems.size() ) - { - pNextRows[nCol-nStartCol] = pCol->maItems[nIndex].nRow; - pNextIndices[nCol-nStartCol] = nIndex; - } - else - { - pNextRows[nCol-nStartCol] = MAXROWCOUNT; // Nothing found - pNextIndices[nCol-nStartCol] = MAXROWCOUNT; - } - - Advance(); - maCurCell.assign(*pCell); - return &maCurCell; - } - else + if (!bMore) return NULL; + + // Return the current non-empty cell, and move the cursor to the next one. + rCol = mnCol; + rRow = mnRow; + + ColParam& r = maColPositions[mnCol-nStartCol]; + size_t nOffset = static_cast<size_t>(mnRow) - r.maPos->position; + maCurCell.assign(r.maPos, nOffset); + Advance(); + + return &maCurCell; } -bool ScHorizontalCellIterator::ReturnNext( SCCOL& rCol, SCROW& rRow ) +bool ScHorizontalCellIterator::GetPos( SCCOL& rCol, SCROW& rRow ) { - rCol = nCol; - rRow = nRow; + rCol = mnCol; + rRow = mnRow; return bMore; } void ScHorizontalCellIterator::Advance() { - bool bFound = false; - SCCOL i; + // Find the next non-empty cell in the current row. + for (SCCOL i = mnCol+1; i <= nEndCol; ++i) + { + ColParam& r = maColPositions[i-nStartCol]; + if (r.maPos == r.maEnd) + continue; - for (i=nCol+1; i<=nEndCol && !bFound; i++) - if (pNextRows[i-nStartCol] == nRow) - { - nCol = i; - bFound = true; - } + if (r.maPos->type == sc::element_type_empty) + continue; - if (!bFound) + size_t nRow = static_cast<size_t>(mnRow); + if (nRow < r.maPos->position) + continue; + + if (r.maPos->position + r.maPos->size <= nRow) + continue; + + // Found in the current row. + mnCol = i; + bMore = true; + return; + } + + // Move to the next row that has at least one non-empty cell. + size_t nMinRow = MAXROW+1; + size_t nMinRowCol = maColPositions.size(); + for (size_t i = 0, n = maColPositions.size(); i < n; ++i) { - SCROW nMinRow = MAXROW+1; - for (i=nStartCol; i<=nEndCol; i++) - if (pNextRows[i-nStartCol] < nMinRow) - { - nCol = i; - nMinRow = pNextRows[i-nStartCol]; - } + ColParam& r = maColPositions[i]; + if (r.maPos == r.maEnd) + // This column has ended. + continue; - if (nMinRow <= nEndRow) + // Move to the next block. + ++r.maPos; + + if (r.maPos != r.maEnd && r.maPos->position < nMinRow) { - nRow = nMinRow; - bFound = true; + nMinRow = r.maPos->position; + nMinRowCol = i; } } - if ( !bFound ) + if (nMinRowCol == maColPositions.size() || static_cast<SCROW>(nMinRow) > nEndRow) + { + // No more cells found. bMore = false; + return; + } + + mnCol = nMinRowCol + nStartCol; + mnRow = nMinRow; + bMore = true; } //------------------------------------------------------------------------ diff --git a/sc/source/core/data/documen2.cxx b/sc/source/core/data/documen2.cxx index bc9a5af324d8..df4b328e62e0 100644 --- a/sc/source/core/data/documen2.cxx +++ b/sc/source/core/data/documen2.cxx @@ -587,19 +587,6 @@ void ScDocument::EnsureTable( SCTAB nTab ) maTabs[nTab] = new ScTable(this, nTab, "temp", bExtras, bExtras); } -void ScDocument::PutCell( SCCOL nCol, SCROW nRow, SCTAB nTab, - ScBaseCell* pCell, sal_uLong nFormatIndex, bool bForceTab ) -{ - if (ValidTab(nTab)) - { - if (bForceTab) - EnsureTable(nTab); - - if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) - maTabs[nTab]->PutCell( nCol, nRow, nFormatIndex, pCell ); - } -} - ScRefCellValue ScDocument::GetRefCellValue( const ScAddress& rPos ) { if (!TableExists(rPos.Tab())) diff --git a/sc/source/core/data/documen4.cxx b/sc/source/core/data/documen4.cxx index 90a5d9abff78..0595e109121f 100644 --- a/sc/source/core/data/documen4.cxx +++ b/sc/source/core/data/documen4.cxx @@ -143,13 +143,16 @@ void ScDocument::InsertMatrixFormula(SCCOL nCol1, SCROW nRow1, SCTAB nMax = static_cast<SCTAB>(maTabs.size()); for (; itr != itrEnd && *itr < nMax; ++itr) { - if (maTabs[*itr]) - { - if (*itr == nTab1) - maTabs[*itr]->PutCell(nCol1, nRow1, pCell); - else - maTabs[*itr]->PutCell(nCol1, nRow1, pCell->Clone(*this, ScAddress( nCol1, nRow1, *itr), SC_CLONECELL_STARTLISTENING)); - } + if (!maTabs[*itr]) + continue; + + if (*itr == nTab1) + maTabs[*itr]->SetFormulaCell(nCol1, nRow1, pCell); + else + maTabs[*itr]->SetFormulaCell( + nCol1, nRow1, + new ScFormulaCell( + *pCell, *this, ScAddress(nCol1, nRow1, *itr), SC_CLONECELL_STARTLISTENING)); } ScSingleRefData aRefData; @@ -170,7 +173,6 @@ void ScDocument::InsertMatrixFormula(SCCOL nCol1, SCROW nRow1, { if (maTabs[*itr]) { - maTabs[*itr]->DoColResize( nCol1, nCol2, static_cast<SCSIZE>(nRow2 - nRow1 + 1) ); if (*itr != nTab1) { aRefData.nTab = *itr; @@ -189,7 +191,7 @@ void ScDocument::InsertMatrixFormula(SCCOL nCol1, SCROW nRow1, t->CalcRelFromAbs( aPos ); boost::scoped_ptr<ScTokenArray> pTokArr(aArr.Clone()); pCell = new ScFormulaCell( this, aPos, pTokArr.get(), eGram, MM_REFERENCE ); - maTabs[*itr]->PutCell(j, k, (ScBaseCell*) pCell); + maTabs[*itr]->SetFormulaCell(j, k, pCell); } } } @@ -284,7 +286,8 @@ void ScDocument::InsertTableOp(const ScTabOpParam& rParam, // Mehrfachopera itr = rMark.begin(); for (; itr != itrEnd && *itr < nMax; ++itr) if( maTabs[*itr] ) - maTabs[*itr]->PutCell( j, k, aRefCell.Clone( *this, ScAddress( j, k, *itr ), SC_CLONECELL_STARTLISTENING ) ); + maTabs[*itr]->SetFormulaCell( + j, k, new ScFormulaCell(aRefCell, *this, ScAddress(j, k, *itr), SC_CLONECELL_STARTLISTENING)); } } @@ -428,16 +431,6 @@ void ScDocument::CompileColRowNameFormula() } } -void ScDocument::DoColResize( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCSIZE nAdd ) -{ - if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) - maTabs[nTab]->DoColResize( nCol1, nCol2, nAdd ); - else - { - OSL_FAIL("DoColResize: wrong table"); - } -} - void ScDocument::InvalidateTableArea() { TableContainer::iterator it = maTabs.begin(); diff --git a/sc/source/core/data/documen8.cxx b/sc/source/core/data/documen8.cxx index 236b9e5983b1..3a2030e682e3 100644 --- a/sc/source/core/data/documen8.cxx +++ b/sc/source/core/data/documen8.cxx @@ -423,6 +423,15 @@ ScMacroManager* ScDocument::GetMacroManager() return mpMacroMgr.get(); } +bool ScDocument::IsEmptyData( SCTAB nTab, SCCOL nCol ) const +{ + const ScTable* pTab = FetchTable(nTab); + if (!pTab) + return true; + + return pTab->IsEmptyData(nCol); +} + //------------------------------------------------------------------------ void ScDocument::InvalidateTextWidth( const ScAddress* pAdrFrom, const ScAddress* pAdrTo, diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx index e1997fe22be5..d33711df9343 100644 --- a/sc/source/core/data/document.cxx +++ b/sc/source/core/data/document.cxx @@ -1111,7 +1111,7 @@ bool ScDocument::CanInsertRow( const ScRange& rRange ) const bool bTest = true; for (SCTAB i=nStartTab; i<=nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++) if (maTabs[i]) - bTest &= maTabs[i]->TestInsertRow( nStartCol, nEndCol, nSize ); + bTest &= maTabs[i]->TestInsertRow( nStartCol, nEndCol, nStartRow, nSize ); return bTest; } @@ -1138,7 +1138,7 @@ bool ScDocument::InsertRow( SCCOL nStartCol, SCTAB nStartTab, SetAutoCalc( false ); // avoid mulitple calculations for ( i = nStartTab; i <= nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++) if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i))) - bTest &= maTabs[i]->TestInsertRow( nStartCol, nEndCol, nSize ); + bTest &= maTabs[i]->TestInsertRow(nStartCol, nEndCol, nStartRow, nSize); if (bTest) { // UpdateBroadcastAreas have to be called before UpdateReference, so that entries @@ -3016,24 +3016,6 @@ void ScDocument::FillTabMarked( SCTAB nSrcTab, const ScMarkData& rMark, } } -void ScDocument::PutCell( const ScAddress& rPos, ScBaseCell* pCell, bool bForceTab ) -{ - SCTAB nTab = rPos.Tab(); - if ( bForceTab && ( nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) ) - { - bool bExtras = !bIsUndo; // Spaltenbreiten, Zeilenhoehen, Flags - - if (nTab >= static_cast<SCTAB>(maTabs.size())) - maTabs.resize(nTab + 1,NULL); - maTabs[nTab] = new ScTable(this, nTab, - OUString("temp"), - bExtras, bExtras); - } - - if (maTabs[nTab]) - maTabs[nTab]->PutCell( rPos, pCell ); -} - bool ScDocument::SetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const OUString& rString, ScSetStringParam* pParam ) diff --git a/sc/source/core/data/documentimport.cxx b/sc/source/core/data/documentimport.cxx index ec5b1c498d24..c3be0338ccda 100644 --- a/sc/source/core/data/documentimport.cxx +++ b/sc/source/core/data/documentimport.cxx @@ -75,32 +75,51 @@ void ScDocumentImport::setOriginDate(sal_uInt16 nYear, sal_uInt16 nMonth, sal_uI void ScDocumentImport::setAutoInput(const ScAddress& rPos, const OUString& rStr) { - if (!mpImpl->mrDoc.TableExists(rPos.Tab())) + ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); + if (!pTab) return; - mpImpl->mrDoc.maTabs[rPos.Tab()]->aCol[rPos.Col()].SetString( + pTab->aCol[rPos.Col()].SetString( rPos.Row(), rPos.Tab(), rStr, mpImpl->mrDoc.GetAddressConvention()); } void ScDocumentImport::setNumericCell(const ScAddress& rPos, double fVal) { - insertCell(rPos, new ScValueCell(fVal)); + ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); + if (!pTab) + return; + + pTab->aCol[rPos.Col()].SetValue(rPos.Row(), fVal); } void ScDocumentImport::setStringCell(const ScAddress& rPos, const OUString& rStr) { - insertCell(rPos, new ScStringCell(rStr)); + ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); + if (!pTab) + return; + + pTab->aCol[rPos.Col()].SetRawString(rPos.Row(), rStr); } void ScDocumentImport::setFormulaCell( const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar) { - insertCell(rPos, new ScFormulaCell(&mpImpl->mrDoc, rPos, rFormula, eGrammar)); + ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); + if (!pTab) + return; + + pTab->aCol[rPos.Col()].SetFormulaCell( + rPos.Row(), new ScFormulaCell(&mpImpl->mrDoc, rPos, rFormula, eGrammar)); } void ScDocumentImport::setFormulaCell(const ScAddress& rPos, const ScTokenArray& rArray) { - insertCell(rPos, new ScFormulaCell(&mpImpl->mrDoc, rPos, &rArray)); + ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab()); + if (!pTab) + return; + + pTab->aCol[rPos.Col()].SetFormulaCell( + rPos.Row(), new ScFormulaCell(&mpImpl->mrDoc, rPos, &rArray)); } void ScDocumentImport::finalize() @@ -123,20 +142,4 @@ void ScDocumentImport::finalize() } } -void ScDocumentImport::insertCell(const ScAddress& rPos, ScBaseCell* pCell) -{ - if (!mpImpl->mrDoc.TableExists(rPos.Tab())) - { - pCell->Delete(); - return; - } - - ScColumn& rCol = mpImpl->mrDoc.maTabs[rPos.Tab()]->aCol[rPos.Col()]; - sc::ColumnBlockPosition* p = mpImpl->maBlockPosSet.getBlockPosition(rPos.Tab(), rPos.Col()); - if (p) - rCol.SetCell(*p, rPos.Row(), pCell); - else - rCol.SetCell(rPos.Row(), pCell); -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/core/data/fillinfo.cxx b/sc/source/core/data/fillinfo.cxx index 8eb78e649127..c64a54d94c9e 100644 --- a/sc/source/core/data/fillinfo.cxx +++ b/sc/source/core/data/fillinfo.cxx @@ -40,6 +40,7 @@ #include "colorscale.hxx" #include "stlpool.hxx" #include "cellvalue.hxx" +#include "mtvcellfunc.hxx" #include <iostream> @@ -130,6 +131,68 @@ static void lcl_GetMergeRange( SCsCOL nX, SCsROW nY, SCSIZE nArrY, #define CELLINFO(x,y) pRowInfo[nArrY+y].pCellInfo[nArrX+x] +namespace { + +class RowInfoFiller +{ + ScDocument& mrDoc; + SCTAB mnTab; + RowInfo* mpRowInfo; + SCCOL mnArrX; + SCSIZE& mrArrY; + SCROW mnHiddenEndRow; + bool mbHiddenRow; + + bool isHidden(size_t nRow) + { + SCROW nThisRow = static_cast<SCROW>(nRow); + if (nThisRow > mnHiddenEndRow) + mbHiddenRow = mrDoc.RowHidden(nThisRow, mnTab, NULL, &mnHiddenEndRow); + return mbHiddenRow; + } + + void setInfo(const ScRefCellValue& rCell) + { + RowInfo* pThisRowInfo = &mpRowInfo[mrArrY]; + CellInfo* pInfo = &pThisRowInfo->pCellInfo[mnArrX]; + pInfo->maCell = rCell; + pThisRowInfo->bEmptyText = false; + pInfo->bEmptyCellText = false; + ++mrArrY; + } + +public: + RowInfoFiller(ScDocument& rDoc, SCTAB nTab, RowInfo* pRowInfo, SCCOL nArrX, SCSIZE& rArrY) : + mrDoc(rDoc), mnTab(nTab), mpRowInfo(pRowInfo), mnArrX(nArrX), mrArrY(rArrY), + mnHiddenEndRow(-1), mbHiddenRow(false) {} + + void operator() (size_t nRow, double fVal) + { + if (!isHidden(nRow)) + setInfo(ScRefCellValue(fVal)); + } + + void operator() (size_t nRow, const OUString& rStr) + { + if (!isHidden(nRow)) + setInfo(ScRefCellValue(&rStr)); + } + + void operator() (size_t nRow, const EditTextObject* p) + { + if (!isHidden(nRow)) + setInfo(ScRefCellValue(p)); + } + + void operator() (size_t nRow, const ScFormulaCell* p) + { + if (!isHidden(nRow)) + setInfo(ScRefCellValue(const_cast<ScFormulaCell*>(p))); + } +}; + +} + void ScDocument::FillInfo( ScTableInfo& rTabInfo, SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, SCTAB nTab, double nScaleX, double nScaleY, bool bPageMode, bool bFormulaMode, const ScMarkData* pMarkData ) @@ -363,30 +426,11 @@ void ScDocument::FillInfo( ScTableInfo& rTabInfo, SCCOL nX1, SCROW nY1, SCCOL nX ScColumn* pThisCol = &maTabs[nTab]->aCol[nX]; // Spalten-Daten nArrY = 1; - SCSIZE nUIndex; - bool bHiddenRow = true; - SCROW nHiddenEndRow = -1; - (void) pThisCol->Search( nY1, nUIndex ); - while ( nUIndex < pThisCol->maItems.size() && - (nThisRow=pThisCol->maItems[nUIndex].nRow) <= nY2 ) - { - if (nThisRow > nHiddenEndRow) - bHiddenRow = RowHidden( nThisRow, nTab, NULL, &nHiddenEndRow); - if ( !bHiddenRow ) - { - while ( pRowInfo[nArrY].nRowNo < nThisRow ) - ++nArrY; - OSL_ENSURE( pRowInfo[nArrY].nRowNo == nThisRow, "FillInfo: Row not found" ); - - RowInfo* pThisRowInfo = &pRowInfo[nArrY]; - CellInfo* pInfo = &pThisRowInfo->pCellInfo[nArrX]; - pInfo->maCell.assign(*pThisCol->maItems[nUIndex].pCell); - pThisRowInfo->bEmptyText = false; // Zeile nicht leer - pInfo->bEmptyCellText = false; // Zelle nicht leer - ++nArrY; - } - ++nUIndex; - } + // Iterate between rows nY1 and nY2 and pick up non-empty + // cells that are not hidden. + RowInfoFiller aFunc(*this, nTab, pRowInfo, nArrX, nArrY); + sc::ParseAllNonEmpty( + pThisCol->maCells.begin(), pThisCol->maCells, nY1, nY2, aFunc); if (nX+1 >= nX1) // Attribute/Blockmarken ab nX1-1 { diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx index da36a3435a0c..2cd5317102eb 100644 --- a/sc/source/core/data/formulacell.cxx +++ b/sc/source/core/data/formulacell.cxx @@ -45,6 +45,7 @@ #include "rtl/strbuf.hxx" #include "formulagroup.hxx" #include "listenercontext.hxx" +#include "types.hxx" #include <boost/bind.hpp> @@ -1804,7 +1805,7 @@ bool ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const (reserved: open: 32) */ -sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) +sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) const { switch ( cMatrixFlag ) { @@ -1819,7 +1820,7 @@ sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) if ( aOrg != rOrgPos ) { // First time or a different matrix than last time. rOrgPos = aOrg; - ScFormulaCell* pFCell; + const ScFormulaCell* pFCell; if ( cMatrixFlag == MM_REFERENCE ) pFCell = pDocument->GetFormulaCell(aOrg); else @@ -1866,7 +1867,8 @@ sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) else bCont = false; } while ( bCont ); - pFCell->SetMatColsRows( nC, nR ); + + const_cast<ScFormulaCell*>(pFCell)->SetMatColsRows(nC, nR); } } else @@ -1892,15 +1894,15 @@ sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR ) { if ( dC == 0 ) - nEdges |= 4; // left edge + nEdges |= sc::MatrixEdgeLeft; // left edge if ( dC+1 == nC ) - nEdges |= 16; // right edge + nEdges |= sc::MatrixEdgeRight; // right edge if ( dR == 0 ) - nEdges |= 8; // top edge + nEdges |= sc::MatrixEdgeTop; // top edge if ( dR+1 == nR ) - nEdges |= 2; // bottom edge + nEdges |= sc::MatrixEdgeBottom; // bottom edge if ( !nEdges ) - nEdges = 1; // inside + nEdges = sc::MatrixEdgeInside; // inside } #if OSL_DEBUG_LEVEL > 0 else @@ -2251,7 +2253,7 @@ bool ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode, ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aUndoPos, pOld, eTempGrammar, cMatrixFlag ); pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!) - pUndoDoc->PutCell( aUndoPos, pFCell ); + pUndoDoc->SetFormulaCell(aUndoPos, pFCell); } } bValChanged = false; diff --git a/sc/source/core/data/markdata.cxx b/sc/source/core/data/markdata.cxx index 0bd54badf8a4..a4232f89168a 100644 --- a/sc/source/core/data/markdata.cxx +++ b/sc/source/core/data/markdata.cxx @@ -426,6 +426,13 @@ void ScMarkData::ExtendRangeListTables( ScRangeList* pList ) const } } +ScRangeList ScMarkData::GetMarkedRanges() const +{ + ScRangeList aRet; + FillRangeListWithMarks(&aRet, false); + return aRet; +} + SCCOLROW ScMarkData::GetMarkColumnRanges( SCCOLROW* pRanges ) { if (bMarked) diff --git a/sc/source/core/data/mtvelements.cxx b/sc/source/core/data/mtvelements.cxx index efe07cff5ad7..57573a5a1480 100644 --- a/sc/source/core/data/mtvelements.cxx +++ b/sc/source/core/data/mtvelements.cxx @@ -25,6 +25,22 @@ CellTextAttr::CellTextAttr(sal_uInt16 nTextWidth, sal_uInt8 nScriptType) : mnTextWidth(nTextWidth), mnScriptType(nScriptType) {} +ColumnBlockPosition& ColumnBlockPosition::operator= (const ColumnBlockPosition& r) +{ + miBroadcasterPos = r.miBroadcasterPos; + miCellTextAttrPos = r.miCellTextAttrPos; + miCellPos = r.miCellPos; + return *this; +} + +ColumnBlockConstPosition& ColumnBlockConstPosition::operator= (const ColumnBlockConstPosition& r) +{ + miBroadcasterPos = r.miBroadcasterPos; + miCellTextAttrPos = r.miCellTextAttrPos; + miCellPos = r.miCellPos; + return *this; +} + ColumnBlockPositionSet::ColumnBlockPositionSet(ScDocument& rDoc) : mrDoc(rDoc) {} ColumnBlockPosition* ColumnBlockPositionSet::getBlockPosition(SCTAB nTab, SCCOL nCol) diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx index c0e2c3f94e82..d26a42c2ba64 100644 --- a/sc/source/core/data/table1.cxx +++ b/sc/source/core/data/table1.cxx @@ -516,11 +516,11 @@ bool ScTable::GetCellArea( SCCOL& rEndCol, SCROW& rEndRow ) const SCCOL nMaxX = 0; SCROW nMaxY = 0; for (SCCOL i=0; i<=MAXCOL; i++) - if (!aCol[i].IsEmptyVisData()) + if (!aCol[i].IsEmptyData()) { bFound = true; nMaxX = i; - SCROW nColY = aCol[i].GetLastVisDataPos(); + SCROW nColY = aCol[i].GetLastDataPos(); if (nColY > nMaxY) nMaxY = nColY; } @@ -564,12 +564,12 @@ bool ScTable::GetPrintArea( SCCOL& rEndCol, SCROW& rEndRow, bool bNotes, bool bF SCCOL i; for (i=0; i<=MAXCOL; i++) // Daten testen - if (!aCol[i].IsEmptyVisData()) + if (!aCol[i].IsEmptyData()) { bFound = true; if (i>nMaxX) nMaxX = i; - SCROW nColY = aCol[i].GetLastVisDataPos(); + SCROW nColY = aCol[i].GetLastDataPos(); if (nColY > nMaxY) nMaxY = nColY; } @@ -697,10 +697,10 @@ bool ScTable::GetPrintAreaVer( SCCOL nStartCol, SCCOL nEndCol, } for (i=nStartCol; i<=nEndCol; i++) // Daten testen - if (!aCol[i].IsEmptyVisData()) + if (!aCol[i].IsEmptyData()) { bFound = true; - SCROW nColY = aCol[i].GetLastVisDataPos(); + SCROW nColY = aCol[i].GetLastDataPos(); if (nColY > nMaxY) nMaxY = nColY; } @@ -756,12 +756,12 @@ bool ScTable::GetDataStart( SCCOL& rStartCol, SCROW& rStartRow ) const bool bDatFound = false; for (i=0; i<=MAXCOL; i++) // Daten testen - if (!aCol[i].IsEmptyVisData()) + if (!aCol[i].IsEmptyData()) { if (!bDatFound && i<nMinX) nMinX = i; bFound = bDatFound = true; - SCROW nColY = aCol[i].GetFirstVisDataPos(); + SCROW nColY = aCol[i].GetFirstDataPos(); if (nColY < nMinY) nMinY = nColY; } @@ -991,7 +991,7 @@ SCSIZE ScTable::GetEmptyLinesInBlock( SCCOL nStartCol, SCROW nStartRow, SCCOL nCol; if ((eDir == DIR_BOTTOM) || (eDir == DIR_TOP)) { - nCount = static_cast<SCSIZE>(nEndRow - nStartRow); + nCount = static_cast<SCSIZE>(nEndRow - nStartRow + 1); for (nCol = nStartCol; nCol <= nEndCol; nCol++) nCount = std::min(nCount, aCol[nCol].GetEmptyLinesInBlock(nStartRow, nEndRow, eDir)); } @@ -1385,17 +1385,31 @@ bool ScTable::GetNextMarkedCell( SCCOL& rCol, SCROW& rRow, const ScMarkData& rMa if ( nStart <= MAXROW ) { SCROW nEnd = rArray.GetMarkEnd( nStart, false ); - ScColumnIterator aColIter( &aCol[rCol], nStart, nEnd ); - SCROW nCellRow; - ScBaseCell* pCell = NULL; - while ( aColIter.Next( nCellRow, pCell ) ) + + const sc::CellStoreType& rCells = aCol[rCol].maCells; + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = rCells.position(nStart); + sc::CellStoreType::const_iterator it = aPos.first; + SCROW nTestRow = nStart; + if (it->type == sc::element_type_empty) { - if (pCell) + // Skip the empty block. + nTestRow += it->size - aPos.second; + ++it; + if (it == rCells.end()) { - rRow = nCellRow; - return true; // Zelle gefunden + // No more block. + rRow = MAXROW + 1; + return false; } } + + if (nTestRow < nEnd) + { + // Cell found. + rRow = nTestRow; + return true; + } + rRow = nEnd + 1; // naechsten markierten Bereich suchen } else @@ -1719,12 +1733,12 @@ void ScTable::ExtendPrintArea( OutputDevice* pDev, { SCCOL nPrintCol = nDataCol; VisibleDataCellIterator aIter(*mpHiddenRows, aCol[nDataCol]); - ScBaseCell* pCell = aIter.reset(nStartRow); - if (!pCell) + ScRefCellValue aCell = aIter.reset(nStartRow); + if (aCell.isEmpty()) // No visible cells found in this column. Skip it. continue; - while (pCell) + while (!aCell.isEmpty()) { SCCOL nNewCol = nDataCol; SCROW nRow = aIter.getRow(); @@ -1735,7 +1749,7 @@ void ScTable::ExtendPrintArea( OutputDevice* pDev, MaybeAddExtraColumn(nNewCol, nRow, pDev, nPPTX, nPPTY); if (nNewCol > nPrintCol) nPrintCol = nNewCol; - pCell = aIter.next(); + aCell = aIter.next(); } if (nPrintCol > rEndCol) @@ -1748,8 +1762,8 @@ void ScTable::ExtendPrintArea( OutputDevice* pDev, void ScTable::MaybeAddExtraColumn(SCCOL& rCol, SCROW nRow, OutputDevice* pDev, double nPPTX, double nPPTY) { - ScBaseCell* pCell = aCol[rCol].GetCell(nRow); - if (!pCell || !pCell->HasStringData()) + ScRefCellValue aCell = aCol[rCol].GetCellValue(nRow); + if (!aCell.hasString()) return; bool bFormula = false; //! ueberge @@ -1799,8 +1813,8 @@ void ScTable::MaybeAddExtraColumn(SCCOL& rCol, SCROW nRow, OutputDevice* pDev, d SCCOL nNewCol = rCol; while (nMissing > 0 && nNewCol < MAXCOL) { - ScBaseCell* pNextCell = aCol[nNewCol+1].GetCell(nRow); - if (pNextCell) + ScRefCellValue aNextCell = aCol[nNewCol+1].GetCellValue(nRow); + if (!aNextCell.isEmpty()) // Cell content in a next column ends display of this string. nMissing = 0; else @@ -1868,12 +1882,6 @@ void ScTable::CopyPrintRange(const ScTable& rTable) } } -void ScTable::DoColResize( SCCOL nCol1, SCCOL nCol2, SCSIZE nAdd ) -{ - for (SCCOL nCol=nCol1; nCol<=nCol2; nCol++) - aCol[nCol].ReserveSize(aCol[nCol].GetCellCount() + nAdd); -} - void ScTable::SetRepeatColRange( const ScRange* pNew ) { setPrintRange( pRepeatColRange, pNew ); @@ -1954,7 +1962,6 @@ SCROW ScTable::VisibleDataCellIterator::ROW_NOT_FOUND = -1; ScTable::VisibleDataCellIterator::VisibleDataCellIterator(ScFlatBoolRowSegments& rRowSegs, ScColumn& rColumn) : mrRowSegs(rRowSegs), mrColumn(rColumn), - mpCell(NULL), mnCurRow(ROW_NOT_FOUND), mnUBound(ROW_NOT_FOUND) { @@ -1964,19 +1971,19 @@ ScTable::VisibleDataCellIterator::~VisibleDataCellIterator() { } -ScBaseCell* ScTable::VisibleDataCellIterator::reset(SCROW nRow) +ScRefCellValue ScTable::VisibleDataCellIterator::reset(SCROW nRow) { if (nRow > MAXROW) { mnCurRow = ROW_NOT_FOUND; - return NULL; + return ScRefCellValue(); } ScFlatBoolRowSegments::RangeData aData; if (!mrRowSegs.getRangeData(nRow, aData)) { mnCurRow = ROW_NOT_FOUND; - return NULL; + return ScRefCellValue(); } if (!aData.mbValue) @@ -1995,23 +2002,23 @@ ScBaseCell* ScTable::VisibleDataCellIterator::reset(SCROW nRow) { // Make sure the row doesn't exceed our current limit. mnCurRow = ROW_NOT_FOUND; - return NULL; + return ScRefCellValue(); } } - mpCell = mrColumn.GetCell(mnCurRow); - if (mpCell) + maCell = mrColumn.GetCellValue(mnCurRow); + if (!maCell.isEmpty()) // First visible cell found. - return mpCell; + return maCell; // Find a first visible cell below this row (if any). return next(); } -ScBaseCell* ScTable::VisibleDataCellIterator::next() +ScRefCellValue ScTable::VisibleDataCellIterator::next() { if (mnCurRow == ROW_NOT_FOUND) - return NULL; + return ScRefCellValue(); while (mrColumn.GetNextDataPos(mnCurRow)) { @@ -2022,7 +2029,7 @@ ScBaseCell* ScTable::VisibleDataCellIterator::next() if (!mrRowSegs.getRangeData(mnCurRow, aData)) { mnCurRow = ROW_NOT_FOUND; - return NULL; + return ScRefCellValue(); } if (aData.mbValue) @@ -2037,12 +2044,13 @@ ScBaseCell* ScTable::VisibleDataCellIterator::next() mnUBound = aData.mnRow2; } - mpCell = mrColumn.GetCell(mnCurRow); - if (mpCell) - return mpCell; + maCell = mrColumn.GetCellValue(mnCurRow); + if (!maCell.isEmpty()) + return maCell; } + mnCurRow = ROW_NOT_FOUND; - return NULL; + return ScRefCellValue(); } SCROW ScTable::VisibleDataCellIterator::getRow() const @@ -2166,7 +2174,7 @@ ScRefCellValue ScTable::GetRefCellValue( SCCOL nCol, SCROW nRow ) if (!ValidColRow(nCol, nRow)) return ScRefCellValue(); - return aCol[nCol].GetRefCellValue(nRow); + return aCol[nCol].GetCellValue(nRow); } SvtBroadcaster* ScTable::GetBroadcaster( SCCOL nCol, SCROW nRow ) diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx index 5df2ac12f636..3c5b486efa07 100644 --- a/sc/source/core/data/table2.cxx +++ b/sc/source/core/data/table2.cxx @@ -44,6 +44,9 @@ #include "colorscale.hxx" #include "tokenarray.hxx" #include "clipcontext.hxx" +#include "types.hxx" +#include "editutil.hxx" +#include "mtvcellfunc.hxx" #include "scitems.hxx" #include <editeng/boxitem.hxx> @@ -121,7 +124,7 @@ void ScTable::SetCalcNotification( bool bSet ) } -bool ScTable::TestInsertRow( SCCOL nStartCol, SCCOL nEndCol, SCSIZE nSize ) const +bool ScTable::TestInsertRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize ) const { bool bTest = true; @@ -129,7 +132,7 @@ bool ScTable::TestInsertRow( SCCOL nStartCol, SCCOL nEndCol, SCSIZE nSize ) cons bTest = pOutlineTable->TestInsertRow(nSize); for (SCCOL i=nStartCol; (i<=nEndCol) && bTest; i++) - bTest = aCol[i].TestInsertRow( nSize ); + bTest = aCol[i].TestInsertRow(nStartRow, nSize); return bTest; } @@ -850,6 +853,106 @@ void ScTable::MixMarked( aCol[i].MixMarked(rCxt, rMark, nFunction, bSkipEmpty, pSrcTab->aCol[i]); } +namespace { + +class TransClipHandler +{ + ScTable& mrClipTab; + SCTAB mnSrcTab; + SCCOL mnSrcCol; + size_t mnTopRow; + SCROW mnTransRow; + bool mbAsLink; + bool mbWasCut; + + ScAddress getDestPos(size_t nRow) const + { + return ScAddress(static_cast<SCCOL>(nRow-mnTopRow), mnTransRow, mrClipTab.GetTab()); + } + + ScFormulaCell* createRefCell(size_t nSrcRow, const ScAddress& rDestPos) const + { + ScAddress aSrcPos(mnSrcCol, nSrcRow, mnSrcTab); + ScSingleRefData aRef; + aRef.InitAddress(aSrcPos); // Absolute reference. + aRef.SetFlag3D(true); + + ScTokenArray aArr; + aArr.AddSingleReference(aRef); + return new ScFormulaCell(&mrClipTab.GetDoc(), rDestPos, &aArr); + } + + void setLink(size_t nRow) + { + SCCOL nTransCol = nRow - mnTopRow; + mrClipTab.SetFormulaCell( + nTransCol, mnTransRow, createRefCell(nRow, getDestPos(nRow))); + } + +public: + TransClipHandler(ScTable& rClipTab, SCTAB nSrcTab, SCCOL nSrcCol, size_t nTopRow, SCROW nTransRow, bool bAsLink, bool bWasCut) : + mrClipTab(rClipTab), mnSrcTab(nSrcTab), mnSrcCol(nSrcCol), + mnTopRow(nTopRow), mnTransRow(nTransRow), mbAsLink(bAsLink), mbWasCut(bWasCut) {} + + void operator() (size_t nRow, double fVal) + { + if (mbAsLink) + { + setLink(nRow); + return; + } + + SCCOL nTransCol = nRow - mnTopRow; + mrClipTab.SetValue(nTransCol, mnTransRow, fVal); + } + + void operator() (size_t nRow, const OUString& rStr) + { + if (mbAsLink) + { + setLink(nRow); + return; + } + + SCCOL nTransCol = nRow - mnTopRow; + mrClipTab.SetRawString(nTransCol, mnTransRow, rStr); + } + + void operator() (size_t nRow, const EditTextObject* p) + { + if (mbAsLink) + { + setLink(nRow); + return; + } + + SCCOL nTransCol = nRow - mnTopRow; + mrClipTab.SetEditText(nTransCol, mnTransRow, ScEditUtil::Clone(*p, mrClipTab.GetDoc())); + } + + void operator() (size_t nRow, const ScFormulaCell* p) + { + if (mbAsLink) + { + setLink(nRow); + return; + } + + ScFormulaCell* pNew = new ScFormulaCell( + *p, mrClipTab.GetDoc(), getDestPos(nRow), SC_CLONECELL_STARTLISTENING); + + // Referenzen drehen + // bei Cut werden Referenzen spaeter per UpdateTranspose angepasst + + if (!mbWasCut) + pNew->TransposeReference(); + + SCCOL nTransCol = nRow - mnTopRow; + mrClipTab.SetFormulaCell(nTransCol, mnTransRow, pNew); + } +}; + +} void ScTable::TransposeClip( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScTable* pTransClip, sal_uInt16 nFlags, bool bAsLink ) @@ -861,8 +964,6 @@ void ScTable::TransposeClip( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, for (SCCOL nCol=nCol1; nCol<=nCol2; nCol++) { SCROW nRow; - ScBaseCell* pCell; - if ( bAsLink && nFlags == IDF_ALL ) { // with IDF_ALL, also create links (formulas) for empty cells @@ -882,41 +983,16 @@ void ScTable::TransposeClip( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScTokenArray aArr; aArr.AddSingleReference( aRef ); - ScBaseCell* pNew = new ScFormulaCell( pDestDoc, aDestPos, &aArr ); - pTransClip->PutCell( static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), pNew ); + pTransClip->SetFormulaCell( + static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), + new ScFormulaCell(pDestDoc, aDestPos, &aArr)); } } else { - ScColumnIterator aIter( &aCol[nCol], nRow1, nRow2 ); - while (aIter.Next( nRow, pCell )) - { - ScAddress aDestPos( static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), pTransClip->nTab ); - ScBaseCell* pNew; - if ( bAsLink ) // Referenz erzeugen ? - { - pNew = aCol[nCol].CreateRefCell( pDestDoc, aDestPos, aIter.GetIndex(), nFlags ); - } - else // kopieren - { - ScAddress aOwnPos( nCol, nRow, nTab ); - if (pCell->GetCellType() == CELLTYPE_FORMULA) - { - pNew = pCell->Clone( *pDestDoc, aDestPos, SC_CLONECELL_STARTLISTENING ); - - // Referenzen drehen - // bei Cut werden Referenzen spaeter per UpdateTranspose angepasst - - if (!bWasCut) - ((ScFormulaCell*)pNew)->TransposeReference(); - } - else - { - pNew = pCell->Clone( *pDestDoc, aDestPos ); - } - } - pTransClip->PutCell( static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), pNew ); - } + TransClipHandler aFunc(*pTransClip, nTab, nCol, nRow1, static_cast<SCROW>(nCol-nCol1), bAsLink, bWasCut); + const sc::CellStoreType& rCells = aCol[nCol].maCells; + sc::ParseAllNonEmpty(rCells.begin(), rCells, nRow1, nRow2, aFunc); } // Attribute @@ -1008,7 +1084,7 @@ void ScTable::BroadcastInArea( SCCOL nCol1, SCROW nRow1, if (nRow2 > MAXROW) nRow2 = MAXROW; if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) for (SCCOL i = nCol1; i <= nCol2; i++) - aCol[i].BroadcastInArea( nRow1, nRow2 ); + aCol[i].SetDirty(nRow1, nRow2); } @@ -1312,39 +1388,6 @@ bool ScTable::TestCopyScenarioTo( const ScTable* pDestTab ) const return bOk; } -void ScTable::PutCell( SCCOL nCol, SCROW nRow, ScBaseCell* pCell ) -{ - if (ValidColRow(nCol,nRow)) - { - if (pCell) - aCol[nCol].Insert( nRow, pCell ); - else - aCol[nCol].Delete( nRow ); - } -} - - -void ScTable::PutCell( SCCOL nCol, SCROW nRow, sal_uLong nFormatIndex, ScBaseCell* pCell ) -{ - if (ValidColRow(nCol,nRow)) - { - if (pCell) - aCol[nCol].Insert( nRow, nFormatIndex, pCell ); - else - aCol[nCol].Delete( nRow ); - } -} - - -void ScTable::PutCell( const ScAddress& rPos, ScBaseCell* pCell ) -{ - if (pCell) - aCol[rPos.Col()].Insert( rPos.Row(), pCell ); - else - aCol[rPos.Col()].Delete( rPos.Row() ); -} - - bool ScTable::SetString( SCCOL nCol, SCROW nRow, SCTAB nTabP, const String& rString, ScSetStringParam* pParam ) { @@ -1417,6 +1460,11 @@ void ScTable::SetValue( SCCOL nCol, SCROW nRow, const double& rVal ) aCol[nCol].SetValue( nRow, rVal ); } +void ScTable::SetRawString( SCCOL nCol, SCROW nRow, const OUString& rStr ) +{ + if (ValidColRow(nCol, nRow)) + aCol[nCol].SetRawString(nRow, rStr); +} void ScTable::GetString( SCCOL nCol, SCROW nRow, OUString& rString ) const { @@ -1530,14 +1578,12 @@ CellType ScTable::GetCellType( SCCOL nCol, SCROW nRow ) const return CELLTYPE_NONE; } - -ScBaseCell* ScTable::GetCell( SCCOL nCol, SCROW nRow ) const +ScRefCellValue ScTable::GetCellValue( SCCOL nCol, SCROW nRow ) const { - if (ValidColRow( nCol, nRow )) - return aCol[nCol].GetCell( nRow ); + if (!ValidColRow(nCol, nRow)) + return ScRefCellValue(); - OSL_FAIL("GetCell: out of range"); - return NULL; + return aCol[nCol].GetCellValue(nRow); } void ScTable::GetFirstDataPos(SCCOL& rCol, SCROW& rRow) const @@ -1629,7 +1675,7 @@ void ScTable::SetDirty( const ScRange& rRange ) pDocument->SetAutoCalc( false ); // Mehrfachberechnungen vermeiden SCCOL nCol2 = rRange.aEnd.Col(); for (SCCOL i=rRange.aStart.Col(); i<=nCol2; i++) - aCol[i].SetDirty( rRange ); + aCol[i].SetDirty(rRange.aStart.Row(), rRange.aEnd.Row()); pDocument->SetAutoCalc( bOldAutoCalc ); } @@ -1717,6 +1763,13 @@ void ScTable::CalcAfterLoad() for (SCCOL i=0; i <= MAXCOL; i++) aCol[i].CalcAfterLoad(); } +bool ScTable::IsEmptyData( SCCOL nCol ) const +{ + if (!ValidCol(nCol)) + return true; + + return aCol[nCol].IsEmptyData(); +} void ScTable::ResetChanged( const ScRange& rRange ) { @@ -2001,34 +2054,35 @@ void ScTable::FindMaxRotCol( RowInfo* pRowInfo, SCSIZE nArrCount, SCCOL nX1, SCC bool ScTable::HasBlockMatrixFragment( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const { - // nix:0, mitte:1, unten:2, links:4, oben:8, rechts:16, offen:32 - sal_uInt16 nEdges; + using namespace sc; + + sal_uInt16 nEdges = 0; if ( nCol1 == nCol2 ) { // linke und rechte Spalte - const sal_uInt16 n = 4 | 16; + const sal_uInt16 n = MatrixEdgeLeft | MatrixEdgeRight; nEdges = aCol[nCol1].GetBlockMatrixEdges( nRow1, nRow2, n ); // nicht (4 und 16) oder 1 oder 32 - if ( nEdges && (((nEdges & n) != n) || (nEdges & 33)) ) + if (nEdges && (((nEdges & n) != n) || (nEdges & (MatrixEdgeInside|MatrixEdgeOpen)))) return true; // linke oder rechte Kante fehlt oder offen } else { // linke Spalte - nEdges = aCol[nCol1].GetBlockMatrixEdges( nRow1, nRow2, 4 ); + nEdges = aCol[nCol1].GetBlockMatrixEdges(nRow1, nRow2, MatrixEdgeLeft); // nicht 4 oder 1 oder 32 - if ( nEdges && (((nEdges & 4) != 4) || (nEdges & 33)) ) + if (nEdges && (((nEdges & MatrixEdgeLeft) != MatrixEdgeLeft) || (nEdges & (MatrixEdgeInside|MatrixEdgeOpen)))) return true; // linke Kante fehlt oder offen // rechte Spalte - nEdges = aCol[nCol2].GetBlockMatrixEdges( nRow1, nRow2, 16 ); + nEdges = aCol[nCol2].GetBlockMatrixEdges(nRow1, nRow2, MatrixEdgeRight); // nicht 16 oder 1 oder 32 - if ( nEdges && (((nEdges & 16) != 16) || (nEdges & 33)) ) + if (nEdges && (((nEdges & MatrixEdgeRight) != MatrixEdgeRight) || (nEdges & (MatrixEdgeInside|MatrixEdgeOpen)))) return true; // rechte Kante fehlt oder offen } if ( nRow1 == nRow2 ) { // obere und untere Zeile bool bOpen = false; - const sal_uInt16 n = 2 | 8; + const sal_uInt16 n = MatrixEdgeBottom | MatrixEdgeTop; for ( SCCOL i=nCol1; i<=nCol2; i++) { nEdges = aCol[i].GetBlockMatrixEdges( nRow1, nRow1, n ); @@ -2036,11 +2090,11 @@ bool ScTable::HasBlockMatrixFragment( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCR { if ( (nEdges & n) != n ) return true; // obere oder untere Kante fehlt - if ( nEdges & 4 ) + if (nEdges & MatrixEdgeLeft) bOpen = true; // linke Kante oeffnet, weitersehen else if ( !bOpen ) return true; // es gibt was, was nicht geoeffnet wurde - if ( nEdges & 16 ) + if (nEdges & MatrixEdgeRight) bOpen = false; // rechte Kante schliesst } } @@ -2064,11 +2118,11 @@ bool ScTable::HasBlockMatrixFragment( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCR // in unterer Zeile keine untere Kante if ( (nEdges & n) != n ) return true; - if ( nEdges & 4 ) + if (nEdges & MatrixEdgeLeft) bOpen = true; // linke Kante oeffnet, weitersehen else if ( !bOpen ) return true; // es gibt was, was nicht geoeffnet wurde - if ( nEdges & 16 ) + if (nEdges & MatrixEdgeRight) bOpen = false; // rechte Kante schliesst } } @@ -3332,84 +3386,109 @@ short DiffSign( T a, T b ) (a>b) ? 1 : 0; } +namespace { + +class OutlineArrayFinder +{ + ScRange maRef; + SCCOL mnCol; + SCTAB mnTab; + ScOutlineArray* mpArray; + bool mbSizeChanged; + +public: + OutlineArrayFinder(const ScRange& rRef, SCCOL nCol, SCTAB nTab, ScOutlineArray* pArray, bool bSizeChanged) : + maRef(rRef), mnCol(nCol), mnTab(nTab), mpArray(pArray), + mbSizeChanged(bSizeChanged) {} + + bool operator() (size_t nRow, const ScFormulaCell* pCell) + { + SCROW nRow2 = static_cast<SCROW>(nRow); + + if (!pCell->HasRefListExpressibleAsOneReference(maRef)) + return false; + + if (maRef.aStart.Row() != nRow2 || maRef.aEnd.Row() != nRow2 || + maRef.aStart.Tab() != mnTab || maRef.aEnd.Tab() != mnTab) + return false; + + if (DiffSign(maRef.aStart.Col(), mnCol) != DiffSign(maRef.aEnd.Col(), mnCol)) + return false; + + return mpArray->Insert(maRef.aStart.Col(), maRef.aEnd.Col(), mbSizeChanged); + } +}; + +} void ScTable::DoAutoOutline( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) { + typedef mdds::flat_segment_tree<SCROW, bool> UsedRowsType; + bool bSizeChanged = false; SCCOL nCol; SCROW nRow; - SCROW i; bool bFound; ScOutlineArray* pArray; - ScBaseCell* pCell; ScRange aRef; StartOutlineTable(); // Zeilen - SCROW nCount = nEndRow-nStartRow+1; - bool* pUsed = new bool[nCount]; - for (i=0; i<nCount; i++) - pUsed[i] = false; + UsedRowsType aUsed(0, MAXROW+1, false); for (nCol=nStartCol; nCol<=nEndCol; nCol++) - if (!aCol[nCol].IsEmptyData()) - aCol[nCol].FindUsed( nStartRow, nEndRow, pUsed ); + aCol[nCol].FindUsed(nStartRow, nEndRow, aUsed); + aUsed.build_tree(); pArray = pOutlineTable->GetRowArray(); for (nRow=nStartRow; nRow<=nEndRow; nRow++) - if (pUsed[nRow-nStartRow]) + { + bool bUsed = false; + SCROW nLastRow = nRow; + aUsed.search_tree(nRow, bUsed, NULL, &nLastRow); + if (!bUsed) { - bFound = false; - for (nCol=nStartCol; nCol<=nEndCol && !bFound; nCol++) - if (!aCol[nCol].IsEmptyData()) - { - pCell = aCol[nCol].GetCell( nRow ); - if (pCell) - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - if (((ScFormulaCell*)pCell)->HasRefListExpressibleAsOneReference( aRef )) - if ( aRef.aStart.Col() == nCol && aRef.aEnd.Col() == nCol && - aRef.aStart.Tab() == nTab && aRef.aEnd.Tab() == nTab && - DiffSign( aRef.aStart.Row(), nRow ) == - DiffSign( aRef.aEnd.Row(), nRow ) ) - { - if (pArray->Insert( aRef.aStart.Row(), aRef.aEnd.Row(), bSizeChanged )) - { - bFound = true; - } - } - } + nRow = nLastRow; + continue; } - delete[] pUsed; + bFound = false; + for (nCol=nStartCol; nCol<=nEndCol && !bFound; nCol++) + { + ScRefCellValue aCell = aCol[nCol].GetCellValue(nRow); - // Spalten + if (aCell.meType != CELLTYPE_FORMULA) + continue; - pArray = pOutlineTable->GetColArray(); - for (nCol=nStartCol; nCol<=nEndCol; nCol++) - { - if (!aCol[nCol].IsEmptyData()) - { - bFound = false; - ScColumnIterator aIter( &aCol[nCol], nStartRow, nEndRow ); - while ( aIter.Next( nRow, pCell ) && !bFound ) + if (!aCell.mpFormula->HasRefListExpressibleAsOneReference(aRef)) + continue; + + if ( aRef.aStart.Col() == nCol && aRef.aEnd.Col() == nCol && + aRef.aStart.Tab() == nTab && aRef.aEnd.Tab() == nTab && + DiffSign( aRef.aStart.Row(), nRow ) == + DiffSign( aRef.aEnd.Row(), nRow ) ) { - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - if (((ScFormulaCell*)pCell)->HasRefListExpressibleAsOneReference( aRef )) - if ( aRef.aStart.Row() == nRow && aRef.aEnd.Row() == nRow && - aRef.aStart.Tab() == nTab && aRef.aEnd.Tab() == nTab && - DiffSign( aRef.aStart.Col(), nCol ) == - DiffSign( aRef.aEnd.Col(), nCol ) ) - { - if (pArray->Insert( aRef.aStart.Col(), aRef.aEnd.Col(), bSizeChanged )) - { - bFound = true; - } - } + if (pArray->Insert( aRef.aStart.Row(), aRef.aEnd.Row(), bSizeChanged )) + { + bFound = true; + } } } } + + // Column + pArray = pOutlineTable->GetColArray(); + for (nCol=nStartCol; nCol<=nEndCol; nCol++) + { + if (aCol[nCol].IsEmptyData()) + continue; + + OutlineArrayFinder aFunc(aRef, nCol, nTab, pArray, bSizeChanged); + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = + sc::FindFormula(aCol[nCol].maCells, nStartRow, nEndRow, aFunc); + } } // CopyData - fuer Query in anderen Bereich @@ -3433,27 +3512,26 @@ void ScTable::CopyData( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW n { aSrc.SetCol( nCol ); aDest.SetCol( nDestX ); - ScBaseCell* pCell = GetCell( nCol, nRow ); - if (pCell) + ScCellValue aCell; + aCell.assign(*pDocument, ScAddress(nCol, nRow, nTab)); + + if (aCell.meType == CELLTYPE_FORMULA) { - pCell = pCell->Clone( *pDocument ); - if (pCell->GetCellType() == CELLTYPE_FORMULA) - { - ((ScFormulaCell*)pCell)->UpdateReference( URM_COPY, aRange, - ((SCsCOL) nDestCol) - ((SCsCOL) nStartCol), - ((SCsROW) nDestRow) - ((SCsROW) nStartRow), - ((SCsTAB) nDestTab) - ((SCsTAB) nTab) ); - ((ScFormulaCell*)pCell)->aPos = aDest; - } + aCell.mpFormula->UpdateReference( URM_COPY, aRange, + ((SCsCOL) nDestCol) - ((SCsCOL) nStartCol), + ((SCsROW) nDestRow) - ((SCsROW) nStartRow), + ((SCsTAB) nDestTab) - ((SCsTAB) nTab) ); + aCell.mpFormula->aPos = aDest; } + if (bThisTab) { - PutCell( nDestX, nDestY, pCell ); + aCell.release(aCol[nDestX], nDestY); SetPattern( nDestX, nDestY, *GetPattern( nCol, nRow ), true ); } else { - pDocument->PutCell( aDest, pCell ); + aCell.release(*pDocument, aDest); pDocument->SetPattern( aDest, *GetPattern( nCol, nRow ), true ); } diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx index 0058cacd44b2..e003aa714b43 100644 --- a/sc/source/core/data/table3.cxx +++ b/sc/source/core/data/table3.cxx @@ -54,6 +54,7 @@ #include "docpool.hxx" #include "cellvalue.hxx" #include "tokenarray.hxx" +#include "mtvcellfunc.hxx" #include <vector> #include <boost/unordered_set.hpp> @@ -199,7 +200,7 @@ short Compare( const String &sInput1, const String &sInput2, struct ScSortInfo { - ScBaseCell* pCell; + ScRefCellValue maCell; SCCOLROW nOrg; DECL_FIXEDMEMPOOL_NEWDEL( ScSortInfo ); }; @@ -275,9 +276,8 @@ ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 ) ScColumn* pCol = &aCol[nCol]; for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ ) { -//2do: FillSortInfo an ScColumn und Array abklappern statt Search in GetCell ScSortInfo* pInfo = pArray->Get( nSort, nRow ); - pInfo->pCell = pCol->GetCell( nRow ); + pInfo->maCell = pCol->GetCellValue(nRow); pInfo->nOrg = nRow; } } @@ -291,7 +291,7 @@ ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 ) nCol <= static_cast<SCCOL>(nInd2); nCol++ ) { ScSortInfo* pInfo = pArray->Get( nSort, nCol ); - pInfo->pCell = GetCell( nCol, nRow ); + pInfo->maCell = GetCellValue(nCol, nRow); pInfo->nOrg = nCol; } } @@ -370,27 +370,24 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress ) } } -short ScTable::CompareCell( sal_uInt16 nSort, - ScBaseCell* pCell1, SCCOL nCell1Col, SCROW nCell1Row, - ScBaseCell* pCell2, SCCOL nCell2Col, SCROW nCell2Row ) const +short ScTable::CompareCell( + sal_uInt16 nSort, + ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row, + ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const { short nRes = 0; - CellType eType1 = CELLTYPE_NONE, eType2 = CELLTYPE_NONE; - if (pCell1) - eType1 = pCell1->GetCellType(); - if (pCell2) - eType2 = pCell2->GetCellType(); + CellType eType1 = rCell1.meType, eType2 = rCell2.meType; - if (pCell1) + if (!rCell1.isEmpty()) { - if (pCell2) + if (!rCell2.isEmpty()) { bool bStr1 = ( eType1 != CELLTYPE_VALUE ); - if ( eType1 == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell1)->IsValue() ) + if (eType1 == CELLTYPE_FORMULA && rCell1.mpFormula->IsValue()) bStr1 = false; bool bStr2 = ( eType2 != CELLTYPE_VALUE ); - if ( eType2 == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell2)->IsValue() ) + if (eType2 == CELLTYPE_FORMULA && rCell2.mpFormula->IsValue()) bStr2 = false; if ( bStr1 && bStr2 ) // nur Strings untereinander als String vergleichen! @@ -398,11 +395,11 @@ short ScTable::CompareCell( sal_uInt16 nSort, OUString aStr1; OUString aStr2; if (eType1 == CELLTYPE_STRING) - aStr1 = ((ScStringCell*)pCell1)->GetString(); + aStr1 = *rCell1.mpString; else GetString(nCell1Col, nCell1Row, aStr1); if (eType2 == CELLTYPE_STRING) - aStr2 = ((ScStringCell*)pCell2)->GetString(); + aStr2 = *rCell2.mpString; else GetString(nCell2Col, nCell2Row, aStr2); @@ -445,20 +442,8 @@ short ScTable::CompareCell( sal_uInt16 nSort, nRes = -1; // Zahl vorne else // Zahlen untereinander { - double nVal1; - double nVal2; - if (eType1 == CELLTYPE_VALUE) - nVal1 = ((ScValueCell*)pCell1)->GetValue(); - else if (eType1 == CELLTYPE_FORMULA) - nVal1 = ((ScFormulaCell*)pCell1)->GetValue(); - else - nVal1 = 0; - if (eType2 == CELLTYPE_VALUE) - nVal2 = ((ScValueCell*)pCell2)->GetValue(); - else if (eType2 == CELLTYPE_FORMULA) - nVal2 = ((ScFormulaCell*)pCell2)->GetValue(); - else - nVal2 = 0; + double nVal1 = rCell1.getValue(); + double nVal2 = rCell2.getValue(); if (nVal1 < nVal2) nRes = -1; else if (nVal1 > nVal2) @@ -472,7 +457,7 @@ short ScTable::CompareCell( sal_uInt16 nSort, } else { - if ( pCell2 ) + if (!rCell2.isEmpty()) nRes = 1; else nRes = 0; // beide leer @@ -490,12 +475,12 @@ short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nInd ScSortInfo* pInfo2 = pArray->Get( nSort, nIndex2 ); if ( aSortParam.bByRow ) nRes = CompareCell( nSort, - pInfo1->pCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), pInfo1->nOrg, - pInfo2->pCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), pInfo2->nOrg ); + pInfo1->maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), pInfo1->nOrg, + pInfo2->maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), pInfo2->nOrg ); else nRes = CompareCell( nSort, - pInfo1->pCell, static_cast<SCCOL>(pInfo1->nOrg), aSortParam.maKeyState[nSort].nField, - pInfo2->pCell, static_cast<SCCOL>(pInfo2->nOrg), aSortParam.maKeyState[nSort].nField ); + pInfo1->maCell, static_cast<SCCOL>(pInfo1->nOrg), aSortParam.maKeyState[nSort].nField, + pInfo2->maCell, static_cast<SCCOL>(pInfo2->nOrg), aSortParam.maKeyState[nSort].nField ); } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() ); if( nRes == 0 ) { @@ -692,9 +677,9 @@ short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const do { SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField); - ScBaseCell* pCell1 = aCol[nCol].GetCell( nIndex1 ); - ScBaseCell* pCell2 = aCol[nCol].GetCell( nIndex2 ); - nRes = CompareCell( nSort, pCell1, nCol, nIndex1, pCell2, nCol, nIndex2 ); + ScRefCellValue aCell1 = aCol[nCol].GetCellValue(nIndex1); + ScRefCellValue aCell2 = aCol[nCol].GetCellValue(nIndex2); + nRes = CompareCell(nSort, aCell1, nCol, nIndex1, aCell2, nCol, nIndex2); } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort ); } else @@ -702,10 +687,10 @@ short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const do { SCROW nRow = aSortParam.maKeyState[nSort].nField; - ScBaseCell* pCell1 = aCol[nIndex1].GetCell( nRow ); - ScBaseCell* pCell2 = aCol[nIndex2].GetCell( nRow ); - nRes = CompareCell( nSort, pCell1, static_cast<SCCOL>(nIndex1), - nRow, pCell2, static_cast<SCCOL>(nIndex2), nRow ); + ScRefCellValue aCell1 = aCol[nIndex1].GetCellValue(nRow); + ScRefCellValue aCell2 = aCol[nIndex2].GetCellValue(nRow); + nRes = CompareCell( nSort, aCell1, static_cast<SCCOL>(nIndex1), + nRow, aCell2, static_cast<SCCOL>(nIndex2), nRow ); } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort ); } return nRes; @@ -783,9 +768,39 @@ void ScTable::Sort(const ScSortParam& rSortParam, bool bKeepQuery, ScProgress* p DestroySortCollator(); } +namespace { + +class SubTotalRowFinder +{ + const ScTable& mrTab; + const ScSubTotalParam& mrParam; + +public: + SubTotalRowFinder(const ScTable& rTab, const ScSubTotalParam& rParam) : + mrTab(rTab), mrParam(rParam) {} + + bool operator() (size_t nRow, const ScFormulaCell* pCell) + { + if (!pCell->IsSubTotal()) + return false; -// Testen, ob beim Loeschen von Zwischenergebnissen andere Daten mit geloescht werden -// (fuer Hinweis-Box) + SCCOL nStartCol = mrParam.nCol1; + SCCOL nEndCol = mrParam.nCol2; + + for (SCCOL i = 0; i <= MAXCOL; ++i) + { + if (nStartCol <= i && i <= nEndCol) + continue; + + if (mrTab.HasData(i, nRow)) + return true; + } + + return false; + } +}; + +} bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam ) { @@ -794,31 +809,43 @@ bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam ) SCCOL nEndCol = rParam.nCol2; SCROW nEndRow = rParam.nRow2; - SCCOL nCol; - SCROW nRow; - ScBaseCell* pCell; - - bool bWillDelete = false; - for ( nCol=nStartCol; nCol<=nEndCol && !bWillDelete; nCol++ ) + for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol) { - ScColumnIterator aIter( &aCol[nCol],nStartRow,nEndRow ); - while ( aIter.Next( nRow, pCell ) && !bWillDelete ) - { - if ( pCell->GetCellType() == CELLTYPE_FORMULA ) - if (((ScFormulaCell*)pCell)->IsSubTotal()) - { - for (SCCOL nTestCol=0; nTestCol<=MAXCOL; nTestCol++) - if (nTestCol<nStartCol || nTestCol>nEndCol) - if (aCol[nTestCol].HasDataAt(nRow)) - bWillDelete = true; - } - } + const sc::CellStoreType& rCells = aCol[nCol].maCells; + SubTotalRowFinder aFunc(*this, rParam); + std::pair<sc::CellStoreType::const_iterator,size_t> aPos = + sc::FindFormula(rCells, nStartRow, nEndRow, aFunc); + if (aPos.first != rCells.end()) + return true; } - return bWillDelete; + return false; } -// alte Ergebnisse loeschen -// rParam.nRow2 wird veraendert ! +namespace { + +class RemoveSubTotalsHandler +{ + std::vector<SCROW> maRemoved; +public: + + void operator() (size_t nRow, const ScFormulaCell* p) + { + if (p->IsSubTotal()) + maRemoved.push_back(nRow); + } + + void getRows(std::vector<SCROW>& rRows)< |