Avoid potential negative array index access to cached text.
[LibreOffice.git] / toolkit / source / controls / controlmodelcontainerbase.cxx
blob8c9bba0890de872280541b0758b67fbab98875b8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <controls/controlmodelcontainerbase.hxx>
22 #include <vcl/svapp.hxx>
23 #include <o3tl/safeint.hxx>
24 #include <osl/mutex.hxx>
25 #include <helper/property.hxx>
26 #include <helper/servicenames.hxx>
27 #include <controls/geometrycontrolmodel.hxx>
28 #include <toolkit/controls/unocontrols.hxx>
29 #include <controls/formattedcontrol.hxx>
30 #include <controls/roadmapcontrol.hxx>
31 #include <controls/tkscrollbar.hxx>
32 #include <controls/tabpagemodel.hxx>
33 #include <controls/stdtabcontroller.hxx>
34 #include <com/sun/star/awt/PosSize.hpp>
35 #include <com/sun/star/resource/XStringResourceResolver.hpp>
36 #include <com/sun/star/lang/XInitialization.hpp>
37 #include <cppuhelper/queryinterface.hxx>
38 #include <cppuhelper/weak.hxx>
39 #include <cppuhelper/weakagg.hxx>
40 #include <tools/debug.hxx>
41 #include <comphelper/diagnose_ex.hxx>
42 #include <vcl/outdev.hxx>
43 #include <comphelper/types.hxx>
45 #include "tree/treecontrol.hxx"
46 #include "grid/gridcontrol.hxx"
47 #include <controls/tabpagecontainer.hxx>
49 #include <map>
50 #include <algorithm>
51 #include <tools/urlobj.hxx>
52 #include <osl/file.hxx>
53 #include <sal/log.hxx>
54 #include <controls/dialogcontrol.hxx>
56 #include <helper/unopropertyarrayhelper.hxx>
57 #include "controlmodelcontainerbase_internal.hxx"
59 using namespace ::com::sun::star;
60 using namespace ::com::sun::star::uno;
61 using namespace ::com::sun::star::awt;
62 using namespace ::com::sun::star::lang;
63 using namespace ::com::sun::star::container;
64 using namespace ::com::sun::star::beans;
65 using namespace ::com::sun::star::util;
66 using namespace toolkit;
68 constexpr OUString PROPERTY_RESOURCERESOLVER = u"ResourceResolver"_ustr;
71 namespace
73 const Sequence< OUString >& lcl_getLanguageDependentProperties()
75 // note: properties must be sorted
76 static Sequence<OUString> s_aLanguageDependentProperties{ "HelpText", "Title" };
77 return s_aLanguageDependentProperties;
80 // functor for disposing a control model
81 struct DisposeControlModel
83 void operator()( Reference< XControlModel >& _rxModel )
85 try
87 ::comphelper::disposeComponent( _rxModel );
89 catch (const Exception&)
91 TOOLS_WARN_EXCEPTION("toolkit", "caught an exception while disposing a component" );
99 // functor for cloning a control model, and insertion into a target list
100 struct CloneControlModel
102 private:
103 ControlModelContainerBase::UnoControlModelHolderVector& m_rTargetVector;
105 public:
106 explicit CloneControlModel( ControlModelContainerBase::UnoControlModelHolderVector& _rTargetVector )
107 :m_rTargetVector( _rTargetVector )
111 void operator()( const ControlModelContainerBase::UnoControlModelHolder& _rSource )
113 // clone the source object
114 Reference< XCloneable > xCloneSource( _rSource.first, UNO_QUERY );
115 Reference< XControlModel > xClone( xCloneSource->createClone(), UNO_QUERY );
116 // add to target list
117 m_rTargetVector.emplace_back( xClone, _rSource.second );
122 // functor for comparing a XControlModel with a given reference
123 struct CompareControlModel
125 private:
126 Reference< XControlModel > m_xReference;
127 public:
128 explicit CompareControlModel( const Reference< XControlModel >& _rxReference ) : m_xReference( _rxReference ) { }
130 bool operator()( const ControlModelContainerBase::UnoControlModelHolder& _rCompare )
132 return _rCompare.first.get() == m_xReference.get();
136 constexpr OUString aTabIndexPropertyNameStr( u"TabIndex"_ustr );
138 ControlModelContainerBase::ControlModelContainerBase( const Reference< XComponentContext >& rxContext )
139 :ControlModelContainer_IBase( rxContext )
140 ,maContainerListeners( *this )
141 ,mbGroupsUpToDate( false )
142 ,m_nTabPageId(0)
144 ImplRegisterProperty(BASEPROPERTY_ENABLED);
147 ControlModelContainerBase::ControlModelContainerBase( const ControlModelContainerBase& rModel )
148 : ControlModelContainer_IBase( rModel )
149 , maContainerListeners( *this )
150 , mbGroupsUpToDate( false )
151 , m_nTabPageId( rModel.m_nTabPageId )
155 ControlModelContainerBase::~ControlModelContainerBase()
157 maModels.clear();
158 mbGroupsUpToDate = false;
161 Any ControlModelContainerBase::ImplGetDefaultValue( sal_uInt16 nPropId ) const
163 Any aAny;
165 switch ( nPropId )
167 case BASEPROPERTY_DEFAULTCONTROL:
168 aAny <<= OUString::createFromAscii( szServiceName_UnoControlDialog );
169 break;
170 default:
171 aAny = UnoControlModel::ImplGetDefaultValue( nPropId );
174 return aAny;
177 ::cppu::IPropertyArrayHelper& ControlModelContainerBase::getInfoHelper()
179 static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() );
180 return aHelper;
183 void SAL_CALL ControlModelContainerBase::dispose( )
186 // tell our listeners
188 std::unique_lock aGuard( m_aMutex );
190 EventObject aDisposeEvent;
191 aDisposeEvent.Source = static_cast< XAggregation* >( static_cast< ::cppu::OWeakAggObject* >( this ) );
193 maContainerListeners.disposeAndClear( aGuard, aDisposeEvent );
194 maChangeListeners.disposeAndClear( aGuard, aDisposeEvent );
198 // call the base class
199 UnoControlModel::dispose();
202 // dispose our child models
203 // for this, collect the models (we collect them from maModels, and this is modified when disposing children)
204 ::std::vector< Reference< XControlModel > > aChildModels( maModels.size() );
206 ::std::transform(
207 maModels.begin(), maModels.end(), // source range
208 aChildModels.begin(), // target location
209 []( const UnoControlModelHolder& rUnoControlModelHolder )
210 { return rUnoControlModelHolder.first; } // operation to apply -> select the XControlModel part
213 // now dispose
214 ::std::for_each( aChildModels.begin(), aChildModels.end(), DisposeControlModel() );
215 aChildModels.clear();
217 mbGroupsUpToDate = false;
220 // XMultiPropertySet
221 Reference< XPropertySetInfo > ControlModelContainerBase::getPropertySetInfo( )
223 static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
224 return xInfo;
226 void ControlModelContainerBase::Clone_Impl(ControlModelContainerBase& _rClone) const
228 // clone all children
229 ::std::for_each(
230 maModels.begin(), maModels.end(),
231 CloneControlModel( _rClone.maModels )
234 rtl::Reference<UnoControlModel> ControlModelContainerBase::Clone() const
236 // clone the container itself
237 rtl::Reference<ControlModelContainerBase> pClone = new ControlModelContainerBase( *this );
238 Clone_Impl(*pClone);
240 return pClone;
243 ControlModelContainerBase::UnoControlModelHolderVector::iterator ControlModelContainerBase::ImplFindElement( std::u16string_view rName )
245 return ::std::find_if( maModels.begin(), maModels.end(), [&](const UnoControlModelHolder& elem) { return elem.second == rName; });
248 // ::XMultiServiceFactory
249 Reference< XInterface > ControlModelContainerBase::createInstance( const OUString& aServiceSpecifier )
251 SolarMutexGuard aGuard;
253 rtl::Reference<OGeometryControlModel_Base> pNewModel;
255 if ( aServiceSpecifier == "com.sun.star.awt.UnoControlEditModel" )
256 pNewModel = new OGeometryControlModel< UnoControlEditModel >( m_xContext );
257 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFormattedFieldModel" )
258 pNewModel = new OGeometryControlModel< UnoControlFormattedFieldModel >( m_xContext);
259 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFileControlModel" )
260 pNewModel = new OGeometryControlModel< UnoControlFileControlModel >( m_xContext );
261 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlButtonModel" )
262 pNewModel = new OGeometryControlModel< UnoControlButtonModel >( m_xContext );
263 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlImageControlModel" )
264 pNewModel = new OGeometryControlModel< UnoControlImageControlModel >( m_xContext );
265 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlRadioButtonModel" )
266 pNewModel = new OGeometryControlModel< UnoControlRadioButtonModel >( m_xContext );
267 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlCheckBoxModel" )
268 pNewModel = new OGeometryControlModel< UnoControlCheckBoxModel >( m_xContext );
269 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFixedHyperlinkModel" )
270 pNewModel = new OGeometryControlModel< UnoControlFixedHyperlinkModel >( m_xContext );
271 else if ( aServiceSpecifier == "stardiv.vcl.controlmodel.FixedText" )
272 pNewModel = new OGeometryControlModel< UnoControlFixedTextModel >( m_xContext );
273 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlGroupBoxModel" )
274 pNewModel = new OGeometryControlModel< UnoControlGroupBoxModel >( m_xContext );
275 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlListBoxModel" )
276 pNewModel = new OGeometryControlModel< UnoControlListBoxModel >( m_xContext );
277 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlComboBoxModel" )
278 pNewModel = new OGeometryControlModel< UnoControlComboBoxModel >( m_xContext );
279 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlDateFieldModel" )
280 pNewModel = new OGeometryControlModel< UnoControlDateFieldModel >( m_xContext );
281 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlTimeFieldModel" )
282 pNewModel = new OGeometryControlModel< UnoControlTimeFieldModel >( m_xContext );
283 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlNumericFieldModel" )
284 pNewModel = new OGeometryControlModel< UnoControlNumericFieldModel >( m_xContext );
285 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlCurrencyFieldModel" )
286 pNewModel = new OGeometryControlModel< UnoControlCurrencyFieldModel >( m_xContext );
287 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlPatternFieldModel" )
288 pNewModel = new OGeometryControlModel< UnoControlPatternFieldModel >( m_xContext );
289 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlProgressBarModel" )
290 pNewModel = new OGeometryControlModel< UnoControlProgressBarModel >( m_xContext );
291 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlScrollBarModel" )
292 pNewModel = new OGeometryControlModel< UnoControlScrollBarModel >( m_xContext );
293 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFixedLineModel" )
294 pNewModel = new OGeometryControlModel< UnoControlFixedLineModel >( m_xContext );
295 else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlRoadmapModel" )
296 pNewModel = new OGeometryControlModel< UnoControlRoadmapModel >( m_xContext );
297 else if ( aServiceSpecifier == "com.sun.star.awt.tree.TreeControlModel" )
298 pNewModel = new OGeometryControlModel< UnoTreeModel >( m_xContext );
299 else if ( aServiceSpecifier == "com.sun.star.awt.grid.UnoControlGridModel" )
300 pNewModel = new OGeometryControlModel< UnoGridModel >( m_xContext );
301 else if ( aServiceSpecifier == "com.sun.star.awt.tab.UnoControlTabPageContainerModel" )
302 pNewModel = new OGeometryControlModel< UnoControlTabPageContainerModel >( m_xContext );
303 else if ( aServiceSpecifier == "com.sun.star.awt.UnoMultiPageModel" )
304 pNewModel = new OGeometryControlModel< UnoMultiPageModel >( m_xContext );
305 else if ( aServiceSpecifier == "com.sun.star.awt.tab.UnoControlTabPageModel" )
306 pNewModel = new OGeometryControlModel< UnoControlTabPageModel >( m_xContext );
307 else if ( aServiceSpecifier == "com.sun.star.awt.UnoPageModel" )
308 pNewModel = new OGeometryControlModel< UnoPageModel >( m_xContext );
309 else if ( aServiceSpecifier == "com.sun.star.awt.UnoFrameModel" )
310 pNewModel = new OGeometryControlModel< UnoFrameModel >( m_xContext );
312 if ( !pNewModel )
314 Reference< XInterface > xObject = m_xContext->getServiceManager()->createInstanceWithContext(aServiceSpecifier, m_xContext);
315 Reference< XServiceInfo > xSI( xObject, UNO_QUERY );
316 Reference< XCloneable > xCloneAccess( xSI, UNO_QUERY );
317 Reference< XAggregation > xAgg( xCloneAccess, UNO_QUERY );
318 if ( xAgg.is() )
320 if ( xSI->supportsService("com.sun.star.awt.UnoControlModel") )
322 // release 3 of the 4 references we have to the object
323 xAgg.clear();
324 xSI.clear();
325 xObject.clear();
327 pNewModel = new OCommonGeometryControlModel( xCloneAccess, aServiceSpecifier );
332 return cppu::getXWeak(pNewModel.get());
335 Reference< XInterface > ControlModelContainerBase::createInstanceWithArguments( const OUString& ServiceSpecifier, const Sequence< Any >& i_arguments )
337 const Reference< XInterface > xInstance( createInstance( ServiceSpecifier ) );
338 const Reference< XInitialization > xInstanceInit( xInstance, UNO_QUERY );
339 ENSURE_OR_RETURN( xInstanceInit.is(), "ControlModelContainerBase::createInstanceWithArguments: can't pass the arguments!", xInstance );
340 xInstanceInit->initialize( i_arguments );
341 return xInstance;
344 Sequence< OUString > ControlModelContainerBase::getAvailableServiceNames()
346 return { "com.sun.star.awt.UnoControlEditModel",
347 "com.sun.star.awt.UnoControlFormattedFieldModel",
348 "com.sun.star.awt.UnoControlFileControlModel",
349 "com.sun.star.awt.UnoControlButtonModel",
350 "com.sun.star.awt.UnoControlImageControlModel",
351 "com.sun.star.awt.UnoControlRadioButtonModel",
352 "com.sun.star.awt.UnoControlCheckBoxModel",
353 "com.sun.star.awt.UnoControlFixedTextModel",
354 "com.sun.star.awt.UnoControlGroupBoxModel",
355 "com.sun.star.awt.UnoControlListBoxModel",
356 "com.sun.star.awt.UnoControlComboBoxModel",
357 "com.sun.star.awt.UnoControlDateFieldModel",
358 "com.sun.star.awt.UnoControlTimeFieldModel",
359 "com.sun.star.awt.UnoControlNumericFieldModel",
360 "com.sun.star.awt.UnoControlCurrencyFieldModel",
361 "com.sun.star.awt.UnoControlPatternFieldModel",
362 "com.sun.star.awt.UnoControlProgressBarModel",
363 "com.sun.star.awt.UnoControlScrollBarModel",
364 "com.sun.star.awt.UnoControlFixedLineModel",
365 "com.sun.star.awt.UnoControlRoadmapModel",
366 "com.sun.star.awt.tree.TreeControlModel",
367 "com.sun.star.awt.grid.UnoControlGridModel",
368 "com.sun.star.awt.UnoControlFixedHyperlinkModel",
369 "com.sun.star.awt.tab.UnoControlTabPageContainerModel",
370 "com.sun.star.awt.tab.UnoControlTabPageModel",
371 "com.sun.star.awt.UnoMultiPageModel",
372 "com.sun.star.awt.UnoFrameModel"
376 // XContainer
377 void ControlModelContainerBase::addContainerListener( const Reference< XContainerListener >& l )
379 maContainerListeners.addInterface( l );
382 void ControlModelContainerBase::removeContainerListener( const Reference< XContainerListener >& l )
384 maContainerListeners.removeInterface( l );
387 // XElementAccess
388 Type ControlModelContainerBase::getElementType()
390 Type aType = cppu::UnoType<XControlModel>::get();
391 return aType;
394 sal_Bool ControlModelContainerBase::hasElements()
396 return !maModels.empty();
399 // XNameContainer, XNameReplace, XNameAccess
400 void ControlModelContainerBase::replaceByName( const OUString& aName, const Any& aElement )
402 SolarMutexGuard aGuard;
404 Reference< XControlModel > xNewModel;
405 aElement >>= xNewModel;
406 if ( !xNewModel.is() )
407 throw IllegalArgumentException();
409 UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName );
410 if ( maModels.end() == aElementPos )
411 throw NoSuchElementException();
412 // Dialog behaviour is to have all containee names unique (MSO Userform is the same)
413 // With container controls you could have constructed an existing hierarchy and are now
414 // add this to an existing container, in this case a name nested in the containment
415 // hierarchy of the added control could contain a name clash, if we have access to the
416 // list of global names then recursively check for previously existing names (we need
417 // to do this obviously before the 'this' objects container is updated)
418 Reference< XNameContainer > xAllChildren( getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY );
419 if ( xAllChildren.is() )
421 // remove old control (and children) from global list of containers
422 updateUserFormChildren( xAllChildren, aName, Remove, uno::Reference< XControlModel >() );
423 // Add new control (and containers if they exist)
424 updateUserFormChildren( xAllChildren, aName, Insert, xNewModel );
426 // stop listening at the old model
427 stopControlListening( aElementPos->first );
428 Reference< XControlModel > xReplaced( aElementPos->first );
429 // remember the new model, and start listening
430 aElementPos->first = xNewModel;
431 startControlListening( xNewModel );
433 ContainerEvent aEvent;
434 aEvent.Source = *this;
435 aEvent.Element = aElement;
436 aEvent.ReplacedElement <<= xReplaced;
437 aEvent.Accessor <<= aName;
439 // notify the container listener
440 maContainerListeners.elementReplaced( aEvent );
442 // our "tab controller model" has potentially changed -> notify this
443 implNotifyTabModelChange( aName );
446 Any ControlModelContainerBase::getByName( const OUString& aName )
448 UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName );
449 if ( maModels.end() == aElementPos )
450 throw NoSuchElementException();
452 return Any( aElementPos->first );
455 Sequence< OUString > ControlModelContainerBase::getElementNames()
457 Sequence< OUString > aNames( maModels.size() );
459 ::std::transform(
460 maModels.begin(), maModels.end(), // source range
461 aNames.getArray(), // target range
462 []( const UnoControlModelHolder& rUnoControlModelHolder )
463 { return rUnoControlModelHolder.second; } // operator to apply: select the second element (the name)
466 return aNames;
469 sal_Bool ControlModelContainerBase::hasByName( const OUString& aName )
471 return maModels.end() != ImplFindElement( aName );
474 void ControlModelContainerBase::insertByName( const OUString& aName, const Any& aElement )
476 SolarMutexGuard aGuard;
478 Reference< XControlModel > xM;
479 aElement >>= xM;
481 if ( xM.is() )
483 Reference< beans::XPropertySet > xProps( xM, UNO_QUERY );
484 if ( xProps.is() )
487 Reference< beans::XPropertySetInfo > xPropInfo = xProps->getPropertySetInfo();
489 const OUString& sImageSourceProperty = GetPropertyName( BASEPROPERTY_IMAGEURL );
490 if ( xPropInfo->hasPropertyByName( sImageSourceProperty ) && ImplHasProperty(BASEPROPERTY_DIALOGSOURCEURL) )
492 Any aUrl = xProps->getPropertyValue( sImageSourceProperty );
494 OUString absoluteUrl =
495 getPhysicalLocation( getPropertyValue( GetPropertyName( BASEPROPERTY_DIALOGSOURCEURL ) ), aUrl );
497 aUrl <<= absoluteUrl;
499 xProps->setPropertyValue( sImageSourceProperty , aUrl );
505 if ( aName.isEmpty() || !xM.is() )
506 throw IllegalArgumentException();
508 UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName );
509 if ( maModels.end() != aElementPos )
510 throw ElementExistException();
512 // Dialog behaviour is to have all containee names unique (MSO Userform is the same)
513 // With container controls you could have constructed an existing hierarchy and are now
514 // add this to an existing container, in this case a name nested in the containment
515 // hierarchy of the added control could contain a name clash, if we have access to the
516 // list of global names then we need to recursively check for previously existing
517 // names (we need to do this obviously before the 'this' objects container is updated)
518 // remove old control (and children) from global list of containers
519 Reference< XNameContainer > xAllChildren( getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY );
521 if ( xAllChildren.is() )
522 updateUserFormChildren( xAllChildren, aName, Insert, xM );
523 maModels.emplace_back( xM, aName );
524 mbGroupsUpToDate = false;
525 startControlListening( xM );
527 ContainerEvent aEvent;
528 aEvent.Source = *this;
529 aEvent.Element = aElement;
530 aEvent.Accessor <<= aName;
531 maContainerListeners.elementInserted( aEvent );
533 // our "tab controller model" has potentially changed -> notify this
534 implNotifyTabModelChange( aName );
537 void ControlModelContainerBase::removeByName( const OUString& aName )
539 SolarMutexGuard aGuard;
541 UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName );
542 if ( maModels.end() == aElementPos )
543 throw NoSuchElementException();
545 // Dialog behaviour is to have all containee names unique (MSO Userform is the same)
546 // With container controls you could have constructed an existing hierarchy and are now
547 // removing this control from an existing container, in this case all nested names in
548 // the containment hierarchy of the control to be removed need to be removed from the global
549 // names cache (we need to do this obviously before the 'this' objects container is updated)
550 Reference< XNameContainer > xAllChildren( getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY );
551 if ( xAllChildren.is() )
552 updateUserFormChildren( xAllChildren, aName, Remove, uno::Reference< XControlModel >() );
554 ContainerEvent aEvent;
555 aEvent.Source = *this;
556 aEvent.Element <<= aElementPos->first;
557 aEvent.Accessor <<= aName;
558 maContainerListeners.elementRemoved( aEvent );
560 stopControlListening( aElementPos->first );
561 Reference< XPropertySet > xPS( aElementPos->first, UNO_QUERY );
562 maModels.erase( aElementPos );
563 mbGroupsUpToDate = false;
565 if ( xPS.is() )
569 xPS->setPropertyValue( PROPERTY_RESOURCERESOLVER, Any( Reference< resource::XStringResourceResolver >() ) );
571 catch (const Exception&)
573 DBG_UNHANDLED_EXCEPTION("toolkit.controls");
577 // our "tab controller model" has potentially changed -> notify this
578 implNotifyTabModelChange( aName );
582 sal_Bool SAL_CALL ControlModelContainerBase::getGroupControl( )
584 return true;
588 void SAL_CALL ControlModelContainerBase::setGroupControl( sal_Bool )
590 SAL_WARN("toolkit", "explicit grouping not supported" );
594 void SAL_CALL ControlModelContainerBase::setControlModels( const Sequence< Reference< XControlModel > >& _rControls )
596 SolarMutexGuard aGuard;
598 // set the tab indexes according to the order of models in the sequence
600 sal_Int16 nTabIndex = 1;
602 for ( auto const & control : _rControls )
604 // look up the control in our own structure. This is to prevent invalid arguments
605 UnoControlModelHolderVector::const_iterator aPos =
606 ::std::find_if(
607 maModels.begin(), maModels.end(),
608 CompareControlModel( control )
610 if ( maModels.end() != aPos )
612 // okay, this is an existent model
613 // now set the TabIndex property (if applicable)
614 Reference< XPropertySet > xProps( aPos->first, UNO_QUERY );
615 Reference< XPropertySetInfo > xPSI;
616 if ( xProps.is() )
617 xPSI = xProps->getPropertySetInfo();
618 if ( xPSI.is() && xPSI->hasPropertyByName( aTabIndexPropertyNameStr ) )
619 xProps->setPropertyValue( aTabIndexPropertyNameStr, Any( nTabIndex++ ) );
621 mbGroupsUpToDate = false;
626 typedef ::std::multimap< sal_Int32, Reference< XControlModel > > MapIndexToModel;
629 Sequence< Reference< XControlModel > > SAL_CALL ControlModelContainerBase::getControlModels( )
631 SolarMutexGuard aGuard;
633 MapIndexToModel aSortedModels;
634 // will be the sorted container of all models which have a tab index property
635 ::std::vector< Reference< XControlModel > > aUnindexedModels;
636 // will be the container of all models which do not have a tab index property
638 for ( const auto& rModel : maModels )
640 Reference< XControlModel > xModel( rModel.first );
642 // see if the model has a TabIndex property
643 Reference< XPropertySet > xControlProps( xModel, UNO_QUERY );
644 Reference< XPropertySetInfo > xPSI;
645 if ( xControlProps.is() )
646 xPSI = xControlProps->getPropertySetInfo( );
647 DBG_ASSERT( xPSI.is(), "ControlModelContainerBase::getControlModels: invalid child model!" );
649 // has it?
650 if ( xPSI.is() && xPSI->hasPropertyByName( aTabIndexPropertyNameStr ) )
651 { // yes
652 sal_Int32 nTabIndex = -1;
653 xControlProps->getPropertyValue( aTabIndexPropertyNameStr ) >>= nTabIndex;
655 aSortedModels.emplace( nTabIndex, xModel );
657 else if ( xModel.is() )
658 // no, it hasn't, but we have to include it, anyway
659 aUnindexedModels.push_back( xModel );
662 // okay, here we have a container of all our models, sorted by tab index,
663 // plus a container of "unindexed" models
664 // -> merge them
665 Sequence< Reference< XControlModel > > aReturn( aUnindexedModels.size() + aSortedModels.size() );
666 ::std::transform(
667 aSortedModels.begin(), aSortedModels.end(),
668 ::std::copy( aUnindexedModels.begin(), aUnindexedModels.end(), aReturn.getArray() ),
669 [] ( const MapIndexToModel::value_type& entryIndexToModel )
670 { return entryIndexToModel.second; }
673 return aReturn;
677 void SAL_CALL ControlModelContainerBase::setGroup( const Sequence< Reference< XControlModel > >&, const OUString& )
679 // not supported. We have only implicit grouping:
680 // We only have a sequence of control models, and we _know_ (yes, that's a HACK relying on
681 // implementation details) that VCL does grouping according to the order of controls automatically
682 // At least VCL does this for all we're interested in: Radio buttons.
683 SAL_WARN("toolkit", "grouping not supported" );
686 ////----- XInitialization -------------------------------------------------------------------
687 void SAL_CALL ControlModelContainerBase::initialize (const Sequence<Any>& rArguments)
689 if ( rArguments.getLength() == 1 )
691 sal_Int16 nPageId = -1;
692 if ( !( rArguments[ 0 ] >>= nPageId ))
693 throw lang::IllegalArgumentException();
694 m_nTabPageId = nPageId;
696 else
697 m_nTabPageId = -1;
699 ::sal_Int16 SAL_CALL ControlModelContainerBase::getTabPageID()
701 return m_nTabPageId;
703 sal_Bool SAL_CALL ControlModelContainerBase::getEnabled()
705 SolarMutexGuard aGuard;
706 bool bEnabled = false;
707 getPropertyValue(GetPropertyName(BASEPROPERTY_ENABLED)) >>= bEnabled;
708 return bEnabled;
710 void SAL_CALL ControlModelContainerBase::setEnabled( sal_Bool _enabled )
712 SolarMutexGuard aGuard;
713 setPropertyValue(GetPropertyName(BASEPROPERTY_ENABLED), Any(_enabled));
715 OUString SAL_CALL ControlModelContainerBase::getTitle()
717 SolarMutexGuard aGuard;
718 OUString sTitle;
719 getPropertyValue(GetPropertyName(BASEPROPERTY_TITLE)) >>= sTitle;
720 return sTitle;
722 void SAL_CALL ControlModelContainerBase::setTitle( const OUString& _title )
724 SolarMutexGuard aGuard;
725 setPropertyValue(GetPropertyName(BASEPROPERTY_TITLE),Any(_title));
727 OUString SAL_CALL ControlModelContainerBase::getImageURL()
729 return m_sImageURL;
731 void SAL_CALL ControlModelContainerBase::setImageURL( const OUString& _imageurl )
733 m_sImageURL = _imageurl;
734 SolarMutexGuard aGuard;
735 setPropertyValue(GetPropertyName(BASEPROPERTY_IMAGEURL), Any(_imageurl));
737 OUString SAL_CALL ControlModelContainerBase::getToolTip()
739 return m_sTooltip;
741 void SAL_CALL ControlModelContainerBase::setToolTip( const OUString& _tooltip )
743 m_sTooltip = _tooltip;
747 namespace
749 enum GroupingMachineState
751 eLookingForGroup,
752 eExpandingGroup
756 sal_Int32 lcl_getDialogStep( const Reference< XControlModel >& _rxModel )
758 sal_Int32 nStep = 0;
761 Reference< XPropertySet > xModelProps( _rxModel, UNO_QUERY );
762 xModelProps->getPropertyValue( u"Step"_ustr ) >>= nStep;
764 catch (const Exception&)
766 TOOLS_WARN_EXCEPTION("toolkit", "caught an exception while determining the dialog page" );
768 return nStep;
773 sal_Int32 SAL_CALL ControlModelContainerBase::getGroupCount( )
775 SolarMutexGuard aGuard;
777 implUpdateGroupStructure();
779 return maGroups.size();
783 void SAL_CALL ControlModelContainerBase::getGroup( sal_Int32 _nGroup, Sequence< Reference< XControlModel > >& _rGroup, OUString& _rName )
785 SolarMutexGuard aGuard;
787 implUpdateGroupStructure();
789 if ( ( _nGroup < 0 ) || ( o3tl::make_unsigned(_nGroup) >= maGroups.size() ) )
791 SAL_WARN("toolkit", "invalid argument and I am not allowed to throw exception!" );
792 _rGroup.realloc( 0 );
793 _rName.clear();
795 else
797 AllGroups::const_iterator aGroupPos = maGroups.begin() + _nGroup;
798 _rGroup.realloc( aGroupPos->size() );
799 // copy the models
800 ::std::copy( aGroupPos->begin(), aGroupPos->end(), _rGroup.getArray() );
801 // give the group a name
802 _rName = OUString::number( _nGroup );
807 void SAL_CALL ControlModelContainerBase::getGroupByName( const OUString& _rName, Sequence< Reference< XControlModel > >& _rGroup )
809 SolarMutexGuard aGuard;
811 OUString sDummyName;
812 getGroup( _rName.toInt32( ), _rGroup, sDummyName );
816 void SAL_CALL ControlModelContainerBase::addChangesListener( const Reference< XChangesListener >& _rxListener )
818 std::unique_lock g(m_aMutex);
819 maChangeListeners.addInterface( g, _rxListener );
823 void SAL_CALL ControlModelContainerBase::removeChangesListener( const Reference< XChangesListener >& _rxListener )
825 std::unique_lock g(m_aMutex);
826 maChangeListeners.removeInterface( g, _rxListener );
830 void ControlModelContainerBase::implNotifyTabModelChange( const OUString& _rAccessor )
832 // multiplex to our change listeners:
833 // the changes event
834 ChangesEvent aEvent;
835 aEvent.Source = *this;
836 aEvent.Base <<= aEvent.Source; // the "base of the changes root" is also ourself
837 aEvent.Changes.realloc( 1 ); // exactly one change
838 aEvent.Changes.getArray()[ 0 ].Accessor <<= _rAccessor;
841 std::unique_lock g(m_aMutex);
842 std::vector< Reference< css::util::XChangesListener > > aChangeListeners( maChangeListeners.getElements(g) );
843 g.unlock();
844 for ( const auto& rListener : aChangeListeners )
845 rListener->changesOccurred( aEvent );
849 void ControlModelContainerBase::implUpdateGroupStructure()
851 if ( mbGroupsUpToDate )
852 // nothing to do
853 return;
855 // conditions for a group:
856 // * all elements of the group are radio buttons
857 // * all elements of the group are on the same dialog page
858 // * in the overall control order (determined by the tab index), all elements are subsequent
860 maGroups.clear();
862 const Sequence< Reference< XControlModel > > aControlModels = getControlModels();
864 // in extreme we have as much groups as controls
865 maGroups.reserve( aControlModels.getLength() );
867 GroupingMachineState eState = eLookingForGroup; // the current state of our machine
868 Reference< XServiceInfo > xModelSI; // for checking for a radio button
869 AllGroups::iterator aCurrentGroup = maGroups.end(); // the group which we're currently building
870 sal_Int32 nCurrentGroupStep = -1; // the step which all controls of the current group belong to
873 for ( const Reference< XControlModel >& rControlModel : aControlModels )
875 // we'll need this in every state
876 xModelSI.set(rControlModel, css::uno::UNO_QUERY);
877 // is it a radio button?
878 bool bIsRadioButton = xModelSI.is() && xModelSI->supportsService( "com.sun.star.awt.UnoControlRadioButtonModel" );
880 switch ( eState )
882 case eLookingForGroup:
884 if ( !bIsRadioButton )
885 // this is no radio button -> still looking for the beginning of a group
886 continue;
887 // the current model is a radio button
888 // -> we found the beginning of a new group
889 // create the place for this group
890 size_t nGroups = maGroups.size();
891 maGroups.resize( nGroups + 1 );
892 aCurrentGroup = maGroups.begin() + nGroups;
893 // and add the (only, til now) member
894 aCurrentGroup->push_back( rControlModel );
896 // get the step which all controls of this group now have to belong to
897 nCurrentGroupStep = lcl_getDialogStep( rControlModel );
898 // new state: looking for further members
899 eState = eExpandingGroup;
902 break;
904 case eExpandingGroup:
906 if ( !bIsRadioButton )
907 { // no radio button -> the group is done
908 aCurrentGroup = maGroups.end();
909 eState = eLookingForGroup;
910 continue;
913 // it is a radio button - is it on the proper page?
914 const sal_Int32 nThisModelStep = lcl_getDialogStep( rControlModel );
915 if ( ( nThisModelStep == nCurrentGroupStep ) // the current button is on the same dialog page
916 || ( 0 == nThisModelStep ) // the current button appears on all pages
919 // -> it belongs to the same group
920 aCurrentGroup->push_back( rControlModel );
921 // state still is eExpandingGroup - we're looking for further elements
922 eState = eExpandingGroup;
924 continue;
927 // it's a radio button, but on a different page
928 // -> we open a new group for it
931 // open a new group
932 size_t nGroups = maGroups.size();
933 maGroups.resize( nGroups + 1 );
934 aCurrentGroup = maGroups.begin() + nGroups;
935 // and add the (only, til now) member
936 aCurrentGroup->push_back( rControlModel );
938 nCurrentGroupStep = nThisModelStep;
940 // state is the same: we still are looking for further elements of the current group
941 eState = eExpandingGroup;
943 break;
947 mbGroupsUpToDate = true;
951 void SAL_CALL ControlModelContainerBase::propertyChange( const PropertyChangeEvent& _rEvent )
953 SolarMutexGuard aGuard;
955 DBG_ASSERT( _rEvent.PropertyName == "TabIndex",
956 "ControlModelContainerBase::propertyChange: not listening for this property!" );
958 // the accessor for the changed element
959 OUString sAccessor;
960 UnoControlModelHolderVector::const_iterator aPos =
961 ::std::find_if(
962 maModels.begin(), maModels.end(),
963 CompareControlModel( Reference< XControlModel >( _rEvent.Source, UNO_QUERY ) )
965 OSL_ENSURE( maModels.end() != aPos, "ControlModelContainerBase::propertyChange: don't know this model!" );
966 if ( maModels.end() != aPos )
967 sAccessor = aPos->second;
969 // our groups are not up-to-date
970 mbGroupsUpToDate = false;
972 // notify
973 implNotifyTabModelChange( sAccessor );
977 void SAL_CALL ControlModelContainerBase::disposing( const EventObject& /*rEvent*/ )
982 void ControlModelContainerBase::startControlListening( const Reference< XControlModel >& _rxChildModel )
984 SolarMutexGuard aGuard;
986 Reference< XPropertySet > xModelProps( _rxChildModel, UNO_QUERY );
987 Reference< XPropertySetInfo > xPSI;
988 if ( xModelProps.is() )
989 xPSI = xModelProps->getPropertySetInfo();
991 if ( xPSI.is() && xPSI->hasPropertyByName( aTabIndexPropertyNameStr ) )
992 xModelProps->addPropertyChangeListener( aTabIndexPropertyNameStr, this );
996 void ControlModelContainerBase::stopControlListening( const Reference< XControlModel >& _rxChildModel )
998 SolarMutexGuard aGuard;
1000 Reference< XPropertySet > xModelProps( _rxChildModel, UNO_QUERY );
1001 Reference< XPropertySetInfo > xPSI;
1002 if ( xModelProps.is() )
1003 xPSI = xModelProps->getPropertySetInfo();
1005 if ( xPSI.is() && xPSI->hasPropertyByName( aTabIndexPropertyNameStr ) )
1006 xModelProps->removePropertyChangeListener( aTabIndexPropertyNameStr, this );
1010 // = class ResourceListener
1013 ResourceListener::ResourceListener(
1014 const Reference< util::XModifyListener >& rListener ) :
1015 m_xListener( rListener ),
1016 m_bListening( false )
1020 ResourceListener::~ResourceListener()
1024 // XInterface
1025 Any SAL_CALL ResourceListener::queryInterface( const Type& rType )
1027 Any a = ::cppu::queryInterface(
1028 rType ,
1029 static_cast< XModifyListener* >( this ),
1030 static_cast< XEventListener* >( this ));
1032 if ( a.hasValue() )
1033 return a;
1035 return OWeakObject::queryInterface( rType );
1038 void SAL_CALL ResourceListener::acquire() noexcept
1040 OWeakObject::acquire();
1043 void SAL_CALL ResourceListener::release() noexcept
1045 OWeakObject::release();
1048 void ResourceListener::startListening(
1049 const Reference< resource::XStringResourceResolver >& rResource )
1052 // --- SAFE ---
1053 std::unique_lock aGuard( m_aMutex );
1054 bool bListening( m_bListening );
1055 bool bResourceSet( m_xResource.is() );
1056 aGuard.unlock();
1057 // --- SAFE ---
1059 if ( bListening && bResourceSet )
1060 stopListening();
1062 // --- SAFE ---
1063 aGuard.lock();
1064 m_xResource = rResource;
1065 aGuard.unlock();
1066 // --- SAFE ---
1069 if ( !rResource.is() )
1070 return;
1074 rResource->addModifyListener( this );
1076 // --- SAFE ---
1077 std::scoped_lock aGuard( m_aMutex );
1078 m_bListening = true;
1079 // --- SAFE ---
1081 catch (const RuntimeException&)
1083 throw;
1085 catch (const Exception&)
1090 void ResourceListener::stopListening()
1092 Reference< util::XModifyBroadcaster > xModifyBroadcaster;
1094 // --- SAFE ---
1095 std::unique_lock aGuard( m_aMutex );
1096 if ( m_bListening && m_xResource.is() )
1097 xModifyBroadcaster = m_xResource;
1098 aGuard.unlock();
1099 // --- SAFE ---
1101 if ( !xModifyBroadcaster.is() )
1102 return;
1106 // --- SAFE ---
1107 aGuard.lock();
1108 m_bListening = false;
1109 m_xResource.clear();
1110 aGuard.unlock();
1111 // --- SAFE ---
1113 xModifyBroadcaster->removeModifyListener( this );
1115 catch (const RuntimeException&)
1117 throw;
1119 catch (const Exception&)
1124 // XModifyListener
1125 void SAL_CALL ResourceListener::modified(
1126 const lang::EventObject& aEvent )
1128 Reference< util::XModifyListener > xListener;
1130 // --- SAFE ---
1131 std::unique_lock aGuard( m_aMutex );
1132 xListener = m_xListener;
1133 aGuard.unlock();
1134 // --- SAFE ---
1136 if ( !xListener.is() )
1137 return;
1141 xListener->modified( aEvent );
1143 catch (const RuntimeException&)
1145 throw;
1147 catch (const Exception&)
1152 // XEventListener
1153 void SAL_CALL ResourceListener::disposing(
1154 const EventObject& Source )
1156 Reference< lang::XEventListener > xListener;
1157 Reference< resource::XStringResourceResolver > xResource;
1159 // --- SAFE ---
1160 std::unique_lock aGuard( m_aMutex );
1161 Reference< XInterface > xIfacRes( m_xResource, UNO_QUERY );
1162 Reference< XInterface > xIfacList( m_xListener, UNO_QUERY );
1163 aGuard.unlock();
1164 // --- SAFE ---
1166 if ( Source.Source == xIfacRes )
1168 // --- SAFE ---
1169 aGuard.lock();
1170 m_bListening = false;
1171 xResource = m_xResource;
1172 xListener = m_xListener;
1173 m_xResource.clear();
1174 aGuard.unlock();
1175 // --- SAFE ---
1177 if ( xListener.is() )
1181 xListener->disposing( Source );
1183 catch (const RuntimeException&)
1185 throw;
1187 catch (const Exception&)
1192 else if ( Source.Source == xIfacList )
1194 // --- SAFE ---
1195 aGuard.lock();
1196 m_bListening = false;
1197 xListener = m_xListener;
1198 xResource = m_xResource;
1199 m_xResource.clear();
1200 m_xListener.clear();
1201 aGuard.unlock();
1202 // --- SAFE ---
1204 // Remove ourself as listener from resource resolver
1205 if ( xResource.is() )
1209 xResource->removeModifyListener( this );
1211 catch (const RuntimeException&)
1213 throw;
1215 catch (const Exception&)
1224 ControlContainerBase::ControlContainerBase( const Reference< XComponentContext >& rxContext )
1225 :m_xContext(rxContext)
1226 ,mbSizeModified(false)
1227 ,mbPosModified(false)
1229 maComponentInfos.nWidth = 280;
1230 maComponentInfos.nHeight = 400;
1231 mxListener = new ResourceListener( Reference< util::XModifyListener >(this) );
1234 ControlContainerBase::~ControlContainerBase()
1238 void ControlContainerBase::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer )
1240 SolarMutexGuard aGuard;
1241 UnoControlContainer::createPeer( rxToolkit, rParentPeer );
1244 void ControlContainerBase::ImplInsertControl( Reference< XControlModel > const & rxModel, const OUString& rName )
1246 Reference< XPropertySet > xP( rxModel, UNO_QUERY );
1248 OUString aDefCtrl;
1249 xP->getPropertyValue( GetPropertyName( BASEPROPERTY_DEFAULTCONTROL ) ) >>= aDefCtrl;
1250 Reference < XControl > xCtrl( m_xContext->getServiceManager()->createInstanceWithContext(aDefCtrl, m_xContext), UNO_QUERY );
1252 DBG_ASSERT( xCtrl.is(), "ControlContainerBase::ImplInsertControl: could not create the control!" );
1253 if ( xCtrl.is() )
1255 xCtrl->setModel( rxModel );
1256 addControl( rName, xCtrl );
1257 // will implicitly call addingControl, where we can add the PropertiesChangeListener to the model
1258 // (which we formerly did herein)
1259 // 08.01.2001 - 96008 - fs@openoffice.org
1261 ImplSetPosSize( xCtrl );
1265 void ControlContainerBase::ImplRemoveControl( Reference< XControlModel > const & rxModel )
1267 Sequence< Reference< XControl > > aControls = getControls();
1268 Reference< XControl > xCtrl = StdTabController::FindControl( aControls, rxModel );
1269 if ( xCtrl.is() )
1271 removeControl( xCtrl );
1274 xCtrl->dispose();
1276 catch (const Exception&)
1278 DBG_UNHANDLED_EXCEPTION("toolkit.controls");
1283 void ControlContainerBase::ImplSetPosSize( Reference< XControl >& rxCtrl )
1285 Reference< XPropertySet > xP( rxCtrl->getModel(), UNO_QUERY );
1287 sal_Int32 nX = 0, nY = 0, nWidth = 0, nHeight = 0;
1288 xP->getPropertyValue("PositionX") >>= nX;
1289 xP->getPropertyValue("PositionY") >>= nY;
1290 xP->getPropertyValue("Width") >>= nWidth;
1291 xP->getPropertyValue("Height") >>= nHeight;
1292 MapMode aMode( MapUnit::MapAppFont );
1293 OutputDevice*pOutDev = Application::GetDefaultDevice();
1294 if ( pOutDev )
1296 ::Size aTmp( nX, nY );
1297 aTmp = pOutDev->LogicToPixel( aTmp, aMode );
1298 nX = aTmp.Width();
1299 nY = aTmp.Height();
1300 aTmp = ::Size( nWidth, nHeight );
1301 aTmp = pOutDev->LogicToPixel( aTmp, aMode );
1302 nWidth = aTmp.Width();
1303 nHeight = aTmp.Height();
1305 else
1307 Reference< XWindowPeer > xPeer = ImplGetCompatiblePeer();
1308 Reference< XDevice > xD( xPeer, UNO_QUERY );
1310 SimpleFontMetric aFM;
1311 FontDescriptor aFD;
1312 Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_FONTDESCRIPTOR ) );
1313 aVal >>= aFD;
1314 if ( !aFD.StyleName.isEmpty() )
1316 Reference< XFont > xFont = xD->getFont( aFD );
1317 aFM = xFont->getFontMetric();
1319 else
1321 Reference< XGraphics > xG = xD->createGraphics();
1322 aFM = xG->getFontMetric();
1325 sal_Int16 nH = aFM.Ascent + aFM.Descent;
1326 sal_Int16 nW = nH/2; // calculate average width?!
1328 nX *= nW;
1329 nX /= 4;
1330 nWidth *= nW;
1331 nWidth /= 4;
1332 nY *= nH;
1333 nY /= 8;
1334 nHeight *= nH;
1335 nHeight /= 8;
1337 Reference < XWindow > xW( rxCtrl, UNO_QUERY );
1338 xW->setPosSize( nX, nY, nWidth, nHeight, PosSize::POSSIZE );
1341 void ControlContainerBase::dispose()
1343 EventObject aEvt;
1344 aEvt.Source = getXWeak();
1345 // Notify our listener helper about dispose
1346 // --- SAFE ---
1348 SolarMutexClearableGuard aGuard;
1349 Reference< XEventListener > xListener = mxListener;
1350 mxListener.clear();
1351 aGuard.clear();
1352 // --- SAFE ---
1354 if ( xListener.is() )
1355 xListener->disposing( aEvt );
1356 UnoControlContainer::dispose();
1359 void SAL_CALL ControlContainerBase::disposing(
1360 const EventObject& Source )
1362 UnoControlContainer::disposing( Source );
1365 sal_Bool ControlContainerBase::setModel( const Reference< XControlModel >& rxModel )
1367 SolarMutexGuard aGuard;
1369 // destroy the old tab controller, if existent
1370 if ( mxTabController.is() )
1372 mxTabController->setModel( nullptr ); // just to be sure, should not be necessary
1373 removeTabController( mxTabController );
1374 ::comphelper::disposeComponent( mxTabController ); // just to be sure, should not be necessary
1375 mxTabController.clear();
1378 if ( getModel().is() )
1380 const Sequence< Reference< XControl > > aControls = getControls();
1382 for ( const Reference< XControl >& rCtrl : aControls )
1383 removeControl( rCtrl );
1384 // will implicitly call removingControl, which will remove the PropertyChangeListener
1385 // (which we formerly did herein)
1386 // 08.01.2001 - 96008 - fs@openoffice.org
1388 Reference< XContainer > xC( getModel(), UNO_QUERY );
1389 if ( xC.is() )
1390 xC->removeContainerListener( this );
1392 Reference< XChangesNotifier > xChangeNotifier( getModel(), UNO_QUERY );
1393 if ( xChangeNotifier.is() )
1394 xChangeNotifier->removeChangesListener( this );
1397 bool bRet = UnoControl::setModel( rxModel );
1399 if ( getModel().is() )
1401 Reference< XNameAccess > xNA( getModel(), UNO_QUERY );
1402 if ( xNA.is() )
1404 const Sequence< OUString > aNames = xNA->getElementNames();
1406 Reference< XControlModel > xCtrlModel;
1407 for( const OUString& rName : aNames )
1409 xNA->getByName( rName ) >>= xCtrlModel;
1410 ImplInsertControl( xCtrlModel, rName );
1414 Reference< XContainer > xC( getModel(), UNO_QUERY );
1415 if ( xC.is() )
1416 xC->addContainerListener( this );
1418 Reference< XChangesNotifier > xChangeNotifier( getModel(), UNO_QUERY );
1419 if ( xChangeNotifier.is() )
1420 xChangeNotifier->addChangesListener( this );
1423 Reference< XTabControllerModel > xTabbing( getModel(), UNO_QUERY );
1424 if ( xTabbing.is() )
1426 mxTabController = new StdTabController;
1427 mxTabController->setModel( xTabbing );
1428 addTabController( mxTabController );
1430 ImplStartListingForResourceEvents();
1432 return bRet;
1434 void ControlContainerBase::setDesignMode( sal_Bool bOn )
1436 SolarMutexGuard aGuard;
1438 UnoControl::setDesignMode( bOn );
1440 Sequence< Reference< XControl > > xCtrls = getControls();
1441 for ( Reference< XControl >& rControl : asNonConstRange(xCtrls) )
1442 rControl->setDesignMode( bOn );
1444 // #109067# in design mode the tab controller is not notified about
1445 // tab index changes, therefore the tab order must be activated
1446 // when switching from design mode to live mode
1447 if ( mxTabController.is() && !bOn )
1448 mxTabController->activateTabOrder();
1451 void ControlContainerBase::elementInserted( const ContainerEvent& Event )
1453 SolarMutexGuard aGuard;
1455 Reference< XControlModel > xModel;
1456 OUString aName;
1458 Event.Accessor >>= aName;
1459 Event.Element >>= xModel;
1460 ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementInserted: illegal element!" );
1463 ImplInsertControl( xModel, aName );
1465 catch (const RuntimeException&)
1467 throw;
1469 catch (const Exception&)
1471 DBG_UNHANDLED_EXCEPTION("toolkit.controls");
1475 void ControlContainerBase::elementRemoved( const ContainerEvent& Event )
1477 SolarMutexGuard aGuard;
1479 Reference< XControlModel > xModel;
1480 Event.Element >>= xModel;
1481 ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementRemoved: illegal element!" );
1484 ImplRemoveControl( xModel );
1486 catch (const RuntimeException&)
1488 throw;
1490 catch (const Exception&)
1492 DBG_UNHANDLED_EXCEPTION("toolkit.controls");
1496 void ControlContainerBase::elementReplaced( const ContainerEvent& Event )
1498 SolarMutexGuard aGuard;
1500 Reference< XControlModel > xModel;
1501 Event.ReplacedElement >>= xModel;
1504 OSL_ENSURE( xModel.is(), "ControlContainerBase::elementReplaced: invalid ReplacedElement!" );
1505 if ( xModel.is() )
1506 ImplRemoveControl( xModel );
1508 catch (const RuntimeException&)
1510 throw;
1512 catch (const Exception&)
1514 DBG_UNHANDLED_EXCEPTION("toolkit.controls");
1517 OUString aName;
1518 Event.Accessor >>= aName;
1519 Event.Element >>= xModel;
1520 ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementReplaced: invalid new element!" );
1523 ImplInsertControl( xModel, aName );
1525 catch (const RuntimeException&)
1527 throw;
1529 catch (const Exception&)
1531 DBG_UNHANDLED_EXCEPTION("toolkit.controls");
1535 // XPropertiesChangeListener
1536 void ControlContainerBase::ImplModelPropertiesChanged( const Sequence< PropertyChangeEvent >& rEvents )
1538 if( !isDesignMode() && !mbCreatingCompatiblePeer )
1540 auto pEvt = std::find_if(rEvents.begin(), rEvents.end(),
1541 [](const PropertyChangeEvent& rEvt) {
1542 return rEvt.PropertyName == "PositionX"
1543 || rEvt.PropertyName == "PositionY"
1544 || rEvt.PropertyName == "Width"
1545 || rEvt.PropertyName == "Height";
1547 if (pEvt != rEvents.end())
1549 Reference< XControlModel > xModel( pEvt->Source, UNO_QUERY );
1550 bool bOwnModel = xModel.get() == getModel().get();
1551 if ( bOwnModel )
1553 if ( !mbPosModified && !mbSizeModified )
1555 // Don't set new pos/size if we get new values from window listener
1556 Reference< XControl > xThis(this);
1557 ImplSetPosSize( xThis );
1560 else
1562 Sequence<Reference<XControl> > aControlSequence(getControls());
1563 Reference<XControl> aControlRef( StdTabController::FindControl( aControlSequence, xModel ) );
1564 ImplSetPosSize( aControlRef );
1569 UnoControlContainer::ImplModelPropertiesChanged( rEvents );
1572 void ControlContainerBase::addingControl( const Reference< XControl >& _rxControl )
1574 SolarMutexGuard aGuard;
1575 UnoControlContainer::addingControl( _rxControl );
1577 if ( !_rxControl.is() )
1578 return;
1580 Reference< XMultiPropertySet > xProps( _rxControl->getModel(), UNO_QUERY );
1581 if ( xProps.is() )
1583 const Sequence< OUString > aNames {
1584 "PositionX",
1585 "PositionY",
1586 "Width",
1587 "Height"
1590 xProps->addPropertiesChangeListener( aNames, this );
1594 void ControlContainerBase::removingControl( const Reference< XControl >& _rxControl )
1596 SolarMutexGuard aGuard;
1597 UnoControlContainer::removingControl( _rxControl );
1599 if ( _rxControl.is() )
1601 Reference< XMultiPropertySet > xProps( _rxControl->getModel(), UNO_QUERY );
1602 if ( xProps.is() )
1603 xProps->removePropertiesChangeListener( this );
1608 void SAL_CALL ControlContainerBase::changesOccurred( const ChangesEvent& )
1610 SolarMutexGuard aGuard;
1611 // a tab controller model may have changed
1613 // #109067# in design mode don't notify the tab controller
1614 // about tab index changes
1615 if ( mxTabController.is() && !mbDesignMode )
1616 mxTabController->activateTabOrder();
1618 static void lcl_ApplyResolverToNestedContainees( const Reference< resource::XStringResourceResolver >& xStringResourceResolver, const Reference< XControlContainer >& xContainer )
1620 OUString aPropName( PROPERTY_RESOURCERESOLVER );
1622 Any aNewStringResourceResolver;
1623 aNewStringResourceResolver <<= xStringResourceResolver;
1625 Sequence< OUString > aPropNames { aPropName };
1627 const Sequence< Reference< awt::XControl > > aSeq = xContainer->getControls();
1628 for ( const Reference< XControl >& xControl : aSeq )
1630 Reference< XPropertySet > xPropertySet;
1632 if ( xControl.is() )
1633 xPropertySet.set( xControl->getModel(), UNO_QUERY );
1635 if ( !xPropertySet.is() )
1636 continue;
1640 Reference< resource::XStringResourceResolver > xCurrStringResourceResolver;
1641 Any aOldValue = xPropertySet->getPropertyValue( aPropName );
1642 if ( ( aOldValue >>= xCurrStringResourceResolver )
1643 && ( xStringResourceResolver == xCurrStringResourceResolver )
1646 Reference< XMultiPropertySet > xMultiPropSet( xPropertySet, UNO_QUERY );
1647 Reference< XPropertiesChangeListener > xListener( xPropertySet, UNO_QUERY );
1648 xMultiPropSet->firePropertiesChangeEvent( aPropNames, xListener );
1650 else
1651 xPropertySet->setPropertyValue( aPropName, aNewStringResourceResolver );
1653 catch (const Exception&)
1657 uno::Reference< XControlContainer > xNestedContainer( xControl, uno::UNO_QUERY );
1658 if ( xNestedContainer.is() )
1659 lcl_ApplyResolverToNestedContainees( xStringResourceResolver, xNestedContainer );
1664 void ControlContainerBase::ImplStartListingForResourceEvents()
1666 Reference< resource::XStringResourceResolver > xStringResourceResolver;
1668 if ( !ImplHasProperty(PROPERTY_RESOURCERESOLVER) )
1669 return;
1671 ImplGetPropertyValue( PROPERTY_RESOURCERESOLVER ) >>= xStringResourceResolver;
1673 // Add our helper as listener to retrieve notifications about changes
1674 Reference< util::XModifyListener > rListener( mxListener );
1675 ResourceListener* pResourceListener = static_cast< ResourceListener* >( rListener.get() );
1677 // resource listener will stop listening if resolver reference is empty
1678 if ( pResourceListener )
1679 pResourceListener->startListening( xStringResourceResolver );
1680 ImplUpdateResourceResolver();
1683 void ControlContainerBase::ImplUpdateResourceResolver()
1685 Reference< resource::XStringResourceResolver > xStringResourceResolver;
1687 if ( !ImplHasProperty(PROPERTY_RESOURCERESOLVER) )
1688 return;
1690 ImplGetPropertyValue(PROPERTY_RESOURCERESOLVER) >>= xStringResourceResolver;
1692 if ( !xStringResourceResolver.is() )
1693 return;
1695 lcl_ApplyResolverToNestedContainees( xStringResourceResolver, this );
1697 // propagate resource resolver changes to language dependent props of the dialog
1698 Reference< XPropertySet > xPropertySet( getModel(), UNO_QUERY );
1699 if ( xPropertySet.is() )
1701 Reference< XMultiPropertySet > xMultiPropSet( xPropertySet, UNO_QUERY );
1702 Reference< XPropertiesChangeListener > xListener( xPropertySet, UNO_QUERY );
1703 xMultiPropSet->firePropertiesChangeEvent( lcl_getLanguageDependentProperties(), xListener );
1707 //// ----------------------------------------------------
1708 //// Helper Method to convert relative url to physical location
1709 //// ----------------------------------------------------
1711 OUString getPhysicalLocation( const css::uno::Any& rbase, const css::uno::Any& rUrl )
1714 OUString baseLocation;
1715 OUString url;
1717 rbase >>= baseLocation;
1718 rUrl >>= url;
1720 OUString absoluteURL( url );
1721 if ( !url.isEmpty() )
1723 INetURLObject urlObj(baseLocation);
1724 urlObj.removeSegment();
1725 baseLocation = urlObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1727 const INetURLObject protocolCheck( url );
1728 const INetProtocol protocol = protocolCheck.GetProtocol();
1729 if ( protocol == INetProtocol::NotValid )
1731 OUString testAbsoluteURL;
1732 if ( ::osl::FileBase::E_None == ::osl::FileBase::getAbsoluteFileURL( baseLocation, url, testAbsoluteURL ) )
1733 absoluteURL = testAbsoluteURL;
1737 return absoluteURL;
1740 void
1741 ControlModelContainerBase::updateUserFormChildren( const Reference< XNameContainer >& xAllChildren, const OUString& aName, ChildOperation Operation, const css::uno::Reference< css::awt::XControlModel >& xTarget )
1743 if ( Operation < Insert || Operation > Remove )
1744 throw IllegalArgumentException();
1746 if ( !xAllChildren.is() )
1747 throw IllegalArgumentException();
1749 if ( Operation == Remove )
1751 Reference< XControlModel > xOldModel( xAllChildren->getByName( aName ), UNO_QUERY );
1752 xAllChildren->removeByName( aName );
1754 Reference< XNameContainer > xChildContainer( xOldModel, UNO_QUERY );
1755 if ( xChildContainer.is() )
1757 Reference< XPropertySet > xProps( xChildContainer, UNO_QUERY );
1758 // container control is being removed from this container, reset the
1759 // global list of containers
1760 if ( xProps.is() )
1761 xProps->setPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ), uno::Any( uno::Reference< XNameContainer >() ) );
1762 const Sequence< OUString > aChildNames = xChildContainer->getElementNames();
1763 for ( const auto& rName : aChildNames )
1764 updateUserFormChildren( xAllChildren, rName, Operation, Reference< XControlModel > () );
1767 else if ( Operation == Insert )
1769 xAllChildren->insertByName( aName, uno::Any( xTarget ) );
1770 Reference< XNameContainer > xChildContainer( xTarget, UNO_QUERY );
1771 if ( xChildContainer.is() )
1773 // container control is being added from this container, reset the
1774 // global list of containers to point to the correct global list
1775 Reference< XPropertySet > xProps( xChildContainer, UNO_QUERY );
1776 if ( xProps.is() )
1777 xProps->setPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ), uno::Any( xAllChildren ) );
1778 const Sequence< OUString > aChildNames = xChildContainer->getElementNames();
1779 for ( const auto& rName : aChildNames )
1781 Reference< XControlModel > xChildTarget( xChildContainer->getByName( rName ), UNO_QUERY );
1782 updateUserFormChildren( xAllChildren, rName, Operation, xChildTarget );
1789 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */