1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26
27
28
29 // INCLUDE ---------------------------------------------------------------
30 #include <algorithm>
31 #include <deque>
32
33 #include <boost/bind.hpp>
34
35 #include <vcl/mapmod.hxx>
36 #include <editeng/editobj.hxx>
37 #include <editeng/editstat.hxx>
38
39 #include "cell.hxx"
40 #include "compiler.hxx"
41 #include "formula/errorcodes.hxx"
42 #include "document.hxx"
43 #include "rangenam.hxx"
44 #include "rechead.hxx"
45 #include "refupdat.hxx"
46 #include "scmatrix.hxx"
47 #include "editutil.hxx"
48 #include "chgtrack.hxx"
49 #include "externalrefmgr.hxx"
50
51 using namespace formula;
52
53 // STATIC DATA -----------------------------------------------------------
54
55 #ifdef USE_MEMPOOL
56 const sal_uInt16 nMemPoolEditCell = (0x1000 - 64) / sizeof(ScNoteCell);
IMPL_FIXEDMEMPOOL_NEWDEL(ScEditCell,nMemPoolEditCell,nMemPoolEditCell)57 IMPL_FIXEDMEMPOOL_NEWDEL( ScEditCell, nMemPoolEditCell, nMemPoolEditCell )
58 #endif
59
60 // ============================================================================
61
62 ScEditCell::ScEditCell( const EditTextObject* pObject, ScDocument* pDocP,
63 const SfxItemPool* pFromPool ) :
64 ScBaseCell( CELLTYPE_EDIT ),
65 pString( NULL ),
66 pDoc( pDocP )
67 {
68 SetTextObject( pObject, pFromPool );
69 }
70
ScEditCell(const ScEditCell & rCell,ScDocument & rDoc)71 ScEditCell::ScEditCell( const ScEditCell& rCell, ScDocument& rDoc ) :
72 ScBaseCell( rCell ),
73 pString( NULL ),
74 pDoc( &rDoc )
75 {
76 SetTextObject( rCell.pData, rCell.pDoc->GetEditPool() );
77 }
78
ScEditCell(const String & rString,ScDocument * pDocP)79 ScEditCell::ScEditCell( const String& rString, ScDocument* pDocP ) :
80 ScBaseCell( CELLTYPE_EDIT ),
81 pString( NULL ),
82 pDoc( pDocP )
83 {
84 DBG_ASSERT( rString.Search('\n') != STRING_NOTFOUND ||
85 rString.Search(CHAR_CR) != STRING_NOTFOUND,
86 "EditCell mit einfachem Text !?!?" );
87
88 EditEngine& rEngine = pDoc->GetEditEngine();
89 rEngine.SetText( rString );
90 pData = rEngine.CreateTextObject();
91 }
92
~ScEditCell()93 ScEditCell::~ScEditCell()
94 {
95 delete pData;
96 delete pString;
97
98 #ifdef DBG_UTIL
99 eCellType = CELLTYPE_DESTROYED;
100 #endif
101 }
102
SetData(const EditTextObject * pObject,const SfxItemPool * pFromPool)103 void ScEditCell::SetData( const EditTextObject* pObject,
104 const SfxItemPool* pFromPool )
105 {
106 if ( pString )
107 {
108 delete pString;
109 pString = NULL;
110 }
111 delete pData;
112 SetTextObject( pObject, pFromPool );
113 }
114
GetData(const EditTextObject * & rpObject) const115 void ScEditCell::GetData( const EditTextObject*& rpObject ) const
116 {
117 rpObject = pData;
118 }
119
GetString(String & rString) const120 void ScEditCell::GetString( String& rString ) const
121 {
122 if ( pString )
123 rString = *pString;
124 else if ( pData )
125 {
126 // auch Text von URL-Feldern, Doc-Engine ist eine ScFieldEditEngine
127 EditEngine& rEngine = pDoc->GetEditEngine();
128 rEngine.SetText( *pData );
129 rString = ScEditUtil::GetMultilineString(rEngine); // string with line separators between paragraphs
130 // cache short strings for formulas
131 if ( rString.Len() < 256 )
132 ((ScEditCell*)this)->pString = new String( rString ); //! non-const
133 }
134 else
135 rString.Erase();
136 }
137
SetTextObject(const EditTextObject * pObject,const SfxItemPool * pFromPool)138 void ScEditCell::SetTextObject( const EditTextObject* pObject,
139 const SfxItemPool* pFromPool )
140 {
141 if ( pObject )
142 {
143 if ( pFromPool && pDoc->GetEditPool() == pFromPool )
144 pData = pObject->Clone();
145 else
146 { //! anderer Pool
147 // Leider gibt es keinen anderen Weg, um den Pool umzuhaengen,
148 // als das Object durch eine entsprechende Engine zu schleusen..
149 EditEngine& rEngine = pDoc->GetEditEngine();
150 if ( pObject->HasOnlineSpellErrors() )
151 {
152 sal_uLong nControl = rEngine.GetControlWord();
153 const sal_uLong nSpellControl = EE_CNTRL_ONLINESPELLING | EE_CNTRL_ALLOWBIGOBJS;
154 sal_Bool bNewControl = ( (nControl & nSpellControl) != nSpellControl );
155 if ( bNewControl )
156 rEngine.SetControlWord( nControl | nSpellControl );
157 rEngine.SetText( *pObject );
158 pData = rEngine.CreateTextObject();
159 if ( bNewControl )
160 rEngine.SetControlWord( nControl );
161 }
162 else
163 {
164 rEngine.SetText( *pObject );
165 pData = rEngine.CreateTextObject();
166 }
167 }
168 }
169 else
170 pData = NULL;
171 }
172
173 // ============================================================================
174
175 namespace
176 {
177
178 using std::deque;
179
180 typedef SCCOLROW(*DimensionSelector)(const ScSingleRefData&);
181
182
lcl_GetCol(const ScSingleRefData & rData)183 static SCCOLROW lcl_GetCol(const ScSingleRefData& rData)
184 {
185 return rData.nCol;
186 }
187
188
lcl_GetRow(const ScSingleRefData & rData)189 static SCCOLROW lcl_GetRow(const ScSingleRefData& rData)
190 {
191 return rData.nRow;
192 }
193
194
lcl_GetTab(const ScSingleRefData & rData)195 static SCCOLROW lcl_GetTab(const ScSingleRefData& rData)
196 {
197 return rData.nTab;
198 }
199
200
201 /** Check if both references span the same range in selected dimension.
202 */
203 static bool
lcl_checkRangeDimension(const SingleDoubleRefProvider & rRef1,const SingleDoubleRefProvider & rRef2,const DimensionSelector aWhich)204 lcl_checkRangeDimension(
205 const SingleDoubleRefProvider& rRef1,
206 const SingleDoubleRefProvider& rRef2,
207 const DimensionSelector aWhich)
208 {
209 return
210 aWhich(rRef1.Ref1) == aWhich(rRef2.Ref1)
211 && aWhich(rRef1.Ref2) == aWhich(rRef2.Ref2);
212 }
213
214
215 static bool
lcl_checkRangeDimensions(const SingleDoubleRefProvider & rRef1,const SingleDoubleRefProvider & rRef2,bool & bCol,bool & bRow,bool & bTab)216 lcl_checkRangeDimensions(
217 const SingleDoubleRefProvider& rRef1,
218 const SingleDoubleRefProvider& rRef2,
219 bool& bCol, bool& bRow, bool& bTab)
220 {
221 const bool bSameCols(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetCol));
222 const bool bSameRows(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetRow));
223 const bool bSameTabs(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetTab));
224
225 // Test if exactly two dimensions are equal
226 if (!(bSameCols ^ bSameRows ^ bSameTabs)
227 && (bSameCols || bSameRows || bSameTabs))
228 {
229 bCol = !bSameCols;
230 bRow = !bSameRows;
231 bTab = !bSameTabs;
232 return true;
233 }
234 return false;
235 }
236
237
238 /** Check if references in given reference list can possibly
239 form a range. To do that, two of their dimensions must be the same.
240 */
241 static bool
lcl_checkRangeDimensions(const deque<ScToken * >::const_iterator aBegin,const deque<ScToken * >::const_iterator aEnd,bool & bCol,bool & bRow,bool & bTab)242 lcl_checkRangeDimensions(
243 const deque<ScToken*>::const_iterator aBegin,
244 const deque<ScToken*>::const_iterator aEnd,
245 bool& bCol, bool& bRow, bool& bTab)
246 {
247 deque<ScToken*>::const_iterator aCur(aBegin);
248 ++aCur;
249 const SingleDoubleRefProvider aRef(**aBegin);
250 bool bOk(false);
251 {
252 const SingleDoubleRefProvider aRefCur(**aCur);
253 bOk = lcl_checkRangeDimensions(aRef, aRefCur, bCol, bRow, bTab);
254 }
255 while (bOk && aCur != aEnd)
256 {
257 const SingleDoubleRefProvider aRefCur(**aCur);
258 bool bColTmp(false);
259 bool bRowTmp(false);
260 bool bTabTmp(false);
261 bOk = lcl_checkRangeDimensions(aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
262 bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
263 ++aCur;
264 }
265
266 if (bOk && aCur == aEnd)
267 {
268 bCol = bCol;
269 bRow = bRow;
270 bTab = bTab;
271 return true;
272 }
273 return false;
274 }
275
276
277 bool
lcl_lessReferenceBy(const ScToken * const pRef1,const ScToken * const pRef2,const DimensionSelector aWhich)278 lcl_lessReferenceBy(
279 const ScToken* const pRef1, const ScToken* const pRef2,
280 const DimensionSelector aWhich)
281 {
282 const SingleDoubleRefProvider rRef1(*pRef1);
283 const SingleDoubleRefProvider rRef2(*pRef2);
284 return aWhich(rRef1.Ref1) < aWhich(rRef2.Ref1);
285 }
286
287
288 /** Returns true if range denoted by token pRef2 starts immediately after
289 range denoted by token pRef1. Dimension, in which the comparison takes
290 place, is given by aWhich.
291 */
292 bool
lcl_isImmediatelyFollowing(const ScToken * const pRef1,const ScToken * const pRef2,const DimensionSelector aWhich)293 lcl_isImmediatelyFollowing(
294 const ScToken* const pRef1, const ScToken* const pRef2,
295 const DimensionSelector aWhich)
296 {
297 const SingleDoubleRefProvider rRef1(*pRef1);
298 const SingleDoubleRefProvider rRef2(*pRef2);
299 return aWhich(rRef2.Ref1) - aWhich(rRef1.Ref2) == 1;
300 }
301
302
303 static bool
lcl_checkIfAdjacent(const deque<ScToken * > & rReferences,const DimensionSelector aWhich)304 lcl_checkIfAdjacent(
305 const deque<ScToken*>& rReferences,
306 const DimensionSelector aWhich)
307 {
308 typedef deque<ScToken*>::const_iterator Iter;
309 Iter aBegin(rReferences.begin());
310 Iter aEnd(rReferences.end());
311 Iter aBegin1(aBegin);
312 ++aBegin1, --aEnd;
313 return std::equal(
314 aBegin, aEnd, aBegin1,
315 boost::bind(lcl_isImmediatelyFollowing, _1, _2, aWhich));
316 }
317
318
319 static void
lcl_fillRangeFromRefList(const deque<ScToken * > & rReferences,ScRange & rRange)320 lcl_fillRangeFromRefList(
321 const deque<ScToken*>& rReferences, ScRange& rRange)
322 {
323 const ScSingleRefData aStart(
324 SingleDoubleRefProvider(*rReferences.front()).Ref1);
325 rRange.aStart.Set(aStart.nCol, aStart.nRow, aStart.nTab);
326 const ScSingleRefData aEnd(
327 SingleDoubleRefProvider(*rReferences.back()).Ref2);
328 rRange.aEnd.Set(aEnd.nCol, aEnd.nRow, aEnd.nTab);
329 }
330
331
332 static bool
lcl_refListFormsOneRange(const ScAddress & aPos,deque<ScToken * > & rReferences,ScRange & rRange)333 lcl_refListFormsOneRange(
334 const ScAddress& aPos, deque<ScToken*>& rReferences,
335 ScRange& rRange)
336 {
337 std::for_each(
338 rReferences.begin(), rReferences.end(),
339 bind(&ScToken::CalcAbsIfRel, _1, aPos))
340 ;
341 if (rReferences.size() == 1) {
342 lcl_fillRangeFromRefList(rReferences, rRange);
343 return true;
344 }
345
346 bool bCell(false);
347 bool bRow(false);
348 bool bTab(false);
349 if (lcl_checkRangeDimensions(rReferences.begin(), rReferences.end(),
350 bCell, bRow, bTab))
351 {
352 DimensionSelector aWhich;
353 if (bCell)
354 {
355 aWhich = lcl_GetCol;
356 }
357 else if (bRow)
358 {
359 aWhich = lcl_GetRow;
360 }
361 else if (bTab)
362 {
363 aWhich = lcl_GetTab;
364 }
365 else
366 {
367 OSL_ENSURE(false, "lcl_checkRangeDimensions shouldn't allow that!");
368 aWhich = lcl_GetRow; // initialize to avoid warning
369 }
370 // Sort the references by start of range
371 std::sort(rReferences.begin(), rReferences.end(),
372 boost::bind(lcl_lessReferenceBy, _1, _2, aWhich));
373 if (lcl_checkIfAdjacent(rReferences, aWhich))
374 {
375 lcl_fillRangeFromRefList(rReferences, rRange);
376 return true;
377 }
378 }
379 return false;
380 }
381
382
lcl_isReference(const FormulaToken & rToken)383 bool lcl_isReference(const FormulaToken& rToken)
384 {
385 return
386 rToken.GetType() == svSingleRef ||
387 rToken.GetType() == svDoubleRef;
388 }
389
390 }
391
IsEmpty()392 sal_Bool ScFormulaCell::IsEmpty()
393 {
394 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
395 Interpret();
396 return aResult.GetCellResultType() == formula::svEmptyCell;
397 }
398
IsEmptyDisplayedAsString()399 sal_Bool ScFormulaCell::IsEmptyDisplayedAsString()
400 {
401 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
402 Interpret();
403 return aResult.IsEmptyDisplayedAsString();
404 }
405
IsValue()406 sal_Bool ScFormulaCell::IsValue()
407 {
408 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
409 Interpret();
410 return aResult.IsValue();
411 }
412
GetValue()413 double ScFormulaCell::GetValue()
414 {
415 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
416 Interpret();
417 if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
418 !aResult.GetResultError())
419 return aResult.GetDouble();
420 return 0.0;
421 }
422
GetValueAlways()423 double ScFormulaCell::GetValueAlways()
424 {
425 // for goal seek: return result value even if error code is set
426
427 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
428 Interpret();
429 return aResult.GetDouble();
430 }
431
GetString(String & rString)432 void ScFormulaCell::GetString( String& rString )
433 {
434 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
435 Interpret();
436 if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
437 !aResult.GetResultError())
438 rString = aResult.GetString();
439 else
440 rString.Erase();
441 }
442
GetMatrix()443 const ScMatrix* ScFormulaCell::GetMatrix()
444 {
445 if ( pDocument->GetAutoCalc() )
446 {
447 if( IsDirtyOrInTableOpDirty()
448 // Was stored !bDirty but an accompanying matrix cell was bDirty?
449 || (!bDirty && cMatrixFlag == MM_FORMULA && !aResult.GetMatrix().Is()))
450 Interpret();
451 }
452 return aResult.GetMatrix();
453 }
454
GetMatrixOrigin(ScAddress & rPos) const455 sal_Bool ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const
456 {
457 switch ( cMatrixFlag )
458 {
459 case MM_FORMULA :
460 rPos = aPos;
461 return sal_True;
462 // break;
463 case MM_REFERENCE :
464 {
465 pCode->Reset();
466 ScToken* t = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
467 if( t )
468 {
469 ScSingleRefData& rRef = t->GetSingleRef();
470 rRef.CalcAbsIfRel( aPos );
471 if ( rRef.Valid() )
472 {
473 rPos.Set( rRef.nCol, rRef.nRow, rRef.nTab );
474 return sal_True;
475 }
476 }
477 }
478 break;
479 }
480 return sal_False;
481 }
482
483
484 /*
485 Edge-Values:
486
487 8
488 4 16
489 2
490
491 innerhalb: 1
492 ausserhalb: 0
493 (reserviert: offen: 32)
494 */
495
GetMatrixEdge(ScAddress & rOrgPos)496 sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos )
497 {
498 switch ( cMatrixFlag )
499 {
500 case MM_FORMULA :
501 case MM_REFERENCE :
502 {
503 static SCCOL nC;
504 static SCROW nR;
505 ScAddress aOrg;
506 if ( !GetMatrixOrigin( aOrg ) )
507 return 0; // dumm gelaufen..
508 if ( aOrg != rOrgPos )
509 { // erstes Mal oder andere Matrix als letztes Mal
510 rOrgPos = aOrg;
511 ScFormulaCell* pFCell;
512 if ( cMatrixFlag == MM_REFERENCE )
513 pFCell = (ScFormulaCell*) pDocument->GetCell( aOrg );
514 else
515 pFCell = this; // this MM_FORMULA
516 // this gibt's nur einmal, kein Vergleich auf pFCell==this
517 if ( pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA
518 && pFCell->cMatrixFlag == MM_FORMULA )
519 {
520 pFCell->GetMatColsRows( nC, nR );
521 if ( nC == 0 || nR == 0 )
522 { // aus altem Dokument geladen, neu erzeugen
523 nC = 1;
524 nR = 1;
525 ScAddress aTmpOrg;
526 ScBaseCell* pCell;
527 ScAddress aAdr( aOrg );
528 aAdr.IncCol();
529 sal_Bool bCont = sal_True;
530 do
531 {
532 pCell = pDocument->GetCell( aAdr );
533 if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA
534 && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE
535 && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg )
536 {
537 nC++;
538 aAdr.IncCol();
539 }
540 else
541 bCont = sal_False;
542 } while ( bCont );
543 aAdr = aOrg;
544 aAdr.IncRow();
545 bCont = sal_True;
546 do
547 {
548 pCell = pDocument->GetCell( aAdr );
549 if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA
550 && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE
551 && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg )
552 {
553 nR++;
554 aAdr.IncRow();
555 }
556 else
557 bCont = sal_False;
558 } while ( bCont );
559 pFCell->SetMatColsRows( nC, nR );
560 }
561 }
562 else
563 {
564 #ifdef DBG_UTIL
565 String aTmp;
566 ByteString aMsg( "broken Matrix, no MatFormula at origin, Pos: " );
567 aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
568 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US );
569 aMsg += ", MatOrg: ";
570 aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
571 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US );
572 DBG_ERRORFILE( aMsg.GetBuffer() );
573 #endif
574 return 0; // bad luck ...
575 }
576 }
577 // here we are, healthy and clean, somewhere in between
578 SCsCOL dC = aPos.Col() - aOrg.Col();
579 SCsROW dR = aPos.Row() - aOrg.Row();
580 sal_uInt16 nEdges = 0;
581 if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
582 {
583 if ( dC == 0 )
584 nEdges |= 4; // linke Kante
585 if ( dC+1 == nC )
586 nEdges |= 16; // rechte Kante
587 if ( dR == 0 )
588 nEdges |= 8; // obere Kante
589 if ( dR+1 == nR )
590 nEdges |= 2; // untere Kante
591 if ( !nEdges )
592 nEdges = 1; // mittendrin
593 }
594 #ifdef DBG_UTIL
595 else
596 {
597 String aTmp;
598 ByteString aMsg( "broken Matrix, Pos: " );
599 aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
600 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US );
601 aMsg += ", MatOrg: ";
602 aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
603 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US );
604 aMsg += ", MatCols: ";
605 aMsg += ByteString::CreateFromInt32( nC );
606 aMsg += ", MatRows: ";
607 aMsg += ByteString::CreateFromInt32( nR );
608 aMsg += ", DiffCols: ";
609 aMsg += ByteString::CreateFromInt32( dC );
610 aMsg += ", DiffRows: ";
611 aMsg += ByteString::CreateFromInt32( dR );
612 DBG_ERRORFILE( aMsg.GetBuffer() );
613 }
614 #endif
615 return nEdges;
616 // break;
617 }
618 default:
619 return 0;
620 }
621 }
622
GetErrCode()623 sal_uInt16 ScFormulaCell::GetErrCode()
624 {
625 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
626 Interpret();
627 /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
628 * and not also abused for signaling other error conditions we could bail
629 * out even before attempting to interpret broken code. */
630 sal_uInt16 nErr = pCode->GetCodeError();
631 if (nErr)
632 return nErr;
633 return aResult.GetResultError();
634 }
635
GetRawError()636 sal_uInt16 ScFormulaCell::GetRawError()
637 {
638 sal_uInt16 nErr = pCode->GetCodeError();
639 if (nErr)
640 return nErr;
641 return aResult.GetResultError();
642 }
643
HasOneReference(ScRange & r) const644 sal_Bool ScFormulaCell::HasOneReference( ScRange& r ) const
645 {
646 pCode->Reset();
647 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
648 if( p && !pCode->GetNextReferenceRPN() ) // nur eine!
649 {
650 p->CalcAbsIfRel( aPos );
651 SingleDoubleRefProvider aProv( *p );
652 r.aStart.Set( aProv.Ref1.nCol,
653 aProv.Ref1.nRow,
654 aProv.Ref1.nTab );
655 r.aEnd.Set( aProv.Ref2.nCol,
656 aProv.Ref2.nRow,
657 aProv.Ref2.nTab );
658 return sal_True;
659 }
660 else
661 return sal_False;
662 }
663
664 bool
HasRefListExpressibleAsOneReference(ScRange & rRange) const665 ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const
666 {
667 /* If there appears just one reference in the formula, it's the same
668 as HasOneReference(). If there are more of them, they can denote
669 one range if they are (sole) arguments of one function.
670 Union of these references must form one range and their
671 intersection must be empty set.
672 */
673
674 // Detect the simple case of exactly one reference in advance without all
675 // overhead.
676 // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
677 // work again, where the function does not have only references.
678 if (HasOneReference( rRange))
679 return true;
680
681 pCode->Reset();
682 // Get first reference, if any
683 ScToken* const pFirstReference(
684 dynamic_cast<ScToken*>(pCode->GetNextReferenceRPN()));
685 if (pFirstReference)
686 {
687 // Collect all consecutive references, starting by the one
688 // already found
689 std::deque<ScToken*> aReferences;
690 aReferences.push_back(pFirstReference);
691 FormulaToken* pToken(pCode->NextRPN());
692 FormulaToken* pFunction(0);
693 while (pToken)
694 {
695 if (lcl_isReference(*pToken))
696 {
697 aReferences.push_back(dynamic_cast<ScToken*>(pToken));
698 pToken = pCode->NextRPN();
699 }
700 else
701 {
702 if (pToken->IsFunction())
703 {
704 pFunction = pToken;
705 }
706 break;
707 }
708 }
709 if (pFunction && !pCode->GetNextReferenceRPN()
710 && (pFunction->GetParamCount() == aReferences.size()))
711 {
712 return lcl_refListFormsOneRange(aPos, aReferences, rRange);
713 }
714 }
715 return false;
716 }
717
HasRelNameReference() const718 sal_Bool ScFormulaCell::HasRelNameReference() const
719 {
720 pCode->Reset();
721 ScToken* t;
722 while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL )
723 {
724 if ( t->GetSingleRef().IsRelName() ||
725 (t->GetType() == formula::svDoubleRef &&
726 t->GetDoubleRef().Ref2.IsRelName()) )
727 return sal_True;
728 }
729 return sal_False;
730 }
731
HasColRowName() const732 sal_Bool ScFormulaCell::HasColRowName() const
733 {
734 pCode->Reset();
735 return (pCode->GetNextColRowName() != NULL);
736 }
737
UpdateReference(UpdateRefMode eUpdateRefMode,const ScRange & r,SCsCOL nDx,SCsROW nDy,SCsTAB nDz,ScDocument * pUndoDoc,const ScAddress * pUndoCellPos)738 void ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode,
739 const ScRange& r,
740 SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
741 ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
742 {
743 SCCOL nCol1 = r.aStart.Col();
744 SCROW nRow1 = r.aStart.Row();
745 SCTAB nTab1 = r.aStart.Tab();
746 SCCOL nCol2 = r.aEnd.Col();
747 SCROW nRow2 = r.aEnd.Row();
748 SCTAB nTab2 = r.aEnd.Tab();
749 SCCOL nCol = aPos.Col();
750 SCROW nRow = aPos.Row();
751 SCTAB nTab = aPos.Tab();
752 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
753 if ( pUndoCellPos )
754 aUndoPos = *pUndoCellPos;
755 ScAddress aOldPos( aPos );
756 // sal_Bool bPosChanged = sal_False; // ob diese Zelle bewegt wurde
757 sal_Bool bIsInsert = sal_False;
758 if (eUpdateRefMode == URM_INSDEL)
759 {
760 bIsInsert = (nDx >= 0 && nDy >= 0 && nDz >= 0);
761 if ( nDx && nRow >= nRow1 && nRow <= nRow2 &&
762 nTab >= nTab1 && nTab <= nTab2 )
763 {
764 if (nCol >= nCol1)
765 {
766 nCol = sal::static_int_cast<SCCOL>( nCol + nDx );
767 if ((SCsCOL) nCol < 0)
768 nCol = 0;
769 else if ( nCol > MAXCOL )
770 nCol = MAXCOL;
771 aPos.SetCol( nCol );
772 // bPosChanged = sal_True;
773 }
774 }
775 if ( nDy && nCol >= nCol1 && nCol <= nCol2 &&
776 nTab >= nTab1 && nTab <= nTab2 )
777 {
778 if (nRow >= nRow1)
779 {
780 nRow = sal::static_int_cast<SCROW>( nRow + nDy );
781 if ((SCsROW) nRow < 0)
782 nRow = 0;
783 else if ( nRow > MAXROW )
784 nRow = MAXROW;
785 aPos.SetRow( nRow );
786 // bPosChanged = sal_True;
787 }
788 }
789 if ( nDz && nCol >= nCol1 && nCol <= nCol2 &&
790 nRow >= nRow1 && nRow <= nRow2 )
791 {
792 if (nTab >= nTab1)
793 {
794 SCTAB nMaxTab = pDocument->GetTableCount() - 1;
795 nTab = sal::static_int_cast<SCTAB>( nTab + nDz );
796 if ((SCsTAB) nTab < 0)
797 nTab = 0;
798 else if ( nTab > nMaxTab )
799 nTab = nMaxTab;
800 aPos.SetTab( nTab );
801 // bPosChanged = sal_True;
802 }
803 }
804 }
805 else if ( r.In( aPos ) )
806 {
807 aOldPos.Set( nCol - nDx, nRow - nDy, nTab - nDz );
808 // bPosChanged = sal_True;
809 }
810
811 sal_Bool bHasRefs = sal_False;
812 sal_Bool bHasColRowNames = sal_False;
813 sal_Bool bOnRefMove = sal_False;
814 if ( !pDocument->IsClipOrUndo() )
815 {
816 pCode->Reset();
817 bHasRefs = (pCode->GetNextReferenceRPN() != NULL);
818 if ( !bHasRefs || eUpdateRefMode == URM_COPY )
819 {
820 pCode->Reset();
821 bHasColRowNames = (pCode->GetNextColRowName() != NULL);
822 bHasRefs = bHasRefs || bHasColRowNames;
823 }
824 bOnRefMove = pCode->IsRecalcModeOnRefMove();
825 }
826 if( bHasRefs || bOnRefMove )
827 {
828 ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
829 sal_Bool bValChanged;
830 ScRangeData* pRangeData;
831 sal_Bool bRangeModified; // any range, not only shared formula
832 sal_Bool bRefSizeChanged;
833 if ( bHasRefs )
834 {
835 ScCompiler aComp(pDocument, aPos, *pCode);
836 aComp.SetGrammar(pDocument->GetGrammar());
837 pRangeData = aComp.UpdateReference(eUpdateRefMode, aOldPos, r,
838 nDx, nDy, nDz,
839 bValChanged, bRefSizeChanged);
840 bRangeModified = aComp.HasModifiedRange();
841 }
842 else
843 {
844 bValChanged = sal_False;
845 pRangeData = NULL;
846 bRangeModified = sal_False;
847 bRefSizeChanged = sal_False;
848 }
849 if ( bOnRefMove )
850 bOnRefMove = (bValChanged || (aPos != aOldPos));
851 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
852
853 sal_Bool bColRowNameCompile, bHasRelName, bNewListening, bInDeleteUndo;
854 if ( bHasRefs )
855 {
856 // Upon Insert ColRowNames have to be recompiled in case the
857 // insertion occurs right in front of the range.
858 bColRowNameCompile =
859 (eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0));
860 if ( bColRowNameCompile )
861 {
862 bColRowNameCompile = sal_False;
863 ScToken* t;
864 ScRangePairList* pColList = pDocument->GetColNameRanges();
865 ScRangePairList* pRowList = pDocument->GetRowNameRanges();
866 pCode->Reset();
867 while ( !bColRowNameCompile && (t = static_cast<ScToken*>(pCode->GetNextColRowName())) != NULL )
868 {
869 ScSingleRefData& rRef = t->GetSingleRef();
870 if ( nDy > 0 && rRef.IsColRel() )
871 { // ColName
872 rRef.CalcAbsIfRel( aPos );
873 ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
874 ScRangePair* pR = pColList->Find( aAdr );
875 if ( pR )
876 { // definiert
877 if ( pR->GetRange(1).aStart.Row() == nRow1 )
878 bColRowNameCompile = sal_True;
879 }
880 else
881 { // on the fly
882 if ( rRef.nRow + 1 == nRow1 )
883 bColRowNameCompile = sal_True;
884 }
885 }
886 if ( nDx > 0 && rRef.IsRowRel() )
887 { // RowName
888 rRef.CalcAbsIfRel( aPos );
889 ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
890 ScRangePair* pR = pRowList->Find( aAdr );
891 if ( pR )
892 { // definiert
893 if ( pR->GetRange(1).aStart.Col() == nCol1 )
894 bColRowNameCompile = sal_True;
895 }
896 else
897 { // on the fly
898 if ( rRef.nCol + 1 == nCol1 )
899 bColRowNameCompile = sal_True;
900 }
901 }
902 }
903 }
904 else if ( eUpdateRefMode == URM_MOVE )
905 { // bei Move/D&D neu kompilieren wenn ColRowName verschoben wurde
906 // oder diese Zelle auf einen zeigt und verschoben wurde
907 bColRowNameCompile = bCompile; // evtl. aus Copy-ctor
908 if ( !bColRowNameCompile )
909 {
910 sal_Bool bMoved = (aPos != aOldPos);
911 pCode->Reset();
912 ScToken* t = static_cast<ScToken*>(pCode->GetNextColRowName());
913 if ( t && bMoved )
914 bColRowNameCompile = sal_True;
915 while ( t && !bColRowNameCompile )
916 {
917 ScSingleRefData& rRef = t->GetSingleRef();
918 rRef.CalcAbsIfRel( aPos );
919 if ( rRef.Valid() )
920 {
921 ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
922 if ( r.In( aAdr ) )
923 bColRowNameCompile = sal_True;
924 }
925 t = static_cast<ScToken*>(pCode->GetNextColRowName());
926 }
927 }
928 }
929 else if ( eUpdateRefMode == URM_COPY && bHasColRowNames && bValChanged )
930 {
931 bColRowNameCompile = sal_True;
932 }
933 ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
934 if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
935 bInDeleteUndo = sal_True;
936 else
937 bInDeleteUndo = sal_False;
938 // RelNameRefs are always moved
939 bHasRelName = HasRelNameReference();
940 // Reference changed and new listening needed?
941 // Except in Insert/Delete without specialties.
942 bNewListening = (bRangeModified || pRangeData || bColRowNameCompile
943 || (bValChanged && (eUpdateRefMode != URM_INSDEL ||
944 bInDeleteUndo || bRefSizeChanged)) ||
945 (bHasRelName && eUpdateRefMode != URM_COPY))
946 // #i36299# Don't duplicate action during cut&paste / drag&drop
947 // on a cell in the range moved, start/end listeners is done
948 // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
949 && !(eUpdateRefMode == URM_MOVE &&
950 pDocument->IsInsertingFromOtherDoc() && r.In(aPos));
951 if ( bNewListening )
952 EndListeningTo( pDocument, pOld, aOldPos );
953 }
954 else
955 {
956 bColRowNameCompile = bHasRelName = bNewListening = bInDeleteUndo =
957 sal_False;
958 }
959
960 sal_Bool bNeedDirty;
961 // NeedDirty bei Aenderungen ausser Copy und Move/Insert ohne RelNames
962 if ( bRangeModified || pRangeData || bColRowNameCompile ||
963 (bValChanged && eUpdateRefMode != URM_COPY &&
964 (eUpdateRefMode != URM_MOVE || bHasRelName) &&
965 (!bIsInsert || bHasRelName || bInDeleteUndo ||
966 bRefSizeChanged)) || bOnRefMove)
967 bNeedDirty = sal_True;
968 else
969 bNeedDirty = sal_False;
970 if (pUndoDoc && (bValChanged || pRangeData || bOnRefMove))
971 {
972 // Copy the cell to aUndoPos, which is its current position in the document,
973 // so this works when UpdateReference is called before moving the cells
974 // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
975 // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
976
977 // If there is already a formula cell in the undo document, don't overwrite it,
978 // the first (oldest) is the important cell.
979 if ( pUndoDoc->GetCellType( aUndoPos ) != CELLTYPE_FORMULA )
980 {
981 ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aUndoPos,
982 pOld, eTempGrammar, cMatrixFlag );
983 pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!)
984 pUndoDoc->PutCell( aUndoPos, pFCell );
985 }
986 }
987 // #i116833# If the formula is changed, always invalidate the stream (even if the result is the same).
988 // If the formula is moved, the change is recognized separately.
989 if (bValChanged && pDocument->IsStreamValid(aPos.Tab()))
990 pDocument->SetStreamValid(aPos.Tab(), sal_False);
991 bValChanged = sal_False;
992 if ( pRangeData )
993 { // Replace shared formula with own formula
994 pDocument->RemoveFromFormulaTree( this ); // update formula count
995 delete pCode;
996 pCode = pRangeData->GetCode()->Clone();
997 // #i18937# #i110008# call MoveRelWrap, but with the old position
998 ScCompiler::MoveRelWrap(*pCode, pDocument, aOldPos, pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
999 ScCompiler aComp2(pDocument, aPos, *pCode);
1000 aComp2.SetGrammar(pDocument->GetGrammar());
1001 aComp2.UpdateSharedFormulaReference( eUpdateRefMode, aOldPos, r,
1002 nDx, nDy, nDz );
1003 bValChanged = sal_True;
1004 bNeedDirty = sal_True;
1005 }
1006 if ( ( bCompile = (bCompile || bValChanged || bRangeModified || bColRowNameCompile) ) != 0 )
1007 {
1008 CompileTokenArray( bNewListening ); // kein Listening
1009 bNeedDirty = sal_True;
1010 }
1011 if ( !bInDeleteUndo )
1012 { // In ChangeTrack Delete-Reject listeners are established in
1013 // InsertCol/InsertRow
1014 if ( bNewListening )
1015 {
1016 if ( eUpdateRefMode == URM_INSDEL )
1017 {
1018 // Inserts/Deletes re-establish listeners after all
1019 // UpdateReference calls.
1020 // All replaced shared formula listeners have to be
1021 // established after an Insert or Delete. Do nothing here.
1022 SetNeedsListening( sal_True);
1023 }
1024 else
1025 StartListeningTo( pDocument );
1026 }
1027 }
1028 if ( bNeedDirty && (!(eUpdateRefMode == URM_INSDEL && bHasRelName) || pRangeData) )
1029 { // Referenzen abgeschnitten, ungueltig o.ae.?
1030 sal_Bool bOldAutoCalc = pDocument->GetAutoCalc();
1031 // kein Interpret in SubMinimalRecalc wegen evtl. falscher Referenzen
1032 pDocument->SetAutoCalc( sal_False );
1033 SetDirty();
1034 pDocument->SetAutoCalc( bOldAutoCalc );
1035 }
1036
1037 delete pOld;
1038 }
1039 }
1040
UpdateInsertTab(SCTAB nTable)1041 void ScFormulaCell::UpdateInsertTab(SCTAB nTable)
1042 {
1043 sal_Bool bPosChanged = ( aPos.Tab() >= nTable ? sal_True : sal_False );
1044 pCode->Reset();
1045 if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
1046 {
1047 EndListeningTo( pDocument );
1048 // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateInsertTab !
1049 if ( bPosChanged )
1050 aPos.IncTab();
1051 ScRangeData* pRangeData;
1052 ScCompiler aComp(pDocument, aPos, *pCode);
1053 aComp.SetGrammar(pDocument->GetGrammar());
1054 pRangeData = aComp.UpdateInsertTab( nTable, sal_False );
1055 if (pRangeData) // Shared Formula gegen echte Formel
1056 { // austauschen
1057 sal_Bool bRefChanged;
1058 pDocument->RemoveFromFormulaTree( this ); // update formula count
1059 delete pCode;
1060 pCode = new ScTokenArray( *pRangeData->GetCode() );
1061 ScCompiler aComp2(pDocument, aPos, *pCode);
1062 aComp2.SetGrammar(pDocument->GetGrammar());
1063 aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
1064 aComp2.UpdateInsertTab( nTable, sal_False );
1065 // If the shared formula contained a named range/formula containing
1066 // an absolute reference to a sheet, those have to be readjusted.
1067 aComp2.UpdateDeleteTab( nTable, sal_False, sal_True, bRefChanged );
1068 bCompile = sal_True;
1069 }
1070 // kein StartListeningTo weil pTab[nTab] noch nicht existiert!
1071 }
1072 else if ( bPosChanged )
1073 aPos.IncTab();
1074 }
1075
UpdateDeleteTab(SCTAB nTable,sal_Bool bIsMove)1076 sal_Bool ScFormulaCell::UpdateDeleteTab(SCTAB nTable, sal_Bool bIsMove)
1077 {
1078 sal_Bool bRefChanged = sal_False;
1079 sal_Bool bPosChanged = ( aPos.Tab() > nTable ? sal_True : sal_False );
1080 pCode->Reset();
1081 if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
1082 {
1083 EndListeningTo( pDocument );
1084 // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateDeleteTab !
1085 if ( bPosChanged )
1086 aPos.IncTab(-1);
1087 ScRangeData* pRangeData;
1088 ScCompiler aComp(pDocument, aPos, *pCode);
1089 aComp.SetGrammar(pDocument->GetGrammar());
1090 pRangeData = aComp.UpdateDeleteTab(nTable, bIsMove, sal_False, bRefChanged);
1091 if (pRangeData) // Shared Formula gegen echte Formel
1092 { // austauschen
1093 pDocument->RemoveFromFormulaTree( this ); // update formula count
1094 delete pCode;
1095 pCode = pRangeData->GetCode()->Clone();
1096 ScCompiler aComp2(pDocument, aPos, *pCode);
1097 aComp2.SetGrammar(pDocument->GetGrammar());
1098 aComp2.CompileTokenArray();
1099 aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
1100 aComp2.UpdateDeleteTab( nTable, sal_False, sal_False, bRefChanged );
1101 // If the shared formula contained a named range/formula containing
1102 // an absolute reference to a sheet, those have to be readjusted.
1103 aComp2.UpdateInsertTab( nTable,sal_True );
1104 // bRefChanged kann beim letzten UpdateDeleteTab zurueckgesetzt worden sein
1105 bRefChanged = sal_True;
1106 bCompile = sal_True;
1107 }
1108 // kein StartListeningTo weil pTab[nTab] noch nicht korrekt!
1109 }
1110 else if ( bPosChanged )
1111 aPos.IncTab(-1);
1112
1113 return bRefChanged;
1114 }
1115
UpdateMoveTab(SCTAB nOldPos,SCTAB nNewPos,SCTAB nTabNo)1116 void ScFormulaCell::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo )
1117 {
1118 pCode->Reset();
1119 if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
1120 {
1121 EndListeningTo( pDocument );
1122 // SetTab _nach_ EndListeningTo und _vor_ Compiler UpdateMoveTab !
1123 aPos.SetTab( nTabNo );
1124 ScRangeData* pRangeData;
1125 ScCompiler aComp(pDocument, aPos, *pCode);
1126 aComp.SetGrammar(pDocument->GetGrammar());
1127 pRangeData = aComp.UpdateMoveTab( nOldPos, nNewPos, sal_False );
1128 if (pRangeData) // Shared Formula gegen echte Formel
1129 { // austauschen
1130 pDocument->RemoveFromFormulaTree( this ); // update formula count
1131 delete pCode;
1132 pCode = pRangeData->GetCode()->Clone();
1133 ScCompiler aComp2(pDocument, aPos, *pCode);
1134 aComp2.SetGrammar(pDocument->GetGrammar());
1135 aComp2.CompileTokenArray();
1136 aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
1137 aComp2.UpdateMoveTab( nOldPos, nNewPos, sal_True );
1138 bCompile = sal_True;
1139 }
1140 // kein StartListeningTo weil pTab[nTab] noch nicht korrekt!
1141 }
1142 else
1143 aPos.SetTab( nTabNo );
1144 }
1145
UpdateInsertTabAbs(SCTAB nTable)1146 void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable)
1147 {
1148 if( !pDocument->IsClipOrUndo() )
1149 {
1150 pCode->Reset();
1151 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1152 while( p )
1153 {
1154 ScSingleRefData& rRef1 = p->GetSingleRef();
1155 if( !rRef1.IsTabRel() && (SCsTAB) nTable <= rRef1.nTab )
1156 rRef1.nTab++;
1157 if( p->GetType() == formula::svDoubleRef )
1158 {
1159 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
1160 if( !rRef2.IsTabRel() && (SCsTAB) nTable <= rRef2.nTab )
1161 rRef2.nTab++;
1162 }
1163 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1164 }
1165 }
1166 }
1167
TestTabRefAbs(SCTAB nTable)1168 sal_Bool ScFormulaCell::TestTabRefAbs(SCTAB nTable)
1169 {
1170 sal_Bool bRet = sal_False;
1171 if( !pDocument->IsClipOrUndo() )
1172 {
1173 pCode->Reset();
1174 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1175 while( p )
1176 {
1177 ScSingleRefData& rRef1 = p->GetSingleRef();
1178 if( !rRef1.IsTabRel() )
1179 {
1180 if( (SCsTAB) nTable != rRef1.nTab )
1181 bRet = sal_True;
1182 else if (nTable != aPos.Tab())
1183 rRef1.nTab = aPos.Tab();
1184 }
1185 if( p->GetType() == formula::svDoubleRef )
1186 {
1187 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
1188 if( !rRef2.IsTabRel() )
1189 {
1190 if( (SCsTAB) nTable != rRef2.nTab )
1191 bRet = sal_True;
1192 else if (nTable != aPos.Tab())
1193 rRef2.nTab = aPos.Tab();
1194 }
1195 }
1196 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1197 }
1198 }
1199 return bRet;
1200 }
1201
UpdateCompile(sal_Bool bForceIfNameInUse)1202 void ScFormulaCell::UpdateCompile( sal_Bool bForceIfNameInUse )
1203 {
1204 if ( bForceIfNameInUse && !bCompile )
1205 bCompile = pCode->HasNameOrColRowName();
1206 if ( bCompile )
1207 pCode->SetCodeError( 0 ); // make sure it will really be compiled
1208 CompileTokenArray();
1209 }
1210
1211 // Referenzen transponieren - wird nur in Clipboard-Dokumenten aufgerufen
1212
TransposeReference()1213 void ScFormulaCell::TransposeReference()
1214 {
1215 sal_Bool bFound = sal_False;
1216 pCode->Reset();
1217 ScToken* t;
1218 while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
1219 {
1220 ScSingleRefData& rRef1 = t->GetSingleRef();
1221 if ( rRef1.IsColRel() && rRef1.IsRowRel() )
1222 {
1223 sal_Bool bDouble = (t->GetType() == formula::svDoubleRef);
1224 ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef().Ref2 : rRef1);
1225 if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
1226 {
1227 sal_Int16 nTemp;
1228
1229 nTemp = rRef1.nRelCol;
1230 rRef1.nRelCol = static_cast<SCCOL>(rRef1.nRelRow);
1231 rRef1.nRelRow = static_cast<SCROW>(nTemp);
1232
1233 if ( bDouble )
1234 {
1235 nTemp = rRef2.nRelCol;
1236 rRef2.nRelCol = static_cast<SCCOL>(rRef2.nRelRow);
1237 rRef2.nRelRow = static_cast<SCROW>(nTemp);
1238 }
1239
1240 bFound = sal_True;
1241 }
1242 }
1243 }
1244
1245 if (bFound)
1246 bCompile = sal_True;
1247 }
1248
UpdateTranspose(const ScRange & rSource,const ScAddress & rDest,ScDocument * pUndoDoc)1249 void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
1250 ScDocument* pUndoDoc )
1251 {
1252 EndListeningTo( pDocument );
1253
1254 ScAddress aOldPos = aPos;
1255 sal_Bool bPosChanged = sal_False; // ob diese Zelle bewegt wurde
1256
1257 ScRange aDestRange( rDest, ScAddress(
1258 static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
1259 static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
1260 rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
1261 if ( aDestRange.In( aOldPos ) )
1262 {
1263 // Position zurueckrechnen
1264 SCsCOL nRelPosX = aOldPos.Col();
1265 SCsROW nRelPosY = aOldPos.Row();
1266 SCsTAB nRelPosZ = aOldPos.Tab();
1267 ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart );
1268 aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
1269 bPosChanged = sal_True;
1270 }
1271
1272 ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
1273 sal_Bool bRefChanged = sal_False;
1274 ScToken* t;
1275
1276 ScRangeData* pShared = NULL;
1277 pCode->Reset();
1278 while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
1279 {
1280 if( t->GetOpCode() == ocName )
1281 {
1282 ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() );
1283 if (pName)
1284 {
1285 if (pName->IsModified())
1286 bRefChanged = sal_True;
1287 if (pName->HasType(RT_SHAREDMOD))
1288 pShared = pName;
1289 }
1290 }
1291 else if( t->GetType() != svIndex )
1292 {
1293 t->CalcAbsIfRel( aOldPos );
1294 sal_Bool bMod;
1295 { // own scope for SingleDoubleRefModifier dtor if SingleRef
1296 SingleDoubleRefModifier aMod( *t );
1297 ScComplexRefData& rRef = aMod.Ref();
1298 bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource,
1299 rDest, rRef ) != UR_NOTHING || bPosChanged);
1300 }
1301 if ( bMod )
1302 {
1303 t->CalcRelFromAbs( aPos );
1304 bRefChanged = sal_True;
1305 }
1306 }
1307 }
1308
1309 if (pShared) // Shared Formula gegen echte Formel austauschen
1310 {
1311 pDocument->RemoveFromFormulaTree( this ); // update formula count
1312 delete pCode;
1313 pCode = new ScTokenArray( *pShared->GetCode() );
1314 bRefChanged = sal_True;
1315 pCode->Reset();
1316 while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
1317 {
1318 if( t->GetType() != svIndex )
1319 {
1320 t->CalcAbsIfRel( aOldPos );
1321 sal_Bool bMod;
1322 { // own scope for SingleDoubleRefModifier dtor if SingleRef
1323 SingleDoubleRefModifier aMod( *t );
1324 ScComplexRefData& rRef = aMod.Ref();
1325 bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource,
1326 rDest, rRef ) != UR_NOTHING || bPosChanged);
1327 }
1328 if ( bMod )
1329 t->CalcRelFromAbs( aPos );
1330 }
1331 }
1332 }
1333
1334 if (bRefChanged)
1335 {
1336 if (pUndoDoc)
1337 {
1338 ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aPos, pOld,
1339 eTempGrammar, cMatrixFlag);
1340 pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!)
1341 pUndoDoc->PutCell( aPos.Col(), aPos.Row(), aPos.Tab(), pFCell );
1342 }
1343
1344 bCompile = sal_True;
1345 CompileTokenArray(); // ruft auch StartListeningTo
1346 SetDirty();
1347 }
1348 else
1349 StartListeningTo( pDocument ); // Listener wie vorher
1350
1351 delete pOld;
1352 }
1353
UpdateGrow(const ScRange & rArea,SCCOL nGrowX,SCROW nGrowY)1354 void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
1355 {
1356 EndListeningTo( pDocument );
1357
1358 sal_Bool bRefChanged = sal_False;
1359 ScToken* t;
1360 ScRangeData* pShared = NULL;
1361
1362 pCode->Reset();
1363 while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
1364 {
1365 if( t->GetOpCode() == ocName )
1366 {
1367 ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() );
1368 if (pName)
1369 {
1370 if (pName->IsModified())
1371 bRefChanged = sal_True;
1372 if (pName->HasType(RT_SHAREDMOD))
1373 pShared = pName;
1374 }
1375 }
1376 else if( t->GetType() != svIndex )
1377 {
1378 t->CalcAbsIfRel( aPos );
1379 sal_Bool bMod;
1380 { // own scope for SingleDoubleRefModifier dtor if SingleRef
1381 SingleDoubleRefModifier aMod( *t );
1382 ScComplexRefData& rRef = aMod.Ref();
1383 bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY,
1384 rRef ) != UR_NOTHING);
1385 }
1386 if ( bMod )
1387 {
1388 t->CalcRelFromAbs( aPos );
1389 bRefChanged = sal_True;
1390 }
1391 }
1392 }
1393
1394 if (pShared) // Shared Formula gegen echte Formel austauschen
1395 {
1396 pDocument->RemoveFromFormulaTree( this ); // update formula count
1397 delete pCode;
1398 pCode = new ScTokenArray( *pShared->GetCode() );
1399 bRefChanged = sal_True;
1400 pCode->Reset();
1401 while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
1402 {
1403 if( t->GetType() != svIndex )
1404 {
1405 t->CalcAbsIfRel( aPos );
1406 sal_Bool bMod;
1407 { // own scope for SingleDoubleRefModifier dtor if SingleRef
1408 SingleDoubleRefModifier aMod( *t );
1409 ScComplexRefData& rRef = aMod.Ref();
1410 bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY,
1411 rRef ) != UR_NOTHING);
1412 }
1413 if ( bMod )
1414 t->CalcRelFromAbs( aPos );
1415 }
1416 }
1417 }
1418
1419 if (bRefChanged)
1420 {
1421 bCompile = sal_True;
1422 CompileTokenArray(); // ruft auch StartListeningTo
1423 SetDirty();
1424 }
1425 else
1426 StartListeningTo( pDocument ); // Listener wie vorher
1427 }
1428
lcl_IsRangeNameInUse(sal_uInt16 nIndex,ScTokenArray * pCode,ScRangeName * pNames)1429 sal_Bool lcl_IsRangeNameInUse(sal_uInt16 nIndex, ScTokenArray* pCode, ScRangeName* pNames)
1430 {
1431 for (FormulaToken* p = pCode->First(); p; p = pCode->Next())
1432 {
1433 if (p->GetOpCode() == ocName)
1434 {
1435 if (p->GetIndex() == nIndex)
1436 return sal_True;
1437 else
1438 {
1439 // RangeData kann Null sein in bestimmten Excel-Dateien (#31168#)
1440 ScRangeData* pSubName = pNames->FindIndex(p->GetIndex());
1441 if (pSubName && lcl_IsRangeNameInUse(nIndex,
1442 pSubName->GetCode(), pNames))
1443 return sal_True;
1444 }
1445 }
1446 }
1447 return sal_False;
1448 }
1449
IsRangeNameInUse(sal_uInt16 nIndex) const1450 sal_Bool ScFormulaCell::IsRangeNameInUse(sal_uInt16 nIndex) const
1451 {
1452 return lcl_IsRangeNameInUse( nIndex, pCode, pDocument->GetRangeName() );
1453 }
1454
lcl_FindRangeNamesInUse(std::set<sal_uInt16> & rIndexes,ScTokenArray * pCode,ScRangeName * pNames)1455 void lcl_FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes, ScTokenArray* pCode, ScRangeName* pNames)
1456 {
1457 for (FormulaToken* p = pCode->First(); p; p = pCode->Next())
1458 {
1459 if (p->GetOpCode() == ocName)
1460 {
1461 sal_uInt16 nTokenIndex = p->GetIndex();
1462 rIndexes.insert( nTokenIndex );
1463
1464 ScRangeData* pSubName = pNames->FindIndex(p->GetIndex());
1465 if (pSubName)
1466 lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames);
1467 }
1468 }
1469 }
1470
FindRangeNamesInUse(std::set<sal_uInt16> & rIndexes) const1471 void ScFormulaCell::FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes) const
1472 {
1473 lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() );
1474 }
1475
ReplaceRangeNamesInUse(const ScRangeData::IndexMap & rMap)1476 void ScFormulaCell::ReplaceRangeNamesInUse( const ScRangeData::IndexMap& rMap )
1477 {
1478 for( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
1479 {
1480 if( p->GetOpCode() == ocName )
1481 {
1482 sal_uInt16 nIndex = p->GetIndex();
1483 ScRangeData::IndexMap::const_iterator itr = rMap.find(nIndex);
1484 sal_uInt16 nNewIndex = itr == rMap.end() ? nIndex : itr->second;
1485 if ( nIndex != nNewIndex )
1486 {
1487 p->SetIndex( nNewIndex );
1488 bCompile = sal_True;
1489 }
1490 }
1491 }
1492 if( bCompile )
1493 CompileTokenArray();
1494 }
1495
CompileDBFormula()1496 void ScFormulaCell::CompileDBFormula()
1497 {
1498 for( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
1499 {
1500 if ( p->GetOpCode() == ocDBArea
1501 || (p->GetOpCode() == ocName && p->GetIndex() >= SC_START_INDEX_DB_COLL) )
1502 {
1503 bCompile = sal_True;
1504 CompileTokenArray();
1505 SetDirty();
1506 break;
1507 }
1508 }
1509 }
1510
CompileDBFormula(sal_Bool bCreateFormulaString)1511 void ScFormulaCell::CompileDBFormula( sal_Bool bCreateFormulaString )
1512 {
1513 // zwei Phasen, muessen (!) nacheinander aufgerufen werden:
1514 // 1. FormelString mit alten Namen erzeugen
1515 // 2. FormelString mit neuen Namen kompilieren
1516 if ( bCreateFormulaString )
1517 {
1518 sal_Bool bRecompile = sal_False;
1519 pCode->Reset();
1520 for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
1521 {
1522 switch ( p->GetOpCode() )
1523 {
1524 case ocBad: // DB-Bereich evtl. zugefuegt
1525 case ocColRowName: // #36762# falls Namensgleichheit
1526 case ocDBArea: // DB-Bereich
1527 bRecompile = sal_True;
1528 break;
1529 case ocName:
1530 if ( p->GetIndex() >= SC_START_INDEX_DB_COLL )
1531 bRecompile = sal_True; // DB-Bereich
1532 break;
1533 default:
1534 ; // nothing
1535 }
1536 }
1537 if ( bRecompile )
1538 {
1539 String aFormula;
1540 GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
1541 if ( GetMatrixFlag() != MM_NONE && aFormula.Len() )
1542 {
1543 if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' )
1544 aFormula.Erase( aFormula.Len()-1 , 1 );
1545 if ( aFormula.GetChar(0) == '{' )
1546 aFormula.Erase( 0, 1 );
1547 }
1548 EndListeningTo( pDocument );
1549 pDocument->RemoveFromFormulaTree( this );
1550 pCode->Clear();
1551 SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
1552 }
1553 }
1554 else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
1555 {
1556 Compile( aResult.GetHybridFormula(), sal_False, eTempGrammar );
1557 aResult.SetToken( NULL);
1558 SetDirty();
1559 }
1560 }
1561
CompileNameFormula(sal_Bool bCreateFormulaString)1562 void ScFormulaCell::CompileNameFormula( sal_Bool bCreateFormulaString )
1563 {
1564 // zwei Phasen, muessen (!) nacheinander aufgerufen werden:
1565 // 1. FormelString mit alten RangeNames erzeugen
1566 // 2. FormelString mit neuen RangeNames kompilieren
1567 if ( bCreateFormulaString )
1568 {
1569 sal_Bool bRecompile = sal_False;
1570 pCode->Reset();
1571 for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
1572 {
1573 switch ( p->GetOpCode() )
1574 {
1575 case ocBad: // RangeName evtl. zugefuegt
1576 case ocColRowName: // #36762# falls Namensgleichheit
1577 bRecompile = sal_True;
1578 break;
1579 default:
1580 if ( p->GetType() == svIndex )
1581 bRecompile = sal_True; // RangeName
1582 }
1583 }
1584 if ( bRecompile )
1585 {
1586 String aFormula;
1587 GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
1588 if ( GetMatrixFlag() != MM_NONE && aFormula.Len() )
1589 {
1590 if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' )
1591 aFormula.Erase( aFormula.Len()-1 , 1 );
1592 if ( aFormula.GetChar(0) == '{' )
1593 aFormula.Erase( 0, 1 );
1594 }
1595 EndListeningTo( pDocument );
1596 pDocument->RemoveFromFormulaTree( this );
1597 pCode->Clear();
1598 SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
1599 }
1600 }
1601 else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
1602 {
1603 Compile( aResult.GetHybridFormula(), sal_False, eTempGrammar );
1604 aResult.SetToken( NULL);
1605 SetDirty();
1606 }
1607 }
1608
CompileColRowNameFormula()1609 void ScFormulaCell::CompileColRowNameFormula()
1610 {
1611 pCode->Reset();
1612 for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
1613 {
1614 if ( p->GetOpCode() == ocColRowName )
1615 {
1616 bCompile = sal_True;
1617 CompileTokenArray();
1618 SetDirty();
1619 break;
1620 }
1621 }
1622 }
1623
1624 // ============================================================================
1625
1626