Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / tool / chartlis.cxx
blobd5193fa58e49006f424da3521afc80517b89ab3d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
23 #include "brdcst.hxx"
24 #include "document.hxx"
25 #include "reftokenhelper.hxx"
26 #include "stlalgorithm.hxx"
28 using namespace com::sun::star;
29 using ::std::vector;
30 using ::std::list;
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
39 class ScChartUnoData
41 uno::Reference< chart::XChartDataChangeEventListener > xListener;
42 uno::Reference< chart::XChartData > xSource;
44 public:
45 ScChartUnoData( const uno::Reference< chart::XChartDataChangeEventListener >& rL,
46 const uno::Reference< chart::XChartData >& rS ) :
47 xListener( rL ), xSource( rS ) {}
48 ~ScChartUnoData() {}
50 const uno::Reference< chart::XChartDataChangeEventListener >& GetListener() const { return xListener; }
51 const uno::Reference< chart::XChartData >& GetSource() const { return xSource; }
54 // ScChartListener
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.
64 return;
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)
72 switch (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();
81 break;
82 case ScExternalRefManager::LINK_BROKEN:
83 removeFileId(nFileId);
84 break;
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()
100 return maFileIds;
103 ScChartListener::ScChartListener( const OUString& rName, ScDocument* pDocP,
104 const ScRangeListRef& rRangeList ) :
105 SvtListener(),
106 mpExtRefListener(NULL),
107 mpTokens(new vector<ScTokenRef>),
108 maName(rName),
109 pUnoData( NULL ),
110 mpDoc( pDocP ),
111 bUsed( false ),
112 bDirty( false ),
113 bSeriesRangesScheduled( false )
115 ScRefTokenHelper::getTokensFromRangeList(*mpTokens, *rRangeList);
118 ScChartListener::ScChartListener( const OUString& rName, ScDocument* pDocP, vector<ScTokenRef>* pTokens ) :
119 SvtListener(),
120 mpExtRefListener(NULL),
121 mpTokens(pTokens),
122 maName(rName),
123 pUnoData( NULL ),
124 mpDoc( pDocP ),
125 bUsed( false ),
126 bDirty( false ),
127 bSeriesRangesScheduled( false )
131 ScChartListener::ScChartListener( const ScChartListener& r ) :
132 SvtListener(),
133 mpExtRefListener(NULL),
134 mpTokens(new vector<ScTokenRef>(*r.mpTokens)),
135 maName(r.maName),
136 pUnoData( NULL ),
137 mpDoc( r.mpDoc ),
138 bUsed( false ),
139 bDirty( r.bDirty ),
140 bSeriesRangesScheduled( r.bSeriesRangesScheduled )
142 if ( r.pUnoData )
143 pUnoData = new ScChartUnoData( *r.pUnoData );
145 if (r.mpExtRefListener.get())
147 // Re-register this new listener for the files that the old listener
148 // was listening to.
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() )
165 EndListeningTo();
166 delete pUnoData;
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
181 return maName;
184 void ScChartListener::SetUno(
185 const uno::Reference< chart::XChartDataChangeEventListener >& rListener,
186 const uno::Reference< chart::XChartData >& rSource )
188 delete pUnoData;
189 pUnoData = new ScChartUnoData( rListener, rSource );
192 uno::Reference< chart::XChartDataChangeEventListener > ScChartListener::GetUnoListener() const
194 if ( pUnoData )
195 return pUnoData->GetListener();
196 return uno::Reference< chart::XChartDataChangeEventListener >();
199 uno::Reference< chart::XChartData > ScChartListener::GetUnoSource() const
201 if ( pUnoData )
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))
210 SetUpdateQueue();
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();
220 return ;
222 if ( pUnoData )
224 bDirty = false;
225 // recognize some day what has changed inside the Chart
226 chart::ChartDataChangeEvent aEvent( pUnoData->GetSource(),
227 chart::ChartDataChangeType_ALL,
228 0, 0, 0, 0 );
229 pUnoData->GetListener()->chartDataChanged( aEvent );
231 else if ( mpDoc->GetAutoCalc() )
233 bDirty = false;
234 mpDoc->UpdateChart(GetName());
238 ScRangeListRef ScChartListener::GetRangeList() const
240 ScRangeListRef aRLRef(new ScRangeList);
241 ScRefTokenHelper::getRangeListFromTokens(*aRLRef, *mpTokens, ScAddress());
242 return aRLRef;
245 void ScChartListener::SetRangeList( const ScRangeListRef& rNew )
247 vector<ScTokenRef> aTokens;
248 ScRefTokenHelper::getTokensFromRangeList(aTokens, *rNew);
249 mpTokens->swap(aTokens);
252 namespace {
254 class StartEndListening : public unary_function<ScTokenRef, void>
256 public:
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))
263 return;
265 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
266 if (bExternal)
268 sal_uInt16 nFileId = pToken->GetIndex();
269 ScExternalRefManager* pRefMgr = mpDoc->GetExternalRefManager();
270 ScChartListener::ExternalRefListener* pExtRefListener = mrParent.GetExtRefListener();
271 if (mbStart)
273 pRefMgr->addLinkListener(nFileId, pExtRefListener);
274 pExtRefListener->addFileId(nFileId);
276 else
278 pRefMgr->removeLinkListener(nFileId, pExtRefListener);
279 pExtRefListener->removeFileId(nFileId);
282 else
284 ScRange aRange;
285 ScRefTokenHelper::getRangeFromToken(aRange, pToken, ScAddress(), bExternal);
286 if (mbStart)
287 startListening(aRange);
288 else
289 endListening(aRange);
292 private:
293 void startListening(const ScRange& rRange)
295 if (rRange.aStart == rRange.aEnd)
296 mpDoc->StartListeningCell(rRange.aStart, &mrParent);
297 else
298 mpDoc->StartListeningArea(rRange, &mrParent);
301 void endListening(const ScRange& rRange)
303 if (rRange.aStart == rRange.aEnd)
304 mpDoc->EndListeningCell(rRange.aStart, &mrParent);
305 else
306 mpDoc->EndListeningArea(rRange, &mrParent);
308 private:
309 ScDocument* mpDoc;
310 ScChartListener& mrParent;
311 bool mbStart;
316 void ScChartListener::StartListeningTo()
318 if (!mpTokens.get() || mpTokens->empty())
319 // no references to listen to.
320 return;
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.
329 return;
331 for_each(mpTokens->begin(), mpTokens->end(), StartEndListening(mpDoc, *this, false));
334 void ScChartListener::ChangeListening( const ScRangeListRef& rRangeListRef,
335 bool bDirtyP )
337 EndListeningTo();
338 SetRangeList( rRangeListRef );
339 StartListeningTo();
340 if ( bDirtyP )
341 SetDirty( true );
344 void ScChartListener::UpdateScheduledSeriesRanges()
346 if ( bSeriesRangesScheduled )
348 bSeriesRangesScheduled = false;
349 UpdateSeriesRanges();
353 void ScChartListener::UpdateChartIntersecting( const ScRange& rRange )
355 ScTokenRef pToken;
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()
382 bDirty = true;
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)
394 return false;
396 if (!b1 && !b2)
397 // both token list instances are empty.
398 return true;
400 return *mpTokens == *r.mpTokens;
403 bool ScChartListener::operator!=( const ScChartListener& r ) const
405 return !operator==(r);
408 ScChartHiddenRangeListener::ScChartHiddenRangeListener()
412 ScChartHiddenRangeListener::~ScChartHiddenRangeListener()
414 // empty d'tor
417 // ScChartListenerCollection
418 ScChartListenerCollection::RangeListenerItem::RangeListenerItem(const ScRange& rRange, ScChartHiddenRangeListener* p) :
419 maRange(rRange), mpListener(p)
423 ScChartListenerCollection::ScChartListenerCollection( ScDocument* pDocP ) :
424 pDoc( pDocP )
426 aTimer.SetTimeoutHdl( LINK( this, ScChartListenerCollection, TimerHdl ) );
429 ScChartListenerCollection::ScChartListenerCollection(
430 const ScChartListenerCollection& rColl ) :
431 pDoc( rColl.pDoc )
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
442 maListeners.clear();
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
482 return maListeners;
485 ScChartListenerCollection::ListenersType& ScChartListenerCollection::getListeners()
487 return maListeners;
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);
500 aBuf.append(nNum);
501 OUString aTestName = aBuf.makeStringAndClear();
502 if (maListeners.find(aTestName) == maListeners.end())
503 return aTestName;
505 return OUString();
508 void ScChartListenerCollection::ChangeListening( const OUString& rName,
509 const ScRangeListRef& rRangeListRef, bool bDirty )
511 ScChartListener* pCL = findByName(rName);
512 if (pCL)
514 pCL->EndListeningTo();
515 pCL->SetRangeList( rRangeListRef );
517 else
519 pCL = new ScChartListener(rName, pDoc, rRangeListRef);
520 insert(pCL);
522 pCL->StartListeningTo();
523 if ( bDirty )
524 pCL->SetDirty( true );
527 namespace {
529 class InsertChartListener : public std::unary_function<ScChartListener*, void>
531 ScChartListenerCollection::ListenersType& mrListeners;
532 public:
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;
555 if (p->IsUno())
557 // We don't delete UNO charts; they are to be deleted separately via FreeUno().
558 aUsed.push_back(p);
559 continue;
562 if (p->IsUsed())
564 p->SetUsed(false);
565 aUsed.push_back(p);
567 else
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);
595 else
596 aUsed.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 );
613 aTimer.Start();
616 IMPL_LINK_NOARG(ScChartListenerCollection, TimerHdl)
618 if ( Application::AnyInput( VCL_INPUT_KEYBOARD ) )
620 aTimer.Start();
621 return 0;
623 UpdateDirtyCharts();
624 return 0;
627 void ScChartListenerCollection::UpdateDirtyCharts()
629 ListenersType::iterator it = maListeners.begin(), itEnd = maListeners.end();
630 for (; it != itEnd; ++it)
632 ScChartListener* p = it->second;
633 if (p->IsDirty())
634 p->Update();
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);
647 StartTimer();
650 void ScChartListenerCollection::SetDiffDirty(
651 const ScChartListenerCollection& rCmp, bool bSetChartRangeLists )
653 bool bDirty = false;
654 ListenersType::iterator it = maListeners.begin(), itEnd = maListeners.end();
655 for (; it != itEnd; ++it)
657 ScChartListener* pCL = it->second;
658 OSL_ASSERT(pCL);
659 const ScChartListener* pCLCmp = rCmp.findByName(pCL->GetName());
660 if (!pCLCmp || *pCL != *pCLCmp)
662 if ( bSetChartRangeLists )
664 if (pCLCmp)
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 );
673 else
674 pDoc->SetChartRangeList( pCL->GetName(), pCL->GetRangeList() );
676 bDirty = true;
677 pCL->SetDirty( true );
680 if ( bDirty )
681 StartTimer();
684 void ScChartListenerCollection::SetRangeDirty( const ScRange& rRange )
686 bool bDirty = false;
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 ) )
694 bDirty = true;
695 pCL->SetDirty( true );
698 if ( bDirty )
699 StartTimer();
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())
730 return false;
732 ListenersType::const_iterator it = maListeners.begin(), itEnd = maListeners.end();
733 ListenersType::const_iterator it2 = r.maListeners.begin();
734 for (; it != itEnd; ++it, ++it2)
736 if (*it != *it2)
737 return false;
739 return true;
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);
753 namespace {
755 struct MatchListener : public ::std::unary_function<
756 ScChartListenerCollection::RangeListenerItem, bool>
758 MatchListener(const ScChartHiddenRangeListener* pMatch) :
759 mpMatch(pMatch)
763 bool operator() (const ScChartListenerCollection::RangeListenerItem& rItem) const
765 return mpMatch == rItem.mpListener;
768 private:
769 const ScChartHiddenRangeListener* mpMatch;
773 void ScChartListenerCollection::EndListeningHiddenRange( ScChartHiddenRangeListener* pListener )
775 maHiddenListeners.remove_if(MatchListener(pListener));
778 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */