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