1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <vcl/svapp.hxx>
22 #include "document.hxx"
24 #include "bcaslot.hxx"
25 #include "formulacell.hxx"
26 #include "formula/errorcodes.hxx"
27 #include "scerrors.hxx"
28 #include "docoptio.hxx"
29 #include "refupdat.hxx"
31 #include "progress.hxx"
33 #include "inputopt.hxx"
34 #include "conditio.hxx"
35 #include "colorscale.hxx"
36 #include "sheetevents.hxx"
37 #include "tokenarray.hxx"
38 #include "listenercontext.hxx"
39 #include "formulagroup.hxx"
41 #include <tools/shl.hxx>
44 #include "globstr.hrc"
46 extern const ScFormulaCell
* pLastFormulaTreeTop
; // cellform.cxx Err527 WorkAround
48 // STATIC DATA -----------------------------------------------------------
50 // -----------------------------------------------------------------------
52 void ScDocument::StartListeningArea( const ScRange
& rRange
,
53 SvtListener
* pListener
57 pBASM
->StartListeningArea( rRange
, pListener
);
61 void ScDocument::EndListeningArea( const ScRange
& rRange
,
62 SvtListener
* pListener
66 pBASM
->EndListeningArea( rRange
, pListener
);
69 void ScDocument::Broadcast( const ScHint
& rHint
)
72 return ; // Clipboard or Undo
73 if ( !bHardRecalcState
)
75 ScBulkBroadcast
aBulkBroadcast( pBASM
); // scoped bulk broadcast
76 bool bIsBroadcasted
= false;
77 SvtBroadcaster
* pBC
= GetBroadcaster(rHint
.GetAddress());
80 pBC
->Broadcast( rHint
);
81 bIsBroadcasted
= true;
83 if ( pBASM
->AreaBroadcast( rHint
) || bIsBroadcasted
)
84 TrackFormulas( rHint
.GetId() );
87 // Repaint fuer bedingte Formate mit relativen Referenzen:
88 for(SCTAB nTab
= 0; nTab
< static_cast<SCTAB
>(maTabs
.size()); ++nTab
)
93 ScConditionalFormatList
* pCondFormList
= GetCondFormList(nTab
);
94 if ( pCondFormList
&& rHint
.GetAddress() != BCA_BRDCST_ALWAYS
)
95 pCondFormList
->SourceChanged( rHint
.GetAddress() );
99 if ( rHint
.GetAddress() != BCA_BRDCST_ALWAYS
)
101 SCTAB nTab
= rHint
.GetAddress().Tab();
102 if (nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
] && maTabs
[nTab
]->IsStreamValid())
103 maTabs
[nTab
]->SetStreamValid(false);
108 void ScDocument::AreaBroadcast( const ScHint
& rHint
)
111 return ; // Clipboard or Undo
112 if ( !bHardRecalcState
)
114 ScBulkBroadcast
aBulkBroadcast( pBASM
); // scoped bulk broadcast
115 if ( pBASM
->AreaBroadcast( rHint
) )
116 TrackFormulas( rHint
.GetId() );
119 for(SCTAB nTab
= 0; nTab
< static_cast<SCTAB
>(maTabs
.size()); ++nTab
)
124 ScConditionalFormatList
* pCondFormList
= GetCondFormList(nTab
);
125 if ( pCondFormList
&& rHint
.GetAddress() != BCA_BRDCST_ALWAYS
)
126 pCondFormList
->SourceChanged( rHint
.GetAddress() );
131 void ScDocument::AreaBroadcastInRange( const ScRange
& rRange
, const ScHint
& rHint
)
134 return ; // Clipboard or Undo
135 if ( !bHardRecalcState
)
137 ScBulkBroadcast
aBulkBroadcast( pBASM
); // scoped bulk broadcast
138 if ( pBASM
->AreaBroadcastInRange( rRange
, rHint
) )
139 TrackFormulas( rHint
.GetId() );
142 // Repaint for conditional formats containing relative references.
143 //! This is _THE_ bottle neck!
144 TableContainer::iterator itr
= maTabs
.begin();
145 for(; itr
!= maTabs
.end(); ++itr
)
150 ScConditionalFormatList
* pCondFormList
= (*itr
)->GetCondFormList();
159 rRange
.GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
160 ScAddress
aAddress( rRange
.aStart
);
161 for ( SCTAB nTab
= nTab1
; nTab
<= nTab2
; ++nTab
)
163 aAddress
.SetTab( nTab
);
164 for ( SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
166 aAddress
.SetCol( nCol
);
167 for ( SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
169 aAddress
.SetRow( nRow
);
170 pCondFormList
->SourceChanged( aAddress
);
180 void ScDocument::DelBroadcastAreasInRange( const ScRange
& rRange
)
183 pBASM
->DelBroadcastAreasInRange( rRange
);
186 void ScDocument::StartListeningCell( const ScAddress
& rAddress
,
187 SvtListener
* pListener
)
189 OSL_ENSURE(pListener
, "StartListeningCell: pListener Null");
190 SCTAB nTab
= rAddress
.Tab();
191 if (ValidTab(nTab
) && nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
192 maTabs
[nTab
]->StartListening( rAddress
, pListener
);
195 void ScDocument::EndListeningCell( const ScAddress
& rAddress
,
196 SvtListener
* pListener
)
198 OSL_ENSURE(pListener
, "EndListeningCell: pListener Null");
199 SCTAB nTab
= rAddress
.Tab();
200 if (ValidTab(nTab
) && nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
201 maTabs
[nTab
]->EndListening( rAddress
, pListener
);
204 void ScDocument::StartListeningCell(
205 sc::StartListeningContext
& rCxt
, const ScAddress
& rPos
, SvtListener
& rListener
)
207 ScTable
* pTab
= FetchTable(rPos
.Tab());
211 pTab
->StartListening(rCxt
, rPos
.Col(), rPos
.Row(), rListener
);
214 void ScDocument::EndListeningCell(
215 sc::EndListeningContext
& rCxt
, const ScAddress
& rPos
, SvtListener
& rListener
)
217 ScTable
* pTab
= FetchTable(rPos
.Tab());
221 pTab
->EndListening(rCxt
, rPos
.Col(), rPos
.Row(), rListener
);
224 void ScDocument::EndListeningFormulaCells( std::vector
<ScFormulaCell
*>& rCells
)
229 sc::EndListeningContext
aCxt(*this);
230 std::vector
<ScFormulaCell
*>::iterator it
= rCells
.begin(), itEnd
= rCells
.end();
231 for (; it
!= itEnd
; ++it
)
232 (*it
)->EndListeningTo(aCxt
);
234 aCxt
.purgeEmptyBroadcasters();
237 void ScDocument::PutInFormulaTree( ScFormulaCell
* pCell
)
239 OSL_ENSURE( pCell
, "PutInFormulaTree: pCell Null" );
240 RemoveFromFormulaTree( pCell
);
242 if ( pEOFormulaTree
)
243 pEOFormulaTree
->SetNext( pCell
);
245 pFormulaTree
= pCell
; // kein Ende, kein Anfang..
246 pCell
->SetPrevious( pEOFormulaTree
);
248 pEOFormulaTree
= pCell
;
249 nFormulaCodeInTree
+= pCell
->GetCode()->GetCodeLen();
253 void ScDocument::RemoveFromFormulaTree( ScFormulaCell
* pCell
)
255 OSL_ENSURE( pCell
, "RemoveFromFormulaTree: pCell Null" );
256 ScFormulaCell
* pPrev
= pCell
->GetPrevious();
257 // wenn die Zelle die erste oder sonstwo ist
258 if ( pPrev
|| pFormulaTree
== pCell
)
260 ScFormulaCell
* pNext
= pCell
->GetNext();
262 pPrev
->SetNext( pNext
); // gibt Vorlaeufer
264 pFormulaTree
= pNext
; // ist erste Zelle
266 pNext
->SetPrevious( pPrev
); // gibt Nachfolger
268 pEOFormulaTree
= pPrev
; // ist letzte Zelle
269 pCell
->SetPrevious( 0 );
271 sal_uInt16 nRPN
= pCell
->GetCode()->GetCodeLen();
272 if ( nFormulaCodeInTree
>= nRPN
)
273 nFormulaCodeInTree
-= nRPN
;
276 OSL_FAIL( "RemoveFromFormulaTree: nFormulaCodeInTree < nRPN" );
277 nFormulaCodeInTree
= 0;
280 else if ( !pFormulaTree
&& nFormulaCodeInTree
)
282 OSL_FAIL( "!pFormulaTree && nFormulaCodeInTree != 0" );
283 nFormulaCodeInTree
= 0;
288 bool ScDocument::IsInFormulaTree( ScFormulaCell
* pCell
) const
290 return pCell
->GetPrevious() || pFormulaTree
== pCell
;
294 void ScDocument::CalcFormulaTree( bool bOnlyForced
, bool bProgressBar
, bool bSetAllDirty
)
296 OSL_ENSURE( !IsCalculatingFormulaTree(), "CalcFormulaTree recursion" );
297 // never ever recurse into this, might end up lost in infinity
298 if ( IsCalculatingFormulaTree() )
301 mpFormulaGroupCxt
.reset();
302 bCalculatingFormulaTree
= true;
304 SetForcedFormulaPending( false );
305 bool bOldIdleEnabled
= IsIdleEnabled();
307 bool bOldAutoCalc
= GetAutoCalc();
308 //! _nicht_ SetAutoCalc( true ) weil das evtl. CalcFormulaTree( true )
309 //! aufruft, wenn vorher disabled war und bHasForcedFormulas gesetzt ist
311 if ( bHardRecalcState
)
315 ScFormulaCell
* pCell
= pFormulaTree
;
318 if ( pCell
->GetDirty() )
319 pCell
= pCell
->GetNext(); // alles klar
322 if ( pCell
->GetCode()->IsRecalcModeAlways() )
324 // pCell wird im SetDirty neu angehaengt!
325 ScFormulaCell
* pNext
= pCell
->GetNext();
327 // falls pNext==0 und neue abhaengige hinten angehaengt
328 // wurden, so macht das nichts, da die alle bDirty sind
332 { // andere simpel berechnen
334 pCell
->SetDirtyVar();
335 pCell
= pCell
->GetNext();
339 bool bProgress
= !bOnlyForced
&& nFormulaCodeInTree
&& bProgressBar
;
341 ScProgress::CreateInterpretProgress( this, true );
343 pCell
= pFormulaTree
;
344 ScFormulaCell
* pLastNoGood
= 0;
347 // Interpret setzt bDirty zurueck und callt Remove, auch der referierten!
348 // bei RECALCMODE_ALWAYS bleibt die Zelle
351 if ( pCell
->GetCode()->IsRecalcModeForced() )
358 if ( pCell
->GetPrevious() || pCell
== pFormulaTree
)
359 { // (IsInFormulaTree(pCell)) kein Remove gewesen => next
361 pCell
= pCell
->GetNext();
367 if ( pFormulaTree
->GetDirty() && !bOnlyForced
)
369 pCell
= pFormulaTree
;
374 // IsInFormulaTree(pLastNoGood)
375 if ( pLastNoGood
&& (pLastNoGood
->GetPrevious() ||
376 pLastNoGood
== pFormulaTree
) )
377 pCell
= pLastNoGood
->GetNext();
380 pCell
= pFormulaTree
;
381 while ( pCell
&& !pCell
->GetDirty() )
382 pCell
= pCell
->GetNext();
384 pLastNoGood
= pCell
->GetPrevious();
391 if ( ScProgress::IsUserBreak() )
395 ScProgress::DeleteInterpretProgress();
397 bAutoCalc
= bOldAutoCalc
;
398 EnableIdle(bOldIdleEnabled
);
399 bCalculatingFormulaTree
= false;
401 mpFormulaGroupCxt
.reset();
405 void ScDocument::ClearFormulaTree()
407 ScFormulaCell
* pCell
;
408 ScFormulaCell
* pTree
= pFormulaTree
;
412 pTree
= pCell
->GetNext();
413 if ( !pCell
->GetCode()->IsRecalcModeAlways() )
414 RemoveFromFormulaTree( pCell
);
419 void ScDocument::AppendToFormulaTrack( ScFormulaCell
* pCell
)
421 OSL_ENSURE( pCell
, "AppendToFormulaTrack: pCell Null" );
422 // Zelle kann nicht in beiden Listen gleichzeitig sein
423 RemoveFromFormulaTrack( pCell
);
424 RemoveFromFormulaTree( pCell
);
425 if ( pEOFormulaTrack
)
426 pEOFormulaTrack
->SetNextTrack( pCell
);
428 pFormulaTrack
= pCell
; // kein Ende, kein Anfang..
429 pCell
->SetPreviousTrack( pEOFormulaTrack
);
430 pCell
->SetNextTrack( 0 );
431 pEOFormulaTrack
= pCell
;
432 ++nFormulaTrackCount
;
436 void ScDocument::RemoveFromFormulaTrack( ScFormulaCell
* pCell
)
438 OSL_ENSURE( pCell
, "RemoveFromFormulaTrack: pCell Null" );
439 ScFormulaCell
* pPrev
= pCell
->GetPreviousTrack();
440 // wenn die Zelle die erste oder sonstwo ist
441 if ( pPrev
|| pFormulaTrack
== pCell
)
443 ScFormulaCell
* pNext
= pCell
->GetNextTrack();
445 pPrev
->SetNextTrack( pNext
); // gibt Vorlaeufer
447 pFormulaTrack
= pNext
; // ist erste Zelle
449 pNext
->SetPreviousTrack( pPrev
); // gibt Nachfolger
451 pEOFormulaTrack
= pPrev
; // ist letzte Zelle
452 pCell
->SetPreviousTrack( 0 );
453 pCell
->SetNextTrack( 0 );
454 --nFormulaTrackCount
;
459 bool ScDocument::IsInFormulaTrack( ScFormulaCell
* pCell
) const
461 return pCell
->GetPreviousTrack() || pFormulaTrack
== pCell
;
466 Der erste wird gebroadcastet,
467 die dadurch entstehenden werden durch das Notify an den Track gehaengt.
468 Der nachfolgende broadcastet wieder usw.
469 View stoesst Interpret an.
471 void ScDocument::TrackFormulas( sal_uLong nHintId
)
476 // outside the loop, check if any sheet has a "calculate" event script
477 bool bCalcEvent
= HasAnySheetEventScript( SC_SHEETEVENT_CALCULATE
, true );
479 ScFormulaCell
* pTrack
;
480 ScFormulaCell
* pNext
;
481 pTrack
= pFormulaTrack
;
484 pBC
= GetBroadcaster(pTrack
->aPos
);
485 ScHint
aHint(nHintId
, pTrack
->aPos
);
487 pBC
->Broadcast( aHint
);
488 pBASM
->AreaBroadcast( aHint
);
489 // Repaint fuer bedingte Formate mit relativen Referenzen:
490 TableContainer::iterator itr
= maTabs
.begin();
491 for(; itr
!= maTabs
.end(); ++itr
)
495 ScConditionalFormatList
* pCondFormList
= (*itr
)->GetCondFormList();
497 pCondFormList
->SourceChanged( pTrack
->aPos
);
499 // for "calculate" event, keep track of which sheets are affected by tracked formulas
501 SetCalcNotification( pTrack
->aPos
.Tab() );
502 pTrack
= pTrack
->GetNextTrack();
504 pTrack
= pFormulaTrack
;
505 bool bHaveForced
= false;
508 pNext
= pTrack
->GetNextTrack();
509 RemoveFromFormulaTrack( pTrack
);
510 PutInFormulaTree( pTrack
);
511 if ( pTrack
->GetCode()->IsRecalcModeForced() )
517 SetForcedFormulas( true );
518 if ( bAutoCalc
&& !IsAutoCalcShellDisabled() && !IsInInterpreter()
519 && !IsCalculatingFormulaTree() )
520 CalcFormulaTree( true );
522 SetForcedFormulaPending( true );
525 OSL_ENSURE( nFormulaTrackCount
==0, "TrackFormulas: nFormulaTrackCount!=0" );
529 void ScDocument::StartAllListeners()
531 for ( SCTAB i
= 0; i
< static_cast<SCTAB
>(maTabs
.size()); ++i
)
533 maTabs
[i
]->StartAllListeners();
536 void ScDocument::UpdateBroadcastAreas( UpdateRefMode eUpdateRefMode
,
537 const ScRange
& rRange
, SCsCOL nDx
, SCsROW nDy
, SCsTAB nDz
540 bool bExpandRefsOld
= IsExpandRefs();
541 if ( eUpdateRefMode
== URM_INSDEL
&& (nDx
> 0 || nDy
> 0 || nDz
> 0) )
542 SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() );
544 pBASM
->UpdateBroadcastAreas( eUpdateRefMode
, rRange
, nDx
, nDy
, nDz
);
545 SetExpandRefs( bExpandRefsOld
);
548 void ScDocument::SetAutoCalc( bool bNewAutoCalc
)
550 bool bOld
= bAutoCalc
;
551 bAutoCalc
= bNewAutoCalc
;
552 if ( !bOld
&& bNewAutoCalc
&& bHasForcedFormulas
)
554 if ( IsAutoCalcShellDisabled() )
555 SetForcedFormulaPending( true );
556 else if ( !IsInInterpreter() )
557 CalcFormulaTree( true );
563 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */