1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "cellvaluebinding.hxx"
21 #include <tools/debug.hxx>
22 #include <rtl/math.hxx>
23 #include <com/sun/star/table/XCellRange.hpp>
24 #include <com/sun/star/sheet/XCellAddressable.hpp>
25 #include <com/sun/star/sheet/XCellRangeData.hpp>
26 #include <com/sun/star/container/XIndexAccess.hpp>
27 #include <com/sun/star/beans/PropertyAttribute.hpp>
28 #include <com/sun/star/beans/NamedValue.hpp>
29 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
30 #include <com/sun/star/util/XNumberFormatTypes.hpp>
31 #include <com/sun/star/util/NumberFormat.hpp>
33 //.........................................................................
36 //.........................................................................
38 #define PROP_HANDLE_BOUND_CELL 1
40 using namespace ::com::sun::star::uno
;
41 using namespace ::com::sun::star::lang
;
42 using namespace ::com::sun::star::table
;
43 using namespace ::com::sun::star::text
;
44 using namespace ::com::sun::star::sheet
;
45 using namespace ::com::sun::star::container
;
46 using namespace ::com::sun::star::beans
;
47 using namespace ::com::sun::star::util
;
48 using namespace ::com::sun::star::form::binding
;
50 //=====================================================================
52 //=====================================================================
53 DBG_NAME( OCellValueBinding
)
54 //---------------------------------------------------------------------
56 const char* OCellValueBinding::checkConsistency_static( const void* _pThis
)
58 return static_cast< const OCellValueBinding
* >( _pThis
)->checkConsistency( );
61 const char* OCellValueBinding::checkConsistency( ) const
63 const char* pAssertion
= NULL
;
64 if ( m_xCellText
.is() && !m_xCell
.is() )
65 // there are places (e.g. getSupportedTypes) which rely on the fact
66 // that m_xCellText.is() implies m_xCell.is()
67 pAssertion
= "cell references inconsistent!";
69 // TODO: place any additional checks here to ensure consistency of this instance
74 //---------------------------------------------------------------------
75 OCellValueBinding::OCellValueBinding( const Reference
< XSpreadsheetDocument
>& _rxDocument
, sal_Bool _bListPos
)
76 :OCellValueBinding_Base( m_aMutex
)
77 ,OCellValueBinding_PBase( OCellValueBinding_Base::rBHelper
)
78 ,m_xDocument( _rxDocument
)
79 ,m_aModifyListeners( m_aMutex
)
80 ,m_bInitialized( false )
81 ,m_bListPos( _bListPos
)
83 DBG_CTOR( OCellValueBinding
, checkConsistency_static
);
85 // register our property at the base class
86 CellAddress aInitialPropValue
;
87 registerPropertyNoMember(
88 OUString( "BoundCell" ),
89 PROP_HANDLE_BOUND_CELL
,
90 PropertyAttribute::BOUND
| PropertyAttribute::READONLY
,
91 ::getCppuType( &aInitialPropValue
),
95 // TODO: implement a ReadOnly property as required by the service,
96 // which probably maps to the cell being locked
99 //---------------------------------------------------------------------
100 OCellValueBinding::~OCellValueBinding( )
102 if ( !OCellValueBinding_Base::rBHelper
.bDisposed
)
104 acquire(); // prevent duplicate dtor
108 DBG_DTOR( OCellValueBinding
, checkConsistency_static
);
111 //--------------------------------------------------------------------
112 IMPLEMENT_FORWARD_XINTERFACE2( OCellValueBinding
, OCellValueBinding_Base
, OCellValueBinding_PBase
)
114 //--------------------------------------------------------------------
115 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCellValueBinding
, OCellValueBinding_Base
, OCellValueBinding_PBase
)
117 //--------------------------------------------------------------------
118 void SAL_CALL
OCellValueBinding::disposing()
120 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
122 Reference
<XModifyBroadcaster
> xBroadcaster( m_xCell
, UNO_QUERY
);
123 if ( xBroadcaster
.is() )
125 xBroadcaster
->removeModifyListener( this );
128 WeakAggComponentImplHelperBase::disposing();
130 // TODO: clean up here whatever you need to clean up (e.g. deregister as XEventListener
134 //--------------------------------------------------------------------
135 Reference
< XPropertySetInfo
> SAL_CALL
OCellValueBinding::getPropertySetInfo( ) throw(RuntimeException
)
137 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
138 return createPropertySetInfo( getInfoHelper() ) ;
141 //--------------------------------------------------------------------
142 ::cppu::IPropertyArrayHelper
& SAL_CALL
OCellValueBinding::getInfoHelper()
144 return *OCellValueBinding_PABase::getArrayHelper();
147 //--------------------------------------------------------------------
148 ::cppu::IPropertyArrayHelper
* OCellValueBinding::createArrayHelper( ) const
150 Sequence
< Property
> aProps
;
151 describeProperties( aProps
);
152 return new ::cppu::OPropertyArrayHelper(aProps
);
155 //--------------------------------------------------------------------
156 void SAL_CALL
OCellValueBinding::getFastPropertyValue( Any
& _rValue
, sal_Int32 _nHandle
) const
158 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
159 OSL_ENSURE( _nHandle
== PROP_HANDLE_BOUND_CELL
, "OCellValueBinding::getFastPropertyValue: invalid handle!" );
160 // we only have this one property ....
161 (void)_nHandle
; // avoid warning in product version
164 Reference
< XCellAddressable
> xCellAddress( m_xCell
, UNO_QUERY
);
165 if ( xCellAddress
.is() )
166 _rValue
<<= xCellAddress
->getCellAddress( );
169 //--------------------------------------------------------------------
170 Sequence
< Type
> SAL_CALL
OCellValueBinding::getSupportedValueTypes( ) throw (RuntimeException
)
172 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
176 sal_Int32 nCount
= m_xCellText
.is() ? 3 : m_xCell
.is() ? 1 : 0;
180 Sequence
< Type
> aTypes( nCount
);
183 // an XCell can be used to set/get "double" values
184 aTypes
[0] = ::getCppuType( static_cast< double* >( NULL
) );
185 if ( m_xCellText
.is() )
187 // an XTextRange can be used to set/get "string" values
188 aTypes
[1] = ::getCppuType( static_cast< OUString
* >( NULL
) );
189 // and additionally, we use it to handle booleans
190 aTypes
[2] = ::getCppuType( static_cast< sal_Bool
* >( NULL
) );
193 // add sal_Int32 only if constructed as ListPositionCellBinding
195 aTypes
[nCount
-1] = ::getCppuType( static_cast< sal_Int32
* >( NULL
) );
201 //--------------------------------------------------------------------
202 sal_Bool SAL_CALL
OCellValueBinding::supportsType( const Type
& aType
) throw (RuntimeException
)
204 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
208 // look up in our sequence
209 Sequence
< Type
> aSupportedTypes( getSupportedValueTypes() );
210 const Type
* pTypes
= aSupportedTypes
.getConstArray();
211 const Type
* pTypesEnd
= aSupportedTypes
.getConstArray() + aSupportedTypes
.getLength();
212 while ( pTypes
!= pTypesEnd
)
213 if ( aType
.equals( *pTypes
++ ) )
219 //--------------------------------------------------------------------
220 Any SAL_CALL
OCellValueBinding::getValue( const Type
& aType
) throw (IncompatibleTypesException
, RuntimeException
)
222 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
225 checkValueType( aType
);
228 switch ( aType
.getTypeClass() )
230 case TypeClass_STRING
:
231 OSL_ENSURE( m_xCellText
.is(), "OCellValueBinding::getValue: don't have a text!" );
232 if ( m_xCellText
.is() )
233 aReturn
<<= m_xCellText
->getString();
235 aReturn
<<= OUString();
238 case TypeClass_BOOLEAN
:
239 OSL_ENSURE( m_xCell
.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
242 // check if the cell has a numeric value (this might go into a helper function):
244 sal_Bool bHasValue
= false;
245 CellContentType eCellType
= m_xCell
->getType();
246 if ( eCellType
== CellContentType_VALUE
)
247 bHasValue
= sal_True
;
248 else if ( eCellType
== CellContentType_FORMULA
)
250 // check if the formula result is a value
251 if ( m_xCell
->getError() == 0 )
253 Reference
<XPropertySet
> xProp( m_xCell
, UNO_QUERY
);
256 CellContentType eResultType
;
257 if ( (xProp
->getPropertyValue("FormulaResultType") >>= eResultType
) && eResultType
== CellContentType_VALUE
)
258 bHasValue
= sal_True
;
265 // 0 is "unchecked", any other value is "checked", regardless of number format
266 double nCellValue
= m_xCell
->getValue();
267 sal_Bool bBoolValue
= ( nCellValue
!= 0.0 );
268 aReturn
<<= bBoolValue
;
270 // empty cells, text cells and text or error formula results: leave return value empty
274 case TypeClass_DOUBLE
:
275 OSL_ENSURE( m_xCell
.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
277 aReturn
<<= m_xCell
->getValue();
279 aReturn
<<= (double)0;
283 OSL_ENSURE( m_xCell
.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
286 // The list position value in the cell is 1-based.
287 // We subtract 1 from any cell value (no special handling for 0 or negative values).
289 sal_Int32 nValue
= (sal_Int32
) rtl::math::approxFloor( m_xCell
->getValue() );
295 aReturn
<<= (sal_Int32
)0;
299 OSL_FAIL( "OCellValueBinding::getValue: unreachable code!" );
300 // a type other than double and string should never have survived the checkValueType
306 //--------------------------------------------------------------------
307 void SAL_CALL
OCellValueBinding::setValue( const Any
& aValue
) throw (IncompatibleTypesException
, NoSupportException
, RuntimeException
)
309 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
312 if ( aValue
.hasValue() )
313 checkValueType( aValue
.getValueType() );
315 switch ( aValue
.getValueType().getTypeClass() )
317 case TypeClass_STRING
:
319 OSL_ENSURE( m_xCellText
.is(), "OCellValueBinding::setValue: don't have a text!" );
323 if ( m_xCellText
.is() )
324 m_xCellText
->setString( sText
);
328 case TypeClass_BOOLEAN
:
330 OSL_ENSURE( m_xCell
.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
332 // boolean is stored as values 0 or 1
333 // TODO: set the number format to boolean if no format is set?
335 sal_Bool
bValue( false );
337 double nCellValue
= bValue
? 1.0 : 0.0;
340 m_xCell
->setValue( nCellValue
);
346 case TypeClass_DOUBLE
:
348 OSL_ENSURE( m_xCell
.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
353 m_xCell
->setValue( nValue
);
359 OSL_ENSURE( m_xCell
.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
361 sal_Int32 nValue
= 0;
362 aValue
>>= nValue
; // list index from control layer (0-based)
363 ++nValue
; // the list position value in the cell is 1-based
365 m_xCell
->setValue( nValue
);
371 // #N/A error value can only be set using XCellRangeData
373 Reference
<XCellRangeData
> xData( m_xCell
, UNO_QUERY
);
374 OSL_ENSURE( xData
.is(), "OCellValueBinding::setValue: don't have XCellRangeData!" );
377 Sequence
<Any
> aInner(1); // one empty element
378 Sequence
< Sequence
<Any
> > aOuter( &aInner
, 1 ); // one row
379 xData
->setDataArray( aOuter
);
385 OSL_FAIL( "OCellValueBinding::setValue: unreachable code!" );
386 // a type other than double and string should never have survived the checkValueType
390 //--------------------------------------------------------------------
391 void OCellValueBinding::setBooleanFormat()
393 // set boolean number format if not already set
395 OUString
sPropName( "NumberFormat" );
396 Reference
<XPropertySet
> xCellProp( m_xCell
, UNO_QUERY
);
397 Reference
<XNumberFormatsSupplier
> xSupplier( m_xDocument
, UNO_QUERY
);
398 if ( xSupplier
.is() && xCellProp
.is() )
400 Reference
<XNumberFormats
> xFormats(xSupplier
->getNumberFormats());
401 Reference
<XNumberFormatTypes
> xTypes( xFormats
, UNO_QUERY
);
405 sal_Bool bWasBoolean
= false;
407 sal_Int32 nOldIndex
= ::comphelper::getINT32( xCellProp
->getPropertyValue( sPropName
) );
408 Reference
<XPropertySet
> xOldFormat
;
411 xOldFormat
.set(xFormats
->getByKey( nOldIndex
));
415 // non-existing format - can happen, use defaults
417 if ( xOldFormat
.is() )
419 // use the locale of the existing format
420 xOldFormat
->getPropertyValue("Locale") >>= aLocale
;
422 sal_Int16 nOldType
= ::comphelper::getINT16(
423 xOldFormat
->getPropertyValue("Type") );
424 if ( nOldType
& NumberFormat::LOGICAL
)
425 bWasBoolean
= sal_True
;
430 sal_Int32 nNewIndex
= xTypes
->getStandardFormat( NumberFormat::LOGICAL
, aLocale
);
431 xCellProp
->setPropertyValue( sPropName
, makeAny( nNewIndex
) );
437 //--------------------------------------------------------------------
438 void OCellValueBinding::checkDisposed( ) const SAL_THROW( ( DisposedException
) )
440 if ( OCellValueBinding_Base::rBHelper
.bInDispose
|| OCellValueBinding_Base::rBHelper
.bDisposed
)
441 throw DisposedException();
442 // TODO: is it worth having an error message here?
445 //--------------------------------------------------------------------
446 void OCellValueBinding::checkInitialized() SAL_THROW( ( RuntimeException
) )
448 if ( !m_bInitialized
)
449 throw RuntimeException();
450 // TODO: error message
453 //--------------------------------------------------------------------
454 void OCellValueBinding::checkValueType( const Type
& _rType
) const SAL_THROW( ( IncompatibleTypesException
) )
456 OCellValueBinding
* pNonConstThis
= const_cast< OCellValueBinding
* >( this );
457 if ( !pNonConstThis
->supportsType( _rType
) )
459 OUString
sMessage( "The given type (" );
460 sMessage
+= _rType
.getTypeName();
461 sMessage
+= ") is not supported by this binding.";
462 // TODO: localize this error message
464 throw IncompatibleTypesException( sMessage
, *pNonConstThis
);
465 // TODO: alternatively use a type converter service for this?
469 //--------------------------------------------------------------------
470 OUString SAL_CALL
OCellValueBinding::getImplementationName( ) throw (RuntimeException
)
472 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
474 return OUString( "com.sun.star.comp.sheet.OCellValueBinding" );
477 //--------------------------------------------------------------------
478 sal_Bool SAL_CALL
OCellValueBinding::supportsService( const OUString
& _rServiceName
) throw (RuntimeException
)
480 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
482 Sequence
< OUString
> aSupportedServices( getSupportedServiceNames() );
483 const OUString
* pLookup
= aSupportedServices
.getConstArray();
484 const OUString
* pLookupEnd
= aSupportedServices
.getConstArray() + aSupportedServices
.getLength();
485 while ( pLookup
!= pLookupEnd
)
486 if ( *pLookup
++ == _rServiceName
)
492 //--------------------------------------------------------------------
493 Sequence
< OUString
> SAL_CALL
OCellValueBinding::getSupportedServiceNames( ) throw (RuntimeException
)
495 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
497 Sequence
< OUString
> aServices( m_bListPos
? 3 : 2 );
498 aServices
[ 0 ] = "com.sun.star.table.CellValueBinding";
499 aServices
[ 1 ] = "com.sun.star.form.binding.ValueBinding";
501 aServices
[ 2 ] = "com.sun.star.table.ListPositionCellBinding";
505 //--------------------------------------------------------------------
506 void SAL_CALL
OCellValueBinding::addModifyListener( const Reference
< XModifyListener
>& _rxListener
) throw (RuntimeException
)
508 if ( _rxListener
.is() )
509 m_aModifyListeners
.addInterface( _rxListener
);
512 //--------------------------------------------------------------------
513 void SAL_CALL
OCellValueBinding::removeModifyListener( const Reference
< XModifyListener
>& _rxListener
) throw (RuntimeException
)
515 if ( _rxListener
.is() )
516 m_aModifyListeners
.removeInterface( _rxListener
);
519 //--------------------------------------------------------------------
520 void OCellValueBinding::notifyModified()
523 aEvent
.Source
.set(*this);
525 ::cppu::OInterfaceIteratorHelper
aIter( m_aModifyListeners
);
526 while ( aIter
.hasMoreElements() )
530 static_cast< XModifyListener
* >( aIter
.next() )->modified( aEvent
);
532 catch( const RuntimeException
& )
536 catch( const Exception
& )
538 OSL_FAIL( "OCellValueBinding::notifyModified: caught a (non-runtime) exception!" );
543 //--------------------------------------------------------------------
544 void SAL_CALL
OCellValueBinding::modified( const EventObject
& /* aEvent */ ) throw (RuntimeException
)
546 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
551 //--------------------------------------------------------------------
552 void SAL_CALL
OCellValueBinding::disposing( const EventObject
& aEvent
) throw (RuntimeException
)
554 DBG_CHKTHIS( OCellValueBinding
, checkConsistency_static
);
556 Reference
<XInterface
> xCellInt( m_xCell
, UNO_QUERY
);
557 if ( xCellInt
== aEvent
.Source
)
559 // release references to cell object
565 //--------------------------------------------------------------------
566 void SAL_CALL
OCellValueBinding::initialize( const Sequence
< Any
>& _rArguments
) throw (Exception
, RuntimeException
)
568 if ( m_bInitialized
)
570 // TODO: error message
572 // get the cell address
573 CellAddress aAddress
;
574 sal_Bool bFoundAddress
= false;
576 const Any
* pLoop
= _rArguments
.getConstArray();
577 const Any
* pLoopEnd
= _rArguments
.getConstArray() + _rArguments
.getLength();
578 for ( ; ( pLoop
!= pLoopEnd
) && !bFoundAddress
; ++pLoop
)
581 if ( *pLoop
>>= aValue
)
583 if ( aValue
.Name
== "BoundCell" )
585 if ( aValue
.Value
>>= aAddress
)
586 bFoundAddress
= sal_True
;
591 if ( !bFoundAddress
)
592 // TODO: error message
595 // get the cell object
598 // first the sheets collection
599 Reference
< XIndexAccess
> xSheets
;
600 if ( m_xDocument
.is() )
601 xSheets
.set(xSheets
.query( m_xDocument
->getSheets( ) ));
602 OSL_ENSURE( xSheets
.is(), "OCellValueBinding::initialize: could not retrieve the sheets!" );
606 // the concrete sheet
607 Reference
< XCellRange
> xSheet(xSheets
->getByIndex( aAddress
.Sheet
), UNO_QUERY
);
608 OSL_ENSURE( xSheet
.is(), "OCellValueBinding::initialize: NULL sheet, but no exception!" );
613 m_xCell
.set(xSheet
->getCellByPosition( aAddress
.Column
, aAddress
.Row
));
614 Reference
< XCellAddressable
> xAddressAccess( m_xCell
, UNO_QUERY
);
615 OSL_ENSURE( xAddressAccess
.is(), "OCellValueBinding::initialize: either NULL cell, or cell without address access!" );
619 catch( const Exception
& )
621 OSL_FAIL( "OCellValueBinding::initialize: caught an exception while retrieving the cell object!" );
626 // TODO error message
628 m_xCellText
.set(m_xCellText
.query( m_xCell
));
630 Reference
<XModifyBroadcaster
> xBroadcaster( m_xCell
, UNO_QUERY
);
631 if ( xBroadcaster
.is() )
633 xBroadcaster
->addModifyListener( this );
636 // TODO: add as XEventListener to the cell, so we get notified when it dies,
637 // and can dispose ourself then
639 // TODO: somehow add as listener so we get notified when the address of the cell changes
640 // We need to forward this as change in our BoundCell property to our property change listeners
642 // TODO: be an XModifyBroadcaster, so that changes in our cell can be notified
643 // to the BindableValue which is/will be bound to this instance.
645 m_bInitialized
= sal_True
;
646 // TODO: place your code here
650 //.........................................................................
652 //.........................................................................
654 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */