bump product version to 5.0.4.1
[LibreOffice.git] / sd / source / ui / framework / configuration / ConfigurationUpdater.cxx
blob298924f98a86bd2cd8812b780c071f763c32950a
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 "framework/Configuration.hxx"
25 #include "framework/FrameworkHelper.hxx"
27 #include <comphelper/scopeguard.hxx>
28 #include <tools/diagnose_ex.h>
30 #include <boost/bind.hpp>
32 using namespace ::com::sun::star;
33 using namespace ::com::sun::star::uno;
34 using namespace ::com::sun::star::drawing::framework;
35 using ::sd::framework::FrameworkHelper;
36 using ::std::vector;
38 namespace {
39 static const sal_Int32 snShortTimeout (100);
40 static const sal_Int32 snNormalTimeout (1000);
41 static const sal_Int32 snLongTimeout (10000);
42 static const sal_Int32 snShortTimeoutCountThreshold (1);
43 static const sal_Int32 snNormalTimeoutCountThreshold (5);
46 namespace sd { namespace framework {
48 //===== ConfigurationUpdaterLock ==============================================
50 class ConfigurationUpdaterLock
52 public:
53 ConfigurationUpdaterLock (ConfigurationUpdater& rUpdater)
54 : mrUpdater(rUpdater) { mrUpdater.LockUpdates(); }
55 ~ConfigurationUpdaterLock() { mrUpdater.UnlockUpdates(); }
56 private:
57 ConfigurationUpdater& mrUpdater;
60 //===== ConfigurationUpdater ==================================================
62 ConfigurationUpdater::ConfigurationUpdater (
63 const ::boost::shared_ptr<ConfigurationControllerBroadcaster>& rpBroadcaster,
64 const ::boost::shared_ptr<ConfigurationControllerResourceManager>& rpResourceManager,
65 const Reference<XControllerManager>& rxControllerManager)
66 : mxControllerManager(),
67 mpBroadcaster(rpBroadcaster),
68 mxCurrentConfiguration(Reference<XConfiguration>(new Configuration(NULL, false))),
69 mxRequestedConfiguration(),
70 mbUpdatePending(false),
71 mbUpdateBeingProcessed(false),
72 mnLockCount(0),
73 maUpdateTimer(),
74 mnFailedUpdateCount(0),
75 mpResourceManager(rpResourceManager)
77 // Prepare the timer that is started when after an update the current
78 // and the requested configuration differ. With the timer we try
79 // updates until the two configurations are the same.
80 maUpdateTimer.SetTimeout(snNormalTimeout);
81 maUpdateTimer.SetTimeoutHdl(LINK(this,ConfigurationUpdater,TimeoutHandler));
82 SetControllerManager(rxControllerManager);
85 ConfigurationUpdater::~ConfigurationUpdater()
87 maUpdateTimer.Stop();
90 void ConfigurationUpdater::SetControllerManager(
91 const Reference<XControllerManager>& rxControllerManager)
93 mxControllerManager = rxControllerManager;
96 void ConfigurationUpdater::RequestUpdate (
97 const Reference<XConfiguration>& rxRequestedConfiguration)
99 mxRequestedConfiguration = rxRequestedConfiguration;
101 // Find out whether we really can update the configuration.
102 if (IsUpdatePossible())
104 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": UpdateConfiguration start");
106 // Call UpdateConfiguration while that is possible and while someone
107 // set mbUpdatePending to true in the middle of it.
110 UpdateConfiguration();
112 while (mbUpdatePending && IsUpdatePossible());
114 else
116 mbUpdatePending = true;
117 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": scheduling update for later");
121 bool ConfigurationUpdater::IsUpdatePossible()
123 return ! mbUpdateBeingProcessed
124 && mxControllerManager.is()
125 && mnLockCount==0
126 && mxRequestedConfiguration.is()
127 && mxCurrentConfiguration.is();
130 void ConfigurationUpdater::UpdateConfiguration()
132 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": UpdateConfiguration update");
133 SetUpdateBeingProcessed(true);
134 comphelper::ScopeGuard aScopeGuard (
135 ::boost::bind(&ConfigurationUpdater::SetUpdateBeingProcessed, this, false));
139 mbUpdatePending = false;
141 CleanRequestedConfiguration();
142 ConfigurationClassifier aClassifier(mxRequestedConfiguration, mxCurrentConfiguration);
143 if (aClassifier.Partition())
145 #if OSL_DEBUG_LEVEL >= 2
146 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationUpdater::UpdateConfiguration(");
147 ConfigurationTracer::TraceConfiguration(
148 mxRequestedConfiguration, "requested configuration");
149 ConfigurationTracer::TraceConfiguration(
150 mxCurrentConfiguration, "current configuration");
151 #endif
152 // Notify the beginning of the update.
153 ConfigurationChangeEvent aEvent;
154 aEvent.Type = FrameworkHelper::msConfigurationUpdateStartEvent;
155 aEvent.Configuration = mxRequestedConfiguration;
156 mpBroadcaster->NotifyListeners(aEvent);
158 // Do the actual update. All exceptions are caught and ignored,
159 // so that the end of the update is notified always.
162 if (mnLockCount == 0)
163 UpdateCore(aClassifier);
165 catch(const RuntimeException&)
169 // Notify the end of the update.
170 aEvent.Type = FrameworkHelper::msConfigurationUpdateEndEvent;
171 mpBroadcaster->NotifyListeners(aEvent);
173 CheckUpdateSuccess();
175 else
177 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": nothing to do");
178 #if OSL_DEBUG_LEVEL >= 2
179 ConfigurationTracer::TraceConfiguration(
180 mxRequestedConfiguration, "requested configuration");
181 ConfigurationTracer::TraceConfiguration(
182 mxCurrentConfiguration, "current configuration");
183 #endif
186 catch(const RuntimeException &)
188 DBG_UNHANDLED_EXCEPTION();
191 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationUpdater::UpdateConfiguration)");
192 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": UpdateConfiguration end");
195 void ConfigurationUpdater::CleanRequestedConfiguration()
197 if (mxControllerManager.is())
199 // Request the deactivation of pure anchors that have no child.
200 vector<Reference<XResourceId> > aResourcesToDeactivate;
201 CheckPureAnchors(mxRequestedConfiguration, aResourcesToDeactivate);
202 if (!aResourcesToDeactivate.empty())
204 Reference<XConfigurationController> xCC (
205 mxControllerManager->getConfigurationController());
206 vector<Reference<XResourceId> >::iterator iId;
207 for (iId=aResourcesToDeactivate.begin(); iId!=aResourcesToDeactivate.end(); ++iId)
208 if (iId->is())
209 xCC->requestResourceDeactivation(*iId);
214 void ConfigurationUpdater::CheckUpdateSuccess()
216 // When the two configurations differ then start the timer to call
217 // another update later.
218 if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration))
220 if (mnFailedUpdateCount <= snShortTimeoutCountThreshold)
221 maUpdateTimer.SetTimeout(snShortTimeout);
222 else if (mnFailedUpdateCount < snNormalTimeoutCountThreshold)
223 maUpdateTimer.SetTimeout(snNormalTimeout);
224 else
225 maUpdateTimer.SetTimeout(snLongTimeout);
226 ++mnFailedUpdateCount;
227 maUpdateTimer.Start();
229 else
231 // Update was successful. Reset the failed update count.
232 mnFailedUpdateCount = 0;
236 void ConfigurationUpdater::UpdateCore (const ConfigurationClassifier& rClassifier)
240 #if OSL_DEBUG_LEVEL >= 2
241 rClassifier.TraceResourceIdVector(
242 "requested but not current resources:", rClassifier.GetC1minusC2());
243 rClassifier.TraceResourceIdVector(
244 "current but not requested resources:", rClassifier.GetC2minusC1());
245 rClassifier.TraceResourceIdVector(
246 "requested and current resources:", rClassifier.GetC1andC2());
247 #endif
249 // Updating of the sub controllers is done in two steps. In the
250 // first the sub controllers typically shut down resources that are
251 // not requested anymore. In the second the sub controllers
252 // typically set up resources that have been newly requested.
253 mpResourceManager->DeactivateResources(rClassifier.GetC2minusC1(), mxCurrentConfiguration);
254 mpResourceManager->ActivateResources(rClassifier.GetC1minusC2(), mxCurrentConfiguration);
256 #if OSL_DEBUG_LEVEL >= 2
257 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationController::UpdateConfiguration)");
258 ConfigurationTracer::TraceConfiguration(
259 mxRequestedConfiguration, "requested configuration");
260 ConfigurationTracer::TraceConfiguration(
261 mxCurrentConfiguration, "current configuration");
262 #endif
264 // Deactivate pure anchors that have no child.
265 vector<Reference<XResourceId> > aResourcesToDeactivate;
266 CheckPureAnchors(mxCurrentConfiguration, aResourcesToDeactivate);
267 if (!aResourcesToDeactivate.empty())
268 mpResourceManager->DeactivateResources(aResourcesToDeactivate, mxCurrentConfiguration);
270 catch(const RuntimeException&)
272 DBG_UNHANDLED_EXCEPTION();
276 void ConfigurationUpdater::CheckPureAnchors (
277 const Reference<XConfiguration>& rxConfiguration,
278 vector<Reference<XResourceId> >& rResourcesToDeactivate)
280 if ( ! rxConfiguration.is())
281 return;
283 // Get a list of all resources in the configuration.
284 Sequence<Reference<XResourceId> > aResources(
285 rxConfiguration->getResources(
286 NULL, OUString(), AnchorBindingMode_INDIRECT));
287 sal_Int32 nCount (aResources.getLength());
289 // Prepare the list of pure anchors that have to be deactivated.
290 rResourcesToDeactivate.clear();
292 // Iterate over the list in reverse order because when there is a chain
293 // of pure anchors with only the last one having no child then the whole
294 // list has to be deactivated.
295 sal_Int32 nIndex (nCount-1);
296 while (nIndex >= 0)
298 const Reference<XResourceId> xResourceId (aResources[nIndex]);
299 const Reference<XResource> xResource (
300 mpResourceManager->GetResource(xResourceId).mxResource);
301 bool bDeactiveCurrentResource (false);
303 // Skip all resources that are no pure anchors.
304 if (xResource.is() && xResource->isAnchorOnly())
306 // When xResource is not an anchor of the next resource in
307 // the list then it is the anchor of no resource at all.
308 if (nIndex == nCount-1)
310 // No following anchors, deactivate this one, then remove it
311 // from the list.
312 bDeactiveCurrentResource = true;
314 else
316 const Reference<XResourceId> xPrevResourceId (aResources[nIndex+1]);
317 if ( ! xPrevResourceId.is()
318 || ! xPrevResourceId->isBoundTo(xResourceId, AnchorBindingMode_DIRECT))
320 // The previous resource (id) does not exist or is not bound to
321 // the current anchor.
322 bDeactiveCurrentResource = true;
327 if (bDeactiveCurrentResource)
329 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": deactiving pure anchor " <<
330 OUStringToOString(
331 FrameworkHelper::ResourceIdToString(xResourceId),
332 RTL_TEXTENCODING_UTF8).getStr() << "because it has no children");
333 // Erase element from current configuration.
334 for (sal_Int32 nI=nIndex; nI<nCount-2; ++nI)
335 aResources[nI] = aResources[nI+1];
336 nCount -= 1;
338 rResourcesToDeactivate.push_back(xResourceId);
340 nIndex -= 1;
344 void ConfigurationUpdater::LockUpdates()
346 ++mnLockCount;
349 void ConfigurationUpdater::UnlockUpdates()
351 --mnLockCount;
352 if (mnLockCount == 0 && mbUpdatePending)
354 RequestUpdate(mxRequestedConfiguration);
358 ::boost::shared_ptr<ConfigurationUpdaterLock> ConfigurationUpdater::GetLock()
360 return ::boost::shared_ptr<ConfigurationUpdaterLock>(new ConfigurationUpdaterLock(*this));
363 void ConfigurationUpdater::SetUpdateBeingProcessed (bool bValue)
365 mbUpdateBeingProcessed = bValue;
368 IMPL_LINK_NOARG_TYPED(ConfigurationUpdater, TimeoutHandler, Timer *, void)
370 OSL_TRACE("configuration update timer");
371 if ( ! mbUpdateBeingProcessed
372 && mxCurrentConfiguration.is()
373 && mxRequestedConfiguration.is())
375 if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration))
377 OSL_TRACE("configurations differ, requesting update");
378 RequestUpdate(mxRequestedConfiguration);
383 } } // end of namespace sd::framework
385 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */