Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / ui / unoobj / cellvaluebinding.cxx
blob3aeb54715f7d82609c175e0fda2fc215d54109f5
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>
33 //.........................................................................
34 namespace calc
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 //=====================================================================
51 //= OCellValueBinding
52 //=====================================================================
53 DBG_NAME( OCellValueBinding )
54 //---------------------------------------------------------------------
55 #ifdef DBG_UTIL
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
70 return pAssertion;
72 #endif
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 ),
92 &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
105 dispose();
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
131 // for the cell)
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
163 _rValue.clear();
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 );
173 checkDisposed( );
174 checkInitialized( );
176 sal_Int32 nCount = m_xCellText.is() ? 3 : m_xCell.is() ? 1 : 0;
177 if ( m_bListPos )
178 ++nCount;
180 Sequence< Type > aTypes( nCount );
181 if ( m_xCell.is() )
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
194 if ( m_bListPos )
195 aTypes[nCount-1] = ::getCppuType( static_cast< sal_Int32* >( NULL ) );
198 return aTypes;
201 //--------------------------------------------------------------------
202 sal_Bool SAL_CALL OCellValueBinding::supportsType( const Type& aType ) throw (RuntimeException)
204 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
205 checkDisposed( );
206 checkInitialized( );
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++ ) )
214 return sal_True;
216 return false;
219 //--------------------------------------------------------------------
220 Any SAL_CALL OCellValueBinding::getValue( const Type& aType ) throw (IncompatibleTypesException, RuntimeException)
222 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
223 checkDisposed( );
224 checkInitialized( );
225 checkValueType( aType );
227 Any aReturn;
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();
234 else
235 aReturn <<= OUString();
236 break;
238 case TypeClass_BOOLEAN:
239 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
240 if ( m_xCell.is() )
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 );
254 if ( xProp.is() )
256 CellContentType eResultType;
257 if ( (xProp->getPropertyValue("FormulaResultType") >>= eResultType) && eResultType == CellContentType_VALUE )
258 bHasValue = sal_True;
263 if ( bHasValue )
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
272 break;
274 case TypeClass_DOUBLE:
275 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
276 if ( m_xCell.is() )
277 aReturn <<= m_xCell->getValue();
278 else
279 aReturn <<= (double)0;
280 break;
282 case TypeClass_LONG:
283 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
284 if ( m_xCell.is() )
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() );
290 --nValue;
292 aReturn <<= nValue;
294 else
295 aReturn <<= (sal_Int32)0;
296 break;
298 default:
299 OSL_FAIL( "OCellValueBinding::getValue: unreachable code!" );
300 // a type other than double and string should never have survived the checkValueType
301 // above
303 return aReturn;
306 //--------------------------------------------------------------------
307 void SAL_CALL OCellValueBinding::setValue( const Any& aValue ) throw (IncompatibleTypesException, NoSupportException, RuntimeException)
309 DBG_CHKTHIS( OCellValueBinding, checkConsistency_static );
310 checkDisposed( );
311 checkInitialized( );
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!" );
321 OUString sText;
322 aValue >>= sText;
323 if ( m_xCellText.is() )
324 m_xCellText->setString( sText );
326 break;
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 );
336 aValue >>= bValue;
337 double nCellValue = bValue ? 1.0 : 0.0;
339 if ( m_xCell.is() )
340 m_xCell->setValue( nCellValue );
342 setBooleanFormat();
344 break;
346 case TypeClass_DOUBLE:
348 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
350 double nValue = 0;
351 aValue >>= nValue;
352 if ( m_xCell.is() )
353 m_xCell->setValue( nValue );
355 break;
357 case TypeClass_LONG:
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
364 if ( m_xCell.is() )
365 m_xCell->setValue( nValue );
367 break;
369 case TypeClass_VOID:
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!" );
375 if ( xData.is() )
377 Sequence<Any> aInner(1); // one empty element
378 Sequence< Sequence<Any> > aOuter( &aInner, 1 ); // one row
379 xData->setDataArray( aOuter );
382 break;
384 default:
385 OSL_FAIL( "OCellValueBinding::setValue: unreachable code!" );
386 // a type other than double and string should never have survived the checkValueType
387 // above
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 );
402 if ( xTypes.is() )
404 Locale aLocale;
405 sal_Bool bWasBoolean = false;
407 sal_Int32 nOldIndex = ::comphelper::getINT32( xCellProp->getPropertyValue( sPropName ) );
408 Reference<XPropertySet> xOldFormat;
411 xOldFormat.set(xFormats->getByKey( nOldIndex ));
413 catch ( Exception& )
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;
428 if ( !bWasBoolean )
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 )
487 return sal_True;
489 return false;
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";
500 if ( m_bListPos )
501 aServices[ 2 ] = "com.sun.star.table.ListPositionCellBinding";
502 return aServices;
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()
522 EventObject aEvent;
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& )
534 // silent this
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 );
548 notifyModified();
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
560 m_xCell.clear();
561 m_xCellText.clear();
565 //--------------------------------------------------------------------
566 void SAL_CALL OCellValueBinding::initialize( const Sequence< Any >& _rArguments ) throw (Exception, RuntimeException)
568 if ( m_bInitialized )
569 throw Exception();
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 )
580 NamedValue aValue;
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
593 throw Exception();
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!" );
604 if ( xSheets.is() )
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!" );
610 // the concrete cell
611 if ( xSheet.is() )
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!" );
624 if ( !m_xCell.is() )
625 throw Exception();
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 //.........................................................................
651 } // namespace calc
652 //.........................................................................
654 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */