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