summaryrefslogtreecommitdiffstats
path: root/sd/source/ui/framework/configuration
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sd/source/ui/framework/configuration
parentInitial commit. (diff)
downloadlibreoffice-upstream/4%7.4.7.tar.xz
libreoffice-upstream/4%7.4.7.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sd/source/ui/framework/configuration')
-rw-r--r--sd/source/ui/framework/configuration/ChangeRequestQueue.cxx28
-rw-r--r--sd/source/ui/framework/configuration/ChangeRequestQueue.hxx48
-rw-r--r--sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.cxx180
-rw-r--r--sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.hxx126
-rw-r--r--sd/source/ui/framework/configuration/Configuration.cxx311
-rw-r--r--sd/source/ui/framework/configuration/ConfigurationClassifier.cxx167
-rw-r--r--sd/source/ui/framework/configuration/ConfigurationClassifier.hxx165
-rw-r--r--sd/source/ui/framework/configuration/ConfigurationController.cxx541
-rw-r--r--sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.cxx192
-rw-r--r--sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.hxx138
-rw-r--r--sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.cxx303
-rw-r--r--sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.hxx141
-rw-r--r--sd/source/ui/framework/configuration/ConfigurationTracer.cxx73
-rw-r--r--sd/source/ui/framework/configuration/ConfigurationTracer.hxx58
-rw-r--r--sd/source/ui/framework/configuration/ConfigurationUpdater.cxx376
-rw-r--r--sd/source/ui/framework/configuration/ConfigurationUpdater.hxx209
-rw-r--r--sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.cxx81
-rw-r--r--sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.hxx98
-rw-r--r--sd/source/ui/framework/configuration/ResourceFactoryManager.cxx197
-rw-r--r--sd/source/ui/framework/configuration/ResourceFactoryManager.hxx120
-rw-r--r--sd/source/ui/framework/configuration/ResourceId.cxx503
-rw-r--r--sd/source/ui/framework/configuration/UpdateRequest.cxx47
-rw-r--r--sd/source/ui/framework/configuration/UpdateRequest.hxx70
-rw-r--r--sd/source/ui/framework/configuration/debugtrace.hxx15
24 files changed, 4187 insertions, 0 deletions
diff --git a/sd/source/ui/framework/configuration/ChangeRequestQueue.cxx b/sd/source/ui/framework/configuration/ChangeRequestQueue.cxx
new file mode 100644
index 000000000..0168c162b
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ChangeRequestQueue.cxx
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ChangeRequestQueue.hxx"
+
+namespace sd::framework
+{
+ChangeRequestQueue::ChangeRequestQueue() {}
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ChangeRequestQueue.hxx b/sd/source/ui/framework/configuration/ChangeRequestQueue.hxx
new file mode 100644
index 000000000..e60b5b527
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ChangeRequestQueue.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <queue>
+
+namespace com::sun::star::drawing::framework
+{
+class XConfigurationChangeRequest;
+}
+
+namespace sd::framework
+{
+/** The ChangeRequestQueue stores the pending requests for changes to the
+ requested configuration. It is the task of the
+ ChangeRequestQueueProcessor to process these requests.
+*/
+class ChangeRequestQueue
+ : public ::std::queue<css::uno::Reference<css::drawing::framework::XConfigurationChangeRequest>>
+{
+public:
+ /** Create an empty queue.
+ */
+ ChangeRequestQueue();
+};
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.cxx b/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.cxx
new file mode 100644
index 000000000..da633a540
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.cxx
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "debugtrace.hxx"
+#include "ChangeRequestQueueProcessor.hxx"
+#include "ConfigurationTracer.hxx"
+
+#include "ConfigurationUpdater.hxx"
+
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationChangeRequest.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace {
+
+#if DEBUG_SD_CONFIGURATION_TRACE
+
+void TraceRequest (const Reference<XConfigurationChangeRequest>& rxRequest)
+{
+ Reference<container::XNamed> xNamed (rxRequest, UNO_QUERY);
+ if (xNamed.is())
+ SAL_INFO("sd.fwk", __func__ << ": " << xNamed->getName());
+}
+
+#endif
+
+} // end of anonymous namespace
+
+namespace sd::framework {
+
+ChangeRequestQueueProcessor::ChangeRequestQueueProcessor (
+ const std::shared_ptr<ConfigurationUpdater>& rpConfigurationUpdater)
+ : mnUserEventId(nullptr),
+ mpConfigurationUpdater(rpConfigurationUpdater)
+{
+}
+
+ChangeRequestQueueProcessor::~ChangeRequestQueueProcessor()
+{
+ if (mnUserEventId != nullptr)
+ Application::RemoveUserEvent(mnUserEventId);
+}
+
+void ChangeRequestQueueProcessor::SetConfiguration (
+ const Reference<XConfiguration>& rxConfiguration)
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ mxConfiguration = rxConfiguration;
+ StartProcessing();
+}
+
+void ChangeRequestQueueProcessor::AddRequest (
+ const Reference<XConfigurationChangeRequest>& rxRequest)
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+#if DEBUG_SD_CONFIGURATION_TRACE
+ if (maQueue.empty())
+ {
+ SAL_INFO("sd.fwk", __func__ << ": Adding requests to empty queue");
+ ConfigurationTracer::TraceConfiguration(
+ mxConfiguration, "current configuration of queue processor");
+ }
+ SAL_INFO("sd.fwk", __func__ << ": Adding request");
+ TraceRequest(rxRequest);
+#endif
+
+ maQueue.push(rxRequest);
+ StartProcessing();
+}
+
+void ChangeRequestQueueProcessor::StartProcessing()
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ if (mnUserEventId == nullptr
+ && mxConfiguration.is()
+ && ! maQueue.empty())
+ {
+ SAL_INFO("sd.fwk", __func__ << ": ChangeRequestQueueProcessor scheduling processing");
+ mnUserEventId = Application::PostUserEvent(
+ LINK(this,ChangeRequestQueueProcessor,ProcessEvent));
+ }
+}
+
+IMPL_LINK_NOARG(ChangeRequestQueueProcessor, ProcessEvent, void*, void)
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ mnUserEventId = nullptr;
+
+ ProcessOneEvent();
+
+ if ( ! maQueue.empty())
+ {
+ // Schedule the processing of the next event.
+ StartProcessing();
+ }
+}
+
+void ChangeRequestQueueProcessor::ProcessOneEvent()
+{
+ ::osl::MutexGuard aGuard (maMutex);
+
+ SAL_INFO("sd.fwk", __func__ << ": ProcessOneEvent");
+
+ if (!mxConfiguration.is() || maQueue.empty())
+ return;
+
+ // Get and remove the first entry from the queue.
+ Reference<XConfigurationChangeRequest> xRequest (maQueue.front());
+ maQueue.pop();
+
+ // Execute the change request.
+ if (xRequest.is())
+ {
+#if DEBUG_SD_CONFIGURATION_TRACE
+ TraceRequest(xRequest);
+#endif
+ xRequest->execute(mxConfiguration);
+ }
+
+ if (!maQueue.empty())
+ return;
+
+ SAL_INFO("sd.fwk", __func__ << ": All requests are processed");
+ // The queue is empty so tell the ConfigurationManager to update
+ // its state.
+ if (mpConfigurationUpdater != nullptr)
+ {
+#if DEBUG_SD_CONFIGURATION_TRACE
+ ConfigurationTracer::TraceConfiguration (
+ mxConfiguration, "updating to configuration");
+#endif
+ mpConfigurationUpdater->RequestUpdate(mxConfiguration);
+ }
+}
+
+bool ChangeRequestQueueProcessor::IsEmpty() const
+{
+ return maQueue.empty();
+}
+
+void ChangeRequestQueueProcessor::ProcessUntilEmpty()
+{
+ while ( ! IsEmpty())
+ ProcessOneEvent();
+}
+
+void ChangeRequestQueueProcessor::Clear()
+{
+ ::osl::MutexGuard aGuard (maMutex);
+ ChangeRequestQueue().swap(maQueue);
+}
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.hxx b/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.hxx
new file mode 100644
index 000000000..4afd5af76
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "ChangeRequestQueue.hxx"
+#include <osl/mutex.hxx>
+
+#include <tools/link.hxx>
+
+#include <memory>
+
+namespace com::sun::star::drawing::framework
+{
+class XConfiguration;
+}
+namespace com::sun::star::drawing::framework
+{
+class XConfigurationChangeRequest;
+}
+
+struct ImplSVEvent;
+
+namespace sd::framework
+{
+class ConfigurationUpdater;
+
+/** The ChangeRequestQueueProcessor owns the ChangeRequestQueue and
+ processes the configuration change requests.
+
+ When after processing one entry the queue is empty then the
+ XConfigurationController::update() method is called so that the changes
+ made to the local XConfiguration reference are reflected by the UI.
+
+ Queue entries are processed asynchronously by calling PostUserEvent().
+*/
+class ChangeRequestQueueProcessor
+{
+public:
+ /** The queue processor is created with a reference to an
+ ConfigurationController so that its UpdateConfiguration() method can
+ be called when the queue becomes empty.
+ */
+ explicit ChangeRequestQueueProcessor(const std::shared_ptr<ConfigurationUpdater>& rpUpdater);
+ ~ChangeRequestQueueProcessor();
+
+ /** Sets the configuration who will be changed by subsequent change
+ requests. This method should be called only by the configuration
+ controller who owns the configuration.
+ */
+ void SetConfiguration(
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration);
+
+ /** The given request is appended to the end of the queue and will
+ eventually be processed when all other entries in front of it have
+ been processed.
+ */
+ void AddRequest(
+ const css::uno::Reference<css::drawing::framework::XConfigurationChangeRequest>& rxRequest);
+
+ /** Returns </sal_True> when the queue is empty.
+ */
+ bool IsEmpty() const;
+
+ /** Process all events in the queue synchronously.
+
+ <p>This method is typically called when the framework is shut down
+ to establish an empty configuration.</p>
+ */
+ void ProcessUntilEmpty();
+
+ /** Process the first event in queue.
+ */
+ void ProcessOneEvent();
+
+ /** Remove all events from the queue.
+
+ <p>This method is typically called when the framework is shut down
+ to avoid the processing of still pending activation requests.</p>
+ */
+ void Clear();
+
+private:
+ mutable ::osl::Mutex maMutex;
+
+ ChangeRequestQueue maQueue;
+
+ /** The id returned by the last PostUserEvent() call. This id is stored
+ so that a pending user event can be removed when the queue processor
+ is destroyed.
+ */
+ ImplSVEvent* mnUserEventId;
+
+ css::uno::Reference<css::drawing::framework::XConfiguration> mxConfiguration;
+
+ std::shared_ptr<ConfigurationUpdater> mpConfigurationUpdater;
+
+ /** Initiate the processing of the entries in the queue. The actual
+ processing starts asynchronously.
+ */
+ void StartProcessing();
+
+ /** Callback function for the PostUserEvent() call.
+ */
+ DECL_LINK(ProcessEvent, void*, void);
+};
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/Configuration.cxx b/sd/source/ui/framework/configuration/Configuration.cxx
new file mode 100644
index 000000000..7b813a42b
--- /dev/null
+++ b/sd/source/ui/framework/configuration/Configuration.cxx
@@ -0,0 +1,311 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/Configuration.hxx>
+
+#include <framework/FrameworkHelper.hxx>
+
+#include <com/sun/star/drawing/framework/ConfigurationChangeEvent.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationControllerBroadcaster.hpp>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+using ::sd::framework::FrameworkHelper;
+
+namespace {
+/** Use the XResourceId::compareTo() method to implement a compare operator
+ for STL containers.
+*/
+class XResourceIdLess
+{
+public:
+ bool operator () (const Reference<XResourceId>& rId1, const Reference<XResourceId>& rId2) const
+ {
+ return rId1->compareTo(rId2) == -1;
+ }
+};
+
+} // end of anonymous namespace
+
+namespace sd::framework {
+
+class Configuration::ResourceContainer
+ : public ::std::set<Reference<XResourceId>, XResourceIdLess>
+{
+public:
+ ResourceContainer() {}
+};
+
+//===== Configuration =========================================================
+
+Configuration::Configuration (
+ const Reference<XConfigurationControllerBroadcaster>& rxBroadcaster,
+ bool bBroadcastRequestEvents)
+ : mpResourceContainer(new ResourceContainer()),
+ mxBroadcaster(rxBroadcaster),
+ mbBroadcastRequestEvents(bBroadcastRequestEvents)
+{
+}
+
+Configuration::Configuration (
+ const Reference<XConfigurationControllerBroadcaster>& rxBroadcaster,
+ bool bBroadcastRequestEvents,
+ const ResourceContainer& rResourceContainer)
+ : mpResourceContainer(new ResourceContainer(rResourceContainer)),
+ mxBroadcaster(rxBroadcaster),
+ mbBroadcastRequestEvents(bBroadcastRequestEvents)
+{
+}
+
+Configuration::~Configuration()
+{
+}
+
+void Configuration::disposing(std::unique_lock<std::mutex>&)
+{
+ mpResourceContainer->clear();
+ mxBroadcaster = nullptr;
+}
+
+//----- XConfiguration --------------------------------------------------------
+
+void SAL_CALL Configuration::addResource (const Reference<XResourceId>& rxResourceId)
+{
+ ThrowIfDisposed();
+
+ if ( ! rxResourceId.is() || rxResourceId->getResourceURL().isEmpty())
+ throw css::lang::IllegalArgumentException();
+
+ if (mpResourceContainer->insert(rxResourceId).second)
+ {
+ SAL_INFO("sd.fwk", __func__ << ": Configuration::addResource() " <<
+ FrameworkHelper::ResourceIdToString(rxResourceId));
+ PostEvent(rxResourceId, true);
+ }
+}
+
+void SAL_CALL Configuration::removeResource (const Reference<XResourceId>& rxResourceId)
+{
+ ThrowIfDisposed();
+
+ if ( ! rxResourceId.is() || rxResourceId->getResourceURL().isEmpty())
+ throw css::lang::IllegalArgumentException();
+
+ ResourceContainer::iterator iResource (mpResourceContainer->find(rxResourceId));
+ if (iResource != mpResourceContainer->end())
+ {
+ SAL_INFO("sd.fwk", __func__ << ": Configuration::removeResource() " <<
+ FrameworkHelper::ResourceIdToString(rxResourceId));
+ PostEvent(rxResourceId,false);
+ mpResourceContainer->erase(iResource);
+ }
+}
+
+Sequence<Reference<XResourceId> > SAL_CALL Configuration::getResources (
+ const Reference<XResourceId>& rxAnchorId,
+ const OUString& rsResourceURLPrefix,
+ AnchorBindingMode eMode)
+{
+ std::unique_lock aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ const bool bFilterResources (!rsResourceURLPrefix.isEmpty());
+
+ // Collect the matching resources in a vector.
+ ::std::vector<Reference<XResourceId> > aResources;
+ for (const auto& rxResource : *mpResourceContainer)
+ {
+ if ( ! rxResource->isBoundTo(rxAnchorId,eMode))
+ continue;
+
+ if (bFilterResources)
+ {
+ // Apply the given resource prefix as filter.
+
+ // Make sure that the resource is bound directly to the anchor.
+ if (eMode != AnchorBindingMode_DIRECT
+ && ! rxResource->isBoundTo(rxAnchorId, AnchorBindingMode_DIRECT))
+ {
+ continue;
+ }
+
+ // Make sure that the resource URL matches the given prefix.
+ if ( ! rxResource->getResourceURL().match(rsResourceURLPrefix))
+ {
+ continue;
+ }
+ }
+
+ aResources.push_back(rxResource);
+ }
+
+ return comphelper::containerToSequence(aResources);
+}
+
+sal_Bool SAL_CALL Configuration::hasResource (const Reference<XResourceId>& rxResourceId)
+{
+ std::unique_lock aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ return rxResourceId.is()
+ && mpResourceContainer->find(rxResourceId) != mpResourceContainer->end();
+}
+
+//----- XCloneable ------------------------------------------------------------
+
+Reference<util::XCloneable> SAL_CALL Configuration::createClone()
+{
+ std::unique_lock aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ return new Configuration(
+ mxBroadcaster,
+ mbBroadcastRequestEvents,
+ *mpResourceContainer);
+}
+
+//----- XNamed ----------------------------------------------------------------
+
+OUString SAL_CALL Configuration::getName()
+{
+ std::unique_lock aGuard (m_aMutex);
+ OUStringBuffer aString;
+
+ if (m_bDisposed)
+ aString.append("DISPOSED ");
+ aString.append("Configuration[");
+
+ ResourceContainer::const_iterator iResource;
+ for (iResource=mpResourceContainer->begin();
+ iResource!=mpResourceContainer->end();
+ ++iResource)
+ {
+ if (iResource != mpResourceContainer->begin())
+ aString.append(", ");
+ aString.append(FrameworkHelper::ResourceIdToString(*iResource));
+ }
+ aString.append("]");
+
+ return aString.makeStringAndClear();
+}
+
+void SAL_CALL Configuration::setName (const OUString&)
+{
+ // ignored.
+}
+
+OUString Configuration::getImplementationName()
+{
+ return
+ "com.sun.star.comp.Draw.framework.configuration.Configuration";
+}
+
+sal_Bool Configuration::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> Configuration::getSupportedServiceNames()
+{
+ return css::uno::Sequence<OUString>{
+ "com.sun.star.drawing.framework.Configuration"};
+}
+
+void Configuration::PostEvent (
+ const Reference<XResourceId>& rxResourceId,
+ const bool bActivation)
+{
+ OSL_ASSERT(rxResourceId.is());
+
+ if (!mxBroadcaster.is())
+ return;
+
+ ConfigurationChangeEvent aEvent;
+ aEvent.ResourceId = rxResourceId;
+ if (bActivation)
+ if (mbBroadcastRequestEvents)
+ aEvent.Type = FrameworkHelper::msResourceActivationRequestEvent;
+ else
+ aEvent.Type = FrameworkHelper::msResourceActivationEvent;
+ else
+ if (mbBroadcastRequestEvents)
+ aEvent.Type = FrameworkHelper::msResourceDeactivationRequestEvent;
+ else
+ aEvent.Type = FrameworkHelper::msResourceDeactivationEvent;
+ aEvent.Configuration = this;
+
+ mxBroadcaster->notifyEvent(aEvent);
+}
+
+void Configuration::ThrowIfDisposed() const
+{
+ if (m_bDisposed)
+ {
+ throw lang::DisposedException ("Configuration object has already been disposed",
+ const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
+ }
+}
+
+bool AreConfigurationsEquivalent (
+ const Reference<XConfiguration>& rxConfiguration1,
+ const Reference<XConfiguration>& rxConfiguration2)
+{
+ if (rxConfiguration1.is() != rxConfiguration2.is())
+ return false;
+ if ( ! rxConfiguration1.is() && ! rxConfiguration2.is())
+ return true;
+
+ // Get the lists of resources from the two given configurations.
+ const Sequence<Reference<XResourceId> > aResources1(
+ rxConfiguration1->getResources(
+ nullptr, OUString(), AnchorBindingMode_INDIRECT));
+ const Sequence<Reference<XResourceId> > aResources2(
+ rxConfiguration2->getResources(
+ nullptr, OUString(), AnchorBindingMode_INDIRECT));
+
+ // When the number of resources differ then the configurations can not
+ // be equivalent.
+ // Comparison of the two lists of resource ids relies on their
+ // ordering.
+ return std::equal(aResources1.begin(), aResources1.end(), aResources2.begin(), aResources2.end(),
+ [](const Reference<XResourceId>& a, const Reference<XResourceId>& b) {
+ if (a.is() && b.is())
+ return a->compareTo(b) == 0;
+ return a.is() == b.is();
+ });
+}
+
+} // end of namespace sd::framework
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Draw_framework_configuration_Configuration_get_implementation(
+ css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new sd::framework::Configuration(nullptr, false));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ConfigurationClassifier.cxx b/sd/source/ui/framework/configuration/ConfigurationClassifier.cxx
new file mode 100644
index 000000000..99fc1297d
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ConfigurationClassifier.cxx
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ConfigurationClassifier.hxx"
+
+#include <framework/FrameworkHelper.hxx>
+#include <com/sun/star/drawing/framework/XConfiguration.hpp>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sd::framework {
+
+ConfigurationClassifier::ConfigurationClassifier (
+ const Reference<XConfiguration>& rxConfiguration1,
+ const Reference<XConfiguration>& rxConfiguration2)
+ : mxConfiguration1(rxConfiguration1),
+ mxConfiguration2(rxConfiguration2)
+{
+}
+
+bool ConfigurationClassifier::Partition()
+{
+ maC1minusC2.clear();
+ maC2minusC1.clear();
+
+ PartitionResources(
+ mxConfiguration1->getResources(nullptr, OUString(), AnchorBindingMode_DIRECT),
+ mxConfiguration2->getResources(nullptr, OUString(), AnchorBindingMode_DIRECT));
+
+ return !maC1minusC2.empty() || !maC2minusC1.empty();
+}
+
+void ConfigurationClassifier::PartitionResources (
+ const css::uno::Sequence<Reference<XResourceId> >& rS1,
+ const css::uno::Sequence<Reference<XResourceId> >& rS2)
+{
+ ResourceIdVector aC1minusC2;
+ ResourceIdVector aC2minusC1;
+ ResourceIdVector aC1andC2;
+
+ // Classify the resources in the configurations that are not bound to
+ // other resources.
+ ClassifyResources(
+ rS1,
+ rS2,
+ aC1minusC2,
+ aC2minusC1,
+ aC1andC2);
+
+ SAL_INFO("sd.fwk", __func__ << ": copying resource ids to C1-C2");
+ CopyResources(aC1minusC2, mxConfiguration1, maC1minusC2);
+ SAL_INFO("sd.fwk", __func__ << ": copying resource ids to C2-C1");
+ CopyResources(aC2minusC1, mxConfiguration2, maC2minusC1);
+
+ // Process the unique resources that belong to both configurations.
+ for (const auto& rxResource : aC1andC2)
+ {
+ PartitionResources(
+ mxConfiguration1->getResources(rxResource, OUString(), AnchorBindingMode_DIRECT),
+ mxConfiguration2->getResources(rxResource, OUString(), AnchorBindingMode_DIRECT));
+ }
+}
+
+void ConfigurationClassifier::ClassifyResources (
+ const css::uno::Sequence<Reference<XResourceId> >& rS1,
+ const css::uno::Sequence<Reference<XResourceId> >& rS2,
+ ResourceIdVector& rS1minusS2,
+ ResourceIdVector& rS2minusS1,
+ ResourceIdVector& rS1andS2)
+{
+ // Find all elements in rS1 and place them in rS1minusS2 or rS1andS2
+ // depending on whether they are in rS2 or not.
+ for (const Reference<XResourceId>& rA1 : rS1)
+ {
+ bool bFound = std::any_of(rS2.begin(), rS2.end(),
+ [&rA1](const Reference<XResourceId>& rA2) {
+ return rA1->getResourceURL() == rA2->getResourceURL(); });
+
+ if (bFound)
+ rS1andS2.push_back(rA1);
+ else
+ rS1minusS2.push_back(rA1);
+ }
+
+ // Find all elements in rS2 that are not in rS1. The elements that are
+ // in both rS1 and rS2 have been handled above and are therefore ignored
+ // here.
+ for (const Reference<XResourceId>& rA2 : rS2)
+ {
+ bool bFound = std::any_of(rS1.begin(), rS1.end(),
+ [&rA2](const Reference<XResourceId>& rA1) {
+ return rA2->getResourceURL() == rA1->getResourceURL(); });
+
+ if ( ! bFound)
+ rS2minusS1.push_back(rA2);
+ }
+}
+
+void ConfigurationClassifier::CopyResources (
+ const ResourceIdVector& rSource,
+ const Reference<XConfiguration>& rxConfiguration,
+ ResourceIdVector& rTarget)
+{
+ // Copy all resources bound to the ones in aC1minusC2Unique to rC1minusC2.
+ for (const auto& rxResource : rSource)
+ {
+ const Sequence<Reference<XResourceId> > aBoundResources (
+ rxConfiguration->getResources(
+ rxResource,
+ OUString(),
+ AnchorBindingMode_INDIRECT));
+ const sal_Int32 nL (aBoundResources.getLength());
+
+ rTarget.reserve(rTarget.size() + 1 + nL);
+ rTarget.push_back(rxResource);
+
+ SAL_INFO("sd.fwk", __func__ << ": copying " <<
+ FrameworkHelper::ResourceIdToString(rxResource));
+
+ for (const Reference<XResourceId>& rBoundResource : aBoundResources)
+ {
+ rTarget.push_back(rBoundResource);
+ SAL_INFO("sd.fwk", __func__ << ": copying " <<
+ FrameworkHelper::ResourceIdToString(rBoundResource));
+ }
+ }
+}
+
+#if DEBUG_SD_CONFIGURATION_TRACE
+
+void ConfigurationClassifier::TraceResourceIdVector (
+ const char* pMessage,
+ const ResourceIdVector& rResources)
+{
+
+ SAL_INFO("sd.fwk", __func__ << ": " << pMessage);
+ for (const auto& rxResource : rResources)
+ {
+ OUString sResource (FrameworkHelper::ResourceIdToString(rxResource));
+ SAL_INFO("sd.fwk", __func__ << ": " << sResource);
+ }
+}
+
+#endif
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ConfigurationClassifier.hxx b/sd/source/ui/framework/configuration/ConfigurationClassifier.hxx
new file mode 100644
index 000000000..e9384713b
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ConfigurationClassifier.hxx
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "debugtrace.hxx"
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <vector>
+
+namespace com::sun::star::drawing::framework
+{
+class XConfiguration;
+}
+namespace com::sun::star::drawing::framework
+{
+class XResourceId;
+}
+
+namespace sd::framework
+{
+/** A ConfigurationClassifier object compares two configurations of
+ resources and gives access to the differences. It is used mainly when
+ changes to the current configuration have been requested and the various
+ resource controllers have to be supplied with the set of resources that
+ are to be activated or deactivated.
+*/
+class ConfigurationClassifier
+{
+public:
+ /** Create a new ConfigurationClassifier object that will compare the
+ two given configurations.
+ */
+ ConfigurationClassifier(
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration1,
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration2);
+
+ /** Calculate three lists of resource ids. These contain the resources
+ that belong to one configuration but not the other, or that belong
+ to both configurations.
+ @return
+ When the two configurations differ then return <TRUE/>. When
+ they are equivalent then return <FALSE/>.
+ */
+ bool Partition();
+
+ typedef ::std::vector<css::uno::Reference<css::drawing::framework::XResourceId>>
+ ResourceIdVector;
+
+ /** Return the resources that belong to the configuration given as
+ rxConfiguration1 to the constructor but that do not belong to
+ rxConfiguration2.
+ @return
+ A reference to the, possibly empty, list of resources is
+ returned. This reference remains valid as long as the called
+ ConfigurationClassifier object stays alive.
+ */
+ const ResourceIdVector& GetC1minusC2() const { return maC1minusC2; }
+
+ /** Return the resources that belong to the configuration given as
+ rxConfiguration2 to the constructor but that do not belong to
+ rxConfiguration1.
+ @return
+ A reference to the, possibly empty, list of resources is
+ returned. This reference remains valid as long as the called
+ ConfigurationClassifier object stays alive.
+ */
+ const ResourceIdVector& GetC2minusC1() const { return maC2minusC1; }
+
+#if DEBUG_SD_CONFIGURATION_TRACE
+
+ /** Return the resources that belong to both the configurations that
+ where given to the constructor.
+ @return
+ A reference to the, possibly empty, list of resources is
+ returned. This reference remains valid as long as the called
+ ConfigurationClassifier object stays alive.
+ */
+ const ResourceIdVector& GetC1andC2() const { return maC1andC2; }
+
+ static void TraceResourceIdVector(const char* pMessage, const ResourceIdVector& rResources);
+
+#endif
+
+private:
+ css::uno::Reference<css::drawing::framework::XConfiguration> mxConfiguration1;
+ css::uno::Reference<css::drawing::framework::XConfiguration> mxConfiguration2;
+
+ /** After the call to Classify() this vector holds all elements from
+ mxConfiguration1 that are not in mxConfiguration2.
+ */
+ ResourceIdVector maC1minusC2;
+
+ /** After the call to Classify() this vector holds all elements from
+ mxConfiguration2 that are not in mxConfiguration1.
+ */
+ ResourceIdVector maC2minusC1;
+
+ /** Put all the elements in the two given sequences of resource ids and
+ copy them into one of the resource id result vectors maC1minusC2,
+ maC2minusC1, and maC1andC2. This is done by using only the resource
+ URLs for classification. Therefore this method calls itself
+ recursively.
+ @param rS1
+ One sequence of XResourceId objects.
+ @param rS2
+ Another sequence of XResourceId objects.
+ */
+ void PartitionResources(
+ const css::uno::Sequence<css::uno::Reference<css::drawing::framework::XResourceId>>& rS1,
+ const css::uno::Sequence<css::uno::Reference<css::drawing::framework::XResourceId>>& rS2);
+
+ /** Compare the given sequences of resource ids and put their elements
+ in one of three vectors depending on whether an element belongs to
+ both sequences or to one but not the other. Note that only the
+ resource URLs of the XResourceId objects are used for the
+ classification.
+ @param rS1
+ One sequence of XResourceId objects.
+ @param rS2
+ Another sequence of XResourceId objects.
+ */
+ static void ClassifyResources(
+ const css::uno::Sequence<css::uno::Reference<css::drawing::framework::XResourceId>>& rS1,
+ const css::uno::Sequence<css::uno::Reference<css::drawing::framework::XResourceId>>& rS2,
+ ResourceIdVector& rS1minusS2, ResourceIdVector& rS2minusS1, ResourceIdVector& rS1andS2);
+
+ /** Copy the resources given in rSource to the list of resources
+ specified by rTarget. Resources bound to the ones in rSource,
+ either directly or indirectly, are copied as well.
+ @param rSource
+ All resources and the ones bound to them, either directly or
+ indirectly, are copied.
+ @param rxConfiguration
+ This configuration is used to determine the resources bound to
+ the ones in rSource.
+ @param rTarget
+ This list is filled with resources from rSource and the ones
+ bound to them.
+ */
+ static void CopyResources(
+ const ResourceIdVector& rSource,
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration,
+ ResourceIdVector& rTarget);
+};
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ConfigurationController.cxx b/sd/source/ui/framework/configuration/ConfigurationController.cxx
new file mode 100644
index 000000000..3fc95adb9
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ConfigurationController.cxx
@@ -0,0 +1,541 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/ConfigurationController.hxx>
+#include <framework/Configuration.hxx>
+#include <framework/FrameworkHelper.hxx>
+#include "ConfigurationUpdater.hxx"
+#include "ConfigurationControllerBroadcaster.hxx"
+#include "ConfigurationTracer.hxx"
+#include "GenericConfigurationChangeRequest.hxx"
+#include "ConfigurationControllerResourceManager.hxx"
+#include "ResourceFactoryManager.hxx"
+#include "UpdateRequest.hxx"
+#include "ChangeRequestQueueProcessor.hxx"
+#include "ConfigurationClassifier.hxx"
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+#include <com/sun/star/frame/XController.hpp>
+
+#include <sal/log.hxx>
+#include <osl/mutex.hxx>
+#include <vcl/svapp.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+using ::sd::framework::FrameworkHelper;
+
+namespace sd::framework {
+
+//----- ConfigurationController::Implementation -------------------------------
+
+class ConfigurationController::Implementation
+{
+public:
+ Implementation (
+ ConfigurationController& rController,
+ const Reference<frame::XController>& rxController);
+
+ Reference<XControllerManager> mxControllerManager;
+
+ /** The Broadcaster class implements storing and calling of listeners.
+ */
+ std::shared_ptr<ConfigurationControllerBroadcaster> mpBroadcaster;
+
+ /** The requested configuration which is modified (asynchronously) by
+ calls to requestResourceActivation() and
+ requestResourceDeactivation(). The mpConfigurationUpdater makes the
+ current configuration reflect the content of this one.
+ */
+ css::uno::Reference<css::drawing::framework::XConfiguration> mxRequestedConfiguration;
+
+ std::shared_ptr<ResourceFactoryManager> mpResourceFactoryContainer;
+
+ std::shared_ptr<ConfigurationControllerResourceManager> mpResourceManager;
+
+ std::shared_ptr<ConfigurationUpdater> mpConfigurationUpdater;
+
+ /** The queue processor owns the queue of configuration change request
+ objects and processes the objects.
+ */
+ std::unique_ptr<ChangeRequestQueueProcessor> mpQueueProcessor;
+
+ std::shared_ptr<ConfigurationUpdaterLock> mpConfigurationUpdaterLock;
+
+ sal_Int32 mnLockCount;
+};
+
+//===== ConfigurationController::Lock =========================================
+
+ConfigurationController::Lock::Lock (const Reference<XConfigurationController>& rxController)
+ : mxController(rxController)
+{
+ OSL_ASSERT(mxController.is());
+
+ if (mxController.is())
+ mxController->lock();
+}
+
+ConfigurationController::Lock::~Lock()
+{
+ if (mxController.is())
+ mxController->unlock();
+}
+
+//===== ConfigurationController ===============================================
+
+ConfigurationController::ConfigurationController() noexcept
+ : ConfigurationControllerInterfaceBase(m_aMutex)
+ , mbIsDisposed(false)
+{
+}
+
+ConfigurationController::~ConfigurationController() noexcept
+{
+}
+
+void SAL_CALL ConfigurationController::disposing()
+{
+ if (mpImplementation == nullptr)
+ return;
+
+ SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::disposing");
+ SAL_INFO("sd.fwk", __func__ << ": requesting empty configuration");
+ // To destroy all resources an empty configuration is requested and then,
+ // synchronously, all resulting requests are processed.
+ mpImplementation->mpQueueProcessor->Clear();
+ restoreConfiguration(new Configuration(this,false));
+ mpImplementation->mpQueueProcessor->ProcessUntilEmpty();
+ SAL_INFO("sd.fwk", __func__ << ": all requests processed");
+
+ // Now that all resources have been deactivated, mark the controller as
+ // disposed.
+ mbIsDisposed = true;
+
+ // Release the listeners.
+ lang::EventObject aEvent;
+ aEvent.Source = uno::Reference<uno::XInterface>(static_cast<cppu::OWeakObject*>(this));
+
+ {
+ const SolarMutexGuard aSolarGuard;
+ mpImplementation->mpBroadcaster->DisposeAndClear();
+ }
+
+ mpImplementation->mpQueueProcessor.reset();
+ mpImplementation->mxRequestedConfiguration = nullptr;
+ mpImplementation.reset();
+}
+
+void ConfigurationController::ProcessEvent()
+{
+ if (mpImplementation != nullptr)
+ {
+ OSL_ASSERT(mpImplementation->mpQueueProcessor != nullptr);
+
+ mpImplementation->mpQueueProcessor->ProcessOneEvent();
+ }
+}
+
+void ConfigurationController::RequestSynchronousUpdate()
+{
+ if (mpImplementation == nullptr)
+ return;
+ if (mpImplementation->mpQueueProcessor == nullptr)
+ return;
+ mpImplementation->mpQueueProcessor->ProcessUntilEmpty();
+}
+
+//----- XConfigurationControllerBroadcaster -----------------------------------
+
+void SAL_CALL ConfigurationController::addConfigurationChangeListener (
+ const Reference<XConfigurationChangeListener>& rxListener,
+ const OUString& rsEventType,
+ const Any& rUserData)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ ThrowIfDisposed();
+ OSL_ASSERT(mpImplementation != nullptr);
+ mpImplementation->mpBroadcaster->AddListener(rxListener, rsEventType, rUserData);
+}
+
+void SAL_CALL ConfigurationController::removeConfigurationChangeListener (
+ const Reference<XConfigurationChangeListener>& rxListener)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ ThrowIfDisposed();
+ mpImplementation->mpBroadcaster->RemoveListener(rxListener);
+}
+
+void SAL_CALL ConfigurationController::notifyEvent (
+ const ConfigurationChangeEvent& rEvent)
+{
+ ThrowIfDisposed();
+ mpImplementation->mpBroadcaster->NotifyListeners(rEvent);
+}
+
+//----- XConfigurationController ----------------------------------------------
+
+void SAL_CALL ConfigurationController::lock()
+{
+ OSL_ASSERT(mpImplementation != nullptr);
+ OSL_ASSERT(mpImplementation->mpConfigurationUpdater != nullptr);
+
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ ++mpImplementation->mnLockCount;
+ if (mpImplementation->mpConfigurationUpdaterLock == nullptr)
+ mpImplementation->mpConfigurationUpdaterLock
+ = mpImplementation->mpConfigurationUpdater->GetLock();
+}
+
+void SAL_CALL ConfigurationController::unlock()
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ // Allow unlocking while the ConfigurationController is being disposed
+ // (but not when that is done and the controller is disposed.)
+ if (rBHelper.bDisposed)
+ ThrowIfDisposed();
+
+ OSL_ASSERT(mpImplementation->mnLockCount>0);
+ --mpImplementation->mnLockCount;
+ if (mpImplementation->mnLockCount == 0)
+ mpImplementation->mpConfigurationUpdaterLock.reset();
+}
+
+void SAL_CALL ConfigurationController::requestResourceActivation (
+ const Reference<XResourceId>& rxResourceId,
+ ResourceActivationMode eMode)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ // Check whether we are being disposed. This is handled differently
+ // then being completely disposed because the first thing disposing()
+ // does is to deactivate all remaining resources. This is done via
+ // regular methods which must not throw DisposedExceptions. Therefore
+ // we just return silently during that stage.
+ if (rBHelper.bInDispose)
+ {
+ SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::requestResourceActivation(): ignoring " <<
+ FrameworkHelper::ResourceIdToString(rxResourceId));
+ return;
+ }
+
+ SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::requestResourceActivation() " <<
+ FrameworkHelper::ResourceIdToString(rxResourceId));
+
+ if (!rxResourceId.is())
+ return;
+
+ if (eMode == ResourceActivationMode_REPLACE)
+ {
+ // Get a list of the matching resources and create deactivation
+ // requests for them.
+ const Sequence<Reference<XResourceId> > aResourceList (
+ mpImplementation->mxRequestedConfiguration->getResources(
+ rxResourceId->getAnchor(),
+ rxResourceId->getResourceTypePrefix(),
+ AnchorBindingMode_DIRECT));
+
+ for (const auto& rResource : aResourceList)
+ {
+ // Do not request the deactivation of the resource for which
+ // this method was called. Doing it would not change the
+ // outcome but would result in unnecessary work.
+ if (rxResourceId->compareTo(rResource) == 0)
+ continue;
+
+ // Request the deactivation of a resource and all resources
+ // linked to it.
+ requestResourceDeactivation(rResource);
+ }
+ }
+
+ Reference<XConfigurationChangeRequest> xRequest(
+ new GenericConfigurationChangeRequest(
+ rxResourceId,
+ GenericConfigurationChangeRequest::Activation));
+ postChangeRequest(xRequest);
+}
+
+void SAL_CALL ConfigurationController::requestResourceDeactivation (
+ const Reference<XResourceId>& rxResourceId)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::requestResourceDeactivation() " <<
+ FrameworkHelper::ResourceIdToString(rxResourceId));
+
+ if (!rxResourceId.is())
+ return;
+
+ // Request deactivation of all resources linked to the specified one
+ // as well.
+ const Sequence<Reference<XResourceId> > aLinkedResources (
+ mpImplementation->mxRequestedConfiguration->getResources(
+ rxResourceId,
+ OUString(),
+ AnchorBindingMode_DIRECT));
+ for (const auto& rLinkedResource : aLinkedResources)
+ {
+ // We do not add deactivation requests directly but call this
+ // method recursively, so that when one time there are resources
+ // linked to linked resources, these are handled correctly, too.
+ requestResourceDeactivation(rLinkedResource);
+ }
+
+ // Add a deactivation request for the specified resource.
+ Reference<XConfigurationChangeRequest> xRequest(
+ new GenericConfigurationChangeRequest(
+ rxResourceId,
+ GenericConfigurationChangeRequest::Deactivation));
+ postChangeRequest(xRequest);
+}
+
+Reference<XResource> SAL_CALL ConfigurationController::getResource (
+ const Reference<XResourceId>& rxResourceId)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ ConfigurationControllerResourceManager::ResourceDescriptor aDescriptor (
+ mpImplementation->mpResourceManager->GetResource(rxResourceId));
+ return aDescriptor.mxResource;
+}
+
+void SAL_CALL ConfigurationController::update()
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ if (mpImplementation->mpQueueProcessor->IsEmpty())
+ {
+ // The queue is empty. Add another request that does nothing but
+ // asynchronously trigger a request for an update.
+ mpImplementation->mpQueueProcessor->AddRequest(new UpdateRequest());
+ }
+ else
+ {
+ // The queue is not empty, so we rely on the queue processor to
+ // request an update automatically when the queue becomes empty.
+ }
+}
+
+sal_Bool SAL_CALL ConfigurationController::hasPendingRequests()
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ return ! mpImplementation->mpQueueProcessor->IsEmpty();
+}
+
+void SAL_CALL ConfigurationController::postChangeRequest (
+ const Reference<XConfigurationChangeRequest>& rxRequest)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ mpImplementation->mpQueueProcessor->AddRequest(rxRequest);
+}
+
+Reference<XConfiguration> SAL_CALL ConfigurationController::getRequestedConfiguration()
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ if (mpImplementation->mxRequestedConfiguration.is())
+ return Reference<XConfiguration>(
+ mpImplementation->mxRequestedConfiguration->createClone(), UNO_QUERY);
+ else
+ return Reference<XConfiguration>();
+}
+
+Reference<XConfiguration> SAL_CALL ConfigurationController::getCurrentConfiguration()
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ Reference<XConfiguration> xCurrentConfiguration(
+ mpImplementation->mpConfigurationUpdater->GetCurrentConfiguration());
+ if (xCurrentConfiguration.is())
+ return Reference<XConfiguration>(xCurrentConfiguration->createClone(), UNO_QUERY);
+ else
+ return Reference<XConfiguration>();
+}
+
+/** The given configuration is restored by generating the appropriate set of
+ activation and deactivation requests.
+*/
+void SAL_CALL ConfigurationController::restoreConfiguration (
+ const Reference<XConfiguration>& rxNewConfiguration)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ // We will probably be making a couple of activation and deactivation
+ // requests so lock the configuration controller and let it later update
+ // all changes at once.
+ std::shared_ptr<ConfigurationUpdaterLock> pLock (
+ mpImplementation->mpConfigurationUpdater->GetLock());
+
+ // Get lists of resources that are to be activated or deactivated.
+ Reference<XConfiguration> xCurrentConfiguration (mpImplementation->mxRequestedConfiguration);
+#if OSL_DEBUG_LEVEL >=1
+ SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::restoreConfiguration(");
+ ConfigurationTracer::TraceConfiguration(rxNewConfiguration, "requested configuration");
+ ConfigurationTracer::TraceConfiguration(xCurrentConfiguration, "current configuration");
+#endif
+ ConfigurationClassifier aClassifier (rxNewConfiguration, xCurrentConfiguration);
+ aClassifier.Partition();
+#if DEBUG_SD_CONFIGURATION_TRACE
+ aClassifier.TraceResourceIdVector(
+ "requested but not current resources:\n", aClassifier.GetC1minusC2());
+ aClassifier.TraceResourceIdVector(
+ "current but not requested resources:\n", aClassifier.GetC2minusC1());
+ aClassifier.TraceResourceIdVector(
+ "requested and current resources:\n", aClassifier.GetC1andC2());
+#endif
+
+ // Request the deactivation of resources that are not requested in the
+ // new configuration.
+ const ConfigurationClassifier::ResourceIdVector& rResourcesToDeactivate (
+ aClassifier.GetC2minusC1());
+ for (const auto& rxResource : rResourcesToDeactivate)
+ {
+ requestResourceDeactivation(rxResource);
+ }
+
+ // Request the activation of resources that are requested in the
+ // new configuration but are not part of the current configuration.
+ const ConfigurationClassifier::ResourceIdVector& rResourcesToActivate (
+ aClassifier.GetC1minusC2());
+ for (const auto& rxResource : rResourcesToActivate)
+ {
+ requestResourceActivation(rxResource, ResourceActivationMode_ADD);
+ }
+
+ pLock.reset();
+}
+
+//----- XResourceFactoryManager -----------------------------------------------
+
+void SAL_CALL ConfigurationController::addResourceFactory(
+ const OUString& sResourceURL,
+ const Reference<XResourceFactory>& rxResourceFactory)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+ mpImplementation->mpResourceFactoryContainer->AddFactory(sResourceURL, rxResourceFactory);
+}
+
+void SAL_CALL ConfigurationController::removeResourceFactoryForURL(
+ const OUString& sResourceURL)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+ mpImplementation->mpResourceFactoryContainer->RemoveFactoryForURL(sResourceURL);
+}
+
+void SAL_CALL ConfigurationController::removeResourceFactoryForReference(
+ const Reference<XResourceFactory>& rxResourceFactory)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+ mpImplementation->mpResourceFactoryContainer->RemoveFactoryForReference(rxResourceFactory);
+}
+
+Reference<XResourceFactory> SAL_CALL ConfigurationController::getResourceFactory (
+ const OUString& sResourceURL)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ ThrowIfDisposed();
+
+ return mpImplementation->mpResourceFactoryContainer->GetFactory(sResourceURL);
+}
+
+//----- XInitialization -------------------------------------------------------
+
+void SAL_CALL ConfigurationController::initialize (const Sequence<Any>& aArguments)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ if (aArguments.getLength() == 1)
+ {
+ const SolarMutexGuard aSolarGuard;
+
+ mpImplementation.reset(new Implementation(
+ *this,
+ Reference<frame::XController>(aArguments[0], UNO_QUERY_THROW)));
+ }
+}
+
+void ConfigurationController::ThrowIfDisposed () const
+{
+ if (mbIsDisposed)
+ {
+ throw lang::DisposedException ("ConfigurationController object has already been disposed",
+ const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
+ }
+
+ if (mpImplementation == nullptr)
+ {
+ OSL_ASSERT(mpImplementation != nullptr);
+ throw RuntimeException("ConfigurationController not initialized",
+ const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
+ }
+}
+
+//===== ConfigurationController::Implementation ===============================
+
+ConfigurationController::Implementation::Implementation (
+ ConfigurationController& rController,
+ const Reference<frame::XController>& rxController)
+ : mxControllerManager(rxController, UNO_QUERY_THROW),
+ mpBroadcaster(std::make_shared<ConfigurationControllerBroadcaster>(&rController)),
+ mxRequestedConfiguration(new Configuration(&rController, true)),
+ mpResourceFactoryContainer(std::make_shared<ResourceFactoryManager>(mxControllerManager)),
+ mpResourceManager(
+ std::make_shared<ConfigurationControllerResourceManager>(mpResourceFactoryContainer,mpBroadcaster)),
+ mpConfigurationUpdater(
+ std::make_shared<ConfigurationUpdater>(mpBroadcaster, mpResourceManager,mxControllerManager)),
+ mpQueueProcessor(new ChangeRequestQueueProcessor(mpConfigurationUpdater)),
+ mnLockCount(0)
+{
+ mpQueueProcessor->SetConfiguration(mxRequestedConfiguration);
+}
+
+} // end of namespace sd::framework
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Draw_framework_configuration_ConfigurationController_get_implementation(
+ css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new sd::framework::ConfigurationController());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.cxx b/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.cxx
new file mode 100644
index 000000000..5d9f22255
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.cxx
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ConfigurationControllerBroadcaster.hxx"
+#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
+#include <com/sun/star/drawing/framework/XResource.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sd::framework {
+
+ConfigurationControllerBroadcaster::ConfigurationControllerBroadcaster (
+ const Reference<XConfigurationController>& rxController)
+ : mxConfigurationController(rxController)
+{
+}
+
+void ConfigurationControllerBroadcaster::AddListener(
+ const Reference<XConfigurationChangeListener>& rxListener,
+ const OUString& rsEventType,
+ const Any& rUserData)
+{
+ if ( ! rxListener.is())
+ throw lang::IllegalArgumentException("invalid listener",
+ mxConfigurationController,
+ 0);
+
+ maListenerMap.try_emplace(rsEventType);
+
+ ListenerDescriptor aDescriptor;
+ aDescriptor.mxListener = rxListener;
+ aDescriptor.maUserData = rUserData;
+ maListenerMap[rsEventType].push_back(aDescriptor);
+}
+
+void ConfigurationControllerBroadcaster::RemoveListener(
+ const Reference<XConfigurationChangeListener>& rxListener)
+{
+ if ( ! rxListener.is())
+ throw lang::IllegalArgumentException("invalid listener",
+ mxConfigurationController,
+ 0);
+
+ ListenerList::iterator iList;
+ for (auto& rMap : maListenerMap)
+ {
+ iList = std::find_if(rMap.second.begin(), rMap.second.end(),
+ [&rxListener](const ListenerDescriptor& rList) { return rList.mxListener == rxListener; });
+ if (iList != rMap.second.end())
+ rMap.second.erase(iList);
+ }
+}
+
+void ConfigurationControllerBroadcaster::NotifyListeners (
+ const ListenerList& rList,
+ const ConfigurationChangeEvent& rEvent)
+{
+ // Create a local copy of the event in which the user data is modified
+ // for every listener.
+ ConfigurationChangeEvent aEvent (rEvent);
+
+ for (const auto& rListener : rList)
+ {
+ try
+ {
+ aEvent.UserData = rListener.maUserData;
+ rListener.mxListener->notifyConfigurationChange(aEvent);
+ }
+ catch (const lang::DisposedException& rException)
+ {
+ // When the exception comes from the listener itself then
+ // unregister it.
+ if (rException.Context == rListener.mxListener)
+ RemoveListener(rListener.mxListener);
+ }
+ catch (const RuntimeException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sd");
+ }
+ }
+}
+
+void ConfigurationControllerBroadcaster::NotifyListeners (const ConfigurationChangeEvent& rEvent)
+{
+ // Notify the specialized listeners.
+ ListenerMap::const_iterator iMap (maListenerMap.find(rEvent.Type));
+ if (iMap != maListenerMap.end())
+ {
+ // Create a local list of the listeners to avoid problems with
+ // concurrent changes and to be able to remove disposed listeners.
+ ListenerList aList (iMap->second.begin(), iMap->second.end());
+ NotifyListeners(aList,rEvent);
+ }
+
+ // Notify the universal listeners.
+ iMap = maListenerMap.find(OUString());
+ if (iMap != maListenerMap.end())
+ {
+ // Create a local list of the listeners to avoid problems with
+ // concurrent changes and to be able to remove disposed listeners.
+ ListenerList aList (iMap->second.begin(), iMap->second.end());
+ NotifyListeners(aList,rEvent);
+ }
+}
+
+void ConfigurationControllerBroadcaster::NotifyListeners (
+ const OUString& rsEventType,
+ const Reference<XResourceId>& rxResourceId,
+ const Reference<XResource>& rxResourceObject)
+{
+ ConfigurationChangeEvent aEvent;
+ aEvent.Type = rsEventType;
+ aEvent.ResourceId = rxResourceId;
+ aEvent.ResourceObject = rxResourceObject;
+ try
+ {
+ NotifyListeners(aEvent);
+ }
+ catch (const lang::DisposedException&)
+ {
+ }
+}
+
+void ConfigurationControllerBroadcaster::DisposeAndClear()
+{
+ lang::EventObject aEvent;
+ aEvent.Source = mxConfigurationController;
+ while (!maListenerMap.empty())
+ {
+ ListenerMap::iterator iMap (maListenerMap.begin());
+ if (iMap == maListenerMap.end())
+ break;
+
+ // When the first vector is empty then remove it from the map.
+ if (iMap->second.empty())
+ {
+ maListenerMap.erase(iMap);
+ continue;
+ }
+ else
+ {
+ Reference<XConfigurationChangeListener> xListener (
+ iMap->second.front().mxListener );
+ if (xListener.is())
+ {
+ // Tell the listener that the configuration controller is
+ // being disposed and remove the listener (for all event
+ // types).
+ try
+ {
+ RemoveListener(xListener);
+ xListener->disposing(aEvent);
+ }
+ catch (const RuntimeException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sd");
+ }
+ }
+ else
+ {
+ // Remove just this reference to the listener.
+ iMap->second.erase(iMap->second.begin());
+ }
+ }
+ }
+}
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.hxx b/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.hxx
new file mode 100644
index 000000000..5dfd6843d
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.hxx
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <unordered_map>
+#include <vector>
+
+namespace com::sun::star::drawing::framework { class XConfigurationChangeListener; }
+namespace com::sun::star::drawing::framework { class XConfigurationController; }
+namespace com::sun::star::drawing::framework { class XResource; }
+namespace com::sun::star::drawing::framework { class XResourceId; }
+namespace com::sun::star::drawing::framework { struct ConfigurationChangeEvent; }
+
+namespace sd::framework {
+
+/** This class manages the set of XConfigurationChangeListeners and
+ calls them when the ConfigurationController wants to broadcast an
+ event.
+
+ For every registered combination of listener and event type a user data
+ object is stored. This user data object is then given to the listener
+ whenever it is called for an event. With this the listener can use
+ a switch statement to handle different event types.
+*/
+class ConfigurationControllerBroadcaster
+{
+public:
+ /** The given controller is used as origin of thrown exceptions.
+ */
+ explicit ConfigurationControllerBroadcaster (
+ const css::uno::Reference<
+ css::drawing::framework::XConfigurationController>& rxController);
+
+ /** Add a listener for one type of event. When one listener is
+ interested in more than one event type this method has to be called
+ once for every event type. Alternatively it can register as
+ universal listener that will be called for all event types.
+ @param rxListener
+ A valid reference to a listener.
+ @param rsEventType
+ The type of event that the listener will be called for. The
+ empty string is a special value in that the listener will be
+ called for all event types.
+ @param rUserData
+ This object is passed to the listener whenever it is called for
+ the specified event type. For different event types different
+ user data objects can be provided.
+ @throws IllegalArgumentException
+ when an empty listener reference is given.
+ */
+ void AddListener(
+ const css::uno::Reference<
+ css::drawing::framework::XConfigurationChangeListener>& rxListener,
+ const OUString& rsEventType,
+ const css::uno::Any& rUserData);
+
+ /** Remove all references to the given listener. When one listener has
+ been registered for more than one type of event then it is removed
+ for all of them.
+ @param rxListener
+ A valid reference to a listener.
+ @throws IllegalArgumentException
+ when an empty listener reference is given.
+ */
+ void RemoveListener(
+ const css::uno::Reference<
+ css::drawing::framework::XConfigurationChangeListener>& rxListener);
+
+ /** Broadcast the given event to all listeners that have been registered
+ for its type of event as well as all universal listeners.
+
+ When calling a listener results in a DisposedException being thrown
+ the listener is unregistered automatically.
+ */
+ void NotifyListeners (
+ const css::drawing::framework::ConfigurationChangeEvent& rEvent);
+
+ /** This convenience variant of NotifyListeners create the event from
+ the given arguments.
+ */
+ void NotifyListeners (
+ const OUString& rsEventType,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId,
+ const css::uno::Reference<css::drawing::framework::XResource>& rxResourceObject);
+
+ /** Call all listeners and inform them that the
+ ConfigurationController is being disposed. When this method returns
+ the list of registered listeners is empty. Further calls to
+ RemoveListener() are not necessary but do not result in an error.
+ */
+ void DisposeAndClear();
+
+private:
+ css::uno::Reference<css::drawing::framework::XConfigurationController> mxConfigurationController;
+ class ListenerDescriptor
+ {
+ public:
+ css::uno::Reference<css::drawing::framework::XConfigurationChangeListener> mxListener;
+ css::uno::Any maUserData;
+ };
+ typedef std::vector<ListenerDescriptor> ListenerList;
+ typedef std::unordered_map
+ <OUString,
+ ListenerList> ListenerMap;
+ ListenerMap maListenerMap;
+
+ /** Broadcast the given event to all listeners in the given list.
+
+ When calling a listener results in a DisposedException being thrown
+ the listener is unregistered automatically.
+ */
+ void NotifyListeners (
+ const ListenerList& rList,
+ const css::drawing::framework::ConfigurationChangeEvent& rEvent);
+};
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.cxx b/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.cxx
new file mode 100644
index 000000000..904011d7d
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.cxx
@@ -0,0 +1,303 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ConfigurationControllerResourceManager.hxx"
+#include "ConfigurationControllerBroadcaster.hxx"
+#include "ResourceFactoryManager.hxx"
+#include <framework/FrameworkHelper.hxx>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/drawing/framework/XConfiguration.hpp>
+#include <com/sun/star/drawing/framework/XResourceFactory.hpp>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+#include <algorithm>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sd::framework {
+
+//===== ConfigurationControllerResourceManager ================================
+
+ConfigurationControllerResourceManager::ConfigurationControllerResourceManager (
+ const std::shared_ptr<ResourceFactoryManager>& rpResourceFactoryContainer,
+ const std::shared_ptr<ConfigurationControllerBroadcaster>& rpBroadcaster)
+ : maResourceMap(ResourceComparator()),
+ mpResourceFactoryContainer(rpResourceFactoryContainer),
+ mpBroadcaster(rpBroadcaster)
+{
+}
+
+ConfigurationControllerResourceManager::~ConfigurationControllerResourceManager()
+{
+}
+
+ConfigurationControllerResourceManager::ResourceDescriptor
+ ConfigurationControllerResourceManager::GetResource (
+ const Reference<XResourceId>& rxResourceId)
+{
+ ::osl::MutexGuard aGuard (maMutex);
+ ResourceMap::const_iterator iResource (maResourceMap.find(rxResourceId));
+ if (iResource != maResourceMap.end())
+ return iResource->second;
+ else
+ return ResourceDescriptor();
+}
+
+void ConfigurationControllerResourceManager::ActivateResources (
+ const ::std::vector<Reference<XResourceId> >& rResources,
+ const Reference<XConfiguration>& rxConfiguration)
+{
+ ::osl::MutexGuard aGuard (maMutex);
+ // Iterate in normal order over the resources that are to be
+ // activated so that resources on which others depend are activated
+ // before the depending resources are activated.
+ for (const Reference<XResourceId>& xResource : rResources)
+ ActivateResource(xResource, rxConfiguration);
+}
+
+void ConfigurationControllerResourceManager::DeactivateResources (
+ const ::std::vector<Reference<XResourceId> >& rResources,
+ const Reference<XConfiguration>& rxConfiguration)
+{
+ ::osl::MutexGuard aGuard (maMutex);
+ // Iterate in reverse order over the resources that are to be
+ // deactivated so that resources on which others depend are deactivated
+ // only when the depending resources have already been deactivated.
+ ::std::for_each(
+ rResources.rbegin(),
+ rResources.rend(),
+ [&] (Reference<XResourceId> const& xResource) {
+ return DeactivateResource(xResource, rxConfiguration);
+ } );
+}
+
+/* In this method we do following steps.
+ 1. Get the factory with which the resource will be created.
+ 2. Create the resource.
+ 3. Add the resource to the URL->Object map of the configuration
+ controller.
+ 4. Add the resource id to the current configuration.
+ 5. Notify listeners.
+*/
+void ConfigurationControllerResourceManager::ActivateResource (
+ const Reference<XResourceId>& rxResourceId,
+ const Reference<XConfiguration>& rxConfiguration)
+{
+ if ( ! rxResourceId.is())
+ {
+ OSL_ASSERT(rxResourceId.is());
+ return;
+ }
+
+ SAL_INFO("sd.fwk", __func__ << ": activating resource " <<
+ FrameworkHelper::ResourceIdToString(rxResourceId));
+
+ // 1. Get the factory.
+ const OUString sResourceURL (rxResourceId->getResourceURL());
+ Reference<XResourceFactory> xFactory (mpResourceFactoryContainer->GetFactory(sResourceURL));
+ if ( ! xFactory.is())
+ {
+ SAL_INFO("sd.fwk", __func__ << ": no factory found for " << sResourceURL);
+ return;
+ }
+
+ try
+ {
+ // 2. Create the resource.
+ Reference<XResource> xResource;
+ try
+ {
+ xResource = xFactory->createResource(rxResourceId);
+ }
+ catch (lang::DisposedException&)
+ {
+ // The factory is disposed and can be removed from the list
+ // of registered factories.
+ mpResourceFactoryContainer->RemoveFactoryForReference(xFactory);
+ }
+ catch (Exception&) {}
+
+ if (xResource.is())
+ {
+ SAL_INFO("sd.fwk", __func__ << ": successfully created");
+ // 3. Add resource to URL->Object map.
+ AddResource(xResource, xFactory);
+
+ // 4. Add resource id to current configuration.
+ rxConfiguration->addResource(rxResourceId);
+
+ // 5. Notify the new resource to listeners of the ConfigurationController.
+ mpBroadcaster->NotifyListeners(
+ FrameworkHelper::msResourceActivationEvent,
+ rxResourceId,
+ xResource);
+ }
+ else
+ {
+ SAL_INFO("sd.fwk", __func__ << ": resource creation failed");
+ }
+ }
+ catch (RuntimeException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sd");
+ }
+}
+
+/* In this method we do following steps.
+ 1. Remove the resource from the URL->Object map of the configuration
+ controller.
+ 2. Notify listeners that deactivation has started.
+ 3. Remove the resource id from the current configuration.
+ 4. Release the resource.
+ 5. Notify listeners about that deactivation is completed.
+*/
+void ConfigurationControllerResourceManager::DeactivateResource (
+ const Reference<XResourceId>& rxResourceId,
+ const Reference<XConfiguration>& rxConfiguration)
+{
+ if ( ! rxResourceId.is())
+ return;
+
+#if OSL_DEBUG_LEVEL >= 1
+ bool bSuccess (false);
+#endif
+ try
+ {
+ // 1. Remove resource from URL->Object map.
+ ResourceDescriptor aDescriptor (RemoveResource(rxResourceId));
+
+ if (aDescriptor.mxResource.is() && aDescriptor.mxResourceFactory.is())
+ {
+ // 2. Notify listeners that the resource is being deactivated.
+ mpBroadcaster->NotifyListeners(
+ FrameworkHelper::msResourceDeactivationEvent,
+ rxResourceId,
+ aDescriptor.mxResource);
+
+ // 3. Remove resource id from current configuration.
+ rxConfiguration->removeResource(rxResourceId);
+
+ // 4. Release the resource.
+ try
+ {
+ aDescriptor.mxResourceFactory->releaseResource(aDescriptor.mxResource);
+ }
+ catch (const lang::DisposedException& rException)
+ {
+ if ( ! rException.Context.is()
+ || rException.Context == aDescriptor.mxResourceFactory)
+ {
+ // The factory is disposed and can be removed from the
+ // list of registered factories.
+ mpResourceFactoryContainer->RemoveFactoryForReference(
+ aDescriptor.mxResourceFactory);
+ }
+ }
+
+#if OSL_DEBUG_LEVEL >= 1
+ bSuccess = true;
+#endif
+ }
+ }
+ catch (RuntimeException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sd");
+ }
+
+ // 5. Notify listeners that the resource is being deactivated.
+ mpBroadcaster->NotifyListeners(
+ FrameworkHelper::msResourceDeactivationEndEvent,
+ rxResourceId,
+ nullptr);
+
+#if OSL_DEBUG_LEVEL >= 1
+ if (bSuccess)
+ SAL_INFO("sd.fwk", __func__ << ": successfully deactivated " <<
+ FrameworkHelper::ResourceIdToString(rxResourceId));
+ else
+ SAL_INFO("sd.fwk", __func__ << ": activating resource " <<
+ FrameworkHelper::ResourceIdToString(rxResourceId)
+ << " failed");
+#endif
+}
+
+void ConfigurationControllerResourceManager::AddResource (
+ const Reference<XResource>& rxResource,
+ const Reference<XResourceFactory>& rxFactory)
+{
+ if ( ! rxResource.is())
+ {
+ OSL_ASSERT(rxResource.is());
+ return;
+ }
+
+ // Add the resource to the resource container.
+ ResourceDescriptor aDescriptor;
+ aDescriptor.mxResource = rxResource;
+ aDescriptor.mxResourceFactory = rxFactory;
+ maResourceMap[rxResource->getResourceId()] = aDescriptor;
+
+#if OSL_DEBUG_LEVEL >= 2
+ SAL_INFO("sd.fwk", __func__ << ": ConfigurationControllerResourceManager::AddResource(): added " <<
+ FrameworkHelper::ResourceIdToString(rxResource->getResourceId()) <<
+ " -> " << rxResource.get());
+#endif
+}
+
+ConfigurationControllerResourceManager::ResourceDescriptor
+ ConfigurationControllerResourceManager::RemoveResource (
+ const Reference<XResourceId>& rxResourceId)
+{
+ ResourceDescriptor aDescriptor;
+
+ ResourceMap::iterator iResource (maResourceMap.find(rxResourceId));
+ if (iResource != maResourceMap.end())
+ {
+#if OSL_DEBUG_LEVEL >= 2
+ SAL_INFO("sd.fwk", __func__ << ": ConfigurationControllerResourceManager::RemoveResource(): removing " <<
+ FrameworkHelper::ResourceIdToString(rxResourceId) <<
+ " -> " << &iResource);
+#endif
+
+ aDescriptor = iResource->second;
+ maResourceMap.erase(rxResourceId);
+ }
+
+ return aDescriptor;
+}
+
+//===== ConfigurationControllerResourceManager::ResourceComparator ============
+
+bool ConfigurationControllerResourceManager::ResourceComparator::operator() (
+ const Reference<XResourceId>& rxId1,
+ const Reference<XResourceId>& rxId2) const
+{
+ if (rxId1.is() && rxId2.is())
+ return rxId1->compareTo(rxId2)<0;
+ else if (rxId1.is())
+ return true;
+ else
+ return false;
+}
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.hxx b/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.hxx
new file mode 100644
index 000000000..f3a3d6d76
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.hxx
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <osl/mutex.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+namespace com::sun::star::drawing::framework { class XConfiguration; }
+namespace com::sun::star::drawing::framework { class XResourceFactory; }
+namespace com::sun::star::drawing::framework { class XResource; }
+namespace com::sun::star::drawing::framework { class XResourceId; }
+
+namespace sd::framework {
+
+class ConfigurationControllerBroadcaster;
+class ResourceFactoryManager;
+
+/** Manage the set of active resources. Activate and deactivate resources.
+*/
+class ConfigurationControllerResourceManager
+{
+public:
+ /** For every active resource both the resource itself as well as its
+ creating factory are remembered, so that on deactivation, the
+ resource can be deactivated by this factory.
+ */
+ class ResourceDescriptor
+ {
+ public:
+ css::uno::Reference<css::drawing::framework::XResource> mxResource;
+ css::uno::Reference<css::drawing::framework::XResourceFactory> mxResourceFactory;
+ };
+
+ /** A new ResourceManager object is created with the resource factory
+ container for creating resources and the event broadcaster for
+ notifying ConfigurationChangeListeners of activated or deactivated
+ resources.
+ */
+ ConfigurationControllerResourceManager (
+ const std::shared_ptr<ResourceFactoryManager>& rpResourceFactoryContainer,
+ const std::shared_ptr<ConfigurationControllerBroadcaster>& rpBroadcaster);
+
+ ~ConfigurationControllerResourceManager();
+
+ /// Forbid copy construction and copy assignment
+ ConfigurationControllerResourceManager(const ConfigurationControllerResourceManager&) = delete;
+ ConfigurationControllerResourceManager& operator=(const ConfigurationControllerResourceManager&) = delete;
+
+ /** Activate all the resources that are specified by resource ids in
+ rResources. The resource ids of activated resources are added to
+ the given configuration. Activated resources are notified to all
+ interested ConfigurationChangeListeners.
+ */
+ void ActivateResources (
+ const ::std::vector<
+ css::uno::Reference<css::drawing::framework::XResourceId> >& rResources,
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration);
+
+ /** Deactivate all the resources that are specified by resource ids in
+ rResources. The resource ids of deactivated resources are removed
+ from the given configuration. Activated resources are notified to all
+ interested ConfigurationChangeListeners.
+ */
+ void DeactivateResources (
+ const ::std::vector<
+ css::uno::Reference<css::drawing::framework::XResourceId> >& rResources,
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration);
+
+ /** Return the descriptor for the specified resource.
+ @return
+ When there is no active resource for the given resource id then
+ an empty descriptor is returned.
+ */
+ ResourceDescriptor GetResource (
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId);
+
+private:
+ osl::Mutex maMutex;
+
+ class ResourceComparator
+ {
+ public:
+ bool operator() (
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxId1,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxId2) const;
+ };
+
+ typedef ::std::map<
+ css::uno::Reference<css::drawing::framework::XResourceId>,
+ ResourceDescriptor,
+ ResourceComparator> ResourceMap;
+ ResourceMap maResourceMap;
+
+ std::shared_ptr<ResourceFactoryManager> mpResourceFactoryContainer;
+
+ /** This broadcaster is used to notify the activation and deactivation
+ of resources.
+ */
+ std::shared_ptr<ConfigurationControllerBroadcaster> mpBroadcaster;
+
+ void ActivateResource (
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId,
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration);
+
+ void DeactivateResource (
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId,
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration);
+
+ void AddResource (
+ const css::uno::Reference<css::drawing::framework::XResource>& rxResource,
+ const css::uno::Reference<css::drawing::framework::XResourceFactory>& rxFactory);
+
+ ResourceDescriptor RemoveResource (
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId);
+};
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ConfigurationTracer.cxx b/sd/source/ui/framework/configuration/ConfigurationTracer.cxx
new file mode 100644
index 000000000..00ddd5ff1
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ConfigurationTracer.cxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ConfigurationTracer.hxx"
+
+#include <com/sun/star/drawing/framework/XConfiguration.hpp>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sd::framework {
+
+void ConfigurationTracer::TraceConfiguration (
+ const Reference<XConfiguration>& rxConfiguration,
+ const char* pMessage)
+{
+#if OSL_DEBUG_LEVEL >=1
+ SAL_INFO("sd.ui","" << pMessage << " at " << rxConfiguration.get() << " {");
+ if (rxConfiguration.is())
+ {
+ TraceBoundResources(rxConfiguration, nullptr, 0);
+ }
+ else
+ {
+ SAL_INFO("sd.ui"," empty");
+ }
+ SAL_INFO("sd.ui","}");
+#else
+ (void)rxConfiguration;
+ (void)pMessage;
+#endif
+}
+
+#if OSL_DEBUG_LEVEL >=1
+void ConfigurationTracer::TraceBoundResources (
+ const Reference<XConfiguration>& rxConfiguration,
+ const Reference<XResourceId>& rxResourceId,
+ const int nIndentation)
+{
+ const Sequence<Reference<XResourceId> > aResourceList (
+ rxConfiguration->getResources(rxResourceId, OUString(), AnchorBindingMode_DIRECT));
+ static const OUStringLiteral sIndentation (u" ");
+ for (Reference<XResourceId> const & resourceId : aResourceList)
+ {
+ OUString sLine (resourceId->getResourceURL());
+ for (int i=0; i<nIndentation; ++i)
+ sLine = sIndentation + sLine;
+ SAL_INFO("sd.ui", "" << sLine);
+ TraceBoundResources(rxConfiguration, resourceId, nIndentation+1);
+ }
+}
+#endif
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ConfigurationTracer.hxx b/sd/source/ui/framework/configuration/ConfigurationTracer.hxx
new file mode 100644
index 000000000..337fae569
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ConfigurationTracer.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+namespace com::sun::star::drawing::framework
+{
+class XConfiguration;
+}
+namespace com::sun::star::drawing::framework
+{
+class XResourceId;
+}
+namespace com::sun::star::uno
+{
+template <typename> class Reference;
+}
+
+namespace sd::framework
+{
+/** Print debug information about configurations to the standard error
+ output channel.
+*/
+class ConfigurationTracer
+{
+public:
+ static void TraceConfiguration(
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration,
+ const char* pMessage);
+#if OSL_DEBUG_LEVEL >= 1
+ static void TraceBoundResources(
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId,
+ const int nIndentation);
+#endif
+};
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx b/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx
new file mode 100644
index 000000000..96ac74186
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx
@@ -0,0 +1,376 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ConfigurationUpdater.hxx"
+#include "ConfigurationTracer.hxx"
+#include "ConfigurationClassifier.hxx"
+#include "ConfigurationControllerBroadcaster.hxx"
+#include "ConfigurationControllerResourceManager.hxx"
+#include <framework/Configuration.hxx>
+#include <framework/FrameworkHelper.hxx>
+
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+#include <comphelper/scopeguard.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+using ::sd::framework::FrameworkHelper;
+using ::std::vector;
+
+namespace {
+const sal_Int32 snShortTimeout (100);
+const sal_Int32 snNormalTimeout (1000);
+const sal_Int32 snLongTimeout (10000);
+const sal_Int32 snShortTimeoutCountThreshold (1);
+const sal_Int32 snNormalTimeoutCountThreshold (5);
+}
+
+namespace sd::framework {
+
+//===== ConfigurationUpdaterLock ==============================================
+
+class ConfigurationUpdaterLock
+{
+public:
+ explicit ConfigurationUpdaterLock (ConfigurationUpdater& rUpdater)
+ : mrUpdater(rUpdater) { mrUpdater.LockUpdates(); }
+ ~ConfigurationUpdaterLock() { mrUpdater.UnlockUpdates(); }
+private:
+ ConfigurationUpdater& mrUpdater;
+};
+
+//===== ConfigurationUpdater ==================================================
+
+ConfigurationUpdater::ConfigurationUpdater (
+ const std::shared_ptr<ConfigurationControllerBroadcaster>& rpBroadcaster,
+ const std::shared_ptr<ConfigurationControllerResourceManager>& rpResourceManager,
+ const Reference<XControllerManager>& rxControllerManager)
+ : mpBroadcaster(rpBroadcaster),
+ mxCurrentConfiguration(Reference<XConfiguration>(new Configuration(nullptr, false))),
+ mbUpdatePending(false),
+ mbUpdateBeingProcessed(false),
+ mnLockCount(0),
+ maUpdateTimer("sd::ConfigurationUpdater 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.SetInvokeHandler(LINK(this,ConfigurationUpdater,TimeoutHandler));
+ mxControllerManager = rxControllerManager;
+}
+
+ConfigurationUpdater::~ConfigurationUpdater()
+{
+ maUpdateTimer.Stop();
+}
+
+void ConfigurationUpdater::RequestUpdate (
+ const Reference<XConfiguration>& rxRequestedConfiguration)
+{
+ mxRequestedConfiguration = rxRequestedConfiguration;
+
+ // Find out whether we really can update the configuration.
+ if (IsUpdatePossible())
+ {
+ SAL_INFO("sd.fwk", __func__ << ": UpdateConfiguration start");
+
+ // Call UpdateConfiguration while that is possible and while someone
+ // set mbUpdatePending to true in the middle of it.
+ do
+ {
+ UpdateConfiguration();
+ }
+ while (mbUpdatePending && IsUpdatePossible());
+ }
+ else
+ {
+ mbUpdatePending = true;
+ SAL_INFO("sd.fwk", __func__ << ": scheduling update for later");
+ }
+}
+
+bool ConfigurationUpdater::IsUpdatePossible() const
+{
+ return ! mbUpdateBeingProcessed
+ && mxControllerManager.is()
+ && mnLockCount==0
+ && mxRequestedConfiguration.is()
+ && mxCurrentConfiguration.is();
+}
+
+void ConfigurationUpdater::UpdateConfiguration()
+{
+ SAL_INFO("sd.fwk", __func__ << ": UpdateConfiguration update");
+ SetUpdateBeingProcessed(true);
+ comphelper::ScopeGuard aScopeGuard (
+ [this] () { return this->SetUpdateBeingProcessed(false); });
+
+ try
+ {
+ mbUpdatePending = false;
+
+ CleanRequestedConfiguration();
+ ConfigurationClassifier aClassifier(mxRequestedConfiguration, mxCurrentConfiguration);
+ if (aClassifier.Partition())
+ {
+#if DEBUG_SD_CONFIGURATION_TRACE
+ SAL_INFO("sd.fwk", __func__ << ": ConfigurationUpdater::UpdateConfiguration(");
+ ConfigurationTracer::TraceConfiguration(
+ mxRequestedConfiguration, "requested configuration");
+ ConfigurationTracer::TraceConfiguration(
+ mxCurrentConfiguration, "current configuration");
+#endif
+ // Notify the beginning 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 end of the update is notified always.
+ try
+ {
+ if (mnLockCount == 0)
+ UpdateCore(aClassifier);
+ }
+ catch(const RuntimeException&)
+ {
+ }
+
+ // Notify the end of the update.
+ aEvent.Type = FrameworkHelper::msConfigurationUpdateEndEvent;
+ mpBroadcaster->NotifyListeners(aEvent);
+
+ CheckUpdateSuccess();
+ }
+ else
+ {
+ SAL_INFO("sd.fwk", __func__ << ": nothing to do");
+#if DEBUG_SD_CONFIGURATION_TRACE
+ ConfigurationTracer::TraceConfiguration(
+ mxRequestedConfiguration, "requested configuration");
+ ConfigurationTracer::TraceConfiguration(
+ mxCurrentConfiguration, "current configuration");
+#endif
+ }
+ }
+ catch(const RuntimeException &)
+ {
+ DBG_UNHANDLED_EXCEPTION("sd");
+ }
+
+ SAL_INFO("sd.fwk", __func__ << ": ConfigurationUpdater::UpdateConfiguration)");
+ SAL_INFO("sd.fwk", __func__ << ": UpdateConfiguration end");
+}
+
+void ConfigurationUpdater::CleanRequestedConfiguration()
+{
+ if (!mxControllerManager.is())
+ return;
+
+ // Request the deactivation of pure anchors that have no child.
+ vector<Reference<XResourceId> > aResourcesToDeactivate;
+ CheckPureAnchors(mxRequestedConfiguration, aResourcesToDeactivate);
+ if (!aResourcesToDeactivate.empty())
+ {
+ Reference<XConfigurationController> xCC (
+ mxControllerManager->getConfigurationController());
+ for (const auto& rxId : aResourcesToDeactivate)
+ if (rxId.is())
+ xCC->requestResourceDeactivation(rxId);
+ }
+}
+
+void ConfigurationUpdater::CheckUpdateSuccess()
+{
+ // 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 successful. Reset the failed update count.
+ mnFailedUpdateCount = 0;
+ }
+}
+
+void ConfigurationUpdater::UpdateCore (const ConfigurationClassifier& rClassifier)
+{
+ try
+ {
+#if DEBUG_SD_CONFIGURATION_TRACE
+ rClassifier.TraceResourceIdVector(
+ "requested but not current resources:", rClassifier.GetC1minusC2());
+ rClassifier.TraceResourceIdVector(
+ "current but not requested resources:", rClassifier.GetC2minusC1());
+ rClassifier.TraceResourceIdVector(
+ "requested and current resources:", 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 DEBUG_SD_CONFIGURATION_TRACE
+ SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::UpdateConfiguration)");
+ ConfigurationTracer::TraceConfiguration(
+ mxRequestedConfiguration, "requested configuration");
+ ConfigurationTracer::TraceConfiguration(
+ mxCurrentConfiguration, "current configuration");
+#endif
+
+ // Deactivate pure anchors that have no child.
+ vector<Reference<XResourceId> > aResourcesToDeactivate;
+ CheckPureAnchors(mxCurrentConfiguration, aResourcesToDeactivate);
+ if (!aResourcesToDeactivate.empty())
+ mpResourceManager->DeactivateResources(aResourcesToDeactivate, mxCurrentConfiguration);
+ }
+ catch(const RuntimeException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sd");
+ }
+}
+
+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(
+ nullptr, OUString(), AnchorBindingMode_INDIRECT));
+ auto aResourcesRange = asNonConstRange(aResources);
+ 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 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)
+ {
+ SAL_INFO("sd.fwk", __func__ << ": deactivating pure anchor " <<
+ FrameworkHelper::ResourceIdToString(xResourceId) <<
+ "because it has no children");
+ // Erase element from current configuration.
+ for (sal_Int32 nI=nIndex; nI<nCount-2; ++nI)
+ aResourcesRange[nI] = aResources[nI+1];
+ nCount -= 1;
+
+ rResourcesToDeactivate.push_back(xResourceId);
+ }
+ nIndex -= 1;
+ }
+}
+
+void ConfigurationUpdater::LockUpdates()
+{
+ ++mnLockCount;
+}
+
+void ConfigurationUpdater::UnlockUpdates()
+{
+ --mnLockCount;
+ if (mnLockCount == 0 && mbUpdatePending)
+ {
+ RequestUpdate(mxRequestedConfiguration);
+ }
+}
+
+std::shared_ptr<ConfigurationUpdaterLock> ConfigurationUpdater::GetLock()
+{
+ return std::make_shared<ConfigurationUpdaterLock>(*this);
+}
+
+void ConfigurationUpdater::SetUpdateBeingProcessed (bool bValue)
+{
+ mbUpdateBeingProcessed = bValue;
+}
+
+IMPL_LINK_NOARG(ConfigurationUpdater, TimeoutHandler, Timer *, void)
+{
+ if ( ! mbUpdateBeingProcessed
+ && mxCurrentConfiguration.is()
+ && mxRequestedConfiguration.is())
+ {
+ if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration))
+ {
+ RequestUpdate(mxRequestedConfiguration);
+ }
+ }
+}
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ConfigurationUpdater.hxx b/sd/source/ui/framework/configuration/ConfigurationUpdater.hxx
new file mode 100644
index 000000000..9fba364b1
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ConfigurationUpdater.hxx
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <vcl/timer.hxx>
+#include <memory>
+#include <vector>
+
+namespace com::sun::star::drawing::framework
+{
+class XConfiguration;
+}
+namespace com::sun::star::drawing::framework
+{
+class XControllerManager;
+}
+namespace com::sun::star::drawing::framework
+{
+class XResourceId;
+}
+
+namespace sd::framework
+{
+class ConfigurationClassifier;
+class ConfigurationUpdaterLock;
+class ConfigurationControllerResourceManager;
+class ConfigurationControllerBroadcaster;
+
+/** This is a helper class for the ConfigurationController. It handles the
+ update of the current configuration so that it looks like a requested
+ configuration. An update is made by activating or deactivating drawing
+ framework resources.
+
+ When an update is not successful, i.e. after the update the current
+ configuration is not equivalent to the requested configuration, then a
+ timer is started to repeat the update after a short time.
+*/
+class ConfigurationUpdater
+{
+public:
+ /** Create a new ConfigurationUpdater object that notifies configuration
+ changes and the start and end of updates via the given broadcaster.
+ */
+ ConfigurationUpdater(
+ const std::shared_ptr<ConfigurationControllerBroadcaster>& rpBroadcaster,
+ const std::shared_ptr<ConfigurationControllerResourceManager>& rpResourceManager,
+ const css::uno::Reference<css::drawing::framework::XControllerManager>&
+ rxControllerManager);
+ ~ConfigurationUpdater();
+
+ /** Request an update of the current configuration so that it looks like
+ the given requested configuration. It checks whether an update of
+ the current configuration can be done. Calls UpdateConfiguration()
+ if that is the case. Otherwise it schedules a later call to
+ UpdateConfiguration().
+ */
+ void RequestUpdate(const css::uno::Reference<css::drawing::framework::XConfiguration>&
+ rxRequestedConfiguration);
+
+ const css::uno::Reference<css::drawing::framework::XConfiguration>&
+ GetCurrentConfiguration() const
+ {
+ return mxCurrentConfiguration;
+ }
+
+ friend class ConfigurationUpdaterLock;
+ /** Return a lock of the called ConfigurationUpdater. While the
+ returned object exists no update of the current configuration is
+ made.
+ */
+ std::shared_ptr<ConfigurationUpdaterLock> GetLock();
+
+private:
+ /** A reference to the XControllerManager is kept so that
+ UpdateConfiguration() has access to the other sub controllers.
+ */
+ css::uno::Reference<css::drawing::framework::XControllerManager> mxControllerManager;
+
+ std::shared_ptr<ConfigurationControllerBroadcaster> mpBroadcaster;
+
+ /** The current configuration holds the resources that are currently
+ active. It is modified during an update.
+ */
+ css::uno::Reference<css::drawing::framework::XConfiguration> mxCurrentConfiguration;
+
+ /** The requested configuration holds the resources that have been
+ requested to activate or to deactivate since the last update. It is
+ (usually) not modified during an update. This configuration is
+ maintained by the ConfigurationController and given to the
+ ConfigurationUpdater in the RequestUpdate() method.
+ */
+ css::uno::Reference<css::drawing::framework::XConfiguration> mxRequestedConfiguration;
+
+ /** This flag is set to </sal_True> when an update of the current
+ configuration was requested (because the last request in the queue
+ was processed) but could not be executed because the
+ ConfigurationController was locked. A call to UpdateConfiguration()
+ resets the flag to </sal_False>.
+ */
+ bool mbUpdatePending;
+
+ /** This flag is set to </sal_True> while the UpdateConfiguration() method
+ is running. It is used to prevent reentrance problems with this
+ method.
+ */
+ bool mbUpdateBeingProcessed;
+
+ /** The ConfigurationController is locked when this count has a value
+ larger then zero. If the controller is locked then updates of the
+ current configuration are not made.
+ */
+ sal_Int32 mnLockCount;
+
+ /** This timer is used to check from time to time whether the requested
+ configuration and the current configuration are identical and request
+ an update when they are not.
+ This is used to overcome problems with resources that become
+ available asynchronously.
+ */
+ Timer maUpdateTimer;
+
+ /** The number of failed updates (those after which the current
+ configuration is not equivalent to the requested configuration) is
+ used to determine how long to wait before another update is made.
+ */
+ sal_Int32 mnFailedUpdateCount;
+
+ std::shared_ptr<ConfigurationControllerResourceManager> mpResourceManager;
+
+ /** This method does the main work of an update. It calls the sub
+ controllers that are responsible for the various types of resources
+ and tells them to update their active resources. It notifies
+ listeners about the start and end of the configuration update.
+ */
+ void UpdateConfiguration();
+
+ /** Basically calls UpdaterStart() andUpdateEnd() and makes some debug
+ output.
+ */
+ void UpdateCore(const ConfigurationClassifier& rClassifier);
+
+ /** Check for all pure anchors if they have at least one child.
+ Childless pure anchors are deactivated.
+ This affects only the current configuration.
+ */
+ void CheckPureAnchors(
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration,
+ ::std::vector<css::uno::Reference<css::drawing::framework::XResourceId>>&
+ rResourcesToDeactivate);
+
+ /** Remove from the requested configuration all pure anchors that have no
+ child. Requested but not yet activated anchors can not be removed
+ because without the actual resource the 'pureness' of an anchor can
+ not be determined.
+ */
+ void CleanRequestedConfiguration();
+
+ /** Check the success of a recently executed configuration update.
+ When the update failed then start the timer.
+ */
+ void CheckUpdateSuccess();
+
+ /** This method sets the mbUpdateBeingProcessed member that is used to
+ prevent reentrance problems. This method allows function objects
+ easily and safely to modify the variable.
+ */
+ void SetUpdateBeingProcessed(bool bValue);
+
+ /** Return whether it is possible to do an update of the configuration.
+ This takes into account whether another update is currently being
+ executed, the lock count, and whether the configuration controller
+ is still valid.
+ */
+ bool IsUpdatePossible() const;
+
+ /** Lock updates of the current configuration. For intermediate requests
+ for updates mbUpdatePending is set to <TRUE/>.
+ */
+ void LockUpdates();
+
+ /** When an update was requested since the last LockUpdates() call then
+ RequestUpdate() is called.
+ */
+ void UnlockUpdates();
+
+ DECL_LINK(TimeoutHandler, Timer*, void);
+};
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.cxx b/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.cxx
new file mode 100644
index 000000000..fa6d41503
--- /dev/null
+++ b/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.cxx
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "GenericConfigurationChangeRequest.hxx"
+
+#include <framework/FrameworkHelper.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sd::framework {
+
+GenericConfigurationChangeRequest::GenericConfigurationChangeRequest (
+ const Reference<XResourceId>& rxResourceId,
+ const Mode eMode)
+ : mxResourceId(rxResourceId),
+ meMode(eMode)
+{
+ if ( ! rxResourceId.is() || rxResourceId->getResourceURL().isEmpty())
+ throw css::lang::IllegalArgumentException();
+}
+
+GenericConfigurationChangeRequest::~GenericConfigurationChangeRequest() noexcept
+{
+}
+
+void SAL_CALL GenericConfigurationChangeRequest::execute (
+ const Reference<XConfiguration>& rxConfiguration)
+{
+ if (!rxConfiguration.is())
+ return;
+
+ switch (meMode)
+ {
+ case Activation:
+ rxConfiguration->addResource(mxResourceId);
+ break;
+
+ case Deactivation:
+ rxConfiguration->removeResource(mxResourceId);
+ break;
+ }
+}
+
+OUString SAL_CALL GenericConfigurationChangeRequest::getName()
+{
+ return OUString::Concat("GenericConfigurationChangeRequest ")
+ + (meMode==Activation
+ ? std::u16string_view(u"activate ") : std::u16string_view(u"deactivate "))
+ + FrameworkHelper::ResourceIdToString(mxResourceId);
+}
+
+void SAL_CALL GenericConfigurationChangeRequest::setName (const OUString&)
+{
+ // Ignored.
+}
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.hxx b/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.hxx
new file mode 100644
index 000000000..3caa7a8ca
--- /dev/null
+++ b/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.hxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/drawing/framework/XConfigurationChangeRequest.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <comphelper/compbase.hxx>
+
+namespace com::sun::star::drawing::framework { class XConfiguration; }
+namespace com::sun::star::drawing::framework { class XResourceId; }
+
+namespace sd::framework {
+
+typedef comphelper::WeakComponentImplHelper <
+ css::drawing::framework::XConfigurationChangeRequest,
+ css::container::XNamed
+ > GenericConfigurationChangeRequestInterfaceBase;
+
+/** This implementation of the XConfigurationChangeRequest interface
+ represents a single explicit request for a configuration change. On its
+ execution it may result in other, implicit, configuration changes. For
+ example this is the case when the deactivation of a unique resource is
+ requested: the resources linked to it have to be deactivated as well.
+*/
+class GenericConfigurationChangeRequest final
+ : public GenericConfigurationChangeRequestInterfaceBase
+{
+public:
+ /** This enum specified whether the activation or deactivation of a
+ resource is requested.
+ */
+ enum Mode { Activation, Deactivation };
+
+ /** Create a new object that represents the request for activation or
+ deactivation of the specified resource.
+ @param rxsResourceId
+ Id of the resource that is to be activated or deactivated.
+ @param eMode
+ The mode specifies whether to activate or to deactivate the
+ resource.
+ @throws css::css::lang::IllegalArgumentException
+ */
+ GenericConfigurationChangeRequest (
+ const css::uno::Reference<css::drawing::framework::XResourceId>&
+ rxResourceId,
+ const Mode eMode);
+
+ virtual ~GenericConfigurationChangeRequest() noexcept override;
+
+ // XConfigurationChangeOperation
+
+ /** The requested configuration change is executed on the given
+ configuration. Additionally to the explicitly requested change
+ other changes have to be made as well. See class description for an
+ example.
+ @param rxConfiguration
+ The configuration to which the requested change is made.
+ */
+ virtual void SAL_CALL execute (
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration) override;
+
+ // XNamed
+
+ /** Return a human readable string representation. This is used for
+ debugging purposes.
+ */
+ virtual OUString SAL_CALL getName() override;
+
+ /** This call is ignored because the XNamed interface is (mis)used to
+ give access to a human readable name for debugging purposes.
+ */
+ virtual void SAL_CALL setName (const OUString& rName) override;
+
+private:
+ const css::uno::Reference<css::drawing::framework::XResourceId> mxResourceId;
+ const Mode meMode;
+};
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ResourceFactoryManager.cxx b/sd/source/ui/framework/configuration/ResourceFactoryManager.cxx
new file mode 100644
index 000000000..4817c1360
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ResourceFactoryManager.cxx
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ResourceFactoryManager.hxx"
+#include <tools/wldcrd.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+#include <comphelper/processfactory.hxx>
+#include <sal/log.hxx>
+
+#include <algorithm>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+#undef VERBOSE
+//#define VERBOSE 1
+
+namespace sd::framework {
+
+ResourceFactoryManager::ResourceFactoryManager (const Reference<XControllerManager>& rxManager)
+ : mxControllerManager(rxManager)
+{
+ // Create the URL transformer.
+ Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
+ mxURLTransformer = util::URLTransformer::create(xContext);
+}
+
+ResourceFactoryManager::~ResourceFactoryManager()
+{
+ for (auto& rXInterfaceResource : maFactoryMap)
+ {
+ Reference<lang::XComponent> xComponent (rXInterfaceResource.second, UNO_QUERY);
+ rXInterfaceResource.second = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ Reference<lang::XComponent> xComponent (mxURLTransformer, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+}
+
+void ResourceFactoryManager::AddFactory (
+ const OUString& rsURL,
+ const Reference<XResourceFactory>& rxFactory)
+{
+ if ( ! rxFactory.is())
+ throw lang::IllegalArgumentException();
+ if (rsURL.isEmpty())
+ throw lang::IllegalArgumentException();
+
+ std::scoped_lock aGuard (maMutex);
+
+ if (rsURL.indexOf('*') >= 0 || rsURL.indexOf('?') >= 0)
+ {
+ // The URL is a URL pattern not a single URL.
+ maFactoryPatternList.emplace_back(rsURL, rxFactory);
+
+#if defined VERBOSE && VERBOSE>=1
+ SAL_INFO("sd","ResourceFactoryManager::AddFactory pattern " << rsURL << std::hex << rxFactory.get());
+#endif
+ }
+ else
+ {
+ maFactoryMap[rsURL] = rxFactory;
+
+#if defined VERBOSE && VERBOSE>=1
+ SAL_INFO("sd", "ResourceFactoryManager::AddFactory fixed " << rsURL << " 0x" << std::hex << rxFactory.get());
+#endif
+ }
+}
+
+void ResourceFactoryManager::RemoveFactoryForURL (
+ const OUString& rsURL)
+{
+ if (rsURL.isEmpty())
+ throw lang::IllegalArgumentException();
+
+ std::scoped_lock aGuard (maMutex);
+
+ FactoryMap::iterator iFactory (maFactoryMap.find(rsURL));
+ if (iFactory != maFactoryMap.end())
+ {
+ maFactoryMap.erase(iFactory);
+ }
+ else
+ {
+ // The URL may be a pattern. Look that up.
+ auto iPattern = std::find_if(maFactoryPatternList.begin(), maFactoryPatternList.end(),
+ [&rsURL](const FactoryPatternList::value_type& rPattern) { return rPattern.first == rsURL; });
+ if (iPattern != maFactoryPatternList.end())
+ {
+ // Found the pattern. Remove it.
+ maFactoryPatternList.erase(iPattern);
+ }
+ }
+}
+
+void ResourceFactoryManager::RemoveFactoryForReference(
+ const Reference<XResourceFactory>& rxFactory)
+{
+ std::scoped_lock aGuard (maMutex);
+
+ // Collect a list with all keys that map to the given factory.
+ ::std::vector<OUString> aKeys;
+ for (const auto& rFactory : maFactoryMap)
+ if (rFactory.second == rxFactory)
+ aKeys.push_back(rFactory.first);
+
+ // Remove the entries whose keys we just have collected.
+ for (const auto& rKey : aKeys)
+ maFactoryMap.erase(rKey);
+
+ // Remove the pattern entries whose factories are identical to the given
+ // factory.
+ maFactoryPatternList.erase(
+ std::remove_if(
+ maFactoryPatternList.begin(),
+ maFactoryPatternList.end(),
+ [&] (FactoryPatternList::value_type const& it) { return it.second == rxFactory; }),
+ maFactoryPatternList.end());
+}
+
+Reference<XResourceFactory> ResourceFactoryManager::GetFactory (
+ const OUString& rsCompleteURL)
+{
+ OUString sURLBase (rsCompleteURL);
+ if (mxURLTransformer.is())
+ {
+ util::URL aURL;
+ aURL.Complete = rsCompleteURL;
+ if (mxURLTransformer->parseStrict(aURL))
+ sURLBase = aURL.Main;
+ }
+
+ Reference<XResourceFactory> xFactory = FindFactory(sURLBase);
+
+ if ( ! xFactory.is() && mxControllerManager.is())
+ {
+ Reference<XModuleController> xModuleController(mxControllerManager->getModuleController());
+ if (xModuleController.is())
+ {
+ // Ask the module controller to provide a factory of the
+ // requested view type. Note that this can (and should) cause
+ // intermediate calls to AddFactory().
+ xModuleController->requestResource(sURLBase);
+
+ xFactory = FindFactory(sURLBase);
+ }
+ }
+
+ return xFactory;
+}
+
+Reference<XResourceFactory> ResourceFactoryManager::FindFactory (const OUString& rsURLBase)
+{
+ std::scoped_lock aGuard (maMutex);
+ FactoryMap::const_iterator iFactory (maFactoryMap.find(rsURLBase));
+ if (iFactory != maFactoryMap.end())
+ return iFactory->second;
+ else
+ {
+ // Check the URL patterns.
+ auto iPattern = std::find_if(maFactoryPatternList.begin(), maFactoryPatternList.end(),
+ [&rsURLBase](const FactoryPatternList::value_type& rPattern) {
+ WildCard aWildCard (rPattern.first);
+ return aWildCard.Matches(rsURLBase);
+ });
+ if (iPattern != maFactoryPatternList.end())
+ return iPattern->second;
+ }
+ return nullptr;
+}
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ResourceFactoryManager.hxx b/sd/source/ui/framework/configuration/ResourceFactoryManager.hxx
new file mode 100644
index 000000000..61daf383b
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ResourceFactoryManager.hxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <mutex>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ustring.hxx>
+
+namespace com::sun::star::drawing::framework { class XControllerManager; }
+namespace com::sun::star::drawing::framework { class XResourceFactory; }
+namespace com::sun::star::util { class XURLTransformer; }
+
+namespace sd::framework {
+
+/** Container of resource factories of the drawing framework.
+*/
+class ResourceFactoryManager
+{
+public:
+ explicit ResourceFactoryManager (
+ const css::uno::Reference<css::drawing::framework::XControllerManager>& rxManager);
+
+ ~ResourceFactoryManager();
+
+ /** Register a resource factory for one type of resource.
+ @param rsURL
+ The type of the resource that will be created by the factory.
+ @param rxFactory
+ The factory that will create resource objects of the specified type.
+ @throws css::uno::RuntimeException
+ */
+ void AddFactory (
+ const OUString& rsURL,
+ const css::uno::Reference<css::drawing::framework::XResourceFactory>& rxFactory);
+
+ /** Unregister the specified factory.
+ @param rsURL
+ Unregister only the factory for this URL. When the same factory
+ is registered for other URLs then these remain registered.
+ @throws css::uno::RuntimeException
+ */
+ void RemoveFactoryForURL(
+ const OUString& rsURL);
+
+ /** Unregister the specified factory.
+ @param rxFactory
+ Unregister the this factory for all URLs that it has been
+ registered for.
+ @throws css::uno::RuntimeException
+ */
+ void RemoveFactoryForReference(
+ const css::uno::Reference<css::drawing::framework::XResourceFactory>& rxFactory);
+
+ /** Return a factory that can create resources specified by the given URL.
+ @param rsCompleteURL
+ This URL specifies the type of the resource. It may contain arguments.
+ @return
+ When a factory for the specified URL has been registered by a
+ previous call to AddFactory() then a reference to that factory
+ is returned. Otherwise an empty reference is returned.
+ @throws css::uno::RuntimeException
+ */
+ css::uno::Reference<css::drawing::framework::XResourceFactory> GetFactory (
+ const OUString& rsURL);
+
+private:
+ std::mutex maMutex;
+ typedef std::unordered_map<
+ OUString,
+ css::uno::Reference<css::drawing::framework::XResourceFactory> > FactoryMap;
+ FactoryMap maFactoryMap;
+
+ typedef ::std::vector<
+ ::std::pair<
+ OUString,
+ css::uno::Reference<css::drawing::framework::XResourceFactory> > >
+ FactoryPatternList;
+ FactoryPatternList maFactoryPatternList;
+
+ css::uno::Reference<css::drawing::framework::XControllerManager> mxControllerManager;
+ css::uno::Reference<css::util::XURLTransformer> mxURLTransformer;
+
+ /** Look up the factory for the given URL.
+ @param rsURLBase
+ The css::tools::URL.Main part of a URL. Arguments have to be
+ stripped off by the caller.
+ @return
+ When the factory has not yet been added then return NULL.
+ @throws css::uno::RuntimeException
+ */
+ css::uno::Reference<css::drawing::framework::XResourceFactory> FindFactory (
+ const OUString& rsURLBase);
+};
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/ResourceId.cxx b/sd/source/ui/framework/configuration/ResourceId.cxx
new file mode 100644
index 000000000..1845b353f
--- /dev/null
+++ b/sd/source/ui/framework/configuration/ResourceId.cxx
@@ -0,0 +1,503 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/ResourceId.hxx>
+#include <tools/SdGlobalResourceContainer.hxx>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <rtl/ref.hxx>
+
+#include <algorithm>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::drawing::framework;
+
+/** When the USE_OPTIMIZATIONS symbol is defined then at some optimizations
+ are activated that work only together with XResourceId objects that are
+ implemented by the ResourceId class. For other implementations of when
+ the USE_OPTIMIZATIONS symbol is not defined then alternative code is
+ used instead.
+*/
+#define USE_OPTIMIZATIONS
+
+namespace sd::framework {
+
+//===== ResourceId ============================================================
+
+WeakReference<util::XURLTransformer> ResourceId::mxURLTransformerWeak;
+
+ResourceId::ResourceId()
+ : maResourceURLs(0)
+{
+}
+
+ResourceId::ResourceId (
+ std::vector<OUString>&& rResourceURLs)
+ : maResourceURLs(std::move(rResourceURLs))
+{
+ ParseResourceURL();
+}
+
+ResourceId::ResourceId (
+ const OUString& rsResourceURL)
+ : maResourceURLs(1, rsResourceURL)
+{
+ // Handle the special case of an empty resource URL.
+ if (rsResourceURL.isEmpty())
+ maResourceURLs.clear();
+ ParseResourceURL();
+}
+
+ResourceId::ResourceId (
+ const OUString& rsResourceURL,
+ const OUString& rsAnchorURL)
+ : maResourceURLs(2)
+{
+ maResourceURLs[0] = rsResourceURL;
+ maResourceURLs[1] = rsAnchorURL;
+ ParseResourceURL();
+}
+
+ResourceId::ResourceId (
+ const OUString& rsResourceURL,
+ const OUString& rsFirstAnchorURL,
+ const Sequence<OUString>& rAnchorURLs)
+ : maResourceURLs(2+rAnchorURLs.getLength())
+{
+ maResourceURLs[0] = rsResourceURL;
+ maResourceURLs[1] = rsFirstAnchorURL;
+ std::copy(rAnchorURLs.begin(), rAnchorURLs.end(), std::next(maResourceURLs.begin(), 2));
+ ParseResourceURL();
+}
+
+ResourceId::~ResourceId()
+{
+ mpURL.reset();
+}
+
+OUString SAL_CALL
+ ResourceId::getResourceURL()
+{
+ if (!maResourceURLs.empty())
+ return maResourceURLs[0];
+ else
+ return OUString();
+}
+
+util::URL SAL_CALL
+ ResourceId::getFullResourceURL()
+{
+ if (mpURL != nullptr)
+ return *mpURL;
+
+ Reference<util::XURLTransformer> xURLTransformer (mxURLTransformerWeak);
+ if (xURLTransformer.is() && !maResourceURLs.empty() )
+ {
+ mpURL.reset(new util::URL);
+ mpURL->Complete = maResourceURLs[0];
+ xURLTransformer->parseStrict(*mpURL);
+ return *mpURL;
+ }
+
+ util::URL aURL;
+ if (!maResourceURLs.empty())
+ aURL.Complete = maResourceURLs[0];
+ return aURL;
+}
+
+sal_Bool SAL_CALL
+ ResourceId::hasAnchor()
+{
+ return maResourceURLs.size()>1;
+}
+
+Reference<XResourceId> SAL_CALL
+ ResourceId::getAnchor()
+{
+ ::rtl::Reference<ResourceId> rResourceId (new ResourceId());
+ const sal_Int32 nAnchorCount (maResourceURLs.size()-1);
+ if (nAnchorCount > 0)
+ {
+ rResourceId->maResourceURLs.resize(nAnchorCount);
+ for (sal_Int32 nIndex=0; nIndex<nAnchorCount; ++nIndex)
+ rResourceId->maResourceURLs[nIndex] = maResourceURLs[nIndex+1];
+ }
+ return rResourceId;
+}
+
+Sequence<OUString> SAL_CALL
+ ResourceId::getAnchorURLs()
+{
+ const sal_Int32 nAnchorCount (maResourceURLs.size() - 1);
+ if (nAnchorCount > 0)
+ {
+ Sequence<OUString> aAnchorURLs (nAnchorCount);
+ std::copy_n(maResourceURLs.begin() + 1, nAnchorCount, aAnchorURLs.getArray());
+ return aAnchorURLs;
+ }
+ else
+ return Sequence<OUString>();
+}
+
+OUString SAL_CALL
+ ResourceId::getResourceTypePrefix()
+{
+ if (!maResourceURLs.empty() )
+ {
+ // Return the "private:resource/<type>/" prefix.
+
+ // Get the prefix that ends with the second "/".
+ const OUString& rsResourceURL (maResourceURLs[0]);
+ sal_Int32 nPrefixEnd (rsResourceURL.indexOf('/'));
+ if (nPrefixEnd >= 0)
+ nPrefixEnd = rsResourceURL.indexOf('/', nPrefixEnd+1) + 1;
+ else
+ nPrefixEnd = 0;
+
+ return rsResourceURL.copy(0,nPrefixEnd);
+ }
+ else
+ return OUString();
+}
+
+sal_Int16 SAL_CALL
+ ResourceId::compareTo (const Reference<XResourceId>& rxResourceId)
+{
+ sal_Int16 nResult (0);
+
+ if ( ! rxResourceId.is())
+ {
+ // The empty reference is interpreted as empty resource id object.
+ if (!maResourceURLs.empty())
+ nResult = +1;
+ else
+ nResult = 0;
+ }
+ else
+ {
+ ResourceId* pId = nullptr;
+#ifdef USE_OPTIMIZATIONS
+ pId = dynamic_cast<ResourceId*>(rxResourceId.get());
+#endif
+ if (pId != nullptr)
+ {
+ // We have direct access to the implementation of the given
+ // resource id object.
+ nResult = CompareToLocalImplementation(*pId);
+ }
+ else
+ {
+ // We have to do the comparison via the UNO interface of the
+ // given resource id object.
+ nResult = CompareToExternalImplementation(rxResourceId);
+ }
+ }
+
+ return nResult;
+}
+
+sal_Int16 ResourceId::CompareToLocalImplementation (const ResourceId& rId) const
+{
+ sal_Int16 nResult (0);
+
+ const sal_uInt32 nLocalURLCount (maResourceURLs.size());
+ const sal_uInt32 nURLCount(rId.maResourceURLs.size());
+
+ // Start comparison with the top most anchors.
+ for (sal_Int32 nIndex=nURLCount-1,nLocalIndex=nLocalURLCount-1;
+ nIndex>=0 && nLocalIndex>=0;
+ --nIndex,--nLocalIndex)
+ {
+ const OUString sLocalURL (maResourceURLs[nLocalIndex]);
+ const OUString sURL (rId.maResourceURLs[nIndex]);
+ const sal_Int32 nLocalResult (sURL.compareTo(sLocalURL));
+ if (nLocalResult != 0)
+ {
+ if (nLocalResult < 0)
+ nResult = -1;
+ else
+ nResult = +1;
+ break;
+ }
+ }
+
+ if (nResult == 0)
+ {
+ // No difference found yet. When the lengths are the same then the
+ // two resource ids are equivalent. Otherwise the shorter comes
+ // first.
+ if (nLocalURLCount != nURLCount)
+ {
+ if (nLocalURLCount < nURLCount)
+ nResult = -1;
+ else
+ nResult = +1;
+ }
+ }
+
+ return nResult;
+}
+
+sal_Int16 ResourceId::CompareToExternalImplementation (const Reference<XResourceId>& rxId) const
+{
+ sal_Int16 nResult (0);
+
+ const Sequence<OUString> aAnchorURLs (rxId->getAnchorURLs());
+ const sal_uInt32 nLocalURLCount (maResourceURLs.size());
+ const sal_uInt32 nURLCount(1+aAnchorURLs.getLength());
+
+ // Start comparison with the top most anchors.
+ sal_Int32 nLocalResult (0);
+ for (sal_Int32 nIndex=nURLCount-1,nLocalIndex=nLocalURLCount-1;
+ nIndex>=0&&nLocalIndex>=0;
+ --nIndex,--nLocalIndex)
+ {
+ if (nIndex == 0 )
+ nLocalResult = maResourceURLs[nIndex].compareTo(rxId->getResourceURL());
+ else
+ nLocalResult = maResourceURLs[nIndex].compareTo(aAnchorURLs[nIndex-1]);
+ if (nLocalResult != 0)
+ {
+ if (nLocalResult < 0)
+ nResult = -1;
+ else
+ nResult = +1;
+ break;
+ }
+ }
+
+ if (nResult == 0)
+ {
+ // No difference found yet. When the lengths are the same then the
+ // two resource ids are equivalent. Otherwise the shorter comes
+ // first.
+ if (nLocalURLCount != nURLCount)
+ {
+ if (nLocalURLCount < nURLCount)
+ nResult = -1;
+ else
+ nResult = +1;
+ }
+ }
+
+ return nResult;
+}
+
+sal_Bool SAL_CALL
+ ResourceId::isBoundTo (
+ const Reference<XResourceId>& rxResourceId,
+ AnchorBindingMode eMode)
+{
+ if ( ! rxResourceId.is())
+ {
+ // An empty reference is interpreted as empty resource id.
+ return IsBoundToAnchor(nullptr, nullptr, eMode);
+ }
+
+ ResourceId* pId = nullptr;
+#ifdef USE_OPTIMIZATIONS
+ pId = dynamic_cast<ResourceId*>(rxResourceId.get());
+#endif
+ if (pId != nullptr)
+ {
+ return IsBoundToAnchor(pId->maResourceURLs, eMode);
+ }
+ else
+ {
+ const OUString sResourceURL (rxResourceId->getResourceURL());
+ const Sequence<OUString> aAnchorURLs (rxResourceId->getAnchorURLs());
+ return IsBoundToAnchor(&sResourceURL, &aAnchorURLs, eMode);
+ }
+}
+
+sal_Bool SAL_CALL
+ ResourceId::isBoundToURL (
+ const OUString& rsAnchorURL,
+ AnchorBindingMode eMode)
+{
+ return IsBoundToAnchor(&rsAnchorURL, nullptr, eMode);
+}
+
+Reference<XResourceId> SAL_CALL
+ ResourceId::clone()
+{
+ return new ResourceId(std::vector(maResourceURLs));
+}
+
+//----- XInitialization -------------------------------------------------------
+
+void SAL_CALL ResourceId::initialize (const Sequence<Any>& aArguments)
+{
+ for (const auto& rArgument : aArguments)
+ {
+ OUString sResourceURL;
+ if (rArgument >>= sResourceURL)
+ maResourceURLs.push_back(sResourceURL);
+ else
+ {
+ Reference<XResourceId> xAnchor;
+ if (rArgument >>= xAnchor)
+ {
+ if (xAnchor.is())
+ {
+ maResourceURLs.push_back(xAnchor->getResourceURL());
+ const Sequence<OUString> aAnchorURLs (xAnchor->getAnchorURLs());
+ maResourceURLs.insert( maResourceURLs.end(), aAnchorURLs.begin(), aAnchorURLs.end() );
+ }
+ }
+ }
+ }
+ ParseResourceURL();
+}
+
+OUString ResourceId::getImplementationName()
+{
+ return "com.sun.star.comp.Draw.framework.ResourceId";
+}
+
+sal_Bool ResourceId::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> ResourceId::getSupportedServiceNames()
+{
+ return css::uno::Sequence<OUString>{
+ "com.sun.star.drawing.framework.ResourceId"};
+}
+
+/** When eMode is DIRECTLY then the anchor of the called object and the
+ anchor represented by the given sequence of anchor URLs have to be
+ identical. When eMode is RECURSIVE then the anchor of the called
+ object has to start with the given anchor URLs.
+*/
+bool ResourceId::IsBoundToAnchor (
+ const OUString* psFirstAnchorURL,
+ const Sequence<OUString>* paAnchorURLs,
+ AnchorBindingMode eMode) const
+{
+ const sal_uInt32 nLocalAnchorURLCount (maResourceURLs.size() - 1);
+ const bool bHasFirstAnchorURL (psFirstAnchorURL!=nullptr);
+ const sal_uInt32 nAnchorURLCount ((bHasFirstAnchorURL?1:0)
+ + (paAnchorURLs!=nullptr ? paAnchorURLs->getLength() : 0));
+
+ // Check the lengths.
+ if (nLocalAnchorURLCount<nAnchorURLCount ||
+ (eMode==AnchorBindingMode_DIRECT && nLocalAnchorURLCount!=nAnchorURLCount))
+ {
+ return false;
+ }
+
+ // Compare the nAnchorURLCount bottom-most anchor URLs of this resource
+ // id and the given anchor.
+ sal_uInt32 nOffset = 0;
+ if (paAnchorURLs != nullptr)
+ {
+ sal_uInt32 nCount = paAnchorURLs->getLength();
+ while (nOffset < nCount)
+ {
+ if ( maResourceURLs[nLocalAnchorURLCount - nOffset] !=
+ (*paAnchorURLs)[nCount - 1 - nOffset] )
+ {
+ return false;
+ }
+ ++nOffset;
+ }
+ }
+ if (bHasFirstAnchorURL)
+ {
+ if ( *psFirstAnchorURL != maResourceURLs[nLocalAnchorURLCount - nOffset] )
+ return false;
+ }
+
+ return true;
+}
+
+bool ResourceId::IsBoundToAnchor (
+ const ::std::vector<OUString>& rAnchorURLs,
+ AnchorBindingMode eMode) const
+{
+ const sal_uInt32 nLocalAnchorURLCount (maResourceURLs.size() - 1);
+ const sal_uInt32 nAnchorURLCount (rAnchorURLs.size());
+
+ // Check the lengths.
+ if (nLocalAnchorURLCount<nAnchorURLCount ||
+ (eMode==AnchorBindingMode_DIRECT && nLocalAnchorURLCount!=nAnchorURLCount))
+ {
+ return false;
+ }
+
+ // Compare the nAnchorURLCount bottom-most anchor URLs of this resource
+ // id and the given anchor.
+ for (sal_uInt32 nOffset=0; nOffset<nAnchorURLCount; ++nOffset)
+ {
+ if ( maResourceURLs[nLocalAnchorURLCount - nOffset] !=
+ rAnchorURLs[nAnchorURLCount - 1 - nOffset] )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ResourceId::ParseResourceURL()
+{
+ ::osl::Guard< ::osl::Mutex > aGuard (::osl::Mutex::getGlobalMutex());
+ Reference<util::XURLTransformer> xURLTransformer (mxURLTransformerWeak);
+ if ( ! xURLTransformer.is())
+ {
+ // Create the URL transformer.
+ Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
+ xURLTransformer.set(util::URLTransformer::create(xContext));
+ mxURLTransformerWeak = xURLTransformer;
+ SdGlobalResourceContainer::Instance().AddResource(
+ Reference<XInterface>(xURLTransformer,UNO_QUERY));
+ }
+
+ if (xURLTransformer.is() && !maResourceURLs.empty() )
+ {
+ mpURL.reset(new util::URL);
+ mpURL->Complete = maResourceURLs[0];
+ xURLTransformer->parseStrict(*mpURL);
+ if (mpURL->Main == maResourceURLs[0])
+ mpURL.reset();
+ else
+ maResourceURLs[0] = mpURL->Main;
+ }
+}
+
+} // end of namespace sd::framework
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Draw_framework_ResourceID_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new sd::framework::ResourceId());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/UpdateRequest.cxx b/sd/source/ui/framework/configuration/UpdateRequest.cxx
new file mode 100644
index 000000000..b6c5e8c42
--- /dev/null
+++ b/sd/source/ui/framework/configuration/UpdateRequest.cxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "UpdateRequest.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sd::framework
+{
+UpdateRequest::UpdateRequest() noexcept {}
+
+UpdateRequest::~UpdateRequest() noexcept {}
+
+void SAL_CALL UpdateRequest::execute(const Reference<XConfiguration>&)
+{
+ // Do nothing here. The configuration is updated when the request queue
+ // becomes empty.
+}
+
+OUString SAL_CALL UpdateRequest::getName() { return "UpdateRequest"; }
+
+void SAL_CALL UpdateRequest::setName(const OUString&)
+{
+ // Ignored.
+}
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/UpdateRequest.hxx b/sd/source/ui/framework/configuration/UpdateRequest.hxx
new file mode 100644
index 000000000..712167154
--- /dev/null
+++ b/sd/source/ui/framework/configuration/UpdateRequest.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/drawing/framework/XConfigurationChangeRequest.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <comphelper/compbase.hxx>
+
+namespace com::sun::star::drawing::framework { class XConfiguration; }
+
+namespace sd::framework {
+
+typedef comphelper::WeakComponentImplHelper <
+ css::drawing::framework::XConfigurationChangeRequest,
+ css::container::XNamed
+ > UpdateRequestInterfaceBase;
+
+/** This update request is used to request configuration updates
+ asynchronous when no other requests are being processed. When there are
+ other requests then we can simply wait until the last one is executed:
+ the configuration is updated when the request queue becomes empty. This
+ is use by this implementation as well. The execute() method does not
+ really do anything. This request just triggers the update of the
+ configuration when it is removed as last request from the queue.
+*/
+class UpdateRequest final
+ : public UpdateRequestInterfaceBase
+{
+public:
+ UpdateRequest() noexcept;
+ virtual ~UpdateRequest() noexcept override;
+
+ // XConfigurationChangeOperation
+
+ virtual void SAL_CALL execute (
+ const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration) override;
+
+ // XNamed
+
+ /** Return a human readable string representation. This is used for
+ debugging purposes.
+ */
+ virtual OUString SAL_CALL getName() override;
+
+ /** This call is ignored because the XNamed interface is (mis)used to
+ give access to a human readable name for debugging purposes.
+ */
+ virtual void SAL_CALL setName (const OUString& rName) override;
+};
+
+} // end of namespace sd::framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/framework/configuration/debugtrace.hxx b/sd/source/ui/framework/configuration/debugtrace.hxx
new file mode 100644
index 000000000..b520d0ff3
--- /dev/null
+++ b/sd/source/ui/framework/configuration/debugtrace.hxx
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+/// Centrally define activation of configuration debug traces.
+#define DEBUG_SD_CONFIGURATION_TRACE 0
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */