LibreOffice Module dbaccess (master) 1
subcomponentmanager.cxx
Go to the documentation of this file.
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
21#include "AppController.hxx"
22#include <strings.hxx>
23
24#include <com/sun/star/frame/XFrame.hpp>
25#include <com/sun/star/frame/XModel.hpp>
26#include <com/sun/star/frame/XModel2.hpp>
27#include <com/sun/star/util/XCloseable.hpp>
28#include <com/sun/star/awt/XTopWindow.hpp>
29#include <com/sun/star/embed/XComponentSupplier.hpp>
30#include <com/sun/star/ucb/XCommandProcessor.hpp>
31#include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
32#include <com/sun/star/beans/XPropertySet.hpp>
33
35#include <dbaccess/dataview.hxx>
36#include <utility>
37#include <vcl/svapp.hxx>
38#include <osl/mutex.hxx>
39
40#include <algorithm>
41
42namespace dbaui
43{
44
45 using ::com::sun::star::uno::Reference;
46 using ::com::sun::star::uno::UNO_QUERY;
47 using ::com::sun::star::uno::UNO_QUERY_THROW;
48 using ::com::sun::star::uno::UNO_SET_THROW;
49 using ::com::sun::star::uno::Exception;
50 using ::com::sun::star::uno::RuntimeException;
51 using ::com::sun::star::uno::Any;
52 using ::com::sun::star::uno::Sequence;
53 using ::com::sun::star::frame::XFrame;
54 using ::com::sun::star::frame::XController;
55 using ::com::sun::star::frame::XModel;
56 using ::com::sun::star::lang::EventObject;
57 using ::com::sun::star::lang::XComponent;
58 using ::com::sun::star::frame::XModel2;
59 using ::com::sun::star::container::XEnumeration;
60 using ::com::sun::star::util::XCloseable;
61 using ::com::sun::star::awt::XTopWindow;
62 using ::com::sun::star::embed::XComponentSupplier;
63 using ::com::sun::star::ucb::XCommandProcessor;
64 using ::com::sun::star::ucb::Command;
65 using ::com::sun::star::document::XDocumentEventBroadcaster;
66 using ::com::sun::star::beans::XPropertySet;
67 using ::com::sun::star::beans::PropertyChangeEvent;
68
69 // helper structs
70 namespace
71 {
72 struct SubComponentDescriptor
73 {
75 OUString sName;
77 sal_Int32 nComponentType;
81 Reference< XFrame > xFrame;
83 Reference< XController > xController;
85 Reference< XModel > xModel;
87 Reference< XCommandProcessor > xComponentCommandProcessor;
89 Reference< XPropertySet > xDocumentDefinitionProperties;
90
92 :nComponentType( -1 )
94 {
95 }
96
97 SubComponentDescriptor( OUString i_sName, const sal_Int32 i_nComponentType,
98 const ElementOpenMode i_eOpenMode, const Reference< XComponent >& i_rComponent )
99 :sName(std::move( i_sName ))
100 ,nComponentType( i_nComponentType )
101 ,eOpenMode( i_eOpenMode )
102 {
103 if ( !impl_constructFrom( i_rComponent ) )
104 {
105 // i_rComponent is neither a model, nor a controller, nor a frame
106 // => it must be a css.sdb.DocumentDefinition
107 Reference< XComponentSupplier > xCompSupp( i_rComponent, UNO_QUERY_THROW );
108 Reference< XComponent > xComponent( xCompSupp->getComponent(), UNO_QUERY_THROW );
109 if ( !impl_constructFrom( xComponent ) )
110 throw RuntimeException("Illegal component type." );
111 xComponentCommandProcessor.set( i_rComponent, UNO_QUERY_THROW );
112 xDocumentDefinitionProperties.set( i_rComponent, UNO_QUERY_THROW );
113 }
114 }
115
116 bool is() const { return xFrame.is(); }
117
118 private:
119 bool impl_constructFrom( const Reference< XComponent >& _rxComponent )
120 {
121 // is it a model?
122 xModel.set( _rxComponent, UNO_QUERY );
123 if ( xModel.is() )
124 {
125 xController.set( xModel->getCurrentController() );
126 if ( xController.is() )
127 xFrame.set( xController->getFrame(), UNO_SET_THROW );
128 }
129 else
130 {
131 // is it a controller?
132 xController.set( _rxComponent, UNO_QUERY );
133 if ( xController.is() )
134 {
135 xFrame.set( xController->getFrame(), UNO_SET_THROW );
136 }
137 else
138 {
139 // is it a frame?
140 xFrame.set( _rxComponent, UNO_QUERY );
141 if ( !xFrame.is() )
142 return false;
143
144 // ensure we have a controller
145 xController.set( xFrame->getController(), UNO_SET_THROW );
146 }
147
148 // check whether there is a model (not required)
149 xModel.set( xController->getModel() );
150 }
151
152 return true;
153 }
154 };
155
156 struct SelectSubComponent
157 {
158 Reference< XComponent > operator()( const SubComponentDescriptor &_desc ) const
159 {
160 if ( _desc.xModel.is() )
161 return _desc.xModel;
162 OSL_ENSURE( _desc.xController.is(), "SelectSubComponent::operator(): illegal component!" );
163 return _desc.xController;
164 }
165 };
166
167 typedef std::vector< SubComponentDescriptor > SubComponents;
168
169 struct SubComponentMatch
170 {
171 public:
172 SubComponentMatch( OUString i_sName, const sal_Int32 i_nComponentType,
173 const ElementOpenMode i_eOpenMode )
174 :m_sName(std::move( i_sName ))
175 ,m_nComponentType( i_nComponentType )
176 ,m_eOpenMode( i_eOpenMode )
177 {
178 }
179
180 bool operator()( const SubComponentDescriptor& i_rCompareWith ) const
181 {
182 return ( m_sName == i_rCompareWith.sName )
183 && ( m_nComponentType == i_rCompareWith.nComponentType )
184 && ( m_eOpenMode == i_rCompareWith.eOpenMode );
185 }
186 private:
187 const OUString m_sName;
188 const sal_Int32 m_nComponentType;
190 };
191 }
192
193 // SubComponentManager_Data
195 {
197 :m_rController( _rController )
198 ,m_aMutex(std::move( _aMutex ))
199 {
200 }
201
203 mutable ::comphelper::SharedMutex m_aMutex;
204 SubComponents m_aComponents;
205
206 ::osl::Mutex& getMutex() const { return m_aMutex; }
207 };
208
209 // SubComponentManager
210 SubComponentManager::SubComponentManager( OApplicationController& _rController, const ::comphelper::SharedMutex& _rMutex )
211 :m_pData( new SubComponentManager_Data( _rController, _rMutex ) )
212 {
213 }
214
216 {
217 }
218
220 {
221 ::osl::MutexGuard aGuard( m_pData->getMutex() );
222 m_pData->m_aComponents.clear();
223 }
224
225 namespace
226 {
227 bool lcl_fallbackToAnotherController( SubComponentDescriptor& _rCompDesc )
228 {
229 Reference< XController > xFallback;
230 OSL_PRECOND( _rCompDesc.xModel.is(), "lcl_fallbackToAnotherController: illegal call!" );
231 if ( !_rCompDesc.xModel.is() )
232 return false;
233
234 xFallback.set( _rCompDesc.xModel->getCurrentController() );
235 if ( xFallback == _rCompDesc.xController )
236 // don't accept the very same controller as fallback
237 xFallback.clear();
238
239 if ( !xFallback.is() )
240 {
241 // perhaps XModel2 can be of help here
242 Reference< XModel2 > xModel2( _rCompDesc.xModel, UNO_QUERY );
243 Reference< XEnumeration > xControllerEnum;
244 if ( xModel2.is() )
245 xControllerEnum = xModel2->getControllers();
246 while ( xControllerEnum.is() && xControllerEnum->hasMoreElements() )
247 {
248 xFallback.set( xControllerEnum->nextElement(), UNO_QUERY );
249 if ( xFallback == _rCompDesc.xController )
250 xFallback.clear();
251 }
252 }
253
254 if ( xFallback.is() )
255 {
256 _rCompDesc.xController = xFallback;
257 _rCompDesc.xFrame.set( xFallback->getFrame(), UNO_SET_THROW );
258 return true;
259 }
260
261 return false;
262 }
263
264 bool lcl_closeComponent( const Reference< XCommandProcessor >& _rxCommandProcessor )
265 {
266 bool bSuccess = false;
267 try
268 {
269 sal_Int32 nCommandIdentifier = _rxCommandProcessor->createCommandIdentifier();
270
272 aCommand.Name = "close";
273 _rxCommandProcessor->execute( aCommand, nCommandIdentifier, nullptr );
274 bSuccess = true;
275 }
276 catch( const Exception& )
277 {
278 DBG_UNHANDLED_EXCEPTION("dbaccess");
279 }
280 return bSuccess;
281 }
282
283 bool lcl_closeComponent( const SubComponentDescriptor& _rComponent )
284 {
285 if ( _rComponent.xComponentCommandProcessor.is() )
286 return lcl_closeComponent( _rComponent.xComponentCommandProcessor );
287
288 Reference< XController > xController( _rComponent.xController );
289 OSL_ENSURE( xController.is(), "lcl_closeComponent: invalid controller!" );
290
291 // suspend the controller in the document
292 if ( xController.is() )
293 if ( !xController->suspend( true ) )
294 return false;
295
296 bool bSuccess = false;
297 try
298 {
299 Reference< XCloseable > xCloseable( _rComponent.xFrame, UNO_QUERY_THROW );
300 xCloseable->close( true );
301 bSuccess = true;
302 }
303 catch( const Exception& )
304 {
305 DBG_UNHANDLED_EXCEPTION("dbaccess");
306 }
307 return bSuccess;
308 }
309
310 void lcl_notifySubComponentEvent( const SubComponentManager_Data& _rData, const char* _pAsciiEventName,
311 const SubComponentDescriptor& _rComponent )
312 {
313 try
314 {
315 Reference< XDocumentEventBroadcaster > xBroadcaster( _rData.m_rController.getModel(), UNO_QUERY_THROW );
316 xBroadcaster->notifyDocumentEvent(
317 OUString::createFromAscii( _pAsciiEventName ),
318 &_rData.m_rController,
319 Any( _rComponent.xFrame )
320 );
321 }
322 catch( const Exception& )
323 {
324 DBG_UNHANDLED_EXCEPTION("dbaccess");
325 }
326 }
327 }
328
329 void SAL_CALL SubComponentManager::propertyChange( const PropertyChangeEvent& i_rEvent )
330 {
331 if ( i_rEvent.PropertyName != PROPERTY_NAME )
332 // by definition, it's allowed to broadcast more than what we've registered for
333 return;
334
335 // find the sub component whose name changed
336 for (auto & component : m_pData->m_aComponents)
337 {
338 if ( component.xDocumentDefinitionProperties != i_rEvent.Source )
339 continue;
340
341 OUString sNewName;
342 OSL_VERIFY( i_rEvent.NewValue >>= sNewName );
343
344 #if OSL_DEBUG_LEVEL > 0
345 OUString sOldKnownName( component.sName );
346 OUString sOldName;
347 OSL_VERIFY( i_rEvent.OldValue >>= sOldName );
348 OSL_ENSURE( sOldName == sOldKnownName, "SubComponentManager::propertyChange: inconsistency in the old names!" );
349 #endif
350
351 component.sName = sNewName;
352 break;
353 }
354 }
355
356 void SAL_CALL SubComponentManager::disposing( const EventObject& _rSource )
357 {
358 ::osl::ClearableMutexGuard aGuard( m_pData->getMutex() );
359
360 SubComponentDescriptor aClosedComponent;
361
362 for ( SubComponents::iterator comp = m_pData->m_aComponents.begin();
363 comp != m_pData->m_aComponents.end();
364 ++comp
365 )
366 {
367 bool bRemove = false;
368
369 if ( comp->xController == _rSource.Source )
370 {
371 if ( !comp->xModel.is() )
372 {
373 bRemove = true;
374 }
375 else
376 {
377 // maybe this is just one view to the sub document, and only this view is closed
378 if ( !lcl_fallbackToAnotherController( *comp ) )
379 {
380 bRemove = true;
381 }
382 }
383 }
384 else if ( comp->xModel == _rSource.Source )
385 {
386 bRemove = true;
387 }
388
389 if ( bRemove )
390 {
391 aClosedComponent = *comp;
392 m_pData->m_aComponents.erase( comp );
393 break;
394 }
395 }
396
397 if ( aClosedComponent.is() )
398 {
399 aGuard.clear();
400 lcl_notifySubComponentEvent( *m_pData, "OnSubComponentClosed", aClosedComponent );
401 }
402 }
403
405 {
406 ::osl::MutexGuard aGuard( m_pData->getMutex() );
407
408 Sequence< Reference< XComponent > > aComponents( m_pData->m_aComponents.size() );
409 std::transform(
410 m_pData->m_aComponents.begin(),
411 m_pData->m_aComponents.end(),
412 aComponents.getArray(),
413 SelectSubComponent()
414 );
415 return aComponents;
416 }
417
419 {
420 SolarMutexGuard aSolarGuard;
421 ::osl::MutexGuard aGuard( m_pData->getMutex() );
422
423 try
424 {
425 SubComponents aWorkingCopy( m_pData->m_aComponents );
426 for (auto const& elem : aWorkingCopy)
427 {
428 lcl_closeComponent(elem);
429 }
430 }
431 catch ( const Exception& )
432 {
433 DBG_UNHANDLED_EXCEPTION("dbaccess");
434 }
435
436 return empty();
437 }
438
440 {
441 ::osl::MutexGuard aGuard( m_pData->getMutex() );
442 return m_pData->m_aComponents.empty();
443 }
444
445 void SubComponentManager::onSubComponentOpened( const OUString& _rName, const sal_Int32 _nComponentType,
446 const ElementOpenMode _eOpenMode, const Reference< XComponent >& _rxComponent )
447 {
448 ::osl::ClearableMutexGuard aGuard( m_pData->getMutex() );
449
450#if OSL_DEBUG_LEVEL > 0
451 if ( !_rName.isEmpty() )
452 {
453 // check there does not already exist such a component
454 auto subComponentNotExists = std::none_of(
455 m_pData->m_aComponents.begin(),
456 m_pData->m_aComponents.end(),
457 SubComponentMatch( _rName, _nComponentType, _eOpenMode )
458 );
459 OSL_ENSURE( subComponentNotExists, "already existent!" );
460 }
461#endif
462 SubComponentDescriptor aElement( _rName, _nComponentType, _eOpenMode, _rxComponent );
463 ENSURE_OR_THROW( aElement.xModel.is() || aElement.xController.is(), "illegal component" );
464
465 m_pData->m_aComponents.push_back( aElement );
466
467 // add as listener
468 if ( aElement.xController.is() )
469 aElement.xController->addEventListener( this );
470 if ( aElement.xModel.is() )
471 aElement.xModel->addEventListener( this );
472 if ( aElement.xDocumentDefinitionProperties.is() )
473 aElement.xDocumentDefinitionProperties->addPropertyChangeListener( PROPERTY_NAME, this );
474
475 // notify this to interested parties
476 aGuard.clear();
477 lcl_notifySubComponentEvent( *m_pData, "OnSubComponentOpened", aElement );
478 }
479
480 bool SubComponentManager::activateSubFrame( const OUString& _rName, const sal_Int32 _nComponentType,
481 const ElementOpenMode _eOpenMode, Reference< XComponent >& o_rComponent ) const
482 {
483 ::osl::MutexGuard aGuard( m_pData->getMutex() );
484
485 SubComponents::const_iterator pos = std::find_if(
486 m_pData->m_aComponents.begin(),
487 m_pData->m_aComponents.end(),
488 SubComponentMatch( _rName, _nComponentType, _eOpenMode )
489 );
490 if ( pos == m_pData->m_aComponents.end() )
491 // no component with this name/type/open mode
492 return false;
493
494 const Reference< XFrame > xFrame( pos->xFrame, UNO_SET_THROW );
495 const Reference< XTopWindow > xTopWindow( xFrame->getContainerWindow(), UNO_QUERY_THROW );
496 xTopWindow->toFront();
497
498 if ( pos->xModel.is() )
499 o_rComponent = pos->xModel.get();
500 else if ( pos->xController.is() )
501 o_rComponent = pos->xController.get();
502 else
503 o_rComponent = pos->xFrame.get();
504
505 return true;
506 }
507
508 bool SubComponentManager::closeSubFrames( std::u16string_view i_rName, const sal_Int32 _nComponentType )
509 {
510 ::osl::MutexGuard aGuard( m_pData->getMutex() );
511 ENSURE_OR_RETURN_FALSE( !i_rName.empty(), "SubComponentManager::closeSubFrames: illegal name!" );
512
513 SubComponents aWorkingCopy( m_pData->m_aComponents );
514 for (auto const& elem : aWorkingCopy)
515 {
516 if ( ( elem.sName != i_rName ) || ( elem.nComponentType != _nComponentType ) )
517 continue;
518
519 if ( !lcl_closeComponent(elem) )
520 return false;
521 }
522
523 return true;
524 }
525
527 OUString& o_rName, sal_Int32& o_rComponentType )
528 {
529 for (auto const& component : m_pData->m_aComponents)
530 {
531 if ( ( component.xModel.is()
532 && ( component.xModel == i_rComponent )
533 )
534 || ( component.xController.is()
535 && ( component.xController == i_rComponent )
536 )
537 || ( component.xFrame.is()
538 && ( component.xFrame == i_rComponent )
539 )
540 )
541 {
542 o_rName = component.sName;
543 o_rComponentType = component.nComponentType;
544 return true;
545 }
546 }
547 return false;
548 }
549
550} // namespace dbaui
551
552/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool closeSubFrames(std::u16string_view _rName, const sal_Int32 _nComponentType)
closes all frames of the given component
bool lookupSubComponent(const css::uno::Reference< css::lang::XComponent > &i_rComponent, OUString &o_rName, sal_Int32 &o_rComponentType)
searches for the given sub component
void onSubComponentOpened(const OUString &_rName, const sal_Int32 _nComponentType, const ElementOpenMode _eOpenMode, const css::uno::Reference< css::lang::XComponent > &_rxComponent)
std::unique_ptr< SubComponentManager_Data > m_pData
virtual ~SubComponentManager() override
bool activateSubFrame(const OUString &_rName, const sal_Int32 _nComponentType, const ElementOpenMode _eOpenMode, css::uno::Reference< css::lang::XComponent > &o_rComponent) const
activates (i.e.
virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent &evt) override
SubComponentManager(OApplicationController &_rController, const ::comphelper::SharedMutex &_rMutex)
css::uno::Sequence< css::uno::Reference< css::lang::XComponent > > getSubComponents() const
ControllerFrame_Data * m_pData
#define ENSURE_OR_RETURN_FALSE(c, m)
#define ENSURE_OR_THROW(c, m)
#define DBG_UNHANDLED_EXCEPTION(...)
@ Exception
comp
constexpr OUStringLiteral PROPERTY_NAME(u"Name")
SubComponentManager_Data(OApplicationController &_rController, ::comphelper::SharedMutex _aMutex)
OApplicationController & m_rController
mutable::comphelper::SharedMutex m_aMutex
Reference< XController > xController
the controller of the sub component. Must not be <NULL>
const ElementOpenMode m_eOpenMode
sal_Int32 nComponentType
type of the component - an ElementType value, except for relation design
const sal_Int32 m_nComponentType
ElementOpenMode eOpenMode
the mode in which the sub component has been opened
Reference< XPropertySet > xDocumentDefinitionProperties
the document definition which holds the component, if any; as PropertySet
Reference< XFrame > xFrame
the frame which the component resides in. Must not be <NULL>
Reference< XModel > xModel
the model of the sub component. Might be <NULL>
const OUString m_sName
Reference< XCommandProcessor > xComponentCommandProcessor
the document definition which holds the component, if any; as CommandProcessor
OUString aCommand
size_t pos