1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: cellvaluebinding.cxx,v $
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 //.........................................................................
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 //=====================================================================
65 //=====================================================================
66 DBG_NAME( OCellValueBinding
)
67 //---------------------------------------------------------------------
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
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
),
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
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
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
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
);
190 sal_Int32 nCount
= m_xCellText
.is() ? 3 : m_xCell
.is() ? 1 : 0;
194 Sequence
< Type
> aTypes( nCount
);
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
209 aTypes
[nCount
-1] = ::getCppuType( static_cast< sal_Int32
* >( NULL
) );
215 //--------------------------------------------------------------------
216 sal_Bool SAL_CALL
OCellValueBinding::supportsType( const Type
& aType
) throw (RuntimeException
)
218 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
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
++ ) )
233 //--------------------------------------------------------------------
234 Any SAL_CALL
OCellValueBinding::getValue( const Type
& aType
) throw (IncompatibleTypesException
, RuntimeException
)
236 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
239 checkValueType( aType
);
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();
249 aReturn
<<= ::rtl::OUString();
252 case TypeClass_BOOLEAN
:
253 DBG_ASSERT( m_xCell
.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
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
);
270 CellContentType eResultType
;
271 if ( (xProp
->getPropertyValue(::rtl::OUString::createFromAscii( "FormulaResultType" ) ) >>= eResultType
) && eResultType
== CellContentType_VALUE
)
272 bHasValue
= sal_True
;
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
288 case TypeClass_DOUBLE
:
289 DBG_ASSERT( m_xCell
.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
291 aReturn
<<= m_xCell
->getValue();
293 aReturn
<<= (double)0;
297 DBG_ASSERT( m_xCell
.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
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() );
309 aReturn
<<= (sal_Int32
)0;
313 DBG_ERROR( "OCellValueBinding::getValue: unreachable code!" );
314 // a type other than double and string should never have survived the checkValueType
320 //--------------------------------------------------------------------
321 void SAL_CALL
OCellValueBinding::setValue( const Any
& aValue
) throw (IncompatibleTypesException
, NoSupportException
, RuntimeException
)
323 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
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
;
337 if ( m_xCellText
.is() )
338 m_xCellText
->setString( sText
);
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
);
351 double nCellValue
= bValue
? 1.0 : 0.0;
354 m_xCell
->setValue( nCellValue
);
360 case TypeClass_DOUBLE
:
362 DBG_ASSERT( m_xCell
.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
367 m_xCell
->setValue( nValue
);
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
379 m_xCell
->setValue( nValue
);
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!" );
391 Sequence
<Any
> aInner(1); // one empty element
392 Sequence
< Sequence
<Any
> > aOuter( &aInner
, 1 ); // one row
393 xData
->setDataArray( aOuter
);
399 DBG_ERROR( "OCellValueBinding::setValue: unreachable code!" );
400 // a type other than double and string should never have survived the checkValueType
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
);
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
));
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
;
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
)
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" ) );
515 aServices
[ 2 ] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.table.ListPositionCellBinding" ) );
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()
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
& )
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
);
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
579 //--------------------------------------------------------------------
580 void SAL_CALL
OCellValueBinding::initialize( const Sequence
< Any
>& _rArguments
) throw (Exception
, RuntimeException
)
582 if ( m_bInitialized
)
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
)
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
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!" );
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!" );
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!" );
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 //.........................................................................
666 //.........................................................................