| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sc.hxx" |
| |
| |
| |
| //------------------------------------------------------------------ |
| |
| #include "scitems.hxx" |
| #include <sfx2/objsh.hxx> |
| #include <svl/itemset.hxx> |
| #include <svl/zforlist.hxx> |
| #include <rtl/math.hxx> |
| #include <unotools/collatorwrapper.hxx> |
| |
| #include "conditio.hxx" |
| #include "cell.hxx" |
| #include "document.hxx" |
| #include "hints.hxx" |
| #include "compiler.hxx" |
| #include "rechead.hxx" |
| #include "rangelst.hxx" |
| #include "stlpool.hxx" |
| #include "rangenam.hxx" |
| |
| using namespace formula; |
| //------------------------------------------------------------------------ |
| |
| SV_IMPL_OP_PTRARR_SORT( ScConditionalFormats_Impl, ScConditionalFormatPtr ); |
| |
| //------------------------------------------------------------------------ |
| |
| sal_Bool lcl_HasRelRef( ScDocument* pDoc, ScTokenArray* pFormula, sal_uInt16 nRecursion = 0 ) |
| { |
| if (pFormula) |
| { |
| pFormula->Reset(); |
| FormulaToken* t; |
| for( t = pFormula->Next(); t; t = pFormula->Next() ) |
| { |
| switch( t->GetType() ) |
| { |
| case svDoubleRef: |
| { |
| ScSingleRefData& rRef2 = static_cast<ScToken*>(t)->GetDoubleRef().Ref2; |
| if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() ) |
| return sal_True; |
| } |
| // fall through |
| |
| case svSingleRef: |
| { |
| ScSingleRefData& rRef1 = static_cast<ScToken*>(t)->GetSingleRef(); |
| if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() ) |
| return sal_True; |
| } |
| break; |
| |
| case svIndex: |
| { |
| if( t->GetOpCode() == ocName ) // DB areas always absolute |
| if( ScRangeData* pRangeData = pDoc->GetRangeName()->FindIndex( t->GetIndex() ) ) |
| if( (nRecursion < 42) && lcl_HasRelRef( pDoc, pRangeData->GetCode(), nRecursion + 1 ) ) |
| return sal_True; |
| } |
| break; |
| |
| // #i34474# function result dependent on cell position |
| case svByte: |
| { |
| switch( t->GetOpCode() ) |
| { |
| case ocRow: // ROW() returns own row index |
| case ocColumn: // COLUMN() returns own column index |
| case ocTable: // SHEET() returns own sheet index |
| case ocCell: // CELL() may return own cell address |
| return sal_True; |
| // break; |
| default: |
| { |
| // added to avoid warnings |
| } |
| } |
| } |
| break; |
| |
| default: |
| { |
| // added to avoid warnings |
| } |
| } |
| } |
| } |
| return sal_False; |
| } |
| |
| ScConditionEntry::ScConditionEntry( const ScConditionEntry& r ) : |
| eOp(r.eOp), |
| nOptions(r.nOptions), |
| nVal1(r.nVal1), |
| nVal2(r.nVal2), |
| aStrVal1(r.aStrVal1), |
| aStrVal2(r.aStrVal2), |
| aStrNmsp1(r.aStrNmsp1), |
| aStrNmsp2(r.aStrNmsp2), |
| eTempGrammar1(r.eTempGrammar1), |
| eTempGrammar2(r.eTempGrammar2), |
| bIsStr1(r.bIsStr1), |
| bIsStr2(r.bIsStr2), |
| pFormula1(NULL), |
| pFormula2(NULL), |
| aSrcPos(r.aSrcPos), |
| aSrcString(r.aSrcString), |
| pFCell1(NULL), |
| pFCell2(NULL), |
| pDoc(r.pDoc), |
| bRelRef1(r.bRelRef1), |
| bRelRef2(r.bRelRef2), |
| bFirstRun(sal_True) |
| { |
| // ScTokenArray copy ctor erzeugt flache Kopie |
| |
| if (r.pFormula1) |
| pFormula1 = new ScTokenArray( *r.pFormula1 ); |
| if (r.pFormula2) |
| pFormula2 = new ScTokenArray( *r.pFormula2 ); |
| |
| // Formelzellen werden erst bei IsValid angelegt |
| } |
| |
| ScConditionEntry::ScConditionEntry( ScDocument* pDocument, const ScConditionEntry& r ) : |
| eOp(r.eOp), |
| nOptions(r.nOptions), |
| nVal1(r.nVal1), |
| nVal2(r.nVal2), |
| aStrVal1(r.aStrVal1), |
| aStrVal2(r.aStrVal2), |
| aStrNmsp1(r.aStrNmsp1), |
| aStrNmsp2(r.aStrNmsp2), |
| eTempGrammar1(r.eTempGrammar1), |
| eTempGrammar2(r.eTempGrammar2), |
| bIsStr1(r.bIsStr1), |
| bIsStr2(r.bIsStr2), |
| pFormula1(NULL), |
| pFormula2(NULL), |
| aSrcPos(r.aSrcPos), |
| aSrcString(r.aSrcString), |
| pFCell1(NULL), |
| pFCell2(NULL), |
| pDoc(pDocument), |
| bRelRef1(r.bRelRef1), |
| bRelRef2(r.bRelRef2), |
| bFirstRun(sal_True) |
| { |
| // echte Kopie der Formeln (fuer Ref-Undo) |
| |
| if (r.pFormula1) |
| pFormula1 = r.pFormula1->Clone(); |
| if (r.pFormula2) |
| pFormula2 = r.pFormula2->Clone(); |
| |
| // Formelzellen werden erst bei IsValid angelegt |
| //! im Clipboard nicht - dann vorher interpretieren !!! |
| } |
| |
| ScConditionEntry::ScConditionEntry( ScConditionMode eOper, |
| const String& rExpr1, const String& rExpr2, ScDocument* pDocument, const ScAddress& rPos, |
| const String& rExprNmsp1, const String& rExprNmsp2, |
| FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2 ) : |
| eOp(eOper), |
| nOptions(0), // spaeter... |
| nVal1(0.0), |
| nVal2(0.0), |
| aStrNmsp1(rExprNmsp1), |
| aStrNmsp2(rExprNmsp2), |
| eTempGrammar1(eGrammar1), |
| eTempGrammar2(eGrammar2), |
| bIsStr1(sal_False), |
| bIsStr2(sal_False), |
| pFormula1(NULL), |
| pFormula2(NULL), |
| aSrcPos(rPos), |
| pFCell1(NULL), |
| pFCell2(NULL), |
| pDoc(pDocument), |
| bRelRef1(sal_False), |
| bRelRef2(sal_False), |
| bFirstRun(sal_True) |
| { |
| Compile( rExpr1, rExpr2, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2, sal_False ); |
| |
| // Formelzellen werden erst bei IsValid angelegt |
| } |
| |
| ScConditionEntry::ScConditionEntry( ScConditionMode eOper, |
| const ScTokenArray* pArr1, const ScTokenArray* pArr2, |
| ScDocument* pDocument, const ScAddress& rPos ) : |
| eOp(eOper), |
| nOptions(0), // spaeter... |
| nVal1(0.0), |
| nVal2(0.0), |
| eTempGrammar1(FormulaGrammar::GRAM_DEFAULT), |
| eTempGrammar2(FormulaGrammar::GRAM_DEFAULT), |
| bIsStr1(sal_False), |
| bIsStr2(sal_False), |
| pFormula1(NULL), |
| pFormula2(NULL), |
| aSrcPos(rPos), |
| pFCell1(NULL), |
| pFCell2(NULL), |
| pDoc(pDocument), |
| bRelRef1(sal_False), |
| bRelRef2(sal_False), |
| bFirstRun(sal_True) |
| { |
| if ( pArr1 ) |
| { |
| pFormula1 = new ScTokenArray( *pArr1 ); |
| if ( pFormula1->GetLen() == 1 ) |
| { |
| // einzelne (konstante Zahl) ? |
| FormulaToken* pToken = pFormula1->First(); |
| if ( pToken->GetOpCode() == ocPush ) |
| { |
| if ( pToken->GetType() == svDouble ) |
| { |
| nVal1 = pToken->GetDouble(); |
| DELETEZ(pFormula1); // nicht als Formel merken |
| } |
| else if ( pToken->GetType() == svString ) |
| { |
| bIsStr1 = sal_True; |
| aStrVal1 = pToken->GetString(); |
| DELETEZ(pFormula1); // nicht als Formel merken |
| } |
| } |
| } |
| bRelRef1 = lcl_HasRelRef( pDoc, pFormula1 ); |
| } |
| if ( pArr2 ) |
| { |
| pFormula2 = new ScTokenArray( *pArr2 ); |
| if ( pFormula2->GetLen() == 1 ) |
| { |
| // einzelne (konstante Zahl) ? |
| FormulaToken* pToken = pFormula2->First(); |
| if ( pToken->GetOpCode() == ocPush ) |
| { |
| if ( pToken->GetType() == svDouble ) |
| { |
| nVal2 = pToken->GetDouble(); |
| DELETEZ(pFormula2); // nicht als Formel merken |
| } |
| else if ( pToken->GetType() == svString ) |
| { |
| bIsStr2 = sal_True; |
| aStrVal2 = pToken->GetString(); |
| DELETEZ(pFormula2); // nicht als Formel merken |
| } |
| } |
| } |
| bRelRef2 = lcl_HasRelRef( pDoc, pFormula2 ); |
| } |
| |
| // formula cells are created at IsValid |
| } |
| |
| ScConditionEntry::~ScConditionEntry() |
| { |
| delete pFCell1; |
| delete pFCell2; |
| |
| delete pFormula1; |
| delete pFormula2; |
| } |
| |
| void ScConditionEntry::Compile( const String& rExpr1, const String& rExpr2, |
| const String& rExprNmsp1, const String& rExprNmsp2, |
| FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2, sal_Bool bTextToReal ) |
| { |
| if ( rExpr1.Len() || rExpr2.Len() ) |
| { |
| ScCompiler aComp( pDoc, aSrcPos ); |
| |
| if ( rExpr1.Len() ) |
| { |
| aComp.SetGrammar( eGrammar1 ); |
| if ( pDoc->IsImportingXML() && !bTextToReal ) |
| { |
| // temporary formula string as string tokens |
| //! merge with lcl_ScDocFunc_CreateTokenArrayXML |
| pFormula1 = new ScTokenArray; |
| pFormula1->AddString( rExpr1 ); |
| // bRelRef1 is set when the formula is compiled again (CompileXML) |
| } |
| else |
| { |
| pFormula1 = aComp.CompileString( rExpr1, rExprNmsp1 ); |
| if ( pFormula1->GetLen() == 1 ) |
| { |
| // einzelne (konstante Zahl) ? |
| FormulaToken* pToken = pFormula1->First(); |
| if ( pToken->GetOpCode() == ocPush ) |
| { |
| if ( pToken->GetType() == svDouble ) |
| { |
| nVal1 = pToken->GetDouble(); |
| DELETEZ(pFormula1); // nicht als Formel merken |
| } |
| else if ( pToken->GetType() == svString ) |
| { |
| bIsStr1 = sal_True; |
| aStrVal1 = pToken->GetString(); |
| DELETEZ(pFormula1); // nicht als Formel merken |
| } |
| } |
| } |
| bRelRef1 = lcl_HasRelRef( pDoc, pFormula1 ); |
| } |
| } |
| |
| if ( rExpr2.Len() ) |
| { |
| aComp.SetGrammar( eGrammar2 ); |
| if ( pDoc->IsImportingXML() && !bTextToReal ) |
| { |
| // temporary formula string as string tokens |
| //! merge with lcl_ScDocFunc_CreateTokenArrayXML |
| pFormula2 = new ScTokenArray; |
| pFormula2->AddString( rExpr2 ); |
| // bRelRef2 is set when the formula is compiled again (CompileXML) |
| } |
| else |
| { |
| pFormula2 = aComp.CompileString( rExpr2, rExprNmsp2 ); |
| if ( pFormula2->GetLen() == 1 ) |
| { |
| // einzelne (konstante Zahl) ? |
| FormulaToken* pToken = pFormula2->First(); |
| if ( pToken->GetOpCode() == ocPush ) |
| { |
| if ( pToken->GetType() == svDouble ) |
| { |
| nVal2 = pToken->GetDouble(); |
| DELETEZ(pFormula2); // nicht als Formel merken |
| } |
| else if ( pToken->GetType() == svString ) |
| { |
| bIsStr2 = sal_True; |
| aStrVal2 = pToken->GetString(); |
| DELETEZ(pFormula2); // nicht als Formel merken |
| } |
| } |
| } |
| bRelRef2 = lcl_HasRelRef( pDoc, pFormula2 ); |
| } |
| } |
| } |
| } |
| |
| void ScConditionEntry::MakeCells( const ScAddress& rPos ) // Formelzellen anlegen |
| { |
| if ( !pDoc->IsClipOrUndo() ) // nie im Clipboard rechnen! |
| { |
| if ( pFormula1 && !pFCell1 && !bRelRef1 ) |
| { |
| pFCell1 = new ScFormulaCell( pDoc, rPos, pFormula1 ); |
| pFCell1->StartListeningTo( pDoc ); |
| } |
| |
| if ( pFormula2 && !pFCell2 && !bRelRef2 ) |
| { |
| pFCell2 = new ScFormulaCell( pDoc, rPos, pFormula2 ); |
| pFCell2->StartListeningTo( pDoc ); |
| } |
| } |
| } |
| |
| void ScConditionEntry::SetIgnoreBlank(sal_Bool bSet) |
| { |
| // Das Bit SC_COND_NOBLANKS wird gesetzt, wenn Blanks nicht ignoriert werden |
| // (nur bei Gueltigkeit) |
| |
| if (bSet) |
| nOptions &= ~SC_COND_NOBLANKS; |
| else |
| nOptions |= SC_COND_NOBLANKS; |
| } |
| |
| void ScConditionEntry::CompileAll() |
| { |
| // Formelzellen loeschen, dann wird beim naechsten IsValid neu kompiliert |
| |
| DELETEZ(pFCell1); |
| DELETEZ(pFCell2); |
| } |
| |
| void ScConditionEntry::CompileXML() |
| { |
| // #b4974740# First parse the formula source position if it was stored as text |
| |
| if ( aSrcString.Len() ) |
| { |
| ScAddress aNew; |
| /* XML is always in OOo:A1 format, although R1C1 would be more amenable |
| * to compression */ |
| if ( aNew.Parse( aSrcString, pDoc ) & SCA_VALID ) |
| aSrcPos = aNew; |
| // if the position is invalid, there isn't much we can do at this time |
| aSrcString.Erase(); |
| } |
| |
| // Convert the text tokens that were created during XML import into real tokens. |
| |
| Compile( GetExpression(aSrcPos, 0, 0, eTempGrammar1), |
| GetExpression(aSrcPos, 1, 0, eTempGrammar2), |
| aStrNmsp1, aStrNmsp2, eTempGrammar1, eTempGrammar2, sal_True ); |
| } |
| |
| void ScConditionEntry::SetSrcString( const String& rNew ) |
| { |
| // aSrcString is only evaluated in CompileXML |
| DBG_ASSERT( pDoc->IsImportingXML(), "SetSrcString is only valid for XML import" ); |
| |
| aSrcString = rNew; |
| } |
| |
| void ScConditionEntry::SetFormula1( const ScTokenArray& rArray ) |
| { |
| DELETEZ( pFormula1 ); |
| if( rArray.GetLen() > 0 ) |
| { |
| pFormula1 = new ScTokenArray( rArray ); |
| bRelRef1 = lcl_HasRelRef( pDoc, pFormula1 ); |
| } |
| } |
| |
| void ScConditionEntry::SetFormula2( const ScTokenArray& rArray ) |
| { |
| DELETEZ( pFormula2 ); |
| if( rArray.GetLen() > 0 ) |
| { |
| pFormula2 = new ScTokenArray( rArray ); |
| bRelRef2 = lcl_HasRelRef( pDoc, pFormula2 ); |
| } |
| } |
| |
| void lcl_CondUpdateInsertTab( ScTokenArray& rCode, SCTAB nInsTab, SCTAB nPosTab, sal_Bool& rChanged ) |
| { |
| // Insert table: only update absolute table references. |
| // (Similar to ScCompiler::UpdateInsertTab with bIsName=sal_True, result is the same as for named ranges) |
| // For deleting, ScCompiler::UpdateDeleteTab is used because of the handling of invalid references. |
| |
| rCode.Reset(); |
| ScToken* p = static_cast<ScToken*>(rCode.GetNextReference()); |
| while( p ) |
| { |
| ScSingleRefData& rRef1 = p->GetSingleRef(); |
| if ( !rRef1.IsTabRel() && nInsTab <= rRef1.nTab ) |
| { |
| rRef1.nTab += 1; |
| rRef1.nRelTab = rRef1.nTab - nPosTab; |
| rChanged = sal_True; |
| } |
| if( p->GetType() == svDoubleRef ) |
| { |
| ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; |
| if ( !rRef2.IsTabRel() && nInsTab <= rRef2.nTab ) |
| { |
| rRef2.nTab += 1; |
| rRef2.nRelTab = rRef2.nTab - nPosTab; |
| rChanged = sal_True; |
| } |
| } |
| p = static_cast<ScToken*>(rCode.GetNextReference()); |
| } |
| } |
| |
| void ScConditionEntry::UpdateReference( UpdateRefMode eUpdateRefMode, |
| const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) |
| { |
| sal_Bool bInsertTab = ( eUpdateRefMode == URM_INSDEL && nDz == 1 ); |
| sal_Bool bDeleteTab = ( eUpdateRefMode == URM_INSDEL && nDz == -1 ); |
| |
| sal_Bool bChanged1 = sal_False; |
| sal_Bool bChanged2 = sal_False; |
| |
| if (pFormula1) |
| { |
| if ( bInsertTab ) |
| lcl_CondUpdateInsertTab( *pFormula1, rRange.aStart.Tab(), aSrcPos.Tab(), bChanged1 ); |
| else |
| { |
| ScCompiler aComp( pDoc, aSrcPos, *pFormula1 ); |
| aComp.SetGrammar(pDoc->GetGrammar()); |
| if ( bDeleteTab ) |
| aComp.UpdateDeleteTab( rRange.aStart.Tab(), sal_False, sal_True, bChanged1 ); |
| else |
| aComp.UpdateNameReference( eUpdateRefMode, rRange, nDx, nDy, nDz, bChanged1 ); |
| } |
| |
| if (bChanged1) |
| DELETEZ(pFCell1); // is created again in IsValid |
| } |
| if (pFormula2) |
| { |
| if ( bInsertTab ) |
| lcl_CondUpdateInsertTab( *pFormula2, rRange.aStart.Tab(), aSrcPos.Tab(), bChanged2 ); |
| else |
| { |
| ScCompiler aComp( pDoc, aSrcPos, *pFormula2); |
| aComp.SetGrammar(pDoc->GetGrammar()); |
| if ( bDeleteTab ) |
| aComp.UpdateDeleteTab( rRange.aStart.Tab(), sal_False, sal_True, bChanged2 ); |
| else |
| aComp.UpdateNameReference( eUpdateRefMode, rRange, nDx, nDy, nDz, bChanged2 ); |
| } |
| |
| if (bChanged2) |
| DELETEZ(pFCell2); // is created again in IsValid |
| } |
| } |
| |
| void ScConditionEntry::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos ) |
| { |
| if (pFormula1) |
| { |
| ScCompiler aComp( pDoc, aSrcPos, *pFormula1); |
| aComp.SetGrammar(pDoc->GetGrammar()); |
| aComp.UpdateMoveTab(nOldPos, nNewPos, sal_True ); |
| DELETEZ(pFCell1); |
| } |
| if (pFormula2) |
| { |
| ScCompiler aComp( pDoc, aSrcPos, *pFormula2); |
| aComp.SetGrammar(pDoc->GetGrammar()); |
| aComp.UpdateMoveTab(nOldPos, nNewPos, sal_True ); |
| DELETEZ(pFCell2); |
| } |
| } |
| |
| //! als Vergleichsoperator ans TokenArray ??? |
| |
| sal_Bool lcl_IsEqual( const ScTokenArray* pArr1, const ScTokenArray* pArr2 ) |
| { |
| // verglichen wird nur das nicht-UPN Array |
| |
| if ( pArr1 && pArr2 ) |
| { |
| sal_uInt16 nLen = pArr1->GetLen(); |
| if ( pArr2->GetLen() != nLen ) |
| return sal_False; |
| |
| FormulaToken** ppToken1 = pArr1->GetArray(); |
| FormulaToken** ppToken2 = pArr2->GetArray(); |
| for (sal_uInt16 i=0; i<nLen; i++) |
| { |
| if ( ppToken1[i] != ppToken2[i] && |
| !(*ppToken1[i] == *ppToken2[i]) ) |
| return sal_False; // Unterschied |
| } |
| return sal_True; // alle Eintraege gleich |
| } |
| else |
| return !pArr1 && !pArr2; // beide 0 -> gleich |
| } |
| |
| int ScConditionEntry::operator== ( const ScConditionEntry& r ) const |
| { |
| sal_Bool bEq = (eOp == r.eOp && nOptions == r.nOptions && |
| lcl_IsEqual( pFormula1, r.pFormula1 ) && |
| lcl_IsEqual( pFormula2, r.pFormula2 )); |
| if (bEq) |
| { |
| // for formulas, the reference positions must be compared, too |
| // (including aSrcString, for inserting the entries during XML import) |
| if ( ( pFormula1 || pFormula2 ) && ( aSrcPos != r.aSrcPos || aSrcString != r.aSrcString ) ) |
| bEq = sal_False; |
| |
| // wenn keine Formeln, Werte vergleichen |
| if ( !pFormula1 && ( nVal1 != r.nVal1 || aStrVal1 != r.aStrVal1 || bIsStr1 != r.bIsStr1 ) ) |
| bEq = sal_False; |
| if ( !pFormula2 && ( nVal2 != r.nVal2 || aStrVal2 != r.aStrVal2 || bIsStr2 != r.bIsStr2 ) ) |
| bEq = sal_False; |
| } |
| |
| return bEq; |
| } |
| |
| void ScConditionEntry::Interpret( const ScAddress& rPos ) |
| { |
| // Formelzellen anlegen |
| // dabei koennen neue Broadcaster (Note-Zellen) ins Dokument eingefuegt werden !!!! |
| |
| if ( ( pFormula1 && !pFCell1 ) || ( pFormula2 && !pFCell2 ) ) |
| MakeCells( rPos ); |
| |
| // Formeln auswerten |
| |
| sal_Bool bDirty = sal_False; //! 1 und 2 getrennt ??? |
| |
| ScFormulaCell* pTemp1 = NULL; |
| ScFormulaCell* pEff1 = pFCell1; |
| if ( bRelRef1 ) |
| { |
| pTemp1 = new ScFormulaCell( pDoc, rPos, pFormula1 ); // ohne Listening |
| pEff1 = pTemp1; |
| } |
| if ( pEff1 ) |
| { |
| if (!pEff1->IsRunning()) // keine 522 erzeugen |
| { |
| //! Changed statt Dirty abfragen !!! |
| if (pEff1->GetDirty() && !bRelRef1 && pDoc->GetAutoCalc()) |
| bDirty = sal_True; |
| if (pEff1->IsValue()) |
| { |
| bIsStr1 = sal_False; |
| nVal1 = pEff1->GetValue(); |
| aStrVal1.Erase(); |
| } |
| else |
| { |
| bIsStr1 = sal_True; |
| pEff1->GetString( aStrVal1 ); |
| nVal1 = 0.0; |
| } |
| } |
| } |
| delete pTemp1; |
| |
| ScFormulaCell* pTemp2 = NULL; |
| ScFormulaCell* pEff2 = pFCell2; //@ 1!=2 |
| if ( bRelRef2 ) |
| { |
| pTemp2 = new ScFormulaCell( pDoc, rPos, pFormula2 ); // ohne Listening |
| pEff2 = pTemp2; |
| } |
| if ( pEff2 ) |
| { |
| if (!pEff2->IsRunning()) // keine 522 erzeugen |
| { |
| if (pEff2->GetDirty() && !bRelRef2 && pDoc->GetAutoCalc()) |
| bDirty = sal_True; |
| if (pEff2->IsValue()) |
| { |
| bIsStr2 = sal_False; |
| nVal2 = pEff2->GetValue(); |
| aStrVal2.Erase(); |
| } |
| else |
| { |
| bIsStr2 = sal_True; |
| pEff2->GetString( aStrVal2 ); |
| nVal2 = 0.0; |
| } |
| } |
| } |
| delete pTemp2; |
| |
| // wenn IsRunning, bleiben die letzten Werte erhalten |
| |
| if (bDirty && !bFirstRun) |
| { |
| // bei bedingten Formaten neu painten |
| |
| DataChanged( NULL ); // alles |
| } |
| |
| bFirstRun = sal_False; |
| } |
| |
| sal_Bool ScConditionEntry::IsValid( double nArg ) const |
| { |
| // Interpret muss schon gerufen sein |
| |
| if ( bIsStr1 ) |
| { |
| // wenn auf String getestet wird, bei Zahlen immer sal_False, ausser bei "ungleich" |
| |
| return ( eOp == SC_COND_NOTEQUAL ); |
| } |
| |
| if ( eOp == SC_COND_BETWEEN || eOp == SC_COND_NOTBETWEEN ) |
| if ( bIsStr2 ) |
| return sal_False; |
| |
| double nComp1 = nVal1; // Kopie, damit vertauscht werden kann |
| double nComp2 = nVal2; |
| |
| if ( eOp == SC_COND_BETWEEN || eOp == SC_COND_NOTBETWEEN ) |
| if ( nComp1 > nComp2 ) |
| { |
| // richtige Reihenfolge fuer Wertebereich |
| double nTemp = nComp1; nComp1 = nComp2; nComp2 = nTemp; |
| } |
| |
| // Alle Grenzfaelle muessen per ::rtl::math::approxEqual getestet werden! |
| |
| sal_Bool bValid = sal_False; |
| switch (eOp) |
| { |
| case SC_COND_NONE: |
| break; // immer sal_False; |
| case SC_COND_EQUAL: |
| bValid = ::rtl::math::approxEqual( nArg, nComp1 ); |
| break; |
| case SC_COND_NOTEQUAL: |
| bValid = !::rtl::math::approxEqual( nArg, nComp1 ); |
| break; |
| case SC_COND_GREATER: |
| bValid = ( nArg > nComp1 ) && !::rtl::math::approxEqual( nArg, nComp1 ); |
| break; |
| case SC_COND_EQGREATER: |
| bValid = ( nArg >= nComp1 ) || ::rtl::math::approxEqual( nArg, nComp1 ); |
| break; |
| case SC_COND_LESS: |
| bValid = ( nArg < nComp1 ) && !::rtl::math::approxEqual( nArg, nComp1 ); |
| break; |
| case SC_COND_EQLESS: |
| bValid = ( nArg <= nComp1 ) || ::rtl::math::approxEqual( nArg, nComp1 ); |
| break; |
| case SC_COND_BETWEEN: |
| bValid = ( nArg >= nComp1 && nArg <= nComp2 ) || |
| ::rtl::math::approxEqual( nArg, nComp1 ) || ::rtl::math::approxEqual( nArg, nComp2 ); |
| break; |
| case SC_COND_NOTBETWEEN: |
| bValid = ( nArg < nComp1 || nArg > nComp2 ) && |
| !::rtl::math::approxEqual( nArg, nComp1 ) && !::rtl::math::approxEqual( nArg, nComp2 ); |
| break; |
| case SC_COND_DIRECT: |
| bValid = !::rtl::math::approxEqual( nComp1, 0.0 ); |
| break; |
| default: |
| DBG_ERROR("unbekannte Operation bei ScConditionEntry"); |
| break; |
| } |
| return bValid; |
| } |
| |
| sal_Bool ScConditionEntry::IsValidStr( const String& rArg ) const |
| { |
| // Interpret muss schon gerufen sein |
| |
| if ( eOp == SC_COND_DIRECT ) // Formel ist unabhaengig vom Inhalt |
| return !::rtl::math::approxEqual( nVal1, 0.0 ); |
| |
| // Wenn Bedingung Zahl enthaelt, immer sal_False, ausser bei "ungleich" |
| |
| if ( !bIsStr1 ) |
| return ( eOp == SC_COND_NOTEQUAL ); |
| if ( eOp == SC_COND_BETWEEN || eOp == SC_COND_NOTBETWEEN ) |
| if ( !bIsStr2 ) |
| return sal_False; |
| |
| String aUpVal1( aStrVal1 ); //! als Member? (dann auch in Interpret setzen) |
| String aUpVal2( aStrVal2 ); |
| |
| if ( eOp == SC_COND_BETWEEN || eOp == SC_COND_NOTBETWEEN ) |
| if ( ScGlobal::GetCollator()->compareString( aUpVal1, aUpVal2 ) |
| == COMPARE_GREATER ) |
| { |
| // richtige Reihenfolge fuer Wertebereich |
| String aTemp( aUpVal1 ); aUpVal1 = aUpVal2; aUpVal2 = aTemp; |
| } |
| |
| sal_Bool bValid; |
| switch ( eOp ) |
| { |
| case SC_COND_EQUAL: |
| bValid = (ScGlobal::GetCollator()->compareString( |
| rArg, aUpVal1 ) == COMPARE_EQUAL); |
| break; |
| case SC_COND_NOTEQUAL: |
| bValid = (ScGlobal::GetCollator()->compareString( |
| rArg, aUpVal1 ) != COMPARE_EQUAL); |
| break; |
| default: |
| { |
| sal_Int32 nCompare = ScGlobal::GetCollator()->compareString( |
| rArg, aUpVal1 ); |
| switch ( eOp ) |
| { |
| case SC_COND_GREATER: |
| bValid = ( nCompare == COMPARE_GREATER ); |
| break; |
| case SC_COND_EQGREATER: |
| bValid = ( nCompare == COMPARE_EQUAL || nCompare == COMPARE_GREATER ); |
| break; |
| case SC_COND_LESS: |
| bValid = ( nCompare == COMPARE_LESS ); |
| break; |
| case SC_COND_EQLESS: |
| bValid = ( nCompare == COMPARE_EQUAL || nCompare == COMPARE_LESS ); |
| break; |
| case SC_COND_BETWEEN: |
| case SC_COND_NOTBETWEEN: |
| // Test auf NOTBETWEEN: |
| bValid = ( nCompare == COMPARE_LESS || |
| ScGlobal::GetCollator()->compareString( rArg, |
| aUpVal2 ) == COMPARE_GREATER ); |
| if ( eOp == SC_COND_BETWEEN ) |
| bValid = !bValid; |
| break; |
| // SC_COND_DIRECT schon oben abgefragt |
| default: |
| DBG_ERROR("unbekannte Operation bei ScConditionEntry"); |
| bValid = sal_False; |
| break; |
| } |
| } |
| } |
| return bValid; |
| } |
| |
| sal_Bool ScConditionEntry::IsCellValid( ScBaseCell* pCell, const ScAddress& rPos ) const |
| { |
| ((ScConditionEntry*)this)->Interpret(rPos); // Formeln auswerten |
| |
| double nArg = 0.0; |
| String aArgStr; |
| sal_Bool bVal = sal_True; |
| |
| if ( pCell ) |
| { |
| CellType eType = pCell->GetCellType(); |
| switch (eType) |
| { |
| case CELLTYPE_VALUE: |
| nArg = ((ScValueCell*)pCell)->GetValue(); |
| break; |
| case CELLTYPE_FORMULA: |
| { |
| ScFormulaCell* pFCell = (ScFormulaCell*)pCell; |
| bVal = pFCell->IsValue(); |
| if (bVal) |
| nArg = pFCell->GetValue(); |
| else |
| pFCell->GetString(aArgStr); |
| } |
| break; |
| case CELLTYPE_STRING: |
| case CELLTYPE_EDIT: |
| bVal = sal_False; |
| if ( eType == CELLTYPE_STRING ) |
| ((ScStringCell*)pCell)->GetString(aArgStr); |
| else |
| ((ScEditCell*)pCell)->GetString(aArgStr); |
| break; |
| |
| default: |
| pCell = NULL; // Note-Zellen wie leere |
| break; |
| } |
| } |
| |
| if (!pCell) |
| if (bIsStr1) |
| bVal = sal_False; // leere Zellen je nach Bedingung |
| |
| if (bVal) |
| return IsValid( nArg ); |
| else |
| return IsValidStr( aArgStr ); |
| } |
| |
| String ScConditionEntry::GetExpression( const ScAddress& rCursor, sal_uInt16 nIndex, |
| sal_uLong nNumFmt, |
| const FormulaGrammar::Grammar eGrammar ) const |
| { |
| String aRet; |
| |
| if ( FormulaGrammar::isEnglish( eGrammar) && nNumFmt == 0 ) |
| nNumFmt = pDoc->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US ); |
| |
| if ( nIndex==0 ) |
| { |
| if ( pFormula1 ) |
| { |
| ScCompiler aComp(pDoc, rCursor, *pFormula1); |
| aComp.SetGrammar(eGrammar); |
| aComp.CreateStringFromTokenArray( aRet ); |
| } |
| else if (bIsStr1) |
| { |
| aRet = '"'; |
| aRet += aStrVal1; |
| aRet += '"'; |
| } |
| else |
| pDoc->GetFormatTable()->GetInputLineString(nVal1, nNumFmt, aRet); |
| } |
| else if ( nIndex==1 ) |
| { |
| if ( pFormula2 ) |
| { |
| ScCompiler aComp(pDoc, rCursor, *pFormula2); |
| aComp.SetGrammar(eGrammar); |
| aComp.CreateStringFromTokenArray( aRet ); |
| } |
| else if (bIsStr2) |
| { |
| aRet = '"'; |
| aRet += aStrVal2; |
| aRet += '"'; |
| } |
| else |
| pDoc->GetFormatTable()->GetInputLineString(nVal2, nNumFmt, aRet); |
| } |
| else |
| { |
| DBG_ERROR("GetExpression: falscher Index"); |
| } |
| |
| return aRet; |
| } |
| |
| ScTokenArray* ScConditionEntry::CreateTokenArry( sal_uInt16 nIndex ) const |
| { |
| ScTokenArray* pRet = NULL; |
| ScAddress aAddr; |
| |
| if ( nIndex==0 ) |
| { |
| if ( pFormula1 ) |
| pRet = new ScTokenArray( *pFormula1 ); |
| else |
| { |
| pRet = new ScTokenArray(); |
| if (bIsStr1) |
| pRet->AddString( aStrVal1.GetBuffer() ); |
| else |
| pRet->AddDouble( nVal1 ); |
| } |
| } |
| else if ( nIndex==1 ) |
| { |
| if ( pFormula2 ) |
| pRet = new ScTokenArray( *pFormula2 ); |
| else |
| { |
| pRet = new ScTokenArray(); |
| if (bIsStr2) |
| pRet->AddString( aStrVal2.GetBuffer() ); |
| else |
| pRet->AddDouble( nVal2 ); |
| } |
| } |
| else |
| { |
| DBG_ERROR("GetExpression: falscher Index"); |
| } |
| |
| return pRet; |
| } |
| |
| void ScConditionEntry::SourceChanged( const ScAddress& rChanged ) |
| { |
| for (sal_uInt16 nPass = 0; nPass < 2; nPass++) |
| { |
| ScTokenArray* pFormula = nPass ? pFormula2 : pFormula1; |
| if (pFormula) |
| { |
| pFormula->Reset(); |
| ScToken* t; |
| while ( ( t = static_cast<ScToken*>(pFormula->GetNextReference()) ) != NULL ) |
| { |
| SingleDoubleRefProvider aProv( *t ); |
| if ( aProv.Ref1.IsColRel() || aProv.Ref1.IsRowRel() || aProv.Ref1.IsTabRel() || |
| aProv.Ref2.IsColRel() || aProv.Ref2.IsRowRel() || aProv.Ref2.IsTabRel() ) |
| { |
| // absolut muss getroffen sein, relativ bestimmt Bereich |
| |
| sal_Bool bHit = sal_True; |
| SCsCOL nCol1; |
| SCsROW nRow1; |
| SCsTAB nTab1; |
| SCsCOL nCol2; |
| SCsROW nRow2; |
| SCsTAB nTab2; |
| |
| if ( aProv.Ref1.IsColRel() ) |
| nCol2 = rChanged.Col() - aProv.Ref1.nRelCol; |
| else |
| { |
| bHit &= ( rChanged.Col() >= aProv.Ref1.nCol ); |
| nCol2 = MAXCOL; |
| } |
| if ( aProv.Ref1.IsRowRel() ) |
| nRow2 = rChanged.Row() - aProv.Ref1.nRelRow; |
| else |
| { |
| bHit &= ( rChanged.Row() >= aProv.Ref1.nRow ); |
| nRow2 = MAXROW; |
| } |
| if ( aProv.Ref1.IsTabRel() ) |
| nTab2 = rChanged.Tab() - aProv.Ref1.nRelTab; |
| else |
| { |
| bHit &= ( rChanged.Tab() >= aProv.Ref1.nTab ); |
| nTab2 = MAXTAB; |
| } |
| |
| if ( aProv.Ref2.IsColRel() ) |
| nCol1 = rChanged.Col() - aProv.Ref2.nRelCol; |
| else |
| { |
| bHit &= ( rChanged.Col() <= aProv.Ref2.nCol ); |
| nCol1 = 0; |
| } |
| if ( aProv.Ref2.IsRowRel() ) |
| nRow1 = rChanged.Row() - aProv.Ref2.nRelRow; |
| else |
| { |
| bHit &= ( rChanged.Row() <= aProv.Ref2.nRow ); |
| nRow1 = 0; |
| } |
| if ( aProv.Ref2.IsTabRel() ) |
| nTab1 = rChanged.Tab() - aProv.Ref2.nRelTab; |
| else |
| { |
| bHit &= ( rChanged.Tab() <= aProv.Ref2.nTab ); |
| nTab1 = 0; |
| } |
| |
| if ( bHit ) |
| { |
| //! begrenzen |
| |
| ScRange aPaint( nCol1,nRow1,nTab1, nCol2,nRow2,nTab2 ); |
| |
| // kein Paint, wenn es nur die Zelle selber ist |
| if ( aPaint.aStart != rChanged || aPaint.aEnd != rChanged ) |
| DataChanged( &aPaint ); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| ScAddress ScConditionEntry::GetValidSrcPos() const |
| { |
| // return a position that's adjusted to allow textual representation of expressions if possible |
| |
| SCTAB nMinTab = aSrcPos.Tab(); |
| SCTAB nMaxTab = nMinTab; |
| |
| for (sal_uInt16 nPass = 0; nPass < 2; nPass++) |
| { |
| ScTokenArray* pFormula = nPass ? pFormula2 : pFormula1; |
| if (pFormula) |
| { |
| pFormula->Reset(); |
| ScToken* t; |
| while ( ( t = static_cast<ScToken*>(pFormula->GetNextReference()) ) != NULL ) |
| { |
| ScSingleRefData& rRef1 = t->GetSingleRef(); |
| if ( rRef1.IsTabRel() && !rRef1.IsTabDeleted() ) |
| { |
| if ( rRef1.nTab < nMinTab ) |
| nMinTab = rRef1.nTab; |
| if ( rRef1.nTab > nMaxTab ) |
| nMaxTab = rRef1.nTab; |
| } |
| if ( t->GetType() == svDoubleRef ) |
| { |
| ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; |
| if ( rRef2.IsTabRel() && !rRef2.IsTabDeleted() ) |
| { |
| if ( rRef2.nTab < nMinTab ) |
| nMinTab = rRef2.nTab; |
| if ( rRef2.nTab > nMaxTab ) |
| nMaxTab = rRef2.nTab; |
| } |
| } |
| } |
| } |
| } |
| |
| ScAddress aValidPos = aSrcPos; |
| SCTAB nTabCount = pDoc->GetTableCount(); |
| if ( nMaxTab >= nTabCount && nMinTab > 0 ) |
| aValidPos.SetTab( aSrcPos.Tab() - nMinTab ); // so the lowest tab ref will be on 0 |
| |
| if ( aValidPos.Tab() >= nTabCount ) |
| aValidPos.SetTab( nTabCount - 1 ); // ensure a valid position even if some references will be invalid |
| |
| return aValidPos; |
| } |
| |
| void ScConditionEntry::DataChanged( const ScRange* /* pModified */ ) const |
| { |
| // nix |
| } |
| |
| bool ScConditionEntry::MarkUsedExternalReferences() const |
| { |
| bool bAllMarked = false; |
| for (sal_uInt16 nPass = 0; !bAllMarked && nPass < 2; nPass++) |
| { |
| ScTokenArray* pFormula = nPass ? pFormula2 : pFormula1; |
| if (pFormula) |
| bAllMarked = pDoc->MarkUsedExternalReferences( *pFormula); |
| } |
| return bAllMarked; |
| } |
| |
| //------------------------------------------------------------------------ |
| |
| ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper, |
| const String& rExpr1, const String& rExpr2, |
| ScDocument* pDocument, const ScAddress& rPos, |
| const String& rStyle, |
| const String& rExprNmsp1, const String& rExprNmsp2, |
| FormulaGrammar::Grammar eGrammar1, |
| FormulaGrammar::Grammar eGrammar2 ) : |
| ScConditionEntry( eOper, rExpr1, rExpr2, pDocument, rPos, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2 ), |
| aStyleName( rStyle ), |
| pParent( NULL ) |
| { |
| } |
| |
| ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper, |
| const ScTokenArray* pArr1, const ScTokenArray* pArr2, |
| ScDocument* pDocument, const ScAddress& rPos, |
| const String& rStyle ) : |
| ScConditionEntry( eOper, pArr1, pArr2, pDocument, rPos ), |
| aStyleName( rStyle ), |
| pParent( NULL ) |
| { |
| } |
| |
| ScCondFormatEntry::ScCondFormatEntry( const ScCondFormatEntry& r ) : |
| ScConditionEntry( r ), |
| aStyleName( r.aStyleName ), |
| pParent( NULL ) |
| { |
| } |
| |
| ScCondFormatEntry::ScCondFormatEntry( ScDocument* pDocument, const ScCondFormatEntry& r ) : |
| ScConditionEntry( pDocument, r ), |
| aStyleName( r.aStyleName ), |
| pParent( NULL ) |
| { |
| } |
| |
| int ScCondFormatEntry::operator== ( const ScCondFormatEntry& r ) const |
| { |
| return ScConditionEntry::operator==( r ) && |
| aStyleName == r.aStyleName; |
| |
| // Range wird nicht verglichen |
| } |
| |
| ScCondFormatEntry::~ScCondFormatEntry() |
| { |
| } |
| |
| void ScCondFormatEntry::DataChanged( const ScRange* pModified ) const |
| { |
| if ( pParent ) |
| pParent->DoRepaint( pModified ); |
| } |
| |
| //------------------------------------------------------------------------ |
| |
| ScConditionalFormat::ScConditionalFormat(sal_uInt32 nNewKey, ScDocument* pDocument) : |
| pDoc( pDocument ), |
| pAreas( NULL ), |
| nKey( nNewKey ), |
| ppEntries( NULL ), |
| nEntryCount( 0 ) |
| { |
| } |
| |
| ScConditionalFormat::ScConditionalFormat(const ScConditionalFormat& r) : |
| pDoc( r.pDoc ), |
| pAreas( NULL ), |
| nKey( r.nKey ), |
| ppEntries( NULL ), |
| nEntryCount( r.nEntryCount ) |
| { |
| if (nEntryCount) |
| { |
| ppEntries = new ScCondFormatEntry*[nEntryCount]; |
| for (sal_uInt16 i=0; i<nEntryCount; i++) |
| { |
| ppEntries[i] = new ScCondFormatEntry(*r.ppEntries[i]); |
| ppEntries[i]->SetParent(this); |
| } |
| } |
| } |
| |
| ScConditionalFormat* ScConditionalFormat::Clone(ScDocument* pNewDoc) const |
| { |
| // echte Kopie der Formeln (fuer Ref-Undo / zwischen Dokumenten) |
| |
| if (!pNewDoc) |
| pNewDoc = pDoc; |
| |
| ScConditionalFormat* pNew = new ScConditionalFormat(nKey, pNewDoc); |
| DBG_ASSERT(!pNew->ppEntries, "wo kommen die Eintraege her?"); |
| |
| if (nEntryCount) |
| { |
| pNew->ppEntries = new ScCondFormatEntry*[nEntryCount]; |
| for (sal_uInt16 i=0; i<nEntryCount; i++) |
| { |
| pNew->ppEntries[i] = new ScCondFormatEntry( pNewDoc, *ppEntries[i] ); |
| pNew->ppEntries[i]->SetParent(pNew); |
| } |
| pNew->nEntryCount = nEntryCount; |
| } |
| |
| return pNew; |
| } |
| |
| sal_Bool ScConditionalFormat::EqualEntries( const ScConditionalFormat& r ) const |
| { |
| if ( nEntryCount != r.nEntryCount ) |
| return sal_False; |
| |
| //! auf gleiche Eintraege in anderer Reihenfolge testen ??? |
| |
| for (sal_uInt16 i=0; i<nEntryCount; i++) |
| if ( ! (*ppEntries[i] == *r.ppEntries[i]) ) |
| return sal_False; |
| |
| return sal_True; |
| } |
| |
| void ScConditionalFormat::AddEntry( const ScCondFormatEntry& rNew ) |
| { |
| ScCondFormatEntry** ppNew = new ScCondFormatEntry*[nEntryCount+1]; |
| for (sal_uInt16 i=0; i<nEntryCount; i++) |
| ppNew[i] = ppEntries[i]; |
| ppNew[nEntryCount] = new ScCondFormatEntry(rNew); |
| ppNew[nEntryCount]->SetParent(this); |
| ++nEntryCount; |
| delete[] ppEntries; |
| ppEntries = ppNew; |
| } |
| |
| ScConditionalFormat::~ScConditionalFormat() |
| { |
| for (sal_uInt16 i=0; i<nEntryCount; i++) |
| delete ppEntries[i]; |
| delete[] ppEntries; |
| |
| delete pAreas; |
| } |
| |
| const ScCondFormatEntry* ScConditionalFormat::GetEntry( sal_uInt16 nPos ) const |
| { |
| if ( nPos < nEntryCount ) |
| return ppEntries[nPos]; |
| else |
| return NULL; |
| } |
| |
| const String& ScConditionalFormat::GetCellStyle( ScBaseCell* pCell, const ScAddress& rPos ) const |
| { |
| for (sal_uInt16 i=0; i<nEntryCount; i++) |
| if ( ppEntries[i]->IsCellValid( pCell, rPos ) ) |
| return ppEntries[i]->GetStyle(); |
| |
| return EMPTY_STRING; |
| } |
| |
| void lcl_Extend( ScRange& rRange, ScDocument* pDoc, sal_Bool bLines ) |
| { |
| SCTAB nTab = rRange.aStart.Tab(); |
| DBG_ASSERT(rRange.aEnd.Tab() == nTab, "lcl_Extend - mehrere Tabellen?"); |
| |
| SCCOL nStartCol = rRange.aStart.Col(); |
| SCROW nStartRow = rRange.aStart.Row(); |
| SCCOL nEndCol = rRange.aEnd.Col(); |
| SCROW nEndRow = rRange.aEnd.Row(); |
| |
| sal_Bool bEx = pDoc->ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nTab ); |
| |
| if (bLines) |
| { |
| if (nStartCol > 0) --nStartCol; |
| if (nStartRow > 0) --nStartRow; |
| if (nEndCol < MAXCOL) ++nEndCol; |
| if (nEndRow < MAXROW) ++nEndRow; |
| } |
| |
| if ( bEx || bLines ) |
| { |
| rRange.aStart.Set( nStartCol, nStartRow, nTab ); |
| rRange.aEnd.Set( nEndCol, nEndRow, nTab ); |
| } |
| } |
| |
| sal_Bool lcl_CutRange( ScRange& rRange, const ScRange& rOther ) |
| { |
| rRange.Justify(); |
| ScRange aCmpRange = rOther; |
| aCmpRange.Justify(); |
| |
| if ( rRange.aStart.Col() <= aCmpRange.aEnd.Col() && |
| rRange.aEnd.Col() >= aCmpRange.aStart.Col() && |
| rRange.aStart.Row() <= aCmpRange.aEnd.Row() && |
| rRange.aEnd.Row() >= aCmpRange.aStart.Row() && |
| rRange.aStart.Tab() <= aCmpRange.aEnd.Tab() && |
| rRange.aEnd.Tab() >= aCmpRange.aStart.Tab() ) |
| { |
| if ( rRange.aStart.Col() < aCmpRange.aStart.Col() ) |
| rRange.aStart.SetCol( aCmpRange.aStart.Col() ); |
| if ( rRange.aStart.Row() < aCmpRange.aStart.Row() ) |
| rRange.aStart.SetRow( aCmpRange.aStart.Row() ); |
| if ( rRange.aStart.Tab() < aCmpRange.aStart.Tab() ) |
| rRange.aStart.SetTab( aCmpRange.aStart.Tab() ); |
| if ( rRange.aEnd.Col() > aCmpRange.aEnd.Col() ) |
| rRange.aEnd.SetCol( aCmpRange.aEnd.Col() ); |
| if ( rRange.aEnd.Row() > aCmpRange.aEnd.Row() ) |
| rRange.aEnd.SetRow( aCmpRange.aEnd.Row() ); |
| if ( rRange.aEnd.Tab() > aCmpRange.aEnd.Tab() ) |
| rRange.aEnd.SetTab( aCmpRange.aEnd.Tab() ); |
| |
| return sal_True; |
| } |
| |
| return sal_False; // ausserhalb |
| } |
| |
| void ScConditionalFormat::DoRepaint( const ScRange* pModified ) |
| { |
| sal_uInt16 i; |
| SfxObjectShell* pSh = pDoc->GetDocumentShell(); |
| if (pSh) |
| { |
| // Rahmen/Schatten enthalten? |
| // (alle Bedingungen testen) |
| sal_Bool bExtend = sal_False; |
| sal_Bool bRotate = sal_False; |
| sal_Bool bAttrTested = sal_False; |
| |
| if (!pAreas) // RangeList ggf. holen |
| { |
| pAreas = new ScRangeList; |
| pDoc->FindConditionalFormat( nKey, *pAreas ); |
| } |
| sal_uInt16 nCount = (sal_uInt16) pAreas->Count(); |
| for (i=0; i<nCount; i++) |
| { |
| ScRange aRange = *pAreas->GetObject(i); |
| sal_Bool bDo = sal_True; |
| if ( pModified ) |
| { |
| if ( !lcl_CutRange( aRange, *pModified ) ) |
| bDo = sal_False; |
| } |
| if (bDo) |
| { |
| if ( !bAttrTested ) |
| { |
| // #116562# Look at the style's content only if the repaint is necessary |
| // for any condition, to avoid the time-consuming Find() if there are many |
| // conditional formats and styles. |
| for (sal_uInt16 nEntry=0; nEntry<nEntryCount; nEntry++) |
| { |
| String aStyle = ppEntries[nEntry]->GetStyle(); |
| if (aStyle.Len()) |
| { |
| SfxStyleSheetBase* pStyleSheet = |
| pDoc->GetStyleSheetPool()->Find( aStyle, SFX_STYLE_FAMILY_PARA ); |
| if ( pStyleSheet ) |
| { |
| const SfxItemSet& rSet = pStyleSheet->GetItemSet(); |
| if (rSet.GetItemState( ATTR_BORDER, sal_True ) == SFX_ITEM_SET || |
| rSet.GetItemState( ATTR_SHADOW, sal_True ) == SFX_ITEM_SET) |
| { |
| bExtend = sal_True; |
| } |
| if (rSet.GetItemState( ATTR_ROTATE_VALUE, sal_True ) == SFX_ITEM_SET || |
| rSet.GetItemState( ATTR_ROTATE_MODE, sal_True ) == SFX_ITEM_SET) |
| { |
| bRotate = sal_True; |
| } |
| } |
| } |
| } |
| bAttrTested = sal_True; |
| } |
| |
| lcl_Extend( aRange, pDoc, bExtend ); // zusammengefasste und bExtend |
| if ( bRotate ) |
| { |
| aRange.aStart.SetCol(0); |
| aRange.aEnd.SetCol(MAXCOL); // gedreht: ganze Zeilen |
| } |
| |
| // gedreht -> ganze Zeilen |
| if ( aRange.aStart.Col() != 0 || aRange.aEnd.Col() != MAXCOL ) |
| { |
| if ( pDoc->HasAttrib( 0,aRange.aStart.Row(),aRange.aStart.Tab(), |
| MAXCOL,aRange.aEnd.Row(),aRange.aEnd.Tab(), |
| HASATTR_ROTATE ) ) |
| { |
| aRange.aStart.SetCol(0); |
| aRange.aEnd.SetCol(MAXCOL); |
| } |
| } |
| |
| pDoc->RepaintRange( aRange ); |
| } |
| } |
| } |
| } |
| |
| void ScConditionalFormat::InvalidateArea() |
| { |
| delete pAreas; |
| pAreas = NULL; |
| } |
| |
| void ScConditionalFormat::CompileAll() |
| { |
| for (sal_uInt16 i=0; i<nEntryCount; i++) |
| ppEntries[i]->CompileAll(); |
| } |
| |
| void ScConditionalFormat::CompileXML() |
| { |
| for (sal_uInt16 i=0; i<nEntryCount; i++) |
| ppEntries[i]->CompileXML(); |
| } |
| |
| void ScConditionalFormat::UpdateReference( UpdateRefMode eUpdateRefMode, |
| const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) |
| { |
| for (sal_uInt16 i=0; i<nEntryCount; i++) |
| ppEntries[i]->UpdateReference(eUpdateRefMode, rRange, nDx, nDy, nDz); |
| |
| delete pAreas; // aus dem AttrArray kommt beim Einfuegen/Loeschen kein Aufruf |
| pAreas = NULL; |
| } |
| |
| void ScConditionalFormat::RenameCellStyle(const String& rOld, const String& rNew) |
| { |
| for (sal_uInt16 i=0; i<nEntryCount; i++) |
| if ( ppEntries[i]->GetStyle() == rOld ) |
| ppEntries[i]->UpdateStyleName( rNew ); |
| } |
| |
| void ScConditionalFormat::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos ) |
| { |
| for (sal_uInt16 i=0; i<nEntryCount; i++) |
| ppEntries[i]->UpdateMoveTab( nOldPos, nNewPos ); |
| |
| delete pAreas; // aus dem AttrArray kommt beim Einfuegen/Loeschen kein Aufruf |
| pAreas = NULL; |
| } |
| |
| void ScConditionalFormat::SourceChanged( const ScAddress& rAddr ) |
| { |
| for (sal_uInt16 i=0; i<nEntryCount; i++) |
| ppEntries[i]->SourceChanged( rAddr ); |
| } |
| |
| bool ScConditionalFormat::MarkUsedExternalReferences() const |
| { |
| bool bAllMarked = false; |
| for (sal_uInt16 i=0; !bAllMarked && i<nEntryCount; i++) |
| bAllMarked = ppEntries[i]->MarkUsedExternalReferences(); |
| return bAllMarked; |
| } |
| |
| //------------------------------------------------------------------------ |
| |
| ScConditionalFormatList::ScConditionalFormatList(const ScConditionalFormatList& rList) : |
| ScConditionalFormats_Impl() |
| { |
| // fuer Ref-Undo - echte Kopie mit neuen Tokens! |
| |
| sal_uInt16 nCount = rList.Count(); |
| |
| for (sal_uInt16 i=0; i<nCount; i++) |
| InsertNew( rList[i]->Clone() ); |
| |
| //! sortierte Eintraege aus rList schneller einfuegen ??? |
| } |
| |
| ScConditionalFormatList::ScConditionalFormatList(ScDocument* pNewDoc, |
| const ScConditionalFormatList& rList) |
| { |
| // fuer neues Dokument - echte Kopie mit neuen Tokens! |
| |
| sal_uInt16 nCount = rList.Count(); |
| |
| for (sal_uInt16 i=0; i<nCount; i++) |
| InsertNew( rList[i]->Clone(pNewDoc) ); |
| |
| //! sortierte Eintraege aus rList schneller einfuegen ??? |
| } |
| |
| sal_Bool ScConditionalFormatList::operator==( const ScConditionalFormatList& r ) const |
| { |
| // fuer Ref-Undo - interne Variablen werden nicht verglichen |
| |
| sal_uInt16 nCount = Count(); |
| sal_Bool bEqual = ( nCount == r.Count() ); |
| for (sal_uInt16 i=0; i<nCount && bEqual; i++) // Eintraege sind sortiert |
| if ( !(*this)[i]->EqualEntries(*r[i]) ) // Eintraege unterschiedlich ? |
| bEqual = sal_False; |
| |
| return bEqual; |
| } |
| |
| ScConditionalFormat* ScConditionalFormatList::GetFormat( sal_uInt32 nKey ) |
| { |
| //! binaer suchen |
| |
| sal_uInt16 nCount = Count(); |
| for (sal_uInt16 i=0; i<nCount; i++) |
| if ((*this)[i]->GetKey() == nKey) |
| return (*this)[i]; |
| |
| DBG_ERROR("ScConditionalFormatList: Eintrag nicht gefunden"); |
| return NULL; |
| } |
| |
| void ScConditionalFormatList::CompileAll() |
| { |
| sal_uInt16 nCount = Count(); |
| for (sal_uInt16 i=0; i<nCount; i++) |
| (*this)[i]->CompileAll(); |
| } |
| |
| void ScConditionalFormatList::CompileXML() |
| { |
| sal_uInt16 nCount = Count(); |
| for (sal_uInt16 i=0; i<nCount; i++) |
| (*this)[i]->CompileXML(); |
| } |
| |
| void ScConditionalFormatList::UpdateReference( UpdateRefMode eUpdateRefMode, |
| const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) |
| { |
| sal_uInt16 nCount = Count(); |
| for (sal_uInt16 i=0; i<nCount; i++) |
| (*this)[i]->UpdateReference( eUpdateRefMode, rRange, nDx, nDy, nDz ); |
| } |
| |
| void ScConditionalFormatList::RenameCellStyle( const String& rOld, const String& rNew ) |
| { |
| sal_uLong nCount=Count(); |
| for (sal_uInt16 i=0; i<nCount; i++) |
| (*this)[i]->RenameCellStyle(rOld,rNew); |
| } |
| |
| void ScConditionalFormatList::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos ) |
| { |
| sal_uInt16 nCount = Count(); |
| for (sal_uInt16 i=0; i<nCount; i++) |
| (*this)[i]->UpdateMoveTab( nOldPos, nNewPos ); |
| } |
| |
| void ScConditionalFormatList::SourceChanged( const ScAddress& rAddr ) |
| { |
| sal_uInt16 nCount = Count(); |
| for (sal_uInt16 i=0; i<nCount; i++) |
| (*this)[i]->SourceChanged( rAddr ); |
| } |
| |
| bool ScConditionalFormatList::MarkUsedExternalReferences() const |
| { |
| bool bAllMarked = false; |
| sal_uInt16 nCount = Count(); |
| for (sal_uInt16 i=0; !bAllMarked && i<nCount; i++) |
| bAllMarked = (*this)[i]->MarkUsedExternalReferences(); |
| return bAllMarked; |
| } |