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_svtools.hxx"
26 
27 #include "svtools/table/tablecontrol.hxx"
28 #include "svtools/table/defaultinputhandler.hxx"
29 #include "svtools/table/tablemodel.hxx"
30 
31 #include "tabledatawindow.hxx"
32 #include "tablecontrol_impl.hxx"
33 #include "tablegeometry.hxx"
34 
35 /** === begin UNO includes === **/
36 #include <com/sun/star/accessibility/XAccessible.hpp>
37 #include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
38 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
39 #include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
40 /** === end UNO includes === **/
41 
42 #include <comphelper/flagguard.hxx>
43 #include <vcl/scrbar.hxx>
44 #include <vcl/seleng.hxx>
45 #include <rtl/ref.hxx>
46 #include <vcl/image.hxx>
47 #include <tools/diagnose_ex.h>
48 
49 #include <functional>
50 #include <numeric>
51 
52 #define MIN_COLUMN_WIDTH_PIXEL  4
53 
54 //......................................................................................................................
55 namespace svt { namespace table
56 {
57 //......................................................................................................................
58 
59     /** === begin UNO using === **/
60     using ::com::sun::star::accessibility::AccessibleTableModelChange;
61     using ::com::sun::star::uno::makeAny;
62     using ::com::sun::star::uno::Any;
63     using ::com::sun::star::accessibility::XAccessible;
64     using ::com::sun::star::uno::Reference;
65     /** === end UNO using === **/
66     namespace AccessibleEventId = ::com::sun::star::accessibility::AccessibleEventId;
67     namespace AccessibleTableModelChangeType = ::com::sun::star::accessibility::AccessibleTableModelChangeType;
68 
69     //==================================================================================================================
70 	//= SuppressCursor
71     //==================================================================================================================
72     class SuppressCursor
73     {
74     private:
75         ITableControl&  m_rTable;
76 
77     public:
SuppressCursor(ITableControl & _rTable)78         SuppressCursor( ITableControl& _rTable )
79             :m_rTable( _rTable )
80         {
81             m_rTable.hideCursor();
82         }
~SuppressCursor()83         ~SuppressCursor()
84         {
85             m_rTable.showCursor();
86         }
87     };
88 
89     //====================================================================
90 	//= EmptyTableModel
91 	//====================================================================
92     /** default implementation of an ->ITableModel, used as fallback when no
93         real model is present
94 
95         Instances of this class are static in any way, and provide the least
96         necessary default functionality for a table model.
97     */
98     class EmptyTableModel : public ITableModel
99     {
100     public:
EmptyTableModel()101         EmptyTableModel()
102         {
103         }
104 
105         // ITableModel overridables
getColumnCount() const106         virtual TableSize           getColumnCount() const
107         {
108             return 0;
109         }
getRowCount() const110         virtual TableSize           getRowCount() const
111         {
112             return 0;
113         }
hasColumnHeaders() const114         virtual bool                hasColumnHeaders() const
115         {
116             return false;
117         }
hasRowHeaders() const118         virtual bool                hasRowHeaders() const
119         {
120             return false;
121         }
isCellEditable(ColPos col,RowPos row) const122         virtual bool                isCellEditable( ColPos col, RowPos row ) const
123         {
124             (void)col;
125             (void)row;
126             return false;
127         }
getColumnModel(ColPos column)128         virtual PColumnModel        getColumnModel( ColPos column )
129         {
130             DBG_ERROR( "EmptyTableModel::getColumnModel: invalid call!" );
131             (void)column;
132             return PColumnModel();
133         }
getRenderer() const134         virtual PTableRenderer      getRenderer() const
135         {
136             return PTableRenderer();
137         }
getInputHandler() const138         virtual PTableInputHandler  getInputHandler() const
139         {
140             return PTableInputHandler();
141         }
getRowHeight() const142         virtual TableMetrics        getRowHeight() const
143         {
144             return 5 * 100;
145         }
setRowHeight(TableMetrics _nRowHeight)146 		virtual void setRowHeight(TableMetrics _nRowHeight)
147         {
148             (void)_nRowHeight;
149         }
getColumnHeaderHeight() const150         virtual TableMetrics        getColumnHeaderHeight() const
151         {
152             return 0;
153         }
getRowHeaderWidth() const154         virtual TableMetrics        getRowHeaderWidth() const
155         {
156             return 0;
157         }
getVerticalScrollbarVisibility() const158 	    virtual ScrollbarVisibility getVerticalScrollbarVisibility() const
159         {
160 		    return ScrollbarShowNever;
161         }
getHorizontalScrollbarVisibility() const162         virtual ScrollbarVisibility getHorizontalScrollbarVisibility() const
163         {
164 		    return ScrollbarShowNever;
165         }
addTableModelListener(const PTableModelListener & i_listener)166         virtual void addTableModelListener( const PTableModelListener& i_listener )
167         {
168             (void)i_listener;
169         }
removeTableModelListener(const PTableModelListener & i_listener)170         virtual void removeTableModelListener( const PTableModelListener& i_listener )
171         {
172             (void)i_listener;
173         }
getLineColor() const174         virtual ::boost::optional< ::Color > getLineColor() const
175         {
176             return ::boost::optional< ::Color >();
177         }
getHeaderBackgroundColor() const178         virtual ::boost::optional< ::Color > getHeaderBackgroundColor() const
179         {
180             return ::boost::optional< ::Color >();
181         }
getHeaderTextColor() const182         virtual ::boost::optional< ::Color > getHeaderTextColor() const
183         {
184             return ::boost::optional< ::Color >();
185         }
getActiveSelectionBackColor() const186         virtual ::boost::optional< ::Color >    getActiveSelectionBackColor() const
187         {
188             return ::boost::optional< ::Color >();
189         }
getInactiveSelectionBackColor() const190         virtual ::boost::optional< ::Color >    getInactiveSelectionBackColor() const
191         {
192             return ::boost::optional< ::Color >();
193         }
getActiveSelectionTextColor() const194         virtual ::boost::optional< ::Color >    getActiveSelectionTextColor() const
195         {
196             return ::boost::optional< ::Color >();
197         }
getInactiveSelectionTextColor() const198         virtual ::boost::optional< ::Color >    getInactiveSelectionTextColor() const
199         {
200             return ::boost::optional< ::Color >();
201         }
getTextColor() const202         virtual ::boost::optional< ::Color > getTextColor() const
203         {
204             return ::boost::optional< ::Color >();
205         }
getTextLineColor() const206         virtual ::boost::optional< ::Color > getTextLineColor() const
207         {
208             return ::boost::optional< ::Color >();
209         }
getRowBackgroundColors() const210         virtual ::boost::optional< ::std::vector< ::Color > > getRowBackgroundColors() const
211         {
212             return ::boost::optional< ::std::vector< ::Color > >();
213         }
getVerticalAlign() const214         virtual ::com::sun::star::style::VerticalAlignment getVerticalAlign() const
215         {
216 	        return com::sun::star::style::VerticalAlignment(0);
217         }
getSortAdapter()218         virtual ITableDataSort* getSortAdapter()
219         {
220             return NULL;
221         }
isEnabled() const222         virtual bool isEnabled() const
223         {
224             return true;
225         }
getCellContent(ColPos const i_col,RowPos const i_row,::com::sun::star::uno::Any & o_cellContent)226         virtual void getCellContent( ColPos const i_col, RowPos const i_row, ::com::sun::star::uno::Any& o_cellContent )
227         {
228             (void)i_row;
229             (void)i_col;
230             o_cellContent.clear();
231         }
getCellToolTip(ColPos const,RowPos const,::com::sun::star::uno::Any &)232         virtual void getCellToolTip( ColPos const, RowPos const, ::com::sun::star::uno::Any& )
233         {
234         }
getRowHeading(RowPos const i_rowPos) const235         virtual Any getRowHeading( RowPos const i_rowPos ) const
236         {
237             (void)i_rowPos;
238             return Any();
239         }
240     };
241 
242 
243     //====================================================================
244 	//= TableControl_Impl
245 	//====================================================================
246     DBG_NAME( TableControl_Impl )
247 
248 #if DBG_UTIL
249     //====================================================================
250 	//= SuspendInvariants
251 	//====================================================================
252     class SuspendInvariants
253     {
254     private:
255         const TableControl_Impl&    m_rTable;
256         sal_Int32                   m_nSuspendFlags;
257 
258     public:
SuspendInvariants(const TableControl_Impl & _rTable,sal_Int32 _nSuspendFlags)259         SuspendInvariants( const TableControl_Impl& _rTable, sal_Int32 _nSuspendFlags )
260             :m_rTable( _rTable )
261             ,m_nSuspendFlags( _nSuspendFlags )
262         {
263             //DBG_ASSERT( ( m_rTable.m_nRequiredInvariants & m_nSuspendFlags ) == m_nSuspendFlags,
264             //    "SuspendInvariants: cannot suspend what is already suspended!" );
265             const_cast< TableControl_Impl& >( m_rTable ).m_nRequiredInvariants &= ~m_nSuspendFlags;
266         }
~SuspendInvariants()267         ~SuspendInvariants()
268         {
269             const_cast< TableControl_Impl& >( m_rTable ).m_nRequiredInvariants |= m_nSuspendFlags;
270         }
271     };
272     #define DBG_SUSPEND_INV( flags ) \
273         SuspendInvariants aSuspendInv( *this, flags );
274 #else
275     #define DBG_SUSPEND_INV( flags )
276 #endif
277 
278 #if DBG_UTIL
279 	//====================================================================
TableControl_Impl_checkInvariants(const void * _pInstance)280     const char* TableControl_Impl_checkInvariants( const void* _pInstance )
281     {
282         return static_cast< const TableControl_Impl* >( _pInstance )->impl_checkInvariants();
283     }
284 
285     namespace
286     {
287         template< typename SCALAR_TYPE >
lcl_checkLimitsExclusive(SCALAR_TYPE _nValue,SCALAR_TYPE _nMin,SCALAR_TYPE _nMax)288         bool lcl_checkLimitsExclusive( SCALAR_TYPE _nValue, SCALAR_TYPE _nMin, SCALAR_TYPE _nMax )
289         {
290             return ( _nValue > _nMin ) && ( _nValue < _nMax );
291         }
292 
293         template< typename SCALAR_TYPE >
lcl_checkLimitsExclusive_OrDefault_OrFallback(SCALAR_TYPE _nValue,SCALAR_TYPE _nMin,SCALAR_TYPE _nMax,PTableModel _pModel,SCALAR_TYPE _nDefaultOrFallback)294         bool lcl_checkLimitsExclusive_OrDefault_OrFallback( SCALAR_TYPE _nValue, SCALAR_TYPE _nMin, SCALAR_TYPE _nMax,
295             PTableModel _pModel, SCALAR_TYPE _nDefaultOrFallback )
296         {
297             if ( !_pModel )
298                 return _nValue == _nDefaultOrFallback;
299             if ( _nMax <= _nMin )
300                 return _nDefaultOrFallback == _nValue;
301             return lcl_checkLimitsExclusive( _nValue, _nMin, _nMax );
302         }
303     }
304 
305     //------------------------------------------------------------------------------------------------------------------
impl_checkInvariants() const306     const sal_Char* TableControl_Impl::impl_checkInvariants() const
307     {
308         if ( !m_pModel )
309             return "no model, not even an EmptyTableModel";
310 
311         if ( !m_pDataWindow )
312             return "invalid data window!";
313 
314         if ( m_pModel->getColumnCount() != m_nColumnCount )
315             return "column counts are inconsistent!";
316 
317         if ( m_pModel->getRowCount() != m_nRowCount )
318             return "row counts are inconsistent!";
319 
320         if ( ( m_nCurColumn != COL_INVALID ) && !m_aColumnWidths.empty() && ( m_nCurColumn < 0 ) || ( m_nCurColumn >= (ColPos)m_aColumnWidths.size() ) )
321             return "current column is invalid!";
322 
323         if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nTopRow, (RowPos)-1, m_nRowCount, getModel(), (RowPos)0 ) )
324             return "invalid top row value!";
325 
326         if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nCurRow, (RowPos)-1, m_nRowCount, getModel(), ROW_INVALID ) )
327             return "invalid current row value!";
328 
329         if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nLeftColumn, (ColPos)-1, m_nColumnCount, getModel(), (ColPos)0 ) )
330             return "invalid current column value!";
331 
332         if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nCurColumn, (ColPos)-1, m_nColumnCount, getModel(), COL_INVALID ) )
333             return "invalid current column value!";
334 
335         if  ( m_pInputHandler != m_pModel->getInputHandler() )
336             return "input handler is not the model-provided one!";
337 
338         // m_aSelectedRows should have reasonable content
339         {
340             if ( m_aSelectedRows.size() > size_t( m_pModel->getRowCount() ) )
341                 return "there are more rows selected than actually exist";
342             for (   ::std::vector< RowPos >::const_iterator selRow = m_aSelectedRows.begin();
343                     selRow != m_aSelectedRows.end();
344                     ++selRow
345                 )
346             {
347                 if ( ( *selRow < 0 ) || ( *selRow >= m_pModel->getRowCount() ) )
348                     return "a non-existent row is selected";
349             }
350         }
351 
352         // m_nColHeaderHeightPixel consistent with the model's value?
353         {
354             TableMetrics nHeaderHeight = m_pModel->hasColumnHeaders() ? m_pModel->getColumnHeaderHeight() : 0;
355 			nHeaderHeight = m_rAntiImpl.LogicToPixel( Size( 0, nHeaderHeight ), MAP_APPFONT ).Height();
356             if ( nHeaderHeight != m_nColHeaderHeightPixel )
357                 return "column header heights are inconsistent!";
358         }
359 
360         bool isDummyModel = dynamic_cast< const EmptyTableModel* >( m_pModel.get() ) != NULL;
361         if ( !isDummyModel )
362         {
363             TableMetrics nRowHeight = m_pModel->getRowHeight();
364 			nRowHeight = m_rAntiImpl.LogicToPixel( Size( 0, nRowHeight ), MAP_APPFONT).Height();
365             if ( nRowHeight != m_nRowHeightPixel )
366                 return "row heights are inconsistent!";
367         }
368 
369         // m_nRowHeaderWidthPixel consistent with the model's value?
370         {
371             TableMetrics nHeaderWidth = m_pModel->hasRowHeaders() ? m_pModel->getRowHeaderWidth() : 0;
372 			nHeaderWidth = m_rAntiImpl.LogicToPixel( Size( nHeaderWidth, 0 ), MAP_APPFONT ).Width();
373             if ( nHeaderWidth != m_nRowHeaderWidthPixel )
374                 return "row header widths are inconsistent!";
375         }
376 
377         // m_aColumnWidths consistency
378         if ( size_t( m_nColumnCount ) != m_aColumnWidths.size() )
379             return "wrong number of cached column widths";
380 
381         for (   ColumnPositions::const_iterator col = m_aColumnWidths.begin();
382                 col != m_aColumnWidths.end();
383             )
384         {
385             if ( col->getEnd() < col->getStart() )
386                 return "column widths: 'end' is expected to not be smaller than start";
387 
388             ColumnPositions::const_iterator nextCol = col + 1;
389             if ( nextCol != m_aColumnWidths.end() )
390                 if ( col->getEnd() != nextCol->getStart() )
391                     return "column widths: one column's end should be the next column's start";
392             col = nextCol;
393         }
394 
395         if ( m_nLeftColumn < m_nColumnCount )
396             if ( m_aColumnWidths[ m_nLeftColumn ].getStart() != m_nRowHeaderWidthPixel )
397                 return "the left-most column should start immediately after the row header";
398 
399         if ( m_nCursorHidden < 0 )
400             return "invalid hidden count for the cursor!";
401 
402         if ( ( m_nRequiredInvariants & INV_SCROLL_POSITION ) && m_pVScroll )
403         {
404             DBG_SUSPEND_INV( INV_SCROLL_POSITION );
405                 // prevent infinite recursion
406 
407             if ( m_nLeftColumn < 0 )
408                 return "invalid left-most column index";
409             if ( m_pVScroll->GetThumbPos() != m_nTopRow )
410                 return "vertical scroll bar |position| is incorrect!";
411             if ( m_pVScroll->GetRange().Max() != m_nRowCount )
412                 return "vertical scroll bar |range| is incorrect!";
413             if ( m_pVScroll->GetVisibleSize() != impl_getVisibleRows( false ) )
414                 return "vertical scroll bar |visible size| is incorrect!";
415         }
416 
417         if ( ( m_nRequiredInvariants & INV_SCROLL_POSITION ) && m_pHScroll )
418         {
419             DBG_SUSPEND_INV( INV_SCROLL_POSITION );
420                 // prevent infinite recursion
421 
422             if ( m_pHScroll->GetThumbPos() != m_nLeftColumn )
423                 return "horizontal scroll bar |position| is incorrect!";
424             if ( m_pHScroll->GetRange().Max() != m_nColumnCount )
425                 return "horizontal scroll bar |range| is incorrect!";
426             if ( m_pHScroll->GetVisibleSize() != impl_getVisibleColumns( false ) )
427                 return "horizontal scroll bar |visible size| is incorrect!";
428         }
429 
430         return NULL;
431     }
432 #endif
433 
434 #define DBG_CHECK_ME() \
435     DBG_CHKTHIS( TableControl_Impl, TableControl_Impl_checkInvariants )
436 
437     //------------------------------------------------------------------------------------------------------------------
TableControl_Impl(TableControl & _rAntiImpl)438     TableControl_Impl::TableControl_Impl( TableControl& _rAntiImpl )
439         :m_rAntiImpl            ( _rAntiImpl                    )
440         ,m_pModel               ( new EmptyTableModel           )
441         ,m_pInputHandler        (                               )
442         ,m_nRowHeightPixel      ( 15                            )
443         ,m_nColHeaderHeightPixel( 0                             )
444         ,m_nRowHeaderWidthPixel ( 0                             )
445         ,m_nColumnCount         ( 0                             )
446         ,m_nRowCount            ( 0                             )
447         ,m_bColumnsFit          ( true                          )
448         ,m_nCurColumn           ( COL_INVALID                   )
449         ,m_nCurRow              ( ROW_INVALID                   )
450         ,m_nLeftColumn          ( 0                             )
451         ,m_nTopRow              ( 0                             )
452         ,m_nCursorHidden        ( 1                             )
453         ,m_pDataWindow          ( new TableDataWindow( *this )  )
454         ,m_pVScroll             ( NULL                          )
455         ,m_pHScroll             ( NULL                          )
456         ,m_pScrollCorner        ( NULL                          )
457         ,m_pSelEngine           (                               )
458         ,m_aSelectedRows        (                               )
459         ,m_pTableFunctionSet    ( new TableFunctionSet( this  ) )
460         ,m_nAnchor              ( -1                            )
461         ,m_bUpdatingColWidths   ( false                         )
462         ,m_pAccessibleTable     ( NULL                          )
463 #if DBG_UTIL
464         ,m_nRequiredInvariants ( INV_SCROLL_POSITION )
465 #endif
466     {
467         DBG_CTOR( TableControl_Impl, TableControl_Impl_checkInvariants );
468         m_pSelEngine = new SelectionEngine( m_pDataWindow.get(), m_pTableFunctionSet );
469         m_pSelEngine->SetSelectionMode(SINGLE_SELECTION);
470         m_pDataWindow->SetPosPixel( Point( 0, 0 ) );
471         m_pDataWindow->Show();
472     }
473 
474 	//------------------------------------------------------------------------------------------------------------------
~TableControl_Impl()475     TableControl_Impl::~TableControl_Impl()
476     {
477         DBG_DTOR( TableControl_Impl, TableControl_Impl_checkInvariants );
478 
479         DELETEZ( m_pVScroll );
480         DELETEZ( m_pHScroll );
481         DELETEZ( m_pScrollCorner );
482 		DELETEZ( m_pTableFunctionSet );
483 		DELETEZ( m_pSelEngine );
484     }
485 
486 	//------------------------------------------------------------------------------------------------------------------
setModel(PTableModel _pModel)487     void TableControl_Impl::setModel( PTableModel _pModel )
488     {
489         DBG_CHECK_ME();
490 
491         SuppressCursor aHideCursor( *this );
492 
493         if ( !!m_pModel )
494             m_pModel->removeTableModelListener( shared_from_this() );
495 
496         m_pModel = _pModel;
497         if ( !m_pModel)
498             m_pModel.reset( new EmptyTableModel );
499 
500         m_pModel->addTableModelListener( shared_from_this() );
501 
502         m_nCurRow = ROW_INVALID;
503         m_nCurColumn = COL_INVALID;
504 
505         // recalc some model-dependent cached info
506         impl_ni_updateCachedModelValues();
507         impl_ni_relayout();
508 
509         // completely invalidate
510         m_rAntiImpl.Invalidate();
511 
512         // reset cursor to (0,0)
513         if ( m_nRowCount ) m_nCurRow = 0;
514         if ( m_nColumnCount ) m_nCurColumn = 0;
515     }
516 
517 	//------------------------------------------------------------------------------------------------------------------
518     namespace
519     {
lcl_adjustSelectedRows(::std::vector<RowPos> & io_selectionIndexes,RowPos const i_firstAffectedRowIndex,TableSize const i_offset)520         bool lcl_adjustSelectedRows( ::std::vector< RowPos >& io_selectionIndexes, RowPos const i_firstAffectedRowIndex, TableSize const i_offset )
521         {
522             bool didChanges = false;
523             for (   ::std::vector< RowPos >::iterator selPos = io_selectionIndexes.begin();
524                     selPos != io_selectionIndexes.end();
525                     ++selPos
526                 )
527             {
528                 if ( *selPos < i_firstAffectedRowIndex )
529                     continue;
530                 *selPos += i_offset;
531                 didChanges = true;
532             }
533             return didChanges;
534         }
535     }
536 
537 	//------------------------------------------------------------------------------------------------------------------
rowsInserted(RowPos i_first,RowPos i_last)538     void TableControl_Impl::rowsInserted( RowPos i_first, RowPos i_last )
539     {
540         DBG_CHECK_ME();
541         OSL_PRECOND( i_last >= i_first, "TableControl_Impl::rowsInserted: invalid row indexes!" );
542 
543         TableSize const insertedRows = i_last - i_first + 1;
544 
545         // adjust selection, if necessary
546         bool const selectionChanged = lcl_adjustSelectedRows( m_aSelectedRows, i_first, insertedRows );
547 
548         // adjust our cached row count
549         m_nRowCount = m_pModel->getRowCount();
550 
551         // if the rows have been inserted before the current row, adjust this
552         if ( i_first <= m_nCurRow )
553             goTo( m_nCurColumn, m_nCurRow + insertedRows );
554 
555         // relayout, since the scrollbar need might have changed
556         impl_ni_relayout();
557 
558         // notify A1YY events
559         if ( impl_isAccessibleAlive() )
560 	    {
561             impl_commitAccessibleEvent( AccessibleEventId::TABLE_MODEL_CHANGED,
562                 makeAny( AccessibleTableModelChange( AccessibleTableModelChangeType::INSERT, i_first, i_last, 0, m_pModel->getColumnCount() ) ),
563 			    Any()
564             );
565 	    }
566 
567         // schedule repaint
568         invalidateRowRange( i_first, ROW_INVALID );
569 
570         // call selection handlers, if necessary
571         if ( selectionChanged )
572             m_rAntiImpl.Select();
573     }
574 
575 	//------------------------------------------------------------------------------------------------------------------
rowsRemoved(RowPos i_first,RowPos i_last)576     void TableControl_Impl::rowsRemoved( RowPos i_first, RowPos i_last )
577     {
578         sal_Int32 firstRemovedRow = i_first;
579         sal_Int32 lastRemovedRow = i_last;
580 
581         // adjust selection, if necessary
582         bool selectionChanged = false;
583 	    if ( i_first == -1 )
584 	    {
585             selectionChanged = markAllRowsAsDeselected();
586 
587             firstRemovedRow = 0;
588             lastRemovedRow = m_nRowCount - 1;
589 	    }
590 	    else
591 	    {
592             ENSURE_OR_RETURN_VOID( i_last >= i_first, "TableControl_Impl::rowsRemoved: illegal indexes!" );
593 
594             for ( sal_Int32 row = i_first; row <= i_last; ++row )
595             {
596                 if ( markRowAsDeselected( row ) )
597                     selectionChanged = true;
598             }
599 
600             if ( lcl_adjustSelectedRows( m_aSelectedRows, i_last + 1, i_first - i_last - 1 ) )
601                 selectionChanged = true;
602 	    }
603 
604         // adjust cached row count
605         m_nRowCount = m_pModel->getRowCount();
606 
607         // adjust the current row, if it is larger than the row count now
608         if ( m_nCurRow >= m_nRowCount )
609         {
610             if ( m_nRowCount > 0 )
611                 goTo( m_nCurColumn, m_nRowCount - 1 );
612             else
613             {
614                 m_nCurRow = ROW_INVALID;
615                 m_nTopRow = 0;
616             }
617         }
618         else if ( m_nRowCount == 0 )
619         {
620             m_nTopRow = 0;
621         }
622 
623 
624         // relayout, since the scrollbar need might have changed
625         impl_ni_relayout();
626 
627         // notify A11Y events
628         if ( impl_isAccessibleAlive() )
629 	    {
630 		    commitTableEvent(
631                 AccessibleEventId::TABLE_MODEL_CHANGED,
632                 makeAny( AccessibleTableModelChange(
633                     AccessibleTableModelChangeType::DELETE,
634                     firstRemovedRow,
635                     lastRemovedRow,
636                     0,
637                     m_pModel->getColumnCount()
638                 ) ),
639 			    Any()
640             );
641 	    }
642 
643         // schedule a repaint
644         invalidateRowRange( firstRemovedRow, ROW_INVALID );
645 
646         // call selection handlers, if necessary
647         if ( selectionChanged )
648             m_rAntiImpl.Select();
649     }
650 
651 	//------------------------------------------------------------------------------------------------------------------
columnInserted(ColPos const i_colIndex)652     void TableControl_Impl::columnInserted( ColPos const i_colIndex )
653     {
654         m_nColumnCount = m_pModel->getColumnCount();
655         impl_ni_relayout();
656 
657         m_rAntiImpl.Invalidate();
658 
659         OSL_UNUSED( i_colIndex );
660    }
661 
662 	//------------------------------------------------------------------------------------------------------------------
columnRemoved(ColPos const i_colIndex)663     void TableControl_Impl::columnRemoved( ColPos const i_colIndex )
664     {
665         m_nColumnCount = m_pModel->getColumnCount();
666 
667         // adjust the current column, if it is larger than the column count now
668         if ( m_nCurColumn >= m_nColumnCount )
669         {
670             if ( m_nColumnCount > 0 )
671                 goTo( m_nCurColumn - 1, m_nCurRow );
672             else
673                 m_nCurColumn = COL_INVALID;
674         }
675 
676         impl_ni_relayout();
677 
678         m_rAntiImpl.Invalidate();
679 
680         OSL_UNUSED( i_colIndex );
681     }
682 
683 	//------------------------------------------------------------------------------------------------------------------
allColumnsRemoved()684     void TableControl_Impl::allColumnsRemoved()
685     {
686         m_nColumnCount = m_pModel->getColumnCount();
687         impl_ni_relayout();
688 
689         m_rAntiImpl.Invalidate();
690     }
691 
692 	//------------------------------------------------------------------------------------------------------------------
cellsUpdated(ColPos const i_firstCol,ColPos i_lastCol,RowPos const i_firstRow,RowPos const i_lastRow)693     void TableControl_Impl::cellsUpdated( ColPos const i_firstCol, ColPos i_lastCol, RowPos const i_firstRow, RowPos const i_lastRow )
694     {
695 		invalidateRowRange( i_firstRow, i_lastRow );
696 
697         OSL_UNUSED( i_firstCol );
698         OSL_UNUSED( i_lastCol );
699     }
700 
701 	//------------------------------------------------------------------------------------------------------------------
tableMetricsChanged()702     void TableControl_Impl::tableMetricsChanged()
703     {
704         impl_ni_updateCachedTableMetrics();
705         impl_ni_relayout();
706         m_rAntiImpl.Invalidate();
707     }
708 
709 	//------------------------------------------------------------------------------------------------------------------
impl_invalidateColumn(ColPos const i_column)710     void TableControl_Impl::impl_invalidateColumn( ColPos const i_column )
711     {
712         DBG_CHECK_ME();
713 
714         Rectangle const aAllCellsArea( impl_getAllVisibleCellsArea() );
715 
716         const TableColumnGeometry aColumn( *this, aAllCellsArea, i_column );
717         if ( aColumn.isValid() )
718             m_rAntiImpl.Invalidate( aColumn.getRect() );
719     }
720 
721     //------------------------------------------------------------------------------------------------------------------
columnChanged(ColPos const i_column,ColumnAttributeGroup const i_attributeGroup)722     void TableControl_Impl::columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup )
723     {
724         ColumnAttributeGroup nGroup( i_attributeGroup );
725         if ( nGroup & COL_ATTRS_APPEARANCE )
726         {
727             impl_invalidateColumn( i_column );
728             nGroup &= ~COL_ATTRS_APPEARANCE;
729         }
730 
731         if ( nGroup & COL_ATTRS_WIDTH )
732         {
733             if ( !m_bUpdatingColWidths )
734             {
735                 impl_ni_relayout( i_column );
736                 invalidate( TableAreaAll );
737             }
738 
739             nGroup &= ~COL_ATTRS_WIDTH;
740         }
741 
742         OSL_ENSURE( ( nGroup == COL_ATTRS_NONE ) || ( i_attributeGroup == COL_ATTRS_ALL ),
743             "TableControl_Impl::columnChanged: don't know how to handle this change!" );
744     }
745 
746 	//------------------------------------------------------------------------------------------------------------------
impl_getAllVisibleCellsArea() const747     Rectangle TableControl_Impl::impl_getAllVisibleCellsArea() const
748     {
749         DBG_CHECK_ME();
750 
751         Rectangle aArea( Point( 0, 0 ), Size( 0, 0 ) );
752 
753         // determine the right-most border of the last column which is
754         // at least partially visible
755         aArea.Right() = m_nRowHeaderWidthPixel;
756         if ( !m_aColumnWidths.empty() )
757         {
758             // the number of pixels which are scrolled out of the left hand
759             // side of the window
760             const long nScrolledOutLeft = m_nLeftColumn == 0 ? 0 : m_aColumnWidths[ m_nLeftColumn - 1 ].getEnd();
761 
762             ColumnPositions::const_reverse_iterator loop = m_aColumnWidths.rbegin();
763             do
764             {
765                 aArea.Right() = loop->getEnd() - nScrolledOutLeft + m_nRowHeaderWidthPixel;
766                 ++loop;
767             }
768             while ( (   loop != m_aColumnWidths.rend() )
769                  && (   loop->getEnd() - nScrolledOutLeft >= aArea.Right() )
770                  );
771         }
772         // so far, aArea.Right() denotes the first pixel *after* the cell area
773         --aArea.Right();
774 
775         // determine the last row which is at least partially visible
776         aArea.Bottom() =
777                 m_nColHeaderHeightPixel
778             +   impl_getVisibleRows( true ) * m_nRowHeightPixel
779             -   1;
780 
781         return aArea;
782     }
783 
784 	//------------------------------------------------------------------------------------------------------------------
impl_getAllVisibleDataCellArea() const785     Rectangle TableControl_Impl::impl_getAllVisibleDataCellArea() const
786     {
787         DBG_CHECK_ME();
788 
789         Rectangle aArea( impl_getAllVisibleCellsArea() );
790         aArea.Left() = m_nRowHeaderWidthPixel;
791         aArea.Top() = m_nColHeaderHeightPixel;
792         return aArea;
793     }
794 
795     //------------------------------------------------------------------------------------------------------------------
impl_ni_updateCachedTableMetrics()796     void TableControl_Impl::impl_ni_updateCachedTableMetrics()
797     {
798         m_nRowHeightPixel = m_rAntiImpl.LogicToPixel( Size( 0, m_pModel->getRowHeight() ), MAP_APPFONT ).Height();
799 
800         m_nColHeaderHeightPixel = 0;
801 	    if ( m_pModel->hasColumnHeaders() )
802            m_nColHeaderHeightPixel = m_rAntiImpl.LogicToPixel( Size( 0, m_pModel->getColumnHeaderHeight() ), MAP_APPFONT ).Height();
803 
804         m_nRowHeaderWidthPixel = 0;
805         if ( m_pModel->hasRowHeaders() )
806             m_nRowHeaderWidthPixel = m_rAntiImpl.LogicToPixel( Size( m_pModel->getRowHeaderWidth(), 0 ), MAP_APPFONT).Width();
807     }
808 
809     //------------------------------------------------------------------------------------------------------------------
impl_ni_updateCachedModelValues()810     void TableControl_Impl::impl_ni_updateCachedModelValues()
811     {
812         m_pInputHandler = m_pModel->getInputHandler();
813         if ( !m_pInputHandler )
814             m_pInputHandler.reset( new DefaultInputHandler );
815 
816         m_nColumnCount = m_pModel->getColumnCount();
817         if ( m_nLeftColumn >= m_nColumnCount )
818             m_nLeftColumn = ( m_nColumnCount > 0 ) ? m_nColumnCount - 1 : 0;
819 
820         m_nRowCount = m_pModel->getRowCount();
821         if ( m_nTopRow >= m_nRowCount )
822             m_nTopRow = ( m_nRowCount > 0 ) ? m_nRowCount - 1 : 0;
823 
824         impl_ni_updateCachedTableMetrics();
825     }
826 
827     //------------------------------------------------------------------------------------------------------------------
828     namespace
829     {
830 	    //..............................................................................................................
831         /// determines whether a scrollbar is needed for the given values
lcl_determineScrollbarNeed(long const i_position,ScrollbarVisibility const i_visibility,long const i_availableSpace,long const i_neededSpace)832         bool lcl_determineScrollbarNeed( long const i_position, ScrollbarVisibility const i_visibility,
833             long const i_availableSpace, long const i_neededSpace )
834         {
835             if ( i_visibility == ScrollbarShowNever )
836                 return false;
837             if ( i_visibility == ScrollbarShowAlways )
838                 return true;
839             if ( i_position > 0 )
840                 return true;
841             if ( i_availableSpace >= i_neededSpace )
842                 return false;
843             return true;
844         }
845 
846 	    //..............................................................................................................
lcl_setButtonRepeat(Window & _rWindow,sal_uLong _nDelay)847         void lcl_setButtonRepeat( Window& _rWindow, sal_uLong _nDelay )
848         {
849             AllSettings aSettings = _rWindow.GetSettings();
850 	        MouseSettings aMouseSettings = aSettings.GetMouseSettings();
851 
852             aMouseSettings.SetButtonRepeat( _nDelay );
853 	        aSettings.SetMouseSettings( aMouseSettings );
854 
855 	        _rWindow.SetSettings( aSettings, sal_True );
856         }
857 
858 	    //..............................................................................................................
lcl_updateScrollbar(Window & _rParent,ScrollBar * & _rpBar,bool const i_needBar,long _nVisibleUnits,long _nPosition,long _nLineSize,long _nRange,bool _bHorizontal,const Link & _rScrollHandler)859         bool lcl_updateScrollbar( Window& _rParent, ScrollBar*& _rpBar,
860             bool const i_needBar, long _nVisibleUnits,
861             long _nPosition, long _nLineSize, long _nRange,
862             bool _bHorizontal, const Link& _rScrollHandler )
863         {
864             // do we currently have the scrollbar?
865             bool bHaveBar = _rpBar != NULL;
866 
867             // do we need to correct the scrollbar visibility?
868             if ( bHaveBar && !i_needBar )
869             {
870                 if ( _rpBar->IsTracking() )
871                     _rpBar->EndTracking();
872                 DELETEZ( _rpBar );
873             }
874             else if ( !bHaveBar && i_needBar )
875             {
876                 _rpBar = new ScrollBar(
877                     &_rParent,
878                     WB_DRAG | ( _bHorizontal ? WB_HSCROLL : WB_VSCROLL )
879                 );
880                 _rpBar->SetScrollHdl( _rScrollHandler );
881                 // get some speed into the scrolling ....
882                 lcl_setButtonRepeat( *_rpBar, 0 );
883             }
884 
885             if ( _rpBar )
886             {
887                 _rpBar->SetRange( Range( 0, _nRange ) );
888                 _rpBar->SetVisibleSize( _nVisibleUnits );
889                 _rpBar->SetPageSize( _nVisibleUnits );
890                 _rpBar->SetLineSize( _nLineSize );
891                 _rpBar->SetThumbPos( _nPosition );
892                 _rpBar->Show();
893             }
894 
895             return ( bHaveBar != i_needBar );
896         }
897 
898 	    //..............................................................................................................
899         /** returns the number of rows fitting into the given range,
900             for the given row height. Partially fitting rows are counted, too, if the
901             respective parameter says so.
902         */
lcl_getRowsFittingInto(long _nOverallHeight,long _nRowHeightPixel,bool _bAcceptPartialRow=false)903         TableSize lcl_getRowsFittingInto( long _nOverallHeight, long _nRowHeightPixel, bool _bAcceptPartialRow = false )
904         {
905             return  _bAcceptPartialRow
906                 ?   ( _nOverallHeight + ( _nRowHeightPixel - 1 ) ) / _nRowHeightPixel
907                 :   _nOverallHeight / _nRowHeightPixel;
908         }
909 
910 	    //..............................................................................................................
911         /** returns the number of columns fitting into the given area,
912             with the first visible column as given. Partially fitting columns are counted, too,
913             if the respective parameter says so.
914         */
lcl_getColumnsVisibleWithin(const Rectangle & _rArea,ColPos _nFirstVisibleColumn,const TableControl_Impl & _rControl,bool _bAcceptPartialRow)915         TableSize lcl_getColumnsVisibleWithin( const Rectangle& _rArea, ColPos _nFirstVisibleColumn,
916             const TableControl_Impl& _rControl, bool _bAcceptPartialRow )
917         {
918             TableSize visibleColumns = 0;
919             TableColumnGeometry aColumn( _rControl, _rArea, _nFirstVisibleColumn );
920             while ( aColumn.isValid() )
921             {
922                 if ( !_bAcceptPartialRow )
923                     if ( aColumn.getRect().Right() > _rArea.Right() )
924                         // this column is only partially visible, and this is not allowed
925                         break;
926 
927                 aColumn.moveRight();
928                 ++visibleColumns;
929             }
930             return visibleColumns;
931         }
932 
933     }
934 
935 	//------------------------------------------------------------------------------------------------------------------
impl_ni_calculateColumnWidths(ColPos const i_assumeInflexibleColumnsUpToIncluding,bool const i_assumeVerticalScrollbar,::std::vector<long> & o_newColWidthsPixel) const936     long TableControl_Impl::impl_ni_calculateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding,
937         bool const i_assumeVerticalScrollbar, ::std::vector< long >& o_newColWidthsPixel ) const
938     {
939         // the available horizontal space
940 		long gridWidthPixel = m_rAntiImpl.GetOutputSizePixel().Width();
941         ENSURE_OR_RETURN( !!m_pModel, "TableControl_Impl::impl_ni_calculateColumnWidths: not allowed without a model!", gridWidthPixel );
942 		if ( m_pModel->hasRowHeaders() && ( gridWidthPixel != 0 ) )
943 		{
944 			gridWidthPixel -= m_nRowHeaderWidthPixel;
945 		}
946 
947         if ( i_assumeVerticalScrollbar && ( m_pModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ) )
948 		{
949 	        long nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize();
950 			gridWidthPixel -= nScrollbarMetrics;
951 		}
952 
953         // no need to do anything without columns
954         TableSize const colCount = m_pModel->getColumnCount();
955 		if ( colCount == 0 )
956             return gridWidthPixel;
957 
958         // collect some meta data for our columns:
959         // - their current (pixel) metrics
960         long accumulatedCurrentWidth = 0;
961         ::std::vector< long > currentColWidths;
962         currentColWidths.reserve( colCount );
963         typedef ::std::vector< ::std::pair< long, long > >   ColumnLimits;
964         ColumnLimits effectiveColumnLimits;
965         effectiveColumnLimits.reserve( colCount );
966         long accumulatedMinWidth = 0;
967         long accumulatedMaxWidth = 0;
968         // - their relative flexibility
969         ::std::vector< ::sal_Int32 > columnFlexibilities;
970         columnFlexibilities.reserve( colCount );
971         long flexibilityDenominator = 0;
972         size_t flexibleColumnCount = 0;
973         for ( ColPos col = 0; col < colCount; ++col )
974         {
975 			PColumnModel const pColumn = m_pModel->getColumnModel( col );
976             ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" );
977 
978             // current width
979             long const currentWidth = appFontWidthToPixel( pColumn->getWidth() );
980             currentColWidths.push_back( currentWidth );
981 
982             // accumulated width
983             accumulatedCurrentWidth += currentWidth;
984 
985             // flexibility
986             ::sal_Int32 flexibility = pColumn->getFlexibility();
987             OSL_ENSURE( flexibility >= 0, "TableControl_Impl::impl_ni_calculateColumnWidths: a column's flexibility should be non-negative." );
988             if  (   ( flexibility < 0 )                                 // normalization
989                 ||  ( !pColumn->isResizable() )                         // column not resizeable => no auto-resize
990                 ||  ( col <= i_assumeInflexibleColumnsUpToIncluding )   // column shall be treated as inflexible => respec this
991                 )
992                 flexibility = 0;
993 
994             // min/max width
995             long effectiveMin = currentWidth, effectiveMax = currentWidth;
996             // if the column is not flexible, it will not be asked for min/max, but we assume the current width as limit then
997             if ( flexibility > 0 )
998             {
999                 long const minWidth = appFontWidthToPixel( pColumn->getMinWidth() );
1000                 if ( minWidth > 0 )
1001                     effectiveMin = minWidth;
1002                 else
1003                     effectiveMin = MIN_COLUMN_WIDTH_PIXEL;
1004 
1005                 long const maxWidth = appFontWidthToPixel( pColumn->getMaxWidth() );
1006                 OSL_ENSURE( minWidth <= maxWidth, "TableControl_Impl::impl_ni_calculateColumnWidths: pretty undecided 'bout its width limits, this column!" );
1007                 if ( ( maxWidth > 0 ) && ( maxWidth >= minWidth ) )
1008                     effectiveMax = maxWidth;
1009                 else
1010                     effectiveMax = gridWidthPixel; // TODO: any better guess here?
1011 
1012                 if ( effectiveMin == effectiveMax )
1013                     // if the min and the max are identical, this implies no flexibility at all
1014                     flexibility = 0;
1015             }
1016 
1017             columnFlexibilities.push_back( flexibility );
1018             flexibilityDenominator += flexibility;
1019             if ( flexibility > 0 )
1020                 ++flexibleColumnCount;
1021 
1022             effectiveColumnLimits.push_back( ::std::pair< long, long >( effectiveMin, effectiveMax ) );
1023             accumulatedMinWidth += effectiveMin;
1024             accumulatedMaxWidth += effectiveMax;
1025         }
1026 
1027         o_newColWidthsPixel = currentColWidths;
1028         if ( flexibilityDenominator == 0 )
1029         {
1030             // no column is flexible => don't adjust anything
1031         }
1032         else if ( gridWidthPixel > accumulatedCurrentWidth )
1033         {   // we have space to give away ...
1034             long distributePixel = gridWidthPixel - accumulatedCurrentWidth;
1035             if ( gridWidthPixel > accumulatedMaxWidth )
1036             {
1037                 // ... but the column's maximal widths are still less than we have
1038                 // => set them all to max
1039                 for ( size_t i = 0; i < size_t( colCount ); ++i )
1040                 {
1041                     o_newColWidthsPixel[i] = effectiveColumnLimits[i].second;
1042                 }
1043             }
1044             else
1045             {
1046                 bool startOver = false;
1047                 do
1048                 {
1049                     startOver = false;
1050                     // distribute the remaining space amongst all columns with a positive flexibility
1051                     for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i )
1052                     {
1053                         long const columnFlexibility = columnFlexibilities[i];
1054                         if ( columnFlexibility == 0 )
1055                             continue;
1056 
1057                         long newColWidth = currentColWidths[i] + columnFlexibility * distributePixel / flexibilityDenominator;
1058 
1059                         if ( newColWidth > effectiveColumnLimits[i].second )
1060                         {   // that was too much, we hit the col's maximum
1061                             // set the new width to exactly this maximum
1062                             newColWidth = effectiveColumnLimits[i].second;
1063                             // adjust the flexibility denominator ...
1064                             flexibilityDenominator -= columnFlexibility;
1065                             columnFlexibilities[i] = 0;
1066                             --flexibleColumnCount;
1067                             // ... and the remaining width ...
1068                             long const difference = newColWidth - currentColWidths[i];
1069                             distributePixel -= difference;
1070                             // ... this way, we ensure that the width not taken up by this column is consumed by the other
1071                             // flexible ones (if there are some)
1072 
1073                             // and start over with the first column, since there might be earlier columns which need
1074                             // to be recalculated now
1075                             startOver = true;
1076                         }
1077 
1078                         o_newColWidthsPixel[i] = newColWidth;
1079                     }
1080                 }
1081                 while ( startOver );
1082 
1083                 // are there pixels left (might be caused by rounding errors)?
1084                 distributePixel = gridWidthPixel - ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 );
1085                 while ( ( distributePixel > 0 ) && ( flexibleColumnCount > 0 ) )
1086                 {
1087                     // yes => ignore relative flexibilities, and subsequently distribute single pixels to all flexible
1088                     // columns which did not yet reach their maximum.
1089                     for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( distributePixel > 0 ); ++i )
1090                     {
1091                         if ( columnFlexibilities[i] == 0 )
1092                             continue;
1093 
1094                         OSL_ENSURE( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].second,
1095                             "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" );
1096                         if ( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first )
1097                         {
1098                             columnFlexibilities[i] = 0;
1099                             --flexibleColumnCount;
1100                             continue;
1101                         }
1102 
1103                         ++o_newColWidthsPixel[i];
1104                         --distributePixel;
1105                     }
1106                 }
1107             }
1108         }
1109         else if ( gridWidthPixel < accumulatedCurrentWidth )
1110         {   // we need to take away some space from the columns which allow it ...
1111             long takeAwayPixel = accumulatedCurrentWidth - gridWidthPixel;
1112             if ( gridWidthPixel < accumulatedMinWidth )
1113             {
1114                 // ... but the column's minimal widths are still more than we have
1115                 // => set them all to min
1116                 for ( size_t i = 0; i < size_t( colCount ); ++i )
1117                 {
1118                     o_newColWidthsPixel[i] = effectiveColumnLimits[i].first;
1119                 }
1120             }
1121             else
1122             {
1123                 bool startOver = false;
1124                 do
1125                 {
1126                     startOver = false;
1127                     // take away the space we need from the columns with a positive flexibility
1128                     for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i )
1129                     {
1130                         long const columnFlexibility = columnFlexibilities[i];
1131                         if ( columnFlexibility == 0 )
1132                             continue;
1133 
1134                         long newColWidth = currentColWidths[i] - columnFlexibility * takeAwayPixel / flexibilityDenominator;
1135 
1136                         if ( newColWidth < effectiveColumnLimits[i].first )
1137                         {   // that was too much, we hit the col's minimum
1138                             // set the new width to exactly this minimum
1139                             newColWidth = effectiveColumnLimits[i].first;
1140                             // adjust the flexibility denominator ...
1141                             flexibilityDenominator -= columnFlexibility;
1142                             columnFlexibilities[i] = 0;
1143                             --flexibleColumnCount;
1144                             // ... and the remaining width ...
1145                             long const difference = currentColWidths[i] - newColWidth;
1146                             takeAwayPixel -= difference;
1147 
1148                             // and start over with the first column, since there might be earlier columns which need
1149                             // to be recalculated now
1150                             startOver = true;
1151                         }
1152 
1153                         o_newColWidthsPixel[i] = newColWidth;
1154                     }
1155                 }
1156                 while ( startOver );
1157 
1158                 // are there pixels left (might be caused by rounding errors)?
1159                 takeAwayPixel = ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 ) - gridWidthPixel;
1160                 while ( ( takeAwayPixel > 0 ) && ( flexibleColumnCount > 0 ) )
1161                 {
1162                     // yes => ignore relative flexibilities, and subsequently take away pixels from all flexible
1163                     // columns which did not yet reach their minimum.
1164                     for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( takeAwayPixel > 0 ); ++i )
1165                     {
1166                         if ( columnFlexibilities[i] == 0 )
1167                             continue;
1168 
1169                         OSL_ENSURE( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first,
1170                             "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" );
1171                         if ( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].first )
1172                         {
1173                             columnFlexibilities[i] = 0;
1174                             --flexibleColumnCount;
1175                             continue;
1176                         }
1177 
1178                         --o_newColWidthsPixel[i];
1179                         --takeAwayPixel;
1180                     }
1181                 }
1182             }
1183         }
1184 
1185         return gridWidthPixel;
1186     }
1187 
1188 	//------------------------------------------------------------------------------------------------------------------
impl_ni_relayout(ColPos const i_assumeInflexibleColumnsUpToIncluding)1189     void TableControl_Impl::impl_ni_relayout( ColPos const i_assumeInflexibleColumnsUpToIncluding )
1190     {
1191         ENSURE_OR_RETURN_VOID( !m_bUpdatingColWidths, "TableControl_Impl::impl_ni_relayout: recursive call detected!" );
1192 
1193         m_aColumnWidths.resize( 0 );
1194         if ( !m_pModel )
1195             return;
1196 
1197         ::comphelper::FlagRestorationGuard const aWidthUpdateFlag( m_bUpdatingColWidths, true );
1198         SuppressCursor aHideCursor( *this );
1199 
1200         // layouting steps:
1201         //
1202         // 1. adjust column widths, leaving space for a vertical scrollbar
1203         // 2. determine need for a vertical scrollbar
1204         //    - V-YES: all fine, result from 1. is still valid
1205         //    - V-NO: result from 1. is still under consideration
1206         //
1207         // 3. determine need for a horizontal scrollbar
1208         //   - H-NO: all fine, result from 2. is still valid
1209         //   - H-YES: reconsider need for a vertical scrollbar, if result of 2. was V-NO
1210         //     - V-YES: all fine, result from 1. is still valid
1211         //     - V-NO: redistribute the remaining space (if any) amongst all columns which allow it
1212 
1213         ::std::vector< long > newWidthsPixel;
1214         long gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, true, newWidthsPixel );
1215 
1216         // the width/height of a scrollbar, needed several times below
1217 	    long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize();
1218 
1219         // determine the playground for the data cells (excluding headers)
1220         // TODO: what if the control is smaller than needed for the headers/scrollbars?
1221         Rectangle aDataCellPlayground( Point( 0, 0 ), m_rAntiImpl.GetOutputSizePixel() );
1222         aDataCellPlayground.Left() = m_nRowHeaderWidthPixel;
1223         aDataCellPlayground.Top() = m_nColHeaderHeightPixel;
1224 
1225         OSL_ENSURE( ( m_nRowCount == m_pModel->getRowCount() ) && ( m_nColumnCount == m_pModel->getColumnCount() ),
1226             "TableControl_Impl::impl_ni_relayout: how is this expected to work with invalid data?" );
1227         long const nAllColumnsWidth = ::std::accumulate( newWidthsPixel.begin(), newWidthsPixel.end(), 0 );
1228 
1229         ScrollbarVisibility const eVertScrollbar = m_pModel->getVerticalScrollbarVisibility();
1230         ScrollbarVisibility const eHorzScrollbar = m_pModel->getHorizontalScrollbarVisibility();
1231 
1232         // do we need a vertical scrollbar?
1233         bool bNeedVerticalScrollbar = lcl_determineScrollbarNeed(
1234             m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount );
1235         bool bFirstRoundVScrollNeed = false;
1236         if ( bNeedVerticalScrollbar )
1237         {
1238             aDataCellPlayground.Right() -= nScrollbarMetrics;
1239             bFirstRoundVScrollNeed = true;
1240         }
1241 
1242         // do we need a horizontal scrollbar?
1243         bool const bNeedHorizontalScrollbar = lcl_determineScrollbarNeed(
1244             m_nLeftColumn, eHorzScrollbar, aDataCellPlayground.GetWidth(), nAllColumnsWidth );
1245         if ( bNeedHorizontalScrollbar )
1246         {
1247             aDataCellPlayground.Bottom() -= nScrollbarMetrics;
1248 
1249             // now that we just found that we need a horizontal scrollbar,
1250             // the need for a vertical one may have changed, since the horizontal
1251             // SB might just occupy enough space so that not all rows do fit
1252             // anymore
1253             if  ( !bFirstRoundVScrollNeed )
1254             {
1255                 bNeedVerticalScrollbar = lcl_determineScrollbarNeed(
1256                     m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount );
1257                 if ( bNeedVerticalScrollbar )
1258                 {
1259                     aDataCellPlayground.Right() -= nScrollbarMetrics;
1260                 }
1261             }
1262         }
1263 
1264         // the initial call to impl_ni_calculateColumnWidths assumed that we need a vertical scrollbar. If, by now,
1265         // we know that this is not the case, re-calculate the column widths.
1266         if ( !bNeedVerticalScrollbar )
1267             gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, false, newWidthsPixel );
1268 
1269         // update the column objects with the new widths we finally calculated
1270         TableSize const colCount = m_pModel->getColumnCount();
1271         m_aColumnWidths.reserve( colCount );
1272         long accumulatedWidthPixel = m_nRowHeaderWidthPixel;
1273         bool anyColumnWidthChanged = false;
1274         for ( ColPos col = 0; col < colCount; ++col )
1275         {
1276             const long columnStart = accumulatedWidthPixel;
1277             const long columnEnd = columnStart + newWidthsPixel[col];
1278 			m_aColumnWidths.push_back( MutableColumnMetrics( columnStart, columnEnd ) );
1279             accumulatedWidthPixel = columnEnd;
1280 
1281             // and don't forget to forward this to the column models
1282 			PColumnModel const pColumn = m_pModel->getColumnModel( col );
1283             ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" );
1284 
1285             long const oldColumnWidthAppFont = pColumn->getWidth();
1286             long const newColumnWidthAppFont = pixelWidthToAppFont( newWidthsPixel[col] );
1287             pColumn->setWidth( newColumnWidthAppFont );
1288 
1289             anyColumnWidthChanged |= ( oldColumnWidthAppFont != newColumnWidthAppFont );
1290         }
1291 
1292         // if the column widths changed, ensure everything is repainted
1293         if ( anyColumnWidthChanged )
1294             invalidate( TableAreaAll );
1295 
1296         // if the column resizing happened to leave some space at the right, but there are columns
1297         // scrolled out to the left, scroll them in
1298         while   (   ( m_nLeftColumn > 0 )
1299                 &&  ( accumulatedWidthPixel - m_aColumnWidths[ m_nLeftColumn - 1 ].getStart() <= gridWidthPixel )
1300                 )
1301         {
1302             --m_nLeftColumn;
1303         }
1304 
1305         // now adjust the column metrics, since they currently ignore the horizontal scroll position
1306         if ( m_nLeftColumn > 0 )
1307         {
1308             const long offsetPixel = m_aColumnWidths[ 0 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getStart();
1309             for (   ColumnPositions::iterator colPos = m_aColumnWidths.begin();
1310                     colPos != m_aColumnWidths.end();
1311                     ++colPos
1312                  )
1313             {
1314                 colPos->move( offsetPixel );
1315             }
1316         }
1317 
1318         // show or hide the scrollbars as needed, and position the data window
1319         impl_ni_positionChildWindows( aDataCellPlayground, bNeedVerticalScrollbar, bNeedHorizontalScrollbar );
1320     }
1321 
1322 	//------------------------------------------------------------------------------------------------------------------
impl_ni_positionChildWindows(Rectangle const & i_dataCellPlayground,bool const i_verticalScrollbar,bool const i_horizontalScrollbar)1323     void TableControl_Impl::impl_ni_positionChildWindows( Rectangle const & i_dataCellPlayground,
1324         bool const i_verticalScrollbar, bool const i_horizontalScrollbar )
1325     {
1326 	    long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize();
1327 
1328         // create or destroy the vertical scrollbar, as needed
1329         lcl_updateScrollbar(
1330             m_rAntiImpl,
1331             m_pVScroll,
1332             i_verticalScrollbar,
1333             lcl_getRowsFittingInto( i_dataCellPlayground.GetHeight(), m_nRowHeightPixel ),
1334                                                                     // visible units
1335             m_nTopRow,                                              // current position
1336             1,                                                      // line size
1337             m_nRowCount,                                            // range
1338             false,                                                  // vertical
1339             LINK( this, TableControl_Impl, OnScroll )               // scroll handler
1340         );
1341 
1342         // position it
1343         if ( m_pVScroll )
1344         {
1345             Rectangle aScrollbarArea(
1346                 Point( i_dataCellPlayground.Right() + 1, 0 ),
1347                 Size( nScrollbarMetrics, i_dataCellPlayground.Bottom() + 1 )
1348             );
1349             m_pVScroll->SetPosSizePixel(
1350                 aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() );
1351         }
1352 
1353         // create or destroy the horizontal scrollbar, as needed
1354         lcl_updateScrollbar(
1355             m_rAntiImpl,
1356             m_pHScroll,
1357             i_horizontalScrollbar,
1358             lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ),
1359                                                                     // visible units
1360             m_nLeftColumn,                                          // current position
1361             1,                                                      // line size
1362             m_nColumnCount,                                         // range
1363             true,                                                   // horizontal
1364             LINK( this, TableControl_Impl, OnScroll )               // scroll handler
1365         );
1366 
1367         // position it
1368         if ( m_pHScroll )
1369         {
1370 			TableSize const nVisibleUnits = lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false );
1371 			TableMetrics const nRange = m_nColumnCount;
1372 			if( m_nLeftColumn + nVisibleUnits == nRange - 1 )
1373 			{
1374 				if ( m_aColumnWidths[ nRange - 1 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getEnd() + m_aColumnWidths[ nRange-1 ].getWidth() > i_dataCellPlayground.GetWidth() )
1375 				{
1376 					m_pHScroll->SetVisibleSize( nVisibleUnits -1 );
1377 					m_pHScroll->SetPageSize( nVisibleUnits - 1 );
1378 				}
1379 			}
1380             Rectangle aScrollbarArea(
1381                 Point( 0, i_dataCellPlayground.Bottom() + 1 ),
1382                 Size( i_dataCellPlayground.Right() + 1, nScrollbarMetrics )
1383             );
1384             m_pHScroll->SetPosSizePixel(
1385                 aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() );
1386         }
1387 
1388         // the corner window connecting the two scrollbars in the lower right corner
1389         bool bHaveScrollCorner = NULL != m_pScrollCorner;
1390         bool bNeedScrollCorner = ( NULL != m_pHScroll ) && ( NULL != m_pVScroll );
1391         if ( bHaveScrollCorner && !bNeedScrollCorner )
1392         {
1393             DELETEZ( m_pScrollCorner );
1394         }
1395         else if ( !bHaveScrollCorner && bNeedScrollCorner )
1396         {
1397             m_pScrollCorner = new ScrollBarBox( &m_rAntiImpl );
1398             m_pScrollCorner->SetSizePixel( Size( nScrollbarMetrics, nScrollbarMetrics ) );
1399             m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) );
1400             m_pScrollCorner->Show();
1401         }
1402 		else if(bHaveScrollCorner && bNeedScrollCorner)
1403 		{
1404 			m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) );
1405             m_pScrollCorner->Show();
1406         }
1407 
1408         // resize the data window
1409         m_pDataWindow->SetSizePixel( Size(
1410             i_dataCellPlayground.GetWidth() + m_nRowHeaderWidthPixel,
1411             i_dataCellPlayground.GetHeight() + m_nColHeaderHeightPixel
1412         ) );
1413     }
1414 
1415     //------------------------------------------------------------------------------------------------------------------
onResize()1416     void TableControl_Impl::onResize()
1417     {
1418         DBG_CHECK_ME();
1419 
1420         impl_ni_relayout();
1421 		checkCursorPosition();
1422     }
1423 
1424     //------------------------------------------------------------------------------------------------------------------
doPaintContent(const Rectangle & _rUpdateRect)1425     void TableControl_Impl::doPaintContent( const Rectangle& _rUpdateRect )
1426     {
1427         DBG_CHECK_ME();
1428 
1429         if ( !getModel() )
1430             return;
1431         PTableRenderer pRenderer = getModel()->getRenderer();
1432         DBG_ASSERT( !!pRenderer, "TableDataWindow::doPaintContent: invalid renderer!" );
1433         if ( !pRenderer )
1434             return;
1435 
1436         // our current style settings, to be passed to the renderer
1437         const StyleSettings& rStyle = m_rAntiImpl.GetSettings().GetStyleSettings();
1438 		m_nRowCount = m_pModel->getRowCount();
1439         // the area occupied by all (at least partially) visible cells, including
1440         // headers
1441         Rectangle const aAllCellsWithHeaders( impl_getAllVisibleCellsArea() );
1442 
1443         // ............................
1444         // draw the header column area
1445         if ( m_pModel->hasColumnHeaders() )
1446         {
1447             TableRowGeometry const aHeaderRow( *this, Rectangle( Point( 0, 0 ),
1448                 aAllCellsWithHeaders.BottomRight() ), ROW_COL_HEADERS );
1449 			Rectangle const aColRect(aHeaderRow.getRect());
1450             pRenderer->PaintHeaderArea(
1451                 *m_pDataWindow, aColRect, true, false, rStyle
1452             );
1453             // Note that strictly, aHeaderRow.getRect() also contains the intersection between column
1454             // and row header area. However, below we go to paint this intersection, again,
1455             // so this hopefully doesn't hurt if we already paint it here.
1456 
1457             for ( TableCellGeometry aCell( aHeaderRow, m_nLeftColumn );
1458                   aCell.isValid();
1459                   aCell.moveRight()
1460                 )
1461             {
1462                 if ( _rUpdateRect.GetIntersection( aCell.getRect() ).IsEmpty() )
1463                     continue;
1464 
1465                 bool isActiveColumn = ( aCell.getColumn() == getCurrentColumn() );
1466                 bool isSelectedColumn = false;
1467                 pRenderer->PaintColumnHeader( aCell.getColumn(), isActiveColumn, isSelectedColumn,
1468                     *m_pDataWindow, aCell.getRect(), rStyle );
1469             }
1470         }
1471         // the area occupied by the row header, if any
1472         Rectangle aRowHeaderArea;
1473         if ( m_pModel->hasRowHeaders() )
1474         {
1475             aRowHeaderArea = aAllCellsWithHeaders;
1476             aRowHeaderArea.Right() = m_nRowHeaderWidthPixel - 1;
1477 
1478 		    TableSize const nVisibleRows = impl_getVisibleRows( true );
1479             TableSize nActualRows = nVisibleRows;
1480 		    if ( m_nTopRow + nActualRows > m_nRowCount )
1481 			    nActualRows = m_nRowCount - m_nTopRow;
1482 			aRowHeaderArea.Bottom() = m_nColHeaderHeightPixel + m_nRowHeightPixel * nActualRows - 1;
1483 
1484             pRenderer->PaintHeaderArea( *m_pDataWindow, aRowHeaderArea, false, true, rStyle );
1485             // Note that strictly, aRowHeaderArea also contains the intersection between column
1486             // and row header area. However, below we go to paint this intersection, again,
1487             // so this hopefully doesn't hurt if we already paint it here.
1488 
1489             if ( m_pModel->hasColumnHeaders() )
1490             {
1491                 TableCellGeometry const aIntersection( *this, Rectangle( Point( 0, 0 ),
1492                     aAllCellsWithHeaders.BottomRight() ), COL_ROW_HEADERS, ROW_COL_HEADERS );
1493 				Rectangle const aInters( aIntersection.getRect() );
1494                 pRenderer->PaintHeaderArea(
1495                     *m_pDataWindow, aInters, true, true, rStyle
1496                 );
1497             }
1498 		}
1499 
1500         // ............................
1501         // draw the table content row by row
1502 
1503         TableSize colCount = getModel()->getColumnCount();
1504 
1505         // paint all rows
1506         Rectangle const aAllDataCellsArea( impl_getAllVisibleDataCellArea() );
1507         for ( TableRowGeometry aRowIterator( *this, aAllCellsWithHeaders, getTopRow() );
1508               aRowIterator.isValid();
1509               aRowIterator.moveDown() )
1510         {
1511             if ( _rUpdateRect.GetIntersection( aRowIterator.getRect() ).IsEmpty() )
1512 				continue;
1513 
1514             bool const isControlFocused = m_rAntiImpl.HasControlFocus();
1515 			bool const isSelectedRow = isRowSelected( aRowIterator.getRow() );
1516 
1517             Rectangle const aRect = aRowIterator.getRect().GetIntersection( aAllDataCellsArea );
1518 
1519             // give the redenderer a chance to prepare the row
1520             pRenderer->PrepareRow(
1521                 aRowIterator.getRow(), isControlFocused, isSelectedRow,
1522 			    *m_pDataWindow, aRect, rStyle
1523             );
1524 
1525             // paint the row header
1526             if ( m_pModel->hasRowHeaders() )
1527             {
1528 				const Rectangle aCurrentRowHeader( aRowHeaderArea.GetIntersection( aRowIterator.getRect() ) );
1529 				pRenderer->PaintRowHeader( isControlFocused, isSelectedRow, *m_pDataWindow, aCurrentRowHeader,
1530                     rStyle );
1531             }
1532 
1533             if ( !colCount )
1534                 continue;
1535 
1536             // paint all cells in this row
1537             for ( TableCellGeometry aCell( aRowIterator, m_nLeftColumn );
1538                   aCell.isValid();
1539                   aCell.moveRight()
1540                 )
1541             {
1542 				bool isSelectedColumn = false;
1543                 pRenderer->PaintCell( aCell.getColumn(), isSelectedRow || isSelectedColumn, isControlFocused,
1544 								*m_pDataWindow, aCell.getRect(), rStyle );
1545 			}
1546         }
1547     }
1548 	//------------------------------------------------------------------------------------------------------------------
hideCursor()1549     void TableControl_Impl::hideCursor()
1550     {
1551         DBG_CHECK_ME();
1552 
1553         if ( ++m_nCursorHidden == 1 )
1554             impl_ni_doSwitchCursor( false );
1555     }
1556 
1557 	//------------------------------------------------------------------------------------------------------------------
showCursor()1558     void TableControl_Impl::showCursor()
1559     {
1560         DBG_CHECK_ME();
1561 
1562         DBG_ASSERT( m_nCursorHidden > 0, "TableControl_Impl::showCursor: cursor not hidden!" );
1563         if ( --m_nCursorHidden == 0 )
1564             impl_ni_doSwitchCursor( true );
1565     }
1566 
1567 	//------------------------------------------------------------------------------------------------------------------
dispatchAction(TableControlAction _eAction)1568     bool TableControl_Impl::dispatchAction( TableControlAction _eAction )
1569     {
1570         DBG_CHECK_ME();
1571 
1572         bool bSuccess = false;
1573         bool selectionChanged = false;
1574 
1575         switch ( _eAction )
1576         {
1577         case cursorDown:
1578 		if ( m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION )
1579 		{
1580 			//if other rows already selected, deselect them
1581 			if ( m_aSelectedRows.size()>0 )
1582 			{
1583                 invalidateSelectedRows();
1584 				m_aSelectedRows.clear();
1585 			}
1586 			if ( m_nCurRow < m_nRowCount-1 )
1587 			{
1588 				++m_nCurRow;
1589 				m_aSelectedRows.push_back(m_nCurRow);
1590 			}
1591 			else
1592 				m_aSelectedRows.push_back(m_nCurRow);
1593 			invalidateRow( m_nCurRow );
1594 			ensureVisible(m_nCurColumn,m_nCurRow,false);
1595             selectionChanged = true;
1596 			bSuccess = true;
1597 		}
1598 		else
1599 		{
1600 			if ( m_nCurRow < m_nRowCount - 1 )
1601 				bSuccess = goTo( m_nCurColumn, m_nCurRow + 1 );
1602 		}
1603             break;
1604 
1605         case cursorUp:
1606 		if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1607 		{
1608 			if(m_aSelectedRows.size()>0)
1609 			{
1610                 invalidateSelectedRows();
1611 				m_aSelectedRows.clear();
1612 			}
1613 			if(m_nCurRow>0)
1614 			{
1615 				--m_nCurRow;
1616 				m_aSelectedRows.push_back(m_nCurRow);
1617 				invalidateRow( m_nCurRow );
1618 			}
1619 			else
1620 			{
1621 				m_aSelectedRows.push_back(m_nCurRow);
1622 				invalidateRow( m_nCurRow );
1623 			}
1624 			ensureVisible(m_nCurColumn,m_nCurRow,false);
1625 			selectionChanged = true;
1626 			bSuccess = true;
1627 		}
1628 		else
1629 		{
1630 			if ( m_nCurRow > 0 )
1631 				bSuccess = goTo( m_nCurColumn, m_nCurRow - 1 );
1632 		}
1633 		break;
1634         case cursorLeft:
1635             if ( m_nCurColumn > 0 )
1636                 bSuccess = goTo( m_nCurColumn - 1, m_nCurRow );
1637             else
1638                 if ( ( m_nCurColumn == 0) && ( m_nCurRow > 0 ) )
1639                     bSuccess = goTo( m_nColumnCount - 1, m_nCurRow - 1 );
1640             break;
1641 
1642         case cursorRight:
1643             if ( m_nCurColumn < m_nColumnCount - 1 )
1644                 bSuccess = goTo( m_nCurColumn + 1, m_nCurRow );
1645             else
1646                 if ( ( m_nCurColumn == m_nColumnCount - 1 ) && ( m_nCurRow < m_nRowCount - 1 ) )
1647                     bSuccess = goTo( 0, m_nCurRow + 1 );
1648             break;
1649 
1650         case cursorToLineStart:
1651             bSuccess = goTo( 0, m_nCurRow );
1652             break;
1653 
1654         case cursorToLineEnd:
1655             bSuccess = goTo( m_nColumnCount - 1, m_nCurRow );
1656             break;
1657 
1658         case cursorToFirstLine:
1659             bSuccess = goTo( m_nCurColumn, 0 );
1660             break;
1661 
1662         case cursorToLastLine:
1663             bSuccess = goTo( m_nCurColumn, m_nRowCount - 1 );
1664             break;
1665 
1666         case cursorPageUp:
1667         {
1668             RowPos nNewRow = ::std::max( (RowPos)0, m_nCurRow - impl_getVisibleRows( false ) );
1669             bSuccess = goTo( m_nCurColumn, nNewRow );
1670         }
1671         break;
1672 
1673         case cursorPageDown:
1674         {
1675             RowPos nNewRow = ::std::min( m_nRowCount - 1, m_nCurRow + impl_getVisibleRows( false ) );
1676             bSuccess = goTo( m_nCurColumn, nNewRow );
1677         }
1678         break;
1679 
1680         case cursorTopLeft:
1681             bSuccess = goTo( 0, 0 );
1682             break;
1683 
1684         case cursorBottomRight:
1685             bSuccess = goTo( m_nColumnCount - 1, m_nRowCount - 1 );
1686             break;
1687 
1688 	    case cursorSelectRow:
1689 	    {
1690 		    if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1691 			    return bSuccess = false;
1692 		    //pos is the position of the current row in the vector of selected rows, if current row is selected
1693 		    int pos = getRowSelectedNumber(m_aSelectedRows, m_nCurRow);
1694 		    //if current row is selected, it should be deselected, when ALT+SPACE are pressed
1695 		    if(pos>-1)
1696 		    {
1697 			    m_aSelectedRows.erase(m_aSelectedRows.begin()+pos);
1698 			    if(m_aSelectedRows.empty() && m_nAnchor != -1)
1699 				    m_nAnchor = -1;
1700 		    }
1701 		    //else select the row->put it in the vector
1702 		    else
1703 			    m_aSelectedRows.push_back(m_nCurRow);
1704 		    invalidateRow( m_nCurRow );
1705 		    selectionChanged = true;
1706 		    bSuccess = true;
1707 	    }
1708             break;
1709 	    case cursorSelectRowUp:
1710 	    {
1711 		    if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1712 			    return bSuccess = false;
1713 		    else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1714 		    {
1715 			    //if there are other selected rows, deselect them
1716 			    return false;
1717 		    }
1718 		    else
1719 		    {
1720 			    //there are other selected rows
1721 			    if(m_aSelectedRows.size()>0)
1722 			    {
1723 				    //the anchor wasn't set -> a region is not selected, that's why clear all selection
1724 				    //and select the current row
1725 				    if(m_nAnchor==-1)
1726 				    {
1727                         invalidateSelectedRows();
1728 					    m_aSelectedRows.clear();
1729 					    m_aSelectedRows.push_back(m_nCurRow);
1730 					    invalidateRow( m_nCurRow );
1731 				    }
1732 				    else
1733 				    {
1734 					    //a region is already selected, prevRow is last selected row and the row above - nextRow - should be selected
1735 					    int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow);
1736 					    int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow-1);
1737 					    if(prevRow>-1)
1738  					    {
1739  						    //if m_nCurRow isn't the upper one, can move up, otherwise not
1740 						    if(m_nCurRow>0)
1741  							    m_nCurRow--;
1742  						    else
1743  							    return bSuccess = true;
1744  						    //if nextRow already selected, deselect it, otherwise select it
1745  						    if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow)
1746  						    {
1747  							    m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow);
1748  							    invalidateRow( m_nCurRow + 1 );
1749  						    }
1750  						    else
1751 						    {
1752  							    m_aSelectedRows.push_back(m_nCurRow);
1753  							    invalidateRow( m_nCurRow );
1754  						    }
1755  					    }
1756 					    else
1757 					    {
1758 						    if(m_nCurRow>0)
1759 						    {
1760 							    m_aSelectedRows.push_back(m_nCurRow);
1761 							    m_nCurRow--;
1762 							    m_aSelectedRows.push_back(m_nCurRow);
1763 							    invalidateSelectedRegion( m_nCurRow+1, m_nCurRow );
1764 						    }
1765 					    }
1766 				    }
1767 			    }
1768 			    else
1769 			    {
1770 				    //if nothing is selected and the current row isn't the upper one
1771 				    //select the current and one row above
1772 				    //otherwise select only the upper row
1773 				    if(m_nCurRow>0)
1774 				    {
1775 					    m_aSelectedRows.push_back(m_nCurRow);
1776 					    m_nCurRow--;
1777 					    m_aSelectedRows.push_back(m_nCurRow);
1778 					    invalidateSelectedRegion( m_nCurRow+1, m_nCurRow );
1779 				    }
1780 				    else
1781 				    {
1782 					    m_aSelectedRows.push_back(m_nCurRow);
1783 					    invalidateRow( m_nCurRow );
1784 				    }
1785 			    }
1786 			    m_pSelEngine->SetAnchor(sal_True);
1787 			    m_nAnchor = m_nCurRow;
1788 			    ensureVisible(m_nCurColumn, m_nCurRow, false);
1789 			    selectionChanged = true;
1790 			    bSuccess = true;
1791 		    }
1792 	    }
1793 	    break;
1794 	    case cursorSelectRowDown:
1795 	    {
1796 		    if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1797 			    bSuccess = false;
1798 		    else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1799 		    {
1800 			    bSuccess = false;
1801 		    }
1802 		    else
1803 		    {
1804 			    if(m_aSelectedRows.size()>0)
1805 			    {
1806 				    //the anchor wasn't set -> a region is not selected, that's why clear all selection
1807 				    //and select the current row
1808 				    if(m_nAnchor==-1)
1809 				    {
1810                         invalidateSelectedRows();
1811 					    m_aSelectedRows.clear();
1812 					    m_aSelectedRows.push_back(m_nCurRow);
1813 					    invalidateRow( m_nCurRow );
1814 					    }
1815 				    else
1816 				    {
1817 					    //a region is already selected, prevRow is last selected row and the row beneath - nextRow - should be selected
1818 					    int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow);
1819 					    int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow+1);
1820 					    if(prevRow>-1)
1821  					    {
1822  						    //if m_nCurRow isn't the last one, can move down, otherwise not
1823  						    if(m_nCurRow<m_nRowCount-1)
1824  							    m_nCurRow++;
1825  						    else
1826 							    return bSuccess = true;
1827  						    //if next row already selected, deselect it, otherwise select it
1828  						    if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow)
1829  						    {
1830  							    m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow);
1831  							    invalidateRow( m_nCurRow - 1 );
1832  						    }
1833  						    else
1834  						    {
1835  							    m_aSelectedRows.push_back(m_nCurRow);
1836  							    invalidateRow( m_nCurRow );
1837  						    }
1838 					    }
1839 					    else
1840 					    {
1841 						    if(m_nCurRow<m_nRowCount-1)
1842 						    {
1843 							    m_aSelectedRows.push_back(m_nCurRow);
1844 							    m_nCurRow++;
1845 							    m_aSelectedRows.push_back(m_nCurRow);
1846 							    invalidateSelectedRegion( m_nCurRow-1, m_nCurRow );
1847 						    }
1848 					    }
1849 				    }
1850 			    }
1851 			    else
1852 			    {
1853 				    //there wasn't any selection, select current and row beneath, otherwise only row beneath
1854 				    if(m_nCurRow<m_nRowCount-1)
1855 				    {
1856 					    m_aSelectedRows.push_back(m_nCurRow);
1857 					    m_nCurRow++;
1858 					    m_aSelectedRows.push_back(m_nCurRow);
1859 					    invalidateSelectedRegion( m_nCurRow-1, m_nCurRow );
1860 				    }
1861 				    else
1862 				    {
1863 					    m_aSelectedRows.push_back(m_nCurRow);
1864 					    invalidateRow( m_nCurRow );
1865 				    }
1866 			    }
1867 			    m_pSelEngine->SetAnchor(sal_True);
1868 			    m_nAnchor = m_nCurRow;
1869 			    ensureVisible(m_nCurColumn, m_nCurRow, false);
1870 			    selectionChanged = true;
1871 			    bSuccess = true;
1872 		    }
1873 	    }
1874         break;
1875 
1876 	    case cursorSelectRowAreaTop:
1877 	    {
1878 		    if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1879 			    bSuccess = false;
1880 		    else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1881 			    bSuccess = false;
1882 		    else
1883 		    {
1884 			    //select the region between the current and the upper row
1885 			    RowPos iter = m_nCurRow;
1886 			    invalidateSelectedRegion( m_nCurRow, 0 );
1887 			    //put the rows in vector
1888 			    while(iter>=0)
1889 			    {
1890 				    if ( !isRowSelected( iter ) )
1891 					    m_aSelectedRows.push_back(iter);
1892 				    --iter;
1893 			    }
1894 			    m_nCurRow = 0;
1895 			    m_nAnchor = m_nCurRow;
1896 			    m_pSelEngine->SetAnchor(sal_True);
1897 			    ensureVisible(m_nCurColumn, 0, false);
1898 			    selectionChanged = true;
1899 			    bSuccess = true;
1900 		    }
1901 	    }
1902         break;
1903 
1904 	    case cursorSelectRowAreaBottom:
1905 	    {
1906 		    if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1907 			    return bSuccess = false;
1908 		    else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1909 			    return bSuccess = false;
1910 		    //select the region between the current and the last row
1911 		    RowPos iter = m_nCurRow;
1912 		    invalidateSelectedRegion( m_nCurRow, m_nRowCount-1 );
1913 		    //put the rows in the vector
1914 		    while(iter<=m_nRowCount)
1915 		    {
1916 			    if ( !isRowSelected( iter ) )
1917 				    m_aSelectedRows.push_back(iter);
1918 			    ++iter;
1919 		    }
1920 		    m_nCurRow = m_nRowCount-1;
1921 		    m_nAnchor = m_nCurRow;
1922 		    m_pSelEngine->SetAnchor(sal_True);
1923 		    ensureVisible(m_nCurColumn, m_nRowCount-1, false);
1924 		    selectionChanged = true;
1925 		    bSuccess = true;
1926 	    }
1927         break;
1928         default:
1929             DBG_ERROR( "TableControl_Impl::dispatchAction: unsupported action!" );
1930             break;
1931         }
1932 
1933         if ( bSuccess && selectionChanged )
1934         {
1935             m_rAntiImpl.Select();
1936         }
1937 
1938         return bSuccess;
1939     }
1940 
1941 	//------------------------------------------------------------------------------------------------------------------
impl_ni_doSwitchCursor(bool _bShow)1942     void TableControl_Impl::impl_ni_doSwitchCursor( bool _bShow )
1943     {
1944         PTableRenderer pRenderer = !!m_pModel ? m_pModel->getRenderer() : PTableRenderer();
1945         if ( !!pRenderer )
1946         {
1947             Rectangle aCellRect;
1948             impl_getCellRect( m_nCurColumn, m_nCurRow, aCellRect );
1949 			if ( _bShow )
1950 				pRenderer->ShowCellCursor( *m_pDataWindow, aCellRect );
1951 			else
1952 				pRenderer->HideCellCursor( *m_pDataWindow, aCellRect );
1953         }
1954     }
1955 
1956     //------------------------------------------------------------------------------------------------------------------
impl_getCellRect(ColPos _nColumn,RowPos _nRow,Rectangle & _rCellRect) const1957     void TableControl_Impl::impl_getCellRect( ColPos _nColumn, RowPos _nRow, Rectangle& _rCellRect ) const
1958     {
1959         DBG_CHECK_ME();
1960 
1961         if  (   !m_pModel
1962             ||  ( COL_INVALID == _nColumn )
1963             ||  ( ROW_INVALID == _nRow )
1964             )
1965         {
1966             _rCellRect.SetEmpty();
1967             return;
1968         }
1969 
1970         TableCellGeometry aCell( *this, impl_getAllVisibleCellsArea(), _nColumn, _nRow );
1971         _rCellRect = aCell.getRect();
1972     }
1973 
1974     //------------------------------------------------------------------------------------------------------------------
getRowAtPoint(const Point & rPoint) const1975     RowPos TableControl_Impl::getRowAtPoint( const Point& rPoint ) const
1976     {
1977 	    DBG_CHECK_ME();
1978         return impl_getRowForAbscissa( rPoint.Y() );
1979     }
1980 
1981     //------------------------------------------------------------------------------------------------------------------
getColAtPoint(const Point & rPoint) const1982     ColPos TableControl_Impl::getColAtPoint( const Point& rPoint ) const
1983     {
1984 	    DBG_CHECK_ME();
1985         return impl_getColumnForOrdinate( rPoint.X() );
1986     }
1987 
1988     //------------------------------------------------------------------------------------------------------------------
hitTest(Point const & i_point) const1989     TableCell TableControl_Impl::hitTest( Point const & i_point ) const
1990     {
1991         TableCell aCell( getColAtPoint( i_point ), getRowAtPoint( i_point ) );
1992         if ( aCell.nColumn > COL_ROW_HEADERS )
1993         {
1994             PColumnModel const pColumn = m_pModel->getColumnModel( aCell.nColumn );
1995             MutableColumnMetrics const & rColInfo( m_aColumnWidths[ aCell.nColumn ] );
1996             if  (   ( rColInfo.getEnd() - 3 <= i_point.X() )
1997                 &&  ( rColInfo.getEnd() >= i_point.X() )
1998                 &&  pColumn->isResizable()
1999                 )
2000             {
2001                 aCell.eArea = ColumnDivider;
2002             }
2003         }
2004         return aCell;
2005     }
2006 
2007     //------------------------------------------------------------------------------------------------------------------
getColumnMetrics(ColPos const i_column) const2008     ColumnMetrics TableControl_Impl::getColumnMetrics( ColPos const i_column ) const
2009     {
2010         DBG_CHECK_ME();
2011 
2012         ENSURE_OR_RETURN( ( i_column >= 0 ) && ( i_column < m_pModel->getColumnCount() ),
2013             "TableControl_Impl::getColumnMetrics: illegal column index!", ColumnMetrics() );
2014         return (ColumnMetrics const &)m_aColumnWidths[ i_column ];
2015     }
2016 
2017     //------------------------------------------------------------------------------------------------------------------
getModel() const2018     PTableModel TableControl_Impl::getModel() const
2019     {
2020         return m_pModel;
2021     }
2022 
2023     //------------------------------------------------------------------------------------------------------------------
getCurrentColumn() const2024     RowPos TableControl_Impl::getCurrentColumn() const
2025     {
2026         return m_nCurColumn;
2027     }
2028 
2029     //------------------------------------------------------------------------------------------------------------------
getCurrentRow() const2030     RowPos TableControl_Impl::getCurrentRow() const
2031     {
2032         return m_nCurRow;
2033     }
2034 
2035     //------------------------------------------------------------------------------------------------------------------
getTableSizePixel() const2036     ::Size TableControl_Impl::getTableSizePixel() const
2037     {
2038         return m_pDataWindow->GetOutputSizePixel();
2039     }
2040 
2041     //------------------------------------------------------------------------------------------------------------------
setPointer(Pointer const & i_pointer)2042     void TableControl_Impl::setPointer( Pointer const & i_pointer )
2043     {
2044         DBG_CHECK_ME();
2045         m_pDataWindow->SetPointer( i_pointer );
2046     }
2047 
2048     //------------------------------------------------------------------------------------------------------------------
captureMouse()2049     void TableControl_Impl::captureMouse()
2050     {
2051         m_pDataWindow->CaptureMouse();
2052     }
2053 
2054     //------------------------------------------------------------------------------------------------------------------
releaseMouse()2055     void TableControl_Impl::releaseMouse()
2056     {
2057         m_pDataWindow->ReleaseMouse();
2058     }
2059 
2060     //------------------------------------------------------------------------------------------------------------------
invalidate(TableArea const i_what)2061     void TableControl_Impl::invalidate( TableArea const i_what )
2062     {
2063         switch ( i_what )
2064         {
2065         case TableAreaColumnHeaders:
2066             m_pDataWindow->Invalidate( calcHeaderRect( true ) );
2067             break;
2068 
2069         case TableAreaRowHeaders:
2070             m_pDataWindow->Invalidate( calcHeaderRect( false ) );
2071             break;
2072 
2073         case TableAreaDataArea:
2074             m_pDataWindow->Invalidate( impl_getAllVisibleDataCellArea() );
2075             break;
2076 
2077         case TableAreaAll:
2078             m_pDataWindow->Invalidate();
2079             m_pDataWindow->GetParent()->Invalidate( INVALIDATE_TRANSPARENT );
2080             break;
2081         }
2082     }
2083 
2084     //------------------------------------------------------------------------------------------------------------------
pixelWidthToAppFont(long const i_pixels) const2085     long TableControl_Impl::pixelWidthToAppFont( long const i_pixels ) const
2086     {
2087         return m_pDataWindow->PixelToLogic( Size( i_pixels, 0 ), MAP_APPFONT ).Width();
2088     }
2089 
2090     //------------------------------------------------------------------------------------------------------------------
appFontWidthToPixel(long const i_appFontUnits) const2091     long TableControl_Impl::appFontWidthToPixel( long const i_appFontUnits ) const
2092     {
2093         return m_pDataWindow->LogicToPixel( Size( i_appFontUnits, 0 ), MAP_APPFONT ).Width();
2094     }
2095 
2096     //------------------------------------------------------------------------------------------------------------------
hideTracking()2097     void TableControl_Impl::hideTracking()
2098     {
2099         m_pDataWindow->HideTracking();
2100     }
2101 
2102     //------------------------------------------------------------------------------------------------------------------
showTracking(Rectangle const & i_location,sal_uInt16 const i_flags)2103     void TableControl_Impl::showTracking( Rectangle const & i_location, sal_uInt16 const i_flags )
2104     {
2105         m_pDataWindow->ShowTracking( i_location, i_flags );
2106     }
2107 
2108     //------------------------------------------------------------------------------------------------------------------
activateCell(ColPos const i_col,RowPos const i_row)2109     bool TableControl_Impl::activateCell( ColPos const i_col, RowPos const i_row )
2110     {
2111 	    DBG_CHECK_ME();
2112         return goTo( i_col, i_row );
2113     }
2114 
2115     //------------------------------------------------------------------------------------------------------------------
invalidateSelectedRegion(RowPos _nPrevRow,RowPos _nCurRow)2116     void TableControl_Impl::invalidateSelectedRegion( RowPos _nPrevRow, RowPos _nCurRow )
2117     {
2118 	    DBG_CHECK_ME();
2119 	    // get the visible area of the table control and set the Left and right border of the region to be repainted
2120 	    Rectangle const aAllCells( impl_getAllVisibleCellsArea() );
2121 
2122         Rectangle aInvalidateRect;
2123 	    aInvalidateRect.Left() = aAllCells.Left();
2124 	    aInvalidateRect.Right() = aAllCells.Right();
2125 	    // if only one row is selected
2126 	    if ( _nPrevRow == _nCurRow )
2127 	    {
2128 	        Rectangle aCellRect;
2129 		    impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
2130 		    aInvalidateRect.Top() = aCellRect.Top();
2131 		    aInvalidateRect.Bottom() = aCellRect.Bottom();
2132 	    }
2133 	    //if the region is above the current row
2134 	    else if(_nPrevRow < _nCurRow )
2135 	    {
2136 	        Rectangle aCellRect;
2137 		    impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect );
2138 		    aInvalidateRect.Top() = aCellRect.Top();
2139 		    impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
2140 		    aInvalidateRect.Bottom() = aCellRect.Bottom();
2141 	    }
2142 	    //if the region is beneath the current row
2143 	    else
2144 	    {
2145             Rectangle aCellRect;
2146 		    impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
2147 		    aInvalidateRect.Top() = aCellRect.Top();
2148 		    impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect );
2149 		    aInvalidateRect.Bottom() = aCellRect.Bottom();
2150 	    }
2151 	    m_pDataWindow->Invalidate( aInvalidateRect );
2152     }
2153 
2154     //------------------------------------------------------------------------------------------------------------------
invalidateSelectedRows()2155     void TableControl_Impl::invalidateSelectedRows()
2156     {
2157         for (   ::std::vector< RowPos >::iterator selRow = m_aSelectedRows.begin();
2158 				selRow != m_aSelectedRows.end();
2159                 ++selRow
2160             )
2161 		{
2162             invalidateRow( *selRow );
2163 		}
2164     }
2165 
2166     //------------------------------------------------------------------------------------------------------------------
invalidateRowRange(RowPos const i_firstRow,RowPos const i_lastRow)2167     void TableControl_Impl::invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow )
2168     {
2169         RowPos const firstRow = i_firstRow < m_nTopRow ? m_nTopRow : i_firstRow;
2170         RowPos const lastVisibleRow = m_nTopRow + impl_getVisibleRows( true ) - 1;
2171         RowPos const lastRow = ( ( i_lastRow == ROW_INVALID ) || ( i_lastRow > lastVisibleRow ) ) ? lastVisibleRow : i_lastRow;
2172 
2173         Rectangle aInvalidateRect;
2174 
2175         Rectangle const aVisibleCellsArea( impl_getAllVisibleCellsArea() );
2176         TableRowGeometry aRow( *this, aVisibleCellsArea, firstRow, true );
2177         while ( aRow.isValid() && ( aRow.getRow() <= lastRow ) )
2178         {
2179             aInvalidateRect.Union( aRow.getRect() );
2180             aRow.moveDown();
2181         }
2182 
2183         if ( i_lastRow == ROW_INVALID )
2184             aInvalidateRect.Bottom() = m_pDataWindow->GetOutputSizePixel().Height();
2185 
2186         m_pDataWindow->Invalidate( aInvalidateRect,
2187             m_pDataWindow->GetControlBackground().GetTransparency() ? INVALIDATE_TRANSPARENT : 0 );
2188     }
2189 
2190     //------------------------------------------------------------------------------
checkCursorPosition()2191     void TableControl_Impl::checkCursorPosition()
2192     {
2193         DBG_CHECK_ME();
2194 
2195 		TableSize nVisibleRows = impl_getVisibleRows(true);
2196 		TableSize nVisibleCols = impl_getVisibleColumns(true);
2197 		if  (   ( m_nTopRow + nVisibleRows > m_nRowCount )
2198             &&  ( m_nRowCount >= nVisibleRows )
2199             )
2200         {
2201 			--m_nTopRow;
2202         }
2203 		else
2204         {
2205 			m_nTopRow = 0;
2206         }
2207 
2208 		if  (   ( m_nLeftColumn + nVisibleCols > m_nColumnCount )
2209             &&  ( m_nColumnCount >= nVisibleCols )
2210             )
2211         {
2212 			--m_nLeftColumn;
2213         }
2214 		else
2215         {
2216 			m_nLeftColumn = 0;
2217         }
2218 
2219 		m_pDataWindow->Invalidate();
2220     }
2221 
2222     //--------------------------------------------------------------------
impl_getVisibleRows(bool _bAcceptPartialRow) const2223     TableSize TableControl_Impl::impl_getVisibleRows( bool _bAcceptPartialRow ) const
2224     {
2225         DBG_CHECK_ME();
2226 
2227         DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleRows: no data window!" );
2228 
2229         return lcl_getRowsFittingInto(
2230             m_pDataWindow->GetOutputSizePixel().Height() - m_nColHeaderHeightPixel,
2231             m_nRowHeightPixel,
2232             _bAcceptPartialRow
2233         );
2234     }
2235 
2236     //--------------------------------------------------------------------
impl_getVisibleColumns(bool _bAcceptPartialCol) const2237     TableSize TableControl_Impl::impl_getVisibleColumns( bool _bAcceptPartialCol ) const
2238     {
2239         DBG_CHECK_ME();
2240 
2241         DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleColumns: no data window!" );
2242 
2243         return lcl_getColumnsVisibleWithin(
2244             Rectangle( Point( 0, 0 ), m_pDataWindow->GetOutputSizePixel() ),
2245             m_nLeftColumn,
2246             *this,
2247             _bAcceptPartialCol
2248         );
2249     }
2250 
2251     //--------------------------------------------------------------------
goTo(ColPos _nColumn,RowPos _nRow)2252     bool TableControl_Impl::goTo( ColPos _nColumn, RowPos _nRow )
2253     {
2254         DBG_CHECK_ME();
2255 
2256         // TODO: give veto listeners a chance
2257 
2258         if  (  ( _nColumn < 0 ) || ( _nColumn >= m_nColumnCount )
2259             || ( _nRow < 0 ) || ( _nRow >= m_nRowCount )
2260             )
2261         {
2262             OSL_ENSURE( false, "TableControl_Impl::goTo: invalid row or column index!" );
2263             return false;
2264         }
2265 
2266         SuppressCursor aHideCursor( *this );
2267         m_nCurColumn = _nColumn;
2268         m_nCurRow = _nRow;
2269 
2270         // ensure that the new cell is visible
2271         ensureVisible( m_nCurColumn, m_nCurRow, false );
2272         return true;
2273     }
2274 
2275     //--------------------------------------------------------------------
ensureVisible(ColPos _nColumn,RowPos _nRow,bool _bAcceptPartialVisibility)2276     void TableControl_Impl::ensureVisible( ColPos _nColumn, RowPos _nRow, bool _bAcceptPartialVisibility )
2277     {
2278         DBG_CHECK_ME();
2279         DBG_ASSERT( ( _nColumn >= 0 ) && ( _nColumn < m_nColumnCount )
2280                  && ( _nRow >= 0 ) && ( _nRow < m_nRowCount ),
2281                  "TableControl_Impl::ensureVisible: invalid coordinates!" );
2282 
2283         SuppressCursor aHideCursor( *this );
2284 
2285         if ( _nColumn < m_nLeftColumn )
2286             impl_scrollColumns( _nColumn - m_nLeftColumn );
2287         else
2288         {
2289             TableSize nVisibleColumns = impl_getVisibleColumns( _bAcceptPartialVisibility );
2290             if ( _nColumn > m_nLeftColumn + nVisibleColumns - 1 )
2291             {
2292                 impl_scrollColumns( _nColumn - ( m_nLeftColumn + nVisibleColumns - 1 ) );
2293                 // TODO: since not all columns have the same width, this might in theory result
2294                 // in the column still not being visible.
2295             }
2296         }
2297 
2298         if ( _nRow < m_nTopRow )
2299             impl_scrollRows( _nRow - m_nTopRow );
2300         else
2301         {
2302             TableSize nVisibleRows = impl_getVisibleRows( _bAcceptPartialVisibility );
2303             if ( _nRow > m_nTopRow + nVisibleRows - 1 )
2304                 impl_scrollRows( _nRow - ( m_nTopRow + nVisibleRows - 1 ) );
2305         }
2306     }
2307 
2308     //--------------------------------------------------------------------
getCellContentAsString(RowPos const i_row,ColPos const i_col)2309     ::rtl::OUString TableControl_Impl::getCellContentAsString( RowPos const i_row, ColPos const i_col )
2310     {
2311         Any aCellValue;
2312         m_pModel->getCellContent( i_col, i_row, aCellValue );
2313 
2314         ::rtl::OUString sCellStringContent;
2315         m_pModel->getRenderer()->GetFormattedCellString( aCellValue, i_col, i_row, sCellStringContent );
2316 
2317         return sCellStringContent;
2318     }
2319 
2320     //--------------------------------------------------------------------
impl_ni_ScrollRows(TableSize _nRowDelta)2321     TableSize TableControl_Impl::impl_ni_ScrollRows( TableSize _nRowDelta )
2322     {
2323         // compute new top row
2324         RowPos nNewTopRow =
2325             ::std::max(
2326                 ::std::min( (RowPos)( m_nTopRow + _nRowDelta ), (RowPos)( m_nRowCount - 1 ) ),
2327                 (RowPos)0
2328             );
2329 
2330         RowPos nOldTopRow = m_nTopRow;
2331         m_nTopRow = nNewTopRow;
2332 
2333         // if updates are enabled currently, scroll the viewport
2334         if ( m_nTopRow != nOldTopRow )
2335         {
2336             DBG_SUSPEND_INV( INV_SCROLL_POSITION );
2337             SuppressCursor aHideCursor( *this );
2338             // TODO: call a onStartScroll at our listener (or better an own onStartScroll,
2339             // which hides the cursor and then calls the listener)
2340             // Same for onEndScroll
2341 
2342             // scroll the view port, if possible
2343             long nPixelDelta = m_nRowHeightPixel * ( m_nTopRow - nOldTopRow );
2344 
2345             Rectangle aDataArea( Point( 0, m_nColHeaderHeightPixel ), m_pDataWindow->GetOutputSizePixel() );
2346 
2347             if  (   m_pDataWindow->GetBackground().IsScrollable()
2348                 &&  abs( nPixelDelta ) < aDataArea.GetHeight()
2349                 )
2350             {
2351                 m_pDataWindow->Scroll( 0, (long)-nPixelDelta, aDataArea, SCROLL_CLIP | SCROLL_UPDATE | SCROLL_CHILDREN);
2352             }
2353             else
2354             {
2355                 m_pDataWindow->Invalidate( INVALIDATE_UPDATE );
2356                 m_pDataWindow->GetParent()->Invalidate( INVALIDATE_TRANSPARENT );
2357             }
2358 
2359             // update the position at the vertical scrollbar
2360             if ( m_pVScroll != NULL )
2361                 m_pVScroll->SetThumbPos( m_nTopRow );
2362         }
2363 
2364         // The scroll bar availaility might change when we scrolled.
2365         // For instance, imagine a view with 10 rows, if which 5 fit into the window, numbered 1 to 10.
2366         // Now let
2367         // - the user scroll to row number 6, so the last 5 rows are visible
2368         // - somebody remove the last 4 rows
2369         // - the user scroll to row number 5 being the top row, so the last two rows are visible
2370         // - somebody remove row number 6
2371         // - the user scroll to row number 1
2372         // => in this case, the need for the scrollbar vanishes immediately.
2373         if ( m_nTopRow == 0 )
2374             m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) );
2375 
2376         return (TableSize)( m_nTopRow - nOldTopRow );
2377     }
2378 
2379     //--------------------------------------------------------------------
impl_scrollRows(TableSize const i_rowDelta)2380     TableSize TableControl_Impl::impl_scrollRows( TableSize const i_rowDelta )
2381     {
2382         DBG_CHECK_ME();
2383         return impl_ni_ScrollRows( i_rowDelta );
2384     }
2385 
2386     //--------------------------------------------------------------------
impl_ni_ScrollColumns(TableSize _nColumnDelta)2387     TableSize TableControl_Impl::impl_ni_ScrollColumns( TableSize _nColumnDelta )
2388     {
2389         // compute new left column
2390         const ColPos nNewLeftColumn =
2391             ::std::max(
2392                 ::std::min( (ColPos)( m_nLeftColumn + _nColumnDelta ), (ColPos)( m_nColumnCount - 1 ) ),
2393                 (ColPos)0
2394             );
2395 
2396         const ColPos nOldLeftColumn = m_nLeftColumn;
2397         m_nLeftColumn = nNewLeftColumn;
2398 
2399         // if updates are enabled currently, scroll the viewport
2400         if ( m_nLeftColumn != nOldLeftColumn )
2401         {
2402             DBG_SUSPEND_INV( INV_SCROLL_POSITION );
2403             SuppressCursor aHideCursor( *this );
2404             // TODO: call a onStartScroll at our listener (or better an own onStartScroll,
2405             // which hides the cursor and then calls the listener)
2406             // Same for onEndScroll
2407 
2408             // scroll the view port, if possible
2409             const Rectangle aDataArea( Point( m_nRowHeaderWidthPixel, 0 ), m_pDataWindow->GetOutputSizePixel() );
2410 
2411             long nPixelDelta =
2412                     m_aColumnWidths[ nOldLeftColumn ].getStart()
2413                 -   m_aColumnWidths[ m_nLeftColumn ].getStart();
2414 
2415             // update our column positions
2416             // Do this *before* scrolling, as SCROLL_UPDATE will trigger a paint, which already needs the correct
2417             // information in m_aColumnWidths
2418             for (   ColumnPositions::iterator colPos = m_aColumnWidths.begin();
2419                     colPos != m_aColumnWidths.end();
2420                     ++colPos
2421                  )
2422             {
2423                 colPos->move( nPixelDelta );
2424             }
2425 
2426             // scroll the window content (if supported and possible), or invalidate the complete window
2427             if  (   m_pDataWindow->GetBackground().IsScrollable()
2428                 &&  abs( nPixelDelta ) < aDataArea.GetWidth()
2429                 )
2430             {
2431                 m_pDataWindow->Scroll( nPixelDelta, 0, aDataArea, SCROLL_CLIP | SCROLL_UPDATE );
2432             }
2433             else
2434             {
2435                 m_pDataWindow->Invalidate( INVALIDATE_UPDATE );
2436                 m_pDataWindow->GetParent()->Invalidate( INVALIDATE_TRANSPARENT );
2437             }
2438 
2439             // update the position at the horizontal scrollbar
2440             if ( m_pHScroll != NULL )
2441                 m_pHScroll->SetThumbPos( m_nLeftColumn );
2442         }
2443 
2444         // The scroll bar availaility might change when we scrolled. This is because we do not hide
2445         // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will
2446         // be auto-hidden when it's scrolled back to pos 0.
2447         if ( m_nLeftColumn == 0 )
2448             m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) );
2449 
2450         return (TableSize)( m_nLeftColumn - nOldLeftColumn );
2451     }
2452 
2453     //------------------------------------------------------------------------------------------------------------------
impl_scrollColumns(TableSize const i_columnDelta)2454     TableSize TableControl_Impl::impl_scrollColumns( TableSize const i_columnDelta )
2455     {
2456         DBG_CHECK_ME();
2457         return impl_ni_ScrollColumns( i_columnDelta );
2458     }
2459 
2460     //------------------------------------------------------------------------------------------------------------------
getSelEngine()2461     SelectionEngine* TableControl_Impl::getSelEngine()
2462     {
2463 		return m_pSelEngine;
2464     }
2465 
2466     //------------------------------------------------------------------------------------------------------------------
getHorzScrollbar()2467     ScrollBar* TableControl_Impl::getHorzScrollbar()
2468     {
2469 		return m_pHScroll;
2470     }
2471 
2472     //------------------------------------------------------------------------------------------------------------------
getVertScrollbar()2473     ScrollBar* TableControl_Impl::getVertScrollbar()
2474     {
2475 		return m_pVScroll;
2476     }
2477 
2478     //------------------------------------------------------------------------------------------------------------------
isRowSelected(RowPos i_row) const2479     bool TableControl_Impl::isRowSelected( RowPos i_row ) const
2480     {
2481 		return ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_row ) != m_aSelectedRows.end();
2482     }
2483 
2484     //------------------------------------------------------------------------------------------------------------------
getSelectedRowIndex(size_t const i_selectionIndex) const2485     RowPos TableControl_Impl::getSelectedRowIndex( size_t const i_selectionIndex ) const
2486     {
2487         if ( i_selectionIndex < m_aSelectedRows.size() )
2488             return m_aSelectedRows[ i_selectionIndex ];
2489         return ROW_INVALID;
2490     }
2491 
2492     //------------------------------------------------------------------------------------------------------------------
getRowSelectedNumber(const::std::vector<RowPos> & selectedRows,RowPos current)2493     int TableControl_Impl::getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current)
2494     {
2495 		std::vector<RowPos>::const_iterator it = ::std::find(selectedRows.begin(),selectedRows.end(),current);
2496 		if ( it != selectedRows.end() )
2497 		{
2498 			return it - selectedRows.begin();
2499 		}
2500 		return -1;
2501     }
2502 
2503     //--------------------------------------------------------------------
impl_getColumnForOrdinate(long const i_ordinate) const2504     ColPos TableControl_Impl::impl_getColumnForOrdinate( long const i_ordinate ) const
2505     {
2506         DBG_CHECK_ME();
2507 
2508         if ( ( m_aColumnWidths.empty() ) || ( i_ordinate < 0 ) )
2509             return COL_INVALID;
2510 
2511         if ( i_ordinate < m_nRowHeaderWidthPixel )
2512             return COL_ROW_HEADERS;
2513 
2514         ColumnPositions::const_iterator lowerBound = ::std::lower_bound(
2515             m_aColumnWidths.begin(),
2516             m_aColumnWidths.end(),
2517             i_ordinate + 1,
2518             ColumnInfoPositionLess()
2519         );
2520         if ( lowerBound == m_aColumnWidths.end() )
2521         {
2522             // point is *behind* the start of the last column ...
2523             if ( i_ordinate < m_aColumnWidths.rbegin()->getEnd() )
2524                 // ... but still before its end
2525                 return m_nColumnCount - 1;
2526             return COL_INVALID;
2527         }
2528         return lowerBound - m_aColumnWidths.begin();
2529     }
2530 
2531     //--------------------------------------------------------------------
impl_getRowForAbscissa(long const i_abscissa) const2532     RowPos TableControl_Impl::impl_getRowForAbscissa( long const i_abscissa ) const
2533     {
2534         DBG_CHECK_ME();
2535 
2536         if ( i_abscissa < 0 )
2537             return ROW_INVALID;
2538 
2539         if ( i_abscissa < m_nColHeaderHeightPixel )
2540             return ROW_COL_HEADERS;
2541 
2542         long const abscissa = i_abscissa - m_nColHeaderHeightPixel;
2543         long const row = m_nTopRow + abscissa / m_nRowHeightPixel;
2544         return row < m_pModel->getRowCount() ? row : ROW_INVALID;
2545     }
2546 
2547     //--------------------------------------------------------------------
markRowAsDeselected(RowPos const i_rowIndex)2548     bool TableControl_Impl::markRowAsDeselected( RowPos const i_rowIndex )
2549     {
2550         DBG_CHECK_ME();
2551 
2552         ::std::vector< RowPos >::iterator selPos = ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_rowIndex );
2553         if ( selPos == m_aSelectedRows.end() )
2554             return false;
2555 
2556         m_aSelectedRows.erase( selPos );
2557         return true;
2558     }
2559 
2560     //--------------------------------------------------------------------
markRowAsSelected(RowPos const i_rowIndex)2561     bool TableControl_Impl::markRowAsSelected( RowPos const i_rowIndex )
2562     {
2563         DBG_CHECK_ME();
2564 
2565         if ( isRowSelected( i_rowIndex ) )
2566             return false;
2567 
2568         SelectionMode const eSelMode = getSelEngine()->GetSelectionMode();
2569         switch ( eSelMode )
2570         {
2571         case SINGLE_SELECTION:
2572             if ( !m_aSelectedRows.empty() )
2573             {
2574                 OSL_ENSURE( m_aSelectedRows.size() == 1, "TableControl::markRowAsSelected: SingleSelection with more than one selected element?" );
2575                 m_aSelectedRows[0] = i_rowIndex;
2576                 break;
2577             }
2578             // fall through
2579 
2580         case MULTIPLE_SELECTION:
2581             m_aSelectedRows.push_back( i_rowIndex );
2582             break;
2583 
2584         default:
2585             OSL_ENSURE( false, "TableControl_Impl::markRowAsSelected: unsupported selection mode!" );
2586             return false;
2587         }
2588 
2589         return true;
2590     }
2591 
2592     //--------------------------------------------------------------------
markAllRowsAsDeselected()2593     bool TableControl_Impl::markAllRowsAsDeselected()
2594     {
2595         if ( m_aSelectedRows.empty() )
2596             return false;
2597 
2598         m_aSelectedRows.clear();
2599         return true;
2600     }
2601 
2602     //--------------------------------------------------------------------
markAllRowsAsSelected()2603     bool TableControl_Impl::markAllRowsAsSelected()
2604     {
2605         DBG_CHECK_ME();
2606 
2607         SelectionMode const eSelMode = getSelEngine()->GetSelectionMode();
2608         ENSURE_OR_RETURN_FALSE( eSelMode == MULTIPLE_SELECTION, "TableControl_Impl::markAllRowsAsSelected: unsupported selection mode!" );
2609 
2610         if ( m_aSelectedRows.size() == size_t( m_pModel->getRowCount() ) )
2611         {
2612         #if OSL_DEBUG_LEVEL > 0
2613             for ( TableSize row = 0; row < m_pModel->getRowCount(); ++row )
2614             {
2615                 OSL_ENSURE( isRowSelected( row ), "TableControl_Impl::markAllRowsAsSelected: inconsistency in the selected rows!" );
2616             }
2617         #endif
2618             // already all rows marked as selected
2619             return false;
2620         }
2621 
2622         m_aSelectedRows.clear();
2623         for ( RowPos i=0; i < m_pModel->getRowCount(); ++i )
2624             m_aSelectedRows.push_back(i);
2625 
2626         return true;
2627     }
2628 
2629     //--------------------------------------------------------------------
commitAccessibleEvent(sal_Int16 const i_eventID,const Any & i_newValue,const Any & i_oldValue)2630     void TableControl_Impl::commitAccessibleEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2631     {
2632         impl_commitAccessibleEvent( i_eventID, i_newValue, i_oldValue );
2633     }
2634 
2635     //--------------------------------------------------------------------
commitCellEvent(sal_Int16 const i_eventID,const Any & i_newValue,const Any & i_oldValue)2636     void TableControl_Impl::commitCellEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2637     {
2638         DBG_CHECK_ME();
2639         if ( impl_isAccessibleAlive() )
2640  	        m_pAccessibleTable->commitCellEvent( i_eventID, i_newValue, i_oldValue );
2641     }
2642 
2643     //--------------------------------------------------------------------
commitTableEvent(sal_Int16 const i_eventID,const Any & i_newValue,const Any & i_oldValue)2644     void TableControl_Impl::commitTableEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2645     {
2646         DBG_CHECK_ME();
2647         if ( impl_isAccessibleAlive() )
2648  	        m_pAccessibleTable->commitTableEvent( i_eventID, i_newValue, i_oldValue );
2649     }
2650 
2651     //--------------------------------------------------------------------
calcHeaderRect(bool bColHeader)2652 	Rectangle TableControl_Impl::calcHeaderRect(bool bColHeader)
2653 	{
2654         Rectangle const aRectTableWithHeaders( impl_getAllVisibleCellsArea() );
2655 		Size const aSizeTableWithHeaders( aRectTableWithHeaders.GetSize() );
2656 		if ( bColHeader )
2657 			return Rectangle( aRectTableWithHeaders.TopLeft(), Size( aSizeTableWithHeaders.Width(), m_nColHeaderHeightPixel ) );
2658 		else
2659 			return Rectangle( aRectTableWithHeaders.TopLeft(), Size( m_nRowHeaderWidthPixel, aSizeTableWithHeaders.Height() ) );
2660 	}
2661 
2662     //--------------------------------------------------------------------
calcHeaderCellRect(bool bColHeader,sal_Int32 nPos)2663     Rectangle TableControl_Impl::calcHeaderCellRect( bool bColHeader, sal_Int32 nPos )
2664     {
2665         Rectangle const aHeaderRect = calcHeaderRect( bColHeader );
2666         TableCellGeometry const aGeometry(
2667             *this, aHeaderRect,
2668             bColHeader ? nPos : COL_ROW_HEADERS,
2669             bColHeader ? ROW_COL_HEADERS : nPos
2670         );
2671         return aGeometry.getRect();
2672     }
2673 
2674     //--------------------------------------------------------------------
calcTableRect()2675 	Rectangle TableControl_Impl::calcTableRect()
2676 	{
2677 		return impl_getAllVisibleDataCellArea();
2678 	}
2679 
2680     //--------------------------------------------------------------------
calcCellRect(sal_Int32 nRow,sal_Int32 nCol)2681     Rectangle TableControl_Impl::calcCellRect( sal_Int32 nRow, sal_Int32 nCol )
2682     {
2683         Rectangle aCellRect;
2684         impl_getCellRect( nRow, nCol, aCellRect );
2685         return aCellRect;
2686     }
2687 
2688     //--------------------------------------------------------------------
2689     IMPL_LINK( TableControl_Impl, OnUpdateScrollbars, void*, /**/ )
2690     {
2691         DBG_CHECK_ME();
2692         // TODO: can't we simply use lcl_updateScrollbar here, so the scrollbars ranges are updated, instead of
2693         // doing a complete re-layout?
2694         impl_ni_relayout();
2695         return 1L;
2696     }
2697 
2698     //--------------------------------------------------------------------
IMPL_LINK(TableControl_Impl,OnScroll,ScrollBar *,_pScrollbar)2699     IMPL_LINK( TableControl_Impl, OnScroll, ScrollBar*, _pScrollbar )
2700     {
2701         DBG_ASSERT( ( _pScrollbar == m_pVScroll ) || ( _pScrollbar == m_pHScroll ),
2702             "TableControl_Impl::OnScroll: where did this come from?" );
2703 
2704         if ( _pScrollbar == m_pVScroll )
2705             impl_ni_ScrollRows( _pScrollbar->GetDelta() );
2706         else
2707             impl_ni_ScrollColumns( _pScrollbar->GetDelta() );
2708 
2709         return 0L;
2710     }
2711 
2712     //------------------------------------------------------------------------------------------------------------------
getAccessible(Window & i_parentWindow)2713     Reference< XAccessible > TableControl_Impl::getAccessible( Window& i_parentWindow )
2714     {
2715         DBG_TESTSOLARMUTEX();
2716 	    if ( m_pAccessibleTable == NULL )
2717 		{
2718 			Reference< XAccessible > const xAccParent = i_parentWindow.GetAccessible();
2719 			if ( xAccParent.is() )
2720 			{
2721 				m_pAccessibleTable = m_aFactoryAccess.getFactory().createAccessibleTableControl(
2722 					xAccParent, m_rAntiImpl
2723 				);
2724 			}
2725 		}
2726 
2727 		Reference< XAccessible > xAccessible;
2728 		if ( m_pAccessibleTable )
2729 			xAccessible = m_pAccessibleTable->getMyself();
2730 		return xAccessible;
2731     }
2732 
2733     //------------------------------------------------------------------------------------------------------------------
disposeAccessible()2734     void TableControl_Impl::disposeAccessible()
2735     {
2736         if ( m_pAccessibleTable )
2737             m_pAccessibleTable->dispose();
2738         m_pAccessibleTable = NULL;
2739     }
2740 
2741     //------------------------------------------------------------------------------------------------------------------
impl_isAccessibleAlive() const2742     bool TableControl_Impl::impl_isAccessibleAlive() const
2743     {
2744         DBG_CHECK_ME();
2745         return ( NULL != m_pAccessibleTable ) && m_pAccessibleTable->isAlive();
2746     }
2747 
2748     //------------------------------------------------------------------------------------------------------------------
impl_commitAccessibleEvent(sal_Int16 const i_eventID,Any const & i_newValue,Any const & i_oldValue)2749     void TableControl_Impl::impl_commitAccessibleEvent( sal_Int16 const i_eventID, Any const & i_newValue, Any const & i_oldValue )
2750     {
2751         DBG_CHECK_ME();
2752         if ( impl_isAccessibleAlive() )
2753  	        m_pAccessibleTable->commitEvent( i_eventID, i_newValue, i_oldValue );
2754     }
2755 
2756     //==================================================================================================================
2757     //= TableFunctionSet
2758     //==================================================================================================================
2759     //------------------------------------------------------------------------------------------------------------------
TableFunctionSet(TableControl_Impl * _pTableControl)2760     TableFunctionSet::TableFunctionSet(TableControl_Impl* _pTableControl)
2761     	:m_pTableControl( _pTableControl)
2762 	    ,m_nCurrentRow( ROW_INVALID )
2763     {
2764     }
2765     //------------------------------------------------------------------------------------------------------------------
~TableFunctionSet()2766     TableFunctionSet::~TableFunctionSet()
2767     {
2768     }
2769     //------------------------------------------------------------------------------------------------------------------
BeginDrag()2770     void TableFunctionSet::BeginDrag()
2771     {
2772     }
2773     //------------------------------------------------------------------------------------------------------------------
CreateAnchor()2774     void TableFunctionSet::CreateAnchor()
2775     {
2776 	    m_pTableControl->setAnchor( m_pTableControl->getCurRow() );
2777     }
2778 
2779     //------------------------------------------------------------------------------------------------------------------
DestroyAnchor()2780     void TableFunctionSet::DestroyAnchor()
2781     {
2782 	    m_pTableControl->setAnchor( ROW_INVALID );
2783     }
2784 
2785     //------------------------------------------------------------------------------------------------------------------
SetCursorAtPoint(const Point & rPoint,sal_Bool bDontSelectAtCursor)2786     sal_Bool TableFunctionSet::SetCursorAtPoint(const Point& rPoint, sal_Bool bDontSelectAtCursor)
2787     {
2788 	    sal_Bool bHandled = sal_False;
2789 	    // newRow is the row which includes the point, getCurRow() is the last selected row, before the mouse click
2790 	    RowPos newRow = m_pTableControl->getRowAtPoint( rPoint );
2791         if ( newRow == ROW_COL_HEADERS )
2792             newRow = m_pTableControl->getTopRow();
2793 
2794         ColPos newCol = m_pTableControl->getColAtPoint( rPoint );
2795         if ( newCol == COL_ROW_HEADERS )
2796             newCol = m_pTableControl->getLeftColumn();
2797 
2798 	    if ( ( newRow == ROW_INVALID ) || ( newCol == COL_INVALID ) )
2799 		    return sal_False;
2800 
2801 	    if ( bDontSelectAtCursor )
2802 	    {
2803 		    if ( m_pTableControl->getSelectedRowCount() > 1 )
2804 			    m_pTableControl->getSelEngine()->AddAlways(sal_True);
2805 		    bHandled = sal_True;
2806 	    }
2807 	    else if ( m_pTableControl->getAnchor() == m_pTableControl->getCurRow() )
2808 	    {
2809 		    //selecting region,
2810 		    int diff = m_pTableControl->getCurRow() - newRow;
2811 		    //selected region lies above the last selection
2812 		    if( diff >= 0)
2813 		    {
2814 			    //put selected rows in vector
2815 			    while ( m_pTableControl->getAnchor() >= newRow )
2816 			    {
2817 				    m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() );
2818 				    m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 );
2819 				    diff--;
2820 			    }
2821 			    m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 );
2822 		    }
2823 		    //selected region lies beneath the last selected row
2824 		    else
2825 		    {
2826 			    while ( m_pTableControl->getAnchor() <= newRow )
2827 			    {
2828 				    m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() );
2829 				    m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 );
2830 				    diff++;
2831 			    }
2832 			    m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 );
2833 		    }
2834 		    m_pTableControl->invalidateSelectedRegion( m_pTableControl->getCurRow(), newRow );
2835 		    bHandled = sal_True;
2836 	    }
2837 	    //no region selected
2838 	    else
2839 	    {
2840 		    if ( !m_pTableControl->hasRowSelection() )
2841                 m_pTableControl->markRowAsSelected( newRow );
2842 		    else
2843 		    {
2844 			    if ( m_pTableControl->getSelEngine()->GetSelectionMode() == SINGLE_SELECTION )
2845 			    {
2846 				    DeselectAll();
2847                     m_pTableControl->markRowAsSelected( newRow );
2848 			    }
2849 			    else
2850 			    {
2851                     m_pTableControl->markRowAsSelected( newRow );
2852 			    }
2853 		    }
2854 		    if ( m_pTableControl->getSelectedRowCount() > 1 && m_pTableControl->getSelEngine()->GetSelectionMode() != SINGLE_SELECTION )
2855 			    m_pTableControl->getSelEngine()->AddAlways(sal_True);
2856 
2857 		    m_pTableControl->invalidateRow( newRow );
2858 		    bHandled = sal_True;
2859 	    }
2860 	    m_pTableControl->goTo( newCol, newRow );
2861 	    return bHandled;
2862     }
2863     //------------------------------------------------------------------------------------------------------------------
IsSelectionAtPoint(const Point & rPoint)2864     sal_Bool TableFunctionSet::IsSelectionAtPoint( const Point& rPoint )
2865     {
2866 	    m_pTableControl->getSelEngine()->AddAlways(sal_False);
2867 	    if ( !m_pTableControl->hasRowSelection() )
2868 		    return sal_False;
2869 	    else
2870 	    {
2871 		    RowPos curRow = m_pTableControl->getRowAtPoint( rPoint );
2872 		    m_pTableControl->setAnchor( ROW_INVALID );
2873 		    bool selected = m_pTableControl->isRowSelected( curRow );
2874 		    m_nCurrentRow = curRow;
2875 		    return selected;
2876 	    }
2877     }
2878     //------------------------------------------------------------------------------------------------------------------
DeselectAtPoint(const Point & rPoint)2879     void TableFunctionSet::DeselectAtPoint( const Point& rPoint )
2880     {
2881 	    (void)rPoint;
2882 	    m_pTableControl->invalidateRow( m_nCurrentRow );
2883 	    m_pTableControl->markRowAsDeselected( m_nCurrentRow );
2884     }
2885 
2886     //------------------------------------------------------------------------------------------------------------------
DeselectAll()2887     void TableFunctionSet::DeselectAll()
2888     {
2889 	    if ( m_pTableControl->hasRowSelection() )
2890 	    {
2891             for ( size_t i=0; i<m_pTableControl->getSelectedRowCount(); ++i )
2892 		    {
2893                 RowPos const rowIndex = m_pTableControl->getSelectedRowIndex(i);
2894 			    m_pTableControl->invalidateRow( rowIndex );
2895 		    }
2896 
2897 		    m_pTableControl->markAllRowsAsDeselected();
2898 	    }
2899     }
2900 
2901 //......................................................................................................................
2902 } } // namespace svt::table
2903 //......................................................................................................................
2904