blob: a75ffd1e6b74ad530ae9c0e99d2e69bded4da7b2 [file] [log] [blame]
/**************************************************************
*
* 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 <map>
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<WindowArranger> mpLayout;
uno::Sequence< beans::PropertyValue > maSavedValues;
PropertyMapEntry( Window* i_pWindow = NULL,
const boost::shared_ptr<WindowArranger>& i_pLayout = boost::shared_ptr<WindowArranger>() )
: 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;
}