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