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/lstner.hxx>
21
22 #include <svl/SfxBroadcaster.hxx>
23 #include <sal/backtrace.hxx>
24 #include <sal/log.hxx>
25
26 #include <algorithm>
27 #include <cassert>
28 #include <vector>
29 #include <memory>
30 #include <map>
31
32 struct SfxListener::Impl
33 {
34 std::vector<SfxBroadcaster*> maBCs;
35 #ifdef DBG_UTIL
36 std::map<SfxBroadcaster*, std::unique_ptr<sal::BacktraceState>>
37 maCallStacks;
38 #endif
39 };
40
41 // simple ctor of class SfxListener
42
SfxListener()43 SfxListener::SfxListener() : mpImpl(new Impl)
44 {
45 }
46
47 // copy ctor of class SfxListener
48
SfxListener(const SfxListener & rListener)49 SfxListener::SfxListener( const SfxListener &rListener ) : mpImpl(new Impl)
50 {
51 for ( size_t n = 0; n < rListener.mpImpl->maBCs.size(); ++n )
52 StartListening( *rListener.mpImpl->maBCs[n] );
53 }
54
55 // unregisters the SfxListener from its SfxBroadcasters
56
~SfxListener()57 SfxListener::~SfxListener() COVERITY_NOEXCEPT_FALSE
58 {
59 // unregister at all remaining broadcasters
60 for ( size_t nPos = 0; nPos < mpImpl->maBCs.size(); ++nPos )
61 {
62 SfxBroadcaster *pBC = mpImpl->maBCs[nPos];
63 pBC->RemoveListener(*this);
64 }
65 }
66
67
68 // unregisters a specific SfxBroadcaster
69
RemoveBroadcaster_Impl(SfxBroadcaster & rBroadcaster)70 void SfxListener::RemoveBroadcaster_Impl( SfxBroadcaster& rBroadcaster )
71 {
72 auto it = std::find( mpImpl->maBCs.begin(), mpImpl->maBCs.end(), &rBroadcaster );
73 if (it != mpImpl->maBCs.end()) {
74 mpImpl->maBCs.erase( it );
75 #ifdef DBG_UTIL
76 mpImpl->maCallStacks.erase( &rBroadcaster );
77 #endif
78 }
79 }
80
81
82
83 /**
84 Registers a specific SfxBroadcaster.
85
86 Some code uses duplicates as a kind of ref-counting thing i.e. they add and remove listeners
87 on different code paths, and they only really stop listening when the last EndListening() is called.
88 */
StartListening(SfxBroadcaster & rBroadcaster,DuplicateHandling eDuplicateHanding)89 void SfxListener::StartListening(SfxBroadcaster& rBroadcaster, DuplicateHandling eDuplicateHanding)
90 {
91 bool bListeningAlready = IsListening( rBroadcaster );
92
93 #ifdef DBG_UTIL
94 if (bListeningAlready && eDuplicateHanding == DuplicateHandling::Unexpected)
95 {
96 auto f = mpImpl->maCallStacks.find( &rBroadcaster );
97 SAL_WARN("svl", "previous StartListening call came from: " << sal::backtrace_to_string(f->second.get()));
98 }
99 #endif
100 assert(!(bListeningAlready && eDuplicateHanding == DuplicateHandling::Unexpected) && "duplicate listener, try building with DBG_UTIL to find the other insert site.");
101
102 if (!bListeningAlready || eDuplicateHanding != DuplicateHandling::Prevent)
103 {
104 rBroadcaster.AddListener(*this);
105 mpImpl->maBCs.push_back( &rBroadcaster );
106 #ifdef DBG_UTIL
107 mpImpl->maCallStacks.emplace( &rBroadcaster, sal::backtrace_get(10) );
108 #endif
109 assert(IsListening(rBroadcaster) && "StartListening failed");
110 }
111 }
112
113 // unregisters a specific SfxBroadcaster
114
EndListening(SfxBroadcaster & rBroadcaster,bool bRemoveAllDuplicates)115 void SfxListener::EndListening( SfxBroadcaster& rBroadcaster, bool bRemoveAllDuplicates )
116 {
117 auto beginIt = mpImpl->maBCs.begin();
118 do
119 {
120 auto it = std::find( beginIt, mpImpl->maBCs.end(), &rBroadcaster );
121 if ( it == mpImpl->maBCs.end() )
122 {
123 break;
124 }
125 rBroadcaster.RemoveListener(*this);
126 beginIt = mpImpl->maBCs.erase( it );
127 #ifdef DBG_UTIL
128 mpImpl->maCallStacks.erase( &rBroadcaster );
129 #endif
130 }
131 while ( bRemoveAllDuplicates );
132 }
133
134
135 // unregisters all Broadcasters
136
EndListeningAll()137 void SfxListener::EndListeningAll()
138 {
139 std::vector<SfxBroadcaster*> aBroadcasters;
140 std::swap(mpImpl->maBCs, aBroadcasters);
141 for (SfxBroadcaster *pBC : aBroadcasters)
142 pBC->RemoveListener(*this);
143 #ifdef DBG_UTIL
144 mpImpl->maCallStacks.clear();
145 #endif
146 }
147
148
IsListening(SfxBroadcaster & rBroadcaster) const149 bool SfxListener::IsListening( SfxBroadcaster& rBroadcaster ) const
150 {
151 return mpImpl->maBCs.end() != std::find( mpImpl->maBCs.begin(), mpImpl->maBCs.end(), &rBroadcaster );
152 }
153
GetBroadcasterCount() const154 sal_uInt16 SfxListener::GetBroadcasterCount() const
155 {
156 return mpImpl->maBCs.size();
157 }
158
GetBroadcasterJOE(sal_uInt16 nNo) const159 SfxBroadcaster* SfxListener::GetBroadcasterJOE( sal_uInt16 nNo ) const
160 {
161 return mpImpl->maBCs[nNo];
162 }
163
164
165 // base implementation of notification handler
166
Notify(SfxBroadcaster & rBroadcaster,const SfxHint &)167 void SfxListener::Notify( SfxBroadcaster& rBroadcaster, const SfxHint& )
168 {
169 (void) rBroadcaster;
170 assert(IsListening(rBroadcaster));
171 }
172
173 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
174