Avoid potential negative array index access to cached text.
[LibreOffice.git] / sc / source / core / data / conditio.cxx
blob24bba7b06ba4976403284b734e32ee506c8125e7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
28 #include <attrib.hxx>
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>
45 #include <memory>
46 #include <numeric>
47 #include <utility>
49 using namespace formula;
51 ScFormatEntry::ScFormatEntry(ScDocument* pDoc):
52 mpDoc(pDoc)
56 bool ScFormatEntry::operator==( const ScFormatEntry& r ) const
58 return IsEqual(r, false);
61 // virtual
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
66 return false;
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 )
83 if (pFormula)
85 FormulaTokenArrayPlainIterator aIter( *pFormula );
86 FormulaToken* t;
87 for( t = aIter.Next(); t; t = aIter.Next() )
89 switch( t->GetType() )
91 case svDoubleRef:
93 ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
94 if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
95 return true;
96 [[fallthrough]];
99 case svSingleRef:
101 ScSingleRefData& rRef1 = *t->GetSingleRef();
102 if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
103 return true;
105 break;
107 case svIndex:
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 ) )
112 return true;
114 break;
116 // #i34474# function result dependent on cell position
117 case svByte:
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
125 return true;
126 default:
128 // added to avoid warnings
132 break;
134 default:
136 // added to avoid warnings
141 return false;
144 namespace {
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()
160 if (!pCondFormat)
161 return;
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;
175 StartListening();
178 ScConditionEntry::ScConditionEntry( const ScConditionEntry& r ) :
179 ScFormatEntry(r.mpDoc),
180 eOp(r.eOp),
181 nOptions(r.nOptions),
182 nVal1(r.nVal1),
183 nVal2(r.nVal2),
184 aStrVal1(r.aStrVal1),
185 aStrVal2(r.aStrVal2),
186 aStrNmsp1(r.aStrNmsp1),
187 aStrNmsp2(r.aStrNmsp2),
188 eTempGrammar1(r.eTempGrammar1),
189 eTempGrammar2(r.eTempGrammar2),
190 bIsStr1(r.bIsStr1),
191 bIsStr2(r.bIsStr2),
192 aSrcPos(r.aSrcPos),
193 aSrcString(r.aSrcString),
194 bRelRef1(r.bRelRef1),
195 bRelRef2(r.bRelRef2),
196 bFirstRun(true),
197 mpListener(new ScFormulaListener(*r.mpDoc)),
198 eConditionType( r.eConditionType ),
199 pCondFormat(r.pCondFormat),
200 mpRepaintTask()
202 // ScTokenArray copy ctor creates a flat copy
203 if (r.pFormula1)
204 pFormula1.reset( new ScTokenArray( *r.pFormula1 ) );
205 if (r.pFormula2)
206 pFormula2.reset( new ScTokenArray( *r.pFormula2 ) );
208 StartListening();
209 // Formula cells are created at IsValid
212 ScConditionEntry::ScConditionEntry( ScDocument& rDocument, const ScConditionEntry& r ) :
213 ScFormatEntry(&rDocument),
214 eOp(r.eOp),
215 nOptions(r.nOptions),
216 nVal1(r.nVal1),
217 nVal2(r.nVal2),
218 aStrVal1(r.aStrVal1),
219 aStrVal2(r.aStrVal2),
220 aStrNmsp1(r.aStrNmsp1),
221 aStrNmsp2(r.aStrNmsp2),
222 eTempGrammar1(r.eTempGrammar1),
223 eTempGrammar2(r.eTempGrammar2),
224 bIsStr1(r.bIsStr1),
225 bIsStr2(r.bIsStr2),
226 aSrcPos(r.aSrcPos),
227 aSrcString(r.aSrcString),
228 bRelRef1(r.bRelRef1),
229 bRelRef2(r.bRelRef2),
230 bFirstRun(true),
231 mpListener(new ScFormulaListener(rDocument)),
232 eConditionType( r.eConditionType),
233 pCondFormat(r.pCondFormat),
234 mpRepaintTask()
236 // Real copy of the formulas (for Ref Undo)
237 if (r.pFormula1)
238 pFormula1 = r.pFormula1->Clone();
239 if (r.pFormula2)
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,
250 Type eType ) :
251 ScFormatEntry(&rDocument),
252 eOp(eOper),
253 nOptions(0),
254 nVal1(0.0),
255 nVal2(0.0),
256 aStrNmsp1(rExprNmsp1),
257 aStrNmsp2(rExprNmsp2),
258 eTempGrammar1(eGrammar1),
259 eTempGrammar2(eGrammar2),
260 bIsStr1(false),
261 bIsStr2(false),
262 aSrcPos(rPos),
263 bRelRef1(false),
264 bRelRef2(false),
265 bFirstRun(true),
266 mpListener(new ScFormulaListener(rDocument)),
267 eConditionType(eType),
268 pCondFormat(nullptr),
269 mpRepaintTask()
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),
280 eOp(eOper),
281 nOptions(0),
282 nVal1(0.0),
283 nVal2(0.0),
284 eTempGrammar1(FormulaGrammar::GRAM_DEFAULT),
285 eTempGrammar2(FormulaGrammar::GRAM_DEFAULT),
286 bIsStr1(false),
287 bIsStr2(false),
288 aSrcPos(rPos),
289 bRelRef1(false),
290 bRelRef2(false),
291 bFirstRun(true),
292 mpListener(new ScFormulaListener(rDocument)),
293 eConditionType(ScFormatEntry::Type::Condition),
294 pCondFormat(nullptr),
295 mpRepaintTask()
297 if ( pArr1 )
299 pFormula1.reset( new ScTokenArray( *pArr1 ) );
300 SimplifyCompiledFormula( pFormula1, nVal1, bIsStr1, aStrVal1 );
301 bRelRef1 = lcl_HasRelRef( mpDoc, pFormula1.get() );
303 if ( pArr2 )
305 pFormula2.reset( new ScTokenArray( *pArr2 ) );
306 SimplifyCompiledFormula( pFormula2, nVal2, bIsStr2, aStrVal2 );
307 bRelRef2 = lcl_HasRelRef( mpDoc, pFormula2.get() );
310 StartListening();
312 // Formula cells are created at IsValid
315 ScConditionEntry::~ScConditionEntry()
319 void ScConditionEntry::SimplifyCompiledFormula( std::unique_ptr<ScTokenArray>& rFormula,
320 double& rVal,
321 bool& rIsStr,
322 OUString& rStrVal )
324 if ( rFormula->GetLen() != 1 )
325 return;
327 // Single (constant number)?
328 FormulaToken* pToken = rFormula->FirstToken();
329 if ( pToken->GetOpCode() != ocPush )
330 return;
332 if ( pToken->GetType() == svDouble )
334 rVal = pToken->GetDouble();
335 rFormula.reset(); // Do not remember as formula
337 else if ( pToken->GetType() == svString )
339 rIsStr = true;
340 rStrVal = pToken->GetString().getString();
341 rFormula.reset(); // Do not remember as formula
345 void ScConditionEntry::SetOperation(ScConditionMode eMode)
347 eOp = 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() )
360 pFormula1.reset();
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)
369 else
371 pFormula1 = aComp.CompileString( rExpr1, rExprNmsp1 );
372 SimplifyCompiledFormula( pFormula1, nVal1, bIsStr1, aStrVal1 );
373 bRelRef1 = lcl_HasRelRef( mpDoc, pFormula1.get() );
377 if ( !rExpr2.isEmpty() )
379 pFormula2.reset();
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)
388 else
390 pFormula2 = aComp.CompileString( rExpr2, rExprNmsp2 );
391 SimplifyCompiledFormula( pFormula2, nVal2, bIsStr2, aStrVal2 );
392 bRelRef2 = lcl_HasRelRef( mpDoc, pFormula2.get() );
397 StartListening();
401 * Create formula cells
403 void ScConditionEntry::MakeCells( const ScAddress& rPos )
405 if ( mpDoc->IsClipOrUndo() ) // Never calculate in the Clipboard!
406 return;
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
430 // (only of valid)
431 if (bSet)
432 nOptions &= ~SC_COND_NOBLANKS;
433 else
434 nOptions |= SC_COND_NOBLANKS;
438 * Delete formula cells, so we re-compile at the next IsValid
440 void ScConditionEntry::CompileAll()
442 pFCell1.reset();
443 pFCell2.reset();
446 void ScConditionEntry::CompileXML()
448 // First parse the formula source position if it was stored as text
449 if ( !aSrcString.isEmpty() )
451 ScAddress aNew;
452 /* XML is always in OOo:A1 format, although R1C1 would be more amenable
453 * to compression */
454 if ( aNew.Parse( aSrcString, *mpDoc ) & ScRefFlags::VALID )
455 aSrcPos = aNew;
456 // if the position is invalid, there isn't much we can do at this time
457 aSrcString.clear();
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?
466 if (pFormula1)
467 mpDoc->CheckLinkFormulaNeedingCheck(*pFormula1);
468 if (pFormula2)
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" );
477 aSrcString = rNew;
480 void ScConditionEntry::SetFormula1( const ScTokenArray& rArray )
482 pFormula1.reset();
483 if( rArray.GetLen() > 0 )
485 pFormula1.reset( new ScTokenArray( rArray ) );
486 SimplifyCompiledFormula(pFormula1, nVal1, bIsStr1, aStrVal1);
487 bRelRef1 = lcl_HasRelRef( mpDoc, pFormula1.get() );
490 StartListening();
493 void ScConditionEntry::SetFormula2( const ScTokenArray& rArray )
495 pFormula2.reset();
496 if( rArray.GetLen() > 0 )
498 pFormula2.reset( new ScTokenArray( rArray ) );
499 SimplifyCompiledFormula(pFormula2, nVal2, bIsStr2, aStrVal2);
500 bRelRef2 = lcl_HasRelRef( mpDoc, pFormula2.get() );
503 StartListening();
506 void ScConditionEntry::UpdateReference( sc::RefUpdateContext& rCxt )
508 if(pCondFormat)
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;
522 if (pFormula1)
524 sc::RefUpdateResult aRes;
525 switch (rCxt.meMode)
527 case URM_INSDEL:
528 aRes = pFormula1->AdjustReferenceOnShift(rCxt, aOldSrcPos);
529 break;
530 case URM_MOVE:
531 aRes = pFormula1->AdjustReferenceOnMove(rCxt, aOldSrcPos, aSrcPos);
532 break;
533 default:
537 if (aRes.mbReferenceModified || bChangedPos)
538 pFCell1.reset(); // is created again in IsValid
541 if (pFormula2)
543 sc::RefUpdateResult aRes;
544 switch (rCxt.meMode)
546 case URM_INSDEL:
547 aRes = pFormula2->AdjustReferenceOnShift(rCxt, aOldSrcPos);
548 break;
549 case URM_MOVE:
550 aRes = pFormula2->AdjustReferenceOnMove(rCxt, aOldSrcPos, aSrcPos);
551 break;
552 default:
556 if (aRes.mbReferenceModified || bChangedPos)
557 pFCell2.reset(); // is created again in IsValid
560 StartListening();
563 void ScConditionEntry::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
565 if (pFormula1)
567 pFormula1->AdjustReferenceOnInsertedTab(rCxt, aSrcPos);
568 pFCell1.reset();
571 if (pFormula2)
573 pFormula2->AdjustReferenceOnInsertedTab(rCxt, aSrcPos);
574 pFCell2.reset();
577 ScRangeUpdater::UpdateInsertTab(aSrcPos, rCxt);
580 void ScConditionEntry::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
582 if (pFormula1)
584 pFormula1->AdjustReferenceOnDeletedTab(rCxt, aSrcPos);
585 pFCell1.reset();
588 if (pFormula2)
590 pFormula2->AdjustReferenceOnDeletedTab(rCxt, aSrcPos);
591 pFCell2.reset();
594 ScRangeUpdater::UpdateDeleteTab(aSrcPos, rCxt);
595 StartListening();
598 void ScConditionEntry::UpdateMoveTab(sc::RefUpdateMoveTabContext& rCxt)
600 sc::RefUpdateResult aResFinal;
601 aResFinal.mnTab = aSrcPos.Tab();
602 if (pFormula1)
604 sc::RefUpdateResult aRes = pFormula1->AdjustReferenceOnMovedTab(rCxt, aSrcPos);
605 if (aRes.mbValueChanged)
606 aResFinal.mnTab = aRes.mnTab;
607 pFCell1.reset();
610 if (pFormula2)
612 sc::RefUpdateResult aRes = pFormula2->AdjustReferenceOnMovedTab(rCxt, aSrcPos);
613 if (aRes.mbValueChanged)
614 aResFinal.mnTab = aRes.mnTab;
615 pFCell2.reset();
618 if (aResFinal.mnTab != aSrcPos.Tab())
619 aSrcPos.SetTab(aResFinal.mnTab);
621 StartListening();
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() );
629 else
630 return !pArr1 && !pArr2; // Both 0? -> the same
633 // virtual
634 bool ScConditionEntry::IsEqual( const ScFormatEntry& rOther, bool bIgnoreSrcPos ) const
636 if (GetType() != rOther.GetType())
637 return false;
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 ));
645 if (!bIgnoreSrcPos)
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 ) )
650 bEq = false;
653 // If not formulas, compare values
654 if ( bEq && !pFormula1 && ( nVal1 != r.nVal1 || aStrVal1 != r.aStrVal1 || bIsStr1 != r.bIsStr1 ) )
655 bEq = false;
656 if ( bEq && !pFormula2 && ( nVal2 != r.nVal2 || aStrVal2 != r.aStrVal2 || bIsStr2 != r.bIsStr2 ) )
657 bEq = false;
659 return bEq;
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 ) )
667 MakeCells( rPos );
669 // Evaluate formulas
670 bool bDirty = false; // 1 and 2 separate?
672 std::optional<ScFormulaCell> oTemp;
673 ScFormulaCell* pEff1 = pFCell1.get();
674 if ( bRelRef1 )
676 if (pFormula1)
677 oTemp.emplace(*mpDoc, rPos, *pFormula1);
678 else
679 oTemp.emplace(*mpDoc, rPos);
680 pEff1 = &*oTemp;
681 pEff1->SetFreeFlying(true);
683 if ( pEff1 )
685 if (!pEff1->IsRunning()) // Don't create 522
687 //TODO: Query Changed instead of Dirty!
688 if (pEff1->GetDirty() && !bRelRef1 && mpDoc->GetAutoCalc())
689 bDirty = true;
690 if (pEff1->IsValue())
692 bIsStr1 = false;
693 nVal1 = pEff1->GetValue();
694 aStrVal1.clear();
696 else
698 bIsStr1 = true;
699 aStrVal1 = pEff1->GetString().getString();
700 nVal1 = 0.0;
704 oTemp.reset();
706 ScFormulaCell* pEff2 = pFCell2.get(); //@ 1!=2
707 if ( bRelRef2 )
709 if (pFormula2)
710 oTemp.emplace(*mpDoc, rPos, *pFormula2);
711 else
712 oTemp.emplace(*mpDoc, rPos);
713 pEff2 = &*oTemp;
714 pEff2->SetFreeFlying(true);
716 if ( pEff2 )
718 if (!pEff2->IsRunning()) // Don't create 522
720 if (pEff2->GetDirty() && !bRelRef2 && mpDoc->GetAutoCalc())
721 bDirty = true;
722 if (pEff2->IsValue())
724 bIsStr2 = false;
725 nVal2 = pEff2->GetValue();
726 aStrVal2.clear();
728 else
730 bIsStr2 = true;
731 aStrVal2 = pEff2->GetString().getString();
732 nVal2 = 0.0;
736 oTemp.reset();
738 // If IsRunning, the last values remain
739 if (bDirty && !bFirstRun)
741 // Repaint everything for dependent formats
742 DataChanged();
745 bFirstRun = false;
748 static bool lcl_GetCellContent( ScRefCellValue& rCell, bool bIsStr1, double& rArg, OUString& rArgStr,
749 const ScDocument* pDoc )
752 if (rCell.isEmpty())
753 return !bIsStr1;
755 bool bVal = true;
757 switch (rCell.getType())
759 case CELLTYPE_VALUE:
760 rArg = rCell.getDouble();
761 break;
762 case CELLTYPE_FORMULA:
764 bVal = rCell.getFormula()->IsValue();
765 if (bVal)
766 rArg = rCell.getFormula()->GetValue();
767 else
768 rArgStr = rCell.getFormula()->GetString().getString();
770 break;
771 case CELLTYPE_STRING:
772 case CELLTYPE_EDIT:
773 bVal = false;
774 if (rCell.getType() == CELLTYPE_STRING)
775 rArgStr = rCell.getSharedString()->getString();
776 else if (rCell.getEditText())
777 rArgStr = ScEditUtil::GetString(*rCell.getEditText(), pDoc);
778 break;
779 default:
783 return bVal;
786 void ScConditionEntry::FillCache() const
788 if(mpCache)
789 return;
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,
809 nCol, nRow, false);
812 for( SCROW r = nRowStart; r <= nRow; r++ )
813 for( SCCOL c = nColStart; c <= nCol; c++ )
815 ScRefCellValue aCell(*mpDoc, ScAddress(c, r, nTab));
816 if (aCell.isEmpty())
817 continue;
819 double nVal = 0.0;
820 OUString aStr;
821 if (!lcl_GetCellContent(aCell, false, nVal, aStr, mpDoc))
823 std::pair<ScConditionEntryCache::StringCacheType::iterator, bool> aResult =
824 mpCache->maStrings.emplace(aStr, 1);
826 if(!aResult.second)
827 aResult.first->second++;
829 else
831 std::pair<ScConditionEntryCache::ValueCacheType::iterator, bool> aResult =
832 mpCache->maValues.emplace(nVal, 1);
834 if(!aResult.second)
835 aResult.first->second++;
837 ++(mpCache->nValueItems);
843 bool ScConditionEntry::IsDuplicate( double nArg, const OUString& rStr ) const
845 FillCache();
847 if(rStr.isEmpty())
849 ScConditionEntryCache::ValueCacheType::iterator itr = mpCache->maValues.find(nArg);
850 if(itr == mpCache->maValues.end())
851 return false;
852 else
854 return itr->second > 1;
857 else
859 ScConditionEntryCache::StringCacheType::iterator itr = mpCache->maStrings.find(rStr);
860 if(itr == mpCache->maStrings.end())
861 return false;
862 else
864 return itr->second > 1;
869 bool ScConditionEntry::IsTopNElement( double nArg ) const
871 FillCache();
873 if(mpCache->nValueItems <= nVal1)
874 return true;
876 size_t nCells = 0;
877 for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr = mpCache->maValues.rbegin(),
878 itrEnd = mpCache->maValues.rend(); itr != itrEnd; ++itr)
880 if(nCells >= nVal1)
881 return false;
882 if(itr->first <= nArg)
883 return true;
884 nCells += itr->second;
887 return true;
890 bool ScConditionEntry::IsBottomNElement( double nArg ) const
892 FillCache();
894 if(mpCache->nValueItems <= nVal1)
895 return true;
897 size_t nCells = 0;
898 for(const auto& [rVal, rCount] : mpCache->maValues)
900 if(nCells >= nVal1)
901 return false;
902 if(rVal >= nArg)
903 return true;
904 nCells += rCount;
907 return true;
910 bool ScConditionEntry::IsTopNPercent( double nArg ) const
912 FillCache();
914 size_t nCells = 0;
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)
920 return false;
921 if(itr->first <= nArg)
922 return true;
923 nCells += itr->second;
926 return true;
929 bool ScConditionEntry::IsBottomNPercent( double nArg ) const
931 FillCache();
933 size_t nCells = 0;
934 size_t nLimitCells = static_cast<size_t>(mpCache->nValueItems*nVal1/100);
935 for(const auto& [rVal, rCount] : mpCache->maValues)
937 if(nCells >= nLimitCells)
938 return false;
939 if(rVal >= nArg)
940 return true;
941 nCells += rCount;
944 return true;
947 bool ScConditionEntry::IsBelowAverage( double nArg, bool bEqual ) const
949 FillCache();
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; });
955 if(bEqual)
956 return (nArg <= nSum/mpCache->nValueItems);
957 else
958 return (nArg < nSum/mpCache->nValueItems);
961 bool ScConditionEntry::IsAboveAverage( double nArg, bool bEqual ) const
963 FillCache();
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; });
969 if(bEqual)
970 return (nArg >= nSum/mpCache->nValueItems);
971 else
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)
982 return true;
985 return false;
988 bool ScConditionEntry::IsValid( double nArg, const ScAddress& rPos ) const
990 // Interpret must already have been called
991 if ( bIsStr1 )
993 switch( eOp )
995 case ScConditionMode::BeginsWith:
996 case ScConditionMode::EndsWith:
997 case ScConditionMode::ContainsText:
998 case ScConditionMode::NotContainsText:
999 break;
1000 case ScConditionMode::NotEqual:
1001 return true;
1002 default:
1003 return false;
1007 if ( eOp == ScConditionMode::Between || eOp == ScConditionMode::NotBetween )
1008 if ( bIsStr2 )
1009 return false;
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;
1021 switch (eOp)
1023 case ScConditionMode::NONE:
1024 break; // Always sal_False
1025 case ScConditionMode::Equal:
1026 bValid = ::rtl::math::approxEqual( nArg, nComp1 );
1027 break;
1028 case ScConditionMode::NotEqual:
1029 bValid = !::rtl::math::approxEqual( nArg, nComp1 );
1030 break;
1031 case ScConditionMode::Greater:
1032 bValid = ( nArg > nComp1 ) && !::rtl::math::approxEqual( nArg, nComp1 );
1033 break;
1034 case ScConditionMode::EqGreater:
1035 bValid = ( nArg >= nComp1 ) || ::rtl::math::approxEqual( nArg, nComp1 );
1036 break;
1037 case ScConditionMode::Less:
1038 bValid = ( nArg < nComp1 ) && !::rtl::math::approxEqual( nArg, nComp1 );
1039 break;
1040 case ScConditionMode::EqLess:
1041 bValid = ( nArg <= nComp1 ) || ::rtl::math::approxEqual( nArg, nComp1 );
1042 break;
1043 case ScConditionMode::Between:
1044 bValid = ( nArg >= nComp1 && nArg <= nComp2 ) ||
1045 ::rtl::math::approxEqual( nArg, nComp1 ) || ::rtl::math::approxEqual( nArg, nComp2 );
1046 break;
1047 case ScConditionMode::NotBetween:
1048 bValid = ( nArg < nComp1 || nArg > nComp2 ) &&
1049 !::rtl::math::approxEqual( nArg, nComp1 ) && !::rtl::math::approxEqual( nArg, nComp2 );
1050 break;
1051 case ScConditionMode::Duplicate:
1052 case ScConditionMode::NotDuplicate:
1053 if( pCondFormat )
1055 bValid = IsDuplicate( nArg, OUString() );
1056 if( eOp == ScConditionMode::NotDuplicate )
1057 bValid = !bValid;
1059 break;
1060 case ScConditionMode::Direct:
1061 bValid = nComp1 != 0.0;
1062 break;
1063 case ScConditionMode::Top10:
1064 bValid = IsTopNElement( nArg );
1065 break;
1066 case ScConditionMode::Bottom10:
1067 bValid = IsBottomNElement( nArg );
1068 break;
1069 case ScConditionMode::TopPercent:
1070 bValid = IsTopNPercent( nArg );
1071 break;
1072 case ScConditionMode::BottomPercent:
1073 bValid = IsBottomNPercent( nArg );
1074 break;
1075 case ScConditionMode::AboveAverage:
1076 case ScConditionMode::AboveEqualAverage:
1077 bValid = IsAboveAverage( nArg, eOp == ScConditionMode::AboveEqualAverage );
1078 break;
1079 case ScConditionMode::BelowAverage:
1080 case ScConditionMode::BelowEqualAverage:
1081 bValid = IsBelowAverage( nArg, eOp == ScConditionMode::BelowEqualAverage );
1082 break;
1083 case ScConditionMode::Error:
1084 case ScConditionMode::NoError:
1085 bValid = IsError( rPos );
1086 if( eOp == ScConditionMode::NoError )
1087 bValid = !bValid;
1088 break;
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);
1096 else
1098 OUString aStr2 = OUString::number(nArg);
1099 bValid = aStr2.startsWith(aStrVal1);
1101 break;
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);
1109 else
1111 OUString aStr2 = OUString::number(nArg);
1112 bValid = aStr2.endsWith(aStrVal1);
1114 break;
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;
1123 else
1125 OUString aStr2 = OUString::number(nArg);
1126 bValid = aStr2.indexOf(aStrVal1) != -1;
1129 if( eOp == ScConditionMode::NotContainsText )
1130 bValid = !bValid;
1131 break;
1132 default:
1133 SAL_WARN("sc", "unknown operation at ScConditionEntry");
1134 break;
1136 return bValid;
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 )
1152 bValid = !bValid;
1153 return bValid;
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".
1163 if (!bIsStr1)
1164 return ( eOp == ScConditionMode::NotEqual );
1165 if ( eOp == ScConditionMode::Between || eOp == ScConditionMode::NotBetween )
1166 if ( !bIsStr2 )
1167 return false;
1169 OUString aUpVal1( aStrVal1 ); //TODO: As a member? (Also set in Interpret)
1170 OUString aUpVal2( aStrVal2 );
1172 switch ( eOp )
1174 case ScConditionMode::Equal:
1175 bValid = ScGlobal::GetTransliteration().isEqual(aUpVal1, rArg);
1176 break;
1177 case ScConditionMode::NotEqual:
1178 bValid = !ScGlobal::GetTransliteration().isEqual(aUpVal1, rArg);
1179 break;
1180 case ScConditionMode::TopPercent:
1181 case ScConditionMode::BottomPercent:
1182 case ScConditionMode::Top10:
1183 case ScConditionMode::Bottom10:
1184 case ScConditionMode::AboveAverage:
1185 case ScConditionMode::BelowAverage:
1186 return false;
1187 case ScConditionMode::BeginsWith:
1188 bValid = ScGlobal::GetTransliteration().isMatch(aUpVal1, rArg);
1189 break;
1190 case ScConditionMode::EndsWith:
1192 sal_Int32 nStart = rArg.getLength();
1193 const sal_Int32 nLen = aUpVal1.getLength();
1194 if (nLen > nStart)
1195 bValid = false;
1196 else
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);
1204 break;
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)
1213 bValid = !bValid;
1215 break;
1216 default:
1218 sal_Int32 nCompare = ScGlobal::GetCollator().compareString(
1219 rArg, aUpVal1 );
1220 switch ( eOp )
1222 case ScConditionMode::Greater:
1223 bValid = ( nCompare > 0 );
1224 break;
1225 case ScConditionMode::EqGreater:
1226 bValid = ( nCompare >= 0 );
1227 break;
1228 case ScConditionMode::Less:
1229 bValid = ( nCompare < 0 );
1230 break;
1231 case ScConditionMode::EqLess:
1232 bValid = ( nCompare <= 0 );
1233 break;
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 )
1241 bValid = !bValid;
1242 break;
1244 default:
1245 SAL_WARN("sc", "unknown operation in ScConditionEntry");
1246 bValid = false;
1247 break;
1251 return bValid;
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;
1261 double nArg = 0.0;
1262 OUString aArgStr;
1263 bool bVal = lcl_GetCellContent( rCell, bIsStr1, nArg, aArgStr, mpDoc );
1264 if (bVal)
1265 return IsValid( nArg, rPos );
1266 else
1267 return IsValidStr( aArgStr, rPos );
1270 OUString ScConditionEntry::GetExpression( const ScAddress& rCursor, sal_uInt16 nIndex,
1271 sal_uInt32 nNumFmt,
1272 const FormulaGrammar::Grammar eGrammar ) const
1274 assert( nIndex <= 1);
1275 OUString aRet;
1277 if ( FormulaGrammar::isEnglish( eGrammar) && nNumFmt == 0 )
1278 nNumFmt = mpDoc->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US );
1280 if ( nIndex==0 )
1282 if ( pFormula1 )
1284 ScCompiler aComp(*mpDoc, rCursor, *pFormula1, eGrammar);
1285 OUStringBuffer aBuffer;
1286 aComp.CreateStringFromTokenArray( aBuffer );
1287 aRet = aBuffer.makeStringAndClear();
1289 else if (bIsStr1)
1291 aRet = "\"" + aStrVal1 + "\"";
1293 else
1294 mpDoc->GetFormatTable()->GetInputLineString(nVal1, nNumFmt, aRet);
1296 else if ( nIndex==1 )
1298 if ( pFormula2 )
1300 ScCompiler aComp(*mpDoc, rCursor, *pFormula2, eGrammar);
1301 OUStringBuffer aBuffer;
1302 aComp.CreateStringFromTokenArray( aBuffer );
1303 aRet = aBuffer.makeStringAndClear();
1305 else if (bIsStr2)
1307 aRet = "\"" + aStrVal2 + "\"";
1309 else
1310 mpDoc->GetFormatTable()->GetInputLineString(nVal2, nNumFmt, aRet);
1313 return aRet;
1316 std::unique_ptr<ScTokenArray> ScConditionEntry::CreateFlatCopiedTokenArray( sal_uInt16 nIndex ) const
1318 assert(nIndex <= 1);
1319 std::unique_ptr<ScTokenArray> pRet;
1321 if ( nIndex==0 )
1323 if ( pFormula1 )
1324 pRet.reset(new ScTokenArray( *pFormula1 ));
1325 else
1327 pRet.reset(new ScTokenArray(*mpDoc));
1328 if (bIsStr1)
1330 svl::SharedStringPool& rSPool = mpDoc->GetSharedStringPool();
1331 pRet->AddString(rSPool.intern(aStrVal1));
1333 else
1334 pRet->AddDouble( nVal1 );
1337 else if ( nIndex==1 )
1339 if ( pFormula2 )
1340 pRet.reset(new ScTokenArray( *pFormula2 ));
1341 else
1343 pRet.reset(new ScTokenArray(*mpDoc));
1344 if (bIsStr2)
1346 svl::SharedStringPool& rSPool = mpDoc->GetSharedStringPool();
1347 pRet->AddString(rSPool.intern(aStrVal2));
1349 else
1350 pRet->AddDouble( nVal2 );
1354 return pRet;
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();
1369 if (pFormula)
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
1406 return aValidPos;
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();
1420 if (pFormula)
1421 bAllMarked = mpDoc->MarkUsedExternalReferences(*pFormula, aSrcPos);
1423 return bAllMarked;
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;
1438 break;
1439 case css::sheet::ConditionOperator2::LESS:
1440 eMode = ScConditionMode::Less;
1441 break;
1442 case css::sheet::ConditionOperator2::GREATER:
1443 eMode = ScConditionMode::Greater;
1444 break;
1445 case css::sheet::ConditionOperator2::LESS_EQUAL:
1446 eMode = ScConditionMode::EqLess;
1447 break;
1448 case css::sheet::ConditionOperator2::GREATER_EQUAL:
1449 eMode = ScConditionMode::EqGreater;
1450 break;
1451 case css::sheet::ConditionOperator2::NOT_EQUAL:
1452 eMode = ScConditionMode::NotEqual;
1453 break;
1454 case css::sheet::ConditionOperator2::BETWEEN:
1455 eMode = ScConditionMode::Between;
1456 break;
1457 case css::sheet::ConditionOperator2::NOT_BETWEEN:
1458 eMode = ScConditionMode::NotBetween;
1459 break;
1460 case css::sheet::ConditionOperator2::FORMULA:
1461 eMode = ScConditionMode::Direct;
1462 break;
1463 case css::sheet::ConditionOperator2::DUPLICATE:
1464 eMode = ScConditionMode::Duplicate;
1465 break;
1466 case css::sheet::ConditionOperator2::NOT_DUPLICATE:
1467 eMode = ScConditionMode::NotDuplicate;
1468 break;
1469 default:
1470 break;
1472 return eMode;
1475 void ScConditionEntry::startRendering()
1477 mpCache.reset();
1480 void ScConditionEntry::endRendering()
1482 mpCache.reset();
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,
1493 OUString aStyle,
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,
1507 OUString aStyle ) :
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)
1527 // virtual
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
1540 if ( pCondFormat )
1541 pCondFormat->DoRepaint();
1544 ScFormatEntry* ScCondFormatEntry::Clone( ScDocument* pDoc ) const
1546 return new ScCondFormatEntry( *pDoc, *this );
1549 void ScConditionEntry::CalcAll()
1551 if (pFCell1 || pFCell2)
1553 if (pFCell1)
1554 pFCell1->SetDirty();
1555 if (pFCell2)
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.
1580 return false;
1582 if( !mpCache )
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);
1594 switch(meType)
1596 case condformat::TODAY:
1597 if( nCurrentDate == nCellDate )
1598 return true;
1599 break;
1600 case condformat::TOMORROW:
1601 if( nCurrentDate == nCellDate -1 )
1602 return true;
1603 break;
1604 case condformat::YESTERDAY:
1605 if( nCurrentDate == nCellDate + 1)
1606 return true;
1607 break;
1608 case condformat::LAST7DAYS:
1609 if( nCurrentDate >= nCellDate && nCurrentDate - 7 < nCellDate )
1610 return true;
1611 break;
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 );
1621 else
1623 Date aBegin(rActDate - 8);
1624 Date aEnd(rActDate - 1);
1625 return aCellDate.IsBetween( aBegin, aEnd );
1628 break;
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 );
1638 else
1640 Date aEnd( rActDate + 6);
1641 return aCellDate.IsBetween( rActDate, aEnd );
1644 break;
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)) );
1653 else
1655 return aCellDate.IsBetween( rActDate + 7, rActDate + 13 );
1658 break;
1659 case condformat::LASTMONTH:
1660 if( rActDate.GetMonth() == 1 )
1662 if( aCellDate.GetMonth() == 12 && rActDate.GetYear() == aCellDate.GetNextYear() )
1663 return true;
1665 else if( rActDate.GetYear() == aCellDate.GetYear() )
1667 if( rActDate.GetMonth() == aCellDate.GetMonth() + 1)
1668 return true;
1670 break;
1671 case condformat::THISMONTH:
1672 if( rActDate.GetYear() == aCellDate.GetYear() )
1674 if( rActDate.GetMonth() == aCellDate.GetMonth() )
1675 return true;
1677 break;
1678 case condformat::NEXTMONTH:
1679 if( rActDate.GetMonth() == 12 )
1681 if( aCellDate.GetMonth() == 1 && rActDate.GetYear() == aCellDate.GetYear() - 1 )
1682 return true;
1684 else if( rActDate.GetYear() == aCellDate.GetYear() )
1686 if( rActDate.GetMonth() == aCellDate.GetMonth() - 1)
1687 return true;
1689 break;
1690 case condformat::LASTYEAR:
1691 if( rActDate.GetYear() == aCellDate.GetNextYear() )
1692 return true;
1693 break;
1694 case condformat::THISYEAR:
1695 if( rActDate.GetYear() == aCellDate.GetYear() )
1696 return true;
1697 break;
1698 case condformat::NEXTYEAR:
1699 if( rActDate.GetYear() == aCellDate.GetYear() - 1 )
1700 return true;
1701 break;
1704 return false;
1707 void ScCondDateFormatEntry::SetDateType( condformat::ScCondFormatDateType eType )
1709 meType = 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()
1724 mpCache.reset();
1727 void ScCondDateFormatEntry::endRendering()
1729 mpCache.reset();
1732 ScColorFormatCache::ScColorFormatCache(ScDocument& rDoc, const ScRangeList& rRanges) :
1733 mrDoc(rDoc)
1735 if (mrDoc.IsClipOrUndo())
1736 return;
1738 for (const ScRange& rRange: rRanges)
1739 mrDoc.StartListeningArea(rRange, false, this);
1742 ScColorFormatCache::~ScColorFormatCache()
1744 if (mrDoc.IsClipOrUndo())
1745 return;
1747 EndListeningAll();
1750 void ScColorFormatCache::Notify(const SfxHint& rHint)
1752 if (rHint.GetId() == SfxHintId::Dying)
1754 EndListeningAll();
1755 return;
1758 maValues.clear();
1762 ScConditionalFormat::ScConditionalFormat(sal_uInt32 nNewKey, ScDocument* pDocument) :
1763 pDoc( pDocument ),
1764 nKey( nNewKey )
1768 std::unique_ptr<ScConditionalFormat> ScConditionalFormat::Clone(ScDocument* pNewDoc) const
1770 // Real copy of the formula (for Ref Undo/between documents)
1771 if (!pNewDoc)
1772 pNewDoc = pDoc;
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());
1784 return pNew;
1787 bool ScConditionalFormat::EqualEntries( const ScConditionalFormat& r, bool bIgnoreSrcPos ) const
1789 if( size() != r.size())
1790 return false;
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);
1798 return false;
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
1803 return true;
1806 void ScConditionalFormat::SetRange( const ScRangeList& rRanges )
1808 maRanges = rRanges;
1809 SAL_WARN_IF(maRanges.empty(), "sc", "the conditional format range is empty! will result in a crash later!");
1810 ResetCache();
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);
1824 DoRepaint();
1828 bool ScConditionalFormat::IsEmpty() const
1830 return maEntries.empty();
1833 size_t ScConditionalFormat::size() const
1835 return maEntries.size();
1838 ScDocument* ScConditionalFormat::GetDocument()
1840 return pDoc;
1843 ScConditionalFormat::~ScConditionalFormat()
1847 const ScFormatEntry* ScConditionalFormat::GetEntry( sal_uInt16 nPos ) const
1849 if ( nPos < size() )
1850 return maEntries[nPos].get();
1851 else
1852 return nullptr;
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();
1874 return OUString();
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();
1912 return aData;
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);
1948 else
1950 for (auto& rxEntry : maEntries)
1951 rxEntry->UpdateReference(rCxt);
1952 maRanges.UpdateReference(rCxt.meMode, pDoc, rCxt.maRange, rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
1955 ResetCache();
1958 void ScConditionalFormat::InsertRow(SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, SCROW nRowPos, SCSIZE nSize)
1960 maRanges.InsertRow(nTab, nColStart, nColEnd, nRowPos, nSize);
1961 ResetCache();
1964 void ScConditionalFormat::InsertCol(SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, SCCOL nColPos, SCSIZE nSize)
1966 maRanges.InsertCol(nTab, nRowStart, nRowEnd, nColPos, nSize);
1967 ResetCache();
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)
1979 // Unaffected.
1980 continue;
1982 rRange.aStart.IncTab(rCxt.mnSheets);
1983 rRange.aEnd.IncTab(rCxt.mnSheets);
1986 ResetCache();
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.
2002 continue;
2004 if (nTab <= rCxt.mnDeletePos+rCxt.mnSheets-1)
2006 // On the deleted sheet(s).
2007 rRange.aStart.SetTab(-1);
2008 rRange.aEnd.SetTab(-1);
2009 continue;
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);
2017 ResetCache();
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)
2034 continue;
2037 if (nTab == rCxt.mnOldPos)
2039 rRange.aStart.SetTab(rCxt.mnNewPos);
2040 rRange.aEnd.SetTab(rCxt.mnNewPos);
2041 continue;
2044 if (rCxt.mnNewPos < rCxt.mnOldPos)
2046 rRange.aStart.IncTab();
2047 rRange.aEnd.IncTab();
2049 else
2051 rRange.aStart.IncTab(-1);
2052 rRange.aEnd.IncTab(-1);
2056 ResetCache();
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())
2065 return;
2067 SCTAB nTab = maRanges[0].aStart.Tab();
2068 maRanges.DeleteArea( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
2069 ResetCache();
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();
2093 if (bAllMarked)
2094 break;
2097 return bAllMarked;
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);
2132 rFormat.CalcAll();
2137 void ScConditionalFormat::ResetCache() const
2139 if (!maRanges.empty() && pDoc)
2140 mpCache = std::make_unique<ScColorFormatCache>(*pDoc, maRanges);
2141 else
2142 mpCache.reset();
2145 void ScConditionalFormat::SetCache(const std::vector<double>& aValues) const
2147 if (!mpCache)
2148 ResetCache();
2149 if (mpCache)
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())
2179 return itr->get();
2181 SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
2182 return nullptr;
2185 const ScConditionalFormat* ScConditionalFormatList::GetFormat( sal_uInt32 nKey ) const
2187 auto itr = m_ConditionalFormats.find(nKey);
2188 if (itr != m_ConditionalFormats.end())
2189 return itr->get();
2191 SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
2192 return nullptr;
2195 void ScConditionalFormatList::CompileAll()
2197 for (auto const& it : m_ConditionalFormats)
2199 it->CompileAll();
2203 void ScConditionalFormatList::CompileXML()
2205 for (auto const& it : m_ConditionalFormats)
2207 it->CompileXML();
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
2221 CheckAllEntries();
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)
2275 bool bValid = true;
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())
2283 bValid = false;
2284 if (rLink.IsSet())
2285 rLink.Call(itr->get());
2286 itr = m_ConditionalFormats.erase(itr);
2288 else
2289 ++itr;
2292 return bValid;
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 );
2300 CheckAllEntries();
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
2325 ScRangeList aRange;
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]);
2334 return aRange;
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();
2351 if (rRange.empty())
2352 continue;
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);
2372 if (itr != end())
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)
2388 it->endRendering();
2392 void ScConditionalFormatList::updateValues()
2394 for (auto const& it : m_ConditionalFormats)
2396 it->updateValues();
2400 void ScConditionalFormatList::clear()
2402 m_ConditionalFormats.clear();
2405 sal_uInt32 ScConditionalFormatList::getMaxKey() const
2407 if (m_ConditionalFormats.empty())
2408 return 0;
2409 return (*m_ConditionalFormats.rbegin())->GetKey();
2412 void ScConditionalFormatList::CalcAll()
2414 for (const auto& aEntry : m_ConditionalFormats)
2416 aEntry->CalcAll();
2421 ScCondFormatData::ScCondFormatData() {}
2423 ScCondFormatData::ScCondFormatData(ScCondFormatData&&) = default;
2425 ScCondFormatData::~ScCondFormatData() {}
2428 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */