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 4788 : void ScDocument::StartListeningArea( const ScRange& rRange,
49 : SvtListener* pListener
50 : )
51 : {
52 4788 : if ( pBASM )
53 4788 : pBASM->StartListeningArea( rRange, pListener );
54 4788 : }
55 :
56 :
57 6280 : void ScDocument::EndListeningArea( const ScRange& rRange,
58 : SvtListener* pListener
59 : )
60 : {
61 6280 : if ( pBASM )
62 6280 : pBASM->EndListeningArea( rRange, pListener );
63 6280 : }
64 :
65 :
66 6074 : void ScDocument::Broadcast( sal_uLong nHint, const ScAddress& rAddr,
67 : ScBaseCell* pCell
68 : )
69 : {
70 6074 : if ( !pBASM )
71 6074 : return ; // Clipboard or Undo
72 6074 : ScHint aHint( nHint, rAddr, pCell );
73 6074 : Broadcast( aHint );
74 : }
75 :
76 :
77 11538 : void ScDocument::Broadcast( const ScHint& rHint )
78 : {
79 11538 : if ( !pBASM )
80 11538 : return ; // Clipboard or Undo
81 11538 : if ( !bHardRecalcState )
82 : {
83 11472 : ScBulkBroadcast aBulkBroadcast( pBASM); // scoped bulk broadcast
84 11472 : bool bIsBroadcasted = false;
85 11472 : ScBaseCell* pCell = rHint.GetCell();
86 11472 : if ( pCell )
87 : {
88 5308 : SvtBroadcaster* pBC = pCell->GetBroadcaster();
89 5308 : if ( pBC )
90 : {
91 298 : pBC->Broadcast( rHint );
92 298 : bIsBroadcasted = true;
93 : }
94 : }
95 11472 : if ( pBASM->AreaBroadcast( rHint ) || bIsBroadcasted )
96 354 : TrackFormulas( rHint.GetId() );
97 : }
98 :
99 : // Repaint fuer bedingte Formate mit relativen Referenzen:
100 34704 : for(SCTAB nTab = 0; nTab < static_cast<SCTAB>(maTabs.size()); ++nTab)
101 : {
102 23166 : if(!maTabs[nTab])
103 0 : continue;
104 :
105 23166 : ScConditionalFormatList* pCondFormList = GetCondFormList(nTab);
106 23166 : if ( pCondFormList && rHint.GetAddress() != BCA_BRDCST_ALWAYS )
107 11918 : pCondFormList->SourceChanged( rHint.GetAddress() );
108 :
109 : }
110 :
111 11538 : if ( rHint.GetAddress() != BCA_BRDCST_ALWAYS )
112 : {
113 5464 : SCTAB nTab = rHint.GetAddress().Tab();
114 5464 : if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->IsStreamValid())
115 16 : maTabs[nTab]->SetStreamValid(false);
116 : }
117 : }
118 :
119 :
120 4 : void ScDocument::AreaBroadcast( const ScHint& rHint )
121 : {
122 4 : if ( !pBASM )
123 4 : return ; // Clipboard or Undo
124 4 : if ( !bHardRecalcState )
125 : {
126 4 : ScBulkBroadcast aBulkBroadcast( pBASM); // scoped bulk broadcast
127 4 : if ( pBASM->AreaBroadcast( rHint ) )
128 0 : TrackFormulas( rHint.GetId() );
129 : }
130 :
131 8 : for(SCTAB nTab = 0; nTab < static_cast<SCTAB>(maTabs.size()); ++nTab)
132 : {
133 4 : if(!maTabs[nTab])
134 0 : continue;
135 :
136 4 : ScConditionalFormatList* pCondFormList = GetCondFormList(nTab);
137 4 : if ( pCondFormList && rHint.GetAddress() != BCA_BRDCST_ALWAYS )
138 4 : pCondFormList->SourceChanged( rHint.GetAddress() );
139 : }
140 : }
141 :
142 :
143 2 : void ScDocument::AreaBroadcastInRange( const ScRange& rRange, const ScHint& rHint )
144 : {
145 2 : if ( !pBASM )
146 2 : return ; // Clipboard or Undo
147 2 : if ( !bHardRecalcState )
148 : {
149 2 : ScBulkBroadcast aBulkBroadcast( pBASM); // scoped bulk broadcast
150 2 : 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 2 : TableContainer::iterator itr = maTabs.begin();
157 10 : for(; itr != maTabs.end(); ++itr)
158 : {
159 8 : if(!*itr)
160 0 : continue;
161 :
162 8 : ScConditionalFormatList* pCondFormList = (*itr)->GetCondFormList();
163 8 : if ( pCondFormList )
164 : {
165 : SCCOL nCol1;
166 : SCROW nRow1;
167 : SCTAB nTab1;
168 : SCCOL nCol2;
169 : SCROW nRow2;
170 : SCTAB nTab2;
171 8 : rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
172 8 : ScAddress aAddress( rRange.aStart );
173 16 : for ( SCTAB nTab = nTab1; nTab <= nTab2; ++nTab )
174 : {
175 8 : aAddress.SetTab( nTab );
176 16 : for ( SCCOL nCol = nCol1; nCol <= nCol2; ++nCol )
177 : {
178 8 : aAddress.SetCol( nCol );
179 32 : for ( SCROW nRow = nRow1; nRow <= nRow2; ++nRow )
180 : {
181 24 : aAddress.SetRow( nRow );
182 24 : pCondFormList->SourceChanged( aAddress );
183 : }
184 : }
185 : }
186 : }
187 :
188 : }
189 : }
190 :
191 :
192 86 : void ScDocument::DelBroadcastAreasInRange( const ScRange& rRange )
193 : {
194 86 : if ( pBASM )
195 86 : pBASM->DelBroadcastAreasInRange( rRange );
196 86 : }
197 :
198 10180 : void ScDocument::StartListeningCell( const ScAddress& rAddress,
199 : SvtListener* pListener )
200 : {
201 : OSL_ENSURE(pListener, "StartListeningCell: pListener Null");
202 10180 : SCTAB nTab = rAddress.Tab();
203 10180 : if (VALIDTAB(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
204 10180 : maTabs[nTab]->StartListening( rAddress, pListener );
205 10180 : }
206 :
207 454 : void ScDocument::EndListeningCell( const ScAddress& rAddress,
208 : SvtListener* pListener )
209 : {
210 : OSL_ENSURE(pListener, "EndListeningCell: pListener Null");
211 454 : SCTAB nTab = rAddress.Tab();
212 454 : if (VALIDTAB(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
213 454 : maTabs[nTab]->EndListening( rAddress, pListener );
214 454 : }
215 :
216 :
217 5826 : void ScDocument::PutInFormulaTree( ScFormulaCell* pCell )
218 : {
219 : OSL_ENSURE( pCell, "PutInFormulaTree: pCell Null" );
220 5826 : RemoveFromFormulaTree( pCell );
221 : // anhaengen
222 5826 : if ( pEOFormulaTree )
223 5606 : pEOFormulaTree->SetNext( pCell );
224 : else
225 220 : pFormulaTree = pCell; // kein Ende, kein Anfang..
226 5826 : pCell->SetPrevious( pEOFormulaTree );
227 5826 : pCell->SetNext( 0 );
228 5826 : pEOFormulaTree = pCell;
229 5826 : nFormulaCodeInTree += pCell->GetCode()->GetCodeLen();
230 5826 : }
231 :
232 :
233 24592 : void ScDocument::RemoveFromFormulaTree( ScFormulaCell* pCell )
234 : {
235 : OSL_ENSURE( pCell, "RemoveFromFormulaTree: pCell Null" );
236 24592 : ScFormulaCell* pPrev = pCell->GetPrevious();
237 : // wenn die Zelle die erste oder sonstwo ist
238 24592 : if ( pPrev || pFormulaTree == pCell )
239 : {
240 5728 : ScFormulaCell* pNext = pCell->GetNext();
241 5728 : if ( pPrev )
242 3948 : pPrev->SetNext( pNext ); // gibt Vorlaeufer
243 : else
244 1780 : pFormulaTree = pNext; // ist erste Zelle
245 5728 : if ( pNext )
246 5408 : pNext->SetPrevious( pPrev ); // gibt Nachfolger
247 : else
248 320 : pEOFormulaTree = pPrev; // ist letzte Zelle
249 5728 : pCell->SetPrevious( 0 );
250 5728 : pCell->SetNext( 0 );
251 5728 : sal_uInt16 nRPN = pCell->GetCode()->GetCodeLen();
252 5728 : if ( nFormulaCodeInTree >= nRPN )
253 5682 : nFormulaCodeInTree -= nRPN;
254 : else
255 : {
256 : OSL_FAIL( "RemoveFromFormulaTree: nFormulaCodeInTree < nRPN" );
257 46 : nFormulaCodeInTree = 0;
258 5728 : }
259 : }
260 18864 : else if ( !pFormulaTree && nFormulaCodeInTree )
261 : {
262 : OSL_FAIL( "!pFormulaTree && nFormulaCodeInTree != 0" );
263 0 : nFormulaCodeInTree = 0;
264 : }
265 24592 : }
266 :
267 :
268 2188 : bool ScDocument::IsInFormulaTree( ScFormulaCell* pCell ) const
269 : {
270 2188 : return pCell->GetPrevious() || pFormulaTree == pCell;
271 : }
272 :
273 :
274 98 : 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 98 : if ( IsCalculatingFormulaTree() )
279 98 : return ;
280 98 : bCalculatingFormulaTree = true;
281 :
282 98 : SetForcedFormulaPending( false );
283 98 : bool bOldIdleDisabled = IsIdleDisabled();
284 98 : DisableIdle( true );
285 98 : bool bOldAutoCalc = GetAutoCalc();
286 : //! _nicht_ SetAutoCalc( true ) weil das evtl. CalcFormulaTree( true )
287 : //! aufruft, wenn vorher disabled war und bHasForcedFormulas gesetzt ist
288 98 : bAutoCalc = true;
289 98 : if ( bHardRecalcState )
290 0 : CalcAll();
291 : else
292 : {
293 98 : ScFormulaCell* pCell = pFormulaTree;
294 290 : while ( pCell )
295 : {
296 94 : if ( pCell->GetDirty() )
297 40 : pCell = pCell->GetNext(); // alles klar
298 : else
299 : {
300 54 : if ( pCell->GetCode()->IsRecalcModeAlways() )
301 : {
302 : // pCell wird im SetDirty neu angehaengt!
303 4 : ScFormulaCell* pNext = pCell->GetNext();
304 4 : pCell->SetDirty();
305 : // falls pNext==0 und neue abhaengige hinten angehaengt
306 : // wurden, so macht das nichts, da die alle bDirty sind
307 4 : pCell = pNext;
308 : }
309 : else
310 : { // andere simpel berechnen
311 50 : if( bSetAllDirty )
312 0 : pCell->SetDirtyVar();
313 50 : pCell = pCell->GetNext();
314 : }
315 : }
316 : }
317 98 : bool bProgress = !bOnlyForced && nFormulaCodeInTree && bProgressBar;
318 98 : if ( bProgress )
319 6 : ScProgress::CreateInterpretProgress( this, true );
320 :
321 98 : pCell = pFormulaTree;
322 98 : ScFormulaCell* pLastNoGood = 0;
323 290 : while ( pCell )
324 : {
325 : // Interpret setzt bDirty zurueck und callt Remove, auch der referierten!
326 : // bei RECALCMODE_ALWAYS bleibt die Zelle
327 94 : if ( bOnlyForced )
328 : {
329 0 : if ( pCell->GetCode()->IsRecalcModeForced() )
330 0 : pCell->Interpret();
331 : }
332 : else
333 : {
334 94 : pCell->Interpret();
335 : }
336 94 : if ( pCell->GetPrevious() || pCell == pFormulaTree )
337 : { // (IsInFormulaTree(pCell)) kein Remove gewesen => next
338 88 : pLastNoGood = pCell;
339 88 : pCell = pCell->GetNext();
340 : }
341 : else
342 : {
343 6 : 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 6 : pCell = 0;
368 : }
369 94 : if ( ScProgress::IsUserBreak() )
370 0 : pCell = 0;
371 : }
372 98 : if ( bProgress )
373 6 : ScProgress::DeleteInterpretProgress();
374 : }
375 98 : bAutoCalc = bOldAutoCalc;
376 98 : DisableIdle( bOldIdleDisabled );
377 98 : bCalculatingFormulaTree = false;
378 : }
379 :
380 :
381 108 : void ScDocument::ClearFormulaTree()
382 : {
383 : ScFormulaCell* pCell;
384 108 : ScFormulaCell* pTree = pFormulaTree;
385 264 : while ( pTree )
386 : {
387 48 : pCell = pTree;
388 48 : pTree = pCell->GetNext();
389 48 : if ( !pCell->GetCode()->IsRecalcModeAlways() )
390 0 : RemoveFromFormulaTree( pCell );
391 : }
392 108 : }
393 :
394 :
395 814 : void ScDocument::AppendToFormulaTrack( ScFormulaCell* pCell )
396 : {
397 : OSL_ENSURE( pCell, "AppendToFormulaTrack: pCell Null" );
398 : // Zelle kann nicht in beiden Listen gleichzeitig sein
399 814 : RemoveFromFormulaTrack( pCell );
400 814 : RemoveFromFormulaTree( pCell );
401 814 : if ( pEOFormulaTrack )
402 162 : pEOFormulaTrack->SetNextTrack( pCell );
403 : else
404 652 : pFormulaTrack = pCell; // kein Ende, kein Anfang..
405 814 : pCell->SetPreviousTrack( pEOFormulaTrack );
406 814 : pCell->SetNextTrack( 0 );
407 814 : pEOFormulaTrack = pCell;
408 814 : ++nFormulaTrackCount;
409 814 : }
410 :
411 :
412 1628 : void ScDocument::RemoveFromFormulaTrack( ScFormulaCell* pCell )
413 : {
414 : OSL_ENSURE( pCell, "RemoveFromFormulaTrack: pCell Null" );
415 1628 : ScFormulaCell* pPrev = pCell->GetPreviousTrack();
416 : // wenn die Zelle die erste oder sonstwo ist
417 1628 : if ( pPrev || pFormulaTrack == pCell )
418 : {
419 814 : ScFormulaCell* pNext = pCell->GetNextTrack();
420 814 : if ( pPrev )
421 0 : pPrev->SetNextTrack( pNext ); // gibt Vorlaeufer
422 : else
423 814 : pFormulaTrack = pNext; // ist erste Zelle
424 814 : if ( pNext )
425 162 : pNext->SetPreviousTrack( pPrev ); // gibt Nachfolger
426 : else
427 652 : pEOFormulaTrack = pPrev; // ist letzte Zelle
428 814 : pCell->SetPreviousTrack( 0 );
429 814 : pCell->SetNextTrack( 0 );
430 814 : --nFormulaTrackCount;
431 : }
432 1628 : }
433 :
434 :
435 270 : bool ScDocument::IsInFormulaTrack( ScFormulaCell* pCell ) const
436 : {
437 270 : 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 902 : void ScDocument::TrackFormulas( sal_uLong nHintId )
448 : {
449 :
450 902 : if ( pFormulaTrack )
451 : {
452 : // outside the loop, check if any sheet has a "calculate" event script
453 652 : bool bCalcEvent = HasAnySheetEventScript( SC_SHEETEVENT_CALCULATE, true );
454 : SvtBroadcaster* pBC;
455 : ScFormulaCell* pTrack;
456 : ScFormulaCell* pNext;
457 652 : pTrack = pFormulaTrack;
458 814 : do
459 : {
460 814 : ScHint aHint( nHintId, pTrack->aPos, pTrack );
461 814 : if ( ( pBC = pTrack->GetBroadcaster() ) != NULL )
462 20 : pBC->Broadcast( aHint );
463 814 : pBASM->AreaBroadcast( aHint );
464 : // Repaint fuer bedingte Formate mit relativen Referenzen:
465 814 : TableContainer::iterator itr = maTabs.begin();
466 1900 : for(; itr != maTabs.end(); ++itr)
467 : {
468 1086 : if(!*itr)
469 0 : continue;
470 1086 : ScConditionalFormatList* pCondFormList = (*itr)->GetCondFormList();
471 1086 : if ( pCondFormList )
472 1086 : pCondFormList->SourceChanged( pTrack->aPos );
473 : }
474 : // for "calculate" event, keep track of which sheets are affected by tracked formulas
475 814 : if ( bCalcEvent )
476 0 : SetCalcNotification( pTrack->aPos.Tab() );
477 814 : pTrack = pTrack->GetNextTrack();
478 : } while ( pTrack );
479 652 : pTrack = pFormulaTrack;
480 652 : bool bHaveForced = false;
481 814 : do
482 : {
483 814 : pNext = pTrack->GetNextTrack();
484 814 : RemoveFromFormulaTrack( pTrack );
485 814 : PutInFormulaTree( pTrack );
486 814 : if ( pTrack->GetCode()->IsRecalcModeForced() )
487 0 : bHaveForced = true;
488 814 : pTrack = pNext;
489 : } while ( pTrack );
490 652 : 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 902 : }
502 :
503 :
504 14 : void ScDocument::StartAllListeners()
505 : {
506 28 : for ( SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); ++i )
507 14 : if ( maTabs[i] )
508 14 : maTabs[i]->StartAllListeners();
509 14 : }
510 :
511 126 : void ScDocument::UpdateBroadcastAreas( UpdateRefMode eUpdateRefMode,
512 : const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz
513 : )
514 : {
515 126 : bool bExpandRefsOld = IsExpandRefs();
516 126 : if ( eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0 || nDz > 0) )
517 42 : SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() );
518 126 : if ( pBASM )
519 126 : pBASM->UpdateBroadcastAreas( eUpdateRefMode, rRange, nDx, nDy, nDz );
520 126 : SetExpandRefs( bExpandRefsOld );
521 126 : }
522 :
523 1483620 : void ScDocument::SetAutoCalc( bool bNewAutoCalc )
524 : {
525 1483620 : bool bOld = bAutoCalc;
526 1483620 : bAutoCalc = bNewAutoCalc;
527 1483620 : if ( !bOld && bNewAutoCalc && bHasForcedFormulas )
528 : {
529 0 : if ( IsAutoCalcShellDisabled() )
530 0 : SetForcedFormulaPending( true );
531 0 : else if ( !IsInInterpreter() )
532 0 : CalcFormulaTree( true );
533 : }
534 1483620 : }
535 :
536 :
537 :
538 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|