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:
WindowPropertySetListener(WindowPropertySet * pParent)63 WindowPropertySetListener( WindowPropertySet* pParent )
64 : cppu::WeakComponentImplHelper1< com::sun::star::beans::XPropertyChangeListener >( m_aMutex )
65 , mpParent( pParent )
66 , mbSuspended( false )
67 {}
68
~WindowPropertySetListener()69 virtual ~WindowPropertySetListener()
70 {
71 }
72
73 using cppu::WeakComponentImplHelperBase::disposing;
disposing(const lang::EventObject &)74 virtual void SAL_CALL disposing( const lang::EventObject& ) throw()
75 {
76 }
77
propertyChange(const beans::PropertyChangeEvent & i_rEvent)78 virtual void SAL_CALL propertyChange( const beans::PropertyChangeEvent& i_rEvent ) throw()
79 {
80 if( ! mbSuspended )
81 mpParent->propertyChange( i_rEvent );
82 }
83
suspend(bool i_bSuspended)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
PropertyMapEntryvcl::WindowPropertySetData::PropertyMapEntry100 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
getPropertiesvcl::WindowPropertySetData::PropertyMapEntry106 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
setPropertiesvcl::WindowPropertySetData::PropertyMapEntry115 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
WindowPropertySetData()132 WindowPropertySetData()
133 : mpTopWindow( NULL )
134 , mbOwner( false )
135 , mpListener( NULL )
136 {}
137
~WindowPropertySetData()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
getIdentifiedPropertyName(const rtl::OUString & i_rIdentifier,const rtl::OUString & i_rName)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
spliceIdentifiedPropertyName(const rtl::OUString & i_rIdentifiedPropName,rtl::OUString & o_rIdentifier,rtl::OUString & o_rPropName)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
WindowPropertySet(Window * i_pTopWindow,bool i_bTakeOwnership)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
~WindowPropertySet()196 WindowPropertySet::~WindowPropertySet()
197 {
198 mpImpl->mpTopWindow->RemoveChildEventListener( LINK( this, WindowPropertySet, ChildEventListener ) );
199
200 delete mpImpl;
201 mpImpl = NULL;
202 }
203
getPropertySet() const204 uno::Reference< beans::XPropertySet > WindowPropertySet::getPropertySet() const
205 {
206 return mpImpl->mxPropSet;
207 }
208
addLayoutToSet(const boost::shared_ptr<WindowArranger> & i_pLayout)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
addWindowToSet(Window * i_pWindow)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
setupProperties()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
propertyChange(const beans::PropertyChangeEvent & i_rEvent)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
IMPL_LINK(vcl::WindowPropertySet,ChildEventListener,VclWindowEvent *,pEvent)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