summaryrefslogtreecommitdiff
path: root/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sd/source/ui/framework/configuration/ConfigurationUpdater.cxx')
-rw-r--r--sd/source/ui/framework/configuration/ConfigurationUpdater.cxx479
1 files changed, 479 insertions, 0 deletions
diff --git a/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx b/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx
new file mode 100644
index 000000000000..5721f483595f
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx
@@ -0,0 +1,479 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2008 by Sun Microsystems, Inc.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * $RCSfile: ConfigurationUpdater.cxx,v $
+ * $Revision: 1.6 $
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include "precompiled_sd.hxx"
+
+#include "ConfigurationUpdater.hxx"
+#include "ConfigurationTracer.hxx"
+#include "ConfigurationClassifier.hxx"
+#include "ConfigurationControllerBroadcaster.hxx"
+#include "framework/Configuration.hxx"
+#include "framework/FrameworkHelper.hxx"
+
+#include <comphelper/scopeguard.hxx>
+
+#include <boost/bind.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+using ::sd::framework::FrameworkHelper;
+using ::rtl::OUString;
+using ::std::vector;
+
+#undef VERBOSE
+//#define VERBOSE 2
+
+namespace {
+static const sal_Int32 snShortTimeout (100);
+static const sal_Int32 snNormalTimeout (1000);
+static const sal_Int32 snLongTimeout (10000);
+static const sal_Int32 snShortTimeoutCountThreshold (1);
+static const sal_Int32 snNormalTimeoutCountThreshold (5);
+}
+
+namespace sd { namespace framework {
+
+
+//===== ConfigurationUpdaterLock ==============================================
+
+class ConfigurationUpdaterLock
+{
+public:
+ ConfigurationUpdaterLock (ConfigurationUpdater& rUpdater)
+ : mrUpdater(rUpdater) { mrUpdater.LockUpdates(); }
+ ~ConfigurationUpdaterLock(void) { mrUpdater.UnlockUpdates(); }
+private:
+ ConfigurationUpdater& mrUpdater;
+};
+
+
+
+
+//===== ConfigurationUpdater ==================================================
+
+ConfigurationUpdater::ConfigurationUpdater (
+ const ::boost::shared_ptr<ConfigurationControllerBroadcaster>& rpBroadcaster,
+ const ::boost::shared_ptr<ConfigurationControllerResourceManager>& rpResourceManager,
+ const Reference<XControllerManager>& rxControllerManager)
+ : mxControllerManager(),
+ mpBroadcaster(rpBroadcaster),
+ mxCurrentConfiguration(Reference<XConfiguration>(new Configuration(NULL, false))),
+ mxRequestedConfiguration(),
+ mbUpdatePending(false),
+ mbUpdateBeingProcessed(false),
+ mnLockCount(0),
+ maUpdateTimer(),
+ mnFailedUpdateCount(0),
+ mpResourceManager(rpResourceManager)
+{
+ // Prepare the timer that is started when after an update the current
+ // and the requested configuration differ. With the timer we try
+ // updates until the two configurations are the same.
+ maUpdateTimer.SetTimeout(snNormalTimeout);
+ maUpdateTimer.SetTimeoutHdl(LINK(this,ConfigurationUpdater,TimeoutHandler));
+ SetControllerManager(rxControllerManager);
+}
+
+
+
+
+ConfigurationUpdater::~ConfigurationUpdater (void)
+{
+ maUpdateTimer.Stop();
+}
+
+
+
+
+void ConfigurationUpdater::SetControllerManager(
+ const Reference<XControllerManager>& rxControllerManager)
+{
+ mxControllerManager = rxControllerManager;
+}
+
+
+
+
+void ConfigurationUpdater::RequestUpdate (
+ const Reference<XConfiguration>& rxRequestedConfiguration)
+{
+ mxRequestedConfiguration = rxRequestedConfiguration;
+
+ // Find out whether we really can update the configuration.
+ if (IsUpdatePossible())
+ {
+#if defined VERBOSE && VERBOSE>=1
+ OSL_TRACE("UpdateConfiguration start\n");
+#endif
+
+ // Call UpdateConfiguration while that is possible and while someone
+ // set mbUpdatePending to true in the middle of it.
+ do
+ {
+ UpdateConfiguration();
+
+ if (mbUpdatePending && IsUpdatePossible())
+ continue;
+ }
+ while (false);
+ }
+ else
+ {
+ mbUpdatePending = true;
+#if defined VERBOSE && VERBOSE>=1
+ OSL_TRACE("scheduling update for later\n");
+#endif
+ }
+}
+
+
+
+
+Reference<XConfiguration> ConfigurationUpdater::GetCurrentConfiguration (void) const
+{
+ return mxCurrentConfiguration;
+}
+
+
+
+
+Reference<XConfiguration> ConfigurationUpdater::GetRequestedConfiguration (void) const
+{
+ return mxRequestedConfiguration;
+}
+
+
+
+
+bool ConfigurationUpdater::IsUpdatePossible (void)
+{
+ return ! mbUpdateBeingProcessed
+ && mxControllerManager.is()
+ && mnLockCount==0
+ && mxRequestedConfiguration.is()
+ && mxCurrentConfiguration.is();
+}
+
+
+
+
+void ConfigurationUpdater::UpdateConfiguration (void)
+{
+#if defined VERBOSE && VERBOSE>=1
+ OSL_TRACE("UpdateConfiguration update\n");
+#endif
+ SetUpdateBeingProcessed(true);
+ comphelper::ScopeGuard aScopeGuard (
+ ::boost::bind(&ConfigurationUpdater::SetUpdateBeingProcessed, this, false));
+
+ try
+ {
+ mbUpdatePending = false;
+
+ CleanRequestedConfiguration();
+ ConfigurationClassifier aClassifier(mxRequestedConfiguration, mxCurrentConfiguration);
+ if (aClassifier.Partition())
+ {
+#if defined VERBOSE && VERBOSE>=2
+ OSL_TRACE("ConfigurationUpdater::UpdateConfiguration(\n");
+ ConfigurationTracer::TraceConfiguration(
+ mxRequestedConfiguration, "requested configuration");
+ ConfigurationTracer::TraceConfiguration(
+ mxCurrentConfiguration, "current configuration");
+#endif
+ // Notify the begining of the update.
+ ConfigurationChangeEvent aEvent;
+ aEvent.Type = FrameworkHelper::msConfigurationUpdateStartEvent;
+ aEvent.Configuration = mxRequestedConfiguration;
+ mpBroadcaster->NotifyListeners(aEvent);
+
+ // Do the actual update. All exceptions are caught and ignored,
+ // so that the the end of the update is notified always.
+ try
+ {
+ if (mnLockCount == 0)
+ UpdateCore(aClassifier);
+ }
+ catch(RuntimeException)
+ {
+ }
+
+ // Notify the end of the update.
+ aEvent.Type = FrameworkHelper::msConfigurationUpdateEndEvent;
+ mpBroadcaster->NotifyListeners(aEvent);
+
+ CheckUpdateSuccess();
+ }
+ else
+ {
+#if defined VERBOSE && VERBOSE>0
+ OSL_TRACE("nothing to do\n");
+#if defined VERBOSE && VERBOSE>=2
+ ConfigurationTracer::TraceConfiguration(
+ mxRequestedConfiguration, "requested configuration");
+ ConfigurationTracer::TraceConfiguration(
+ mxCurrentConfiguration, "current configuration");
+#endif
+#endif
+ }
+ }
+ catch (RuntimeException e)
+ {
+ OSL_TRACE("caught exception while updating the current configuration");
+ DBG_ASSERT(false, "caught exception while updating the current configuration");
+ }
+
+#if defined VERBOSE && VERBOSE>0
+ OSL_TRACE("ConfigurationUpdater::UpdateConfiguration)");
+ OSL_TRACE("UpdateConfiguration end");
+#endif
+}
+
+
+
+
+void ConfigurationUpdater::CleanRequestedConfiguration (void)
+{
+ if (mxControllerManager.is())
+ {
+ // Request the deactivation of pure anchors that have no child.
+ vector<Reference<XResourceId> > aResourcesToDeactivate;
+ CheckPureAnchors(mxRequestedConfiguration, aResourcesToDeactivate);
+ if (aResourcesToDeactivate.size() > 0)
+ {
+ Reference<XConfigurationController> xCC (
+ mxControllerManager->getConfigurationController());
+ vector<Reference<XResourceId> >::iterator iId;
+ for (iId=aResourcesToDeactivate.begin(); iId!=aResourcesToDeactivate.end(); ++iId)
+ if (iId->is())
+ xCC->requestResourceDeactivation(*iId);
+ }
+ }
+}
+
+
+
+
+void ConfigurationUpdater::CheckUpdateSuccess (void)
+{
+ // When the two configurations differ then start the timer to call
+ // another update later.
+ if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration))
+ {
+ if (mnFailedUpdateCount <= snShortTimeoutCountThreshold)
+ maUpdateTimer.SetTimeout(snShortTimeout);
+ else if (mnFailedUpdateCount < snNormalTimeoutCountThreshold)
+ maUpdateTimer.SetTimeout(snNormalTimeout);
+ else
+ maUpdateTimer.SetTimeout(snLongTimeout);
+ ++mnFailedUpdateCount;
+ maUpdateTimer.Start();
+ }
+ else
+ {
+ // Update was successfull. Reset the failed update count.
+ mnFailedUpdateCount = 0;
+ }
+}
+
+
+
+
+void ConfigurationUpdater::UpdateCore (const ConfigurationClassifier& rClassifier)
+{
+ try
+ {
+#if defined VERBOSE && VERBOSE>=2
+ rClassifier.TraceResourceIdVector(
+ "requested but not current resources:\n", rClassifier.GetC1minusC2());
+ rClassifier.TraceResourceIdVector(
+ "current but not requested resources:\n", rClassifier.GetC2minusC1());
+ rClassifier.TraceResourceIdVector(
+ "requested and current resources:\n", rClassifier.GetC1andC2());
+#endif
+
+ // Updating of the sub controllers is done in two steps. In the
+ // first the sub controllers typically shut down resources that are
+ // not requested anymore. In the second the sub controllers
+ // typically set up resources that have been newly requested.
+ mpResourceManager->DeactivateResources(rClassifier.GetC2minusC1(), mxCurrentConfiguration);
+ mpResourceManager->ActivateResources(rClassifier.GetC1minusC2(), mxCurrentConfiguration);
+
+#if defined VERBOSE && VERBOSE>=2
+ OSL_TRACE("ConfigurationController::UpdateConfiguration)\n");
+ ConfigurationTracer::TraceConfiguration(
+ mxRequestedConfiguration, "requested configuration\n");
+ ConfigurationTracer::TraceConfiguration(
+ mxCurrentConfiguration, "current configuration\n");
+#endif
+
+ // Deactivate pure anchors that have no child.
+ vector<Reference<XResourceId> > aResourcesToDeactivate;
+ CheckPureAnchors(mxCurrentConfiguration, aResourcesToDeactivate);
+ if (aResourcesToDeactivate.size() > 0)
+ mpResourceManager->DeactivateResources(aResourcesToDeactivate, mxCurrentConfiguration);
+ }
+ catch(RuntimeException)
+ {
+ OSL_ASSERT(false);
+ }
+}
+
+
+
+
+void ConfigurationUpdater::CheckPureAnchors (
+ const Reference<XConfiguration>& rxConfiguration,
+ vector<Reference<XResourceId> >& rResourcesToDeactivate)
+{
+ if ( ! rxConfiguration.is())
+ return;
+
+ // Get a list of all resources in the configuration.
+ Sequence<Reference<XResourceId> > aResources(
+ rxConfiguration->getResources(
+ NULL, OUString(), AnchorBindingMode_INDIRECT));
+ sal_Int32 nCount (aResources.getLength());
+
+ // Prepare the list of pure anchors that have to be deactivated.
+ rResourcesToDeactivate.clear();
+
+ // Iterate over the list in reverse order because when there is a chain
+ // of pure anchors with only the last one having no child then the whole
+ // list has to be deactivated.
+ sal_Int32 nIndex (nCount-1);
+ while (nIndex >= 0)
+ {
+ const Reference<XResourceId> xResourceId (aResources[nIndex]);
+ const Reference<XResource> xResource (
+ mpResourceManager->GetResource(xResourceId).mxResource);
+ bool bDeactiveCurrentResource (false);
+
+ // Skip all resources that are no pure anchors.
+ if (xResource.is() && xResource->isAnchorOnly())
+ {
+ // When xResource is not an anchor of the the next resource in
+ // the list then it is the anchor of no resource at all.
+ if (nIndex == nCount-1)
+ {
+ // No following anchors, deactivate this one, then remove it
+ // from the list.
+ bDeactiveCurrentResource = true;
+ }
+ else
+ {
+ const Reference<XResourceId> xPrevResourceId (aResources[nIndex+1]);
+ if ( ! xPrevResourceId.is()
+ || ! xPrevResourceId->isBoundTo(xResourceId, AnchorBindingMode_DIRECT))
+ {
+ // The previous resource (id) does not exist or is not bound to
+ // the current anchor.
+ bDeactiveCurrentResource = true;
+ }
+ }
+ }
+
+ if (bDeactiveCurrentResource)
+ {
+#if defined VERBOSE && VERBOSE>=2
+ OSL_TRACE("deactiving pure anchor %s because it has no children\n",
+ OUStringToOString(
+ FrameworkHelper::ResourceIdToString(xResourceId),
+ RTL_TEXTENCODING_UTF8).getStr());
+#endif
+ // Erase element from current configuration.
+ for (sal_Int32 nI=nIndex; nI<nCount-2; ++nI)
+ aResources[nI] = aResources[nI+1];
+ nCount -= 1;
+
+ rResourcesToDeactivate.push_back(xResourceId);
+ }
+ nIndex -= 1;
+ }
+}
+
+
+
+
+void ConfigurationUpdater::LockUpdates (void)
+{
+ ++mnLockCount;
+}
+
+
+
+
+void ConfigurationUpdater::UnlockUpdates (void)
+{
+ --mnLockCount;
+ if (mnLockCount == 0 && mbUpdatePending)
+ {
+ RequestUpdate(mxRequestedConfiguration);
+ }
+}
+
+
+
+
+::boost::shared_ptr<ConfigurationUpdaterLock> ConfigurationUpdater::GetLock (void)
+{
+ return ::boost::shared_ptr<ConfigurationUpdaterLock>(new ConfigurationUpdaterLock(*this));
+}
+
+
+
+
+void ConfigurationUpdater::SetUpdateBeingProcessed (bool bValue)
+{
+ mbUpdateBeingProcessed = bValue;
+}
+
+
+
+
+IMPL_LINK(ConfigurationUpdater, TimeoutHandler, Timer*, EMPTYARG)
+{
+ OSL_TRACE("configuration update timer\n");
+ if ( ! mbUpdateBeingProcessed
+ && mxCurrentConfiguration.is()
+ && mxRequestedConfiguration.is())
+ {
+ if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration))
+ {
+ OSL_TRACE("configurations differ, requesting update\n");
+ RequestUpdate(mxRequestedConfiguration);
+ }
+ }
+ return 0;
+}
+
+
+} } // end of namespace sd::framework