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