diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/source/controls/controlmodelcontainerbase.cxx | 1820 |
1 files changed, 1820 insertions, 0 deletions
diff --git a/toolkit/source/controls/controlmodelcontainerbase.cxx b/toolkit/source/controls/controlmodelcontainerbase.cxx new file mode 100644 index 000000000..677d000f9 --- /dev/null +++ b/toolkit/source/controls/controlmodelcontainerbase.cxx @@ -0,0 +1,1820 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <controls/controlmodelcontainerbase.hxx> +#include <vcl/svapp.hxx> +#include <o3tl/safeint.hxx> +#include <osl/mutex.hxx> +#include <toolkit/helper/property.hxx> +#include <helper/servicenames.hxx> +#include <controls/geometrycontrolmodel.hxx> +#include <toolkit/controls/unocontrols.hxx> +#include <controls/formattedcontrol.hxx> +#include <controls/roadmapcontrol.hxx> +#include <controls/tkscrollbar.hxx> +#include <controls/tabpagemodel.hxx> +#include <controls/stdtabcontroller.hxx> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/resource/XStringResourceResolver.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/weakagg.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/outdev.hxx> +#include <comphelper/types.hxx> + +#include "tree/treecontrol.hxx" +#include "grid/gridcontrol.hxx" +#include <controls/tabpagecontainer.hxx> + +#include <map> +#include <algorithm> +#include <tools/urlobj.hxx> +#include <osl/file.hxx> +#include <sal/log.hxx> +#include <controls/dialogcontrol.hxx> + +#include <helper/unopropertyarrayhelper.hxx> +#include "controlmodelcontainerbase_internal.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace toolkit; + +constexpr OUStringLiteral PROPERTY_RESOURCERESOLVER = u"ResourceResolver"; + + +namespace +{ + const Sequence< OUString >& lcl_getLanguageDependentProperties() + { + // note: properties must be sorted + static Sequence<OUString> s_aLanguageDependentProperties{ "HelpText", "Title" }; + return s_aLanguageDependentProperties; + } + +// functor for disposing a control model +struct DisposeControlModel +{ + void operator()( Reference< XControlModel >& _rxModel ) + { + try + { + ::comphelper::disposeComponent( _rxModel ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("toolkit", "caught an exception while disposing a component" ); + } + } +}; + +} + + +// functor for cloning a control model, and insertion into a target list +struct CloneControlModel +{ +private: + ControlModelContainerBase::UnoControlModelHolderVector& m_rTargetVector; + +public: + explicit CloneControlModel( ControlModelContainerBase::UnoControlModelHolderVector& _rTargetVector ) + :m_rTargetVector( _rTargetVector ) + { + } + + void operator()( const ControlModelContainerBase::UnoControlModelHolder& _rSource ) + { + // clone the source object + Reference< XCloneable > xCloneSource( _rSource.first, UNO_QUERY ); + Reference< XControlModel > xClone( xCloneSource->createClone(), UNO_QUERY ); + // add to target list + m_rTargetVector.emplace_back( xClone, _rSource.second ); + } +}; + + +// functor for comparing a XControlModel with a given reference +struct CompareControlModel +{ +private: + Reference< XControlModel > m_xReference; +public: + explicit CompareControlModel( const Reference< XControlModel >& _rxReference ) : m_xReference( _rxReference ) { } + + bool operator()( const ControlModelContainerBase::UnoControlModelHolder& _rCompare ) + { + return _rCompare.first.get() == m_xReference.get(); + } +}; + + +static void lcl_throwIllegalArgumentException( ) +{ // throwing is expensive (in terms of code size), thus we hope the compiler does not inline this... + throw IllegalArgumentException(); +} + + +static void lcl_throwNoSuchElementException( ) +{ // throwing is expensive (in terms of code size), thus we hope the compiler does not inline this... + throw NoSuchElementException(); +} + + +static void lcl_throwElementExistException( ) +{ // throwing is expensive (in terms of code size), thus we hope the compiler does not inline this... + throw ElementExistException(); +} + + +static OUString getTabIndexPropertyName( ) +{ + return "TabIndex"; +} + + +static OUString getStepPropertyName( ) +{ + return "Step"; +} + + + +ControlModelContainerBase::ControlModelContainerBase( const Reference< XComponentContext >& rxContext ) + :ControlModelContainer_IBase( rxContext ) + ,maContainerListeners( *this ) + ,maChangeListeners ( GetMutex() ) + ,mbGroupsUpToDate( false ) + ,m_nTabPageId(0) +{ + ImplRegisterProperty(BASEPROPERTY_ENABLED); +} + +ControlModelContainerBase::ControlModelContainerBase( const ControlModelContainerBase& rModel ) + : ControlModelContainer_IBase( rModel ) + , maContainerListeners( *this ) + , maChangeListeners ( GetMutex() ) + , mbGroupsUpToDate( false ) + , m_nTabPageId( rModel.m_nTabPageId ) +{ +} + +ControlModelContainerBase::~ControlModelContainerBase() +{ + maModels.clear(); + mbGroupsUpToDate = false; +} + +Any ControlModelContainerBase::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + Any aAny; + + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + aAny <<= OUString::createFromAscii( szServiceName_UnoControlDialog ); + break; + default: + aAny = UnoControlModel::ImplGetDefaultValue( nPropId ); + } + + return aAny; +} + +::cppu::IPropertyArrayHelper& ControlModelContainerBase::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +void SAL_CALL ControlModelContainerBase::dispose( ) +{ + + // tell our listeners + { + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + EventObject aDisposeEvent; + aDisposeEvent.Source = static_cast< XAggregation* >( static_cast< ::cppu::OWeakAggObject* >( this ) ); + + maContainerListeners.disposeAndClear( aDisposeEvent ); + maChangeListeners.disposeAndClear( aDisposeEvent ); + } + + + // call the base class + UnoControlModel::dispose(); + + + // dispose our child models + // for this, collect the models (we collect them from maModels, and this is modified when disposing children) + ::std::vector< Reference< XControlModel > > aChildModels( maModels.size() ); + + ::std::transform( + maModels.begin(), maModels.end(), // source range + aChildModels.begin(), // target location + []( const UnoControlModelHolder& rUnoControlModelHolder ) + { return rUnoControlModelHolder.first; } // operation to apply -> select the XControlModel part + ); + + // now dispose + ::std::for_each( aChildModels.begin(), aChildModels.end(), DisposeControlModel() ); + aChildModels.clear(); + + mbGroupsUpToDate = false; +} + +// XMultiPropertySet +Reference< XPropertySetInfo > ControlModelContainerBase::getPropertySetInfo( ) +{ + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +void ControlModelContainerBase::Clone_Impl(ControlModelContainerBase& _rClone) const +{ + // clone all children + ::std::for_each( + maModels.begin(), maModels.end(), + CloneControlModel( _rClone.maModels ) + ); +} +rtl::Reference<UnoControlModel> ControlModelContainerBase::Clone() const +{ + // clone the container itself + rtl::Reference<ControlModelContainerBase> pClone = new ControlModelContainerBase( *this ); + Clone_Impl(*pClone); + + return pClone; +} + +ControlModelContainerBase::UnoControlModelHolderVector::iterator ControlModelContainerBase::ImplFindElement( std::u16string_view rName ) +{ + return ::std::find_if( maModels.begin(), maModels.end(), [&](const UnoControlModelHolder& elem) { return elem.second == rName; }); +} + +// ::XMultiServiceFactory +Reference< XInterface > ControlModelContainerBase::createInstance( const OUString& aServiceSpecifier ) +{ + SolarMutexGuard aGuard; + + rtl::Reference<OGeometryControlModel_Base> pNewModel; + + if ( aServiceSpecifier == "com.sun.star.awt.UnoControlEditModel" ) + pNewModel = new OGeometryControlModel< UnoControlEditModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFormattedFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlFormattedFieldModel >( m_xContext); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFileControlModel" ) + pNewModel = new OGeometryControlModel< UnoControlFileControlModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlButtonModel" ) + pNewModel = new OGeometryControlModel< UnoControlButtonModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlImageControlModel" ) + pNewModel = new OGeometryControlModel< UnoControlImageControlModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlRadioButtonModel" ) + pNewModel = new OGeometryControlModel< UnoControlRadioButtonModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlCheckBoxModel" ) + pNewModel = new OGeometryControlModel< UnoControlCheckBoxModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFixedHyperlinkModel" ) + pNewModel = new OGeometryControlModel< UnoControlFixedHyperlinkModel >( m_xContext ); + else if ( aServiceSpecifier == "stardiv.vcl.controlmodel.FixedText" ) + pNewModel = new OGeometryControlModel< UnoControlFixedTextModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlGroupBoxModel" ) + pNewModel = new OGeometryControlModel< UnoControlGroupBoxModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlListBoxModel" ) + pNewModel = new OGeometryControlModel< UnoControlListBoxModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlComboBoxModel" ) + pNewModel = new OGeometryControlModel< UnoControlComboBoxModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlDateFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlDateFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlTimeFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlTimeFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlNumericFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlNumericFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlCurrencyFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlCurrencyFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlPatternFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlPatternFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlProgressBarModel" ) + pNewModel = new OGeometryControlModel< UnoControlProgressBarModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlScrollBarModel" ) + pNewModel = new OGeometryControlModel< UnoControlScrollBarModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFixedLineModel" ) + pNewModel = new OGeometryControlModel< UnoControlFixedLineModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlRoadmapModel" ) + pNewModel = new OGeometryControlModel< UnoControlRoadmapModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.tree.TreeControlModel" ) + pNewModel = new OGeometryControlModel< UnoTreeModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.grid.UnoControlGridModel" ) + pNewModel = new OGeometryControlModel< UnoGridModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.tab.UnoControlTabPageContainerModel" ) + pNewModel = new OGeometryControlModel< UnoControlTabPageContainerModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoMultiPageModel" ) + pNewModel = new OGeometryControlModel< UnoMultiPageModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.tab.UnoControlTabPageModel" ) + pNewModel = new OGeometryControlModel< UnoControlTabPageModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoPageModel" ) + pNewModel = new OGeometryControlModel< UnoPageModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoFrameModel" ) + pNewModel = new OGeometryControlModel< UnoFrameModel >( m_xContext ); + + if ( !pNewModel ) + { + Reference< XInterface > xObject = m_xContext->getServiceManager()->createInstanceWithContext(aServiceSpecifier, m_xContext); + Reference< XServiceInfo > xSI( xObject, UNO_QUERY ); + Reference< XCloneable > xCloneAccess( xSI, UNO_QUERY ); + Reference< XAggregation > xAgg( xCloneAccess, UNO_QUERY ); + if ( xAgg.is() ) + { + if ( xSI->supportsService("com.sun.star.awt.UnoControlModel") ) + { + // release 3 of the 4 references we have to the object + xAgg.clear(); + xSI.clear(); + xObject.clear(); + + pNewModel = new OCommonGeometryControlModel( xCloneAccess, aServiceSpecifier ); + } + } + } + + Reference< XInterface > xNewModel = static_cast<cppu::OWeakObject*>(pNewModel.get()); + return xNewModel; +} + +Reference< XInterface > ControlModelContainerBase::createInstanceWithArguments( const OUString& ServiceSpecifier, const Sequence< Any >& i_arguments ) +{ + const Reference< XInterface > xInstance( createInstance( ServiceSpecifier ) ); + const Reference< XInitialization > xInstanceInit( xInstance, UNO_QUERY ); + ENSURE_OR_RETURN( xInstanceInit.is(), "ControlModelContainerBase::createInstanceWithArguments: can't pass the arguments!", xInstance ); + xInstanceInit->initialize( i_arguments ); + return xInstance; +} + +Sequence< OUString > ControlModelContainerBase::getAvailableServiceNames() +{ + return { "com.sun.star.awt.UnoControlEditModel", + "com.sun.star.awt.UnoControlFormattedFieldModel", + "com.sun.star.awt.UnoControlFileControlModel", + "com.sun.star.awt.UnoControlButtonModel", + "com.sun.star.awt.UnoControlImageControlModel", + "com.sun.star.awt.UnoControlRadioButtonModel", + "com.sun.star.awt.UnoControlCheckBoxModel", + "com.sun.star.awt.UnoControlFixedTextModel", + "com.sun.star.awt.UnoControlGroupBoxModel", + "com.sun.star.awt.UnoControlListBoxModel", + "com.sun.star.awt.UnoControlComboBoxModel", + "com.sun.star.awt.UnoControlDateFieldModel", + "com.sun.star.awt.UnoControlTimeFieldModel", + "com.sun.star.awt.UnoControlNumericFieldModel", + "com.sun.star.awt.UnoControlCurrencyFieldModel", + "com.sun.star.awt.UnoControlPatternFieldModel", + "com.sun.star.awt.UnoControlProgressBarModel", + "com.sun.star.awt.UnoControlScrollBarModel", + "com.sun.star.awt.UnoControlFixedLineModel", + "com.sun.star.awt.UnoControlRoadmapModel", + "com.sun.star.awt.tree.TreeControlModel", + "com.sun.star.awt.grid.UnoControlGridModel", + "com.sun.star.awt.UnoControlFixedHyperlinkModel", + "com.sun.star.awt.tab.UnoControlTabPageContainerModel", + "com.sun.star.awt.tab.UnoControlTabPageModel", + "com.sun.star.awt.UnoMultiPageModel", + "com.sun.star.awt.UnoFrameModel" + }; +} + +// XContainer +void ControlModelContainerBase::addContainerListener( const Reference< XContainerListener >& l ) +{ + maContainerListeners.addInterface( l ); +} + +void ControlModelContainerBase::removeContainerListener( const Reference< XContainerListener >& l ) +{ + maContainerListeners.removeInterface( l ); +} + +// XElementAccess +Type ControlModelContainerBase::getElementType() +{ + Type aType = cppu::UnoType<XControlModel>::get(); + return aType; +} + +sal_Bool ControlModelContainerBase::hasElements() +{ + return !maModels.empty(); +} + +// XNameContainer, XNameReplace, XNameAccess +void ControlModelContainerBase::replaceByName( const OUString& aName, const Any& aElement ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xNewModel; + aElement >>= xNewModel; + if ( !xNewModel.is() ) + lcl_throwIllegalArgumentException(); + + UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName ); + if ( maModels.end() == aElementPos ) + lcl_throwNoSuchElementException(); + // Dialog behaviour is to have all containee names unique (MSO Userform is the same) + // With container controls you could have constructed an existing hierarchy and are now + // add this to an existing container, in this case a name nested in the containment + // hierarchy of the added control could contain a name clash, if we have access to the + // list of global names then recursively check for previously existing names (we need + // to do this obviously before the 'this' objects container is updated) + Reference< XNameContainer > xAllChildren( getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY ); + if ( xAllChildren.is() ) + { + // remove old control (and children) from global list of containers + updateUserFormChildren( xAllChildren, aName, Remove, uno::Reference< XControlModel >() ); + // Add new control (and containers if they exist) + updateUserFormChildren( xAllChildren, aName, Insert, xNewModel ); + } + // stop listening at the old model + stopControlListening( aElementPos->first ); + Reference< XControlModel > xReplaced( aElementPos->first ); + // remember the new model, and start listening + aElementPos->first = xNewModel; + startControlListening( xNewModel ); + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aElement; + aEvent.ReplacedElement <<= xReplaced; + aEvent.Accessor <<= aName; + + // notify the container listener + maContainerListeners.elementReplaced( aEvent ); + + // our "tab controller model" has potentially changed -> notify this + implNotifyTabModelChange( aName ); +} + +Any ControlModelContainerBase::getByName( const OUString& aName ) +{ + UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName ); + if ( maModels.end() == aElementPos ) + lcl_throwNoSuchElementException(); + + return Any( aElementPos->first ); +} + +Sequence< OUString > ControlModelContainerBase::getElementNames() +{ + Sequence< OUString > aNames( maModels.size() ); + + ::std::transform( + maModels.begin(), maModels.end(), // source range + aNames.getArray(), // target range + []( const UnoControlModelHolder& rUnoControlModelHolder ) + { return rUnoControlModelHolder.second; } // operator to apply: select the second element (the name) + ); + + return aNames; +} + +sal_Bool ControlModelContainerBase::hasByName( const OUString& aName ) +{ + return maModels.end() != ImplFindElement( aName ); +} + +void ControlModelContainerBase::insertByName( const OUString& aName, const Any& aElement ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xM; + aElement >>= xM; + + if ( xM.is() ) + { + Reference< beans::XPropertySet > xProps( xM, UNO_QUERY ); + if ( xProps.is() ) + { + + Reference< beans::XPropertySetInfo > xPropInfo = xProps->getPropertySetInfo(); + + const OUString& sImageSourceProperty = GetPropertyName( BASEPROPERTY_IMAGEURL ); + if ( xPropInfo->hasPropertyByName( sImageSourceProperty ) && ImplHasProperty(BASEPROPERTY_DIALOGSOURCEURL) ) + { + Any aUrl = xProps->getPropertyValue( sImageSourceProperty ); + + OUString absoluteUrl = + getPhysicalLocation( getPropertyValue( GetPropertyName( BASEPROPERTY_DIALOGSOURCEURL ) ), aUrl ); + + aUrl <<= absoluteUrl; + + xProps->setPropertyValue( sImageSourceProperty , aUrl ); + } + } + } + + + if ( aName.isEmpty() || !xM.is() ) + lcl_throwIllegalArgumentException(); + + UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName ); + if ( maModels.end() != aElementPos ) + lcl_throwElementExistException(); + + // Dialog behaviour is to have all containee names unique (MSO Userform is the same) + // With container controls you could have constructed an existing hierarchy and are now + // add this to an existing container, in this case a name nested in the containment + // hierarchy of the added control could contain a name clash, if we have access to the + // list of global names then we need to recursively check for previously existing + // names (we need to do this obviously before the 'this' objects container is updated) + // remove old control (and children) from global list of containers + Reference< XNameContainer > xAllChildren( getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY ); + + if ( xAllChildren.is() ) + updateUserFormChildren( xAllChildren, aName, Insert, xM ); + maModels.emplace_back( xM, aName ); + mbGroupsUpToDate = false; + startControlListening( xM ); + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aElement; + aEvent.Accessor <<= aName; + maContainerListeners.elementInserted( aEvent ); + + // our "tab controller model" has potentially changed -> notify this + implNotifyTabModelChange( aName ); +} + +void ControlModelContainerBase::removeByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName ); + if ( maModels.end() == aElementPos ) + lcl_throwNoSuchElementException(); + + // Dialog behaviour is to have all containee names unique (MSO Userform is the same) + // With container controls you could have constructed an existing hierarchy and are now + // removing this control from an existing container, in this case all nested names in + // the containment hierarchy of the control to be removed need to be removed from the global + // names cache (we need to do this obviously before the 'this' objects container is updated) + Reference< XNameContainer > xAllChildren( getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY ); + if ( xAllChildren.is() ) + updateUserFormChildren( xAllChildren, aName, Remove, uno::Reference< XControlModel >() ); + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element <<= aElementPos->first; + aEvent.Accessor <<= aName; + maContainerListeners.elementRemoved( aEvent ); + + stopControlListening( aElementPos->first ); + Reference< XPropertySet > xPS( aElementPos->first, UNO_QUERY ); + maModels.erase( aElementPos ); + mbGroupsUpToDate = false; + + if ( xPS.is() ) + { + try + { + xPS->setPropertyValue( PROPERTY_RESOURCERESOLVER, Any( Reference< resource::XStringResourceResolver >() ) ); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + + // our "tab controller model" has potentially changed -> notify this + implNotifyTabModelChange( aName ); +} + + +sal_Bool SAL_CALL ControlModelContainerBase::getGroupControl( ) +{ + return true; +} + + +void SAL_CALL ControlModelContainerBase::setGroupControl( sal_Bool ) +{ + SAL_WARN("toolkit", "explicit grouping not supported" ); +} + + +void SAL_CALL ControlModelContainerBase::setControlModels( const Sequence< Reference< XControlModel > >& _rControls ) +{ + SolarMutexGuard aGuard; + + // set the tab indexes according to the order of models in the sequence + + sal_Int16 nTabIndex = 1; + + for ( auto const & control : _rControls ) + { + // look up the control in our own structure. This is to prevent invalid arguments + UnoControlModelHolderVector::const_iterator aPos = + ::std::find_if( + maModels.begin(), maModels.end(), + CompareControlModel( control ) + ); + if ( maModels.end() != aPos ) + { + // okay, this is an existent model + // now set the TabIndex property (if applicable) + Reference< XPropertySet > xProps( aPos->first, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI; + if ( xProps.is() ) + xPSI = xProps->getPropertySetInfo(); + if ( xPSI.is() && xPSI->hasPropertyByName( getTabIndexPropertyName() ) ) + xProps->setPropertyValue( getTabIndexPropertyName(), Any( nTabIndex++ ) ); + } + mbGroupsUpToDate = false; + } +} + + +typedef ::std::multimap< sal_Int32, Reference< XControlModel > > MapIndexToModel; + + +Sequence< Reference< XControlModel > > SAL_CALL ControlModelContainerBase::getControlModels( ) +{ + SolarMutexGuard aGuard; + + MapIndexToModel aSortedModels; + // will be the sorted container of all models which have a tab index property + ::std::vector< Reference< XControlModel > > aUnindexedModels; + // will be the container of all models which do not have a tab index property + + for ( const auto& rModel : maModels ) + { + Reference< XControlModel > xModel( rModel.first ); + + // see if the model has a TabIndex property + Reference< XPropertySet > xControlProps( xModel, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI; + if ( xControlProps.is() ) + xPSI = xControlProps->getPropertySetInfo( ); + DBG_ASSERT( xPSI.is(), "ControlModelContainerBase::getControlModels: invalid child model!" ); + + // has it? + if ( xPSI.is() && xPSI->hasPropertyByName( getTabIndexPropertyName() ) ) + { // yes + sal_Int32 nTabIndex = -1; + xControlProps->getPropertyValue( getTabIndexPropertyName() ) >>= nTabIndex; + + aSortedModels.emplace( nTabIndex, xModel ); + } + else if ( xModel.is() ) + // no, it hasn't, but we have to include it, anyway + aUnindexedModels.push_back( xModel ); + } + + // okay, here we have a container of all our models, sorted by tab index, + // plus a container of "unindexed" models + // -> merge them + Sequence< Reference< XControlModel > > aReturn( aUnindexedModels.size() + aSortedModels.size() ); + ::std::transform( + aSortedModels.begin(), aSortedModels.end(), + ::std::copy( aUnindexedModels.begin(), aUnindexedModels.end(), aReturn.getArray() ), + [] ( const MapIndexToModel::value_type& entryIndexToModel ) + { return entryIndexToModel.second; } + ); + + return aReturn; +} + + +void SAL_CALL ControlModelContainerBase::setGroup( const Sequence< Reference< XControlModel > >&, const OUString& ) +{ + // not supported. We have only implicit grouping: + // We only have a sequence of control models, and we _know_ (yes, that's a HACK relying on + // implementation details) that VCL does grouping according to the order of controls automatically + // At least VCL does this for all we're interested in: Radio buttons. + SAL_WARN("toolkit", "grouping not supported" ); +} + +////----- XInitialization ------------------------------------------------------------------- +void SAL_CALL ControlModelContainerBase::initialize (const Sequence<Any>& rArguments) +{ + if ( rArguments.getLength() == 1 ) + { + sal_Int16 nPageId = -1; + if ( !( rArguments[ 0 ] >>= nPageId )) + throw lang::IllegalArgumentException(); + m_nTabPageId = nPageId; + } + else + m_nTabPageId = -1; +} +::sal_Int16 SAL_CALL ControlModelContainerBase::getTabPageID() +{ + return m_nTabPageId; +} +sal_Bool SAL_CALL ControlModelContainerBase::getEnabled() +{ + SolarMutexGuard aGuard; + Reference<XPropertySet> xThis(*this, UNO_QUERY); + bool bEnabled = false; + xThis->getPropertyValue(GetPropertyName(BASEPROPERTY_ENABLED)) >>= bEnabled; + return bEnabled; +} +void SAL_CALL ControlModelContainerBase::setEnabled( sal_Bool _enabled ) +{ + SolarMutexGuard aGuard; + Reference<XPropertySet> xThis(*this, UNO_QUERY); + xThis->setPropertyValue(GetPropertyName(BASEPROPERTY_ENABLED), Any(_enabled)); +} +OUString SAL_CALL ControlModelContainerBase::getTitle() +{ + SolarMutexGuard aGuard; + Reference<XPropertySet> xThis(*this,UNO_QUERY); + OUString sTitle; + xThis->getPropertyValue(GetPropertyName(BASEPROPERTY_TITLE)) >>= sTitle; + return sTitle; +} +void SAL_CALL ControlModelContainerBase::setTitle( const OUString& _title ) +{ + SolarMutexGuard aGuard; + Reference<XPropertySet> xThis(*this,UNO_QUERY); + xThis->setPropertyValue(GetPropertyName(BASEPROPERTY_TITLE),Any(_title)); +} +OUString SAL_CALL ControlModelContainerBase::getImageURL() +{ + return m_sImageURL; +} +void SAL_CALL ControlModelContainerBase::setImageURL( const OUString& _imageurl ) +{ + m_sImageURL = _imageurl; +} +OUString SAL_CALL ControlModelContainerBase::getToolTip() +{ + return m_sTooltip; +} +void SAL_CALL ControlModelContainerBase::setToolTip( const OUString& _tooltip ) +{ + m_sTooltip = _tooltip; +} + + +namespace +{ + enum GroupingMachineState + { + eLookingForGroup, + eExpandingGroup + }; + + + sal_Int32 lcl_getDialogStep( const Reference< XControlModel >& _rxModel ) + { + sal_Int32 nStep = 0; + try + { + Reference< XPropertySet > xModelProps( _rxModel, UNO_QUERY ); + xModelProps->getPropertyValue( getStepPropertyName() ) >>= nStep; + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("toolkit", "caught an exception while determining the dialog page" ); + } + return nStep; + } +} + + +sal_Int32 SAL_CALL ControlModelContainerBase::getGroupCount( ) +{ + SolarMutexGuard aGuard; + + implUpdateGroupStructure(); + + return maGroups.size(); +} + + +void SAL_CALL ControlModelContainerBase::getGroup( sal_Int32 _nGroup, Sequence< Reference< XControlModel > >& _rGroup, OUString& _rName ) +{ + SolarMutexGuard aGuard; + + implUpdateGroupStructure(); + + if ( ( _nGroup < 0 ) || ( o3tl::make_unsigned(_nGroup) >= maGroups.size() ) ) + { + SAL_WARN("toolkit", "invalid argument and I am not allowed to throw exception!" ); + _rGroup.realloc( 0 ); + _rName.clear(); + } + else + { + AllGroups::const_iterator aGroupPos = maGroups.begin() + _nGroup; + _rGroup.realloc( aGroupPos->size() ); + // copy the models + ::std::copy( aGroupPos->begin(), aGroupPos->end(), _rGroup.getArray() ); + // give the group a name + _rName = OUString::number( _nGroup ); + } +} + + +void SAL_CALL ControlModelContainerBase::getGroupByName( const OUString& _rName, Sequence< Reference< XControlModel > >& _rGroup ) +{ + SolarMutexGuard aGuard; + + OUString sDummyName; + getGroup( _rName.toInt32( ), _rGroup, sDummyName ); +} + + +void SAL_CALL ControlModelContainerBase::addChangesListener( const Reference< XChangesListener >& _rxListener ) +{ + maChangeListeners.addInterface( _rxListener ); +} + + +void SAL_CALL ControlModelContainerBase::removeChangesListener( const Reference< XChangesListener >& _rxListener ) +{ + maChangeListeners.removeInterface( _rxListener ); +} + + +void ControlModelContainerBase::implNotifyTabModelChange( const OUString& _rAccessor ) +{ + // multiplex to our change listeners: + // the changes event + ChangesEvent aEvent; + aEvent.Source = *this; + aEvent.Base <<= aEvent.Source; // the "base of the changes root" is also ourself + aEvent.Changes.realloc( 1 ); // exactly one change + aEvent.Changes.getArray()[ 0 ].Accessor <<= _rAccessor; + + + std::vector< Reference< css::util::XChangesListener > > aChangeListeners( maChangeListeners.getElements() ); + for ( const auto& rListener : aChangeListeners ) + rListener->changesOccurred( aEvent ); +} + + +void ControlModelContainerBase::implUpdateGroupStructure() +{ + if ( mbGroupsUpToDate ) + // nothing to do + return; + + // conditions for a group: + // * all elements of the group are radio buttons + // * all elements of the group are on the same dialog page + // * in the overall control order (determined by the tab index), all elements are subsequent + + maGroups.clear(); + + const Sequence< Reference< XControlModel > > aControlModels = getControlModels(); + + // in extreme we have as much groups as controls + maGroups.reserve( aControlModels.getLength() ); + + GroupingMachineState eState = eLookingForGroup; // the current state of our machine + Reference< XServiceInfo > xModelSI; // for checking for a radio button + AllGroups::iterator aCurrentGroup = maGroups.end(); // the group which we're currently building + sal_Int32 nCurrentGroupStep = -1; // the step which all controls of the current group belong to + + + for ( const Reference< XControlModel >& rControlModel : aControlModels ) + { + // we'll need this in every state + xModelSI.set(rControlModel, css::uno::UNO_QUERY); + // is it a radio button? + bool bIsRadioButton = xModelSI.is() && xModelSI->supportsService( "com.sun.star.awt.UnoControlRadioButtonModel" ); + + switch ( eState ) + { + case eLookingForGroup: + { + if ( !bIsRadioButton ) + // this is no radio button -> still looking for the beginning of a group + continue; + // the current model is a radio button + // -> we found the beginning of a new group + // create the place for this group + size_t nGroups = maGroups.size(); + maGroups.resize( nGroups + 1 ); + aCurrentGroup = maGroups.begin() + nGroups; + // and add the (only, til now) member + aCurrentGroup->push_back( rControlModel ); + + // get the step which all controls of this group now have to belong to + nCurrentGroupStep = lcl_getDialogStep( rControlModel ); + // new state: looking for further members + eState = eExpandingGroup; + + } + break; + + case eExpandingGroup: + { + if ( !bIsRadioButton ) + { // no radio button -> the group is done + aCurrentGroup = maGroups.end(); + eState = eLookingForGroup; + continue; + } + + // it is a radio button - is it on the proper page? + const sal_Int32 nThisModelStep = lcl_getDialogStep( rControlModel ); + if ( ( nThisModelStep == nCurrentGroupStep ) // the current button is on the same dialog page + || ( 0 == nThisModelStep ) // the current button appears on all pages + ) + { + // -> it belongs to the same group + aCurrentGroup->push_back( rControlModel ); + // state still is eExpandingGroup - we're looking for further elements + eState = eExpandingGroup; + + continue; + } + + // it's a radio button, but on a different page + // -> we open a new group for it + + + // open a new group + size_t nGroups = maGroups.size(); + maGroups.resize( nGroups + 1 ); + aCurrentGroup = maGroups.begin() + nGroups; + // and add the (only, til now) member + aCurrentGroup->push_back( rControlModel ); + + nCurrentGroupStep = nThisModelStep; + + // state is the same: we still are looking for further elements of the current group + eState = eExpandingGroup; + } + break; + } + } + + mbGroupsUpToDate = true; +} + + +void SAL_CALL ControlModelContainerBase::propertyChange( const PropertyChangeEvent& _rEvent ) +{ + SolarMutexGuard aGuard; + + DBG_ASSERT( _rEvent.PropertyName == "TabIndex", + "ControlModelContainerBase::propertyChange: not listening for this property!" ); + + // the accessor for the changed element + OUString sAccessor; + UnoControlModelHolderVector::const_iterator aPos = + ::std::find_if( + maModels.begin(), maModels.end(), + CompareControlModel( Reference< XControlModel >( _rEvent.Source, UNO_QUERY ) ) + ); + OSL_ENSURE( maModels.end() != aPos, "ControlModelContainerBase::propertyChange: don't know this model!" ); + if ( maModels.end() != aPos ) + sAccessor = aPos->second; + + // our groups are not up-to-date + mbGroupsUpToDate = false; + + // notify + implNotifyTabModelChange( sAccessor ); +} + + +void SAL_CALL ControlModelContainerBase::disposing( const EventObject& /*rEvent*/ ) +{ +} + + +void ControlModelContainerBase::startControlListening( const Reference< XControlModel >& _rxChildModel ) +{ + SolarMutexGuard aGuard; + + Reference< XPropertySet > xModelProps( _rxChildModel, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI; + if ( xModelProps.is() ) + xPSI = xModelProps->getPropertySetInfo(); + + if ( xPSI.is() && xPSI->hasPropertyByName( getTabIndexPropertyName() ) ) + xModelProps->addPropertyChangeListener( getTabIndexPropertyName(), this ); +} + + +void ControlModelContainerBase::stopControlListening( const Reference< XControlModel >& _rxChildModel ) +{ + SolarMutexGuard aGuard; + + Reference< XPropertySet > xModelProps( _rxChildModel, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI; + if ( xModelProps.is() ) + xPSI = xModelProps->getPropertySetInfo(); + + if ( xPSI.is() && xPSI->hasPropertyByName( getTabIndexPropertyName() ) ) + xModelProps->removePropertyChangeListener( getTabIndexPropertyName(), this ); +} + + +// = class ResourceListener + + +ResourceListener::ResourceListener( + const Reference< util::XModifyListener >& rListener ) : + m_xListener( rListener ), + m_bListening( false ) +{ +} + +ResourceListener::~ResourceListener() +{ +} + +// XInterface +Any SAL_CALL ResourceListener::queryInterface( const Type& rType ) +{ + Any a = ::cppu::queryInterface( + rType , + static_cast< XModifyListener* >( this ), + static_cast< XEventListener* >( this )); + + if ( a.hasValue() ) + return a; + + return OWeakObject::queryInterface( rType ); +} + +void SAL_CALL ResourceListener::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL ResourceListener::release() noexcept +{ + OWeakObject::release(); +} + +void ResourceListener::startListening( + const Reference< resource::XStringResourceResolver >& rResource ) +{ + { + // --- SAFE --- + std::unique_lock aGuard( m_aMutex ); + bool bListening( m_bListening ); + bool bResourceSet( m_xResource.is() ); + aGuard.unlock(); + // --- SAFE --- + + if ( bListening && bResourceSet ) + stopListening(); + + // --- SAFE --- + aGuard.lock(); + m_xResource = rResource; + aGuard.unlock(); + // --- SAFE --- + } + + if ( !rResource.is() ) + return; + + try + { + rResource->addModifyListener( this ); + + // --- SAFE --- + std::scoped_lock aGuard( m_aMutex ); + m_bListening = true; + // --- SAFE --- + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } +} + +void ResourceListener::stopListening() +{ + Reference< util::XModifyBroadcaster > xModifyBroadcaster; + + // --- SAFE --- + std::unique_lock aGuard( m_aMutex ); + if ( m_bListening && m_xResource.is() ) + xModifyBroadcaster = m_xResource; + aGuard.unlock(); + // --- SAFE --- + + if ( !xModifyBroadcaster.is() ) + return; + + try + { + // --- SAFE --- + aGuard.lock(); + m_bListening = false; + m_xResource.clear(); + aGuard.unlock(); + // --- SAFE --- + + xModifyBroadcaster->removeModifyListener( this ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } +} + +// XModifyListener +void SAL_CALL ResourceListener::modified( + const lang::EventObject& aEvent ) +{ + Reference< util::XModifyListener > xListener; + + // --- SAFE --- + std::unique_lock aGuard( m_aMutex ); + xListener = m_xListener; + aGuard.unlock(); + // --- SAFE --- + + if ( !xListener.is() ) + return; + + try + { + xListener->modified( aEvent ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } +} + +// XEventListener +void SAL_CALL ResourceListener::disposing( + const EventObject& Source ) +{ + Reference< lang::XEventListener > xListener; + Reference< resource::XStringResourceResolver > xResource; + + // --- SAFE --- + std::unique_lock aGuard( m_aMutex ); + Reference< XInterface > xIfacRes( m_xResource, UNO_QUERY ); + Reference< XInterface > xIfacList( m_xListener, UNO_QUERY ); + aGuard.unlock(); + // --- SAFE --- + + if ( Source.Source == xIfacRes ) + { + // --- SAFE --- + aGuard.lock(); + m_bListening = false; + xResource = m_xResource; + xListener = m_xListener; + m_xResource.clear(); + aGuard.unlock(); + // --- SAFE --- + + if ( xListener.is() ) + { + try + { + xListener->disposing( Source ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } + } + else if ( Source.Source == xIfacList ) + { + // --- SAFE --- + aGuard.lock(); + m_bListening = false; + xListener = m_xListener; + xResource = m_xResource; + m_xResource.clear(); + m_xListener.clear(); + aGuard.unlock(); + // --- SAFE --- + + // Remove ourself as listener from resource resolver + if ( xResource.is() ) + { + try + { + xResource->removeModifyListener( this ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } + } +} + + + +ControlContainerBase::ControlContainerBase( const Reference< XComponentContext >& rxContext ) + :m_xContext(rxContext) + ,mbSizeModified(false) + ,mbPosModified(false) +{ + maComponentInfos.nWidth = 280; + maComponentInfos.nHeight = 400; + mxListener = new ResourceListener( Reference< util::XModifyListener >(this) ); +} + +ControlContainerBase::~ControlContainerBase() +{ +} + +void ControlContainerBase::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) +{ + SolarMutexGuard aGuard; + UnoControlContainer::createPeer( rxToolkit, rParentPeer ); +} + +void ControlContainerBase::ImplInsertControl( Reference< XControlModel > const & rxModel, const OUString& rName ) +{ + Reference< XPropertySet > xP( rxModel, UNO_QUERY ); + + OUString aDefCtrl; + xP->getPropertyValue( GetPropertyName( BASEPROPERTY_DEFAULTCONTROL ) ) >>= aDefCtrl; + Reference < XControl > xCtrl( m_xContext->getServiceManager()->createInstanceWithContext(aDefCtrl, m_xContext), UNO_QUERY ); + + DBG_ASSERT( xCtrl.is(), "ControlContainerBase::ImplInsertControl: could not create the control!" ); + if ( xCtrl.is() ) + { + xCtrl->setModel( rxModel ); + addControl( rName, xCtrl ); + // will implicitly call addingControl, where we can add the PropertiesChangeListener to the model + // (which we formerly did herein) + // 08.01.2001 - 96008 - fs@openoffice.org + + ImplSetPosSize( xCtrl ); + } +} + +void ControlContainerBase::ImplRemoveControl( Reference< XControlModel > const & rxModel ) +{ + Sequence< Reference< XControl > > aControls = getControls(); + Reference< XControl > xCtrl = StdTabController::FindControl( aControls, rxModel ); + if ( xCtrl.is() ) + { + removeControl( xCtrl ); + try + { + xCtrl->dispose(); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } +} + +void ControlContainerBase::ImplSetPosSize( Reference< XControl >& rxCtrl ) +{ + Reference< XPropertySet > xP( rxCtrl->getModel(), UNO_QUERY ); + + sal_Int32 nX = 0, nY = 0, nWidth = 0, nHeight = 0; + xP->getPropertyValue("PositionX") >>= nX; + xP->getPropertyValue("PositionY") >>= nY; + xP->getPropertyValue("Width") >>= nWidth; + xP->getPropertyValue("Height") >>= nHeight; + MapMode aMode( MapUnit::MapAppFont ); + OutputDevice*pOutDev = Application::GetDefaultDevice(); + if ( pOutDev ) + { + ::Size aTmp( nX, nY ); + aTmp = pOutDev->LogicToPixel( aTmp, aMode ); + nX = aTmp.Width(); + nY = aTmp.Height(); + aTmp = ::Size( nWidth, nHeight ); + aTmp = pOutDev->LogicToPixel( aTmp, aMode ); + nWidth = aTmp.Width(); + nHeight = aTmp.Height(); + } + else + { + Reference< XWindowPeer > xPeer = ImplGetCompatiblePeer(); + Reference< XDevice > xD( xPeer, UNO_QUERY ); + + SimpleFontMetric aFM; + FontDescriptor aFD; + Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_FONTDESCRIPTOR ) ); + aVal >>= aFD; + if ( !aFD.StyleName.isEmpty() ) + { + Reference< XFont > xFont = xD->getFont( aFD ); + aFM = xFont->getFontMetric(); + } + else + { + Reference< XGraphics > xG = xD->createGraphics(); + aFM = xG->getFontMetric(); + } + + sal_Int16 nH = aFM.Ascent + aFM.Descent; + sal_Int16 nW = nH/2; // calculate average width?! + + nX *= nW; + nX /= 4; + nWidth *= nW; + nWidth /= 4; + nY *= nH; + nY /= 8; + nHeight *= nH; + nHeight /= 8; + } + Reference < XWindow > xW( rxCtrl, UNO_QUERY ); + xW->setPosSize( nX, nY, nWidth, nHeight, PosSize::POSSIZE ); +} + +void ControlContainerBase::dispose() +{ + EventObject aEvt; + aEvt.Source = static_cast< ::cppu::OWeakObject* >( this ); + // Notify our listener helper about dispose + // --- SAFE --- + + SolarMutexClearableGuard aGuard; + Reference< XEventListener > xListener = mxListener; + mxListener.clear(); + aGuard.clear(); + // --- SAFE --- + + if ( xListener.is() ) + xListener->disposing( aEvt ); + UnoControlContainer::dispose(); +} + +void SAL_CALL ControlContainerBase::disposing( + const EventObject& Source ) +{ + UnoControlContainer::disposing( Source ); +} + +sal_Bool ControlContainerBase::setModel( const Reference< XControlModel >& rxModel ) +{ + SolarMutexGuard aGuard; + + // destroy the old tab controller, if existent + if ( mxTabController.is() ) + { + mxTabController->setModel( nullptr ); // just to be sure, should not be necessary + removeTabController( mxTabController ); + ::comphelper::disposeComponent( mxTabController ); // just to be sure, should not be necessary + mxTabController.clear(); + } + + if ( getModel().is() ) + { + const Sequence< Reference< XControl > > aControls = getControls(); + + for ( const Reference< XControl >& rCtrl : aControls ) + removeControl( rCtrl ); + // will implicitly call removingControl, which will remove the PropertyChangeListener + // (which we formerly did herein) + // 08.01.2001 - 96008 - fs@openoffice.org + + Reference< XContainer > xC( getModel(), UNO_QUERY ); + if ( xC.is() ) + xC->removeContainerListener( this ); + + Reference< XChangesNotifier > xChangeNotifier( getModel(), UNO_QUERY ); + if ( xChangeNotifier.is() ) + xChangeNotifier->removeChangesListener( this ); + } + + bool bRet = UnoControl::setModel( rxModel ); + + if ( getModel().is() ) + { + Reference< XNameAccess > xNA( getModel(), UNO_QUERY ); + if ( xNA.is() ) + { + const Sequence< OUString > aNames = xNA->getElementNames(); + + Reference< XControlModel > xCtrlModel; + for( const OUString& rName : aNames ) + { + xNA->getByName( rName ) >>= xCtrlModel; + ImplInsertControl( xCtrlModel, rName ); + } + } + + Reference< XContainer > xC( getModel(), UNO_QUERY ); + if ( xC.is() ) + xC->addContainerListener( this ); + + Reference< XChangesNotifier > xChangeNotifier( getModel(), UNO_QUERY ); + if ( xChangeNotifier.is() ) + xChangeNotifier->addChangesListener( this ); + } + + Reference< XTabControllerModel > xTabbing( getModel(), UNO_QUERY ); + if ( xTabbing.is() ) + { + mxTabController = new StdTabController; + mxTabController->setModel( xTabbing ); + addTabController( mxTabController ); + } + ImplStartListingForResourceEvents(); + + return bRet; +} +void ControlContainerBase::setDesignMode( sal_Bool bOn ) +{ + SolarMutexGuard aGuard; + + UnoControl::setDesignMode( bOn ); + + Sequence< Reference< XControl > > xCtrls = getControls(); + for ( Reference< XControl >& rControl : asNonConstRange(xCtrls) ) + rControl->setDesignMode( bOn ); + + // #109067# in design mode the tab controller is not notified about + // tab index changes, therefore the tab order must be activated + // when switching from design mode to live mode + if ( mxTabController.is() && !bOn ) + mxTabController->activateTabOrder(); +} + +void ControlContainerBase::elementInserted( const ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xModel; + OUString aName; + + Event.Accessor >>= aName; + Event.Element >>= xModel; + ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementInserted: illegal element!" ); + try + { + ImplInsertControl( xModel, aName ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } +} + +void ControlContainerBase::elementRemoved( const ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xModel; + Event.Element >>= xModel; + ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementRemoved: illegal element!" ); + try + { + ImplRemoveControl( xModel ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } +} + +void ControlContainerBase::elementReplaced( const ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xModel; + Event.ReplacedElement >>= xModel; + try + { + OSL_ENSURE( xModel.is(), "ControlContainerBase::elementReplaced: invalid ReplacedElement!" ); + if ( xModel.is() ) + ImplRemoveControl( xModel ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + + OUString aName; + Event.Accessor >>= aName; + Event.Element >>= xModel; + ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementReplaced: invalid new element!" ); + try + { + ImplInsertControl( xModel, aName ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } +} + +// XPropertiesChangeListener +void ControlContainerBase::ImplModelPropertiesChanged( const Sequence< PropertyChangeEvent >& rEvents ) +{ + if( !isDesignMode() && !mbCreatingCompatiblePeer ) + { + auto pEvt = std::find_if(rEvents.begin(), rEvents.end(), + [](const PropertyChangeEvent& rEvt) { + return rEvt.PropertyName == "PositionX" + || rEvt.PropertyName == "PositionY" + || rEvt.PropertyName == "Width" + || rEvt.PropertyName == "Height"; + }); + if (pEvt != rEvents.end()) + { + Reference< XControlModel > xModel( pEvt->Source, UNO_QUERY ); + bool bOwnModel = xModel.get() == getModel().get(); + if ( bOwnModel ) + { + if ( !mbPosModified && !mbSizeModified ) + { + // Don't set new pos/size if we get new values from window listener + Reference< XControl > xThis(this); + ImplSetPosSize( xThis ); + } + } + else + { + Sequence<Reference<XControl> > aControlSequence(getControls()); + Reference<XControl> aControlRef( StdTabController::FindControl( aControlSequence, xModel ) ); + ImplSetPosSize( aControlRef ); + } + } + } + + UnoControlContainer::ImplModelPropertiesChanged( rEvents ); +} + +void ControlContainerBase::addingControl( const Reference< XControl >& _rxControl ) +{ + SolarMutexGuard aGuard; + UnoControlContainer::addingControl( _rxControl ); + + if ( !_rxControl.is() ) + return; + + Reference< XMultiPropertySet > xProps( _rxControl->getModel(), UNO_QUERY ); + if ( xProps.is() ) + { + const Sequence< OUString > aNames { + "PositionX", + "PositionY", + "Width", + "Height" + }; + + xProps->addPropertiesChangeListener( aNames, this ); + } +} + +void ControlContainerBase::removingControl( const Reference< XControl >& _rxControl ) +{ + SolarMutexGuard aGuard; + UnoControlContainer::removingControl( _rxControl ); + + if ( _rxControl.is() ) + { + Reference< XMultiPropertySet > xProps( _rxControl->getModel(), UNO_QUERY ); + if ( xProps.is() ) + xProps->removePropertiesChangeListener( this ); + } + +} + +void SAL_CALL ControlContainerBase::changesOccurred( const ChangesEvent& ) +{ + SolarMutexGuard aGuard; + // a tab controller model may have changed + + // #109067# in design mode don't notify the tab controller + // about tab index changes + if ( mxTabController.is() && !mbDesignMode ) + mxTabController->activateTabOrder(); +} +static void lcl_ApplyResolverToNestedContainees( const Reference< resource::XStringResourceResolver >& xStringResourceResolver, const Reference< XControlContainer >& xContainer ) +{ + OUString aPropName( PROPERTY_RESOURCERESOLVER ); + + Any aNewStringResourceResolver; + aNewStringResourceResolver <<= xStringResourceResolver; + + Sequence< OUString > aPropNames { aPropName }; + + const Sequence< Reference< awt::XControl > > aSeq = xContainer->getControls(); + for ( const Reference< XControl >& xControl : aSeq ) + { + Reference< XPropertySet > xPropertySet; + + if ( xControl.is() ) + xPropertySet.set( xControl->getModel(), UNO_QUERY ); + + if ( !xPropertySet.is() ) + continue; + + try + { + Reference< resource::XStringResourceResolver > xCurrStringResourceResolver; + Any aOldValue = xPropertySet->getPropertyValue( aPropName ); + if ( ( aOldValue >>= xCurrStringResourceResolver ) + && ( xStringResourceResolver == xCurrStringResourceResolver ) + ) + { + Reference< XMultiPropertySet > xMultiPropSet( xPropertySet, UNO_QUERY ); + Reference< XPropertiesChangeListener > xListener( xPropertySet, UNO_QUERY ); + xMultiPropSet->firePropertiesChangeEvent( aPropNames, xListener ); + } + else + xPropertySet->setPropertyValue( aPropName, aNewStringResourceResolver ); + } + catch (const Exception&) + { + } + + uno::Reference< XControlContainer > xNestedContainer( xControl, uno::UNO_QUERY ); + if ( xNestedContainer.is() ) + lcl_ApplyResolverToNestedContainees( xStringResourceResolver, xNestedContainer ); + + } + +} +void ControlContainerBase::ImplStartListingForResourceEvents() +{ + Reference< resource::XStringResourceResolver > xStringResourceResolver; + + if ( !ImplHasProperty(PROPERTY_RESOURCERESOLVER) ) + return; + + ImplGetPropertyValue( PROPERTY_RESOURCERESOLVER ) >>= xStringResourceResolver; + + // Add our helper as listener to retrieve notifications about changes + Reference< util::XModifyListener > rListener( mxListener ); + ResourceListener* pResourceListener = static_cast< ResourceListener* >( rListener.get() ); + + // resource listener will stop listening if resolver reference is empty + if ( pResourceListener ) + pResourceListener->startListening( xStringResourceResolver ); + ImplUpdateResourceResolver(); +} + +void ControlContainerBase::ImplUpdateResourceResolver() +{ + Reference< resource::XStringResourceResolver > xStringResourceResolver; + + if ( !ImplHasProperty(PROPERTY_RESOURCERESOLVER) ) + return; + + ImplGetPropertyValue(PROPERTY_RESOURCERESOLVER) >>= xStringResourceResolver; + + if ( !xStringResourceResolver.is() ) + return; + + lcl_ApplyResolverToNestedContainees( xStringResourceResolver, this ); + + // propagate resource resolver changes to language dependent props of the dialog + Reference< XPropertySet > xPropertySet( getModel(), UNO_QUERY ); + if ( xPropertySet.is() ) + { + Reference< XMultiPropertySet > xMultiPropSet( xPropertySet, UNO_QUERY ); + Reference< XPropertiesChangeListener > xListener( xPropertySet, UNO_QUERY ); + xMultiPropSet->firePropertiesChangeEvent( lcl_getLanguageDependentProperties(), xListener ); + } +} + +//// ---------------------------------------------------- +//// Helper Method to convert relative url to physical location +//// ---------------------------------------------------- + +OUString getPhysicalLocation( const css::uno::Any& rbase, const css::uno::Any& rUrl ) +{ + + OUString baseLocation; + OUString url; + + rbase >>= baseLocation; + rUrl >>= url; + + OUString absoluteURL( url ); + if ( !url.isEmpty() ) + { + INetURLObject urlObj(baseLocation); + urlObj.removeSegment(); + baseLocation = urlObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + const INetURLObject protocolCheck( url ); + const INetProtocol protocol = protocolCheck.GetProtocol(); + if ( protocol == INetProtocol::NotValid ) + { + OUString testAbsoluteURL; + if ( ::osl::FileBase::E_None == ::osl::FileBase::getAbsoluteFileURL( baseLocation, url, testAbsoluteURL ) ) + absoluteURL = testAbsoluteURL; + } + } + + return absoluteURL; +} + +void +ControlModelContainerBase::updateUserFormChildren( const Reference< XNameContainer >& xAllChildren, const OUString& aName, ChildOperation Operation, const css::uno::Reference< css::awt::XControlModel >& xTarget ) +{ + if ( Operation < Insert || Operation > Remove ) + throw IllegalArgumentException(); + + if ( !xAllChildren.is() ) + throw IllegalArgumentException(); + + if ( Operation == Remove ) + { + Reference< XControlModel > xOldModel( xAllChildren->getByName( aName ), UNO_QUERY ); + xAllChildren->removeByName( aName ); + + Reference< XNameContainer > xChildContainer( xOldModel, UNO_QUERY ); + if ( xChildContainer.is() ) + { + Reference< XPropertySet > xProps( xChildContainer, UNO_QUERY ); + // container control is being removed from this container, reset the + // global list of containers + if ( xProps.is() ) + xProps->setPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ), uno::Any( uno::Reference< XNameContainer >() ) ); + const Sequence< OUString > aChildNames = xChildContainer->getElementNames(); + for ( const auto& rName : aChildNames ) + updateUserFormChildren( xAllChildren, rName, Operation, Reference< XControlModel > () ); + } + } + else if ( Operation == Insert ) + { + xAllChildren->insertByName( aName, uno::Any( xTarget ) ); + Reference< XNameContainer > xChildContainer( xTarget, UNO_QUERY ); + if ( xChildContainer.is() ) + { + // container control is being added from this container, reset the + // global list of containers to point to the correct global list + Reference< XPropertySet > xProps( xChildContainer, UNO_QUERY ); + if ( xProps.is() ) + xProps->setPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ), uno::Any( xAllChildren ) ); + const Sequence< OUString > aChildNames = xChildContainer->getElementNames(); + for ( const auto& rName : aChildNames ) + { + Reference< XControlModel > xChildTarget( xChildContainer->getByName( rName ), UNO_QUERY ); + updateUserFormChildren( xAllChildren, rName, Operation, xChildTarget ); + } + } + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |