1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "ConfigurationUpdater.hxx"
21 #include "ConfigurationTracer.hxx"
22 #include "ConfigurationClassifier.hxx"
23 #include "ConfigurationControllerBroadcaster.hxx"
24 #include "ConfigurationControllerResourceManager.hxx"
25 #include <framework/Configuration.hxx>
26 #include <framework/FrameworkHelper.hxx>
27 #include <DrawController.hxx>
29 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
30 #include <comphelper/scopeguard.hxx>
31 #include <comphelper/diagnose_ex.hxx>
32 #include <sal/log.hxx>
36 using namespace ::com::sun::star
;
37 using namespace ::com::sun::star::uno
;
38 using namespace ::com::sun::star::drawing::framework
;
39 using ::sd::framework::FrameworkHelper
;
43 const sal_Int32
snShortTimeout (100);
44 const sal_Int32
snNormalTimeout (1000);
45 const sal_Int32
snLongTimeout (10000);
46 const sal_Int32
snShortTimeoutCountThreshold (1);
47 const sal_Int32
snNormalTimeoutCountThreshold (5);
50 namespace sd::framework
{
52 //===== ConfigurationUpdaterLock ==============================================
54 class ConfigurationUpdaterLock
57 explicit ConfigurationUpdaterLock (ConfigurationUpdater
& rUpdater
)
58 : mrUpdater(rUpdater
) { mrUpdater
.LockUpdates(); }
59 ~ConfigurationUpdaterLock() { mrUpdater
.UnlockUpdates(); }
61 ConfigurationUpdater
& mrUpdater
;
64 //===== ConfigurationUpdater ==================================================
66 ConfigurationUpdater::ConfigurationUpdater (
67 std::shared_ptr
<ConfigurationControllerBroadcaster
> pBroadcaster
,
68 std::shared_ptr
<ConfigurationControllerResourceManager
> pResourceManager
,
69 const rtl::Reference
<::sd::DrawController
>& rxControllerManager
)
70 : mpBroadcaster(std::move(pBroadcaster
)),
71 mxCurrentConfiguration(Reference
<XConfiguration
>(new Configuration(nullptr, false))),
72 mbUpdatePending(false),
73 mbUpdateBeingProcessed(false),
75 maUpdateTimer("sd::ConfigurationUpdater maUpdateTimer"),
76 mnFailedUpdateCount(0),
77 mpResourceManager(std::move(pResourceManager
))
79 // Prepare the timer that is started when after an update the current
80 // and the requested configuration differ. With the timer we try
81 // updates until the two configurations are the same.
82 maUpdateTimer
.SetTimeout(snNormalTimeout
);
83 maUpdateTimer
.SetInvokeHandler(LINK(this,ConfigurationUpdater
,TimeoutHandler
));
84 mxControllerManager
= rxControllerManager
;
87 ConfigurationUpdater::~ConfigurationUpdater()
92 void ConfigurationUpdater::RequestUpdate (
93 const Reference
<XConfiguration
>& rxRequestedConfiguration
)
95 mxRequestedConfiguration
= rxRequestedConfiguration
;
97 // Find out whether we really can update the configuration.
98 if (IsUpdatePossible())
100 SAL_INFO("sd.fwk", __func__
<< ": UpdateConfiguration start");
102 // Call UpdateConfiguration while that is possible and while someone
103 // set mbUpdatePending to true in the middle of it.
106 UpdateConfiguration();
108 while (mbUpdatePending
&& IsUpdatePossible());
112 mbUpdatePending
= true;
113 SAL_INFO("sd.fwk", __func__
<< ": scheduling update for later");
117 bool ConfigurationUpdater::IsUpdatePossible() const
119 return ! mbUpdateBeingProcessed
120 && mxControllerManager
.is()
122 && mxRequestedConfiguration
.is()
123 && mxCurrentConfiguration
.is();
126 void ConfigurationUpdater::UpdateConfiguration()
128 SAL_INFO("sd.fwk", __func__
<< ": UpdateConfiguration update");
129 SetUpdateBeingProcessed(true);
130 comphelper::ScopeGuard
aScopeGuard (
131 [this] () { return this->SetUpdateBeingProcessed(false); });
135 mbUpdatePending
= false;
137 CleanRequestedConfiguration();
138 ConfigurationClassifier
aClassifier(mxRequestedConfiguration
, mxCurrentConfiguration
);
139 if (aClassifier
.Partition())
141 #if DEBUG_SD_CONFIGURATION_TRACE
142 SAL_INFO("sd.fwk", __func__
<< ": ConfigurationUpdater::UpdateConfiguration(");
143 ConfigurationTracer::TraceConfiguration(
144 mxRequestedConfiguration
, "requested configuration");
145 ConfigurationTracer::TraceConfiguration(
146 mxCurrentConfiguration
, "current configuration");
148 // Notify the beginning of the update.
149 ConfigurationChangeEvent aEvent
;
150 aEvent
.Type
= FrameworkHelper::msConfigurationUpdateStartEvent
;
151 aEvent
.Configuration
= mxRequestedConfiguration
;
152 mpBroadcaster
->NotifyListeners(aEvent
);
154 // Do the actual update. All exceptions are caught and ignored,
155 // so that the end of the update is notified always.
158 if (mnLockCount
== 0)
159 UpdateCore(aClassifier
);
161 catch(const RuntimeException
&)
165 // Notify the end of the update.
166 aEvent
.Type
= FrameworkHelper::msConfigurationUpdateEndEvent
;
167 mpBroadcaster
->NotifyListeners(aEvent
);
169 CheckUpdateSuccess();
173 SAL_INFO("sd.fwk", __func__
<< ": nothing to do");
174 #if DEBUG_SD_CONFIGURATION_TRACE
175 ConfigurationTracer::TraceConfiguration(
176 mxRequestedConfiguration
, "requested configuration");
177 ConfigurationTracer::TraceConfiguration(
178 mxCurrentConfiguration
, "current configuration");
182 catch(const RuntimeException
&)
184 DBG_UNHANDLED_EXCEPTION("sd");
187 SAL_INFO("sd.fwk", __func__
<< ": ConfigurationUpdater::UpdateConfiguration)");
188 SAL_INFO("sd.fwk", __func__
<< ": UpdateConfiguration end");
191 void ConfigurationUpdater::CleanRequestedConfiguration()
193 if (!mxControllerManager
.is())
196 // Request the deactivation of pure anchors that have no child.
197 vector
<Reference
<XResourceId
> > aResourcesToDeactivate
;
198 CheckPureAnchors(mxRequestedConfiguration
, aResourcesToDeactivate
);
199 if (!aResourcesToDeactivate
.empty())
201 Reference
<XConfigurationController
> xCC (
202 mxControllerManager
->getConfigurationController());
203 for (const auto& rxId
: aResourcesToDeactivate
)
205 xCC
->requestResourceDeactivation(rxId
);
209 void ConfigurationUpdater::CheckUpdateSuccess()
211 // When the two configurations differ then start the timer to call
212 // another update later.
213 if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration
, mxRequestedConfiguration
))
215 if (mnFailedUpdateCount
<= snShortTimeoutCountThreshold
)
216 maUpdateTimer
.SetTimeout(snShortTimeout
);
217 else if (mnFailedUpdateCount
< snNormalTimeoutCountThreshold
)
218 maUpdateTimer
.SetTimeout(snNormalTimeout
);
220 maUpdateTimer
.SetTimeout(snLongTimeout
);
221 ++mnFailedUpdateCount
;
222 maUpdateTimer
.Start();
226 // Update was successful. Reset the failed update count.
227 mnFailedUpdateCount
= 0;
231 void ConfigurationUpdater::UpdateCore (const ConfigurationClassifier
& rClassifier
)
235 #if DEBUG_SD_CONFIGURATION_TRACE
236 rClassifier
.TraceResourceIdVector(
237 "requested but not current resources:", rClassifier
.GetC1minusC2());
238 rClassifier
.TraceResourceIdVector(
239 "current but not requested resources:", rClassifier
.GetC2minusC1());
240 rClassifier
.TraceResourceIdVector(
241 "requested and current resources:", rClassifier
.GetC1andC2());
244 // Updating of the sub controllers is done in two steps. In the
245 // first the sub controllers typically shut down resources that are
246 // not requested anymore. In the second the sub controllers
247 // typically set up resources that have been newly requested.
248 mpResourceManager
->DeactivateResources(rClassifier
.GetC2minusC1(), mxCurrentConfiguration
);
249 mpResourceManager
->ActivateResources(rClassifier
.GetC1minusC2(), mxCurrentConfiguration
);
251 #if DEBUG_SD_CONFIGURATION_TRACE
252 SAL_INFO("sd.fwk", __func__
<< ": ConfigurationController::UpdateConfiguration)");
253 ConfigurationTracer::TraceConfiguration(
254 mxRequestedConfiguration
, "requested configuration");
255 ConfigurationTracer::TraceConfiguration(
256 mxCurrentConfiguration
, "current configuration");
259 // Deactivate pure anchors that have no child.
260 vector
<Reference
<XResourceId
> > aResourcesToDeactivate
;
261 CheckPureAnchors(mxCurrentConfiguration
, aResourcesToDeactivate
);
262 if (!aResourcesToDeactivate
.empty())
263 mpResourceManager
->DeactivateResources(aResourcesToDeactivate
, mxCurrentConfiguration
);
265 catch(const RuntimeException
&)
267 DBG_UNHANDLED_EXCEPTION("sd");
271 void ConfigurationUpdater::CheckPureAnchors (
272 const Reference
<XConfiguration
>& rxConfiguration
,
273 vector
<Reference
<XResourceId
> >& rResourcesToDeactivate
)
275 if ( ! rxConfiguration
.is())
278 // Get a list of all resources in the configuration.
279 Sequence
<Reference
<XResourceId
> > aResources(
280 rxConfiguration
->getResources(
281 nullptr, OUString(), AnchorBindingMode_INDIRECT
));
282 auto aResourcesRange
= asNonConstRange(aResources
);
283 sal_Int32
nCount (aResources
.getLength());
285 // Prepare the list of pure anchors that have to be deactivated.
286 rResourcesToDeactivate
.clear();
288 // Iterate over the list in reverse order because when there is a chain
289 // of pure anchors with only the last one having no child then the whole
290 // list has to be deactivated.
291 sal_Int32
nIndex (nCount
-1);
294 const Reference
<XResourceId
> xResourceId (aResources
[nIndex
]);
295 const Reference
<XResource
> xResource (
296 mpResourceManager
->GetResource(xResourceId
).mxResource
);
297 bool bDeactiveCurrentResource (false);
299 // Skip all resources that are no pure anchors.
300 if (xResource
.is() && xResource
->isAnchorOnly())
302 // When xResource is not an anchor of the next resource in
303 // the list then it is the anchor of no resource at all.
304 if (nIndex
== nCount
-1)
306 // No following anchors, deactivate this one, then remove it
308 bDeactiveCurrentResource
= true;
312 const Reference
<XResourceId
> xPrevResourceId (aResources
[nIndex
+1]);
313 if ( ! xPrevResourceId
.is()
314 || ! xPrevResourceId
->isBoundTo(xResourceId
, AnchorBindingMode_DIRECT
))
316 // The previous resource (id) does not exist or is not bound to
317 // the current anchor.
318 bDeactiveCurrentResource
= true;
323 if (bDeactiveCurrentResource
)
325 SAL_INFO("sd.fwk", __func__
<< ": deactivating pure anchor " <<
326 FrameworkHelper::ResourceIdToString(xResourceId
) <<
327 "because it has no children");
328 // Erase element from current configuration.
329 for (sal_Int32 nI
=nIndex
; nI
<nCount
-2; ++nI
)
330 aResourcesRange
[nI
] = aResources
[nI
+1];
333 rResourcesToDeactivate
.push_back(xResourceId
);
339 void ConfigurationUpdater::LockUpdates()
344 void ConfigurationUpdater::UnlockUpdates()
347 if (mnLockCount
== 0 && mbUpdatePending
)
349 RequestUpdate(mxRequestedConfiguration
);
353 std::shared_ptr
<ConfigurationUpdaterLock
> ConfigurationUpdater::GetLock()
355 return std::make_shared
<ConfigurationUpdaterLock
>(*this);
358 void ConfigurationUpdater::SetUpdateBeingProcessed (bool bValue
)
360 mbUpdateBeingProcessed
= bValue
;
363 IMPL_LINK_NOARG(ConfigurationUpdater
, TimeoutHandler
, Timer
*, void)
365 if ( ! mbUpdateBeingProcessed
366 && mxCurrentConfiguration
.is()
367 && mxRequestedConfiguration
.is())
369 if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration
, mxRequestedConfiguration
))
371 RequestUpdate(mxRequestedConfiguration
);
376 } // end of namespace sd::framework
378 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */