xref: /trunk/main/vcl/source/window/wpropset.cxx (revision 9f62ea84)
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