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