LibreOffice Module sc (master) 1
chartlis.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <memory>
21#include <utility>
22#include <vcl/svapp.hxx>
23
24#include <chartlis.hxx>
25#include <brdcst.hxx>
26#include <document.hxx>
27#include <reftokenhelper.hxx>
28#include <formula/token.hxx>
29#include <com/sun/star/chart/XChartDataChangeEventListener.hpp>
30
31using namespace com::sun::star;
32using ::std::vector;
33using ::std::for_each;
34
35// Update chart listeners quickly, to get a similar behavior to loaded charts
36// which register UNO listeners.
37
39{
40 uno::Reference< chart::XChartDataChangeEventListener > xListener;
41 uno::Reference< chart::XChartData > xSource;
42
43public:
44 ScChartUnoData( uno::Reference< chart::XChartDataChangeEventListener > xL,
45 uno::Reference< chart::XChartData > xS ) :
46 xListener(std::move( xL )), xSource(std::move( xS )) {}
47
48 const uno::Reference< chart::XChartDataChangeEventListener >& GetListener() const { return xListener; }
49 const uno::Reference< chart::XChartData >& GetSource() const { return xSource; }
50};
51
52// ScChartListener
54 mrParent(rParent), m_pDoc(&rDoc)
55{
56}
57
59{
60 if (!m_pDoc || m_pDoc->IsInDtorClear())
61 // The document is being destroyed. Do nothing.
62 return;
63
64 // Make sure to remove all pointers to this object.
65 m_pDoc->GetExternalRefManager()->removeLinkListener(this);
66}
67
69{
70 switch (eType)
71 {
73 {
74 if (maFileIds.count(nFileId))
75 // We are listening to this external document. Send an update
76 // request to the chart.
77 mrParent.SetUpdateQueue();
78 }
79 break;
81 removeFileId(nFileId);
82 break;
84 m_pDoc = nullptr;
85 break;
86 }
87}
88
90{
91 maFileIds.insert(nFileId);
92}
93
95{
96 maFileIds.erase(nFileId);
97}
98
100 const ScRangeListRef& rRangeList ) :
101 maName(std::move(aName)),
102 mrDoc( rDocP ),
103 bUsed( false ),
104 bDirty( false )
105{
107}
108
109ScChartListener::ScChartListener( OUString aName, ScDocument& rDocP, vector<ScTokenRef> aTokens ) :
110 maTokens(std::move(aTokens)),
111 maName(std::move(aName)),
112 mrDoc( rDocP ),
113 bUsed( false ),
114 bDirty( false )
115{
116}
117
119{
120 if ( HasBroadcaster() )
122 pUnoData.reset();
123
125 {
126 // Stop listening to all external files.
128 const std::unordered_set<sal_uInt16>& rFileIds = mpExtRefListener->getAllFileIds();
129 for (const auto& rFileId : rFileIds)
130 pRefMgr->removeLinkListener(rFileId, mpExtRefListener.get());
131 }
132}
133
135 const uno::Reference< chart::XChartDataChangeEventListener >& rListener,
136 const uno::Reference< chart::XChartData >& rSource )
137{
138 pUnoData.reset( new ScChartUnoData( rListener, rSource ) );
139}
140
141uno::Reference< chart::XChartDataChangeEventListener > ScChartListener::GetUnoListener() const
142{
143 if ( pUnoData )
144 return pUnoData->GetListener();
145 return uno::Reference< chart::XChartDataChangeEventListener >();
146}
147
148uno::Reference< chart::XChartData > ScChartListener::GetUnoSource() const
149{
150 if ( pUnoData )
151 return pUnoData->GetSource();
152 return uno::Reference< chart::XChartData >();
153}
154
156{
157 if (rHint.GetId() == SfxHintId::ScDataChanged)
159}
160
162{
163 if ( mrDoc.IsInInterpreter() )
164 { // If interpreting do nothing and restart timer so we don't
165 // interfere with interpreter and don't produce an Err522 or similar.
166 // This may happen if we are rescheduled via Basic function.
168 return ;
169 }
170 if ( pUnoData )
171 {
172 bDirty = false;
173 // recognize some day what has changed inside the Chart
174 chart::ChartDataChangeEvent aEvent( pUnoData->GetSource(),
175 chart::ChartDataChangeType_ALL,
176 0, 0, 0, 0 );
177 pUnoData->GetListener()->chartDataChanged( aEvent );
178 }
179 else if ( mrDoc.GetAutoCalc() )
180 {
181 bDirty = false;
183 }
184}
185
187{
188 ScRangeListRef aRLRef(new ScRangeList);
190 return aRLRef;
191}
192
194{
195 vector<ScTokenRef> aTokens;
197 maTokens.swap(aTokens);
198}
199
200namespace {
201
202class StartEndListening
203{
204public:
205 StartEndListening(ScDocument& rDoc, ScChartListener& rParent, bool bStart) :
206 mrDoc(rDoc), mrParent(rParent), mbStart(bStart) {}
207
208 void operator() (const ScTokenRef& pToken)
209 {
210 if (!ScRefTokenHelper::isRef(pToken))
211 return;
212
213 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
214 if (bExternal)
215 {
216 sal_uInt16 nFileId = pToken->GetIndex();
217 ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
218 ScChartListener::ExternalRefListener* pExtRefListener = mrParent.GetExtRefListener();
219 if (mbStart)
220 {
221 pRefMgr->addLinkListener(nFileId, pExtRefListener);
222 pExtRefListener->addFileId(nFileId);
223 }
224 else
225 {
226 pRefMgr->removeLinkListener(nFileId, pExtRefListener);
227 pExtRefListener->removeFileId(nFileId);
228 }
229 }
230 else
231 {
232 ScRange aRange;
233 ScRefTokenHelper::getRangeFromToken(&mrDoc, aRange, pToken, ScAddress(), bExternal);
234 if (mbStart)
235 startListening(aRange);
236 else
237 endListening(aRange);
238 }
239 }
240private:
241 void startListening(const ScRange& rRange)
242 {
243 if (rRange.aStart == rRange.aEnd)
244 mrDoc.StartListeningCell(rRange.aStart, &mrParent);
245 else
246 mrDoc.StartListeningArea(rRange, false, &mrParent);
247 }
248
249 void endListening(const ScRange& rRange)
250 {
251 if (rRange.aStart == rRange.aEnd)
252 mrDoc.EndListeningCell(rRange.aStart, &mrParent);
253 else
254 mrDoc.EndListeningArea(rRange, false, &mrParent);
255 }
256private:
257 ScDocument& mrDoc;
259 bool mbStart;
260};
261
262}
263
265{
266 if (maTokens.empty())
267 // no references to listen to.
268 return;
269
270 for_each(maTokens.begin(), maTokens.end(), StartEndListening(mrDoc, *this, true));
271}
272
274{
275 if (maTokens.empty())
276 // no references to listen to.
277 return;
278
279 for_each(maTokens.begin(), maTokens.end(), StartEndListening(mrDoc, *this, false));
280}
281
283 bool bDirtyP )
284{
286 SetRangeList( rRangeListRef );
288 if ( bDirtyP )
289 SetDirty( true );
290}
291
293{
294 ScTokenRef pToken;
296
298 {
299 // force update (chart has to be loaded), don't use ScChartListener::Update
301 }
302}
303
305{
306 if (!mpExtRefListener)
307 mpExtRefListener.reset(new ExternalRefListener(*this, mrDoc));
308
309 return mpExtRefListener.get();
310}
311
313{
314 bDirty = true;
316}
317
319{
320 bool b1 = !maTokens.empty();
321 bool b2 = !r.maTokens.empty();
322
323 if (&mrDoc != &r.mrDoc || bUsed != r.bUsed || bDirty != r.bDirty ||
324 GetName() != r.GetName() || b1 != b2)
325 return false;
326
327 if (!b1 && !b2)
328 // both token list instances are empty.
329 return true;
330
331 return maTokens == r.maTokens;
332}
333
335{
336 return !operator==(r);
337}
338
340{
341}
342
344{
345 // empty d'tor
346}
347
349{
351 aIdle.SetPriority( TaskPriority::REPAINT );
352}
353
355 meModifiedDuringUpdate( SC_CLCUPDATE_NONE ),
356 aIdle( "sc::ScChartListenerCollection aIdle" ),
357 rDoc( rDocP )
358{
359 Init();
360}
361
363 const ScChartListenerCollection& rColl ) :
364 meModifiedDuringUpdate( SC_CLCUPDATE_NONE ),
365 aIdle( "sc::ScChartListenerCollection aIdle" ),
366 rDoc( rColl.rDoc )
367{
368 Init();
369}
370
372{
373 // remove ChartListener objects before aIdle dtor is called, because
374 // ScChartListener::EndListeningTo may cause ScChartListenerCollection::StartTimer
375 // to be called if an empty ScNoteCell is deleted
376
377 m_Listeners.clear();
378}
379
381{
382 for (auto const& it : m_Listeners)
383 {
384 it.second->StartListeningTo();
385 }
386}
387
389{
392 OUString aName = pListener->GetName();
393 return m_Listeners.insert(std::make_pair(aName, std::unique_ptr<ScChartListener>(pListener))).second;
394}
395
397{
400 m_Listeners.erase(rName);
401}
402
404{
405 ListenersType::iterator const it = m_Listeners.find(rName);
406 return it == m_Listeners.end() ? nullptr : it->second.get();
407}
408
410{
411 ListenersType::const_iterator const it = m_Listeners.find(rName);
412 return it == m_Listeners.end() ? nullptr : it->second.get();
413}
414
416{
417 return !m_Listeners.empty();
418}
419
420OUString ScChartListenerCollection::getUniqueName(std::u16string_view rPrefix) const
421{
422 for (sal_Int32 nNum = 1; nNum < 10000; ++nNum) // arbitrary limit to prevent infinite loop.
423 {
424 OUString aTestName = rPrefix + OUString::number(nNum);
425 if (m_Listeners.find(aTestName) == m_Listeners.end())
426 return aTestName;
427 }
428 return OUString();
429}
430
432 const ScRangeListRef& rRangeListRef )
433{
434 ScChartListener* pCL = findByName(rName);
435 if (pCL)
436 {
437 pCL->EndListeningTo();
438 pCL->SetRangeList( rRangeListRef );
439 }
440 else
441 {
442 pCL = new ScChartListener(rName, rDoc, rRangeListRef);
443 insert(pCL);
444 }
445 pCL->StartListeningTo();
446}
447
449{
452
453 ListenersType aUsed;
454
455 for (auto & pair : m_Listeners)
456 {
457 ScChartListener* p = pair.second.get();
458 if (p->IsUno())
459 {
460 // We don't delete UNO charts; they are to be deleted separately via FreeUno().
461 aUsed.insert(std::make_pair(pair.first, std::move(pair.second)));
462 continue;
463 }
464
465 if (p->IsUsed())
466 {
467 p->SetUsed(false);
468 aUsed.insert(std::make_pair(pair.first, std::move(pair.second)));
469 }
470 }
471
472 m_Listeners = std::move(aUsed);
473}
474
475void ScChartListenerCollection::FreeUno( const uno::Reference< chart::XChartDataChangeEventListener >& rListener,
476 const uno::Reference< chart::XChartData >& rSource )
477{
480
481 for (auto it = m_Listeners.begin(); it != m_Listeners.end(); )
482 {
483 ScChartListener *const p = it->second.get();
484 if (p->IsUno() && p->GetUnoListener() == rListener && p->GetUnoSource() == rSource)
485 it = m_Listeners.erase(it);
486 else
487 ++it;
488 }
489}
490
492{
493 aIdle.Start();
494}
495
497{
498 if ( Application::AnyInput( VclInputFlags::KEYBOARD ) )
499 {
500 aIdle.Start();
501 return;
502 }
503 UpdateDirtyCharts();
504}
505
507{
508 // During ScChartListener::Update() the most nasty things can happen due to
509 // UNO listeners, e.g. reentrant calls via BASIC to insert() and FreeUno()
510 // and similar that modify m_Listeners and invalidate iterators.
512
513 for (auto const& it : m_Listeners)
514 {
515 ScChartListener *const p = it.second.get();
516 if (p->IsDirty())
517 p->Update();
518
520 break; // iterator is invalid
521
522 if (aIdle.IsActive() && !rDoc.IsImportingXML())
523 break; // one interfered
524 }
526}
527
529{
530 for (auto const& it : m_Listeners)
531 {
532 it.second->SetDirty(true);
533 }
534
535 StartTimer();
536}
537
539 const ScChartListenerCollection& rCmp, bool bSetChartRangeLists )
540{
541 bool bDirty = false;
542 for (auto const& it : m_Listeners)
543 {
544 ScChartListener *const pCL = it.second.get();
545 assert(pCL);
546 const ScChartListener* pCLCmp = rCmp.findByName(pCL->GetName());
547 if (!pCLCmp || *pCL != *pCLCmp)
548 {
549 if ( bSetChartRangeLists )
550 {
551 if (pCLCmp)
552 {
553 const ScRangeListRef& rList1 = pCL->GetRangeList();
554 const ScRangeListRef& rList2 = pCLCmp->GetRangeList();
555 bool b1 = rList1.is();
556 bool b2 = rList2.is();
557 if ( b1 != b2 || (b1 && b2 && (*rList1 != *rList2)) )
558 rDoc.SetChartRangeList( pCL->GetName(), rList1 );
559 }
560 else
561 rDoc.SetChartRangeList( pCL->GetName(), pCL->GetRangeList() );
562 }
563 bDirty = true;
564 pCL->SetDirty( true );
565 }
566 }
567 if ( bDirty )
568 StartTimer();
569}
570
572{
573 bool bDirty = false;
574 for (auto const& it : m_Listeners)
575 {
576 ScChartListener *const pCL = it.second.get();
577 const ScRangeListRef& rList = pCL->GetRangeList();
578 if ( rList.is() && rList->Intersects( rRange ) )
579 {
580 bDirty = true;
581 pCL->SetDirty( true );
582 }
583 }
584 if ( bDirty )
585 StartTimer();
586
587 // New hidden range listener implementation
588 for (auto& [pListener, rHiddenRange] : maHiddenListeners)
589 {
590 if (rHiddenRange.Intersects(rRange))
591 {
592 pListener->notify();
593 }
594 }
595}
596
598{
599 ScRange aRange( 0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab );
600 for (auto const& it : m_Listeners)
601 {
602 it.second->UpdateChartIntersecting(aRange);
603 }
604}
605
607{
608 // Do not use ScStrCollection::operator==() here that uses IsEqual and Compare.
609 // Use ScChartListener::operator==() instead.
610 if (&rDoc != &r.rDoc)
611 return false;
612
613 return std::equal(m_Listeners.begin(), m_Listeners.end(), r.m_Listeners.begin(), r.m_Listeners.end(),
614 [](const ListenersType::value_type& lhs, const ListenersType::value_type& rhs) {
615 return (lhs.first == rhs.first) && (*lhs.second == *rhs.second);
616 });
617}
618
620{
621 maHiddenListeners.insert(std::make_pair<>(pListener, rRange));
622}
623
625{
626 auto range = maHiddenListeners.equal_range(pListener);
627 maHiddenListeners.erase(range.first, range.second);
628}
629
630/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
BaseContainerNodeSharedPtr & mrParent
AnyEventRef aEvent
OUString maName
IMPL_LINK_NOARG(ScChartListenerCollection, TimerHdl, Timer *, void)
Definition: chartlis.cxx:496
static bool AnyInput(VclInputFlags nType=VCL_INPUT_ANY)
virtual void Start(bool bStartTimer=true) override
virtual ~ScChartHiddenRangeListener()
Definition: chartlis.cxx:343
void EndListeningHiddenRange(ScChartHiddenRangeListener *pListener)
Remove all ranges associated with passed listener instance from the list of hidden range listeners.
Definition: chartlis.cxx:624
ScChartListener * findByName(const OUString &rName)
Definition: chartlis.cxx:403
void SetDiffDirty(const ScChartListenerCollection &, bool bSetChartRangeLists)
Definition: chartlis.cxx:538
bool insert(ScChartListener *pListener)
Definition: chartlis.cxx:388
void SetRangeDirty(const ScRange &rRange)
Definition: chartlis.cxx:571
void StartListeningHiddenRange(const ScRange &rRange, ScChartHiddenRangeListener *pListener)
Start listening on hide/show change within specified cell range.
Definition: chartlis.cxx:619
bool hasListeners() const
Definition: chartlis.cxx:415
void UpdateChartsContainingTab(SCTAB nTab)
Definition: chartlis.cxx:597
std::map< OUString, std::unique_ptr< ScChartListener > > ListenersType
Definition: chartlis.hxx:125
OUString getUniqueName(std::u16string_view rPrefix) const
Create a unique name that's not taken by any existing chart listener objects.
Definition: chartlis.cxx:420
ListenersType m_Listeners
Definition: chartlis.hxx:128
ScChartListenerCollection(ScDocument &rDoc)
Definition: chartlis.cxx:354
void FreeUno(const css::uno::Reference< css::chart::XChartDataChangeEventListener > &rListener, const css::uno::Reference< css::chart::XChartData > &rSource)
Definition: chartlis.cxx:475
enum ScChartListenerCollection::UpdateStatus meModifiedDuringUpdate
void removeByName(const OUString &rName)
Definition: chartlis.cxx:396
bool operator==(const ScChartListenerCollection &r) const
Definition: chartlis.cxx:606
void ChangeListening(const OUString &rName, const ScRangeListRef &rRangeListRef)
Definition: chartlis.cxx:431
std::unordered_multimap< ScChartHiddenRangeListener *, ScRange > maHiddenListeners
Definition: chartlis.hxx:136
virtual void notify(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) override
Definition: chartlis.cxx:68
virtual ~ExternalRefListener() override
Definition: chartlis.cxx:58
void addFileId(sal_uInt16 nFileId)
Definition: chartlis.cxx:89
ExternalRefListener(ScChartListener &rParent, ScDocument &rDoc)
Definition: chartlis.cxx:53
void removeFileId(sal_uInt16 nFileId)
Definition: chartlis.cxx:94
void SetRangeList(const ScRangeListRef &rNew)
Definition: chartlis.cxx:193
bool operator!=(const ScChartListener &r) const
Definition: chartlis.cxx:334
std::vector< ScTokenRef > maTokens
Definition: chartlis.hxx:65
void StartListeningTo()
Definition: chartlis.cxx:264
void SetUpdateQueue()
Definition: chartlis.cxx:312
void SetUno(const css::uno::Reference< css::chart::XChartDataChangeEventListener > &rListener, const css::uno::Reference< css::chart::XChartData > &rSource)
Definition: chartlis.cxx:134
void SetDirty(bool bFlg)
Definition: chartlis.hxx:103
OUString maName
Definition: chartlis.hxx:67
void EndListeningTo()
Definition: chartlis.cxx:273
std::unique_ptr< ScChartUnoData > pUnoData
Definition: chartlis.hxx:68
ScChartListener(OUString aName, ScDocument &rDoc, const ScRangeListRef &rRangeListRef)
Definition: chartlis.cxx:99
css::uno::Reference< css::chart::XChartDataChangeEventListener > GetUnoListener() const
Definition: chartlis.cxx:141
std::unique_ptr< ExternalRefListener > mpExtRefListener
Definition: chartlis.hxx:64
virtual ~ScChartListener() override
Definition: chartlis.cxx:118
void UpdateChartIntersecting(const ScRange &rRange)
Definition: chartlis.cxx:292
virtual void Notify(const SfxHint &rHint) override
Definition: chartlis.cxx:155
void ChangeListening(const ScRangeListRef &rRangeListRef, bool bDirty)
Definition: chartlis.cxx:282
ScDocument & mrDoc
Definition: chartlis.hxx:69
css::uno::Reference< css::chart::XChartData > GetUnoSource() const
Definition: chartlis.cxx:148
bool operator==(const ScChartListener &) const
Definition: chartlis.cxx:318
const OUString & GetName() const
Definition: chartlis.hxx:83
ExternalRefListener * GetExtRefListener()
Definition: chartlis.cxx:304
ScRangeListRef GetRangeList() const
Definition: chartlis.cxx:186
uno::Reference< chart::XChartData > xSource
Definition: chartlis.cxx:41
const uno::Reference< chart::XChartDataChangeEventListener > & GetListener() const
Definition: chartlis.cxx:48
ScChartUnoData(uno::Reference< chart::XChartDataChangeEventListener > xL, uno::Reference< chart::XChartData > xS)
Definition: chartlis.cxx:44
const uno::Reference< chart::XChartData > & GetSource() const
Definition: chartlis.cxx:49
uno::Reference< chart::XChartDataChangeEventListener > xListener
Definition: chartlis.cxx:40
SC_DLLPUBLIC SCCOL MaxCol() const
Definition: document.hxx:891
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:892
SC_DLLPUBLIC ScChartListenerCollection * GetChartListenerCollection() const
Definition: document.hxx:2228
SC_DLLPUBLIC ScExternalRefManager * GetExternalRefManager() const
Definition: documen3.cxx:633
SC_DLLPUBLIC bool GetAutoCalc() const
Definition: document.hxx:1411
bool IsInInterpreter() const
Definition: document.hxx:2405
void UpdateChart(const OUString &rName)
Definition: documen5.cxx:335
void SetChartRangeList(std::u16string_view rChartName, const ScRangeListRef &rNewRangeListRef)
only assigns the new RangeList, no ChartListener or the like
Definition: documen5.cxx:485
bool IsImportingXML() const
Definition: document.hxx:2222
void addLinkListener(sal_uInt16 nFileId, LinkListener *pListener)
Register a new link listener to a specified external document.
void removeLinkListener(sal_uInt16 nFileId, LinkListener *pListener)
Remove an existing link listener.
ScAddress aEnd
Definition: address.hxx:498
ScAddress aStart
Definition: address.hxx:497
SfxHintId GetId() const
bool HasBroadcaster() const
bool IsActive() const
void SetPriority(TaskPriority ePriority)
void SetInvokeHandler(const Link< Timer *, void > &rLink)
bool is() const
DocumentType eType
OUString aName
void * p
bool SC_DLLPUBLIC isRef(const ScTokenRef &pToken)
void getRangeListFromTokens(const ScDocument *pDoc, ScRangeList &rRangeList, const ::std::vector< ScTokenRef > &pTokens, const ScAddress &rPos)
void getTokensFromRangeList(const ScDocument *pDoc, ::std::vector< ScTokenRef > &pTokens, const ScRangeList &rRanges)
bool getRangeFromToken(const ScDocument *pDoc, ScRange &rRange, const ScTokenRef &pToken, const ScAddress &rPos, bool bExternal=false)
bool SC_DLLPUBLIC intersects(const ScDocument *pDoc, const ::std::vector< ScTokenRef > &rTokens, const ScTokenRef &pToken, const ScAddress &rPos)
void getTokenFromRange(const ScDocument *pDoc, ScTokenRef &pToken, const ScRange &rRange)
Create a double reference token from a range object.
bool SC_DLLPUBLIC isExternalRef(const ScTokenRef &pToken)
sal_Int16 SCTAB
Definition: types.hxx:22
::boost::intrusive_ptr< formula::FormulaToken > ScTokenRef
Definition: types.hxx:29