1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_sc.hxx" 26 #include "celllistsource.hxx" 27 #include <tools/debug.hxx> 28 #include <com/sun/star/text/XTextRange.hpp> 29 #include <com/sun/star/sheet/XCellRangeAddressable.hpp> 30 #include <com/sun/star/util/XModifyBroadcaster.hpp> 31 #include <com/sun/star/container/XIndexAccess.hpp> 32 #include <com/sun/star/beans/PropertyAttribute.hpp> 33 #include <com/sun/star/beans/NamedValue.hpp> 34 35 //......................................................................... 36 namespace calc 37 { 38 //......................................................................... 39 40 #define PROP_HANDLE_RANGE_ADDRESS 1 41 42 using namespace ::com::sun::star::uno; 43 using namespace ::com::sun::star::lang; 44 using namespace ::com::sun::star::table; 45 using namespace ::com::sun::star::text; 46 using namespace ::com::sun::star::sheet; 47 using namespace ::com::sun::star::container; 48 using namespace ::com::sun::star::beans; 49 using namespace ::com::sun::star::util; 50 using namespace ::com::sun::star::form::binding; 51 52 //===================================================================== 53 //= OCellListSource 54 //===================================================================== DBG_NAME(OCellListSource) const55 DBG_NAME( OCellListSource ) 56 //--------------------------------------------------------------------- 57 #ifdef DBG_UTIL 58 const char* OCellListSource::checkConsistency_static( const void* _pThis ) 59 { 60 return static_cast< const OCellListSource* >( _pThis )->checkConsistency( ); 61 } 62 checkConsistency() const63 const char* OCellListSource::checkConsistency( ) const 64 { 65 const char* pAssertion = NULL; 66 67 // TODO: place any checks here to ensure consistency of this instance 68 69 return pAssertion; 70 } 71 #endif 72 73 //--------------------------------------------------------------------- OCellListSource(const Reference<XSpreadsheetDocument> & _rxDocument)74 OCellListSource::OCellListSource( const Reference< XSpreadsheetDocument >& _rxDocument ) 75 :OCellListSource_Base( m_aMutex ) 76 ,OCellListSource_PBase( OCellListSource_Base::rBHelper ) 77 ,m_xDocument( _rxDocument ) 78 ,m_aListEntryListeners( m_aMutex ) 79 ,m_bInitialized( sal_False ) 80 { 81 DBG_CTOR( OCellListSource, checkConsistency_static ); 82 83 OSL_PRECOND( m_xDocument.is(), "OCellListSource::OCellListSource: invalid document!" ); 84 85 // register our property at the base class 86 CellRangeAddress aInitialPropValue; 87 registerPropertyNoMember( 88 ::rtl::OUString::createFromAscii( "CellRange" ), 89 PROP_HANDLE_RANGE_ADDRESS, 90 PropertyAttribute::BOUND | PropertyAttribute::READONLY, 91 ::getCppuType( &aInitialPropValue ), 92 &aInitialPropValue 93 ); 94 } 95 96 //--------------------------------------------------------------------- ~OCellListSource()97 OCellListSource::~OCellListSource( ) 98 { 99 if ( !OCellListSource_Base::rBHelper.bDisposed ) 100 { 101 acquire(); // prevent duplicate dtor 102 dispose(); 103 } 104 105 DBG_DTOR( OCellListSource, checkConsistency_static ); 106 } 107 108 //-------------------------------------------------------------------- IMPLEMENT_FORWARD_XINTERFACE2(OCellListSource,OCellListSource_Base,OCellListSource_PBase)109 IMPLEMENT_FORWARD_XINTERFACE2( OCellListSource, OCellListSource_Base, OCellListSource_PBase ) 110 111 //-------------------------------------------------------------------- 112 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCellListSource, OCellListSource_Base, OCellListSource_PBase ) 113 114 //-------------------------------------------------------------------- 115 void SAL_CALL OCellListSource::disposing() 116 { 117 ::osl::MutexGuard aGuard( m_aMutex ); 118 DBG_CHKTHIS( OCellListSource, checkConsistency_static ); 119 120 Reference<XModifyBroadcaster> xBroadcaster( m_xRange, UNO_QUERY ); 121 if ( xBroadcaster.is() ) 122 { 123 xBroadcaster->removeModifyListener( this ); 124 } 125 126 EventObject aDisposeEvent( *this ); 127 m_aListEntryListeners.disposeAndClear( aDisposeEvent ); 128 129 // OCellListSource_Base::disposing(); 130 WeakAggComponentImplHelperBase::disposing(); 131 132 // TODO: clean up here whatever you need to clean up (e.g. revoking listeners etc.) 133 } 134 135 //-------------------------------------------------------------------- getPropertySetInfo()136 Reference< XPropertySetInfo > SAL_CALL OCellListSource::getPropertySetInfo( ) throw(RuntimeException) 137 { 138 DBG_CHKTHIS( OCellListSource, checkConsistency_static ); 139 return createPropertySetInfo( getInfoHelper() ) ; 140 } 141 142 //-------------------------------------------------------------------- getInfoHelper()143 ::cppu::IPropertyArrayHelper& SAL_CALL OCellListSource::getInfoHelper() 144 { 145 return *OCellListSource_PABase::getArrayHelper(); 146 } 147 148 //-------------------------------------------------------------------- createArrayHelper() const149 ::cppu::IPropertyArrayHelper* OCellListSource::createArrayHelper( ) const 150 { 151 Sequence< Property > aProps; 152 describeProperties( aProps ); 153 return new ::cppu::OPropertyArrayHelper(aProps); 154 } 155 156 //-------------------------------------------------------------------- getFastPropertyValue(Any & _rValue,sal_Int32 _nHandle) const157 void SAL_CALL OCellListSource::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const 158 { 159 DBG_CHKTHIS( OCellListSource, checkConsistency_static ); 160 DBG_ASSERT( _nHandle == PROP_HANDLE_RANGE_ADDRESS, "OCellListSource::getFastPropertyValue: invalid handle!" ); 161 // we only have this one property .... 162 (void)_nHandle; // avoid warning in product version 163 164 _rValue <<= getRangeAddress( ); 165 } 166 167 //-------------------------------------------------------------------- checkDisposed() const168 void OCellListSource::checkDisposed( ) const SAL_THROW( ( DisposedException ) ) 169 { 170 if ( OCellListSource_Base::rBHelper.bInDispose || OCellListSource_Base::rBHelper.bDisposed ) 171 throw DisposedException(); 172 // TODO: is it worth having an error message here? 173 } 174 175 //-------------------------------------------------------------------- checkInitialized()176 void OCellListSource::checkInitialized() SAL_THROW( ( RuntimeException ) ) 177 { 178 if ( !m_bInitialized ) 179 throw RuntimeException(); 180 // TODO: error message 181 } 182 183 //-------------------------------------------------------------------- getImplementationName()184 ::rtl::OUString SAL_CALL OCellListSource::getImplementationName( ) throw (RuntimeException) 185 { 186 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.sheet.OCellListSource" ) ); 187 } 188 189 //-------------------------------------------------------------------- supportsService(const::rtl::OUString & _rServiceName)190 sal_Bool SAL_CALL OCellListSource::supportsService( const ::rtl::OUString& _rServiceName ) throw (RuntimeException) 191 { 192 Sequence< ::rtl::OUString > aSupportedServices( getSupportedServiceNames() ); 193 const ::rtl::OUString* pLookup = aSupportedServices.getConstArray(); 194 const ::rtl::OUString* pLookupEnd = aSupportedServices.getConstArray() + aSupportedServices.getLength(); 195 while ( pLookup != pLookupEnd ) 196 if ( *pLookup++ == _rServiceName ) 197 return sal_True; 198 199 return sal_False; 200 } 201 202 //-------------------------------------------------------------------- getSupportedServiceNames()203 Sequence< ::rtl::OUString > SAL_CALL OCellListSource::getSupportedServiceNames( ) throw (RuntimeException) 204 { 205 Sequence< ::rtl::OUString > aServices( 2 ); 206 aServices[ 0 ] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.table.CellRangeListSource" ) ); 207 aServices[ 1 ] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.form.binding.ListEntrySource" ) ); 208 return aServices; 209 } 210 211 //-------------------------------------------------------------------- getRangeAddress() const212 CellRangeAddress OCellListSource::getRangeAddress( ) const 213 { 214 OSL_PRECOND( m_xRange.is(), "OCellListSource::getRangeAddress: invalid range!" ); 215 216 CellRangeAddress aAddress; 217 Reference< XCellRangeAddressable > xRangeAddress( m_xRange, UNO_QUERY ); 218 if ( xRangeAddress.is() ) 219 aAddress = xRangeAddress->getRangeAddress( ); 220 return aAddress; 221 } 222 223 //-------------------------------------------------------------------- getCellTextContent_noCheck(sal_Int32 _nRangeRelativeColumn,sal_Int32 _nRangeRelativeRow)224 ::rtl::OUString OCellListSource::getCellTextContent_noCheck( sal_Int32 _nRangeRelativeColumn, sal_Int32 _nRangeRelativeRow ) 225 { 226 OSL_PRECOND( m_xRange.is(), "OCellListSource::getRangeAddress: invalid range!" ); 227 Reference< XTextRange > xCellText; 228 if ( m_xRange.is() ) 229 xCellText.set(xCellText.query( m_xRange->getCellByPosition( _nRangeRelativeColumn, _nRangeRelativeRow ) )); 230 231 ::rtl::OUString sText; 232 if ( xCellText.is() ) 233 sText = xCellText->getString(); 234 return sText; 235 } 236 237 //-------------------------------------------------------------------- getListEntryCount()238 sal_Int32 SAL_CALL OCellListSource::getListEntryCount( ) throw (RuntimeException) 239 { 240 ::osl::MutexGuard aGuard( m_aMutex ); 241 DBG_CHKTHIS( OCellListSource, checkConsistency_static ); 242 checkDisposed(); 243 checkInitialized(); 244 245 CellRangeAddress aAddress( getRangeAddress( ) ); 246 return aAddress.EndRow - aAddress.StartRow + 1; 247 } 248 249 //-------------------------------------------------------------------- getListEntry(sal_Int32 _nPosition)250 ::rtl::OUString SAL_CALL OCellListSource::getListEntry( sal_Int32 _nPosition ) throw (IndexOutOfBoundsException, RuntimeException) 251 { 252 ::osl::MutexGuard aGuard( m_aMutex ); 253 DBG_CHKTHIS( OCellListSource, checkConsistency_static ); 254 checkDisposed(); 255 checkInitialized(); 256 257 if ( _nPosition >= getListEntryCount() ) 258 throw IndexOutOfBoundsException(); 259 260 return getCellTextContent_noCheck( 0, _nPosition ); 261 } 262 263 //-------------------------------------------------------------------- getAllListEntries()264 Sequence< ::rtl::OUString > SAL_CALL OCellListSource::getAllListEntries( ) throw (RuntimeException) 265 { 266 ::osl::MutexGuard aGuard( m_aMutex ); 267 DBG_CHKTHIS( OCellListSource, checkConsistency_static ); 268 checkDisposed(); 269 checkInitialized(); 270 271 Sequence< ::rtl::OUString > aAllEntries( getListEntryCount() ); 272 ::rtl::OUString* pAllEntries = aAllEntries.getArray(); 273 for ( sal_Int32 i = 0; i < aAllEntries.getLength(); ++i ) 274 { 275 *pAllEntries++ = getCellTextContent_noCheck( 0, i ); 276 } 277 278 return aAllEntries; 279 } 280 281 //-------------------------------------------------------------------- addListEntryListener(const Reference<XListEntryListener> & _rxListener)282 void SAL_CALL OCellListSource::addListEntryListener( const Reference< XListEntryListener >& _rxListener ) throw (NullPointerException, RuntimeException) 283 { 284 ::osl::MutexGuard aGuard( m_aMutex ); 285 DBG_CHKTHIS( OCellListSource, checkConsistency_static ); 286 checkDisposed(); 287 checkInitialized(); 288 289 if ( !_rxListener.is() ) 290 throw NullPointerException(); 291 292 m_aListEntryListeners.addInterface( _rxListener ); 293 } 294 295 //-------------------------------------------------------------------- removeListEntryListener(const Reference<XListEntryListener> & _rxListener)296 void SAL_CALL OCellListSource::removeListEntryListener( const Reference< XListEntryListener >& _rxListener ) throw (NullPointerException, RuntimeException) 297 { 298 ::osl::MutexGuard aGuard( m_aMutex ); 299 DBG_CHKTHIS( OCellListSource, checkConsistency_static ); 300 checkDisposed(); 301 checkInitialized(); 302 303 if ( !_rxListener.is() ) 304 throw NullPointerException(); 305 306 m_aListEntryListeners.removeInterface( _rxListener ); 307 } 308 309 //-------------------------------------------------------------------- modified(const EventObject &)310 void SAL_CALL OCellListSource::modified( const EventObject& /* aEvent */ ) throw (RuntimeException) 311 { 312 DBG_CHKTHIS( OCellListSource, checkConsistency_static ); 313 314 notifyModified(); 315 } 316 317 //-------------------------------------------------------------------- notifyModified()318 void OCellListSource::notifyModified() 319 { 320 EventObject aEvent; 321 aEvent.Source.set(*this); 322 323 ::cppu::OInterfaceIteratorHelper aIter( m_aListEntryListeners ); 324 while ( aIter.hasMoreElements() ) 325 { 326 try 327 { 328 static_cast< XListEntryListener* >( aIter.next() )->allEntriesChanged( aEvent ); 329 } 330 catch( const RuntimeException& ) 331 { 332 // silent this 333 } 334 catch( const Exception& ) 335 { 336 DBG_ERROR( "OCellListSource::notifyModified: caught a (non-runtime) exception!" ); 337 } 338 } 339 340 } 341 342 //-------------------------------------------------------------------- disposing(const EventObject & aEvent)343 void SAL_CALL OCellListSource::disposing( const EventObject& aEvent ) throw (RuntimeException) 344 { 345 DBG_CHKTHIS( OCellListSource, checkConsistency_static ); 346 347 Reference<XInterface> xRangeInt( m_xRange, UNO_QUERY ); 348 if ( xRangeInt == aEvent.Source ) 349 { 350 // release references to range object 351 m_xRange.clear(); 352 } 353 } 354 355 //-------------------------------------------------------------------- initialize(const Sequence<Any> & _rArguments)356 void SAL_CALL OCellListSource::initialize( const Sequence< Any >& _rArguments ) throw (Exception, RuntimeException) 357 { 358 if ( m_bInitialized ) 359 throw Exception(); 360 // TODO: error message 361 362 // get the cell address 363 CellRangeAddress aRangeAddress; 364 sal_Bool bFoundAddress = sal_False; 365 366 const Any* pLoop = _rArguments.getConstArray(); 367 const Any* pLoopEnd = _rArguments.getConstArray() + _rArguments.getLength(); 368 for ( ; ( pLoop != pLoopEnd ) && !bFoundAddress; ++pLoop ) 369 { 370 NamedValue aValue; 371 if ( *pLoop >>= aValue ) 372 { 373 if ( aValue.Name.equalsAscii( "CellRange" ) ) 374 { 375 if ( aValue.Value >>= aRangeAddress ) 376 bFoundAddress = sal_True; 377 } 378 } 379 } 380 381 if ( !bFoundAddress ) 382 // TODO: error message 383 throw Exception(); 384 385 // determine the range we're bound to 386 try 387 { 388 if ( m_xDocument.is() ) 389 { 390 // first the sheets collection 391 Reference< XIndexAccess > xSheets(m_xDocument->getSheets( ), UNO_QUERY); 392 DBG_ASSERT( xSheets.is(), "OCellListSource::initialize: could not retrieve the sheets!" ); 393 394 if ( xSheets.is() ) 395 { 396 // the concrete sheet 397 Reference< XCellRange > xSheet(xSheets->getByIndex( aRangeAddress.Sheet ), UNO_QUERY); 398 DBG_ASSERT( xSheet.is(), "OCellListSource::initialize: NULL sheet, but no exception!" ); 399 400 // the concrete cell 401 if ( xSheet.is() ) 402 { 403 m_xRange.set(xSheet->getCellRangeByPosition( 404 aRangeAddress.StartColumn, aRangeAddress.StartRow, 405 aRangeAddress.EndColumn, aRangeAddress.EndRow)); 406 DBG_ASSERT( Reference< XCellRangeAddressable >( m_xRange, UNO_QUERY ).is(), "OCellListSource::initialize: either NULL range, or cell without address access!" ); 407 } 408 } 409 } 410 } 411 catch( const Exception& ) 412 { 413 DBG_ERROR( "OCellListSource::initialize: caught an exception while retrieving the cell object!" ); 414 } 415 416 417 if ( !m_xRange.is() ) 418 throw Exception(); 419 // TODO error message 420 421 Reference<XModifyBroadcaster> xBroadcaster( m_xRange, UNO_QUERY ); 422 if ( xBroadcaster.is() ) 423 { 424 xBroadcaster->addModifyListener( this ); 425 } 426 427 // TODO: add as XEventListener to the cell range, so we get notified when it dies, 428 // and can dispose ourself then 429 430 // TODO: somehow add as listener so we get notified when the address of the cell range changes 431 // We need to forward this as change in our CellRange property to our property change listeners 432 433 // TODO: somehow add as listener to the cells in the range, so that we get notified 434 // when their content changes. We need to forward this to our list entry listeners then 435 436 // TODO: somehow add as listener so that we get notified of insertions and removals of rows in our 437 // range. In this case, we need to fire a change in our CellRange property, and additionally 438 // notify our XListEntryListeners 439 440 m_bInitialized = sal_True; 441 } 442 443 //......................................................................... 444 } // namespace calc 445 //......................................................................... 446