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 "chartlis.hxx"
24 #include "document.hxx"
25 #include "reftokenhelper.hxx"
26 #include "stlalgorithm.hxx"
28 using namespace com::sun::star
;
31 using ::std::auto_ptr
;
32 using ::std::unary_function
;
33 using ::std::for_each
;
35 // Update chart listeners quickly, to get a similar behavior to loaded charts
36 // which register UNO listeners.
37 #define SC_CHARTTIMEOUT 10
41 uno::Reference
< chart::XChartDataChangeEventListener
> xListener
;
42 uno::Reference
< chart::XChartData
> xSource
;
45 ScChartUnoData( const uno::Reference
< chart::XChartDataChangeEventListener
>& rL
,
46 const uno::Reference
< chart::XChartData
>& rS
) :
47 xListener( rL
), xSource( rS
) {}
50 const uno::Reference
< chart::XChartDataChangeEventListener
>& GetListener() const { return xListener
; }
51 const uno::Reference
< chart::XChartData
>& GetSource() const { return xSource
; }
55 ScChartListener::ExternalRefListener::ExternalRefListener(ScChartListener
& rParent
, ScDocument
* pDoc
) :
56 mrParent(rParent
), mpDoc(pDoc
)
60 ScChartListener::ExternalRefListener::~ExternalRefListener()
62 if (!mpDoc
|| mpDoc
->IsInDtorClear())
63 // The document is being destroyed. Do nothing.
66 // Make sure to remove all pointers to this object.
67 mpDoc
->GetExternalRefManager()->removeLinkListener(this);
70 void ScChartListener::ExternalRefListener::notify(sal_uInt16 nFileId
, ScExternalRefManager::LinkUpdateType eType
)
74 case ScExternalRefManager::LINK_MODIFIED
:
76 if (maFileIds
.count(nFileId
))
77 // We are listening to this external document. Send an update
78 // requst to the chart.
79 mrParent
.SetUpdateQueue();
82 case ScExternalRefManager::LINK_BROKEN
:
83 removeFileId(nFileId
);
88 void ScChartListener::ExternalRefListener::addFileId(sal_uInt16 nFileId
)
90 maFileIds
.insert(nFileId
);
93 void ScChartListener::ExternalRefListener::removeFileId(sal_uInt16 nFileId
)
95 maFileIds
.erase(nFileId
);
98 boost::unordered_set
<sal_uInt16
>& ScChartListener::ExternalRefListener::getAllFileIds()
103 ScChartListener::ScChartListener( const OUString
& rName
, ScDocument
* pDocP
,
104 const ScRangeListRef
& rRangeList
) :
106 mpExtRefListener(NULL
),
107 mpTokens(new vector
<ScTokenRef
>),
113 bSeriesRangesScheduled( false )
115 ScRefTokenHelper::getTokensFromRangeList(*mpTokens
, *rRangeList
);
118 ScChartListener::ScChartListener( const OUString
& rName
, ScDocument
* pDocP
, vector
<ScTokenRef
>* pTokens
) :
120 mpExtRefListener(NULL
),
127 bSeriesRangesScheduled( false )
131 ScChartListener::ScChartListener( const ScChartListener
& r
) :
133 mpExtRefListener(NULL
),
134 mpTokens(new vector
<ScTokenRef
>(*r
.mpTokens
)),
140 bSeriesRangesScheduled( r
.bSeriesRangesScheduled
)
143 pUnoData
= new ScChartUnoData( *r
.pUnoData
);
145 if (r
.mpExtRefListener
.get())
147 // Re-register this new listener for the files that the old listener
150 ScExternalRefManager
* pRefMgr
= mpDoc
->GetExternalRefManager();
151 const boost::unordered_set
<sal_uInt16
>& rFileIds
= r
.mpExtRefListener
->getAllFileIds();
152 mpExtRefListener
.reset(new ExternalRefListener(*this, mpDoc
));
153 boost::unordered_set
<sal_uInt16
>::const_iterator itr
= rFileIds
.begin(), itrEnd
= rFileIds
.end();
154 for (; itr
!= itrEnd
; ++itr
)
156 pRefMgr
->addLinkListener(*itr
, mpExtRefListener
.get());
157 mpExtRefListener
->addFileId(*itr
);
162 ScChartListener::~ScChartListener()
164 if ( HasBroadcaster() )
168 if (mpExtRefListener
.get())
170 // Stop listening to all external files.
171 ScExternalRefManager
* pRefMgr
= mpDoc
->GetExternalRefManager();
172 const boost::unordered_set
<sal_uInt16
>& rFileIds
= mpExtRefListener
->getAllFileIds();
173 boost::unordered_set
<sal_uInt16
>::const_iterator itr
= rFileIds
.begin(), itrEnd
= rFileIds
.end();
174 for (; itr
!= itrEnd
; ++itr
)
175 pRefMgr
->removeLinkListener(*itr
, mpExtRefListener
.get());
179 const OUString
& ScChartListener::GetName() const
184 void ScChartListener::SetUno(
185 const uno::Reference
< chart::XChartDataChangeEventListener
>& rListener
,
186 const uno::Reference
< chart::XChartData
>& rSource
)
189 pUnoData
= new ScChartUnoData( rListener
, rSource
);
192 uno::Reference
< chart::XChartDataChangeEventListener
> ScChartListener::GetUnoListener() const
195 return pUnoData
->GetListener();
196 return uno::Reference
< chart::XChartDataChangeEventListener
>();
199 uno::Reference
< chart::XChartData
> ScChartListener::GetUnoSource() const
202 return pUnoData
->GetSource();
203 return uno::Reference
< chart::XChartData
>();
206 void ScChartListener::Notify( SvtBroadcaster
&, const SfxHint
& rHint
)
208 const ScHint
* p
= dynamic_cast<const ScHint
*>(&rHint
);
209 if (p
&& (p
->GetId() & SC_HINT_DATACHANGED
))
213 void ScChartListener::Update()
215 if ( mpDoc
->IsInInterpreter() )
216 { // If interpreting do nothing and restart timer so we don't
217 // interfere with interpreter and don't produce an Err522 or similar.
218 // This may happen if we are rescheduled via Basic function.
219 mpDoc
->GetChartListenerCollection()->StartTimer();
225 // recognize some day what has changed inside the Chart
226 chart::ChartDataChangeEvent
aEvent( pUnoData
->GetSource(),
227 chart::ChartDataChangeType_ALL
,
229 pUnoData
->GetListener()->chartDataChanged( aEvent
);
231 else if ( mpDoc
->GetAutoCalc() )
234 mpDoc
->UpdateChart(GetName());
238 ScRangeListRef
ScChartListener::GetRangeList() const
240 ScRangeListRef
aRLRef(new ScRangeList
);
241 ScRefTokenHelper::getRangeListFromTokens(*aRLRef
, *mpTokens
, ScAddress());
245 void ScChartListener::SetRangeList( const ScRangeListRef
& rNew
)
247 vector
<ScTokenRef
> aTokens
;
248 ScRefTokenHelper::getTokensFromRangeList(aTokens
, *rNew
);
249 mpTokens
->swap(aTokens
);
254 class StartEndListening
: public unary_function
<ScTokenRef
, void>
257 StartEndListening(ScDocument
* pDoc
, ScChartListener
& rParent
, bool bStart
) :
258 mpDoc(pDoc
), mrParent(rParent
), mbStart(bStart
) {}
260 void operator() (const ScTokenRef
& pToken
)
262 if (!ScRefTokenHelper::isRef(pToken
))
265 bool bExternal
= ScRefTokenHelper::isExternalRef(pToken
);
268 sal_uInt16 nFileId
= pToken
->GetIndex();
269 ScExternalRefManager
* pRefMgr
= mpDoc
->GetExternalRefManager();
270 ScChartListener::ExternalRefListener
* pExtRefListener
= mrParent
.GetExtRefListener();
273 pRefMgr
->addLinkListener(nFileId
, pExtRefListener
);
274 pExtRefListener
->addFileId(nFileId
);
278 pRefMgr
->removeLinkListener(nFileId
, pExtRefListener
);
279 pExtRefListener
->removeFileId(nFileId
);
285 ScRefTokenHelper::getRangeFromToken(aRange
, pToken
, ScAddress(), bExternal
);
287 startListening(aRange
);
289 endListening(aRange
);
293 void startListening(const ScRange
& rRange
)
295 if (rRange
.aStart
== rRange
.aEnd
)
296 mpDoc
->StartListeningCell(rRange
.aStart
, &mrParent
);
298 mpDoc
->StartListeningArea(rRange
, &mrParent
);
301 void endListening(const ScRange
& rRange
)
303 if (rRange
.aStart
== rRange
.aEnd
)
304 mpDoc
->EndListeningCell(rRange
.aStart
, &mrParent
);
306 mpDoc
->EndListeningArea(rRange
, &mrParent
);
310 ScChartListener
& mrParent
;
316 void ScChartListener::StartListeningTo()
318 if (!mpTokens
.get() || mpTokens
->empty())
319 // no references to listen to.
322 for_each(mpTokens
->begin(), mpTokens
->end(), StartEndListening(mpDoc
, *this, true));
325 void ScChartListener::EndListeningTo()
327 if (!mpTokens
.get() || mpTokens
->empty())
328 // no references to listen to.
331 for_each(mpTokens
->begin(), mpTokens
->end(), StartEndListening(mpDoc
, *this, false));
334 void ScChartListener::ChangeListening( const ScRangeListRef
& rRangeListRef
,
338 SetRangeList( rRangeListRef
);
344 void ScChartListener::UpdateScheduledSeriesRanges()
346 if ( bSeriesRangesScheduled
)
348 bSeriesRangesScheduled
= false;
349 UpdateSeriesRanges();
353 void ScChartListener::UpdateChartIntersecting( const ScRange
& rRange
)
356 ScRefTokenHelper::getTokenFromRange(pToken
, rRange
);
358 if (ScRefTokenHelper::intersects(*mpTokens
, pToken
, ScAddress()))
360 // force update (chart has to be loaded), don't use ScChartListener::Update
361 mpDoc
->UpdateChart(GetName());
365 void ScChartListener::UpdateSeriesRanges()
367 ScRangeListRef
pRangeList(new ScRangeList
);
368 ScRefTokenHelper::getRangeListFromTokens(*pRangeList
, *mpTokens
, ScAddress());
369 mpDoc
->SetChartRangeList(GetName(), pRangeList
);
372 ScChartListener::ExternalRefListener
* ScChartListener::GetExtRefListener()
374 if (!mpExtRefListener
.get())
375 mpExtRefListener
.reset(new ExternalRefListener(*this, mpDoc
));
377 return mpExtRefListener
.get();
380 void ScChartListener::SetUpdateQueue()
383 mpDoc
->GetChartListenerCollection()->StartTimer();
386 bool ScChartListener::operator==( const ScChartListener
& r
) const
388 bool b1
= (mpTokens
.get() && !mpTokens
->empty());
389 bool b2
= (r
.mpTokens
.get() && !r
.mpTokens
->empty());
391 if (mpDoc
!= r
.mpDoc
|| bUsed
!= r
.bUsed
|| bDirty
!= r
.bDirty
||
392 bSeriesRangesScheduled
!= r
.bSeriesRangesScheduled
||
393 GetName() != r
.GetName() || b1
!= b2
)
397 // both token list instances are empty.
400 return *mpTokens
== *r
.mpTokens
;
403 bool ScChartListener::operator!=( const ScChartListener
& r
) const
405 return !operator==(r
);
408 ScChartHiddenRangeListener::ScChartHiddenRangeListener()
412 ScChartHiddenRangeListener::~ScChartHiddenRangeListener()
417 // ScChartListenerCollection
418 ScChartListenerCollection::RangeListenerItem::RangeListenerItem(const ScRange
& rRange
, ScChartHiddenRangeListener
* p
) :
419 maRange(rRange
), mpListener(p
)
423 ScChartListenerCollection::ScChartListenerCollection( ScDocument
* pDocP
) :
426 aTimer
.SetTimeoutHdl( LINK( this, ScChartListenerCollection
, TimerHdl
) );
429 ScChartListenerCollection::ScChartListenerCollection(
430 const ScChartListenerCollection
& rColl
) :
433 aTimer
.SetTimeoutHdl( LINK( this, ScChartListenerCollection
, TimerHdl
) );
436 ScChartListenerCollection::~ScChartListenerCollection()
438 // remove ChartListener objects before aTimer dtor is called, because
439 // ScChartListener::EndListeningTo may cause ScChartListenerCollection::StartTimer
440 // to be called if an empty ScNoteCell is deleted
445 void ScChartListenerCollection::StartAllListeners()
447 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
448 for (; it
!= itEnd
; ++it
)
449 it
->second
->StartListeningTo();
452 void ScChartListenerCollection::insert(ScChartListener
* pListener
)
454 OUString aName
= pListener
->GetName();
455 maListeners
.insert(aName
, pListener
);
458 void ScChartListenerCollection::removeByName(const OUString
& rName
)
460 maListeners
.erase(rName
);
463 ScChartListener
* ScChartListenerCollection::findByName(const OUString
& rName
)
465 ListenersType::iterator it
= maListeners
.find(rName
);
466 return it
== maListeners
.end() ? NULL
: it
->second
;
469 const ScChartListener
* ScChartListenerCollection::findByName(const OUString
& rName
) const
471 ListenersType::const_iterator it
= maListeners
.find(rName
);
472 return it
== maListeners
.end() ? NULL
: it
->second
;
475 bool ScChartListenerCollection::hasListeners() const
477 return !maListeners
.empty();
480 const ScChartListenerCollection::ListenersType
& ScChartListenerCollection::getListeners() const
485 ScChartListenerCollection::ListenersType
& ScChartListenerCollection::getListeners()
490 ScChartListenerCollection::StringSetType
& ScChartListenerCollection::getNonOleObjectNames()
492 return maNonOleObjectNames
;
495 OUString
ScChartListenerCollection::getUniqueName(const OUString
& rPrefix
) const
497 for (sal_Int32 nNum
= 1; nNum
< 10000; ++nNum
) // arbitrary limit to prevent infinite loop.
499 OUStringBuffer
aBuf(rPrefix
);
501 OUString aTestName
= aBuf
.makeStringAndClear();
502 if (maListeners
.find(aTestName
) == maListeners
.end())
508 void ScChartListenerCollection::ChangeListening( const OUString
& rName
,
509 const ScRangeListRef
& rRangeListRef
, bool bDirty
)
511 ScChartListener
* pCL
= findByName(rName
);
514 pCL
->EndListeningTo();
515 pCL
->SetRangeList( rRangeListRef
);
519 pCL
= new ScChartListener(rName
, pDoc
, rRangeListRef
);
522 pCL
->StartListeningTo();
524 pCL
->SetDirty( true );
529 class InsertChartListener
: public std::unary_function
<ScChartListener
*, void>
531 ScChartListenerCollection::ListenersType
& mrListeners
;
533 InsertChartListener(ScChartListenerCollection::ListenersType
& rListeners
) :
534 mrListeners(rListeners
) {}
536 void operator() (ScChartListener
* p
)
538 OUString aName
= p
->GetName();
539 mrListeners
.insert(aName
, p
);
545 void ScChartListenerCollection::FreeUnused()
547 std::vector
<ScChartListener
*> aUsed
, aUnused
;
549 // First, filter each listener into 'used' and 'unused' categories.
551 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
552 for (; it
!= itEnd
; ++it
)
554 ScChartListener
* p
= it
->second
;
557 // We don't delete UNO charts; they are to be deleted separately via FreeUno().
568 aUnused
.push_back(p
);
572 // Release all pointers currently managed by the ptr_map container.
573 maListeners
.release().release();
575 // Re-insert the listeners we need to keep.
576 std::for_each(aUsed
.begin(), aUsed
.end(), InsertChartListener(maListeners
));
578 // Now, delete the ones no longer needed.
579 std::for_each(aUnused
.begin(), aUnused
.end(), ScDeleteObjectByPtr
<ScChartListener
>());
582 void ScChartListenerCollection::FreeUno( const uno::Reference
< chart::XChartDataChangeEventListener
>& rListener
,
583 const uno::Reference
< chart::XChartData
>& rSource
)
585 std::vector
<ScChartListener
*> aUsed
, aUnused
;
587 // First, filter each listener into 'used' and 'unused' categories.
589 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
590 for (; it
!= itEnd
; ++it
)
592 ScChartListener
* p
= it
->second
;
593 if (p
->IsUno() && p
->GetUnoListener() == rListener
&& p
->GetUnoSource() == rSource
)
594 aUnused
.push_back(p
);
600 // Release all pointers currently managed by the ptr_map container.
601 maListeners
.release().release();
603 // Re-insert the listeners we need to keep.
604 std::for_each(aUsed
.begin(), aUsed
.end(), InsertChartListener(maListeners
));
606 // Now, delete the ones no longer needed.
607 std::for_each(aUnused
.begin(), aUnused
.end(), ScDeleteObjectByPtr
<ScChartListener
>());
610 void ScChartListenerCollection::StartTimer()
612 aTimer
.SetTimeout( SC_CHARTTIMEOUT
);
616 IMPL_LINK_NOARG(ScChartListenerCollection
, TimerHdl
)
618 if ( Application::AnyInput( VCL_INPUT_KEYBOARD
) )
627 void ScChartListenerCollection::UpdateDirtyCharts()
629 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
630 for (; it
!= itEnd
; ++it
)
632 ScChartListener
* p
= it
->second
;
636 if (aTimer
.IsActive() && !pDoc
->IsImportingXML())
637 break; // one interfered
641 void ScChartListenerCollection::SetDirty()
643 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
644 for (; it
!= itEnd
; ++it
)
645 it
->second
->SetDirty(true);
650 void ScChartListenerCollection::SetDiffDirty(
651 const ScChartListenerCollection
& rCmp
, bool bSetChartRangeLists
)
654 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
655 for (; it
!= itEnd
; ++it
)
657 ScChartListener
* pCL
= it
->second
;
659 const ScChartListener
* pCLCmp
= rCmp
.findByName(pCL
->GetName());
660 if (!pCLCmp
|| *pCL
!= *pCLCmp
)
662 if ( bSetChartRangeLists
)
666 const ScRangeListRef
& rList1
= pCL
->GetRangeList();
667 const ScRangeListRef
& rList2
= pCLCmp
->GetRangeList();
668 bool b1
= rList1
.Is();
669 bool b2
= rList2
.Is();
670 if ( b1
!= b2
|| (b1
&& b2
&& (*rList1
!= *rList2
)) )
671 pDoc
->SetChartRangeList( pCL
->GetName(), rList1
);
674 pDoc
->SetChartRangeList( pCL
->GetName(), pCL
->GetRangeList() );
677 pCL
->SetDirty( true );
684 void ScChartListenerCollection::SetRangeDirty( const ScRange
& rRange
)
687 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
688 for (; it
!= itEnd
; ++it
)
690 ScChartListener
* pCL
= it
->second
;
691 const ScRangeListRef
& rList
= pCL
->GetRangeList();
692 if ( rList
.Is() && rList
->Intersects( rRange
) )
695 pCL
->SetDirty( true );
701 // New hidden range listener implementation
702 for (list
<RangeListenerItem
>::iterator itr
= maHiddenListeners
.begin(), itrEnd
= maHiddenListeners
.end();
703 itr
!= itrEnd
; ++itr
)
705 if (itr
->maRange
.Intersects(rRange
))
706 itr
->mpListener
->notify();
710 void ScChartListenerCollection::UpdateScheduledSeriesRanges()
712 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
713 for (; it
!= itEnd
; ++it
)
714 it
->second
->UpdateScheduledSeriesRanges();
717 void ScChartListenerCollection::UpdateChartsContainingTab( SCTAB nTab
)
719 ScRange
aRange( 0, 0, nTab
, MAXCOL
, MAXROW
, nTab
);
720 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
721 for (; it
!= itEnd
; ++it
)
722 it
->second
->UpdateChartIntersecting(aRange
);
725 bool ScChartListenerCollection::operator==( const ScChartListenerCollection
& r
) const
727 // Do not use ScStrCollection::operator==() here that uses IsEqual und Compare.
728 // Use ScChartListener::operator==() instead.
729 if (pDoc
!= r
.pDoc
|| maListeners
.size() != r
.maListeners
.size())
732 ListenersType::const_iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
733 ListenersType::const_iterator it2
= r
.maListeners
.begin();
734 for (; it
!= itEnd
; ++it
, ++it2
)
742 bool ScChartListenerCollection::operator!=( const ScChartListenerCollection
& r
) const
744 return !operator==(r
);
747 void ScChartListenerCollection::StartListeningHiddenRange( const ScRange
& rRange
, ScChartHiddenRangeListener
* pListener
)
749 RangeListenerItem
aItem(rRange
, pListener
);
750 maHiddenListeners
.push_back(aItem
);
755 struct MatchListener
: public ::std::unary_function
<
756 ScChartListenerCollection::RangeListenerItem
, bool>
758 MatchListener(const ScChartHiddenRangeListener
* pMatch
) :
763 bool operator() (const ScChartListenerCollection::RangeListenerItem
& rItem
) const
765 return mpMatch
== rItem
.mpListener
;
769 const ScChartHiddenRangeListener
* mpMatch
;
773 void ScChartListenerCollection::EndListeningHiddenRange( ScChartHiddenRangeListener
* pListener
)
775 maHiddenListeners
.remove_if(MatchListener(pListener
));
778 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */