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>
34 #include <tools/diagnose_ex.h>
35 
36 namespace 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 
81  IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCellListSource, OCellListSource_Base, OCellListSource_PBase )
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  WeakAggComponentImplHelperBase::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", static_cast<cppu::OWeakObject*>(this));
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 );
230  checkDisposed();
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 );
240  checkDisposed();
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 );
252  checkDisposed();
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 );
268  checkDisposed();
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 );
287  checkDisposed();
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 );
299  checkDisposed();
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", static_cast<cppu::OWeakObject*>(this));
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", static_cast<cppu::OWeakObject*>(this));
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", static_cast<cppu::OWeakObject*>(this));
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: */
IMPLEMENT_FORWARD_XINTERFACE2(ChildWindowPane, ChildWindowPaneInterfaceBase, Pane)
OCellListSource(const css::uno::Reference< css::sheet::XSpreadsheetDocument > &_rxDocument)
has XInitialization::initialize been called?
Reference< XOfficeDatabaseDocument > m_xDocument
::comphelper::OPropertyContainer OCellListSource_PBase
int nCount
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
std::mutex m_aMutex
#define PROP_HANDLE_RANGE_ADDRESS
#define TOOLS_WARN_EXCEPTION(area, stream)
int i
void checkDisposed(bool _bThrow)
css::uno::Reference< ListenerT > const & next()
unsigned char sal_Bool
::cppu::WeakAggComponentImplHelper4< css::form::binding::XListEntryTypedSource, css::util::XModifyListener, css::lang::XServiceInfo, css::lang::XInitialization > OCellListSource_Base
void dispose()
AnyEventRef aEvent
bool m_bDetectedRangeSegmentation false