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"
27 #include <boost/checked_delete.hpp>
29 using namespace com::sun::star
;
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.
40 uno::Reference
< chart::XChartDataChangeEventListener
> xListener
;
41 uno::Reference
< chart::XChartData
> xSource
;
44 ScChartUnoData( const uno::Reference
< chart::XChartDataChangeEventListener
>& rL
,
45 const uno::Reference
< chart::XChartData
>& rS
) :
46 xListener( rL
), xSource( rS
) {}
49 const uno::Reference
< chart::XChartDataChangeEventListener
>& GetListener() const { return xListener
; }
50 const uno::Reference
< chart::XChartData
>& GetSource() const { return xSource
; }
54 ScChartListener::ExternalRefListener::ExternalRefListener(ScChartListener
& rParent
, ScDocument
* pDoc
) :
55 mrParent(rParent
), mpDoc(pDoc
)
59 ScChartListener::ExternalRefListener::~ExternalRefListener()
61 if (!mpDoc
|| mpDoc
->IsInDtorClear())
62 // The document is being destroyed. Do nothing.
65 // Make sure to remove all pointers to this object.
66 mpDoc
->GetExternalRefManager()->removeLinkListener(this);
69 void ScChartListener::ExternalRefListener::notify(sal_uInt16 nFileId
, ScExternalRefManager::LinkUpdateType eType
)
73 case ScExternalRefManager::LINK_MODIFIED
:
75 if (maFileIds
.count(nFileId
))
76 // We are listening to this external document. Send an update
77 // requst to the chart.
78 mrParent
.SetUpdateQueue();
81 case ScExternalRefManager::LINK_BROKEN
:
82 removeFileId(nFileId
);
87 void ScChartListener::ExternalRefListener::addFileId(sal_uInt16 nFileId
)
89 maFileIds
.insert(nFileId
);
92 void ScChartListener::ExternalRefListener::removeFileId(sal_uInt16 nFileId
)
94 maFileIds
.erase(nFileId
);
97 ScChartListener::ScChartListener( const OUString
& rName
, ScDocument
* pDocP
,
98 const ScRangeListRef
& rRangeList
) :
100 mpExtRefListener(NULL
),
101 mpTokens(new vector
<ScTokenRef
>),
107 bSeriesRangesScheduled( false )
109 ScRefTokenHelper::getTokensFromRangeList(*mpTokens
, *rRangeList
);
112 ScChartListener::ScChartListener( const OUString
& rName
, ScDocument
* pDocP
, vector
<ScTokenRef
>* pTokens
) :
114 mpExtRefListener(NULL
),
121 bSeriesRangesScheduled( false )
125 ScChartListener::ScChartListener( const ScChartListener
& r
) :
127 mpExtRefListener(NULL
),
128 mpTokens(new vector
<ScTokenRef
>(*r
.mpTokens
)),
134 bSeriesRangesScheduled( r
.bSeriesRangesScheduled
)
137 pUnoData
= new ScChartUnoData( *r
.pUnoData
);
139 if (r
.mpExtRefListener
.get())
141 // Re-register this new listener for the files that the old listener
144 ScExternalRefManager
* pRefMgr
= mpDoc
->GetExternalRefManager();
145 const std::unordered_set
<sal_uInt16
>& rFileIds
= r
.mpExtRefListener
->getAllFileIds();
146 mpExtRefListener
.reset(new ExternalRefListener(*this, mpDoc
));
147 std::unordered_set
<sal_uInt16
>::const_iterator itr
= rFileIds
.begin(), itrEnd
= rFileIds
.end();
148 for (; itr
!= itrEnd
; ++itr
)
150 pRefMgr
->addLinkListener(*itr
, mpExtRefListener
.get());
151 mpExtRefListener
->addFileId(*itr
);
156 ScChartListener::~ScChartListener()
158 if ( HasBroadcaster() )
162 if (mpExtRefListener
.get())
164 // Stop listening to all external files.
165 ScExternalRefManager
* pRefMgr
= mpDoc
->GetExternalRefManager();
166 const std::unordered_set
<sal_uInt16
>& rFileIds
= mpExtRefListener
->getAllFileIds();
167 std::unordered_set
<sal_uInt16
>::const_iterator itr
= rFileIds
.begin(), itrEnd
= rFileIds
.end();
168 for (; itr
!= itrEnd
; ++itr
)
169 pRefMgr
->removeLinkListener(*itr
, mpExtRefListener
.get());
173 void ScChartListener::SetUno(
174 const uno::Reference
< chart::XChartDataChangeEventListener
>& rListener
,
175 const uno::Reference
< chart::XChartData
>& rSource
)
178 pUnoData
= new ScChartUnoData( rListener
, rSource
);
181 uno::Reference
< chart::XChartDataChangeEventListener
> ScChartListener::GetUnoListener() const
184 return pUnoData
->GetListener();
185 return uno::Reference
< chart::XChartDataChangeEventListener
>();
188 uno::Reference
< chart::XChartData
> ScChartListener::GetUnoSource() const
191 return pUnoData
->GetSource();
192 return uno::Reference
< chart::XChartData
>();
195 void ScChartListener::Notify( const SfxHint
& rHint
)
197 const ScHint
* p
= dynamic_cast<const ScHint
*>(&rHint
);
198 if (p
&& (p
->GetId() & SC_HINT_DATACHANGED
))
202 void ScChartListener::Update()
204 if ( mpDoc
->IsInInterpreter() )
205 { // If interpreting do nothing and restart timer so we don't
206 // interfere with interpreter and don't produce an Err522 or similar.
207 // This may happen if we are rescheduled via Basic function.
208 mpDoc
->GetChartListenerCollection()->StartTimer();
214 // recognize some day what has changed inside the Chart
215 chart::ChartDataChangeEvent
aEvent( pUnoData
->GetSource(),
216 chart::ChartDataChangeType_ALL
,
218 pUnoData
->GetListener()->chartDataChanged( aEvent
);
220 else if ( mpDoc
->GetAutoCalc() )
223 mpDoc
->UpdateChart(GetName());
227 ScRangeListRef
ScChartListener::GetRangeList() const
229 ScRangeListRef
aRLRef(new ScRangeList
);
230 ScRefTokenHelper::getRangeListFromTokens(*aRLRef
, *mpTokens
, ScAddress());
234 void ScChartListener::SetRangeList( const ScRangeListRef
& rNew
)
236 vector
<ScTokenRef
> aTokens
;
237 ScRefTokenHelper::getTokensFromRangeList(aTokens
, *rNew
);
238 mpTokens
->swap(aTokens
);
243 class StartEndListening
: public unary_function
<ScTokenRef
, void>
246 StartEndListening(ScDocument
* pDoc
, ScChartListener
& rParent
, bool bStart
) :
247 mpDoc(pDoc
), mrParent(rParent
), mbStart(bStart
) {}
249 void operator() (const ScTokenRef
& pToken
)
251 if (!ScRefTokenHelper::isRef(pToken
))
254 bool bExternal
= ScRefTokenHelper::isExternalRef(pToken
);
257 sal_uInt16 nFileId
= pToken
->GetIndex();
258 ScExternalRefManager
* pRefMgr
= mpDoc
->GetExternalRefManager();
259 ScChartListener::ExternalRefListener
* pExtRefListener
= mrParent
.GetExtRefListener();
262 pRefMgr
->addLinkListener(nFileId
, pExtRefListener
);
263 pExtRefListener
->addFileId(nFileId
);
267 pRefMgr
->removeLinkListener(nFileId
, pExtRefListener
);
268 pExtRefListener
->removeFileId(nFileId
);
274 ScRefTokenHelper::getRangeFromToken(aRange
, pToken
, ScAddress(), bExternal
);
276 startListening(aRange
);
278 endListening(aRange
);
282 void startListening(const ScRange
& rRange
)
284 if (rRange
.aStart
== rRange
.aEnd
)
285 mpDoc
->StartListeningCell(rRange
.aStart
, &mrParent
);
287 mpDoc
->StartListeningArea(rRange
, false, &mrParent
);
290 void endListening(const ScRange
& rRange
)
292 if (rRange
.aStart
== rRange
.aEnd
)
293 mpDoc
->EndListeningCell(rRange
.aStart
, &mrParent
);
295 mpDoc
->EndListeningArea(rRange
, false, &mrParent
);
299 ScChartListener
& mrParent
;
305 void ScChartListener::StartListeningTo()
307 if (!mpTokens
.get() || mpTokens
->empty())
308 // no references to listen to.
311 for_each(mpTokens
->begin(), mpTokens
->end(), StartEndListening(mpDoc
, *this, true));
314 void ScChartListener::EndListeningTo()
316 if (!mpTokens
.get() || mpTokens
->empty())
317 // no references to listen to.
320 for_each(mpTokens
->begin(), mpTokens
->end(), StartEndListening(mpDoc
, *this, false));
323 void ScChartListener::ChangeListening( const ScRangeListRef
& rRangeListRef
,
327 SetRangeList( rRangeListRef
);
333 void ScChartListener::UpdateScheduledSeriesRanges()
335 if ( bSeriesRangesScheduled
)
337 bSeriesRangesScheduled
= false;
338 UpdateSeriesRanges();
342 void ScChartListener::UpdateChartIntersecting( const ScRange
& rRange
)
345 ScRefTokenHelper::getTokenFromRange(pToken
, rRange
);
347 if (ScRefTokenHelper::intersects(*mpTokens
, pToken
, ScAddress()))
349 // force update (chart has to be loaded), don't use ScChartListener::Update
350 mpDoc
->UpdateChart(GetName());
354 void ScChartListener::UpdateSeriesRanges()
356 ScRangeListRef
pRangeList(new ScRangeList
);
357 ScRefTokenHelper::getRangeListFromTokens(*pRangeList
, *mpTokens
, ScAddress());
358 mpDoc
->SetChartRangeList(GetName(), pRangeList
);
361 ScChartListener::ExternalRefListener
* ScChartListener::GetExtRefListener()
363 if (!mpExtRefListener
.get())
364 mpExtRefListener
.reset(new ExternalRefListener(*this, mpDoc
));
366 return mpExtRefListener
.get();
369 void ScChartListener::SetUpdateQueue()
372 mpDoc
->GetChartListenerCollection()->StartTimer();
375 bool ScChartListener::operator==( const ScChartListener
& r
) const
377 bool b1
= (mpTokens
.get() && !mpTokens
->empty());
378 bool b2
= (r
.mpTokens
.get() && !r
.mpTokens
->empty());
380 if (mpDoc
!= r
.mpDoc
|| bUsed
!= r
.bUsed
|| bDirty
!= r
.bDirty
||
381 bSeriesRangesScheduled
!= r
.bSeriesRangesScheduled
||
382 GetName() != r
.GetName() || b1
!= b2
)
386 // both token list instances are empty.
389 return *mpTokens
== *r
.mpTokens
;
392 bool ScChartListener::operator!=( const ScChartListener
& r
) const
394 return !operator==(r
);
397 ScChartHiddenRangeListener::ScChartHiddenRangeListener()
401 ScChartHiddenRangeListener::~ScChartHiddenRangeListener()
406 // ScChartListenerCollection
407 ScChartListenerCollection::RangeListenerItem::RangeListenerItem(const ScRange
& rRange
, ScChartHiddenRangeListener
* p
) :
408 maRange(rRange
), mpListener(p
)
412 ScChartListenerCollection::ScChartListenerCollection( ScDocument
* pDocP
) :
413 meModifiedDuringUpdate( SC_CLCUPDATE_NONE
),
416 aIdle
.SetIdleHdl( LINK( this, ScChartListenerCollection
, TimerHdl
) );
419 ScChartListenerCollection::ScChartListenerCollection(
420 const ScChartListenerCollection
& rColl
) :
421 meModifiedDuringUpdate( SC_CLCUPDATE_NONE
),
424 aIdle
.SetIdleHdl( LINK( this, ScChartListenerCollection
, TimerHdl
) );
427 ScChartListenerCollection::~ScChartListenerCollection()
429 // remove ChartListener objects before aIdle dtor is called, because
430 // ScChartListener::EndListeningTo may cause ScChartListenerCollection::StartTimer
431 // to be called if an empty ScNoteCell is deleted
436 void ScChartListenerCollection::StartAllListeners()
438 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
439 for (; it
!= itEnd
; ++it
)
440 it
->second
->StartListeningTo();
443 void ScChartListenerCollection::insert(ScChartListener
* pListener
)
445 if (meModifiedDuringUpdate
== SC_CLCUPDATE_RUNNING
)
446 meModifiedDuringUpdate
= SC_CLCUPDATE_MODIFIED
;
447 OUString aName
= pListener
->GetName();
448 maListeners
.insert(aName
, pListener
);
451 void ScChartListenerCollection::removeByName(const OUString
& rName
)
453 if (meModifiedDuringUpdate
== SC_CLCUPDATE_RUNNING
)
454 meModifiedDuringUpdate
= SC_CLCUPDATE_MODIFIED
;
455 maListeners
.erase(rName
);
458 ScChartListener
* ScChartListenerCollection::findByName(const OUString
& rName
)
460 ListenersType::iterator it
= maListeners
.find(rName
);
461 return it
== maListeners
.end() ? NULL
: it
->second
;
464 const ScChartListener
* ScChartListenerCollection::findByName(const OUString
& rName
) const
466 ListenersType::const_iterator it
= maListeners
.find(rName
);
467 return it
== maListeners
.end() ? NULL
: it
->second
;
470 bool ScChartListenerCollection::hasListeners() const
472 return !maListeners
.empty();
475 OUString
ScChartListenerCollection::getUniqueName(const OUString
& rPrefix
) const
477 for (sal_Int32 nNum
= 1; nNum
< 10000; ++nNum
) // arbitrary limit to prevent infinite loop.
479 OUStringBuffer
aBuf(rPrefix
);
481 OUString aTestName
= aBuf
.makeStringAndClear();
482 if (maListeners
.find(aTestName
) == maListeners
.end())
488 void ScChartListenerCollection::ChangeListening( const OUString
& rName
,
489 const ScRangeListRef
& rRangeListRef
, bool bDirty
)
491 ScChartListener
* pCL
= findByName(rName
);
494 pCL
->EndListeningTo();
495 pCL
->SetRangeList( rRangeListRef
);
499 pCL
= new ScChartListener(rName
, pDoc
, rRangeListRef
);
502 pCL
->StartListeningTo();
504 pCL
->SetDirty( true );
509 class InsertChartListener
: public std::unary_function
<ScChartListener
*, void>
511 ScChartListenerCollection::ListenersType
& mrListeners
;
513 InsertChartListener(ScChartListenerCollection::ListenersType
& rListeners
) :
514 mrListeners(rListeners
) {}
516 void operator() (ScChartListener
* p
)
518 OUString aName
= p
->GetName();
519 mrListeners
.insert(aName
, p
);
525 void ScChartListenerCollection::FreeUnused()
527 if (meModifiedDuringUpdate
== SC_CLCUPDATE_RUNNING
)
528 meModifiedDuringUpdate
= SC_CLCUPDATE_MODIFIED
;
530 ListenersType aUsed
, aUnused
;
532 // First, filter each listener into 'used' and 'unused' categories.
534 while(!maListeners
.empty())
536 ScChartListener
* p
= maListeners
.begin()->second
;
539 // We don't delete UNO charts; they are to be deleted separately via FreeUno().
540 aUsed
.transfer(maListeners
.begin(), maListeners
);
547 aUsed
.transfer(maListeners
.begin(), maListeners
);
550 aUnused
.transfer(maListeners
.begin(), maListeners
);
555 std::swap(aUsed
, maListeners
);
558 void ScChartListenerCollection::FreeUno( const uno::Reference
< chart::XChartDataChangeEventListener
>& rListener
,
559 const uno::Reference
< chart::XChartData
>& rSource
)
561 if (meModifiedDuringUpdate
== SC_CLCUPDATE_RUNNING
)
562 meModifiedDuringUpdate
= SC_CLCUPDATE_MODIFIED
;
564 std::vector
<ScChartListener
*> aUsed
, aUnused
;
566 // First, filter each listener into 'used' and 'unused' categories.
568 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
569 for (; it
!= itEnd
; ++it
)
571 ScChartListener
* p
= it
->second
;
572 if (p
->IsUno() && p
->GetUnoListener() == rListener
&& p
->GetUnoSource() == rSource
)
573 aUnused
.push_back(p
);
579 // Release all pointers currently managed by the ptr_map container.
580 // coverity[leaked_storage] - no leak, because because we will take care of them below
581 maListeners
.release().release();
583 // Re-insert the listeners we need to keep.
584 std::for_each(aUsed
.begin(), aUsed
.end(), InsertChartListener(maListeners
));
586 // Now, delete the ones no longer needed.
587 std::for_each(aUnused
.begin(), aUnused
.end(), boost::checked_deleter
<ScChartListener
>());
590 void ScChartListenerCollection::StartTimer()
592 aIdle
.SetPriority( SchedulerPriority::REPAINT
);
596 IMPL_LINK_NOARG_TYPED(ScChartListenerCollection
, TimerHdl
, Idle
*, void)
598 if ( Application::AnyInput( VclInputFlags::KEYBOARD
) )
606 void ScChartListenerCollection::UpdateDirtyCharts()
608 // During ScChartListener::Update() the most nasty things can happen due to
609 // UNO listeners, e.g. reentrant calls via BASIC to insert() and FreeUno()
610 // and similar that modify maListeners and invalidate iterators.
611 meModifiedDuringUpdate
= SC_CLCUPDATE_RUNNING
;
613 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
614 for (; it
!= itEnd
; ++it
)
616 ScChartListener
* p
= it
->second
;
620 if (meModifiedDuringUpdate
== SC_CLCUPDATE_MODIFIED
)
621 break; // iterator is invalid
623 if (aIdle
.IsActive() && !pDoc
->IsImportingXML())
624 break; // one interfered
626 meModifiedDuringUpdate
= SC_CLCUPDATE_NONE
;
629 void ScChartListenerCollection::SetDirty()
631 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
632 for (; it
!= itEnd
; ++it
)
633 it
->second
->SetDirty(true);
638 void ScChartListenerCollection::SetDiffDirty(
639 const ScChartListenerCollection
& rCmp
, bool bSetChartRangeLists
)
642 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
643 for (; it
!= itEnd
; ++it
)
645 ScChartListener
* pCL
= it
->second
;
647 const ScChartListener
* pCLCmp
= rCmp
.findByName(pCL
->GetName());
648 if (!pCLCmp
|| *pCL
!= *pCLCmp
)
650 if ( bSetChartRangeLists
)
654 const ScRangeListRef
& rList1
= pCL
->GetRangeList();
655 const ScRangeListRef
& rList2
= pCLCmp
->GetRangeList();
656 bool b1
= rList1
.Is();
657 bool b2
= rList2
.Is();
658 if ( b1
!= b2
|| (b1
&& b2
&& (*rList1
!= *rList2
)) )
659 pDoc
->SetChartRangeList( pCL
->GetName(), rList1
);
662 pDoc
->SetChartRangeList( pCL
->GetName(), pCL
->GetRangeList() );
665 pCL
->SetDirty( true );
672 void ScChartListenerCollection::SetRangeDirty( const ScRange
& rRange
)
675 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
676 for (; it
!= itEnd
; ++it
)
678 ScChartListener
* pCL
= it
->second
;
679 const ScRangeListRef
& rList
= pCL
->GetRangeList();
680 if ( rList
.Is() && rList
->Intersects( rRange
) )
683 pCL
->SetDirty( true );
689 // New hidden range listener implementation
690 for (list
<RangeListenerItem
>::iterator itr
= maHiddenListeners
.begin(), itrEnd
= maHiddenListeners
.end();
691 itr
!= itrEnd
; ++itr
)
693 if (itr
->maRange
.Intersects(rRange
))
694 itr
->mpListener
->notify();
698 void ScChartListenerCollection::UpdateScheduledSeriesRanges()
700 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
701 for (; it
!= itEnd
; ++it
)
702 it
->second
->UpdateScheduledSeriesRanges();
705 void ScChartListenerCollection::UpdateChartsContainingTab( SCTAB nTab
)
707 ScRange
aRange( 0, 0, nTab
, MAXCOL
, MAXROW
, nTab
);
708 ListenersType::iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
709 for (; it
!= itEnd
; ++it
)
710 it
->second
->UpdateChartIntersecting(aRange
);
713 bool ScChartListenerCollection::operator==( const ScChartListenerCollection
& r
) const
715 // Do not use ScStrCollection::operator==() here that uses IsEqual und Compare.
716 // Use ScChartListener::operator==() instead.
717 if (pDoc
!= r
.pDoc
|| maListeners
.size() != r
.maListeners
.size())
720 ListenersType::const_iterator it
= maListeners
.begin(), itEnd
= maListeners
.end();
721 ListenersType::const_iterator it2
= r
.maListeners
.begin();
722 for (; it
!= itEnd
; ++it
, ++it2
)
730 bool ScChartListenerCollection::operator!=( const ScChartListenerCollection
& r
) const
732 return !operator==(r
);
735 void ScChartListenerCollection::StartListeningHiddenRange( const ScRange
& rRange
, ScChartHiddenRangeListener
* pListener
)
737 RangeListenerItem
aItem(rRange
, pListener
);
738 maHiddenListeners
.push_back(aItem
);
743 struct MatchListener
: public ::std::unary_function
<
744 ScChartListenerCollection::RangeListenerItem
, bool>
746 MatchListener(const ScChartHiddenRangeListener
* pMatch
) :
751 bool operator() (const ScChartListenerCollection::RangeListenerItem
& rItem
) const
753 return mpMatch
== rItem
.mpListener
;
757 const ScChartHiddenRangeListener
* mpMatch
;
761 void ScChartListenerCollection::EndListeningHiddenRange( ScChartHiddenRangeListener
* pListener
)
763 maHiddenListeners
.remove_if(MatchListener(pListener
));
766 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */