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