Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sd / source / ui / framework / configuration / ConfigurationUpdater.cxx
blobfeea95eacc13fe470c1c609d6b9f181dab6f9166
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
33 #include <utility>
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;
40 using ::std::vector;
42 namespace {
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
56 public:
57 explicit ConfigurationUpdaterLock (ConfigurationUpdater& rUpdater)
58 : mrUpdater(rUpdater) { mrUpdater.LockUpdates(); }
59 ~ConfigurationUpdaterLock() { mrUpdater.UnlockUpdates(); }
60 private:
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),
74 mnLockCount(0),
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()
89 maUpdateTimer.Stop();
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());
110 else
112 mbUpdatePending = true;
113 SAL_INFO("sd.fwk", __func__ << ": scheduling update for later");
117 bool ConfigurationUpdater::IsUpdatePossible() const
119 return ! mbUpdateBeingProcessed
120 && mxControllerManager.is()
121 && mnLockCount==0
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");
147 #endif
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();
171 else
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");
179 #endif
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())
194 return;
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)
204 if (rxId.is())
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);
219 else
220 maUpdateTimer.SetTimeout(snLongTimeout);
221 ++mnFailedUpdateCount;
222 maUpdateTimer.Start();
224 else
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());
242 #endif
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");
257 #endif
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())
276 return;
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);
292 while (nIndex >= 0)
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
307 // from the list.
308 bDeactiveCurrentResource = true;
310 else
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];
331 nCount -= 1;
333 rResourcesToDeactivate.push_back(xResourceId);
335 nIndex -= 1;
339 void ConfigurationUpdater::LockUpdates()
341 ++mnLockCount;
344 void ConfigurationUpdater::UnlockUpdates()
346 --mnLockCount;
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: */