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