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 "ConfigurationControllerBroadcaster.hxx"
21 #include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp>
22 #include <com/sun/star/drawing/framework/XConfigurationController.hpp>
23 #include <com/sun/star/drawing/framework/XResource.hpp>
24 #include <com/sun/star/lang/IllegalArgumentException.hpp>
25 #include <com/sun/star/lang/DisposedException.hpp>
26 #include <tools/diagnose_ex.h>
27
28 using namespace ::com::sun::star;
29 using namespace ::com::sun::star::uno;
30 using namespace ::com::sun::star::drawing::framework;
31
32 namespace sd::framework {
33
ConfigurationControllerBroadcaster(const Reference<XConfigurationController> & rxController)34 ConfigurationControllerBroadcaster::ConfigurationControllerBroadcaster (
35 const Reference<XConfigurationController>& rxController)
36 : mxConfigurationController(rxController),
37 maListenerMap()
38 {
39 }
40
AddListener(const Reference<XConfigurationChangeListener> & rxListener,const OUString & rsEventType,const Any & rUserData)41 void ConfigurationControllerBroadcaster::AddListener(
42 const Reference<XConfigurationChangeListener>& rxListener,
43 const OUString& rsEventType,
44 const Any& rUserData)
45 {
46 if ( ! rxListener.is())
47 throw lang::IllegalArgumentException("invalid listener",
48 mxConfigurationController,
49 0);
50
51 maListenerMap.try_emplace(rsEventType);
52
53 ListenerDescriptor aDescriptor;
54 aDescriptor.mxListener = rxListener;
55 aDescriptor.maUserData = rUserData;
56 maListenerMap[rsEventType].push_back(aDescriptor);
57 }
58
RemoveListener(const Reference<XConfigurationChangeListener> & rxListener)59 void ConfigurationControllerBroadcaster::RemoveListener(
60 const Reference<XConfigurationChangeListener>& rxListener)
61 {
62 if ( ! rxListener.is())
63 throw lang::IllegalArgumentException("invalid listener",
64 mxConfigurationController,
65 0);
66
67 ListenerList::iterator iList;
68 for (auto& rMap : maListenerMap)
69 {
70 iList = std::find_if(rMap.second.begin(), rMap.second.end(),
71 [&rxListener](const ListenerDescriptor& rList) { return rList.mxListener == rxListener; });
72 if (iList != rMap.second.end())
73 rMap.second.erase(iList);
74 }
75 }
76
NotifyListeners(const ListenerList & rList,const ConfigurationChangeEvent & rEvent)77 void ConfigurationControllerBroadcaster::NotifyListeners (
78 const ListenerList& rList,
79 const ConfigurationChangeEvent& rEvent)
80 {
81 // Create a local copy of the event in which the user data is modified
82 // for every listener.
83 ConfigurationChangeEvent aEvent (rEvent);
84
85 for (const auto& rListener : rList)
86 {
87 try
88 {
89 aEvent.UserData = rListener.maUserData;
90 rListener.mxListener->notifyConfigurationChange(aEvent);
91 }
92 catch (const lang::DisposedException& rException)
93 {
94 // When the exception comes from the listener itself then
95 // unregister it.
96 if (rException.Context == rListener.mxListener)
97 RemoveListener(rListener.mxListener);
98 }
99 catch (const RuntimeException&)
100 {
101 DBG_UNHANDLED_EXCEPTION("sd");
102 }
103 }
104 }
105
NotifyListeners(const ConfigurationChangeEvent & rEvent)106 void ConfigurationControllerBroadcaster::NotifyListeners (const ConfigurationChangeEvent& rEvent)
107 {
108 // Notify the specialized listeners.
109 ListenerMap::const_iterator iMap (maListenerMap.find(rEvent.Type));
110 if (iMap != maListenerMap.end())
111 {
112 // Create a local list of the listeners to avoid problems with
113 // concurrent changes and to be able to remove disposed listeners.
114 ListenerList aList (iMap->second.begin(), iMap->second.end());
115 NotifyListeners(aList,rEvent);
116 }
117
118 // Notify the universal listeners.
119 iMap = maListenerMap.find(OUString());
120 if (iMap != maListenerMap.end())
121 {
122 // Create a local list of the listeners to avoid problems with
123 // concurrent changes and to be able to remove disposed listeners.
124 ListenerList aList (iMap->second.begin(), iMap->second.end());
125 NotifyListeners(aList,rEvent);
126 }
127 }
128
NotifyListeners(const OUString & rsEventType,const Reference<XResourceId> & rxResourceId,const Reference<XResource> & rxResourceObject)129 void ConfigurationControllerBroadcaster::NotifyListeners (
130 const OUString& rsEventType,
131 const Reference<XResourceId>& rxResourceId,
132 const Reference<XResource>& rxResourceObject)
133 {
134 ConfigurationChangeEvent aEvent;
135 aEvent.Type = rsEventType;
136 aEvent.ResourceId = rxResourceId;
137 aEvent.ResourceObject = rxResourceObject;
138 try
139 {
140 NotifyListeners(aEvent);
141 }
142 catch (const lang::DisposedException&)
143 {
144 }
145 }
146
DisposeAndClear()147 void ConfigurationControllerBroadcaster::DisposeAndClear()
148 {
149 lang::EventObject aEvent;
150 aEvent.Source = mxConfigurationController;
151 while (!maListenerMap.empty())
152 {
153 ListenerMap::iterator iMap (maListenerMap.begin());
154 if (iMap == maListenerMap.end())
155 break;
156
157 // When the first vector is empty then remove it from the map.
158 if (iMap->second.empty())
159 {
160 maListenerMap.erase(iMap);
161 continue;
162 }
163 else
164 {
165 Reference<XConfigurationChangeListener> xListener (
166 iMap->second.front().mxListener );
167 if (xListener.is())
168 {
169 // Tell the listener that the configuration controller is
170 // being disposed and remove the listener (for all event
171 // types).
172 try
173 {
174 RemoveListener(xListener);
175 xListener->disposing(aEvent);
176 }
177 catch (const RuntimeException&)
178 {
179 DBG_UNHANDLED_EXCEPTION("sd");
180 }
181 }
182 else
183 {
184 // Remove just this reference to the listener.
185 iMap->second.erase(iMap->second.begin());
186 }
187 }
188 }
189 }
190
191 } // end of namespace sd::framework
192
193 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
194