xref: /trunk/main/sc/source/core/data/documen7.cxx (revision b3f79822)
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