Get the style color and number just once
[LibreOffice.git] / toolkit / source / controls / controlmodelcontainerbase.cxx
blobb7c8466f1fe6ceb3dbf96db28677e264c46491a2
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{ u"HelpText"_ustr, u"Title"_ustr };
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 <<= sServiceName_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(u"com.sun.star.awt.UnoControlModel"_ustr) )
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 { u"com.sun.star.awt.UnoControlEditModel"_ustr,
347 u"com.sun.star.awt.UnoControlFormattedFieldModel"_ustr,
348 u"com.sun.star.awt.UnoControlFileControlModel"_ustr,
349 u"com.sun.star.awt.UnoControlButtonModel"_ustr,
350 u"com.sun.star.awt.UnoControlImageControlModel"_ustr,
351 u"com.sun.star.awt.UnoControlRadioButtonModel"_ustr,
352 u"com.sun.star.awt.UnoControlCheckBoxModel"_ustr,
353 u"com.sun.star.awt.UnoControlFixedTextModel"_ustr,
354 u"com.sun.star.awt.UnoControlGroupBoxModel"_ustr,
355 u"com.sun.star.awt.UnoControlListBoxModel"_ustr,
356 u"com.sun.star.awt.UnoControlComboBoxModel"_ustr,
357 u"com.sun.star.awt.UnoControlDateFieldModel"_ustr,
358 u"com.sun.star.awt.UnoControlTimeFieldModel"_ustr,
359 u"com.sun.star.awt.UnoControlNumericFieldModel"_ustr,
360 u"com.sun.star.awt.UnoControlCurrencyFieldModel"_ustr,
361 u"com.sun.star.awt.UnoControlPatternFieldModel"_ustr,
362 u"com.sun.star.awt.UnoControlProgressBarModel"_ustr,
363 u"com.sun.star.awt.UnoControlScrollBarModel"_ustr,
364 u"com.sun.star.awt.UnoControlFixedLineModel"_ustr,
365 u"com.sun.star.awt.UnoControlRoadmapModel"_ustr,
366 u"com.sun.star.awt.tree.TreeControlModel"_ustr,
367 u"com.sun.star.awt.grid.UnoControlGridModel"_ustr,
368 u"com.sun.star.awt.UnoControlFixedHyperlinkModel"_ustr,
369 u"com.sun.star.awt.tab.UnoControlTabPageContainerModel"_ustr,
370 u"com.sun.star.awt.tab.UnoControlTabPageModel"_ustr,
371 u"com.sun.star.awt.UnoMultiPageModel"_ustr,
372 u"com.sun.star.awt.UnoFrameModel"_ustr
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( u"com.sun.star.awt.UnoControlRadioButtonModel"_ustr );
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(u"PositionX"_ustr) >>= nX;
1289 xP->getPropertyValue(u"PositionY"_ustr) >>= nY;
1290 xP->getPropertyValue(u"Width"_ustr) >>= nWidth;
1291 xP->getPropertyValue(u"Height"_ustr) >>= 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 mxTabController.clear();
1377 if ( getModel().is() )
1379 const Sequence< Reference< XControl > > aControls = getControls();
1381 for ( const Reference< XControl >& rCtrl : aControls )
1382 removeControl( rCtrl );
1383 // will implicitly call removingControl, which will remove the PropertyChangeListener
1384 // (which we formerly did herein)
1385 // 08.01.2001 - 96008 - fs@openoffice.org
1387 Reference< XContainer > xC( getModel(), UNO_QUERY );
1388 if ( xC.is() )
1389 xC->removeContainerListener( this );
1391 Reference< XChangesNotifier > xChangeNotifier( getModel(), UNO_QUERY );
1392 if ( xChangeNotifier.is() )
1393 xChangeNotifier->removeChangesListener( this );
1396 bool bRet = UnoControl::setModel( rxModel );
1398 if ( getModel().is() )
1400 Reference< XNameAccess > xNA( getModel(), UNO_QUERY );
1401 if ( xNA.is() )
1403 const Sequence< OUString > aNames = xNA->getElementNames();
1405 Reference< XControlModel > xCtrlModel;
1406 for( const OUString& rName : aNames )
1408 xNA->getByName( rName ) >>= xCtrlModel;
1409 ImplInsertControl( xCtrlModel, rName );
1413 Reference< XContainer > xC( getModel(), UNO_QUERY );
1414 if ( xC.is() )
1415 xC->addContainerListener( this );
1417 Reference< XChangesNotifier > xChangeNotifier( getModel(), UNO_QUERY );
1418 if ( xChangeNotifier.is() )
1419 xChangeNotifier->addChangesListener( this );
1422 Reference< XTabControllerModel > xTabbing( getModel(), UNO_QUERY );
1423 if ( xTabbing.is() )
1425 mxTabController = new StdTabController;
1426 mxTabController->setModel( xTabbing );
1427 addTabController( mxTabController );
1429 ImplStartListingForResourceEvents();
1431 return bRet;
1433 void ControlContainerBase::setDesignMode( sal_Bool bOn )
1435 SolarMutexGuard aGuard;
1437 UnoControl::setDesignMode( bOn );
1439 Sequence< Reference< XControl > > xCtrls = getControls();
1440 for ( Reference< XControl >& rControl : asNonConstRange(xCtrls) )
1441 rControl->setDesignMode( bOn );
1443 // #109067# in design mode the tab controller is not notified about
1444 // tab index changes, therefore the tab order must be activated
1445 // when switching from design mode to live mode
1446 if ( mxTabController.is() && !bOn )
1447 mxTabController->activateTabOrder();
1450 void ControlContainerBase::elementInserted( const ContainerEvent& Event )
1452 SolarMutexGuard aGuard;
1454 Reference< XControlModel > xModel;
1455 OUString aName;
1457 Event.Accessor >>= aName;
1458 Event.Element >>= xModel;
1459 ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementInserted: illegal element!" );
1462 ImplInsertControl( xModel, aName );
1464 catch (const RuntimeException&)
1466 throw;
1468 catch (const Exception&)
1470 DBG_UNHANDLED_EXCEPTION("toolkit.controls");
1474 void ControlContainerBase::elementRemoved( const ContainerEvent& Event )
1476 SolarMutexGuard aGuard;
1478 Reference< XControlModel > xModel;
1479 Event.Element >>= xModel;
1480 ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementRemoved: illegal element!" );
1483 ImplRemoveControl( xModel );
1485 catch (const RuntimeException&)
1487 throw;
1489 catch (const Exception&)
1491 DBG_UNHANDLED_EXCEPTION("toolkit.controls");
1495 void ControlContainerBase::elementReplaced( const ContainerEvent& Event )
1497 SolarMutexGuard aGuard;
1499 Reference< XControlModel > xModel;
1500 Event.ReplacedElement >>= xModel;
1503 OSL_ENSURE( xModel.is(), "ControlContainerBase::elementReplaced: invalid ReplacedElement!" );
1504 if ( xModel.is() )
1505 ImplRemoveControl( xModel );
1507 catch (const RuntimeException&)
1509 throw;
1511 catch (const Exception&)
1513 DBG_UNHANDLED_EXCEPTION("toolkit.controls");
1516 OUString aName;
1517 Event.Accessor >>= aName;
1518 Event.Element >>= xModel;
1519 ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementReplaced: invalid new element!" );
1522 ImplInsertControl( xModel, aName );
1524 catch (const RuntimeException&)
1526 throw;
1528 catch (const Exception&)
1530 DBG_UNHANDLED_EXCEPTION("toolkit.controls");
1534 // XPropertiesChangeListener
1535 void ControlContainerBase::ImplModelPropertiesChanged( const Sequence< PropertyChangeEvent >& rEvents )
1537 if( !isDesignMode() && !mbCreatingCompatiblePeer )
1539 auto pEvt = std::find_if(rEvents.begin(), rEvents.end(),
1540 [](const PropertyChangeEvent& rEvt) {
1541 return rEvt.PropertyName == "PositionX"
1542 || rEvt.PropertyName == "PositionY"
1543 || rEvt.PropertyName == "Width"
1544 || rEvt.PropertyName == "Height";
1546 if (pEvt != rEvents.end())
1548 Reference< XControlModel > xModel( pEvt->Source, UNO_QUERY );
1549 bool bOwnModel = xModel.get() == getModel().get();
1550 if ( bOwnModel )
1552 if ( !mbPosModified && !mbSizeModified )
1554 // Don't set new pos/size if we get new values from window listener
1555 Reference< XControl > xThis(this);
1556 ImplSetPosSize( xThis );
1559 else
1561 Sequence<Reference<XControl> > aControlSequence(getControls());
1562 Reference<XControl> aControlRef( StdTabController::FindControl( aControlSequence, xModel ) );
1563 ImplSetPosSize( aControlRef );
1568 UnoControlContainer::ImplModelPropertiesChanged( rEvents );
1571 void ControlContainerBase::addingControl( const Reference< XControl >& _rxControl )
1573 SolarMutexGuard aGuard;
1574 UnoControlContainer::addingControl( _rxControl );
1576 if ( !_rxControl.is() )
1577 return;
1579 Reference< XMultiPropertySet > xProps( _rxControl->getModel(), UNO_QUERY );
1580 if ( xProps.is() )
1582 const Sequence< OUString > aNames {
1583 u"PositionX"_ustr,
1584 u"PositionY"_ustr,
1585 u"Width"_ustr,
1586 u"Height"_ustr
1589 xProps->addPropertiesChangeListener( aNames, this );
1593 void ControlContainerBase::removingControl( const Reference< XControl >& _rxControl )
1595 SolarMutexGuard aGuard;
1596 UnoControlContainer::removingControl( _rxControl );
1598 if ( _rxControl.is() )
1600 Reference< XMultiPropertySet > xProps( _rxControl->getModel(), UNO_QUERY );
1601 if ( xProps.is() )
1602 xProps->removePropertiesChangeListener( this );
1607 void SAL_CALL ControlContainerBase::changesOccurred( const ChangesEvent& )
1609 SolarMutexGuard aGuard;
1610 // a tab controller model may have changed
1612 // #109067# in design mode don't notify the tab controller
1613 // about tab index changes
1614 if ( mxTabController.is() && !mbDesignMode )
1615 mxTabController->activateTabOrder();
1617 static void lcl_ApplyResolverToNestedContainees( const Reference< resource::XStringResourceResolver >& xStringResourceResolver, const Reference< XControlContainer >& xContainer )
1619 OUString aPropName( PROPERTY_RESOURCERESOLVER );
1621 Any aNewStringResourceResolver;
1622 aNewStringResourceResolver <<= xStringResourceResolver;
1624 Sequence< OUString > aPropNames { aPropName };
1626 const Sequence< Reference< awt::XControl > > aSeq = xContainer->getControls();
1627 for ( const Reference< XControl >& xControl : aSeq )
1629 Reference< XPropertySet > xPropertySet;
1631 if ( xControl.is() )
1632 xPropertySet.set( xControl->getModel(), UNO_QUERY );
1634 if ( !xPropertySet.is() )
1635 continue;
1639 Reference< resource::XStringResourceResolver > xCurrStringResourceResolver;
1640 Any aOldValue = xPropertySet->getPropertyValue( aPropName );
1641 if ( ( aOldValue >>= xCurrStringResourceResolver )
1642 && ( xStringResourceResolver == xCurrStringResourceResolver )
1645 Reference< XMultiPropertySet > xMultiPropSet( xPropertySet, UNO_QUERY );
1646 Reference< XPropertiesChangeListener > xListener( xPropertySet, UNO_QUERY );
1647 xMultiPropSet->firePropertiesChangeEvent( aPropNames, xListener );
1649 else
1650 xPropertySet->setPropertyValue( aPropName, aNewStringResourceResolver );
1652 catch (const Exception&)
1656 uno::Reference< XControlContainer > xNestedContainer( xControl, uno::UNO_QUERY );
1657 if ( xNestedContainer.is() )
1658 lcl_ApplyResolverToNestedContainees( xStringResourceResolver, xNestedContainer );
1663 void ControlContainerBase::ImplStartListingForResourceEvents()
1665 Reference< resource::XStringResourceResolver > xStringResourceResolver;
1667 if ( !ImplHasProperty(PROPERTY_RESOURCERESOLVER) )
1668 return;
1670 ImplGetPropertyValue( PROPERTY_RESOURCERESOLVER ) >>= xStringResourceResolver;
1672 // Add our helper as listener to retrieve notifications about changes
1673 Reference< util::XModifyListener > rListener( mxListener );
1674 ResourceListener* pResourceListener = static_cast< ResourceListener* >( rListener.get() );
1676 // resource listener will stop listening if resolver reference is empty
1677 if ( pResourceListener )
1678 pResourceListener->startListening( xStringResourceResolver );
1679 ImplUpdateResourceResolver();
1682 void ControlContainerBase::ImplUpdateResourceResolver()
1684 Reference< resource::XStringResourceResolver > xStringResourceResolver;
1686 if ( !ImplHasProperty(PROPERTY_RESOURCERESOLVER) )
1687 return;
1689 ImplGetPropertyValue(PROPERTY_RESOURCERESOLVER) >>= xStringResourceResolver;
1691 if ( !xStringResourceResolver.is() )
1692 return;
1694 lcl_ApplyResolverToNestedContainees( xStringResourceResolver, this );
1696 // propagate resource resolver changes to language dependent props of the dialog
1697 Reference< XPropertySet > xPropertySet( getModel(), UNO_QUERY );
1698 if ( xPropertySet.is() )
1700 Reference< XMultiPropertySet > xMultiPropSet( xPropertySet, UNO_QUERY );
1701 Reference< XPropertiesChangeListener > xListener( xPropertySet, UNO_QUERY );
1702 xMultiPropSet->firePropertiesChangeEvent( lcl_getLanguageDependentProperties(), xListener );
1706 //// ----------------------------------------------------
1707 //// Helper Method to convert relative url to physical location
1708 //// ----------------------------------------------------
1710 OUString getPhysicalLocation( const css::uno::Any& rbase, const css::uno::Any& rUrl )
1713 OUString baseLocation;
1714 OUString url;
1716 rbase >>= baseLocation;
1717 rUrl >>= url;
1719 OUString absoluteURL( url );
1720 if ( !url.isEmpty() )
1722 INetURLObject urlObj(baseLocation);
1723 urlObj.removeSegment();
1724 baseLocation = urlObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1726 const INetURLObject protocolCheck( url );
1727 const INetProtocol protocol = protocolCheck.GetProtocol();
1728 if ( protocol == INetProtocol::NotValid )
1730 OUString testAbsoluteURL;
1731 if ( ::osl::FileBase::E_None == ::osl::FileBase::getAbsoluteFileURL( baseLocation, url, testAbsoluteURL ) )
1732 absoluteURL = testAbsoluteURL;
1736 return absoluteURL;
1739 void
1740 ControlModelContainerBase::updateUserFormChildren( const Reference< XNameContainer >& xAllChildren, const OUString& aName, ChildOperation Operation, const css::uno::Reference< css::awt::XControlModel >& xTarget )
1742 if ( Operation < Insert || Operation > Remove )
1743 throw IllegalArgumentException();
1745 if ( !xAllChildren.is() )
1746 throw IllegalArgumentException();
1748 if ( Operation == Remove )
1750 Reference< XControlModel > xOldModel( xAllChildren->getByName( aName ), UNO_QUERY );
1751 xAllChildren->removeByName( aName );
1753 Reference< XNameContainer > xChildContainer( xOldModel, UNO_QUERY );
1754 if ( xChildContainer.is() )
1756 Reference< XPropertySet > xProps( xChildContainer, UNO_QUERY );
1757 // container control is being removed from this container, reset the
1758 // global list of containers
1759 if ( xProps.is() )
1760 xProps->setPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ), uno::Any( uno::Reference< XNameContainer >() ) );
1761 const Sequence< OUString > aChildNames = xChildContainer->getElementNames();
1762 for ( const auto& rName : aChildNames )
1763 updateUserFormChildren( xAllChildren, rName, Operation, Reference< XControlModel > () );
1766 else if ( Operation == Insert )
1768 xAllChildren->insertByName( aName, uno::Any( xTarget ) );
1769 Reference< XNameContainer > xChildContainer( xTarget, UNO_QUERY );
1770 if ( xChildContainer.is() )
1772 // container control is being added from this container, reset the
1773 // global list of containers to point to the correct global list
1774 Reference< XPropertySet > xProps( xChildContainer, UNO_QUERY );
1775 if ( xProps.is() )
1776 xProps->setPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ), uno::Any( xAllChildren ) );
1777 const Sequence< OUString > aChildNames = xChildContainer->getElementNames();
1778 for ( const auto& rName : aChildNames )
1780 Reference< XControlModel > xChildTarget( xChildContainer->getByName( rName ), UNO_QUERY );
1781 updateUserFormChildren( xAllChildren, rName, Operation, xChildTarget );
1788 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */