tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / core / data / conditio.cxx
blob97b1c73b0e0854093c400d2b6f95417aec1d2da5
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;
437 void ScConditionEntry::SetCaseSensitive(bool bSet)
439 // The bit SC_COND_CASESENS is set if validation compare is case sensitive
440 // (only of valid)
441 if (bSet)
442 nOptions |= SC_COND_CASESENS;
443 else
444 nOptions &= ~SC_COND_CASESENS;
448 * Delete formula cells, so we re-compile at the next IsValid
450 void ScConditionEntry::CompileAll()
452 pFCell1.reset();
453 pFCell2.reset();
456 void ScConditionEntry::CompileXML()
458 // First parse the formula source position if it was stored as text
459 if ( !aSrcString.isEmpty() )
461 ScAddress aNew;
462 /* XML is always in OOo:A1 format, although R1C1 would be more amenable
463 * to compression */
464 if ( aNew.Parse( aSrcString, *mpDoc ) & ScRefFlags::VALID )
465 aSrcPos = aNew;
466 // if the position is invalid, there isn't much we can do at this time
467 aSrcString.clear();
470 // Convert the text tokens that were created during XML import into real tokens.
471 Compile( GetExpression(aSrcPos, 0, 0, eTempGrammar1),
472 GetExpression(aSrcPos, 1, 0, eTempGrammar2),
473 aStrNmsp1, aStrNmsp2, eTempGrammar1, eTempGrammar2, true );
475 // Importing ocDde/ocWebservice?
476 if (pFormula1)
477 mpDoc->CheckLinkFormulaNeedingCheck(*pFormula1);
478 if (pFormula2)
479 mpDoc->CheckLinkFormulaNeedingCheck(*pFormula2);
482 void ScConditionEntry::SetSrcString( const OUString& rNew )
484 // aSrcString is only evaluated in CompileXML
485 SAL_WARN_IF( !mpDoc->IsImportingXML(), "sc", "SetSrcString is only valid for XML import" );
487 aSrcString = rNew;
490 void ScConditionEntry::SetFormula1( const ScTokenArray& rArray )
492 pFormula1.reset();
493 if( rArray.GetLen() > 0 )
495 pFormula1.reset( new ScTokenArray( rArray ) );
496 SimplifyCompiledFormula(pFormula1, nVal1, bIsStr1, aStrVal1);
497 bRelRef1 = lcl_HasRelRef( mpDoc, pFormula1.get() );
500 StartListening();
503 void ScConditionEntry::SetFormula2( const ScTokenArray& rArray )
505 pFormula2.reset();
506 if( rArray.GetLen() > 0 )
508 pFormula2.reset( new ScTokenArray( rArray ) );
509 SimplifyCompiledFormula(pFormula2, nVal2, bIsStr2, aStrVal2);
510 bRelRef2 = lcl_HasRelRef( mpDoc, pFormula2.get() );
513 StartListening();
516 void ScConditionEntry::UpdateReference( sc::RefUpdateContext& rCxt )
518 if(pCondFormat)
519 aSrcPos = pCondFormat->GetRange().Combine().aStart;
520 ScAddress aOldSrcPos = aSrcPos;
521 bool bChangedPos = false;
522 if (rCxt.meMode == URM_INSDEL && rCxt.maRange.Contains(aSrcPos))
524 ScAddress aErrorPos( ScAddress::UNINITIALIZED );
525 if (!aSrcPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, *mpDoc))
527 assert(!"can't move ScConditionEntry");
529 bChangedPos = aSrcPos != aOldSrcPos;
532 if (pFormula1)
534 sc::RefUpdateResult aRes;
535 switch (rCxt.meMode)
537 case URM_INSDEL:
538 aRes = pFormula1->AdjustReferenceOnShift(rCxt, aOldSrcPos);
539 break;
540 case URM_MOVE:
541 aRes = pFormula1->AdjustReferenceOnMove(rCxt, aOldSrcPos, aSrcPos);
542 break;
543 default:
547 if (aRes.mbReferenceModified || bChangedPos)
548 pFCell1.reset(); // is created again in IsValid
551 if (pFormula2)
553 sc::RefUpdateResult aRes;
554 switch (rCxt.meMode)
556 case URM_INSDEL:
557 aRes = pFormula2->AdjustReferenceOnShift(rCxt, aOldSrcPos);
558 break;
559 case URM_MOVE:
560 aRes = pFormula2->AdjustReferenceOnMove(rCxt, aOldSrcPos, aSrcPos);
561 break;
562 default:
566 if (aRes.mbReferenceModified || bChangedPos)
567 pFCell2.reset(); // is created again in IsValid
570 StartListening();
573 void ScConditionEntry::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
575 if (pFormula1)
577 pFormula1->AdjustReferenceOnInsertedTab(rCxt, aSrcPos);
578 pFCell1.reset();
581 if (pFormula2)
583 pFormula2->AdjustReferenceOnInsertedTab(rCxt, aSrcPos);
584 pFCell2.reset();
587 ScRangeUpdater::UpdateInsertTab(aSrcPos, rCxt);
590 void ScConditionEntry::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
592 if (pFormula1)
594 pFormula1->AdjustReferenceOnDeletedTab(rCxt, aSrcPos);
595 pFCell1.reset();
598 if (pFormula2)
600 pFormula2->AdjustReferenceOnDeletedTab(rCxt, aSrcPos);
601 pFCell2.reset();
604 ScRangeUpdater::UpdateDeleteTab(aSrcPos, rCxt);
605 StartListening();
608 void ScConditionEntry::UpdateMoveTab(sc::RefUpdateMoveTabContext& rCxt)
610 sc::RefUpdateResult aResFinal;
611 aResFinal.mnTab = aSrcPos.Tab();
612 if (pFormula1)
614 sc::RefUpdateResult aRes = pFormula1->AdjustReferenceOnMovedTab(rCxt, aSrcPos);
615 if (aRes.mbValueChanged)
616 aResFinal.mnTab = aRes.mnTab;
617 pFCell1.reset();
620 if (pFormula2)
622 sc::RefUpdateResult aRes = pFormula2->AdjustReferenceOnMovedTab(rCxt, aSrcPos);
623 if (aRes.mbValueChanged)
624 aResFinal.mnTab = aRes.mnTab;
625 pFCell2.reset();
628 if (aResFinal.mnTab != aSrcPos.Tab())
629 aSrcPos.SetTab(aResFinal.mnTab);
631 StartListening();
634 static bool lcl_IsEqual( const std::unique_ptr<ScTokenArray>& pArr1, const std::unique_ptr<ScTokenArray>& pArr2 )
636 // We only compare the non-RPN array
637 if ( pArr1 && pArr2 )
638 return pArr1->EqualTokens( pArr2.get() );
639 else
640 return !pArr1 && !pArr2; // Both 0? -> the same
643 // virtual
644 bool ScConditionEntry::IsEqual( const ScFormatEntry& rOther, bool bIgnoreSrcPos ) const
646 if (GetType() != rOther.GetType())
647 return false;
649 const ScConditionEntry& r = static_cast<const ScConditionEntry&>(rOther);
651 if (eOp != r.eOp || nOptions != r.nOptions
652 || !lcl_IsEqual(pFormula1, r.pFormula1) || !lcl_IsEqual(pFormula2, r.pFormula2))
653 return false;
655 if (!bIgnoreSrcPos)
657 // for formulas, the reference positions must be compared, too
658 // (including aSrcString, for inserting the entries during XML import)
659 if ( ( pFormula1 || pFormula2 ) && ( aSrcPos != r.aSrcPos || aSrcString != r.aSrcString ) )
660 return false;
663 // If not formulas, compare values
664 if ( !pFormula1 && ( nVal1 != r.nVal1 || aStrVal1 != r.aStrVal1 || bIsStr1 != r.bIsStr1 ) )
665 return false;
666 if ( !pFormula2 && ( nVal2 != r.nVal2 || aStrVal2 != r.aStrVal2 || bIsStr2 != r.bIsStr2 ) )
667 return false;
669 return true;
672 void ScConditionEntry::Interpret( const ScAddress& rPos )
674 // Create formula cells
675 // Note: New Broadcaster (Note cells) may be inserted into the document!
676 if ( ( pFormula1 && !pFCell1 ) || ( pFormula2 && !pFCell2 ) )
677 MakeCells( rPos );
679 // Evaluate formulas
680 bool bDirty = false; // 1 and 2 separate?
682 std::optional<ScFormulaCell> oTemp;
683 ScFormulaCell* pEff1 = pFCell1.get();
684 if ( bRelRef1 )
686 if (pFormula1)
687 oTemp.emplace(*mpDoc, rPos, *pFormula1);
688 else
689 oTemp.emplace(*mpDoc, rPos);
690 pEff1 = &*oTemp;
691 pEff1->SetFreeFlying(true);
693 if ( pEff1 )
695 if (!pEff1->IsRunning()) // Don't create 522
697 //TODO: Query Changed instead of Dirty!
698 if (pEff1->GetDirty() && !bRelRef1 && mpDoc->GetAutoCalc())
699 bDirty = true;
700 if (pEff1->IsValue())
702 bIsStr1 = false;
703 nVal1 = pEff1->GetValue();
704 aStrVal1.clear();
706 else
708 bIsStr1 = true;
709 aStrVal1 = pEff1->GetString().getString();
710 nVal1 = 0.0;
714 oTemp.reset();
716 ScFormulaCell* pEff2 = pFCell2.get(); //@ 1!=2
717 if ( bRelRef2 )
719 if (pFormula2)
720 oTemp.emplace(*mpDoc, rPos, *pFormula2);
721 else
722 oTemp.emplace(*mpDoc, rPos);
723 pEff2 = &*oTemp;
724 pEff2->SetFreeFlying(true);
726 if ( pEff2 )
728 if (!pEff2->IsRunning()) // Don't create 522
730 if (pEff2->GetDirty() && !bRelRef2 && mpDoc->GetAutoCalc())
731 bDirty = true;
732 if (pEff2->IsValue())
734 bIsStr2 = false;
735 nVal2 = pEff2->GetValue();
736 aStrVal2.clear();
738 else
740 bIsStr2 = true;
741 aStrVal2 = pEff2->GetString().getString();
742 nVal2 = 0.0;
746 oTemp.reset();
748 // If IsRunning, the last values remain
749 if (bDirty && !bFirstRun)
751 // Repaint everything for dependent formats
752 DataChanged();
755 bFirstRun = false;
758 static bool lcl_GetCellContent( ScRefCellValue& rCell, bool bIsStr1, double& rArg, OUString& rArgStr,
759 const ScDocument* pDoc )
762 if (rCell.isEmpty())
763 return !bIsStr1;
765 bool bVal = true;
767 switch (rCell.getType())
769 case CELLTYPE_VALUE:
770 rArg = rCell.getDouble();
771 break;
772 case CELLTYPE_FORMULA:
774 bVal = rCell.getFormula()->IsValue();
775 if (bVal)
776 rArg = rCell.getFormula()->GetValue();
777 else
778 rArgStr = rCell.getFormula()->GetString().getString();
780 break;
781 case CELLTYPE_STRING:
782 case CELLTYPE_EDIT:
783 bVal = false;
784 if (rCell.getType() == CELLTYPE_STRING)
785 rArgStr = rCell.getSharedString()->getString();
786 else if (rCell.getEditText())
787 rArgStr = ScEditUtil::GetString(*rCell.getEditText(), pDoc);
788 break;
789 default:
793 return bVal;
796 void ScConditionEntry::FillCache() const
798 if(mpCache)
799 return;
801 const ScRangeList& rRanges = pCondFormat->GetRange();
802 mpCache.reset(new ScConditionEntryCache);
803 size_t nListCount = rRanges.size();
804 for( size_t i = 0; i < nListCount; i++ )
806 const ScRange & rRange = rRanges[i];
807 SCROW nRow = rRange.aEnd.Row();
808 SCCOL nCol = rRange.aEnd.Col();
809 SCCOL nColStart = rRange.aStart.Col();
810 SCROW nRowStart = rRange.aStart.Row();
811 SCTAB nTab = rRange.aStart.Tab();
813 // temporary fix to workaround slow duplicate entry
814 // conditions, prevent to use a whole row
815 if(nRow == mpDoc->MaxRow())
817 bool bShrunk = false;
818 mpDoc->ShrinkToUsedDataArea(bShrunk, nTab, nColStart, nRowStart,
819 nCol, nRow, false);
822 for( SCROW r = nRowStart; r <= nRow; r++ )
823 for( SCCOL c = nColStart; c <= nCol; c++ )
825 ScRefCellValue aCell(*mpDoc, ScAddress(c, r, nTab));
826 if (aCell.isEmpty())
827 continue;
829 double nVal = 0.0;
830 OUString aStr;
831 if (!lcl_GetCellContent(aCell, false, nVal, aStr, mpDoc))
833 std::pair<ScConditionEntryCache::StringCacheType::iterator, bool> aResult =
834 mpCache->maStrings.emplace(aStr, 1);
836 if(!aResult.second)
837 aResult.first->second++;
839 else
841 std::pair<ScConditionEntryCache::ValueCacheType::iterator, bool> aResult =
842 mpCache->maValues.emplace(nVal, 1);
844 if(!aResult.second)
845 aResult.first->second++;
847 ++(mpCache->nValueItems);
853 bool ScConditionEntry::IsDuplicate( double nArg, const OUString& rStr ) const
855 FillCache();
857 if(rStr.isEmpty())
859 ScConditionEntryCache::ValueCacheType::iterator itr = mpCache->maValues.find(nArg);
860 if(itr == mpCache->maValues.end())
861 return false;
862 else
864 return itr->second > 1;
867 else
869 ScConditionEntryCache::StringCacheType::iterator itr = mpCache->maStrings.find(rStr);
870 if(itr == mpCache->maStrings.end())
871 return false;
872 else
874 return itr->second > 1;
879 bool ScConditionEntry::IsTopNElement( double nArg ) const
881 FillCache();
883 if(mpCache->nValueItems <= nVal1)
884 return true;
886 size_t nCells = 0;
887 for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr = mpCache->maValues.rbegin(),
888 itrEnd = mpCache->maValues.rend(); itr != itrEnd; ++itr)
890 if(nCells >= nVal1)
891 return false;
892 if(itr->first <= nArg)
893 return true;
894 nCells += itr->second;
897 return true;
900 bool ScConditionEntry::IsBottomNElement( double nArg ) const
902 FillCache();
904 if(mpCache->nValueItems <= nVal1)
905 return true;
907 size_t nCells = 0;
908 for(const auto& [rVal, rCount] : mpCache->maValues)
910 if(nCells >= nVal1)
911 return false;
912 if(rVal >= nArg)
913 return true;
914 nCells += rCount;
917 return true;
920 bool ScConditionEntry::IsTopNPercent( double nArg ) const
922 FillCache();
924 size_t nCells = 0;
925 size_t nLimitCells = static_cast<size_t>(mpCache->nValueItems*nVal1/100);
926 for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr = mpCache->maValues.rbegin(),
927 itrEnd = mpCache->maValues.rend(); itr != itrEnd; ++itr)
929 if(nCells >= nLimitCells)
930 return false;
931 if(itr->first <= nArg)
932 return true;
933 nCells += itr->second;
936 return true;
939 bool ScConditionEntry::IsBottomNPercent( double nArg ) const
941 FillCache();
943 size_t nCells = 0;
944 size_t nLimitCells = static_cast<size_t>(mpCache->nValueItems*nVal1/100);
945 for(const auto& [rVal, rCount] : mpCache->maValues)
947 if(nCells >= nLimitCells)
948 return false;
949 if(rVal >= nArg)
950 return true;
951 nCells += rCount;
954 return true;
957 bool ScConditionEntry::IsBelowAverage( double nArg, bool bEqual ) const
959 FillCache();
961 double nSum = std::accumulate(mpCache->maValues.begin(), mpCache->maValues.end(), double(0),
962 [](const double& rSum, const ScConditionEntryCache::ValueCacheType::value_type& rEntry) {
963 return rSum + rEntry.first * rEntry.second; });
965 if(bEqual)
966 return (nArg <= nSum/mpCache->nValueItems);
967 else
968 return (nArg < nSum/mpCache->nValueItems);
971 bool ScConditionEntry::IsAboveAverage( double nArg, bool bEqual ) const
973 FillCache();
975 double nSum = std::accumulate(mpCache->maValues.begin(), mpCache->maValues.end(), double(0),
976 [](const double& rSum, const ScConditionEntryCache::ValueCacheType::value_type& rEntry) {
977 return rSum + rEntry.first * rEntry.second; });
979 if(bEqual)
980 return (nArg >= nSum/mpCache->nValueItems);
981 else
982 return (nArg > nSum/mpCache->nValueItems);
985 bool ScConditionEntry::IsError( const ScAddress& rPos ) const
987 ScRefCellValue rCell(*mpDoc, rPos);
989 if (rCell.getType() == CELLTYPE_FORMULA)
991 if (rCell.getFormula()->GetErrCode() != FormulaError::NONE)
992 return true;
995 return false;
998 bool ScConditionEntry::IsValid( double nArg, const ScAddress& rPos ) const
1000 // Interpret must already have been called
1001 if ( bIsStr1 )
1003 switch( eOp )
1005 case ScConditionMode::BeginsWith:
1006 case ScConditionMode::EndsWith:
1007 case ScConditionMode::ContainsText:
1008 case ScConditionMode::NotContainsText:
1009 break;
1010 case ScConditionMode::NotEqual:
1011 return true;
1012 default:
1013 return false;
1017 if ( eOp == ScConditionMode::Between || eOp == ScConditionMode::NotBetween )
1018 if ( bIsStr2 )
1019 return false;
1021 double nComp1 = nVal1; // Copy, so that it can be changed
1022 double nComp2 = nVal2;
1024 if ( eOp == ScConditionMode::Between || eOp == ScConditionMode::NotBetween )
1025 if ( nComp1 > nComp2 )
1026 // Right order for value range
1027 std::swap( nComp1, nComp2 );
1029 // All corner cases need to be tested with ::rtl::math::approxEqual!
1030 bool bValid = false;
1031 switch (eOp)
1033 case ScConditionMode::NONE:
1034 break; // Always sal_False
1035 case ScConditionMode::Equal:
1036 bValid = ::rtl::math::approxEqual( nArg, nComp1 );
1037 break;
1038 case ScConditionMode::NotEqual:
1039 bValid = !::rtl::math::approxEqual( nArg, nComp1 );
1040 break;
1041 case ScConditionMode::Greater:
1042 bValid = ( nArg > nComp1 ) && !::rtl::math::approxEqual( nArg, nComp1 );
1043 break;
1044 case ScConditionMode::EqGreater:
1045 bValid = ( nArg >= nComp1 ) || ::rtl::math::approxEqual( nArg, nComp1 );
1046 break;
1047 case ScConditionMode::Less:
1048 bValid = ( nArg < nComp1 ) && !::rtl::math::approxEqual( nArg, nComp1 );
1049 break;
1050 case ScConditionMode::EqLess:
1051 bValid = ( nArg <= nComp1 ) || ::rtl::math::approxEqual( nArg, nComp1 );
1052 break;
1053 case ScConditionMode::Between:
1054 bValid = ( nArg >= nComp1 && nArg <= nComp2 ) ||
1055 ::rtl::math::approxEqual( nArg, nComp1 ) || ::rtl::math::approxEqual( nArg, nComp2 );
1056 break;
1057 case ScConditionMode::NotBetween:
1058 bValid = ( nArg < nComp1 || nArg > nComp2 ) &&
1059 !::rtl::math::approxEqual( nArg, nComp1 ) && !::rtl::math::approxEqual( nArg, nComp2 );
1060 break;
1061 case ScConditionMode::Duplicate:
1062 case ScConditionMode::NotDuplicate:
1063 if( pCondFormat )
1065 bValid = IsDuplicate( nArg, OUString() );
1066 if( eOp == ScConditionMode::NotDuplicate )
1067 bValid = !bValid;
1069 break;
1070 case ScConditionMode::Direct:
1071 bValid = nComp1 != 0.0;
1072 break;
1073 case ScConditionMode::Top10:
1074 bValid = IsTopNElement( nArg );
1075 break;
1076 case ScConditionMode::Bottom10:
1077 bValid = IsBottomNElement( nArg );
1078 break;
1079 case ScConditionMode::TopPercent:
1080 bValid = IsTopNPercent( nArg );
1081 break;
1082 case ScConditionMode::BottomPercent:
1083 bValid = IsBottomNPercent( nArg );
1084 break;
1085 case ScConditionMode::AboveAverage:
1086 case ScConditionMode::AboveEqualAverage:
1087 bValid = IsAboveAverage( nArg, eOp == ScConditionMode::AboveEqualAverage );
1088 break;
1089 case ScConditionMode::BelowAverage:
1090 case ScConditionMode::BelowEqualAverage:
1091 bValid = IsBelowAverage( nArg, eOp == ScConditionMode::BelowEqualAverage );
1092 break;
1093 case ScConditionMode::Error:
1094 case ScConditionMode::NoError:
1095 bValid = IsError( rPos );
1096 if( eOp == ScConditionMode::NoError )
1097 bValid = !bValid;
1098 break;
1099 case ScConditionMode::BeginsWith:
1100 if(aStrVal1.isEmpty())
1102 OUString aStr = OUString::number(nVal1);
1103 OUString aStr2 = OUString::number(nArg);
1104 bValid = aStr2.startsWith(aStr);
1106 else
1108 OUString aStr2 = OUString::number(nArg);
1109 bValid = aStr2.startsWith(aStrVal1);
1111 break;
1112 case ScConditionMode::EndsWith:
1113 if(aStrVal1.isEmpty())
1115 OUString aStr = OUString::number(nVal1);
1116 OUString aStr2 = OUString::number(nArg);
1117 bValid = aStr2.endsWith(aStr);
1119 else
1121 OUString aStr2 = OUString::number(nArg);
1122 bValid = aStr2.endsWith(aStrVal1);
1124 break;
1125 case ScConditionMode::ContainsText:
1126 case ScConditionMode::NotContainsText:
1127 if(aStrVal1.isEmpty())
1129 OUString aStr = OUString::number(nVal1);
1130 OUString aStr2 = OUString::number(nArg);
1131 bValid = aStr2.indexOf(aStr) != -1;
1133 else
1135 OUString aStr2 = OUString::number(nArg);
1136 bValid = aStr2.indexOf(aStrVal1) != -1;
1139 if( eOp == ScConditionMode::NotContainsText )
1140 bValid = !bValid;
1141 break;
1142 default:
1143 SAL_WARN("sc", "unknown operation at ScConditionEntry");
1144 break;
1146 return bValid;
1149 bool ScConditionEntry::IsValidStr( const OUString& rArg, const ScAddress& rPos ) const
1151 bool bValid = false;
1152 // Interpret must already have been called
1153 if ( eOp == ScConditionMode::Direct ) // Formula is independent from the content
1154 return nVal1 != 0.0;
1156 if ( eOp == ScConditionMode::Duplicate || eOp == ScConditionMode::NotDuplicate )
1158 if( pCondFormat && !rArg.isEmpty() )
1160 bValid = IsDuplicate( 0.0, rArg );
1161 if( eOp == ScConditionMode::NotDuplicate )
1162 bValid = !bValid;
1163 return bValid;
1167 if (eOp == ScConditionMode::Error)
1168 return IsError(rPos);
1169 if (eOp == ScConditionMode::NoError)
1170 return !IsError(rPos);
1172 // If number contains condition, always false, except for "not equal".
1173 if (!bIsStr1)
1174 return ( eOp == ScConditionMode::NotEqual );
1175 if ( eOp == ScConditionMode::Between || eOp == ScConditionMode::NotBetween )
1176 if ( !bIsStr2 )
1177 return false;
1179 OUString aUpVal1( aStrVal1 ); //TODO: As a member? (Also set in Interpret)
1180 OUString aUpVal2( aStrVal2 );
1182 switch ( eOp )
1184 case ScConditionMode::Equal:
1185 bValid = ScGlobal::GetTransliteration(IsCaseSensitive()).isEqual(aUpVal1, rArg);
1186 break;
1187 case ScConditionMode::NotEqual:
1188 bValid = !ScGlobal::GetTransliteration(IsCaseSensitive()).isEqual(aUpVal1, rArg);
1189 break;
1190 case ScConditionMode::TopPercent:
1191 case ScConditionMode::BottomPercent:
1192 case ScConditionMode::Top10:
1193 case ScConditionMode::Bottom10:
1194 case ScConditionMode::AboveAverage:
1195 case ScConditionMode::BelowAverage:
1196 return false;
1197 case ScConditionMode::BeginsWith:
1198 bValid = ScGlobal::GetTransliteration(IsCaseSensitive()).isMatch(aUpVal1, rArg);
1199 break;
1200 case ScConditionMode::EndsWith:
1202 sal_Int32 nStart = rArg.getLength();
1203 const sal_Int32 nLen = aUpVal1.getLength();
1204 if (nLen > nStart)
1205 bValid = false;
1206 else
1208 nStart = nStart - nLen;
1209 sal_Int32 nMatch1(0), nMatch2(0);
1210 bValid = ScGlobal::GetTransliteration(IsCaseSensitive()).equals(rArg, nStart, nLen, nMatch1,
1211 aUpVal1, 0, nLen, nMatch2);
1214 break;
1215 case ScConditionMode::ContainsText:
1216 case ScConditionMode::NotContainsText:
1218 const OUString aArgStr(!IsCaseSensitive() ? ScGlobal::getCharClass().lowercase(rArg) : rArg);
1219 const OUString aValStr(!IsCaseSensitive() ? ScGlobal::getCharClass().lowercase(aUpVal1) : aUpVal1);
1220 bValid = aArgStr.indexOf(aValStr) != -1;
1222 if(eOp == ScConditionMode::NotContainsText)
1223 bValid = !bValid;
1225 break;
1226 default:
1228 sal_Int32 nCompare = ScGlobal::GetCollator(IsCaseSensitive()).compareString(
1229 rArg, aUpVal1 );
1230 switch ( eOp )
1232 case ScConditionMode::Greater:
1233 bValid = ( nCompare > 0 );
1234 break;
1235 case ScConditionMode::EqGreater:
1236 bValid = ( nCompare >= 0 );
1237 break;
1238 case ScConditionMode::Less:
1239 bValid = ( nCompare < 0 );
1240 break;
1241 case ScConditionMode::EqLess:
1242 bValid = ( nCompare <= 0 );
1243 break;
1244 case ScConditionMode::Between:
1245 case ScConditionMode::NotBetween:
1247 const sal_Int32 nCompare2 = ScGlobal::GetCollator(IsCaseSensitive()).compareString(rArg, aUpVal2);
1248 // Test for NOTBETWEEN:
1249 bValid = (nCompare > 0 && nCompare2 > 0) || (nCompare < 0 && nCompare2 < 0);
1250 if ( eOp == ScConditionMode::Between )
1251 bValid = !bValid;
1252 break;
1254 default:
1255 SAL_WARN("sc", "unknown operation in ScConditionEntry");
1256 bValid = false;
1257 break;
1261 return bValid;
1264 bool ScConditionEntry::IsCellValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
1266 const_cast<ScConditionEntry*>(this)->Interpret(rPos); // Evaluate formula
1268 if ( eOp == ScConditionMode::Direct )
1269 return nVal1 != 0.0;
1271 double nArg = 0.0;
1272 OUString aArgStr;
1273 bool bVal = lcl_GetCellContent( rCell, bIsStr1, nArg, aArgStr, mpDoc );
1274 if (bVal)
1275 return IsValid( nArg, rPos );
1276 else
1277 return IsValidStr( aArgStr, rPos );
1280 OUString ScConditionEntry::GetExpression( const ScAddress& rCursor, sal_uInt16 nIndex,
1281 sal_uInt32 nNumFmt,
1282 const FormulaGrammar::Grammar eGrammar ) const
1284 assert( nIndex <= 1);
1285 OUString aRet;
1287 if ( FormulaGrammar::isEnglish( eGrammar) && nNumFmt == 0 )
1288 nNumFmt = mpDoc->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US );
1290 if ( nIndex==0 )
1292 if ( pFormula1 )
1294 ScCompiler aComp(*mpDoc, rCursor, *pFormula1, eGrammar);
1295 OUStringBuffer aBuffer;
1296 aComp.CreateStringFromTokenArray( aBuffer );
1297 aRet = aBuffer.makeStringAndClear();
1299 else if (bIsStr1)
1301 aRet = "\"" + aStrVal1 + "\"";
1303 else
1304 aRet = mpDoc->GetFormatTable()->GetInputLineString(nVal1, nNumFmt);
1306 else if ( nIndex==1 )
1308 if ( pFormula2 )
1310 ScCompiler aComp(*mpDoc, rCursor, *pFormula2, eGrammar);
1311 OUStringBuffer aBuffer;
1312 aComp.CreateStringFromTokenArray( aBuffer );
1313 aRet = aBuffer.makeStringAndClear();
1315 else if (bIsStr2)
1317 aRet = "\"" + aStrVal2 + "\"";
1319 else
1320 aRet = mpDoc->GetFormatTable()->GetInputLineString(nVal2, nNumFmt);
1323 return aRet;
1326 std::unique_ptr<ScTokenArray> ScConditionEntry::CreateFlatCopiedTokenArray( sal_uInt16 nIndex ) const
1328 assert(nIndex <= 1);
1329 std::unique_ptr<ScTokenArray> pRet;
1331 if ( nIndex==0 )
1333 if ( pFormula1 )
1334 pRet.reset(new ScTokenArray( *pFormula1 ));
1335 else
1337 pRet.reset(new ScTokenArray(*mpDoc));
1338 if (bIsStr1)
1340 svl::SharedStringPool& rSPool = mpDoc->GetSharedStringPool();
1341 pRet->AddString(rSPool.intern(aStrVal1));
1343 else
1344 pRet->AddDouble( nVal1 );
1347 else if ( nIndex==1 )
1349 if ( pFormula2 )
1350 pRet.reset(new ScTokenArray( *pFormula2 ));
1351 else
1353 pRet.reset(new ScTokenArray(*mpDoc));
1354 if (bIsStr2)
1356 svl::SharedStringPool& rSPool = mpDoc->GetSharedStringPool();
1357 pRet->AddString(rSPool.intern(aStrVal2));
1359 else
1360 pRet->AddDouble( nVal2 );
1364 return pRet;
1368 * Return a position that's adjusted to allow textual representation
1369 * of expressions if possible
1371 ScAddress ScConditionEntry::GetValidSrcPos() const
1373 SCTAB nMinTab = aSrcPos.Tab();
1374 SCTAB nMaxTab = nMinTab;
1376 for (sal_uInt16 nPass = 0; nPass < 2; nPass++)
1378 ScTokenArray* pFormula = nPass ? pFormula2.get() : pFormula1.get();
1379 if (pFormula)
1381 for ( auto t: pFormula->References() )
1383 ScSingleRefData& rRef1 = *t->GetSingleRef();
1384 ScAddress aAbs = rRef1.toAbs(*mpDoc, aSrcPos);
1385 if (!rRef1.IsTabDeleted())
1387 if (aAbs.Tab() < nMinTab)
1388 nMinTab = aAbs.Tab();
1389 if (aAbs.Tab() > nMaxTab)
1390 nMaxTab = aAbs.Tab();
1392 if ( t->GetType() == svDoubleRef )
1394 ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
1395 aAbs = rRef2.toAbs(*mpDoc, aSrcPos);
1396 if (!rRef2.IsTabDeleted())
1398 if (aAbs.Tab() < nMinTab)
1399 nMinTab = aAbs.Tab();
1400 if (aAbs.Tab() > nMaxTab)
1401 nMaxTab = aAbs.Tab();
1408 ScAddress aValidPos = aSrcPos;
1409 SCTAB nTabCount = mpDoc->GetTableCount();
1410 if ( nMaxTab >= nTabCount && nMinTab > 0 )
1411 aValidPos.SetTab( aSrcPos.Tab() - nMinTab ); // so the lowest tab ref will be on 0
1413 if ( aValidPos.Tab() >= nTabCount )
1414 aValidPos.SetTab( nTabCount - 1 ); // ensure a valid position even if some references will be invalid
1416 return aValidPos;
1419 void ScConditionEntry::DataChanged() const
1421 //FIXME: Nothing so far
1424 bool ScConditionEntry::MarkUsedExternalReferences() const
1426 bool bAllMarked = false;
1427 for (sal_uInt16 nPass = 0; !bAllMarked && nPass < 2; nPass++)
1429 ScTokenArray* pFormula = nPass ? pFormula2.get() : pFormula1.get();
1430 if (pFormula)
1431 bAllMarked = mpDoc->MarkUsedExternalReferences(*pFormula, aSrcPos);
1433 return bAllMarked;
1436 ScFormatEntry* ScConditionEntry::Clone(ScDocument* pDoc) const
1438 return new ScConditionEntry(*pDoc, *this);
1441 ScConditionMode ScConditionEntry::GetModeFromApi(css::sheet::ConditionOperator nOperation)
1443 ScConditionMode eMode = ScConditionMode::NONE;
1444 switch (static_cast<sal_Int32>(nOperation))
1446 case css::sheet::ConditionOperator2::EQUAL:
1447 eMode = ScConditionMode::Equal;
1448 break;
1449 case css::sheet::ConditionOperator2::LESS:
1450 eMode = ScConditionMode::Less;
1451 break;
1452 case css::sheet::ConditionOperator2::GREATER:
1453 eMode = ScConditionMode::Greater;
1454 break;
1455 case css::sheet::ConditionOperator2::LESS_EQUAL:
1456 eMode = ScConditionMode::EqLess;
1457 break;
1458 case css::sheet::ConditionOperator2::GREATER_EQUAL:
1459 eMode = ScConditionMode::EqGreater;
1460 break;
1461 case css::sheet::ConditionOperator2::NOT_EQUAL:
1462 eMode = ScConditionMode::NotEqual;
1463 break;
1464 case css::sheet::ConditionOperator2::BETWEEN:
1465 eMode = ScConditionMode::Between;
1466 break;
1467 case css::sheet::ConditionOperator2::NOT_BETWEEN:
1468 eMode = ScConditionMode::NotBetween;
1469 break;
1470 case css::sheet::ConditionOperator2::FORMULA:
1471 eMode = ScConditionMode::Direct;
1472 break;
1473 case css::sheet::ConditionOperator2::DUPLICATE:
1474 eMode = ScConditionMode::Duplicate;
1475 break;
1476 case css::sheet::ConditionOperator2::NOT_DUPLICATE:
1477 eMode = ScConditionMode::NotDuplicate;
1478 break;
1479 default:
1480 break;
1482 return eMode;
1485 void ScConditionEntry::startRendering()
1487 mpCache.reset();
1490 void ScConditionEntry::endRendering()
1492 mpCache.reset();
1495 bool ScConditionEntry::NeedsRepaint() const
1497 return mpListener->NeedsRepaint();
1500 ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper,
1501 const OUString& rExpr1, const OUString& rExpr2,
1502 ScDocument& rDocument, const ScAddress& rPos,
1503 OUString aStyle,
1504 const OUString& rExprNmsp1, const OUString& rExprNmsp2,
1505 FormulaGrammar::Grammar eGrammar1,
1506 FormulaGrammar::Grammar eGrammar2,
1507 ScFormatEntry::Type eType ) :
1508 ScConditionEntry( eOper, rExpr1, rExpr2, rDocument, rPos, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2, eType ),
1509 aStyleName(std::move( aStyle )),
1510 eCondFormatType( eType )
1514 ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper,
1515 const ScTokenArray* pArr1, const ScTokenArray* pArr2,
1516 ScDocument& rDocument, const ScAddress& rPos,
1517 OUString aStyle ) :
1518 ScConditionEntry( eOper, pArr1, pArr2, rDocument, rPos ),
1519 aStyleName(std::move( aStyle ))
1523 ScCondFormatEntry::ScCondFormatEntry( const ScCondFormatEntry& r ) :
1524 ScConditionEntry( r ),
1525 aStyleName( r.aStyleName ),
1526 eCondFormatType( r.eCondFormatType)
1530 ScCondFormatEntry::ScCondFormatEntry( ScDocument& rDocument, const ScCondFormatEntry& r ) :
1531 ScConditionEntry( rDocument, r ),
1532 aStyleName( r.aStyleName ),
1533 eCondFormatType( r.eCondFormatType)
1537 // virtual
1538 bool ScCondFormatEntry::IsEqual( const ScFormatEntry& r, bool bIgnoreSrcPos ) const
1540 return ScConditionEntry::IsEqual(r, bIgnoreSrcPos) &&
1541 (aStyleName == static_cast<const ScCondFormatEntry&>(r).aStyleName);
1544 ScCondFormatEntry::~ScCondFormatEntry()
1548 void ScCondFormatEntry::DataChanged() const
1550 if ( pCondFormat )
1551 pCondFormat->DoRepaint();
1554 ScFormatEntry* ScCondFormatEntry::Clone( ScDocument* pDoc ) const
1556 return new ScCondFormatEntry( *pDoc, *this );
1559 void ScConditionEntry::CalcAll()
1561 if (pFCell1 || pFCell2)
1563 if (pFCell1)
1564 pFCell1->SetDirty();
1565 if (pFCell2)
1566 pFCell2->SetDirty();
1567 pCondFormat->DoRepaint();
1571 ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument* pDoc )
1572 : ScFormatEntry( pDoc )
1573 , meType(condformat::TODAY)
1577 ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument* pDoc, const ScCondDateFormatEntry& rFormat ):
1578 ScFormatEntry( pDoc ),
1579 meType( rFormat.meType ),
1580 maStyleName( rFormat.maStyleName )
1584 bool ScCondDateFormatEntry::IsValid( const ScAddress& rPos ) const
1586 ScRefCellValue rCell(*mpDoc, rPos);
1588 if (!rCell.hasNumeric())
1589 // non-numerical cell.
1590 return false;
1592 if( !mpCache )
1593 mpCache.reset( new Date( Date::SYSTEM ) );
1595 const Date& rActDate = *mpCache;
1596 SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
1597 sal_Int32 nCurrentDate = rActDate - pFormatter->GetNullDate();
1599 double nVal = rCell.getValue();
1600 sal_Int32 nCellDate = static_cast<sal_Int32>(::rtl::math::approxFloor(nVal));
1601 Date aCellDate = pFormatter->GetNullDate();
1602 aCellDate.AddDays(nCellDate);
1604 switch(meType)
1606 case condformat::TODAY:
1607 if( nCurrentDate == nCellDate )
1608 return true;
1609 break;
1610 case condformat::TOMORROW:
1611 if( nCurrentDate == nCellDate -1 )
1612 return true;
1613 break;
1614 case condformat::YESTERDAY:
1615 if( nCurrentDate == nCellDate + 1)
1616 return true;
1617 break;
1618 case condformat::LAST7DAYS:
1619 if( nCurrentDate >= nCellDate && nCurrentDate - 7 < nCellDate )
1620 return true;
1621 break;
1622 case condformat::LASTWEEK:
1624 const DayOfWeek eDay = rActDate.GetDayOfWeek();
1625 if( eDay != SUNDAY )
1627 Date aBegin(rActDate - (8 + static_cast<sal_Int32>(eDay)));
1628 Date aEnd(rActDate - (2 + static_cast<sal_Int32>(eDay)));
1629 return aCellDate.IsBetween( aBegin, aEnd );
1631 else
1633 Date aBegin(rActDate - 8);
1634 Date aEnd(rActDate - 1);
1635 return aCellDate.IsBetween( aBegin, aEnd );
1638 break;
1639 case condformat::THISWEEK:
1641 const DayOfWeek eDay = rActDate.GetDayOfWeek();
1642 if( eDay != SUNDAY )
1644 Date aBegin(rActDate - (1 + static_cast<sal_Int32>(eDay)));
1645 Date aEnd(rActDate + (5 - static_cast<sal_Int32>(eDay)));
1646 return aCellDate.IsBetween( aBegin, aEnd );
1648 else
1650 Date aEnd( rActDate + 6);
1651 return aCellDate.IsBetween( rActDate, aEnd );
1654 break;
1655 case condformat::NEXTWEEK:
1657 const DayOfWeek eDay = rActDate.GetDayOfWeek();
1658 if( eDay != SUNDAY )
1660 return aCellDate.IsBetween( rActDate + (6 - static_cast<sal_Int32>(eDay)),
1661 rActDate + (12 - static_cast<sal_Int32>(eDay)) );
1663 else
1665 return aCellDate.IsBetween( rActDate + 7, rActDate + 13 );
1668 break;
1669 case condformat::LASTMONTH:
1670 if( rActDate.GetMonth() == 1 )
1672 if( aCellDate.GetMonth() == 12 && rActDate.GetYear() == aCellDate.GetNextYear() )
1673 return true;
1675 else if( rActDate.GetYear() == aCellDate.GetYear() )
1677 if( rActDate.GetMonth() == aCellDate.GetMonth() + 1)
1678 return true;
1680 break;
1681 case condformat::THISMONTH:
1682 if( rActDate.GetYear() == aCellDate.GetYear() )
1684 if( rActDate.GetMonth() == aCellDate.GetMonth() )
1685 return true;
1687 break;
1688 case condformat::NEXTMONTH:
1689 if( rActDate.GetMonth() == 12 )
1691 if( aCellDate.GetMonth() == 1 && rActDate.GetYear() == aCellDate.GetYear() - 1 )
1692 return true;
1694 else if( rActDate.GetYear() == aCellDate.GetYear() )
1696 if( rActDate.GetMonth() == aCellDate.GetMonth() - 1)
1697 return true;
1699 break;
1700 case condformat::LASTYEAR:
1701 if( rActDate.GetYear() == aCellDate.GetNextYear() )
1702 return true;
1703 break;
1704 case condformat::THISYEAR:
1705 if( rActDate.GetYear() == aCellDate.GetYear() )
1706 return true;
1707 break;
1708 case condformat::NEXTYEAR:
1709 if( rActDate.GetYear() == aCellDate.GetYear() - 1 )
1710 return true;
1711 break;
1714 return false;
1717 void ScCondDateFormatEntry::SetDateType( condformat::ScCondFormatDateType eType )
1719 meType = eType;
1722 void ScCondDateFormatEntry::SetStyleName( const OUString& rStyleName )
1724 maStyleName = rStyleName;
1727 ScFormatEntry* ScCondDateFormatEntry::Clone( ScDocument* pDoc ) const
1729 return new ScCondDateFormatEntry( pDoc, *this );
1732 void ScCondDateFormatEntry::startRendering()
1734 mpCache.reset();
1737 void ScCondDateFormatEntry::endRendering()
1739 mpCache.reset();
1742 ScColorFormatCache::ScColorFormatCache(ScDocument& rDoc, const ScRangeList& rRanges) :
1743 mrDoc(rDoc)
1745 if (mrDoc.IsClipOrUndo())
1746 return;
1748 for (const ScRange& rRange: rRanges)
1749 mrDoc.StartListeningArea(rRange, false, this);
1752 ScColorFormatCache::~ScColorFormatCache()
1754 if (mrDoc.IsClipOrUndo())
1755 return;
1757 EndListeningAll();
1760 void ScColorFormatCache::Notify(const SfxHint& rHint)
1762 if (rHint.GetId() == SfxHintId::Dying)
1764 EndListeningAll();
1765 return;
1768 maValues.clear();
1772 ScConditionalFormat::ScConditionalFormat(sal_uInt32 nNewKey, ScDocument* pDocument) :
1773 mpDoc( pDocument ),
1774 mnKey( nNewKey )
1778 std::unique_ptr<ScConditionalFormat> ScConditionalFormat::Clone(ScDocument* pNewDoc) const
1780 // Real copy of the formula (for Ref Undo/between documents)
1781 if (!pNewDoc)
1782 pNewDoc = mpDoc;
1784 std::unique_ptr<ScConditionalFormat> pNew(new ScConditionalFormat(mnKey, pNewDoc));
1785 pNew->SetRange( maRanges ); // prerequisite for listeners
1787 for (const auto& rxEntry : maEntries)
1789 ScFormatEntry* pNewEntry = rxEntry->Clone(pNewDoc);
1790 pNew->maEntries.push_back( std::unique_ptr<ScFormatEntry>(pNewEntry) );
1791 pNewEntry->SetParent(pNew.get());
1794 return pNew;
1797 bool ScConditionalFormat::EqualEntries( const ScConditionalFormat& r, bool bIgnoreSrcPos ) const
1799 if( size() != r.size())
1800 return false;
1802 //TODO: Test for same entries in reverse order?
1803 if (! std::equal(maEntries.begin(), maEntries.end(), r.maEntries.begin(),
1804 [&bIgnoreSrcPos](const std::unique_ptr<ScFormatEntry>& p1, const std::unique_ptr<ScFormatEntry>& p2) -> bool
1806 return p1->IsEqual(*p2, bIgnoreSrcPos);
1808 return false;
1810 // right now don't check for same range
1811 // we only use this method to merge same conditional formats from
1812 // old ODF data structure
1813 return true;
1816 void ScConditionalFormat::SetRange( const ScRangeList& rRanges )
1818 maRanges = rRanges;
1819 SAL_WARN_IF(maRanges.empty(), "sc", "the conditional format range is empty! will result in a crash later!");
1820 ResetCache();
1823 void ScConditionalFormat::AddEntry( ScFormatEntry* pNew )
1825 maEntries.push_back( std::unique_ptr<ScFormatEntry>(pNew));
1826 pNew->SetParent(this);
1829 void ScConditionalFormat::RemoveEntry(size_t n)
1831 if (n < maEntries.size())
1833 maEntries.erase(maEntries.begin() + n);
1834 DoRepaint();
1838 bool ScConditionalFormat::IsEmpty() const
1840 return maEntries.empty();
1843 size_t ScConditionalFormat::size() const
1845 return maEntries.size();
1848 ScDocument* ScConditionalFormat::GetDocument()
1850 return mpDoc;
1853 ScConditionalFormat::~ScConditionalFormat()
1857 const ScFormatEntry* ScConditionalFormat::GetEntry( sal_uInt16 nPos ) const
1859 if ( nPos < size() )
1860 return maEntries[nPos].get();
1861 else
1862 return nullptr;
1865 OUString ScConditionalFormat::GetCellStyle( ScRefCellValue& rCell, const ScAddress& rPos ) const
1867 for (const auto& rxEntry : maEntries)
1869 if(rxEntry->GetType() == ScFormatEntry::Type::Condition ||
1870 rxEntry->GetType() == ScFormatEntry::Type::ExtCondition)
1872 const ScCondFormatEntry& rEntry = static_cast<const ScCondFormatEntry&>(*rxEntry);
1873 if (rEntry.IsCellValid(rCell, rPos))
1874 return rEntry.GetStyle();
1876 else if(rxEntry->GetType() == ScFormatEntry::Type::Date)
1878 const ScCondDateFormatEntry& rEntry = static_cast<const ScCondDateFormatEntry&>(*rxEntry);
1879 if (rEntry.IsValid( rPos ))
1880 return rEntry.GetStyleName();
1884 return OUString();
1887 ScCondFormatData ScConditionalFormat::GetData( ScRefCellValue& rCell, const ScAddress& rPos ) const
1889 ScCondFormatData aData;
1890 for(const auto& rxEntry : maEntries)
1892 if( (rxEntry->GetType() == ScFormatEntry::Type::Condition ||
1893 rxEntry->GetType() == ScFormatEntry::Type::ExtCondition) &&
1894 aData.aStyleName.isEmpty())
1896 const ScCondFormatEntry& rEntry = static_cast<const ScCondFormatEntry&>(*rxEntry);
1897 if (rEntry.IsCellValid(rCell, rPos))
1898 aData.aStyleName = rEntry.GetStyle();
1900 else if(rxEntry->GetType() == ScFormatEntry::Type::Colorscale && !aData.mxColorScale)
1902 const ScColorScaleFormat& rEntry = static_cast<const ScColorScaleFormat&>(*rxEntry);
1903 aData.mxColorScale = rEntry.GetColor(rPos);
1905 else if(rxEntry->GetType() == ScFormatEntry::Type::Databar && !aData.pDataBar)
1907 const ScDataBarFormat& rEntry = static_cast<const ScDataBarFormat&>(*rxEntry);
1908 aData.pDataBar = rEntry.GetDataBarInfo(rPos);
1910 else if(rxEntry->GetType() == ScFormatEntry::Type::Iconset && !aData.pIconSet)
1912 const ScIconSetFormat& rEntry = static_cast<const ScIconSetFormat&>(*rxEntry);
1913 aData.pIconSet = rEntry.GetIconSetInfo(rPos);
1915 else if(rxEntry->GetType() == ScFormatEntry::Type::Date && aData.aStyleName.isEmpty())
1917 const ScCondDateFormatEntry& rEntry = static_cast<const ScCondDateFormatEntry&>(*rxEntry);
1918 if ( rEntry.IsValid( rPos ) )
1919 aData.aStyleName = rEntry.GetStyleName();
1922 return aData;
1925 void ScConditionalFormat::DoRepaint()
1927 // all conditional format cells
1928 mpDoc->RepaintRange( maRanges );
1931 void ScConditionalFormat::CompileAll()
1933 for(auto& rxEntry : maEntries)
1934 if(rxEntry->GetType() == ScFormatEntry::Type::Condition ||
1935 rxEntry->GetType() == ScFormatEntry::Type::ExtCondition)
1936 static_cast<ScCondFormatEntry&>(*rxEntry).CompileAll();
1939 void ScConditionalFormat::CompileXML()
1941 for(auto& rxEntry : maEntries)
1942 if(rxEntry->GetType() == ScFormatEntry::Type::Condition ||
1943 rxEntry->GetType() == ScFormatEntry::Type::ExtCondition)
1944 static_cast<ScCondFormatEntry&>(*rxEntry).CompileXML();
1947 void ScConditionalFormat::UpdateReference( sc::RefUpdateContext& rCxt, bool bCopyAsMove )
1949 if (rCxt.meMode == URM_COPY && bCopyAsMove)
1951 // ScConditionEntry::UpdateReference() obtains its aSrcPos from
1952 // maRanges and does not update it on URM_COPY, but it's needed later
1953 // for the moved position, so update maRanges beforehand.
1954 maRanges.UpdateReference(URM_MOVE, mpDoc, rCxt.maRange, rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
1955 for (auto& rxEntry : maEntries)
1956 rxEntry->UpdateReference(rCxt);
1958 else
1960 for (auto& rxEntry : maEntries)
1961 rxEntry->UpdateReference(rCxt);
1962 maRanges.UpdateReference(rCxt.meMode, mpDoc, rCxt.maRange, rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
1965 ResetCache();
1968 void ScConditionalFormat::InsertRow(SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, SCROW nRowPos, SCSIZE nSize)
1970 maRanges.InsertRow(nTab, nColStart, nColEnd, nRowPos, nSize);
1971 ResetCache();
1974 void ScConditionalFormat::InsertCol(SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, SCCOL nColPos, SCSIZE nSize)
1976 maRanges.InsertCol(nTab, nRowStart, nRowEnd, nColPos, nSize);
1977 ResetCache();
1980 void ScConditionalFormat::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
1982 for (size_t i = 0, n = maRanges.size(); i < n; ++i)
1984 // We assume that the start and end sheet indices are equal.
1985 ScRange & rRange = maRanges[i];
1986 SCTAB nTab = rRange.aStart.Tab();
1988 if (nTab < rCxt.mnInsertPos)
1989 // Unaffected.
1990 continue;
1992 rRange.aStart.IncTab(rCxt.mnSheets);
1993 rRange.aEnd.IncTab(rCxt.mnSheets);
1996 ResetCache();
1998 for (auto& rxEntry : maEntries)
1999 rxEntry->UpdateInsertTab(rCxt);
2002 void ScConditionalFormat::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
2004 for (size_t i = 0, n = maRanges.size(); i < n; ++i)
2006 // We assume that the start and end sheet indices are equal.
2007 ScRange & rRange = maRanges[i];
2008 SCTAB nTab = rRange.aStart.Tab();
2010 if (nTab < rCxt.mnDeletePos)
2011 // Left of the deleted sheet(s). Unaffected.
2012 continue;
2014 if (nTab <= rCxt.mnDeletePos+rCxt.mnSheets-1)
2016 // On the deleted sheet(s).
2017 rRange.aStart.SetTab(-1);
2018 rRange.aEnd.SetTab(-1);
2019 continue;
2022 // Right of the deleted sheet(s). Adjust the sheet indices.
2023 rRange.aStart.IncTab(-1*rCxt.mnSheets);
2024 rRange.aEnd.IncTab(-1*rCxt.mnSheets);
2027 ResetCache();
2029 for (auto& rxEntry : maEntries)
2030 rxEntry->UpdateDeleteTab(rCxt);
2033 void ScConditionalFormat::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
2035 size_t n = maRanges.size();
2036 SCTAB nMinTab = std::min<SCTAB>(rCxt.mnOldPos, rCxt.mnNewPos);
2037 SCTAB nMaxTab = std::max<SCTAB>(rCxt.mnOldPos, rCxt.mnNewPos);
2038 for(size_t i = 0; i < n; ++i)
2040 ScRange & rRange = maRanges[i];
2041 SCTAB nTab = rRange.aStart.Tab();
2042 if(nTab < nMinTab || nTab > nMaxTab)
2044 continue;
2047 if (nTab == rCxt.mnOldPos)
2049 rRange.aStart.SetTab(rCxt.mnNewPos);
2050 rRange.aEnd.SetTab(rCxt.mnNewPos);
2051 continue;
2054 if (rCxt.mnNewPos < rCxt.mnOldPos)
2056 rRange.aStart.IncTab();
2057 rRange.aEnd.IncTab();
2059 else
2061 rRange.aStart.IncTab(-1);
2062 rRange.aEnd.IncTab(-1);
2066 ResetCache();
2068 for (auto& rxEntry : maEntries)
2069 rxEntry->UpdateMoveTab(rCxt);
2072 void ScConditionalFormat::DeleteArea( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
2074 if (maRanges.empty())
2075 return;
2077 SCTAB nTab = maRanges[0].aStart.Tab();
2078 maRanges.DeleteArea( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
2079 ResetCache();
2082 void ScConditionalFormat::RenameCellStyle(std::u16string_view rOld, const OUString& rNew)
2084 for(const auto& rxEntry : maEntries)
2085 if(rxEntry->GetType() == ScFormatEntry::Type::Condition ||
2086 rxEntry->GetType() == ScFormatEntry::Type::ExtCondition)
2088 ScCondFormatEntry& rFormat = static_cast<ScCondFormatEntry&>(*rxEntry);
2089 if(rFormat.GetStyle() == rOld)
2090 rFormat.UpdateStyleName( rNew );
2094 bool ScConditionalFormat::MarkUsedExternalReferences() const
2096 bool bAllMarked = false;
2097 for(const auto& rxEntry : maEntries)
2098 if(rxEntry->GetType() == ScFormatEntry::Type::Condition ||
2099 rxEntry->GetType() == ScFormatEntry::Type::ExtCondition)
2101 const ScCondFormatEntry& rFormat = static_cast<const ScCondFormatEntry&>(*rxEntry);
2102 bAllMarked = rFormat.MarkUsedExternalReferences();
2103 if (bAllMarked)
2104 break;
2107 return bAllMarked;
2110 void ScConditionalFormat::startRendering()
2112 for(auto& rxEntry : maEntries)
2114 rxEntry->startRendering();
2118 void ScConditionalFormat::endRendering()
2120 for(auto& rxEntry : maEntries)
2122 rxEntry->endRendering();
2126 void ScConditionalFormat::updateValues()
2128 for(auto& rxEntry : maEntries)
2130 rxEntry->updateValues();
2134 void ScConditionalFormat::CalcAll()
2136 for(const auto& rxEntry : maEntries)
2138 if (rxEntry->GetType() == ScFormatEntry::Type::Condition ||
2139 rxEntry->GetType() == ScFormatEntry::Type::ExtCondition)
2141 ScCondFormatEntry& rFormat = static_cast<ScCondFormatEntry&>(*rxEntry);
2142 rFormat.CalcAll();
2147 void ScConditionalFormat::ResetCache() const
2149 if (!maRanges.empty() && mpDoc)
2150 mpCache = std::make_unique<ScColorFormatCache>(*mpDoc, maRanges);
2151 else
2152 mpCache.reset();
2155 void ScConditionalFormat::SetCache(const std::vector<double>& aValues) const
2157 if (!mpCache)
2158 ResetCache();
2159 if (mpCache)
2160 mpCache->maValues = aValues;
2163 std::vector<double>* ScConditionalFormat::GetCache() const
2165 return mpCache ? &mpCache->maValues : nullptr;
2168 ScConditionalFormatList::ScConditionalFormatList(const ScConditionalFormatList& rList)
2170 for(const auto& rxFormat : rList)
2171 InsertNew( rxFormat->Clone() );
2174 ScConditionalFormatList::ScConditionalFormatList(ScDocument& rDoc, const ScConditionalFormatList& rList)
2176 for(const auto& rxFormat : rList)
2177 InsertNew( rxFormat->Clone(&rDoc) );
2180 void ScConditionalFormatList::InsertNew( std::unique_ptr<ScConditionalFormat> pNew )
2182 m_ConditionalFormats.insert(std::move(pNew));
2185 ScConditionalFormat* ScConditionalFormatList::GetFormat( sal_uInt32 nKey )
2187 auto itr = m_ConditionalFormats.find(nKey);
2188 if (itr != m_ConditionalFormats.end())
2189 return itr->get();
2191 SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
2192 return nullptr;
2195 const ScConditionalFormat* ScConditionalFormatList::GetFormat( sal_uInt32 nKey ) const
2197 auto itr = m_ConditionalFormats.find(nKey);
2198 if (itr != m_ConditionalFormats.end())
2199 return itr->get();
2201 SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
2202 return nullptr;
2205 void ScConditionalFormatList::CompileAll()
2207 for (auto const& it : m_ConditionalFormats)
2209 it->CompileAll();
2213 void ScConditionalFormatList::CompileXML()
2215 for (auto const& it : m_ConditionalFormats)
2217 it->CompileXML();
2221 void ScConditionalFormatList::UpdateReference( sc::RefUpdateContext& rCxt )
2223 for (auto const& it : m_ConditionalFormats)
2225 it->UpdateReference(rCxt);
2228 if (rCxt.meMode == URM_INSDEL)
2230 // need to check which must be deleted
2231 CheckAllEntries();
2235 void ScConditionalFormatList::InsertRow(SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, SCROW nRowPos, SCSIZE nSize)
2237 for (auto const& it : m_ConditionalFormats)
2239 it->InsertRow(nTab, nColStart, nColEnd, nRowPos, nSize);
2243 void ScConditionalFormatList::InsertCol(SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, SCCOL nColPos, SCSIZE nSize)
2245 for (auto const& it : m_ConditionalFormats)
2247 it->InsertCol(nTab, nRowStart, nRowEnd, nColPos, nSize);
2251 void ScConditionalFormatList::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
2253 for (auto const& it : m_ConditionalFormats)
2255 it->UpdateInsertTab(rCxt);
2259 void ScConditionalFormatList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
2261 for (auto const& it : m_ConditionalFormats)
2263 it->UpdateDeleteTab(rCxt);
2267 void ScConditionalFormatList::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
2269 for (auto const& it : m_ConditionalFormats)
2271 it->UpdateMoveTab(rCxt);
2275 void ScConditionalFormatList::RenameCellStyle( std::u16string_view rOld, const OUString& rNew )
2277 for (auto const& it : m_ConditionalFormats)
2279 it->RenameCellStyle(rOld, rNew);
2283 bool ScConditionalFormatList::CheckAllEntries(const Link<ScConditionalFormat*,void>& rLink)
2285 bool bValid = true;
2287 // need to check which must be deleted
2288 iterator itr = m_ConditionalFormats.begin();
2289 while(itr != m_ConditionalFormats.end())
2291 if ((*itr)->GetRange().empty())
2293 bValid = false;
2294 if (rLink.IsSet())
2295 rLink.Call(itr->get());
2296 itr = m_ConditionalFormats.erase(itr);
2298 else
2299 ++itr;
2302 return bValid;
2305 void ScConditionalFormatList::DeleteArea( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
2307 for (auto& rxFormat : m_ConditionalFormats)
2308 rxFormat->DeleteArea( nCol1, nRow1, nCol2, nRow2 );
2310 CheckAllEntries();
2313 ScConditionalFormatList::iterator ScConditionalFormatList::begin()
2315 return m_ConditionalFormats.begin();
2318 ScConditionalFormatList::const_iterator ScConditionalFormatList::begin() const
2320 return m_ConditionalFormats.begin();
2323 ScConditionalFormatList::iterator ScConditionalFormatList::end()
2325 return m_ConditionalFormats.end();
2328 ScConditionalFormatList::const_iterator ScConditionalFormatList::end() const
2330 return m_ConditionalFormats.end();
2333 ScRangeList ScConditionalFormatList::GetCombinedRange() const
2335 ScRangeList aRange;
2336 for (auto& itr: m_ConditionalFormats)
2338 const ScRangeList& rRange = itr->GetRange();
2339 for (size_t i = 0, n = rRange.size(); i < n; ++i)
2341 aRange.Join(rRange[i]);
2344 return aRange;
2347 void ScConditionalFormatList::RemoveFromDocument(ScDocument& rDoc) const
2349 ScRangeList aRange = GetCombinedRange();
2350 ScMarkData aMark(rDoc.GetSheetLimits());
2351 aMark.MarkFromRangeList(aRange, true);
2352 sal_uInt16 const pItems[2] = { sal_uInt16(ATTR_CONDITIONAL),0};
2353 rDoc.ClearSelectionItems(pItems, aMark);
2356 void ScConditionalFormatList::AddToDocument(ScDocument& rDoc) const
2358 for (auto& itr: m_ConditionalFormats)
2360 const ScRangeList& rRange = itr->GetRange();
2361 if (rRange.empty())
2362 continue;
2364 SCTAB nTab = rRange.front().aStart.Tab();
2365 rDoc.AddCondFormatData(rRange, nTab, itr->GetKey());
2369 size_t ScConditionalFormatList::size() const
2371 return m_ConditionalFormats.size();
2374 bool ScConditionalFormatList::empty() const
2376 return m_ConditionalFormats.empty();
2379 void ScConditionalFormatList::erase( sal_uLong nIndex )
2381 auto itr = m_ConditionalFormats.find(nIndex);
2382 if (itr != end())
2383 m_ConditionalFormats.erase(itr);
2386 void ScConditionalFormatList::startRendering()
2388 for (auto const& it : m_ConditionalFormats)
2390 it->startRendering();
2394 void ScConditionalFormatList::endRendering()
2396 for (auto const& it : m_ConditionalFormats)
2398 it->endRendering();
2402 void ScConditionalFormatList::updateValues()
2404 for (auto const& it : m_ConditionalFormats)
2406 it->updateValues();
2410 void ScConditionalFormatList::clear()
2412 m_ConditionalFormats.clear();
2415 sal_uInt32 ScConditionalFormatList::getMaxKey() const
2417 if (m_ConditionalFormats.empty())
2418 return 0;
2419 return (*m_ConditionalFormats.rbegin())->GetKey();
2422 void ScConditionalFormatList::CalcAll()
2424 for (const auto& aEntry : m_ConditionalFormats)
2426 aEntry->CalcAll();
2431 ScCondFormatData::ScCondFormatData() {}
2433 ScCondFormatData::ScCondFormatData(ScCondFormatData&&) = default;
2435 ScCondFormatData::~ScCondFormatData() {}
2438 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */