xref: /trunk/main/sc/source/core/data/cell2.cxx (revision 03eb9a00)
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