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"
40 #include <refhint.hxx>
42 #include "globstr.hrc"
44 extern const ScFormulaCell
* pLastFormulaTreeTop
; // cellform.cxx Err527 WorkAround
46 // STATIC DATA -----------------------------------------------------------
48 void ScDocument::StartListeningArea(
49 const ScRange
& rRange
, bool bGroupListening
, SvtListener
* pListener
)
52 pBASM
->StartListeningArea(rRange
, bGroupListening
, pListener
);
55 void ScDocument::EndListeningArea( const ScRange
& rRange
, bool bGroupListening
, SvtListener
* pListener
)
58 pBASM
->EndListeningArea(rRange
, bGroupListening
, pListener
);
61 void ScDocument::Broadcast( const ScHint
& rHint
)
64 return ; // Clipboard or Undo
65 if ( !bHardRecalcState
)
67 ScBulkBroadcast
aBulkBroadcast( pBASM
); // scoped bulk broadcast
68 bool bIsBroadcasted
= false;
69 SvtBroadcaster
* pBC
= GetBroadcaster(rHint
.GetAddress());
72 pBC
->Broadcast( rHint
);
73 bIsBroadcasted
= true;
75 if ( pBASM
->AreaBroadcast( rHint
) || bIsBroadcasted
)
76 TrackFormulas( rHint
.GetId() );
79 // Repaint for conditional formats with relative references:
80 for(SCTAB nTab
= 0; nTab
< static_cast<SCTAB
>(maTabs
.size()); ++nTab
)
85 ScConditionalFormatList
* pCondFormList
= GetCondFormList(nTab
);
86 if ( pCondFormList
&& rHint
.GetAddress() != BCA_BRDCST_ALWAYS
)
87 pCondFormList
->SourceChanged( rHint
.GetAddress() );
91 if ( rHint
.GetAddress() != BCA_BRDCST_ALWAYS
)
93 SCTAB nTab
= rHint
.GetAddress().Tab();
94 if (nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
] && maTabs
[nTab
]->IsStreamValid())
95 maTabs
[nTab
]->SetStreamValid(false);
99 void ScDocument::BroadcastCells( const ScRange
& rRange
, sal_uLong nHint
, bool bBroadcastSingleBroadcasters
)
101 ClearFormulaContext();
104 return; // Clipboard or Undo
106 SCTAB nTab1
= rRange
.aStart
.Tab();
107 SCTAB nTab2
= rRange
.aEnd
.Tab();
108 SCROW nRow1
= rRange
.aStart
.Row();
109 SCROW nRow2
= rRange
.aEnd
.Row();
110 SCCOL nCol1
= rRange
.aStart
.Col();
111 SCCOL nCol2
= rRange
.aEnd
.Col();
113 if (!bHardRecalcState
)
115 ScBulkBroadcast
aBulkBroadcast( pBASM
); // scoped bulk broadcast
116 bool bIsBroadcasted
= false;
118 if (bBroadcastSingleBroadcasters
)
120 ScHint
aHint(nHint
, ScAddress());
122 for (SCTAB nTab
= nTab1
; nTab
<= nTab2
; ++nTab
)
124 ScTable
* pTab
= FetchTable(nTab
);
128 bIsBroadcasted
|= pTab
->BroadcastBroadcasters( nCol1
, nRow1
, nCol2
, nRow2
, aHint
);
132 if (pBASM
->AreaBroadcast(rRange
, nHint
) || bIsBroadcasted
)
133 TrackFormulas(nHint
);
136 // Repaint for conditional formats with relative references:
137 for (SCTAB nTab
= nTab1
; nTab
<= nTab2
; ++nTab
)
139 ScTable
* pTab
= FetchTable(nTab
);
143 ScConditionalFormatList
* pCondFormList
= GetCondFormList(nTab
);
144 if (pCondFormList
&& !pCondFormList
->empty())
146 /* TODO: looping over all possible cells is a terrible bottle neck,
147 * for each cell looping over all conditional formats even worse,
148 * this certainly needs a better method. */
149 ScAddress
aAddress( 0, 0, nTab
);
150 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
152 aAddress
.SetRow(nRow
);
153 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
155 aAddress
.SetCol(nCol
);
156 pCondFormList
->SourceChanged(aAddress
);
162 for (SCTAB nTab
= nTab1
; nTab
<= nTab2
; ++nTab
)
164 ScTable
* pTab
= FetchTable(nTab
);
166 pTab
->SetStreamValid(false);
169 BroadcastUno(SfxSimpleHint(SC_HINT_DATACHANGED
));
174 class RefMovedNotifier
: std::unary_function
<SvtListener
*, void>
176 const sc::RefMovedHint
& mrHint
;
178 RefMovedNotifier( const sc::RefMovedHint
& rHint
) : mrHint(rHint
) {}
180 void operator() ( SvtListener
* p
)
188 void ScDocument::BroadcastRefMoved( const sc::RefMovedHint
& rHint
)
191 // clipboard or undo document.
194 const ScRange
& rSrcRange
= rHint
.getRange(); // old range
195 const ScAddress
& rDelta
= rHint
.getDelta();
197 // Get all area listeners that listens on the old range, and end their listening.
198 std::vector
<sc::AreaListener
> aAreaListeners
= pBASM
->GetAllListeners(rSrcRange
, sc::AreaInside
);
200 std::vector
<sc::AreaListener
>::iterator it
= aAreaListeners
.begin(), itEnd
= aAreaListeners
.end();
201 for (; it
!= itEnd
; ++it
)
203 pBASM
->EndListeningArea(it
->maArea
, it
->mbGroupListening
, it
->mpListener
);
204 it
->mpListener
->Notify(rHint
); // Adjust the references.
208 // Collect all listeners listening into the range.
209 std::vector
<SvtListener
*> aListeners
;
210 for (SCTAB nTab
= rSrcRange
.aStart
.Tab(); nTab
<= rSrcRange
.aEnd
.Tab(); ++nTab
)
212 ScTable
* pTab
= FetchTable(nTab
);
216 pTab
->CollectListeners(
218 rSrcRange
.aStart
.Col(), rSrcRange
.aStart
.Row(),
219 rSrcRange
.aEnd
.Col(), rSrcRange
.aEnd
.Row());
222 // Remove any duplicate listener entries. We must ensure that we notify
223 // each unique listener only once.
224 std::sort(aListeners
.begin(), aListeners
.end());
225 aListeners
.erase(std::unique(aListeners
.begin(), aListeners
.end()), aListeners
.end());
227 // Notify the listeners.
228 std::for_each(aListeners
.begin(), aListeners
.end(), RefMovedNotifier(rHint
));
230 for (SCTAB nTab
= rSrcRange
.aStart
.Tab(); nTab
<= rSrcRange
.aEnd
.Tab(); ++nTab
)
232 ScTable
* pTab
= FetchTable(nTab
);
236 SCTAB nDestTab
= nTab
+ rDelta
.Tab();
237 ScTable
* pDestTab
= FetchTable(nDestTab
);
241 // Move the listeners from the old location to the new.
242 pTab
->TransferListeners(
243 *pDestTab
, rSrcRange
.aStart
.Col(), rSrcRange
.aStart
.Row(),
244 rSrcRange
.aEnd
.Col(), rSrcRange
.aEnd
.Row(), rDelta
.Col(), rDelta
.Row());
247 // Re-start area listeners on the new range.
249 std::vector
<sc::AreaListener
>::iterator it
= aAreaListeners
.begin(), itEnd
= aAreaListeners
.end();
250 for (; it
!= itEnd
; ++it
)
252 ScRange aNewRange
= it
->maArea
;
253 aNewRange
.Move(rDelta
.Col(), rDelta
.Row(), rDelta
.Tab());
254 pBASM
->StartListeningArea(aNewRange
, it
->mbGroupListening
, it
->mpListener
);
259 void ScDocument::AreaBroadcast( const ScHint
& rHint
)
262 return ; // Clipboard or Undo
263 if ( !bHardRecalcState
)
265 ScBulkBroadcast
aBulkBroadcast( pBASM
); // scoped bulk broadcast
266 if ( pBASM
->AreaBroadcast( rHint
) )
267 TrackFormulas( rHint
.GetId() );
270 for(SCTAB nTab
= 0; nTab
< static_cast<SCTAB
>(maTabs
.size()); ++nTab
)
275 ScConditionalFormatList
* pCondFormList
= GetCondFormList(nTab
);
276 if ( pCondFormList
&& rHint
.GetAddress() != BCA_BRDCST_ALWAYS
)
277 pCondFormList
->SourceChanged( rHint
.GetAddress() );
281 void ScDocument::DelBroadcastAreasInRange( const ScRange
& rRange
)
284 pBASM
->DelBroadcastAreasInRange( rRange
);
287 void ScDocument::StartListeningCell( const ScAddress
& rAddress
,
288 SvtListener
* pListener
)
290 OSL_ENSURE(pListener
, "StartListeningCell: pListener Null");
291 SCTAB nTab
= rAddress
.Tab();
292 if (ValidTab(nTab
) && nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
293 maTabs
[nTab
]->StartListening( rAddress
, pListener
);
296 void ScDocument::EndListeningCell( const ScAddress
& rAddress
,
297 SvtListener
* pListener
)
299 OSL_ENSURE(pListener
, "EndListeningCell: pListener Null");
300 SCTAB nTab
= rAddress
.Tab();
301 if (ValidTab(nTab
) && nTab
< static_cast<SCTAB
>(maTabs
.size()) && maTabs
[nTab
])
302 maTabs
[nTab
]->EndListening( rAddress
, pListener
);
305 void ScDocument::StartListeningCell(
306 sc::StartListeningContext
& rCxt
, const ScAddress
& rPos
, SvtListener
& rListener
)
308 ScTable
* pTab
= FetchTable(rPos
.Tab());
312 pTab
->StartListening(rCxt
, rPos
.Col(), rPos
.Row(), rListener
);
315 void ScDocument::EndListeningCell(
316 sc::EndListeningContext
& rCxt
, const ScAddress
& rPos
, SvtListener
& rListener
)
318 ScTable
* pTab
= FetchTable(rPos
.Tab());
322 pTab
->EndListening(rCxt
, rPos
.Col(), rPos
.Row(), rListener
);
325 void ScDocument::EndListeningFormulaCells( std::vector
<ScFormulaCell
*>& rCells
)
330 sc::EndListeningContext
aCxt(*this);
331 std::vector
<ScFormulaCell
*>::iterator it
= rCells
.begin(), itEnd
= rCells
.end();
332 for (; it
!= itEnd
; ++it
)
333 (*it
)->EndListeningTo(aCxt
);
335 aCxt
.purgeEmptyBroadcasters();
338 void ScDocument::PutInFormulaTree( ScFormulaCell
* pCell
)
340 OSL_ENSURE( pCell
, "PutInFormulaTree: pCell Null" );
341 RemoveFromFormulaTree( pCell
);
343 if ( pEOFormulaTree
)
344 pEOFormulaTree
->SetNext( pCell
);
346 pFormulaTree
= pCell
; // No end, no beginning..
347 pCell
->SetPrevious( pEOFormulaTree
);
349 pEOFormulaTree
= pCell
;
350 nFormulaCodeInTree
+= pCell
->GetCode()->GetCodeLen();
353 void ScDocument::RemoveFromFormulaTree( ScFormulaCell
* pCell
)
355 OSL_ENSURE( pCell
, "RemoveFromFormulaTree: pCell Null" );
356 ScFormulaCell
* pPrev
= pCell
->GetPrevious();
357 assert(pPrev
!= pCell
); // pointing to itself?!?
358 // if the cell is first or somewhere in chain
359 if ( pPrev
|| pFormulaTree
== pCell
)
361 ScFormulaCell
* pNext
= pCell
->GetNext();
362 assert(pNext
!= pCell
); // pointing to itself?!?
365 assert(pFormulaTree
!= pCell
); // if this cell is also head something's wrong
366 pPrev
->SetNext( pNext
); // predecessor exists, set successor
370 pFormulaTree
= pNext
; // this cell was first cell
374 assert(pEOFormulaTree
!= pCell
); // if this cell is also tail something's wrong
375 pNext
->SetPrevious( pPrev
); // successor exists, set predecessor
379 pEOFormulaTree
= pPrev
; // this cell was last cell
381 pCell
->SetPrevious( 0 );
383 sal_uInt16 nRPN
= pCell
->GetCode()->GetCodeLen();
384 if ( nFormulaCodeInTree
>= nRPN
)
385 nFormulaCodeInTree
-= nRPN
;
388 OSL_FAIL( "RemoveFromFormulaTree: nFormulaCodeInTree < nRPN" );
389 nFormulaCodeInTree
= 0;
392 else if ( !pFormulaTree
&& nFormulaCodeInTree
)
394 OSL_FAIL( "!pFormulaTree && nFormulaCodeInTree != 0" );
395 nFormulaCodeInTree
= 0;
399 bool ScDocument::IsInFormulaTree( ScFormulaCell
* pCell
) const
401 return pCell
->GetPrevious() || pFormulaTree
== pCell
;
404 void ScDocument::CalcFormulaTree( bool bOnlyForced
, bool bProgressBar
, bool bSetAllDirty
)
406 OSL_ENSURE( !IsCalculatingFormulaTree(), "CalcFormulaTree recursion" );
407 // never ever recurse into this, might end up lost in infinity
408 if ( IsCalculatingFormulaTree() )
411 mpFormulaGroupCxt
.reset();
412 bCalculatingFormulaTree
= true;
414 SetForcedFormulaPending( false );
415 bool bOldIdleEnabled
= IsIdleEnabled();
417 bool bOldAutoCalc
= GetAutoCalc();
418 //ATTENTION: _not_ SetAutoCalc( true ) because this might call CalcFormulaTree( true )
419 //ATTENTION: if it was disabled before and bHasForcedFormulas is set
421 if ( bHardRecalcState
)
425 ::std::vector
<ScFormulaCell
*> vAlwaysDirty
;
426 ScFormulaCell
* pCell
= pFormulaTree
;
429 if ( pCell
->GetDirty() )
430 pCell
= pCell
->GetNext(); // all clear
433 if ( pCell
->GetCode()->IsRecalcModeAlways() )
435 // pCell and dependents are to be set dirty again, collect
436 // them first and broadcast afterwards to not break the
437 // FormulaTree chain here.
438 vAlwaysDirty
.push_back( pCell
);
439 pCell
= pCell
->GetNext();
442 { // calculate the other single
444 pCell
->SetDirtyVar();
445 pCell
= pCell
->GetNext();
449 for (::std::vector
<ScFormulaCell
*>::iterator
it( vAlwaysDirty
.begin()), itEnd( vAlwaysDirty
.end());
453 if (!pCell
->GetDirty())
457 bool bProgress
= !bOnlyForced
&& nFormulaCodeInTree
&& bProgressBar
;
459 ScProgress::CreateInterpretProgress( this, true );
461 pCell
= pFormulaTree
;
462 ScFormulaCell
* pLastNoGood
= 0;
465 // Interpret resets bDirty and calls Remove, also the referenced!
466 // the Cell remains when ScRecalcMode::ALWAYS.
469 if ( pCell
->GetCode()->IsRecalcModeForced() )
476 if ( pCell
->GetPrevious() || pCell
== pFormulaTree
)
477 { // (IsInFormulaTree(pCell)) no Remove was called => next
479 pCell
= pCell
->GetNext();
485 if ( pFormulaTree
->GetDirty() && !bOnlyForced
)
487 pCell
= pFormulaTree
;
492 // IsInFormulaTree(pLastNoGood)
493 if ( pLastNoGood
&& (pLastNoGood
->GetPrevious() ||
494 pLastNoGood
== pFormulaTree
) )
495 pCell
= pLastNoGood
->GetNext();
498 pCell
= pFormulaTree
;
499 while ( pCell
&& !pCell
->GetDirty() )
500 pCell
= pCell
->GetNext();
502 pLastNoGood
= pCell
->GetPrevious();
509 if ( ScProgress::IsUserBreak() )
513 ScProgress::DeleteInterpretProgress();
515 bAutoCalc
= bOldAutoCalc
;
516 EnableIdle(bOldIdleEnabled
);
517 bCalculatingFormulaTree
= false;
519 mpFormulaGroupCxt
.reset();
522 void ScDocument::ClearFormulaTree()
524 ScFormulaCell
* pCell
;
525 ScFormulaCell
* pTree
= pFormulaTree
;
529 pTree
= pCell
->GetNext();
530 if ( !pCell
->GetCode()->IsRecalcModeAlways() )
531 RemoveFromFormulaTree( pCell
);
535 void ScDocument::AppendToFormulaTrack( ScFormulaCell
* pCell
)
537 OSL_ENSURE( pCell
, "AppendToFormulaTrack: pCell Null" );
538 // The cell can not be in both lists at the same time
539 RemoveFromFormulaTrack( pCell
);
540 RemoveFromFormulaTree( pCell
);
541 if ( pEOFormulaTrack
)
542 pEOFormulaTrack
->SetNextTrack( pCell
);
544 pFormulaTrack
= pCell
; // No end, no beginning..
545 pCell
->SetPreviousTrack( pEOFormulaTrack
);
546 pCell
->SetNextTrack( 0 );
547 pEOFormulaTrack
= pCell
;
548 ++nFormulaTrackCount
;
551 void ScDocument::RemoveFromFormulaTrack( ScFormulaCell
* pCell
)
553 OSL_ENSURE( pCell
, "RemoveFromFormulaTrack: pCell Null" );
554 ScFormulaCell
* pPrev
= pCell
->GetPreviousTrack();
555 assert(pPrev
!= pCell
); // pointing to itself?!?
556 // if the cell is first or somewhere in chain
557 if ( pPrev
|| pFormulaTrack
== pCell
)
559 ScFormulaCell
* pNext
= pCell
->GetNextTrack();
560 assert(pNext
!= pCell
); // pointing to itself?!?
563 assert(pFormulaTrack
!= pCell
); // if this cell is also head something's wrong
564 pPrev
->SetNextTrack( pNext
); // predecessor exists, set successor
568 pFormulaTrack
= pNext
; // this cell was first cell
572 assert(pEOFormulaTrack
!= pCell
); // if this cell is also tail something's wrong
573 pNext
->SetPreviousTrack( pPrev
); // successor exists, set predecessor
577 pEOFormulaTrack
= pPrev
; // this cell was last cell
579 pCell
->SetPreviousTrack( 0 );
580 pCell
->SetNextTrack( 0 );
581 --nFormulaTrackCount
;
585 bool ScDocument::IsInFormulaTrack( ScFormulaCell
* pCell
) const
587 return pCell
->GetPreviousTrack() || pFormulaTrack
== pCell
;
591 The first is broadcasted,
592 the ones that are created through this are appended to the Track by Notify.
593 The next is broadcasted again, and so on.
594 View initiates Interpret.
596 void ScDocument::TrackFormulas( sal_uLong nHintId
)
601 // outside the loop, check if any sheet has a "calculate" event script
602 bool bCalcEvent
= HasAnySheetEventScript( SC_SHEETEVENT_CALCULATE
, true );
603 ScFormulaCell
* pTrack
;
604 ScFormulaCell
* pNext
;
605 pTrack
= pFormulaTrack
;
608 SvtBroadcaster
* pBC
= GetBroadcaster(pTrack
->aPos
);
609 ScHint
aHint(nHintId
, pTrack
->aPos
);
611 pBC
->Broadcast( aHint
);
612 pBASM
->AreaBroadcast( aHint
);
613 // Repaint for conditional formats with relative references:
614 TableContainer::iterator itr
= maTabs
.begin();
615 for(; itr
!= maTabs
.end(); ++itr
)
619 ScConditionalFormatList
* pCondFormList
= (*itr
)->GetCondFormList();
621 pCondFormList
->SourceChanged( pTrack
->aPos
);
623 // for "calculate" event, keep track of which sheets are affected by tracked formulas
625 SetCalcNotification( pTrack
->aPos
.Tab() );
626 pTrack
= pTrack
->GetNextTrack();
628 pTrack
= pFormulaTrack
;
629 bool bHaveForced
= false;
632 pNext
= pTrack
->GetNextTrack();
633 RemoveFromFormulaTrack( pTrack
);
634 PutInFormulaTree( pTrack
);
635 if ( pTrack
->GetCode()->IsRecalcModeForced() )
641 SetForcedFormulas( true );
642 if ( bAutoCalc
&& !IsAutoCalcShellDisabled() && !IsInInterpreter()
643 && !IsCalculatingFormulaTree() )
644 CalcFormulaTree( true );
646 SetForcedFormulaPending( true );
649 OSL_ENSURE( nFormulaTrackCount
==0, "TrackFormulas: nFormulaTrackCount!=0" );
652 void ScDocument::StartAllListeners()
654 sc::StartListeningContext
aCxt(*this);
655 for ( SCTAB i
= 0; i
< static_cast<SCTAB
>(maTabs
.size()); ++i
)
657 maTabs
[i
]->StartListeners(aCxt
, true);
660 void ScDocument::UpdateBroadcastAreas( UpdateRefMode eUpdateRefMode
,
661 const ScRange
& rRange
, SCsCOL nDx
, SCsROW nDy
, SCsTAB nDz
664 bool bExpandRefsOld
= IsExpandRefs();
665 if ( eUpdateRefMode
== URM_INSDEL
&& (nDx
> 0 || nDy
> 0 || nDz
> 0) )
666 SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() );
668 pBASM
->UpdateBroadcastAreas( eUpdateRefMode
, rRange
, nDx
, nDy
, nDz
);
669 SetExpandRefs( bExpandRefsOld
);
672 void ScDocument::SetAutoCalc( bool bNewAutoCalc
)
674 bool bOld
= bAutoCalc
;
675 bAutoCalc
= bNewAutoCalc
;
676 if ( !bOld
&& bNewAutoCalc
&& bHasForcedFormulas
)
678 if ( IsAutoCalcShellDisabled() )
679 SetForcedFormulaPending( true );
680 else if ( !IsInInterpreter() )
681 CalcFormulaTree( true );
685 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */