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 <svl/numformat.hxx>
22 #include <rtl/math.hxx>
23 #include <sal/log.hxx>
24 #include <unotools/collatorwrapper.hxx>
26 #include <com/sun/star/sheet/ConditionOperator2.hpp>
29 #include <conditio.hxx>
30 #include <formulacell.hxx>
31 #include <document.hxx>
32 #include <compiler.hxx>
33 #include <rangelst.hxx>
34 #include <rangenam.hxx>
35 #include <rangeutl.hxx>
36 #include <colorscale.hxx>
37 #include <cellvalue.hxx>
38 #include <editutil.hxx>
39 #include <tokenarray.hxx>
40 #include <fillinfo.hxx>
41 #include <refupdatecontext.hxx>
42 #include <formula/errorcodes.hxx>
43 #include <svl/sharedstring.hxx>
44 #include <svl/sharedstringpool.hxx>
49 using namespace formula
;
51 ScFormatEntry::ScFormatEntry(ScDocument
* pDoc
):
56 bool ScFormatEntry::operator==( const ScFormatEntry
& r
) const
58 return IsEqual(r
, false);
62 bool ScFormatEntry::IsEqual( const ScFormatEntry
& /*r*/, bool /*bIgnoreSrcPos*/ ) const
64 // By default, return false; this makes sense for all cases except ScConditionEntry
65 // As soon as databar and color scale are tested we need to think about the range
69 void ScFormatEntry::startRendering()
73 void ScFormatEntry::endRendering()
77 void ScFormatEntry::updateValues()
81 static bool lcl_HasRelRef( ScDocument
* pDoc
, const ScTokenArray
* pFormula
, sal_uInt16 nRecursion
= 0 )
85 FormulaTokenArrayPlainIterator
aIter( *pFormula
);
87 for( t
= aIter
.Next(); t
; t
= aIter
.Next() )
89 switch( t
->GetType() )
93 ScSingleRefData
& rRef2
= t
->GetDoubleRef()->Ref2
;
94 if ( rRef2
.IsColRel() || rRef2
.IsRowRel() || rRef2
.IsTabRel() )
101 ScSingleRefData
& rRef1
= *t
->GetSingleRef();
102 if ( rRef1
.IsColRel() || rRef1
.IsRowRel() || rRef1
.IsTabRel() )
109 if( t
->GetOpCode() == ocName
) // DB areas always absolute
110 if( ScRangeData
* pRangeData
= pDoc
->FindRangeNameBySheetAndIndex( t
->GetSheet(), t
->GetIndex()) )
111 if( (nRecursion
< 42) && lcl_HasRelRef( pDoc
, pRangeData
->GetCode(), nRecursion
+ 1 ) )
116 // #i34474# function result dependent on cell position
119 switch( t
->GetOpCode() )
121 case ocRow
: // ROW() returns own row index
122 case ocColumn
: // COLUMN() returns own column index
123 case ocSheet
: // SHEET() returns own sheet index
124 case ocCell
: // CELL() may return own cell address
128 // added to avoid warnings
136 // added to avoid warnings
146 void start_listen_to(ScFormulaListener
& rListener
, const ScTokenArray
* pTokens
, const ScRangeList
& rRangeList
)
148 size_t n
= rRangeList
.size();
149 for (size_t i
= 0; i
< n
; ++i
)
151 const ScRange
& rRange
= rRangeList
[i
];
152 rListener
.addTokenArray(pTokens
, rRange
);
158 void ScConditionEntry::StartListening()
163 mpRepaintTask
= std::make_unique
<RepaintInIdle
>(pCondFormat
);
164 const ScRangeList
& rRanges
= pCondFormat
->GetRange();
165 mpListener
->stopListening();
166 start_listen_to(*mpListener
, pFormula1
.get(), rRanges
);
167 start_listen_to(*mpListener
, pFormula2
.get(), rRanges
);
169 mpListener
->setCallback([&]() { mpRepaintTask
->Start();});
172 void ScConditionEntry::SetParent(ScConditionalFormat
* pParent
)
174 pCondFormat
= pParent
;
178 ScConditionEntry::ScConditionEntry( const ScConditionEntry
& r
) :
179 ScFormatEntry(r
.mpDoc
),
181 nOptions(r
.nOptions
),
184 aStrVal1(r
.aStrVal1
),
185 aStrVal2(r
.aStrVal2
),
186 aStrNmsp1(r
.aStrNmsp1
),
187 aStrNmsp2(r
.aStrNmsp2
),
188 eTempGrammar1(r
.eTempGrammar1
),
189 eTempGrammar2(r
.eTempGrammar2
),
193 aSrcString(r
.aSrcString
),
194 bRelRef1(r
.bRelRef1
),
195 bRelRef2(r
.bRelRef2
),
197 mpListener(new ScFormulaListener(*r
.mpDoc
)),
198 eConditionType( r
.eConditionType
),
199 pCondFormat(r
.pCondFormat
),
202 // ScTokenArray copy ctor creates a flat copy
204 pFormula1
.reset( new ScTokenArray( *r
.pFormula1
) );
206 pFormula2
.reset( new ScTokenArray( *r
.pFormula2
) );
209 // Formula cells are created at IsValid
212 ScConditionEntry::ScConditionEntry( ScDocument
& rDocument
, const ScConditionEntry
& r
) :
213 ScFormatEntry(&rDocument
),
215 nOptions(r
.nOptions
),
218 aStrVal1(r
.aStrVal1
),
219 aStrVal2(r
.aStrVal2
),
220 aStrNmsp1(r
.aStrNmsp1
),
221 aStrNmsp2(r
.aStrNmsp2
),
222 eTempGrammar1(r
.eTempGrammar1
),
223 eTempGrammar2(r
.eTempGrammar2
),
227 aSrcString(r
.aSrcString
),
228 bRelRef1(r
.bRelRef1
),
229 bRelRef2(r
.bRelRef2
),
231 mpListener(new ScFormulaListener(rDocument
)),
232 eConditionType( r
.eConditionType
),
233 pCondFormat(r
.pCondFormat
),
236 // Real copy of the formulas (for Ref Undo)
238 pFormula1
= r
.pFormula1
->Clone();
240 pFormula2
= r
.pFormula2
->Clone();
242 // Formula cells are created at IsValid
243 // TODO: But not in the Clipboard! So interpret beforehand!
246 ScConditionEntry::ScConditionEntry( ScConditionMode eOper
,
247 const OUString
& rExpr1
, const OUString
& rExpr2
, ScDocument
& rDocument
, const ScAddress
& rPos
,
248 const OUString
& rExprNmsp1
, const OUString
& rExprNmsp2
,
249 FormulaGrammar::Grammar eGrammar1
, FormulaGrammar::Grammar eGrammar2
,
251 ScFormatEntry(&rDocument
),
256 aStrNmsp1(rExprNmsp1
),
257 aStrNmsp2(rExprNmsp2
),
258 eTempGrammar1(eGrammar1
),
259 eTempGrammar2(eGrammar2
),
266 mpListener(new ScFormulaListener(rDocument
)),
267 eConditionType(eType
),
268 pCondFormat(nullptr),
271 Compile( rExpr1
, rExpr2
, rExprNmsp1
, rExprNmsp2
, eGrammar1
, eGrammar2
, false );
273 // Formula cells are created at IsValid
276 ScConditionEntry::ScConditionEntry( ScConditionMode eOper
,
277 const ScTokenArray
* pArr1
, const ScTokenArray
* pArr2
,
278 ScDocument
& rDocument
, const ScAddress
& rPos
) :
279 ScFormatEntry(&rDocument
),
284 eTempGrammar1(FormulaGrammar::GRAM_DEFAULT
),
285 eTempGrammar2(FormulaGrammar::GRAM_DEFAULT
),
292 mpListener(new ScFormulaListener(rDocument
)),
293 eConditionType(ScFormatEntry::Type::Condition
),
294 pCondFormat(nullptr),
299 pFormula1
.reset( new ScTokenArray( *pArr1
) );
300 SimplifyCompiledFormula( pFormula1
, nVal1
, bIsStr1
, aStrVal1
);
301 bRelRef1
= lcl_HasRelRef( mpDoc
, pFormula1
.get() );
305 pFormula2
.reset( new ScTokenArray( *pArr2
) );
306 SimplifyCompiledFormula( pFormula2
, nVal2
, bIsStr2
, aStrVal2
);
307 bRelRef2
= lcl_HasRelRef( mpDoc
, pFormula2
.get() );
312 // Formula cells are created at IsValid
315 ScConditionEntry::~ScConditionEntry()
319 void ScConditionEntry::SimplifyCompiledFormula( std::unique_ptr
<ScTokenArray
>& rFormula
,
324 if ( rFormula
->GetLen() != 1 )
327 // Single (constant number)?
328 FormulaToken
* pToken
= rFormula
->FirstToken();
329 if ( pToken
->GetOpCode() != ocPush
)
332 if ( pToken
->GetType() == svDouble
)
334 rVal
= pToken
->GetDouble();
335 rFormula
.reset(); // Do not remember as formula
337 else if ( pToken
->GetType() == svString
)
340 rStrVal
= pToken
->GetString().getString();
341 rFormula
.reset(); // Do not remember as formula
345 void ScConditionEntry::SetOperation(ScConditionMode eMode
)
350 void ScConditionEntry::Compile( const OUString
& rExpr1
, const OUString
& rExpr2
,
351 const OUString
& rExprNmsp1
, const OUString
& rExprNmsp2
,
352 FormulaGrammar::Grammar eGrammar1
, FormulaGrammar::Grammar eGrammar2
, bool bTextToReal
)
354 if ( !rExpr1
.isEmpty() || !rExpr2
.isEmpty() )
356 ScCompiler
aComp( *mpDoc
, aSrcPos
);
358 if ( !rExpr1
.isEmpty() )
361 aComp
.SetGrammar( eGrammar1
);
362 if ( mpDoc
->IsImportingXML() && !bTextToReal
)
364 // temporary formula string as string tokens
365 pFormula1
.reset( new ScTokenArray(*mpDoc
) );
366 pFormula1
->AssignXMLString( rExpr1
, rExprNmsp1
);
367 // bRelRef1 is set when the formula is compiled again (CompileXML)
371 pFormula1
= aComp
.CompileString( rExpr1
, rExprNmsp1
);
372 SimplifyCompiledFormula( pFormula1
, nVal1
, bIsStr1
, aStrVal1
);
373 bRelRef1
= lcl_HasRelRef( mpDoc
, pFormula1
.get() );
377 if ( !rExpr2
.isEmpty() )
380 aComp
.SetGrammar( eGrammar2
);
381 if ( mpDoc
->IsImportingXML() && !bTextToReal
)
383 // temporary formula string as string tokens
384 pFormula2
.reset( new ScTokenArray(*mpDoc
) );
385 pFormula2
->AssignXMLString( rExpr2
, rExprNmsp2
);
386 // bRelRef2 is set when the formula is compiled again (CompileXML)
390 pFormula2
= aComp
.CompileString( rExpr2
, rExprNmsp2
);
391 SimplifyCompiledFormula( pFormula2
, nVal2
, bIsStr2
, aStrVal2
);
392 bRelRef2
= lcl_HasRelRef( mpDoc
, pFormula2
.get() );
401 * Create formula cells
403 void ScConditionEntry::MakeCells( const ScAddress
& rPos
)
405 if ( mpDoc
->IsClipOrUndo() ) // Never calculate in the Clipboard!
408 if ( pFormula1
&& !pFCell1
&& !bRelRef1
)
410 // pFCell1 will hold a flat-copied ScTokenArray sharing ref-counted
411 // code tokens with pFormula1
412 pFCell1
.reset( new ScFormulaCell(*mpDoc
, rPos
, *pFormula1
) );
413 pFCell1
->SetFreeFlying(true);
414 pFCell1
->StartListeningTo( *mpDoc
);
417 if ( pFormula2
&& !pFCell2
&& !bRelRef2
)
419 // pFCell2 will hold a flat-copied ScTokenArray sharing ref-counted
420 // code tokens with pFormula2
421 pFCell2
.reset( new ScFormulaCell(*mpDoc
, rPos
, *pFormula2
) );
422 pFCell2
->SetFreeFlying(true);
423 pFCell2
->StartListeningTo( *mpDoc
);
427 void ScConditionEntry::SetIgnoreBlank(bool bSet
)
429 // The bit SC_COND_NOBLANKS is set if blanks are not ignored
432 nOptions
&= ~SC_COND_NOBLANKS
;
434 nOptions
|= SC_COND_NOBLANKS
;
437 void ScConditionEntry::SetCaseSensitive(bool bSet
)
439 // The bit SC_COND_CASESENS is set if validation compare is case sensitive
442 nOptions
|= SC_COND_CASESENS
;
444 nOptions
&= ~SC_COND_CASESENS
;
448 * Delete formula cells, so we re-compile 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
) & ScRefFlags::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 );
475 // Importing ocDde/ocWebservice?
477 mpDoc
->CheckLinkFormulaNeedingCheck(*pFormula1
);
479 mpDoc
->CheckLinkFormulaNeedingCheck(*pFormula2
);
482 void ScConditionEntry::SetSrcString( const OUString
& rNew
)
484 // aSrcString is only evaluated in CompileXML
485 SAL_WARN_IF( !mpDoc
->IsImportingXML(), "sc", "SetSrcString is only valid for XML import" );
490 void ScConditionEntry::SetFormula1( const ScTokenArray
& rArray
)
493 if( rArray
.GetLen() > 0 )
495 pFormula1
.reset( new ScTokenArray( rArray
) );
496 SimplifyCompiledFormula(pFormula1
, nVal1
, bIsStr1
, aStrVal1
);
497 bRelRef1
= lcl_HasRelRef( mpDoc
, pFormula1
.get() );
503 void ScConditionEntry::SetFormula2( const ScTokenArray
& rArray
)
506 if( rArray
.GetLen() > 0 )
508 pFormula2
.reset( new ScTokenArray( rArray
) );
509 SimplifyCompiledFormula(pFormula2
, nVal2
, bIsStr2
, aStrVal2
);
510 bRelRef2
= lcl_HasRelRef( mpDoc
, pFormula2
.get() );
516 void ScConditionEntry::UpdateReference( sc::RefUpdateContext
& rCxt
)
519 aSrcPos
= pCondFormat
->GetRange().Combine().aStart
;
520 ScAddress aOldSrcPos
= aSrcPos
;
521 bool bChangedPos
= false;
522 if (rCxt
.meMode
== URM_INSDEL
&& rCxt
.maRange
.Contains(aSrcPos
))
524 ScAddress
aErrorPos( ScAddress::UNINITIALIZED
);
525 if (!aSrcPos
.Move(rCxt
.mnColDelta
, rCxt
.mnRowDelta
, rCxt
.mnTabDelta
, aErrorPos
, *mpDoc
))
527 assert(!"can't move ScConditionEntry");
529 bChangedPos
= aSrcPos
!= aOldSrcPos
;
534 sc::RefUpdateResult aRes
;
538 aRes
= pFormula1
->AdjustReferenceOnShift(rCxt
, aOldSrcPos
);
541 aRes
= pFormula1
->AdjustReferenceOnMove(rCxt
, aOldSrcPos
, aSrcPos
);
547 if (aRes
.mbReferenceModified
|| bChangedPos
)
548 pFCell1
.reset(); // is created again in IsValid
553 sc::RefUpdateResult aRes
;
557 aRes
= pFormula2
->AdjustReferenceOnShift(rCxt
, aOldSrcPos
);
560 aRes
= pFormula2
->AdjustReferenceOnMove(rCxt
, aOldSrcPos
, aSrcPos
);
566 if (aRes
.mbReferenceModified
|| bChangedPos
)
567 pFCell2
.reset(); // is created again in IsValid
573 void ScConditionEntry::UpdateInsertTab( sc::RefUpdateInsertTabContext
& rCxt
)
577 pFormula1
->AdjustReferenceOnInsertedTab(rCxt
, aSrcPos
);
583 pFormula2
->AdjustReferenceOnInsertedTab(rCxt
, aSrcPos
);
587 ScRangeUpdater::UpdateInsertTab(aSrcPos
, rCxt
);
590 void ScConditionEntry::UpdateDeleteTab( sc::RefUpdateDeleteTabContext
& rCxt
)
594 pFormula1
->AdjustReferenceOnDeletedTab(rCxt
, aSrcPos
);
600 pFormula2
->AdjustReferenceOnDeletedTab(rCxt
, aSrcPos
);
604 ScRangeUpdater::UpdateDeleteTab(aSrcPos
, rCxt
);
608 void ScConditionEntry::UpdateMoveTab(sc::RefUpdateMoveTabContext
& rCxt
)
610 sc::RefUpdateResult aResFinal
;
611 aResFinal
.mnTab
= aSrcPos
.Tab();
614 sc::RefUpdateResult aRes
= pFormula1
->AdjustReferenceOnMovedTab(rCxt
, aSrcPos
);
615 if (aRes
.mbValueChanged
)
616 aResFinal
.mnTab
= aRes
.mnTab
;
622 sc::RefUpdateResult aRes
= pFormula2
->AdjustReferenceOnMovedTab(rCxt
, aSrcPos
);
623 if (aRes
.mbValueChanged
)
624 aResFinal
.mnTab
= aRes
.mnTab
;
628 if (aResFinal
.mnTab
!= aSrcPos
.Tab())
629 aSrcPos
.SetTab(aResFinal
.mnTab
);
634 static bool lcl_IsEqual( const std::unique_ptr
<ScTokenArray
>& pArr1
, const std::unique_ptr
<ScTokenArray
>& pArr2
)
636 // We only compare the non-RPN array
637 if ( pArr1
&& pArr2
)
638 return pArr1
->EqualTokens( pArr2
.get() );
640 return !pArr1
&& !pArr2
; // Both 0? -> the same
644 bool ScConditionEntry::IsEqual( const ScFormatEntry
& rOther
, bool bIgnoreSrcPos
) const
646 if (GetType() != rOther
.GetType())
649 const ScConditionEntry
& r
= static_cast<const ScConditionEntry
&>(rOther
);
651 if (eOp
!= r
.eOp
|| nOptions
!= r
.nOptions
652 || !lcl_IsEqual(pFormula1
, r
.pFormula1
) || !lcl_IsEqual(pFormula2
, r
.pFormula2
))
657 // for formulas, the reference positions must be compared, too
658 // (including aSrcString, for inserting the entries during XML import)
659 if ( ( pFormula1
|| pFormula2
) && ( aSrcPos
!= r
.aSrcPos
|| aSrcString
!= r
.aSrcString
) )
663 // If not formulas, compare values
664 if ( !pFormula1
&& ( nVal1
!= r
.nVal1
|| aStrVal1
!= r
.aStrVal1
|| bIsStr1
!= r
.bIsStr1
) )
666 if ( !pFormula2
&& ( nVal2
!= r
.nVal2
|| aStrVal2
!= r
.aStrVal2
|| bIsStr2
!= r
.bIsStr2
) )
672 void ScConditionEntry::Interpret( const ScAddress
& rPos
)
674 // Create formula cells
675 // Note: New Broadcaster (Note cells) may be inserted into the document!
676 if ( ( pFormula1
&& !pFCell1
) || ( pFormula2
&& !pFCell2
) )
680 bool bDirty
= false; // 1 and 2 separate?
682 std::optional
<ScFormulaCell
> oTemp
;
683 ScFormulaCell
* pEff1
= pFCell1
.get();
687 oTemp
.emplace(*mpDoc
, rPos
, *pFormula1
);
689 oTemp
.emplace(*mpDoc
, rPos
);
691 pEff1
->SetFreeFlying(true);
695 if (!pEff1
->IsRunning()) // Don't create 522
697 //TODO: Query Changed instead of Dirty!
698 if (pEff1
->GetDirty() && !bRelRef1
&& mpDoc
->GetAutoCalc())
700 if (pEff1
->IsValue())
703 nVal1
= pEff1
->GetValue();
709 aStrVal1
= pEff1
->GetString().getString();
716 ScFormulaCell
* pEff2
= pFCell2
.get(); //@ 1!=2
720 oTemp
.emplace(*mpDoc
, rPos
, *pFormula2
);
722 oTemp
.emplace(*mpDoc
, rPos
);
724 pEff2
->SetFreeFlying(true);
728 if (!pEff2
->IsRunning()) // Don't create 522
730 if (pEff2
->GetDirty() && !bRelRef2
&& mpDoc
->GetAutoCalc())
732 if (pEff2
->IsValue())
735 nVal2
= pEff2
->GetValue();
741 aStrVal2
= pEff2
->GetString().getString();
748 // If IsRunning, the last values remain
749 if (bDirty
&& !bFirstRun
)
751 // Repaint everything for dependent formats
758 static bool lcl_GetCellContent( ScRefCellValue
& rCell
, bool bIsStr1
, double& rArg
, OUString
& rArgStr
,
759 const ScDocument
* pDoc
)
767 switch (rCell
.getType())
770 rArg
= rCell
.getDouble();
772 case CELLTYPE_FORMULA
:
774 bVal
= rCell
.getFormula()->IsValue();
776 rArg
= rCell
.getFormula()->GetValue();
778 rArgStr
= rCell
.getFormula()->GetString().getString();
781 case CELLTYPE_STRING
:
784 if (rCell
.getType() == CELLTYPE_STRING
)
785 rArgStr
= rCell
.getSharedString()->getString();
786 else if (rCell
.getEditText())
787 rArgStr
= ScEditUtil::GetString(*rCell
.getEditText(), pDoc
);
796 void ScConditionEntry::FillCache() const
801 const ScRangeList
& rRanges
= pCondFormat
->GetRange();
802 mpCache
.reset(new ScConditionEntryCache
);
803 size_t nListCount
= rRanges
.size();
804 for( size_t i
= 0; i
< nListCount
; i
++ )
806 const ScRange
& rRange
= rRanges
[i
];
807 SCROW nRow
= rRange
.aEnd
.Row();
808 SCCOL nCol
= rRange
.aEnd
.Col();
809 SCCOL nColStart
= rRange
.aStart
.Col();
810 SCROW nRowStart
= rRange
.aStart
.Row();
811 SCTAB nTab
= rRange
.aStart
.Tab();
813 // temporary fix to workaround slow duplicate entry
814 // conditions, prevent to use a whole row
815 if(nRow
== mpDoc
->MaxRow())
817 bool bShrunk
= false;
818 mpDoc
->ShrinkToUsedDataArea(bShrunk
, nTab
, nColStart
, nRowStart
,
822 for( SCROW r
= nRowStart
; r
<= nRow
; r
++ )
823 for( SCCOL c
= nColStart
; c
<= nCol
; c
++ )
825 ScRefCellValue
aCell(*mpDoc
, ScAddress(c
, r
, nTab
));
831 if (!lcl_GetCellContent(aCell
, false, nVal
, aStr
, mpDoc
))
833 std::pair
<ScConditionEntryCache::StringCacheType::iterator
, bool> aResult
=
834 mpCache
->maStrings
.emplace(aStr
, 1);
837 aResult
.first
->second
++;
841 std::pair
<ScConditionEntryCache::ValueCacheType::iterator
, bool> aResult
=
842 mpCache
->maValues
.emplace(nVal
, 1);
845 aResult
.first
->second
++;
847 ++(mpCache
->nValueItems
);
853 bool ScConditionEntry::IsDuplicate( double nArg
, const OUString
& rStr
) const
859 ScConditionEntryCache::ValueCacheType::iterator itr
= mpCache
->maValues
.find(nArg
);
860 if(itr
== mpCache
->maValues
.end())
864 return itr
->second
> 1;
869 ScConditionEntryCache::StringCacheType::iterator itr
= mpCache
->maStrings
.find(rStr
);
870 if(itr
== mpCache
->maStrings
.end())
874 return itr
->second
> 1;
879 bool ScConditionEntry::IsTopNElement( double nArg
) const
883 if(mpCache
->nValueItems
<= nVal1
)
887 for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr
= mpCache
->maValues
.rbegin(),
888 itrEnd
= mpCache
->maValues
.rend(); itr
!= itrEnd
; ++itr
)
892 if(itr
->first
<= nArg
)
894 nCells
+= itr
->second
;
900 bool ScConditionEntry::IsBottomNElement( double nArg
) const
904 if(mpCache
->nValueItems
<= nVal1
)
908 for(const auto& [rVal
, rCount
] : mpCache
->maValues
)
920 bool ScConditionEntry::IsTopNPercent( double nArg
) const
925 size_t nLimitCells
= static_cast<size_t>(mpCache
->nValueItems
*nVal1
/100);
926 for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr
= mpCache
->maValues
.rbegin(),
927 itrEnd
= mpCache
->maValues
.rend(); itr
!= itrEnd
; ++itr
)
929 if(nCells
>= nLimitCells
)
931 if(itr
->first
<= nArg
)
933 nCells
+= itr
->second
;
939 bool ScConditionEntry::IsBottomNPercent( double nArg
) const
944 size_t nLimitCells
= static_cast<size_t>(mpCache
->nValueItems
*nVal1
/100);
945 for(const auto& [rVal
, rCount
] : mpCache
->maValues
)
947 if(nCells
>= nLimitCells
)
957 bool ScConditionEntry::IsBelowAverage( double nArg
, bool bEqual
) const
961 double nSum
= std::accumulate(mpCache
->maValues
.begin(), mpCache
->maValues
.end(), double(0),
962 [](const double& rSum
, const ScConditionEntryCache::ValueCacheType::value_type
& rEntry
) {
963 return rSum
+ rEntry
.first
* rEntry
.second
; });
966 return (nArg
<= nSum
/mpCache
->nValueItems
);
968 return (nArg
< nSum
/mpCache
->nValueItems
);
971 bool ScConditionEntry::IsAboveAverage( double nArg
, bool bEqual
) const
975 double nSum
= std::accumulate(mpCache
->maValues
.begin(), mpCache
->maValues
.end(), double(0),
976 [](const double& rSum
, const ScConditionEntryCache::ValueCacheType::value_type
& rEntry
) {
977 return rSum
+ rEntry
.first
* rEntry
.second
; });
980 return (nArg
>= nSum
/mpCache
->nValueItems
);
982 return (nArg
> nSum
/mpCache
->nValueItems
);
985 bool ScConditionEntry::IsError( const ScAddress
& rPos
) const
987 ScRefCellValue
rCell(*mpDoc
, rPos
);
989 if (rCell
.getType() == CELLTYPE_FORMULA
)
991 if (rCell
.getFormula()->GetErrCode() != FormulaError::NONE
)
998 bool ScConditionEntry::IsValid( double nArg
, const ScAddress
& rPos
) const
1000 // Interpret must already have been called
1005 case ScConditionMode::BeginsWith
:
1006 case ScConditionMode::EndsWith
:
1007 case ScConditionMode::ContainsText
:
1008 case ScConditionMode::NotContainsText
:
1010 case ScConditionMode::NotEqual
:
1017 if ( eOp
== ScConditionMode::Between
|| eOp
== ScConditionMode::NotBetween
)
1021 double nComp1
= nVal1
; // Copy, so that it can be changed
1022 double nComp2
= nVal2
;
1024 if ( eOp
== ScConditionMode::Between
|| eOp
== ScConditionMode::NotBetween
)
1025 if ( nComp1
> nComp2
)
1026 // Right order for value range
1027 std::swap( nComp1
, nComp2
);
1029 // All corner cases need to be tested with ::rtl::math::approxEqual!
1030 bool bValid
= false;
1033 case ScConditionMode::NONE
:
1034 break; // Always sal_False
1035 case ScConditionMode::Equal
:
1036 bValid
= ::rtl::math::approxEqual( nArg
, nComp1
);
1038 case ScConditionMode::NotEqual
:
1039 bValid
= !::rtl::math::approxEqual( nArg
, nComp1
);
1041 case ScConditionMode::Greater
:
1042 bValid
= ( nArg
> nComp1
) && !::rtl::math::approxEqual( nArg
, nComp1
);
1044 case ScConditionMode::EqGreater
:
1045 bValid
= ( nArg
>= nComp1
) || ::rtl::math::approxEqual( nArg
, nComp1
);
1047 case ScConditionMode::Less
:
1048 bValid
= ( nArg
< nComp1
) && !::rtl::math::approxEqual( nArg
, nComp1
);
1050 case ScConditionMode::EqLess
:
1051 bValid
= ( nArg
<= nComp1
) || ::rtl::math::approxEqual( nArg
, nComp1
);
1053 case ScConditionMode::Between
:
1054 bValid
= ( nArg
>= nComp1
&& nArg
<= nComp2
) ||
1055 ::rtl::math::approxEqual( nArg
, nComp1
) || ::rtl::math::approxEqual( nArg
, nComp2
);
1057 case ScConditionMode::NotBetween
:
1058 bValid
= ( nArg
< nComp1
|| nArg
> nComp2
) &&
1059 !::rtl::math::approxEqual( nArg
, nComp1
) && !::rtl::math::approxEqual( nArg
, nComp2
);
1061 case ScConditionMode::Duplicate
:
1062 case ScConditionMode::NotDuplicate
:
1065 bValid
= IsDuplicate( nArg
, OUString() );
1066 if( eOp
== ScConditionMode::NotDuplicate
)
1070 case ScConditionMode::Direct
:
1071 bValid
= nComp1
!= 0.0;
1073 case ScConditionMode::Top10
:
1074 bValid
= IsTopNElement( nArg
);
1076 case ScConditionMode::Bottom10
:
1077 bValid
= IsBottomNElement( nArg
);
1079 case ScConditionMode::TopPercent
:
1080 bValid
= IsTopNPercent( nArg
);
1082 case ScConditionMode::BottomPercent
:
1083 bValid
= IsBottomNPercent( nArg
);
1085 case ScConditionMode::AboveAverage
:
1086 case ScConditionMode::AboveEqualAverage
:
1087 bValid
= IsAboveAverage( nArg
, eOp
== ScConditionMode::AboveEqualAverage
);
1089 case ScConditionMode::BelowAverage
:
1090 case ScConditionMode::BelowEqualAverage
:
1091 bValid
= IsBelowAverage( nArg
, eOp
== ScConditionMode::BelowEqualAverage
);
1093 case ScConditionMode::Error
:
1094 case ScConditionMode::NoError
:
1095 bValid
= IsError( rPos
);
1096 if( eOp
== ScConditionMode::NoError
)
1099 case ScConditionMode::BeginsWith
:
1100 if(aStrVal1
.isEmpty())
1102 OUString aStr
= OUString::number(nVal1
);
1103 OUString aStr2
= OUString::number(nArg
);
1104 bValid
= aStr2
.startsWith(aStr
);
1108 OUString aStr2
= OUString::number(nArg
);
1109 bValid
= aStr2
.startsWith(aStrVal1
);
1112 case ScConditionMode::EndsWith
:
1113 if(aStrVal1
.isEmpty())
1115 OUString aStr
= OUString::number(nVal1
);
1116 OUString aStr2
= OUString::number(nArg
);
1117 bValid
= aStr2
.endsWith(aStr
);
1121 OUString aStr2
= OUString::number(nArg
);
1122 bValid
= aStr2
.endsWith(aStrVal1
);
1125 case ScConditionMode::ContainsText
:
1126 case ScConditionMode::NotContainsText
:
1127 if(aStrVal1
.isEmpty())
1129 OUString aStr
= OUString::number(nVal1
);
1130 OUString aStr2
= OUString::number(nArg
);
1131 bValid
= aStr2
.indexOf(aStr
) != -1;
1135 OUString aStr2
= OUString::number(nArg
);
1136 bValid
= aStr2
.indexOf(aStrVal1
) != -1;
1139 if( eOp
== ScConditionMode::NotContainsText
)
1143 SAL_WARN("sc", "unknown operation at ScConditionEntry");
1149 bool ScConditionEntry::IsValidStr( const OUString
& rArg
, const ScAddress
& rPos
) const
1151 bool bValid
= false;
1152 // Interpret must already have been called
1153 if ( eOp
== ScConditionMode::Direct
) // Formula is independent from the content
1154 return nVal1
!= 0.0;
1156 if ( eOp
== ScConditionMode::Duplicate
|| eOp
== ScConditionMode::NotDuplicate
)
1158 if( pCondFormat
&& !rArg
.isEmpty() )
1160 bValid
= IsDuplicate( 0.0, rArg
);
1161 if( eOp
== ScConditionMode::NotDuplicate
)
1167 if (eOp
== ScConditionMode::Error
)
1168 return IsError(rPos
);
1169 if (eOp
== ScConditionMode::NoError
)
1170 return !IsError(rPos
);
1172 // If number contains condition, always false, except for "not equal".
1174 return ( eOp
== ScConditionMode::NotEqual
);
1175 if ( eOp
== ScConditionMode::Between
|| eOp
== ScConditionMode::NotBetween
)
1179 OUString
aUpVal1( aStrVal1
); //TODO: As a member? (Also set in Interpret)
1180 OUString
aUpVal2( aStrVal2
);
1184 case ScConditionMode::Equal
:
1185 bValid
= ScGlobal::GetTransliteration(IsCaseSensitive()).isEqual(aUpVal1
, rArg
);
1187 case ScConditionMode::NotEqual
:
1188 bValid
= !ScGlobal::GetTransliteration(IsCaseSensitive()).isEqual(aUpVal1
, rArg
);
1190 case ScConditionMode::TopPercent
:
1191 case ScConditionMode::BottomPercent
:
1192 case ScConditionMode::Top10
:
1193 case ScConditionMode::Bottom10
:
1194 case ScConditionMode::AboveAverage
:
1195 case ScConditionMode::BelowAverage
:
1197 case ScConditionMode::BeginsWith
:
1198 bValid
= ScGlobal::GetTransliteration(IsCaseSensitive()).isMatch(aUpVal1
, rArg
);
1200 case ScConditionMode::EndsWith
:
1202 sal_Int32 nStart
= rArg
.getLength();
1203 const sal_Int32 nLen
= aUpVal1
.getLength();
1208 nStart
= nStart
- nLen
;
1209 sal_Int32
nMatch1(0), nMatch2(0);
1210 bValid
= ScGlobal::GetTransliteration(IsCaseSensitive()).equals(rArg
, nStart
, nLen
, nMatch1
,
1211 aUpVal1
, 0, nLen
, nMatch2
);
1215 case ScConditionMode::ContainsText
:
1216 case ScConditionMode::NotContainsText
:
1218 const OUString
aArgStr(!IsCaseSensitive() ? ScGlobal::getCharClass().lowercase(rArg
) : rArg
);
1219 const OUString
aValStr(!IsCaseSensitive() ? ScGlobal::getCharClass().lowercase(aUpVal1
) : aUpVal1
);
1220 bValid
= aArgStr
.indexOf(aValStr
) != -1;
1222 if(eOp
== ScConditionMode::NotContainsText
)
1228 sal_Int32 nCompare
= ScGlobal::GetCollator(IsCaseSensitive()).compareString(
1232 case ScConditionMode::Greater
:
1233 bValid
= ( nCompare
> 0 );
1235 case ScConditionMode::EqGreater
:
1236 bValid
= ( nCompare
>= 0 );
1238 case ScConditionMode::Less
:
1239 bValid
= ( nCompare
< 0 );
1241 case ScConditionMode::EqLess
:
1242 bValid
= ( nCompare
<= 0 );
1244 case ScConditionMode::Between
:
1245 case ScConditionMode::NotBetween
:
1247 const sal_Int32 nCompare2
= ScGlobal::GetCollator(IsCaseSensitive()).compareString(rArg
, aUpVal2
);
1248 // Test for NOTBETWEEN:
1249 bValid
= (nCompare
> 0 && nCompare2
> 0) || (nCompare
< 0 && nCompare2
< 0);
1250 if ( eOp
== ScConditionMode::Between
)
1255 SAL_WARN("sc", "unknown operation in ScConditionEntry");
1264 bool ScConditionEntry::IsCellValid( ScRefCellValue
& rCell
, const ScAddress
& rPos
) const
1266 const_cast<ScConditionEntry
*>(this)->Interpret(rPos
); // Evaluate formula
1268 if ( eOp
== ScConditionMode::Direct
)
1269 return nVal1
!= 0.0;
1273 bool bVal
= lcl_GetCellContent( rCell
, bIsStr1
, nArg
, aArgStr
, mpDoc
);
1275 return IsValid( nArg
, rPos
);
1277 return IsValidStr( aArgStr
, rPos
);
1280 OUString
ScConditionEntry::GetExpression( const ScAddress
& rCursor
, sal_uInt16 nIndex
,
1282 const FormulaGrammar::Grammar eGrammar
) const
1284 assert( nIndex
<= 1);
1287 if ( FormulaGrammar::isEnglish( eGrammar
) && nNumFmt
== 0 )
1288 nNumFmt
= mpDoc
->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US
);
1294 ScCompiler
aComp(*mpDoc
, rCursor
, *pFormula1
, eGrammar
);
1295 OUStringBuffer aBuffer
;
1296 aComp
.CreateStringFromTokenArray( aBuffer
);
1297 aRet
= aBuffer
.makeStringAndClear();
1301 aRet
= "\"" + aStrVal1
+ "\"";
1304 aRet
= mpDoc
->GetFormatTable()->GetInputLineString(nVal1
, nNumFmt
);
1306 else if ( nIndex
==1 )
1310 ScCompiler
aComp(*mpDoc
, rCursor
, *pFormula2
, eGrammar
);
1311 OUStringBuffer aBuffer
;
1312 aComp
.CreateStringFromTokenArray( aBuffer
);
1313 aRet
= aBuffer
.makeStringAndClear();
1317 aRet
= "\"" + aStrVal2
+ "\"";
1320 aRet
= mpDoc
->GetFormatTable()->GetInputLineString(nVal2
, nNumFmt
);
1326 std::unique_ptr
<ScTokenArray
> ScConditionEntry::CreateFlatCopiedTokenArray( sal_uInt16 nIndex
) const
1328 assert(nIndex
<= 1);
1329 std::unique_ptr
<ScTokenArray
> pRet
;
1334 pRet
.reset(new ScTokenArray( *pFormula1
));
1337 pRet
.reset(new ScTokenArray(*mpDoc
));
1340 svl::SharedStringPool
& rSPool
= mpDoc
->GetSharedStringPool();
1341 pRet
->AddString(rSPool
.intern(aStrVal1
));
1344 pRet
->AddDouble( nVal1
);
1347 else if ( nIndex
==1 )
1350 pRet
.reset(new ScTokenArray( *pFormula2
));
1353 pRet
.reset(new ScTokenArray(*mpDoc
));
1356 svl::SharedStringPool
& rSPool
= mpDoc
->GetSharedStringPool();
1357 pRet
->AddString(rSPool
.intern(aStrVal2
));
1360 pRet
->AddDouble( nVal2
);
1368 * Return a position that's adjusted to allow textual representation
1369 * of expressions if possible
1371 ScAddress
ScConditionEntry::GetValidSrcPos() const
1373 SCTAB nMinTab
= aSrcPos
.Tab();
1374 SCTAB nMaxTab
= nMinTab
;
1376 for (sal_uInt16 nPass
= 0; nPass
< 2; nPass
++)
1378 ScTokenArray
* pFormula
= nPass
? pFormula2
.get() : pFormula1
.get();
1381 for ( auto t
: pFormula
->References() )
1383 ScSingleRefData
& rRef1
= *t
->GetSingleRef();
1384 ScAddress aAbs
= rRef1
.toAbs(*mpDoc
, aSrcPos
);
1385 if (!rRef1
.IsTabDeleted())
1387 if (aAbs
.Tab() < nMinTab
)
1388 nMinTab
= aAbs
.Tab();
1389 if (aAbs
.Tab() > nMaxTab
)
1390 nMaxTab
= aAbs
.Tab();
1392 if ( t
->GetType() == svDoubleRef
)
1394 ScSingleRefData
& rRef2
= t
->GetDoubleRef()->Ref2
;
1395 aAbs
= rRef2
.toAbs(*mpDoc
, aSrcPos
);
1396 if (!rRef2
.IsTabDeleted())
1398 if (aAbs
.Tab() < nMinTab
)
1399 nMinTab
= aAbs
.Tab();
1400 if (aAbs
.Tab() > nMaxTab
)
1401 nMaxTab
= aAbs
.Tab();
1408 ScAddress aValidPos
= aSrcPos
;
1409 SCTAB nTabCount
= mpDoc
->GetTableCount();
1410 if ( nMaxTab
>= nTabCount
&& nMinTab
> 0 )
1411 aValidPos
.SetTab( aSrcPos
.Tab() - nMinTab
); // so the lowest tab ref will be on 0
1413 if ( aValidPos
.Tab() >= nTabCount
)
1414 aValidPos
.SetTab( nTabCount
- 1 ); // ensure a valid position even if some references will be invalid
1419 void ScConditionEntry::DataChanged() const
1421 //FIXME: Nothing so far
1424 bool ScConditionEntry::MarkUsedExternalReferences() const
1426 bool bAllMarked
= false;
1427 for (sal_uInt16 nPass
= 0; !bAllMarked
&& nPass
< 2; nPass
++)
1429 ScTokenArray
* pFormula
= nPass
? pFormula2
.get() : pFormula1
.get();
1431 bAllMarked
= mpDoc
->MarkUsedExternalReferences(*pFormula
, aSrcPos
);
1436 ScFormatEntry
* ScConditionEntry::Clone(ScDocument
* pDoc
) const
1438 return new ScConditionEntry(*pDoc
, *this);
1441 ScConditionMode
ScConditionEntry::GetModeFromApi(css::sheet::ConditionOperator nOperation
)
1443 ScConditionMode eMode
= ScConditionMode::NONE
;
1444 switch (static_cast<sal_Int32
>(nOperation
))
1446 case css::sheet::ConditionOperator2::EQUAL
:
1447 eMode
= ScConditionMode::Equal
;
1449 case css::sheet::ConditionOperator2::LESS
:
1450 eMode
= ScConditionMode::Less
;
1452 case css::sheet::ConditionOperator2::GREATER
:
1453 eMode
= ScConditionMode::Greater
;
1455 case css::sheet::ConditionOperator2::LESS_EQUAL
:
1456 eMode
= ScConditionMode::EqLess
;
1458 case css::sheet::ConditionOperator2::GREATER_EQUAL
:
1459 eMode
= ScConditionMode::EqGreater
;
1461 case css::sheet::ConditionOperator2::NOT_EQUAL
:
1462 eMode
= ScConditionMode::NotEqual
;
1464 case css::sheet::ConditionOperator2::BETWEEN
:
1465 eMode
= ScConditionMode::Between
;
1467 case css::sheet::ConditionOperator2::NOT_BETWEEN
:
1468 eMode
= ScConditionMode::NotBetween
;
1470 case css::sheet::ConditionOperator2::FORMULA
:
1471 eMode
= ScConditionMode::Direct
;
1473 case css::sheet::ConditionOperator2::DUPLICATE
:
1474 eMode
= ScConditionMode::Duplicate
;
1476 case css::sheet::ConditionOperator2::NOT_DUPLICATE
:
1477 eMode
= ScConditionMode::NotDuplicate
;
1485 void ScConditionEntry::startRendering()
1490 void ScConditionEntry::endRendering()
1495 bool ScConditionEntry::NeedsRepaint() const
1497 return mpListener
->NeedsRepaint();
1500 ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper
,
1501 const OUString
& rExpr1
, const OUString
& rExpr2
,
1502 ScDocument
& rDocument
, const ScAddress
& rPos
,
1504 const OUString
& rExprNmsp1
, const OUString
& rExprNmsp2
,
1505 FormulaGrammar::Grammar eGrammar1
,
1506 FormulaGrammar::Grammar eGrammar2
,
1507 ScFormatEntry::Type eType
) :
1508 ScConditionEntry( eOper
, rExpr1
, rExpr2
, rDocument
, rPos
, rExprNmsp1
, rExprNmsp2
, eGrammar1
, eGrammar2
, eType
),
1509 aStyleName(std::move( aStyle
)),
1510 eCondFormatType( eType
)
1514 ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper
,
1515 const ScTokenArray
* pArr1
, const ScTokenArray
* pArr2
,
1516 ScDocument
& rDocument
, const ScAddress
& rPos
,
1518 ScConditionEntry( eOper
, pArr1
, pArr2
, rDocument
, rPos
),
1519 aStyleName(std::move( aStyle
))
1523 ScCondFormatEntry::ScCondFormatEntry( const ScCondFormatEntry
& r
) :
1524 ScConditionEntry( r
),
1525 aStyleName( r
.aStyleName
),
1526 eCondFormatType( r
.eCondFormatType
)
1530 ScCondFormatEntry::ScCondFormatEntry( ScDocument
& rDocument
, const ScCondFormatEntry
& r
) :
1531 ScConditionEntry( rDocument
, r
),
1532 aStyleName( r
.aStyleName
),
1533 eCondFormatType( r
.eCondFormatType
)
1538 bool ScCondFormatEntry::IsEqual( const ScFormatEntry
& r
, bool bIgnoreSrcPos
) const
1540 return ScConditionEntry::IsEqual(r
, bIgnoreSrcPos
) &&
1541 (aStyleName
== static_cast<const ScCondFormatEntry
&>(r
).aStyleName
);
1544 ScCondFormatEntry::~ScCondFormatEntry()
1548 void ScCondFormatEntry::DataChanged() const
1551 pCondFormat
->DoRepaint();
1554 ScFormatEntry
* ScCondFormatEntry::Clone( ScDocument
* pDoc
) const
1556 return new ScCondFormatEntry( *pDoc
, *this );
1559 void ScConditionEntry::CalcAll()
1561 if (pFCell1
|| pFCell2
)
1564 pFCell1
->SetDirty();
1566 pFCell2
->SetDirty();
1567 pCondFormat
->DoRepaint();
1571 ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument
* pDoc
)
1572 : ScFormatEntry( pDoc
)
1573 , meType(condformat::TODAY
)
1577 ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument
* pDoc
, const ScCondDateFormatEntry
& rFormat
):
1578 ScFormatEntry( pDoc
),
1579 meType( rFormat
.meType
),
1580 maStyleName( rFormat
.maStyleName
)
1584 bool ScCondDateFormatEntry::IsValid( const ScAddress
& rPos
) const
1586 ScRefCellValue
rCell(*mpDoc
, rPos
);
1588 if (!rCell
.hasNumeric())
1589 // non-numerical cell.
1593 mpCache
.reset( new Date( Date::SYSTEM
) );
1595 const Date
& rActDate
= *mpCache
;
1596 SvNumberFormatter
* pFormatter
= mpDoc
->GetFormatTable();
1597 sal_Int32 nCurrentDate
= rActDate
- pFormatter
->GetNullDate();
1599 double nVal
= rCell
.getValue();
1600 sal_Int32 nCellDate
= static_cast<sal_Int32
>(::rtl::math::approxFloor(nVal
));
1601 Date aCellDate
= pFormatter
->GetNullDate();
1602 aCellDate
.AddDays(nCellDate
);
1606 case condformat::TODAY
:
1607 if( nCurrentDate
== nCellDate
)
1610 case condformat::TOMORROW
:
1611 if( nCurrentDate
== nCellDate
-1 )
1614 case condformat::YESTERDAY
:
1615 if( nCurrentDate
== nCellDate
+ 1)
1618 case condformat::LAST7DAYS
:
1619 if( nCurrentDate
>= nCellDate
&& nCurrentDate
- 7 < nCellDate
)
1622 case condformat::LASTWEEK
:
1624 const DayOfWeek eDay
= rActDate
.GetDayOfWeek();
1625 if( eDay
!= SUNDAY
)
1627 Date
aBegin(rActDate
- (8 + static_cast<sal_Int32
>(eDay
)));
1628 Date
aEnd(rActDate
- (2 + static_cast<sal_Int32
>(eDay
)));
1629 return aCellDate
.IsBetween( aBegin
, aEnd
);
1633 Date
aBegin(rActDate
- 8);
1634 Date
aEnd(rActDate
- 1);
1635 return aCellDate
.IsBetween( aBegin
, aEnd
);
1639 case condformat::THISWEEK
:
1641 const DayOfWeek eDay
= rActDate
.GetDayOfWeek();
1642 if( eDay
!= SUNDAY
)
1644 Date
aBegin(rActDate
- (1 + static_cast<sal_Int32
>(eDay
)));
1645 Date
aEnd(rActDate
+ (5 - static_cast<sal_Int32
>(eDay
)));
1646 return aCellDate
.IsBetween( aBegin
, aEnd
);
1650 Date
aEnd( rActDate
+ 6);
1651 return aCellDate
.IsBetween( rActDate
, aEnd
);
1655 case condformat::NEXTWEEK
:
1657 const DayOfWeek eDay
= rActDate
.GetDayOfWeek();
1658 if( eDay
!= SUNDAY
)
1660 return aCellDate
.IsBetween( rActDate
+ (6 - static_cast<sal_Int32
>(eDay
)),
1661 rActDate
+ (12 - static_cast<sal_Int32
>(eDay
)) );
1665 return aCellDate
.IsBetween( rActDate
+ 7, rActDate
+ 13 );
1669 case condformat::LASTMONTH
:
1670 if( rActDate
.GetMonth() == 1 )
1672 if( aCellDate
.GetMonth() == 12 && rActDate
.GetYear() == aCellDate
.GetNextYear() )
1675 else if( rActDate
.GetYear() == aCellDate
.GetYear() )
1677 if( rActDate
.GetMonth() == aCellDate
.GetMonth() + 1)
1681 case condformat::THISMONTH
:
1682 if( rActDate
.GetYear() == aCellDate
.GetYear() )
1684 if( rActDate
.GetMonth() == aCellDate
.GetMonth() )
1688 case condformat::NEXTMONTH
:
1689 if( rActDate
.GetMonth() == 12 )
1691 if( aCellDate
.GetMonth() == 1 && rActDate
.GetYear() == aCellDate
.GetYear() - 1 )
1694 else if( rActDate
.GetYear() == aCellDate
.GetYear() )
1696 if( rActDate
.GetMonth() == aCellDate
.GetMonth() - 1)
1700 case condformat::LASTYEAR
:
1701 if( rActDate
.GetYear() == aCellDate
.GetNextYear() )
1704 case condformat::THISYEAR
:
1705 if( rActDate
.GetYear() == aCellDate
.GetYear() )
1708 case condformat::NEXTYEAR
:
1709 if( rActDate
.GetYear() == aCellDate
.GetYear() - 1 )
1717 void ScCondDateFormatEntry::SetDateType( condformat::ScCondFormatDateType eType
)
1722 void ScCondDateFormatEntry::SetStyleName( const OUString
& rStyleName
)
1724 maStyleName
= rStyleName
;
1727 ScFormatEntry
* ScCondDateFormatEntry::Clone( ScDocument
* pDoc
) const
1729 return new ScCondDateFormatEntry( pDoc
, *this );
1732 void ScCondDateFormatEntry::startRendering()
1737 void ScCondDateFormatEntry::endRendering()
1742 ScColorFormatCache::ScColorFormatCache(ScDocument
& rDoc
, const ScRangeList
& rRanges
) :
1745 if (mrDoc
.IsClipOrUndo())
1748 for (const ScRange
& rRange
: rRanges
)
1749 mrDoc
.StartListeningArea(rRange
, false, this);
1752 ScColorFormatCache::~ScColorFormatCache()
1754 if (mrDoc
.IsClipOrUndo())
1760 void ScColorFormatCache::Notify(const SfxHint
& rHint
)
1762 if (rHint
.GetId() == SfxHintId::Dying
)
1772 ScConditionalFormat::ScConditionalFormat(sal_uInt32 nNewKey
, ScDocument
* pDocument
) :
1778 std::unique_ptr
<ScConditionalFormat
> ScConditionalFormat::Clone(ScDocument
* pNewDoc
) const
1780 // Real copy of the formula (for Ref Undo/between documents)
1784 std::unique_ptr
<ScConditionalFormat
> pNew(new ScConditionalFormat(mnKey
, pNewDoc
));
1785 pNew
->SetRange( maRanges
); // prerequisite for listeners
1787 for (const auto& rxEntry
: maEntries
)
1789 ScFormatEntry
* pNewEntry
= rxEntry
->Clone(pNewDoc
);
1790 pNew
->maEntries
.push_back( std::unique_ptr
<ScFormatEntry
>(pNewEntry
) );
1791 pNewEntry
->SetParent(pNew
.get());
1797 bool ScConditionalFormat::EqualEntries( const ScConditionalFormat
& r
, bool bIgnoreSrcPos
) const
1799 if( size() != r
.size())
1802 //TODO: Test for same entries in reverse order?
1803 if (! std::equal(maEntries
.begin(), maEntries
.end(), r
.maEntries
.begin(),
1804 [&bIgnoreSrcPos
](const std::unique_ptr
<ScFormatEntry
>& p1
, const std::unique_ptr
<ScFormatEntry
>& p2
) -> bool
1806 return p1
->IsEqual(*p2
, bIgnoreSrcPos
);
1810 // right now don't check for same range
1811 // we only use this method to merge same conditional formats from
1812 // old ODF data structure
1816 void ScConditionalFormat::SetRange( const ScRangeList
& rRanges
)
1819 SAL_WARN_IF(maRanges
.empty(), "sc", "the conditional format range is empty! will result in a crash later!");
1823 void ScConditionalFormat::AddEntry( ScFormatEntry
* pNew
)
1825 maEntries
.push_back( std::unique_ptr
<ScFormatEntry
>(pNew
));
1826 pNew
->SetParent(this);
1829 void ScConditionalFormat::RemoveEntry(size_t n
)
1831 if (n
< maEntries
.size())
1833 maEntries
.erase(maEntries
.begin() + n
);
1838 bool ScConditionalFormat::IsEmpty() const
1840 return maEntries
.empty();
1843 size_t ScConditionalFormat::size() const
1845 return maEntries
.size();
1848 ScDocument
* ScConditionalFormat::GetDocument()
1853 ScConditionalFormat::~ScConditionalFormat()
1857 const ScFormatEntry
* ScConditionalFormat::GetEntry( sal_uInt16 nPos
) const
1859 if ( nPos
< size() )
1860 return maEntries
[nPos
].get();
1865 OUString
ScConditionalFormat::GetCellStyle( ScRefCellValue
& rCell
, const ScAddress
& rPos
) const
1867 for (const auto& rxEntry
: maEntries
)
1869 if(rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
1870 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
1872 const ScCondFormatEntry
& rEntry
= static_cast<const ScCondFormatEntry
&>(*rxEntry
);
1873 if (rEntry
.IsCellValid(rCell
, rPos
))
1874 return rEntry
.GetStyle();
1876 else if(rxEntry
->GetType() == ScFormatEntry::Type::Date
)
1878 const ScCondDateFormatEntry
& rEntry
= static_cast<const ScCondDateFormatEntry
&>(*rxEntry
);
1879 if (rEntry
.IsValid( rPos
))
1880 return rEntry
.GetStyleName();
1887 ScCondFormatData
ScConditionalFormat::GetData( ScRefCellValue
& rCell
, const ScAddress
& rPos
) const
1889 ScCondFormatData aData
;
1890 for(const auto& rxEntry
: maEntries
)
1892 if( (rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
1893 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
) &&
1894 aData
.aStyleName
.isEmpty())
1896 const ScCondFormatEntry
& rEntry
= static_cast<const ScCondFormatEntry
&>(*rxEntry
);
1897 if (rEntry
.IsCellValid(rCell
, rPos
))
1898 aData
.aStyleName
= rEntry
.GetStyle();
1900 else if(rxEntry
->GetType() == ScFormatEntry::Type::Colorscale
&& !aData
.mxColorScale
)
1902 const ScColorScaleFormat
& rEntry
= static_cast<const ScColorScaleFormat
&>(*rxEntry
);
1903 aData
.mxColorScale
= rEntry
.GetColor(rPos
);
1905 else if(rxEntry
->GetType() == ScFormatEntry::Type::Databar
&& !aData
.pDataBar
)
1907 const ScDataBarFormat
& rEntry
= static_cast<const ScDataBarFormat
&>(*rxEntry
);
1908 aData
.pDataBar
= rEntry
.GetDataBarInfo(rPos
);
1910 else if(rxEntry
->GetType() == ScFormatEntry::Type::Iconset
&& !aData
.pIconSet
)
1912 const ScIconSetFormat
& rEntry
= static_cast<const ScIconSetFormat
&>(*rxEntry
);
1913 aData
.pIconSet
= rEntry
.GetIconSetInfo(rPos
);
1915 else if(rxEntry
->GetType() == ScFormatEntry::Type::Date
&& aData
.aStyleName
.isEmpty())
1917 const ScCondDateFormatEntry
& rEntry
= static_cast<const ScCondDateFormatEntry
&>(*rxEntry
);
1918 if ( rEntry
.IsValid( rPos
) )
1919 aData
.aStyleName
= rEntry
.GetStyleName();
1925 void ScConditionalFormat::DoRepaint()
1927 // all conditional format cells
1928 mpDoc
->RepaintRange( maRanges
);
1931 void ScConditionalFormat::CompileAll()
1933 for(auto& rxEntry
: maEntries
)
1934 if(rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
1935 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
1936 static_cast<ScCondFormatEntry
&>(*rxEntry
).CompileAll();
1939 void ScConditionalFormat::CompileXML()
1941 for(auto& rxEntry
: maEntries
)
1942 if(rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
1943 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
1944 static_cast<ScCondFormatEntry
&>(*rxEntry
).CompileXML();
1947 void ScConditionalFormat::UpdateReference( sc::RefUpdateContext
& rCxt
, bool bCopyAsMove
)
1949 if (rCxt
.meMode
== URM_COPY
&& bCopyAsMove
)
1951 // ScConditionEntry::UpdateReference() obtains its aSrcPos from
1952 // maRanges and does not update it on URM_COPY, but it's needed later
1953 // for the moved position, so update maRanges beforehand.
1954 maRanges
.UpdateReference(URM_MOVE
, mpDoc
, rCxt
.maRange
, rCxt
.mnColDelta
, rCxt
.mnRowDelta
, rCxt
.mnTabDelta
);
1955 for (auto& rxEntry
: maEntries
)
1956 rxEntry
->UpdateReference(rCxt
);
1960 for (auto& rxEntry
: maEntries
)
1961 rxEntry
->UpdateReference(rCxt
);
1962 maRanges
.UpdateReference(rCxt
.meMode
, mpDoc
, rCxt
.maRange
, rCxt
.mnColDelta
, rCxt
.mnRowDelta
, rCxt
.mnTabDelta
);
1968 void ScConditionalFormat::InsertRow(SCTAB nTab
, SCCOL nColStart
, SCCOL nColEnd
, SCROW nRowPos
, SCSIZE nSize
)
1970 maRanges
.InsertRow(nTab
, nColStart
, nColEnd
, nRowPos
, nSize
);
1974 void ScConditionalFormat::InsertCol(SCTAB nTab
, SCROW nRowStart
, SCROW nRowEnd
, SCCOL nColPos
, SCSIZE nSize
)
1976 maRanges
.InsertCol(nTab
, nRowStart
, nRowEnd
, nColPos
, nSize
);
1980 void ScConditionalFormat::UpdateInsertTab( sc::RefUpdateInsertTabContext
& rCxt
)
1982 for (size_t i
= 0, n
= maRanges
.size(); i
< n
; ++i
)
1984 // We assume that the start and end sheet indices are equal.
1985 ScRange
& rRange
= maRanges
[i
];
1986 SCTAB nTab
= rRange
.aStart
.Tab();
1988 if (nTab
< rCxt
.mnInsertPos
)
1992 rRange
.aStart
.IncTab(rCxt
.mnSheets
);
1993 rRange
.aEnd
.IncTab(rCxt
.mnSheets
);
1998 for (auto& rxEntry
: maEntries
)
1999 rxEntry
->UpdateInsertTab(rCxt
);
2002 void ScConditionalFormat::UpdateDeleteTab( sc::RefUpdateDeleteTabContext
& rCxt
)
2004 for (size_t i
= 0, n
= maRanges
.size(); i
< n
; ++i
)
2006 // We assume that the start and end sheet indices are equal.
2007 ScRange
& rRange
= maRanges
[i
];
2008 SCTAB nTab
= rRange
.aStart
.Tab();
2010 if (nTab
< rCxt
.mnDeletePos
)
2011 // Left of the deleted sheet(s). Unaffected.
2014 if (nTab
<= rCxt
.mnDeletePos
+rCxt
.mnSheets
-1)
2016 // On the deleted sheet(s).
2017 rRange
.aStart
.SetTab(-1);
2018 rRange
.aEnd
.SetTab(-1);
2022 // Right of the deleted sheet(s). Adjust the sheet indices.
2023 rRange
.aStart
.IncTab(-1*rCxt
.mnSheets
);
2024 rRange
.aEnd
.IncTab(-1*rCxt
.mnSheets
);
2029 for (auto& rxEntry
: maEntries
)
2030 rxEntry
->UpdateDeleteTab(rCxt
);
2033 void ScConditionalFormat::UpdateMoveTab( sc::RefUpdateMoveTabContext
& rCxt
)
2035 size_t n
= maRanges
.size();
2036 SCTAB nMinTab
= std::min
<SCTAB
>(rCxt
.mnOldPos
, rCxt
.mnNewPos
);
2037 SCTAB nMaxTab
= std::max
<SCTAB
>(rCxt
.mnOldPos
, rCxt
.mnNewPos
);
2038 for(size_t i
= 0; i
< n
; ++i
)
2040 ScRange
& rRange
= maRanges
[i
];
2041 SCTAB nTab
= rRange
.aStart
.Tab();
2042 if(nTab
< nMinTab
|| nTab
> nMaxTab
)
2047 if (nTab
== rCxt
.mnOldPos
)
2049 rRange
.aStart
.SetTab(rCxt
.mnNewPos
);
2050 rRange
.aEnd
.SetTab(rCxt
.mnNewPos
);
2054 if (rCxt
.mnNewPos
< rCxt
.mnOldPos
)
2056 rRange
.aStart
.IncTab();
2057 rRange
.aEnd
.IncTab();
2061 rRange
.aStart
.IncTab(-1);
2062 rRange
.aEnd
.IncTab(-1);
2068 for (auto& rxEntry
: maEntries
)
2069 rxEntry
->UpdateMoveTab(rCxt
);
2072 void ScConditionalFormat::DeleteArea( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
2074 if (maRanges
.empty())
2077 SCTAB nTab
= maRanges
[0].aStart
.Tab();
2078 maRanges
.DeleteArea( nCol1
, nRow1
, nTab
, nCol2
, nRow2
, nTab
);
2082 void ScConditionalFormat::RenameCellStyle(std::u16string_view rOld
, const OUString
& rNew
)
2084 for(const auto& rxEntry
: maEntries
)
2085 if(rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
2086 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
2088 ScCondFormatEntry
& rFormat
= static_cast<ScCondFormatEntry
&>(*rxEntry
);
2089 if(rFormat
.GetStyle() == rOld
)
2090 rFormat
.UpdateStyleName( rNew
);
2094 bool ScConditionalFormat::MarkUsedExternalReferences() const
2096 bool bAllMarked
= false;
2097 for(const auto& rxEntry
: maEntries
)
2098 if(rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
2099 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
2101 const ScCondFormatEntry
& rFormat
= static_cast<const ScCondFormatEntry
&>(*rxEntry
);
2102 bAllMarked
= rFormat
.MarkUsedExternalReferences();
2110 void ScConditionalFormat::startRendering()
2112 for(auto& rxEntry
: maEntries
)
2114 rxEntry
->startRendering();
2118 void ScConditionalFormat::endRendering()
2120 for(auto& rxEntry
: maEntries
)
2122 rxEntry
->endRendering();
2126 void ScConditionalFormat::updateValues()
2128 for(auto& rxEntry
: maEntries
)
2130 rxEntry
->updateValues();
2134 void ScConditionalFormat::CalcAll()
2136 for(const auto& rxEntry
: maEntries
)
2138 if (rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
2139 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
2141 ScCondFormatEntry
& rFormat
= static_cast<ScCondFormatEntry
&>(*rxEntry
);
2147 void ScConditionalFormat::ResetCache() const
2149 if (!maRanges
.empty() && mpDoc
)
2150 mpCache
= std::make_unique
<ScColorFormatCache
>(*mpDoc
, maRanges
);
2155 void ScConditionalFormat::SetCache(const std::vector
<double>& aValues
) const
2160 mpCache
->maValues
= aValues
;
2163 std::vector
<double>* ScConditionalFormat::GetCache() const
2165 return mpCache
? &mpCache
->maValues
: nullptr;
2168 ScConditionalFormatList::ScConditionalFormatList(const ScConditionalFormatList
& rList
)
2170 for(const auto& rxFormat
: rList
)
2171 InsertNew( rxFormat
->Clone() );
2174 ScConditionalFormatList::ScConditionalFormatList(ScDocument
& rDoc
, const ScConditionalFormatList
& rList
)
2176 for(const auto& rxFormat
: rList
)
2177 InsertNew( rxFormat
->Clone(&rDoc
) );
2180 void ScConditionalFormatList::InsertNew( std::unique_ptr
<ScConditionalFormat
> pNew
)
2182 m_ConditionalFormats
.insert(std::move(pNew
));
2185 ScConditionalFormat
* ScConditionalFormatList::GetFormat( sal_uInt32 nKey
)
2187 auto itr
= m_ConditionalFormats
.find(nKey
);
2188 if (itr
!= m_ConditionalFormats
.end())
2191 SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
2195 const ScConditionalFormat
* ScConditionalFormatList::GetFormat( sal_uInt32 nKey
) const
2197 auto itr
= m_ConditionalFormats
.find(nKey
);
2198 if (itr
!= m_ConditionalFormats
.end())
2201 SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
2205 void ScConditionalFormatList::CompileAll()
2207 for (auto const& it
: m_ConditionalFormats
)
2213 void ScConditionalFormatList::CompileXML()
2215 for (auto const& it
: m_ConditionalFormats
)
2221 void ScConditionalFormatList::UpdateReference( sc::RefUpdateContext
& rCxt
)
2223 for (auto const& it
: m_ConditionalFormats
)
2225 it
->UpdateReference(rCxt
);
2228 if (rCxt
.meMode
== URM_INSDEL
)
2230 // need to check which must be deleted
2235 void ScConditionalFormatList::InsertRow(SCTAB nTab
, SCCOL nColStart
, SCCOL nColEnd
, SCROW nRowPos
, SCSIZE nSize
)
2237 for (auto const& it
: m_ConditionalFormats
)
2239 it
->InsertRow(nTab
, nColStart
, nColEnd
, nRowPos
, nSize
);
2243 void ScConditionalFormatList::InsertCol(SCTAB nTab
, SCROW nRowStart
, SCROW nRowEnd
, SCCOL nColPos
, SCSIZE nSize
)
2245 for (auto const& it
: m_ConditionalFormats
)
2247 it
->InsertCol(nTab
, nRowStart
, nRowEnd
, nColPos
, nSize
);
2251 void ScConditionalFormatList::UpdateInsertTab( sc::RefUpdateInsertTabContext
& rCxt
)
2253 for (auto const& it
: m_ConditionalFormats
)
2255 it
->UpdateInsertTab(rCxt
);
2259 void ScConditionalFormatList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext
& rCxt
)
2261 for (auto const& it
: m_ConditionalFormats
)
2263 it
->UpdateDeleteTab(rCxt
);
2267 void ScConditionalFormatList::UpdateMoveTab( sc::RefUpdateMoveTabContext
& rCxt
)
2269 for (auto const& it
: m_ConditionalFormats
)
2271 it
->UpdateMoveTab(rCxt
);
2275 void ScConditionalFormatList::RenameCellStyle( std::u16string_view rOld
, const OUString
& rNew
)
2277 for (auto const& it
: m_ConditionalFormats
)
2279 it
->RenameCellStyle(rOld
, rNew
);
2283 bool ScConditionalFormatList::CheckAllEntries(const Link
<ScConditionalFormat
*,void>& rLink
)
2287 // need to check which must be deleted
2288 iterator itr
= m_ConditionalFormats
.begin();
2289 while(itr
!= m_ConditionalFormats
.end())
2291 if ((*itr
)->GetRange().empty())
2295 rLink
.Call(itr
->get());
2296 itr
= m_ConditionalFormats
.erase(itr
);
2305 void ScConditionalFormatList::DeleteArea( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
2307 for (auto& rxFormat
: m_ConditionalFormats
)
2308 rxFormat
->DeleteArea( nCol1
, nRow1
, nCol2
, nRow2
);
2313 ScConditionalFormatList::iterator
ScConditionalFormatList::begin()
2315 return m_ConditionalFormats
.begin();
2318 ScConditionalFormatList::const_iterator
ScConditionalFormatList::begin() const
2320 return m_ConditionalFormats
.begin();
2323 ScConditionalFormatList::iterator
ScConditionalFormatList::end()
2325 return m_ConditionalFormats
.end();
2328 ScConditionalFormatList::const_iterator
ScConditionalFormatList::end() const
2330 return m_ConditionalFormats
.end();
2333 ScRangeList
ScConditionalFormatList::GetCombinedRange() const
2336 for (auto& itr
: m_ConditionalFormats
)
2338 const ScRangeList
& rRange
= itr
->GetRange();
2339 for (size_t i
= 0, n
= rRange
.size(); i
< n
; ++i
)
2341 aRange
.Join(rRange
[i
]);
2347 void ScConditionalFormatList::RemoveFromDocument(ScDocument
& rDoc
) const
2349 ScRangeList aRange
= GetCombinedRange();
2350 ScMarkData
aMark(rDoc
.GetSheetLimits());
2351 aMark
.MarkFromRangeList(aRange
, true);
2352 sal_uInt16
const pItems
[2] = { sal_uInt16(ATTR_CONDITIONAL
),0};
2353 rDoc
.ClearSelectionItems(pItems
, aMark
);
2356 void ScConditionalFormatList::AddToDocument(ScDocument
& rDoc
) const
2358 for (auto& itr
: m_ConditionalFormats
)
2360 const ScRangeList
& rRange
= itr
->GetRange();
2364 SCTAB nTab
= rRange
.front().aStart
.Tab();
2365 rDoc
.AddCondFormatData(rRange
, nTab
, itr
->GetKey());
2369 size_t ScConditionalFormatList::size() const
2371 return m_ConditionalFormats
.size();
2374 bool ScConditionalFormatList::empty() const
2376 return m_ConditionalFormats
.empty();
2379 void ScConditionalFormatList::erase( sal_uLong nIndex
)
2381 auto itr
= m_ConditionalFormats
.find(nIndex
);
2383 m_ConditionalFormats
.erase(itr
);
2386 void ScConditionalFormatList::startRendering()
2388 for (auto const& it
: m_ConditionalFormats
)
2390 it
->startRendering();
2394 void ScConditionalFormatList::endRendering()
2396 for (auto const& it
: m_ConditionalFormats
)
2402 void ScConditionalFormatList::updateValues()
2404 for (auto const& it
: m_ConditionalFormats
)
2410 void ScConditionalFormatList::clear()
2412 m_ConditionalFormats
.clear();
2415 sal_uInt32
ScConditionalFormatList::getMaxKey() const
2417 if (m_ConditionalFormats
.empty())
2419 return (*m_ConditionalFormats
.rbegin())->GetKey();
2422 void ScConditionalFormatList::CalcAll()
2424 for (const auto& aEntry
: m_ConditionalFormats
)
2431 ScCondFormatData::ScCondFormatData() {}
2433 ScCondFormatData::ScCondFormatData(ScCondFormatData
&&) = default;
2435 ScCondFormatData::~ScCondFormatData() {}
2438 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */