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