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 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_extensions.hxx"
26 #include "cellbindinghandler.hxx"
27 #include "formstrings.hxx"
28 #include "formmetadata.hxx"
29 #include "cellbindinghelper.hxx"
30 
31 /** === begin UNO includes === **/
32 #include <com/sun/star/form/binding/XValueBinding.hpp>
33 #include <com/sun/star/table/CellAddress.hpp>
34 #include <com/sun/star/inspection/XObjectInspectorUI.hpp>
35 /** === end UNO includes === **/
36 #include <tools/debug.hxx>
37 
38 //------------------------------------------------------------------------
createRegistryInfo_CellBindingPropertyHandler()39 extern "C" void SAL_CALL createRegistryInfo_CellBindingPropertyHandler()
40 {
41     ::pcr::CellBindingPropertyHandler::registerImplementation();
42 }
43 
44 //........................................................................
45 namespace pcr
46 {
47 //........................................................................
48 
49     using namespace ::com::sun::star::uno;
50     using namespace ::com::sun::star::table;
51     using namespace ::com::sun::star::lang;
52     using namespace ::com::sun::star::beans;
53     using namespace ::com::sun::star::script;
54     using namespace ::com::sun::star::frame;
55     using namespace ::com::sun::star::inspection;
56     using namespace ::com::sun::star::form::binding;
57     using namespace ::comphelper;
58 
59 	//====================================================================
60 	//= CellBindingPropertyHandler
61 	//====================================================================
DBG_NAME(CellBindingPropertyHandler)62     DBG_NAME( CellBindingPropertyHandler )
63 	//--------------------------------------------------------------------
64     CellBindingPropertyHandler::CellBindingPropertyHandler( const Reference< XComponentContext >& _rxContext )
65         :CellBindingPropertyHandler_Base( _rxContext )
66         ,m_pCellExchangeConverter( new DefaultEnumRepresentation( *m_pInfoService, ::getCppuType( static_cast< sal_Int16* >( NULL ) ), PROPERTY_ID_CELL_EXCHANGE_TYPE ) )
67     {
68         DBG_CTOR( CellBindingPropertyHandler, NULL );
69     }
70 
71     //--------------------------------------------------------------------
getImplementationName_static()72     ::rtl::OUString SAL_CALL CellBindingPropertyHandler::getImplementationName_static(  ) throw (RuntimeException)
73     {
74         return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.extensions.CellBindingPropertyHandler" ) );
75     }
76 
77     //--------------------------------------------------------------------
getSupportedServiceNames_static()78     Sequence< ::rtl::OUString > SAL_CALL CellBindingPropertyHandler::getSupportedServiceNames_static(  ) throw (RuntimeException)
79     {
80         Sequence< ::rtl::OUString > aSupported( 1 );
81         aSupported[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.form.inspection.CellBindingPropertyHandler" ) );
82         return aSupported;
83     }
84 
85     //--------------------------------------------------------------------
onNewComponent()86     void CellBindingPropertyHandler::onNewComponent()
87     {
88         PropertyHandlerComponent::onNewComponent();
89 
90         Reference< XModel > xDocument( impl_getContextDocument_nothrow() );
91         DBG_ASSERT( xDocument.is(), "CellBindingPropertyHandler::onNewComponent: no document!" );
92         if ( CellBindingHelper::isSpreadsheetDocument( xDocument ) )
93             m_pHelper.reset( new CellBindingHelper( m_xComponent, xDocument ) );
94     }
95 
96 	//--------------------------------------------------------------------
~CellBindingPropertyHandler()97     CellBindingPropertyHandler::~CellBindingPropertyHandler( )
98     {
99         DBG_DTOR( CellBindingPropertyHandler, NULL );
100     }
101 
102     //--------------------------------------------------------------------
getActuatingProperties()103     Sequence< ::rtl::OUString > SAL_CALL CellBindingPropertyHandler::getActuatingProperties( ) throw (RuntimeException)
104     {
105         Sequence< ::rtl::OUString > aInterestingProperties( 3 );
106         aInterestingProperties[0] = PROPERTY_LIST_CELL_RANGE;
107         aInterestingProperties[1] = PROPERTY_BOUND_CELL;
108         aInterestingProperties[2] = PROPERTY_CONTROLSOURCE;
109         return aInterestingProperties;
110     }
111 
112 	//--------------------------------------------------------------------
actuatingPropertyChanged(const::rtl::OUString & _rActuatingPropertyName,const Any & _rNewValue,const Any &,const Reference<XObjectInspectorUI> & _rxInspectorUI,sal_Bool _bFirstTimeInit)113     void SAL_CALL CellBindingPropertyHandler::actuatingPropertyChanged( const ::rtl::OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) throw (NullPointerException, RuntimeException)
114     {
115         ::osl::MutexGuard aGuard( m_aMutex );
116         PropertyId nActuatingPropId( impl_getPropertyId_throw( _rActuatingPropertyName ) );
117         OSL_PRECOND( m_pHelper.get(), "CellBindingPropertyHandler::actuatingPropertyChanged: inconsistentcy!" );
118             // if we survived impl_getPropertyId_throw, we should have a helper, since no helper implies no properties
119 
120         OSL_PRECOND( _rxInspectorUI.is(), "FormComponentPropertyHandler::actuatingPropertyChanged: no access to the UI!" );
121         if ( !_rxInspectorUI.is() )
122             throw NullPointerException();
123 
124         ::std::vector< PropertyId > aDependentProperties;
125 
126         switch ( nActuatingPropId )
127         {
128         // ----- BoundCell -----
129         case PROPERTY_ID_BOUND_CELL:
130         {
131             // the SQL-data-binding related properties need to be enabled if and only if
132             // there is *no* valid cell binding
133             Reference< XValueBinding > xBinding;
134             _rNewValue >>= xBinding;
135 
136             if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_CELL_EXCHANGE_TYPE ) )
137                 _rxInspectorUI->enablePropertyUI( PROPERTY_CELL_EXCHANGE_TYPE, xBinding.is() );
138             if ( impl_componentHasProperty_throw( PROPERTY_CONTROLSOURCE ) )
139                 _rxInspectorUI->enablePropertyUI( PROPERTY_CONTROLSOURCE, !xBinding.is() );
140 
141             if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_FILTERPROPOSAL ) )
142                 _rxInspectorUI->enablePropertyUI( PROPERTY_FILTERPROPOSAL, !xBinding.is() );
143             if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_EMPTY_IS_NULL ) )
144                 _rxInspectorUI->enablePropertyUI( PROPERTY_EMPTY_IS_NULL, !xBinding.is() );
145 
146             aDependentProperties.push_back( PROPERTY_ID_BOUNDCOLUMN );
147 
148             if ( !xBinding.is() && m_pHelper->getCurrentBinding().is() )
149             {
150                 // ensure that the "transfer selection as" property is reset. Since we can't remember
151                 // it at the object itself, but derive it from the binding only, we have to normalize
152                 // it now that there *is* no binding anymore.
153                 setPropertyValue( PROPERTY_CELL_EXCHANGE_TYPE, makeAny( (sal_Int16) 0 ) );
154             }
155         }
156         break;
157 
158         // ----- CellRange -----
159         case PROPERTY_ID_LIST_CELL_RANGE:
160         {
161             // the list source related properties need to be enabled if and only if
162             // there is *no* valid external list source for the control
163             Reference< XListEntrySource > xSource;
164             _rNewValue >>= xSource;
165 
166             _rxInspectorUI->enablePropertyUI( PROPERTY_STRINGITEMLIST, !xSource.is() );
167             _rxInspectorUI->enablePropertyUI( PROPERTY_LISTSOURCE, !xSource.is() );
168             _rxInspectorUI->enablePropertyUI( PROPERTY_LISTSOURCETYPE, !xSource.is() );
169 
170             aDependentProperties.push_back( PROPERTY_ID_BOUNDCOLUMN );
171 
172             // also reset the list entries if the cell range is reset
173             // #i28319# - 2004-04-27 - fs@openoffice.org
174             if ( !_bFirstTimeInit )
175             {
176                 try
177                 {
178                     if ( !xSource.is() )
179                         setPropertyValue( PROPERTY_STRINGITEMLIST, makeAny( Sequence< ::rtl::OUString >() ) );
180                 }
181                 catch( const Exception& )
182                 {
183             	    OSL_ENSURE( sal_False, "OPropertyBrowserController::actuatingPropertyChanged( ListCellRange ): caught an exception while resetting the string items!" );
184                 }
185             }
186         }
187         break;  // case PROPERTY_ID_LIST_CELL_RANGE
188 
189         // ----- DataField -----
190         case PROPERTY_ID_CONTROLSOURCE:
191         {
192             ::rtl::OUString sControlSource;
193             _rNewValue >>= sControlSource;
194             if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_BOUND_CELL ) )
195                 _rxInspectorUI->enablePropertyUI( PROPERTY_BOUND_CELL, sControlSource.getLength() == 0 );
196         }
197         break;  // case PROPERTY_ID_CONTROLSOURCE
198 
199         default:
200             DBG_ERROR( "CellBindingPropertyHandler::actuatingPropertyChanged: did not register for this property!" );
201         }
202 
203         for ( ::std::vector< PropertyId >::const_iterator loopAffected = aDependentProperties.begin();
204               loopAffected != aDependentProperties.end();
205               ++loopAffected
206             )
207         {
208             impl_updateDependentProperty_nothrow( *loopAffected, _rxInspectorUI );
209         }
210     }
211 
212 	//--------------------------------------------------------------------
impl_updateDependentProperty_nothrow(PropertyId _nPropId,const Reference<XObjectInspectorUI> & _rxInspectorUI) const213     void CellBindingPropertyHandler::impl_updateDependentProperty_nothrow( PropertyId _nPropId, const Reference< XObjectInspectorUI >& _rxInspectorUI ) const
214     {
215         try
216         {
217             switch ( _nPropId )
218             {
219             // ----- BoundColumn -----
220             case PROPERTY_ID_BOUNDCOLUMN:
221             {
222                 CellBindingPropertyHandler* pNonConstThis = const_cast< CellBindingPropertyHandler* >( this );
223                 Reference< XValueBinding > xBinding( pNonConstThis->getPropertyValue( PROPERTY_BOUND_CELL ), UNO_QUERY );
224                 Reference< XListEntrySource > xListSource( pNonConstThis->getPropertyValue( PROPERTY_LIST_CELL_RANGE ), UNO_QUERY );
225 
226                 if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_BOUNDCOLUMN ) )
227                     _rxInspectorUI->enablePropertyUI( PROPERTY_BOUNDCOLUMN, !xBinding.is() && !xListSource.is() );
228             }
229             break;  // case PROPERTY_ID_BOUNDCOLUMN
230 
231             }   // switch
232 
233         }
234         catch( const Exception& )
235         {
236         	OSL_ENSURE( sal_False, "CellBindingPropertyHandler::impl_updateDependentProperty_nothrow: caught an exception!" );
237         }
238     }
239 
240 	//--------------------------------------------------------------------
getPropertyValue(const::rtl::OUString & _rPropertyName)241     Any SAL_CALL CellBindingPropertyHandler::getPropertyValue( const ::rtl::OUString& _rPropertyName ) throw (UnknownPropertyException, RuntimeException)
242     {
243         ::osl::MutexGuard aGuard( m_aMutex );
244         PropertyId nPropId( impl_getPropertyId_throw( _rPropertyName ) );
245 
246         OSL_ENSURE( m_pHelper.get(), "CellBindingPropertyHandler::getPropertyValue: inconsistency!" );
247             // if we survived impl_getPropertyId_throw, we should have a helper, since no helper implies no properties
248 
249         Any aReturn;
250         switch ( nPropId )
251         {
252         case PROPERTY_ID_BOUND_CELL:
253         {
254             Reference< XValueBinding > xBinding( m_pHelper->getCurrentBinding() );
255             if ( !m_pHelper->isCellBinding( xBinding ) )
256                 xBinding.clear();
257 
258             aReturn <<= xBinding;
259         }
260         break;
261 
262         case PROPERTY_ID_LIST_CELL_RANGE:
263         {
264             Reference< XListEntrySource > xSource( m_pHelper->getCurrentListSource() );
265             if ( !m_pHelper->isCellRangeListSource( xSource ) )
266                 xSource.clear();
267 
268             aReturn <<= xSource;
269         }
270         break;
271 
272         case PROPERTY_ID_CELL_EXCHANGE_TYPE:
273         {
274             Reference< XValueBinding > xBinding( m_pHelper->getCurrentBinding() );
275             aReturn <<= (sal_Int16)( m_pHelper->isCellIntegerBinding( xBinding ) ? 1 : 0 );
276         }
277         break;
278 
279         default:
280             DBG_ERROR( "CellBindingPropertyHandler::getPropertyValue: cannot handle this!" );
281             break;
282         }
283         return aReturn;
284     }
285 
286     //--------------------------------------------------------------------
setPropertyValue(const::rtl::OUString & _rPropertyName,const Any & _rValue)287     void SAL_CALL CellBindingPropertyHandler::setPropertyValue( const ::rtl::OUString& _rPropertyName, const Any& _rValue ) throw (UnknownPropertyException, RuntimeException)
288     {
289         ::osl::MutexGuard aGuard( m_aMutex );
290         PropertyId nPropId( impl_getPropertyId_throw( _rPropertyName ) );
291 
292         OSL_ENSURE( m_pHelper.get(), "CellBindingPropertyHandler::setPropertyValue: inconsistency!" );
293             // if we survived impl_getPropertyId_throw, we should have a helper, since no helper implies no properties
294 
295         try
296         {
297             Any aOldValue = getPropertyValue( _rPropertyName );
298 
299             switch ( nPropId )
300             {
301             case PROPERTY_ID_BOUND_CELL:
302             {
303                 Reference< XValueBinding > xBinding;
304                 _rValue >>= xBinding;
305                 m_pHelper->setBinding( xBinding );
306             }
307             break;
308 
309             case PROPERTY_ID_LIST_CELL_RANGE:
310             {
311                 Reference< XListEntrySource > xSource;
312                 _rValue >>= xSource;
313                 m_pHelper->setListSource( xSource );
314             }
315             break;
316 
317             case PROPERTY_ID_CELL_EXCHANGE_TYPE:
318             {
319                 sal_Int16 nExchangeType = 0;
320                 OSL_VERIFY( _rValue >>= nExchangeType );
321 
322                 Reference< XValueBinding > xBinding = m_pHelper->getCurrentBinding( );
323                 if ( xBinding.is() )
324                 {
325                     sal_Bool bNeedIntegerBinding = ( nExchangeType == 1 );
326                     if ( (bool)bNeedIntegerBinding != m_pHelper->isCellIntegerBinding( xBinding ) )
327                     {
328                         CellAddress aAddress;
329                         if ( m_pHelper->getAddressFromCellBinding( xBinding, aAddress ) )
330                         {
331                             xBinding = m_pHelper->createCellBindingFromAddress( aAddress, bNeedIntegerBinding );
332                             m_pHelper->setBinding( xBinding );
333                         }
334                     }
335                 }
336             }
337             break;
338 
339             default:
340                 DBG_ERROR( "CellBindingPropertyHandler::setPropertyValue: cannot handle this!" );
341                 break;
342             }
343 
344             impl_setContextDocumentModified_nothrow();
345 
346             Any aNewValue( getPropertyValue( _rPropertyName ) );
347             firePropertyChange( _rPropertyName, nPropId, aOldValue, aNewValue );
348             // TODO/UNOize: can't we make this a part of the base class, for all those "virtual"
349             // properties? Base class'es |setPropertyValue| could call some |doSetPropertyValue|,
350             // and handle the listener notification itself
351         }
352         catch( const Exception& )
353         {
354         	OSL_ENSURE( sal_False, "CellBindingPropertyHandler::setPropertyValue: caught an exception!" );
355         }
356     }
357 
358     //--------------------------------------------------------------------
convertToPropertyValue(const::rtl::OUString & _rPropertyName,const Any & _rControlValue)359     Any SAL_CALL CellBindingPropertyHandler::convertToPropertyValue( const ::rtl::OUString& _rPropertyName, const Any& _rControlValue ) throw (UnknownPropertyException, RuntimeException)
360     {
361         ::osl::MutexGuard aGuard( m_aMutex );
362         Any aPropertyValue;
363 
364         OSL_ENSURE( m_pHelper.get(), "CellBindingPropertyHandler::convertToPropertyValue: we have no SupportedProperties!" );
365         if ( !m_pHelper.get() )
366             return aPropertyValue;
367 
368         PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) );
369 
370         ::rtl::OUString sControlValue;
371         OSL_VERIFY( _rControlValue >>= sControlValue );
372 		switch( nPropId )
373 		{
374             case PROPERTY_ID_LIST_CELL_RANGE:
375                 aPropertyValue <<= m_pHelper->createCellListSourceFromStringAddress( sControlValue );
376                 break;
377 
378 			case PROPERTY_ID_BOUND_CELL:
379             {
380                 // if we have the possibility of an integer binding, then we must preserve
381                 // this property's value (e.g. if the current binding is an integer binding, then
382                 // the newly created one must be, too)
383                 bool bIntegerBinding = false;
384                 if ( m_pHelper->isCellIntegerBindingAllowed() )
385                 {
386                     sal_Int16 nCurrentBindingType = 0;
387                     getPropertyValue( PROPERTY_CELL_EXCHANGE_TYPE ) >>= nCurrentBindingType;
388                     bIntegerBinding = ( nCurrentBindingType != 0 );
389                 }
390                 aPropertyValue <<= m_pHelper->createCellBindingFromStringAddress( sControlValue, bIntegerBinding );
391             }
392             break;
393 
394             case PROPERTY_ID_CELL_EXCHANGE_TYPE:
395                 m_pCellExchangeConverter->getValueFromDescription( sControlValue, aPropertyValue );
396                 break;
397 
398             default:
399                 DBG_ERROR( "CellBindingPropertyHandler::convertToPropertyValue: cannot handle this!" );
400                 break;
401         }
402 
403         return aPropertyValue;
404     }
405 
406     //--------------------------------------------------------------------
convertToControlValue(const::rtl::OUString & _rPropertyName,const Any & _rPropertyValue,const Type &)407     Any SAL_CALL CellBindingPropertyHandler::convertToControlValue( const ::rtl::OUString& _rPropertyName,
408         const Any& _rPropertyValue, const Type& /*_rControlValueType*/ ) throw (UnknownPropertyException, RuntimeException)
409     {
410         ::osl::MutexGuard aGuard( m_aMutex );
411         Any aControlValue;
412 
413         OSL_ENSURE( m_pHelper.get(), "CellBindingPropertyHandler::convertToControlValue: we have no SupportedProperties!" );
414         if ( !m_pHelper.get() )
415             return aControlValue;
416 
417         PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) );
418 
419 		switch ( nPropId )
420 		{
421             case PROPERTY_ID_BOUND_CELL:
422             {
423                 Reference< XValueBinding > xBinding;
424 #if OSL_DEBUG_LEVEL > 0
425                 sal_Bool bSuccess =
426 #endif
427                 _rPropertyValue >>= xBinding;
428                 OSL_ENSURE( bSuccess, "CellBindingPropertyHandler::convertToControlValue: invalid value (1)!" );
429 
430                 // the only value binding we support so far is linking to spreadsheet cells
431                 aControlValue <<= m_pHelper->getStringAddressFromCellBinding( xBinding );
432             }
433             break;
434 
435             case PROPERTY_ID_LIST_CELL_RANGE:
436             {
437                 Reference< XListEntrySource > xSource;
438 #if OSL_DEBUG_LEVEL > 0
439                 sal_Bool bSuccess =
440 #endif
441                 _rPropertyValue >>= xSource;
442                 OSL_ENSURE( bSuccess, "CellBindingPropertyHandler::convertToControlValue: invalid value (2)!" );
443 
444                 // the only value binding we support so far is linking to spreadsheet cells
445                 aControlValue <<= m_pHelper->getStringAddressFromCellListSource( xSource );
446             }
447             break;
448 
449             case PROPERTY_ID_CELL_EXCHANGE_TYPE:
450                 aControlValue <<= m_pCellExchangeConverter->getDescriptionForValue( _rPropertyValue );
451                 break;
452 
453             default:
454                 DBG_ERROR( "CellBindingPropertyHandler::convertToControlValue: cannot handle this!" );
455                 break;
456 		}
457 
458         return aControlValue;
459     }
460 
461     //--------------------------------------------------------------------
doDescribeSupportedProperties() const462     Sequence< Property > SAL_CALL CellBindingPropertyHandler::doDescribeSupportedProperties() const
463     {
464         ::std::vector< Property > aProperties;
465 
466         bool bAllowCellLinking      = m_pHelper.get() && m_pHelper->isCellBindingAllowed();
467         bool bAllowCellIntLinking   = m_pHelper.get() && m_pHelper->isCellIntegerBindingAllowed();
468         bool bAllowListCellRange    = m_pHelper.get() && m_pHelper->isListCellRangeAllowed();
469         if ( bAllowCellLinking || bAllowListCellRange || bAllowCellIntLinking )
470         {
471             sal_Int32 nPos =  ( bAllowCellLinking    ? 1 : 0 )
472                             + ( bAllowListCellRange  ? 1 : 0 )
473                             + ( bAllowCellIntLinking ? 1 : 0 );
474             aProperties.resize( nPos );
475 
476             if ( bAllowCellLinking )
477             {
478                 aProperties[ --nPos ] = Property( PROPERTY_BOUND_CELL, PROPERTY_ID_BOUND_CELL,
479                     ::getCppuType( static_cast< ::rtl::OUString* >( NULL ) ), 0 );
480             }
481             if ( bAllowCellIntLinking )
482             {
483                 aProperties[ --nPos ] = Property( PROPERTY_CELL_EXCHANGE_TYPE, PROPERTY_ID_CELL_EXCHANGE_TYPE,
484                     ::getCppuType( static_cast< sal_Int16* >( NULL ) ), 0 );
485             }
486             if ( bAllowListCellRange )
487             {
488                 aProperties[ --nPos ] = Property( PROPERTY_LIST_CELL_RANGE, PROPERTY_ID_LIST_CELL_RANGE,
489                     ::getCppuType( static_cast< ::rtl::OUString* >( NULL ) ), 0 );
490             }
491         }
492 
493         if ( aProperties.empty() )
494             return Sequence< Property >();
495         return Sequence< Property >( &(*aProperties.begin()), aProperties.size() );
496     }
497 
498 //........................................................................
499 }   // namespace pcr
500 //........................................................................
501