diff options
Diffstat (limited to 'sc/inc/formulacell.hxx')
-rw-r--r-- | sc/inc/formulacell.hxx | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx new file mode 100644 index 0000000000..70f113d507 --- /dev/null +++ b/sc/inc/formulacell.hxx @@ -0,0 +1,520 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <map> +#include <memory> +#include <optional> + +#include <formula/tokenarray.hxx> +#include <formula/errorcodes.hxx> +#include <svl/listener.hxx> + +#include "types.hxx" +#include "interpretercontext.hxx" +#include "document.hxx" +#include "docoptio.hxx" +#include "formulalogger.hxx" +#include "formularesult.hxx" +#include "tokenarray.hxx" +#include "grouparealistener.hxx" + +namespace sc { + +class StartListeningContext; +class EndListeningContext; +struct RefUpdateContext; +struct RefUpdateInsertTabContext; +struct RefUpdateDeleteTabContext; +struct RefUpdateMoveTabContext; +class CompileFormulaContext; +class UpdatedRangeNames; + +} + +class ScFormulaCell; +class ScProgress; +enum class SvNumFormatType : sal_Int16; + +struct AreaListenerKey +{ + ScRange maRange; + bool mbStartFixed; + bool mbEndFixed; + + AreaListenerKey( const ScRange& rRange, bool bStartFixed, bool bEndFixed ) : + maRange(rRange), mbStartFixed(bStartFixed), mbEndFixed(bEndFixed) {} + + bool operator < ( const AreaListenerKey& r ) const; +}; + +typedef std::map<AreaListenerKey, sc::FormulaGroupAreaListener> AreaListenersType; + +struct SC_DLLPUBLIC ScFormulaCellGroup +{ + AreaListenersType m_AreaListeners; +public: + + mutable size_t mnRefCount; + + std::optional<ScTokenArray> mpCode; + ScFormulaCell *mpTopCell; + SCROW mnLength; // How many of these do we have ? + sal_Int32 mnWeight; + SvNumFormatType mnFormatType; + bool mbInvariant:1; + bool mbSubTotal:1; + bool mbPartOfCycle:1; // To flag FG's part of a cycle + + sal_uInt8 meCalcState; + + ScFormulaCellGroup(); + ScFormulaCellGroup(const ScFormulaCellGroup&) = delete; + const ScFormulaCellGroup& operator=(const ScFormulaCellGroup&) = delete; + ~ScFormulaCellGroup(); + + void setCode( const ScTokenArray& rCode ); + void compileCode( + ScDocument& rDoc, const ScAddress& rPos, formula::FormulaGrammar::Grammar eGram ); + + sc::FormulaGroupAreaListener* getAreaListener( + ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed ); + + void endAllGroupListening( ScDocument& rDoc ); +}; + +inline void intrusive_ptr_add_ref(const ScFormulaCellGroup *p) +{ + p->mnRefCount++; +} + +inline void intrusive_ptr_release(const ScFormulaCellGroup *p) +{ + if( --p->mnRefCount == 0 ) + delete p; +} + +enum class ScMatrixMode : sal_uInt8 { + NONE = 0, // No matrix formula + Formula = 1, // Upper left matrix formula cell + Reference = 2 // Remaining cells, via ocMatRef reference token +}; + +class SC_DLLPUBLIC ScFormulaCell final : public SvtListener +{ +private: + ScFormulaCellGroupRef mxGroup; // Group of formulae we're part of + bool bDirty : 1; // Must be (re)calculated + bool bTableOpDirty : 1; // Dirty flag for TableOp + bool bChanged : 1; // Whether something changed regarding display/representation + bool bRunning : 1; // Already interpreting right now + bool bCompile : 1; // Must be (re)compiled + bool bSubTotal : 1; // Cell is part of or contains a SubTotal + bool bIsIterCell : 1; // Cell is part of a circular reference + bool bInChangeTrack : 1; // Cell is in ChangeTrack + bool bNeedListening : 1; // Listeners need to be re-established after UpdateReference + bool mbNeedsNumberFormat : 1; // set the calculated number format as hard number format + bool mbAllowNumberFormatChange : 1; /* allow setting further calculated + number formats as hard number format */ + bool mbPostponedDirty : 1; // if cell needs to be set dirty later + bool mbIsExtRef : 1; // has references in ScExternalRefManager; never cleared after set + bool mbSeenInPath : 1; // For detecting cycle involving formula groups and singleton formulacells + bool mbFreeFlying : 1; // Cell is out of sheets interpreted, like in conditional format + ScMatrixMode cMatrixFlag : 8; + sal_uInt16 nSeenInIteration : 16; // Iteration cycle in which the cell was last encountered + SvNumFormatType nFormatType : 16; + ScFormulaResult aResult; + formula::FormulaGrammar::Grammar eTempGrammar; // used between string (creation) and (re)compilation + // If this cell is in a cell group (mxGroup!=nullptr), then this pCode is a not-owning pointer + // to the mxGroup's mpCode, which owns the array. If the cell is not in a group, this is an owning pointer. + ScTokenArray* pCode; // The token array + ScDocument& rDocument; + ScFormulaCell* pPrevious; + ScFormulaCell* pNext; + ScFormulaCell* pPreviousTrack; + ScFormulaCell* pNextTrack; + + /** + * Update reference in response to cell copy-n-paste. + */ + bool UpdateReferenceOnCopy( + const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos ); + + ScFormulaCell( const ScFormulaCell& ) = delete; + + bool CheckComputeDependencies(sc::FormulaLogger::GroupScope& rScope, bool fromFirstRow, + SCROW nStartOffset, SCROW nEndOffset, bool bCalcDependencyOnly = false, + ScRangeList* pSuccessfulDependencies = nullptr, + ScAddress* pFailedAndDirtiedAddress = nullptr); + bool InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope& aScope, + bool& bDependencyComputed, + bool& bDependencyCheckFailed, + SCROW nStartOffset, SCROW nEndOffset); + bool InterpretFormulaGroupOpenCL(sc::FormulaLogger::GroupScope& aScope, + bool& bDependencyComputed, + bool& bDependencyCheckFailed); + bool InterpretInvariantFormulaGroup(); + +public: + + + enum ScInterpretTailParameter + { + SCITP_NORMAL, + SCITP_FROM_ITERATION, + SCITP_CLOSE_ITERATION_CIRCLE + }; + void InterpretTail( ScInterpreterContext&, ScInterpretTailParameter ); + + void HandleStuffAfterParallelCalculation(ScInterpreter* pInterpreter); + + enum CompareState { NotEqual = 0, EqualInvariant, EqualRelativeRef }; + + ScAddress aPos; + + virtual ~ScFormulaCell() override; + + ScFormulaCell* Clone() const; + ScFormulaCell* Clone( const ScAddress& rPos ) const; + + ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos ); + + /** + * Transfer the ownership of the passed token array instance to the + * formula cell being constructed. The caller <i>must not</i> pass a NULL + * token array pointer. + */ + ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos, std::unique_ptr<ScTokenArray> pArray, + const formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_DEFAULT, + ScMatrixMode cMatInd = ScMatrixMode::NONE ); + + ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos, const ScTokenArray& rArray, + const formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_DEFAULT, + ScMatrixMode cMatInd = ScMatrixMode::NONE ); + + ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos, const ScFormulaCellGroupRef& xGroup, + const formula::FormulaGrammar::Grammar = formula::FormulaGrammar::GRAM_DEFAULT, + ScMatrixMode = ScMatrixMode::NONE ); + + /** With formula string and grammar to compile with. + formula::FormulaGrammar::GRAM_DEFAULT effectively isformula::FormulaGrammar::GRAM_NATIVE_UI that + also includes formula::FormulaGrammar::CONV_UNSPECIFIED, therefore uses the address + convention associated with rPos::nTab by default. */ + ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos, + const OUString& rFormula, + const formula::FormulaGrammar::Grammar = formula::FormulaGrammar::GRAM_DEFAULT, + ScMatrixMode cMatInd = ScMatrixMode::NONE ); + + ScFormulaCell(const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, ScCloneFlags nCloneFlags = ScCloneFlags::Default); + + void SetFreeFlying( bool b ) { mbFreeFlying = b; } + + size_t GetHash() const; + + OUString GetFormula( const formula::FormulaGrammar::Grammar = formula::FormulaGrammar::GRAM_DEFAULT, + const ScInterpreterContext* pContext = nullptr ) const; + OUString GetFormula( sc::CompileFormulaContext& rCxt, const ScInterpreterContext* pContext = nullptr ) const; + + void SetDirty( bool bDirtyFlag=true ); + void SetDirtyVar(); + // If setting entire document dirty after load, no broadcasts but still append to FormulaTree. + void SetDirtyAfterLoad(); + void ResetTableOpDirtyVar(); + void SetTableOpDirty(); + + bool IsDirtyOrInTableOpDirty() const + { + return bDirty || (bTableOpDirty && rDocument.IsInInterpreterTableOp()); + } + + bool GetDirty() const { return bDirty; } + void ResetDirty(); + bool NeedsListening() const { return bNeedListening; } + void SetNeedsListening( bool bVar ); + void SetNeedsDirty( bool bVar ); + void SetNeedNumberFormat( bool bVal ); + bool NeedsNumberFormat() const { return mbNeedsNumberFormat;} + SvNumFormatType GetFormatType() const { return nFormatType; } + void Compile(const OUString& rFormula, + bool bNoListening, + const formula::FormulaGrammar::Grammar ); + void Compile( + sc::CompileFormulaContext& rCxt, const OUString& rFormula, bool bNoListening = false ); + + void CompileTokenArray( bool bNoListening = false ); + void CompileTokenArray( sc::CompileFormulaContext& rCxt, bool bNoListening = false ); + void CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rProgress ); // compile temporary string tokens + void CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening ); + bool MarkUsedExternalReferences(); + // Returns true if the cell was interpreted as part of the formula group. + // The parameters may limit which subset of the formula group should be interpreted, if possible. + bool Interpret(SCROW nStartOffset = -1, SCROW nEndOffset = -1); + bool IsIterCell() const { return bIsIterCell; } + sal_uInt16 GetSeenInIteration() const { return nSeenInIteration; } + + bool HasOneReference( ScRange& r ) const; + /* Checks if the formula contains reference list that can be + expressed by one reference (like A1;A2;A3:A5 -> A1:A5). The + reference list is not required to be sorted (i.e. A3;A1;A2 is + still recognized as A1:A3), but no overlapping is allowed. + If one reference is recognized, the rRange is filled. + + It is similar to HasOneReference(), but more general. + */ + bool HasRefListExpressibleAsOneReference(ScRange& rRange) const; + + enum class RelNameRef + { + NONE, ///< no relative reference from named expression + SINGLE, ///< only single cell relative reference + DOUBLE ///< at least one range relative reference from named expression + }; + RelNameRef HasRelNameReference() const; + + bool UpdateReference( + const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc = nullptr, const ScAddress* pUndoCellPos = nullptr ); + + /** + * Shift the position of formula cell as part of reference update. + * + * @return true if the position has shifted, false otherwise. + */ + bool UpdatePosOnShift( const sc::RefUpdateContext& rCxt ); + + /** + * Update reference in response to cell insertion or deletion. + */ + bool UpdateReferenceOnShift( + const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos ); + + /** + * Update reference in response to cell move. + */ + bool UpdateReferenceOnMove( + const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos ); + + void TransposeReference(); + void UpdateTranspose( const ScRange& rSource, const ScAddress& rDest, + ScDocument* pUndoDoc ); + + void UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ); + + void UpdateInsertTab( const sc::RefUpdateInsertTabContext& rCxt ); + void UpdateInsertTabAbs(SCTAB nTable); + void UpdateDeleteTab( const sc::RefUpdateDeleteTabContext& rCxt ); + void UpdateMoveTab( const sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo ); + bool TestTabRefAbs(SCTAB nTable); + void UpdateCompile( bool bForceIfNameInUse ); + void FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes) const; + bool IsSubTotal() const { return bSubTotal;} + bool IsChanged() const { return bChanged;} + void SetChanged(bool b); + bool IsEmpty(); // formula::svEmptyCell result + // display as empty string if formula::svEmptyCell result + bool IsEmptyDisplayedAsString(); + bool IsValue(); // also true if formula::svEmptyCell + bool IsValueNoError(); + bool IsValueNoError() const; + double GetValue(); + const svl::SharedString & GetString(); + + /** + * Get a numeric value without potentially triggering re-calculation. + */ + double GetRawValue() const; + + /** + * Get a string value without potentially triggering re-calculation. + */ + const svl::SharedString & GetRawString() const; + const ScMatrix* GetMatrix(); + bool GetMatrixOrigin( const ScDocument& rDoc, ScAddress& rPos ) const; + void GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows ); + sc::MatrixEdge GetMatrixEdge( const ScDocument& rDoc, ScAddress& rOrgPos ) const; + FormulaError GetErrCode(); // interpret first if necessary + FormulaError GetRawError() const; // don't interpret, just return code or result error + bool GetErrorOrValue( FormulaError& rErr, double& rVal ); + sc::FormulaResultValue GetResult(); + sc::FormulaResultValue GetResult() const; + ScMatrixMode GetMatrixFlag() const { return cMatrixFlag;} + ScTokenArray* GetCode() { return pCode;} + const ScTokenArray* GetCode() const { return pCode;} + + void SetCode( std::unique_ptr<ScTokenArray> pNew ); + + bool IsRunning() const { return bRunning;} + void SetRunning( bool bVal ); + void CompileDBFormula( sc::CompileFormulaContext& rCxt ); + void CompileColRowNameFormula( sc::CompileFormulaContext& rCxt ); + ScFormulaCell* GetPrevious() const { return pPrevious; } + ScFormulaCell* GetNext() const { return pNext; } + void SetPrevious( ScFormulaCell* pF ); + void SetNext( ScFormulaCell* pF ); + ScFormulaCell* GetPreviousTrack() const { return pPreviousTrack; } + ScFormulaCell* GetNextTrack() const { return pNextTrack; } + void SetPreviousTrack( ScFormulaCell* pF ); + void SetNextTrack( ScFormulaCell* pF ); + + virtual void Notify( const SfxHint& rHint ) override; + virtual void Query( SvtListener::QueryBase& rQuery ) const override; + + void SetCompile( bool bVal ); + ScDocument& GetDocument() const { return rDocument;} + void SetMatColsRows( SCCOL nCols, SCROW nRows ); + void GetMatColsRows( SCCOL& nCols, SCROW& nRows ) const; + + // cell belongs to ChangeTrack and not to the real document + void SetInChangeTrack( bool bVal ); + bool IsInChangeTrack() const { return bInChangeTrack;} + + // For import filters! + void AddRecalcMode( ScRecalcMode ); + /** For import only: set a double result. */ + void SetHybridDouble( double n ); + /** For import only: set a string result. + If for whatever reason you have to use both, SetHybridDouble() and + SetHybridString() or SetHybridFormula(), use SetHybridDouble() first + for performance reasons.*/ + void SetHybridString( const svl::SharedString& r ); + /** For import only: set an empty cell result to be displayed as empty string. + If for whatever reason you have to use both, SetHybridDouble() and + SetHybridEmptyDisplayedAsString() or SetHybridFormula(), use + SetHybridDouble() first for performance reasons and use + SetHybridEmptyDisplayedAsString() last because SetHybridDouble() and + SetHybridString() will override it.*/ + void SetHybridEmptyDisplayedAsString(); + /** For import only: set a temporary formula string to be compiled later. + If for whatever reason you have to use both, SetHybridDouble() and + SetHybridString() or SetHybridFormula(), use SetHybridDouble() first + for performance reasons.*/ + void SetHybridFormula( + const OUString& r, const formula::FormulaGrammar::Grammar eGrammar ); + + OUString GetHybridFormula() const; + + void SetResultMatrix( SCCOL nCols, SCROW nRows, const ScConstMatrixRef& pMat, const formula::FormulaToken* pUL ); + + /** For import only: set a double result. + Use this instead of SetHybridDouble() if there is no (temporary) + formula string because the formula is present as a token array, as it + is the case for binary Excel import. + */ + void SetResultDouble( double n ); + + void SetResultToken( const formula::FormulaToken* pToken ); + + const svl::SharedString & GetResultString() const; + + bool HasHybridStringResult() const; + + /* Sets the shared code array to error state in addition to the cell result */ + void SetErrCode( FormulaError n ); + + /* Sets just the result to error */ + void SetResultError( FormulaError n ); + + bool IsHyperLinkCell() const; + std::unique_ptr<EditTextObject> CreateURLObject(); + void GetURLResult( OUString& rURL, OUString& rCellText ); + + /** Determines whether or not the result string contains more than one paragraph */ + bool IsMultilineResult(); + + bool NeedsInterpret() const + { + if (bIsIterCell) + // Shortcut to force return of current value and not enter Interpret() + // as we're looping over all iteration cells. + return false; + + if (!IsDirtyOrInTableOpDirty()) + return false; + + return rDocument.GetAutoCalc() || (cMatrixFlag != ScMatrixMode::NONE) + || (pCode->IsRecalcModeMustAfterImport() && !pCode->IsRecalcModeAlways()); + } + + void MaybeInterpret() + { + if (NeedsInterpret()) + { + if (bRunning && !rDocument.GetDocOptions().IsIter() && rDocument.IsThreadedGroupCalcInProgress()) + { + // This is actually copied from Interpret()'s if(bRunning) + // block that once caught this circular reference but now is + // prepended with various threaded group calc things which the + // assert() below is supposed to fail on when entering again. + // Nevertheless, we need some state here the caller can obtain. + aResult.SetResultError( FormulaError::CircularReference ); + } + else + { + assert(!rDocument.IsThreadedGroupCalcInProgress()); + Interpret(); + } + } + } + + /** + * Turn a non-grouped cell into the top of a grouped cell. + */ + ScFormulaCellGroupRef CreateCellGroup( SCROW nLen, bool bInvariant ); + const ScFormulaCellGroupRef& GetCellGroup() const { return mxGroup;} + void SetCellGroup( const ScFormulaCellGroupRef &xRef ); + + CompareState CompareByTokenArray( const ScFormulaCell& rOther ) const; + + bool InterpretFormulaGroup(SCROW nStartOffset = -1, SCROW nEndOffset = -1); + + // nOnlyNames may be one or more of SC_LISTENING_NAMES_* + void StartListeningTo( ScDocument& rDoc ); + void StartListeningTo( sc::StartListeningContext& rCxt ); + void EndListeningTo( + ScDocument& rDoc, ScTokenArray* pArr = nullptr, ScAddress aPos = ScAddress() ); + void EndListeningTo( sc::EndListeningContext& rCxt ); + + bool IsShared() const; + bool IsSharedTop() const; + SCROW GetSharedTopRow() const; + SCROW GetSharedLength() const; + + // An estimate of the number of cells referenced by the formula + sal_Int32 GetWeight() const; + + ScTokenArray* GetSharedCode(); + const ScTokenArray* GetSharedCode() const; + + void SyncSharedCode(); + + bool IsPostponedDirty() const { return mbPostponedDirty;} + + void SetIsExtRef() { mbIsExtRef = true; } + bool GetSeenInPath() const { return mbSeenInPath; } + void SetSeenInPath(bool bSet) { mbSeenInPath = bSet; } + +#if DUMP_COLUMN_STORAGE + void Dump() const; +#endif +}; + +inline bool ScDocument::IsInFormulaTree( const ScFormulaCell* pCell ) const { return pCell->GetPrevious() || pFormulaTree == pCell; } +inline bool ScDocument::IsInFormulaTrack( const ScFormulaCell* pCell ) const { return pCell->GetPreviousTrack() || pFormulaTrack == pCell; } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |