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