Bump version to 4.3-4
[LibreOffice.git] / sc / source / ui / unoobj / cellvaluebinding.cxx
blob06172745b326a4f98ef6125eea220fa657165cdb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
35 namespace calc
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;
52 #ifdef DBG_UTIL
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
67 return pAssertion;
69 #endif
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 ),
87 &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
100 dispose();
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
122 // for the cell)
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
152 _rValue.clear();
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)
161 checkDisposed( );
162 checkInitialized( );
164 sal_Int32 nCount = m_xCellText.is() ? 3 : m_xCell.is() ? 1 : 0;
165 if ( m_bListPos )
166 ++nCount;
168 Sequence< Type > aTypes( nCount );
169 if ( m_xCell.is() )
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
182 if ( m_bListPos )
183 aTypes[nCount-1] = cppu::UnoType<sal_Int32>::get();
186 return aTypes;
190 sal_Bool SAL_CALL OCellValueBinding::supportsType( const Type& aType ) throw (RuntimeException, std::exception)
192 checkDisposed( );
193 checkInitialized( );
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++ ) )
201 return sal_True;
203 return false;
207 Any SAL_CALL OCellValueBinding::getValue( const Type& aType ) throw (IncompatibleTypesException, RuntimeException, std::exception)
209 checkDisposed( );
210 checkInitialized( );
211 checkValueType( aType );
213 Any aReturn;
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();
220 else
221 aReturn <<= OUString();
222 break;
224 case TypeClass_BOOLEAN:
225 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
226 if ( m_xCell.is() )
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 )
233 bHasValue = true;
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 );
240 if ( xProp.is() )
242 CellContentType eResultType;
243 if ( (xProp->getPropertyValue("FormulaResultType") >>= eResultType) && eResultType == CellContentType_VALUE )
244 bHasValue = true;
249 if ( bHasValue )
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
258 break;
260 case TypeClass_DOUBLE:
261 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
262 if ( m_xCell.is() )
263 aReturn <<= m_xCell->getValue();
264 else
265 aReturn <<= (double)0;
266 break;
268 case TypeClass_LONG:
269 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
270 if ( m_xCell.is() )
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() );
276 --nValue;
278 aReturn <<= nValue;
280 else
281 aReturn <<= (sal_Int32)0;
282 break;
284 default:
285 OSL_FAIL( "OCellValueBinding::getValue: unreachable code!" );
286 // a type other than double and string should never have survived the checkValueType
287 // above
289 return aReturn;
293 void SAL_CALL OCellValueBinding::setValue( const Any& aValue ) throw (IncompatibleTypesException, NoSupportException, RuntimeException, std::exception)
295 checkDisposed( );
296 checkInitialized( );
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!" );
306 OUString sText;
307 aValue >>= sText;
308 if ( m_xCellText.is() )
309 m_xCellText->setString( sText );
311 break;
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 );
321 aValue >>= bValue;
322 double nCellValue = bValue ? 1.0 : 0.0;
324 if ( m_xCell.is() )
325 m_xCell->setValue( nCellValue );
327 setBooleanFormat();
329 break;
331 case TypeClass_DOUBLE:
333 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
335 double nValue = 0;
336 aValue >>= nValue;
337 if ( m_xCell.is() )
338 m_xCell->setValue( nValue );
340 break;
342 case TypeClass_LONG:
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
349 if ( m_xCell.is() )
350 m_xCell->setValue( nValue );
352 break;
354 case TypeClass_VOID:
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!" );
360 if ( xData.is() )
362 Sequence<Any> aInner(1); // one empty element
363 Sequence< Sequence<Any> > aOuter( &aInner, 1 ); // one row
364 xData->setDataArray( aOuter );
367 break;
369 default:
370 OSL_FAIL( "OCellValueBinding::setValue: unreachable code!" );
371 // a type other than double and string should never have survived the checkValueType
372 // above
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 );
387 if ( xTypes.is() )
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 ));
398 catch ( Exception& )
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 )
410 bWasBoolean = true;
413 if ( !bWasBoolean )
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";
469 if ( m_bListPos )
470 aServices[ 2 ] = "com.sun.star.table.ListPositionCellBinding";
471 return aServices;
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()
490 EventObject aEvent;
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& )
502 // silent this
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)
514 notifyModified();
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
524 m_xCell.clear();
525 m_xCellText.clear();
530 void SAL_CALL OCellValueBinding::initialize( const Sequence< Any >& _rArguments ) throw (Exception, RuntimeException, std::exception)
532 if ( m_bInitialized )
533 throw Exception();
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 )
544 NamedValue aValue;
545 if ( *pLoop >>= aValue )
547 if ( aValue.Name == "BoundCell" )
549 if ( aValue.Value >>= aAddress )
550 bFoundAddress = true;
555 if ( !bFoundAddress )
556 // TODO: error message
557 throw Exception();
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!" );
568 if ( xSheets.is() )
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!" );
574 // the concrete cell
575 if ( xSheet.is() )
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!" );
588 if ( !m_xCell.is() )
589 throw Exception();
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
613 } // namespace calc
616 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */