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