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 "celllistsource.hxx"
21 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
22 #include <com/sun/star/lang/NotInitializedException.hpp>
23 #include <com/sun/star/lang/NullPointerException.hpp>
24 #include <com/sun/star/table/XCellRange.hpp>
25 #include <com/sun/star/text/XTextRange.hpp>
26 #include <com/sun/star/sheet/XCellRangeAddressable.hpp>
27 #include <com/sun/star/sheet/FormulaResult.hpp>
28 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
29 #include <com/sun/star/util/XModifyBroadcaster.hpp>
30 #include <com/sun/star/container/XIndexAccess.hpp>
31 #include <com/sun/star/beans/PropertyAttribute.hpp>
32 #include <com/sun/star/beans/NamedValue.hpp>
33 #include <cppuhelper/supportsservice.hxx>
34 #include <comphelper/diagnose_ex.hxx>
39 #define PROP_HANDLE_RANGE_ADDRESS 1
41 using namespace ::com::sun::star::uno
;
42 using namespace ::com::sun::star::lang
;
43 using namespace ::com::sun::star::table
;
44 using namespace ::com::sun::star::text
;
45 using namespace ::com::sun::star::sheet
;
46 using namespace ::com::sun::star::container
;
47 using namespace ::com::sun::star::beans
;
48 using namespace ::com::sun::star::util
;
49 using namespace ::com::sun::star::form::binding
;
51 OCellListSource::OCellListSource( const Reference
< XSpreadsheetDocument
>& _rxDocument
)
52 :m_xDocument( _rxDocument
)
53 ,m_bInitialized( false )
55 OSL_PRECOND( m_xDocument
.is(), "OCellListSource::OCellListSource: invalid document!" );
57 // register our property at the base class
58 registerPropertyNoMember(
60 PROP_HANDLE_RANGE_ADDRESS
,
61 PropertyAttribute::BOUND
| PropertyAttribute::READONLY
,
62 cppu::UnoType
<CellRangeAddress
>::get(),
63 css::uno::Any(CellRangeAddress())
67 OCellListSource::~OCellListSource( )
71 acquire(); // prevent duplicate dtor
76 IMPLEMENT_FORWARD_XINTERFACE2( OCellListSource
, OCellListSource_Base
, OCellListSource_PBase
)
78 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCellListSource
, OCellListSource_Base
, OCellListSource_PBase
)
80 void OCellListSource::disposing(std::unique_lock
<std::mutex
>& rGuard
)
82 Reference
<XModifyBroadcaster
> xBroadcaster( m_xRange
, UNO_QUERY
);
83 if ( xBroadcaster
.is() )
85 xBroadcaster
->removeModifyListener( this );
88 EventObject
aDisposeEvent( *this );
89 m_aListEntryListeners
.disposeAndClear( rGuard
, aDisposeEvent
);
91 WeakComponentImplHelperBase::disposing(rGuard
);
93 // TODO: clean up here whatever you need to clean up (e.g. revoking listeners etc.)
96 Reference
< XPropertySetInfo
> SAL_CALL
OCellListSource::getPropertySetInfo( )
98 return createPropertySetInfo( getInfoHelper() ) ;
101 ::cppu::IPropertyArrayHelper
& OCellListSource::getInfoHelper()
103 return *OCellListSource_PABase::getArrayHelper();
106 ::cppu::IPropertyArrayHelper
* OCellListSource::createArrayHelper( ) const
108 Sequence
< Property
> aProps
;
109 describeProperties( aProps
);
110 return new ::cppu::OPropertyArrayHelper(aProps
);
113 void OCellListSource::getFastPropertyValue( std::unique_lock
<std::mutex
>& /*rGuard*/, Any
& _rValue
, sal_Int32 _nHandle
) const
115 OSL_ENSURE( _nHandle
== PROP_HANDLE_RANGE_ADDRESS
, "OCellListSource::getFastPropertyValue: invalid handle!" );
116 // we only have this one property...
118 _rValue
<<= getRangeAddress( );
121 void OCellListSource::checkInitialized()
123 if ( !m_bInitialized
)
124 throw NotInitializedException(u
"CellListSource is not initialized"_ustr
, getXWeak());
127 OUString SAL_CALL
OCellListSource::getImplementationName( )
129 return u
"com.sun.star.comp.sheet.OCellListSource"_ustr
;
132 sal_Bool SAL_CALL
OCellListSource::supportsService( const OUString
& _rServiceName
)
134 return cppu::supportsService(this, _rServiceName
);
137 Sequence
< OUString
> SAL_CALL
OCellListSource::getSupportedServiceNames( )
139 return {u
"com.sun.star.table.CellRangeListSource"_ustr
,
140 u
"com.sun.star.form.binding.ListEntrySource"_ustr
};
143 CellRangeAddress
OCellListSource::getRangeAddress( ) const
145 OSL_PRECOND( m_xRange
.is(), "OCellListSource::getRangeAddress: invalid range!" );
147 CellRangeAddress aAddress
;
148 Reference
< XCellRangeAddressable
> xRangeAddress( m_xRange
, UNO_QUERY
);
149 if ( xRangeAddress
.is() )
150 aAddress
= xRangeAddress
->getRangeAddress( );
154 OUString
OCellListSource::getCellTextContent_noCheck( std::unique_lock
<std::mutex
>& /*rGuard*/, sal_Int32 _nRangeRelativeRow
, css::uno::Any
* pAny
)
158 OSL_PRECOND( m_xRange
.is(), "OCellListSource::getRangeAddress: invalid range!" );
163 Reference
< XCell
> xCell( m_xRange
->getCellByPosition( 0, _nRangeRelativeRow
));
171 Reference
< XTextRange
> xCellText
;
172 xCellText
.set( xCell
, UNO_QUERY
);
175 sText
= xCellText
->getString(); // formatted output string
179 switch (xCell
->getType())
181 case CellContentType_VALUE
:
182 *pAny
<<= xCell
->getValue();
184 case CellContentType_TEXT
:
187 case CellContentType_FORMULA
:
188 if (xCell
->getError())
189 *pAny
<<= sText
; // Err:... or #...!
192 Reference
< XPropertySet
> xProp( xCell
, UNO_QUERY
);
195 sal_Int32 nResultType
;
196 if ((xProp
->getPropertyValue(u
"FormulaResultType2"_ustr
) >>= nResultType
) &&
197 nResultType
== FormulaResult::VALUE
)
198 *pAny
<<= xCell
->getValue();
204 case CellContentType_EMPTY
:
205 *pAny
<<= OUString();
208 ; // nothing, if actually occurred it would result in #N/A being displayed if selected
215 sal_Int32 SAL_CALL
OCellListSource::getListEntryCount( )
217 std::unique_lock
<std::mutex
> aGuard( m_aMutex
);
218 throwIfDisposed(aGuard
);
220 return getListEntryCount(aGuard
);
223 sal_Int32
OCellListSource::getListEntryCount( std::unique_lock
<std::mutex
>& /*rGuard*/ )
225 CellRangeAddress
aAddress( getRangeAddress( ) );
226 return aAddress
.EndRow
- aAddress
.StartRow
+ 1;
229 OUString SAL_CALL
OCellListSource::getListEntry( sal_Int32 _nPosition
)
231 std::unique_lock
<std::mutex
> aGuard( m_aMutex
);
232 throwIfDisposed(aGuard
);
235 if ( _nPosition
>= getListEntryCount() )
236 throw IndexOutOfBoundsException();
238 return getCellTextContent_noCheck( aGuard
, _nPosition
, nullptr );
241 Sequence
< OUString
> SAL_CALL
OCellListSource::getAllListEntries( )
243 std::unique_lock
<std::mutex
> aGuard( m_aMutex
);
244 throwIfDisposed(aGuard
);
247 Sequence
< OUString
> aAllEntries( getListEntryCount(aGuard
) );
248 OUString
* pAllEntries
= aAllEntries
.getArray();
249 for ( sal_Int32 i
= 0; i
< aAllEntries
.getLength(); ++i
)
251 *pAllEntries
++ = getCellTextContent_noCheck( aGuard
, i
, nullptr );
257 Sequence
< OUString
> SAL_CALL
OCellListSource::getAllListEntriesTyped( Sequence
< Any
>& rDataValues
)
259 std::unique_lock
<std::mutex
> aGuard( m_aMutex
);
260 throwIfDisposed(aGuard
);
263 const sal_Int32 nCount
= getListEntryCount(aGuard
);
264 Sequence
< OUString
> aAllEntries( nCount
);
265 rDataValues
= Sequence
< Any
>( nCount
);
266 OUString
* pAllEntries
= aAllEntries
.getArray();
267 Any
* pDataValues
= rDataValues
.getArray();
268 for ( sal_Int32 i
= 0; i
< nCount
; ++i
)
270 *pAllEntries
++ = getCellTextContent_noCheck( aGuard
, i
, pDataValues
++ );
276 void SAL_CALL
OCellListSource::addListEntryListener( const Reference
< XListEntryListener
>& _rxListener
)
278 std::unique_lock
<std::mutex
> aGuard( m_aMutex
);
279 throwIfDisposed(aGuard
);
282 if ( !_rxListener
.is() )
283 throw NullPointerException();
285 m_aListEntryListeners
.addInterface( aGuard
, _rxListener
);
288 void SAL_CALL
OCellListSource::removeListEntryListener( const Reference
< XListEntryListener
>& _rxListener
)
290 std::unique_lock
<std::mutex
> aGuard( m_aMutex
);
291 throwIfDisposed(aGuard
);
294 if ( !_rxListener
.is() )
295 throw NullPointerException();
297 m_aListEntryListeners
.removeInterface( aGuard
, _rxListener
);
300 void SAL_CALL
OCellListSource::modified( const EventObject
& /* aEvent */ )
305 void OCellListSource::notifyModified()
307 std::unique_lock
<std::mutex
> aGuard( m_aMutex
);
309 aEvent
.Source
.set(*this);
311 m_aListEntryListeners
.forEach(aGuard
,
312 [&aEvent
] (const css::uno::Reference
<css::form::binding::XListEntryListener
>& l
)
316 l
->allEntriesChanged( aEvent
);
318 catch( const RuntimeException
& )
322 catch( const Exception
& )
324 TOOLS_WARN_EXCEPTION( "sc", "OCellListSource::notifyModified: caught a (non-runtime) exception!" );
329 void SAL_CALL
OCellListSource::disposing( const EventObject
& aEvent
)
331 Reference
<XInterface
> xRangeInt( m_xRange
, UNO_QUERY
);
332 if ( xRangeInt
== aEvent
.Source
)
334 // release references to range object
339 void SAL_CALL
OCellListSource::initialize( const Sequence
< Any
>& _rArguments
)
341 if ( m_bInitialized
)
342 throw RuntimeException(u
"CellListSource is already initialized"_ustr
, getXWeak());
344 // get the cell address
345 CellRangeAddress aRangeAddress
;
346 bool bFoundAddress
= false;
348 for ( const Any
& rArg
: _rArguments
)
351 if ( rArg
>>= aValue
)
353 if ( aValue
.Name
== "CellRange" )
355 if ( aValue
.Value
>>= aRangeAddress
)
357 bFoundAddress
= true;
364 if ( !bFoundAddress
)
365 throw RuntimeException(u
"Cell not found"_ustr
, getXWeak());
367 // determine the range we're bound to
370 if ( m_xDocument
.is() )
372 // first the sheets collection
373 Reference
< XIndexAccess
> xSheets(m_xDocument
->getSheets( ), UNO_QUERY
);
374 OSL_ENSURE( xSheets
.is(), "OCellListSource::initialize: could not retrieve the sheets!" );
378 // the concrete sheet
379 Reference
< XCellRange
> xSheet(xSheets
->getByIndex( aRangeAddress
.Sheet
), UNO_QUERY
);
380 OSL_ENSURE( xSheet
.is(), "OCellListSource::initialize: NULL sheet, but no exception!" );
385 m_xRange
.set(xSheet
->getCellRangeByPosition(
386 aRangeAddress
.StartColumn
, aRangeAddress
.StartRow
,
387 aRangeAddress
.EndColumn
, aRangeAddress
.EndRow
));
388 OSL_ENSURE( Reference
< XCellRangeAddressable
>( m_xRange
, UNO_QUERY
).is(), "OCellListSource::initialize: either NULL range, or cell without address access!" );
393 catch( const Exception
& )
395 TOOLS_WARN_EXCEPTION( "sc", "OCellListSource::initialize: caught an exception while retrieving the cell object!" );
398 if ( !m_xRange
.is() )
399 throw RuntimeException(u
"Failed to retrieve cell range"_ustr
, getXWeak());
401 Reference
<XModifyBroadcaster
> xBroadcaster( m_xRange
, UNO_QUERY
);
402 if ( xBroadcaster
.is() )
404 xBroadcaster
->addModifyListener( this );
407 // TODO: add as XEventListener to the cell range, so we get notified when it dies,
408 // and can dispose ourself then
410 // TODO: somehow add as listener so we get notified when the address of the cell range changes
411 // We need to forward this as change in our CellRange property to our property change listeners
413 // TODO: somehow add as listener to the cells in the range, so that we get notified
414 // when their content changes. We need to forward this to our list entry listeners then
416 // TODO: somehow add as listener so that we get notified of insertions and removals of rows in our
417 // range. In this case, we need to fire a change in our CellRange property, and additionally
418 // notify our XListEntryListeners
420 m_bInitialized
= true;
425 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */