xref: /AOO41X/main/sc/source/core/data/documen7.cxx (revision b3f79822e811ac3493b185030a72c3c5a51f32d8)
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 // INCLUDE ---------------------------------------------------------------
28 
29 #include <vcl/svapp.hxx>
30 
31 #if defined( WNT ) && defined( erBEEP )
32 #include <svwin.h>
33 #define erBEEPER() Beep( 666, 66 )
34 #else
35 #define erBEEPER()
36 #endif
37 
38 #include "document.hxx"
39 #include "brdcst.hxx"
40 #include "bcaslot.hxx"
41 #include "cell.hxx"
42 #include "formula/errorcodes.hxx"       // errCircularReference
43 #include "scerrors.hxx"
44 #include "docoptio.hxx"
45 #include "refupdat.hxx"
46 #include "table.hxx"
47 #include "progress.hxx"
48 #include "scmod.hxx"        // SC_MOD
49 #include "inputopt.hxx"     // GetExpandRefs
50 #include "conditio.hxx"
51 #include "sheetevents.hxx"
52 #include <tools/shl.hxx>
53 
54 
55 #include "globstr.hrc"
56 
57 extern const ScFormulaCell* pLastFormulaTreeTop;    // cellform.cxx Err527 WorkAround
58 
59 // STATIC DATA -----------------------------------------------------------
60 
61 #ifdef erDEBUG
62 sal_uLong erCountBCAInserts = 0;
63 sal_uLong erCountBCAFinds = 0;
64 #endif
65 
66 // -----------------------------------------------------------------------
67 
StartListeningArea(const ScRange & rRange,SvtListener * pListener)68 void ScDocument::StartListeningArea( const ScRange& rRange,
69         SvtListener* pListener
70     )
71 {
72     if ( pBASM )
73         pBASM->StartListeningArea( rRange, pListener );
74 }
75 
76 
EndListeningArea(const ScRange & rRange,SvtListener * pListener)77 void ScDocument::EndListeningArea( const ScRange& rRange,
78         SvtListener* pListener
79     )
80 {
81     if ( pBASM )
82         pBASM->EndListeningArea( rRange, pListener );
83 }
84 
85 
Broadcast(sal_uLong nHint,const ScAddress & rAddr,ScBaseCell * pCell)86 void ScDocument::Broadcast( sal_uLong nHint, const ScAddress& rAddr,
87         ScBaseCell* pCell
88     )
89 {
90     if ( !pBASM )
91         return ;    // Clipboard or Undo
92     ScHint aHint( nHint, rAddr, pCell );
93     Broadcast( aHint );
94 }
95 
96 
Broadcast(const ScHint & rHint)97 void ScDocument::Broadcast( const ScHint& rHint )
98 {
99     if ( !pBASM )
100         return ;    // Clipboard or Undo
101     if ( !nHardRecalcState )
102     {
103         ScBulkBroadcast aBulkBroadcast( pBASM);     // scoped bulk broadcast
104         sal_Bool bIsBroadcasted = sal_False;
105         ScBaseCell* pCell = rHint.GetCell();
106         if ( pCell )
107         {
108             SvtBroadcaster* pBC = pCell->GetBroadcaster();
109             if ( pBC )
110             {
111                 pBC->Broadcast( rHint );
112                 bIsBroadcasted = sal_True;
113             }
114         }
115         if ( pBASM->AreaBroadcast( rHint ) || bIsBroadcasted )
116             TrackFormulas( rHint.GetId() );
117     }
118 
119     //  Repaint fuer bedingte Formate mit relativen Referenzen:
120     if ( pCondFormList && rHint.GetAddress() != BCA_BRDCST_ALWAYS )
121         pCondFormList->SourceChanged( rHint.GetAddress() );
122 
123     if ( rHint.GetAddress() != BCA_BRDCST_ALWAYS )
124     {
125         SCTAB nTab = rHint.GetAddress().Tab();
126         if (pTab[nTab] && pTab[nTab]->IsStreamValid())
127             pTab[nTab]->SetStreamValid(sal_False);
128     }
129 }
130 
131 
AreaBroadcast(const ScHint & rHint)132 void ScDocument::AreaBroadcast( const ScHint& rHint )
133 {
134     if ( !pBASM )
135         return ;    // Clipboard or Undo
136     if ( !nHardRecalcState )
137     {
138         ScBulkBroadcast aBulkBroadcast( pBASM);     // scoped bulk broadcast
139         if ( pBASM->AreaBroadcast( rHint ) )
140             TrackFormulas( rHint.GetId() );
141     }
142 
143     //  Repaint fuer bedingte Formate mit relativen Referenzen:
144     if ( pCondFormList && rHint.GetAddress() != BCA_BRDCST_ALWAYS )
145         pCondFormList->SourceChanged( rHint.GetAddress() );
146 }
147 
148 
AreaBroadcastInRange(const ScRange & rRange,const ScHint & rHint)149 void ScDocument::AreaBroadcastInRange( const ScRange& rRange, const ScHint& rHint )
150 {
151     if ( !pBASM )
152         return ;    // Clipboard or Undo
153     if ( !nHardRecalcState )
154     {
155         ScBulkBroadcast aBulkBroadcast( pBASM);     // scoped bulk broadcast
156         if ( pBASM->AreaBroadcastInRange( rRange, rHint ) )
157             TrackFormulas( rHint.GetId() );
158     }
159 
160     // Repaint for conditional formats containing relative references.
161     //! This is _THE_ bottle neck!
162     if ( pCondFormList )
163     {
164         SCCOL nCol;
165         SCROW nRow;
166         SCTAB nTab;
167         SCCOL nCol1;
168         SCROW nRow1;
169         SCTAB nTab1;
170         SCCOL nCol2;
171         SCROW nRow2;
172         SCTAB nTab2;
173         rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
174         ScAddress aAddress( rRange.aStart );
175         for ( nTab = nTab1; nTab <= nTab2; ++nTab )
176         {
177             aAddress.SetTab( nTab );
178             for ( nCol = nCol1; nCol <= nCol2; ++nCol )
179             {
180                 aAddress.SetCol( nCol );
181                 for ( nRow = nRow1; nRow <= nRow2; ++nRow )
182                 {
183                     aAddress.SetRow( nRow );
184                     pCondFormList->SourceChanged( aAddress );
185                 }
186             }
187         }
188     }
189 }
190 
191 
DelBroadcastAreasInRange(const ScRange & rRange)192 void ScDocument::DelBroadcastAreasInRange( const ScRange& rRange )
193 {
194     if ( pBASM )
195         pBASM->DelBroadcastAreasInRange( rRange );
196 }
197 
StartListeningCell(const ScAddress & rAddress,SvtListener * pListener)198 void ScDocument::StartListeningCell( const ScAddress& rAddress,
199                                             SvtListener* pListener )
200 {
201     DBG_ASSERT(pListener, "StartListeningCell: pListener Null");
202     SCTAB nTab = rAddress.Tab();
203     if (pTab[nTab])
204         pTab[nTab]->StartListening( rAddress, pListener );
205 }
206 
EndListeningCell(const ScAddress & rAddress,SvtListener * pListener)207 void ScDocument::EndListeningCell( const ScAddress& rAddress,
208                                             SvtListener* pListener )
209 {
210     DBG_ASSERT(pListener, "EndListeningCell: pListener Null");
211     SCTAB nTab = rAddress.Tab();
212     if (pTab[nTab])
213         pTab[nTab]->EndListening( rAddress, pListener );
214 }
215 
216 
PutInFormulaTree(ScFormulaCell * pCell)217 void ScDocument::PutInFormulaTree( ScFormulaCell* pCell )
218 {
219     DBG_ASSERT( pCell, "PutInFormulaTree: pCell Null" );
220     RemoveFromFormulaTree( pCell );
221     // anhaengen
222     if ( pEOFormulaTree )
223         pEOFormulaTree->SetNext( pCell );
224     else
225         pFormulaTree = pCell;               // kein Ende, kein Anfang..
226     pCell->SetPrevious( pEOFormulaTree );
227     pCell->SetNext( 0 );
228     pEOFormulaTree = pCell;
229     nFormulaCodeInTree += pCell->GetCode()->GetCodeLen();
230 }
231 
232 
RemoveFromFormulaTree(ScFormulaCell * pCell)233 void ScDocument::RemoveFromFormulaTree( ScFormulaCell* pCell )
234 {
235     DBG_ASSERT( pCell, "RemoveFromFormulaTree: pCell Null" );
236     ScFormulaCell* pPrev = pCell->GetPrevious();
237     // wenn die Zelle die erste oder sonstwo ist
238     if ( pPrev || pFormulaTree == pCell )
239     {
240         ScFormulaCell* pNext = pCell->GetNext();
241         if ( pPrev )
242             pPrev->SetNext( pNext );        // gibt Vorlaeufer
243         else
244             pFormulaTree = pNext;           // ist erste Zelle
245         if ( pNext )
246             pNext->SetPrevious( pPrev );    // gibt Nachfolger
247         else
248             pEOFormulaTree = pPrev;         // ist letzte Zelle
249         pCell->SetPrevious( 0 );
250         pCell->SetNext( 0 );
251         sal_uInt16 nRPN = pCell->GetCode()->GetCodeLen();
252         if ( nFormulaCodeInTree >= nRPN )
253             nFormulaCodeInTree -= nRPN;
254         else
255         {
256             DBG_ERRORFILE( "RemoveFromFormulaTree: nFormulaCodeInTree < nRPN" );
257             nFormulaCodeInTree = 0;
258         }
259     }
260     else if ( !pFormulaTree && nFormulaCodeInTree )
261     {
262         DBG_ERRORFILE( "!pFormulaTree && nFormulaCodeInTree != 0" );
263         nFormulaCodeInTree = 0;
264     }
265 }
266 
267 
IsInFormulaTree(ScFormulaCell * pCell) const268 sal_Bool ScDocument::IsInFormulaTree( ScFormulaCell* pCell ) const
269 {
270     return pCell->GetPrevious() || pFormulaTree == pCell;
271 }
272 
273 
CalcFormulaTree(sal_Bool bOnlyForced,sal_Bool bNoProgress)274 void ScDocument::CalcFormulaTree( sal_Bool bOnlyForced, sal_Bool bNoProgress )
275 {
276     DBG_ASSERT( !IsCalculatingFormulaTree(), "CalcFormulaTree recursion" );
277     // never ever recurse into this, might end up lost in infinity
278     if ( IsCalculatingFormulaTree() )
279         return ;
280     bCalculatingFormulaTree = sal_True;
281 
282     SetForcedFormulaPending( sal_False );
283     sal_Bool bOldIdleDisabled = IsIdleDisabled();
284     DisableIdle( sal_True );
285     sal_Bool bOldAutoCalc = GetAutoCalc();
286     //! _nicht_ SetAutoCalc( sal_True ) weil das evtl. CalcFormulaTree( sal_True )
287     //! aufruft, wenn vorher disabled war und bHasForcedFormulas gesetzt ist
288     bAutoCalc = sal_True;
289     if ( nHardRecalcState )
290         CalcAll();
291     else
292     {
293         ScFormulaCell* pCell = pFormulaTree;
294         while ( pCell )
295         {
296             if ( pCell->GetDirty() )
297                 pCell = pCell->GetNext();       // alles klar
298             else
299             {
300                 if ( pCell->GetCode()->IsRecalcModeAlways() )
301                 {
302                     // pCell wird im SetDirty neu angehaengt!
303                     ScFormulaCell* pNext = pCell->GetNext();
304                     pCell->SetDirty();
305                     // falls pNext==0 und neue abhaengige hinten angehaengt
306                     // wurden, so macht das nichts, da die alle bDirty sind
307                     pCell = pNext;
308                 }
309                 else
310                 {   // andere simpel berechnen
311                     pCell->SetDirtyVar();
312                     pCell = pCell->GetNext();
313                 }
314             }
315         }
316         sal_Bool bProgress = !bOnlyForced && nFormulaCodeInTree && !bNoProgress;
317         if ( bProgress )
318             ScProgress::CreateInterpretProgress( this, sal_True );
319 
320         pCell = pFormulaTree;
321         ScFormulaCell* pLastNoGood = 0;
322         while ( pCell )
323         {
324             // Interpret setzt bDirty zurueck und callt Remove, auch der referierten!
325             // bei RECALCMODE_ALWAYS bleibt die Zelle
326             if ( bOnlyForced )
327             {
328                 if ( pCell->GetCode()->IsRecalcModeForced() )
329                     pCell->Interpret();
330             }
331             else
332             {
333                 pCell->Interpret();
334             }
335             if ( pCell->GetPrevious() || pCell == pFormulaTree )
336             {   // (IsInFormulaTree(pCell)) kein Remove gewesen => next
337                 pLastNoGood = pCell;
338                 pCell = pCell->GetNext();
339             }
340             else
341             {
342                 if ( pFormulaTree )
343                 {
344                     if ( pFormulaTree->GetDirty() && !bOnlyForced )
345                     {
346                         pCell = pFormulaTree;
347                         pLastNoGood = 0;
348                     }
349                     else
350                     {
351                         // IsInFormulaTree(pLastNoGood)
352                         if ( pLastNoGood && (pLastNoGood->GetPrevious() ||
353                                 pLastNoGood == pFormulaTree) )
354                             pCell = pLastNoGood->GetNext();
355                         else
356                         {
357                             pCell = pFormulaTree;
358                             while ( pCell && !pCell->GetDirty() )
359                                 pCell = pCell->GetNext();
360                             if ( pCell )
361                                 pLastNoGood = pCell->GetPrevious();
362                         }
363                     }
364                 }
365                 else
366                     pCell = 0;
367             }
368             if ( ScProgress::IsUserBreak() )
369                 pCell = 0;
370         }
371         if ( bProgress )
372             ScProgress::DeleteInterpretProgress();
373     }
374     bAutoCalc = bOldAutoCalc;
375     DisableIdle( bOldIdleDisabled );
376     bCalculatingFormulaTree = sal_False;
377 }
378 
379 
ClearFormulaTree()380 void ScDocument::ClearFormulaTree()
381 {
382     ScFormulaCell* pCell;
383     ScFormulaCell* pTree = pFormulaTree;
384     while ( pTree )
385     {
386         pCell = pTree;
387         pTree = pCell->GetNext();
388         if ( !pCell->GetCode()->IsRecalcModeAlways() )
389             RemoveFromFormulaTree( pCell );
390     }
391 }
392 
393 
AppendToFormulaTrack(ScFormulaCell * pCell)394 void ScDocument::AppendToFormulaTrack( ScFormulaCell* pCell )
395 {
396     DBG_ASSERT( pCell, "AppendToFormulaTrack: pCell Null" );
397     // Zelle kann nicht in beiden Listen gleichzeitig sein
398     RemoveFromFormulaTrack( pCell );
399     RemoveFromFormulaTree( pCell );
400     if ( pEOFormulaTrack )
401         pEOFormulaTrack->SetNextTrack( pCell );
402     else
403         pFormulaTrack = pCell;              // kein Ende, kein Anfang..
404     pCell->SetPreviousTrack( pEOFormulaTrack );
405     pCell->SetNextTrack( 0 );
406     pEOFormulaTrack = pCell;
407     ++nFormulaTrackCount;
408 }
409 
410 
RemoveFromFormulaTrack(ScFormulaCell * pCell)411 void ScDocument::RemoveFromFormulaTrack( ScFormulaCell* pCell )
412 {
413     DBG_ASSERT( pCell, "RemoveFromFormulaTrack: pCell Null" );
414     ScFormulaCell* pPrev = pCell->GetPreviousTrack();
415     // wenn die Zelle die erste oder sonstwo ist
416     if ( pPrev || pFormulaTrack == pCell )
417     {
418         ScFormulaCell* pNext = pCell->GetNextTrack();
419         if ( pPrev )
420             pPrev->SetNextTrack( pNext );       // gibt Vorlaeufer
421         else
422             pFormulaTrack = pNext;              // ist erste Zelle
423         if ( pNext )
424             pNext->SetPreviousTrack( pPrev );   // gibt Nachfolger
425         else
426             pEOFormulaTrack = pPrev;            // ist letzte Zelle
427         pCell->SetPreviousTrack( 0 );
428         pCell->SetNextTrack( 0 );
429         --nFormulaTrackCount;
430     }
431 }
432 
433 
IsInFormulaTrack(ScFormulaCell * pCell) const434 sal_Bool ScDocument::IsInFormulaTrack( ScFormulaCell* pCell ) const
435 {
436     return pCell->GetPreviousTrack() || pFormulaTrack == pCell;
437 }
438 
439 
440 /*
441     Der erste wird gebroadcastet,
442     die dadurch entstehenden werden durch das Notify an den Track gehaengt.
443     Der nachfolgende broadcastet wieder usw.
444     View stoesst Interpret an.
445  */
TrackFormulas(sal_uLong nHintId)446 void ScDocument::TrackFormulas( sal_uLong nHintId )
447 {
448 
449     if ( pFormulaTrack )
450     {
451         erBEEPER();
452         // outside the loop, check if any sheet has a "calculate" event script
453         bool bCalcEvent = HasAnySheetEventScript( SC_SHEETEVENT_CALCULATE, true );
454         SvtBroadcaster* pBC;
455         ScFormulaCell* pTrack;
456         ScFormulaCell* pNext;
457         pTrack = pFormulaTrack;
458         do
459         {
460             ScHint aHint( nHintId, pTrack->aPos, pTrack );
461             if ( ( pBC = pTrack->GetBroadcaster() ) != NULL )
462                 pBC->Broadcast( aHint );
463             pBASM->AreaBroadcast( aHint );
464             //  Repaint fuer bedingte Formate mit relativen Referenzen:
465             if ( pCondFormList )
466                 pCondFormList->SourceChanged( pTrack->aPos );
467             // for "calculate" event, keep track of which sheets are affected by tracked formulas
468             if ( bCalcEvent )
469                 SetCalcNotification( pTrack->aPos.Tab() );
470             pTrack = pTrack->GetNextTrack();
471         } while ( pTrack );
472         pTrack = pFormulaTrack;
473         sal_Bool bHaveForced = sal_False;
474         do
475         {
476             pNext = pTrack->GetNextTrack();
477             RemoveFromFormulaTrack( pTrack );
478             PutInFormulaTree( pTrack );
479             if ( pTrack->GetCode()->IsRecalcModeForced() )
480                 bHaveForced = sal_True;
481             pTrack = pNext;
482         } while ( pTrack );
483         if ( bHaveForced )
484         {
485             SetForcedFormulas( sal_True );
486             if ( bAutoCalc && !IsAutoCalcShellDisabled() && !IsInInterpreter()
487                     && !IsCalculatingFormulaTree() )
488                 CalcFormulaTree( sal_True );
489             else
490                 SetForcedFormulaPending( sal_True );
491         }
492     }
493     DBG_ASSERT( nFormulaTrackCount==0, "TrackFormulas: nFormulaTrackCount!=0" );
494 }
495 
496 
StartAllListeners()497 void ScDocument::StartAllListeners()
498 {
499     for ( SCTAB i = 0; i <= MAXTAB; ++i )
500         if ( pTab[i] )
501             pTab[i]->StartAllListeners();
502 }
503 
UpdateBroadcastAreas(UpdateRefMode eUpdateRefMode,const ScRange & rRange,SCsCOL nDx,SCsROW nDy,SCsTAB nDz)504 void ScDocument::UpdateBroadcastAreas( UpdateRefMode eUpdateRefMode,
505         const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz
506     )
507 {
508     sal_Bool bExpandRefsOld = IsExpandRefs();
509     if ( eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0 || nDz > 0) )
510         SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() );
511     if ( pBASM )
512         pBASM->UpdateBroadcastAreas( eUpdateRefMode, rRange, nDx, nDy, nDz );
513     SetExpandRefs( bExpandRefsOld );
514 }
515 
SetAutoCalc(sal_Bool bNewAutoCalc)516 void ScDocument::SetAutoCalc( sal_Bool bNewAutoCalc )
517 {
518     sal_Bool bOld = bAutoCalc;
519     bAutoCalc = bNewAutoCalc;
520     if ( !bOld && bNewAutoCalc && bHasForcedFormulas )
521     {
522         if ( IsAutoCalcShellDisabled() )
523             SetForcedFormulaPending( sal_True );
524         else if ( !IsInInterpreter() )
525             CalcFormulaTree( sal_True );
526     }
527 }
528 
529 
530 
531