1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <framework/ConfigurationController.hxx>
21 #include <framework/Configuration.hxx>
22 #include <framework/FrameworkHelper.hxx>
23 #include "ConfigurationUpdater.hxx"
24 #include "ConfigurationControllerBroadcaster.hxx"
25 #include "ConfigurationTracer.hxx"
26 #include "GenericConfigurationChangeRequest.hxx"
27 #include "ConfigurationControllerResourceManager.hxx"
28 #include "ResourceFactoryManager.hxx"
29 #include "UpdateRequest.hxx"
30 #include "ChangeRequestQueueProcessor.hxx"
31 #include "ConfigurationClassifier.hxx"
32 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
33 #include <com/sun/star/frame/XController.hpp>
34
35 #include <sal/log.hxx>
36 #include <osl/mutex.hxx>
37 #include <vcl/svapp.hxx>
38 #include <memory>
39
40 using namespace ::com::sun::star;
41 using namespace ::com::sun::star::uno;
42 using namespace ::com::sun::star::drawing::framework;
43 using ::sd::framework::FrameworkHelper;
44
45 namespace sd { namespace framework {
46
47 //----- ConfigurationController::Implementation -------------------------------
48
49 class ConfigurationController::Implementation
50 {
51 public:
52 Implementation (
53 ConfigurationController& rController,
54 const Reference<frame::XController>& rxController);
55
56 Reference<XControllerManager> mxControllerManager;
57
58 /** The Broadcaster class implements storing and calling of listeners.
59 */
60 std::shared_ptr<ConfigurationControllerBroadcaster> mpBroadcaster;
61
62 /** The requested configuration which is modified (asynchronously) by
63 calls to requestResourceActivation() and
64 requestResourceDeactivation(). The mpConfigurationUpdater makes the
65 current configuration reflect the content of this one.
66 */
67 css::uno::Reference<css::drawing::framework::XConfiguration> mxRequestedConfiguration;
68
69 std::shared_ptr<ResourceFactoryManager> mpResourceFactoryContainer;
70
71 std::shared_ptr<ConfigurationControllerResourceManager> mpResourceManager;
72
73 std::shared_ptr<ConfigurationUpdater> mpConfigurationUpdater;
74
75 /** The queue processor owns the queue of configuration change request
76 objects and processes the objects.
77 */
78 std::unique_ptr<ChangeRequestQueueProcessor> mpQueueProcessor;
79
80 std::shared_ptr<ConfigurationUpdaterLock> mpConfigurationUpdaterLock;
81
82 sal_Int32 mnLockCount;
83 };
84
85 //===== ConfigurationController::Lock =========================================
86
Lock(const Reference<XConfigurationController> & rxController)87 ConfigurationController::Lock::Lock (const Reference<XConfigurationController>& rxController)
88 : mxController(rxController)
89 {
90 OSL_ASSERT(mxController.is());
91
92 if (mxController.is())
93 mxController->lock();
94 }
95
~Lock()96 ConfigurationController::Lock::~Lock()
97 {
98 if (mxController.is())
99 mxController->unlock();
100 }
101
102 //===== ConfigurationController ===============================================
103
ConfigurationController()104 ConfigurationController::ConfigurationController() throw()
105 : ConfigurationControllerInterfaceBase(MutexOwner::maMutex)
106 , mpImplementation()
107 , mbIsDisposed(false)
108 {
109 }
110
~ConfigurationController()111 ConfigurationController::~ConfigurationController() throw()
112 {
113 }
114
disposing()115 void SAL_CALL ConfigurationController::disposing()
116 {
117 if (mpImplementation == nullptr)
118 return;
119
120 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationController::disposing");
121 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": requesting empty configuration");
122 // To destroy all resources an empty configuration is requested and then,
123 // synchronously, all resulting requests are processed.
124 mpImplementation->mpQueueProcessor->Clear();
125 restoreConfiguration(new Configuration(this,false));
126 mpImplementation->mpQueueProcessor->ProcessUntilEmpty();
127 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": all requests processed");
128
129 // Now that all resources have been deactivated, mark the controller as
130 // disposed.
131 mbIsDisposed = true;
132
133 // Release the listeners.
134 lang::EventObject aEvent;
135 aEvent.Source = uno::Reference<uno::XInterface>(static_cast<cppu::OWeakObject*>(this));
136
137 {
138 const SolarMutexGuard aSolarGuard;
139 mpImplementation->mpBroadcaster->DisposeAndClear();
140 }
141
142 mpImplementation->mpQueueProcessor.reset();
143 mpImplementation->mxRequestedConfiguration = nullptr;
144 mpImplementation.reset();
145 }
146
ProcessEvent()147 void ConfigurationController::ProcessEvent()
148 {
149 if (mpImplementation != nullptr)
150 {
151 OSL_ASSERT(mpImplementation->mpQueueProcessor != nullptr);
152
153 mpImplementation->mpQueueProcessor->ProcessOneEvent();
154 }
155 }
156
RequestSynchronousUpdate()157 void ConfigurationController::RequestSynchronousUpdate()
158 {
159 if (mpImplementation == nullptr)
160 return;
161 if (mpImplementation->mpQueueProcessor == nullptr)
162 return;
163 mpImplementation->mpQueueProcessor->ProcessUntilEmpty();
164 }
165
166 //----- XConfigurationControllerBroadcaster -----------------------------------
167
addConfigurationChangeListener(const Reference<XConfigurationChangeListener> & rxListener,const OUString & rsEventType,const Any & rUserData)168 void SAL_CALL ConfigurationController::addConfigurationChangeListener (
169 const Reference<XConfigurationChangeListener>& rxListener,
170 const OUString& rsEventType,
171 const Any& rUserData)
172 {
173 ::osl::MutexGuard aGuard (maMutex);
174
175 ThrowIfDisposed();
176 OSL_ASSERT(mpImplementation != nullptr);
177 mpImplementation->mpBroadcaster->AddListener(rxListener, rsEventType, rUserData);
178 }
179
removeConfigurationChangeListener(const Reference<XConfigurationChangeListener> & rxListener)180 void SAL_CALL ConfigurationController::removeConfigurationChangeListener (
181 const Reference<XConfigurationChangeListener>& rxListener)
182 {
183 ::osl::MutexGuard aGuard (maMutex);
184
185 ThrowIfDisposed();
186 mpImplementation->mpBroadcaster->RemoveListener(rxListener);
187 }
188
notifyEvent(const ConfigurationChangeEvent & rEvent)189 void SAL_CALL ConfigurationController::notifyEvent (
190 const ConfigurationChangeEvent& rEvent)
191 {
192 ThrowIfDisposed();
193 mpImplementation->mpBroadcaster->NotifyListeners(rEvent);
194 }
195
196 //----- XConfigurationController ----------------------------------------------
197
lock()198 void SAL_CALL ConfigurationController::lock()
199 {
200 OSL_ASSERT(mpImplementation != nullptr);
201 OSL_ASSERT(mpImplementation->mpConfigurationUpdater != nullptr);
202
203 ::osl::MutexGuard aGuard (maMutex);
204 ThrowIfDisposed();
205
206 ++mpImplementation->mnLockCount;
207 if (mpImplementation->mpConfigurationUpdaterLock == nullptr)
208 mpImplementation->mpConfigurationUpdaterLock
209 = mpImplementation->mpConfigurationUpdater->GetLock();
210 }
211
unlock()212 void SAL_CALL ConfigurationController::unlock()
213 {
214 ::osl::MutexGuard aGuard (maMutex);
215
216 // Allow unlocking while the ConfigurationController is being disposed
217 // (but not when that is done and the controller is disposed.)
218 if (rBHelper.bDisposed)
219 ThrowIfDisposed();
220
221 OSL_ASSERT(mpImplementation->mnLockCount>0);
222 --mpImplementation->mnLockCount;
223 if (mpImplementation->mnLockCount == 0)
224 mpImplementation->mpConfigurationUpdaterLock.reset();
225 }
226
requestResourceActivation(const Reference<XResourceId> & rxResourceId,ResourceActivationMode eMode)227 void SAL_CALL ConfigurationController::requestResourceActivation (
228 const Reference<XResourceId>& rxResourceId,
229 ResourceActivationMode eMode)
230 {
231 ::osl::MutexGuard aGuard (maMutex);
232 ThrowIfDisposed();
233
234 // Check whether we are being disposed. This is handled differently
235 // then being completely disposed because the first thing disposing()
236 // does is to deactivate all remaining resources. This is done via
237 // regular methods which must not throw DisposedExceptions. Therefore
238 // we just return silently during that stage.
239 if (rBHelper.bInDispose)
240 {
241 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationController::requestResourceActivation(): ignoring " <<
242 FrameworkHelper::ResourceIdToString(rxResourceId));
243 return;
244 }
245
246 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationController::requestResourceActivation() " <<
247 FrameworkHelper::ResourceIdToString(rxResourceId));
248
249 if (!rxResourceId.is())
250 return;
251
252 if (eMode == ResourceActivationMode_REPLACE)
253 {
254 // Get a list of the matching resources and create deactivation
255 // requests for them.
256 const Sequence<Reference<XResourceId> > aResourceList (
257 mpImplementation->mxRequestedConfiguration->getResources(
258 rxResourceId->getAnchor(),
259 rxResourceId->getResourceTypePrefix(),
260 AnchorBindingMode_DIRECT));
261
262 for (const auto& rResource : aResourceList)
263 {
264 // Do not request the deactivation of the resource for which
265 // this method was called. Doing it would not change the
266 // outcome but would result in unnecessary work.
267 if (rxResourceId->compareTo(rResource) == 0)
268 continue;
269
270 // Request the deactivation of a resource and all resources
271 // linked to it.
272 requestResourceDeactivation(rResource);
273 }
274 }
275
276 Reference<XConfigurationChangeRequest> xRequest(
277 new GenericConfigurationChangeRequest(
278 rxResourceId,
279 GenericConfigurationChangeRequest::Activation));
280 postChangeRequest(xRequest);
281 }
282
requestResourceDeactivation(const Reference<XResourceId> & rxResourceId)283 void SAL_CALL ConfigurationController::requestResourceDeactivation (
284 const Reference<XResourceId>& rxResourceId)
285 {
286 ::osl::MutexGuard aGuard (maMutex);
287 ThrowIfDisposed();
288
289 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationController::requestResourceDeactivation() " <<
290 FrameworkHelper::ResourceIdToString(rxResourceId));
291
292 if (!rxResourceId.is())
293 return;
294
295 // Request deactivation of all resources linked to the specified one
296 // as well.
297 const Sequence<Reference<XResourceId> > aLinkedResources (
298 mpImplementation->mxRequestedConfiguration->getResources(
299 rxResourceId,
300 OUString(),
301 AnchorBindingMode_DIRECT));
302 for (const auto& rLinkedResource : aLinkedResources)
303 {
304 // We do not add deactivation requests directly but call this
305 // method recursively, so that when one time there are resources
306 // linked to linked resources, these are handled correctly, too.
307 requestResourceDeactivation(rLinkedResource);
308 }
309
310 // Add a deactivation request for the specified resource.
311 Reference<XConfigurationChangeRequest> xRequest(
312 new GenericConfigurationChangeRequest(
313 rxResourceId,
314 GenericConfigurationChangeRequest::Deactivation));
315 postChangeRequest(xRequest);
316 }
317
getResource(const Reference<XResourceId> & rxResourceId)318 Reference<XResource> SAL_CALL ConfigurationController::getResource (
319 const Reference<XResourceId>& rxResourceId)
320 {
321 ::osl::MutexGuard aGuard (maMutex);
322 ThrowIfDisposed();
323
324 ConfigurationControllerResourceManager::ResourceDescriptor aDescriptor (
325 mpImplementation->mpResourceManager->GetResource(rxResourceId));
326 return aDescriptor.mxResource;
327 }
328
update()329 void SAL_CALL ConfigurationController::update()
330 {
331 ::osl::MutexGuard aGuard (maMutex);
332 ThrowIfDisposed();
333
334 if (mpImplementation->mpQueueProcessor->IsEmpty())
335 {
336 // The queue is empty. Add another request that does nothing but
337 // asynchronously trigger a request for an update.
338 mpImplementation->mpQueueProcessor->AddRequest(new UpdateRequest());
339 }
340 else
341 {
342 // The queue is not empty, so we rely on the queue processor to
343 // request an update automatically when the queue becomes empty.
344 }
345 }
346
hasPendingRequests()347 sal_Bool SAL_CALL ConfigurationController::hasPendingRequests()
348 {
349 ::osl::MutexGuard aGuard (maMutex);
350 ThrowIfDisposed();
351
352 return ! mpImplementation->mpQueueProcessor->IsEmpty();
353 }
354
postChangeRequest(const Reference<XConfigurationChangeRequest> & rxRequest)355 void SAL_CALL ConfigurationController::postChangeRequest (
356 const Reference<XConfigurationChangeRequest>& rxRequest)
357 {
358 ::osl::MutexGuard aGuard (maMutex);
359 ThrowIfDisposed();
360
361 mpImplementation->mpQueueProcessor->AddRequest(rxRequest);
362 }
363
getRequestedConfiguration()364 Reference<XConfiguration> SAL_CALL ConfigurationController::getRequestedConfiguration()
365 {
366 ::osl::MutexGuard aGuard (maMutex);
367 ThrowIfDisposed();
368
369 if (mpImplementation->mxRequestedConfiguration.is())
370 return Reference<XConfiguration>(
371 mpImplementation->mxRequestedConfiguration->createClone(), UNO_QUERY);
372 else
373 return Reference<XConfiguration>();
374 }
375
getCurrentConfiguration()376 Reference<XConfiguration> SAL_CALL ConfigurationController::getCurrentConfiguration()
377 {
378 ::osl::MutexGuard aGuard (maMutex);
379 ThrowIfDisposed();
380
381 Reference<XConfiguration> xCurrentConfiguration(
382 mpImplementation->mpConfigurationUpdater->GetCurrentConfiguration());
383 if (xCurrentConfiguration.is())
384 return Reference<XConfiguration>(xCurrentConfiguration->createClone(), UNO_QUERY);
385 else
386 return Reference<XConfiguration>();
387 }
388
389 /** The given configuration is restored by generating the appropriate set of
390 activation and deactivation requests.
391 */
restoreConfiguration(const Reference<XConfiguration> & rxNewConfiguration)392 void SAL_CALL ConfigurationController::restoreConfiguration (
393 const Reference<XConfiguration>& rxNewConfiguration)
394 {
395 ::osl::MutexGuard aGuard (maMutex);
396 ThrowIfDisposed();
397
398 // We will probably be making a couple of activation and deactivation
399 // requests so lock the configuration controller and let it later update
400 // all changes at once.
401 std::shared_ptr<ConfigurationUpdaterLock> pLock (
402 mpImplementation->mpConfigurationUpdater->GetLock());
403
404 // Get lists of resources that are to be activated or deactivated.
405 Reference<XConfiguration> xCurrentConfiguration (mpImplementation->mxRequestedConfiguration);
406 #if OSL_DEBUG_LEVEL >=1
407 SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationController::restoreConfiguration(");
408 ConfigurationTracer::TraceConfiguration(rxNewConfiguration, "requested configuration");
409 ConfigurationTracer::TraceConfiguration(xCurrentConfiguration, "current configuration");
410 #endif
411 ConfigurationClassifier aClassifier (rxNewConfiguration, xCurrentConfiguration);
412 aClassifier.Partition();
413 #if DEBUG_SD_CONFIGURATION_TRACE
414 aClassifier.TraceResourceIdVector(
415 "requested but not current resources:\n", aClassifier.GetC1minusC2());
416 aClassifier.TraceResourceIdVector(
417 "current but not requested resources:\n", aClassifier.GetC2minusC1());
418 aClassifier.TraceResourceIdVector(
419 "requested and current resources:\n", aClassifier.GetC1andC2());
420 #endif
421
422 // Request the deactivation of resources that are not requested in the
423 // new configuration.
424 const ConfigurationClassifier::ResourceIdVector& rResourcesToDeactivate (
425 aClassifier.GetC2minusC1());
426 for (const auto& rxResource : rResourcesToDeactivate)
427 {
428 requestResourceDeactivation(rxResource);
429 }
430
431 // Request the activation of resources that are requested in the
432 // new configuration but are not part of the current configuration.
433 const ConfigurationClassifier::ResourceIdVector& rResourcesToActivate (
434 aClassifier.GetC1minusC2());
435 for (const auto& rxResource : rResourcesToActivate)
436 {
437 requestResourceActivation(rxResource, ResourceActivationMode_ADD);
438 }
439
440 pLock.reset();
441 }
442
443 //----- XResourceFactoryManager -----------------------------------------------
444
addResourceFactory(const OUString & sResourceURL,const Reference<XResourceFactory> & rxResourceFactory)445 void SAL_CALL ConfigurationController::addResourceFactory(
446 const OUString& sResourceURL,
447 const Reference<XResourceFactory>& rxResourceFactory)
448 {
449 ::osl::MutexGuard aGuard (maMutex);
450 ThrowIfDisposed();
451 mpImplementation->mpResourceFactoryContainer->AddFactory(sResourceURL, rxResourceFactory);
452 }
453
removeResourceFactoryForURL(const OUString & sResourceURL)454 void SAL_CALL ConfigurationController::removeResourceFactoryForURL(
455 const OUString& sResourceURL)
456 {
457 ::osl::MutexGuard aGuard (maMutex);
458 ThrowIfDisposed();
459 mpImplementation->mpResourceFactoryContainer->RemoveFactoryForURL(sResourceURL);
460 }
461
removeResourceFactoryForReference(const Reference<XResourceFactory> & rxResourceFactory)462 void SAL_CALL ConfigurationController::removeResourceFactoryForReference(
463 const Reference<XResourceFactory>& rxResourceFactory)
464 {
465 ::osl::MutexGuard aGuard (maMutex);
466 ThrowIfDisposed();
467 mpImplementation->mpResourceFactoryContainer->RemoveFactoryForReference(rxResourceFactory);
468 }
469
getResourceFactory(const OUString & sResourceURL)470 Reference<XResourceFactory> SAL_CALL ConfigurationController::getResourceFactory (
471 const OUString& sResourceURL)
472 {
473 ::osl::MutexGuard aGuard (maMutex);
474 ThrowIfDisposed();
475
476 return mpImplementation->mpResourceFactoryContainer->GetFactory(sResourceURL);
477 }
478
479 //----- XInitialization -------------------------------------------------------
480
initialize(const Sequence<Any> & aArguments)481 void SAL_CALL ConfigurationController::initialize (const Sequence<Any>& aArguments)
482 {
483 ::osl::MutexGuard aGuard (maMutex);
484
485 if (aArguments.getLength() == 1)
486 {
487 const SolarMutexGuard aSolarGuard;
488
489 mpImplementation.reset(new Implementation(
490 *this,
491 Reference<frame::XController>(aArguments[0], UNO_QUERY_THROW)));
492 }
493 }
494
ThrowIfDisposed() const495 void ConfigurationController::ThrowIfDisposed () const
496 {
497 if (mbIsDisposed)
498 {
499 throw lang::DisposedException ("ConfigurationController object has already been disposed",
500 const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
501 }
502
503 if (mpImplementation == nullptr)
504 {
505 OSL_ASSERT(mpImplementation != nullptr);
506 throw RuntimeException("ConfigurationController not initialized",
507 const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
508 }
509 }
510
511 //===== ConfigurationController::Implementation ===============================
512
Implementation(ConfigurationController & rController,const Reference<frame::XController> & rxController)513 ConfigurationController::Implementation::Implementation (
514 ConfigurationController& rController,
515 const Reference<frame::XController>& rxController)
516 : mxControllerManager(rxController, UNO_QUERY_THROW),
517 mpBroadcaster(new ConfigurationControllerBroadcaster(&rController)),
518 mxRequestedConfiguration(new Configuration(&rController, true)),
519 mpResourceFactoryContainer(new ResourceFactoryManager(mxControllerManager)),
520 mpResourceManager(
521 new ConfigurationControllerResourceManager(mpResourceFactoryContainer,mpBroadcaster)),
522 mpConfigurationUpdater(
523 new ConfigurationUpdater(mpBroadcaster, mpResourceManager,mxControllerManager)),
524 mpQueueProcessor(new ChangeRequestQueueProcessor(mpConfigurationUpdater)),
525 mpConfigurationUpdaterLock(),
526 mnLockCount(0)
527 {
528 mpQueueProcessor->SetConfiguration(mxRequestedConfiguration);
529 }
530
531 } } // end of namespace sd::framework
532
533
534 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 &)535 com_sun_star_comp_Draw_framework_configuration_ConfigurationController_get_implementation(
536 css::uno::XComponentContext*,
537 css::uno::Sequence<css::uno::Any> const &)
538 {
539 return cppu::acquire(new sd::framework::ConfigurationController());
540 }
541
542
543 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
544