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 #include "precompiled_toolkit.hxx"
25 
26 #include "sortablegriddatamodel.hxx"
27 #include "toolkit/helper/servicenames.hxx"
28 
29 /** === begin UNO includes === **/
30 #include <com/sun/star/i18n/XCollator.hpp>
31 #include <com/sun/star/lang/IllegalArgumentException.hpp>
32 #include <com/sun/star/ucb/AlreadyInitializedException.hpp>
33 /** === end UNO includes === **/
34 
35 #include <comphelper/anycompare.hxx>
36 #include <cppuhelper/typeprovider.hxx>
37 #include <tools/diagnose_ex.h>
38 #include <tools/debug.hxx>
39 #include <vcl/svapp.hxx>
40 
41 #include <set>
42 
43 //......................................................................................................................
44 namespace toolkit
45 {
46 //......................................................................................................................
47 
48 	/** === begin UNO using === **/
49     using ::com::sun::star::uno::TypeClass;
50     using ::com::sun::star::uno::TypeClass_VOID;
51 	using ::com::sun::star::uno::Reference;
52 	using ::com::sun::star::uno::XInterface;
53 	using ::com::sun::star::uno::UNO_QUERY;
54 	using ::com::sun::star::uno::UNO_QUERY_THROW;
55 	using ::com::sun::star::uno::UNO_SET_THROW;
56 	using ::com::sun::star::uno::Exception;
57 	using ::com::sun::star::uno::RuntimeException;
58 	using ::com::sun::star::uno::Any;
59 	using ::com::sun::star::uno::makeAny;
60 	using ::com::sun::star::uno::Sequence;
61 	using ::com::sun::star::uno::Type;
62     using ::com::sun::star::lang::IndexOutOfBoundsException;
63     using ::com::sun::star::lang::IllegalArgumentException;
64     using ::com::sun::star::awt::grid::XGridDataListener;
65     using ::com::sun::star::beans::Pair;
66     using ::com::sun::star::util::XCloneable;
67     using ::com::sun::star::i18n::XCollator;
68     using ::com::sun::star::lang::IllegalArgumentException;
69     using ::com::sun::star::lang::XMultiServiceFactory;
70     using ::com::sun::star::awt::grid::GridDataEvent;
71     using ::com::sun::star::lang::EventObject;
72     using ::com::sun::star::ucb::AlreadyInitializedException;
73 	/** === end UNO using === **/
74 
75 #ifdef DBG_UTIL
SortableGridDataModel_checkInvariants(const void * _pInstance)76     const char* SortableGridDataModel_checkInvariants( const void* _pInstance )
77     {
78         return static_cast< const SortableGridDataModel* >( _pInstance )->checkInvariants();
79     }
80 
81 	//------------------------------------------------------------------------------------------------------------------
checkInvariants() const82     const char* SortableGridDataModel::checkInvariants() const
83     {
84         if ( m_publicToPrivateRowIndex.size() != m_privateToPublicRowIndex.size() )
85             return "inconsistent index maps";
86 
87         if ( m_delegator.is() )
88         {
89             if ( m_publicToPrivateRowIndex.size() != size_t( m_delegator->getRowCount() ) )
90                 return "wrong cached row count";
91         }
92         else
93         {
94             if ( !m_publicToPrivateRowIndex.empty() )
95                 return "disposed or not initialized, but having a non-empty map";
96         }
97 
98         for ( size_t publicIndex=0; publicIndex<m_publicToPrivateRowIndex.size(); ++publicIndex )
99         {
100             ::sal_Int32 const privateIndex = m_publicToPrivateRowIndex[ publicIndex ];
101             if ( ( privateIndex < 0 ) || ( size_t( privateIndex ) >= m_privateToPublicRowIndex.size() ) )
102                 return "invalid cached private index";
103 
104             if ( m_privateToPublicRowIndex[ privateIndex ] != sal_Int32( publicIndex ) )
105                 return "index map traversal not commutavive";
106         }
107 
108         if ( impl_isSorted_nothrow() && m_publicToPrivateRowIndex.empty() )
109             return "sorted, but no row index translation tables";
110 
111         if ( !impl_isSorted_nothrow() && !m_publicToPrivateRowIndex.empty() )
112             return "unsorted, but have index translation tables";
113 
114         return NULL;
115     }
116 #endif
117 
118 #define DBG_CHECK_ME() \
119     DBG_CHKTHIS( SortableGridDataModel, SortableGridDataModel_checkInvariants )
120 
121 	//------------------------------------------------------------------------------------------------------------------
122     namespace
123     {
124         template< class STLCONTAINER >
lcl_clear(STLCONTAINER & i_container)125         static void lcl_clear( STLCONTAINER& i_container )
126         {
127             STLCONTAINER empty;
128             empty.swap( i_container );
129         }
130     }
131 
132 	//==================================================================================================================
133 	//= SortableGridDataModel
134 	//==================================================================================================================
DBG_NAME(SortableGridDataModel)135     DBG_NAME( SortableGridDataModel )
136 	//------------------------------------------------------------------------------------------------------------------
137     SortableGridDataModel::SortableGridDataModel( Reference< XMultiServiceFactory > const & i_factory )
138         :SortableGridDataModel_Base( m_aMutex )
139         ,SortableGridDataModel_PrivateBase()
140         ,m_context( i_factory )
141         ,m_isInitialized( false )
142         ,m_delegator()
143         ,m_collator()
144         ,m_currentSortColumn( -1 )
145         ,m_sortAscending( true )
146         ,m_publicToPrivateRowIndex()
147         ,m_privateToPublicRowIndex()
148     {
149         DBG_CTOR( SortableGridDataModel, SortableGridDataModel_checkInvariants );
150     }
151 
152 	//------------------------------------------------------------------------------------------------------------------
SortableGridDataModel(SortableGridDataModel const & i_copySource)153     SortableGridDataModel::SortableGridDataModel( SortableGridDataModel const & i_copySource )
154         :cppu::BaseMutex()
155         ,SortableGridDataModel_Base( m_aMutex )
156         ,SortableGridDataModel_PrivateBase()
157         ,m_context( i_copySource.m_context )
158         ,m_isInitialized( true )
159         ,m_delegator()
160         ,m_collator( i_copySource.m_collator )
161         ,m_currentSortColumn( i_copySource.m_currentSortColumn )
162         ,m_sortAscending( i_copySource.m_sortAscending )
163         ,m_publicToPrivateRowIndex( i_copySource.m_publicToPrivateRowIndex )
164         ,m_privateToPublicRowIndex( i_copySource.m_privateToPublicRowIndex )
165     {
166         DBG_CTOR( SortableGridDataModel, SortableGridDataModel_checkInvariants );
167 
168         ENSURE_OR_THROW( i_copySource.m_delegator.is(),
169             "not expected to be called for a disposed copy source!" );
170         m_delegator.set( i_copySource.m_delegator->createClone(), UNO_QUERY_THROW );
171     }
172 
173 	//------------------------------------------------------------------------------------------------------------------
~SortableGridDataModel()174     SortableGridDataModel::~SortableGridDataModel()
175     {
176         if ( !rBHelper.bDisposed )
177         {
178             acquire();
179             dispose();
180         }
181 
182         DBG_DTOR( SortableGridDataModel, SortableGridDataModel_checkInvariants );
183     }
184 
185     //------------------------------------------------------------------------------------------------------------------
queryInterface(const Type & aType)186     Any SAL_CALL SortableGridDataModel::queryInterface( const Type& aType ) throw (RuntimeException)
187     {
188         Any aReturn( SortableGridDataModel_Base::queryInterface( aType ) );
189         if ( !aReturn.hasValue() )
190             aReturn = SortableGridDataModel_PrivateBase::queryInterface( aType );
191         return aReturn;
192     }
193 
194     //------------------------------------------------------------------------------------------------------------------
acquire()195     void SAL_CALL SortableGridDataModel::acquire(  ) throw ()
196     {
197         SortableGridDataModel_Base::acquire();
198     }
199 
200     //------------------------------------------------------------------------------------------------------------------
release()201     void SAL_CALL SortableGridDataModel::release(  ) throw ()
202     {
203         SortableGridDataModel_Base::release();
204     }
205 
206     //------------------------------------------------------------------------------------------------------------------
getTypes()207     Sequence< Type > SAL_CALL SortableGridDataModel::getTypes(  ) throw (RuntimeException)
208     {
209         return SortableGridDataModel_Base::getTypes();
210         // don't expose the types got via SortableGridDataModel_PrivateBase - they're private, after all
211     }
212 
213     //------------------------------------------------------------------------------------------------------------------
getImplementationId()214     Sequence< ::sal_Int8 > SAL_CALL SortableGridDataModel::getImplementationId(  ) throw (RuntimeException)
215     {
216         static ::cppu::OImplementationId aId;
217         return aId.getImplementationId();
218     }
219 
220     //------------------------------------------------------------------------------------------------------------------
221     namespace
222     {
lcl_loadDefaultCollator_throw(::comphelper::ComponentContext const & i_context)223         Reference< XCollator > lcl_loadDefaultCollator_throw( ::comphelper::ComponentContext const & i_context )
224         {
225             Reference< XCollator > const xCollator( i_context.createComponent( "com.sun.star.i18n.Collator" ), UNO_QUERY_THROW );
226 		    xCollator->loadDefaultCollator( Application::GetSettings().GetLocale(), 0 );
227             return xCollator;
228         }
229     }
230 
231     //------------------------------------------------------------------------------------------------------------------
initialize(const Sequence<Any> & i_arguments)232     void SAL_CALL SortableGridDataModel::initialize( const Sequence< Any >& i_arguments ) throw (Exception, RuntimeException)
233     {
234         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
235         DBG_CHECK_ME();
236 
237         if ( m_delegator.is() )
238             throw AlreadyInitializedException( ::rtl::OUString(), *this );
239 
240         Reference< XMutableGridDataModel > xDelegator;
241         Reference< XCollator > xCollator;
242         switch ( i_arguments.getLength() )
243         {
244         case 1: // SortableGridDataModel.create( XMutableGridDataModel )
245             xDelegator.set( i_arguments[0], UNO_QUERY );
246             xCollator = lcl_loadDefaultCollator_throw( m_context );
247             break;
248 
249         case 2: // SortableGridDataModel.createWithCollator( XMutableGridDataModel, XCollator )
250             xDelegator.set( i_arguments[0], UNO_QUERY );
251             xCollator.set( i_arguments[1], UNO_QUERY );
252             if ( !xCollator.is() )
253                 throw IllegalArgumentException( ::rtl::OUString(), *this, 2 );
254             break;
255         }
256         if ( !xDelegator.is() )
257             throw IllegalArgumentException( ::rtl::OUString(), *this, 1 );
258 
259         m_delegator = xDelegator;
260         m_collator = xCollator;
261 
262         m_delegator->addGridDataListener( this );
263 
264         m_isInitialized = true;
265     }
266 
267     //------------------------------------------------------------------------------------------------------------------
impl_createPublicEvent(GridDataEvent const & i_originalEvent) const268     GridDataEvent SortableGridDataModel::impl_createPublicEvent( GridDataEvent const & i_originalEvent ) const
269     {
270         GridDataEvent aEvent( i_originalEvent );
271         aEvent.Source = *const_cast< SortableGridDataModel* >( this );
272         aEvent.FirstRow = impl_getPublicRowIndex_nothrow( aEvent.FirstRow );
273         aEvent.LastRow = impl_getPublicRowIndex_nothrow( aEvent.LastRow );
274         return aEvent;
275     }
276 
277     //------------------------------------------------------------------------------------------------------------------
impl_broadcast(void (SAL_CALL XGridDataListener::* i_listenerMethod)(const GridDataEvent &),GridDataEvent const & i_publicEvent,MethodGuard & i_instanceLock)278     void SortableGridDataModel::impl_broadcast( void ( SAL_CALL XGridDataListener::*i_listenerMethod )( const GridDataEvent & ),
279             GridDataEvent const & i_publicEvent, MethodGuard& i_instanceLock )
280     {
281         ::cppu::OInterfaceContainerHelper* pListeners = rBHelper.getContainer( XGridDataListener::static_type() );
282         if ( pListeners == NULL )
283             return;
284 
285         i_instanceLock.clear();
286         pListeners->notifyEach( i_listenerMethod, i_publicEvent );
287     }
288 
289     //------------------------------------------------------------------------------------------------------------------
rowsInserted(const GridDataEvent & i_event)290     void SAL_CALL SortableGridDataModel::rowsInserted( const GridDataEvent& i_event ) throw (RuntimeException)
291     {
292         MethodGuard aGuard( *this, rBHelper );
293         DBG_CHECK_ME();
294 
295         if ( impl_isSorted_nothrow() )
296         {
297             // no infrastructure is in place currently to sort the new row to its proper location,
298             // so we remove the sorting here.
299             impl_removeColumnSort( aGuard );
300             aGuard.reset();
301         }
302 
303         GridDataEvent const aEvent( impl_createPublicEvent( i_event ) );
304         impl_broadcast( &XGridDataListener::rowsInserted, aEvent, aGuard );
305     }
306 
307     //------------------------------------------------------------------------------------------------------------------
308     namespace
309     {
lcl_decrementValuesGreaterThan(::std::vector<::sal_Int32> & io_indexMap,sal_Int32 const i_threshold)310         void lcl_decrementValuesGreaterThan( ::std::vector< ::sal_Int32 > & io_indexMap, sal_Int32 const i_threshold )
311         {
312             for (   ::std::vector< ::sal_Int32 >::iterator loop = io_indexMap.begin();
313                     loop != io_indexMap.end();
314                     ++loop
315                 )
316             {
317                 if ( *loop >= i_threshold )
318                     --*loop;
319             }
320         }
321     }
322 
323     //------------------------------------------------------------------------------------------------------------------
impl_rebuildIndexesAndNotify(MethodGuard & i_instanceLock)324     void SortableGridDataModel::impl_rebuildIndexesAndNotify( MethodGuard& i_instanceLock )
325     {
326         OSL_PRECOND( impl_isSorted_nothrow(), "SortableGridDataModel::impl_rebuildIndexesAndNotify: illegal call!" );
327 
328         // clear the indexes
329         lcl_clear( m_publicToPrivateRowIndex );
330         lcl_clear( m_privateToPublicRowIndex );
331 
332         // rebuild the index
333         if ( !impl_reIndex_nothrow( m_currentSortColumn, m_sortAscending ) )
334         {
335             impl_removeColumnSort( i_instanceLock );
336             return;
337         }
338 
339         // broadcast an artificial event, saying that all rows have been removed
340         GridDataEvent const aRemovalEvent( *this, -1, -1, -1, -1 );
341         impl_broadcast( &XGridDataListener::rowsRemoved, aRemovalEvent, i_instanceLock );
342         i_instanceLock.reset();
343 
344         // broadcast an artificial event, saying that n rows have been added
345         GridDataEvent const aAdditionEvent( *this, -1, -1, 0, m_delegator->getRowCount() - 1 );
346         impl_broadcast( &XGridDataListener::rowsInserted, aAdditionEvent, i_instanceLock );
347     }
348 
349     //------------------------------------------------------------------------------------------------------------------
rowsRemoved(const GridDataEvent & i_event)350     void SAL_CALL SortableGridDataModel::rowsRemoved( const GridDataEvent& i_event ) throw (RuntimeException)
351     {
352         MethodGuard aGuard( *this, rBHelper );
353         DBG_CHECK_ME();
354 
355         // if the data is not sorted, broadcast the event unchanged
356         if ( !impl_isSorted_nothrow() )
357         {
358             GridDataEvent const aEvent( impl_createPublicEvent( i_event ) );
359             impl_broadcast( &XGridDataListener::rowsRemoved, aEvent, aGuard );
360             return;
361         }
362 
363         // if all rows have been removed, also simply multiplex to own listeners
364         if ( i_event.FirstRow < 0 )
365         {
366             lcl_clear( m_publicToPrivateRowIndex );
367             lcl_clear( m_privateToPublicRowIndex );
368             GridDataEvent aEvent( i_event );
369             aEvent.Source = *this;
370             impl_broadcast( &XGridDataListener::rowsRemoved, aEvent, aGuard );
371             return;
372         }
373 
374         bool needReIndex = false;
375         if ( i_event.FirstRow != i_event.LastRow )
376         {
377             OSL_ENSURE( false, "SortableGridDataModel::rowsRemoved: missing implementation - removal of multiple rows!" );
378             needReIndex = true;
379         }
380         else if ( size_t( i_event.FirstRow ) >= m_privateToPublicRowIndex.size() )
381         {
382             OSL_ENSURE( false, "SortableGridDataModel::rowsRemoved: inconsistent/wrong data!" );
383             needReIndex = true;
384         }
385 
386         if ( needReIndex )
387         {
388             impl_rebuildIndexesAndNotify( aGuard );
389             return;
390         }
391 
392         // build public event version
393         GridDataEvent const aEvent( impl_createPublicEvent( i_event ) );
394 
395         // remove the entries from the index maps
396         sal_Int32 const privateIndex = i_event.FirstRow;
397         sal_Int32 const publicIndex = aEvent.FirstRow;
398 
399         m_publicToPrivateRowIndex.erase( m_publicToPrivateRowIndex.begin() + publicIndex );
400         m_privateToPublicRowIndex.erase( m_privateToPublicRowIndex.begin() + privateIndex );
401 
402         // adjust remaining entries in the index maps
403         lcl_decrementValuesGreaterThan( m_publicToPrivateRowIndex, privateIndex );
404         lcl_decrementValuesGreaterThan( m_privateToPublicRowIndex, publicIndex );
405 
406         // broadcast the event
407         impl_broadcast( &XGridDataListener::rowsRemoved, aEvent, aGuard );
408     }
409 
410     //------------------------------------------------------------------------------------------------------------------
dataChanged(const GridDataEvent & i_event)411     void SAL_CALL SortableGridDataModel::dataChanged( const GridDataEvent& i_event ) throw (RuntimeException)
412     {
413         MethodGuard aGuard( *this, rBHelper );
414         DBG_CHECK_ME();
415 
416         GridDataEvent const aEvent( impl_createPublicEvent( i_event ) );
417         impl_broadcast( &XGridDataListener::dataChanged, aEvent, aGuard );
418     }
419 
420     //------------------------------------------------------------------------------------------------------------------
rowHeadingChanged(const GridDataEvent & i_event)421     void SAL_CALL SortableGridDataModel::rowHeadingChanged( const GridDataEvent& i_event ) throw (RuntimeException)
422     {
423         MethodGuard aGuard( *this, rBHelper );
424         DBG_CHECK_ME();
425 
426         GridDataEvent const aEvent( impl_createPublicEvent( i_event ) );
427         impl_broadcast( &XGridDataListener::rowHeadingChanged, aEvent, aGuard );
428     }
429 
430     //------------------------------------------------------------------------------------------------------------------
disposing(const EventObject & i_event)431     void SAL_CALL SortableGridDataModel::disposing( const EventObject& i_event ) throw (RuntimeException)
432     {
433         // not interested in
434         OSL_UNUSED( i_event );
435     }
436 
437     //------------------------------------------------------------------------------------------------------------------
438     namespace
439     {
440         class CellDataLessComparison : public ::std::binary_function< sal_Int32, sal_Int32, bool >
441         {
442         public:
CellDataLessComparison(::std::vector<Any> const & i_data,::comphelper::IKeyPredicateLess & i_predicate,sal_Bool const i_sortAscending)443             CellDataLessComparison(
444                 ::std::vector< Any > const & i_data,
445                 ::comphelper::IKeyPredicateLess& i_predicate,
446                 sal_Bool const i_sortAscending
447             )
448                 :m_data( i_data )
449                 ,m_predicate( i_predicate )
450                 ,m_sortAscending( i_sortAscending )
451             {
452             }
453 
operator ()(sal_Int32 const i_lhs,sal_Int32 const i_rhs) const454             bool operator()( sal_Int32 const i_lhs, sal_Int32 const i_rhs ) const
455             {
456                 Any const & lhs = m_data[ i_lhs ];
457                 Any const & rhs = m_data[ i_rhs ];
458                 // <VOID/> is less than everything else
459                 if ( !lhs.hasValue() )
460                     return m_sortAscending;
461                 if ( !rhs.hasValue() )
462                     return !m_sortAscending;
463 
464                 // actually compare
465                 if ( m_sortAscending )
466                     return m_predicate.isLess( lhs, rhs );
467                 else
468                     return m_predicate.isLess( rhs, lhs );
469             }
470 
471         private:
472             ::std::vector< Any > const &            m_data;
473             ::comphelper::IKeyPredicateLess const & m_predicate;
474             sal_Bool const                          m_sortAscending;
475         };
476     }
477 
478     //------------------------------------------------------------------------------------------------------------------
impl_reIndex_nothrow(::sal_Int32 const i_columnIndex,sal_Bool const i_sortAscending)479     bool SortableGridDataModel::impl_reIndex_nothrow( ::sal_Int32 const i_columnIndex, sal_Bool const i_sortAscending )
480     {
481         ::sal_Int32 const rowCount( getRowCount() );
482         ::std::vector< ::sal_Int32 > aPublicToPrivate( rowCount );
483 
484         try
485         {
486             // build an unsorted translation table, and retrieve the unsorted data
487             ::std::vector< Any > aColumnData( rowCount );
488             Type dataType;
489             for ( ::sal_Int32 rowIndex = 0; rowIndex < rowCount; ++rowIndex )
490             {
491                 aColumnData[ rowIndex ] = m_delegator->getCellData( i_columnIndex, rowIndex );
492                 aPublicToPrivate[ rowIndex ] = rowIndex;
493 
494                 // determine the data types we assume for the complete column
495                 if ( ( dataType.getTypeClass() == TypeClass_VOID ) && aColumnData[ rowIndex ].hasValue() )
496                     dataType = aColumnData[ rowIndex ].getValueType();
497             }
498 
499             // get predicate object
500             ::std::auto_ptr< ::comphelper::IKeyPredicateLess > const pPredicate( ::comphelper::getStandardLessPredicate( dataType, m_collator ) );
501             ENSURE_OR_RETURN_FALSE( pPredicate.get(), "SortableGridDataModel::impl_reIndex_nothrow: no sortable data found!" );
502 
503             // then sort
504             CellDataLessComparison const aComparator( aColumnData, *pPredicate, i_sortAscending );
505             ::std::sort( aPublicToPrivate.begin(), aPublicToPrivate.end(), aComparator );
506         }
507         catch( const Exception& )
508         {
509         	DBG_UNHANDLED_EXCEPTION();
510             return false;
511         }
512 
513         // also build the "private to public" mapping
514         ::std::vector< sal_Int32 > aPrivateToPublic( aPublicToPrivate.size() );
515         for ( size_t i=0; i<aPublicToPrivate.size(); ++i )
516             aPrivateToPublic[ aPublicToPrivate[i] ] = i;
517 
518         m_publicToPrivateRowIndex.swap( aPublicToPrivate );
519         m_privateToPublicRowIndex.swap( aPrivateToPublic );
520 
521         return true;
522     }
523 
524     //------------------------------------------------------------------------------------------------------------------
sortByColumn(::sal_Int32 i_columnIndex,::sal_Bool i_sortAscending)525     void SAL_CALL SortableGridDataModel::sortByColumn( ::sal_Int32 i_columnIndex, ::sal_Bool i_sortAscending ) throw (IndexOutOfBoundsException, RuntimeException)
526     {
527         MethodGuard aGuard( *this, rBHelper );
528         DBG_CHECK_ME();
529 
530         if ( ( i_columnIndex < 0 ) || ( i_columnIndex >= getColumnCount() ) )
531             throw IndexOutOfBoundsException( ::rtl::OUString(), *this );
532 
533         if ( !impl_reIndex_nothrow( i_columnIndex, i_sortAscending ) )
534             return;
535 
536         m_currentSortColumn = i_columnIndex;
537         m_sortAscending = i_sortAscending;
538 
539         impl_broadcast(
540             &XGridDataListener::dataChanged,
541             GridDataEvent( *this, -1, -1, -1, -1 ),
542             aGuard
543         );
544     }
545 
546     //------------------------------------------------------------------------------------------------------------------
impl_removeColumnSort_noBroadcast()547     void SortableGridDataModel::impl_removeColumnSort_noBroadcast()
548     {
549         lcl_clear( m_publicToPrivateRowIndex );
550         lcl_clear( m_privateToPublicRowIndex );
551 
552         m_currentSortColumn = -1;
553         m_sortAscending = sal_True;
554     }
555 
556     //------------------------------------------------------------------------------------------------------------------
impl_removeColumnSort(MethodGuard & i_instanceLock)557     void SortableGridDataModel::impl_removeColumnSort( MethodGuard& i_instanceLock )
558     {
559         impl_removeColumnSort_noBroadcast();
560         impl_broadcast(
561             &XGridDataListener::dataChanged,
562             GridDataEvent( *this, -1, -1, -1, -1 ),
563             i_instanceLock
564         );
565     }
566 
567     //------------------------------------------------------------------------------------------------------------------
removeColumnSort()568     void SAL_CALL SortableGridDataModel::removeColumnSort(  ) throw (RuntimeException)
569     {
570         MethodGuard aGuard( *this, rBHelper );
571         DBG_CHECK_ME();
572         impl_removeColumnSort( aGuard );
573     }
574 
575     //------------------------------------------------------------------------------------------------------------------
getCurrentSortOrder()576     Pair< ::sal_Int32, ::sal_Bool > SAL_CALL SortableGridDataModel::getCurrentSortOrder(  ) throw (RuntimeException)
577     {
578         MethodGuard aGuard( *this, rBHelper );
579         DBG_CHECK_ME();
580 
581         return Pair< ::sal_Int32, ::sal_Bool >( m_currentSortColumn, m_sortAscending );
582     }
583 
584     //------------------------------------------------------------------------------------------------------------------
addRow(const Any & i_heading,const Sequence<Any> & i_data)585     void SAL_CALL SortableGridDataModel::addRow( const Any& i_heading, const Sequence< Any >& i_data ) throw (RuntimeException)
586     {
587         MethodGuard aGuard( *this, rBHelper );
588         DBG_CHECK_ME();
589 
590         Reference< XMutableGridDataModel > const delegator( m_delegator );
591         aGuard.clear();
592         delegator->addRow( i_heading, i_data );
593     }
594 
595     //------------------------------------------------------------------------------------------------------------------
addRows(const Sequence<Any> & i_headings,const Sequence<Sequence<Any>> & i_data)596     void SAL_CALL SortableGridDataModel::addRows( const Sequence< Any >& i_headings, const Sequence< Sequence< Any > >& i_data ) throw (IllegalArgumentException, RuntimeException)
597     {
598         MethodGuard aGuard( *this, rBHelper );
599         DBG_CHECK_ME();
600 
601         Reference< XMutableGridDataModel > const delegator( m_delegator );
602         aGuard.clear();
603         delegator->addRows( i_headings, i_data );
604     }
605 
606     //------------------------------------------------------------------------------------------------------------------
insertRow(::sal_Int32 i_index,const Any & i_heading,const Sequence<Any> & i_data)607     void SAL_CALL SortableGridDataModel::insertRow( ::sal_Int32 i_index, const Any& i_heading, const Sequence< Any >& i_data ) throw (RuntimeException, IndexOutOfBoundsException)
608     {
609         MethodGuard aGuard( *this, rBHelper );
610         DBG_CHECK_ME();
611 
612         ::sal_Int32 const rowIndex = i_index == getRowCount() ? i_index : impl_getPrivateRowIndex_throw( i_index );
613             // note that |RowCount| is a valid index in this method, but not for impl_getPrivateRowIndex_throw
614 
615         Reference< XMutableGridDataModel > const delegator( m_delegator );
616         aGuard.clear();
617         delegator->insertRow( rowIndex, i_heading, i_data );
618     }
619 
620     //------------------------------------------------------------------------------------------------------------------
insertRows(::sal_Int32 i_index,const Sequence<Any> & i_headings,const Sequence<Sequence<Any>> & i_data)621     void SAL_CALL SortableGridDataModel::insertRows( ::sal_Int32 i_index, const Sequence< Any>& i_headings, const Sequence< Sequence< Any > >& i_data ) throw (IllegalArgumentException, IndexOutOfBoundsException, RuntimeException)
622     {
623         MethodGuard aGuard( *this, rBHelper );
624         DBG_CHECK_ME();
625 
626         ::sal_Int32 const rowIndex = i_index == getRowCount() ? i_index : impl_getPrivateRowIndex_throw( i_index );
627             // note that |RowCount| is a valid index in this method, but not for impl_getPrivateRowIndex_throw
628 
629         Reference< XMutableGridDataModel > const delegator( m_delegator );
630         aGuard.clear();
631         delegator->insertRows( rowIndex, i_headings, i_data );
632     }
633 
634     //------------------------------------------------------------------------------------------------------------------
removeRow(::sal_Int32 i_rowIndex)635     void SAL_CALL SortableGridDataModel::removeRow( ::sal_Int32 i_rowIndex ) throw (IndexOutOfBoundsException, RuntimeException)
636     {
637         MethodGuard aGuard( *this, rBHelper );
638         DBG_CHECK_ME();
639 
640         ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex );
641 
642         Reference< XMutableGridDataModel > const delegator( m_delegator );
643         aGuard.clear();
644         delegator->removeRow( rowIndex );
645     }
646 
647     //------------------------------------------------------------------------------------------------------------------
removeAllRows()648     void SAL_CALL SortableGridDataModel::removeAllRows(  ) throw (RuntimeException)
649     {
650         MethodGuard aGuard( *this, rBHelper );
651         DBG_CHECK_ME();
652 
653         Reference< XMutableGridDataModel > const delegator( m_delegator );
654         aGuard.clear();
655         delegator->removeAllRows();
656     }
657 
658     //------------------------------------------------------------------------------------------------------------------
updateCellData(::sal_Int32 i_columnIndex,::sal_Int32 i_rowIndex,const Any & i_value)659     void SAL_CALL SortableGridDataModel::updateCellData( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex, const Any& i_value ) throw (IndexOutOfBoundsException, RuntimeException)
660     {
661         MethodGuard aGuard( *this, rBHelper );
662         DBG_CHECK_ME();
663 
664         ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex );
665 
666         Reference< XMutableGridDataModel > const delegator( m_delegator );
667         aGuard.clear();
668         delegator->updateCellData( i_columnIndex, rowIndex, i_value );
669     }
670 
671     //------------------------------------------------------------------------------------------------------------------
updateRowData(const Sequence<::sal_Int32> & i_columnIndexes,::sal_Int32 i_rowIndex,const Sequence<Any> & i_values)672     void SAL_CALL SortableGridDataModel::updateRowData( const Sequence< ::sal_Int32 >& i_columnIndexes, ::sal_Int32 i_rowIndex, const Sequence< Any >& i_values ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
673     {
674         MethodGuard aGuard( *this, rBHelper );
675         DBG_CHECK_ME();
676 
677         ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex );
678 
679         Reference< XMutableGridDataModel > const delegator( m_delegator );
680         aGuard.clear();
681         delegator->updateRowData( i_columnIndexes, rowIndex, i_values );
682     }
683 
684     //------------------------------------------------------------------------------------------------------------------
updateRowHeading(::sal_Int32 i_rowIndex,const Any & i_heading)685     void SAL_CALL SortableGridDataModel::updateRowHeading( ::sal_Int32 i_rowIndex, const Any& i_heading ) throw (IndexOutOfBoundsException, RuntimeException)
686     {
687         MethodGuard aGuard( *this, rBHelper );
688         DBG_CHECK_ME();
689 
690         ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex );
691 
692         Reference< XMutableGridDataModel > const delegator( m_delegator );
693         aGuard.clear();
694         delegator->updateRowHeading( rowIndex, i_heading );
695     }
696 
697     //------------------------------------------------------------------------------------------------------------------
updateCellToolTip(::sal_Int32 i_columnIndex,::sal_Int32 i_rowIndex,const Any & i_value)698     void SAL_CALL SortableGridDataModel::updateCellToolTip( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex, const Any& i_value ) throw (IndexOutOfBoundsException, RuntimeException)
699     {
700         MethodGuard aGuard( *this, rBHelper );
701         DBG_CHECK_ME();
702 
703         ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex );
704 
705         Reference< XMutableGridDataModel > const delegator( m_delegator );
706         aGuard.clear();
707         delegator->updateCellToolTip( i_columnIndex, rowIndex, i_value );
708     }
709 
710     //------------------------------------------------------------------------------------------------------------------
updateRowToolTip(::sal_Int32 i_rowIndex,const Any & i_value)711     void SAL_CALL SortableGridDataModel::updateRowToolTip( ::sal_Int32 i_rowIndex, const Any& i_value ) throw (IndexOutOfBoundsException, RuntimeException)
712     {
713         MethodGuard aGuard( *this, rBHelper );
714         DBG_CHECK_ME();
715 
716         ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex );
717 
718         Reference< XMutableGridDataModel > const delegator( m_delegator );
719         aGuard.clear();
720         delegator->updateRowToolTip( rowIndex, i_value );
721     }
722 
723     //------------------------------------------------------------------------------------------------------------------
addGridDataListener(const Reference<XGridDataListener> & i_listener)724     void SAL_CALL SortableGridDataModel::addGridDataListener( const Reference< XGridDataListener >& i_listener ) throw (RuntimeException)
725     {
726 	    rBHelper.addListener( XGridDataListener::static_type(), i_listener );
727     }
728 
729     //------------------------------------------------------------------------------------------------------------------
removeGridDataListener(const Reference<XGridDataListener> & i_listener)730     void SAL_CALL SortableGridDataModel::removeGridDataListener( const Reference< XGridDataListener >& i_listener ) throw (RuntimeException)
731     {
732 	    rBHelper.removeListener( XGridDataListener::static_type(), i_listener );
733     }
734 
735     //------------------------------------------------------------------------------------------------------------------
getRowCount()736     ::sal_Int32 SAL_CALL SortableGridDataModel::getRowCount() throw (RuntimeException)
737     {
738         MethodGuard aGuard( *this, rBHelper );
739         DBG_CHECK_ME();
740 
741         Reference< XMutableGridDataModel > const delegator( m_delegator );
742         aGuard.clear();
743         return delegator->getRowCount();
744     }
745 
746     //------------------------------------------------------------------------------------------------------------------
getColumnCount()747     ::sal_Int32 SAL_CALL SortableGridDataModel::getColumnCount() throw (RuntimeException)
748     {
749         MethodGuard aGuard( *this, rBHelper );
750         DBG_CHECK_ME();
751 
752         Reference< XMutableGridDataModel > const delegator( m_delegator );
753         aGuard.clear();
754         return delegator->getColumnCount();
755     }
756 
757     //------------------------------------------------------------------------------------------------------------------
getCellData(::sal_Int32 i_columnIndex,::sal_Int32 i_rowIndex)758     Any SAL_CALL SortableGridDataModel::getCellData( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) throw (IndexOutOfBoundsException, RuntimeException)
759     {
760         MethodGuard aGuard( *this, rBHelper );
761         DBG_CHECK_ME();
762 
763         ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex );
764 
765         Reference< XMutableGridDataModel > const delegator( m_delegator );
766         aGuard.clear();
767         return delegator->getCellData( i_columnIndex, rowIndex );
768     }
769 
770     //------------------------------------------------------------------------------------------------------------------
getCellToolTip(::sal_Int32 i_columnIndex,::sal_Int32 i_rowIndex)771     Any SAL_CALL SortableGridDataModel::getCellToolTip( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) throw (IndexOutOfBoundsException, RuntimeException)
772     {
773         MethodGuard aGuard( *this, rBHelper );
774         DBG_CHECK_ME();
775 
776         ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex );
777 
778         Reference< XMutableGridDataModel > const delegator( m_delegator );
779         aGuard.clear();
780         return delegator->getCellToolTip( i_columnIndex, rowIndex );
781     }
782 
783     //------------------------------------------------------------------------------------------------------------------
getRowHeading(::sal_Int32 i_rowIndex)784     Any SAL_CALL SortableGridDataModel::getRowHeading( ::sal_Int32 i_rowIndex ) throw (IndexOutOfBoundsException, RuntimeException)
785     {
786         MethodGuard aGuard( *this, rBHelper );
787         DBG_CHECK_ME();
788 
789         ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex );
790 
791         Reference< XMutableGridDataModel > const delegator( m_delegator );
792         aGuard.clear();
793         return delegator->getRowHeading( rowIndex );
794     }
795 
796     //------------------------------------------------------------------------------------------------------------------
getRowData(::sal_Int32 i_rowIndex)797     Sequence< Any > SAL_CALL SortableGridDataModel::getRowData( ::sal_Int32 i_rowIndex ) throw (IndexOutOfBoundsException, RuntimeException)
798     {
799         MethodGuard aGuard( *this, rBHelper );
800         DBG_CHECK_ME();
801 
802         ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex );
803 
804         Reference< XMutableGridDataModel > const delegator( m_delegator );
805         aGuard.clear();
806         return delegator->getRowData( rowIndex );
807     }
808 
809     //------------------------------------------------------------------------------------------------------------------
disposing()810     void SAL_CALL SortableGridDataModel::disposing()
811     {
812         m_currentSortColumn = -1;
813 
814         Reference< XComponent > const delegatorComponent( m_delegator.get() );
815         m_delegator->removeGridDataListener( this );
816         m_delegator.clear();
817         delegatorComponent->dispose();
818 
819         Reference< XComponent > const collatorComponent( m_collator, UNO_QUERY );
820         m_collator.clear();
821         if ( collatorComponent.is() )
822             collatorComponent->dispose();
823 
824         lcl_clear( m_publicToPrivateRowIndex );
825         lcl_clear( m_privateToPublicRowIndex );
826     }
827 
828     //------------------------------------------------------------------------------------------------------------------
createClone()829     Reference< XCloneable > SAL_CALL SortableGridDataModel::createClone(  ) throw (RuntimeException)
830     {
831         MethodGuard aGuard( *this, rBHelper );
832         DBG_CHECK_ME();
833 
834         return new SortableGridDataModel( *this );
835     }
836 
837     //------------------------------------------------------------------------------------------------------------------
getImplementationName()838     ::rtl::OUString SAL_CALL SortableGridDataModel::getImplementationName(  ) throw (RuntimeException)
839     {
840         return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "org.openoffice.comp.toolkit.SortableGridDataModel" ) );
841     }
842 
843     //------------------------------------------------------------------------------------------------------------------
supportsService(const::rtl::OUString & i_serviceName)844     ::sal_Bool SAL_CALL SortableGridDataModel::supportsService( const ::rtl::OUString& i_serviceName ) throw (RuntimeException)
845     {
846         Sequence< ::rtl::OUString > const aServiceNames( getSupportedServiceNames() );
847         for ( sal_Int32 i=0; i<aServiceNames.getLength(); ++i )
848             if ( aServiceNames[i] == i_serviceName )
849                 return sal_True;
850         return sal_False;
851     }
852 
853     //------------------------------------------------------------------------------------------------------------------
getSupportedServiceNames()854     Sequence< ::rtl::OUString > SAL_CALL SortableGridDataModel::getSupportedServiceNames(  ) throw (RuntimeException)
855     {
856         Sequence< ::rtl::OUString > aServiceNames(1);
857         aServiceNames[0] = ::rtl::OUString::createFromAscii( szServiceName_SortableGridDataModel );
858         return aServiceNames;
859     }
860 
861     //------------------------------------------------------------------------------------------------------------------
impl_getPrivateRowIndex_throw(::sal_Int32 const i_publicRowIndex) const862     ::sal_Int32 SortableGridDataModel::impl_getPrivateRowIndex_throw( ::sal_Int32 const i_publicRowIndex ) const
863     {
864         if ( ( i_publicRowIndex < 0 ) || ( i_publicRowIndex >= m_delegator->getRowCount() ) )
865             throw IndexOutOfBoundsException( ::rtl::OUString(), *const_cast< SortableGridDataModel* >( this ) );
866 
867         if ( !impl_isSorted_nothrow() )
868             // no need to translate anything
869             return i_publicRowIndex;
870 
871         ENSURE_OR_RETURN( size_t( i_publicRowIndex ) < m_publicToPrivateRowIndex.size(),
872             "SortableGridDataModel::impl_getPrivateRowIndex_throw: inconsistency!", i_publicRowIndex );
873                 // obviously the translation table contains too few elements - it should have exactly |getRowCount()|
874                 // elements
875 
876         return m_publicToPrivateRowIndex[ i_publicRowIndex ];
877     }
878 
879     //------------------------------------------------------------------------------------------------------------------
impl_getPublicRowIndex_nothrow(::sal_Int32 const i_privateRowIndex) const880     ::sal_Int32 SortableGridDataModel::impl_getPublicRowIndex_nothrow( ::sal_Int32 const i_privateRowIndex ) const
881     {
882         if ( !impl_isSorted_nothrow() )
883             // no need to translate anything
884             return i_privateRowIndex;
885 
886         if ( i_privateRowIndex < 0 )
887             return i_privateRowIndex;
888 
889         ENSURE_OR_RETURN( size_t( i_privateRowIndex ) < m_privateToPublicRowIndex.size(),
890             "SortableGridDataModel::impl_getPublicRowIndex_nothrow: invalid index!", i_privateRowIndex );
891 
892         return m_privateToPublicRowIndex[ i_privateRowIndex ];
893     }
894 
895 //......................................................................................................................
896 } // namespace toolkit
897 //......................................................................................................................
898 
SortableGridDataModel_CreateInstance(const::com::sun::star::uno::Reference<::com::sun::star::lang::XMultiServiceFactory> & i_factory)899 ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > SAL_CALL SortableGridDataModel_CreateInstance( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& i_factory )
900 {
901     return *( new ::toolkit::SortableGridDataModel( i_factory ) );
902 }
903