xref: /trunk/main/sc/source/core/tool/chartlis.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 
32 
33 #include <vcl/svapp.hxx>
34 
35 #include "chartlis.hxx"
36 #include "brdcst.hxx"
37 #include "document.hxx"
38 #include "reftokenhelper.hxx"
39 
40 using namespace com::sun::star;
41 using ::std::vector;
42 using ::std::list;
43 using ::std::hash_set;
44 using ::std::auto_ptr;
45 using ::std::unary_function;
46 using ::std::for_each;
47 
48 //2do: DocOption TimeOut?
49 //#define SC_CHARTTIMEOUT 1000		// eine Sekunde keine Aenderung/KeyEvent
50 
51 // Update chart listeners quickly, to get a similar behavior to loaded charts
52 // which register UNO listeners.
53 #define SC_CHARTTIMEOUT 10
54 
55 
56 // ====================================================================
57 
58 class ScChartUnoData
59 {
60 	uno::Reference< chart::XChartDataChangeEventListener >	xListener;
61 	uno::Reference< chart::XChartData >						xSource;
62 
63 public:
64 			ScChartUnoData( const uno::Reference< chart::XChartDataChangeEventListener >& rL,
65 							const uno::Reference< chart::XChartData >& rS ) :
66 					xListener( rL ), xSource( rS ) {}
67 			~ScChartUnoData() {}
68 
69 	const uno::Reference< chart::XChartDataChangeEventListener >& GetListener() const	{ return xListener; }
70 	const uno::Reference< chart::XChartData >& GetSource() const						{ return xSource; }
71 };
72 
73 
74 // === ScChartListener ================================================
75 
76 ScChartListener::ExternalRefListener::ExternalRefListener(ScChartListener& rParent, ScDocument* pDoc) :
77     mrParent(rParent), mpDoc(pDoc)
78 {
79 }
80 
81 ScChartListener::ExternalRefListener::~ExternalRefListener()
82 {
83     if (!mpDoc || mpDoc->IsInDtorClear())
84         // The document is being destroyed.  Do nothing.
85         return;
86 
87     // Make sure to remove all pointers to this object.
88     mpDoc->GetExternalRefManager()->removeLinkListener(this);
89 }
90 
91 void ScChartListener::ExternalRefListener::notify(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType)
92 {
93     switch (eType)
94     {
95         case ScExternalRefManager::LINK_MODIFIED:
96         {
97             if (maFileIds.count(nFileId))
98                 // We are listening to this external document.  Send an update
99                 // requst to the chart.
100                 mrParent.SetUpdateQueue();
101         }
102         break;
103         case ScExternalRefManager::LINK_BROKEN:
104             removeFileId(nFileId);
105         break;
106     }
107 }
108 
109 void ScChartListener::ExternalRefListener::addFileId(sal_uInt16 nFileId)
110 {
111     maFileIds.insert(nFileId);
112 }
113 
114 void ScChartListener::ExternalRefListener::removeFileId(sal_uInt16 nFileId)
115 {
116     maFileIds.erase(nFileId);
117 }
118 
119 hash_set<sal_uInt16>& ScChartListener::ExternalRefListener::getAllFileIds()
120 {
121     return maFileIds;
122 }
123 
124 // ----------------------------------------------------------------------------
125 
126 ScChartListener::ScChartListener( const String& rName, ScDocument* pDocP,
127         const ScRange& rRange ) :
128     StrData( rName ),
129     SvtListener(),
130     mpExtRefListener(NULL),
131     mpTokens(new vector<ScSharedTokenRef>),
132     pUnoData( NULL ),
133     pDoc( pDocP ),
134     bUsed( sal_False ),
135     bDirty( sal_False ),
136     bSeriesRangesScheduled( sal_False )
137 {
138     SetRangeList( rRange );
139 }
140 
141 ScChartListener::ScChartListener( const String& rName, ScDocument* pDocP,
142         const ScRangeListRef& rRangeList ) :
143     StrData( rName ),
144     SvtListener(),
145     mpExtRefListener(NULL),
146     mpTokens(new vector<ScSharedTokenRef>),
147     pUnoData( NULL ),
148     pDoc( pDocP ),
149     bUsed( sal_False ),
150     bDirty( sal_False ),
151     bSeriesRangesScheduled( sal_False )
152 {
153     ScRefTokenHelper::getTokensFromRangeList(*mpTokens, *rRangeList);
154 }
155 
156 ScChartListener::ScChartListener( const String& rName, ScDocument* pDocP, vector<ScSharedTokenRef>* pTokens ) :
157     StrData( rName ),
158     SvtListener(),
159     mpExtRefListener(NULL),
160     mpTokens(pTokens),
161     pUnoData( NULL ),
162     pDoc( pDocP ),
163     bUsed( sal_False ),
164     bDirty( sal_False ),
165     bSeriesRangesScheduled( sal_False )
166 {
167 }
168 
169 ScChartListener::ScChartListener( const ScChartListener& r ) :
170     StrData( r ),
171     SvtListener(),
172     mpExtRefListener(NULL),
173     mpTokens(new vector<ScSharedTokenRef>(*r.mpTokens)),
174     pUnoData( NULL ),
175     pDoc( r.pDoc ),
176     bUsed( sal_False ),
177     bDirty( r.bDirty ),
178     bSeriesRangesScheduled( r.bSeriesRangesScheduled )
179 {
180     if ( r.pUnoData )
181         pUnoData = new ScChartUnoData( *r.pUnoData );
182 
183     if (r.mpExtRefListener.get())
184     {
185         // Re-register this new listener for the files that the old listener
186         // was listening to.
187 
188         ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
189         const hash_set<sal_uInt16>& rFileIds = r.mpExtRefListener->getAllFileIds();
190         mpExtRefListener.reset(new ExternalRefListener(*this, pDoc));
191         hash_set<sal_uInt16>::const_iterator itr = rFileIds.begin(), itrEnd = rFileIds.end();
192         for (; itr != itrEnd; ++itr)
193         {
194             pRefMgr->addLinkListener(*itr, mpExtRefListener.get());
195             mpExtRefListener->addFileId(*itr);
196         }
197     }
198 }
199 
200 ScChartListener::~ScChartListener()
201 {
202 	if ( HasBroadcaster() )
203 		EndListeningTo();
204 	delete pUnoData;
205 
206     if (mpExtRefListener.get())
207     {
208         // Stop listening to all external files.
209         ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
210         const hash_set<sal_uInt16>& rFileIds = mpExtRefListener->getAllFileIds();
211         hash_set<sal_uInt16>::const_iterator itr = rFileIds.begin(), itrEnd = rFileIds.end();
212         for (; itr != itrEnd; ++itr)
213             pRefMgr->removeLinkListener(*itr, mpExtRefListener.get());
214     }
215 }
216 
217 ScDataObject* ScChartListener::Clone() const
218 {
219 	return new ScChartListener( *this );
220 }
221 
222 void ScChartListener::SetUno(
223 		const uno::Reference< chart::XChartDataChangeEventListener >& rListener,
224 		const uno::Reference< chart::XChartData >& rSource )
225 {
226 //	DBG_ASSERT( rListener.is() && rSource.is(), "Nullpointer bei SetUno" );
227 	delete pUnoData;
228 	pUnoData = new ScChartUnoData( rListener, rSource );
229 }
230 
231 uno::Reference< chart::XChartDataChangeEventListener > ScChartListener::GetUnoListener() const
232 {
233 	if ( pUnoData )
234 		return pUnoData->GetListener();
235 	return uno::Reference< chart::XChartDataChangeEventListener >();
236 }
237 
238 uno::Reference< chart::XChartData > ScChartListener::GetUnoSource() const
239 {
240 	if ( pUnoData )
241 		return pUnoData->GetSource();
242 	return uno::Reference< chart::XChartData >();
243 }
244 
245 void ScChartListener::Notify( SvtBroadcaster&, const SfxHint& rHint )
246 {
247     const ScHint* p = dynamic_cast<const ScHint*>(&rHint);
248     if (p && (p->GetId() & (SC_HINT_DATACHANGED | SC_HINT_DYING)))
249         SetUpdateQueue();
250 }
251 
252 void ScChartListener::Update()
253 {
254 	if ( pDoc->IsInInterpreter() )
255 	{	// #73482# If interpreting do nothing and restart timer so we don't
256 		// interfere with interpreter and don't produce an Err522 or similar.
257 		// This may happen if we are rescheduled via Basic function.
258 		pDoc->GetChartListenerCollection()->StartTimer();
259 		return ;
260 	}
261 	if ( pUnoData )
262 	{
263 		bDirty = sal_False;
264 		//!	irgendwann mal erkennen, was sich innerhalb des Charts geaendert hat
265 		chart::ChartDataChangeEvent aEvent( pUnoData->GetSource(),
266 										chart::ChartDataChangeType_ALL,
267 										0, 0, 0, 0 );
268 		pUnoData->GetListener()->chartDataChanged( aEvent );
269 	}
270 	else if ( pDoc->GetAutoCalc() )
271 	{
272 		bDirty = sal_False;
273 		pDoc->UpdateChart( GetString());
274 	}
275 }
276 
277 ScRangeListRef ScChartListener::GetRangeList() const
278 {
279     ScRangeListRef aRLRef(new ScRangeList);
280     ScRefTokenHelper::getRangeListFromTokens(*aRLRef, *mpTokens);
281     return aRLRef;
282 }
283 
284 void ScChartListener::SetRangeList( const ScRangeListRef& rNew )
285 {
286     vector<ScSharedTokenRef> aTokens;
287     ScRefTokenHelper::getTokensFromRangeList(aTokens, *rNew);
288     mpTokens->swap(aTokens);
289 }
290 
291 void ScChartListener::SetRangeList( const ScRange& rRange )
292 {
293     ScSharedTokenRef pToken;
294     ScRefTokenHelper::getTokenFromRange(pToken, rRange);
295     mpTokens->push_back(pToken);
296 }
297 
298 namespace {
299 
300 class StartEndListening : public unary_function<ScSharedTokenRef, void>
301 {
302 public:
303     StartEndListening(ScDocument* pDoc, ScChartListener& rParent, bool bStart) :
304         mpDoc(pDoc), mrParent(rParent), mbStart(bStart) {}
305 
306     void operator() (const ScSharedTokenRef& pToken)
307     {
308         if (!ScRefTokenHelper::isRef(pToken))
309             return;
310 
311         bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
312         if (bExternal)
313         {
314             sal_uInt16 nFileId = pToken->GetIndex();
315             ScExternalRefManager* pRefMgr = mpDoc->GetExternalRefManager();
316             ScChartListener::ExternalRefListener* pExtRefListener = mrParent.GetExtRefListener();
317             if (mbStart)
318             {
319                 pRefMgr->addLinkListener(nFileId, pExtRefListener);
320                 pExtRefListener->addFileId(nFileId);
321             }
322             else
323             {
324                 pRefMgr->removeLinkListener(nFileId, pExtRefListener);
325                 pExtRefListener->removeFileId(nFileId);
326             }
327         }
328         else
329         {
330             ScRange aRange;
331             ScRefTokenHelper::getRangeFromToken(aRange, pToken, bExternal);
332             if (mbStart)
333                 startListening(aRange);
334             else
335                 endListening(aRange);
336         }
337     }
338 
339 private:
340     void startListening(const ScRange& rRange)
341     {
342         if (rRange.aStart == rRange.aEnd)
343             mpDoc->StartListeningCell(rRange.aStart, &mrParent);
344         else
345             mpDoc->StartListeningArea(rRange, &mrParent);
346     }
347 
348     void endListening(const ScRange& rRange)
349     {
350         if (rRange.aStart == rRange.aEnd)
351             mpDoc->EndListeningCell(rRange.aStart, &mrParent);
352         else
353             mpDoc->EndListeningArea(rRange, &mrParent);
354     }
355 
356 private:
357     ScDocument* mpDoc;
358     ScChartListener& mrParent;
359     bool mbStart;
360 };
361 
362 }
363 
364 void ScChartListener::StartListeningTo()
365 {
366     if (!mpTokens.get() || mpTokens->empty())
367         // no references to listen to.
368         return;
369 
370     for_each(mpTokens->begin(), mpTokens->end(), StartEndListening(pDoc, *this, true));
371 }
372 
373 void ScChartListener::EndListeningTo()
374 {
375     if (!mpTokens.get() || mpTokens->empty())
376         // no references to listen to.
377         return;
378 
379     for_each(mpTokens->begin(), mpTokens->end(), StartEndListening(pDoc, *this, false));
380 }
381 
382 
383 void ScChartListener::ChangeListening( const ScRangeListRef& rRangeListRef,
384 			sal_Bool bDirtyP  )
385 {
386 	EndListeningTo();
387 	SetRangeList( rRangeListRef );
388 	StartListeningTo();
389 	if ( bDirtyP )
390 		SetDirty( sal_True );
391 }
392 
393 
394 void ScChartListener::UpdateScheduledSeriesRanges()
395 {
396 	if ( bSeriesRangesScheduled )
397 	{
398 		bSeriesRangesScheduled = sal_False;
399 		UpdateSeriesRanges();
400 	}
401 }
402 
403 
404 void ScChartListener::UpdateChartIntersecting( const ScRange& rRange )
405 {
406     ScSharedTokenRef pToken;
407     ScRefTokenHelper::getTokenFromRange(pToken, rRange);
408 
409     if (ScRefTokenHelper::intersects(*mpTokens, pToken))
410     {
411         // force update (chart has to be loaded), don't use ScChartListener::Update
412         pDoc->UpdateChart( GetString());
413     }
414 }
415 
416 
417 void ScChartListener::UpdateSeriesRanges()
418 {
419     ScRangeListRef pRangeList(new ScRangeList);
420     ScRefTokenHelper::getRangeListFromTokens(*pRangeList, *mpTokens);
421     pDoc->SetChartRangeList(GetString(), pRangeList);
422 }
423 
424 ScChartListener::ExternalRefListener* ScChartListener::GetExtRefListener()
425 {
426     if (!mpExtRefListener.get())
427         mpExtRefListener.reset(new ExternalRefListener(*this, pDoc));
428 
429     return mpExtRefListener.get();
430 }
431 
432 void ScChartListener::SetUpdateQueue()
433 {
434     bDirty = true;
435     pDoc->GetChartListenerCollection()->StartTimer();
436 }
437 
438 sal_Bool ScChartListener::operator==( const ScChartListener& r )
439 {
440     bool b1 = (mpTokens.get() && !mpTokens->empty());
441     bool b2 = (r.mpTokens.get() && !r.mpTokens->empty());
442 
443     if (pDoc != r.pDoc || bUsed != r.bUsed || bDirty != r.bDirty ||
444         bSeriesRangesScheduled != r.bSeriesRangesScheduled ||
445         GetString() != r.GetString() || b1 != b2)
446         return false;
447 
448     if (!b1 && !b2)
449         // both token list instances are empty.
450         return true;
451 
452     return *mpTokens == *r.mpTokens;
453 }
454 
455 // ============================================================================
456 
457 ScChartHiddenRangeListener::ScChartHiddenRangeListener()
458 {
459 }
460 
461 ScChartHiddenRangeListener::~ScChartHiddenRangeListener()
462 {
463     // empty d'tor
464 }
465 
466 // === ScChartListenerCollection ======================================
467 
468 ScChartListenerCollection::RangeListenerItem::RangeListenerItem(const ScRange& rRange, ScChartHiddenRangeListener* p) :
469     maRange(rRange), mpListener(p)
470 {
471 }
472 
473 ScChartListenerCollection::ScChartListenerCollection( ScDocument* pDocP ) :
474 	ScStrCollection( 4, 4, sal_False ),
475 	pDoc( pDocP )
476 {
477 	aTimer.SetTimeoutHdl( LINK( this, ScChartListenerCollection, TimerHdl ) );
478 }
479 
480 ScChartListenerCollection::ScChartListenerCollection(
481 		const ScChartListenerCollection& rColl ) :
482 	ScStrCollection( rColl ),
483 	pDoc( rColl.pDoc )
484 {
485 	aTimer.SetTimeoutHdl( LINK( this, ScChartListenerCollection, TimerHdl ) );
486 }
487 
488 ScChartListenerCollection::~ScChartListenerCollection()
489 {
490 	//	#96783# remove ChartListener objects before aTimer dtor is called, because
491 	//	ScChartListener::EndListeningTo may cause ScChartListenerCollection::StartTimer
492 	//	to be called if an empty ScNoteCell is deleted
493 
494 	if (GetCount())
495 		FreeAll();
496 }
497 
498 ScDataObject*	ScChartListenerCollection::Clone() const
499 {
500 	return new ScChartListenerCollection( *this );
501 }
502 
503 void ScChartListenerCollection::StartAllListeners()
504 {
505 	for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ )
506 	{
507 		((ScChartListener*) pItems[ nIndex ])->StartListeningTo();
508 	}
509 }
510 
511 void ScChartListenerCollection::ChangeListening( const String& rName,
512 		const ScRangeListRef& rRangeListRef, sal_Bool bDirty )
513 {
514 	ScChartListener aCLSearcher( rName, pDoc, rRangeListRef );
515 	ScChartListener* pCL;
516 	sal_uInt16 nIndex;
517 	if ( Search( &aCLSearcher, nIndex ) )
518 	{
519 		pCL = (ScChartListener*) pItems[ nIndex ];
520 		pCL->EndListeningTo();
521 		pCL->SetRangeList( rRangeListRef );
522 	}
523 	else
524 	{
525 		pCL = new ScChartListener( aCLSearcher );
526 		Insert( pCL );
527 	}
528 	pCL->StartListeningTo();
529 	if ( bDirty )
530 		pCL->SetDirty( sal_True );
531 }
532 
533 void ScChartListenerCollection::FreeUnused()
534 {
535 	// rueckwaerts wg. Pointer-Aufrueckerei im Array
536 	for ( sal_uInt16 nIndex = nCount; nIndex-- >0; )
537 	{
538 		ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ];
539 		//	Uno-Charts nicht rauskicken
540 		//	(werden per FreeUno von aussen geloescht)
541 		if ( !pCL->IsUno() )
542 		{
543 			if ( pCL->IsUsed() )
544 				pCL->SetUsed( sal_False );
545 			else
546 				Free( pCL );
547 		}
548 	}
549 }
550 
551 void ScChartListenerCollection::FreeUno( const uno::Reference< chart::XChartDataChangeEventListener >& rListener,
552 										 const uno::Reference< chart::XChartData >& rSource )
553 {
554 	// rueckwaerts wg. Pointer-Aufrueckerei im Array
555 	for ( sal_uInt16 nIndex = nCount; nIndex-- >0; )
556 	{
557 		ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ];
558 		if ( pCL->IsUno() &&
559 			 pCL->GetUnoListener() == rListener &&
560 			 pCL->GetUnoSource() == rSource )
561 		{
562 			Free( pCL );
563 		}
564 		//!	sollte nur einmal vorkommen?
565 	}
566 }
567 
568 void ScChartListenerCollection::StartTimer()
569 {
570 	aTimer.SetTimeout( SC_CHARTTIMEOUT );
571 	aTimer.Start();
572 }
573 
574 IMPL_LINK( ScChartListenerCollection, TimerHdl, Timer*, EMPTYARG )
575 {
576 	if ( Application::AnyInput( INPUT_KEYBOARD ) )
577 	{
578 		aTimer.Start();
579 		return 0;
580 	}
581 	UpdateDirtyCharts();
582 	return 0;
583 }
584 
585 void ScChartListenerCollection::UpdateDirtyCharts()
586 {
587 	for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ )
588 	{
589 		ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ];
590 		if ( pCL->IsDirty() )
591 			pCL->Update();
592 		if ( aTimer.IsActive() && !pDoc->IsImportingXML())
593 			break;						// da kam einer dazwischen
594 	}
595 }
596 
597 
598 void ScChartListenerCollection::SetDirty()
599 {
600 	for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ )
601 	{
602 		ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ];
603 		pCL->SetDirty( sal_True );
604 	}
605 	StartTimer();
606 }
607 
608 
609 void ScChartListenerCollection::SetDiffDirty(
610 			const ScChartListenerCollection& rCmp, sal_Bool bSetChartRangeLists )
611 {
612 	sal_Bool bDirty = sal_False;
613 	for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ )
614 	{
615 		ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ];
616 		sal_uInt16 nFound;
617 		sal_Bool bFound = rCmp.Search( pCL, nFound );
618 		if ( !bFound ||	(*pCL != *((const ScChartListener*) rCmp.pItems[ nFound ])) )
619 		{
620 			if ( bSetChartRangeLists )
621 			{
622 				if ( bFound )
623 				{
624 					const ScRangeListRef& rList1 = pCL->GetRangeList();
625 					const ScRangeListRef& rList2 =
626 						((const ScChartListener*) rCmp.pItems[ nFound ])->GetRangeList();
627 					sal_Bool b1 = rList1.Is();
628 					sal_Bool b2 = rList2.Is();
629 					if ( b1 != b2 || (b1 && b2 && (*rList1 != *rList2)) )
630 						pDoc->SetChartRangeList( pCL->GetString(), rList1 );
631 				}
632 				else
633 					pDoc->SetChartRangeList( pCL->GetString(), pCL->GetRangeList() );
634 			}
635 			bDirty = sal_True;
636 			pCL->SetDirty( sal_True );
637 		}
638 	}
639 	if ( bDirty )
640 		StartTimer();
641 }
642 
643 
644 void ScChartListenerCollection::SetRangeDirty( const ScRange& rRange )
645 {
646 	sal_Bool bDirty = sal_False;
647 	for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ )
648 	{
649 		ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ];
650 		const ScRangeListRef& rList = pCL->GetRangeList();
651 		if ( rList.Is() && rList->Intersects( rRange ) )
652 		{
653 			bDirty = sal_True;
654 			pCL->SetDirty( sal_True );
655 		}
656 	}
657 	if ( bDirty )
658 		StartTimer();
659 
660     // New hidden range listener implementation
661     for (list<RangeListenerItem>::iterator itr = maHiddenListeners.begin(), itrEnd = maHiddenListeners.end();
662           itr != itrEnd; ++itr)
663     {
664         if (itr->maRange.Intersects(rRange))
665             itr->mpListener->notify();
666     }
667 }
668 
669 
670 void ScChartListenerCollection::UpdateScheduledSeriesRanges()
671 {
672 	for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ )
673 	{
674 		ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ];
675 		pCL->UpdateScheduledSeriesRanges();
676 	}
677 }
678 
679 
680 void ScChartListenerCollection::UpdateChartsContainingTab( SCTAB nTab )
681 {
682 	ScRange aRange( 0, 0, nTab, MAXCOL, MAXROW, nTab );
683 	for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ )
684 	{
685 		ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ];
686 		pCL->UpdateChartIntersecting( aRange );
687 	}
688 }
689 
690 
691 sal_Bool ScChartListenerCollection::operator==( const ScChartListenerCollection& r )
692 {
693 	// hier nicht ScStrCollection::operator==() verwenden, der umstaendlich via
694 	// IsEqual und Compare laeuft, stattdessen ScChartListener::operator==()
695 	if ( pDoc != r.pDoc || nCount != r.nCount )
696 		return sal_False;
697 	for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ )
698 	{
699 		if ( *((ScChartListener*) pItems[ nIndex ]) !=
700 				*((ScChartListener*) r.pItems[ nIndex ]) )
701 			return sal_False;
702 	}
703 	return sal_True;
704 }
705 
706 void ScChartListenerCollection::StartListeningHiddenRange( const ScRange& rRange, ScChartHiddenRangeListener* pListener )
707 {
708     RangeListenerItem aItem(rRange, pListener);
709     maHiddenListeners.push_back(aItem);
710 }
711 
712 namespace {
713 
714 struct MatchListener : public ::std::unary_function<
715         ScChartListenerCollection::RangeListenerItem, bool>
716 {
717     MatchListener(const ScChartHiddenRangeListener* pMatch) :
718         mpMatch(pMatch)
719     {
720     }
721 
722     bool operator() (const ScChartListenerCollection::RangeListenerItem& rItem) const
723     {
724         return mpMatch == rItem.mpListener;
725     }
726 
727 private:
728     const ScChartHiddenRangeListener* mpMatch;
729 };
730 
731 }
732 void ScChartListenerCollection::EndListeningHiddenRange( ScChartHiddenRangeListener* pListener )
733 {
734     maHiddenListeners.remove_if(MatchListener(pListener));
735 }
736 
737