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 <svl/SfxBroadcaster.hxx>
21 
22 #include <svl/hint.hxx>
23 #include <svl/lstner.hxx>
24 #include <tools/debug.hxx>
25 
26 #include <algorithm>
27 #include <cassert>
28 #include <vector>
29 
30 
31 typedef std::vector<SfxListener*> SfxListenerArr_Impl;
32 
33 struct SfxBroadcaster::Impl
34 {
35     /** Contains the positions of removed listeners. */
36     std::vector<size_t>     m_RemovedPositions;
37     SfxListenerArr_Impl     m_Listeners;
38 };
39 
40 // broadcast immediately
41 
Broadcast(const SfxHint & rHint)42 void SfxBroadcaster::Broadcast( const SfxHint &rHint )
43 {
44     // notify all registered listeners exactly once
45     for (size_t i = 0; i < mpImpl->m_Listeners.size(); ++i)
46     {
47         SfxListener *const pListener = mpImpl->m_Listeners[i];
48         if (pListener)
49             pListener->Notify( *this, rHint );
50     }
51 }
52 
53 // unregister all listeners
54 
~SfxBroadcaster()55 SfxBroadcaster::~SfxBroadcaster() COVERITY_NOEXCEPT_FALSE
56 {
57     Broadcast( SfxHint(SfxHintId::Dying) );
58 
59     // remove all still registered listeners
60     for (size_t i = 0; i < mpImpl->m_Listeners.size(); ++i)
61     {
62         SfxListener *const pListener = mpImpl->m_Listeners[i];
63         if (pListener)
64             pListener->RemoveBroadcaster_Impl(*this);
65     }
66 }
67 
68 
69 // simple ctor of class SfxBroadcaster
70 
SfxBroadcaster()71 SfxBroadcaster::SfxBroadcaster() : mpImpl(new Impl)
72 {
73 }
74 
75 
76 // copy ctor of class SfxBroadcaster
77 
78 
SfxBroadcaster(const SfxBroadcaster & rBC)79 SfxBroadcaster::SfxBroadcaster( const SfxBroadcaster &rBC ) : mpImpl(new Impl)
80 {
81     for (size_t i = 0; i < rBC.mpImpl->m_Listeners.size(); ++i)
82     {
83         SfxListener *const pListener = rBC.mpImpl->m_Listeners[i];
84         if (pListener)
85             pListener->StartListening( *this );
86     }
87 }
88 
89 
90 // add a new SfxListener to the list
91 
AddListener(SfxListener & rListener)92 void SfxBroadcaster::AddListener( SfxListener& rListener )
93 {
94     DBG_TESTSOLARMUTEX();
95     if (mpImpl->m_RemovedPositions.empty())
96     {
97         mpImpl->m_Listeners.push_back(&rListener);
98     }
99     else
100     {
101         size_t targetPosition = mpImpl->m_RemovedPositions.back();
102         mpImpl->m_RemovedPositions.pop_back();
103         assert(mpImpl->m_Listeners[targetPosition] == nullptr);
104         mpImpl->m_Listeners[targetPosition] = &rListener;
105     }
106 }
107 
108 
109 // forward a notification to all registered listeners
110 
Forward(SfxBroadcaster & rBC,const SfxHint & rHint)111 void SfxBroadcaster::Forward(SfxBroadcaster& rBC, const SfxHint& rHint)
112 {
113     for (size_t i = 0; i < mpImpl->m_Listeners.size(); ++i)
114     {
115         SfxListener *const pListener = mpImpl->m_Listeners[i];
116         if (pListener)
117             pListener->Notify( rBC, rHint );
118     }
119 }
120 
121 
122 // remove one SfxListener from the list
123 
RemoveListener(SfxListener & rListener)124 void SfxBroadcaster::RemoveListener( SfxListener& rListener )
125 {
126     DBG_TESTSOLARMUTEX();
127 
128     // First, check the slots either side of the last removed slot, makes a significant
129     // difference when the list is large.
130     int positionOfRemovedElement = -1;
131     if (!mpImpl->m_RemovedPositions.empty())
132     {
133         auto i = mpImpl->m_RemovedPositions.back();
134         if (i < mpImpl->m_Listeners.size() - 2 && mpImpl->m_Listeners[i+1] == &rListener)
135         {
136             positionOfRemovedElement = i + 1;
137         }
138         else if (i > 0 && mpImpl->m_Listeners[i-1] == &rListener)
139         {
140             positionOfRemovedElement = i-1;
141         }
142     }
143     // then scan the whole list if we didn't find it
144     if (positionOfRemovedElement == -1)
145     {
146         SfxListenerArr_Impl::iterator aIter = std::find(
147                 mpImpl->m_Listeners.begin(), mpImpl->m_Listeners.end(), &rListener);
148         positionOfRemovedElement = std::distance(mpImpl->m_Listeners.begin(), aIter);
149     }
150     // DO NOT erase the listener, set the pointer to 0
151     // because the current continuation may contain this->Broadcast
152     mpImpl->m_Listeners[positionOfRemovedElement] = nullptr;
153     mpImpl->m_RemovedPositions.push_back(positionOfRemovedElement);
154 }
155 
HasListeners() const156 bool SfxBroadcaster::HasListeners() const
157 {
158     return (GetListenerCount() != 0);
159 }
160 
GetListenerCount() const161 size_t SfxBroadcaster::GetListenerCount() const
162 {
163     assert(mpImpl->m_Listeners.size() >= mpImpl->m_RemovedPositions.size());
164     return mpImpl->m_Listeners.size() - mpImpl->m_RemovedPositions.size();
165 }
166 
GetSizeOfVector() const167 size_t SfxBroadcaster::GetSizeOfVector() const
168 {
169     return mpImpl->m_Listeners.size();
170 }
171 
GetListener(size_t nNo) const172 SfxListener* SfxBroadcaster::GetListener( size_t nNo ) const
173 {
174     return mpImpl->m_Listeners[nNo];
175 }
176 
177 
178 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
179