1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "scitems.hxx"
21 #include <sfx2/objsh.hxx>
22 #include <svl/itemset.hxx>
23 #include <svl/zforlist.hxx>
24 #include <rtl/math.hxx>
25 #include <unotools/collatorwrapper.hxx>
27 #include <com/sun/star/sheet/ConditionOperator2.hpp>
29 #include "conditio.hxx"
30 #include "formulacell.hxx"
31 #include "document.hxx"
33 #include "compiler.hxx"
34 #include "rechead.hxx"
35 #include "rangelst.hxx"
36 #include "stlpool.hxx"
37 #include "rangenam.hxx"
38 #include "colorscale.hxx"
39 #include "cellvalue.hxx"
40 #include "editutil.hxx"
41 #include "tokenarray.hxx"
42 #include "refupdatecontext.hxx"
43 #include <svl/sharedstring.hxx>
44 #include <svl/sharedstringpool.hxx>
45 #include <boost/scoped_ptr.hpp>
47 using namespace formula
;
49 ScFormatEntry::ScFormatEntry(ScDocument
* pDoc
):
54 bool ScFormatEntry::operator==( const ScFormatEntry
& r
) const
56 if(GetType() != r
.GetType())
61 case condformat::CONDITION
:
62 return static_cast<const ScCondFormatEntry
&>(*this) == static_cast<const ScCondFormatEntry
&>(r
);
64 // TODO: implement also this case
65 // actually return false for these cases is not that bad
66 // as soon as databar and color scale are tested we need
67 // to think about the range
72 void ScFormatEntry::startRendering()
76 void ScFormatEntry::endRendering()
80 static bool lcl_HasRelRef( ScDocument
* pDoc
, ScTokenArray
* pFormula
, sal_uInt16 nRecursion
= 0 )
86 for( t
= pFormula
->Next(); t
; t
= pFormula
->Next() )
88 switch( t
->GetType() )
92 ScSingleRefData
& rRef2
= t
->GetDoubleRef()->Ref2
;
93 if ( rRef2
.IsColRel() || rRef2
.IsRowRel() || rRef2
.IsTabRel() )
100 ScSingleRefData
& rRef1
= *t
->GetSingleRef();
101 if ( rRef1
.IsColRel() || rRef1
.IsRowRel() || rRef1
.IsTabRel() )
108 if( t
->GetOpCode() == ocName
) // DB areas always absolute
109 if( ScRangeData
* pRangeData
= pDoc
->GetRangeName()->findByIndex( t
->GetIndex() ) )
110 if( (nRecursion
< 42) && lcl_HasRelRef( pDoc
, pRangeData
->GetCode(), nRecursion
+ 1 ) )
115 // #i34474# function result dependent on cell position
118 switch( t
->GetOpCode() )
120 case ocRow
: // ROW() returns own row index
121 case ocColumn
: // COLUMN() returns own column index
122 case ocSheet
: // SHEET() returns own sheet index
123 case ocCell
: // CELL() may return own cell address
127 // added to avoid warnings
135 // added to avoid warnings
143 ScConditionEntry::ScConditionEntry( const ScConditionEntry
& r
) :
144 ScFormatEntry(r
.mpDoc
),
146 nOptions(r
.nOptions
),
149 aStrVal1(r
.aStrVal1
),
150 aStrVal2(r
.aStrVal2
),
151 aStrNmsp1(r
.aStrNmsp1
),
152 aStrNmsp2(r
.aStrNmsp2
),
153 eTempGrammar1(r
.eTempGrammar1
),
154 eTempGrammar2(r
.eTempGrammar2
),
160 aSrcString(r
.aSrcString
),
163 bRelRef1(r
.bRelRef1
),
164 bRelRef2(r
.bRelRef2
),
166 pCondFormat(r
.pCondFormat
)
168 // ScTokenArray copy ctor creates a flat copy
170 pFormula1
= new ScTokenArray( *r
.pFormula1
);
172 pFormula2
= new ScTokenArray( *r
.pFormula2
);
174 // Formula cells are created at IsValid
177 ScConditionEntry::ScConditionEntry( ScDocument
* pDocument
, const ScConditionEntry
& r
) :
178 ScFormatEntry(pDocument
),
180 nOptions(r
.nOptions
),
183 aStrVal1(r
.aStrVal1
),
184 aStrVal2(r
.aStrVal2
),
185 aStrNmsp1(r
.aStrNmsp1
),
186 aStrNmsp2(r
.aStrNmsp2
),
187 eTempGrammar1(r
.eTempGrammar1
),
188 eTempGrammar2(r
.eTempGrammar2
),
194 aSrcString(r
.aSrcString
),
197 bRelRef1(r
.bRelRef1
),
198 bRelRef2(r
.bRelRef2
),
200 pCondFormat(r
.pCondFormat
)
202 // Real copy of the formulas (for Ref Undo)
204 pFormula1
= r
.pFormula1
->Clone();
206 pFormula2
= r
.pFormula2
->Clone();
208 // Formula cells are created at IsValid
209 // TODO: But not in the Clipboard! So interpret beforehand!
212 ScConditionEntry::ScConditionEntry( ScConditionMode eOper
,
213 const OUString
& rExpr1
, const OUString
& rExpr2
, ScDocument
* pDocument
, const ScAddress
& rPos
,
214 const OUString
& rExprNmsp1
, const OUString
& rExprNmsp2
,
215 FormulaGrammar::Grammar eGrammar1
, FormulaGrammar::Grammar eGrammar2
) :
216 ScFormatEntry(pDocument
),
221 aStrNmsp1(rExprNmsp1
),
222 aStrNmsp2(rExprNmsp2
),
223 eTempGrammar1(eGrammar1
),
224 eTempGrammar2(eGrammar2
),
237 Compile( rExpr1
, rExpr2
, rExprNmsp1
, rExprNmsp2
, eGrammar1
, eGrammar2
, false );
239 // Formula cells are created at IsValid
242 ScConditionEntry::ScConditionEntry( ScConditionMode eOper
,
243 const ScTokenArray
* pArr1
, const ScTokenArray
* pArr2
,
244 ScDocument
* pDocument
, const ScAddress
& rPos
) :
245 ScFormatEntry(pDocument
),
250 eTempGrammar1(FormulaGrammar::GRAM_DEFAULT
),
251 eTempGrammar2(FormulaGrammar::GRAM_DEFAULT
),
266 pFormula1
= new ScTokenArray( *pArr1
);
267 if ( pFormula1
->GetLen() == 1 )
269 // Single (constant number)?
270 FormulaToken
* pToken
= pFormula1
->First();
271 if ( pToken
->GetOpCode() == ocPush
)
273 if ( pToken
->GetType() == svDouble
)
275 nVal1
= pToken
->GetDouble();
276 DELETEZ(pFormula1
); // Do not remember as formula
278 else if ( pToken
->GetType() == svString
)
281 aStrVal1
= pToken
->GetString().getString();
282 DELETEZ(pFormula1
); // Do not remember as formula
286 bRelRef1
= lcl_HasRelRef( mpDoc
, pFormula1
);
290 pFormula2
= new ScTokenArray( *pArr2
);
291 if ( pFormula2
->GetLen() == 1 )
293 // Single (constant number)?
294 FormulaToken
* pToken
= pFormula2
->First();
295 if ( pToken
->GetOpCode() == ocPush
)
297 if ( pToken
->GetType() == svDouble
)
299 nVal2
= pToken
->GetDouble();
300 DELETEZ(pFormula2
); // Do not remember as formula
302 else if ( pToken
->GetType() == svString
)
305 aStrVal2
= pToken
->GetString().getString();
306 DELETEZ(pFormula2
); // Do not remember as formula
310 bRelRef2
= lcl_HasRelRef( mpDoc
, pFormula2
);
313 // Formula cells are created at IsValid
316 ScConditionEntry::~ScConditionEntry()
325 void ScConditionEntry::SetOperation(ScConditionMode eMode
)
330 void ScConditionEntry::Compile( const OUString
& rExpr1
, const OUString
& rExpr2
,
331 const OUString
& rExprNmsp1
, const OUString
& rExprNmsp2
,
332 FormulaGrammar::Grammar eGrammar1
, FormulaGrammar::Grammar eGrammar2
, bool bTextToReal
)
334 if ( !rExpr1
.isEmpty() || !rExpr2
.isEmpty() )
336 ScCompiler
aComp( mpDoc
, aSrcPos
);
338 if ( !rExpr1
.isEmpty() )
341 aComp
.SetGrammar( eGrammar1
);
342 if ( mpDoc
->IsImportingXML() && !bTextToReal
)
344 // temporary formula string as string tokens
345 //TODO: merge with lcl_ScDocFunc_CreateTokenArrayXML
346 pFormula1
= new ScTokenArray
;
347 pFormula1
->AddStringXML( rExpr1
);
348 // bRelRef1 is set when the formula is compiled again (CompileXML)
352 pFormula1
= aComp
.CompileString( rExpr1
, rExprNmsp1
);
353 if ( pFormula1
->GetLen() == 1 )
355 // Single (constant number)?
356 FormulaToken
* pToken
= pFormula1
->First();
357 if ( pToken
->GetOpCode() == ocPush
)
359 if ( pToken
->GetType() == svDouble
)
361 nVal1
= pToken
->GetDouble();
362 DELETEZ(pFormula1
); // Do not remember as formula
364 else if ( pToken
->GetType() == svString
)
367 aStrVal1
= pToken
->GetString().getString();
368 DELETEZ(pFormula1
); // Do not remember as formula
372 bRelRef1
= lcl_HasRelRef( mpDoc
, pFormula1
);
376 if ( !rExpr2
.isEmpty() )
379 aComp
.SetGrammar( eGrammar2
);
380 if ( mpDoc
->IsImportingXML() && !bTextToReal
)
382 // temporary formula string as string tokens
383 //TODO: merge with lcl_ScDocFunc_CreateTokenArrayXML
384 pFormula2
= new ScTokenArray
;
385 pFormula2
->AddStringXML( rExpr2
);
386 // bRelRef2 is set when the formula is compiled again (CompileXML)
390 pFormula2
= aComp
.CompileString( rExpr2
, rExprNmsp2
);
391 if ( pFormula2
->GetLen() == 1 )
393 // Sigle (constant number)?
394 FormulaToken
* pToken
= pFormula2
->First();
395 if ( pToken
->GetOpCode() == ocPush
)
397 if ( pToken
->GetType() == svDouble
)
399 nVal2
= pToken
->GetDouble();
400 DELETEZ(pFormula2
); // Do not remember as formula
402 else if ( pToken
->GetType() == svString
)
405 aStrVal2
= pToken
->GetString().getString();
406 DELETEZ(pFormula2
); // Do not remember as formula
410 bRelRef2
= lcl_HasRelRef( mpDoc
, pFormula2
);
417 * Create formula cells
419 void ScConditionEntry::MakeCells( const ScAddress
& rPos
)
421 if ( !mpDoc
->IsClipOrUndo() ) // Never calculate in the Clipboard!
423 if ( pFormula1
&& !pFCell1
&& !bRelRef1
)
425 pFCell1
= new ScFormulaCell(mpDoc
, rPos
, *pFormula1
);
426 pFCell1
->StartListeningTo( mpDoc
);
429 if ( pFormula2
&& !pFCell2
&& !bRelRef2
)
431 pFCell2
= new ScFormulaCell(mpDoc
, rPos
, *pFormula2
);
432 pFCell2
->StartListeningTo( mpDoc
);
437 void ScConditionEntry::SetIgnoreBlank(bool bSet
)
439 // The bit SC_COND_NOBLANKS is set if blanks are not ignored
442 nOptions
&= ~SC_COND_NOBLANKS
;
444 nOptions
|= SC_COND_NOBLANKS
;
448 * Delete formula cells, so we re-complile at the next IsValid
450 void ScConditionEntry::CompileAll()
456 void ScConditionEntry::CompileXML()
458 // First parse the formula source position if it was stored as text
459 if ( !aSrcString
.isEmpty() )
462 /* XML is always in OOo:A1 format, although R1C1 would be more amenable
464 if ( aNew
.Parse( aSrcString
, mpDoc
) & SCA_VALID
)
466 // if the position is invalid, there isn't much we can do at this time
470 // Convert the text tokens that were created during XML import into real tokens.
471 Compile( GetExpression(aSrcPos
, 0, 0, eTempGrammar1
),
472 GetExpression(aSrcPos
, 1, 0, eTempGrammar2
),
473 aStrNmsp1
, aStrNmsp2
, eTempGrammar1
, eTempGrammar2
, true );
476 void ScConditionEntry::SetSrcString( const OUString
& rNew
)
478 // aSrcString is only evaluated in CompileXML
479 SAL_WARN_IF( !mpDoc
->IsImportingXML(), "sc", "SetSrcString is only valid for XML import" );
484 void ScConditionEntry::SetFormula1( const ScTokenArray
& rArray
)
486 DELETEZ( pFormula1
);
487 if( rArray
.GetLen() > 0 )
489 pFormula1
= new ScTokenArray( rArray
);
490 bRelRef1
= lcl_HasRelRef( mpDoc
, pFormula1
);
494 void ScConditionEntry::SetFormula2( const ScTokenArray
& rArray
)
496 DELETEZ( pFormula2
);
497 if( rArray
.GetLen() > 0 )
499 pFormula2
= new ScTokenArray( rArray
);
500 bRelRef2
= lcl_HasRelRef( mpDoc
, pFormula2
);
504 void ScConditionEntry::UpdateReference( sc::RefUpdateContext
& rCxt
)
507 aSrcPos
= pCondFormat
->GetRange().Combine().aStart
;
508 ScAddress aOldSrcPos
= aSrcPos
;
509 bool bChangedPos
= false;
510 if (rCxt
.meMode
== URM_INSDEL
&& rCxt
.maRange
.In(aSrcPos
))
512 aSrcPos
.Move(rCxt
.mnColDelta
, rCxt
.mnRowDelta
, rCxt
.mnTabDelta
);
513 bChangedPos
= aSrcPos
!= aOldSrcPos
;
518 sc::RefUpdateResult aRes
;
522 aRes
= pFormula1
->AdjustReferenceOnShift(rCxt
, aOldSrcPos
);
525 aRes
= pFormula1
->AdjustReferenceOnMove(rCxt
, aOldSrcPos
, aSrcPos
);
531 if (aRes
.mbReferenceModified
|| bChangedPos
)
532 DELETEZ(pFCell1
); // is created again in IsValid
537 sc::RefUpdateResult aRes
;
541 aRes
= pFormula2
->AdjustReferenceOnShift(rCxt
, aOldSrcPos
);
544 aRes
= pFormula2
->AdjustReferenceOnMove(rCxt
, aOldSrcPos
, aSrcPos
);
550 if (aRes
.mbReferenceModified
|| bChangedPos
)
551 DELETEZ(pFCell2
); // is created again in IsValid
555 void ScConditionEntry::UpdateInsertTab( sc::RefUpdateInsertTabContext
& rCxt
)
559 pFormula1
->AdjustReferenceOnInsertedTab(rCxt
, aSrcPos
);
565 pFormula2
->AdjustReferenceOnInsertedTab(rCxt
, aSrcPos
);
570 void ScConditionEntry::UpdateDeleteTab( sc::RefUpdateDeleteTabContext
& rCxt
)
574 pFormula1
->AdjustReferenceOnDeletedTab(rCxt
, aSrcPos
);
580 pFormula2
->AdjustReferenceOnDeletedTab(rCxt
, aSrcPos
);
585 void ScConditionEntry::UpdateMoveTab( sc::RefUpdateMoveTabContext
& rCxt
)
589 pFormula1
->AdjustReferenceOnMovedTab(rCxt
, aSrcPos
);
595 pFormula2
->AdjustReferenceOnMovedTab(rCxt
, aSrcPos
);
600 //FIXME: Make this a comparison operator at the TokenArray?
601 static bool lcl_IsEqual( const ScTokenArray
* pArr1
, const ScTokenArray
* pArr2
)
603 // We only compare the non-RPN array
604 if ( pArr1
&& pArr2
)
606 sal_uInt16 nLen
= pArr1
->GetLen();
607 if ( pArr2
->GetLen() != nLen
)
610 FormulaToken
** ppToken1
= pArr1
->GetArray();
611 FormulaToken
** ppToken2
= pArr2
->GetArray();
612 for (sal_uInt16 i
=0; i
<nLen
; i
++)
614 if ( ppToken1
[i
] != ppToken2
[i
] &&
615 !(*ppToken1
[i
] == *ppToken2
[i
]) )
616 return false; // Difference
618 return true; // All entries are the same
621 return !pArr1
&& !pArr2
; // Both 0? -> the same
624 bool ScConditionEntry::operator== ( const ScConditionEntry
& r
) const
626 bool bEq
= (eOp
== r
.eOp
&& nOptions
== r
.nOptions
&&
627 lcl_IsEqual( pFormula1
, r
.pFormula1
) &&
628 lcl_IsEqual( pFormula2
, r
.pFormula2
));
631 // for formulas, the reference positions must be compared, too
632 // (including aSrcString, for inserting the entries during XML import)
633 if ( ( pFormula1
|| pFormula2
) && ( aSrcPos
!= r
.aSrcPos
|| aSrcString
!= r
.aSrcString
) )
636 // If not formulas, compare values
637 if ( !pFormula1
&& ( nVal1
!= r
.nVal1
|| aStrVal1
!= r
.aStrVal1
|| bIsStr1
!= r
.bIsStr1
) )
639 if ( !pFormula2
&& ( nVal2
!= r
.nVal2
|| aStrVal2
!= r
.aStrVal2
|| bIsStr2
!= r
.bIsStr2
) )
646 void ScConditionEntry::Interpret( const ScAddress
& rPos
)
648 // Create formula cells
649 // Note: New Broadcaster (Note cells) may be inserted into the document!
650 if ( ( pFormula1
&& !pFCell1
) || ( pFormula2
&& !pFCell2
) )
654 bool bDirty
= false; // 1 and 2 separate?
656 boost::scoped_ptr
<ScFormulaCell
> pTemp1
;
657 ScFormulaCell
* pEff1
= pFCell1
;
660 pTemp1
.reset(pFormula1
? new ScFormulaCell(mpDoc
, rPos
, *pFormula1
) : new ScFormulaCell(mpDoc
, rPos
));
661 pEff1
= pTemp1
.get();
665 if (!pEff1
->IsRunning()) // Don't create 522
667 //TODO: Query Changed instead of Dirty!
668 if (pEff1
->GetDirty() && !bRelRef1
&& mpDoc
->GetAutoCalc())
670 if (pEff1
->IsValue())
673 nVal1
= pEff1
->GetValue();
679 aStrVal1
= pEff1
->GetString().getString();
686 boost::scoped_ptr
<ScFormulaCell
> pTemp2
;
687 ScFormulaCell
* pEff2
= pFCell2
; //@ 1!=2
690 pTemp2
.reset(pFormula2
? new ScFormulaCell(mpDoc
, rPos
, *pFormula2
) : new ScFormulaCell(mpDoc
, rPos
));
691 pEff2
= pTemp2
.get();
695 if (!pEff2
->IsRunning()) // Don't create 522
697 if (pEff2
->GetDirty() && !bRelRef2
&& mpDoc
->GetAutoCalc())
699 if (pEff2
->IsValue())
702 nVal2
= pEff2
->GetValue();
708 aStrVal2
= pEff2
->GetString().getString();
715 // If IsRunning, the last values remain
716 if (bDirty
&& !bFirstRun
)
718 // Repaint everything for dependent formats
725 static bool lcl_GetCellContent( ScRefCellValue
& rCell
, bool bIsStr1
, double& rArg
, OUString
& rArgStr
,
726 const ScDocument
* pDoc
)
734 switch (rCell
.meType
)
737 rArg
= rCell
.mfValue
;
739 case CELLTYPE_FORMULA
:
741 bVal
= rCell
.mpFormula
->IsValue();
743 rArg
= rCell
.mpFormula
->GetValue();
745 rArgStr
= rCell
.mpFormula
->GetString().getString();
748 case CELLTYPE_STRING
:
751 if (rCell
.meType
== CELLTYPE_STRING
)
752 rArgStr
= rCell
.mpString
->getString();
753 else if (rCell
.mpEditText
)
754 rArgStr
= ScEditUtil::GetString(*rCell
.mpEditText
, pDoc
);
763 void ScConditionEntry::FillCache() const
767 const ScRangeList
& rRanges
= pCondFormat
->GetRange();
768 mpCache
.reset(new ScConditionEntryCache
);
769 size_t nListCount
= rRanges
.size();
770 for( size_t i
= 0; i
< nListCount
; i
++ )
772 const ScRange
*aRange
= rRanges
[i
];
773 SCROW nRow
= aRange
->aEnd
.Row();
774 SCCOL nCol
= aRange
->aEnd
.Col();
775 SCCOL nColStart
= aRange
->aStart
.Col();
776 SCROW nRowStart
= aRange
->aStart
.Row();
777 SCTAB nTab
= aRange
->aStart
.Tab();
779 // temporary fix to workaorund slow duplicate entry
780 // conditions, prevent to use a whole row
783 bool bShrunk
= false;
784 mpDoc
->ShrinkToUsedDataArea(bShrunk
, nTab
, nColStart
, nRowStart
,
788 for( SCROW r
= nRowStart
; r
<= nRow
; r
++ )
789 for( SCCOL c
= nColStart
; c
<= nCol
; c
++ )
791 ScRefCellValue aCell
;
792 aCell
.assign(*mpDoc
, ScAddress(c
, r
, nTab
));
798 if (!lcl_GetCellContent(aCell
, false, nVal
, aStr
, mpDoc
))
800 std::pair
<ScConditionEntryCache::StringCacheType::iterator
, bool> aResult
=
801 mpCache
->maStrings
.insert(
802 ScConditionEntryCache::StringCacheType::value_type(aStr
, 1));
805 aResult
.first
->second
++;
809 std::pair
<ScConditionEntryCache::ValueCacheType::iterator
, bool> aResult
=
810 mpCache
->maValues
.insert(
811 ScConditionEntryCache::ValueCacheType::value_type(nVal
, 1));
814 aResult
.first
->second
++;
816 ++(mpCache
->nValueItems
);
823 bool ScConditionEntry::IsDuplicate( double nArg
, const OUString
& rStr
) const
829 ScConditionEntryCache::ValueCacheType::iterator itr
= mpCache
->maValues
.find(nArg
);
830 if(itr
== mpCache
->maValues
.end())
842 ScConditionEntryCache::StringCacheType::iterator itr
= mpCache
->maStrings
.find(rStr
);
843 if(itr
== mpCache
->maStrings
.end())
855 bool ScConditionEntry::IsTopNElement( double nArg
) const
859 if(mpCache
->nValueItems
<= nVal1
)
863 for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr
= mpCache
->maValues
.rbegin(),
864 itrEnd
= mpCache
->maValues
.rend(); itr
!= itrEnd
; ++itr
)
868 if(itr
->first
<= nArg
)
870 nCells
+= itr
->second
;
876 bool ScConditionEntry::IsBottomNElement( double nArg
) const
880 if(mpCache
->nValueItems
<= nVal1
)
884 for(ScConditionEntryCache::ValueCacheType::const_iterator itr
= mpCache
->maValues
.begin(),
885 itrEnd
= mpCache
->maValues
.end(); itr
!= itrEnd
; ++itr
)
889 if(itr
->first
>= nArg
)
891 nCells
+= itr
->second
;
897 bool ScConditionEntry::IsTopNPercent( double nArg
) const
902 size_t nLimitCells
= static_cast<size_t>(mpCache
->nValueItems
*nVal1
/100);
903 for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr
= mpCache
->maValues
.rbegin(),
904 itrEnd
= mpCache
->maValues
.rend(); itr
!= itrEnd
; ++itr
)
906 if(nCells
>= nLimitCells
)
908 if(itr
->first
<= nArg
)
910 nCells
+= itr
->second
;
916 bool ScConditionEntry::IsBottomNPercent( double nArg
) const
921 size_t nLimitCells
= static_cast<size_t>(mpCache
->nValueItems
*nVal1
/100);
922 for(ScConditionEntryCache::ValueCacheType::const_iterator itr
= mpCache
->maValues
.begin(),
923 itrEnd
= mpCache
->maValues
.end(); itr
!= itrEnd
; ++itr
)
925 if(nCells
>= nLimitCells
)
927 if(itr
->first
>= nArg
)
929 nCells
+= itr
->second
;
935 bool ScConditionEntry::IsBelowAverage( double nArg
, bool bEqual
) const
940 for(ScConditionEntryCache::ValueCacheType::const_iterator itr
= mpCache
->maValues
.begin(),
941 itrEnd
= mpCache
->maValues
.end(); itr
!= itrEnd
; ++itr
)
943 nSum
+= itr
->first
* itr
->second
;
947 return (nArg
<= nSum
/mpCache
->nValueItems
);
949 return (nArg
< nSum
/mpCache
->nValueItems
);
952 bool ScConditionEntry::IsAboveAverage( double nArg
, bool bEqual
) const
957 for(ScConditionEntryCache::ValueCacheType::const_iterator itr
= mpCache
->maValues
.begin(),
958 itrEnd
= mpCache
->maValues
.end(); itr
!= itrEnd
; ++itr
)
960 nSum
+= itr
->first
* itr
->second
;
964 return (nArg
>= nSum
/mpCache
->nValueItems
);
966 return (nArg
> nSum
/mpCache
->nValueItems
);
969 bool ScConditionEntry::IsError( const ScAddress
& rPos
) const
971 switch (mpDoc
->GetCellType(rPos
))
975 case CELLTYPE_FORMULA
:
977 ScFormulaCell
* pFormulaCell
= const_cast<ScFormulaCell
*>(mpDoc
->GetFormulaCell(rPos
));
978 if (pFormulaCell
&& pFormulaCell
->GetErrCode())
981 case CELLTYPE_STRING
:
990 bool ScConditionEntry::IsValid( double nArg
, const ScAddress
& rPos
) const
992 // Interpret must already have been called
997 case SC_COND_BEGINS_WITH
:
998 case SC_COND_ENDS_WITH
:
999 case SC_COND_CONTAINS_TEXT
:
1000 case SC_COND_NOT_CONTAINS_TEXT
:
1002 case SC_COND_NOTEQUAL
:
1009 if ( eOp
== SC_COND_BETWEEN
|| eOp
== SC_COND_NOTBETWEEN
)
1013 double nComp1
= nVal1
; // Copy, so that it can be changed
1014 double nComp2
= nVal2
;
1016 if ( eOp
== SC_COND_BETWEEN
|| eOp
== SC_COND_NOTBETWEEN
)
1017 if ( nComp1
> nComp2
)
1019 // Right order for value range
1020 double nTemp
= nComp1
; nComp1
= nComp2
; nComp2
= nTemp
;
1023 // All corner cases need to be tested with ::rtl::math::approxEqual!
1024 bool bValid
= false;
1028 break; // Always sal_False
1030 bValid
= ::rtl::math::approxEqual( nArg
, nComp1
);
1032 case SC_COND_NOTEQUAL
:
1033 bValid
= !::rtl::math::approxEqual( nArg
, nComp1
);
1035 case SC_COND_GREATER
:
1036 bValid
= ( nArg
> nComp1
) && !::rtl::math::approxEqual( nArg
, nComp1
);
1038 case SC_COND_EQGREATER
:
1039 bValid
= ( nArg
>= nComp1
) || ::rtl::math::approxEqual( nArg
, nComp1
);
1042 bValid
= ( nArg
< nComp1
) && !::rtl::math::approxEqual( nArg
, nComp1
);
1044 case SC_COND_EQLESS
:
1045 bValid
= ( nArg
<= nComp1
) || ::rtl::math::approxEqual( nArg
, nComp1
);
1047 case SC_COND_BETWEEN
:
1048 bValid
= ( nArg
>= nComp1
&& nArg
<= nComp2
) ||
1049 ::rtl::math::approxEqual( nArg
, nComp1
) || ::rtl::math::approxEqual( nArg
, nComp2
);
1051 case SC_COND_NOTBETWEEN
:
1052 bValid
= ( nArg
< nComp1
|| nArg
> nComp2
) &&
1053 !::rtl::math::approxEqual( nArg
, nComp1
) && !::rtl::math::approxEqual( nArg
, nComp2
);
1055 case SC_COND_DUPLICATE
:
1056 case SC_COND_NOTDUPLICATE
:
1059 bValid
= IsDuplicate( nArg
, OUString() );
1060 if( eOp
== SC_COND_NOTDUPLICATE
)
1064 case SC_COND_DIRECT
:
1065 bValid
= !::rtl::math::approxEqual( nComp1
, 0.0 );
1068 bValid
= IsTopNElement( nArg
);
1070 case SC_COND_BOTTOM10
:
1071 bValid
= IsBottomNElement( nArg
);
1073 case SC_COND_TOP_PERCENT
:
1074 bValid
= IsTopNPercent( nArg
);
1076 case SC_COND_BOTTOM_PERCENT
:
1077 bValid
= IsBottomNPercent( nArg
);
1079 case SC_COND_ABOVE_AVERAGE
:
1080 case SC_COND_ABOVE_EQUAL_AVERAGE
:
1081 bValid
= IsAboveAverage( nArg
, eOp
== SC_COND_ABOVE_EQUAL_AVERAGE
);
1083 case SC_COND_BELOW_AVERAGE
:
1084 case SC_COND_BELOW_EQUAL_AVERAGE
:
1085 bValid
= IsBelowAverage( nArg
, eOp
== SC_COND_BELOW_EQUAL_AVERAGE
);
1088 case SC_COND_NOERROR
:
1089 bValid
= IsError( rPos
);
1090 if( eOp
== SC_COND_NOERROR
)
1093 case SC_COND_BEGINS_WITH
:
1094 if(aStrVal1
.isEmpty())
1096 OUString aStr
= OUString::number(nVal1
);
1097 OUString aStr2
= OUString::number(nArg
);
1098 bValid
= aStr2
.startsWith(aStr
);
1102 OUString aStr2
= OUString::number(nArg
);
1103 bValid
= aStr2
.startsWith(aStrVal1
);
1106 case SC_COND_ENDS_WITH
:
1107 if(aStrVal1
.isEmpty())
1109 OUString aStr
= OUString::number(nVal1
);
1110 OUString aStr2
= OUString::number(nArg
);
1111 bValid
= !aStr2
.endsWith(aStr
);
1115 OUString aStr2
= OUString::number(nArg
);
1116 bValid
= !aStr2
.endsWith(aStrVal1
);
1119 case SC_COND_CONTAINS_TEXT
:
1120 case SC_COND_NOT_CONTAINS_TEXT
:
1121 if(aStrVal1
.isEmpty())
1123 OUString aStr
= OUString::number(nVal1
);
1124 OUString aStr2
= OUString::number(nArg
);
1125 bValid
= aStr2
.indexOf(aStr
) != -1;
1129 OUString aStr2
= OUString::number(nArg
);
1130 bValid
= aStr2
.indexOf(aStrVal1
) != -1;
1133 if( eOp
== SC_COND_NOT_CONTAINS_TEXT
)
1137 SAL_WARN("sc", "unknown operation at ScConditionEntry");
1143 bool ScConditionEntry::IsValidStr( const OUString
& rArg
, const ScAddress
& rPos
) const
1145 bool bValid
= false;
1146 // Interpret must already have been called
1147 if ( eOp
== SC_COND_DIRECT
) // Formula is independent from the content
1148 return !::rtl::math::approxEqual( nVal1
, 0.0 );
1150 if ( eOp
== SC_COND_DUPLICATE
|| eOp
== SC_COND_NOTDUPLICATE
)
1152 if( pCondFormat
&& !rArg
.isEmpty() )
1154 bValid
= IsDuplicate( 0.0, rArg
);
1155 if( eOp
== SC_COND_NOTDUPLICATE
)
1161 // If number contains condition, always false, except for "not equal".
1162 if ( !bIsStr1
&& (eOp
!= SC_COND_ERROR
&& eOp
!= SC_COND_NOERROR
) )
1163 return ( eOp
== SC_COND_NOTEQUAL
);
1164 if ( eOp
== SC_COND_BETWEEN
|| eOp
== SC_COND_NOTBETWEEN
)
1168 OUString
aUpVal1( aStrVal1
); //TODO: As a member? (Also set in Interpret)
1169 OUString
aUpVal2( aStrVal2
);
1171 if ( eOp
== SC_COND_BETWEEN
|| eOp
== SC_COND_NOTBETWEEN
)
1172 if (ScGlobal::GetCollator()->compareString( aUpVal1
, aUpVal2
) > 0)
1174 // Right order for value range
1175 OUString
aTemp( aUpVal1
); aUpVal1
= aUpVal2
; aUpVal2
= aTemp
;
1181 bValid
= (ScGlobal::GetCollator()->compareString(
1182 rArg
, aUpVal1
) == 0);
1184 case SC_COND_NOTEQUAL
:
1185 bValid
= (ScGlobal::GetCollator()->compareString(
1186 rArg
, aUpVal1
) != 0);
1188 case SC_COND_TOP_PERCENT
:
1189 case SC_COND_BOTTOM_PERCENT
:
1191 case SC_COND_BOTTOM10
:
1192 case SC_COND_ABOVE_AVERAGE
:
1193 case SC_COND_BELOW_AVERAGE
:
1196 case SC_COND_NOERROR
:
1197 bValid
= IsError( rPos
);
1198 if(eOp
== SC_COND_NOERROR
)
1201 case SC_COND_BEGINS_WITH
:
1202 bValid
= rArg
.startsWith(aUpVal1
);
1204 case SC_COND_ENDS_WITH
:
1205 bValid
= rArg
.endsWith(aUpVal1
);
1207 case SC_COND_CONTAINS_TEXT
:
1208 case SC_COND_NOT_CONTAINS_TEXT
:
1209 bValid
= rArg
.indexOf(aUpVal1
) != -1;
1210 if(eOp
== SC_COND_NOT_CONTAINS_TEXT
)
1215 sal_Int32 nCompare
= ScGlobal::GetCollator()->compareString(
1219 case SC_COND_GREATER
:
1220 bValid
= ( nCompare
> 0 );
1222 case SC_COND_EQGREATER
:
1223 bValid
= ( nCompare
>= 0 );
1226 bValid
= ( nCompare
< 0 );
1228 case SC_COND_EQLESS
:
1229 bValid
= ( nCompare
<= 0 );
1231 case SC_COND_BETWEEN
:
1232 case SC_COND_NOTBETWEEN
:
1233 // Test for NOTBETWEEN:
1234 bValid
= ( nCompare
< 0 ||
1235 ScGlobal::GetCollator()->compareString( rArg
,
1237 if ( eOp
== SC_COND_BETWEEN
)
1240 // SC_COND_DIRECT already handled above
1242 SAL_WARN("sc", "unknown operation in ScConditionEntry");
1251 bool ScConditionEntry::IsCellValid( ScRefCellValue
& rCell
, const ScAddress
& rPos
) const
1253 const_cast<ScConditionEntry
*>(this)->Interpret(rPos
); // Evaluate formula
1257 bool bVal
= lcl_GetCellContent( rCell
, bIsStr1
, nArg
, aArgStr
, mpDoc
);
1259 return IsValid( nArg
, rPos
);
1261 return IsValidStr( aArgStr
, rPos
);
1264 OUString
ScConditionEntry::GetExpression( const ScAddress
& rCursor
, sal_uInt16 nIndex
,
1266 const FormulaGrammar::Grammar eGrammar
) const
1268 assert( nIndex
<= 1);
1271 if ( FormulaGrammar::isEnglish( eGrammar
) && nNumFmt
== 0 )
1272 nNumFmt
= mpDoc
->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US
);
1278 ScCompiler
aComp(mpDoc
, rCursor
, *pFormula1
);
1279 aComp
.SetGrammar(eGrammar
);
1280 OUStringBuffer aBuffer
;
1281 aComp
.CreateStringFromTokenArray( aBuffer
);
1282 aRet
= aBuffer
.makeStringAndClear();
1291 mpDoc
->GetFormatTable()->GetInputLineString(nVal1
, nNumFmt
, aRet
);
1293 else if ( nIndex
==1 )
1297 ScCompiler
aComp(mpDoc
, rCursor
, *pFormula2
);
1298 aComp
.SetGrammar(eGrammar
);
1299 OUStringBuffer aBuffer
;
1300 aComp
.CreateStringFromTokenArray( aBuffer
);
1301 aRet
= aBuffer
.makeStringAndClear();
1310 mpDoc
->GetFormatTable()->GetInputLineString(nVal2
, nNumFmt
, aRet
);
1316 ScTokenArray
* ScConditionEntry::CreateTokenArry( sal_uInt16 nIndex
) const
1318 assert(nIndex
<= 1);
1319 ScTokenArray
* pRet
= NULL
;
1325 pRet
= new ScTokenArray( *pFormula1
);
1328 pRet
= new ScTokenArray();
1331 svl::SharedStringPool
& rSPool
= mpDoc
->GetSharedStringPool();
1332 pRet
->AddString(rSPool
.intern(aStrVal1
));
1335 pRet
->AddDouble( nVal1
);
1338 else if ( nIndex
==1 )
1341 pRet
= new ScTokenArray( *pFormula2
);
1344 pRet
= new ScTokenArray();
1347 svl::SharedStringPool
& rSPool
= mpDoc
->GetSharedStringPool();
1348 pRet
->AddString(rSPool
.intern(aStrVal2
));
1351 pRet
->AddDouble( nVal2
);
1358 void ScConditionEntry::SourceChanged( const ScAddress
& rChanged
)
1360 for (sal_uInt16 nPass
= 0; nPass
< 2; nPass
++)
1362 ScTokenArray
* pFormula
= nPass
? pFormula2
: pFormula1
;
1366 formula::FormulaToken
* t
;
1367 while ( ( t
= pFormula
->GetNextReference() ) != NULL
)
1369 SingleDoubleRefProvider
aProv( *t
);
1370 if ( aProv
.Ref1
.IsColRel() || aProv
.Ref1
.IsRowRel() || aProv
.Ref1
.IsTabRel() ||
1371 aProv
.Ref2
.IsColRel() || aProv
.Ref2
.IsRowRel() || aProv
.Ref2
.IsTabRel() )
1373 // Absolute must be reached, relative determines range
1382 if ( aProv
.Ref1
.IsColRel() )
1383 nCol2
= rChanged
.Col() - aProv
.Ref1
.Col();
1386 bHit
&= (rChanged
.Col() >= aProv
.Ref1
.Col());
1389 if ( aProv
.Ref1
.IsRowRel() )
1390 nRow2
= rChanged
.Row() - aProv
.Ref1
.Row();
1393 bHit
&= ( rChanged
.Row() >= aProv
.Ref1
.Row() );
1396 if ( aProv
.Ref1
.IsTabRel() )
1397 nTab2
= rChanged
.Tab() - aProv
.Ref1
.Tab();
1400 bHit
&= (rChanged
.Tab() >= aProv
.Ref1
.Tab());
1404 if ( aProv
.Ref2
.IsColRel() )
1405 nCol1
= rChanged
.Col() - aProv
.Ref2
.Col();
1408 bHit
&= ( rChanged
.Col() <= aProv
.Ref2
.Col() );
1411 if ( aProv
.Ref2
.IsRowRel() )
1412 nRow1
= rChanged
.Row() - aProv
.Ref2
.Row();
1415 bHit
&= (rChanged
.Row() <= aProv
.Ref2
.Row());
1418 if ( aProv
.Ref2
.IsTabRel() )
1419 nTab1
= rChanged
.Tab() - aProv
.Ref2
.Tab();
1422 bHit
&= (rChanged
.Tab() <= aProv
.Ref2
.Tab());
1429 ScRange
aPaint( nCol1
,nRow1
,nTab1
, nCol2
,nRow2
,nTab2
);
1431 // No paint if it's the cell itself
1432 if ( aPaint
.IsValid() && (aPaint
.aStart
!= rChanged
|| aPaint
.aEnd
!= rChanged
))
1433 DataChanged( &aPaint
);
1442 * Return a position that's adjusted to allow textual representation
1443 * of expressions if possible
1445 ScAddress
ScConditionEntry::GetValidSrcPos() const
1447 SCTAB nMinTab
= aSrcPos
.Tab();
1448 SCTAB nMaxTab
= nMinTab
;
1450 for (sal_uInt16 nPass
= 0; nPass
< 2; nPass
++)
1452 ScTokenArray
* pFormula
= nPass
? pFormula2
: pFormula1
;
1456 formula::FormulaToken
* t
;
1457 while ( ( t
= pFormula
->GetNextReference() ) != NULL
)
1459 ScSingleRefData
& rRef1
= *t
->GetSingleRef();
1460 ScAddress aAbs
= rRef1
.toAbs(aSrcPos
);
1461 if (!rRef1
.IsTabDeleted())
1463 if (aAbs
.Tab() < nMinTab
)
1464 nMinTab
= aAbs
.Tab();
1465 if (aAbs
.Tab() > nMaxTab
)
1466 nMaxTab
= aAbs
.Tab();
1468 if ( t
->GetType() == svDoubleRef
)
1470 ScSingleRefData
& rRef2
= t
->GetDoubleRef()->Ref2
;
1471 aAbs
= rRef2
.toAbs(aSrcPos
);
1472 if (!rRef2
.IsTabDeleted())
1474 if (aAbs
.Tab() < nMinTab
)
1475 nMinTab
= aAbs
.Tab();
1476 if (aAbs
.Tab() > nMaxTab
)
1477 nMaxTab
= aAbs
.Tab();
1484 ScAddress aValidPos
= aSrcPos
;
1485 SCTAB nTabCount
= mpDoc
->GetTableCount();
1486 if ( nMaxTab
>= nTabCount
&& nMinTab
> 0 )
1487 aValidPos
.SetTab( aSrcPos
.Tab() - nMinTab
); // so the lowest tab ref will be on 0
1489 if ( aValidPos
.Tab() >= nTabCount
)
1490 aValidPos
.SetTab( nTabCount
- 1 ); // ensure a valid position even if some references will be invalid
1495 void ScConditionEntry::DataChanged( const ScRange
* /* pModified */ ) const
1497 //FIXME: Nothing so far
1500 bool ScConditionEntry::MarkUsedExternalReferences() const
1502 bool bAllMarked
= false;
1503 for (sal_uInt16 nPass
= 0; !bAllMarked
&& nPass
< 2; nPass
++)
1505 ScTokenArray
* pFormula
= nPass
? pFormula2
: pFormula1
;
1507 bAllMarked
= mpDoc
->MarkUsedExternalReferences(*pFormula
, aSrcPos
);
1512 ScFormatEntry
* ScConditionEntry::Clone(ScDocument
* pDoc
) const
1514 return new ScConditionEntry(pDoc
, *this);
1517 ScConditionMode
ScConditionEntry::GetModeFromApi(sal_Int32 nOperation
)
1519 ScConditionMode eMode
= SC_COND_NONE
;
1522 case com::sun::star::sheet::ConditionOperator2::EQUAL
:
1523 eMode
= SC_COND_EQUAL
;
1525 case com::sun::star::sheet::ConditionOperator2::LESS
:
1526 eMode
= SC_COND_LESS
;
1528 case com::sun::star::sheet::ConditionOperator2::GREATER
:
1529 eMode
= SC_COND_GREATER
;
1531 case com::sun::star::sheet::ConditionOperator2::LESS_EQUAL
:
1532 eMode
= SC_COND_EQLESS
;
1534 case com::sun::star::sheet::ConditionOperator2::GREATER_EQUAL
:
1535 eMode
= SC_COND_EQGREATER
;
1537 case com::sun::star::sheet::ConditionOperator2::NOT_EQUAL
:
1538 eMode
= SC_COND_NOTEQUAL
;
1540 case com::sun::star::sheet::ConditionOperator2::BETWEEN
:
1541 eMode
= SC_COND_BETWEEN
;
1543 case com::sun::star::sheet::ConditionOperator2::NOT_BETWEEN
:
1544 eMode
= SC_COND_NOTBETWEEN
;
1546 case com::sun::star::sheet::ConditionOperator2::FORMULA
:
1547 eMode
= SC_COND_DIRECT
;
1549 case com::sun::star::sheet::ConditionOperator2::DUPLICATE
:
1550 eMode
= SC_COND_DUPLICATE
;
1552 case com::sun::star::sheet::ConditionOperator2::NOT_DUPLICATE
:
1553 eMode
= SC_COND_NOTDUPLICATE
;
1561 void ScConditionEntry::startRendering()
1566 void ScConditionEntry::endRendering()
1571 ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper
,
1572 const OUString
& rExpr1
, const OUString
& rExpr2
,
1573 ScDocument
* pDocument
, const ScAddress
& rPos
,
1574 const OUString
& rStyle
,
1575 const OUString
& rExprNmsp1
, const OUString
& rExprNmsp2
,
1576 FormulaGrammar::Grammar eGrammar1
,
1577 FormulaGrammar::Grammar eGrammar2
) :
1578 ScConditionEntry( eOper
, rExpr1
, rExpr2
, pDocument
, rPos
, rExprNmsp1
, rExprNmsp2
, eGrammar1
, eGrammar2
),
1579 aStyleName( rStyle
)
1583 ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper
,
1584 const ScTokenArray
* pArr1
, const ScTokenArray
* pArr2
,
1585 ScDocument
* pDocument
, const ScAddress
& rPos
,
1586 const OUString
& rStyle
) :
1587 ScConditionEntry( eOper
, pArr1
, pArr2
, pDocument
, rPos
),
1588 aStyleName( rStyle
)
1592 ScCondFormatEntry::ScCondFormatEntry( const ScCondFormatEntry
& r
) :
1593 ScConditionEntry( r
),
1594 aStyleName( r
.aStyleName
)
1598 ScCondFormatEntry::ScCondFormatEntry( ScDocument
* pDocument
, const ScCondFormatEntry
& r
) :
1599 ScConditionEntry( pDocument
, r
),
1600 aStyleName( r
.aStyleName
)
1604 bool ScCondFormatEntry::operator== ( const ScCondFormatEntry
& r
) const
1606 return ScConditionEntry::operator==( r
) &&
1607 aStyleName
== r
.aStyleName
;
1610 ScCondFormatEntry::~ScCondFormatEntry()
1614 void ScCondFormatEntry::DataChanged( const ScRange
* pModified
) const
1617 pCondFormat
->DoRepaint( pModified
);
1620 ScFormatEntry
* ScCondFormatEntry::Clone( ScDocument
* pDoc
) const
1622 return new ScCondFormatEntry( pDoc
, *this );
1625 ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument
* pDoc
)
1626 : ScFormatEntry( pDoc
)
1627 , meType(condformat::TODAY
)
1631 ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument
* pDoc
, const ScCondDateFormatEntry
& rFormat
):
1632 ScFormatEntry( pDoc
),
1633 meType( rFormat
.meType
),
1634 maStyleName( rFormat
.maStyleName
)
1638 bool ScCondDateFormatEntry::IsValid( const ScAddress
& rPos
) const
1640 CellType eCellType
= mpDoc
->GetCellType(rPos
);
1642 if (eCellType
== CELLTYPE_NONE
)
1646 if (eCellType
!= CELLTYPE_VALUE
&& eCellType
!= CELLTYPE_FORMULA
)
1647 // non-numerical cell.
1651 mpCache
.reset( new Date( Date::SYSTEM
) );
1653 const Date
& rActDate
= *mpCache
;
1654 SvNumberFormatter
* pFormatter
= mpDoc
->GetFormatTable();
1655 long nCurrentDate
= rActDate
- *(pFormatter
->GetNullDate());
1657 double nVal
= mpDoc
->GetValue(rPos
);
1658 long nCellDate
= (long) ::rtl::math::approxFloor(nVal
);
1659 Date aCellDate
= *(pFormatter
->GetNullDate());
1660 aCellDate
+= (long) ::rtl::math::approxFloor(nVal
);
1664 case condformat::TODAY
:
1665 if( nCurrentDate
== nCellDate
)
1668 case condformat::TOMORROW
:
1669 if( nCurrentDate
== nCellDate
-1 )
1672 case condformat::YESTERDAY
:
1673 if( nCurrentDate
== nCellDate
+ 1)
1676 case condformat::LAST7DAYS
:
1677 if( nCurrentDate
>= nCellDate
&& nCurrentDate
- 7 < nCellDate
)
1680 case condformat::LASTWEEK
:
1681 if( rActDate
.GetDayOfWeek() != SUNDAY
)
1683 Date
aBegin(rActDate
- 8 - static_cast<long>(rActDate
.GetDayOfWeek()));
1684 Date
aEnd(rActDate
- 2 -static_cast<long>(rActDate
.GetDayOfWeek()));
1685 return aCellDate
.IsBetween( aBegin
, aEnd
);
1689 Date
aBegin(rActDate
- 8);
1690 Date
aEnd(rActDate
- 1);
1691 return aCellDate
.IsBetween( aBegin
, aEnd
);
1694 case condformat::THISWEEK
:
1695 if( rActDate
.GetDayOfWeek() != SUNDAY
)
1697 Date
aBegin(rActDate
- 1 - static_cast<long>(rActDate
.GetDayOfWeek()));
1698 Date
aEnd(rActDate
+ 5 - static_cast<long>(rActDate
.GetDayOfWeek()));
1699 return aCellDate
.IsBetween( aBegin
, aEnd
);
1703 Date
aEnd( rActDate
+ 6);
1704 return aCellDate
.IsBetween( rActDate
, aEnd
);
1707 case condformat::NEXTWEEK
:
1708 if( rActDate
.GetDayOfWeek() != SUNDAY
)
1710 return aCellDate
.IsBetween( rActDate
+ 6 - static_cast<long>(rActDate
.GetDayOfWeek()), rActDate
+ 12 - static_cast<long>(rActDate
.GetDayOfWeek()) );
1714 return aCellDate
.IsBetween( rActDate
+ 7, rActDate
+ 13 );
1717 case condformat::LASTMONTH
:
1718 if( rActDate
.GetMonth() == 1 )
1720 if( aCellDate
.GetMonth() == 12 && rActDate
.GetYear() == aCellDate
.GetYear() + 1 )
1723 else if( rActDate
.GetYear() == aCellDate
.GetYear() )
1725 if( rActDate
.GetMonth() == aCellDate
.GetMonth() + 1)
1729 case condformat::THISMONTH
:
1730 if( rActDate
.GetYear() == aCellDate
.GetYear() )
1732 if( rActDate
.GetMonth() == aCellDate
.GetMonth() )
1736 case condformat::NEXTMONTH
:
1737 if( rActDate
.GetMonth() == 12 )
1739 if( aCellDate
.GetMonth() == 1 && rActDate
.GetYear() == aCellDate
.GetYear() - 1 )
1742 else if( rActDate
.GetYear() == aCellDate
.GetYear() )
1744 if( rActDate
.GetMonth() == aCellDate
.GetMonth() - 1)
1748 case condformat::LASTYEAR
:
1749 if( rActDate
.GetYear() == aCellDate
.GetYear() + 1 )
1752 case condformat::THISYEAR
:
1753 if( rActDate
.GetYear() == aCellDate
.GetYear() )
1756 case condformat::NEXTYEAR
:
1757 if( rActDate
.GetYear() == aCellDate
.GetYear() - 1 )
1765 void ScCondDateFormatEntry::SetDateType( condformat::ScCondFormatDateType eType
)
1770 void ScCondDateFormatEntry::SetStyleName( const OUString
& rStyleName
)
1772 maStyleName
= rStyleName
;
1775 ScFormatEntry
* ScCondDateFormatEntry::Clone( ScDocument
* pDoc
) const
1777 return new ScCondDateFormatEntry( pDoc
, *this );
1780 bool ScCondDateFormatEntry::operator==( const ScFormatEntry
& r
) const
1782 if(r
.GetType() != condformat::DATE
)
1785 const ScCondDateFormatEntry
& rEntry
= static_cast<const ScCondDateFormatEntry
&>(r
);
1787 if(rEntry
.meType
!= meType
)
1790 return rEntry
.maStyleName
== maStyleName
;
1793 void ScCondDateFormatEntry::startRendering()
1798 void ScCondDateFormatEntry::endRendering()
1803 ScConditionalFormat::ScConditionalFormat(sal_uInt32 nNewKey
, ScDocument
* pDocument
) :
1809 ScConditionalFormat
* ScConditionalFormat::Clone(ScDocument
* pNewDoc
) const
1811 // Real copy of the formula (for Ref Undo/between documents)
1815 ScConditionalFormat
* pNew
= new ScConditionalFormat(nKey
, pNewDoc
);
1817 for (CondFormatContainer::const_iterator itr
= maEntries
.begin(); itr
!= maEntries
.end(); ++itr
)
1819 ScFormatEntry
* pNewEntry
= itr
->Clone(pNewDoc
);
1820 pNew
->maEntries
.push_back( pNewEntry
);
1821 pNewEntry
->SetParent(pNew
);
1823 pNew
->SetRange( maRanges
);
1828 bool ScConditionalFormat::EqualEntries( const ScConditionalFormat
& r
) const
1830 if( size() != r
.size())
1833 //TODO: Test for same entries in reverse order?
1834 for (sal_uInt16 i
=0; i
<size(); i
++)
1835 if ( ! (maEntries
== r
.maEntries
) )
1838 // right now don't check for same range
1839 // we only use this method to merge same conditional formats from
1840 // old ODF data structure
1844 void ScConditionalFormat::SetRange( const ScRangeList
& rRanges
)
1847 SAL_WARN_IF(maRanges
.empty(), "sc", "the conditional format range is empty! will result in a crash later!");
1850 void ScConditionalFormat::AddEntry( ScFormatEntry
* pNew
)
1852 maEntries
.push_back(pNew
);
1853 pNew
->SetParent(this);
1856 void ScConditionalFormat::RemoveEntry(size_t n
)
1858 if (n
< maEntries
.size())
1860 maEntries
.erase(maEntries
.begin() + n
);
1865 bool ScConditionalFormat::IsEmpty() const
1867 return maEntries
.empty();
1870 size_t ScConditionalFormat::size() const
1872 return maEntries
.size();
1875 ScConditionalFormat::~ScConditionalFormat()
1879 const ScFormatEntry
* ScConditionalFormat::GetEntry( sal_uInt16 nPos
) const
1881 if ( nPos
< size() )
1882 return &maEntries
[nPos
];
1887 const OUString
& ScConditionalFormat::GetCellStyle( ScRefCellValue
& rCell
, const ScAddress
& rPos
) const
1889 for (CondFormatContainer::const_iterator itr
= maEntries
.begin(); itr
!= maEntries
.end(); ++itr
)
1891 if(itr
->GetType() == condformat::CONDITION
)
1893 const ScCondFormatEntry
& rEntry
= static_cast<const ScCondFormatEntry
&>(*itr
);
1894 if (rEntry
.IsCellValid(rCell
, rPos
))
1895 return rEntry
.GetStyle();
1897 else if(itr
->GetType() == condformat::DATE
)
1899 const ScCondDateFormatEntry
& rEntry
= static_cast<const ScCondDateFormatEntry
&>(*itr
);
1900 if (rEntry
.IsValid( rPos
))
1901 return rEntry
.GetStyleName();
1905 return EMPTY_OUSTRING
;
1908 ScCondFormatData
ScConditionalFormat::GetData( ScRefCellValue
& rCell
, const ScAddress
& rPos
) const
1910 ScCondFormatData aData
;
1911 for(CondFormatContainer::const_iterator itr
= maEntries
.begin(); itr
!= maEntries
.end(); ++itr
)
1913 if(itr
->GetType() == condformat::CONDITION
&& aData
.aStyleName
.isEmpty())
1915 const ScCondFormatEntry
& rEntry
= static_cast<const ScCondFormatEntry
&>(*itr
);
1916 if (rEntry
.IsCellValid(rCell
, rPos
))
1917 aData
.aStyleName
= rEntry
.GetStyle();
1919 else if(itr
->GetType() == condformat::COLORSCALE
&& !aData
.pColorScale
)
1921 const ScColorScaleFormat
& rEntry
= static_cast<const ScColorScaleFormat
&>(*itr
);
1922 aData
.pColorScale
= rEntry
.GetColor(rPos
);
1924 else if(itr
->GetType() == condformat::DATABAR
&& !aData
.pDataBar
)
1926 const ScDataBarFormat
& rEntry
= static_cast<const ScDataBarFormat
&>(*itr
);
1927 aData
.pDataBar
= rEntry
.GetDataBarInfo(rPos
);
1929 else if(itr
->GetType() == condformat::ICONSET
&& !aData
.pIconSet
)
1931 const ScIconSetFormat
& rEntry
= static_cast<const ScIconSetFormat
&>(*itr
);
1932 aData
.pIconSet
= rEntry
.GetIconSetInfo(rPos
);
1934 else if(itr
->GetType() == condformat::DATE
&& aData
.aStyleName
.isEmpty())
1936 const ScCondDateFormatEntry
& rEntry
= static_cast<const ScCondDateFormatEntry
&>(*itr
);
1937 if ( rEntry
.IsValid( rPos
) )
1938 aData
.aStyleName
= rEntry
.GetStyleName();
1944 void ScConditionalFormat::DoRepaint( const ScRange
* pModified
)
1948 if(maRanges
.Intersects(*pModified
))
1949 pDoc
->RepaintRange(*pModified
);
1953 // all conditional format cells
1954 pDoc
->RepaintRange( maRanges
);
1958 void ScConditionalFormat::CompileAll()
1960 for(CondFormatContainer::iterator itr
= maEntries
.begin(); itr
!= maEntries
.end(); ++itr
)
1961 if(itr
->GetType() == condformat::CONDITION
)
1962 static_cast<ScCondFormatEntry
&>(*itr
).CompileAll();
1965 void ScConditionalFormat::CompileXML()
1967 for(CondFormatContainer::iterator itr
= maEntries
.begin(); itr
!= maEntries
.end(); ++itr
)
1968 if(itr
->GetType() == condformat::CONDITION
)
1969 static_cast<ScCondFormatEntry
&>(*itr
).CompileXML();
1972 void ScConditionalFormat::UpdateReference( sc::RefUpdateContext
& rCxt
, bool bCopyAsMove
)
1974 for(CondFormatContainer::iterator itr
= maEntries
.begin(); itr
!= maEntries
.end(); ++itr
)
1975 itr
->UpdateReference(rCxt
);
1977 if (rCxt
.meMode
== URM_COPY
&& bCopyAsMove
)
1978 maRanges
.UpdateReference(URM_MOVE
, pDoc
, rCxt
.maRange
, rCxt
.mnColDelta
, rCxt
.mnRowDelta
, rCxt
.mnTabDelta
);
1980 maRanges
.UpdateReference(rCxt
.meMode
, pDoc
, rCxt
.maRange
, rCxt
.mnColDelta
, rCxt
.mnRowDelta
, rCxt
.mnTabDelta
);
1983 void ScConditionalFormat::InsertRow(SCTAB nTab
, SCCOL nColStart
, SCCOL nColEnd
, SCROW nRowPos
, SCSIZE nSize
)
1985 maRanges
.InsertRow(nTab
, nColStart
, nColEnd
, nRowPos
, nSize
);
1988 void ScConditionalFormat::InsertCol(SCTAB nTab
, SCROW nRowStart
, SCROW nRowEnd
, SCCOL nColPos
, SCSIZE nSize
)
1990 maRanges
.InsertCol(nTab
, nRowStart
, nRowEnd
, nColPos
, nSize
);
1993 void ScConditionalFormat::UpdateInsertTab( sc::RefUpdateInsertTabContext
& rCxt
)
1995 for (size_t i
= 0, n
= maRanges
.size(); i
< n
; ++i
)
1997 // We assume that the start and end sheet indices are equal.
1998 ScRange
* pRange
= maRanges
[i
];
1999 SCTAB nTab
= pRange
->aStart
.Tab();
2001 if (nTab
< rCxt
.mnInsertPos
)
2005 pRange
->aStart
.IncTab(rCxt
.mnSheets
);
2006 pRange
->aEnd
.IncTab(rCxt
.mnSheets
);
2009 for (CondFormatContainer::iterator it
= maEntries
.begin(); it
!= maEntries
.end(); ++it
)
2010 it
->UpdateInsertTab(rCxt
);
2013 void ScConditionalFormat::UpdateDeleteTab( sc::RefUpdateDeleteTabContext
& rCxt
)
2015 for (size_t i
= 0, n
= maRanges
.size(); i
< n
; ++i
)
2017 // We assume that the start and end sheet indices are equal.
2018 ScRange
* pRange
= maRanges
[i
];
2019 SCTAB nTab
= pRange
->aStart
.Tab();
2021 if (nTab
< rCxt
.mnDeletePos
)
2022 // Left of the deleted sheet(s). Unaffected.
2025 if (nTab
<= rCxt
.mnDeletePos
+rCxt
.mnSheets
-1)
2027 // On the deleted sheet(s).
2028 pRange
->aStart
.SetTab(-1);
2029 pRange
->aEnd
.SetTab(-1);
2033 // Right of the deleted sheet(s). Adjust the sheet indices.
2034 pRange
->aStart
.IncTab(-1*rCxt
.mnSheets
);
2035 pRange
->aEnd
.IncTab(-1*rCxt
.mnSheets
);
2038 for (CondFormatContainer::iterator it
= maEntries
.begin(); it
!= maEntries
.end(); ++it
)
2039 it
->UpdateDeleteTab(rCxt
);
2042 void ScConditionalFormat::UpdateMoveTab( sc::RefUpdateMoveTabContext
& rCxt
)
2044 size_t n
= maRanges
.size();
2045 SCTAB nMinTab
= std::min
<SCTAB
>(rCxt
.mnOldPos
, rCxt
.mnNewPos
);
2046 SCTAB nMaxTab
= std::max
<SCTAB
>(rCxt
.mnOldPos
, rCxt
.mnNewPos
);
2047 for(size_t i
= 0; i
< n
; ++i
)
2049 ScRange
* pRange
= maRanges
[i
];
2050 SCTAB nTab
= pRange
->aStart
.Tab();
2051 if(nTab
< nMinTab
|| nTab
> nMaxTab
)
2056 if (nTab
== rCxt
.mnOldPos
)
2058 pRange
->aStart
.SetTab(rCxt
.mnNewPos
);
2059 pRange
->aEnd
.SetTab(rCxt
.mnNewPos
);
2063 if (rCxt
.mnNewPos
< rCxt
.mnOldPos
)
2065 pRange
->aStart
.IncTab();
2066 pRange
->aEnd
.IncTab();
2070 pRange
->aStart
.IncTab(-1);
2071 pRange
->aEnd
.IncTab(-1);
2075 for (CondFormatContainer::iterator it
= maEntries
.begin(); it
!= maEntries
.end(); ++it
)
2076 it
->UpdateMoveTab(rCxt
);
2079 void ScConditionalFormat::DeleteArea( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
2081 if (maRanges
.empty())
2084 SCTAB nTab
= maRanges
[0]->aStart
.Tab();
2085 maRanges
.DeleteArea( nCol1
, nRow1
, nTab
, nCol2
, nRow2
, nTab
);
2088 void ScConditionalFormat::RenameCellStyle(const OUString
& rOld
, const OUString
& rNew
)
2090 for(CondFormatContainer::iterator itr
= maEntries
.begin(); itr
!= maEntries
.end(); ++itr
)
2091 if(itr
->GetType() == condformat::CONDITION
)
2093 ScCondFormatEntry
& rFormat
= static_cast<ScCondFormatEntry
&>(*itr
);
2094 if(rFormat
.GetStyle() == rOld
)
2095 rFormat
.UpdateStyleName( rNew
);
2099 void ScConditionalFormat::SourceChanged( const ScAddress
& rAddr
)
2101 for(CondFormatContainer::iterator itr
= maEntries
.begin(); itr
!= maEntries
.end(); ++itr
)
2103 condformat::ScFormatEntryType eEntryType
= itr
->GetType();
2104 if( eEntryType
== condformat::CONDITION
)
2106 ScCondFormatEntry
& rFormat
= static_cast<ScCondFormatEntry
&>(*itr
);
2107 rFormat
.SourceChanged( rAddr
);
2109 else if( eEntryType
== condformat::COLORSCALE
||
2110 eEntryType
== condformat::DATABAR
||
2111 eEntryType
== condformat::ICONSET
)
2113 ScColorFormat
& rFormat
= static_cast<ScColorFormat
&>(*itr
);
2114 if(rFormat
.NeedsRepaint())
2116 // we need to repaint the whole range anyway
2124 bool ScConditionalFormat::MarkUsedExternalReferences() const
2126 bool bAllMarked
= false;
2127 for(CondFormatContainer::const_iterator itr
= maEntries
.begin(); itr
!= maEntries
.end() && !bAllMarked
; ++itr
)
2128 if(itr
->GetType() == condformat::CONDITION
)
2130 const ScCondFormatEntry
& rFormat
= static_cast<const ScCondFormatEntry
&>(*itr
);
2131 bAllMarked
= rFormat
.MarkUsedExternalReferences();
2137 void ScConditionalFormat::startRendering()
2139 for(CondFormatContainer::iterator itr
= maEntries
.begin(); itr
!= maEntries
.end(); ++itr
)
2141 itr
->startRendering();
2145 void ScConditionalFormat::endRendering()
2147 for(CondFormatContainer::iterator itr
= maEntries
.begin(); itr
!= maEntries
.end(); ++itr
)
2149 itr
->endRendering();
2153 ScConditionalFormatList::ScConditionalFormatList(const ScConditionalFormatList
& rList
)
2155 for(const_iterator itr
= rList
.begin(); itr
!= rList
.end(); ++itr
)
2156 InsertNew( itr
->Clone() );
2159 ScConditionalFormatList::ScConditionalFormatList(ScDocument
* pDoc
, const ScConditionalFormatList
& rList
)
2161 for(const_iterator itr
= rList
.begin(); itr
!= rList
.end(); ++itr
)
2162 InsertNew( itr
->Clone(pDoc
) );
2165 void ScConditionalFormatList::InsertNew( ScConditionalFormat
* pNew
)
2167 maConditionalFormats
.insert(pNew
);
2170 bool ScConditionalFormatList::operator==( const ScConditionalFormatList
& r
) const
2172 // For Ref Undo - internal variables are not compared
2173 sal_uInt16 nCount
= size();
2174 bool bEqual
= ( nCount
== r
.size() );
2175 const_iterator locIterator
= begin();
2176 for(const_iterator itr
= r
.begin(); itr
!= r
.end() && bEqual
; ++itr
, ++locIterator
)
2177 if ( !locIterator
->EqualEntries(*itr
) ) // Entries differ?
2183 ScConditionalFormat
* ScConditionalFormatList::GetFormat( sal_uInt32 nKey
)
2185 //FIXME: Binary search
2186 for( iterator itr
= begin(); itr
!= end(); ++itr
)
2187 if (itr
->GetKey() == nKey
)
2190 SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
2194 const ScConditionalFormat
* ScConditionalFormatList::GetFormat( sal_uInt32 nKey
) const
2196 //FIXME: Binary search
2197 for ( const_iterator itr
= begin(); itr
!= end(); ++itr
)
2198 if (itr
->GetKey() == nKey
)
2201 SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
2205 void ScConditionalFormatList::CompileAll()
2207 for( iterator itr
= begin(); itr
!= end(); ++itr
)
2211 void ScConditionalFormatList::CompileXML()
2213 for( iterator itr
= begin(); itr
!= end(); ++itr
)
2217 void ScConditionalFormatList::UpdateReference( sc::RefUpdateContext
& rCxt
)
2219 for( iterator itr
= begin(); itr
!= end(); ++itr
)
2220 itr
->UpdateReference(rCxt
);
2222 if (rCxt
.meMode
== URM_INSDEL
)
2224 // need to check which must be deleted
2229 void ScConditionalFormatList::InsertRow(SCTAB nTab
, SCCOL nColStart
, SCCOL nColEnd
, SCROW nRowPos
, SCSIZE nSize
)
2231 for(iterator it
= begin(), itEnd
= end(); it
!= itEnd
; ++it
)
2232 it
->InsertRow(nTab
, nColStart
, nColEnd
, nRowPos
, nSize
);
2235 void ScConditionalFormatList::InsertCol(SCTAB nTab
, SCROW nRowStart
, SCROW nRowEnd
, SCCOL nColPos
, SCSIZE nSize
)
2237 for(iterator it
= begin(), itEnd
= end(); it
!= itEnd
; ++it
)
2238 it
->InsertCol(nTab
, nRowStart
, nRowEnd
, nColPos
, nSize
);
2241 void ScConditionalFormatList::UpdateInsertTab( sc::RefUpdateInsertTabContext
& rCxt
)
2243 for (iterator it
= begin(); it
!= end(); ++it
)
2244 it
->UpdateInsertTab(rCxt
);
2247 void ScConditionalFormatList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext
& rCxt
)
2249 for (iterator it
= begin(); it
!= end(); ++it
)
2250 it
->UpdateDeleteTab(rCxt
);
2253 void ScConditionalFormatList::UpdateMoveTab( sc::RefUpdateMoveTabContext
& rCxt
)
2255 for (iterator it
= begin(); it
!= end(); ++it
)
2256 it
->UpdateMoveTab(rCxt
);
2259 void ScConditionalFormatList::RenameCellStyle( const OUString
& rOld
, const OUString
& rNew
)
2261 for( iterator itr
= begin(); itr
!= end(); ++itr
)
2262 itr
->RenameCellStyle(rOld
,rNew
);
2265 bool ScConditionalFormatList::CheckAllEntries()
2269 // need to check which must be deleted
2270 iterator itr
= begin();
2273 if(itr
->GetRange().empty())
2276 maConditionalFormats
.erase(itr
++);
2285 void ScConditionalFormatList::DeleteArea( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
2287 for( iterator itr
= begin(); itr
!= end(); ++itr
)
2288 itr
->DeleteArea( nCol1
, nRow1
, nCol2
, nRow2
);
2293 void ScConditionalFormatList::SourceChanged( const ScAddress
& rAddr
)
2295 for( iterator itr
= begin(); itr
!= end(); ++itr
)
2296 itr
->SourceChanged( rAddr
);
2299 ScConditionalFormatList::iterator
ScConditionalFormatList::begin()
2301 return maConditionalFormats
.begin();
2304 ScConditionalFormatList::const_iterator
ScConditionalFormatList::begin() const
2306 return maConditionalFormats
.begin();
2309 ScConditionalFormatList::iterator
ScConditionalFormatList::end()
2311 return maConditionalFormats
.end();
2314 ScConditionalFormatList::const_iterator
ScConditionalFormatList::end() const
2316 return maConditionalFormats
.end();
2319 size_t ScConditionalFormatList::size() const
2321 return maConditionalFormats
.size();
2324 bool ScConditionalFormatList::empty() const
2326 return maConditionalFormats
.empty();
2329 void ScConditionalFormatList::erase( sal_uLong nIndex
)
2331 for( iterator itr
= begin(); itr
!= end(); ++itr
)
2333 if( itr
->GetKey() == nIndex
)
2335 maConditionalFormats
.erase(itr
);
2341 void ScConditionalFormatList::startRendering()
2343 for(iterator itr
= begin(); itr
!= end(); ++itr
)
2345 itr
->startRendering();
2349 void ScConditionalFormatList::endRendering()
2351 for(iterator itr
= begin(); itr
!= end(); ++itr
)
2353 itr
->endRendering();
2357 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */