/************************************************************** * * 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 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ #include "precompiled_vcl.hxx" #include "vcl/wpropset.hxx" #include "vcl/window.hxx" #include "vcl/vclevent.hxx" #include "svdata.hxx" #include "com/sun/star/lang/XMultiServiceFactory.hpp" #include "com/sun/star/beans/PropertyValue.hpp" #include "com/sun/star/beans/PropertyAttribute.hpp" #include "com/sun/star/beans/XPropertySet.hpp" #include "com/sun/star/beans/XPropertyContainer.hpp" #include "com/sun/star/beans/XPropertyAccess.hpp" #include "cppuhelper/basemutex.hxx" #include "cppuhelper/compbase1.hxx" #include using namespace vcl; using namespace com::sun::star; /* TODO: - release solarmutex during outside UNO calls - in ChildEventListener protect against reentry by using PostUserEvent */ class vcl::WindowPropertySetListener : public cppu::BaseMutex, public cppu::WeakComponentImplHelper1< com::sun::star::beans::XPropertyChangeListener >, private boost::noncopyable { WindowPropertySet* mpParent; bool mbSuspended; public: WindowPropertySetListener( WindowPropertySet* pParent ) : cppu::WeakComponentImplHelper1< com::sun::star::beans::XPropertyChangeListener >( m_aMutex ) , mpParent( pParent ) , mbSuspended( false ) {} virtual ~WindowPropertySetListener() { } using cppu::WeakComponentImplHelperBase::disposing; virtual void SAL_CALL disposing( const lang::EventObject& ) throw() { } virtual void SAL_CALL propertyChange( const beans::PropertyChangeEvent& i_rEvent ) throw() { if( ! mbSuspended ) mpParent->propertyChange( i_rEvent ); } void suspend( bool i_bSuspended ) { mbSuspended = i_bSuspended; } }; class vcl::WindowPropertySetData { public: struct PropertyMapEntry { Window* mpWindow; boost::shared_ptr mpLayout; uno::Sequence< beans::PropertyValue > maSavedValues; PropertyMapEntry( Window* i_pWindow = NULL, const boost::shared_ptr& i_pLayout = boost::shared_ptr() ) : mpWindow( i_pWindow ) , mpLayout( i_pLayout ) {} uno::Sequence< beans::PropertyValue > getProperties() const { if( mpWindow ) return mpWindow->getProperties(); else if( mpLayout.get() ) return mpLayout->getProperties(); return uno::Sequence< beans::PropertyValue >(); } void setProperties( const uno::Sequence< beans::PropertyValue >& i_rProps ) const { if( mpWindow ) mpWindow->setProperties( i_rProps ); else if( mpLayout.get() ) mpLayout->setProperties( i_rProps ); } }; Window* mpTopWindow; bool mbOwner; std::map< rtl::OUString, PropertyMapEntry > maProperties; uno::Reference< beans::XPropertySet > mxPropSet; uno::Reference< beans::XPropertyAccess > mxPropSetAccess; uno::Reference< beans::XPropertyChangeListener > mxListener; vcl::WindowPropertySetListener* mpListener; WindowPropertySetData() : mpTopWindow( NULL ) , mbOwner( false ) , mpListener( NULL ) {} ~WindowPropertySetData() { // release layouters, possibly interface properties before destroying // the involved parent to be on the safe side maProperties.clear(); if( mbOwner ) delete mpTopWindow; } }; static rtl::OUString getIdentifiedPropertyName( const rtl::OUString& i_rIdentifier, const rtl::OUString& i_rName ) { rtl::OUStringBuffer aBuf( i_rIdentifier.getLength() + 1 + i_rName.getLength() ); aBuf.append( i_rIdentifier ); aBuf.append( sal_Unicode( '#' ) ); aBuf.append( i_rName ); return aBuf.makeStringAndClear(); } static void spliceIdentifiedPropertyName( const rtl::OUString& i_rIdentifiedPropName, rtl::OUString& o_rIdentifier, rtl::OUString& o_rPropName ) { sal_Int32 nIndex = 0; o_rIdentifier = i_rIdentifiedPropName.getToken( 0, sal_Unicode( '#' ), nIndex ); if( nIndex != -1 ) o_rPropName = i_rIdentifiedPropName.copy( nIndex ); else o_rPropName = rtl::OUString(); } WindowPropertySet::WindowPropertySet( Window* i_pTopWindow, bool i_bTakeOwnership ) : mpImpl( new vcl::WindowPropertySetData ) { mpImpl->mpTopWindow = i_pTopWindow; mpImpl->mbOwner = i_bTakeOwnership; mpImpl->mpTopWindow->AddChildEventListener( LINK( this, WindowPropertySet, ChildEventListener ) ); mpImpl->mxPropSet = uno::Reference< beans::XPropertySet >( ImplGetSVData()->maAppData.mxMSF->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.beans.PropertyBag" ) ) ), uno::UNO_QUERY ); OSL_ENSURE( mpImpl->mxPropSet.is(), "could not create instance of com.sun.star.beans.PropertyBag" ); mpImpl->mxPropSetAccess = uno::Reference< beans::XPropertyAccess >( mpImpl->mxPropSet, uno::UNO_QUERY ); OSL_ENSURE( mpImpl->mxPropSet.is(), "could not query XPropertyAccess interface" ); if( ! mpImpl->mxPropSetAccess.is() ) mpImpl->mxPropSet.clear(); addWindowToSet( i_pTopWindow ); setupProperties(); if( mpImpl->mxPropSet.is() ) { mpImpl->mxListener.set( mpImpl->mpListener = new WindowPropertySetListener( this ) ); } } WindowPropertySet::~WindowPropertySet() { mpImpl->mpTopWindow->RemoveChildEventListener( LINK( this, WindowPropertySet, ChildEventListener ) ); delete mpImpl; mpImpl = NULL; } uno::Reference< beans::XPropertySet > WindowPropertySet::getPropertySet() const { return mpImpl->mxPropSet; } void WindowPropertySet::addLayoutToSet( const boost::shared_ptr< WindowArranger >& i_pLayout ) { if( i_pLayout.get() ) { if( i_pLayout->getIdentifier().getLength() ) { WindowPropertySetData::PropertyMapEntry& rEntry = mpImpl->maProperties[ i_pLayout->getIdentifier() ]; OSL_ENSURE( rEntry.mpWindow == 0 && rEntry.mpLayout.get() == 0, "inserted layout has duplicate name" ); rEntry.mpWindow = NULL; rEntry.mpLayout = i_pLayout; rEntry.maSavedValues = i_pLayout->getProperties(); } // insert child layouts size_t nChildren = i_pLayout->countElements(); for( size_t i = 0; i < nChildren; i++ ) addLayoutToSet( i_pLayout->getChild( i ) ); } } void WindowPropertySet::addWindowToSet( Window* i_pWindow ) { if( i_pWindow->getIdentifier().getLength() ) // no name, no properties { WindowPropertySetData::PropertyMapEntry& rEntry = mpImpl->maProperties[ i_pWindow->getIdentifier() ]; OSL_ENSURE( rEntry.mpWindow == 0 && rEntry.mpLayout.get() == 0, "inserted window has duplicate name" ); rEntry.mpWindow = i_pWindow; rEntry.mpLayout.reset(); rEntry.maSavedValues = i_pWindow->getProperties(); } addLayoutToSet( i_pWindow->getLayout() ); Window* pWin = i_pWindow->GetWindow( WINDOW_FIRSTCHILD ); while( pWin ) { addWindowToSet( pWin ); pWin = pWin->GetWindow( WINDOW_NEXT ); } } void WindowPropertySet::setupProperties() { uno::Reference< beans::XPropertyContainer > xCont( mpImpl->mxPropSet, uno::UNO_QUERY ); OSL_ENSURE( xCont.is(), "could not get XPropertyContainer interface" ); if( ! xCont.is() ) return; for( std::map< rtl::OUString, WindowPropertySetData::PropertyMapEntry >::iterator it = mpImpl->maProperties.begin(); it != mpImpl->maProperties.end(); ++it ) { uno::Sequence< beans::PropertyValue > aOutsideValues( it->second.maSavedValues ); beans::PropertyValue* pVal = aOutsideValues.getArray(); for( sal_Int32 i = 0; i < aOutsideValues.getLength(); i++ ) { pVal[i].Name = getIdentifiedPropertyName( it->first, pVal[i].Name ); xCont->addProperty( pVal[i].Name, beans::PropertyAttribute::BOUND | beans:: PropertyAttribute::CONSTRAINED, pVal[i].Value ); } } } void WindowPropertySet::propertyChange( const beans::PropertyChangeEvent& i_rEvent ) { rtl::OUString aIdentifier, aProperty; spliceIdentifiedPropertyName( i_rEvent.PropertyName, aIdentifier, aProperty ); std::map< rtl::OUString, WindowPropertySetData::PropertyMapEntry >::iterator it = mpImpl->maProperties.find( aIdentifier ); if( it != mpImpl->maProperties.end() ) { uno::Sequence< beans::PropertyValue > aSet( 1 ); aSet[0].Name = aProperty; aSet[0].Value = i_rEvent.NewValue; it->second.setProperties( aSet ); } } IMPL_LINK( vcl::WindowPropertySet, ChildEventListener, VclWindowEvent*, pEvent ) { // find window in our properties std::map< rtl::OUString, WindowPropertySetData::PropertyMapEntry >::iterator it = mpImpl->maProperties.find( pEvent->GetWindow()->getIdentifier() ); if( it != mpImpl->maProperties.end() ) // this is valid, some unnamed child may have sent an event { sal_uLong nId = pEvent->GetId(); // check if anything interesting happened if( // general windowy things nId == VCLEVENT_WINDOW_SHOW || nId == VCLEVENT_WINDOW_HIDE || nId == VCLEVENT_WINDOW_ENABLED || nId == VCLEVENT_WINDOW_DISABLED || // button thingies nId == VCLEVENT_BUTTON_CLICK || nId == VCLEVENT_PUSHBUTTON_TOGGLE || nId == VCLEVENT_RADIOBUTTON_TOGGLE || nId == VCLEVENT_CHECKBOX_TOGGLE || // listbox nId == VCLEVENT_LISTBOX_SELECT || // edit nId == VCLEVENT_EDIT_MODIFY ) { WindowPropertySetData::PropertyMapEntry& rEntry = it->second; // collect changes uno::Sequence< beans::PropertyValue > aNewProps( rEntry.getProperties() ); uno::Sequence< beans::PropertyValue > aNewPropsOut( aNewProps ); // translate to identified properties beans::PropertyValue* pValues = aNewPropsOut.getArray(); for( sal_Int32 i = 0; i < aNewPropsOut.getLength(); i++ ) pValues[i].Name = getIdentifiedPropertyName( it->first, pValues[i].Name ); // broadcast changes bool bWasVeto = false; mpImpl->mpListener->suspend( true ); try { mpImpl->mxPropSetAccess->setPropertyValues( aNewPropsOut ); } catch( beans::PropertyVetoException& ) { bWasVeto = true; } mpImpl->mpListener->suspend( false ); if( ! bWasVeto ) // changes accepted ? rEntry.maSavedValues = rEntry.getProperties(); else // no, reset rEntry.setProperties( rEntry.maSavedValues ); } } return 0; }