LibreOffice Module sc (master) 1
celllistsource.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
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>
35
36namespace calc
37{
38
39#define PROP_HANDLE_RANGE_ADDRESS 1
40
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;
50
51 OCellListSource::OCellListSource( const Reference< XSpreadsheetDocument >& _rxDocument )
54 ,m_xDocument( _rxDocument )
55 ,m_aListEntryListeners( m_aMutex )
56 ,m_bInitialized( false )
57 {
58 OSL_PRECOND( m_xDocument.is(), "OCellListSource::OCellListSource: invalid document!" );
59
60 // register our property at the base class
61 registerPropertyNoMember(
62 "CellRange",
64 PropertyAttribute::BOUND | PropertyAttribute::READONLY,
66 css::uno::Any(CellRangeAddress())
67 );
68 }
69
70 OCellListSource::~OCellListSource( )
71 {
72 if ( !OCellListSource_Base::rBHelper.bDisposed )
73 {
74 acquire(); // prevent duplicate dtor
75 dispose();
76 }
77 }
78
80
82
83 void SAL_CALL OCellListSource::disposing()
84 {
85 ::osl::MutexGuard aGuard( m_aMutex );
86
87 Reference<XModifyBroadcaster> xBroadcaster( m_xRange, UNO_QUERY );
88 if ( xBroadcaster.is() )
89 {
90 xBroadcaster->removeModifyListener( this );
91 }
92
93 EventObject aDisposeEvent( *this );
94 m_aListEntryListeners.disposeAndClear( aDisposeEvent );
95
96 WeakComponentImplHelperBase::disposing();
97
98 // TODO: clean up here whatever you need to clean up (e.g. revoking listeners etc.)
99 }
100
101 Reference< XPropertySetInfo > SAL_CALL OCellListSource::getPropertySetInfo( )
102 {
103 return createPropertySetInfo( getInfoHelper() ) ;
104 }
105
106 ::cppu::IPropertyArrayHelper& SAL_CALL OCellListSource::getInfoHelper()
107 {
108 return *OCellListSource_PABase::getArrayHelper();
109 }
110
111 ::cppu::IPropertyArrayHelper* OCellListSource::createArrayHelper( ) const
112 {
113 Sequence< Property > aProps;
114 describeProperties( aProps );
115 return new ::cppu::OPropertyArrayHelper(aProps);
116 }
117
118 void SAL_CALL OCellListSource::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
119 {
120 OSL_ENSURE( _nHandle == PROP_HANDLE_RANGE_ADDRESS, "OCellListSource::getFastPropertyValue: invalid handle!" );
121 // we only have this one property...
122
123 _rValue <<= getRangeAddress( );
124 }
125
126 void OCellListSource::checkDisposed( ) const
127 {
128 if ( OCellListSource_Base::rBHelper.bInDispose || OCellListSource_Base::rBHelper.bDisposed )
129 throw DisposedException();
130 // TODO: is it worth having an error message here?
131 }
132
133 void OCellListSource::checkInitialized()
134 {
135 if ( !m_bInitialized )
136 throw NotInitializedException("CellListSource is not initialized", getXWeak());
137 }
138
139 OUString SAL_CALL OCellListSource::getImplementationName( )
140 {
141 return "com.sun.star.comp.sheet.OCellListSource";
142 }
143
144 sal_Bool SAL_CALL OCellListSource::supportsService( const OUString& _rServiceName )
145 {
146 return cppu::supportsService(this, _rServiceName);
147 }
148
149 Sequence< OUString > SAL_CALL OCellListSource::getSupportedServiceNames( )
150 {
151 return {"com.sun.star.table.CellRangeListSource",
152 "com.sun.star.form.binding.ListEntrySource"};
153 }
154
155 CellRangeAddress OCellListSource::getRangeAddress( ) const
156 {
157 OSL_PRECOND( m_xRange.is(), "OCellListSource::getRangeAddress: invalid range!" );
158
159 CellRangeAddress aAddress;
160 Reference< XCellRangeAddressable > xRangeAddress( m_xRange, UNO_QUERY );
161 if ( xRangeAddress.is() )
162 aAddress = xRangeAddress->getRangeAddress( );
163 return aAddress;
164 }
165
166 OUString OCellListSource::getCellTextContent_noCheck( sal_Int32 _nRangeRelativeRow, css::uno::Any* pAny )
167 {
168 OUString sText;
169
170 OSL_PRECOND( m_xRange.is(), "OCellListSource::getRangeAddress: invalid range!" );
171
172 if (!m_xRange.is())
173 return sText;
174
175 Reference< XCell > xCell( m_xRange->getCellByPosition( 0, _nRangeRelativeRow ));
176 if (!xCell.is())
177 {
178 if (pAny)
179 *pAny <<= sText;
180 return sText;
181 }
182
183 Reference< XTextRange > xCellText;
184 xCellText.set( xCell, UNO_QUERY);
185
186 if (xCellText.is())
187 sText = xCellText->getString(); // formatted output string
188
189 if (pAny)
190 {
191 switch (xCell->getType())
192 {
193 case CellContentType_VALUE:
194 *pAny <<= xCell->getValue();
195 break;
196 case CellContentType_TEXT:
197 *pAny <<= sText;
198 break;
199 case CellContentType_FORMULA:
200 if (xCell->getError())
201 *pAny <<= sText; // Err:... or #...!
202 else
203 {
204 Reference< XPropertySet > xProp( xCell, UNO_QUERY);
205 if (xProp.is())
206 {
207 sal_Int32 nResultType;
208 if ((xProp->getPropertyValue("FormulaResultType2") >>= nResultType) &&
209 nResultType == FormulaResult::VALUE)
210 *pAny <<= xCell->getValue();
211 else
212 *pAny <<= sText;
213 }
214 }
215 break;
216 case CellContentType_EMPTY:
217 *pAny <<= OUString();
218 break;
219 default:
220 ; // nothing, if actually occurred it would result in #N/A being displayed if selected
221 }
222 }
223
224 return sText;
225 }
226
227 sal_Int32 SAL_CALL OCellListSource::getListEntryCount( )
228 {
229 ::osl::MutexGuard aGuard( m_aMutex );
231 checkInitialized();
232
233 CellRangeAddress aAddress( getRangeAddress( ) );
234 return aAddress.EndRow - aAddress.StartRow + 1;
235 }
236
237 OUString SAL_CALL OCellListSource::getListEntry( sal_Int32 _nPosition )
238 {
239 ::osl::MutexGuard aGuard( m_aMutex );
241 checkInitialized();
242
243 if ( _nPosition >= getListEntryCount() )
244 throw IndexOutOfBoundsException();
245
246 return getCellTextContent_noCheck( _nPosition, nullptr );
247 }
248
249 Sequence< OUString > SAL_CALL OCellListSource::getAllListEntries( )
250 {
251 ::osl::MutexGuard aGuard( m_aMutex );
253 checkInitialized();
254
255 Sequence< OUString > aAllEntries( getListEntryCount() );
256 OUString* pAllEntries = aAllEntries.getArray();
257 for ( sal_Int32 i = 0; i < aAllEntries.getLength(); ++i )
258 {
259 *pAllEntries++ = getCellTextContent_noCheck( i, nullptr );
260 }
261
262 return aAllEntries;
263 }
264
265 Sequence< OUString > SAL_CALL OCellListSource::getAllListEntriesTyped( Sequence< Any >& rDataValues )
266 {
267 ::osl::MutexGuard aGuard( m_aMutex );
269 checkInitialized();
270
271 const sal_Int32 nCount = getListEntryCount();
272 Sequence< OUString > aAllEntries( nCount );
273 rDataValues = Sequence< Any >( nCount );
274 OUString* pAllEntries = aAllEntries.getArray();
275 Any* pDataValues = rDataValues.getArray();
276 for ( sal_Int32 i = 0; i < nCount; ++i )
277 {
278 *pAllEntries++ = getCellTextContent_noCheck( i, pDataValues++ );
279 }
280
281 return aAllEntries;
282 }
283
284 void SAL_CALL OCellListSource::addListEntryListener( const Reference< XListEntryListener >& _rxListener )
285 {
286 ::osl::MutexGuard aGuard( m_aMutex );
288 checkInitialized();
289
290 if ( !_rxListener.is() )
291 throw NullPointerException();
292
293 m_aListEntryListeners.addInterface( _rxListener );
294 }
295
296 void SAL_CALL OCellListSource::removeListEntryListener( const Reference< XListEntryListener >& _rxListener )
297 {
298 ::osl::MutexGuard aGuard( m_aMutex );
300 checkInitialized();
301
302 if ( !_rxListener.is() )
303 throw NullPointerException();
304
305 m_aListEntryListeners.removeInterface( _rxListener );
306 }
307
308 void SAL_CALL OCellListSource::modified( const EventObject& /* aEvent */ )
309 {
310 notifyModified();
311 }
312
313 void OCellListSource::notifyModified()
314 {
315 EventObject aEvent;
316 aEvent.Source.set(*this);
317
318 ::comphelper::OInterfaceIteratorHelper3 aIter( m_aListEntryListeners );
319 while ( aIter.hasMoreElements() )
320 {
321 try
322 {
323 aIter.next()->allEntriesChanged( aEvent );
324 }
325 catch( const RuntimeException& )
326 {
327 // silent this
328 }
329 catch( const Exception& )
330 {
331 TOOLS_WARN_EXCEPTION( "sc", "OCellListSource::notifyModified: caught a (non-runtime) exception!" );
332 }
333 }
334
335 }
336
337 void SAL_CALL OCellListSource::disposing( const EventObject& aEvent )
338 {
339 Reference<XInterface> xRangeInt( m_xRange, UNO_QUERY );
340 if ( xRangeInt == aEvent.Source )
341 {
342 // release references to range object
343 m_xRange.clear();
344 }
345 }
346
347 void SAL_CALL OCellListSource::initialize( const Sequence< Any >& _rArguments )
348 {
349 if ( m_bInitialized )
350 throw RuntimeException("CellListSource is already initialized", getXWeak());
351
352 // get the cell address
353 CellRangeAddress aRangeAddress;
354 bool bFoundAddress = false;
355
356 for ( const Any& rArg : _rArguments )
357 {
358 NamedValue aValue;
359 if ( rArg >>= aValue )
360 {
361 if ( aValue.Name == "CellRange" )
362 {
363 if ( aValue.Value >>= aRangeAddress )
364 {
365 bFoundAddress = true;
366 break;
367 }
368 }
369 }
370 }
371
372 if ( !bFoundAddress )
373 throw RuntimeException("Cell not found", getXWeak());
374
375 // determine the range we're bound to
376 try
377 {
378 if ( m_xDocument.is() )
379 {
380 // first the sheets collection
381 Reference< XIndexAccess > xSheets(m_xDocument->getSheets( ), UNO_QUERY);
382 OSL_ENSURE( xSheets.is(), "OCellListSource::initialize: could not retrieve the sheets!" );
383
384 if ( xSheets.is() )
385 {
386 // the concrete sheet
387 Reference< XCellRange > xSheet(xSheets->getByIndex( aRangeAddress.Sheet ), UNO_QUERY);
388 OSL_ENSURE( xSheet.is(), "OCellListSource::initialize: NULL sheet, but no exception!" );
389
390 // the concrete cell
391 if ( xSheet.is() )
392 {
393 m_xRange.set(xSheet->getCellRangeByPosition(
394 aRangeAddress.StartColumn, aRangeAddress.StartRow,
395 aRangeAddress.EndColumn, aRangeAddress.EndRow));
396 OSL_ENSURE( Reference< XCellRangeAddressable >( m_xRange, UNO_QUERY ).is(), "OCellListSource::initialize: either NULL range, or cell without address access!" );
397 }
398 }
399 }
400 }
401 catch( const Exception& )
402 {
403 TOOLS_WARN_EXCEPTION( "sc", "OCellListSource::initialize: caught an exception while retrieving the cell object!" );
404 }
405
406 if ( !m_xRange.is() )
407 throw RuntimeException("Failed to retrieve cell range", getXWeak());
408
409 Reference<XModifyBroadcaster> xBroadcaster( m_xRange, UNO_QUERY );
410 if ( xBroadcaster.is() )
411 {
412 xBroadcaster->addModifyListener( this );
413 }
414
415 // TODO: add as XEventListener to the cell range, so we get notified when it dies,
416 // and can dispose ourself then
417
418 // TODO: somehow add as listener so we get notified when the address of the cell range changes
419 // We need to forward this as change in our CellRange property to our property change listeners
420
421 // TODO: somehow add as listener to the cells in the range, so that we get notified
422 // when their content changes. We need to forward this to our list entry listeners then
423
424 // TODO: somehow add as listener so that we get notified of insertions and removals of rows in our
425 // range. In this case, we need to fire a change in our CellRange property, and additionally
426 // notify our XListEntryListeners
427
428 m_bInitialized = true;
429 }
430
431} // namespace calc
432
433/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
AnyEventRef aEvent
#define PROP_HANDLE_RANGE_ADDRESS
OCellListSource(const css::uno::Reference< css::sheet::XSpreadsheetDocument > &_rxDocument)
has XInitialization::initialize been called?
css::uno::Reference< ListenerT > const & next()
int nCount
Reference< XOfficeDatabaseDocument > m_xDocument
#define TOOLS_WARN_EXCEPTION(area, stream)
std::mutex m_aMutex
::comphelper::OPropertyContainer OCellListSource_PBase
::cppu::WeakComponentImplHelper< css::form::binding::XListEntryTypedSource, css::util::XModifyListener, css::lang::XServiceInfo, css::lang::XInitialization > OCellListSource_Base
@ Exception
void checkDisposed(bool _bThrow)
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
int i
IMPLEMENT_FORWARD_XTYPEPROVIDER2(ChildWindowPane, ChildWindowPaneInterfaceBase, Pane)
IMPLEMENT_FORWARD_XINTERFACE2(ChildWindowPane, ChildWindowPaneInterfaceBase, Pane)
void dispose()
unsigned char sal_Bool