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
;
438 * Delete formula cells, so we re-compile at the next IsValid
440 void ScConditionEntry::CompileAll()
446 void ScConditionEntry::CompileXML()
448 // First parse the formula source position if it was stored as text
449 if ( !aSrcString
.isEmpty() )
452 /* XML is always in OOo:A1 format, although R1C1 would be more amenable
454 if ( aNew
.Parse( aSrcString
, *mpDoc
) & ScRefFlags::VALID
)
456 // if the position is invalid, there isn't much we can do at this time
460 // Convert the text tokens that were created during XML import into real tokens.
461 Compile( GetExpression(aSrcPos
, 0, 0, eTempGrammar1
),
462 GetExpression(aSrcPos
, 1, 0, eTempGrammar2
),
463 aStrNmsp1
, aStrNmsp2
, eTempGrammar1
, eTempGrammar2
, true );
465 // Importing ocDde/ocWebservice?
467 mpDoc
->CheckLinkFormulaNeedingCheck(*pFormula1
);
469 mpDoc
->CheckLinkFormulaNeedingCheck(*pFormula2
);
472 void ScConditionEntry::SetSrcString( const OUString
& rNew
)
474 // aSrcString is only evaluated in CompileXML
475 SAL_WARN_IF( !mpDoc
->IsImportingXML(), "sc", "SetSrcString is only valid for XML import" );
480 void ScConditionEntry::SetFormula1( const ScTokenArray
& rArray
)
483 if( rArray
.GetLen() > 0 )
485 pFormula1
.reset( new ScTokenArray( rArray
) );
486 SimplifyCompiledFormula(pFormula1
, nVal1
, bIsStr1
, aStrVal1
);
487 bRelRef1
= lcl_HasRelRef( mpDoc
, pFormula1
.get() );
493 void ScConditionEntry::SetFormula2( const ScTokenArray
& rArray
)
496 if( rArray
.GetLen() > 0 )
498 pFormula2
.reset( new ScTokenArray( rArray
) );
499 SimplifyCompiledFormula(pFormula2
, nVal2
, bIsStr2
, aStrVal2
);
500 bRelRef2
= lcl_HasRelRef( mpDoc
, pFormula2
.get() );
506 void ScConditionEntry::UpdateReference( sc::RefUpdateContext
& rCxt
)
509 aSrcPos
= pCondFormat
->GetRange().Combine().aStart
;
510 ScAddress aOldSrcPos
= aSrcPos
;
511 bool bChangedPos
= false;
512 if (rCxt
.meMode
== URM_INSDEL
&& rCxt
.maRange
.Contains(aSrcPos
))
514 ScAddress
aErrorPos( ScAddress::UNINITIALIZED
);
515 if (!aSrcPos
.Move(rCxt
.mnColDelta
, rCxt
.mnRowDelta
, rCxt
.mnTabDelta
, aErrorPos
, *mpDoc
))
517 assert(!"can't move ScConditionEntry");
519 bChangedPos
= aSrcPos
!= aOldSrcPos
;
524 sc::RefUpdateResult aRes
;
528 aRes
= pFormula1
->AdjustReferenceOnShift(rCxt
, aOldSrcPos
);
531 aRes
= pFormula1
->AdjustReferenceOnMove(rCxt
, aOldSrcPos
, aSrcPos
);
537 if (aRes
.mbReferenceModified
|| bChangedPos
)
538 pFCell1
.reset(); // is created again in IsValid
543 sc::RefUpdateResult aRes
;
547 aRes
= pFormula2
->AdjustReferenceOnShift(rCxt
, aOldSrcPos
);
550 aRes
= pFormula2
->AdjustReferenceOnMove(rCxt
, aOldSrcPos
, aSrcPos
);
556 if (aRes
.mbReferenceModified
|| bChangedPos
)
557 pFCell2
.reset(); // is created again in IsValid
563 void ScConditionEntry::UpdateInsertTab( sc::RefUpdateInsertTabContext
& rCxt
)
567 pFormula1
->AdjustReferenceOnInsertedTab(rCxt
, aSrcPos
);
573 pFormula2
->AdjustReferenceOnInsertedTab(rCxt
, aSrcPos
);
577 ScRangeUpdater::UpdateInsertTab(aSrcPos
, rCxt
);
580 void ScConditionEntry::UpdateDeleteTab( sc::RefUpdateDeleteTabContext
& rCxt
)
584 pFormula1
->AdjustReferenceOnDeletedTab(rCxt
, aSrcPos
);
590 pFormula2
->AdjustReferenceOnDeletedTab(rCxt
, aSrcPos
);
594 ScRangeUpdater::UpdateDeleteTab(aSrcPos
, rCxt
);
598 void ScConditionEntry::UpdateMoveTab(sc::RefUpdateMoveTabContext
& rCxt
)
600 sc::RefUpdateResult aResFinal
;
601 aResFinal
.mnTab
= aSrcPos
.Tab();
604 sc::RefUpdateResult aRes
= pFormula1
->AdjustReferenceOnMovedTab(rCxt
, aSrcPos
);
605 if (aRes
.mbValueChanged
)
606 aResFinal
.mnTab
= aRes
.mnTab
;
612 sc::RefUpdateResult aRes
= pFormula2
->AdjustReferenceOnMovedTab(rCxt
, aSrcPos
);
613 if (aRes
.mbValueChanged
)
614 aResFinal
.mnTab
= aRes
.mnTab
;
618 if (aResFinal
.mnTab
!= aSrcPos
.Tab())
619 aSrcPos
.SetTab(aResFinal
.mnTab
);
624 static bool lcl_IsEqual( const std::unique_ptr
<ScTokenArray
>& pArr1
, const std::unique_ptr
<ScTokenArray
>& pArr2
)
626 // We only compare the non-RPN array
627 if ( pArr1
&& pArr2
)
628 return pArr1
->EqualTokens( pArr2
.get() );
630 return !pArr1
&& !pArr2
; // Both 0? -> the same
634 bool ScConditionEntry::IsEqual( const ScFormatEntry
& rOther
, bool bIgnoreSrcPos
) const
636 if (GetType() != rOther
.GetType())
639 const ScConditionEntry
& r
= static_cast<const ScConditionEntry
&>(rOther
);
641 bool bEq
= (eOp
== r
.eOp
&& nOptions
== r
.nOptions
&&
642 lcl_IsEqual( pFormula1
, r
.pFormula1
) &&
643 lcl_IsEqual( pFormula2
, r
.pFormula2
));
647 // for formulas, the reference positions must be compared, too
648 // (including aSrcString, for inserting the entries during XML import)
649 if ( bEq
&& ( pFormula1
|| pFormula2
) && ( aSrcPos
!= r
.aSrcPos
|| aSrcString
!= r
.aSrcString
) )
653 // If not formulas, compare values
654 if ( bEq
&& !pFormula1
&& ( nVal1
!= r
.nVal1
|| aStrVal1
!= r
.aStrVal1
|| bIsStr1
!= r
.bIsStr1
) )
656 if ( bEq
&& !pFormula2
&& ( nVal2
!= r
.nVal2
|| aStrVal2
!= r
.aStrVal2
|| bIsStr2
!= r
.bIsStr2
) )
662 void ScConditionEntry::Interpret( const ScAddress
& rPos
)
664 // Create formula cells
665 // Note: New Broadcaster (Note cells) may be inserted into the document!
666 if ( ( pFormula1
&& !pFCell1
) || ( pFormula2
&& !pFCell2
) )
670 bool bDirty
= false; // 1 and 2 separate?
672 std::optional
<ScFormulaCell
> oTemp
;
673 ScFormulaCell
* pEff1
= pFCell1
.get();
677 oTemp
.emplace(*mpDoc
, rPos
, *pFormula1
);
679 oTemp
.emplace(*mpDoc
, rPos
);
681 pEff1
->SetFreeFlying(true);
685 if (!pEff1
->IsRunning()) // Don't create 522
687 //TODO: Query Changed instead of Dirty!
688 if (pEff1
->GetDirty() && !bRelRef1
&& mpDoc
->GetAutoCalc())
690 if (pEff1
->IsValue())
693 nVal1
= pEff1
->GetValue();
699 aStrVal1
= pEff1
->GetString().getString();
706 ScFormulaCell
* pEff2
= pFCell2
.get(); //@ 1!=2
710 oTemp
.emplace(*mpDoc
, rPos
, *pFormula2
);
712 oTemp
.emplace(*mpDoc
, rPos
);
714 pEff2
->SetFreeFlying(true);
718 if (!pEff2
->IsRunning()) // Don't create 522
720 if (pEff2
->GetDirty() && !bRelRef2
&& mpDoc
->GetAutoCalc())
722 if (pEff2
->IsValue())
725 nVal2
= pEff2
->GetValue();
731 aStrVal2
= pEff2
->GetString().getString();
738 // If IsRunning, the last values remain
739 if (bDirty
&& !bFirstRun
)
741 // Repaint everything for dependent formats
748 static bool lcl_GetCellContent( ScRefCellValue
& rCell
, bool bIsStr1
, double& rArg
, OUString
& rArgStr
,
749 const ScDocument
* pDoc
)
757 switch (rCell
.getType())
760 rArg
= rCell
.getDouble();
762 case CELLTYPE_FORMULA
:
764 bVal
= rCell
.getFormula()->IsValue();
766 rArg
= rCell
.getFormula()->GetValue();
768 rArgStr
= rCell
.getFormula()->GetString().getString();
771 case CELLTYPE_STRING
:
774 if (rCell
.getType() == CELLTYPE_STRING
)
775 rArgStr
= rCell
.getSharedString()->getString();
776 else if (rCell
.getEditText())
777 rArgStr
= ScEditUtil::GetString(*rCell
.getEditText(), pDoc
);
786 void ScConditionEntry::FillCache() const
791 const ScRangeList
& rRanges
= pCondFormat
->GetRange();
792 mpCache
.reset(new ScConditionEntryCache
);
793 size_t nListCount
= rRanges
.size();
794 for( size_t i
= 0; i
< nListCount
; i
++ )
796 const ScRange
& rRange
= rRanges
[i
];
797 SCROW nRow
= rRange
.aEnd
.Row();
798 SCCOL nCol
= rRange
.aEnd
.Col();
799 SCCOL nColStart
= rRange
.aStart
.Col();
800 SCROW nRowStart
= rRange
.aStart
.Row();
801 SCTAB nTab
= rRange
.aStart
.Tab();
803 // temporary fix to workaround slow duplicate entry
804 // conditions, prevent to use a whole row
805 if(nRow
== mpDoc
->MaxRow())
807 bool bShrunk
= false;
808 mpDoc
->ShrinkToUsedDataArea(bShrunk
, nTab
, nColStart
, nRowStart
,
812 for( SCROW r
= nRowStart
; r
<= nRow
; r
++ )
813 for( SCCOL c
= nColStart
; c
<= nCol
; c
++ )
815 ScRefCellValue
aCell(*mpDoc
, ScAddress(c
, r
, nTab
));
821 if (!lcl_GetCellContent(aCell
, false, nVal
, aStr
, mpDoc
))
823 std::pair
<ScConditionEntryCache::StringCacheType::iterator
, bool> aResult
=
824 mpCache
->maStrings
.emplace(aStr
, 1);
827 aResult
.first
->second
++;
831 std::pair
<ScConditionEntryCache::ValueCacheType::iterator
, bool> aResult
=
832 mpCache
->maValues
.emplace(nVal
, 1);
835 aResult
.first
->second
++;
837 ++(mpCache
->nValueItems
);
843 bool ScConditionEntry::IsDuplicate( double nArg
, const OUString
& rStr
) const
849 ScConditionEntryCache::ValueCacheType::iterator itr
= mpCache
->maValues
.find(nArg
);
850 if(itr
== mpCache
->maValues
.end())
854 return itr
->second
> 1;
859 ScConditionEntryCache::StringCacheType::iterator itr
= mpCache
->maStrings
.find(rStr
);
860 if(itr
== mpCache
->maStrings
.end())
864 return itr
->second
> 1;
869 bool ScConditionEntry::IsTopNElement( double nArg
) const
873 if(mpCache
->nValueItems
<= nVal1
)
877 for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr
= mpCache
->maValues
.rbegin(),
878 itrEnd
= mpCache
->maValues
.rend(); itr
!= itrEnd
; ++itr
)
882 if(itr
->first
<= nArg
)
884 nCells
+= itr
->second
;
890 bool ScConditionEntry::IsBottomNElement( double nArg
) const
894 if(mpCache
->nValueItems
<= nVal1
)
898 for(const auto& [rVal
, rCount
] : mpCache
->maValues
)
910 bool ScConditionEntry::IsTopNPercent( double nArg
) const
915 size_t nLimitCells
= static_cast<size_t>(mpCache
->nValueItems
*nVal1
/100);
916 for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr
= mpCache
->maValues
.rbegin(),
917 itrEnd
= mpCache
->maValues
.rend(); itr
!= itrEnd
; ++itr
)
919 if(nCells
>= nLimitCells
)
921 if(itr
->first
<= nArg
)
923 nCells
+= itr
->second
;
929 bool ScConditionEntry::IsBottomNPercent( double nArg
) const
934 size_t nLimitCells
= static_cast<size_t>(mpCache
->nValueItems
*nVal1
/100);
935 for(const auto& [rVal
, rCount
] : mpCache
->maValues
)
937 if(nCells
>= nLimitCells
)
947 bool ScConditionEntry::IsBelowAverage( double nArg
, bool bEqual
) const
951 double nSum
= std::accumulate(mpCache
->maValues
.begin(), mpCache
->maValues
.end(), double(0),
952 [](const double& rSum
, const ScConditionEntryCache::ValueCacheType::value_type
& rEntry
) {
953 return rSum
+ rEntry
.first
* rEntry
.second
; });
956 return (nArg
<= nSum
/mpCache
->nValueItems
);
958 return (nArg
< nSum
/mpCache
->nValueItems
);
961 bool ScConditionEntry::IsAboveAverage( double nArg
, bool bEqual
) const
965 double nSum
= std::accumulate(mpCache
->maValues
.begin(), mpCache
->maValues
.end(), double(0),
966 [](const double& rSum
, const ScConditionEntryCache::ValueCacheType::value_type
& rEntry
) {
967 return rSum
+ rEntry
.first
* rEntry
.second
; });
970 return (nArg
>= nSum
/mpCache
->nValueItems
);
972 return (nArg
> nSum
/mpCache
->nValueItems
);
975 bool ScConditionEntry::IsError( const ScAddress
& rPos
) const
977 ScRefCellValue
rCell(*mpDoc
, rPos
);
979 if (rCell
.getType() == CELLTYPE_FORMULA
)
981 if (rCell
.getFormula()->GetErrCode() != FormulaError::NONE
)
988 bool ScConditionEntry::IsValid( double nArg
, const ScAddress
& rPos
) const
990 // Interpret must already have been called
995 case ScConditionMode::BeginsWith
:
996 case ScConditionMode::EndsWith
:
997 case ScConditionMode::ContainsText
:
998 case ScConditionMode::NotContainsText
:
1000 case ScConditionMode::NotEqual
:
1007 if ( eOp
== ScConditionMode::Between
|| eOp
== ScConditionMode::NotBetween
)
1011 double nComp1
= nVal1
; // Copy, so that it can be changed
1012 double nComp2
= nVal2
;
1014 if ( eOp
== ScConditionMode::Between
|| eOp
== ScConditionMode::NotBetween
)
1015 if ( nComp1
> nComp2
)
1016 // Right order for value range
1017 std::swap( nComp1
, nComp2
);
1019 // All corner cases need to be tested with ::rtl::math::approxEqual!
1020 bool bValid
= false;
1023 case ScConditionMode::NONE
:
1024 break; // Always sal_False
1025 case ScConditionMode::Equal
:
1026 bValid
= ::rtl::math::approxEqual( nArg
, nComp1
);
1028 case ScConditionMode::NotEqual
:
1029 bValid
= !::rtl::math::approxEqual( nArg
, nComp1
);
1031 case ScConditionMode::Greater
:
1032 bValid
= ( nArg
> nComp1
) && !::rtl::math::approxEqual( nArg
, nComp1
);
1034 case ScConditionMode::EqGreater
:
1035 bValid
= ( nArg
>= nComp1
) || ::rtl::math::approxEqual( nArg
, nComp1
);
1037 case ScConditionMode::Less
:
1038 bValid
= ( nArg
< nComp1
) && !::rtl::math::approxEqual( nArg
, nComp1
);
1040 case ScConditionMode::EqLess
:
1041 bValid
= ( nArg
<= nComp1
) || ::rtl::math::approxEqual( nArg
, nComp1
);
1043 case ScConditionMode::Between
:
1044 bValid
= ( nArg
>= nComp1
&& nArg
<= nComp2
) ||
1045 ::rtl::math::approxEqual( nArg
, nComp1
) || ::rtl::math::approxEqual( nArg
, nComp2
);
1047 case ScConditionMode::NotBetween
:
1048 bValid
= ( nArg
< nComp1
|| nArg
> nComp2
) &&
1049 !::rtl::math::approxEqual( nArg
, nComp1
) && !::rtl::math::approxEqual( nArg
, nComp2
);
1051 case ScConditionMode::Duplicate
:
1052 case ScConditionMode::NotDuplicate
:
1055 bValid
= IsDuplicate( nArg
, OUString() );
1056 if( eOp
== ScConditionMode::NotDuplicate
)
1060 case ScConditionMode::Direct
:
1061 bValid
= nComp1
!= 0.0;
1063 case ScConditionMode::Top10
:
1064 bValid
= IsTopNElement( nArg
);
1066 case ScConditionMode::Bottom10
:
1067 bValid
= IsBottomNElement( nArg
);
1069 case ScConditionMode::TopPercent
:
1070 bValid
= IsTopNPercent( nArg
);
1072 case ScConditionMode::BottomPercent
:
1073 bValid
= IsBottomNPercent( nArg
);
1075 case ScConditionMode::AboveAverage
:
1076 case ScConditionMode::AboveEqualAverage
:
1077 bValid
= IsAboveAverage( nArg
, eOp
== ScConditionMode::AboveEqualAverage
);
1079 case ScConditionMode::BelowAverage
:
1080 case ScConditionMode::BelowEqualAverage
:
1081 bValid
= IsBelowAverage( nArg
, eOp
== ScConditionMode::BelowEqualAverage
);
1083 case ScConditionMode::Error
:
1084 case ScConditionMode::NoError
:
1085 bValid
= IsError( rPos
);
1086 if( eOp
== ScConditionMode::NoError
)
1089 case ScConditionMode::BeginsWith
:
1090 if(aStrVal1
.isEmpty())
1092 OUString aStr
= OUString::number(nVal1
);
1093 OUString aStr2
= OUString::number(nArg
);
1094 bValid
= aStr2
.startsWith(aStr
);
1098 OUString aStr2
= OUString::number(nArg
);
1099 bValid
= aStr2
.startsWith(aStrVal1
);
1102 case ScConditionMode::EndsWith
:
1103 if(aStrVal1
.isEmpty())
1105 OUString aStr
= OUString::number(nVal1
);
1106 OUString aStr2
= OUString::number(nArg
);
1107 bValid
= aStr2
.endsWith(aStr
);
1111 OUString aStr2
= OUString::number(nArg
);
1112 bValid
= aStr2
.endsWith(aStrVal1
);
1115 case ScConditionMode::ContainsText
:
1116 case ScConditionMode::NotContainsText
:
1117 if(aStrVal1
.isEmpty())
1119 OUString aStr
= OUString::number(nVal1
);
1120 OUString aStr2
= OUString::number(nArg
);
1121 bValid
= aStr2
.indexOf(aStr
) != -1;
1125 OUString aStr2
= OUString::number(nArg
);
1126 bValid
= aStr2
.indexOf(aStrVal1
) != -1;
1129 if( eOp
== ScConditionMode::NotContainsText
)
1133 SAL_WARN("sc", "unknown operation at ScConditionEntry");
1139 bool ScConditionEntry::IsValidStr( const OUString
& rArg
, const ScAddress
& rPos
) const
1141 bool bValid
= false;
1142 // Interpret must already have been called
1143 if ( eOp
== ScConditionMode::Direct
) // Formula is independent from the content
1144 return nVal1
!= 0.0;
1146 if ( eOp
== ScConditionMode::Duplicate
|| eOp
== ScConditionMode::NotDuplicate
)
1148 if( pCondFormat
&& !rArg
.isEmpty() )
1150 bValid
= IsDuplicate( 0.0, rArg
);
1151 if( eOp
== ScConditionMode::NotDuplicate
)
1157 if (eOp
== ScConditionMode::Error
)
1158 return IsError(rPos
);
1159 if (eOp
== ScConditionMode::NoError
)
1160 return !IsError(rPos
);
1162 // If number contains condition, always false, except for "not equal".
1164 return ( eOp
== ScConditionMode::NotEqual
);
1165 if ( eOp
== ScConditionMode::Between
|| eOp
== ScConditionMode::NotBetween
)
1169 OUString
aUpVal1( aStrVal1
); //TODO: As a member? (Also set in Interpret)
1170 OUString
aUpVal2( aStrVal2
);
1174 case ScConditionMode::Equal
:
1175 bValid
= ScGlobal::GetTransliteration().isEqual(aUpVal1
, rArg
);
1177 case ScConditionMode::NotEqual
:
1178 bValid
= !ScGlobal::GetTransliteration().isEqual(aUpVal1
, rArg
);
1180 case ScConditionMode::TopPercent
:
1181 case ScConditionMode::BottomPercent
:
1182 case ScConditionMode::Top10
:
1183 case ScConditionMode::Bottom10
:
1184 case ScConditionMode::AboveAverage
:
1185 case ScConditionMode::BelowAverage
:
1187 case ScConditionMode::BeginsWith
:
1188 bValid
= ScGlobal::GetTransliteration().isMatch(aUpVal1
, rArg
);
1190 case ScConditionMode::EndsWith
:
1192 sal_Int32 nStart
= rArg
.getLength();
1193 const sal_Int32 nLen
= aUpVal1
.getLength();
1198 nStart
= nStart
- nLen
;
1199 sal_Int32
nMatch1(0), nMatch2(0);
1200 bValid
= ScGlobal::GetTransliteration().equals(rArg
, nStart
, nLen
, nMatch1
,
1201 aUpVal1
, 0, nLen
, nMatch2
);
1205 case ScConditionMode::ContainsText
:
1206 case ScConditionMode::NotContainsText
:
1208 const OUString
aArgStr(ScGlobal::getCharClass().lowercase(rArg
));
1209 const OUString
aValStr(ScGlobal::getCharClass().lowercase(aUpVal1
));
1210 bValid
= aArgStr
.indexOf(aValStr
) != -1;
1212 if(eOp
== ScConditionMode::NotContainsText
)
1218 sal_Int32 nCompare
= ScGlobal::GetCollator().compareString(
1222 case ScConditionMode::Greater
:
1223 bValid
= ( nCompare
> 0 );
1225 case ScConditionMode::EqGreater
:
1226 bValid
= ( nCompare
>= 0 );
1228 case ScConditionMode::Less
:
1229 bValid
= ( nCompare
< 0 );
1231 case ScConditionMode::EqLess
:
1232 bValid
= ( nCompare
<= 0 );
1234 case ScConditionMode::Between
:
1235 case ScConditionMode::NotBetween
:
1237 const sal_Int32 nCompare2
= ScGlobal::GetCollator().compareString(rArg
, aUpVal2
);
1238 // Test for NOTBETWEEN:
1239 bValid
= (nCompare
> 0 && nCompare2
> 0) || (nCompare
< 0 && nCompare2
< 0);
1240 if ( eOp
== ScConditionMode::Between
)
1245 SAL_WARN("sc", "unknown operation in ScConditionEntry");
1254 bool ScConditionEntry::IsCellValid( ScRefCellValue
& rCell
, const ScAddress
& rPos
) const
1256 const_cast<ScConditionEntry
*>(this)->Interpret(rPos
); // Evaluate formula
1258 if ( eOp
== ScConditionMode::Direct
)
1259 return nVal1
!= 0.0;
1263 bool bVal
= lcl_GetCellContent( rCell
, bIsStr1
, nArg
, aArgStr
, mpDoc
);
1265 return IsValid( nArg
, rPos
);
1267 return IsValidStr( aArgStr
, rPos
);
1270 OUString
ScConditionEntry::GetExpression( const ScAddress
& rCursor
, sal_uInt16 nIndex
,
1272 const FormulaGrammar::Grammar eGrammar
) const
1274 assert( nIndex
<= 1);
1277 if ( FormulaGrammar::isEnglish( eGrammar
) && nNumFmt
== 0 )
1278 nNumFmt
= mpDoc
->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US
);
1284 ScCompiler
aComp(*mpDoc
, rCursor
, *pFormula1
, eGrammar
);
1285 OUStringBuffer aBuffer
;
1286 aComp
.CreateStringFromTokenArray( aBuffer
);
1287 aRet
= aBuffer
.makeStringAndClear();
1291 aRet
= "\"" + aStrVal1
+ "\"";
1294 mpDoc
->GetFormatTable()->GetInputLineString(nVal1
, nNumFmt
, aRet
);
1296 else if ( nIndex
==1 )
1300 ScCompiler
aComp(*mpDoc
, rCursor
, *pFormula2
, eGrammar
);
1301 OUStringBuffer aBuffer
;
1302 aComp
.CreateStringFromTokenArray( aBuffer
);
1303 aRet
= aBuffer
.makeStringAndClear();
1307 aRet
= "\"" + aStrVal2
+ "\"";
1310 mpDoc
->GetFormatTable()->GetInputLineString(nVal2
, nNumFmt
, aRet
);
1316 std::unique_ptr
<ScTokenArray
> ScConditionEntry::CreateFlatCopiedTokenArray( sal_uInt16 nIndex
) const
1318 assert(nIndex
<= 1);
1319 std::unique_ptr
<ScTokenArray
> pRet
;
1324 pRet
.reset(new ScTokenArray( *pFormula1
));
1327 pRet
.reset(new ScTokenArray(*mpDoc
));
1330 svl::SharedStringPool
& rSPool
= mpDoc
->GetSharedStringPool();
1331 pRet
->AddString(rSPool
.intern(aStrVal1
));
1334 pRet
->AddDouble( nVal1
);
1337 else if ( nIndex
==1 )
1340 pRet
.reset(new ScTokenArray( *pFormula2
));
1343 pRet
.reset(new ScTokenArray(*mpDoc
));
1346 svl::SharedStringPool
& rSPool
= mpDoc
->GetSharedStringPool();
1347 pRet
->AddString(rSPool
.intern(aStrVal2
));
1350 pRet
->AddDouble( nVal2
);
1358 * Return a position that's adjusted to allow textual representation
1359 * of expressions if possible
1361 ScAddress
ScConditionEntry::GetValidSrcPos() const
1363 SCTAB nMinTab
= aSrcPos
.Tab();
1364 SCTAB nMaxTab
= nMinTab
;
1366 for (sal_uInt16 nPass
= 0; nPass
< 2; nPass
++)
1368 ScTokenArray
* pFormula
= nPass
? pFormula2
.get() : pFormula1
.get();
1371 for ( auto t
: pFormula
->References() )
1373 ScSingleRefData
& rRef1
= *t
->GetSingleRef();
1374 ScAddress aAbs
= rRef1
.toAbs(*mpDoc
, aSrcPos
);
1375 if (!rRef1
.IsTabDeleted())
1377 if (aAbs
.Tab() < nMinTab
)
1378 nMinTab
= aAbs
.Tab();
1379 if (aAbs
.Tab() > nMaxTab
)
1380 nMaxTab
= aAbs
.Tab();
1382 if ( t
->GetType() == svDoubleRef
)
1384 ScSingleRefData
& rRef2
= t
->GetDoubleRef()->Ref2
;
1385 aAbs
= rRef2
.toAbs(*mpDoc
, aSrcPos
);
1386 if (!rRef2
.IsTabDeleted())
1388 if (aAbs
.Tab() < nMinTab
)
1389 nMinTab
= aAbs
.Tab();
1390 if (aAbs
.Tab() > nMaxTab
)
1391 nMaxTab
= aAbs
.Tab();
1398 ScAddress aValidPos
= aSrcPos
;
1399 SCTAB nTabCount
= mpDoc
->GetTableCount();
1400 if ( nMaxTab
>= nTabCount
&& nMinTab
> 0 )
1401 aValidPos
.SetTab( aSrcPos
.Tab() - nMinTab
); // so the lowest tab ref will be on 0
1403 if ( aValidPos
.Tab() >= nTabCount
)
1404 aValidPos
.SetTab( nTabCount
- 1 ); // ensure a valid position even if some references will be invalid
1409 void ScConditionEntry::DataChanged() const
1411 //FIXME: Nothing so far
1414 bool ScConditionEntry::MarkUsedExternalReferences() const
1416 bool bAllMarked
= false;
1417 for (sal_uInt16 nPass
= 0; !bAllMarked
&& nPass
< 2; nPass
++)
1419 ScTokenArray
* pFormula
= nPass
? pFormula2
.get() : pFormula1
.get();
1421 bAllMarked
= mpDoc
->MarkUsedExternalReferences(*pFormula
, aSrcPos
);
1426 ScFormatEntry
* ScConditionEntry::Clone(ScDocument
* pDoc
) const
1428 return new ScConditionEntry(*pDoc
, *this);
1431 ScConditionMode
ScConditionEntry::GetModeFromApi(css::sheet::ConditionOperator nOperation
)
1433 ScConditionMode eMode
= ScConditionMode::NONE
;
1434 switch (static_cast<sal_Int32
>(nOperation
))
1436 case css::sheet::ConditionOperator2::EQUAL
:
1437 eMode
= ScConditionMode::Equal
;
1439 case css::sheet::ConditionOperator2::LESS
:
1440 eMode
= ScConditionMode::Less
;
1442 case css::sheet::ConditionOperator2::GREATER
:
1443 eMode
= ScConditionMode::Greater
;
1445 case css::sheet::ConditionOperator2::LESS_EQUAL
:
1446 eMode
= ScConditionMode::EqLess
;
1448 case css::sheet::ConditionOperator2::GREATER_EQUAL
:
1449 eMode
= ScConditionMode::EqGreater
;
1451 case css::sheet::ConditionOperator2::NOT_EQUAL
:
1452 eMode
= ScConditionMode::NotEqual
;
1454 case css::sheet::ConditionOperator2::BETWEEN
:
1455 eMode
= ScConditionMode::Between
;
1457 case css::sheet::ConditionOperator2::NOT_BETWEEN
:
1458 eMode
= ScConditionMode::NotBetween
;
1460 case css::sheet::ConditionOperator2::FORMULA
:
1461 eMode
= ScConditionMode::Direct
;
1463 case css::sheet::ConditionOperator2::DUPLICATE
:
1464 eMode
= ScConditionMode::Duplicate
;
1466 case css::sheet::ConditionOperator2::NOT_DUPLICATE
:
1467 eMode
= ScConditionMode::NotDuplicate
;
1475 void ScConditionEntry::startRendering()
1480 void ScConditionEntry::endRendering()
1485 bool ScConditionEntry::NeedsRepaint() const
1487 return mpListener
->NeedsRepaint();
1490 ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper
,
1491 const OUString
& rExpr1
, const OUString
& rExpr2
,
1492 ScDocument
& rDocument
, const ScAddress
& rPos
,
1494 const OUString
& rExprNmsp1
, const OUString
& rExprNmsp2
,
1495 FormulaGrammar::Grammar eGrammar1
,
1496 FormulaGrammar::Grammar eGrammar2
,
1497 ScFormatEntry::Type eType
) :
1498 ScConditionEntry( eOper
, rExpr1
, rExpr2
, rDocument
, rPos
, rExprNmsp1
, rExprNmsp2
, eGrammar1
, eGrammar2
, eType
),
1499 aStyleName(std::move( aStyle
)),
1500 eCondFormatType( eType
)
1504 ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper
,
1505 const ScTokenArray
* pArr1
, const ScTokenArray
* pArr2
,
1506 ScDocument
& rDocument
, const ScAddress
& rPos
,
1508 ScConditionEntry( eOper
, pArr1
, pArr2
, rDocument
, rPos
),
1509 aStyleName(std::move( aStyle
))
1513 ScCondFormatEntry::ScCondFormatEntry( const ScCondFormatEntry
& r
) :
1514 ScConditionEntry( r
),
1515 aStyleName( r
.aStyleName
),
1516 eCondFormatType( r
.eCondFormatType
)
1520 ScCondFormatEntry::ScCondFormatEntry( ScDocument
& rDocument
, const ScCondFormatEntry
& r
) :
1521 ScConditionEntry( rDocument
, r
),
1522 aStyleName( r
.aStyleName
),
1523 eCondFormatType( r
.eCondFormatType
)
1528 bool ScCondFormatEntry::IsEqual( const ScFormatEntry
& r
, bool bIgnoreSrcPos
) const
1530 return ScConditionEntry::IsEqual(r
, bIgnoreSrcPos
) &&
1531 (aStyleName
== static_cast<const ScCondFormatEntry
&>(r
).aStyleName
);
1534 ScCondFormatEntry::~ScCondFormatEntry()
1538 void ScCondFormatEntry::DataChanged() const
1541 pCondFormat
->DoRepaint();
1544 ScFormatEntry
* ScCondFormatEntry::Clone( ScDocument
* pDoc
) const
1546 return new ScCondFormatEntry( *pDoc
, *this );
1549 void ScConditionEntry::CalcAll()
1551 if (pFCell1
|| pFCell2
)
1554 pFCell1
->SetDirty();
1556 pFCell2
->SetDirty();
1557 pCondFormat
->DoRepaint();
1561 ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument
* pDoc
)
1562 : ScFormatEntry( pDoc
)
1563 , meType(condformat::TODAY
)
1567 ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument
* pDoc
, const ScCondDateFormatEntry
& rFormat
):
1568 ScFormatEntry( pDoc
),
1569 meType( rFormat
.meType
),
1570 maStyleName( rFormat
.maStyleName
)
1574 bool ScCondDateFormatEntry::IsValid( const ScAddress
& rPos
) const
1576 ScRefCellValue
rCell(*mpDoc
, rPos
);
1578 if (!rCell
.hasNumeric())
1579 // non-numerical cell.
1583 mpCache
.reset( new Date( Date::SYSTEM
) );
1585 const Date
& rActDate
= *mpCache
;
1586 SvNumberFormatter
* pFormatter
= mpDoc
->GetFormatTable();
1587 sal_Int32 nCurrentDate
= rActDate
- pFormatter
->GetNullDate();
1589 double nVal
= rCell
.getValue();
1590 sal_Int32 nCellDate
= static_cast<sal_Int32
>(::rtl::math::approxFloor(nVal
));
1591 Date aCellDate
= pFormatter
->GetNullDate();
1592 aCellDate
.AddDays(nCellDate
);
1596 case condformat::TODAY
:
1597 if( nCurrentDate
== nCellDate
)
1600 case condformat::TOMORROW
:
1601 if( nCurrentDate
== nCellDate
-1 )
1604 case condformat::YESTERDAY
:
1605 if( nCurrentDate
== nCellDate
+ 1)
1608 case condformat::LAST7DAYS
:
1609 if( nCurrentDate
>= nCellDate
&& nCurrentDate
- 7 < nCellDate
)
1612 case condformat::LASTWEEK
:
1614 const DayOfWeek eDay
= rActDate
.GetDayOfWeek();
1615 if( eDay
!= SUNDAY
)
1617 Date
aBegin(rActDate
- (8 + static_cast<sal_Int32
>(eDay
)));
1618 Date
aEnd(rActDate
- (2 + static_cast<sal_Int32
>(eDay
)));
1619 return aCellDate
.IsBetween( aBegin
, aEnd
);
1623 Date
aBegin(rActDate
- 8);
1624 Date
aEnd(rActDate
- 1);
1625 return aCellDate
.IsBetween( aBegin
, aEnd
);
1629 case condformat::THISWEEK
:
1631 const DayOfWeek eDay
= rActDate
.GetDayOfWeek();
1632 if( eDay
!= SUNDAY
)
1634 Date
aBegin(rActDate
- (1 + static_cast<sal_Int32
>(eDay
)));
1635 Date
aEnd(rActDate
+ (5 - static_cast<sal_Int32
>(eDay
)));
1636 return aCellDate
.IsBetween( aBegin
, aEnd
);
1640 Date
aEnd( rActDate
+ 6);
1641 return aCellDate
.IsBetween( rActDate
, aEnd
);
1645 case condformat::NEXTWEEK
:
1647 const DayOfWeek eDay
= rActDate
.GetDayOfWeek();
1648 if( eDay
!= SUNDAY
)
1650 return aCellDate
.IsBetween( rActDate
+ (6 - static_cast<sal_Int32
>(eDay
)),
1651 rActDate
+ (12 - static_cast<sal_Int32
>(eDay
)) );
1655 return aCellDate
.IsBetween( rActDate
+ 7, rActDate
+ 13 );
1659 case condformat::LASTMONTH
:
1660 if( rActDate
.GetMonth() == 1 )
1662 if( aCellDate
.GetMonth() == 12 && rActDate
.GetYear() == aCellDate
.GetNextYear() )
1665 else if( rActDate
.GetYear() == aCellDate
.GetYear() )
1667 if( rActDate
.GetMonth() == aCellDate
.GetMonth() + 1)
1671 case condformat::THISMONTH
:
1672 if( rActDate
.GetYear() == aCellDate
.GetYear() )
1674 if( rActDate
.GetMonth() == aCellDate
.GetMonth() )
1678 case condformat::NEXTMONTH
:
1679 if( rActDate
.GetMonth() == 12 )
1681 if( aCellDate
.GetMonth() == 1 && rActDate
.GetYear() == aCellDate
.GetYear() - 1 )
1684 else if( rActDate
.GetYear() == aCellDate
.GetYear() )
1686 if( rActDate
.GetMonth() == aCellDate
.GetMonth() - 1)
1690 case condformat::LASTYEAR
:
1691 if( rActDate
.GetYear() == aCellDate
.GetNextYear() )
1694 case condformat::THISYEAR
:
1695 if( rActDate
.GetYear() == aCellDate
.GetYear() )
1698 case condformat::NEXTYEAR
:
1699 if( rActDate
.GetYear() == aCellDate
.GetYear() - 1 )
1707 void ScCondDateFormatEntry::SetDateType( condformat::ScCondFormatDateType eType
)
1712 void ScCondDateFormatEntry::SetStyleName( const OUString
& rStyleName
)
1714 maStyleName
= rStyleName
;
1717 ScFormatEntry
* ScCondDateFormatEntry::Clone( ScDocument
* pDoc
) const
1719 return new ScCondDateFormatEntry( pDoc
, *this );
1722 void ScCondDateFormatEntry::startRendering()
1727 void ScCondDateFormatEntry::endRendering()
1732 ScColorFormatCache::ScColorFormatCache(ScDocument
& rDoc
, const ScRangeList
& rRanges
) :
1735 if (mrDoc
.IsClipOrUndo())
1738 for (const ScRange
& rRange
: rRanges
)
1739 mrDoc
.StartListeningArea(rRange
, false, this);
1742 ScColorFormatCache::~ScColorFormatCache()
1744 if (mrDoc
.IsClipOrUndo())
1750 void ScColorFormatCache::Notify(const SfxHint
& rHint
)
1752 if (rHint
.GetId() == SfxHintId::Dying
)
1762 ScConditionalFormat::ScConditionalFormat(sal_uInt32 nNewKey
, ScDocument
* pDocument
) :
1768 std::unique_ptr
<ScConditionalFormat
> ScConditionalFormat::Clone(ScDocument
* pNewDoc
) const
1770 // Real copy of the formula (for Ref Undo/between documents)
1774 std::unique_ptr
<ScConditionalFormat
> pNew(new ScConditionalFormat(nKey
, pNewDoc
));
1775 pNew
->SetRange( maRanges
); // prerequisite for listeners
1777 for (const auto& rxEntry
: maEntries
)
1779 ScFormatEntry
* pNewEntry
= rxEntry
->Clone(pNewDoc
);
1780 pNew
->maEntries
.push_back( std::unique_ptr
<ScFormatEntry
>(pNewEntry
) );
1781 pNewEntry
->SetParent(pNew
.get());
1787 bool ScConditionalFormat::EqualEntries( const ScConditionalFormat
& r
, bool bIgnoreSrcPos
) const
1789 if( size() != r
.size())
1792 //TODO: Test for same entries in reverse order?
1793 if (! std::equal(maEntries
.begin(), maEntries
.end(), r
.maEntries
.begin(),
1794 [&bIgnoreSrcPos
](const std::unique_ptr
<ScFormatEntry
>& p1
, const std::unique_ptr
<ScFormatEntry
>& p2
) -> bool
1796 return p1
->IsEqual(*p2
, bIgnoreSrcPos
);
1800 // right now don't check for same range
1801 // we only use this method to merge same conditional formats from
1802 // old ODF data structure
1806 void ScConditionalFormat::SetRange( const ScRangeList
& rRanges
)
1809 SAL_WARN_IF(maRanges
.empty(), "sc", "the conditional format range is empty! will result in a crash later!");
1813 void ScConditionalFormat::AddEntry( ScFormatEntry
* pNew
)
1815 maEntries
.push_back( std::unique_ptr
<ScFormatEntry
>(pNew
));
1816 pNew
->SetParent(this);
1819 void ScConditionalFormat::RemoveEntry(size_t n
)
1821 if (n
< maEntries
.size())
1823 maEntries
.erase(maEntries
.begin() + n
);
1828 bool ScConditionalFormat::IsEmpty() const
1830 return maEntries
.empty();
1833 size_t ScConditionalFormat::size() const
1835 return maEntries
.size();
1838 ScDocument
* ScConditionalFormat::GetDocument()
1843 ScConditionalFormat::~ScConditionalFormat()
1847 const ScFormatEntry
* ScConditionalFormat::GetEntry( sal_uInt16 nPos
) const
1849 if ( nPos
< size() )
1850 return maEntries
[nPos
].get();
1855 OUString
ScConditionalFormat::GetCellStyle( ScRefCellValue
& rCell
, const ScAddress
& rPos
) const
1857 for (const auto& rxEntry
: maEntries
)
1859 if(rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
1860 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
1862 const ScCondFormatEntry
& rEntry
= static_cast<const ScCondFormatEntry
&>(*rxEntry
);
1863 if (rEntry
.IsCellValid(rCell
, rPos
))
1864 return rEntry
.GetStyle();
1866 else if(rxEntry
->GetType() == ScFormatEntry::Type::Date
)
1868 const ScCondDateFormatEntry
& rEntry
= static_cast<const ScCondDateFormatEntry
&>(*rxEntry
);
1869 if (rEntry
.IsValid( rPos
))
1870 return rEntry
.GetStyleName();
1877 ScCondFormatData
ScConditionalFormat::GetData( ScRefCellValue
& rCell
, const ScAddress
& rPos
) const
1879 ScCondFormatData aData
;
1880 for(const auto& rxEntry
: maEntries
)
1882 if( (rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
1883 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
) &&
1884 aData
.aStyleName
.isEmpty())
1886 const ScCondFormatEntry
& rEntry
= static_cast<const ScCondFormatEntry
&>(*rxEntry
);
1887 if (rEntry
.IsCellValid(rCell
, rPos
))
1888 aData
.aStyleName
= rEntry
.GetStyle();
1890 else if(rxEntry
->GetType() == ScFormatEntry::Type::Colorscale
&& !aData
.mxColorScale
)
1892 const ScColorScaleFormat
& rEntry
= static_cast<const ScColorScaleFormat
&>(*rxEntry
);
1893 aData
.mxColorScale
= rEntry
.GetColor(rPos
);
1895 else if(rxEntry
->GetType() == ScFormatEntry::Type::Databar
&& !aData
.pDataBar
)
1897 const ScDataBarFormat
& rEntry
= static_cast<const ScDataBarFormat
&>(*rxEntry
);
1898 aData
.pDataBar
= rEntry
.GetDataBarInfo(rPos
);
1900 else if(rxEntry
->GetType() == ScFormatEntry::Type::Iconset
&& !aData
.pIconSet
)
1902 const ScIconSetFormat
& rEntry
= static_cast<const ScIconSetFormat
&>(*rxEntry
);
1903 aData
.pIconSet
= rEntry
.GetIconSetInfo(rPos
);
1905 else if(rxEntry
->GetType() == ScFormatEntry::Type::Date
&& aData
.aStyleName
.isEmpty())
1907 const ScCondDateFormatEntry
& rEntry
= static_cast<const ScCondDateFormatEntry
&>(*rxEntry
);
1908 if ( rEntry
.IsValid( rPos
) )
1909 aData
.aStyleName
= rEntry
.GetStyleName();
1915 void ScConditionalFormat::DoRepaint()
1917 // all conditional format cells
1918 pDoc
->RepaintRange( maRanges
);
1921 void ScConditionalFormat::CompileAll()
1923 for(auto& rxEntry
: maEntries
)
1924 if(rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
1925 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
1926 static_cast<ScCondFormatEntry
&>(*rxEntry
).CompileAll();
1929 void ScConditionalFormat::CompileXML()
1931 for(auto& rxEntry
: maEntries
)
1932 if(rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
1933 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
1934 static_cast<ScCondFormatEntry
&>(*rxEntry
).CompileXML();
1937 void ScConditionalFormat::UpdateReference( sc::RefUpdateContext
& rCxt
, bool bCopyAsMove
)
1939 if (rCxt
.meMode
== URM_COPY
&& bCopyAsMove
)
1941 // ScConditionEntry::UpdateReference() obtains its aSrcPos from
1942 // maRanges and does not update it on URM_COPY, but it's needed later
1943 // for the moved position, so update maRanges beforehand.
1944 maRanges
.UpdateReference(URM_MOVE
, pDoc
, rCxt
.maRange
, rCxt
.mnColDelta
, rCxt
.mnRowDelta
, rCxt
.mnTabDelta
);
1945 for (auto& rxEntry
: maEntries
)
1946 rxEntry
->UpdateReference(rCxt
);
1950 for (auto& rxEntry
: maEntries
)
1951 rxEntry
->UpdateReference(rCxt
);
1952 maRanges
.UpdateReference(rCxt
.meMode
, pDoc
, rCxt
.maRange
, rCxt
.mnColDelta
, rCxt
.mnRowDelta
, rCxt
.mnTabDelta
);
1958 void ScConditionalFormat::InsertRow(SCTAB nTab
, SCCOL nColStart
, SCCOL nColEnd
, SCROW nRowPos
, SCSIZE nSize
)
1960 maRanges
.InsertRow(nTab
, nColStart
, nColEnd
, nRowPos
, nSize
);
1964 void ScConditionalFormat::InsertCol(SCTAB nTab
, SCROW nRowStart
, SCROW nRowEnd
, SCCOL nColPos
, SCSIZE nSize
)
1966 maRanges
.InsertCol(nTab
, nRowStart
, nRowEnd
, nColPos
, nSize
);
1970 void ScConditionalFormat::UpdateInsertTab( sc::RefUpdateInsertTabContext
& rCxt
)
1972 for (size_t i
= 0, n
= maRanges
.size(); i
< n
; ++i
)
1974 // We assume that the start and end sheet indices are equal.
1975 ScRange
& rRange
= maRanges
[i
];
1976 SCTAB nTab
= rRange
.aStart
.Tab();
1978 if (nTab
< rCxt
.mnInsertPos
)
1982 rRange
.aStart
.IncTab(rCxt
.mnSheets
);
1983 rRange
.aEnd
.IncTab(rCxt
.mnSheets
);
1988 for (auto& rxEntry
: maEntries
)
1989 rxEntry
->UpdateInsertTab(rCxt
);
1992 void ScConditionalFormat::UpdateDeleteTab( sc::RefUpdateDeleteTabContext
& rCxt
)
1994 for (size_t i
= 0, n
= maRanges
.size(); i
< n
; ++i
)
1996 // We assume that the start and end sheet indices are equal.
1997 ScRange
& rRange
= maRanges
[i
];
1998 SCTAB nTab
= rRange
.aStart
.Tab();
2000 if (nTab
< rCxt
.mnDeletePos
)
2001 // Left of the deleted sheet(s). Unaffected.
2004 if (nTab
<= rCxt
.mnDeletePos
+rCxt
.mnSheets
-1)
2006 // On the deleted sheet(s).
2007 rRange
.aStart
.SetTab(-1);
2008 rRange
.aEnd
.SetTab(-1);
2012 // Right of the deleted sheet(s). Adjust the sheet indices.
2013 rRange
.aStart
.IncTab(-1*rCxt
.mnSheets
);
2014 rRange
.aEnd
.IncTab(-1*rCxt
.mnSheets
);
2019 for (auto& rxEntry
: maEntries
)
2020 rxEntry
->UpdateDeleteTab(rCxt
);
2023 void ScConditionalFormat::UpdateMoveTab( sc::RefUpdateMoveTabContext
& rCxt
)
2025 size_t n
= maRanges
.size();
2026 SCTAB nMinTab
= std::min
<SCTAB
>(rCxt
.mnOldPos
, rCxt
.mnNewPos
);
2027 SCTAB nMaxTab
= std::max
<SCTAB
>(rCxt
.mnOldPos
, rCxt
.mnNewPos
);
2028 for(size_t i
= 0; i
< n
; ++i
)
2030 ScRange
& rRange
= maRanges
[i
];
2031 SCTAB nTab
= rRange
.aStart
.Tab();
2032 if(nTab
< nMinTab
|| nTab
> nMaxTab
)
2037 if (nTab
== rCxt
.mnOldPos
)
2039 rRange
.aStart
.SetTab(rCxt
.mnNewPos
);
2040 rRange
.aEnd
.SetTab(rCxt
.mnNewPos
);
2044 if (rCxt
.mnNewPos
< rCxt
.mnOldPos
)
2046 rRange
.aStart
.IncTab();
2047 rRange
.aEnd
.IncTab();
2051 rRange
.aStart
.IncTab(-1);
2052 rRange
.aEnd
.IncTab(-1);
2058 for (auto& rxEntry
: maEntries
)
2059 rxEntry
->UpdateMoveTab(rCxt
);
2062 void ScConditionalFormat::DeleteArea( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
2064 if (maRanges
.empty())
2067 SCTAB nTab
= maRanges
[0].aStart
.Tab();
2068 maRanges
.DeleteArea( nCol1
, nRow1
, nTab
, nCol2
, nRow2
, nTab
);
2072 void ScConditionalFormat::RenameCellStyle(std::u16string_view rOld
, const OUString
& rNew
)
2074 for(const auto& rxEntry
: maEntries
)
2075 if(rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
2076 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
2078 ScCondFormatEntry
& rFormat
= static_cast<ScCondFormatEntry
&>(*rxEntry
);
2079 if(rFormat
.GetStyle() == rOld
)
2080 rFormat
.UpdateStyleName( rNew
);
2084 bool ScConditionalFormat::MarkUsedExternalReferences() const
2086 bool bAllMarked
= false;
2087 for(const auto& rxEntry
: maEntries
)
2088 if(rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
2089 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
2091 const ScCondFormatEntry
& rFormat
= static_cast<const ScCondFormatEntry
&>(*rxEntry
);
2092 bAllMarked
= rFormat
.MarkUsedExternalReferences();
2100 void ScConditionalFormat::startRendering()
2102 for(auto& rxEntry
: maEntries
)
2104 rxEntry
->startRendering();
2108 void ScConditionalFormat::endRendering()
2110 for(auto& rxEntry
: maEntries
)
2112 rxEntry
->endRendering();
2116 void ScConditionalFormat::updateValues()
2118 for(auto& rxEntry
: maEntries
)
2120 rxEntry
->updateValues();
2124 void ScConditionalFormat::CalcAll()
2126 for(const auto& rxEntry
: maEntries
)
2128 if (rxEntry
->GetType() == ScFormatEntry::Type::Condition
||
2129 rxEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
2131 ScCondFormatEntry
& rFormat
= static_cast<ScCondFormatEntry
&>(*rxEntry
);
2137 void ScConditionalFormat::ResetCache() const
2139 if (!maRanges
.empty() && pDoc
)
2140 mpCache
= std::make_unique
<ScColorFormatCache
>(*pDoc
, maRanges
);
2145 void ScConditionalFormat::SetCache(const std::vector
<double>& aValues
) const
2150 mpCache
->maValues
= aValues
;
2153 std::vector
<double>* ScConditionalFormat::GetCache() const
2155 return mpCache
? &mpCache
->maValues
: nullptr;
2158 ScConditionalFormatList::ScConditionalFormatList(const ScConditionalFormatList
& rList
)
2160 for(const auto& rxFormat
: rList
)
2161 InsertNew( rxFormat
->Clone() );
2164 ScConditionalFormatList::ScConditionalFormatList(ScDocument
& rDoc
, const ScConditionalFormatList
& rList
)
2166 for(const auto& rxFormat
: rList
)
2167 InsertNew( rxFormat
->Clone(&rDoc
) );
2170 void ScConditionalFormatList::InsertNew( std::unique_ptr
<ScConditionalFormat
> pNew
)
2172 m_ConditionalFormats
.insert(std::move(pNew
));
2175 ScConditionalFormat
* ScConditionalFormatList::GetFormat( sal_uInt32 nKey
)
2177 auto itr
= m_ConditionalFormats
.find(nKey
);
2178 if (itr
!= m_ConditionalFormats
.end())
2181 SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
2185 const ScConditionalFormat
* ScConditionalFormatList::GetFormat( sal_uInt32 nKey
) const
2187 auto itr
= m_ConditionalFormats
.find(nKey
);
2188 if (itr
!= m_ConditionalFormats
.end())
2191 SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
2195 void ScConditionalFormatList::CompileAll()
2197 for (auto const& it
: m_ConditionalFormats
)
2203 void ScConditionalFormatList::CompileXML()
2205 for (auto const& it
: m_ConditionalFormats
)
2211 void ScConditionalFormatList::UpdateReference( sc::RefUpdateContext
& rCxt
)
2213 for (auto const& it
: m_ConditionalFormats
)
2215 it
->UpdateReference(rCxt
);
2218 if (rCxt
.meMode
== URM_INSDEL
)
2220 // need to check which must be deleted
2225 void ScConditionalFormatList::InsertRow(SCTAB nTab
, SCCOL nColStart
, SCCOL nColEnd
, SCROW nRowPos
, SCSIZE nSize
)
2227 for (auto const& it
: m_ConditionalFormats
)
2229 it
->InsertRow(nTab
, nColStart
, nColEnd
, nRowPos
, nSize
);
2233 void ScConditionalFormatList::InsertCol(SCTAB nTab
, SCROW nRowStart
, SCROW nRowEnd
, SCCOL nColPos
, SCSIZE nSize
)
2235 for (auto const& it
: m_ConditionalFormats
)
2237 it
->InsertCol(nTab
, nRowStart
, nRowEnd
, nColPos
, nSize
);
2241 void ScConditionalFormatList::UpdateInsertTab( sc::RefUpdateInsertTabContext
& rCxt
)
2243 for (auto const& it
: m_ConditionalFormats
)
2245 it
->UpdateInsertTab(rCxt
);
2249 void ScConditionalFormatList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext
& rCxt
)
2251 for (auto const& it
: m_ConditionalFormats
)
2253 it
->UpdateDeleteTab(rCxt
);
2257 void ScConditionalFormatList::UpdateMoveTab( sc::RefUpdateMoveTabContext
& rCxt
)
2259 for (auto const& it
: m_ConditionalFormats
)
2261 it
->UpdateMoveTab(rCxt
);
2265 void ScConditionalFormatList::RenameCellStyle( std::u16string_view rOld
, const OUString
& rNew
)
2267 for (auto const& it
: m_ConditionalFormats
)
2269 it
->RenameCellStyle(rOld
, rNew
);
2273 bool ScConditionalFormatList::CheckAllEntries(const Link
<ScConditionalFormat
*,void>& rLink
)
2277 // need to check which must be deleted
2278 iterator itr
= m_ConditionalFormats
.begin();
2279 while(itr
!= m_ConditionalFormats
.end())
2281 if ((*itr
)->GetRange().empty())
2285 rLink
.Call(itr
->get());
2286 itr
= m_ConditionalFormats
.erase(itr
);
2295 void ScConditionalFormatList::DeleteArea( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
2297 for (auto& rxFormat
: m_ConditionalFormats
)
2298 rxFormat
->DeleteArea( nCol1
, nRow1
, nCol2
, nRow2
);
2303 ScConditionalFormatList::iterator
ScConditionalFormatList::begin()
2305 return m_ConditionalFormats
.begin();
2308 ScConditionalFormatList::const_iterator
ScConditionalFormatList::begin() const
2310 return m_ConditionalFormats
.begin();
2313 ScConditionalFormatList::iterator
ScConditionalFormatList::end()
2315 return m_ConditionalFormats
.end();
2318 ScConditionalFormatList::const_iterator
ScConditionalFormatList::end() const
2320 return m_ConditionalFormats
.end();
2323 ScRangeList
ScConditionalFormatList::GetCombinedRange() const
2326 for (auto& itr
: m_ConditionalFormats
)
2328 const ScRangeList
& rRange
= itr
->GetRange();
2329 for (size_t i
= 0, n
= rRange
.size(); i
< n
; ++i
)
2331 aRange
.Join(rRange
[i
]);
2337 void ScConditionalFormatList::RemoveFromDocument(ScDocument
& rDoc
) const
2339 ScRangeList aRange
= GetCombinedRange();
2340 ScMarkData
aMark(rDoc
.GetSheetLimits());
2341 aMark
.MarkFromRangeList(aRange
, true);
2342 sal_uInt16
const pItems
[2] = { sal_uInt16(ATTR_CONDITIONAL
),0};
2343 rDoc
.ClearSelectionItems(pItems
, aMark
);
2346 void ScConditionalFormatList::AddToDocument(ScDocument
& rDoc
) const
2348 for (auto& itr
: m_ConditionalFormats
)
2350 const ScRangeList
& rRange
= itr
->GetRange();
2354 SCTAB nTab
= rRange
.front().aStart
.Tab();
2355 rDoc
.AddCondFormatData(rRange
, nTab
, itr
->GetKey());
2359 size_t ScConditionalFormatList::size() const
2361 return m_ConditionalFormats
.size();
2364 bool ScConditionalFormatList::empty() const
2366 return m_ConditionalFormats
.empty();
2369 void ScConditionalFormatList::erase( sal_uLong nIndex
)
2371 auto itr
= m_ConditionalFormats
.find(nIndex
);
2373 m_ConditionalFormats
.erase(itr
);
2376 void ScConditionalFormatList::startRendering()
2378 for (auto const& it
: m_ConditionalFormats
)
2380 it
->startRendering();
2384 void ScConditionalFormatList::endRendering()
2386 for (auto const& it
: m_ConditionalFormats
)
2392 void ScConditionalFormatList::updateValues()
2394 for (auto const& it
: m_ConditionalFormats
)
2400 void ScConditionalFormatList::clear()
2402 m_ConditionalFormats
.clear();
2405 sal_uInt32
ScConditionalFormatList::getMaxKey() const
2407 if (m_ConditionalFormats
.empty())
2409 return (*m_ConditionalFormats
.rbegin())->GetKey();
2412 void ScConditionalFormatList::CalcAll()
2414 for (const auto& aEntry
: m_ConditionalFormats
)
2421 ScCondFormatData::ScCondFormatData() {}
2423 ScCondFormatData::ScCondFormatData(ScCondFormatData
&&) = default;
2425 ScCondFormatData::~ScCondFormatData() {}
2428 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */