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>
32 #include <cppuhelper/supportsservice.hxx>
39 #define PROP_HANDLE_BOUND_CELL 1
41 namespace lang
= ::com::sun::star::lang
;
42 using namespace ::com::sun::star::uno
;
43 using namespace ::com::sun::star::lang
;
44 using namespace ::com::sun::star::table
;
45 using namespace ::com::sun::star::text
;
46 using namespace ::com::sun::star::sheet
;
47 using namespace ::com::sun::star::container
;
48 using namespace ::com::sun::star::beans
;
49 using namespace ::com::sun::star::util
;
50 using namespace ::com::sun::star::form::binding
;
53 const char* OCellValueBinding::checkConsistency_static( const void* _pThis
)
55 return static_cast< const OCellValueBinding
* >( _pThis
)->checkConsistency( );
58 const char* OCellValueBinding::checkConsistency( ) const
60 const char* pAssertion
= NULL
;
61 if ( m_xCellText
.is() && !m_xCell
.is() )
62 // there are places (e.g. getSupportedTypes) which rely on the fact
63 // that m_xCellText.is() implies m_xCell.is()
64 pAssertion
= "cell references inconsistent!";
66 // TODO: place any additional checks here to ensure consistency of this instance
72 OCellValueBinding::OCellValueBinding( const Reference
< XSpreadsheetDocument
>& _rxDocument
, bool _bListPos
)
73 :OCellValueBinding_Base( m_aMutex
)
74 ,OCellValueBinding_PBase( OCellValueBinding_Base::rBHelper
)
75 ,m_xDocument( _rxDocument
)
76 ,m_aModifyListeners( m_aMutex
)
77 ,m_bInitialized( false )
78 ,m_bListPos( _bListPos
)
80 // register our property at the base class
81 CellAddress aInitialPropValue
;
82 registerPropertyNoMember(
83 OUString( "BoundCell" ),
84 PROP_HANDLE_BOUND_CELL
,
85 PropertyAttribute::BOUND
| PropertyAttribute::READONLY
,
86 ::getCppuType( &aInitialPropValue
),
90 // TODO: implement a ReadOnly property as required by the service,
91 // which probably maps to the cell being locked
95 OCellValueBinding::~OCellValueBinding( )
97 if ( !OCellValueBinding_Base::rBHelper
.bDisposed
)
99 acquire(); // prevent duplicate dtor
105 IMPLEMENT_FORWARD_XINTERFACE2( OCellValueBinding
, OCellValueBinding_Base
, OCellValueBinding_PBase
)
108 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCellValueBinding
, OCellValueBinding_Base
, OCellValueBinding_PBase
)
111 void SAL_CALL
OCellValueBinding::disposing()
113 Reference
<XModifyBroadcaster
> xBroadcaster( m_xCell
, UNO_QUERY
);
114 if ( xBroadcaster
.is() )
116 xBroadcaster
->removeModifyListener( this );
119 WeakAggComponentImplHelperBase::disposing();
121 // TODO: clean up here whatever you need to clean up (e.g. deregister as XEventListener
126 Reference
< XPropertySetInfo
> SAL_CALL
OCellValueBinding::getPropertySetInfo( ) throw(RuntimeException
, std::exception
)
128 return createPropertySetInfo( getInfoHelper() ) ;
132 ::cppu::IPropertyArrayHelper
& SAL_CALL
OCellValueBinding::getInfoHelper()
134 return *OCellValueBinding_PABase::getArrayHelper();
138 ::cppu::IPropertyArrayHelper
* OCellValueBinding::createArrayHelper( ) const
140 Sequence
< Property
> aProps
;
141 describeProperties( aProps
);
142 return new ::cppu::OPropertyArrayHelper(aProps
);
146 void SAL_CALL
OCellValueBinding::getFastPropertyValue( Any
& _rValue
, sal_Int32 _nHandle
) const
148 OSL_ENSURE( _nHandle
== PROP_HANDLE_BOUND_CELL
, "OCellValueBinding::getFastPropertyValue: invalid handle!" );
149 // we only have this one property ....
150 (void)_nHandle
; // avoid warning in product version
153 Reference
< XCellAddressable
> xCellAddress( m_xCell
, UNO_QUERY
);
154 if ( xCellAddress
.is() )
155 _rValue
<<= xCellAddress
->getCellAddress( );
159 Sequence
< Type
> SAL_CALL
OCellValueBinding::getSupportedValueTypes( ) throw (RuntimeException
, std::exception
)
164 sal_Int32 nCount
= m_xCellText
.is() ? 3 : m_xCell
.is() ? 1 : 0;
168 Sequence
< Type
> aTypes( nCount
);
171 // an XCell can be used to set/get "double" values
172 aTypes
[0] = ::cppu::UnoType
<double>::get();
173 if ( m_xCellText
.is() )
175 // an XTextRange can be used to set/get "string" values
176 aTypes
[1] = ::cppu::UnoType
<OUString
>::get();
177 // and additionally, we use it to handle booleans
178 aTypes
[2] = ::cppu::UnoType
<sal_Bool
>::get();
181 // add sal_Int32 only if constructed as ListPositionCellBinding
183 aTypes
[nCount
-1] = cppu::UnoType
<sal_Int32
>::get();
190 sal_Bool SAL_CALL
OCellValueBinding::supportsType( const Type
& aType
) throw (RuntimeException
, std::exception
)
195 // look up in our sequence
196 Sequence
< Type
> aSupportedTypes( getSupportedValueTypes() );
197 const Type
* pTypes
= aSupportedTypes
.getConstArray();
198 const Type
* pTypesEnd
= aSupportedTypes
.getConstArray() + aSupportedTypes
.getLength();
199 while ( pTypes
!= pTypesEnd
)
200 if ( aType
.equals( *pTypes
++ ) )
207 Any SAL_CALL
OCellValueBinding::getValue( const Type
& aType
) throw (IncompatibleTypesException
, RuntimeException
, std::exception
)
211 checkValueType( aType
);
214 switch ( aType
.getTypeClass() )
216 case TypeClass_STRING
:
217 OSL_ENSURE( m_xCellText
.is(), "OCellValueBinding::getValue: don't have a text!" );
218 if ( m_xCellText
.is() )
219 aReturn
<<= m_xCellText
->getString();
221 aReturn
<<= OUString();
224 case TypeClass_BOOLEAN
:
225 OSL_ENSURE( m_xCell
.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
228 // check if the cell has a numeric value (this might go into a helper function):
230 bool bHasValue
= false;
231 CellContentType eCellType
= m_xCell
->getType();
232 if ( eCellType
== CellContentType_VALUE
)
234 else if ( eCellType
== CellContentType_FORMULA
)
236 // check if the formula result is a value
237 if ( m_xCell
->getError() == 0 )
239 Reference
<XPropertySet
> xProp( m_xCell
, UNO_QUERY
);
242 CellContentType eResultType
;
243 if ( (xProp
->getPropertyValue("FormulaResultType") >>= eResultType
) && eResultType
== CellContentType_VALUE
)
251 // 0 is "unchecked", any other value is "checked", regardless of number format
252 double nCellValue
= m_xCell
->getValue();
253 bool bBoolValue
= ( nCellValue
!= 0.0 );
254 aReturn
<<= bBoolValue
;
256 // empty cells, text cells and text or error formula results: leave return value empty
260 case TypeClass_DOUBLE
:
261 OSL_ENSURE( m_xCell
.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
263 aReturn
<<= m_xCell
->getValue();
265 aReturn
<<= (double)0;
269 OSL_ENSURE( m_xCell
.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
272 // The list position value in the cell is 1-based.
273 // We subtract 1 from any cell value (no special handling for 0 or negative values).
275 sal_Int32 nValue
= (sal_Int32
) rtl::math::approxFloor( m_xCell
->getValue() );
281 aReturn
<<= (sal_Int32
)0;
285 OSL_FAIL( "OCellValueBinding::getValue: unreachable code!" );
286 // a type other than double and string should never have survived the checkValueType
293 void SAL_CALL
OCellValueBinding::setValue( const Any
& aValue
) throw (IncompatibleTypesException
, NoSupportException
, RuntimeException
, std::exception
)
297 if ( aValue
.hasValue() )
298 checkValueType( aValue
.getValueType() );
300 switch ( aValue
.getValueType().getTypeClass() )
302 case TypeClass_STRING
:
304 OSL_ENSURE( m_xCellText
.is(), "OCellValueBinding::setValue: don't have a text!" );
308 if ( m_xCellText
.is() )
309 m_xCellText
->setString( sText
);
313 case TypeClass_BOOLEAN
:
315 OSL_ENSURE( m_xCell
.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
317 // boolean is stored as values 0 or 1
318 // TODO: set the number format to boolean if no format is set?
320 bool bValue( false );
322 double nCellValue
= bValue
? 1.0 : 0.0;
325 m_xCell
->setValue( nCellValue
);
331 case TypeClass_DOUBLE
:
333 OSL_ENSURE( m_xCell
.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
338 m_xCell
->setValue( nValue
);
344 OSL_ENSURE( m_xCell
.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
346 sal_Int32 nValue
= 0;
347 aValue
>>= nValue
; // list index from control layer (0-based)
348 ++nValue
; // the list position value in the cell is 1-based
350 m_xCell
->setValue( nValue
);
356 // #N/A error value can only be set using XCellRangeData
358 Reference
<XCellRangeData
> xData( m_xCell
, UNO_QUERY
);
359 OSL_ENSURE( xData
.is(), "OCellValueBinding::setValue: don't have XCellRangeData!" );
362 Sequence
<Any
> aInner(1); // one empty element
363 Sequence
< Sequence
<Any
> > aOuter( &aInner
, 1 ); // one row
364 xData
->setDataArray( aOuter
);
370 OSL_FAIL( "OCellValueBinding::setValue: unreachable code!" );
371 // a type other than double and string should never have survived the checkValueType
376 void OCellValueBinding::setBooleanFormat()
378 // set boolean number format if not already set
380 OUString
sPropName( "NumberFormat" );
381 Reference
<XPropertySet
> xCellProp( m_xCell
, UNO_QUERY
);
382 Reference
<XNumberFormatsSupplier
> xSupplier( m_xDocument
, UNO_QUERY
);
383 if ( xSupplier
.is() && xCellProp
.is() )
385 Reference
<XNumberFormats
> xFormats(xSupplier
->getNumberFormats());
386 Reference
<XNumberFormatTypes
> xTypes( xFormats
, UNO_QUERY
);
389 lang::Locale aLocale
;
390 bool bWasBoolean
= false;
392 sal_Int32 nOldIndex
= ::comphelper::getINT32( xCellProp
->getPropertyValue( sPropName
) );
393 Reference
<XPropertySet
> xOldFormat
;
396 xOldFormat
.set(xFormats
->getByKey( nOldIndex
));
400 // non-existing format - can happen, use defaults
402 if ( xOldFormat
.is() )
404 // use the locale of the existing format
405 xOldFormat
->getPropertyValue("Locale") >>= aLocale
;
407 sal_Int16 nOldType
= ::comphelper::getINT16(
408 xOldFormat
->getPropertyValue("Type") );
409 if ( nOldType
& NumberFormat::LOGICAL
)
415 sal_Int32 nNewIndex
= xTypes
->getStandardFormat( NumberFormat::LOGICAL
, aLocale
);
416 xCellProp
->setPropertyValue( sPropName
, makeAny( nNewIndex
) );
423 void OCellValueBinding::checkDisposed( ) const SAL_THROW( ( DisposedException
) )
425 if ( OCellValueBinding_Base::rBHelper
.bInDispose
|| OCellValueBinding_Base::rBHelper
.bDisposed
)
426 throw DisposedException();
427 // TODO: is it worth having an error message here?
431 void OCellValueBinding::checkInitialized() SAL_THROW( ( RuntimeException
) )
433 if ( !m_bInitialized
)
434 throw RuntimeException();
435 // TODO: error message
439 void OCellValueBinding::checkValueType( const Type
& _rType
) const SAL_THROW( ( IncompatibleTypesException
) )
441 OCellValueBinding
* pNonConstThis
= const_cast< OCellValueBinding
* >( this );
442 if ( !pNonConstThis
->supportsType( _rType
) )
444 OUString
sMessage( "The given type (" );
445 sMessage
+= _rType
.getTypeName();
446 sMessage
+= ") is not supported by this binding.";
447 // TODO: localize this error message
449 throw IncompatibleTypesException( sMessage
, *pNonConstThis
);
450 // TODO: alternatively use a type converter service for this?
454 OUString SAL_CALL
OCellValueBinding::getImplementationName( ) throw (RuntimeException
, std::exception
)
456 return OUString( "com.sun.star.comp.sheet.OCellValueBinding" );
459 sal_Bool SAL_CALL
OCellValueBinding::supportsService( const OUString
& _rServiceName
) throw (RuntimeException
, std::exception
)
461 return cppu::supportsService(this, _rServiceName
);
464 Sequence
< OUString
> SAL_CALL
OCellValueBinding::getSupportedServiceNames( ) throw (RuntimeException
, std::exception
)
466 Sequence
< OUString
> aServices( m_bListPos
? 3 : 2 );
467 aServices
[ 0 ] = "com.sun.star.table.CellValueBinding";
468 aServices
[ 1 ] = "com.sun.star.form.binding.ValueBinding";
470 aServices
[ 2 ] = "com.sun.star.table.ListPositionCellBinding";
474 void SAL_CALL
OCellValueBinding::addModifyListener( const Reference
< XModifyListener
>& _rxListener
) throw (RuntimeException
, std::exception
)
476 if ( _rxListener
.is() )
477 m_aModifyListeners
.addInterface( _rxListener
);
481 void SAL_CALL
OCellValueBinding::removeModifyListener( const Reference
< XModifyListener
>& _rxListener
) throw (RuntimeException
, std::exception
)
483 if ( _rxListener
.is() )
484 m_aModifyListeners
.removeInterface( _rxListener
);
488 void OCellValueBinding::notifyModified()
491 aEvent
.Source
.set(*this);
493 ::cppu::OInterfaceIteratorHelper
aIter( m_aModifyListeners
);
494 while ( aIter
.hasMoreElements() )
498 static_cast< XModifyListener
* >( aIter
.next() )->modified( aEvent
);
500 catch( const RuntimeException
& )
504 catch( const Exception
& )
506 OSL_FAIL( "OCellValueBinding::notifyModified: caught a (non-runtime) exception!" );
512 void SAL_CALL
OCellValueBinding::modified( const EventObject
& /* aEvent */ ) throw (RuntimeException
, std::exception
)
518 void SAL_CALL
OCellValueBinding::disposing( const EventObject
& aEvent
) throw (RuntimeException
, std::exception
)
520 Reference
<XInterface
> xCellInt( m_xCell
, UNO_QUERY
);
521 if ( xCellInt
== aEvent
.Source
)
523 // release references to cell object
530 void SAL_CALL
OCellValueBinding::initialize( const Sequence
< Any
>& _rArguments
) throw (Exception
, RuntimeException
, std::exception
)
532 if ( m_bInitialized
)
534 // TODO: error message
536 // get the cell address
537 CellAddress aAddress
;
538 bool bFoundAddress
= false;
540 const Any
* pLoop
= _rArguments
.getConstArray();
541 const Any
* pLoopEnd
= _rArguments
.getConstArray() + _rArguments
.getLength();
542 for ( ; ( pLoop
!= pLoopEnd
) && !bFoundAddress
; ++pLoop
)
545 if ( *pLoop
>>= aValue
)
547 if ( aValue
.Name
== "BoundCell" )
549 if ( aValue
.Value
>>= aAddress
)
550 bFoundAddress
= true;
555 if ( !bFoundAddress
)
556 // TODO: error message
559 // get the cell object
562 // first the sheets collection
563 Reference
< XIndexAccess
> xSheets
;
564 if ( m_xDocument
.is() )
565 xSheets
.set(xSheets
.query( m_xDocument
->getSheets( ) ));
566 OSL_ENSURE( xSheets
.is(), "OCellValueBinding::initialize: could not retrieve the sheets!" );
570 // the concrete sheet
571 Reference
< XCellRange
> xSheet(xSheets
->getByIndex( aAddress
.Sheet
), UNO_QUERY
);
572 OSL_ENSURE( xSheet
.is(), "OCellValueBinding::initialize: NULL sheet, but no exception!" );
577 m_xCell
.set(xSheet
->getCellByPosition( aAddress
.Column
, aAddress
.Row
));
578 Reference
< XCellAddressable
> xAddressAccess( m_xCell
, UNO_QUERY
);
579 OSL_ENSURE( xAddressAccess
.is(), "OCellValueBinding::initialize: either NULL cell, or cell without address access!" );
583 catch( const Exception
& )
585 OSL_FAIL( "OCellValueBinding::initialize: caught an exception while retrieving the cell object!" );
590 // TODO error message
592 m_xCellText
.set(m_xCellText
.query( m_xCell
));
594 Reference
<XModifyBroadcaster
> xBroadcaster( m_xCell
, UNO_QUERY
);
595 if ( xBroadcaster
.is() )
597 xBroadcaster
->addModifyListener( this );
600 // TODO: add as XEventListener to the cell, so we get notified when it dies,
601 // and can dispose ourself then
603 // TODO: somehow add as listener so we get notified when the address of the cell changes
604 // We need to forward this as change in our BoundCell property to our property change listeners
606 // TODO: be an XModifyBroadcaster, so that changes in our cell can be notified
607 // to the BindableValue which is/will be bound to this instance.
609 m_bInitialized
= true;
610 // TODO: place your code here
616 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */