Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "scitems.hxx"
21 : #include <sfx2/objsh.hxx>
22 : #include <svl/itemset.hxx>
23 : #include <svl/zforlist.hxx>
24 : #include <rtl/math.hxx>
25 : #include <unotools/collatorwrapper.hxx>
26 :
27 : #include <com/sun/star/sheet/ConditionOperator2.hpp>
28 :
29 : #include "conditio.hxx"
30 : #include "formulacell.hxx"
31 : #include "document.hxx"
32 : #include "hints.hxx"
33 : #include "compiler.hxx"
34 : #include "rechead.hxx"
35 : #include "rangelst.hxx"
36 : #include "stlpool.hxx"
37 : #include "rangenam.hxx"
38 : #include "colorscale.hxx"
39 : #include "cellvalue.hxx"
40 : #include "editutil.hxx"
41 : #include "tokenarray.hxx"
42 : #include "refupdatecontext.hxx"
43 : #include <svl/sharedstring.hxx>
44 : #include <svl/sharedstringpool.hxx>
45 : #include <boost/scoped_ptr.hpp>
46 :
47 : using namespace formula;
48 :
49 425 : ScFormatEntry::ScFormatEntry(ScDocument* pDoc):
50 425 : mpDoc(pDoc)
51 : {
52 425 : }
53 :
54 0 : bool ScFormatEntry::operator==( const ScFormatEntry& r ) const
55 : {
56 0 : if(GetType() != r.GetType())
57 0 : return false;
58 :
59 0 : switch(GetType())
60 : {
61 : case condformat::CONDITION:
62 0 : return static_cast<const ScCondFormatEntry&>(*this) == static_cast<const ScCondFormatEntry&>(r);
63 : default:
64 : // TODO: implement also this case
65 : // actually return false for these cases is not that bad
66 : // as soon as databar and color scale are tested we need
67 : // to think about the range
68 0 : return false;
69 : }
70 : }
71 :
72 0 : void ScFormatEntry::startRendering()
73 : {
74 0 : }
75 :
76 0 : void ScFormatEntry::endRendering()
77 : {
78 0 : }
79 :
80 213 : static bool lcl_HasRelRef( ScDocument* pDoc, ScTokenArray* pFormula, sal_uInt16 nRecursion = 0 )
81 : {
82 213 : if (pFormula)
83 : {
84 36 : pFormula->Reset();
85 : FormulaToken* t;
86 66 : for( t = pFormula->Next(); t; t = pFormula->Next() )
87 : {
88 54 : switch( t->GetType() )
89 : {
90 : case svDoubleRef:
91 : {
92 8 : ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
93 8 : if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
94 7 : return true;
95 : }
96 : // fall through
97 :
98 : case svSingleRef:
99 : {
100 22 : ScSingleRefData& rRef1 = *t->GetSingleRef();
101 22 : if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
102 17 : return true;
103 : }
104 5 : break;
105 :
106 : case svIndex:
107 : {
108 1 : if( t->GetOpCode() == ocName ) // DB areas always absolute
109 1 : if( ScRangeData* pRangeData = pDoc->GetRangeName()->findByIndex( t->GetIndex() ) )
110 1 : if( (nRecursion < 42) && lcl_HasRelRef( pDoc, pRangeData->GetCode(), nRecursion + 1 ) )
111 0 : return true;
112 : }
113 1 : break;
114 :
115 : // #i34474# function result dependent on cell position
116 : case svByte:
117 : {
118 12 : switch( t->GetOpCode() )
119 : {
120 : case ocRow: // ROW() returns own row index
121 : case ocColumn: // COLUMN() returns own column index
122 : case ocSheet: // SHEET() returns own sheet index
123 : case ocCell: // CELL() may return own cell address
124 0 : return true;
125 : default:
126 : {
127 : // added to avoid warnings
128 : }
129 : }
130 : }
131 12 : break;
132 :
133 : default:
134 : {
135 : // added to avoid warnings
136 : }
137 : }
138 : }
139 : }
140 189 : return false;
141 : }
142 :
143 3 : ScConditionEntry::ScConditionEntry( const ScConditionEntry& r ) :
144 : ScFormatEntry(r.mpDoc),
145 : eOp(r.eOp),
146 : nOptions(r.nOptions),
147 : nVal1(r.nVal1),
148 : nVal2(r.nVal2),
149 : aStrVal1(r.aStrVal1),
150 : aStrVal2(r.aStrVal2),
151 : aStrNmsp1(r.aStrNmsp1),
152 : aStrNmsp2(r.aStrNmsp2),
153 : eTempGrammar1(r.eTempGrammar1),
154 : eTempGrammar2(r.eTempGrammar2),
155 : bIsStr1(r.bIsStr1),
156 : bIsStr2(r.bIsStr2),
157 : pFormula1(NULL),
158 : pFormula2(NULL),
159 : aSrcPos(r.aSrcPos),
160 : aSrcString(r.aSrcString),
161 : pFCell1(NULL),
162 : pFCell2(NULL),
163 : bRelRef1(r.bRelRef1),
164 : bRelRef2(r.bRelRef2),
165 : bFirstRun(true),
166 3 : pCondFormat(r.pCondFormat)
167 : {
168 : // ScTokenArray copy ctor creates a flat copy
169 3 : if (r.pFormula1)
170 1 : pFormula1 = new ScTokenArray( *r.pFormula1 );
171 3 : if (r.pFormula2)
172 0 : pFormula2 = new ScTokenArray( *r.pFormula2 );
173 :
174 : // Formula cells are created at IsValid
175 3 : }
176 :
177 86 : ScConditionEntry::ScConditionEntry( ScDocument* pDocument, const ScConditionEntry& r ) :
178 : ScFormatEntry(pDocument),
179 : eOp(r.eOp),
180 : nOptions(r.nOptions),
181 : nVal1(r.nVal1),
182 : nVal2(r.nVal2),
183 : aStrVal1(r.aStrVal1),
184 : aStrVal2(r.aStrVal2),
185 : aStrNmsp1(r.aStrNmsp1),
186 : aStrNmsp2(r.aStrNmsp2),
187 : eTempGrammar1(r.eTempGrammar1),
188 : eTempGrammar2(r.eTempGrammar2),
189 : bIsStr1(r.bIsStr1),
190 : bIsStr2(r.bIsStr2),
191 : pFormula1(NULL),
192 : pFormula2(NULL),
193 : aSrcPos(r.aSrcPos),
194 : aSrcString(r.aSrcString),
195 : pFCell1(NULL),
196 : pFCell2(NULL),
197 : bRelRef1(r.bRelRef1),
198 : bRelRef2(r.bRelRef2),
199 : bFirstRun(true),
200 86 : pCondFormat(r.pCondFormat)
201 : {
202 : // Real copy of the formulas (for Ref Undo)
203 86 : if (r.pFormula1)
204 47 : pFormula1 = r.pFormula1->Clone();
205 86 : if (r.pFormula2)
206 15 : pFormula2 = r.pFormula2->Clone();
207 :
208 : // Formula cells are created at IsValid
209 : // TODO: But not in the Clipboard! So interpret beforehand!
210 86 : }
211 :
212 186 : ScConditionEntry::ScConditionEntry( ScConditionMode eOper,
213 : const OUString& rExpr1, const OUString& rExpr2, ScDocument* pDocument, const ScAddress& rPos,
214 : const OUString& rExprNmsp1, const OUString& rExprNmsp2,
215 : FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2 ) :
216 : ScFormatEntry(pDocument),
217 : eOp(eOper),
218 : nOptions(0),
219 : nVal1(0.0),
220 : nVal2(0.0),
221 : aStrNmsp1(rExprNmsp1),
222 : aStrNmsp2(rExprNmsp2),
223 : eTempGrammar1(eGrammar1),
224 : eTempGrammar2(eGrammar2),
225 : bIsStr1(false),
226 : bIsStr2(false),
227 : pFormula1(NULL),
228 : pFormula2(NULL),
229 : aSrcPos(rPos),
230 : pFCell1(NULL),
231 : pFCell2(NULL),
232 : bRelRef1(false),
233 : bRelRef2(false),
234 : bFirstRun(true),
235 186 : pCondFormat(NULL)
236 : {
237 186 : Compile( rExpr1, rExpr2, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2, false );
238 :
239 : // Formula cells are created at IsValid
240 186 : }
241 :
242 36 : ScConditionEntry::ScConditionEntry( ScConditionMode eOper,
243 : const ScTokenArray* pArr1, const ScTokenArray* pArr2,
244 : ScDocument* pDocument, const ScAddress& rPos ) :
245 : ScFormatEntry(pDocument),
246 : eOp(eOper),
247 : nOptions(0),
248 : nVal1(0.0),
249 : nVal2(0.0),
250 : eTempGrammar1(FormulaGrammar::GRAM_DEFAULT),
251 : eTempGrammar2(FormulaGrammar::GRAM_DEFAULT),
252 : bIsStr1(false),
253 : bIsStr2(false),
254 : pFormula1(NULL),
255 : pFormula2(NULL),
256 : aSrcPos(rPos),
257 : pFCell1(NULL),
258 : pFCell2(NULL),
259 : bRelRef1(false),
260 : bRelRef2(false),
261 : bFirstRun(true),
262 36 : pCondFormat(NULL)
263 : {
264 36 : if ( pArr1 )
265 : {
266 34 : pFormula1 = new ScTokenArray( *pArr1 );
267 34 : if ( pFormula1->GetLen() == 1 )
268 : {
269 : // Single (constant number)?
270 28 : FormulaToken* pToken = pFormula1->First();
271 28 : if ( pToken->GetOpCode() == ocPush )
272 : {
273 27 : if ( pToken->GetType() == svDouble )
274 : {
275 24 : nVal1 = pToken->GetDouble();
276 24 : DELETEZ(pFormula1); // Do not remember as formula
277 : }
278 3 : else if ( pToken->GetType() == svString )
279 : {
280 1 : bIsStr1 = true;
281 1 : aStrVal1 = pToken->GetString().getString();
282 1 : DELETEZ(pFormula1); // Do not remember as formula
283 : }
284 : }
285 : }
286 34 : bRelRef1 = lcl_HasRelRef( mpDoc, pFormula1 );
287 : }
288 36 : if ( pArr2 )
289 : {
290 1 : pFormula2 = new ScTokenArray( *pArr2 );
291 1 : if ( pFormula2->GetLen() == 1 )
292 : {
293 : // Single (constant number)?
294 1 : FormulaToken* pToken = pFormula2->First();
295 1 : if ( pToken->GetOpCode() == ocPush )
296 : {
297 1 : if ( pToken->GetType() == svDouble )
298 : {
299 0 : nVal2 = pToken->GetDouble();
300 0 : DELETEZ(pFormula2); // Do not remember as formula
301 : }
302 1 : else if ( pToken->GetType() == svString )
303 : {
304 0 : bIsStr2 = true;
305 0 : aStrVal2 = pToken->GetString().getString();
306 0 : DELETEZ(pFormula2); // Do not remember as formula
307 : }
308 : }
309 : }
310 1 : bRelRef2 = lcl_HasRelRef( mpDoc, pFormula2 );
311 : }
312 :
313 : // Formula cells are created at IsValid
314 36 : }
315 :
316 616 : ScConditionEntry::~ScConditionEntry()
317 : {
318 308 : delete pFCell1;
319 308 : delete pFCell2;
320 :
321 308 : delete pFormula1;
322 308 : delete pFormula2;
323 308 : }
324 :
325 0 : void ScConditionEntry::SetOperation(ScConditionMode eMode)
326 : {
327 0 : eOp = eMode;
328 0 : }
329 :
330 257 : void ScConditionEntry::Compile( const OUString& rExpr1, const OUString& rExpr2,
331 : const OUString& rExprNmsp1, const OUString& rExprNmsp2,
332 : FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2, bool bTextToReal )
333 : {
334 257 : if ( !rExpr1.isEmpty() || !rExpr2.isEmpty() )
335 : {
336 199 : ScCompiler aComp( mpDoc, aSrcPos );
337 :
338 199 : if ( !rExpr1.isEmpty() )
339 : {
340 199 : delete pFormula1;
341 199 : aComp.SetGrammar( eGrammar1 );
342 199 : if ( mpDoc->IsImportingXML() && !bTextToReal )
343 : {
344 : // temporary formula string as string tokens
345 : //TODO: merge with lcl_ScDocFunc_CreateTokenArrayXML
346 104 : pFormula1 = new ScTokenArray;
347 104 : pFormula1->AddStringXML( rExpr1 );
348 : // bRelRef1 is set when the formula is compiled again (CompileXML)
349 : }
350 : else
351 : {
352 95 : pFormula1 = aComp.CompileString( rExpr1, rExprNmsp1 );
353 95 : if ( pFormula1->GetLen() == 1 )
354 : {
355 : // Single (constant number)?
356 83 : FormulaToken* pToken = pFormula1->First();
357 83 : if ( pToken->GetOpCode() == ocPush )
358 : {
359 83 : if ( pToken->GetType() == svDouble )
360 : {
361 71 : nVal1 = pToken->GetDouble();
362 71 : DELETEZ(pFormula1); // Do not remember as formula
363 : }
364 12 : else if ( pToken->GetType() == svString )
365 : {
366 0 : bIsStr1 = true;
367 0 : aStrVal1 = pToken->GetString().getString();
368 0 : DELETEZ(pFormula1); // Do not remember as formula
369 : }
370 : }
371 : }
372 95 : bRelRef1 = lcl_HasRelRef( mpDoc, pFormula1 );
373 : }
374 : }
375 :
376 199 : if ( !rExpr2.isEmpty() )
377 : {
378 110 : delete pFormula2;
379 110 : aComp.SetGrammar( eGrammar2 );
380 110 : if ( mpDoc->IsImportingXML() && !bTextToReal )
381 : {
382 : // temporary formula string as string tokens
383 : //TODO: merge with lcl_ScDocFunc_CreateTokenArrayXML
384 28 : pFormula2 = new ScTokenArray;
385 28 : pFormula2->AddStringXML( rExpr2 );
386 : // bRelRef2 is set when the formula is compiled again (CompileXML)
387 : }
388 : else
389 : {
390 82 : pFormula2 = aComp.CompileString( rExpr2, rExprNmsp2 );
391 82 : if ( pFormula2->GetLen() == 1 )
392 : {
393 : // Sigle (constant number)?
394 82 : FormulaToken* pToken = pFormula2->First();
395 82 : if ( pToken->GetOpCode() == ocPush )
396 : {
397 82 : if ( pToken->GetType() == svDouble )
398 : {
399 81 : nVal2 = pToken->GetDouble();
400 81 : DELETEZ(pFormula2); // Do not remember as formula
401 : }
402 1 : else if ( pToken->GetType() == svString )
403 : {
404 0 : bIsStr2 = true;
405 0 : aStrVal2 = pToken->GetString().getString();
406 0 : DELETEZ(pFormula2); // Do not remember as formula
407 : }
408 : }
409 : }
410 82 : bRelRef2 = lcl_HasRelRef( mpDoc, pFormula2 );
411 : }
412 199 : }
413 : }
414 257 : }
415 :
416 : /**
417 : * Create formula cells
418 : */
419 825 : void ScConditionEntry::MakeCells( const ScAddress& rPos )
420 : {
421 825 : if ( !mpDoc->IsClipOrUndo() ) // Never calculate in the Clipboard!
422 : {
423 825 : if ( pFormula1 && !pFCell1 && !bRelRef1 )
424 : {
425 5 : pFCell1 = new ScFormulaCell(mpDoc, rPos, *pFormula1);
426 5 : pFCell1->StartListeningTo( mpDoc );
427 : }
428 :
429 825 : if ( pFormula2 && !pFCell2 && !bRelRef2 )
430 : {
431 1 : pFCell2 = new ScFormulaCell(mpDoc, rPos, *pFormula2);
432 1 : pFCell2->StartListeningTo( mpDoc );
433 : }
434 : }
435 825 : }
436 :
437 35 : void ScConditionEntry::SetIgnoreBlank(bool bSet)
438 : {
439 : // The bit SC_COND_NOBLANKS is set if blanks are not ignored
440 : // (only of valid)
441 35 : if (bSet)
442 34 : nOptions &= ~SC_COND_NOBLANKS;
443 : else
444 1 : nOptions |= SC_COND_NOBLANKS;
445 35 : }
446 :
447 : /**
448 : * Delete formula cells, so we re-complile at the next IsValid
449 : */
450 1 : void ScConditionEntry::CompileAll()
451 : {
452 1 : DELETEZ(pFCell1);
453 1 : DELETEZ(pFCell2);
454 1 : }
455 :
456 71 : void ScConditionEntry::CompileXML()
457 : {
458 : // First parse the formula source position if it was stored as text
459 71 : if ( !aSrcString.isEmpty() )
460 : {
461 71 : ScAddress aNew;
462 : /* XML is always in OOo:A1 format, although R1C1 would be more amenable
463 : * to compression */
464 71 : if ( aNew.Parse( aSrcString, mpDoc ) & SCA_VALID )
465 71 : aSrcPos = aNew;
466 : // if the position is invalid, there isn't much we can do at this time
467 71 : aSrcString.clear();
468 : }
469 :
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 71 : aStrNmsp1, aStrNmsp2, eTempGrammar1, eTempGrammar2, true );
474 71 : }
475 :
476 122 : void ScConditionEntry::SetSrcString( const OUString& rNew )
477 : {
478 : // aSrcString is only evaluated in CompileXML
479 : SAL_WARN_IF( !mpDoc->IsImportingXML(), "sc", "SetSrcString is only valid for XML import" );
480 :
481 122 : aSrcString = rNew;
482 122 : }
483 :
484 0 : void ScConditionEntry::SetFormula1( const ScTokenArray& rArray )
485 : {
486 0 : DELETEZ( pFormula1 );
487 0 : if( rArray.GetLen() > 0 )
488 : {
489 0 : pFormula1 = new ScTokenArray( rArray );
490 0 : bRelRef1 = lcl_HasRelRef( mpDoc, pFormula1 );
491 : }
492 0 : }
493 :
494 0 : void ScConditionEntry::SetFormula2( const ScTokenArray& rArray )
495 : {
496 0 : DELETEZ( pFormula2 );
497 0 : if( rArray.GetLen() > 0 )
498 : {
499 0 : pFormula2 = new ScTokenArray( rArray );
500 0 : bRelRef2 = lcl_HasRelRef( mpDoc, pFormula2 );
501 : }
502 0 : }
503 :
504 36 : void ScConditionEntry::UpdateReference( sc::RefUpdateContext& rCxt )
505 : {
506 36 : if(pCondFormat)
507 20 : aSrcPos = pCondFormat->GetRange().Combine().aStart;
508 36 : ScAddress aOldSrcPos = aSrcPos;
509 36 : bool bChangedPos = false;
510 36 : if (rCxt.meMode == URM_INSDEL && rCxt.maRange.In(aSrcPos))
511 : {
512 1 : aSrcPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
513 1 : bChangedPos = aSrcPos != aOldSrcPos;
514 : }
515 :
516 36 : if (pFormula1)
517 : {
518 21 : sc::RefUpdateResult aRes;
519 21 : switch (rCxt.meMode)
520 : {
521 : case URM_INSDEL:
522 5 : aRes = pFormula1->AdjustReferenceOnShift(rCxt, aOldSrcPos);
523 5 : break;
524 : case URM_MOVE:
525 0 : aRes = pFormula1->AdjustReferenceOnMove(rCxt, aOldSrcPos, aSrcPos);
526 0 : break;
527 : default:
528 : ;
529 : }
530 :
531 21 : if (aRes.mbReferenceModified || bChangedPos)
532 3 : DELETEZ(pFCell1); // is created again in IsValid
533 : }
534 :
535 36 : if (pFormula2)
536 : {
537 1 : sc::RefUpdateResult aRes;
538 1 : switch (rCxt.meMode)
539 : {
540 : case URM_INSDEL:
541 0 : aRes = pFormula2->AdjustReferenceOnShift(rCxt, aOldSrcPos);
542 0 : break;
543 : case URM_MOVE:
544 0 : aRes = pFormula2->AdjustReferenceOnMove(rCxt, aOldSrcPos, aSrcPos);
545 0 : break;
546 : default:
547 : ;
548 : }
549 :
550 1 : if (aRes.mbReferenceModified || bChangedPos)
551 0 : DELETEZ(pFCell2); // is created again in IsValid
552 : }
553 36 : }
554 :
555 3 : void ScConditionEntry::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
556 : {
557 3 : if (pFormula1)
558 : {
559 1 : pFormula1->AdjustReferenceOnInsertedTab(rCxt, aSrcPos);
560 1 : DELETEZ(pFCell1);
561 : }
562 :
563 3 : if (pFormula2)
564 : {
565 0 : pFormula2->AdjustReferenceOnInsertedTab(rCxt, aSrcPos);
566 0 : DELETEZ(pFCell2);
567 : }
568 3 : }
569 :
570 4 : void ScConditionEntry::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
571 : {
572 4 : if (pFormula1)
573 : {
574 2 : pFormula1->AdjustReferenceOnDeletedTab(rCxt, aSrcPos);
575 2 : DELETEZ(pFCell1);
576 : }
577 :
578 4 : if (pFormula2)
579 : {
580 0 : pFormula2->AdjustReferenceOnDeletedTab(rCxt, aSrcPos);
581 0 : DELETEZ(pFCell2);
582 : }
583 4 : }
584 :
585 0 : void ScConditionEntry::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
586 : {
587 0 : if (pFormula1)
588 : {
589 0 : pFormula1->AdjustReferenceOnMovedTab(rCxt, aSrcPos);
590 0 : DELETEZ(pFCell1);
591 : }
592 :
593 0 : if (pFormula2)
594 : {
595 0 : pFormula2->AdjustReferenceOnMovedTab(rCxt, aSrcPos);
596 0 : DELETEZ(pFCell2);
597 : }
598 0 : }
599 :
600 : //FIXME: Make this a comparison operator at the TokenArray?
601 95 : static bool lcl_IsEqual( const ScTokenArray* pArr1, const ScTokenArray* pArr2 )
602 : {
603 : // We only compare the non-RPN array
604 95 : if ( pArr1 && pArr2 )
605 : {
606 21 : sal_uInt16 nLen = pArr1->GetLen();
607 21 : if ( pArr2->GetLen() != nLen )
608 0 : return false;
609 :
610 21 : FormulaToken** ppToken1 = pArr1->GetArray();
611 21 : FormulaToken** ppToken2 = pArr2->GetArray();
612 42 : for (sal_uInt16 i=0; i<nLen; i++)
613 : {
614 42 : if ( ppToken1[i] != ppToken2[i] &&
615 21 : !(*ppToken1[i] == *ppToken2[i]) )
616 0 : return false; // Difference
617 : }
618 21 : return true; // All entries are the same
619 : }
620 : else
621 74 : return !pArr1 && !pArr2; // Both 0? -> the same
622 : }
623 :
624 115 : bool ScConditionEntry::operator== ( const ScConditionEntry& r ) const
625 : {
626 218 : bool bEq = (eOp == r.eOp && nOptions == r.nOptions &&
627 210 : lcl_IsEqual( pFormula1, r.pFormula1 ) &&
628 162 : lcl_IsEqual( pFormula2, r.pFormula2 ));
629 115 : if (bEq)
630 : {
631 : // for formulas, the reference positions must be compared, too
632 : // (including aSrcString, for inserting the entries during XML import)
633 47 : if ( ( pFormula1 || pFormula2 ) && ( aSrcPos != r.aSrcPos || aSrcString != r.aSrcString ) )
634 0 : bEq = false;
635 :
636 : // If not formulas, compare values
637 47 : if ( !pFormula1 && ( nVal1 != r.nVal1 || aStrVal1 != r.aStrVal1 || bIsStr1 != r.bIsStr1 ) )
638 7 : bEq = false;
639 47 : if ( !pFormula2 && ( nVal2 != r.nVal2 || aStrVal2 != r.aStrVal2 || bIsStr2 != r.bIsStr2 ) )
640 7 : bEq = false;
641 : }
642 :
643 115 : return bEq;
644 : }
645 :
646 3500 : void ScConditionEntry::Interpret( const ScAddress& rPos )
647 : {
648 : // Create formula cells
649 : // Note: New Broadcaster (Note cells) may be inserted into the document!
650 3500 : if ( ( pFormula1 && !pFCell1 ) || ( pFormula2 && !pFCell2 ) )
651 825 : MakeCells( rPos );
652 :
653 : // Evaluate formulas
654 3500 : bool bDirty = false; // 1 and 2 separate?
655 :
656 3500 : boost::scoped_ptr<ScFormulaCell> pTemp1;
657 3500 : ScFormulaCell* pEff1 = pFCell1;
658 3500 : if ( bRelRef1 )
659 : {
660 820 : pTemp1.reset(pFormula1 ? new ScFormulaCell(mpDoc, rPos, *pFormula1) : new ScFormulaCell(mpDoc, rPos));
661 820 : pEff1 = pTemp1.get();
662 : }
663 3500 : if ( pEff1 )
664 : {
665 2491 : if (!pEff1->IsRunning()) // Don't create 522
666 : {
667 : //TODO: Query Changed instead of Dirty!
668 2491 : if (pEff1->GetDirty() && !bRelRef1 && mpDoc->GetAutoCalc())
669 5 : bDirty = true;
670 2491 : if (pEff1->IsValue())
671 : {
672 2491 : bIsStr1 = false;
673 2491 : nVal1 = pEff1->GetValue();
674 2491 : aStrVal1.clear();
675 : }
676 : else
677 : {
678 0 : bIsStr1 = true;
679 0 : aStrVal1 = pEff1->GetString().getString();
680 0 : nVal1 = 0.0;
681 : }
682 : }
683 : }
684 3500 : pTemp1.reset();
685 :
686 7000 : boost::scoped_ptr<ScFormulaCell> pTemp2;
687 3500 : ScFormulaCell* pEff2 = pFCell2; //@ 1!=2
688 3500 : if ( bRelRef2 )
689 : {
690 94 : pTemp2.reset(pFormula2 ? new ScFormulaCell(mpDoc, rPos, *pFormula2) : new ScFormulaCell(mpDoc, rPos));
691 94 : pEff2 = pTemp2.get();
692 : }
693 3500 : if ( pEff2 )
694 : {
695 141 : if (!pEff2->IsRunning()) // Don't create 522
696 : {
697 141 : if (pEff2->GetDirty() && !bRelRef2 && mpDoc->GetAutoCalc())
698 1 : bDirty = true;
699 141 : if (pEff2->IsValue())
700 : {
701 141 : bIsStr2 = false;
702 141 : nVal2 = pEff2->GetValue();
703 141 : aStrVal2.clear();
704 : }
705 : else
706 : {
707 0 : bIsStr2 = true;
708 0 : aStrVal2 = pEff2->GetString().getString();
709 0 : nVal2 = 0.0;
710 : }
711 : }
712 : }
713 3500 : pTemp2.reset();
714 :
715 : // If IsRunning, the last values remain
716 3500 : if (bDirty && !bFirstRun)
717 : {
718 : // Repaint everything for dependent formats
719 0 : DataChanged( NULL );
720 : }
721 :
722 7000 : bFirstRun = false;
723 3500 : }
724 :
725 4486 : static bool lcl_GetCellContent( ScRefCellValue& rCell, bool bIsStr1, double& rArg, OUString& rArgStr,
726 : const ScDocument* pDoc )
727 : {
728 :
729 4486 : if (rCell.isEmpty())
730 2408 : return !bIsStr1;
731 :
732 2078 : bool bVal = true;
733 :
734 2078 : switch (rCell.meType)
735 : {
736 : case CELLTYPE_VALUE:
737 2077 : rArg = rCell.mfValue;
738 2077 : break;
739 : case CELLTYPE_FORMULA:
740 : {
741 0 : bVal = rCell.mpFormula->IsValue();
742 0 : if (bVal)
743 0 : rArg = rCell.mpFormula->GetValue();
744 : else
745 0 : rArgStr = rCell.mpFormula->GetString().getString();
746 : }
747 0 : break;
748 : case CELLTYPE_STRING:
749 : case CELLTYPE_EDIT:
750 1 : bVal = false;
751 1 : if (rCell.meType == CELLTYPE_STRING)
752 1 : rArgStr = rCell.mpString->getString();
753 0 : else if (rCell.mpEditText)
754 0 : rArgStr = ScEditUtil::GetString(*rCell.mpEditText, pDoc);
755 1 : break;
756 : default:
757 : ;
758 : }
759 :
760 2078 : return bVal;
761 : }
762 :
763 986 : void ScConditionEntry::FillCache() const
764 : {
765 986 : if(!mpCache)
766 : {
767 47 : const ScRangeList& rRanges = pCondFormat->GetRange();
768 47 : mpCache.reset(new ScConditionEntryCache);
769 47 : size_t nListCount = rRanges.size();
770 95 : for( size_t i = 0; i < nListCount; i++ )
771 : {
772 48 : const ScRange *aRange = rRanges[i];
773 48 : SCROW nRow = aRange->aEnd.Row();
774 48 : SCCOL nCol = aRange->aEnd.Col();
775 48 : SCCOL nColStart = aRange->aStart.Col();
776 48 : SCROW nRowStart = aRange->aStart.Row();
777 48 : SCTAB nTab = aRange->aStart.Tab();
778 :
779 : // temporary fix to workaorund slow duplicate entry
780 : // conditions, prevent to use a whole row
781 48 : if(nRow == MAXROW)
782 : {
783 0 : bool bShrunk = false;
784 : mpDoc->ShrinkToUsedDataArea(bShrunk, nTab, nColStart, nRowStart,
785 0 : nCol, nRow, false);
786 : }
787 :
788 1034 : for( SCROW r = nRowStart; r <= nRow; r++ )
789 1972 : for( SCCOL c = nColStart; c <= nCol; c++ )
790 : {
791 986 : ScRefCellValue aCell;
792 986 : aCell.assign(*mpDoc, ScAddress(c, r, nTab));
793 986 : if (aCell.isEmpty())
794 0 : continue;
795 :
796 986 : double nVal = 0.0;
797 1972 : OUString aStr;
798 986 : if (!lcl_GetCellContent(aCell, false, nVal, aStr, mpDoc))
799 : {
800 : std::pair<ScConditionEntryCache::StringCacheType::iterator, bool> aResult =
801 0 : mpCache->maStrings.insert(
802 0 : ScConditionEntryCache::StringCacheType::value_type(aStr, 1));
803 :
804 0 : if(!aResult.second)
805 0 : aResult.first->second++;
806 : }
807 : else
808 : {
809 : std::pair<ScConditionEntryCache::ValueCacheType::iterator, bool> aResult =
810 986 : mpCache->maValues.insert(
811 1972 : ScConditionEntryCache::ValueCacheType::value_type(nVal, 1));
812 :
813 986 : if(!aResult.second)
814 149 : aResult.first->second++;
815 :
816 986 : ++(mpCache->nValueItems);
817 : }
818 986 : }
819 : }
820 : }
821 986 : }
822 :
823 0 : bool ScConditionEntry::IsDuplicate( double nArg, const OUString& rStr ) const
824 : {
825 0 : FillCache();
826 :
827 0 : if(rStr.isEmpty())
828 : {
829 0 : ScConditionEntryCache::ValueCacheType::iterator itr = mpCache->maValues.find(nArg);
830 0 : if(itr == mpCache->maValues.end())
831 0 : return false;
832 : else
833 : {
834 0 : if(itr->second > 1)
835 0 : return true;
836 : else
837 0 : return false;
838 : }
839 : }
840 : else
841 : {
842 0 : ScConditionEntryCache::StringCacheType::iterator itr = mpCache->maStrings.find(rStr);
843 0 : if(itr == mpCache->maStrings.end())
844 0 : return false;
845 : else
846 : {
847 0 : if(itr->second > 1)
848 0 : return true;
849 : else
850 0 : return false;
851 : }
852 : }
853 : }
854 :
855 146 : bool ScConditionEntry::IsTopNElement( double nArg ) const
856 : {
857 146 : FillCache();
858 :
859 146 : if(mpCache->nValueItems <= nVal1)
860 0 : return true;
861 :
862 146 : size_t nCells = 0;
863 917 : for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr = mpCache->maValues.rbegin(),
864 146 : itrEnd = mpCache->maValues.rend(); itr != itrEnd; ++itr)
865 : {
866 771 : if(nCells >= nVal1)
867 257 : return false;
868 660 : if(itr->first <= nArg)
869 35 : return true;
870 625 : nCells += itr->second;
871 : }
872 :
873 0 : return true;
874 : }
875 :
876 147 : bool ScConditionEntry::IsBottomNElement( double nArg ) const
877 : {
878 147 : FillCache();
879 :
880 147 : if(mpCache->nValueItems <= nVal1)
881 0 : return true;
882 :
883 147 : size_t nCells = 0;
884 651 : for(ScConditionEntryCache::ValueCacheType::const_iterator itr = mpCache->maValues.begin(),
885 147 : itrEnd = mpCache->maValues.end(); itr != itrEnd; ++itr)
886 : {
887 504 : if(nCells >= nVal1)
888 252 : return false;
889 399 : if(itr->first >= nArg)
890 42 : return true;
891 357 : nCells += itr->second;
892 : }
893 :
894 0 : return true;
895 : }
896 :
897 147 : bool ScConditionEntry::IsTopNPercent( double nArg ) const
898 : {
899 147 : FillCache();
900 :
901 147 : size_t nCells = 0;
902 147 : size_t nLimitCells = static_cast<size_t>(mpCache->nValueItems*nVal1/100);
903 567 : for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr = mpCache->maValues.rbegin(),
904 147 : itrEnd = mpCache->maValues.rend(); itr != itrEnd; ++itr)
905 : {
906 420 : if(nCells >= nLimitCells)
907 280 : return false;
908 287 : if(itr->first <= nArg)
909 14 : return true;
910 273 : nCells += itr->second;
911 : }
912 :
913 0 : return true;
914 : }
915 :
916 147 : bool ScConditionEntry::IsBottomNPercent( double nArg ) const
917 : {
918 147 : FillCache();
919 :
920 147 : size_t nCells = 0;
921 147 : size_t nLimitCells = static_cast<size_t>(mpCache->nValueItems*nVal1/100);
922 546 : for(ScConditionEntryCache::ValueCacheType::const_iterator itr = mpCache->maValues.begin(),
923 147 : itrEnd = mpCache->maValues.end(); itr != itrEnd; ++itr)
924 : {
925 399 : if(nCells >= nLimitCells)
926 266 : return false;
927 280 : if(itr->first >= nArg)
928 28 : return true;
929 252 : nCells += itr->second;
930 : }
931 :
932 0 : return true;
933 : }
934 :
935 168 : bool ScConditionEntry::IsBelowAverage( double nArg, bool bEqual ) const
936 : {
937 168 : FillCache();
938 :
939 168 : double nSum = 0;
940 3276 : for(ScConditionEntryCache::ValueCacheType::const_iterator itr = mpCache->maValues.begin(),
941 168 : itrEnd = mpCache->maValues.end(); itr != itrEnd; ++itr)
942 : {
943 2940 : nSum += itr->first * itr->second;
944 : }
945 :
946 168 : if(bEqual)
947 84 : return (nArg <= nSum/mpCache->nValueItems);
948 : else
949 84 : return (nArg < nSum/mpCache->nValueItems);
950 : }
951 :
952 231 : bool ScConditionEntry::IsAboveAverage( double nArg, bool bEqual ) const
953 : {
954 231 : FillCache();
955 :
956 231 : double nSum = 0;
957 4536 : for(ScConditionEntryCache::ValueCacheType::const_iterator itr = mpCache->maValues.begin(),
958 231 : itrEnd = mpCache->maValues.end(); itr != itrEnd; ++itr)
959 : {
960 4074 : nSum += itr->first * itr->second;
961 : }
962 :
963 231 : if(bEqual)
964 84 : return (nArg >= nSum/mpCache->nValueItems);
965 : else
966 147 : return (nArg > nSum/mpCache->nValueItems);
967 : }
968 :
969 0 : bool ScConditionEntry::IsError( const ScAddress& rPos ) const
970 : {
971 0 : switch (mpDoc->GetCellType(rPos))
972 : {
973 : case CELLTYPE_VALUE:
974 0 : return false;
975 : case CELLTYPE_FORMULA:
976 : {
977 0 : ScFormulaCell* pFormulaCell = mpDoc->GetFormulaCell(rPos);
978 0 : if (pFormulaCell && pFormulaCell->GetErrCode())
979 0 : return true;
980 : }
981 : case CELLTYPE_STRING:
982 : case CELLTYPE_EDIT:
983 0 : return false;
984 : default:
985 0 : break;
986 : }
987 0 : return false;
988 : }
989 :
990 3499 : bool ScConditionEntry::IsValid( double nArg, const ScAddress& rPos ) const
991 : {
992 : // Interpret must already have been called
993 3499 : if ( bIsStr1 )
994 : {
995 0 : switch( eOp )
996 : {
997 : case SC_COND_BEGINS_WITH:
998 : case SC_COND_ENDS_WITH:
999 : case SC_COND_CONTAINS_TEXT:
1000 : case SC_COND_NOT_CONTAINS_TEXT:
1001 0 : break;
1002 : case SC_COND_NOTEQUAL:
1003 0 : return true;
1004 : default:
1005 0 : return false;
1006 : }
1007 : }
1008 :
1009 3499 : if ( eOp == SC_COND_BETWEEN || eOp == SC_COND_NOTBETWEEN )
1010 102 : if ( bIsStr2 )
1011 0 : return false;
1012 :
1013 3499 : double nComp1 = nVal1; // Copy, so that it can be changed
1014 3499 : double nComp2 = nVal2;
1015 :
1016 3499 : if ( eOp == SC_COND_BETWEEN || eOp == SC_COND_NOTBETWEEN )
1017 102 : if ( nComp1 > nComp2 )
1018 : {
1019 : // Right order for value range
1020 0 : double nTemp = nComp1; nComp1 = nComp2; nComp2 = nTemp;
1021 : }
1022 :
1023 : // All corner cases need to be tested with ::rtl::math::approxEqual!
1024 3499 : bool bValid = false;
1025 3499 : switch (eOp)
1026 : {
1027 : case SC_COND_NONE:
1028 0 : break; // Always sal_False
1029 : case SC_COND_EQUAL:
1030 1673 : bValid = ::rtl::math::approxEqual( nArg, nComp1 );
1031 1673 : break;
1032 : case SC_COND_NOTEQUAL:
1033 0 : bValid = !::rtl::math::approxEqual( nArg, nComp1 );
1034 0 : break;
1035 : case SC_COND_GREATER:
1036 714 : bValid = ( nArg > nComp1 ) && !::rtl::math::approxEqual( nArg, nComp1 );
1037 714 : break;
1038 : case SC_COND_EQGREATER:
1039 2 : bValid = ( nArg >= nComp1 ) || ::rtl::math::approxEqual( nArg, nComp1 );
1040 2 : break;
1041 : case SC_COND_LESS:
1042 8 : bValid = ( nArg < nComp1 ) && !::rtl::math::approxEqual( nArg, nComp1 );
1043 8 : break;
1044 : case SC_COND_EQLESS:
1045 0 : bValid = ( nArg <= nComp1 ) || ::rtl::math::approxEqual( nArg, nComp1 );
1046 0 : break;
1047 : case SC_COND_BETWEEN:
1048 100 : bValid = ( nArg >= nComp1 && nArg <= nComp2 ) ||
1049 130 : ::rtl::math::approxEqual( nArg, nComp1 ) || ::rtl::math::approxEqual( nArg, nComp2 );
1050 98 : break;
1051 : case SC_COND_NOTBETWEEN:
1052 6 : bValid = ( nArg < nComp1 || nArg > nComp2 ) &&
1053 8 : !::rtl::math::approxEqual( nArg, nComp1 ) && !::rtl::math::approxEqual( nArg, nComp2 );
1054 4 : break;
1055 : case SC_COND_DUPLICATE:
1056 : case SC_COND_NOTDUPLICATE:
1057 0 : if( pCondFormat )
1058 : {
1059 0 : bValid = IsDuplicate( nArg, OUString() );
1060 0 : if( eOp == SC_COND_NOTDUPLICATE )
1061 0 : bValid = !bValid;
1062 : }
1063 0 : break;
1064 : case SC_COND_DIRECT:
1065 14 : bValid = !::rtl::math::approxEqual( nComp1, 0.0 );
1066 14 : break;
1067 : case SC_COND_TOP10:
1068 146 : bValid = IsTopNElement( nArg );
1069 146 : break;
1070 : case SC_COND_BOTTOM10:
1071 147 : bValid = IsBottomNElement( nArg );
1072 147 : break;
1073 : case SC_COND_TOP_PERCENT:
1074 147 : bValid = IsTopNPercent( nArg );
1075 147 : break;
1076 : case SC_COND_BOTTOM_PERCENT:
1077 147 : bValid = IsBottomNPercent( nArg );
1078 147 : break;
1079 : case SC_COND_ABOVE_AVERAGE:
1080 : case SC_COND_ABOVE_EQUAL_AVERAGE:
1081 231 : bValid = IsAboveAverage( nArg, eOp == SC_COND_ABOVE_EQUAL_AVERAGE );
1082 231 : break;
1083 : case SC_COND_BELOW_AVERAGE:
1084 : case SC_COND_BELOW_EQUAL_AVERAGE:
1085 168 : bValid = IsBelowAverage( nArg, eOp == SC_COND_BELOW_EQUAL_AVERAGE );
1086 168 : break;
1087 : case SC_COND_ERROR:
1088 : case SC_COND_NOERROR:
1089 0 : bValid = IsError( rPos );
1090 0 : if( eOp == SC_COND_NOERROR )
1091 0 : bValid = !bValid;
1092 0 : break;
1093 : case SC_COND_BEGINS_WITH:
1094 0 : if(aStrVal1.isEmpty())
1095 : {
1096 0 : OUString aStr = OUString::number(nVal1);
1097 0 : OUString aStr2 = OUString::number(nArg);
1098 0 : bValid = aStr2.startsWith(aStr);
1099 : }
1100 : else
1101 : {
1102 0 : OUString aStr2 = OUString::number(nArg);
1103 0 : bValid = aStr2.startsWith(aStrVal1);
1104 : }
1105 0 : break;
1106 : case SC_COND_ENDS_WITH:
1107 0 : if(aStrVal1.isEmpty())
1108 : {
1109 0 : OUString aStr = OUString::number(nVal1);
1110 0 : OUString aStr2 = OUString::number(nArg);
1111 0 : bValid = !aStr2.endsWith(aStr);
1112 : }
1113 : else
1114 : {
1115 0 : OUString aStr2 = OUString::number(nArg);
1116 0 : bValid = !aStr2.endsWith(aStrVal1);
1117 : }
1118 0 : break;
1119 : case SC_COND_CONTAINS_TEXT:
1120 : case SC_COND_NOT_CONTAINS_TEXT:
1121 0 : if(aStrVal1.isEmpty())
1122 : {
1123 0 : OUString aStr = OUString::number(nVal1);
1124 0 : OUString aStr2 = OUString::number(nArg);
1125 0 : bValid = aStr2.indexOf(aStr) != -1;
1126 : }
1127 : else
1128 : {
1129 0 : OUString aStr2 = OUString::number(nArg);
1130 0 : bValid = aStr2.indexOf(aStrVal1) != -1;
1131 : }
1132 :
1133 0 : if( eOp == SC_COND_NOT_CONTAINS_TEXT )
1134 0 : bValid = !bValid;
1135 0 : break;
1136 : default:
1137 : SAL_WARN("sc", "unknown operation at ScConditionEntry");
1138 0 : break;
1139 : }
1140 3499 : return bValid;
1141 : }
1142 :
1143 1 : bool ScConditionEntry::IsValidStr( const OUString& rArg, const ScAddress& rPos ) const
1144 : {
1145 1 : bool bValid = false;
1146 : // Interpret must already have been called
1147 1 : if ( eOp == SC_COND_DIRECT ) // Formula is independent from the content
1148 0 : return !::rtl::math::approxEqual( nVal1, 0.0 );
1149 :
1150 1 : if ( eOp == SC_COND_DUPLICATE || eOp == SC_COND_NOTDUPLICATE )
1151 : {
1152 0 : if( pCondFormat && !rArg.isEmpty() )
1153 : {
1154 0 : bValid = IsDuplicate( 0.0, rArg );
1155 0 : if( eOp == SC_COND_NOTDUPLICATE )
1156 0 : bValid = !bValid;
1157 0 : return bValid;
1158 : }
1159 : }
1160 :
1161 : // If number contains condition, always false, except for "not equal".
1162 1 : if ( !bIsStr1 && (eOp != SC_COND_ERROR && eOp != SC_COND_NOERROR) )
1163 0 : return ( eOp == SC_COND_NOTEQUAL );
1164 1 : if ( eOp == SC_COND_BETWEEN || eOp == SC_COND_NOTBETWEEN )
1165 0 : if ( !bIsStr2 )
1166 0 : return false;
1167 :
1168 1 : OUString aUpVal1( aStrVal1 ); //TODO: As a member? (Also set in Interpret)
1169 2 : OUString aUpVal2( aStrVal2 );
1170 :
1171 1 : if ( eOp == SC_COND_BETWEEN || eOp == SC_COND_NOTBETWEEN )
1172 0 : if (ScGlobal::GetCollator()->compareString( aUpVal1, aUpVal2 ) > 0)
1173 : {
1174 : // Right order for value range
1175 0 : OUString aTemp( aUpVal1 ); aUpVal1 = aUpVal2; aUpVal2 = aTemp;
1176 : }
1177 :
1178 1 : switch ( eOp )
1179 : {
1180 : case SC_COND_EQUAL:
1181 : bValid = (ScGlobal::GetCollator()->compareString(
1182 0 : rArg, aUpVal1 ) == 0);
1183 0 : break;
1184 : case SC_COND_NOTEQUAL:
1185 : bValid = (ScGlobal::GetCollator()->compareString(
1186 0 : rArg, aUpVal1 ) != 0);
1187 0 : break;
1188 : case SC_COND_TOP_PERCENT:
1189 : case SC_COND_BOTTOM_PERCENT:
1190 : case SC_COND_TOP10:
1191 : case SC_COND_BOTTOM10:
1192 : case SC_COND_ABOVE_AVERAGE:
1193 : case SC_COND_BELOW_AVERAGE:
1194 0 : return false;
1195 : case SC_COND_ERROR:
1196 : case SC_COND_NOERROR:
1197 0 : bValid = IsError( rPos );
1198 0 : if(eOp == SC_COND_NOERROR)
1199 0 : bValid = !bValid;
1200 0 : break;
1201 : case SC_COND_BEGINS_WITH:
1202 0 : bValid = rArg.startsWith(aUpVal1);
1203 0 : break;
1204 : case SC_COND_ENDS_WITH:
1205 0 : bValid = rArg.endsWith(aUpVal1);
1206 0 : break;
1207 : case SC_COND_CONTAINS_TEXT:
1208 : case SC_COND_NOT_CONTAINS_TEXT:
1209 1 : bValid = rArg.indexOf(aUpVal1) != -1;
1210 1 : if(eOp == SC_COND_NOT_CONTAINS_TEXT)
1211 0 : bValid = !bValid;
1212 1 : break;
1213 : default:
1214 : {
1215 : sal_Int32 nCompare = ScGlobal::GetCollator()->compareString(
1216 0 : rArg, aUpVal1 );
1217 0 : switch ( eOp )
1218 : {
1219 : case SC_COND_GREATER:
1220 0 : bValid = ( nCompare > 0 );
1221 0 : break;
1222 : case SC_COND_EQGREATER:
1223 0 : bValid = ( nCompare >= 0 );
1224 0 : break;
1225 : case SC_COND_LESS:
1226 0 : bValid = ( nCompare < 0 );
1227 0 : break;
1228 : case SC_COND_EQLESS:
1229 0 : bValid = ( nCompare <= 0 );
1230 0 : break;
1231 : case SC_COND_BETWEEN:
1232 : case SC_COND_NOTBETWEEN:
1233 : // Test for NOTBETWEEN:
1234 0 : bValid = ( nCompare < 0 ||
1235 : ScGlobal::GetCollator()->compareString( rArg,
1236 0 : aUpVal2 ) > 0 );
1237 0 : if ( eOp == SC_COND_BETWEEN )
1238 0 : bValid = !bValid;
1239 0 : break;
1240 : // SC_COND_DIRECT already handled above
1241 : default:
1242 : SAL_WARN("sc", "unknown operation in ScConditionEntry");
1243 0 : bValid = false;
1244 0 : break;
1245 : }
1246 : }
1247 : }
1248 2 : return bValid;
1249 : }
1250 :
1251 3500 : bool ScConditionEntry::IsCellValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
1252 : {
1253 3500 : const_cast<ScConditionEntry*>(this)->Interpret(rPos); // Evaluate formula
1254 :
1255 3500 : double nArg = 0.0;
1256 3500 : OUString aArgStr;
1257 3500 : bool bVal = lcl_GetCellContent( rCell, bIsStr1, nArg, aArgStr, mpDoc );
1258 3500 : if (bVal)
1259 3499 : return IsValid( nArg, rPos );
1260 : else
1261 1 : return IsValidStr( aArgStr, rPos );
1262 : }
1263 :
1264 305 : OUString ScConditionEntry::GetExpression( const ScAddress& rCursor, sal_uInt16 nIndex,
1265 : sal_uLong nNumFmt,
1266 : const FormulaGrammar::Grammar eGrammar ) const
1267 : {
1268 : assert( nIndex <= 1);
1269 305 : OUString aRet;
1270 :
1271 305 : if ( FormulaGrammar::isEnglish( eGrammar) && nNumFmt == 0 )
1272 280 : nNumFmt = mpDoc->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US );
1273 :
1274 305 : if ( nIndex==0 )
1275 : {
1276 161 : if ( pFormula1 )
1277 : {
1278 65 : ScCompiler aComp(mpDoc, rCursor, *pFormula1);
1279 65 : aComp.SetGrammar(eGrammar);
1280 130 : OUStringBuffer aBuffer;
1281 65 : aComp.CreateStringFromTokenArray( aBuffer );
1282 130 : aRet = aBuffer.makeStringAndClear();
1283 : }
1284 96 : else if (bIsStr1)
1285 : {
1286 0 : aRet = "\"";
1287 0 : aRet += aStrVal1;
1288 0 : aRet += "\"";
1289 : }
1290 : else
1291 96 : mpDoc->GetFormatTable()->GetInputLineString(nVal1, nNumFmt, aRet);
1292 : }
1293 144 : else if ( nIndex==1 )
1294 : {
1295 144 : if ( pFormula2 )
1296 : {
1297 11 : ScCompiler aComp(mpDoc, rCursor, *pFormula2);
1298 11 : aComp.SetGrammar(eGrammar);
1299 22 : OUStringBuffer aBuffer;
1300 11 : aComp.CreateStringFromTokenArray( aBuffer );
1301 22 : aRet = aBuffer.makeStringAndClear();
1302 : }
1303 133 : else if (bIsStr2)
1304 : {
1305 0 : aRet = "\"";
1306 0 : aRet += aStrVal2;
1307 0 : aRet += "\"";
1308 : }
1309 : else
1310 133 : mpDoc->GetFormatTable()->GetInputLineString(nVal2, nNumFmt, aRet);
1311 : }
1312 :
1313 305 : return aRet;
1314 : }
1315 :
1316 21 : ScTokenArray* ScConditionEntry::CreateTokenArry( sal_uInt16 nIndex ) const
1317 : {
1318 : assert(nIndex <= 1);
1319 21 : ScTokenArray* pRet = NULL;
1320 21 : ScAddress aAddr;
1321 :
1322 21 : if ( nIndex==0 )
1323 : {
1324 21 : if ( pFormula1 )
1325 9 : pRet = new ScTokenArray( *pFormula1 );
1326 : else
1327 : {
1328 12 : pRet = new ScTokenArray();
1329 12 : if (bIsStr1)
1330 : {
1331 0 : svl::SharedStringPool& rSPool = mpDoc->GetSharedStringPool();
1332 0 : pRet->AddString(rSPool.intern(aStrVal1));
1333 : }
1334 : else
1335 12 : pRet->AddDouble( nVal1 );
1336 : }
1337 : }
1338 0 : else if ( nIndex==1 )
1339 : {
1340 0 : if ( pFormula2 )
1341 0 : pRet = new ScTokenArray( *pFormula2 );
1342 : else
1343 : {
1344 0 : pRet = new ScTokenArray();
1345 0 : if (bIsStr2)
1346 : {
1347 0 : svl::SharedStringPool& rSPool = mpDoc->GetSharedStringPool();
1348 0 : pRet->AddString(rSPool.intern(aStrVal2));
1349 : }
1350 : else
1351 0 : pRet->AddDouble( nVal2 );
1352 : }
1353 : }
1354 :
1355 21 : return pRet;
1356 : }
1357 :
1358 94 : void ScConditionEntry::SourceChanged( const ScAddress& rChanged )
1359 : {
1360 282 : for (sal_uInt16 nPass = 0; nPass < 2; nPass++)
1361 : {
1362 188 : ScTokenArray* pFormula = nPass ? pFormula2 : pFormula1;
1363 188 : if (pFormula)
1364 : {
1365 141 : pFormula->Reset();
1366 : formula::FormulaToken* t;
1367 423 : while ( ( t = pFormula->GetNextReference() ) != NULL )
1368 : {
1369 141 : SingleDoubleRefProvider aProv( *t );
1370 423 : if ( aProv.Ref1.IsColRel() || aProv.Ref1.IsRowRel() || aProv.Ref1.IsTabRel() ||
1371 141 : aProv.Ref2.IsColRel() || aProv.Ref2.IsRowRel() || aProv.Ref2.IsTabRel() )
1372 : {
1373 : // Absolute must be reached, relative determines range
1374 141 : bool bHit = true;
1375 : SCsCOL nCol1;
1376 : SCsROW nRow1;
1377 : SCsTAB nTab1;
1378 : SCsCOL nCol2;
1379 : SCsROW nRow2;
1380 : SCsTAB nTab2;
1381 :
1382 141 : if ( aProv.Ref1.IsColRel() )
1383 0 : nCol2 = rChanged.Col() - aProv.Ref1.Col();
1384 : else
1385 : {
1386 141 : bHit &= (rChanged.Col() >= aProv.Ref1.Col());
1387 141 : nCol2 = MAXCOL;
1388 : }
1389 141 : if ( aProv.Ref1.IsRowRel() )
1390 0 : nRow2 = rChanged.Row() - aProv.Ref1.Row();
1391 : else
1392 : {
1393 141 : bHit &= ( rChanged.Row() >= aProv.Ref1.Row() );
1394 141 : nRow2 = MAXROW;
1395 : }
1396 141 : if ( aProv.Ref1.IsTabRel() )
1397 141 : nTab2 = rChanged.Tab() - aProv.Ref1.Tab();
1398 : else
1399 : {
1400 0 : bHit &= (rChanged.Tab() >= aProv.Ref1.Tab());
1401 0 : nTab2 = MAXTAB;
1402 : }
1403 :
1404 141 : if ( aProv.Ref2.IsColRel() )
1405 0 : nCol1 = rChanged.Col() - aProv.Ref2.Col();
1406 : else
1407 : {
1408 141 : bHit &= ( rChanged.Col() <= aProv.Ref2.Col() );
1409 141 : nCol1 = 0;
1410 : }
1411 141 : if ( aProv.Ref2.IsRowRel() )
1412 0 : nRow1 = rChanged.Row() - aProv.Ref2.Row();
1413 : else
1414 : {
1415 141 : bHit &= (rChanged.Row() <= aProv.Ref2.Row());
1416 141 : nRow1 = 0;
1417 : }
1418 141 : if ( aProv.Ref2.IsTabRel() )
1419 141 : nTab1 = rChanged.Tab() - aProv.Ref2.Tab();
1420 : else
1421 : {
1422 0 : bHit &= (rChanged.Tab() <= aProv.Ref2.Tab());
1423 0 : nTab1 = 0;
1424 : }
1425 :
1426 141 : if ( bHit )
1427 : {
1428 : // Limit paint!
1429 11 : ScRange aPaint( nCol1,nRow1,nTab1, nCol2,nRow2,nTab2 );
1430 :
1431 : // No paint if it's the cell itself
1432 11 : if ( aPaint.IsValid() && (aPaint.aStart != rChanged || aPaint.aEnd != rChanged ))
1433 11 : DataChanged( &aPaint );
1434 : }
1435 : }
1436 141 : }
1437 : }
1438 : }
1439 94 : }
1440 :
1441 : /**
1442 : * Return a position that's adjusted to allow textual representation
1443 : * of expressions if possible
1444 : */
1445 76 : ScAddress ScConditionEntry::GetValidSrcPos() const
1446 : {
1447 76 : SCTAB nMinTab = aSrcPos.Tab();
1448 76 : SCTAB nMaxTab = nMinTab;
1449 :
1450 228 : for (sal_uInt16 nPass = 0; nPass < 2; nPass++)
1451 : {
1452 152 : ScTokenArray* pFormula = nPass ? pFormula2 : pFormula1;
1453 152 : if (pFormula)
1454 : {
1455 8 : pFormula->Reset();
1456 : formula::FormulaToken* t;
1457 22 : while ( ( t = pFormula->GetNextReference() ) != NULL )
1458 : {
1459 6 : ScSingleRefData& rRef1 = *t->GetSingleRef();
1460 6 : ScAddress aAbs = rRef1.toAbs(aSrcPos);
1461 6 : if (!rRef1.IsTabDeleted())
1462 : {
1463 6 : if (aAbs.Tab() < nMinTab)
1464 0 : nMinTab = aAbs.Tab();
1465 6 : if (aAbs.Tab() > nMaxTab)
1466 0 : nMaxTab = aAbs.Tab();
1467 : }
1468 6 : if ( t->GetType() == svDoubleRef )
1469 : {
1470 2 : ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
1471 2 : aAbs = rRef2.toAbs(aSrcPos);
1472 2 : if (!rRef2.IsTabDeleted())
1473 : {
1474 2 : if (aAbs.Tab() < nMinTab)
1475 0 : nMinTab = aAbs.Tab();
1476 2 : if (aAbs.Tab() > nMaxTab)
1477 0 : nMaxTab = aAbs.Tab();
1478 : }
1479 : }
1480 : }
1481 : }
1482 : }
1483 :
1484 76 : ScAddress aValidPos = aSrcPos;
1485 76 : SCTAB nTabCount = mpDoc->GetTableCount();
1486 76 : if ( nMaxTab >= nTabCount && nMinTab > 0 )
1487 0 : aValidPos.SetTab( aSrcPos.Tab() - nMinTab ); // so the lowest tab ref will be on 0
1488 :
1489 76 : if ( aValidPos.Tab() >= nTabCount )
1490 0 : aValidPos.SetTab( nTabCount - 1 ); // ensure a valid position even if some references will be invalid
1491 :
1492 76 : return aValidPos;
1493 : }
1494 :
1495 0 : void ScConditionEntry::DataChanged( const ScRange* /* pModified */ ) const
1496 : {
1497 : //FIXME: Nothing so far
1498 0 : }
1499 :
1500 0 : bool ScConditionEntry::MarkUsedExternalReferences() const
1501 : {
1502 0 : bool bAllMarked = false;
1503 0 : for (sal_uInt16 nPass = 0; !bAllMarked && nPass < 2; nPass++)
1504 : {
1505 0 : ScTokenArray* pFormula = nPass ? pFormula2 : pFormula1;
1506 0 : if (pFormula)
1507 0 : bAllMarked = mpDoc->MarkUsedExternalReferences(*pFormula, aSrcPos);
1508 : }
1509 0 : return bAllMarked;
1510 : }
1511 :
1512 0 : ScFormatEntry* ScConditionEntry::Clone(ScDocument* pDoc) const
1513 : {
1514 0 : return new ScConditionEntry(pDoc, *this);
1515 : }
1516 :
1517 59 : ScConditionMode ScConditionEntry::GetModeFromApi(sal_Int32 nOperation)
1518 : {
1519 59 : ScConditionMode eMode = SC_COND_NONE;
1520 59 : switch (nOperation)
1521 : {
1522 : case com::sun::star::sheet::ConditionOperator2::EQUAL:
1523 18 : eMode = SC_COND_EQUAL;
1524 18 : break;
1525 : case com::sun::star::sheet::ConditionOperator2::LESS:
1526 3 : eMode = SC_COND_LESS;
1527 3 : break;
1528 : case com::sun::star::sheet::ConditionOperator2::GREATER:
1529 7 : eMode = SC_COND_GREATER;
1530 7 : break;
1531 : case com::sun::star::sheet::ConditionOperator2::LESS_EQUAL:
1532 0 : eMode = SC_COND_EQLESS;
1533 0 : break;
1534 : case com::sun::star::sheet::ConditionOperator2::GREATER_EQUAL:
1535 6 : eMode = SC_COND_EQGREATER;
1536 6 : break;
1537 : case com::sun::star::sheet::ConditionOperator2::NOT_EQUAL:
1538 0 : eMode = SC_COND_NOTEQUAL;
1539 0 : break;
1540 : case com::sun::star::sheet::ConditionOperator2::BETWEEN:
1541 13 : eMode = SC_COND_BETWEEN;
1542 13 : break;
1543 : case com::sun::star::sheet::ConditionOperator2::NOT_BETWEEN:
1544 6 : eMode = SC_COND_NOTBETWEEN;
1545 6 : break;
1546 : case com::sun::star::sheet::ConditionOperator2::FORMULA:
1547 6 : eMode = SC_COND_DIRECT;
1548 6 : break;
1549 : case com::sun::star::sheet::ConditionOperator2::DUPLICATE:
1550 0 : eMode = SC_COND_DUPLICATE;
1551 0 : break;
1552 : case com::sun::star::sheet::ConditionOperator2::NOT_DUPLICATE:
1553 0 : eMode = SC_COND_NOTDUPLICATE;
1554 0 : break;
1555 : default:
1556 0 : break;
1557 : }
1558 59 : return eMode;
1559 : }
1560 :
1561 169 : void ScConditionEntry::startRendering()
1562 : {
1563 169 : mpCache.reset();
1564 169 : }
1565 :
1566 169 : void ScConditionEntry::endRendering()
1567 : {
1568 169 : mpCache.reset();
1569 169 : }
1570 :
1571 119 : ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper,
1572 : const OUString& rExpr1, const OUString& rExpr2,
1573 : ScDocument* pDocument, const ScAddress& rPos,
1574 : const OUString& rStyle,
1575 : const OUString& rExprNmsp1, const OUString& rExprNmsp2,
1576 : FormulaGrammar::Grammar eGrammar1,
1577 : FormulaGrammar::Grammar eGrammar2 ) :
1578 : ScConditionEntry( eOper, rExpr1, rExpr2, pDocument, rPos, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2 ),
1579 119 : aStyleName( rStyle )
1580 : {
1581 119 : }
1582 :
1583 33 : ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper,
1584 : const ScTokenArray* pArr1, const ScTokenArray* pArr2,
1585 : ScDocument* pDocument, const ScAddress& rPos,
1586 : const OUString& rStyle ) :
1587 : ScConditionEntry( eOper, pArr1, pArr2, pDocument, rPos ),
1588 33 : aStyleName( rStyle )
1589 : {
1590 33 : }
1591 :
1592 0 : ScCondFormatEntry::ScCondFormatEntry( const ScCondFormatEntry& r ) :
1593 : ScConditionEntry( r ),
1594 0 : aStyleName( r.aStyleName )
1595 : {
1596 0 : }
1597 :
1598 58 : ScCondFormatEntry::ScCondFormatEntry( ScDocument* pDocument, const ScCondFormatEntry& r ) :
1599 : ScConditionEntry( pDocument, r ),
1600 58 : aStyleName( r.aStyleName )
1601 : {
1602 58 : }
1603 :
1604 0 : bool ScCondFormatEntry::operator== ( const ScCondFormatEntry& r ) const
1605 : {
1606 0 : return ScConditionEntry::operator==( r ) &&
1607 0 : aStyleName == r.aStyleName;
1608 : }
1609 :
1610 414 : ScCondFormatEntry::~ScCondFormatEntry()
1611 : {
1612 414 : }
1613 :
1614 11 : void ScCondFormatEntry::DataChanged( const ScRange* pModified ) const
1615 : {
1616 11 : if ( pCondFormat )
1617 11 : pCondFormat->DoRepaint( pModified );
1618 11 : }
1619 :
1620 58 : ScFormatEntry* ScCondFormatEntry::Clone( ScDocument* pDoc ) const
1621 : {
1622 58 : return new ScCondFormatEntry( pDoc, *this );
1623 : }
1624 :
1625 4 : ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument* pDoc )
1626 : : ScFormatEntry( pDoc )
1627 4 : , meType(condformat::TODAY)
1628 : {
1629 4 : }
1630 :
1631 0 : ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument* pDoc, const ScCondDateFormatEntry& rFormat ):
1632 : ScFormatEntry( pDoc ),
1633 : meType( rFormat.meType ),
1634 0 : maStyleName( rFormat.maStyleName )
1635 : {
1636 0 : }
1637 :
1638 0 : bool ScCondDateFormatEntry::IsValid( const ScAddress& rPos ) const
1639 : {
1640 0 : CellType eCellType = mpDoc->GetCellType(rPos);
1641 :
1642 0 : if (eCellType == CELLTYPE_NONE)
1643 : // empty cell.
1644 0 : return false;
1645 :
1646 0 : if (eCellType != CELLTYPE_VALUE && eCellType != CELLTYPE_FORMULA)
1647 : // non-numerical cell.
1648 0 : return false;
1649 :
1650 0 : if( !mpCache )
1651 0 : mpCache.reset( new Date( Date::SYSTEM ) );
1652 :
1653 0 : const Date& rActDate = *mpCache;
1654 0 : SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
1655 0 : long nCurrentDate = rActDate - *(pFormatter->GetNullDate());
1656 :
1657 0 : double nVal = mpDoc->GetValue(rPos);
1658 0 : long nCellDate = (long) ::rtl::math::approxFloor(nVal);
1659 0 : Date aCellDate = *(pFormatter->GetNullDate());
1660 0 : aCellDate += (long) ::rtl::math::approxFloor(nVal);
1661 :
1662 0 : switch(meType)
1663 : {
1664 : case condformat::TODAY:
1665 0 : if( nCurrentDate == nCellDate )
1666 0 : return true;
1667 0 : break;
1668 : case condformat::TOMORROW:
1669 0 : if( nCurrentDate == nCellDate -1 )
1670 0 : return true;
1671 0 : break;
1672 : case condformat::YESTERDAY:
1673 0 : if( nCurrentDate == nCellDate + 1)
1674 0 : return true;
1675 0 : break;
1676 : case condformat::LAST7DAYS:
1677 0 : if( nCurrentDate >= nCellDate && nCurrentDate - 7 < nCellDate )
1678 0 : return true;
1679 0 : break;
1680 : case condformat::LASTWEEK:
1681 0 : if( rActDate.GetDayOfWeek() != SUNDAY )
1682 : {
1683 0 : Date aBegin(rActDate - 8 - static_cast<long>(rActDate.GetDayOfWeek()));
1684 0 : Date aEnd(rActDate - 2 -static_cast<long>(rActDate.GetDayOfWeek()));
1685 0 : return aCellDate.IsBetween( aBegin, aEnd );
1686 : }
1687 : else
1688 : {
1689 0 : Date aBegin(rActDate - 8);
1690 0 : Date aEnd(rActDate - 1);
1691 0 : return aCellDate.IsBetween( aBegin, aEnd );
1692 : }
1693 : break;
1694 : case condformat::THISWEEK:
1695 0 : if( rActDate.GetDayOfWeek() != SUNDAY )
1696 : {
1697 0 : Date aBegin(rActDate - 1 - static_cast<long>(rActDate.GetDayOfWeek()));
1698 0 : Date aEnd(rActDate + 5 - static_cast<long>(rActDate.GetDayOfWeek()));
1699 0 : return aCellDate.IsBetween( aBegin, aEnd );
1700 : }
1701 : else
1702 : {
1703 0 : Date aEnd( rActDate + 6);
1704 0 : return aCellDate.IsBetween( rActDate, aEnd );
1705 : }
1706 : break;
1707 : case condformat::NEXTWEEK:
1708 0 : if( rActDate.GetDayOfWeek() != SUNDAY )
1709 : {
1710 0 : return aCellDate.IsBetween( rActDate + 6 - static_cast<long>(rActDate.GetDayOfWeek()), rActDate + 12 - static_cast<long>(rActDate.GetDayOfWeek()) );
1711 : }
1712 : else
1713 : {
1714 0 : return aCellDate.IsBetween( rActDate + 7, rActDate + 13 );
1715 : }
1716 : break;
1717 : case condformat::LASTMONTH:
1718 0 : if( rActDate.GetMonth() == 1 )
1719 : {
1720 0 : if( aCellDate.GetMonth() == 12 && rActDate.GetYear() == aCellDate.GetYear() + 1 )
1721 0 : return true;
1722 : }
1723 0 : else if( rActDate.GetYear() == aCellDate.GetYear() )
1724 : {
1725 0 : if( rActDate.GetMonth() == aCellDate.GetMonth() + 1)
1726 0 : return true;
1727 : }
1728 0 : break;
1729 : case condformat::THISMONTH:
1730 0 : if( rActDate.GetYear() == aCellDate.GetYear() )
1731 : {
1732 0 : if( rActDate.GetMonth() == aCellDate.GetMonth() )
1733 0 : return true;
1734 : }
1735 0 : break;
1736 : case condformat::NEXTMONTH:
1737 0 : if( rActDate.GetMonth() == 12 )
1738 : {
1739 0 : if( aCellDate.GetMonth() == 1 && rActDate.GetYear() == aCellDate.GetYear() - 1 )
1740 0 : return true;
1741 : }
1742 0 : else if( rActDate.GetYear() == aCellDate.GetYear() )
1743 : {
1744 0 : if( rActDate.GetMonth() == aCellDate.GetMonth() - 1)
1745 0 : return true;
1746 : }
1747 0 : break;
1748 : case condformat::LASTYEAR:
1749 0 : if( rActDate.GetYear() == aCellDate.GetYear() + 1 )
1750 0 : return true;
1751 0 : break;
1752 : case condformat::THISYEAR:
1753 0 : if( rActDate.GetYear() == aCellDate.GetYear() )
1754 0 : return true;
1755 0 : break;
1756 : case condformat::NEXTYEAR:
1757 0 : if( rActDate.GetYear() == aCellDate.GetYear() - 1 )
1758 0 : return true;
1759 0 : break;
1760 : }
1761 :
1762 0 : return false;
1763 : }
1764 :
1765 4 : void ScCondDateFormatEntry::SetDateType( condformat::ScCondFormatDateType eType )
1766 : {
1767 4 : meType = eType;
1768 4 : }
1769 :
1770 4 : void ScCondDateFormatEntry::SetStyleName( const OUString& rStyleName )
1771 : {
1772 4 : maStyleName = rStyleName;
1773 4 : }
1774 :
1775 0 : ScFormatEntry* ScCondDateFormatEntry::Clone( ScDocument* pDoc ) const
1776 : {
1777 0 : return new ScCondDateFormatEntry( pDoc, *this );
1778 : }
1779 :
1780 0 : bool ScCondDateFormatEntry::operator==( const ScFormatEntry& r ) const
1781 : {
1782 0 : if(r.GetType() != condformat::DATE)
1783 0 : return false;
1784 :
1785 0 : const ScCondDateFormatEntry& rEntry = static_cast<const ScCondDateFormatEntry&>(r);
1786 :
1787 0 : if(rEntry.meType != meType)
1788 0 : return false;
1789 :
1790 0 : return rEntry.maStyleName == maStyleName;
1791 : }
1792 :
1793 0 : void ScCondDateFormatEntry::startRendering()
1794 : {
1795 0 : mpCache.reset();
1796 0 : }
1797 :
1798 0 : void ScCondDateFormatEntry::endRendering()
1799 : {
1800 0 : mpCache.reset();
1801 0 : }
1802 :
1803 250 : ScConditionalFormat::ScConditionalFormat(sal_uInt32 nNewKey, ScDocument* pDocument) :
1804 : pDoc( pDocument ),
1805 250 : nKey( nNewKey )
1806 : {
1807 250 : }
1808 :
1809 56 : ScConditionalFormat* ScConditionalFormat::Clone(ScDocument* pNewDoc) const
1810 : {
1811 : // Real copy of the formula (for Ref Undo/between documents)
1812 56 : if (!pNewDoc)
1813 2 : pNewDoc = pDoc;
1814 :
1815 56 : ScConditionalFormat* pNew = new ScConditionalFormat(nKey, pNewDoc);
1816 :
1817 114 : for (CondFormatContainer::const_iterator itr = maEntries.begin(); itr != maEntries.end(); ++itr)
1818 : {
1819 58 : ScFormatEntry* pNewEntry = itr->Clone(pNewDoc);
1820 58 : pNew->maEntries.push_back( pNewEntry );
1821 58 : pNewEntry->SetParent(pNew);
1822 : }
1823 56 : pNew->SetRange( maRanges );
1824 :
1825 56 : return pNew;
1826 : }
1827 :
1828 0 : bool ScConditionalFormat::EqualEntries( const ScConditionalFormat& r ) const
1829 : {
1830 0 : if( size() != r.size())
1831 0 : return false;
1832 :
1833 : //TODO: Test for same entries in reverse order?
1834 0 : for (size_t i=0; i<size(); i++)
1835 0 : if ( ! (maEntries == r.maEntries ) )
1836 0 : return false;
1837 :
1838 : // right now don't check for same range
1839 : // we only use this method to merge same conditional formats from
1840 : // old ODF data structure
1841 0 : return true;
1842 : }
1843 :
1844 249 : void ScConditionalFormat::SetRange( const ScRangeList& rRanges )
1845 : {
1846 249 : maRanges = rRanges;
1847 : SAL_WARN_IF(maRanges.empty(), "sc", "the conditional format range is empty! will result in a crash later!");
1848 249 : }
1849 :
1850 260 : void ScConditionalFormat::AddEntry( ScFormatEntry* pNew )
1851 : {
1852 260 : maEntries.push_back(pNew);
1853 260 : pNew->SetParent(this);
1854 260 : }
1855 :
1856 0 : void ScConditionalFormat::RemoveEntry(size_t n)
1857 : {
1858 0 : if (n < maEntries.size())
1859 : {
1860 0 : maEntries.erase(maEntries.begin() + n);
1861 0 : DoRepaint(NULL);
1862 : }
1863 0 : }
1864 :
1865 3 : bool ScConditionalFormat::IsEmpty() const
1866 : {
1867 3 : return maEntries.empty();
1868 : }
1869 :
1870 13512 : size_t ScConditionalFormat::size() const
1871 : {
1872 13512 : return maEntries.size();
1873 : }
1874 :
1875 234 : ScConditionalFormat::~ScConditionalFormat()
1876 : {
1877 234 : }
1878 :
1879 6832 : const ScFormatEntry* ScConditionalFormat::GetEntry( sal_uInt16 nPos ) const
1880 : {
1881 6832 : if ( nPos < size() )
1882 6832 : return &maEntries[nPos];
1883 : else
1884 0 : return NULL;
1885 : }
1886 :
1887 695 : const OUString& ScConditionalFormat::GetCellStyle( ScRefCellValue& rCell, const ScAddress& rPos ) const
1888 : {
1889 1148 : for (CondFormatContainer::const_iterator itr = maEntries.begin(); itr != maEntries.end(); ++itr)
1890 : {
1891 713 : if(itr->GetType() == condformat::CONDITION)
1892 : {
1893 713 : const ScCondFormatEntry& rEntry = static_cast<const ScCondFormatEntry&>(*itr);
1894 713 : if (rEntry.IsCellValid(rCell, rPos))
1895 520 : return rEntry.GetStyle();
1896 : }
1897 0 : else if(itr->GetType() == condformat::DATE)
1898 : {
1899 0 : const ScCondDateFormatEntry& rEntry = static_cast<const ScCondDateFormatEntry&>(*itr);
1900 0 : if (rEntry.IsValid( rPos ))
1901 0 : return rEntry.GetStyleName();
1902 : }
1903 : }
1904 :
1905 435 : return EMPTY_OUSTRING;
1906 : }
1907 :
1908 2740 : ScCondFormatData ScConditionalFormat::GetData( ScRefCellValue& rCell, const ScAddress& rPos ) const
1909 : {
1910 2740 : ScCondFormatData aData;
1911 5542 : for(CondFormatContainer::const_iterator itr = maEntries.begin(); itr != maEntries.end(); ++itr)
1912 : {
1913 2802 : if(itr->GetType() == condformat::CONDITION && aData.aStyleName.isEmpty())
1914 : {
1915 2787 : const ScCondFormatEntry& rEntry = static_cast<const ScCondFormatEntry&>(*itr);
1916 2787 : if (rEntry.IsCellValid(rCell, rPos))
1917 176 : aData.aStyleName = rEntry.GetStyle();
1918 : }
1919 15 : else if(itr->GetType() == condformat::COLORSCALE && !aData.pColorScale)
1920 : {
1921 0 : const ScColorScaleFormat& rEntry = static_cast<const ScColorScaleFormat&>(*itr);
1922 0 : aData.pColorScale = rEntry.GetColor(rPos);
1923 : }
1924 15 : else if(itr->GetType() == condformat::DATABAR && !aData.pDataBar)
1925 : {
1926 0 : const ScDataBarFormat& rEntry = static_cast<const ScDataBarFormat&>(*itr);
1927 0 : aData.pDataBar = rEntry.GetDataBarInfo(rPos);
1928 : }
1929 15 : else if(itr->GetType() == condformat::ICONSET && !aData.pIconSet)
1930 : {
1931 0 : const ScIconSetFormat& rEntry = static_cast<const ScIconSetFormat&>(*itr);
1932 0 : aData.pIconSet = rEntry.GetIconSetInfo(rPos);
1933 : }
1934 15 : else if(itr->GetType() == condformat::DATE && aData.aStyleName.isEmpty())
1935 : {
1936 0 : const ScCondDateFormatEntry& rEntry = static_cast<const ScCondDateFormatEntry&>(*itr);
1937 0 : if ( rEntry.IsValid( rPos ) )
1938 0 : aData.aStyleName = rEntry.GetStyleName();
1939 : }
1940 : }
1941 2740 : return aData;
1942 : }
1943 :
1944 11 : void ScConditionalFormat::DoRepaint( const ScRange* pModified )
1945 : {
1946 11 : if(pModified)
1947 : {
1948 11 : if(maRanges.Intersects(*pModified))
1949 11 : pDoc->RepaintRange(*pModified);
1950 : }
1951 : else
1952 : {
1953 : // all conditional format cells
1954 0 : pDoc->RepaintRange( maRanges );
1955 : }
1956 11 : }
1957 :
1958 1 : void ScConditionalFormat::CompileAll()
1959 : {
1960 2 : for(CondFormatContainer::iterator itr = maEntries.begin(); itr != maEntries.end(); ++itr)
1961 1 : if(itr->GetType() == condformat::CONDITION)
1962 1 : static_cast<ScCondFormatEntry&>(*itr).CompileAll();
1963 1 : }
1964 :
1965 88 : void ScConditionalFormat::CompileXML()
1966 : {
1967 218 : for(CondFormatContainer::iterator itr = maEntries.begin(); itr != maEntries.end(); ++itr)
1968 130 : if(itr->GetType() == condformat::CONDITION)
1969 69 : static_cast<ScCondFormatEntry&>(*itr).CompileXML();
1970 88 : }
1971 :
1972 19 : void ScConditionalFormat::UpdateReference( sc::RefUpdateContext& rCxt, bool bCopyAsMove )
1973 : {
1974 39 : for(CondFormatContainer::iterator itr = maEntries.begin(); itr != maEntries.end(); ++itr)
1975 20 : itr->UpdateReference(rCxt);
1976 :
1977 19 : if (rCxt.meMode == URM_COPY && bCopyAsMove)
1978 12 : maRanges.UpdateReference(URM_MOVE, pDoc, rCxt.maRange, rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
1979 : else
1980 7 : maRanges.UpdateReference(rCxt.meMode, pDoc, rCxt.maRange, rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
1981 19 : }
1982 :
1983 1 : void ScConditionalFormat::InsertRow(SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, SCROW nRowPos, SCSIZE nSize)
1984 : {
1985 1 : maRanges.InsertRow(nTab, nColStart, nColEnd, nRowPos, nSize);
1986 1 : }
1987 :
1988 2 : void ScConditionalFormat::InsertCol(SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, SCCOL nColPos, SCSIZE nSize)
1989 : {
1990 2 : maRanges.InsertCol(nTab, nRowStart, nRowEnd, nColPos, nSize);
1991 2 : }
1992 :
1993 3 : void ScConditionalFormat::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
1994 : {
1995 6 : for (size_t i = 0, n = maRanges.size(); i < n; ++i)
1996 : {
1997 : // We assume that the start and end sheet indices are equal.
1998 3 : ScRange* pRange = maRanges[i];
1999 3 : SCTAB nTab = pRange->aStart.Tab();
2000 :
2001 3 : if (nTab < rCxt.mnInsertPos)
2002 : // Unaffected.
2003 1 : continue;
2004 :
2005 2 : pRange->aStart.IncTab(rCxt.mnSheets);
2006 2 : pRange->aEnd.IncTab(rCxt.mnSheets);
2007 : }
2008 :
2009 6 : for (CondFormatContainer::iterator it = maEntries.begin(); it != maEntries.end(); ++it)
2010 3 : it->UpdateInsertTab(rCxt);
2011 3 : }
2012 :
2013 4 : void ScConditionalFormat::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
2014 : {
2015 8 : for (size_t i = 0, n = maRanges.size(); i < n; ++i)
2016 : {
2017 : // We assume that the start and end sheet indices are equal.
2018 4 : ScRange* pRange = maRanges[i];
2019 4 : SCTAB nTab = pRange->aStart.Tab();
2020 :
2021 4 : if (nTab < rCxt.mnDeletePos)
2022 : // Left of the deleted sheet(s). Unaffected.
2023 1 : continue;
2024 :
2025 3 : if (nTab <= rCxt.mnDeletePos+rCxt.mnSheets-1)
2026 : {
2027 : // On the deleted sheet(s).
2028 2 : pRange->aStart.SetTab(-1);
2029 2 : pRange->aEnd.SetTab(-1);
2030 2 : continue;
2031 : }
2032 :
2033 : // Right of the deleted sheet(s). Adjust the sheet indices.
2034 1 : pRange->aStart.IncTab(-1*rCxt.mnSheets);
2035 1 : pRange->aEnd.IncTab(-1*rCxt.mnSheets);
2036 : }
2037 :
2038 8 : for (CondFormatContainer::iterator it = maEntries.begin(); it != maEntries.end(); ++it)
2039 4 : it->UpdateDeleteTab(rCxt);
2040 4 : }
2041 :
2042 0 : void ScConditionalFormat::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
2043 : {
2044 0 : size_t n = maRanges.size();
2045 0 : SCTAB nMinTab = std::min<SCTAB>(rCxt.mnOldPos, rCxt.mnNewPos);
2046 0 : SCTAB nMaxTab = std::max<SCTAB>(rCxt.mnOldPos, rCxt.mnNewPos);
2047 0 : for(size_t i = 0; i < n; ++i)
2048 : {
2049 0 : ScRange* pRange = maRanges[i];
2050 0 : SCTAB nTab = pRange->aStart.Tab();
2051 0 : if(nTab < nMinTab || nTab > nMaxTab)
2052 : {
2053 0 : continue;
2054 : }
2055 :
2056 0 : if (nTab == rCxt.mnOldPos)
2057 : {
2058 0 : pRange->aStart.SetTab(rCxt.mnNewPos);
2059 0 : pRange->aEnd.SetTab(rCxt.mnNewPos);
2060 0 : continue;
2061 : }
2062 :
2063 0 : if (rCxt.mnNewPos < rCxt.mnOldPos)
2064 : {
2065 0 : pRange->aStart.IncTab();
2066 0 : pRange->aEnd.IncTab();
2067 : }
2068 : else
2069 : {
2070 0 : pRange->aStart.IncTab(-1);
2071 0 : pRange->aEnd.IncTab(-1);
2072 : }
2073 : }
2074 :
2075 0 : for (CondFormatContainer::iterator it = maEntries.begin(); it != maEntries.end(); ++it)
2076 0 : it->UpdateMoveTab(rCxt);
2077 0 : }
2078 :
2079 74 : void ScConditionalFormat::DeleteArea( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
2080 : {
2081 74 : if (maRanges.empty())
2082 74 : return;
2083 :
2084 74 : SCTAB nTab = maRanges[0]->aStart.Tab();
2085 74 : maRanges.DeleteArea( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
2086 : }
2087 :
2088 0 : void ScConditionalFormat::RenameCellStyle(const OUString& rOld, const OUString& rNew)
2089 : {
2090 0 : for(CondFormatContainer::iterator itr = maEntries.begin(); itr != maEntries.end(); ++itr)
2091 0 : if(itr->GetType() == condformat::CONDITION)
2092 : {
2093 0 : ScCondFormatEntry& rFormat = static_cast<ScCondFormatEntry&>(*itr);
2094 0 : if(rFormat.GetStyle() == rOld)
2095 0 : rFormat.UpdateStyleName( rNew );
2096 : }
2097 0 : }
2098 :
2099 99 : void ScConditionalFormat::SourceChanged( const ScAddress& rAddr )
2100 : {
2101 193 : for(CondFormatContainer::iterator itr = maEntries.begin(); itr != maEntries.end(); ++itr)
2102 : {
2103 94 : condformat::ScFormatEntryType eEntryType = itr->GetType();
2104 94 : if( eEntryType == condformat::CONDITION)
2105 : {
2106 94 : ScCondFormatEntry& rFormat = static_cast<ScCondFormatEntry&>(*itr);
2107 94 : rFormat.SourceChanged( rAddr );
2108 : }
2109 0 : else if( eEntryType == condformat::COLORSCALE ||
2110 0 : eEntryType == condformat::DATABAR ||
2111 : eEntryType == condformat::ICONSET )
2112 : {
2113 0 : ScColorFormat& rFormat = static_cast<ScColorFormat&>(*itr);
2114 0 : if(rFormat.NeedsRepaint())
2115 : {
2116 : // we need to repaint the whole range anyway
2117 0 : DoRepaint(NULL);
2118 0 : return;
2119 : }
2120 : }
2121 : }
2122 : }
2123 :
2124 0 : bool ScConditionalFormat::MarkUsedExternalReferences() const
2125 : {
2126 0 : bool bAllMarked = false;
2127 0 : for(CondFormatContainer::const_iterator itr = maEntries.begin(); itr != maEntries.end() && !bAllMarked; ++itr)
2128 0 : if(itr->GetType() == condformat::CONDITION)
2129 : {
2130 0 : const ScCondFormatEntry& rFormat = static_cast<const ScCondFormatEntry&>(*itr);
2131 0 : bAllMarked = rFormat.MarkUsedExternalReferences();
2132 : }
2133 :
2134 0 : return bAllMarked;
2135 : }
2136 :
2137 166 : void ScConditionalFormat::startRendering()
2138 : {
2139 335 : for(CondFormatContainer::iterator itr = maEntries.begin(); itr != maEntries.end(); ++itr)
2140 : {
2141 169 : itr->startRendering();
2142 : }
2143 166 : }
2144 :
2145 166 : void ScConditionalFormat::endRendering()
2146 : {
2147 335 : for(CondFormatContainer::iterator itr = maEntries.begin(); itr != maEntries.end(); ++itr)
2148 : {
2149 169 : itr->endRendering();
2150 : }
2151 166 : }
2152 :
2153 0 : ScConditionalFormatList::ScConditionalFormatList(const ScConditionalFormatList& rList)
2154 : {
2155 0 : for(const_iterator itr = rList.begin(); itr != rList.end(); ++itr)
2156 0 : InsertNew( itr->Clone() );
2157 0 : }
2158 :
2159 824 : ScConditionalFormatList::ScConditionalFormatList(ScDocument* pDoc, const ScConditionalFormatList& rList)
2160 : {
2161 866 : for(const_iterator itr = rList.begin(); itr != rList.end(); ++itr)
2162 42 : InsertNew( itr->Clone(pDoc) );
2163 824 : }
2164 :
2165 229 : void ScConditionalFormatList::InsertNew( ScConditionalFormat* pNew )
2166 : {
2167 229 : maConditionalFormats.insert(pNew);
2168 229 : }
2169 :
2170 0 : bool ScConditionalFormatList::operator==( const ScConditionalFormatList& r ) const
2171 : {
2172 : // For Ref Undo - internal variables are not compared
2173 0 : sal_uInt16 nCount = size();
2174 0 : bool bEqual = ( nCount == r.size() );
2175 0 : const_iterator locIterator = begin();
2176 0 : for(const_iterator itr = r.begin(); itr != r.end() && bEqual; ++itr, ++locIterator)
2177 0 : if ( !locIterator->EqualEntries(*itr) ) // Entries differ?
2178 0 : bEqual = false;
2179 :
2180 0 : return bEqual;
2181 : }
2182 :
2183 9333 : ScConditionalFormat* ScConditionalFormatList::GetFormat( sal_uInt32 nKey )
2184 : {
2185 : //FIXME: Binary search
2186 11195 : for( iterator itr = begin(); itr != end(); ++itr)
2187 11195 : if (itr->GetKey() == nKey)
2188 9333 : return &(*itr);
2189 :
2190 : SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
2191 0 : return NULL;
2192 : }
2193 :
2194 695 : const ScConditionalFormat* ScConditionalFormatList::GetFormat( sal_uInt32 nKey ) const
2195 : {
2196 : //FIXME: Binary search
2197 3064 : for ( const_iterator itr = begin(); itr != end(); ++itr)
2198 3064 : if (itr->GetKey() == nKey)
2199 695 : return &(*itr);
2200 :
2201 : SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
2202 0 : return NULL;
2203 : }
2204 :
2205 14 : void ScConditionalFormatList::CompileAll()
2206 : {
2207 15 : for( iterator itr = begin(); itr != end(); ++itr)
2208 1 : itr->CompileAll();
2209 14 : }
2210 :
2211 331 : void ScConditionalFormatList::CompileXML()
2212 : {
2213 419 : for( iterator itr = begin(); itr != end(); ++itr)
2214 88 : itr->CompileXML();
2215 331 : }
2216 :
2217 258 : void ScConditionalFormatList::UpdateReference( sc::RefUpdateContext& rCxt )
2218 : {
2219 265 : for( iterator itr = begin(); itr != end(); ++itr)
2220 7 : itr->UpdateReference(rCxt);
2221 :
2222 258 : if (rCxt.meMode == URM_INSDEL)
2223 : {
2224 : // need to check which must be deleted
2225 184 : CheckAllEntries();
2226 : }
2227 258 : }
2228 :
2229 54 : void ScConditionalFormatList::InsertRow(SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, SCROW nRowPos, SCSIZE nSize)
2230 : {
2231 55 : for(iterator it = begin(), itEnd = end(); it != itEnd; ++it)
2232 1 : it->InsertRow(nTab, nColStart, nColEnd, nRowPos, nSize);
2233 54 : }
2234 :
2235 37 : void ScConditionalFormatList::InsertCol(SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, SCCOL nColPos, SCSIZE nSize)
2236 : {
2237 39 : for(iterator it = begin(), itEnd = end(); it != itEnd; ++it)
2238 2 : it->InsertCol(nTab, nRowStart, nRowEnd, nColPos, nSize);
2239 37 : }
2240 :
2241 148 : void ScConditionalFormatList::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
2242 : {
2243 151 : for (iterator it = begin(); it != end(); ++it)
2244 3 : it->UpdateInsertTab(rCxt);
2245 148 : }
2246 :
2247 249 : void ScConditionalFormatList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
2248 : {
2249 253 : for (iterator it = begin(); it != end(); ++it)
2250 4 : it->UpdateDeleteTab(rCxt);
2251 249 : }
2252 :
2253 39 : void ScConditionalFormatList::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
2254 : {
2255 39 : for (iterator it = begin(); it != end(); ++it)
2256 0 : it->UpdateMoveTab(rCxt);
2257 39 : }
2258 :
2259 0 : void ScConditionalFormatList::RenameCellStyle( const OUString& rOld, const OUString& rNew )
2260 : {
2261 0 : for( iterator itr = begin(); itr != end(); ++itr)
2262 0 : itr->RenameCellStyle(rOld,rNew);
2263 0 : }
2264 :
2265 1460 : bool ScConditionalFormatList::CheckAllEntries()
2266 : {
2267 1460 : bool bValid = true;
2268 :
2269 : // need to check which must be deleted
2270 1460 : iterator itr = begin();
2271 3085 : while(itr != end())
2272 : {
2273 165 : if(itr->GetRange().empty())
2274 : {
2275 4 : bValid = false;
2276 4 : maConditionalFormats.erase(itr++);
2277 : }
2278 : else
2279 161 : ++itr;
2280 : }
2281 :
2282 1460 : return bValid;
2283 : }
2284 :
2285 1252 : void ScConditionalFormatList::DeleteArea( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
2286 : {
2287 1326 : for( iterator itr = begin(); itr != end(); ++itr)
2288 74 : itr->DeleteArea( nCol1, nRow1, nCol2, nRow2 );
2289 :
2290 1252 : CheckAllEntries();
2291 1252 : }
2292 :
2293 78979 : void ScConditionalFormatList::SourceChanged( const ScAddress& rAddr )
2294 : {
2295 79078 : for( iterator itr = begin(); itr != end(); ++itr)
2296 99 : itr->SourceChanged( rAddr );
2297 78979 : }
2298 :
2299 114413 : ScConditionalFormatList::iterator ScConditionalFormatList::begin()
2300 : {
2301 114413 : return maConditionalFormats.begin();
2302 : }
2303 :
2304 1660 : ScConditionalFormatList::const_iterator ScConditionalFormatList::begin() const
2305 : {
2306 1660 : return maConditionalFormats.begin();
2307 : }
2308 :
2309 117516 : ScConditionalFormatList::iterator ScConditionalFormatList::end()
2310 : {
2311 117516 : return maConditionalFormats.end();
2312 : }
2313 :
2314 4104 : ScConditionalFormatList::const_iterator ScConditionalFormatList::end() const
2315 : {
2316 4104 : return maConditionalFormats.end();
2317 : }
2318 :
2319 44 : size_t ScConditionalFormatList::size() const
2320 : {
2321 44 : return maConditionalFormats.size();
2322 : }
2323 :
2324 0 : void ScConditionalFormatList::erase( sal_uLong nIndex )
2325 : {
2326 0 : for( iterator itr = begin(); itr != end(); ++itr )
2327 : {
2328 0 : if( itr->GetKey() == nIndex )
2329 : {
2330 0 : maConditionalFormats.erase(itr);
2331 0 : break;
2332 : }
2333 : }
2334 0 : }
2335 :
2336 10483 : void ScConditionalFormatList::startRendering()
2337 : {
2338 10649 : for(iterator itr = begin(); itr != end(); ++itr)
2339 : {
2340 166 : itr->startRendering();
2341 : }
2342 10483 : }
2343 :
2344 10483 : void ScConditionalFormatList::endRendering()
2345 : {
2346 10649 : for(iterator itr = begin(); itr != end(); ++itr)
2347 : {
2348 166 : itr->endRendering();
2349 : }
2350 10639 : }
2351 :
2352 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|