607 lines
22 KiB
C++
607 lines
22 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include "cellvaluebinding.hxx"
|
|
#include <rtl/math.hxx>
|
|
#include <com/sun/star/form/binding/IncompatibleTypesException.hpp>
|
|
#include <com/sun/star/lang/NotInitializedException.hpp>
|
|
#include <com/sun/star/text/XTextRange.hpp>
|
|
#include <com/sun/star/table/XCellRange.hpp>
|
|
#include <com/sun/star/sheet/FormulaResult.hpp>
|
|
#include <com/sun/star/sheet/XCellAddressable.hpp>
|
|
#include <com/sun/star/sheet/XCellRangeData.hpp>
|
|
#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
|
|
#include <com/sun/star/container/XIndexAccess.hpp>
|
|
#include <com/sun/star/beans/PropertyAttribute.hpp>
|
|
#include <com/sun/star/beans/NamedValue.hpp>
|
|
#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
|
|
#include <com/sun/star/util/XNumberFormatTypes.hpp>
|
|
#include <com/sun/star/util/NumberFormat.hpp>
|
|
#include <cppuhelper/supportsservice.hxx>
|
|
#include <comphelper/types.hxx>
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
|
|
namespace calc
|
|
{
|
|
|
|
#define PROP_HANDLE_BOUND_CELL 1
|
|
|
|
namespace lang = css::lang;
|
|
using namespace ::com::sun::star::uno;
|
|
using namespace ::com::sun::star::lang;
|
|
using namespace ::com::sun::star::table;
|
|
using namespace ::com::sun::star::text;
|
|
using namespace ::com::sun::star::sheet;
|
|
using namespace ::com::sun::star::container;
|
|
using namespace ::com::sun::star::beans;
|
|
using namespace ::com::sun::star::util;
|
|
using namespace ::com::sun::star::form::binding;
|
|
|
|
OCellValueBinding::OCellValueBinding( const Reference< XSpreadsheetDocument >& _rxDocument, bool _bListPos )
|
|
:m_xDocument( _rxDocument )
|
|
,m_bInitialized( false )
|
|
,m_bListPos( _bListPos )
|
|
{
|
|
// register our property at the base class
|
|
registerPropertyNoMember(
|
|
u"BoundCell"_ustr,
|
|
PROP_HANDLE_BOUND_CELL,
|
|
PropertyAttribute::BOUND | PropertyAttribute::READONLY,
|
|
cppu::UnoType<CellAddress>::get(),
|
|
css::uno::Any(CellAddress())
|
|
);
|
|
|
|
// TODO: implement a ReadOnly property as required by the service,
|
|
// which probably maps to the cell being locked
|
|
}
|
|
|
|
OCellValueBinding::~OCellValueBinding( )
|
|
{
|
|
if ( !m_bDisposed )
|
|
{
|
|
acquire(); // prevent duplicate dtor
|
|
dispose();
|
|
}
|
|
}
|
|
|
|
IMPLEMENT_FORWARD_XINTERFACE2( OCellValueBinding, OCellValueBinding_Base, OCellValueBinding_PBase )
|
|
|
|
IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCellValueBinding, OCellValueBinding_Base, OCellValueBinding_PBase )
|
|
|
|
void OCellValueBinding::disposing( std::unique_lock<std::mutex>& rGuard )
|
|
{
|
|
Reference<XModifyBroadcaster> xBroadcaster( m_xCell, UNO_QUERY );
|
|
if ( xBroadcaster.is() )
|
|
{
|
|
xBroadcaster->removeModifyListener( this );
|
|
}
|
|
|
|
WeakComponentImplHelperBase::disposing(rGuard);
|
|
|
|
// TODO: clean up here whatever you need to clean up (e.g. deregister as XEventListener
|
|
// for the cell)
|
|
}
|
|
|
|
Reference< XPropertySetInfo > SAL_CALL OCellValueBinding::getPropertySetInfo( )
|
|
{
|
|
return createPropertySetInfo( getInfoHelper() ) ;
|
|
}
|
|
|
|
::cppu::IPropertyArrayHelper& OCellValueBinding::getInfoHelper()
|
|
{
|
|
return *OCellValueBinding_PABase::getArrayHelper();
|
|
}
|
|
|
|
::cppu::IPropertyArrayHelper* OCellValueBinding::createArrayHelper( ) const
|
|
{
|
|
Sequence< Property > aProps;
|
|
describeProperties( aProps );
|
|
return new ::cppu::OPropertyArrayHelper(aProps);
|
|
}
|
|
|
|
void OCellValueBinding::getFastPropertyValue( std::unique_lock<std::mutex>& /*rGuard*/, Any& _rValue, sal_Int32 _nHandle ) const
|
|
{
|
|
OSL_ENSURE( _nHandle == PROP_HANDLE_BOUND_CELL, "OCellValueBinding::getFastPropertyValue: invalid handle!" );
|
|
// we only have this one property...
|
|
|
|
_rValue.clear();
|
|
Reference< XCellAddressable > xCellAddress( m_xCell, UNO_QUERY );
|
|
if ( xCellAddress.is() )
|
|
_rValue <<= xCellAddress->getCellAddress( );
|
|
}
|
|
|
|
Sequence< Type > SAL_CALL OCellValueBinding::getSupportedValueTypes( )
|
|
{
|
|
std::unique_lock<std::mutex> aGuard(m_aMutex);
|
|
throwIfDisposed(aGuard);
|
|
checkInitialized( );
|
|
return getSupportedValueTypes(aGuard);
|
|
}
|
|
|
|
Sequence< Type > OCellValueBinding::getSupportedValueTypes( std::unique_lock<std::mutex>& /*rGuard*/ ) const
|
|
{
|
|
sal_Int32 nCount = m_xCellText.is() ? 3 : m_xCell.is() ? 1 : 0;
|
|
if ( m_bListPos )
|
|
++nCount;
|
|
|
|
Sequence< Type > aTypes( nCount );
|
|
if ( m_xCell.is() )
|
|
{
|
|
auto pTypes = aTypes.getArray();
|
|
|
|
// an XCell can be used to set/get "double" values
|
|
pTypes[0] = ::cppu::UnoType<double>::get();
|
|
if ( m_xCellText.is() )
|
|
{
|
|
// an XTextRange can be used to set/get "string" values
|
|
pTypes[1] = ::cppu::UnoType<OUString>::get();
|
|
// and additionally, we use it to handle booleans
|
|
pTypes[2] = ::cppu::UnoType<sal_Bool>::get();
|
|
}
|
|
|
|
// add sal_Int32 only if constructed as ListPositionCellBinding
|
|
if ( m_bListPos )
|
|
pTypes[nCount-1] = cppu::UnoType<sal_Int32>::get();
|
|
}
|
|
|
|
return aTypes;
|
|
}
|
|
|
|
sal_Bool SAL_CALL OCellValueBinding::supportsType( const Type& aType )
|
|
{
|
|
std::unique_lock<std::mutex> aGuard(m_aMutex);
|
|
throwIfDisposed(aGuard);
|
|
checkInitialized( );
|
|
return supportsType(aGuard, aType);
|
|
}
|
|
|
|
bool OCellValueBinding::supportsType( std::unique_lock<std::mutex>& rGuard, const Type& aType ) const
|
|
{
|
|
// look up in our sequence
|
|
const Sequence< Type > aSupportedTypes( getSupportedValueTypes(rGuard) );
|
|
for ( auto const & i : aSupportedTypes )
|
|
if ( aType == i )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
Any SAL_CALL OCellValueBinding::getValue( const Type& aType )
|
|
{
|
|
std::unique_lock<std::mutex> aGuard(m_aMutex);
|
|
throwIfDisposed(aGuard);
|
|
checkInitialized( );
|
|
checkValueType( aGuard, aType );
|
|
|
|
Any aReturn;
|
|
switch ( aType.getTypeClass() )
|
|
{
|
|
case TypeClass_STRING:
|
|
OSL_ENSURE( m_xCellText.is(), "OCellValueBinding::getValue: don't have a text!" );
|
|
if ( m_xCellText.is() )
|
|
aReturn <<= m_xCellText->getString();
|
|
else
|
|
aReturn <<= OUString();
|
|
break;
|
|
|
|
case TypeClass_BOOLEAN:
|
|
OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
|
|
if ( m_xCell.is() )
|
|
{
|
|
// check if the cell has a numeric value (this might go into a helper function):
|
|
|
|
bool bHasValue = false;
|
|
CellContentType eCellType = m_xCell->getType();
|
|
if ( eCellType == CellContentType_VALUE )
|
|
bHasValue = true;
|
|
else if ( eCellType == CellContentType_FORMULA )
|
|
{
|
|
// check if the formula result is a value
|
|
if ( m_xCell->getError() == 0 )
|
|
{
|
|
Reference<XPropertySet> xProp( m_xCell, UNO_QUERY );
|
|
if ( xProp.is() )
|
|
{
|
|
sal_Int32 nResultType;
|
|
if ( (xProp->getPropertyValue(u"FormulaResultType2"_ustr) >>= nResultType)
|
|
&& nResultType == FormulaResult::VALUE )
|
|
bHasValue = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bHasValue )
|
|
{
|
|
// 0 is "unchecked", any other value is "checked", regardless of number format
|
|
double nCellValue = m_xCell->getValue();
|
|
bool bBoolValue = ( nCellValue != 0.0 );
|
|
aReturn <<= bBoolValue;
|
|
}
|
|
// empty cells, text cells and text or error formula results: leave return value empty
|
|
}
|
|
break;
|
|
|
|
case TypeClass_DOUBLE:
|
|
OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
|
|
if ( m_xCell.is() )
|
|
aReturn <<= m_xCell->getValue();
|
|
else
|
|
aReturn <<= double(0);
|
|
break;
|
|
|
|
case TypeClass_LONG:
|
|
OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
|
|
if ( m_xCell.is() )
|
|
{
|
|
// The list position value in the cell is 1-based.
|
|
// We subtract 1 from any cell value (no special handling for 0 or negative values).
|
|
|
|
sal_Int32 nValue = static_cast<sal_Int32>(rtl::math::approxFloor( m_xCell->getValue() ));
|
|
--nValue;
|
|
|
|
aReturn <<= nValue;
|
|
}
|
|
else
|
|
aReturn <<= sal_Int32(0);
|
|
break;
|
|
|
|
default:
|
|
OSL_FAIL( "OCellValueBinding::getValue: unreachable code!" );
|
|
// a type other than double and string should never have survived the checkValueType
|
|
// above
|
|
}
|
|
return aReturn;
|
|
}
|
|
|
|
void SAL_CALL OCellValueBinding::setValue( const Any& aValue )
|
|
{
|
|
std::unique_lock<std::mutex> aGuard(m_aMutex);
|
|
throwIfDisposed(aGuard);
|
|
checkInitialized( );
|
|
if ( aValue.hasValue() )
|
|
checkValueType( aGuard, aValue.getValueType() );
|
|
|
|
switch ( aValue.getValueTypeClass() )
|
|
{
|
|
case TypeClass_STRING:
|
|
{
|
|
OSL_ENSURE( m_xCellText.is(), "OCellValueBinding::setValue: don't have a text!" );
|
|
|
|
OUString sText;
|
|
aValue >>= sText;
|
|
if ( m_xCellText.is() )
|
|
{
|
|
// might call back into us via modified(EventObject&)
|
|
aGuard.unlock();
|
|
m_xCellText->setString( sText );
|
|
aGuard.lock();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TypeClass_BOOLEAN:
|
|
{
|
|
OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
|
|
|
|
// boolean is stored as values 0 or 1
|
|
// TODO: set the number format to boolean if no format is set?
|
|
|
|
bool bValue( false );
|
|
aValue >>= bValue;
|
|
double nCellValue = bValue ? 1.0 : 0.0;
|
|
|
|
if ( m_xCell.is() )
|
|
{
|
|
// might call back into us via modified(EventObject&)
|
|
aGuard.unlock();
|
|
m_xCell->setValue( nCellValue );
|
|
aGuard.lock();
|
|
}
|
|
|
|
setBooleanFormat();
|
|
}
|
|
break;
|
|
|
|
case TypeClass_DOUBLE:
|
|
{
|
|
OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
|
|
|
|
double nValue = 0;
|
|
aValue >>= nValue;
|
|
if ( m_xCell.is() )
|
|
{
|
|
// might call back into us via modified(EventObject&)
|
|
aGuard.unlock();
|
|
m_xCell->setValue( nValue );
|
|
aGuard.lock();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TypeClass_LONG:
|
|
{
|
|
OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
|
|
|
|
sal_Int32 nValue = 0;
|
|
aValue >>= nValue; // list index from control layer (0-based)
|
|
++nValue; // the list position value in the cell is 1-based
|
|
if ( m_xCell.is() )
|
|
{
|
|
// might call back into us via modified(EventObject&)
|
|
aGuard.unlock();
|
|
m_xCell->setValue( nValue );
|
|
aGuard.lock();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TypeClass_VOID:
|
|
{
|
|
// #N/A error value can only be set using XCellRangeData
|
|
|
|
Reference<XCellRangeData> xData( m_xCell, UNO_QUERY );
|
|
OSL_ENSURE( xData.is(), "OCellValueBinding::setValue: don't have XCellRangeData!" );
|
|
if ( xData.is() )
|
|
{
|
|
Sequence<Any> aInner(1); // one empty element
|
|
Sequence< Sequence<Any> > aOuter( &aInner, 1 ); // one row
|
|
// might call back into us via modified(EventObject&)
|
|
aGuard.unlock();
|
|
xData->setDataArray( aOuter );
|
|
aGuard.lock();
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
OSL_FAIL( "OCellValueBinding::setValue: unreachable code!" );
|
|
// a type other than double and string should never have survived the checkValueType
|
|
// above
|
|
}
|
|
}
|
|
|
|
void OCellValueBinding::setBooleanFormat()
|
|
{
|
|
// set boolean number format if not already set
|
|
|
|
OUString sPropName( u"NumberFormat"_ustr );
|
|
Reference<XPropertySet> xCellProp( m_xCell, UNO_QUERY );
|
|
Reference<XNumberFormatsSupplier> xSupplier( m_xDocument, UNO_QUERY );
|
|
if ( !(xSupplier.is() && xCellProp.is()) )
|
|
return;
|
|
|
|
Reference<XNumberFormats> xFormats(xSupplier->getNumberFormats());
|
|
Reference<XNumberFormatTypes> xTypes( xFormats, UNO_QUERY );
|
|
if ( !xTypes.is() )
|
|
return;
|
|
|
|
lang::Locale aLocale;
|
|
bool bWasBoolean = false;
|
|
|
|
sal_Int32 nOldIndex = ::comphelper::getINT32( xCellProp->getPropertyValue( sPropName ) );
|
|
Reference<XPropertySet> xOldFormat;
|
|
try
|
|
{
|
|
xOldFormat.set(xFormats->getByKey( nOldIndex ));
|
|
}
|
|
catch ( Exception& )
|
|
{
|
|
// non-existing format - can happen, use defaults
|
|
}
|
|
if ( xOldFormat.is() )
|
|
{
|
|
// use the locale of the existing format
|
|
xOldFormat->getPropertyValue(u"Locale"_ustr) >>= aLocale;
|
|
|
|
sal_Int16 nOldType = ::comphelper::getINT16(
|
|
xOldFormat->getPropertyValue(u"Type"_ustr) );
|
|
if ( nOldType & NumberFormat::LOGICAL )
|
|
bWasBoolean = true;
|
|
}
|
|
|
|
if ( !bWasBoolean )
|
|
{
|
|
sal_Int32 nNewIndex = xTypes->getStandardFormat( NumberFormat::LOGICAL, aLocale );
|
|
xCellProp->setPropertyValue( sPropName, Any( nNewIndex ) );
|
|
}
|
|
}
|
|
|
|
void OCellValueBinding::checkInitialized()
|
|
{
|
|
if ( !m_bInitialized )
|
|
throw NotInitializedException(u"CellValueBinding is not initialized"_ustr, getXWeak());
|
|
}
|
|
|
|
void OCellValueBinding::checkValueType( std::unique_lock<std::mutex>& rGuard, const Type& _rType ) const
|
|
{
|
|
if ( !supportsType( rGuard, _rType ) )
|
|
{
|
|
OUString sMessage = "The given type (" +
|
|
_rType.getTypeName() +
|
|
") is not supported by this binding.";
|
|
// TODO: localize this error message
|
|
|
|
throw IncompatibleTypesException( sMessage, const_cast<OCellValueBinding&>(*this) );
|
|
// TODO: alternatively use a type converter service for this?
|
|
}
|
|
}
|
|
|
|
OUString SAL_CALL OCellValueBinding::getImplementationName( )
|
|
{
|
|
return u"com.sun.star.comp.sheet.OCellValueBinding"_ustr;
|
|
}
|
|
|
|
sal_Bool SAL_CALL OCellValueBinding::supportsService( const OUString& _rServiceName )
|
|
{
|
|
return cppu::supportsService(this, _rServiceName);
|
|
}
|
|
|
|
Sequence< OUString > SAL_CALL OCellValueBinding::getSupportedServiceNames( )
|
|
{
|
|
Sequence< OUString > aServices( m_bListPos ? 3 : 2 );
|
|
auto pServices = aServices.getArray();
|
|
pServices[ 0 ] = "com.sun.star.table.CellValueBinding";
|
|
pServices[ 1 ] = "com.sun.star.form.binding.ValueBinding";
|
|
if ( m_bListPos )
|
|
pServices[ 2 ] = "com.sun.star.table.ListPositionCellBinding";
|
|
return aServices;
|
|
}
|
|
|
|
void SAL_CALL OCellValueBinding::addModifyListener( const Reference< XModifyListener >& _rxListener )
|
|
{
|
|
if ( _rxListener.is() )
|
|
{
|
|
std::unique_lock<std::mutex> aGuard(m_aMutex);
|
|
m_aModifyListeners.addInterface( aGuard, _rxListener );
|
|
}
|
|
}
|
|
|
|
void SAL_CALL OCellValueBinding::removeModifyListener( const Reference< XModifyListener >& _rxListener )
|
|
{
|
|
if ( _rxListener.is() )
|
|
{
|
|
std::unique_lock<std::mutex> aGuard(m_aMutex);
|
|
m_aModifyListeners.removeInterface( aGuard, _rxListener );
|
|
}
|
|
}
|
|
|
|
void OCellValueBinding::notifyModified()
|
|
{
|
|
EventObject aEvent;
|
|
aEvent.Source.set(*this);
|
|
|
|
std::unique_lock<std::mutex> aGuard(m_aMutex);
|
|
m_aModifyListeners.forEach(aGuard,
|
|
[&aEvent] (const css::uno::Reference<css::util::XModifyListener> & l)
|
|
{
|
|
try
|
|
{
|
|
l->modified( aEvent );
|
|
}
|
|
catch( const RuntimeException& )
|
|
{
|
|
// silent this
|
|
}
|
|
catch( const Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION( "sc", "OCellValueBinding::notifyModified: caught a (non-runtime) exception!" );
|
|
}
|
|
});
|
|
}
|
|
|
|
void SAL_CALL OCellValueBinding::modified( const EventObject& /* aEvent */ )
|
|
{
|
|
notifyModified();
|
|
}
|
|
|
|
void SAL_CALL OCellValueBinding::disposing( const EventObject& aEvent )
|
|
{
|
|
Reference<XInterface> xCellInt( m_xCell, UNO_QUERY );
|
|
if ( xCellInt == aEvent.Source )
|
|
{
|
|
// release references to cell object
|
|
m_xCell.clear();
|
|
m_xCellText.clear();
|
|
}
|
|
}
|
|
|
|
void SAL_CALL OCellValueBinding::initialize( const Sequence< Any >& _rArguments )
|
|
{
|
|
if ( m_bInitialized )
|
|
throw RuntimeException(u"CellValueBinding is already initialized"_ustr, getXWeak());
|
|
|
|
// get the cell address
|
|
CellAddress aAddress;
|
|
bool bFoundAddress = false;
|
|
|
|
for ( const Any& rArg : _rArguments )
|
|
{
|
|
NamedValue aValue;
|
|
if ( rArg >>= aValue )
|
|
{
|
|
if ( aValue.Name == "BoundCell" )
|
|
{
|
|
if ( aValue.Value >>= aAddress )
|
|
{
|
|
bFoundAddress = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !bFoundAddress )
|
|
throw RuntimeException(u"Cell not found"_ustr, getXWeak());
|
|
|
|
// get the cell object
|
|
try
|
|
{
|
|
// first the sheets collection
|
|
Reference< XIndexAccess > xSheets;
|
|
if ( m_xDocument.is() )
|
|
xSheets.set(m_xDocument->getSheets( ), css::uno::UNO_QUERY);
|
|
OSL_ENSURE( xSheets.is(), "OCellValueBinding::initialize: could not retrieve the sheets!" );
|
|
|
|
if ( xSheets.is() )
|
|
{
|
|
// the concrete sheet
|
|
Reference< XCellRange > xSheet(xSheets->getByIndex( aAddress.Sheet ), UNO_QUERY);
|
|
OSL_ENSURE( xSheet.is(), "OCellValueBinding::initialize: NULL sheet, but no exception!" );
|
|
|
|
// the concrete cell
|
|
if ( xSheet.is() )
|
|
{
|
|
m_xCell.set(xSheet->getCellByPosition( aAddress.Column, aAddress.Row ));
|
|
Reference< XCellAddressable > xAddressAccess( m_xCell, UNO_QUERY );
|
|
OSL_ENSURE( xAddressAccess.is(), "OCellValueBinding::initialize: either NULL cell, or cell without address access!" );
|
|
}
|
|
}
|
|
}
|
|
catch( const Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION( "sc", "OCellValueBinding::initialize: caught an exception while retrieving the cell object!" );
|
|
}
|
|
|
|
if ( !m_xCell.is() )
|
|
throw RuntimeException(u"Failed to retrieve cell object"_ustr, getXWeak());
|
|
|
|
m_xCellText.set(m_xCell, css::uno::UNO_QUERY);
|
|
|
|
Reference<XModifyBroadcaster> xBroadcaster( m_xCell, UNO_QUERY );
|
|
if ( xBroadcaster.is() )
|
|
{
|
|
xBroadcaster->addModifyListener( this );
|
|
}
|
|
|
|
// TODO: add as XEventListener to the cell, so we get notified when it dies,
|
|
// and can dispose ourself then
|
|
|
|
// TODO: somehow add as listener so we get notified when the address of the cell changes
|
|
// We need to forward this as change in our BoundCell property to our property change listeners
|
|
|
|
// TODO: be an XModifyBroadcaster, so that changes in our cell can be notified
|
|
// to the BindableValue which is/will be bound to this instance.
|
|
|
|
m_bInitialized = true;
|
|
// TODO: place your code here
|
|
}
|
|
|
|
} // namespace calc
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|