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> 33 #include <cppuhelper/supportsservice.hxx> 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 OCellListSource(const Reference<XSpreadsheetDocument> & _rxDocument)51 OCellListSource::OCellListSource( const Reference< XSpreadsheetDocument >& _rxDocument ) 52 :OCellListSource_Base( m_aMutex ) 53 ,OCellListSource_PBase( OCellListSource_Base::rBHelper ) 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", 63 PROP_HANDLE_RANGE_ADDRESS, 64 PropertyAttribute::BOUND | PropertyAttribute::READONLY, 65 cppu::UnoType<CellRangeAddress>::get(), 66 css::uno::Any(CellRangeAddress()) 67 ); 68 } 69 ~OCellListSource()70 OCellListSource::~OCellListSource( ) 71 { 72 if ( !OCellListSource_Base::rBHelper.bDisposed ) 73 { 74 acquire(); // prevent duplicate dtor 75 dispose(); 76 } 77 } 78 IMPLEMENT_FORWARD_XINTERFACE2(OCellListSource,OCellListSource_Base,OCellListSource_PBase)79 IMPLEMENT_FORWARD_XINTERFACE2( OCellListSource, OCellListSource_Base, OCellListSource_PBase ) 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 getPropertySetInfo()101 Reference< XPropertySetInfo > SAL_CALL OCellListSource::getPropertySetInfo( ) 102 { 103 return createPropertySetInfo( getInfoHelper() ) ; 104 } 105 getInfoHelper()106 ::cppu::IPropertyArrayHelper& SAL_CALL OCellListSource::getInfoHelper() 107 { 108 return *OCellListSource_PABase::getArrayHelper(); 109 } 110 createArrayHelper() const111 ::cppu::IPropertyArrayHelper* OCellListSource::createArrayHelper( ) const 112 { 113 Sequence< Property > aProps; 114 describeProperties( aProps ); 115 return new ::cppu::OPropertyArrayHelper(aProps); 116 } 117 getFastPropertyValue(Any & _rValue,sal_Int32 _nHandle) const118 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 checkDisposed() const126 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 checkInitialized()133 void OCellListSource::checkInitialized() 134 { 135 if ( !m_bInitialized ) 136 throw NotInitializedException("CellListSource is not initialized", static_cast<cppu::OWeakObject*>(this)); 137 } 138 getImplementationName()139 OUString SAL_CALL OCellListSource::getImplementationName( ) 140 { 141 return "com.sun.star.comp.sheet.OCellListSource"; 142 } 143 supportsService(const OUString & _rServiceName)144 sal_Bool SAL_CALL OCellListSource::supportsService( const OUString& _rServiceName ) 145 { 146 return cppu::supportsService(this, _rServiceName); 147 } 148 getSupportedServiceNames()149 Sequence< OUString > SAL_CALL OCellListSource::getSupportedServiceNames( ) 150 { 151 return {"com.sun.star.table.CellRangeListSource", 152 "com.sun.star.form.binding.ListEntrySource"}; 153 } 154 getRangeAddress() const155 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 getCellTextContent_noCheck(sal_Int32 _nRangeRelativeRow,css::uno::Any * pAny)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 getListEntryCount()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 getListEntry(sal_Int32 _nPosition)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 getAllListEntries()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 getAllListEntriesTyped(Sequence<Any> & rDataValues)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 addListEntryListener(const Reference<XListEntryListener> & _rxListener)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 removeListEntryListener(const Reference<XListEntryListener> & _rxListener)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 modified(const EventObject &)308 void SAL_CALL OCellListSource::modified( const EventObject& /* aEvent */ ) 309 { 310 notifyModified(); 311 } 312 notifyModified()313 void OCellListSource::notifyModified() 314 { 315 EventObject aEvent; 316 aEvent.Source.set(*this); 317 318 ::comphelper::OInterfaceIteratorHelper2 aIter( m_aListEntryListeners ); 319 while ( aIter.hasMoreElements() ) 320 { 321 try 322 { 323 static_cast< XListEntryListener* >( 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 disposing(const EventObject & aEvent)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 initialize(const Sequence<Any> & _rArguments)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: */ 434