1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 #include "precompiled_vcl.hxx" 29 30 #include "vcl/wpropset.hxx" 31 #include "vcl/window.hxx" 32 #include "vcl/vclevent.hxx" 33 34 #include "svdata.hxx" 35 36 #include "com/sun/star/lang/XMultiServiceFactory.hpp" 37 #include "com/sun/star/beans/PropertyValue.hpp" 38 #include "com/sun/star/beans/PropertyAttribute.hpp" 39 #include "com/sun/star/beans/XPropertySet.hpp" 40 #include "com/sun/star/beans/XPropertyContainer.hpp" 41 #include "com/sun/star/beans/XPropertyAccess.hpp" 42 43 #include "cppuhelper/basemutex.hxx" 44 #include "cppuhelper/compbase1.hxx" 45 46 #include <map> 47 48 using namespace vcl; 49 using namespace com::sun::star; 50 51 /* 52 53 TODO: 54 - release solarmutex during outside UNO calls 55 - in ChildEventListener protect against reentry by using PostUserEvent 56 57 */ 58 59 class vcl::WindowPropertySetListener : 60 public cppu::BaseMutex, 61 public cppu::WeakComponentImplHelper1< com::sun::star::beans::XPropertyChangeListener >, 62 private boost::noncopyable 63 { 64 WindowPropertySet* mpParent; 65 bool mbSuspended; 66 public: 67 WindowPropertySetListener( WindowPropertySet* pParent ) 68 : cppu::WeakComponentImplHelper1< com::sun::star::beans::XPropertyChangeListener >( m_aMutex ) 69 , mpParent( pParent ) 70 , mbSuspended( false ) 71 {} 72 73 virtual ~WindowPropertySetListener() 74 { 75 } 76 77 using cppu::WeakComponentImplHelperBase::disposing; 78 virtual void SAL_CALL disposing( const lang::EventObject& ) throw() 79 { 80 } 81 82 virtual void SAL_CALL propertyChange( const beans::PropertyChangeEvent& i_rEvent ) throw() 83 { 84 if( ! mbSuspended ) 85 mpParent->propertyChange( i_rEvent ); 86 } 87 88 void suspend( bool i_bSuspended ) 89 { 90 mbSuspended = i_bSuspended; 91 } 92 }; 93 94 class vcl::WindowPropertySetData 95 { 96 public: 97 98 struct PropertyMapEntry 99 { 100 Window* mpWindow; 101 boost::shared_ptr<WindowArranger> mpLayout; 102 uno::Sequence< beans::PropertyValue > maSavedValues; 103 104 PropertyMapEntry( Window* i_pWindow = NULL, 105 const boost::shared_ptr<WindowArranger>& i_pLayout = boost::shared_ptr<WindowArranger>() ) 106 : mpWindow( i_pWindow ) 107 , mpLayout( i_pLayout ) 108 {} 109 110 uno::Sequence< beans::PropertyValue > getProperties() const 111 { 112 if( mpWindow ) 113 return mpWindow->getProperties(); 114 else if( mpLayout.get() ) 115 return mpLayout->getProperties(); 116 return uno::Sequence< beans::PropertyValue >(); 117 } 118 119 void setProperties( const uno::Sequence< beans::PropertyValue >& i_rProps ) const 120 { 121 if( mpWindow ) 122 mpWindow->setProperties( i_rProps ); 123 else if( mpLayout.get() ) 124 mpLayout->setProperties( i_rProps ); 125 } 126 }; 127 128 Window* mpTopWindow; 129 bool mbOwner; 130 std::map< rtl::OUString, PropertyMapEntry > maProperties; 131 uno::Reference< beans::XPropertySet > mxPropSet; 132 uno::Reference< beans::XPropertyAccess > mxPropSetAccess; 133 uno::Reference< beans::XPropertyChangeListener > mxListener; 134 vcl::WindowPropertySetListener* mpListener; 135 136 WindowPropertySetData() 137 : mpTopWindow( NULL ) 138 , mbOwner( false ) 139 , mpListener( NULL ) 140 {} 141 142 ~WindowPropertySetData() 143 { 144 // release layouters, possibly interface properties before destroying 145 // the involved parent to be on the safe side 146 maProperties.clear(); 147 if( mbOwner ) 148 delete mpTopWindow; 149 } 150 }; 151 152 static rtl::OUString getIdentifiedPropertyName( const rtl::OUString& i_rIdentifier, const rtl::OUString& i_rName ) 153 { 154 rtl::OUStringBuffer aBuf( i_rIdentifier.getLength() + 1 + i_rName.getLength() ); 155 aBuf.append( i_rIdentifier ); 156 aBuf.append( sal_Unicode( '#' ) ); 157 aBuf.append( i_rName ); 158 return aBuf.makeStringAndClear(); 159 } 160 161 static void spliceIdentifiedPropertyName( const rtl::OUString& i_rIdentifiedPropName, 162 rtl::OUString& o_rIdentifier, 163 rtl::OUString& o_rPropName ) 164 { 165 sal_Int32 nIndex = 0; 166 o_rIdentifier = i_rIdentifiedPropName.getToken( 0, sal_Unicode( '#' ), nIndex ); 167 if( nIndex != -1 ) 168 o_rPropName = i_rIdentifiedPropName.copy( nIndex ); 169 else 170 o_rPropName = rtl::OUString(); 171 } 172 173 WindowPropertySet::WindowPropertySet( Window* i_pTopWindow, bool i_bTakeOwnership ) 174 : mpImpl( new vcl::WindowPropertySetData ) 175 { 176 mpImpl->mpTopWindow = i_pTopWindow; 177 mpImpl->mbOwner = i_bTakeOwnership; 178 179 mpImpl->mpTopWindow->AddChildEventListener( LINK( this, WindowPropertySet, ChildEventListener ) ); 180 181 mpImpl->mxPropSet = uno::Reference< beans::XPropertySet >( 182 ImplGetSVData()->maAppData.mxMSF->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.beans.PropertyBag" ) ) ), 183 uno::UNO_QUERY ); 184 OSL_ENSURE( mpImpl->mxPropSet.is(), "could not create instance of com.sun.star.beans.PropertyBag" ); 185 mpImpl->mxPropSetAccess = uno::Reference< beans::XPropertyAccess >( mpImpl->mxPropSet, uno::UNO_QUERY ); 186 OSL_ENSURE( mpImpl->mxPropSet.is(), "could not query XPropertyAccess interface" ); 187 if( ! mpImpl->mxPropSetAccess.is() ) 188 mpImpl->mxPropSet.clear(); 189 190 addWindowToSet( i_pTopWindow ); 191 192 setupProperties(); 193 194 if( mpImpl->mxPropSet.is() ) 195 { 196 mpImpl->mxListener.set( mpImpl->mpListener = new WindowPropertySetListener( this ) ); 197 } 198 } 199 200 WindowPropertySet::~WindowPropertySet() 201 { 202 mpImpl->mpTopWindow->RemoveChildEventListener( LINK( this, WindowPropertySet, ChildEventListener ) ); 203 204 delete mpImpl; 205 mpImpl = NULL; 206 } 207 208 uno::Reference< beans::XPropertySet > WindowPropertySet::getPropertySet() const 209 { 210 return mpImpl->mxPropSet; 211 } 212 213 void WindowPropertySet::addLayoutToSet( const boost::shared_ptr< WindowArranger >& i_pLayout ) 214 { 215 if( i_pLayout.get() ) 216 { 217 if( i_pLayout->getIdentifier().getLength() ) 218 { 219 WindowPropertySetData::PropertyMapEntry& rEntry = mpImpl->maProperties[ i_pLayout->getIdentifier() ]; 220 OSL_ENSURE( rEntry.mpWindow == 0 && rEntry.mpLayout.get() == 0, "inserted layout has duplicate name" ); 221 rEntry.mpWindow = NULL; 222 rEntry.mpLayout = i_pLayout; 223 rEntry.maSavedValues = i_pLayout->getProperties(); 224 } 225 // insert child layouts 226 size_t nChildren = i_pLayout->countElements(); 227 for( size_t i = 0; i < nChildren; i++ ) 228 addLayoutToSet( i_pLayout->getChild( i ) ); 229 } 230 } 231 232 void WindowPropertySet::addWindowToSet( Window* i_pWindow ) 233 { 234 if( i_pWindow->getIdentifier().getLength() ) // no name, no properties 235 { 236 WindowPropertySetData::PropertyMapEntry& rEntry = mpImpl->maProperties[ i_pWindow->getIdentifier() ]; 237 OSL_ENSURE( rEntry.mpWindow == 0 && rEntry.mpLayout.get() == 0, "inserted window has duplicate name" ); 238 rEntry.mpWindow = i_pWindow; 239 rEntry.mpLayout.reset(); 240 rEntry.maSavedValues = i_pWindow->getProperties(); 241 } 242 addLayoutToSet( i_pWindow->getLayout() ); 243 244 Window* pWin = i_pWindow->GetWindow( WINDOW_FIRSTCHILD ); 245 while( pWin ) 246 { 247 addWindowToSet( pWin ); 248 pWin = pWin->GetWindow( WINDOW_NEXT ); 249 } 250 } 251 252 void WindowPropertySet::setupProperties() 253 { 254 uno::Reference< beans::XPropertyContainer > xCont( mpImpl->mxPropSet, uno::UNO_QUERY ); 255 OSL_ENSURE( xCont.is(), "could not get XPropertyContainer interface" ); 256 if( ! xCont.is() ) 257 return; 258 259 for( std::map< rtl::OUString, WindowPropertySetData::PropertyMapEntry >::iterator it 260 = mpImpl->maProperties.begin(); it != mpImpl->maProperties.end(); ++it ) 261 { 262 uno::Sequence< beans::PropertyValue > aOutsideValues( it->second.maSavedValues ); 263 beans::PropertyValue* pVal = aOutsideValues.getArray(); 264 for( sal_Int32 i = 0; i < aOutsideValues.getLength(); i++ ) 265 { 266 pVal[i].Name = getIdentifiedPropertyName( it->first, pVal[i].Name ); 267 xCont->addProperty( pVal[i].Name, 268 beans::PropertyAttribute::BOUND | beans:: PropertyAttribute::CONSTRAINED, 269 pVal[i].Value 270 ); 271 } 272 } 273 } 274 275 void WindowPropertySet::propertyChange( const beans::PropertyChangeEvent& i_rEvent ) 276 { 277 rtl::OUString aIdentifier, aProperty; 278 spliceIdentifiedPropertyName( i_rEvent.PropertyName, aIdentifier, aProperty ); 279 std::map< rtl::OUString, WindowPropertySetData::PropertyMapEntry >::iterator it = 280 mpImpl->maProperties.find( aIdentifier ); 281 if( it != mpImpl->maProperties.end() ) 282 { 283 uno::Sequence< beans::PropertyValue > aSet( 1 ); 284 aSet[0].Name = aProperty; 285 aSet[0].Value = i_rEvent.NewValue; 286 it->second.setProperties( aSet ); 287 } 288 } 289 290 IMPL_LINK( vcl::WindowPropertySet, ChildEventListener, VclWindowEvent*, pEvent ) 291 { 292 // find window in our properties 293 std::map< rtl::OUString, WindowPropertySetData::PropertyMapEntry >::iterator it 294 = mpImpl->maProperties.find( pEvent->GetWindow()->getIdentifier() ); 295 if( it != mpImpl->maProperties.end() ) // this is valid, some unnamed child may have sent an event 296 { 297 sal_uLong nId = pEvent->GetId(); 298 // check if anything interesting happened 299 if( 300 // general windowy things 301 nId == VCLEVENT_WINDOW_SHOW || 302 nId == VCLEVENT_WINDOW_HIDE || 303 nId == VCLEVENT_WINDOW_ENABLED || 304 nId == VCLEVENT_WINDOW_DISABLED || 305 // button thingies 306 nId == VCLEVENT_BUTTON_CLICK || 307 nId == VCLEVENT_PUSHBUTTON_TOGGLE || 308 nId == VCLEVENT_RADIOBUTTON_TOGGLE || 309 nId == VCLEVENT_CHECKBOX_TOGGLE || 310 // listbox 311 nId == VCLEVENT_LISTBOX_SELECT || 312 // edit 313 nId == VCLEVENT_EDIT_MODIFY 314 ) 315 { 316 WindowPropertySetData::PropertyMapEntry& rEntry = it->second; 317 // collect changes 318 uno::Sequence< beans::PropertyValue > aNewProps( rEntry.getProperties() ); 319 uno::Sequence< beans::PropertyValue > aNewPropsOut( aNewProps ); 320 321 // translate to identified properties 322 beans::PropertyValue* pValues = aNewPropsOut.getArray(); 323 for( sal_Int32 i = 0; i < aNewPropsOut.getLength(); i++ ) 324 pValues[i].Name = getIdentifiedPropertyName( it->first, pValues[i].Name ); 325 326 // broadcast changes 327 bool bWasVeto = false; 328 mpImpl->mpListener->suspend( true ); 329 try 330 { 331 mpImpl->mxPropSetAccess->setPropertyValues( aNewPropsOut ); 332 } 333 catch( beans::PropertyVetoException& ) 334 { 335 bWasVeto = true; 336 } 337 mpImpl->mpListener->suspend( false ); 338 339 if( ! bWasVeto ) // changes accepted ? 340 rEntry.maSavedValues = rEntry.getProperties(); 341 else // no, reset 342 rEntry.setProperties( rEntry.maSavedValues ); 343 } 344 } 345 346 return 0; 347 } 348