diff options
Diffstat (limited to 'sd/source/ui/framework')
54 files changed, 9738 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: */ diff --git a/sd/source/ui/framework/factories/BasicPaneFactory.cxx b/sd/source/ui/framework/factories/BasicPaneFactory.cxx new file mode 100644 index 000000000..c01d315a3 --- /dev/null +++ b/sd/source/ui/framework/factories/BasicPaneFactory.cxx @@ -0,0 +1,432 @@ +/* -*- 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 <memory> +#include <sal/config.h> + +#include "BasicPaneFactory.hxx" + +#include "ChildWindowPane.hxx" +#include "FrameWindowPane.hxx" +#include "FullScreenPane.hxx" + +#include <comphelper/servicehelper.hxx> +#include <framework/FrameworkHelper.hxx> +#include <PaneShells.hxx> +#include <ViewShellBase.hxx> +#include <PaneChildWindows.hxx> +#include <DrawController.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace { + enum PaneId { + CenterPaneId, + FullScreenPaneId, + LeftImpressPaneId, + LeftDrawPaneId + }; + + const sal_Int32 gnConfigurationUpdateStartEvent(0); + const sal_Int32 gnConfigurationUpdateEndEvent(1); +} + +namespace sd::framework { + +/** Store URL, XPane reference and (local) PaneId for every pane factory + that is registered at the PaneController. +*/ +class BasicPaneFactory::PaneDescriptor +{ +public: + OUString msPaneURL; + Reference<XResource> mxPane; + PaneId mePaneId; + /** The mbReleased flag is set when the pane has been released. Some + panes are just hidden and destroyed. When the pane is reused this + flag is reset. + */ + bool mbIsReleased; + + bool CompareURL(std::u16string_view rsPaneURL) const { return msPaneURL == rsPaneURL; } + bool ComparePane(const Reference<XResource>& rxPane) const { return mxPane == rxPane; } +}; + +class BasicPaneFactory::PaneContainer + : public ::std::vector<PaneDescriptor> +{ +public: + PaneContainer() {} +}; + +//===== PaneFactory =========================================================== + +BasicPaneFactory::BasicPaneFactory ( + const Reference<XComponentContext>& rxContext) + : mxComponentContext(rxContext), + mpViewShellBase(nullptr), + mpPaneContainer(new PaneContainer) +{ +} + +BasicPaneFactory::~BasicPaneFactory() +{ +} + +void BasicPaneFactory::disposing(std::unique_lock<std::mutex>&) +{ + Reference<XConfigurationController> xCC (mxConfigurationControllerWeak); + if (xCC.is()) + { + xCC->removeResourceFactoryForReference(this); + xCC->removeConfigurationChangeListener(this); + mxConfigurationControllerWeak.clear(); + } + + for (const auto& rDescriptor : *mpPaneContainer) + { + if (rDescriptor.mbIsReleased) + { + Reference<XComponent> xComponent (rDescriptor.mxPane, UNO_QUERY); + if (xComponent.is()) + { + xComponent->removeEventListener(this); + xComponent->dispose(); + } + } + } +} + +void SAL_CALL BasicPaneFactory::initialize (const Sequence<Any>& aArguments) +{ + if (!aArguments.hasElements()) + return; + + try + { + // Get the XController from the first argument. + Reference<frame::XController> xController (aArguments[0], UNO_QUERY_THROW); + + // Tunnel through the controller to obtain access to the ViewShellBase. + try + { + Reference<lang::XUnoTunnel> xTunnel (xController, UNO_QUERY_THROW); + if (auto pController = comphelper::getFromUnoTunnel<DrawController>(xTunnel)) + mpViewShellBase = pController->GetViewShellBase(); + } + catch(RuntimeException&) + {} + + Reference<XControllerManager> xCM (xController, UNO_QUERY_THROW); + Reference<XConfigurationController> xCC (xCM->getConfigurationController()); + mxConfigurationControllerWeak = xCC; + + // Add pane factories for the two left panes (one for Impress and one for + // Draw) and the center pane. + if (xController.is() && xCC.is()) + { + PaneDescriptor aDescriptor; + aDescriptor.msPaneURL = FrameworkHelper::msCenterPaneURL; + aDescriptor.mePaneId = CenterPaneId; + aDescriptor.mbIsReleased = false; + mpPaneContainer->push_back(aDescriptor); + xCC->addResourceFactory(aDescriptor.msPaneURL, this); + + aDescriptor.msPaneURL = FrameworkHelper::msFullScreenPaneURL; + aDescriptor.mePaneId = FullScreenPaneId; + mpPaneContainer->push_back(aDescriptor); + xCC->addResourceFactory(aDescriptor.msPaneURL, this); + + aDescriptor.msPaneURL = FrameworkHelper::msLeftImpressPaneURL; + aDescriptor.mePaneId = LeftImpressPaneId; + mpPaneContainer->push_back(aDescriptor); + xCC->addResourceFactory(aDescriptor.msPaneURL, this); + + aDescriptor.msPaneURL = FrameworkHelper::msLeftDrawPaneURL; + aDescriptor.mePaneId = LeftDrawPaneId; + mpPaneContainer->push_back(aDescriptor); + xCC->addResourceFactory(aDescriptor.msPaneURL, this); + } + + // Register as configuration change listener. + if (xCC.is()) + { + xCC->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateStartEvent, + Any(gnConfigurationUpdateStartEvent)); + xCC->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateEndEvent, + Any(gnConfigurationUpdateEndEvent)); + } + } + catch (RuntimeException&) + { + Reference<XConfigurationController> xCC (mxConfigurationControllerWeak); + if (xCC.is()) + xCC->removeResourceFactoryForReference(this); + } +} + +//===== XPaneFactory ========================================================== + +Reference<XResource> SAL_CALL BasicPaneFactory::createResource ( + const Reference<XResourceId>& rxPaneId) +{ + ThrowIfDisposed(); + + Reference<XResource> xPane; + + // Based on the ResourceURL of the given ResourceId look up the + // corresponding factory descriptor. + PaneContainer::iterator iDescriptor ( + ::std::find_if ( + mpPaneContainer->begin(), + mpPaneContainer->end(), + [&] (PaneDescriptor const& rPane) { + return rPane.CompareURL(rxPaneId->getResourceURL()); + } )); + + if (iDescriptor == mpPaneContainer->end()) + { + // The requested pane can not be created by any of the factories + // managed by the called BasicPaneFactory object. + throw lang::IllegalArgumentException("BasicPaneFactory::createPane() called for unknown resource id", + nullptr, + 0); + } + + if (iDescriptor->mxPane.is()) + { + // The pane has already been created and is still active (has + // not yet been released). This should not happen. + xPane = iDescriptor->mxPane; + } + else + { + // Create a new pane. + switch (iDescriptor->mePaneId) + { + case CenterPaneId: + xPane = CreateFrameWindowPane(rxPaneId); + break; + + case FullScreenPaneId: + xPane = CreateFullScreenPane(mxComponentContext, rxPaneId); + break; + + case LeftImpressPaneId: + case LeftDrawPaneId: + xPane = CreateChildWindowPane( + rxPaneId, + *iDescriptor); + break; + } + iDescriptor->mxPane = xPane; + + // Listen for the pane being disposed. + Reference<lang::XComponent> xComponent (xPane, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(this); + } + iDescriptor->mbIsReleased = false; + + + return xPane; +} + +void SAL_CALL BasicPaneFactory::releaseResource ( + const Reference<XResource>& rxPane) +{ + ThrowIfDisposed(); + + // Based on the given XPane reference look up the corresponding factory + // descriptor. + PaneContainer::iterator iDescriptor ( + ::std::find_if( + mpPaneContainer->begin(), + mpPaneContainer->end(), + [&] (PaneDescriptor const& rPane) { return rPane.ComparePane(rxPane); } )); + + if (iDescriptor == mpPaneContainer->end()) + { + // The given XPane reference is either empty or the pane was not + // created by any of the factories managed by the called + // BasicPaneFactory object. + throw lang::IllegalArgumentException("BasicPaneFactory::releasePane() called for pane that was not created by same factory.", + nullptr, + 0); + } + + // The given pane was created by one of the factories. Child + // windows are just hidden and will be reused when requested later. + // Other windows are disposed and their reference is reset so that + // on the next createPane() call for the same pane type the pane is + // created anew. + ChildWindowPane* pChildWindowPane = dynamic_cast<ChildWindowPane*>(rxPane.get()); + if (pChildWindowPane != nullptr) + { + iDescriptor->mbIsReleased = true; + pChildWindowPane->Hide(); + } + else + { + iDescriptor->mxPane = nullptr; + Reference<XComponent> xComponent (rxPane, UNO_QUERY); + if (xComponent.is()) + { + // We are disposing the pane and do not have to be informed of + // that. + xComponent->removeEventListener(this); + xComponent->dispose(); + } + } + +} + +//===== XConfigurationChangeListener ========================================== + +void SAL_CALL BasicPaneFactory::notifyConfigurationChange ( + const ConfigurationChangeEvent& /* rEvent */ ) +{ + // FIXME: nothing to do +} + +//===== lang::XEventListener ================================================== + +void SAL_CALL BasicPaneFactory::disposing ( + const lang::EventObject& rEventObject) +{ + if (mxConfigurationControllerWeak.get() == rEventObject.Source) + { + mxConfigurationControllerWeak.clear(); + } + else + { + // Has one of the panes been disposed? If so, then release the + // reference to that pane, but not the pane descriptor. + Reference<XResource> xPane (rEventObject.Source, UNO_QUERY); + PaneContainer::iterator iDescriptor ( + ::std::find_if ( + mpPaneContainer->begin(), + mpPaneContainer->end(), + [&] (PaneDescriptor const& rPane) { return rPane.ComparePane(xPane); } )); + if (iDescriptor != mpPaneContainer->end()) + { + iDescriptor->mxPane = nullptr; + } + } +} + +Reference<XResource> BasicPaneFactory::CreateFrameWindowPane ( + const Reference<XResourceId>& rxPaneId) +{ + Reference<XResource> xPane; + + if (mpViewShellBase != nullptr) + { + xPane = new FrameWindowPane(rxPaneId, mpViewShellBase->GetViewWindow()); + } + + return xPane; +} + +Reference<XResource> BasicPaneFactory::CreateFullScreenPane ( + const Reference<XComponentContext>& rxComponentContext, + const Reference<XResourceId>& rxPaneId) +{ + Reference<XResource> xPane ( + new FullScreenPane( + rxComponentContext, + rxPaneId, + mpViewShellBase->GetViewWindow())); + + return xPane; +} + +Reference<XResource> BasicPaneFactory::CreateChildWindowPane ( + const Reference<XResourceId>& rxPaneId, + const PaneDescriptor& rDescriptor) +{ + Reference<XResource> xPane; + + if (mpViewShellBase != nullptr) + { + // Create the corresponding shell and determine the id of the child window. + sal_uInt16 nChildWindowId = 0; + ::std::unique_ptr<SfxShell> pShell; + switch (rDescriptor.mePaneId) + { + case LeftImpressPaneId: + pShell.reset(new LeftImpressPaneShell()); + nChildWindowId = ::sd::LeftPaneImpressChildWindow::GetChildWindowId(); + break; + + case LeftDrawPaneId: + pShell.reset(new LeftDrawPaneShell()); + nChildWindowId = ::sd::LeftPaneDrawChildWindow::GetChildWindowId(); + break; + + default: + break; + } + + // With shell and child window id create the ChildWindowPane + // wrapper. + if (pShell != nullptr) + { + xPane = new ChildWindowPane( + rxPaneId, + nChildWindowId, + *mpViewShellBase, + std::move(pShell)); + } + } + + return xPane; +} + +void BasicPaneFactory::ThrowIfDisposed() const +{ + if (m_bDisposed) + { + throw lang::DisposedException ("BasicPaneFactory object has already been disposed", + const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this))); + } +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_BasicPaneFactory_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::framework::BasicPaneFactory(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicPaneFactory.hxx b/sd/source/ui/framework/factories/BasicPaneFactory.hxx new file mode 100644 index 000000000..317776e48 --- /dev/null +++ b/sd/source/ui/framework/factories/BasicPaneFactory.hxx @@ -0,0 +1,131 @@ +/* -*- 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/XResourceFactory.hpp> +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <comphelper/compbase.hxx> +#include <cppuhelper/weakref.hxx> + +#include <memory> + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd { + +class ViewShellBase; +} + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::lang::XInitialization, + css::drawing::framework::XResourceFactory, + css::drawing::framework::XConfigurationChangeListener + > BasicPaneFactoryInterfaceBase; + +/** This factory provides the frequently used standard panes + private:resource/pane/CenterPane + private:resource/pane/FullScreenPane + private:resource/pane/LeftImpressPane + private:resource/pane/LeftDrawPane + There are two left panes because this is (seems to be) the only way to + show different titles for the left pane in Draw and Impress. +*/ +class BasicPaneFactory + : public BasicPaneFactoryInterfaceBase +{ +public: + explicit BasicPaneFactory ( + const css::uno::Reference<css::uno::XComponentContext>& rxContext); + virtual ~BasicPaneFactory() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence<css::uno::Any>& aArguments) override; + + // XResourceFactory + + virtual css::uno::Reference<css::drawing::framework::XResource> + SAL_CALL createResource ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId) override; + + virtual void SAL_CALL + releaseResource ( + const css::uno::Reference<css::drawing::framework::XResource>& rxPane) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // lang::XEventListener + + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEventObject) override; + +private: + css::uno::Reference<css::uno::XComponentContext> mxComponentContext; + css::uno::WeakReference<css::drawing::framework::XConfigurationController> + mxConfigurationControllerWeak; + ViewShellBase* mpViewShellBase; + class PaneDescriptor; + class PaneContainer; + std::unique_ptr<PaneContainer> mpPaneContainer; + + /** Create a new instance of FrameWindowPane. + @param rPaneId + There is only one frame window so this id is just checked to + have the correct value. + */ + css::uno::Reference<css::drawing::framework::XResource> + CreateFrameWindowPane ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId); + + /** Create a new pane that represents the center pane in full screen + mode. + */ + css::uno::Reference<css::drawing::framework::XResource> + CreateFullScreenPane ( + const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext, + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId); + + /** Create a new instance of ChildWindowPane. + @param rPaneId + The ResourceURL member defines which side pane to create. + */ + css::uno::Reference<css::drawing::framework::XResource> + CreateChildWindowPane ( + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxPaneId, + const PaneDescriptor& rDescriptor); + + /// @throws css::lang::DisposedException + void ThrowIfDisposed() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicToolBarFactory.cxx b/sd/source/ui/framework/factories/BasicToolBarFactory.cxx new file mode 100644 index 000000000..af79a88ea --- /dev/null +++ b/sd/source/ui/framework/factories/BasicToolBarFactory.cxx @@ -0,0 +1,161 @@ +/* -*- 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 "BasicToolBarFactory.hxx" + +#include <ViewTabBar.hxx> +#include <framework/FrameworkHelper.hxx> +#include <unotools/mediadescriptor.hxx> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/drawing/framework/XConfigurationController.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +//===== BasicToolBarFactory =================================================== + +BasicToolBarFactory::BasicToolBarFactory () +{ +} + +BasicToolBarFactory::~BasicToolBarFactory() +{ +} + +void BasicToolBarFactory::disposing(std::unique_lock<std::mutex>&) +{ + Shutdown(); +} + +void BasicToolBarFactory::Shutdown() +{ + Reference<lang::XComponent> xComponent (mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(static_cast<lang::XEventListener*>(this)); + if (mxConfigurationController.is()) + { + mxConfigurationController->removeResourceFactoryForReference(this); + mxConfigurationController = nullptr; + } +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL BasicToolBarFactory::initialize (const Sequence<Any>& aArguments) +{ + if (!aArguments.hasElements()) + return; + + try + { + // Get the XController from the first argument. + mxController.set(aArguments[0], UNO_QUERY_THROW); + + utl::MediaDescriptor aDescriptor (mxController->getModel()->getArgs()); + if ( ! aDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_PREVIEW, + false)) + { + // Register the factory for its supported tool bars. + Reference<XControllerManager> xControllerManager(mxController, UNO_QUERY_THROW); + mxConfigurationController = xControllerManager->getConfigurationController(); + if (mxConfigurationController.is()) + { + mxConfigurationController->addResourceFactory( + FrameworkHelper::msViewTabBarURL, this); + } + + Reference<lang::XComponent> xComponent (mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast<lang::XEventListener*>(this)); + } + else + { + // The view shell is in preview mode and thus does not need + // the view tab bar. + mxConfigurationController = nullptr; + } + } + catch (RuntimeException&) + { + Shutdown(); + throw; + } +} + +//----- lang::XEventListener -------------------------------------------------- + +void SAL_CALL BasicToolBarFactory::disposing ( + const lang::EventObject& rEventObject) +{ + if (rEventObject.Source == mxConfigurationController) + mxConfigurationController = nullptr; +} + +//===== XPaneFactory ========================================================== + +Reference<XResource> SAL_CALL BasicToolBarFactory::createResource ( + const Reference<XResourceId>& rxToolBarId) +{ + ThrowIfDisposed(); + + if (rxToolBarId->getResourceURL() != FrameworkHelper::msViewTabBarURL) + throw lang::IllegalArgumentException(); + + Reference<XResource> xToolBar = new ViewTabBar(rxToolBarId, mxController); + return xToolBar; +} + +void SAL_CALL BasicToolBarFactory::releaseResource ( + const Reference<XResource>& rxToolBar) +{ + ThrowIfDisposed(); + + Reference<XComponent> xComponent (rxToolBar, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); +} + +void BasicToolBarFactory::ThrowIfDisposed() const +{ + if (m_bDisposed) + { + throw lang::DisposedException ("BasicToolBarFactory object has already been disposed", + const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this))); + } +} + +} // end of namespace sd::framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_BasicToolBarFactory_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::framework::BasicToolBarFactory); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicToolBarFactory.hxx b/sd/source/ui/framework/factories/BasicToolBarFactory.hxx new file mode 100644 index 000000000..fdaf92788 --- /dev/null +++ b/sd/source/ui/framework/factories/BasicToolBarFactory.hxx @@ -0,0 +1,84 @@ +/* -*- 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/lang/XInitialization.hpp> +#include <com/sun/star/drawing/framework/XResourceFactory.hpp> +#include <comphelper/compbase.hxx> + +namespace com::sun::star::frame { class XController; } +namespace com::sun::star::drawing::framework { class XResourceId; } +namespace com::sun::star::drawing::framework { class XConfigurationController; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XResourceFactory, + css::lang::XInitialization, + css::lang::XEventListener + > BasicToolBarFactoryInterfaceBase; + +/** This factory provides some of the frequently used tool bars: + private:resource/toolbar/ViewTabBar +*/ +class BasicToolBarFactory + : public BasicToolBarFactoryInterfaceBase +{ +public: + BasicToolBarFactory (); + virtual ~BasicToolBarFactory() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // ToolBarFactory + + virtual css::uno::Reference<css::drawing::framework::XResource> SAL_CALL + createResource ( + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxToolBarId) override; + + virtual void SAL_CALL + releaseResource ( + const css::uno::Reference<css::drawing::framework::XResource>& + rxToolBar) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence<css::uno::Any>& aArguments) override; + + // lang::XEventListener + + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEventObject) override; + +private: + css::uno::Reference<css::drawing::framework::XConfigurationController> mxConfigurationController; + css::uno::Reference<css::frame::XController> mxController; + + void Shutdown(); + + /// @throws css::lang::DisposedException + void ThrowIfDisposed() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicViewFactory.cxx b/sd/source/ui/framework/factories/BasicViewFactory.cxx new file mode 100644 index 000000000..425cb4446 --- /dev/null +++ b/sd/source/ui/framework/factories/BasicViewFactory.cxx @@ -0,0 +1,518 @@ +/* -*- 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 "BasicViewFactory.hxx" + +#include <framework/ViewShellWrapper.hxx> +#include <framework/FrameworkHelper.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <framework/Pane.hxx> +#include <DrawController.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellManager.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <GraphicViewShell.hxx> +#include <OutlineViewShell.hxx> +#include <PresentationViewShell.hxx> +#include <SlideSorterViewShell.hxx> +#include <FrameView.hxx> +#include <Window.hxx> + +#include <comphelper/servicehelper.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/wrkwin.hxx> +#include <toolkit/helper/vclunohelper.hxx> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace sd::framework { + +//===== ViewDescriptor ======================================================== + +class BasicViewFactory::ViewDescriptor +{ +public: + Reference<XResource> mxView; + std::shared_ptr<sd::ViewShell> mpViewShell; + Reference<XResourceId> mxViewId; + static bool CompareView (const std::shared_ptr<ViewDescriptor>& rpDescriptor, + const Reference<XResource>& rxView) + { return rpDescriptor->mxView.get() == rxView.get(); } +}; + +//===== BasicViewFactory::ViewShellContainer ================================== + +class BasicViewFactory::ViewShellContainer + : public ::std::vector<std::shared_ptr<ViewDescriptor> > +{ +public: + ViewShellContainer() {}; +}; + +class BasicViewFactory::ViewCache + : public ::std::vector<std::shared_ptr<ViewDescriptor> > +{ +public: + ViewCache() {}; +}; + +//===== ViewFactory =========================================================== + +BasicViewFactory::BasicViewFactory () + : mpViewShellContainer(new ViewShellContainer()), + mpBase(nullptr), + mpFrameView(nullptr), + mpWindow(VclPtr<WorkWindow>::Create(nullptr,WB_STDWORK)), + mpViewCache(std::make_shared<ViewCache>()), + mxLocalPane(new Pane(Reference<XResourceId>(), mpWindow.get())) +{ +} + +BasicViewFactory::~BasicViewFactory() +{ +} + +void BasicViewFactory::disposing(std::unique_lock<std::mutex>&) +{ + // Disconnect from the frame view. + if (mpFrameView != nullptr) + { + mpFrameView->Disconnect(); + mpFrameView = nullptr; + } + + // Release the view cache. + for (const auto& rxView : *mpViewCache) + { + ReleaseView(rxView, true); + } + + // Release the view shell container. At this point no one other than us + // should hold references to the view shells (at the moment this is a + // trivial requirement, because no one other than us holds a shared + // pointer). + // ViewShellContainer::const_iterator iView; + for (const auto& rxView : *mpViewShellContainer) + { + OSL_ASSERT(rxView->mpViewShell.use_count() == 1); + } + mpViewShellContainer.reset(); +} + +Reference<XResource> SAL_CALL BasicViewFactory::createResource ( + const Reference<XResourceId>& rxViewId) +{ + Reference<XResource> xView; + const bool bIsCenterPane ( + rxViewId->isBoundToURL(FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)); + + // Get the pane for the anchor URL. + Reference<XPane> xPane; + if (mxConfigurationController.is()) + xPane.set(mxConfigurationController->getResource(rxViewId->getAnchor()), UNO_QUERY); + + // For main views use the frame view of the last main view. + ::sd::FrameView* pFrameView = nullptr; + if (xPane.is() && bIsCenterPane) + { + pFrameView = mpFrameView; + } + + // Get Window pointer for XWindow of the pane. + vcl::Window* pWindow = nullptr; + if (xPane.is()) + pWindow = VCLUnoHelper::GetWindow(xPane->getWindow()); + + // Get the view frame. + SfxViewFrame* pFrame = nullptr; + if (mpBase != nullptr) + pFrame = mpBase->GetViewFrame(); + + if (pFrame != nullptr && mpBase!=nullptr && pWindow!=nullptr) + { + // Try to get the view from the cache. + std::shared_ptr<ViewDescriptor> pDescriptor (GetViewFromCache(rxViewId, xPane)); + + // When the requested view is not in the cache then create a new view. + if (pDescriptor == nullptr) + { + pDescriptor = CreateView(rxViewId, *pFrame, *pWindow, xPane, pFrameView, bIsCenterPane); + } + + if (pDescriptor != nullptr) + xView = pDescriptor->mxView; + + mpViewShellContainer->push_back(pDescriptor); + + if (bIsCenterPane) + ActivateCenterView(pDescriptor); + else + pWindow->Resize(); + } + + return xView; +} + +void SAL_CALL BasicViewFactory::releaseResource (const Reference<XResource>& rxView) +{ + if ( ! rxView.is()) + throw lang::IllegalArgumentException(); + + if (!rxView.is() || !mpBase) + return; + + ViewShellContainer::iterator iViewShell ( + ::std::find_if( + mpViewShellContainer->begin(), + mpViewShellContainer->end(), + [&] (std::shared_ptr<ViewDescriptor> const& pVD) { + return ViewDescriptor::CompareView(pVD, rxView); + } )); + if (iViewShell == mpViewShellContainer->end()) + { + throw lang::IllegalArgumentException(); + } + + std::shared_ptr<ViewShell> pViewShell ((*iViewShell)->mpViewShell); + + if ((*iViewShell)->mxViewId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + // Obtain a pointer to and connect to the frame view of the + // view. The next view, that is created, will be + // initialized with this frame view. + if (mpFrameView == nullptr) + { + mpFrameView = pViewShell->GetFrameView(); + if (mpFrameView) + mpFrameView->Connect(); + } + + // With the view in the center pane the sub controller is + // released, too. + mpBase->GetDrawController().SetSubController( + Reference<drawing::XDrawSubController>()); + + SfxViewShell* pSfxViewShell = pViewShell->GetViewShell(); + if (pSfxViewShell != nullptr) + pSfxViewShell->DisconnectAllClients(); + } + + ReleaseView(*iViewShell, false); + + mpViewShellContainer->erase(iViewShell); +} + +void SAL_CALL BasicViewFactory::initialize (const Sequence<Any>& aArguments) +{ + if (!aArguments.hasElements()) + return; + + try + { + // Get the XController from the first argument. + Reference<frame::XController> xController (aArguments[0], UNO_QUERY_THROW); + + // Tunnel through the controller to obtain a ViewShellBase. + Reference<lang::XUnoTunnel> xTunnel (xController, UNO_QUERY_THROW); + ::sd::DrawController* pController = comphelper::getFromUnoTunnel<sd::DrawController>(xTunnel); + if (pController != nullptr) + mpBase = pController->GetViewShellBase(); + + // Register the factory for its supported views. + Reference<XControllerManager> xCM (xController,UNO_QUERY_THROW); + mxConfigurationController = xCM->getConfigurationController(); + if ( ! mxConfigurationController.is()) + throw RuntimeException(); + mxConfigurationController->addResourceFactory(FrameworkHelper::msImpressViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msDrawViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msOutlineViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msNotesViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msHandoutViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msPresentationViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msSlideSorterURL, this); + } + catch (RuntimeException&) + { + mpBase = nullptr; + if (mxConfigurationController.is()) + mxConfigurationController->removeResourceFactoryForReference(this); + throw; + } +} + +std::shared_ptr<BasicViewFactory::ViewDescriptor> BasicViewFactory::CreateView ( + const Reference<XResourceId>& rxViewId, + SfxViewFrame& rFrame, + vcl::Window& rWindow, + const Reference<XPane>& rxPane, + FrameView* pFrameView, + const bool bIsCenterPane) +{ + auto pDescriptor = std::make_shared<ViewDescriptor>(); + + pDescriptor->mpViewShell = CreateViewShell( + rxViewId, + rFrame, + rWindow, + pFrameView); + pDescriptor->mxViewId = rxViewId; + + if (pDescriptor->mpViewShell != nullptr) + { + pDescriptor->mpViewShell->Init(bIsCenterPane); + mpBase->GetViewShellManager()->ActivateViewShell(pDescriptor->mpViewShell.get()); + + Reference<awt::XWindow> xWindow(rxPane->getWindow()); + rtl::Reference<ViewShellWrapper> wrapper(new ViewShellWrapper( + pDescriptor->mpViewShell, + rxViewId, + xWindow)); + + // register ViewShellWrapper on pane window + if (xWindow.is()) + { + xWindow->addWindowListener(wrapper); + if (pDescriptor->mpViewShell != nullptr) + { + pDescriptor->mpViewShell->Resize(); + } + } + + pDescriptor->mxView = wrapper.get(); + } + + return pDescriptor; +} + +std::shared_ptr<ViewShell> BasicViewFactory::CreateViewShell ( + const Reference<XResourceId>& rxViewId, + SfxViewFrame& rFrame, + vcl::Window& rWindow, + FrameView* pFrameView) +{ + std::shared_ptr<ViewShell> pViewShell; + const OUString& rsViewURL (rxViewId->getResourceURL()); + if (rsViewURL == FrameworkHelper::msImpressViewURL) + { + pViewShell = + std::make_shared<DrawViewShell>( + *mpBase, + &rWindow, + PageKind::Standard, + pFrameView); + pViewShell->GetContentWindow()->set_id("impress_win"); + } + else if (rsViewURL == FrameworkHelper::msDrawViewURL) + { + pViewShell = std::shared_ptr<GraphicViewShell>( + new GraphicViewShell(*mpBase, &rWindow, pFrameView), + o3tl::default_delete<GraphicViewShell>()); + pViewShell->GetContentWindow()->set_id("draw_win"); + } + else if (rsViewURL == FrameworkHelper::msOutlineViewURL) + { + pViewShell = + std::make_shared<OutlineViewShell>( + &rFrame, + *mpBase, + &rWindow, + pFrameView); + pViewShell->GetContentWindow()->set_id("outline_win"); + } + else if (rsViewURL == FrameworkHelper::msNotesViewURL) + { + pViewShell = + std::make_shared<DrawViewShell>( + *mpBase, + &rWindow, + PageKind::Notes, + pFrameView); + pViewShell->GetContentWindow()->set_id("notes_win"); + } + else if (rsViewURL == FrameworkHelper::msHandoutViewURL) + { + pViewShell = + std::make_shared<DrawViewShell>( + *mpBase, + &rWindow, + PageKind::Handout, + pFrameView); + pViewShell->GetContentWindow()->set_id("handout_win"); + } + else if (rsViewURL == FrameworkHelper::msPresentationViewURL) + { + pViewShell = + std::make_shared<PresentationViewShell>( + *mpBase, + &rWindow, + pFrameView); + pViewShell->GetContentWindow()->set_id("presentation_win"); + } + else if (rsViewURL == FrameworkHelper::msSlideSorterURL) + { + pViewShell = ::sd::slidesorter::SlideSorterViewShell::Create ( + &rFrame, + *mpBase, + &rWindow, + pFrameView); + pViewShell->GetContentWindow()->set_id("slidesorter"); + } + + return pViewShell; +} + +void BasicViewFactory::ReleaseView ( + const std::shared_ptr<ViewDescriptor>& rpDescriptor, + bool bDoNotCache) +{ + bool bIsCacheable (!bDoNotCache && IsCacheable(rpDescriptor)); + + if (bIsCacheable) + { + Reference<XRelocatableResource> xResource (rpDescriptor->mxView, UNO_QUERY); + if (xResource.is()) + { + if (mxLocalPane.is()) + if (xResource->relocateToAnchor(mxLocalPane)) + mpViewCache->push_back(rpDescriptor); + else + bIsCacheable = false; + else + bIsCacheable = false; + } + else + { + bIsCacheable = false; + } + } + + if ( ! bIsCacheable) + { + // Shut down the current view shell. + rpDescriptor->mpViewShell->Shutdown (); + mpBase->GetDocShell()->Disconnect(rpDescriptor->mpViewShell.get()); + mpBase->GetViewShellManager()->DeactivateViewShell(rpDescriptor->mpViewShell.get()); + + Reference<XComponent> xComponent (rpDescriptor->mxView, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + } +} + +bool BasicViewFactory::IsCacheable (const std::shared_ptr<ViewDescriptor>& rpDescriptor) +{ + bool bIsCacheable (false); + + Reference<XRelocatableResource> xResource (rpDescriptor->mxView, UNO_QUERY); + if (xResource.is()) + { + static ::std::vector<Reference<XResourceId> > s_aCacheableResources = [&]() + { + ::std::vector<Reference<XResourceId> > tmp; + FrameworkHelper::Instance(*mpBase); + + // The slide sorter and the task panel are cacheable and relocatable. + tmp.push_back(FrameworkHelper::CreateResourceId( + FrameworkHelper::msSlideSorterURL, FrameworkHelper::msLeftDrawPaneURL)); + tmp.push_back(FrameworkHelper::CreateResourceId( + FrameworkHelper::msSlideSorterURL, FrameworkHelper::msLeftImpressPaneURL)); + return tmp; + }(); + + bIsCacheable = std::any_of(s_aCacheableResources.begin(), s_aCacheableResources.end(), + [&rpDescriptor](const Reference<XResourceId>& rxId) { return rxId->compareTo(rpDescriptor->mxViewId) == 0; }); + } + + return bIsCacheable; +} + +std::shared_ptr<BasicViewFactory::ViewDescriptor> BasicViewFactory::GetViewFromCache ( + const Reference<XResourceId>& rxViewId, + const Reference<XPane>& rxPane) +{ + std::shared_ptr<ViewDescriptor> pDescriptor; + + // Search for the requested view in the cache. + ViewCache::iterator iEntry = std::find_if(mpViewCache->begin(), mpViewCache->end(), + [&rxViewId](const ViewCache::value_type& rxEntry) { return rxEntry->mxViewId->compareTo(rxViewId) == 0; }); + if (iEntry != mpViewCache->end()) + { + pDescriptor = *iEntry; + mpViewCache->erase(iEntry); + } + + // When the view has been found then relocate it to the given pane and + // remove it from the cache. + if (pDescriptor != nullptr) + { + bool bRelocationSuccessful (false); + Reference<XRelocatableResource> xResource (pDescriptor->mxView, UNO_QUERY); + if (xResource.is() && rxPane.is()) + { + if (xResource->relocateToAnchor(rxPane)) + bRelocationSuccessful = true; + } + + if ( ! bRelocationSuccessful) + { + ReleaseView(pDescriptor, true); + pDescriptor.reset(); + } + } + + return pDescriptor; +} + +void BasicViewFactory::ActivateCenterView ( + const std::shared_ptr<ViewDescriptor>& rpDescriptor) +{ + mpBase->GetDocShell()->Connect(rpDescriptor->mpViewShell.get()); + + // During the creation of the new sub-shell, resize requests were not + // forwarded to it because it was not yet registered. Therefore, we + // have to request a resize now. + rpDescriptor->mpViewShell->UIFeatureChanged(); + if (mpBase->GetDocShell()->IsInPlaceActive()) + mpBase->GetViewFrame()->Resize(true); + + mpBase->GetDrawController().SetSubController( + rpDescriptor->mpViewShell->CreateSubController()); +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_BasicViewFactory_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::framework::BasicViewFactory); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicViewFactory.hxx b/sd/source/ui/framework/factories/BasicViewFactory.hxx new file mode 100644 index 000000000..ccd5cbbda --- /dev/null +++ b/sd/source/ui/framework/factories/BasicViewFactory.hxx @@ -0,0 +1,129 @@ +/* -*- 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/XResourceFactory.hpp> +#include <com/sun/star/lang/XInitialization.hpp> + +#include <comphelper/compbase.hxx> + +#include <vcl/vclptr.hxx> +#include <memory> + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XPane; } + +namespace sd { +class ViewShell; +class ViewShellBase; +class FrameView; +} +class SfxViewFrame; +namespace vcl { class Window; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XResourceFactory, + css::lang::XInitialization + > BasicViewFactoryInterfaceBase; + +/** Factory for the frequently used standard views of the drawing framework: + private:resource/view/ + private:resource/view/ImpressView + private:resource/view/GraphicView + private:resource/view/OutlineView + private:resource/view/NotesView + private:resource/view/HandoutView + private:resource/view/SlideSorter + private:resource/view/PresentationView + private:resource/view/TaskPane + For some views in some panes this class also acts as a cache. +*/ +class BasicViewFactory + : public BasicViewFactoryInterfaceBase +{ +public: + BasicViewFactory (); + virtual ~BasicViewFactory() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XViewFactory + + virtual css::uno::Reference<css::drawing::framework::XResource> + SAL_CALL createResource ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId) override; + + virtual void SAL_CALL releaseResource ( + const css::uno::Reference<css::drawing::framework::XResource>& xView) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence<css::uno::Any>& aArguments) override; + +private: + css::uno::Reference<css::drawing::framework::XConfigurationController> + mxConfigurationController; + class ViewDescriptor; + class ViewShellContainer; + std::unique_ptr<ViewShellContainer> mpViewShellContainer; + ViewShellBase* mpBase; + FrameView* mpFrameView; + + class ViewCache; + ScopedVclPtr<vcl::Window> mpWindow; + std::shared_ptr<ViewCache> mpViewCache; + + css::uno::Reference<css::drawing::framework::XPane> mxLocalPane; + + std::shared_ptr<ViewDescriptor> CreateView ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId, + SfxViewFrame& rFrame, + vcl::Window& rWindow, + const css::uno::Reference<css::drawing::framework::XPane>& rxPane, + FrameView* pFrameView, + const bool bIsCenterView); + + std::shared_ptr<ViewShell> CreateViewShell ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId, + SfxViewFrame& rFrame, + vcl::Window& rWindow, + FrameView* pFrameView); + + void ActivateCenterView ( + const std::shared_ptr<ViewDescriptor>& rpDescriptor); + + void ReleaseView ( + const std::shared_ptr<ViewDescriptor>& rpDescriptor, + bool bDoNotCache); + + bool IsCacheable ( + const std::shared_ptr<ViewDescriptor>& rpDescriptor); + + std::shared_ptr<ViewDescriptor> GetViewFromCache ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId, + const css::uno::Reference<css::drawing::framework::XPane>& rxPane); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/ChildWindowPane.cxx b/sd/source/ui/framework/factories/ChildWindowPane.cxx new file mode 100644 index 000000000..136f6fcb7 --- /dev/null +++ b/sd/source/ui/framework/factories/ChildWindowPane.cxx @@ -0,0 +1,219 @@ +/* -*- 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 <memory> +#include <sal/config.h> +#include <sal/log.hxx> + +#include <utility> + +#include "ChildWindowPane.hxx" + +#include <PaneDockingWindow.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellManager.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <sfx2/viewfrm.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +ChildWindowPane::ChildWindowPane ( + const Reference<XResourceId>& rxPaneId, + sal_uInt16 nChildWindowId, + ViewShellBase& rViewShellBase, + ::std::unique_ptr<SfxShell> && pShell) + : ChildWindowPaneInterfaceBase(rxPaneId,nullptr), + mnChildWindowId(nChildWindowId), + mrViewShellBase(rViewShellBase), + mpShell(std::move(pShell)), + mbHasBeenActivated(false) +{ + mrViewShellBase.GetViewShellManager()->ActivateShell(mpShell.get()); + + SfxViewFrame* pViewFrame = mrViewShellBase.GetViewFrame(); + if (pViewFrame == nullptr) + return; + + if (mrViewShellBase.IsActive()) + { + if (pViewFrame->KnowsChildWindow(mnChildWindowId)) + { + if (pViewFrame->HasChildWindow(mnChildWindowId)) + { + // The ViewShellBase has already been activated. Make + // the child window visible as soon as possible. + pViewFrame->SetChildWindow(mnChildWindowId, true); + } + else + { + // The window is created asynchronously. Rely on the + // ConfigurationUpdater to try another update, and with + // that another request for this window, in a short + // time. + } + } + else + { + SAL_WARN("sd", "ChildWindowPane:not known"); + } + } + else + { + // The ViewShellBase has not yet been activated. Hide the + // window and wait a little before it is made visible. See + // comments in the GetWindow() method for an explanation. + pViewFrame->SetChildWindow(mnChildWindowId, false); + } +} + +ChildWindowPane::~ChildWindowPane() +{ +} + +void ChildWindowPane::Hide() +{ + SfxViewFrame* pViewFrame = mrViewShellBase.GetViewFrame(); + if (pViewFrame != nullptr) + if (pViewFrame->KnowsChildWindow(mnChildWindowId)) + if (pViewFrame->HasChildWindow(mnChildWindowId)) + pViewFrame->SetChildWindow(mnChildWindowId, false); + + // Release the window because when the child window is shown again it + // may use a different window. + mxWindow = nullptr; +} + +void SAL_CALL ChildWindowPane::disposing() +{ + ::osl::MutexGuard aGuard (m_aMutex); + + mrViewShellBase.GetViewShellManager()->DeactivateShell(mpShell.get()); + mpShell.reset(); + + if (mxWindow.is()) + { + mxWindow->removeEventListener(this); + } + + Pane::disposing(); +} + +vcl::Window* ChildWindowPane::GetWindow() +{ + do + { + if (mxWindow.is()) + // Window already exists => nothing to do. + break; + + // When the window is not yet present then obtain it only when the + // shell has already been activated. The activation is not + // necessary for the code to work properly but is used to optimize + // the layouting and displaying of the window. When it is made + // visible too early then some layouting seems to be made twice or at + // an inconvenient time and the overall process of initializing the + // Impress takes longer. + if (!mbHasBeenActivated && mpShell != nullptr && !mpShell->IsActive()) + break; + + mbHasBeenActivated = true; + SfxViewFrame* pViewFrame = mrViewShellBase.GetViewFrame(); + if (pViewFrame == nullptr) + break; + // The view frame has to know the child window. This can be the + // case, when for example the document is in read-only mode: the + // task pane is then not available. + if ( ! pViewFrame->KnowsChildWindow(mnChildWindowId)) + break; + + pViewFrame->SetChildWindow(mnChildWindowId, true); + SfxChildWindow* pChildWindow = pViewFrame->GetChildWindow(mnChildWindowId); + if (pChildWindow == nullptr) + if (pViewFrame->HasChildWindow(mnChildWindowId)) + { + // The child window is not yet visible. Ask the view frame + // to show it and try again to get access to the child + // window. + pViewFrame->ShowChildWindow(mnChildWindowId); + pChildWindow = pViewFrame->GetChildWindow(mnChildWindowId); + } + + // When the child window is still not visible then we have to try later. + if (pChildWindow == nullptr) + break; + + // From the child window get the docking window and from that the + // content window that is the container for the actual content. + PaneDockingWindow* pDockingWindow = dynamic_cast<PaneDockingWindow*>( + pChildWindow->GetWindow()); + if (pDockingWindow == nullptr) + break; + + // At last, we have access to the window and its UNO wrapper. + mpWindow = &pDockingWindow->GetContentWindow(); + mxWindow = VCLUnoHelper::GetInterface(mpWindow); + + // Register as window listener to be informed when the child window + // is hidden. + if (mxWindow.is()) + mxWindow->addEventListener(this); + } + while (false); + + return mpWindow; +} + +Reference<awt::XWindow> SAL_CALL ChildWindowPane::getWindow() +{ + if (mpWindow == nullptr || ! mxWindow.is()) + GetWindow(); + return Pane::getWindow(); +} + +IMPLEMENT_FORWARD_XINTERFACE2( + ChildWindowPane, + ChildWindowPaneInterfaceBase, + Pane); +IMPLEMENT_FORWARD_XTYPEPROVIDER2( + ChildWindowPane, + ChildWindowPaneInterfaceBase, + Pane); + +//----- XEventListener -------------------------------------------------------- + +void SAL_CALL ChildWindowPane::disposing (const lang::EventObject& rEvent) +{ + ThrowIfDisposed(); + + if (rEvent.Source == mxWindow) + { + // The window is gone but the pane remains alive. The next call to + // GetWindow() may create the window anew. + mxWindow = nullptr; + mpWindow = nullptr; + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/ChildWindowPane.hxx b/sd/source/ui/framework/factories/ChildWindowPane.hxx new file mode 100644 index 000000000..082177757 --- /dev/null +++ b/sd/source/ui/framework/factories/ChildWindowPane.hxx @@ -0,0 +1,101 @@ +/* -*- 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 <framework/Pane.hxx> + +#include <com/sun/star/lang/XEventListener.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/uno3.hxx> +#include <memory> + +class SfxShell; + +namespace sd { class ViewShellBase; } +namespace com::sun::star::awt { class XWindow; } +namespace com::sun::star::drawing::framework { class XResourceId; } + +namespace sd::framework { + +typedef ::cppu::ImplInheritanceHelper < + ::sd::framework::Pane, + css::lang::XEventListener + > ChildWindowPaneInterfaceBase; + +/** The ChildWindowPane listens to the child window and disposes itself when + the child window becomes inaccessible. This happens for instance when a + document is made read-only and the task pane is turned off. +*/ +class ChildWindowPane + : public ChildWindowPaneInterfaceBase +{ +public: + ChildWindowPane ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId, + sal_uInt16 nChildWindowId, + ViewShellBase& rViewShellBase, + ::std::unique_ptr<SfxShell> && pShell); + virtual ~ChildWindowPane() override; + + /** Hide the pane. To make the pane visible again, call GetWindow(). + */ + void Hide(); + + virtual void SAL_CALL disposing() override; + + /** This returns the content window when the child window is already + visible. Otherwise <NULL/> is returned. In that case a later call + may return the requested window (making a child window visible is an + asynchronous process.) + Note that GetWindow() may return different Window pointers when + Hide() is called in between. + */ + virtual vcl::Window* GetWindow() override; + + /** The local getWindow() first calls GetWindow() to provide a valid + window pointer before forwarding the call to the base class. + */ + virtual css::uno::Reference<css::awt::XWindow> + SAL_CALL getWindow() override; + + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // XEventListener + + virtual void SAL_CALL disposing( + const css::lang::EventObject& rEvent) override; + +private: + sal_uInt16 mnChildWindowId; + ViewShellBase& mrViewShellBase; + ::std::unique_ptr<SfxShell> mpShell; + + /** This flag is set when the pane shell has been activated at least + once. It is used to optimize the start-up performance (by not + showing the window too early) and by not delaying its creation at + later times. + */ + bool mbHasBeenActivated; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/FrameWindowPane.cxx b/sd/source/ui/framework/factories/FrameWindowPane.cxx new file mode 100644 index 000000000..1f4b387d8 --- /dev/null +++ b/sd/source/ui/framework/factories/FrameWindowPane.cxx @@ -0,0 +1,39 @@ +/* -*- 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 "FrameWindowPane.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework +{ +FrameWindowPane::FrameWindowPane(const Reference<XResourceId>& rxPaneId, vcl::Window* pWindow) + : Pane(rxPaneId, pWindow) +{ +} + +FrameWindowPane::~FrameWindowPane() noexcept {} + +sal_Bool SAL_CALL FrameWindowPane::isAnchorOnly() { return false; } + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/FrameWindowPane.hxx b/sd/source/ui/framework/factories/FrameWindowPane.hxx new file mode 100644 index 000000000..67da37fdf --- /dev/null +++ b/sd/source/ui/framework/factories/FrameWindowPane.hxx @@ -0,0 +1,50 @@ +/* -*- 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 <framework/Pane.hxx> + +namespace com::sun::star::drawing::framework +{ +class XResourceId; +} + +namespace sd::framework +{ +/** This subclass is not necessary anymore. We can remove it if that + remains so. +*/ +class FrameWindowPane : public Pane +{ +public: + FrameWindowPane(const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId, + vcl::Window* pWindow); + virtual ~FrameWindowPane() noexcept override; + + /** A frame window typically can (and should) exists on its own without + children, if only to visualize that something (a view) is missing. + Therefore this method always returns <FALSE/>. + */ + virtual sal_Bool SAL_CALL isAnchorOnly() override; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/FullScreenPane.cxx b/sd/source/ui/framework/factories/FullScreenPane.cxx new file mode 100644 index 000000000..dbf34213f --- /dev/null +++ b/sd/source/ui/framework/factories/FullScreenPane.cxx @@ -0,0 +1,226 @@ +/* -*- 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 "FullScreenPane.hxx" +#include <vcl/vclevent.hxx> +#include <vcl/wrkwin.hxx> +#include <o3tl/string_view.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/util/URL.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +FullScreenPane::FullScreenPane ( + const Reference<XComponentContext>& rxComponentContext, + const Reference<XResourceId>& rxPaneId, + const vcl::Window* pViewShellWindow) + : FrameWindowPane(rxPaneId,nullptr), + mxComponentContext(rxComponentContext) +{ + vcl::Window* pParent = nullptr; + mpWorkWindow.reset(VclPtr<WorkWindow>::Create( + + pParent, + 0)); // For debugging (non-fullscreen) use WB_BORDER | WB_MOVEABLE | WB_SIZEABLE)); + + if ( ! rxPaneId.is()) + throw lang::IllegalArgumentException(); + + sal_Int32 nScreenNumber = 1; + ExtractArguments(rxPaneId, nScreenNumber); + + if (!mpWorkWindow) + return; + + // Create a new top-level window that is displayed full screen. + mpWorkWindow->ShowFullScreenMode(true, nScreenNumber); + // For debugging (non-fullscreen) use mpWorkWindow->SetScreenNumber(nScreenNumber); + mpWorkWindow->SetMenuBarMode(MenuBarMode::Hide); + mpWorkWindow->SetBorderStyle(WindowBorderStyle::REMOVEBORDER); + mpWorkWindow->SetBackground(Wallpaper()); + // Don't show the window right now in order to allow the setting of an + // accessibility object: accessibility objects are typically + // requested by AT-tools when the window is shown. Chaining it + // afterwards may or may not work. + + // Add resize listener at the work window. + Link<VclWindowEvent&,void> aWindowEventHandler (LINK(this, FullScreenPane, WindowEventHandler)); + mpWorkWindow->AddEventListener(aWindowEventHandler); + + // Set title and icon of the new window to those of the current window + // of the view shell. + if (pViewShellWindow != nullptr) + { + const SystemWindow* pSystemWindow = pViewShellWindow->GetSystemWindow(); + mpWorkWindow->SetText(pSystemWindow->GetText()); + mpWorkWindow->SetIcon(pSystemWindow->GetIcon()); + } + + // For some reason the VCL canvas can not paint into a WorkWindow. + // Therefore a child window is created that covers the WorkWindow + // completely. + mpWindow = VclPtr<vcl::Window>::Create(mpWorkWindow.get()); + mpWindow->SetPosSizePixel(Point(0,0), mpWorkWindow->GetSizePixel()); + mpWindow->SetBackground(Wallpaper()); + mxWindow = VCLUnoHelper::GetInterface(mpWindow); + + // Create the canvas. + mxCanvas = CreateCanvas(); + + mpWindow->GrabFocus(); +} + +FullScreenPane::~FullScreenPane() noexcept +{ +} + +void SAL_CALL FullScreenPane::disposing() +{ + mpWindow.disposeAndClear(); + + if (mpWorkWindow) + { + Link<VclWindowEvent&,void> aWindowEventHandler (LINK(this, FullScreenPane, WindowEventHandler)); + mpWorkWindow->RemoveEventListener(aWindowEventHandler); + mpWorkWindow.disposeAndClear(); + } + + FrameWindowPane::disposing(); +} + +//----- XPane ----------------------------------------------------------------- + +sal_Bool SAL_CALL FullScreenPane::isVisible() +{ + ThrowIfDisposed(); + + if (mpWindow != nullptr) + return mpWindow->IsReallyVisible(); + else + return false; +} + +void SAL_CALL FullScreenPane::setVisible (const sal_Bool bIsVisible) +{ + ThrowIfDisposed(); + + if (mpWindow != nullptr) + mpWindow->Show(bIsVisible); + if (mpWorkWindow != nullptr) + mpWorkWindow->Show(bIsVisible); +} + +Reference<css::accessibility::XAccessible> SAL_CALL FullScreenPane::getAccessible() +{ + ThrowIfDisposed(); + + if (mpWorkWindow != nullptr) + return mpWorkWindow->GetAccessible(false); + else + return nullptr; +} + +void SAL_CALL FullScreenPane::setAccessible ( + const Reference<css::accessibility::XAccessible>& rxAccessible) +{ + ThrowIfDisposed(); + + if (mpWindow == nullptr) + return; + + Reference<lang::XInitialization> xInitializable (rxAccessible, UNO_QUERY); + if (xInitializable.is()) + { + vcl::Window* pParentWindow = mpWindow->GetParent(); + Reference<css::accessibility::XAccessible> xAccessibleParent; + if (pParentWindow != nullptr) + xAccessibleParent = pParentWindow->GetAccessible(); + Sequence<Any> aArguments{ Any(xAccessibleParent) }; + xInitializable->initialize(aArguments); + } + GetWindow()->SetAccessible(rxAccessible); +} + +IMPL_LINK(FullScreenPane, WindowEventHandler, VclWindowEvent&, rEvent, void) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowResize: + GetWindow()->SetPosPixel(Point(0,0)); + GetWindow()->SetSizePixel(Size( + mpWorkWindow->GetSizePixel().Width(), + mpWorkWindow->GetSizePixel().Height())); + break; + + case VclEventId::ObjectDying: + mpWorkWindow.disposeAndClear(); + break; + + default: break; + } +} + +Reference<rendering::XCanvas> FullScreenPane::CreateCanvas() +{ + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(mxWindow); + if (!pWindow) + throw RuntimeException(); + + Sequence<Any> aArg{ // common: first any is VCL pointer to window (for VCL canvas) + Any(reinterpret_cast<sal_Int64>(pWindow.get())), + Any(css::awt::Rectangle()), + Any(false), + Any(mxWindow) + }; + + Reference<lang::XMultiServiceFactory> xFactory ( + mxComponentContext->getServiceManager(), UNO_QUERY_THROW); + return Reference<rendering::XCanvas>( + xFactory->createInstanceWithArguments("com.sun.star.rendering.SpriteCanvas.VCL", + aArg), + UNO_QUERY); +} + +void FullScreenPane::ExtractArguments ( + const Reference<XResourceId>& rxPaneId, + sal_Int32& rnScreenNumberReturnValue) +{ + // Extract arguments from the resource URL. + const util::URL aURL = rxPaneId->getFullResourceURL(); + for (sal_Int32 nIndex{ 0 }; nIndex >= 0; ) + { + const std::u16string_view aToken = o3tl::getToken(aURL.Arguments, 0, '&', nIndex); + std::u16string_view sValue; + if (o3tl::starts_with(aToken, u"ScreenNumber=", &sValue)) + { + rnScreenNumberReturnValue = o3tl::toInt32(sValue); + } + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/FullScreenPane.hxx b/sd/source/ui/framework/factories/FullScreenPane.hxx new file mode 100644 index 000000000..b33804ee5 --- /dev/null +++ b/sd/source/ui/framework/factories/FullScreenPane.hxx @@ -0,0 +1,85 @@ +/* -*- 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 "FrameWindowPane.hxx" +#include <tools/link.hxx> +#include <vcl/wrkwin.hxx> + +class VclWindowEvent; + +namespace vcl { class Window; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd::framework { + +/** The full screen pane creates a pane that covers the complete application + window, i.e. that hides menu bar, tool bars, status bars. +*/ +class FullScreenPane + : public FrameWindowPane +{ +public: + /** Create a new full screen pane. + @param rxComponentContext + Used for creating a new canvas. + @param rxPaneId + The resource id of the new pane. + @param pViewShellWindow + The top-level parent of this window is used to obtain title and + icon for the new top-level window. + */ + FullScreenPane ( + const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext, + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId, + const vcl::Window* pViewShellWindow); + virtual ~FullScreenPane() noexcept override; + + virtual void SAL_CALL disposing() override; + + //----- XPane ------------------------------------------------------------- + + virtual sal_Bool SAL_CALL isVisible() override; + + virtual void SAL_CALL setVisible (sal_Bool bIsVisible) override; + + virtual css::uno::Reference<css::accessibility::XAccessible> SAL_CALL getAccessible() override; + + virtual void SAL_CALL setAccessible ( + const css::uno::Reference<css::accessibility::XAccessible>& rxAccessible) override; + + DECL_LINK(WindowEventHandler, VclWindowEvent&, void); + +protected: + virtual css::uno::Reference<css::rendering::XCanvas> + CreateCanvas() override; + +private: + css::uno::Reference<css::uno::XComponentContext> mxComponentContext; + VclPtr<WorkWindow> mpWorkWindow; + + static void ExtractArguments ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId, + sal_Int32& rnScreenNumberReturnValue); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/Pane.cxx b/sd/source/ui/framework/factories/Pane.cxx new file mode 100644 index 000000000..a188f0e11 --- /dev/null +++ b/sd/source/ui/framework/factories/Pane.cxx @@ -0,0 +1,178 @@ +/* -*- 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/Pane.hxx> + +#include <osl/mutex.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/window.hxx> +#include <cppcanvas/vclfactory.hxx> +#include <com/sun/star/rendering/XSpriteCanvas.hpp> +#include <comphelper/servicehelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +Pane::Pane ( + const Reference<XResourceId>& rxPaneId, + vcl::Window* pWindow) + noexcept + : PaneInterfaceBase(m_aMutex), + mxPaneId(rxPaneId), + mpWindow(pWindow), + mxWindow(VCLUnoHelper::GetInterface(pWindow)) +{ +} + +Pane::~Pane() +{ +} + +void Pane::disposing() +{ + mxWindow = nullptr; + mpWindow = nullptr; +} + +vcl::Window* Pane::GetWindow() +{ + if (mxWindow.is()) + return mpWindow; + else + return nullptr; +} + +//----- XPane ----------------------------------------------------------------- + +Reference<awt::XWindow> SAL_CALL Pane::getWindow() +{ + ThrowIfDisposed(); + + return mxWindow; +} + +Reference<rendering::XCanvas> SAL_CALL Pane::getCanvas() +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + if ( ! mxCanvas.is()) + mxCanvas = CreateCanvas(); + + return mxCanvas; +} + +//----- XPane2 ---------------------------------------------------------------- + +sal_Bool SAL_CALL Pane::isVisible() +{ + ThrowIfDisposed(); + + const vcl::Window* pWindow = GetWindow(); + if (pWindow != nullptr) + return pWindow->IsVisible(); + else + return false; +} + +void SAL_CALL Pane::setVisible (sal_Bool bIsVisible) +{ + ThrowIfDisposed(); + + vcl::Window* pWindow = GetWindow(); + if (pWindow != nullptr) + pWindow->Show(bIsVisible); +} + +Reference<css::accessibility::XAccessible> SAL_CALL Pane::getAccessible() +{ + ThrowIfDisposed(); + vcl::Window* pWindow = GetWindow(); + if (pWindow != nullptr) + return pWindow->GetAccessible(false); + else + return nullptr; +} + +void SAL_CALL Pane::setAccessible ( + const Reference<css::accessibility::XAccessible>& rxAccessible) +{ + ThrowIfDisposed(); + vcl::Window* pWindow = GetWindow(); + if (pWindow != nullptr) + pWindow->SetAccessible(rxAccessible); +} + +//----- XResource ------------------------------------------------------------- + +Reference<XResourceId> SAL_CALL Pane::getResourceId() +{ + ThrowIfDisposed(); + + return mxPaneId; +} + +sal_Bool SAL_CALL Pane::isAnchorOnly() +{ + return true; +} + +//----- XUnoTunnel ------------------------------------------------------------ + +const Sequence<sal_Int8>& Pane::getUnoTunnelId() +{ + static const comphelper::UnoIdInit thePaneUnoTunnelId; + return thePaneUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL Pane::getSomething (const Sequence<sal_Int8>& rId) +{ + return comphelper::getSomethingImpl(rId, this); +} + +Reference<rendering::XCanvas> Pane::CreateCanvas() +{ + Reference<rendering::XCanvas> xCanvas; + + if (mpWindow != nullptr) + { + ::cppcanvas::SpriteCanvasSharedPtr pCanvas ( + cppcanvas::VCLFactory::createSpriteCanvas(*mpWindow)); + if (pCanvas) + xCanvas.set(pCanvas->getUNOSpriteCanvas()); + } + + return xCanvas; +} + +void Pane::ThrowIfDisposed() const +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + throw lang::DisposedException ("Pane object has already been disposed", + const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this))); + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/PresentationFactory.cxx b/sd/source/ui/framework/factories/PresentationFactory.cxx new file mode 100644 index 000000000..8cf603809 --- /dev/null +++ b/sd/source/ui/framework/factories/PresentationFactory.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 <framework/PresentationFactory.hxx> + +#include <DrawController.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XView.hpp> +#include <comphelper/servicehelper.hxx> +#include <comphelper/compbase.hxx> +#include <tools/diagnose_ex.h> +#include <slideshow.hxx> + +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; + + +namespace sd::framework { + +namespace { + +typedef comphelper::WeakComponentImplHelper<lang::XInitialization> PresentationFactoryProviderInterfaceBase; + +class PresentationFactoryProvider + : public PresentationFactoryProviderInterfaceBase +{ +public: + PresentationFactoryProvider (); + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence<css::uno::Any>& aArguments) override; +}; + +typedef comphelper::WeakComponentImplHelper<XView> PresentationViewInterfaceBase; + +/** The PresentationView is not an actual view, it is a marker whose + existence in a configuration indicates that a slideshow is running + (in another application window). +*/ +class PresentationView + : public PresentationViewInterfaceBase +{ +public: + explicit PresentationView (const Reference<XResourceId>& rxViewId) + : mxResourceId(rxViewId) {}; + + // XView + + virtual Reference<XResourceId> SAL_CALL getResourceId() override + { return mxResourceId; }; + + virtual sal_Bool SAL_CALL isAnchorOnly() override + { return false; } + +private: + Reference<XResourceId> mxResourceId; +}; + +} // end of anonymous namespace. + +//===== PresentationFactory =================================================== + +constexpr OUStringLiteral gsPresentationViewURL = u"private:resource/view/Presentation"; + +PresentationFactory::PresentationFactory ( + const Reference<frame::XController>& rxController) + : mxController(rxController) +{ +} + +PresentationFactory::~PresentationFactory() +{ +} + +//----- XViewFactory ---------------------------------------------------------- + +Reference<XResource> SAL_CALL PresentationFactory::createResource ( + const Reference<XResourceId>& rxViewId) +{ + ThrowIfDisposed(); + + if (rxViewId.is()) + if ( ! rxViewId->hasAnchor() && rxViewId->getResourceURL() == gsPresentationViewURL) + return new PresentationView(rxViewId); + + return Reference<XResource>(); +} + +void SAL_CALL PresentationFactory::releaseResource ( + const Reference<XResource>&) +{ + ThrowIfDisposed(); + + auto pController = comphelper::getFromUnoTunnel<sd::DrawController>(mxController); + if (pController != nullptr) + { + ViewShellBase* pBase = pController->GetViewShellBase(); + if (pBase != nullptr) + SlideShow::Stop( *pBase ); + } +} + +//===== XConfigurationChangeListener ========================================== + +void SAL_CALL PresentationFactory::notifyConfigurationChange ( + const ConfigurationChangeEvent&) +{} + +//===== lang::XEventListener ================================================== + +void SAL_CALL PresentationFactory::disposing ( + const lang::EventObject&) +{} + +void PresentationFactory::ThrowIfDisposed() const +{ + if (m_bDisposed) + { + throw lang::DisposedException ("PresentationFactory object has already been disposed", + const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this))); + } +} + +namespace { + +//===== PresentationFactoryProvider =========================================== + +PresentationFactoryProvider::PresentationFactoryProvider () +{ +} + +// XInitialization + +void SAL_CALL PresentationFactoryProvider::initialize( + const Sequence<Any>& aArguments) +{ + if (!aArguments.hasElements()) + return; + + try + { + // Get the XController from the first argument. + Reference<frame::XController> xController (aArguments[0], UNO_QUERY_THROW); + Reference<XControllerManager> xCM (xController, UNO_QUERY_THROW); + Reference<XConfigurationController> xCC (xCM->getConfigurationController()); + if (xCC.is()) + xCC->addResourceFactory( + gsPresentationViewURL, + new PresentationFactory(xController)); + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +} // end of anonymous namespace. + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_PresentationFactoryProvider_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::framework::PresentationFactoryProvider); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/ViewShellWrapper.cxx b/sd/source/ui/framework/factories/ViewShellWrapper.cxx new file mode 100644 index 000000000..8f0fcd976 --- /dev/null +++ b/sd/source/ui/framework/factories/ViewShellWrapper.cxx @@ -0,0 +1,252 @@ +/* -*- 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/ViewShellWrapper.hxx> +#include <sdpage.hxx> +#include <ViewShell.hxx> + +#include <SlideSorter.hxx> +#include <SlideSorterViewShell.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlideSorterController.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <model/SlsPageDescriptor.hxx> + +#include <com/sun/star/drawing/framework/XPane.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/servicehelper.hxx> +#include <sal/log.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::com::sun::star::awt::XWindow; + +namespace sd::framework { + +ViewShellWrapper::ViewShellWrapper ( + const std::shared_ptr<ViewShell>& pViewShell, + const Reference<XResourceId>& rxViewId, + const Reference<awt::XWindow>& rxWindow) + : mpViewShell(pViewShell), + mpSlideSorterViewShell( + std::dynamic_pointer_cast< ::sd::slidesorter::SlideSorterViewShell >( pViewShell )), + mxViewId(rxViewId), + mxWindow(rxWindow) +{ +} + +ViewShellWrapper::~ViewShellWrapper() +{ +} + +void ViewShellWrapper::disposing(std::unique_lock<std::mutex>&) +{ + SAL_INFO("sd.ui", "disposing ViewShellWrapper " << this); + Reference<awt::XWindow> xWindow (mxWindow); + if (xWindow.is()) + { + SAL_INFO( + "sd.ui", + "removing ViewShellWrapper " << this << " from window listener at " + << mxWindow.get()); + xWindow->removeWindowListener(this); + } + + mpSlideSorterViewShell.reset(); + mpViewShell.reset(); +} + +uno::Any SAL_CALL ViewShellWrapper::queryInterface( const uno::Type & rType ) +{ + if( mpSlideSorterViewShell && + rType == cppu::UnoType<view::XSelectionSupplier>::get() ) + { + uno::Reference<view::XSelectionSupplier> xSupplier( this ); + return Any(xSupplier); + } + else + return ViewShellWrapperInterfaceBase::queryInterface( rType ); +} + +//----- XResource ------------------------------------------------------------- + +Reference<XResourceId> SAL_CALL ViewShellWrapper::getResourceId() +{ + return mxViewId; +} + +sal_Bool SAL_CALL ViewShellWrapper::isAnchorOnly() +{ + return false; +} + +//----- XSelectionSupplier -------------------------------------------------- + +sal_Bool SAL_CALL ViewShellWrapper::select( const css::uno::Any& aSelection ) +{ + if (!mpSlideSorterViewShell) + return false; + + ::sd::slidesorter::controller::SlideSorterController& rSlideSorterController + = mpSlideSorterViewShell->GetSlideSorter().GetController(); + ::sd::slidesorter::controller::PageSelector& rSelector (rSlideSorterController.GetPageSelector()); + rSelector.DeselectAllPages(); + Sequence<Reference<drawing::XDrawPage> > xPages; + aSelection >>= xPages; + for (const auto& rPage : std::as_const(xPages)) + { + Reference<beans::XPropertySet> xSet (rPage, UNO_QUERY); + if (xSet.is()) + { + try + { + Any aNumber = xSet->getPropertyValue("Number"); + sal_Int32 nPageNumber = 0; + aNumber >>= nPageNumber; + nPageNumber -=1; // Transform 1-based page numbers to 0-based ones. + rSelector.SelectPage(nPageNumber); + } + catch (const RuntimeException&) + { + } + } + } + + return true; +} + +uno::Any SAL_CALL ViewShellWrapper::getSelection() +{ + Any aResult; + + if (!mpSlideSorterViewShell) + return aResult; + + slidesorter::model::PageEnumeration aSelectedPages ( + slidesorter::model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mpSlideSorterViewShell->GetSlideSorter().GetModel())); + int nSelectedPageCount ( + mpSlideSorterViewShell->GetSlideSorter().GetController().GetPageSelector().GetSelectedPageCount()); + + Sequence<Reference<XInterface> > aPages(nSelectedPageCount); + auto aPagesRange = asNonConstRange(aPages); + int nIndex = 0; + while (aSelectedPages.HasMoreElements() && nIndex<nSelectedPageCount) + { + slidesorter::model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + aPagesRange[nIndex++] = pDescriptor->GetPage()->getUnoPage(); + } + aResult <<= aPages; + + return aResult; +} + +void SAL_CALL ViewShellWrapper::addSelectionChangeListener( const uno::Reference< view::XSelectionChangeListener >& ) +{ +} + +void SAL_CALL ViewShellWrapper::removeSelectionChangeListener( const uno::Reference< view::XSelectionChangeListener >& ) +{ +} + +//----- XRelocatableResource -------------------------------------------------- + +sal_Bool SAL_CALL ViewShellWrapper::relocateToAnchor ( + const Reference<XResource>& xResource) +{ + bool bResult (false); + + Reference<XPane> xPane (xResource, UNO_QUERY); + if (xPane.is()) + { + // Detach from the window of the old pane. + Reference<awt::XWindow> xWindow (mxWindow); + if (xWindow.is()) + xWindow->removeWindowListener(this); + mxWindow = nullptr; + + if (mpViewShell != nullptr) + { + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xPane->getWindow()); + if (pWindow && mpViewShell->RelocateToParentWindow(pWindow)) + { + bResult = true; + + // Attach to the window of the new pane. + xWindow = xPane->getWindow(); + if (xWindow.is()) + { + xWindow->addWindowListener(this); + mpViewShell->Resize(); + } + } + } + } + + return bResult; +} + +//----- XUnoTunnel ------------------------------------------------------------ + +const Sequence<sal_Int8>& ViewShellWrapper::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theViewShellWrapperUnoTunnelId; + return theViewShellWrapperUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL ViewShellWrapper::getSomething (const Sequence<sal_Int8>& rId) +{ + return comphelper::getSomethingImpl(rId, this); +} + +//===== awt::XWindowListener ================================================== + +void SAL_CALL ViewShellWrapper::windowResized (const awt::WindowEvent&) +{ + ViewShell* pViewShell (mpViewShell.get()); + if (pViewShell != nullptr) + pViewShell->Resize(); +} + +void SAL_CALL ViewShellWrapper::windowMoved (const awt::WindowEvent&) {} + +void SAL_CALL ViewShellWrapper::windowShown (const lang::EventObject&) +{ + ViewShell* pViewShell (mpViewShell.get()); + if (pViewShell != nullptr) + pViewShell->Resize(); +} + +void SAL_CALL ViewShellWrapper::windowHidden (const lang::EventObject&) {} + +//===== XEventListener ======================================================== + +void SAL_CALL ViewShellWrapper::disposing (const lang::EventObject& rEvent) +{ + if (rEvent.Source == mxWindow) + mxWindow = nullptr; +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/CenterViewFocusModule.cxx b/sd/source/ui/framework/module/CenterViewFocusModule.cxx new file mode 100644 index 000000000..e36f95e33 --- /dev/null +++ b/sd/source/ui/framework/module/CenterViewFocusModule.cxx @@ -0,0 +1,151 @@ +/* -*- 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 "CenterViewFocusModule.hxx" + +#include <framework/FrameworkHelper.hxx> +#include <framework/ViewShellWrapper.hxx> + +#include <DrawController.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellManager.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XConfigurationController.hpp> +#include <comphelper/servicehelper.hxx> + +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 { + +//===== CenterViewFocusModule ==================================================== + +CenterViewFocusModule::CenterViewFocusModule (Reference<frame::XController> const & rxController) + : mbValid(false), + mpBase(nullptr), + mbNewViewCreated(false) +{ + Reference<XControllerManager> xControllerManager (rxController, UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + + // Tunnel through the controller to obtain a ViewShellBase. + auto pController = comphelper::getFromUnoTunnel<sd::DrawController>(rxController); + if (pController != nullptr) + mpBase = pController->GetViewShellBase(); + + // Check, if all required objects do exist. + if (mxConfigurationController.is() && mpBase!=nullptr) + { + mbValid = true; + } + } + + if (mbValid) + { + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateEndEvent, + Any()); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any()); + } +} + +CenterViewFocusModule::~CenterViewFocusModule() +{ +} + +void CenterViewFocusModule::disposing(std::unique_lock<std::mutex>&) +{ + if (mxConfigurationController.is()) + mxConfigurationController->removeConfigurationChangeListener(this); + + mbValid = false; + mxConfigurationController = nullptr; + mpBase = nullptr; +} + +void SAL_CALL CenterViewFocusModule::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (mbValid) + { + if (rEvent.Type == FrameworkHelper::msConfigurationUpdateEndEvent) + { + HandleNewView(rEvent.Configuration); + } + else if (rEvent.Type == FrameworkHelper::msResourceActivationEvent) + { + if (rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix)) + mbNewViewCreated = true; + } + } +} + +void CenterViewFocusModule::HandleNewView ( + const Reference<XConfiguration>& rxConfiguration) +{ + if (!mbNewViewCreated) + return; + + mbNewViewCreated = false; + // Make the center pane the active one. Tunnel through the + // controller to obtain a ViewShell pointer. + + Sequence<Reference<XResourceId> > xViewIds (rxConfiguration->getResources( + FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL), + FrameworkHelper::msViewURLPrefix, + AnchorBindingMode_DIRECT)); + Reference<XView> xView; + if (xViewIds.hasElements()) + xView.set( mxConfigurationController->getResource(xViewIds[0]),UNO_QUERY); + if (mpBase!=nullptr) + { + auto pViewShellWrapper = comphelper::getFromUnoTunnel<ViewShellWrapper>(xView); + if (pViewShellWrapper != nullptr) + { + std::shared_ptr<ViewShell> pViewShell = pViewShellWrapper->GetViewShell(); + if (pViewShell != nullptr) + mpBase->GetViewShellManager()->MoveToTop(*pViewShell); + } + } +} + +void SAL_CALL CenterViewFocusModule::disposing ( + const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is()) + if (rEvent.Source == mxConfigurationController) + { + mbValid = false; + mxConfigurationController = nullptr; + mpBase = nullptr; + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/CenterViewFocusModule.hxx b/sd/source/ui/framework/module/CenterViewFocusModule.hxx new file mode 100644 index 000000000..c6d5d348e --- /dev/null +++ b/sd/source/ui/framework/module/CenterViewFocusModule.hxx @@ -0,0 +1,90 @@ +/* -*- 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/XConfigurationChangeListener.hpp> +#include <comphelper/compbase.hxx> + +namespace com::sun::star::drawing::framework +{ +class XConfigurationController; +} +namespace com::sun::star::frame +{ +class XController; +} + +namespace sd +{ +class ViewShellBase; +} + +namespace sd::framework +{ +typedef comphelper::WeakComponentImplHelper<css::drawing::framework::XConfigurationChangeListener> + CenterViewFocusModuleInterfaceBase; + +/** This module waits for new views to be created for the center pane and + then moves the center view to the top most place on the shell stack. As + we are moving away from the shell stack this module may become obsolete + or has to be modified. +*/ +class CenterViewFocusModule final : public CenterViewFocusModuleInterfaceBase +{ +public: + explicit CenterViewFocusModule( + css::uno::Reference<css::frame::XController> const& rxController); + virtual ~CenterViewFocusModule() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing(const css::lang::EventObject& rEvent) override; + +private: + class ViewShellContainer; + + bool mbValid; + css::uno::Reference<css::drawing::framework::XConfigurationController> + mxConfigurationController; + ViewShellBase* mpBase; + /** This flag indicates whether in the last configuration change cycle a + new view has been created and thus the center view has to be moved + to the top of the shell stack. + */ + bool mbNewViewCreated; + + /** At the end of an update of the current configuration this method + handles a new view in the center pane by moving the associated view + shell to the top of the shell stack. + */ + void HandleNewView( + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/DrawModule.cxx b/sd/source/ui/framework/module/DrawModule.cxx new file mode 100644 index 000000000..17f4671fc --- /dev/null +++ b/sd/source/ui/framework/module/DrawModule.cxx @@ -0,0 +1,41 @@ +/* -*- 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/DrawModule.hxx> + +#include <framework/FrameworkHelper.hxx> +#include "CenterViewFocusModule.hxx" +#include "SlideSorterModule.hxx" +#include "ToolBarModule.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::framework +{ +void DrawModule::Initialize(Reference<frame::XController> const& rxController) +{ + new sd::framework::CenterViewFocusModule(rxController); + new sd::framework::SlideSorterModule(rxController, FrameworkHelper::msLeftDrawPaneURL); + new ToolBarModule(rxController); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ImpressModule.cxx b/sd/source/ui/framework/module/ImpressModule.cxx new file mode 100644 index 000000000..139b250fd --- /dev/null +++ b/sd/source/ui/framework/module/ImpressModule.cxx @@ -0,0 +1,51 @@ +/* -*- 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/ImpressModule.hxx> + +#include <framework/FrameworkHelper.hxx> +#include "ViewTabBarModule.hxx" +#include "CenterViewFocusModule.hxx" +#include "SlideSorterModule.hxx" +#include "ToolBarModule.hxx" +#include "ShellStackGuard.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::framework { + +void ImpressModule::Initialize (Reference<frame::XController> const & rxController) +{ + new CenterViewFocusModule(rxController); + new ViewTabBarModule( + rxController, + FrameworkHelper::CreateResourceId( + FrameworkHelper::msViewTabBarURL, + FrameworkHelper::msCenterPaneURL)); + new SlideSorterModule( + rxController, + FrameworkHelper::msLeftImpressPaneURL); + new ToolBarModule(rxController); + new ShellStackGuard(rxController); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ModuleController.cxx b/sd/source/ui/framework/module/ModuleController.cxx new file mode 100644 index 000000000..acd12ec8a --- /dev/null +++ b/sd/source/ui/framework/module/ModuleController.cxx @@ -0,0 +1,244 @@ +/* -*- 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/ModuleController.hxx> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/container/XNameAccess.hpp> + +#include <tools/ConfigurationAccess.hxx> +#include <comphelper/processfactory.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::tools::ConfigurationAccess; + +namespace sd::framework { + +const sal_uInt32 snFactoryPropertyCount (2); +const sal_uInt32 snStartupPropertyCount (1); + +//===== ModuleController ====================================================== +Reference<XModuleController> ModuleController::CreateInstance ( + const Reference<XComponentContext>& rxContext) +{ + return new ModuleController(rxContext); +} + +ModuleController::ModuleController (const Reference<XComponentContext>& rxContext) +{ + /** Load a list of URL to service mappings from the + /org.openoffice.Office.Impress/MultiPaneGUI/Framework/ResourceFactories + configuration entry. The mappings are stored in the + mpResourceToFactoryMap member. + */ + try + { + ConfigurationAccess aConfiguration ( + rxContext, + "/org.openoffice.Office.Impress/", + ConfigurationAccess::READ_ONLY); + Reference<container::XNameAccess> xFactories ( + aConfiguration.GetConfigurationNode("MultiPaneGUI/Framework/ResourceFactories"), + UNO_QUERY); + ::std::vector<OUString> aProperties (snFactoryPropertyCount); + aProperties[0] = "ServiceName"; + aProperties[1] = "ResourceList"; + ConfigurationAccess::ForAll( + xFactories, + aProperties, + [this] (OUString const&, ::std::vector<Any> const& xs) { + return this->ProcessFactory(xs); + } ); + } + catch (Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +ModuleController::~ModuleController() noexcept +{ +} + +void ModuleController::disposing(std::unique_lock<std::mutex>&) +{ + // Break the cyclic reference back to DrawController object + maLoadedFactories.clear(); + maResourceToFactoryMap.clear(); + mxController.clear(); +} + +void ModuleController::ProcessFactory (const ::std::vector<Any>& rValues) +{ + OSL_ASSERT(rValues.size() == snFactoryPropertyCount); + + // Get the service name of the factory. + OUString sServiceName; + rValues[0] >>= sServiceName; + + // Get all resource URLs that are created by the factory. + Reference<container::XNameAccess> xResources (rValues[1], UNO_QUERY); + ::std::vector<OUString> aURLs; + tools::ConfigurationAccess::FillList( + xResources, + "URL", + aURLs); + + SAL_INFO("sd.fwk", __func__ << ": ModuleController::adding factory " << sServiceName); + + // Add the resource URLs to the map. + for (const auto& rResource : aURLs) + { + maResourceToFactoryMap[rResource] = sServiceName; + SAL_INFO("sd.fwk", __func__ << ": " << rResource); + } +} + +void ModuleController::InstantiateStartupServices() +{ + try + { + tools::ConfigurationAccess aConfiguration ( + "/org.openoffice.Office.Impress/", + tools::ConfigurationAccess::READ_ONLY); + Reference<container::XNameAccess> xFactories ( + aConfiguration.GetConfigurationNode("MultiPaneGUI/Framework/StartupServices"), + UNO_QUERY); + ::std::vector<OUString> aProperties (snStartupPropertyCount); + aProperties[0] = "ServiceName"; + tools::ConfigurationAccess::ForAll( + xFactories, + aProperties, + [this] (OUString const&, ::std::vector<Any> const& xs) { + return this->ProcessStartupService(xs); + } ); + } + catch (Exception&) + { + SAL_WARN("sd.fwk", "ERROR in ModuleController::InstantiateStartupServices"); + } +} + +void ModuleController::ProcessStartupService (const ::std::vector<Any>& rValues) +{ + OSL_ASSERT(rValues.size() == snStartupPropertyCount); + + try + { + // Get the service name of the startup service. + OUString sServiceName; + rValues[0] >>= sServiceName; + + // Instantiate service. + Reference<uno::XComponentContext> xContext = + ::comphelper::getProcessComponentContext(); + + // Create the startup service. + Sequence<Any> aArguments{ Any(mxController) }; + // Note that when the new object will be destroyed at the end of + // this scope when it does not register itself anywhere. + // Typically it will add itself as ConfigurationChangeListener + // at the configuration controller. + xContext->getServiceManager()->createInstanceWithArgumentsAndContext(sServiceName, aArguments, xContext); + + SAL_INFO("sd.fwk", __func__ << ": ModuleController::created startup service " << sServiceName); + } + catch (Exception&) + { + SAL_WARN("sd.fwk", "ERROR in ModuleController::ProcessStartupServices"); + } +} + +//----- XModuleController ----------------------------------------------------- + +void SAL_CALL ModuleController::requestResource (const OUString& rsResourceURL) +{ + auto iFactory = maResourceToFactoryMap.find(rsResourceURL); + if (iFactory == maResourceToFactoryMap.end()) + return; + + // Check that the factory has already been loaded and not been + // destroyed in the meantime. + Reference<XInterface> xFactory; + auto iLoadedFactory = maLoadedFactories.find(iFactory->second); + if (iLoadedFactory != maLoadedFactories.end()) + xFactory.set(iLoadedFactory->second, UNO_QUERY); + if ( xFactory.is()) + return; + + // Create a new instance of the factory. + Reference<uno::XComponentContext> xContext = + ::comphelper::getProcessComponentContext(); + + // Create the factory service. + Sequence<Any> aArguments{ Any(mxController) }; + try + { + xFactory = xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + iFactory->second, + aArguments, + xContext); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sd.fwk", "caught exception while creating factory"); + } + + // Remember that this factory has been instanced. + maLoadedFactories[iFactory->second] = xFactory; +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL ModuleController::initialize (const Sequence<Any>& aArguments) +{ + if (aArguments.hasElements()) + { + try + { + // Get the XController from the first argument. + mxController.set(aArguments[0], UNO_QUERY_THROW); + + InstantiateStartupServices(); + } + catch (RuntimeException&) + {} + } +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_module_ModuleController_get_implementation( + css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + css::uno::Reference< css::uno::XInterface > xModCont ( sd::framework::ModuleController::CreateInstance(context) ); + xModCont->acquire(); + return xModCont.get(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/PresentationModule.cxx b/sd/source/ui/framework/module/PresentationModule.cxx new file mode 100644 index 000000000..fb0ac0558 --- /dev/null +++ b/sd/source/ui/framework/module/PresentationModule.cxx @@ -0,0 +1,36 @@ +/* -*- 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/PresentationModule.hxx> + +#include "CenterViewFocusModule.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::framework +{ +void PresentationModule::Initialize(Reference<frame::XController> const& rxController) +{ + new sd::framework::CenterViewFocusModule(rxController); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ShellStackGuard.cxx b/sd/source/ui/framework/module/ShellStackGuard.cxx new file mode 100644 index 000000000..83d73b055 --- /dev/null +++ b/sd/source/ui/framework/module/ShellStackGuard.cxx @@ -0,0 +1,150 @@ +/* -*- 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 "ShellStackGuard.hxx" + +#include <framework/ConfigurationController.hxx> +#include <framework/FrameworkHelper.hxx> + +#include <DrawController.hxx> +#include <ViewShellBase.hxx> +#include <sfx2/printer.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <comphelper/servicehelper.hxx> + +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 { + +//===== CenterViewFocusModule ==================================================== + +ShellStackGuard::ShellStackGuard (Reference<frame::XController> const & rxController) + : mpBase(nullptr), + maPrinterPollingIdle("sd ShellStackGuard PrinterPollingIdle") +{ + Reference<XControllerManager> xControllerManager (rxController, UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + + // Tunnel through the controller to obtain a ViewShellBase. + auto pController = comphelper::getFromUnoTunnel<sd::DrawController>(rxController); + if (pController != nullptr) + mpBase = pController->GetViewShellBase(); + } + + if (mxConfigurationController.is()) + { + // Listen for update starts so that the following update can be + // prevented in case of a printing printer. + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateStartEvent, + Any()); + + // Prepare the printer polling. + maPrinterPollingIdle.SetInvokeHandler(LINK(this,ShellStackGuard,TimeoutHandler)); + } +} + +ShellStackGuard::~ShellStackGuard() +{ +} + +void ShellStackGuard::disposing(std::unique_lock<std::mutex>&) +{ + if (mxConfigurationController) + { + mxConfigurationController->removeConfigurationChangeListener(this); + mxConfigurationController = nullptr; + } + mpBase = nullptr; +} + +void SAL_CALL ShellStackGuard::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (rEvent.Type == FrameworkHelper::msConfigurationUpdateStartEvent) + { + if (mpUpdateLock == nullptr && IsPrinting()) + { + // Prevent configuration updates while the printer is printing. + mpUpdateLock.reset(new ConfigurationController::Lock(mxConfigurationController)); + + // Start polling for the printer having finished printing. + maPrinterPollingIdle.Start(); + } + } +} + +void SAL_CALL ShellStackGuard::disposing ( + const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is()) + if (rEvent.Source == mxConfigurationController) + { + mxConfigurationController = nullptr; + mpBase = nullptr; + } +} + +IMPL_LINK(ShellStackGuard, TimeoutHandler, Timer*, pIdle, void) +{ +#ifdef DEBUG + OSL_ASSERT(pIdle==&maPrinterPollingIdle); +#else + (void)pIdle; +#endif + if (mpUpdateLock == nullptr) + return; + + if ( ! IsPrinting()) + { + // Printing finished. Release the update lock. + mpUpdateLock.reset(); + } + else + { + // Wait long for the printing to finish. + maPrinterPollingIdle.Start(); + } +} + +bool ShellStackGuard::IsPrinting() const +{ + if (mpBase != nullptr) + { + SfxPrinter* pPrinter = mpBase->GetPrinter(); + if (pPrinter != nullptr + && pPrinter->IsPrinting()) + { + return true; + } + } + + return false; +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ShellStackGuard.hxx b/sd/source/ui/framework/module/ShellStackGuard.hxx new file mode 100644 index 000000000..72b7ed2c6 --- /dev/null +++ b/sd/source/ui/framework/module/ShellStackGuard.hxx @@ -0,0 +1,94 @@ +/* -*- 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 <framework/ConfigurationController.hxx> + +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> + +#include <vcl/idle.hxx> +#include <comphelper/compbase.hxx> +#include <memory> + +namespace com::sun::star::drawing::framework +{ +class XConfigurationController; +} +namespace com::sun::star::frame +{ +class XController; +} + +namespace sd +{ +class ViewShellBase; +} + +namespace sd::framework +{ +typedef comphelper::WeakComponentImplHelper<css::drawing::framework::XConfigurationChangeListener> + ShellStackGuardInterfaceBase; + +/** This module locks updates of the current configuration in situations + when the shell stack must not be modified. + + On every start of a configuration update the ShellStackGuard checks the + printer. If it is printing the configuration update is locked. It then + polls the printer and unlocks updates when printing finishes. + + When in the future there are no resources left that use shells then this + module can be removed. +*/ +class ShellStackGuard : public ShellStackGuardInterfaceBase +{ +public: + explicit ShellStackGuard(css::uno::Reference<css::frame::XController> const& rxController); + virtual ~ShellStackGuard() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing(const css::lang::EventObject& rEvent) override; + +private: + css::uno::Reference<css::drawing::framework::XConfigurationController> + mxConfigurationController; + ViewShellBase* mpBase; + std::unique_ptr<ConfigurationController::Lock> mpUpdateLock; + Idle maPrinterPollingIdle; + + DECL_LINK(TimeoutHandler, Timer*, void); + + /** Return <TRUE/> when the printer is printing. Return <FALSE/> when + the printer is not printing, or there is no printer, or something + else went wrong. + */ + bool IsPrinting() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/SlideSorterModule.cxx b/sd/source/ui/framework/module/SlideSorterModule.cxx new file mode 100644 index 000000000..dbe30f0d3 --- /dev/null +++ b/sd/source/ui/framework/module/SlideSorterModule.cxx @@ -0,0 +1,313 @@ +/* -*- 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 "SlideSorterModule.hxx" + +#include <framework/FrameworkHelper.hxx> +#include <framework/ConfigurationController.hxx> +#include <com/sun/star/drawing/framework/XTabBar.hpp> +#include <com/sun/star/drawing/framework/TabBarButton.hpp> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/frame/XController.hpp> + +#include <strings.hrc> +#include <sdresid.hxx> +#include <svtools/slidesorterbaropt.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace { + const sal_Int32 ResourceActivationRequestEvent = 0; + const sal_Int32 ResourceDeactivationRequestEvent = 1; +} + +namespace sd::framework { + +//===== SlideSorterModule ================================================== + +SlideSorterModule::SlideSorterModule ( + const Reference<frame::XController>& rxController, + const OUString& rsLeftPaneURL) + : mxResourceId(FrameworkHelper::CreateResourceId(FrameworkHelper::msSlideSorterURL, rsLeftPaneURL)), + mxMainViewAnchorId(FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL)), + mxViewTabBarId(FrameworkHelper::CreateResourceId( + FrameworkHelper::msViewTabBarURL, + FrameworkHelper::msCenterPaneURL)), + mxControllerManager(rxController,UNO_QUERY) +{ + Reference<XControllerManager> xControllerManager (rxController, UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + + if (mxConfigurationController.is()) + { + uno::Reference<lang::XComponent> const xComppnent( + mxConfigurationController, UNO_QUERY_THROW); + xComppnent->addEventListener(this); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationRequestEvent, + Any(ResourceActivationRequestEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceDeactivationRequestEvent, + Any(ResourceDeactivationRequestEvent)); + } + } + if (!mxConfigurationController.is()) + return; + + UpdateViewTabBar(nullptr); + + if (SvtSlideSorterBarOptions().GetVisibleImpressView()) + AddActiveMainView(FrameworkHelper::msImpressViewURL); + if (SvtSlideSorterBarOptions().GetVisibleOutlineView()) + AddActiveMainView(FrameworkHelper::msOutlineViewURL); + if (SvtSlideSorterBarOptions().GetVisibleNotesView()) + AddActiveMainView(FrameworkHelper::msNotesViewURL); + if (SvtSlideSorterBarOptions().GetVisibleHandoutView()) + AddActiveMainView(FrameworkHelper::msHandoutViewURL); + if (SvtSlideSorterBarOptions().GetVisibleSlideSorterView()) + AddActiveMainView(FrameworkHelper::msSlideSorterURL); + if (SvtSlideSorterBarOptions().GetVisibleDrawView()) + AddActiveMainView(FrameworkHelper::msDrawViewURL); + + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any()); +} + +SlideSorterModule::~SlideSorterModule() +{ +} + +void SlideSorterModule::SaveResourceState() +{ + SvtSlideSorterBarOptions().SetVisibleImpressView(IsResourceActive(FrameworkHelper::msImpressViewURL)); + SvtSlideSorterBarOptions().SetVisibleOutlineView(IsResourceActive(FrameworkHelper::msOutlineViewURL)); + SvtSlideSorterBarOptions().SetVisibleNotesView(IsResourceActive(FrameworkHelper::msNotesViewURL)); + SvtSlideSorterBarOptions().SetVisibleHandoutView(IsResourceActive(FrameworkHelper::msHandoutViewURL)); + SvtSlideSorterBarOptions().SetVisibleSlideSorterView(IsResourceActive(FrameworkHelper::msSlideSorterURL)); + SvtSlideSorterBarOptions().SetVisibleDrawView(IsResourceActive(FrameworkHelper::msDrawViewURL)); +} + +void SAL_CALL SlideSorterModule::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (rEvent.Type == FrameworkHelper::msResourceActivationEvent) + { + if (rEvent.ResourceId->compareTo(mxViewTabBarId) == 0) + { + // Update the view tab bar because the view tab bar has just + // become active. + UpdateViewTabBar(Reference<XTabBar>(rEvent.ResourceObject,UNO_QUERY)); + } + else if (rEvent.ResourceId->getResourceTypePrefix() == + FrameworkHelper::msViewURLPrefix + && rEvent.ResourceId->isBoundTo( + FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL), + AnchorBindingMode_DIRECT)) + { + // Update the view tab bar because the view in the center pane + // has changed. + UpdateViewTabBar(nullptr); + } + return; + } + + OSL_ASSERT(rEvent.ResourceId.is()); + sal_Int32 nEventType = 0; + rEvent.UserData >>= nEventType; + switch (nEventType) + { + case ResourceActivationRequestEvent: + if (rEvent.ResourceId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, + AnchorBindingMode_DIRECT)) + { + // A resource directly bound to the center pane has been + // requested. + if (rEvent.ResourceId->getResourceTypePrefix() == + FrameworkHelper::msViewURLPrefix) + { + // The requested resource is a view. Show or hide the + // resource managed by this ResourceManager accordingly. + HandleMainViewSwitch( + rEvent.ResourceId->getResourceURL(), + true); + } + } + else if (rEvent.ResourceId->compareTo(mxResourceId) == 0) + { + // The resource managed by this ResourceManager has been + // explicitly been requested (maybe by us). Remember this + // setting. + HandleResourceRequest(true, rEvent.Configuration); + } + break; + + case ResourceDeactivationRequestEvent: + if (rEvent.ResourceId->compareTo(mxMainViewAnchorId) == 0) + { + HandleMainViewSwitch( + OUString(), + false); + } + else if (rEvent.ResourceId->compareTo(mxResourceId) == 0) + { + // The resource managed by this ResourceManager has been + // explicitly been requested to be hidden (maybe by us). + // Remember this setting. + HandleResourceRequest(false, rEvent.Configuration); + } + break; + } +} + +void SlideSorterModule::UpdateViewTabBar (const Reference<XTabBar>& rxTabBar) +{ + if ( ! mxControllerManager.is()) + return; + + Reference<XTabBar> xBar (rxTabBar); + if ( ! xBar.is()) + { + Reference<XConfigurationController> xCC ( + mxControllerManager->getConfigurationController()); + if (xCC.is()) + xBar.set(xCC->getResource(mxViewTabBarId), UNO_QUERY); + } + + if (!xBar.is()) + return; + + TabBarButton aButtonA; + aButtonA.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msSlideSorterURL, + FrameworkHelper::msCenterPaneURL); + aButtonA.ButtonLabel = SdResId(STR_SLIDE_SORTER_MODE); + + TabBarButton aButtonB; + aButtonB.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msHandoutViewURL, + FrameworkHelper::msCenterPaneURL); + + if ( ! xBar->hasTabBarButton(aButtonA)) + xBar->addTabBarButtonAfter(aButtonA, aButtonB); +} + +void SlideSorterModule::AddActiveMainView ( + const OUString& rsMainViewURL) +{ + maActiveMainViewContainer.insert(rsMainViewURL); +} + +bool SlideSorterModule::IsResourceActive ( + const OUString& rsMainViewURL) +{ + return (maActiveMainViewContainer.find(rsMainViewURL) != maActiveMainViewContainer.end()); +} + +void SlideSorterModule::disposing(std::unique_lock<std::mutex>&) +{ + if (mxConfigurationController.is()) + { + uno::Reference<lang::XComponent> const xComponent(mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(this); + + mxConfigurationController->removeConfigurationChangeListener(this); + mxConfigurationController = nullptr; + } +} + +void SlideSorterModule::HandleMainViewSwitch ( + const OUString& rsViewURL, + const bool bIsActivated) +{ + if (bIsActivated) + msCurrentMainViewURL = rsViewURL; + else + msCurrentMainViewURL.clear(); + + if (!mxConfigurationController.is()) + return; + + ConfigurationController::Lock aLock (mxConfigurationController); + + if (maActiveMainViewContainer.find(msCurrentMainViewURL) + != maActiveMainViewContainer.end()) + { + // Activate resource. + mxConfigurationController->requestResourceActivation( + mxResourceId->getAnchor(), + ResourceActivationMode_ADD); + mxConfigurationController->requestResourceActivation( + mxResourceId, + ResourceActivationMode_REPLACE); + } + else + { + mxConfigurationController->requestResourceDeactivation(mxResourceId); + } +} + +void SlideSorterModule::HandleResourceRequest( + bool bActivation, + const Reference<XConfiguration>& rxConfiguration) +{ + Sequence<Reference<XResourceId> > aCenterViews = rxConfiguration->getResources( + FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL), + FrameworkHelper::msViewURLPrefix, + AnchorBindingMode_DIRECT); + if (aCenterViews.getLength() == 1) + { + if (bActivation) + { + maActiveMainViewContainer.insert(aCenterViews[0]->getResourceURL()); + } + else + { + maActiveMainViewContainer.erase(aCenterViews[0]->getResourceURL()); + } + } +} + +void SAL_CALL SlideSorterModule::disposing ( + const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is() + && rEvent.Source == mxConfigurationController) + { + SaveResourceState(); + // Without the configuration controller this class can do nothing. + mxConfigurationController = nullptr; + dispose(); + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/SlideSorterModule.hxx b/sd/source/ui/framework/module/SlideSorterModule.hxx new file mode 100644 index 000000000..bec9f5c3c --- /dev/null +++ b/sd/source/ui/framework/module/SlideSorterModule.hxx @@ -0,0 +1,97 @@ +/* -*- 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/XConfigurationChangeListener.hpp> +#include <comphelper/compbase.hxx> +#include <memory> +#include <set> + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XControllerManager; } +namespace com::sun::star::drawing::framework { class XTabBar; } +namespace com::sun::star::frame { class XController; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeListener + > SlideSorterModuleBase; + +/** This module is responsible for showing the slide sorter bar and the + slide sorter view in the center pane. + + Manage the activation state of one resource depending on the view in the + center pane. The ResourceManager remembers in which configuration to + activate and in which to deactivate the resource. When the resource is + deactivated or activated manually by the user then the ResourceManager + detects this and remembers it for the future. +*/ +class SlideSorterModule final + : public SlideSorterModuleBase +{ +public: + SlideSorterModule ( + const css::uno::Reference<css::frame::XController>& rxController, + const OUString& rsLeftPaneURL); + virtual ~SlideSorterModule() override; + + /** Remember the given URL as one of a center pane view for which to + activate the resource managed by the called object. + */ + void AddActiveMainView (const OUString& rsMainViewURL); + bool IsResourceActive (const OUString& rsMainViewURL); + void SaveResourceState(); + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XConfigurationChangeListener + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEvent) override; + +private: + css::uno::Reference<css::drawing::framework::XConfigurationController> + mxConfigurationController; + ::std::set<OUString> maActiveMainViewContainer; + /// The resource managed by this class. + css::uno::Reference<css::drawing::framework::XResourceId> mxResourceId; + /// The anchor of the main view. + css::uno::Reference<css::drawing::framework::XResourceId> mxMainViewAnchorId; + OUString msCurrentMainViewURL; + css::uno::Reference<css::drawing::framework::XResourceId> mxViewTabBarId; + css::uno::Reference<css::drawing::framework::XControllerManager> mxControllerManager; + + void HandleMainViewSwitch ( + const OUString& rsViewURL, + const bool bIsActivated); + void HandleResourceRequest( + bool bActivation, + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration); + void UpdateViewTabBar ( + const css::uno::Reference<css::drawing::framework::XTabBar>& rxViewTabBar); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ToolBarModule.cxx b/sd/source/ui/framework/module/ToolBarModule.cxx new file mode 100644 index 000000000..3cecf7b03 --- /dev/null +++ b/sd/source/ui/framework/module/ToolBarModule.cxx @@ -0,0 +1,191 @@ +/* -*- 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 "ToolBarModule.hxx" +#include <ViewShellBase.hxx> +#include <DrawController.hxx> +#include <comphelper/servicehelper.hxx> +#include <framework/FrameworkHelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace { + const sal_Int32 gnConfigurationUpdateStartEvent(0); + const sal_Int32 gnConfigurationUpdateEndEvent(1); + const sal_Int32 gnResourceActivationRequestEvent(2); + const sal_Int32 gnResourceDeactivationRequestEvent(3); +} + +namespace sd::framework { + +//===== ToolBarModule ========================================================= + +ToolBarModule::ToolBarModule ( + const Reference<frame::XController>& rxController) + : mpBase(nullptr), + mbMainViewSwitchUpdatePending(false) +{ + // Tunnel through the controller to obtain a ViewShellBase. + auto pController = comphelper::getFromUnoTunnel<sd::DrawController>(rxController); + if (pController != nullptr) + mpBase = pController->GetViewShellBase(); + + Reference<XControllerManager> xControllerManager (rxController, UNO_QUERY); + if (!xControllerManager.is()) + return; + + mxConfigurationController = xControllerManager->getConfigurationController(); + if (!mxConfigurationController.is()) + return; + + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateStartEvent, + Any(gnConfigurationUpdateStartEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateEndEvent, + Any(gnConfigurationUpdateEndEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationRequestEvent, + Any(gnResourceActivationRequestEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceDeactivationRequestEvent, + Any(gnResourceDeactivationRequestEvent)); +} + +ToolBarModule::~ToolBarModule() +{ +} + +void ToolBarModule::disposing(std::unique_lock<std::mutex>&) +{ + if (mxConfigurationController.is()) + { + mxConfigurationController->removeConfigurationChangeListener(this); + mxConfigurationController = nullptr; + } +} + +void SAL_CALL ToolBarModule::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (!mxConfigurationController.is()) + return; + + sal_Int32 nEventType = 0; + rEvent.UserData >>= nEventType; + switch (nEventType) + { + case gnConfigurationUpdateStartEvent: + HandleUpdateStart(); + break; + + case gnConfigurationUpdateEndEvent: + HandleUpdateEnd(); + break; + + case gnResourceActivationRequestEvent: + case gnResourceDeactivationRequestEvent: + // Remember the request for the activation or deactivation + // of the center pane view. When that happens then on end + // of the next configuration update the set of visible tool + // bars will be updated. + if ( ! mbMainViewSwitchUpdatePending) + if (rEvent.ResourceId->getResourceURL().match( + FrameworkHelper::msViewURLPrefix) + && rEvent.ResourceId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + mbMainViewSwitchUpdatePending = true; + } + break; + } +} + +void ToolBarModule::HandleUpdateStart() +{ + // Lock the ToolBarManager and tell it to lock the ViewShellManager as + // well. This way the ToolBarManager can optimize the releasing of + // locks and arranging of updates of both tool bars and the view shell + // stack. + if (mpBase != nullptr) + { + std::shared_ptr<ToolBarManager> pToolBarManager (mpBase->GetToolBarManager()); + mpToolBarManagerLock.reset(new ToolBarManager::UpdateLock(pToolBarManager)); + pToolBarManager->LockViewShellManager(); + } +} + +void ToolBarModule::HandleUpdateEnd() +{ + if (mbMainViewSwitchUpdatePending) + { + mbMainViewSwitchUpdatePending = false; + // Update the set of visible tool bars and deactivate those that are + // no longer visible. This is done before the old view shell is + // destroyed in order to avoid unnecessary updates of those tool + // bars. + std::shared_ptr<ToolBarManager> pToolBarManager (mpBase->GetToolBarManager()); + std::shared_ptr<FrameworkHelper> pFrameworkHelper ( + FrameworkHelper::Instance(*mpBase)); + ViewShell* pViewShell + = pFrameworkHelper->GetViewShell(FrameworkHelper::msCenterPaneURL).get(); + if (pViewShell != nullptr) + { + pToolBarManager->MainViewShellChanged(*pViewShell); + pToolBarManager->SelectionHasChanged( + *pViewShell, + *pViewShell->GetView()); + pToolBarManager->PreUpdate(); + } + else + { + pToolBarManager->MainViewShellChanged(); + pToolBarManager->PreUpdate(); + } + } + + // Releasing the update lock of the ToolBarManager will let the + // ToolBarManager with the help of the ViewShellManager take care of + // updating tool bars and view shell with the minimal amount of + // shell stack modifications and tool bar updates. + mpToolBarManagerLock.reset(); +} + +void SAL_CALL ToolBarModule::disposing (const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is() + && rEvent.Source == mxConfigurationController) + { + // Without the configuration controller this class can do nothing. + mxConfigurationController = nullptr; + dispose(); + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ToolBarModule.hxx b/sd/source/ui/framework/module/ToolBarModule.hxx new file mode 100644 index 000000000..f9189657d --- /dev/null +++ b/sd/source/ui/framework/module/ToolBarModule.hxx @@ -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 . + */ + +#pragma once + +#include <ToolBarManager.hxx> +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> +#include <comphelper/compbase.hxx> +#include <o3tl/deleter.hxx> +#include <memory> + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::frame { class XController; } + +namespace sd { +class ViewShellBase; +} + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeListener + > ToolBarModuleInterfaceBase; + +/** This module is responsible for locking the ToolBarManager during + configuration updates and for triggering ToolBarManager updates. +*/ +class ToolBarModule final + : public ToolBarModuleInterfaceBase +{ +public: + /** Create a new module. + @param rxController + This is the access point to the drawing framework. + */ + explicit ToolBarModule ( + const css::uno::Reference<css::frame::XController>& rxController); + virtual ~ToolBarModule() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEvent) override; + +private: + css::uno::Reference< + css::drawing::framework::XConfigurationController> mxConfigurationController; + ViewShellBase* mpBase; + std::unique_ptr<ToolBarManager::UpdateLock, o3tl::default_delete<ToolBarManager::UpdateLock>> mpToolBarManagerLock; + bool mbMainViewSwitchUpdatePending; + + void HandleUpdateStart(); + void HandleUpdateEnd(); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ViewTabBarModule.cxx b/sd/source/ui/framework/module/ViewTabBarModule.cxx new file mode 100644 index 000000000..4f5dd4828 --- /dev/null +++ b/sd/source/ui/framework/module/ViewTabBarModule.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 "ViewTabBarModule.hxx" + +#include <framework/FrameworkHelper.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XTabBar.hpp> +#include <com/sun/star/frame/XController.hpp> + +#include <strings.hrc> +#include <sdresid.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace { + +const sal_Int32 ResourceActivationRequestEvent = 0; +const sal_Int32 ResourceDeactivationRequestEvent = 1; +const sal_Int32 ResourceActivationEvent = 2; + +} + +namespace sd::framework { + +//===== ViewTabBarModule ================================================== + +ViewTabBarModule::ViewTabBarModule ( + const Reference<frame::XController>& rxController, + const Reference<XResourceId>& rxViewTabBarId) + : mxViewTabBarId(rxViewTabBarId) +{ + Reference<XControllerManager> xControllerManager (rxController, UNO_QUERY); + + if (!xControllerManager.is()) + return; + + mxConfigurationController = xControllerManager->getConfigurationController(); + if (!mxConfigurationController.is()) + return; + + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationRequestEvent, + Any(ResourceActivationRequestEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceDeactivationRequestEvent, + Any(ResourceDeactivationRequestEvent)); + + UpdateViewTabBar(nullptr); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any(ResourceActivationEvent)); +} + +ViewTabBarModule::~ViewTabBarModule() +{ +} + +void ViewTabBarModule::disposing(std::unique_lock<std::mutex>&) +{ + if (mxConfigurationController.is()) + { + mxConfigurationController->removeConfigurationChangeListener(this); + mxConfigurationController = nullptr; + } +} + +void SAL_CALL ViewTabBarModule::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (!mxConfigurationController.is()) + return; + + sal_Int32 nEventType = 0; + rEvent.UserData >>= nEventType; + switch (nEventType) + { + case ResourceActivationRequestEvent: + if (mxViewTabBarId->isBoundTo(rEvent.ResourceId, AnchorBindingMode_DIRECT)) + { + mxConfigurationController->requestResourceActivation( + mxViewTabBarId, + ResourceActivationMode_ADD); + } + break; + + case ResourceDeactivationRequestEvent: + if (mxViewTabBarId->isBoundTo(rEvent.ResourceId, AnchorBindingMode_DIRECT)) + { + mxConfigurationController->requestResourceDeactivation(mxViewTabBarId); + } + break; + + case ResourceActivationEvent: + if (rEvent.ResourceId->compareTo(mxViewTabBarId) == 0) + { + UpdateViewTabBar(Reference<XTabBar>(rEvent.ResourceObject,UNO_QUERY)); + } + } +} + +void SAL_CALL ViewTabBarModule::disposing ( + const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is() + && rEvent.Source == mxConfigurationController) + { + // Without the configuration controller this class can do nothing. + mxConfigurationController = nullptr; + dispose(); + } +} + +void ViewTabBarModule::UpdateViewTabBar (const Reference<XTabBar>& rxTabBar) +{ + if (!mxConfigurationController.is()) + return; + + Reference<XTabBar> xBar (rxTabBar); + if ( ! xBar.is()) + xBar.set( mxConfigurationController->getResource(mxViewTabBarId), UNO_QUERY); + + if (!xBar.is()) + return; + + TabBarButton aEmptyButton; + + Reference<XResourceId> xAnchor (mxViewTabBarId->getAnchor()); + + TabBarButton aImpressViewButton; + aImpressViewButton.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msImpressViewURL, + xAnchor); + aImpressViewButton.ButtonLabel = SdResId(STR_NORMAL_MODE); + if ( ! xBar->hasTabBarButton(aImpressViewButton)) + xBar->addTabBarButtonAfter(aImpressViewButton, aEmptyButton); + + TabBarButton aOutlineViewButton; + aOutlineViewButton.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msOutlineViewURL, + xAnchor); + aOutlineViewButton.ButtonLabel = SdResId(STR_OUTLINE_MODE); + if ( ! xBar->hasTabBarButton(aOutlineViewButton)) + xBar->addTabBarButtonAfter(aOutlineViewButton, aImpressViewButton); + + TabBarButton aNotesViewButton; + aNotesViewButton.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msNotesViewURL, + xAnchor); + aNotesViewButton.ButtonLabel = SdResId(STR_NOTES_MODE); + if ( ! xBar->hasTabBarButton(aNotesViewButton)) + xBar->addTabBarButtonAfter(aNotesViewButton, aOutlineViewButton); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ViewTabBarModule.hxx b/sd/source/ui/framework/module/ViewTabBarModule.hxx new file mode 100644 index 000000000..bfb252b8d --- /dev/null +++ b/sd/source/ui/framework/module/ViewTabBarModule.hxx @@ -0,0 +1,83 @@ +/* -*- 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/XConfigurationChangeListener.hpp> +#include <comphelper/compbase.hxx> + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XTabBar; } +namespace com::sun::star::frame { class XController; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeListener + > ViewTabBarModuleInterfaceBase; + +/** This module is responsible for showing the ViewTabBar above the view in + the center pane. +*/ +class ViewTabBarModule + : public ViewTabBarModuleInterfaceBase +{ +public: + /** Create a new module that controls the view tab bar above the view + in the specified pane. + @param rxController + This is the access point to the drawing framework. + @param rxViewTabBarId + This ResourceId specifies which tab bar is to be managed by the + new module. + */ + ViewTabBarModule ( + const css::uno::Reference<css::frame::XController>& rxController, + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxViewTabBarId); + virtual ~ViewTabBarModule() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEvent) override; + +private: + css::uno::Reference< + css::drawing::framework::XConfigurationController> mxConfigurationController; + css::uno::Reference<css::drawing::framework::XResourceId> mxViewTabBarId; + + /** This is the place where the view tab bar is filled. Only missing + buttons are added, so it is safe to call this method multiple + times. + */ + void UpdateViewTabBar ( + const css::uno::Reference<css::drawing::framework::XTabBar>& rxTabBar); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/tools/FrameworkHelper.cxx b/sd/source/ui/framework/tools/FrameworkHelper.cxx new file mode 100644 index 000000000..dceecd510 --- /dev/null +++ b/sd/source/ui/framework/tools/FrameworkHelper.cxx @@ -0,0 +1,952 @@ +/* -*- 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 <osl/time.h> + +#include <framework/FrameworkHelper.hxx> + +#include <framework/ConfigurationController.hxx> +#include <framework/ResourceId.hxx> +#include <framework/ViewShellWrapper.hxx> +#include <ViewShellBase.hxx> +#include <DrawViewShell.hxx> +#include <ViewShellHint.hxx> +#include <app.hrc> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <comphelper/servicehelper.hxx> +#include <comphelper/compbase.hxx> +#include <svl/lstner.hxx> +#include <rtl/ustrbuf.hxx> + +#include <sfx2/request.hxx> + +#include <vcl/svapp.hxx> +#include <osl/doublecheckedlocking.h> +#include <osl/getglobalmutex.hxx> +#include <tools/diagnose_ex.h> +#include <memory> +#include <unordered_map> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace { + +//----- CallbackCaller -------------------------------------------------------- + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeListener + > CallbackCallerInterfaceBase; + +/** A CallbackCaller registers as listener at an XConfigurationController + object and waits for the notification of one type of event. When that + event is received, or when the CallbackCaller detects at its + construction that the event will not be sent in the near future, the + actual callback object is called and the CallbackCaller destroys itself. +*/ +class CallbackCaller + : public CallbackCallerInterfaceBase +{ +public: + /** Create a new CallbackCaller object. This object controls its own + lifetime by acquiring a reference to itself in the constructor. + When it detects that the event will not be notified in the near + future (because the queue of pending configuration change operations + is empty and therefore no event will be sent int the near future, it + does not acquires a reference and thus initiates its destruction in + the constructor.) + @param rBase + This ViewShellBase object is used to determine the + XConfigurationController at which to register. + @param rsEventType + The event type which the callback is waiting for. + @param pCallback + The callback object which is to be notified. The caller will + typically release his reference to the caller so that when the + CallbackCaller dies (after having called the callback) the + callback is destroyed. + */ + CallbackCaller ( + const ::sd::ViewShellBase& rBase, + const OUString& rsEventType, + const ::sd::framework::FrameworkHelper::ConfigurationChangeEventFilter& rFilter, + const ::sd::framework::FrameworkHelper::Callback& rCallback); + + virtual void disposing(std::unique_lock<std::mutex>&) override; + // XEventListener + virtual void SAL_CALL disposing (const lang::EventObject& rEvent) override; + // XConfigurationChangeListener + virtual void SAL_CALL notifyConfigurationChange (const ConfigurationChangeEvent& rEvent) override; + +private: + OUString msEventType; + Reference<XConfigurationController> mxConfigurationController; + ::sd::framework::FrameworkHelper::ConfigurationChangeEventFilter maFilter; + ::sd::framework::FrameworkHelper::Callback maCallback; +}; + +//----- LifetimeController ---------------------------------------------------- + +typedef comphelper::WeakComponentImplHelper < + css::lang::XEventListener + > LifetimeControllerInterfaceBase; + +/** This class helps controlling the lifetime of the + FrameworkHelper. Register at a ViewShellBase object and an XController + object and call Dispose() at the associated FrameworkHelper object when + one of them and Release() when both of them are destroyed. +*/ +class LifetimeController + : public LifetimeControllerInterfaceBase, + public SfxListener +{ +public: + explicit LifetimeController (::sd::ViewShellBase& rBase); + virtual ~LifetimeController() override; + + /** XEventListener. This method is called when the frame::XController + is being destroyed. + */ + using WeakComponentImplHelperBase::disposing; + virtual void SAL_CALL disposing (const lang::EventObject& rEvent) override; + + /** This method is called when the ViewShellBase is being destroyed. + */ + virtual void Notify (SfxBroadcaster& rBroadcaster, const SfxHint& rHint) override; + +private: + ::sd::ViewShellBase& mrBase; + bool mbListeningToViewShellBase; + bool mbListeningToController; + + /** When one or both of the mbListeningToViewShellBase and + mbListeningToController members were modified then call this method + to either dispose or release the associated FrameworkHelper. + */ + void Update(); +}; + +} // end of anonymous namespace + +namespace sd::framework { + +namespace { + + class FrameworkHelperAllPassFilter + { + public: + bool operator() (const css::drawing::framework::ConfigurationChangeEvent&) { return true; } + }; + + class FrameworkHelperResourceIdFilter + { + public: + explicit FrameworkHelperResourceIdFilter ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId); + bool operator() (const css::drawing::framework::ConfigurationChangeEvent& rEvent) + { return mxResourceId.is() && rEvent.ResourceId.is() + && mxResourceId->compareTo(rEvent.ResourceId) == 0; } + private: + css::uno::Reference<css::drawing::framework::XResourceId> mxResourceId; + }; + +} // end of anonymous namespace + +// Pane URLS. + +const OUString FrameworkHelper::msCenterPaneURL( msPaneURLPrefix + "CenterPane"); +const OUString FrameworkHelper::msFullScreenPaneURL( msPaneURLPrefix + "FullScreenPane"); +const OUString FrameworkHelper::msLeftImpressPaneURL( msPaneURLPrefix + "LeftImpressPane"); +const OUString FrameworkHelper::msLeftDrawPaneURL( msPaneURLPrefix + "LeftDrawPane"); + +// View URLs. + +const OUString FrameworkHelper::msImpressViewURL( msViewURLPrefix + "ImpressView"); +const OUString FrameworkHelper::msDrawViewURL( msViewURLPrefix + "GraphicView"); +const OUString FrameworkHelper::msOutlineViewURL( msViewURLPrefix + "OutlineView"); +const OUString FrameworkHelper::msNotesViewURL( msViewURLPrefix + "NotesView"); +const OUString FrameworkHelper::msHandoutViewURL( msViewURLPrefix + "HandoutView"); +const OUString FrameworkHelper::msSlideSorterURL( msViewURLPrefix + "SlideSorter"); +const OUString FrameworkHelper::msPresentationViewURL( msViewURLPrefix + "PresentationView"); +const OUString FrameworkHelper::msSidebarViewURL( msViewURLPrefix + "SidebarView"); + +// Tool bar URLs. + +const OUString FrameworkHelper::msViewTabBarURL( msToolBarURLPrefix + "ViewTabBar"); + +//----- helper ---------------------------------------------------------------- +namespace +{ + ::std::shared_ptr< ViewShell > lcl_getViewShell( const Reference< XResource >& i_rViewShellWrapper ) + { + ::std::shared_ptr< ViewShell > pViewShell; + try + { + Reference<lang::XUnoTunnel> xViewTunnel( i_rViewShellWrapper, UNO_QUERY_THROW ); + if (auto pWrapper = comphelper::getFromUnoTunnel<ViewShellWrapper>(xViewTunnel)) + pViewShell = pWrapper->GetViewShell(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + return pViewShell; + } + Reference< XResource > lcl_getFirstViewInPane( const Reference< XConfigurationController >& i_rConfigController, + const Reference< XResourceId >& i_rPaneId ) + { + try + { + Reference< XConfiguration > xConfiguration( i_rConfigController->getRequestedConfiguration(), UNO_SET_THROW ); + Sequence< Reference< XResourceId > > aViewIds( xConfiguration->getResources( + i_rPaneId, FrameworkHelper::msViewURLPrefix, AnchorBindingMode_DIRECT ) ); + if ( aViewIds.hasElements() ) + return i_rConfigController->getResource( aViewIds[0] ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + return nullptr; + } +} + +//----- FrameworkHelper::ViewURLMap ------------------------------------------- + +/** The ViewURLMap is used to translate between the view URLs used by the + drawing framework and the enums defined in the ViewShell class. +*/ +class FrameworkHelper::ViewURLMap + : public std::unordered_map< + OUString, + ViewShell::ShellType> +{ +public: + ViewURLMap() {} +}; + +//----- Framework::DisposeListener --------------------------------------------- + +namespace { + typedef comphelper::WeakComponentImplHelper < + css::lang::XEventListener + > FrameworkHelperDisposeListenerInterfaceBase; +} + +class FrameworkHelper::DisposeListener + : public FrameworkHelperDisposeListenerInterfaceBase +{ +public: + explicit DisposeListener (const ::std::shared_ptr<FrameworkHelper>& rpHelper); + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + virtual void SAL_CALL disposing (const lang::EventObject& rEventObject) override; + +private: + ::std::shared_ptr<FrameworkHelper> mpHelper; +}; + +//----- FrameworkHelper::Deleter ---------------------------------------------- + +class FrameworkHelper::Deleter +{ +public: + void operator()(FrameworkHelper* pObject) + { + delete pObject; + } +}; + +//----- FrameworkHelper ------------------------------------------------------- + +FrameworkHelper::ViewURLMap FrameworkHelper::maViewURLMap; + +FrameworkHelper::InstanceMap FrameworkHelper::maInstanceMap; + +::std::shared_ptr<FrameworkHelper> FrameworkHelper::Instance (ViewShellBase& rBase) +{ + + ::std::shared_ptr<FrameworkHelper> pHelper; + + InstanceMap::const_iterator iHelper (maInstanceMap.find(&rBase)); + if (iHelper == maInstanceMap.end()) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard (aMutexFunctor()); + if (iHelper == maInstanceMap.end()) + { + pHelper = ::std::shared_ptr<FrameworkHelper>( + new FrameworkHelper(rBase), + FrameworkHelper::Deleter()); + pHelper->Initialize(); + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + maInstanceMap[&rBase] = pHelper; + } + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + pHelper = iHelper->second; + } + + return pHelper; +} + +void FrameworkHelper::DisposeInstance (const ViewShellBase& rBase) +{ + InstanceMap::iterator iHelper (maInstanceMap.find(&rBase)); + if (iHelper != maInstanceMap.end()) + { + iHelper->second->Dispose(); + } +} + +void FrameworkHelper::ReleaseInstance (const ViewShellBase& rBase) +{ + InstanceMap::iterator iHelper (maInstanceMap.find(&rBase)); + if (iHelper != maInstanceMap.end()) + maInstanceMap.erase(iHelper); +} + +FrameworkHelper::FrameworkHelper (ViewShellBase& rBase) + : mrBase(rBase) +{ + Reference<XControllerManager> xControllerManager (rBase.GetController(), UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + } + + new LifetimeController(mrBase); +} + +void FrameworkHelper::Initialize() +{ + mxDisposeListener = new DisposeListener(shared_from_this()); +} + +FrameworkHelper::~FrameworkHelper() +{ +} + +void FrameworkHelper::Dispose() +{ + if (mxDisposeListener.is()) + mxDisposeListener->dispose(); + mxConfigurationController = nullptr; +} + +bool FrameworkHelper::IsValid() const +{ + return mxConfigurationController.is(); +} + +::std::shared_ptr<ViewShell> FrameworkHelper::GetViewShell (const OUString& rsPaneURL) +{ + if ( !mxConfigurationController.is() ) + return ::std::shared_ptr<ViewShell>(); + + Reference<XResourceId> xPaneId( CreateResourceId( rsPaneURL ) ); + return lcl_getViewShell( lcl_getFirstViewInPane( mxConfigurationController, xPaneId ) ); +} + +::std::shared_ptr<ViewShell> FrameworkHelper::GetViewShell (const Reference<XView>& rxView) +{ + return lcl_getViewShell( rxView ); +} + +Reference<XView> FrameworkHelper::GetView (const Reference<XResourceId>& rxPaneOrViewId) +{ + Reference<XView> xView; + + if ( ! rxPaneOrViewId.is() || ! mxConfigurationController.is()) + return nullptr; + + try + { + if (rxPaneOrViewId->getResourceURL().match(msViewURLPrefix)) + { + xView.set( mxConfigurationController->getResource( rxPaneOrViewId ), UNO_QUERY ); + } + else + { + xView.set( lcl_getFirstViewInPane( mxConfigurationController, rxPaneOrViewId ), UNO_QUERY ); + } + } + catch (lang::DisposedException&) + { + Dispose(); + } + catch (RuntimeException&) + { + } + + return xView; +} + +Reference<XResourceId> FrameworkHelper::RequestView ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL) +{ + Reference<XResourceId> xViewId; + + try + { + if (mxConfigurationController.is()) + { + mxConfigurationController->requestResourceActivation( + CreateResourceId(rsAnchorURL), + ResourceActivationMode_ADD); + xViewId = CreateResourceId(rsResourceURL, rsAnchorURL); + mxConfigurationController->requestResourceActivation( + xViewId, + ResourceActivationMode_REPLACE); + } + } + catch (lang::DisposedException&) + { + Dispose(); + xViewId = nullptr; + } + catch (RuntimeException&) + { + xViewId = nullptr; + } + + return xViewId; +} + +ViewShell::ShellType FrameworkHelper::GetViewId (const OUString& rsViewURL) +{ + if (maViewURLMap.empty()) + { + maViewURLMap[msImpressViewURL] = ViewShell::ST_IMPRESS; + maViewURLMap[msDrawViewURL] = ViewShell::ST_DRAW; + maViewURLMap[msOutlineViewURL] = ViewShell::ST_OUTLINE; + maViewURLMap[msNotesViewURL] = ViewShell::ST_NOTES; + maViewURLMap[msHandoutViewURL] = ViewShell::ST_HANDOUT; + maViewURLMap[msSlideSorterURL] = ViewShell::ST_SLIDE_SORTER; + maViewURLMap[msPresentationViewURL] = ViewShell::ST_PRESENTATION; + maViewURLMap[msSidebarViewURL] = ViewShell::ST_SIDEBAR; + } + ViewURLMap::const_iterator iView (maViewURLMap.find(rsViewURL)); + if (iView != maViewURLMap.end()) + return iView->second; + else + return ViewShell::ST_NONE; +} + +OUString FrameworkHelper::GetViewURL (ViewShell::ShellType eType) +{ + switch (eType) + { + case ViewShell::ST_IMPRESS : return msImpressViewURL; + case ViewShell::ST_DRAW : return msDrawViewURL; + case ViewShell::ST_OUTLINE : return msOutlineViewURL; + case ViewShell::ST_NOTES : return msNotesViewURL; + case ViewShell::ST_HANDOUT : return msHandoutViewURL; + case ViewShell::ST_SLIDE_SORTER : return msSlideSorterURL; + case ViewShell::ST_PRESENTATION : return msPresentationViewURL; + case ViewShell::ST_SIDEBAR : return msSidebarViewURL; + default: + return OUString(); + } +} + +namespace +{ + +void updateEditMode(const Reference<XView> &xView, const EditMode eEMode, bool updateFrameView) +{ + // Ensure we have the expected edit mode + // The check is only for DrawViewShell as OutlineViewShell + // and SlideSorterViewShell have no master mode + const ::std::shared_ptr<ViewShell> pCenterViewShell (FrameworkHelper::GetViewShell(xView)); + DrawViewShell* pDrawViewShell + = dynamic_cast<DrawViewShell*>(pCenterViewShell.get()); + if (pDrawViewShell != nullptr) + { + pCenterViewShell->Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_START)); + + pDrawViewShell->ChangeEditMode(eEMode, pDrawViewShell->IsLayerModeActive()); + if (updateFrameView) + pDrawViewShell->WriteFrameViewData(); + + pCenterViewShell->Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_END)); + } +} + +void asyncUpdateEditMode(FrameworkHelper* const pHelper, const EditMode eEMode) +{ + Reference<XResourceId> xPaneId ( + FrameworkHelper::CreateResourceId(framework::FrameworkHelper::msCenterPaneURL)); + Reference<XView> xView (pHelper->GetView(xPaneId)); + updateEditMode(xView, eEMode, true); +} + +} + +void FrameworkHelper::HandleModeChangeSlot ( + sal_uInt16 nSlotId, + SfxRequest const & rRequest) +{ + if ( ! mxConfigurationController.is()) + return; + + // Parameters are allowed for NotesMasterPage and SlideMasterPage + // for these command, transfor xxxxMasterPage with param = false + // to ActivatexxxxxMode + if (nSlotId == SID_NOTES_MASTER_MODE || nSlotId == SID_SLIDE_MASTER_MODE) + { + const SfxItemSet* pRequestArguments = rRequest.GetArgs(); + if (pRequestArguments) + { + const SfxBoolItem* pIsActive = rRequest.GetArg<SfxBoolItem>(nSlotId); + if (!pIsActive->GetValue ()) + { + if (nSlotId == SID_NOTES_MASTER_MODE) + nSlotId = SID_NOTES_MODE; + else + nSlotId = SID_NORMAL_MULTI_PANE_GUI; + } + } + } + + try + { + if ( ! mxConfigurationController.is()) + throw RuntimeException(); + + Reference<XResourceId> xPaneId ( + CreateResourceId(framework::FrameworkHelper::msCenterPaneURL)); + Reference<XView> xView (GetView(xPaneId)); + + // Compute requested view + OUString sRequestedView; + switch (nSlotId) + { + // draw + case SID_DRAWINGMODE: + // impress + case SID_NORMAL_MULTI_PANE_GUI: + case SID_SLIDE_MASTER_MODE: + sRequestedView = FrameworkHelper::msImpressViewURL; + break; + + case SID_NOTES_MODE: + case SID_NOTES_MASTER_MODE: + sRequestedView = FrameworkHelper::msNotesViewURL; + break; + + case SID_HANDOUT_MASTER_MODE: + sRequestedView = FrameworkHelper::msHandoutViewURL; + break; + + case SID_SLIDE_SORTER_MULTI_PANE_GUI: + case SID_SLIDE_SORTER_MODE: + sRequestedView = FrameworkHelper::msSlideSorterURL; + break; + + case SID_OUTLINE_MODE: + sRequestedView = FrameworkHelper::msOutlineViewURL; + break; + } + + // Compute requested mode + EditMode eEMode = EditMode::Page; + if (nSlotId == SID_SLIDE_MASTER_MODE + || nSlotId == SID_NOTES_MASTER_MODE + || nSlotId == SID_HANDOUT_MASTER_MODE) + eEMode = EditMode::MasterPage; + // Ensure we have the expected view shell + if (!(xView.is() && xView->getResourceId()->getResourceURL() == sRequestedView)) + + { + const auto xId = CreateResourceId(sRequestedView, msCenterPaneURL); + mxConfigurationController->requestResourceActivation( + xId, + ResourceActivationMode_REPLACE); + RunOnResourceActivation(xId, std::bind(&asyncUpdateEditMode, this, eEMode)); + } + else + { + updateEditMode(xView, eEMode, false); + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +void FrameworkHelper::RunOnConfigurationEvent( + const OUString& rsEventType, + const Callback& rCallback) +{ + RunOnEvent( + rsEventType, + FrameworkHelperAllPassFilter(), + rCallback); +} + +void FrameworkHelper::RunOnResourceActivation( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId, + const Callback& rCallback) +{ + if (mxConfigurationController.is() + && mxConfigurationController->getResource(rxResourceId).is()) + { + rCallback(false); + } + else + { + RunOnEvent( + msResourceActivationEvent, + FrameworkHelperResourceIdFilter(rxResourceId), + rCallback); + } +} + +namespace { + +/** A callback that sets a flag to a specified value when the callback is + called. +*/ +class FlagUpdater +{ +public: + explicit FlagUpdater (bool& rFlag) : mrFlag(rFlag) {} + void operator() (bool) const {mrFlag = true;} +private: + bool& mrFlag; +}; + +} + +void FrameworkHelper::RequestSynchronousUpdate() +{ + rtl::Reference<ConfigurationController> pCC ( + dynamic_cast<ConfigurationController*>(mxConfigurationController.get())); + if (pCC.is()) + pCC->RequestSynchronousUpdate(); +} + +void FrameworkHelper::WaitForEvent (const OUString& rsEventType) const +{ + bool bConfigurationUpdateSeen (false); + + RunOnEvent( + rsEventType, + FrameworkHelperAllPassFilter(), + FlagUpdater(bConfigurationUpdateSeen)); + + sal_uInt32 nStartTime = osl_getGlobalTimer(); + while ( ! bConfigurationUpdateSeen) + { + Application::Reschedule(); + + if( (osl_getGlobalTimer() - nStartTime) > 60000 ) + { + OSL_FAIL("FrameworkHelper::WaitForEvent(), no event for a minute? giving up!"); + break; + } + } +} + +void FrameworkHelper::WaitForUpdate() const +{ + WaitForEvent(msConfigurationUpdateEndEvent); +} + +void FrameworkHelper::RunOnEvent( + const OUString& rsEventType, + const ConfigurationChangeEventFilter& rFilter, + const Callback& rCallback) const +{ + new CallbackCaller(mrBase,rsEventType,rFilter,rCallback); +} + +void FrameworkHelper::disposing (const lang::EventObject& rEventObject) +{ + if (rEventObject.Source == mxConfigurationController) + mxConfigurationController = nullptr; +} + +void FrameworkHelper::UpdateConfiguration() +{ + if (!mxConfigurationController.is()) + return; + + try + { + if (mxConfigurationController.is()) + mxConfigurationController->update(); + } + catch (lang::DisposedException&) + { + Dispose(); + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +OUString FrameworkHelper::ResourceIdToString (const Reference<XResourceId>& rxResourceId) +{ + OUStringBuffer sString; + if (rxResourceId.is()) + { + sString.append(rxResourceId->getResourceURL()); + if (rxResourceId->hasAnchor()) + { + const Sequence<OUString> aAnchorURLs (rxResourceId->getAnchorURLs()); + for (const auto& rAnchorURL : aAnchorURLs) + { + sString.append(" | "); + sString.append(rAnchorURL); + } + } + } + return sString.makeStringAndClear(); +} + +Reference<XResourceId> FrameworkHelper::CreateResourceId (const OUString& rsResourceURL) +{ + return new ::sd::framework::ResourceId(rsResourceURL); +} + +Reference<XResourceId> FrameworkHelper::CreateResourceId ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL) +{ + return new ::sd::framework::ResourceId(rsResourceURL, rsAnchorURL); +} + +Reference<XResourceId> FrameworkHelper::CreateResourceId ( + const OUString& rsResourceURL, + const Reference<XResourceId>& rxAnchorId) +{ + if (rxAnchorId.is()) + return new ::sd::framework::ResourceId( + rsResourceURL, + rxAnchorId->getResourceURL(), + rxAnchorId->getAnchorURLs()); + else + return new ::sd::framework::ResourceId(rsResourceURL); +} + +//----- FrameworkHelper::DisposeListener -------------------------------------- + +FrameworkHelper::DisposeListener::DisposeListener ( + const ::std::shared_ptr<FrameworkHelper>& rpHelper) + : mpHelper(rpHelper) +{ + Reference<XComponent> xComponent (mpHelper->mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(this); +} + +void FrameworkHelper::DisposeListener::disposing(std::unique_lock<std::mutex>&) +{ + Reference<XComponent> xComponent (mpHelper->mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(this); + + mpHelper.reset(); +} + +void SAL_CALL FrameworkHelper::DisposeListener::disposing (const lang::EventObject& rEventObject) +{ + if (mpHelper != nullptr) + mpHelper->disposing(rEventObject); +} + +//===== FrameworkHelperResourceIdFilter ======================================= + +FrameworkHelperResourceIdFilter::FrameworkHelperResourceIdFilter ( + const Reference<XResourceId>& rxResourceId) + : mxResourceId(rxResourceId) +{ +} + +} // end of namespace sd::framework + +namespace { + +//===== CallbackCaller ======================================================== + +CallbackCaller::CallbackCaller ( + const ::sd::ViewShellBase& rBase, + const OUString& rsEventType, + const ::sd::framework::FrameworkHelper::ConfigurationChangeEventFilter& rFilter, + const ::sd::framework::FrameworkHelper::Callback& rCallback) + : msEventType(rsEventType), + maFilter(rFilter), + maCallback(rCallback) +{ + try + { + Reference<XControllerManager> xControllerManager (rBase.GetController(), UNO_QUERY_THROW); + mxConfigurationController = xControllerManager->getConfigurationController(); + if (mxConfigurationController.is()) + { + if (mxConfigurationController->hasPendingRequests()) + mxConfigurationController->addConfigurationChangeListener(this,msEventType,Any()); + else + { + // There are no requests waiting to be processed. Therefore + // no event, especially not the one we are waiting for, will + // be sent in the near future and the callback would never be + // called. + // Call the callback now and tell him that the event it is + // waiting for was not sent. + mxConfigurationController = nullptr; + maCallback(false); + } + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +void CallbackCaller::disposing(std::unique_lock<std::mutex>&) +{ + try + { + if (mxConfigurationController.is()) + { + Reference<XConfigurationController> xCC (mxConfigurationController); + mxConfigurationController = nullptr; + xCC->removeConfigurationChangeListener(this); + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +void SAL_CALL CallbackCaller::disposing (const lang::EventObject& rEvent) +{ + if (rEvent.Source == mxConfigurationController) + { + mxConfigurationController = nullptr; + maCallback(false); + } +} + +void SAL_CALL CallbackCaller::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (!(rEvent.Type == msEventType && maFilter(rEvent))) + return; + + maCallback(true); + if (mxConfigurationController.is()) + { + // Reset the reference to the configuration controller so that + // dispose() will not try to remove the listener a second time. + Reference<XConfigurationController> xCC (mxConfigurationController); + mxConfigurationController = nullptr; + + // Removing this object from the controller may very likely lead + // to its destruction, so no calls after that. + xCC->removeConfigurationChangeListener(this); + } +} + +//----- LifetimeController ------------------------------------------------- + +LifetimeController::LifetimeController (::sd::ViewShellBase& rBase) + : mrBase(rBase), + mbListeningToViewShellBase(false), + mbListeningToController(false) +{ + // Register as listener at the ViewShellBase. Because that is not done + // via a reference we have to increase the reference count manually. + // This is necessary even though listening to the XController did + // increase the reference count because the controller may release its + // reference to us before the ViewShellBase is destroyed. + StartListening(mrBase); + acquire(); + mbListeningToViewShellBase = true; + + Reference<XComponent> xComponent = rBase.GetController(); + if (xComponent.is()) + { + xComponent->addEventListener(this); + mbListeningToController = true; + } +} + +LifetimeController::~LifetimeController() +{ + OSL_ASSERT(!mbListeningToController && !mbListeningToViewShellBase); +} + +void SAL_CALL LifetimeController::disposing (const lang::EventObject&) +{ + mbListeningToController = false; + Update(); +} + +void LifetimeController::Notify (SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::Dying) + { + mbListeningToViewShellBase = false; + Update(); + release(); + } +} + +void LifetimeController::Update() +{ + if (mbListeningToViewShellBase && mbListeningToController) + { + // Both the controller and the ViewShellBase are alive. Keep + // waiting for their destruction. + } + else if (mbListeningToViewShellBase) + { + // The controller has been destroyed but the ViewShellBase is still + // alive. Dispose the associated FrameworkHelper but keep it around + // so that no new instance is created for the dying framework. + ::sd::framework::FrameworkHelper::DisposeInstance(mrBase); + } + else + { + // Both the controller and the ViewShellBase have been destroyed. + // Remove the FrameworkHelper so that the next call its Instance() + // method can create a new instance. + ::sd::framework::FrameworkHelper::ReleaseInstance(mrBase); + } +} + +} // end of anonymous namespace. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |