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_toolkit.hxx"
30 
31 #include "defaultgriddatamodel.hxx"
32 
33 #include <comphelper/stlunosequence.hxx>
34 #include <comphelper/componentguard.hxx>
35 #include <toolkit/helper/servicenames.hxx>
36 #include <tools/diagnose_ex.h>
37 #include <rtl/ref.hxx>
38 
39 #include <algorithm>
40 #include <functional>
41 
42 //......................................................................................................................
43 namespace toolkit
44 //......................................................................................................................
45 {
46     /** === begin UNO using === **/
47     using ::com::sun::star::uno::Reference;
48     using ::com::sun::star::uno::RuntimeException;
49     using ::com::sun::star::uno::Sequence;
50     using ::com::sun::star::uno::UNO_QUERY_THROW;
51     using ::com::sun::star::uno::UNO_QUERY;
52     using ::com::sun::star::uno::XInterface;
53     using ::com::sun::star::lang::XComponent;
54     using ::com::sun::star::lang::EventObject;
55     using ::com::sun::star::uno::Exception;
56     using ::com::sun::star::util::XCloneable;
57     /** === end UNO using === **/
58 
59     using ::comphelper::stl_begin;
60     using ::comphelper::stl_end;
61 
62     //==================================================================================================================
63     //= DefaultGridDataModel
64     //==================================================================================================================
65     //------------------------------------------------------------------------------------------------------------------
66     DefaultGridDataModel::DefaultGridDataModel()
67         :DefaultGridDataModel_Base( m_aMutex )
68         ,m_aRowHeaders()
69         ,m_nColumnCount(0)
70     {
71     }
72 
73     //------------------------------------------------------------------------------------------------------------------
74     DefaultGridDataModel::DefaultGridDataModel( DefaultGridDataModel const & i_copySource )
75         :cppu::BaseMutex()
76         ,DefaultGridDataModel_Base( m_aMutex )
77         ,m_aData( i_copySource.m_aData )
78         ,m_aRowHeaders( i_copySource.m_aRowHeaders )
79         ,m_nColumnCount( i_copySource.m_nColumnCount )
80     {
81     }
82 
83     //------------------------------------------------------------------------------------------------------------------
84     DefaultGridDataModel::~DefaultGridDataModel()
85     {
86     }
87 
88     //------------------------------------------------------------------------------------------------------------------
89     void DefaultGridDataModel::broadcast( GridDataEvent const & i_event,
90         void ( SAL_CALL XGridDataListener::*i_listenerMethod )( GridDataEvent const & ), ::comphelper::ComponentGuard & i_instanceLock )
91     {
92 	    ::cppu::OInterfaceContainerHelper* pListeners = rBHelper.getContainer( XGridDataListener::static_type() );
93 	    if ( !pListeners )
94             return;
95 
96         i_instanceLock.clear();
97         pListeners->notifyEach( i_listenerMethod, i_event );
98     }
99 
100     //------------------------------------------------------------------------------------------------------------------
101     ::sal_Int32 SAL_CALL DefaultGridDataModel::getRowCount() throw (::com::sun::star::uno::RuntimeException)
102     {
103         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
104 	    return impl_getRowCount_nolck();
105     }
106 
107     //------------------------------------------------------------------------------------------------------------------
108 	::sal_Int32 SAL_CALL DefaultGridDataModel::getColumnCount() throw (::com::sun::star::uno::RuntimeException)
109     {
110         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
111         return m_nColumnCount;
112     }
113 
114     //------------------------------------------------------------------------------------------------------------------
115     DefaultGridDataModel::CellData const & DefaultGridDataModel::impl_getCellData_throw( sal_Int32 const i_column, sal_Int32 const i_row ) const
116     {
117         if  (   ( i_row < 0 ) || ( size_t( i_row ) > m_aData.size() )
118             ||  ( i_column < 0 ) || ( i_column > m_nColumnCount )
119             )
120             throw IndexOutOfBoundsException( ::rtl::OUString(), *const_cast< DefaultGridDataModel* >( this ) );
121 
122         RowData const & rRow( m_aData[ i_row ] );
123         if ( size_t( i_column ) < rRow.size() )
124             return rRow[ i_column ];
125 
126         static CellData s_aEmpty;
127         return s_aEmpty;
128     }
129 
130     //------------------------------------------------------------------------------------------------------------------
131     DefaultGridDataModel::RowData& DefaultGridDataModel::impl_getRowDataAccess_throw( sal_Int32 const i_rowIndex, size_t const i_requiredColumnCount )
132     {
133         OSL_ENSURE( i_requiredColumnCount <= size_t( m_nColumnCount ), "DefaultGridDataModel::impl_getRowDataAccess_throw: invalid column count!" );
134         if  ( ( i_rowIndex < 0 ) || ( size_t( i_rowIndex ) >= m_aData.size() ) )
135             throw IndexOutOfBoundsException( ::rtl::OUString(), *this );
136 
137         RowData& rRowData( m_aData[ i_rowIndex ] );
138         if ( rRowData.size() < i_requiredColumnCount )
139             rRowData.resize( i_requiredColumnCount );
140         return rRowData;
141     }
142 
143     //------------------------------------------------------------------------------------------------------------------
144     DefaultGridDataModel::CellData& DefaultGridDataModel::impl_getCellDataAccess_throw( sal_Int32 const i_columnIndex, sal_Int32 const i_rowIndex )
145     {
146         if  ( ( i_columnIndex < 0 ) || ( i_columnIndex >= m_nColumnCount ) )
147             throw IndexOutOfBoundsException( ::rtl::OUString(), *this );
148 
149         RowData& rRowData( impl_getRowDataAccess_throw( i_rowIndex, size_t( i_columnIndex + 1 ) ) );
150         return rRowData[ i_columnIndex ];
151     }
152 
153     //------------------------------------------------------------------------------------------------------------------
154     Any SAL_CALL DefaultGridDataModel::getCellData( ::sal_Int32 i_column, ::sal_Int32 i_row ) throw (RuntimeException, IndexOutOfBoundsException)
155     {
156         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
157         return impl_getCellData_throw( i_column, i_row ).first;
158     }
159 
160     //------------------------------------------------------------------------------------------------------------------
161     Any SAL_CALL DefaultGridDataModel::getCellToolTip( ::sal_Int32 i_column, ::sal_Int32 i_row ) throw (RuntimeException, IndexOutOfBoundsException)
162     {
163         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
164         return impl_getCellData_throw( i_column, i_row ).second;
165     }
166 
167     //------------------------------------------------------------------------------------------------------------------
168     Any SAL_CALL DefaultGridDataModel::getRowHeading( ::sal_Int32 i_row ) throw (RuntimeException, IndexOutOfBoundsException)
169     {
170         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
171 
172         if ( ( i_row < 0 ) || ( size_t( i_row ) >= m_aRowHeaders.size() ) )
173             throw IndexOutOfBoundsException( ::rtl::OUString(), *this );
174 
175         return m_aRowHeaders[ i_row ];
176     }
177 
178     //------------------------------------------------------------------------------------------------------------------
179     Sequence< Any > SAL_CALL DefaultGridDataModel::getRowData( ::sal_Int32 i_rowIndex ) throw (IndexOutOfBoundsException, RuntimeException)
180     {
181         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
182 
183         Sequence< Any > resultData( m_nColumnCount );
184         RowData& rRowData = impl_getRowDataAccess_throw( i_rowIndex, m_nColumnCount );
185 
186         ::std::transform( rRowData.begin(), rRowData.end(), resultData.getArray(), ::std::select1st< CellData >() );
187         return resultData;
188     }
189 
190     //------------------------------------------------------------------------------------------------------------------
191     void DefaultGridDataModel::impl_insertRow( sal_Int32 const i_position, Any const & i_heading, Sequence< Any > const & i_rowData, sal_Int32 const i_assumedColCount )
192     {
193         OSL_PRECOND( ( i_assumedColCount <= 0 ) || ( i_assumedColCount >= i_rowData.getLength() ),
194             "DefaultGridDataModel::impl_insertRow: invalid column count!" );
195 
196         // insert heading
197         m_aRowHeaders.insert( m_aRowHeaders.begin() + i_position, i_heading );
198 
199         // create new data row
200         RowData newRow( i_assumedColCount > 0 ? i_assumedColCount : i_rowData.getLength() );
201         RowData::iterator cellData = newRow.begin();
202         for ( const Any* pData = stl_begin( i_rowData ); pData != stl_end( i_rowData ); ++pData, ++cellData )
203             cellData->first = *pData;
204 
205         // insert data row
206         m_aData.insert( m_aData.begin() + i_position, newRow );
207     }
208 
209     //------------------------------------------------------------------------------------------------------------------
210     void SAL_CALL DefaultGridDataModel::addRow( const Any& i_heading, const Sequence< Any >& i_data ) throw (RuntimeException)
211     {
212         insertRow( getRowCount(), i_heading, i_data );
213     }
214 
215     //------------------------------------------------------------------------------------------------------------------
216     void SAL_CALL DefaultGridDataModel::addRows( const Sequence< Any >& i_headings, const Sequence< Sequence< Any > >& i_data ) throw (IllegalArgumentException, RuntimeException)
217     {
218         insertRows( getRowCount(), i_headings, i_data );
219     }
220 
221     //------------------------------------------------------------------------------------------------------------------
222     void SAL_CALL DefaultGridDataModel::insertRow( ::sal_Int32 i_index, const Any& i_heading, const Sequence< Any >& i_data ) throw (RuntimeException, IndexOutOfBoundsException)
223     {
224         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
225 
226         if ( ( i_index < 0 ) || ( i_index > impl_getRowCount_nolck() ) )
227             throw IndexOutOfBoundsException( ::rtl::OUString(), *this );
228 
229         // actually insert the row
230         impl_insertRow( i_index, i_heading, i_data );
231 
232         // update column count
233         sal_Int32 const columnCount = i_data.getLength();
234         if ( columnCount > m_nColumnCount )
235             m_nColumnCount = columnCount;
236 
237         broadcast(
238             GridDataEvent( *this, -1, -1, i_index, i_index ),
239             &XGridDataListener::rowsInserted,
240             aGuard
241         );
242     }
243 
244     //------------------------------------------------------------------------------------------------------------------
245     void SAL_CALL DefaultGridDataModel::insertRows( ::sal_Int32 i_index, const Sequence< Any>& i_headings, const Sequence< Sequence< Any > >& i_data ) throw (IllegalArgumentException, IndexOutOfBoundsException, RuntimeException)
246     {
247         if ( i_headings.getLength() != i_data.getLength() )
248             throw IllegalArgumentException( ::rtl::OUString(), *this, -1 );
249 
250         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
251 
252         if ( ( i_index < 0 ) || ( i_index > impl_getRowCount_nolck() ) )
253             throw IndexOutOfBoundsException( ::rtl::OUString(), *this );
254 
255         sal_Int32 const rowCount = i_headings.getLength();
256         if ( rowCount == 0 )
257             return;
258 
259         // determine max col count in the new data
260         sal_Int32 maxColCount = 0;
261         for ( sal_Int32 row=0; row<rowCount; ++row )
262             if ( i_data[row].getLength() > maxColCount )
263                 maxColCount = i_data[row].getLength();
264 
265         if ( maxColCount < m_nColumnCount )
266             maxColCount = m_nColumnCount;
267 
268         for ( sal_Int32 row=0; row<rowCount;  ++row )
269         {
270             impl_insertRow( i_index + row, i_headings[row], i_data[row], maxColCount );
271         }
272 
273         if ( maxColCount > m_nColumnCount )
274             m_nColumnCount = maxColCount;
275 
276         broadcast(
277             GridDataEvent( *this, -1, -1, i_index, i_index + rowCount - 1 ),
278             &XGridDataListener::rowsInserted,
279             aGuard
280         );
281     }
282 
283     //------------------------------------------------------------------------------------------------------------------
284     void SAL_CALL DefaultGridDataModel::removeRow( ::sal_Int32 i_rowIndex ) throw (IndexOutOfBoundsException, RuntimeException)
285     {
286         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
287 
288 	    if ( ( i_rowIndex < 0 ) || ( size_t( i_rowIndex ) >= m_aData.size() ) )
289             throw IndexOutOfBoundsException( ::rtl::OUString(), *this );
290 
291         m_aRowHeaders.erase( m_aRowHeaders.begin() + i_rowIndex );
292 	    m_aData.erase( m_aData.begin() + i_rowIndex );
293 
294 	    broadcast(
295             GridDataEvent( *this, -1, -1, i_rowIndex, i_rowIndex ),
296             &XGridDataListener::rowsRemoved,
297             aGuard
298         );
299     }
300 
301     //------------------------------------------------------------------------------------------------------------------
302     void SAL_CALL DefaultGridDataModel::removeAllRows(  ) throw (RuntimeException)
303     {
304         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
305 
306         m_aRowHeaders.clear();
307         m_aData.clear();
308 
309 	    broadcast(
310             GridDataEvent( *this, -1, -1, -1, -1 ),
311             &XGridDataListener::rowsRemoved,
312             aGuard
313         );
314     }
315 
316     //------------------------------------------------------------------------------------------------------------------
317     void SAL_CALL DefaultGridDataModel::updateCellData( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex, const Any& i_value ) throw (IndexOutOfBoundsException, RuntimeException)
318     {
319         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
320 
321         impl_getCellDataAccess_throw( i_columnIndex, i_rowIndex ).first = i_value;
322 
323         broadcast(
324             GridDataEvent( *this, i_columnIndex, i_columnIndex, i_rowIndex, i_rowIndex ),
325             &XGridDataListener::dataChanged,
326             aGuard
327         );
328     }
329 
330     //------------------------------------------------------------------------------------------------------------------
331     void SAL_CALL DefaultGridDataModel::updateRowData( const Sequence< ::sal_Int32 >& i_columnIndexes, ::sal_Int32 i_rowIndex, const Sequence< Any >& i_values ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
332     {
333         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
334 
335         if  ( ( i_rowIndex < 0 ) || ( size_t( i_rowIndex ) >= m_aData.size() ) )
336             throw IndexOutOfBoundsException( ::rtl::OUString(), *this );
337 
338         if ( i_columnIndexes.getLength() != i_values.getLength() )
339             throw IllegalArgumentException( ::rtl::OUString(), *this, 1 );
340 
341         sal_Int32 const columnCount = i_columnIndexes.getLength();
342         if ( columnCount == 0 )
343             return;
344 
345         for ( sal_Int32 col = 0; col < columnCount; ++col )
346         {
347             if ( ( i_columnIndexes[col] < 0 ) || ( i_columnIndexes[col] > m_nColumnCount ) )
348                 throw IndexOutOfBoundsException( ::rtl::OUString(), *this );
349         }
350 
351         RowData& rDataRow = m_aData[ i_rowIndex ];
352         for ( sal_Int32 col = 0; col < columnCount; ++col )
353         {
354             sal_Int32 const columnIndex = i_columnIndexes[ col ];
355             if ( size_t( columnIndex ) >= rDataRow.size() )
356                 rDataRow.resize( columnIndex + 1 );
357 
358             rDataRow[ columnIndex ].first = i_values[ col ];
359         }
360 
361         sal_Int32 const firstAffectedColumn = *::std::min_element( stl_begin( i_columnIndexes ), stl_end( i_columnIndexes ) );
362         sal_Int32 const lastAffectedColumn = *::std::max_element( stl_begin( i_columnIndexes ), stl_end( i_columnIndexes ) );
363         broadcast(
364             GridDataEvent( *this, firstAffectedColumn, lastAffectedColumn, i_rowIndex, i_rowIndex ),
365             &XGridDataListener::dataChanged,
366             aGuard
367         );
368     }
369 
370     //------------------------------------------------------------------------------------------------------------------
371     void SAL_CALL DefaultGridDataModel::updateRowHeading( ::sal_Int32 i_rowIndex, const Any& i_heading ) throw (IndexOutOfBoundsException, RuntimeException)
372     {
373         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
374 
375         if  ( ( i_rowIndex < 0 ) || ( size_t( i_rowIndex ) >= m_aRowHeaders.size() ) )
376             throw IndexOutOfBoundsException( ::rtl::OUString(), *this );
377 
378         m_aRowHeaders[ i_rowIndex ] = i_heading;
379 
380         broadcast(
381             GridDataEvent( *this, -1, -1, i_rowIndex, i_rowIndex ),
382             &XGridDataListener::rowHeadingChanged,
383             aGuard
384         );
385     }
386 
387     //------------------------------------------------------------------------------------------------------------------
388     void SAL_CALL DefaultGridDataModel::updateCellToolTip( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex, const Any& i_value ) throw (IndexOutOfBoundsException, RuntimeException)
389     {
390         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
391         impl_getCellDataAccess_throw( i_columnIndex, i_rowIndex ).second = i_value;
392     }
393 
394     //------------------------------------------------------------------------------------------------------------------
395     void SAL_CALL DefaultGridDataModel::updateRowToolTip( ::sal_Int32 i_rowIndex, const Any& i_value ) throw (IndexOutOfBoundsException, RuntimeException)
396     {
397         ::comphelper::ComponentGuard aGuard( *this, rBHelper );
398 
399         RowData& rRowData = impl_getRowDataAccess_throw( i_rowIndex, m_nColumnCount );
400         for ( RowData::iterator cell = rRowData.begin(); cell != rRowData.end(); ++cell )
401             cell->second = i_value;
402     }
403 
404     //------------------------------------------------------------------------------------------------------------------
405     void SAL_CALL DefaultGridDataModel::addGridDataListener( const Reference< grid::XGridDataListener >& i_listener ) throw (RuntimeException)
406     {
407 	    rBHelper.addListener( XGridDataListener::static_type(), i_listener );
408     }
409 
410     //------------------------------------------------------------------------------------------------------------------
411     void SAL_CALL DefaultGridDataModel::removeGridDataListener( const Reference< grid::XGridDataListener >& i_listener ) throw (RuntimeException)
412     {
413 	    rBHelper.removeListener( XGridDataListener::static_type(), i_listener );
414     }
415 
416     //------------------------------------------------------------------------------------------------------------------
417     void SAL_CALL DefaultGridDataModel::disposing()
418     {
419 	    ::com::sun::star::lang::EventObject aEvent;
420 	    aEvent.Source.set( *this );
421 	    rBHelper.aLC.disposeAndClear( aEvent );
422 
423         ::osl::MutexGuard aGuard( m_aMutex );
424         GridData aEmptyData;
425         m_aData.swap( aEmptyData );
426 
427         ::std::vector< Any > aEmptyRowHeaders;
428         m_aRowHeaders.swap( aEmptyRowHeaders );
429 
430         m_nColumnCount = 0;
431     }
432 
433     //------------------------------------------------------------------------------------------------------------------
434     ::rtl::OUString SAL_CALL DefaultGridDataModel::getImplementationName(  ) throw (RuntimeException)
435     {
436         static const ::rtl::OUString aImplName( RTL_CONSTASCII_USTRINGPARAM( "toolkit.DefaultGridDataModel" ) );
437 	    return aImplName;
438     }
439 
440     //------------------------------------------------------------------------------------------------------------------
441     sal_Bool SAL_CALL DefaultGridDataModel::supportsService( const ::rtl::OUString& ServiceName ) throw (RuntimeException)
442     {
443 	    return ServiceName.equalsAscii( szServiceName_DefaultGridDataModel );
444     }
445 
446     //------------------------------------------------------------------------------------------------------------------
447     Sequence< ::rtl::OUString > SAL_CALL DefaultGridDataModel::getSupportedServiceNames(  ) throw (RuntimeException)
448     {
449 	    static const ::rtl::OUString aServiceName( ::rtl::OUString::createFromAscii( szServiceName_DefaultGridDataModel ) );
450 	    static const Sequence< ::rtl::OUString > aSeq( &aServiceName, 1 );
451 	    return aSeq;
452     }
453 
454     //------------------------------------------------------------------------------------------------------------------
455     Reference< XCloneable > SAL_CALL DefaultGridDataModel::createClone(  ) throw (RuntimeException)
456     {
457         return new DefaultGridDataModel( *this );
458     }
459 
460 //......................................................................................................................
461 }   // namespace toolkit
462 //......................................................................................................................
463 
464 Reference< XInterface > SAL_CALL DefaultGridDataModel_CreateInstance( const Reference< XMultiServiceFactory >& )
465 {
466 	return Reference < XInterface >( ( ::cppu::OWeakObject* ) new ::toolkit::DefaultGridDataModel() );
467 }
468