update dev300-m58
[ooovba.git] / sc / source / ui / unoobj / cellvaluebinding.cxx
blob0d191756f08bd73f223971be588db373355ec15d
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: cellvaluebinding.cxx,v $
10 * $Revision: 1.11 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
33 #include "cellvaluebinding.hxx"
34 #include <tools/debug.hxx>
35 #include <rtl/math.hxx>
36 #include <com/sun/star/table/XCellRange.hpp>
37 #include <com/sun/star/sheet/XCellAddressable.hpp>
38 #include <com/sun/star/sheet/XCellRangeData.hpp>
39 #include <com/sun/star/container/XIndexAccess.hpp>
40 #include <com/sun/star/beans/PropertyAttribute.hpp>
41 #include <com/sun/star/beans/NamedValue.hpp>
42 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
43 #include <com/sun/star/util/XNumberFormatTypes.hpp>
44 #include <com/sun/star/util/NumberFormat.hpp>
46 //.........................................................................
47 namespace calc
49 //.........................................................................
51 #define PROP_HANDLE_BOUND_CELL 1
53 using namespace ::com::sun::star::uno;
54 using namespace ::com::sun::star::lang;
55 using namespace ::com::sun::star::table;
56 using namespace ::com::sun::star::text;
57 using namespace ::com::sun::star::sheet;
58 using namespace ::com::sun::star::container;
59 using namespace ::com::sun::star::beans;
60 using namespace ::com::sun::star::util;
61 using namespace ::com::sun::star::form::binding;
63 //=====================================================================
64 //= OCellValueBinding
65 //=====================================================================
66 DBG_NAME( OCellValueBinding )
67 //---------------------------------------------------------------------
68 #ifdef DBG_UTIL
69 const char* OCellValueBinding::checkConsistency_static( const void* _pThis )
71 return static_cast< const OCellValueBinding* >( _pThis )->checkConsistency( );
74 const char* OCellValueBinding::checkConsistency( ) const
76 const char* pAssertion = NULL;
77 if ( m_xCellText.is() && !m_xCell.is() )
78 // there are places (e.g. getSupportedTypes) which rely on the fact
79 // that m_xCellText.is() implies m_xCell.is()
80 pAssertion = "cell references inconsistent!";
82 // TODO: place any additional checks here to ensure consistency of this instance
83 return pAssertion;
85 #endif
87 //---------------------------------------------------------------------
88 OCellValueBinding::OCellValueBinding( const Reference< XSpreadsheetDocument >& _rxDocument, sal_Bool _bListPos )
89 :OCellValueBinding_Base( m_aMutex )
90 ,OCellValueBinding_PBase( OCellValueBinding_Base::rBHelper )
91 ,m_xDocument( _rxDocument )
92 ,m_aModifyListeners( m_aMutex )
93 ,m_bInitialized( sal_False )
94 ,m_bListPos( _bListPos )
96 DBG_CTOR( OCellValueBinding, checkConsistency_static );
98 // register our property at the base class
99 CellAddress aInitialPropValue;
100 registerPropertyNoMember(
101 ::rtl::OUString::createFromAscii( "BoundCell" ),
102 PROP_HANDLE_BOUND_CELL,
103 PropertyAttribute::BOUND | PropertyAttribute::READONLY,
104 ::getCppuType( &aInitialPropValue ),
105 &aInitialPropValue
108 // TODO: implement a ReadOnly property as required by the service,
109 // which probably maps to the cell being locked
112 //---------------------------------------------------------------------
113 OCellValueBinding::~OCellValueBinding( )
115 if ( !OCellValueBinding_Base::rBHelper.bDisposed )
117 acquire(); // prevent duplicate dtor
118 dispose();
121 DBG_DTOR( OCellValueBinding, checkConsistency_static );
124 //--------------------------------------------------------------------
125 IMPLEMENT_FORWARD_XINTERFACE2( OCellValueBinding, OCellValueBinding_Base, OCellValueBinding_PBase )
127 //--------------------------------------------------------------------
128 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCellValueBinding, OCellValueBinding_Base, OCellValueBinding_PBase )
130 //--------------------------------------------------------------------
131 void SAL_CALL OCellValueBinding::disposing()
133 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
135 Reference<XModifyBroadcaster> xBroadcaster( m_xCell, UNO_QUERY );
136 if ( xBroadcaster.is() )
138 xBroadcaster->removeModifyListener( this );
141 // OCellValueBinding_Base::disposing();
142 WeakAggComponentImplHelperBase::disposing();
144 // TODO: clean up here whatever you need to clean up (e.g. deregister as XEventListener
145 // for the cell)
148 //--------------------------------------------------------------------
149 Reference< XPropertySetInfo > SAL_CALL OCellValueBinding::getPropertySetInfo( ) throw(RuntimeException)
151 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
152 return createPropertySetInfo( getInfoHelper() ) ;
155 //--------------------------------------------------------------------
156 ::cppu::IPropertyArrayHelper& SAL_CALL OCellValueBinding::getInfoHelper()
158 return *OCellValueBinding_PABase::getArrayHelper();
161 //--------------------------------------------------------------------
162 ::cppu::IPropertyArrayHelper* OCellValueBinding::createArrayHelper( ) const
164 Sequence< Property > aProps;
165 describeProperties( aProps );
166 return new ::cppu::OPropertyArrayHelper(aProps);
169 //--------------------------------------------------------------------
170 void SAL_CALL OCellValueBinding::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
172 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
173 DBG_ASSERT( _nHandle == PROP_HANDLE_BOUND_CELL, "OCellValueBinding::getFastPropertyValue: invalid handle!" );
174 // we only have this one property ....
175 (void)_nHandle; // avoid warning in product version
177 _rValue.clear();
178 Reference< XCellAddressable > xCellAddress( m_xCell, UNO_QUERY );
179 if ( xCellAddress.is() )
180 _rValue <<= xCellAddress->getCellAddress( );
183 //--------------------------------------------------------------------
184 Sequence< Type > SAL_CALL OCellValueBinding::getSupportedValueTypes( ) throw (RuntimeException)
186 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
187 checkDisposed( );
188 checkInitialized( );
190 sal_Int32 nCount = m_xCellText.is() ? 3 : m_xCell.is() ? 1 : 0;
191 if ( m_bListPos )
192 ++nCount;
194 Sequence< Type > aTypes( nCount );
195 if ( m_xCell.is() )
197 // an XCell can be used to set/get "double" values
198 aTypes[0] = ::getCppuType( static_cast< double* >( NULL ) );
199 if ( m_xCellText.is() )
201 // an XTextRange can be used to set/get "string" values
202 aTypes[1] = ::getCppuType( static_cast< ::rtl::OUString* >( NULL ) );
203 // and additionally, we use it to handle booleans
204 aTypes[2] = ::getCppuType( static_cast< sal_Bool* >( NULL ) );
207 // add sal_Int32 only if constructed as ListPositionCellBinding
208 if ( m_bListPos )
209 aTypes[nCount-1] = ::getCppuType( static_cast< sal_Int32* >( NULL ) );
212 return aTypes;
215 //--------------------------------------------------------------------
216 sal_Bool SAL_CALL OCellValueBinding::supportsType( const Type& aType ) throw (RuntimeException)
218 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
219 checkDisposed( );
220 checkInitialized( );
222 // look up in our sequence
223 Sequence< Type > aSupportedTypes( getSupportedValueTypes() );
224 const Type* pTypes = aSupportedTypes.getConstArray();
225 const Type* pTypesEnd = aSupportedTypes.getConstArray() + aSupportedTypes.getLength();
226 while ( pTypes != pTypesEnd )
227 if ( aType.equals( *pTypes++ ) )
228 return sal_True;
230 return sal_False;
233 //--------------------------------------------------------------------
234 Any SAL_CALL OCellValueBinding::getValue( const Type& aType ) throw (IncompatibleTypesException, RuntimeException)
236 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
237 checkDisposed( );
238 checkInitialized( );
239 checkValueType( aType );
241 Any aReturn;
242 switch ( aType.getTypeClass() )
244 case TypeClass_STRING:
245 DBG_ASSERT( m_xCellText.is(), "OCellValueBinding::getValue: don't have a text!" );
246 if ( m_xCellText.is() )
247 aReturn <<= m_xCellText->getString();
248 else
249 aReturn <<= ::rtl::OUString();
250 break;
252 case TypeClass_BOOLEAN:
253 DBG_ASSERT( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
254 if ( m_xCell.is() )
256 // check if the cell has a numeric value (this might go into a helper function):
258 sal_Bool bHasValue = sal_False;
259 CellContentType eCellType = m_xCell->getType();
260 if ( eCellType == CellContentType_VALUE )
261 bHasValue = sal_True;
262 else if ( eCellType == CellContentType_FORMULA )
264 // check if the formula result is a value
265 if ( m_xCell->getError() == 0 )
267 Reference<XPropertySet> xProp( m_xCell, UNO_QUERY );
268 if ( xProp.is() )
270 CellContentType eResultType;
271 if ( (xProp->getPropertyValue(::rtl::OUString::createFromAscii( "FormulaResultType" ) ) >>= eResultType) && eResultType == CellContentType_VALUE )
272 bHasValue = sal_True;
277 if ( bHasValue )
279 // 0 is "unchecked", any other value is "checked", regardless of number format
280 double nCellValue = m_xCell->getValue();
281 sal_Bool bBoolValue = ( nCellValue != 0.0 );
282 aReturn <<= bBoolValue;
284 // empty cells, text cells and text or error formula results: leave return value empty
286 break;
288 case TypeClass_DOUBLE:
289 DBG_ASSERT( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
290 if ( m_xCell.is() )
291 aReturn <<= m_xCell->getValue();
292 else
293 aReturn <<= (double)0;
294 break;
296 case TypeClass_LONG:
297 DBG_ASSERT( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
298 if ( m_xCell.is() )
300 // The list position value in the cell is 1-based.
301 // We subtract 1 from any cell value (no special handling for 0 or negative values).
303 sal_Int32 nValue = (sal_Int32) rtl::math::approxFloor( m_xCell->getValue() );
304 --nValue;
306 aReturn <<= nValue;
308 else
309 aReturn <<= (sal_Int32)0;
310 break;
312 default:
313 DBG_ERROR( "OCellValueBinding::getValue: unreachable code!" );
314 // a type other than double and string should never have survived the checkValueType
315 // above
317 return aReturn;
320 //--------------------------------------------------------------------
321 void SAL_CALL OCellValueBinding::setValue( const Any& aValue ) throw (IncompatibleTypesException, NoSupportException, RuntimeException)
323 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
324 checkDisposed( );
325 checkInitialized( );
326 if ( aValue.hasValue() )
327 checkValueType( aValue.getValueType() );
329 switch ( aValue.getValueType().getTypeClass() )
331 case TypeClass_STRING:
333 DBG_ASSERT( m_xCellText.is(), "OCellValueBinding::setValue: don't have a text!" );
335 ::rtl::OUString sText;
336 aValue >>= sText;
337 if ( m_xCellText.is() )
338 m_xCellText->setString( sText );
340 break;
342 case TypeClass_BOOLEAN:
344 DBG_ASSERT( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
346 // boolean is stored as values 0 or 1
347 // TODO: set the number format to boolean if no format is set?
349 sal_Bool bValue( sal_False );
350 aValue >>= bValue;
351 double nCellValue = bValue ? 1.0 : 0.0;
353 if ( m_xCell.is() )
354 m_xCell->setValue( nCellValue );
356 setBooleanFormat();
358 break;
360 case TypeClass_DOUBLE:
362 DBG_ASSERT( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
364 double nValue = 0;
365 aValue >>= nValue;
366 if ( m_xCell.is() )
367 m_xCell->setValue( nValue );
369 break;
371 case TypeClass_LONG:
373 DBG_ASSERT( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
375 sal_Int32 nValue = 0;
376 aValue >>= nValue; // list index from control layer (0-based)
377 ++nValue; // the list position value in the cell is 1-based
378 if ( m_xCell.is() )
379 m_xCell->setValue( nValue );
381 break;
383 case TypeClass_VOID:
385 // #N/A error value can only be set using XCellRangeData
387 Reference<XCellRangeData> xData( m_xCell, UNO_QUERY );
388 DBG_ASSERT( xData.is(), "OCellValueBinding::setValue: don't have XCellRangeData!" );
389 if ( xData.is() )
391 Sequence<Any> aInner(1); // one empty element
392 Sequence< Sequence<Any> > aOuter( &aInner, 1 ); // one row
393 xData->setDataArray( aOuter );
396 break;
398 default:
399 DBG_ERROR( "OCellValueBinding::setValue: unreachable code!" );
400 // a type other than double and string should never have survived the checkValueType
401 // above
404 //--------------------------------------------------------------------
405 void OCellValueBinding::setBooleanFormat()
407 // set boolean number format if not already set
409 ::rtl::OUString sPropName( ::rtl::OUString::createFromAscii( "NumberFormat" ) );
410 Reference<XPropertySet> xCellProp( m_xCell, UNO_QUERY );
411 Reference<XNumberFormatsSupplier> xSupplier( m_xDocument, UNO_QUERY );
412 if ( xSupplier.is() && xCellProp.is() )
414 Reference<XNumberFormats> xFormats(xSupplier->getNumberFormats());
415 Reference<XNumberFormatTypes> xTypes( xFormats, UNO_QUERY );
416 if ( xTypes.is() )
418 Locale aLocale;
419 sal_Bool bWasBoolean = sal_False;
421 sal_Int32 nOldIndex = ::comphelper::getINT32( xCellProp->getPropertyValue( sPropName ) );
422 Reference<XPropertySet> xOldFormat;
425 xOldFormat.set(xFormats->getByKey( nOldIndex ));
427 catch ( Exception& )
429 // non-existing format - can happen, use defaults
431 if ( xOldFormat.is() )
433 // use the locale of the existing format
434 xOldFormat->getPropertyValue( ::rtl::OUString::createFromAscii( "Locale" ) ) >>= aLocale;
436 sal_Int16 nOldType = ::comphelper::getINT16(
437 xOldFormat->getPropertyValue( ::rtl::OUString::createFromAscii( "Type" ) ) );
438 if ( nOldType & NumberFormat::LOGICAL )
439 bWasBoolean = sal_True;
442 if ( !bWasBoolean )
444 sal_Int32 nNewIndex = xTypes->getStandardFormat( NumberFormat::LOGICAL, aLocale );
445 xCellProp->setPropertyValue( sPropName, makeAny( nNewIndex ) );
451 //--------------------------------------------------------------------
452 void OCellValueBinding::checkDisposed( ) const SAL_THROW( ( DisposedException ) )
454 if ( OCellValueBinding_Base::rBHelper.bInDispose || OCellValueBinding_Base::rBHelper.bDisposed )
455 throw DisposedException();
456 // TODO: is it worth having an error message here?
459 //--------------------------------------------------------------------
460 void OCellValueBinding::checkInitialized() SAL_THROW( ( RuntimeException ) )
462 if ( !m_bInitialized )
463 throw RuntimeException();
464 // TODO: error message
467 //--------------------------------------------------------------------
468 void OCellValueBinding::checkValueType( const Type& _rType ) const SAL_THROW( ( IncompatibleTypesException ) )
470 OCellValueBinding* pNonConstThis = const_cast< OCellValueBinding* >( this );
471 if ( !pNonConstThis->supportsType( _rType ) )
473 ::rtl::OUString sMessage( RTL_CONSTASCII_USTRINGPARAM( "The given type (" ) );
474 sMessage += _rType.getTypeName();
475 sMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ") is not supported by this binding." ) );
476 // TODO: localize this error message
478 throw IncompatibleTypesException( sMessage, *pNonConstThis );
479 // TODO: alternatively use a type converter service for this?
483 //--------------------------------------------------------------------
484 ::rtl::OUString SAL_CALL OCellValueBinding::getImplementationName( ) throw (RuntimeException)
486 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
488 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.sheet.OCellValueBinding" ) );
491 //--------------------------------------------------------------------
492 sal_Bool SAL_CALL OCellValueBinding::supportsService( const ::rtl::OUString& _rServiceName ) throw (RuntimeException)
494 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
496 Sequence< ::rtl::OUString > aSupportedServices( getSupportedServiceNames() );
497 const ::rtl::OUString* pLookup = aSupportedServices.getConstArray();
498 const ::rtl::OUString* pLookupEnd = aSupportedServices.getConstArray() + aSupportedServices.getLength();
499 while ( pLookup != pLookupEnd )
500 if ( *pLookup++ == _rServiceName )
501 return sal_True;
503 return sal_False;
506 //--------------------------------------------------------------------
507 Sequence< ::rtl::OUString > SAL_CALL OCellValueBinding::getSupportedServiceNames( ) throw (RuntimeException)
509 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
511 Sequence< ::rtl::OUString > aServices( m_bListPos ? 3 : 2 );
512 aServices[ 0 ] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.table.CellValueBinding" ) );
513 aServices[ 1 ] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.form.binding.ValueBinding" ) );
514 if ( m_bListPos )
515 aServices[ 2 ] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.table.ListPositionCellBinding" ) );
516 return aServices;
519 //--------------------------------------------------------------------
520 void SAL_CALL OCellValueBinding::addModifyListener( const Reference< XModifyListener >& _rxListener ) throw (RuntimeException)
522 if ( _rxListener.is() )
523 m_aModifyListeners.addInterface( _rxListener );
526 //--------------------------------------------------------------------
527 void SAL_CALL OCellValueBinding::removeModifyListener( const Reference< XModifyListener >& _rxListener ) throw (RuntimeException)
529 if ( _rxListener.is() )
530 m_aModifyListeners.removeInterface( _rxListener );
533 //--------------------------------------------------------------------
534 void OCellValueBinding::notifyModified()
536 EventObject aEvent;
537 aEvent.Source.set(*this);
539 ::cppu::OInterfaceIteratorHelper aIter( m_aModifyListeners );
540 while ( aIter.hasMoreElements() )
544 static_cast< XModifyListener* >( aIter.next() )->modified( aEvent );
546 catch( const RuntimeException& )
548 // silent this
550 catch( const Exception& )
552 DBG_ERROR( "OCellValueBinding::notifyModified: caught a (non-runtime) exception!" );
557 //--------------------------------------------------------------------
558 void SAL_CALL OCellValueBinding::modified( const EventObject& /* aEvent */ ) throw (RuntimeException)
560 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
562 notifyModified();
565 //--------------------------------------------------------------------
566 void SAL_CALL OCellValueBinding::disposing( const EventObject& aEvent ) throw (RuntimeException)
568 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
570 Reference<XInterface> xCellInt( m_xCell, UNO_QUERY );
571 if ( xCellInt == aEvent.Source )
573 // release references to cell object
574 m_xCell.clear();
575 m_xCellText.clear();
579 //--------------------------------------------------------------------
580 void SAL_CALL OCellValueBinding::initialize( const Sequence< Any >& _rArguments ) throw (Exception, RuntimeException)
582 if ( m_bInitialized )
583 throw Exception();
584 // TODO: error message
586 // get the cell address
587 CellAddress aAddress;
588 sal_Bool bFoundAddress = sal_False;
590 const Any* pLoop = _rArguments.getConstArray();
591 const Any* pLoopEnd = _rArguments.getConstArray() + _rArguments.getLength();
592 for ( ; ( pLoop != pLoopEnd ) && !bFoundAddress; ++pLoop )
594 NamedValue aValue;
595 if ( *pLoop >>= aValue )
597 if ( aValue.Name.equalsAscii( "BoundCell" ) )
599 if ( aValue.Value >>= aAddress )
600 bFoundAddress = sal_True;
605 if ( !bFoundAddress )
606 // TODO: error message
607 throw Exception();
609 // get the cell object
612 // first the sheets collection
613 Reference< XIndexAccess > xSheets;
614 if ( m_xDocument.is() )
615 xSheets.set(xSheets.query( m_xDocument->getSheets( ) ));
616 DBG_ASSERT( xSheets.is(), "OCellValueBinding::initialize: could not retrieve the sheets!" );
618 if ( xSheets.is() )
620 // the concrete sheet
621 Reference< XCellRange > xSheet(xSheets->getByIndex( aAddress.Sheet ), UNO_QUERY);
622 DBG_ASSERT( xSheet.is(), "OCellValueBinding::initialize: NULL sheet, but no exception!" );
624 // the concrete cell
625 if ( xSheet.is() )
627 m_xCell.set(xSheet->getCellByPosition( aAddress.Column, aAddress.Row ));
628 Reference< XCellAddressable > xAddressAccess( m_xCell, UNO_QUERY );
629 DBG_ASSERT( xAddressAccess.is(), "OCellValueBinding::initialize: either NULL cell, or cell without address access!" );
633 catch( const Exception& )
635 DBG_ERROR( "OCellValueBinding::initialize: caught an exception while retrieving the cell object!" );
638 if ( !m_xCell.is() )
639 throw Exception();
640 // TODO error message
642 m_xCellText.set(m_xCellText.query( m_xCell ));
644 Reference<XModifyBroadcaster> xBroadcaster( m_xCell, UNO_QUERY );
645 if ( xBroadcaster.is() )
647 xBroadcaster->addModifyListener( this );
650 // TODO: add as XEventListener to the cell, so we get notified when it dies,
651 // and can dispose ourself then
653 // TODO: somehow add as listener so we get notified when the address of the cell changes
654 // We need to forward this as change in our BoundCell property to our property change listeners
656 // TODO: be an XModifyBroadcaster, so that changes in our cell can be notified
657 // to the BindableValue which is/will be bound to this instance.
659 m_bInitialized = sal_True;
660 // TODO: place your code here
664 //.........................................................................
665 } // namespace calc
666 //.........................................................................