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