1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: chartlis.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
36 #include <vcl/svapp.hxx>
38 #include "chartlis.hxx"
40 #include "document.hxx"
41 #include "reftokenhelper.hxx"
43 using namespace com::sun::star
;
46 using ::std::hash_set
;
47 using ::std::auto_ptr
;
48 using ::std::unary_function
;
49 using ::std::for_each
;
51 //2do: DocOption TimeOut?
52 //#define SC_CHARTTIMEOUT 1000 // eine Sekunde keine Aenderung/KeyEvent
54 // Update chart listeners quickly, to get a similar behavior to loaded charts
55 // which register UNO listeners.
56 #define SC_CHARTTIMEOUT 10
59 // ====================================================================
63 uno::Reference
< chart::XChartDataChangeEventListener
> xListener
;
64 uno::Reference
< chart::XChartData
> xSource
;
67 ScChartUnoData( const uno::Reference
< chart::XChartDataChangeEventListener
>& rL
,
68 const uno::Reference
< chart::XChartData
>& rS
) :
69 xListener( rL
), xSource( rS
) {}
72 const uno::Reference
< chart::XChartDataChangeEventListener
>& GetListener() const { return xListener
; }
73 const uno::Reference
< chart::XChartData
>& GetSource() const { return xSource
; }
77 // === ScChartListener ================================================
79 ScChartListener::ExternalRefListener::ExternalRefListener(ScChartListener
& rParent
, ScDocument
* pDoc
) :
80 mrParent(rParent
), mpDoc(pDoc
)
84 ScChartListener::ExternalRefListener::~ExternalRefListener()
86 if (!mpDoc
|| mpDoc
->IsInDtorClear())
87 // The document is being destroyed. Do nothing.
90 // Make sure to remove all pointers to this object.
91 mpDoc
->GetExternalRefManager()->removeLinkListener(this);
94 void ScChartListener::ExternalRefListener::notify(sal_uInt16 nFileId
, ScExternalRefManager::LinkUpdateType eType
)
98 case ScExternalRefManager::LINK_MODIFIED
:
100 if (maFileIds
.count(nFileId
))
101 // We are listening to this external document. Send an update
102 // requst to the chart.
103 mrParent
.SetUpdateQueue();
106 case ScExternalRefManager::LINK_BROKEN
:
107 removeFileId(nFileId
);
112 void ScChartListener::ExternalRefListener::addFileId(sal_uInt16 nFileId
)
114 maFileIds
.insert(nFileId
);
117 void ScChartListener::ExternalRefListener::removeFileId(sal_uInt16 nFileId
)
119 maFileIds
.erase(nFileId
);
122 hash_set
<sal_uInt16
>& ScChartListener::ExternalRefListener::getAllFileIds()
127 // ----------------------------------------------------------------------------
129 ScChartListener::ScChartListener( const String
& rName
, ScDocument
* pDocP
,
130 const ScRange
& rRange
) :
133 mpExtRefListener(NULL
),
134 mpTokens(new vector
<ScSharedTokenRef
>),
139 bSeriesRangesScheduled( FALSE
)
141 SetRangeList( rRange
);
144 ScChartListener::ScChartListener( const String
& rName
, ScDocument
* pDocP
,
145 const ScRangeListRef
& rRangeList
) :
148 mpExtRefListener(NULL
),
149 mpTokens(new vector
<ScSharedTokenRef
>),
154 bSeriesRangesScheduled( FALSE
)
156 ScRefTokenHelper::getTokensFromRangeList(*mpTokens
, *rRangeList
);
159 ScChartListener::ScChartListener( const String
& rName
, ScDocument
* pDocP
, vector
<ScSharedTokenRef
>* pTokens
) :
162 mpExtRefListener(NULL
),
168 bSeriesRangesScheduled( FALSE
)
172 ScChartListener::ScChartListener( const ScChartListener
& r
) :
175 mpExtRefListener(NULL
),
176 mpTokens(new vector
<ScSharedTokenRef
>(*r
.mpTokens
)),
181 bSeriesRangesScheduled( r
.bSeriesRangesScheduled
)
184 pUnoData
= new ScChartUnoData( *r
.pUnoData
);
186 if (r
.mpExtRefListener
.get())
188 // Re-register this new listener for the files that the old listener
191 ScExternalRefManager
* pRefMgr
= pDoc
->GetExternalRefManager();
192 const hash_set
<sal_uInt16
>& rFileIds
= r
.mpExtRefListener
->getAllFileIds();
193 mpExtRefListener
.reset(new ExternalRefListener(*this, pDoc
));
194 hash_set
<sal_uInt16
>::const_iterator itr
= rFileIds
.begin(), itrEnd
= rFileIds
.end();
195 for (; itr
!= itrEnd
; ++itr
)
197 pRefMgr
->addLinkListener(*itr
, mpExtRefListener
.get());
198 mpExtRefListener
->addFileId(*itr
);
203 ScChartListener::~ScChartListener()
205 if ( HasBroadcaster() )
209 if (mpExtRefListener
.get())
211 // Stop listening to all external files.
212 ScExternalRefManager
* pRefMgr
= pDoc
->GetExternalRefManager();
213 const hash_set
<sal_uInt16
>& rFileIds
= mpExtRefListener
->getAllFileIds();
214 hash_set
<sal_uInt16
>::const_iterator itr
= rFileIds
.begin(), itrEnd
= rFileIds
.end();
215 for (; itr
!= itrEnd
; ++itr
)
216 pRefMgr
->removeLinkListener(*itr
, mpExtRefListener
.get());
220 ScDataObject
* ScChartListener::Clone() const
222 return new ScChartListener( *this );
225 void ScChartListener::SetUno(
226 const uno::Reference
< chart::XChartDataChangeEventListener
>& rListener
,
227 const uno::Reference
< chart::XChartData
>& rSource
)
229 // DBG_ASSERT( rListener.is() && rSource.is(), "Nullpointer bei SetUno" );
231 pUnoData
= new ScChartUnoData( rListener
, rSource
);
234 uno::Reference
< chart::XChartDataChangeEventListener
> ScChartListener::GetUnoListener() const
237 return pUnoData
->GetListener();
238 return uno::Reference
< chart::XChartDataChangeEventListener
>();
241 uno::Reference
< chart::XChartData
> ScChartListener::GetUnoSource() const
244 return pUnoData
->GetSource();
245 return uno::Reference
< chart::XChartData
>();
248 void ScChartListener::Notify( SvtBroadcaster
&, const SfxHint
& rHint
)
250 const ScHint
* p
= dynamic_cast<const ScHint
*>(&rHint
);
251 if (p
&& (p
->GetId() & (SC_HINT_DATACHANGED
| SC_HINT_DYING
)))
255 void ScChartListener::Update()
257 if ( pDoc
->IsInInterpreter() )
258 { // #73482# If interpreting do nothing and restart timer so we don't
259 // interfere with interpreter and don't produce an Err522 or similar.
260 // This may happen if we are rescheduled via Basic function.
261 pDoc
->GetChartListenerCollection()->StartTimer();
267 //! irgendwann mal erkennen, was sich innerhalb des Charts geaendert hat
268 chart::ChartDataChangeEvent
aEvent( pUnoData
->GetSource(),
269 chart::ChartDataChangeType_ALL
,
271 pUnoData
->GetListener()->chartDataChanged( aEvent
);
273 else if ( pDoc
->GetAutoCalc() )
276 pDoc
->UpdateChart( GetString());
280 ScRangeListRef
ScChartListener::GetRangeList() const
282 ScRangeListRef
aRLRef(new ScRangeList
);
283 ScRefTokenHelper::getRangeListFromTokens(*aRLRef
, *mpTokens
);
287 void ScChartListener::SetRangeList( const ScRangeListRef
& rNew
)
289 vector
<ScSharedTokenRef
> aTokens
;
290 ScRefTokenHelper::getTokensFromRangeList(aTokens
, *rNew
);
291 mpTokens
->swap(aTokens
);
294 void ScChartListener::SetRangeList( const ScRange
& rRange
)
296 ScSharedTokenRef pToken
;
297 ScRefTokenHelper::getTokenFromRange(pToken
, rRange
);
298 mpTokens
->push_back(pToken
);
303 class StartEndListening
: public unary_function
<ScSharedTokenRef
, void>
306 StartEndListening(ScDocument
* pDoc
, ScChartListener
& rParent
, bool bStart
) :
307 mpDoc(pDoc
), mrParent(rParent
), mbStart(bStart
) {}
309 void operator() (const ScSharedTokenRef
& pToken
)
311 if (!ScRefTokenHelper::isRef(pToken
))
314 bool bExternal
= ScRefTokenHelper::isExternalRef(pToken
);
317 sal_uInt16 nFileId
= pToken
->GetIndex();
318 ScExternalRefManager
* pRefMgr
= mpDoc
->GetExternalRefManager();
319 ScChartListener::ExternalRefListener
* pExtRefListener
= mrParent
.GetExtRefListener();
322 pRefMgr
->addLinkListener(nFileId
, pExtRefListener
);
323 pExtRefListener
->addFileId(nFileId
);
327 pRefMgr
->removeLinkListener(nFileId
, pExtRefListener
);
328 pExtRefListener
->removeFileId(nFileId
);
334 ScRefTokenHelper::getRangeFromToken(aRange
, pToken
, bExternal
);
336 startListening(aRange
);
338 endListening(aRange
);
343 void startListening(const ScRange
& rRange
)
345 if (rRange
.aStart
== rRange
.aEnd
)
346 mpDoc
->StartListeningCell(rRange
.aStart
, &mrParent
);
348 mpDoc
->StartListeningArea(rRange
, &mrParent
);
351 void endListening(const ScRange
& rRange
)
353 if (rRange
.aStart
== rRange
.aEnd
)
354 mpDoc
->EndListeningCell(rRange
.aStart
, &mrParent
);
356 mpDoc
->EndListeningArea(rRange
, &mrParent
);
361 ScChartListener
& mrParent
;
367 void ScChartListener::StartListeningTo()
369 if (!mpTokens
.get() || mpTokens
->empty())
370 // no references to listen to.
373 for_each(mpTokens
->begin(), mpTokens
->end(), StartEndListening(pDoc
, *this, true));
376 void ScChartListener::EndListeningTo()
378 if (!mpTokens
.get() || mpTokens
->empty())
379 // no references to listen to.
382 for_each(mpTokens
->begin(), mpTokens
->end(), StartEndListening(pDoc
, *this, false));
386 void ScChartListener::ChangeListening( const ScRangeListRef
& rRangeListRef
,
390 SetRangeList( rRangeListRef
);
397 void ScChartListener::UpdateScheduledSeriesRanges()
399 if ( bSeriesRangesScheduled
)
401 bSeriesRangesScheduled
= FALSE
;
402 UpdateSeriesRanges();
407 void ScChartListener::UpdateChartIntersecting( const ScRange
& rRange
)
409 ScSharedTokenRef pToken
;
410 ScRefTokenHelper::getTokenFromRange(pToken
, rRange
);
412 if (ScRefTokenHelper::intersects(*mpTokens
, pToken
))
414 // force update (chart has to be loaded), don't use ScChartListener::Update
415 pDoc
->UpdateChart( GetString());
420 void ScChartListener::UpdateSeriesRanges()
422 ScRangeListRef
pRangeList(new ScRangeList
);
423 ScRefTokenHelper::getRangeListFromTokens(*pRangeList
, *mpTokens
);
424 pDoc
->SetChartRangeList(GetString(), pRangeList
);
427 ScChartListener::ExternalRefListener
* ScChartListener::GetExtRefListener()
429 if (!mpExtRefListener
.get())
430 mpExtRefListener
.reset(new ExternalRefListener(*this, pDoc
));
432 return mpExtRefListener
.get();
435 void ScChartListener::SetUpdateQueue()
438 pDoc
->GetChartListenerCollection()->StartTimer();
441 BOOL
ScChartListener::operator==( const ScChartListener
& r
)
443 bool b1
= (mpTokens
.get() && !mpTokens
->empty());
444 bool b2
= (r
.mpTokens
.get() && !r
.mpTokens
->empty());
446 if (pDoc
!= r
.pDoc
|| bUsed
!= r
.bUsed
|| bDirty
!= r
.bDirty
||
447 bSeriesRangesScheduled
!= r
.bSeriesRangesScheduled
||
448 GetString() != r
.GetString() || b1
!= b2
)
452 // both token list instances are empty.
455 return *mpTokens
== *r
.mpTokens
;
458 // ============================================================================
460 ScChartHiddenRangeListener::ScChartHiddenRangeListener()
464 ScChartHiddenRangeListener::~ScChartHiddenRangeListener()
469 // === ScChartListenerCollection ======================================
471 ScChartListenerCollection::RangeListenerItem::RangeListenerItem(const ScRange
& rRange
, ScChartHiddenRangeListener
* p
) :
472 maRange(rRange
), mpListener(p
)
476 ScChartListenerCollection::ScChartListenerCollection( ScDocument
* pDocP
) :
477 ScStrCollection( 4, 4, FALSE
),
480 aTimer
.SetTimeoutHdl( LINK( this, ScChartListenerCollection
, TimerHdl
) );
483 ScChartListenerCollection::ScChartListenerCollection(
484 const ScChartListenerCollection
& rColl
) :
485 ScStrCollection( rColl
),
488 aTimer
.SetTimeoutHdl( LINK( this, ScChartListenerCollection
, TimerHdl
) );
491 ScChartListenerCollection::~ScChartListenerCollection()
493 // #96783# remove ChartListener objects before aTimer dtor is called, because
494 // ScChartListener::EndListeningTo may cause ScChartListenerCollection::StartTimer
495 // to be called if an empty ScNoteCell is deleted
501 ScDataObject
* ScChartListenerCollection::Clone() const
503 return new ScChartListenerCollection( *this );
506 void ScChartListenerCollection::StartAllListeners()
508 for ( USHORT nIndex
= 0; nIndex
< nCount
; nIndex
++ )
510 ((ScChartListener
*) pItems
[ nIndex
])->StartListeningTo();
514 void ScChartListenerCollection::ChangeListening( const String
& rName
,
515 const ScRangeListRef
& rRangeListRef
, BOOL bDirty
)
517 ScChartListener
aCLSearcher( rName
, pDoc
, rRangeListRef
);
518 ScChartListener
* pCL
;
520 if ( Search( &aCLSearcher
, nIndex
) )
522 pCL
= (ScChartListener
*) pItems
[ nIndex
];
523 pCL
->EndListeningTo();
524 pCL
->SetRangeList( rRangeListRef
);
528 pCL
= new ScChartListener( aCLSearcher
);
531 pCL
->StartListeningTo();
533 pCL
->SetDirty( TRUE
);
536 void ScChartListenerCollection::FreeUnused()
538 // rueckwaerts wg. Pointer-Aufrueckerei im Array
539 for ( USHORT nIndex
= nCount
; nIndex
-- >0; )
541 ScChartListener
* pCL
= (ScChartListener
*) pItems
[ nIndex
];
542 // Uno-Charts nicht rauskicken
543 // (werden per FreeUno von aussen geloescht)
547 pCL
->SetUsed( FALSE
);
554 void ScChartListenerCollection::FreeUno( const uno::Reference
< chart::XChartDataChangeEventListener
>& rListener
,
555 const uno::Reference
< chart::XChartData
>& rSource
)
557 // rueckwaerts wg. Pointer-Aufrueckerei im Array
558 for ( USHORT nIndex
= nCount
; nIndex
-- >0; )
560 ScChartListener
* pCL
= (ScChartListener
*) pItems
[ nIndex
];
562 pCL
->GetUnoListener() == rListener
&&
563 pCL
->GetUnoSource() == rSource
)
567 //! sollte nur einmal vorkommen?
571 void ScChartListenerCollection::StartTimer()
573 aTimer
.SetTimeout( SC_CHARTTIMEOUT
);
577 IMPL_LINK( ScChartListenerCollection
, TimerHdl
, Timer
*, EMPTYARG
)
579 if ( Application::AnyInput( INPUT_KEYBOARD
) )
588 void ScChartListenerCollection::UpdateDirtyCharts()
590 for ( USHORT nIndex
= 0; nIndex
< nCount
; nIndex
++ )
592 ScChartListener
* pCL
= (ScChartListener
*) pItems
[ nIndex
];
593 if ( pCL
->IsDirty() )
595 if ( aTimer
.IsActive() && !pDoc
->IsImportingXML())
596 break; // da kam einer dazwischen
601 void ScChartListenerCollection::SetDirty()
603 for ( USHORT nIndex
= 0; nIndex
< nCount
; nIndex
++ )
605 ScChartListener
* pCL
= (ScChartListener
*) pItems
[ nIndex
];
606 pCL
->SetDirty( TRUE
);
612 void ScChartListenerCollection::SetDiffDirty(
613 const ScChartListenerCollection
& rCmp
, BOOL bSetChartRangeLists
)
616 for ( USHORT nIndex
= 0; nIndex
< nCount
; nIndex
++ )
618 ScChartListener
* pCL
= (ScChartListener
*) pItems
[ nIndex
];
620 BOOL bFound
= rCmp
.Search( pCL
, nFound
);
621 if ( !bFound
|| (*pCL
!= *((const ScChartListener
*) rCmp
.pItems
[ nFound
])) )
623 if ( bSetChartRangeLists
)
627 const ScRangeListRef
& rList1
= pCL
->GetRangeList();
628 const ScRangeListRef
& rList2
=
629 ((const ScChartListener
*) rCmp
.pItems
[ nFound
])->GetRangeList();
630 BOOL b1
= rList1
.Is();
631 BOOL b2
= rList2
.Is();
632 if ( b1
!= b2
|| (b1
&& b2
&& (*rList1
!= *rList2
)) )
633 pDoc
->SetChartRangeList( pCL
->GetString(), rList1
);
636 pDoc
->SetChartRangeList( pCL
->GetString(), pCL
->GetRangeList() );
639 pCL
->SetDirty( TRUE
);
647 void ScChartListenerCollection::SetRangeDirty( const ScRange
& rRange
)
650 for ( USHORT nIndex
= 0; nIndex
< nCount
; nIndex
++ )
652 ScChartListener
* pCL
= (ScChartListener
*) pItems
[ nIndex
];
653 const ScRangeListRef
& rList
= pCL
->GetRangeList();
654 if ( rList
.Is() && rList
->Intersects( rRange
) )
657 pCL
->SetDirty( TRUE
);
663 // New hidden range listener implementation
664 for (list
<RangeListenerItem
>::iterator itr
= maHiddenListeners
.begin(), itrEnd
= maHiddenListeners
.end();
665 itr
!= itrEnd
; ++itr
)
667 if (itr
->maRange
.Intersects(rRange
))
668 itr
->mpListener
->notify();
673 void ScChartListenerCollection::UpdateScheduledSeriesRanges()
675 for ( USHORT nIndex
= 0; nIndex
< nCount
; nIndex
++ )
677 ScChartListener
* pCL
= (ScChartListener
*) pItems
[ nIndex
];
678 pCL
->UpdateScheduledSeriesRanges();
683 void ScChartListenerCollection::UpdateChartsContainingTab( SCTAB nTab
)
685 ScRange
aRange( 0, 0, nTab
, MAXCOL
, MAXROW
, nTab
);
686 for ( USHORT nIndex
= 0; nIndex
< nCount
; nIndex
++ )
688 ScChartListener
* pCL
= (ScChartListener
*) pItems
[ nIndex
];
689 pCL
->UpdateChartIntersecting( aRange
);
694 BOOL
ScChartListenerCollection::operator==( const ScChartListenerCollection
& r
)
696 // hier nicht ScStrCollection::operator==() verwenden, der umstaendlich via
697 // IsEqual und Compare laeuft, stattdessen ScChartListener::operator==()
698 if ( pDoc
!= r
.pDoc
|| nCount
!= r
.nCount
)
700 for ( USHORT nIndex
= 0; nIndex
< nCount
; nIndex
++ )
702 if ( *((ScChartListener
*) pItems
[ nIndex
]) !=
703 *((ScChartListener
*) r
.pItems
[ nIndex
]) )
709 void ScChartListenerCollection::StartListeningHiddenRange( const ScRange
& rRange
, ScChartHiddenRangeListener
* pListener
)
711 RangeListenerItem
aItem(rRange
, pListener
);
712 maHiddenListeners
.push_back(aItem
);
717 struct MatchListener
: public ::std::unary_function
<
718 ScChartListenerCollection::RangeListenerItem
, bool>
720 MatchListener(const ScChartHiddenRangeListener
* pMatch
) :
725 bool operator() (const ScChartListenerCollection::RangeListenerItem
& rItem
) const
727 return mpMatch
== rItem
.mpListener
;
731 const ScChartHiddenRangeListener
* mpMatch
;
735 void ScChartListenerCollection::EndListeningHiddenRange( ScChartHiddenRangeListener
* pListener
)
737 maHiddenListeners
.remove_if(MatchListener(pListener
));