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             break;
2076         }
2077     }
2078 
2079     //------------------------------------------------------------------------------------------------------------------
2080     long TableControl_Impl::pixelWidthToAppFont( long const i_pixels ) const
2081     {
2082         return m_pDataWindow->PixelToLogic( Size( i_pixels, 0 ), MAP_APPFONT ).Width();
2083     }
2084 
2085     //------------------------------------------------------------------------------------------------------------------
2086     long TableControl_Impl::appFontWidthToPixel( long const i_appFontUnits ) const
2087     {
2088         return m_pDataWindow->LogicToPixel( Size( i_appFontUnits, 0 ), MAP_APPFONT ).Width();
2089     }
2090 
2091     //------------------------------------------------------------------------------------------------------------------
2092     void TableControl_Impl::hideTracking()
2093     {
2094         m_pDataWindow->HideTracking();
2095     }
2096 
2097     //------------------------------------------------------------------------------------------------------------------
2098     void TableControl_Impl::showTracking( Rectangle const & i_location, sal_uInt16 const i_flags )
2099     {
2100         m_pDataWindow->ShowTracking( i_location, i_flags );
2101     }
2102 
2103     //------------------------------------------------------------------------------------------------------------------
2104     bool TableControl_Impl::activateCell( ColPos const i_col, RowPos const i_row )
2105     {
2106 	    DBG_CHECK_ME();
2107         return goTo( i_col, i_row );
2108     }
2109 
2110     //------------------------------------------------------------------------------------------------------------------
2111     void TableControl_Impl::invalidateSelectedRegion( RowPos _nPrevRow, RowPos _nCurRow )
2112     {
2113 	    DBG_CHECK_ME();
2114 	    // get the visible area of the table control and set the Left and right border of the region to be repainted
2115 	    Rectangle const aAllCells( impl_getAllVisibleCellsArea() );
2116 
2117         Rectangle aInvalidateRect;
2118 	    aInvalidateRect.Left() = aAllCells.Left();
2119 	    aInvalidateRect.Right() = aAllCells.Right();
2120 	    // if only one row is selected
2121 	    if ( _nPrevRow == _nCurRow )
2122 	    {
2123 	        Rectangle aCellRect;
2124 		    impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
2125 		    aInvalidateRect.Top() = aCellRect.Top();
2126 		    aInvalidateRect.Bottom() = aCellRect.Bottom();
2127 	    }
2128 	    //if the region is above the current row
2129 	    else if(_nPrevRow < _nCurRow )
2130 	    {
2131 	        Rectangle aCellRect;
2132 		    impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect );
2133 		    aInvalidateRect.Top() = aCellRect.Top();
2134 		    impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
2135 		    aInvalidateRect.Bottom() = aCellRect.Bottom();
2136 	    }
2137 	    //if the region is beneath the current row
2138 	    else
2139 	    {
2140             Rectangle aCellRect;
2141 		    impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
2142 		    aInvalidateRect.Top() = aCellRect.Top();
2143 		    impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect );
2144 		    aInvalidateRect.Bottom() = aCellRect.Bottom();
2145 	    }
2146 	    m_pDataWindow->Invalidate( aInvalidateRect );
2147     }
2148 
2149     //------------------------------------------------------------------------------------------------------------------
2150     void TableControl_Impl::invalidateSelectedRows()
2151     {
2152         for (   ::std::vector< RowPos >::iterator selRow = m_aSelectedRows.begin();
2153 				selRow != m_aSelectedRows.end();
2154                 ++selRow
2155             )
2156 		{
2157             invalidateRow( *selRow );
2158 		}
2159     }
2160 
2161     //------------------------------------------------------------------------------------------------------------------
2162     void TableControl_Impl::invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow )
2163     {
2164         RowPos const firstRow = i_firstRow < m_nTopRow ? m_nTopRow : i_firstRow;
2165         RowPos const lastVisibleRow = m_nTopRow + impl_getVisibleRows( true ) - 1;
2166         RowPos const lastRow = ( ( i_lastRow == ROW_INVALID ) || ( i_lastRow > lastVisibleRow ) ) ? lastVisibleRow : i_lastRow;
2167 
2168         Rectangle aInvalidateRect;
2169 
2170         Rectangle const aVisibleCellsArea( impl_getAllVisibleCellsArea() );
2171         TableRowGeometry aRow( *this, aVisibleCellsArea, firstRow, true );
2172         while ( aRow.isValid() && ( aRow.getRow() <= lastRow ) )
2173         {
2174             aInvalidateRect.Union( aRow.getRect() );
2175             aRow.moveDown();
2176         }
2177 
2178         if ( i_lastRow == ROW_INVALID )
2179             aInvalidateRect.Bottom() = m_pDataWindow->GetOutputSizePixel().Height();
2180 
2181         m_pDataWindow->Invalidate( aInvalidateRect,
2182             m_pDataWindow->GetControlBackground().GetTransparency() ? INVALIDATE_TRANSPARENT : 0 );
2183     }
2184 
2185     //------------------------------------------------------------------------------
2186     void TableControl_Impl::checkCursorPosition()
2187     {
2188         DBG_CHECK_ME();
2189 
2190 		TableSize nVisibleRows = impl_getVisibleRows(true);
2191 		TableSize nVisibleCols = impl_getVisibleColumns(true);
2192 		if  (   ( m_nTopRow + nVisibleRows > m_nRowCount )
2193             &&  ( m_nRowCount >= nVisibleRows )
2194             )
2195         {
2196 			--m_nTopRow;
2197         }
2198 		else
2199         {
2200 			m_nTopRow = 0;
2201         }
2202 
2203 		if  (   ( m_nLeftColumn + nVisibleCols > m_nColumnCount )
2204             &&  ( m_nColumnCount >= nVisibleCols )
2205             )
2206         {
2207 			--m_nLeftColumn;
2208         }
2209 		else
2210         {
2211 			m_nLeftColumn = 0;
2212         }
2213 
2214 		m_pDataWindow->Invalidate();
2215     }
2216 
2217     //--------------------------------------------------------------------
2218     TableSize TableControl_Impl::impl_getVisibleRows( bool _bAcceptPartialRow ) const
2219     {
2220         DBG_CHECK_ME();
2221 
2222         DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleRows: no data window!" );
2223 
2224         return lcl_getRowsFittingInto(
2225             m_pDataWindow->GetOutputSizePixel().Height() - m_nColHeaderHeightPixel,
2226             m_nRowHeightPixel,
2227             _bAcceptPartialRow
2228         );
2229     }
2230 
2231     //--------------------------------------------------------------------
2232     TableSize TableControl_Impl::impl_getVisibleColumns( bool _bAcceptPartialCol ) const
2233     {
2234         DBG_CHECK_ME();
2235 
2236         DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleColumns: no data window!" );
2237 
2238         return lcl_getColumnsVisibleWithin(
2239             Rectangle( Point( 0, 0 ), m_pDataWindow->GetOutputSizePixel() ),
2240             m_nLeftColumn,
2241             *this,
2242             _bAcceptPartialCol
2243         );
2244     }
2245 
2246     //--------------------------------------------------------------------
2247     bool TableControl_Impl::goTo( ColPos _nColumn, RowPos _nRow )
2248     {
2249         DBG_CHECK_ME();
2250 
2251         // TODO: give veto listeners a chance
2252 
2253         if  (  ( _nColumn < 0 ) || ( _nColumn >= m_nColumnCount )
2254             || ( _nRow < 0 ) || ( _nRow >= m_nRowCount )
2255             )
2256         {
2257             OSL_ENSURE( false, "TableControl_Impl::goTo: invalid row or column index!" );
2258             return false;
2259         }
2260 
2261         SuppressCursor aHideCursor( *this );
2262         m_nCurColumn = _nColumn;
2263         m_nCurRow = _nRow;
2264 
2265         // ensure that the new cell is visible
2266         ensureVisible( m_nCurColumn, m_nCurRow, false );
2267         return true;
2268     }
2269 
2270     //--------------------------------------------------------------------
2271     void TableControl_Impl::ensureVisible( ColPos _nColumn, RowPos _nRow, bool _bAcceptPartialVisibility )
2272     {
2273         DBG_CHECK_ME();
2274         DBG_ASSERT( ( _nColumn >= 0 ) && ( _nColumn < m_nColumnCount )
2275                  && ( _nRow >= 0 ) && ( _nRow < m_nRowCount ),
2276                  "TableControl_Impl::ensureVisible: invalid coordinates!" );
2277 
2278         SuppressCursor aHideCursor( *this );
2279 
2280         if ( _nColumn < m_nLeftColumn )
2281             impl_scrollColumns( _nColumn - m_nLeftColumn );
2282         else
2283         {
2284             TableSize nVisibleColumns = impl_getVisibleColumns( _bAcceptPartialVisibility );
2285             if ( _nColumn > m_nLeftColumn + nVisibleColumns - 1 )
2286             {
2287                 impl_scrollColumns( _nColumn - ( m_nLeftColumn + nVisibleColumns - 1 ) );
2288                 // TODO: since not all columns have the same width, this might in theory result
2289                 // in the column still not being visible.
2290             }
2291         }
2292 
2293         if ( _nRow < m_nTopRow )
2294             impl_scrollRows( _nRow - m_nTopRow );
2295         else
2296         {
2297             TableSize nVisibleRows = impl_getVisibleRows( _bAcceptPartialVisibility );
2298             if ( _nRow > m_nTopRow + nVisibleRows - 1 )
2299                 impl_scrollRows( _nRow - ( m_nTopRow + nVisibleRows - 1 ) );
2300         }
2301     }
2302 
2303     //--------------------------------------------------------------------
2304     ::rtl::OUString TableControl_Impl::getCellContentAsString( RowPos const i_row, ColPos const i_col )
2305     {
2306         Any aCellValue;
2307         m_pModel->getCellContent( i_col, i_row, aCellValue );
2308 
2309         ::rtl::OUString sCellStringContent;
2310         m_pModel->getRenderer()->GetFormattedCellString( aCellValue, i_col, i_row, sCellStringContent );
2311 
2312         return sCellStringContent;
2313     }
2314 
2315     //--------------------------------------------------------------------
2316     TableSize TableControl_Impl::impl_ni_ScrollRows( TableSize _nRowDelta )
2317     {
2318         // compute new top row
2319         RowPos nNewTopRow =
2320             ::std::max(
2321                 ::std::min( (RowPos)( m_nTopRow + _nRowDelta ), (RowPos)( m_nRowCount - 1 ) ),
2322                 (RowPos)0
2323             );
2324 
2325         RowPos nOldTopRow = m_nTopRow;
2326         m_nTopRow = nNewTopRow;
2327 
2328         // if updates are enabled currently, scroll the viewport
2329         if ( m_nTopRow != nOldTopRow )
2330         {
2331             DBG_SUSPEND_INV( INV_SCROLL_POSITION );
2332             SuppressCursor aHideCursor( *this );
2333             // TODO: call a onStartScroll at our listener (or better an own onStartScroll,
2334             // which hides the cursor and then calls the listener)
2335             // Same for onEndScroll
2336 
2337             // scroll the view port, if possible
2338             long nPixelDelta = m_nRowHeightPixel * ( m_nTopRow - nOldTopRow );
2339 
2340             Rectangle aDataArea( Point( 0, m_nColHeaderHeightPixel ), m_pDataWindow->GetOutputSizePixel() );
2341 
2342             if  (   m_pDataWindow->GetBackground().IsScrollable()
2343                 &&  abs( nPixelDelta ) < aDataArea.GetHeight()
2344                 )
2345             {
2346                 m_pDataWindow->Scroll( 0, (long)-nPixelDelta, aDataArea, SCROLL_CLIP | SCROLL_UPDATE | SCROLL_CHILDREN);
2347             }
2348             else
2349                 m_pDataWindow->Invalidate( INVALIDATE_UPDATE );
2350 
2351             // update the position at the vertical scrollbar
2352             if ( m_pVScroll != NULL )
2353                 m_pVScroll->SetThumbPos( m_nTopRow );
2354         }
2355 
2356         // The scroll bar availaility might change when we scrolled.
2357         // For instance, imagine a view with 10 rows, if which 5 fit into the window, numbered 1 to 10.
2358         // Now let
2359         // - the user scroll to row number 6, so the last 5 rows are visible
2360         // - somebody remove the last 4 rows
2361         // - the user scroll to row number 5 being the top row, so the last two rows are visible
2362         // - somebody remove row number 6
2363         // - the user scroll to row number 1
2364         // => in this case, the need for the scrollbar vanishes immediately.
2365         if ( m_nTopRow == 0 )
2366             m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) );
2367 
2368         return (TableSize)( m_nTopRow - nOldTopRow );
2369     }
2370 
2371     //--------------------------------------------------------------------
2372     TableSize TableControl_Impl::impl_scrollRows( TableSize const i_rowDelta )
2373     {
2374         DBG_CHECK_ME();
2375         return impl_ni_ScrollRows( i_rowDelta );
2376     }
2377 
2378     //--------------------------------------------------------------------
2379     TableSize TableControl_Impl::impl_ni_ScrollColumns( TableSize _nColumnDelta )
2380     {
2381         // compute new left column
2382         const ColPos nNewLeftColumn =
2383             ::std::max(
2384                 ::std::min( (ColPos)( m_nLeftColumn + _nColumnDelta ), (ColPos)( m_nColumnCount - 1 ) ),
2385                 (ColPos)0
2386             );
2387 
2388         const ColPos nOldLeftColumn = m_nLeftColumn;
2389         m_nLeftColumn = nNewLeftColumn;
2390 
2391         // if updates are enabled currently, scroll the viewport
2392         if ( m_nLeftColumn != nOldLeftColumn )
2393         {
2394             DBG_SUSPEND_INV( INV_SCROLL_POSITION );
2395             SuppressCursor aHideCursor( *this );
2396             // TODO: call a onStartScroll at our listener (or better an own onStartScroll,
2397             // which hides the cursor and then calls the listener)
2398             // Same for onEndScroll
2399 
2400             // scroll the view port, if possible
2401             const Rectangle aDataArea( Point( m_nRowHeaderWidthPixel, 0 ), m_pDataWindow->GetOutputSizePixel() );
2402 
2403             long nPixelDelta =
2404                     m_aColumnWidths[ nOldLeftColumn ].getStart()
2405                 -   m_aColumnWidths[ m_nLeftColumn ].getStart();
2406 
2407             // update our column positions
2408             // Do this *before* scrolling, as SCROLL_UPDATE will trigger a paint, which already needs the correct
2409             // information in m_aColumnWidths
2410             for (   ColumnPositions::iterator colPos = m_aColumnWidths.begin();
2411                     colPos != m_aColumnWidths.end();
2412                     ++colPos
2413                  )
2414             {
2415                 colPos->move( nPixelDelta );
2416             }
2417 
2418             // scroll the window content (if supported and possible), or invalidate the complete window
2419             if  (   m_pDataWindow->GetBackground().IsScrollable()
2420                 &&  abs( nPixelDelta ) < aDataArea.GetWidth()
2421                 )
2422             {
2423                 m_pDataWindow->Scroll( nPixelDelta, 0, aDataArea, SCROLL_CLIP | SCROLL_UPDATE );
2424             }
2425             else
2426                 m_pDataWindow->Invalidate( INVALIDATE_UPDATE );
2427 
2428             // update the position at the horizontal scrollbar
2429             if ( m_pHScroll != NULL )
2430                 m_pHScroll->SetThumbPos( m_nLeftColumn );
2431         }
2432 
2433         // The scroll bar availaility might change when we scrolled. This is because we do not hide
2434         // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will
2435         // be auto-hidden when it's scrolled back to pos 0.
2436         if ( m_nLeftColumn == 0 )
2437             m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) );
2438 
2439         return (TableSize)( m_nLeftColumn - nOldLeftColumn );
2440     }
2441 
2442     //------------------------------------------------------------------------------------------------------------------
2443     TableSize TableControl_Impl::impl_scrollColumns( TableSize const i_columnDelta )
2444     {
2445         DBG_CHECK_ME();
2446         return impl_ni_ScrollColumns( i_columnDelta );
2447     }
2448 
2449     //------------------------------------------------------------------------------------------------------------------
2450     SelectionEngine* TableControl_Impl::getSelEngine()
2451     {
2452 		return m_pSelEngine;
2453     }
2454 
2455     //------------------------------------------------------------------------------------------------------------------
2456     ScrollBar* TableControl_Impl::getHorzScrollbar()
2457     {
2458 		return m_pHScroll;
2459     }
2460 
2461     //------------------------------------------------------------------------------------------------------------------
2462     ScrollBar* TableControl_Impl::getVertScrollbar()
2463     {
2464 		return m_pVScroll;
2465     }
2466 
2467     //------------------------------------------------------------------------------------------------------------------
2468     bool TableControl_Impl::isRowSelected( RowPos i_row ) const
2469     {
2470 		return ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_row ) != m_aSelectedRows.end();
2471     }
2472 
2473     //------------------------------------------------------------------------------------------------------------------
2474     RowPos TableControl_Impl::getSelectedRowIndex( size_t const i_selectionIndex ) const
2475     {
2476         if ( i_selectionIndex < m_aSelectedRows.size() )
2477             return m_aSelectedRows[ i_selectionIndex ];
2478         return ROW_INVALID;
2479     }
2480 
2481     //------------------------------------------------------------------------------------------------------------------
2482     int TableControl_Impl::getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current)
2483     {
2484 		std::vector<RowPos>::const_iterator it = ::std::find(selectedRows.begin(),selectedRows.end(),current);
2485 		if ( it != selectedRows.end() )
2486 		{
2487 			return it - selectedRows.begin();
2488 		}
2489 		return -1;
2490     }
2491 
2492     //--------------------------------------------------------------------
2493     ColPos TableControl_Impl::impl_getColumnForOrdinate( long const i_ordinate ) const
2494     {
2495         DBG_CHECK_ME();
2496 
2497         if ( ( m_aColumnWidths.empty() ) || ( i_ordinate < 0 ) )
2498             return COL_INVALID;
2499 
2500         if ( i_ordinate < m_nRowHeaderWidthPixel )
2501             return COL_ROW_HEADERS;
2502 
2503         ColumnPositions::const_iterator lowerBound = ::std::lower_bound(
2504             m_aColumnWidths.begin(),
2505             m_aColumnWidths.end(),
2506             i_ordinate + 1,
2507             ColumnInfoPositionLess()
2508         );
2509         if ( lowerBound == m_aColumnWidths.end() )
2510         {
2511             // point is *behind* the start of the last column ...
2512             if ( i_ordinate < m_aColumnWidths.rbegin()->getEnd() )
2513                 // ... but still before its end
2514                 return m_nColumnCount - 1;
2515             return COL_INVALID;
2516         }
2517         return lowerBound - m_aColumnWidths.begin();
2518     }
2519 
2520     //--------------------------------------------------------------------
2521     RowPos TableControl_Impl::impl_getRowForAbscissa( long const i_abscissa ) const
2522     {
2523         DBG_CHECK_ME();
2524 
2525         if ( i_abscissa < 0 )
2526             return ROW_INVALID;
2527 
2528         if ( i_abscissa < m_nColHeaderHeightPixel )
2529             return ROW_COL_HEADERS;
2530 
2531         long const abscissa = i_abscissa - m_nColHeaderHeightPixel;
2532         long const row = m_nTopRow + abscissa / m_nRowHeightPixel;
2533         return row < m_pModel->getRowCount() ? row : ROW_INVALID;
2534     }
2535 
2536     //--------------------------------------------------------------------
2537     bool TableControl_Impl::markRowAsDeselected( RowPos const i_rowIndex )
2538     {
2539         DBG_CHECK_ME();
2540 
2541         ::std::vector< RowPos >::iterator selPos = ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_rowIndex );
2542         if ( selPos == m_aSelectedRows.end() )
2543             return false;
2544 
2545         m_aSelectedRows.erase( selPos );
2546         return true;
2547     }
2548 
2549     //--------------------------------------------------------------------
2550     bool TableControl_Impl::markRowAsSelected( RowPos const i_rowIndex )
2551     {
2552         DBG_CHECK_ME();
2553 
2554         if ( isRowSelected( i_rowIndex ) )
2555             return false;
2556 
2557         SelectionMode const eSelMode = getSelEngine()->GetSelectionMode();
2558         switch ( eSelMode )
2559         {
2560         case SINGLE_SELECTION:
2561             if ( !m_aSelectedRows.empty() )
2562             {
2563                 OSL_ENSURE( m_aSelectedRows.size() == 1, "TableControl::markRowAsSelected: SingleSelection with more than one selected element?" );
2564                 m_aSelectedRows[0] = i_rowIndex;
2565                 break;
2566             }
2567             // fall through
2568 
2569         case MULTIPLE_SELECTION:
2570             m_aSelectedRows.push_back( i_rowIndex );
2571             break;
2572 
2573         default:
2574             OSL_ENSURE( false, "TableControl_Impl::markRowAsSelected: unsupported selection mode!" );
2575             return false;
2576         }
2577 
2578         return true;
2579     }
2580 
2581     //--------------------------------------------------------------------
2582     bool TableControl_Impl::markAllRowsAsDeselected()
2583     {
2584         if ( m_aSelectedRows.empty() )
2585             return false;
2586 
2587         m_aSelectedRows.clear();
2588         return true;
2589     }
2590 
2591     //--------------------------------------------------------------------
2592     bool TableControl_Impl::markAllRowsAsSelected()
2593     {
2594         DBG_CHECK_ME();
2595 
2596         SelectionMode const eSelMode = getSelEngine()->GetSelectionMode();
2597         ENSURE_OR_RETURN_FALSE( eSelMode == MULTIPLE_SELECTION, "TableControl_Impl::markAllRowsAsSelected: unsupported selection mode!" );
2598 
2599         if ( m_aSelectedRows.size() == size_t( m_pModel->getRowCount() ) )
2600         {
2601         #if OSL_DEBUG_LEVEL > 0
2602             for ( TableSize row = 0; row < m_pModel->getRowCount(); ++row )
2603             {
2604                 OSL_ENSURE( isRowSelected( row ), "TableControl_Impl::markAllRowsAsSelected: inconsistency in the selected rows!" );
2605             }
2606         #endif
2607             // already all rows marked as selected
2608             return false;
2609         }
2610 
2611         m_aSelectedRows.clear();
2612         for ( RowPos i=0; i < m_pModel->getRowCount(); ++i )
2613             m_aSelectedRows.push_back(i);
2614 
2615         return true;
2616     }
2617 
2618     //--------------------------------------------------------------------
2619     void TableControl_Impl::commitAccessibleEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2620     {
2621         impl_commitAccessibleEvent( i_eventID, i_newValue, i_oldValue );
2622     }
2623 
2624     //--------------------------------------------------------------------
2625     void TableControl_Impl::commitCellEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2626     {
2627         DBG_CHECK_ME();
2628         if ( impl_isAccessibleAlive() )
2629  	        m_pAccessibleTable->commitCellEvent( i_eventID, i_newValue, i_oldValue );
2630     }
2631 
2632     //--------------------------------------------------------------------
2633     void TableControl_Impl::commitTableEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2634     {
2635         DBG_CHECK_ME();
2636         if ( impl_isAccessibleAlive() )
2637  	        m_pAccessibleTable->commitTableEvent( i_eventID, i_newValue, i_oldValue );
2638     }
2639 
2640     //--------------------------------------------------------------------
2641 	Rectangle TableControl_Impl::calcHeaderRect(bool bColHeader)
2642 	{
2643         Rectangle const aRectTableWithHeaders( impl_getAllVisibleCellsArea() );
2644 		Size const aSizeTableWithHeaders( aRectTableWithHeaders.GetSize() );
2645 		if ( bColHeader )
2646 			return Rectangle( aRectTableWithHeaders.TopLeft(), Size( aSizeTableWithHeaders.Width(), m_nColHeaderHeightPixel ) );
2647 		else
2648 			return Rectangle( aRectTableWithHeaders.TopLeft(), Size( m_nRowHeaderWidthPixel, aSizeTableWithHeaders.Height() ) );
2649 	}
2650 
2651     //--------------------------------------------------------------------
2652     Rectangle TableControl_Impl::calcHeaderCellRect( bool bColHeader, sal_Int32 nPos )
2653     {
2654         Rectangle const aHeaderRect = calcHeaderRect( bColHeader );
2655         TableCellGeometry const aGeometry(
2656             *this, aHeaderRect,
2657             bColHeader ? nPos : COL_ROW_HEADERS,
2658             bColHeader ? ROW_COL_HEADERS : nPos
2659         );
2660         return aGeometry.getRect();
2661     }
2662 
2663     //--------------------------------------------------------------------
2664 	Rectangle TableControl_Impl::calcTableRect()
2665 	{
2666 		return impl_getAllVisibleDataCellArea();
2667 	}
2668 
2669     //--------------------------------------------------------------------
2670     Rectangle TableControl_Impl::calcCellRect( sal_Int32 nRow, sal_Int32 nCol )
2671     {
2672         Rectangle aCellRect;
2673         impl_getCellRect( nRow, nCol, aCellRect );
2674         return aCellRect;
2675     }
2676 
2677     //--------------------------------------------------------------------
2678     IMPL_LINK( TableControl_Impl, OnUpdateScrollbars, void*, /**/ )
2679     {
2680         DBG_CHECK_ME();
2681         // TODO: can't we simply use lcl_updateScrollbar here, so the scrollbars ranges are updated, instead of
2682         // doing a complete re-layout?
2683         impl_ni_relayout();
2684         return 1L;
2685     }
2686 
2687     //--------------------------------------------------------------------
2688     IMPL_LINK( TableControl_Impl, OnScroll, ScrollBar*, _pScrollbar )
2689     {
2690         DBG_ASSERT( ( _pScrollbar == m_pVScroll ) || ( _pScrollbar == m_pHScroll ),
2691             "TableControl_Impl::OnScroll: where did this come from?" );
2692 
2693         if ( _pScrollbar == m_pVScroll )
2694             impl_ni_ScrollRows( _pScrollbar->GetDelta() );
2695         else
2696             impl_ni_ScrollColumns( _pScrollbar->GetDelta() );
2697 
2698         return 0L;
2699     }
2700 
2701     //------------------------------------------------------------------------------------------------------------------
2702     Reference< XAccessible > TableControl_Impl::getAccessible( Window& i_parentWindow )
2703     {
2704         DBG_TESTSOLARMUTEX();
2705 	    if ( m_pAccessibleTable == NULL )
2706 		{
2707 			Reference< XAccessible > const xAccParent = i_parentWindow.GetAccessible();
2708 			if ( xAccParent.is() )
2709 			{
2710 				m_pAccessibleTable = m_aFactoryAccess.getFactory().createAccessibleTableControl(
2711 					xAccParent, m_rAntiImpl
2712 				);
2713 			}
2714 		}
2715 
2716 		Reference< XAccessible > xAccessible;
2717 		if ( m_pAccessibleTable )
2718 			xAccessible = m_pAccessibleTable->getMyself();
2719 		return xAccessible;
2720     }
2721 
2722     //------------------------------------------------------------------------------------------------------------------
2723     void TableControl_Impl::disposeAccessible()
2724     {
2725         if ( m_pAccessibleTable )
2726             m_pAccessibleTable->dispose();
2727         m_pAccessibleTable = NULL;
2728     }
2729 
2730     //------------------------------------------------------------------------------------------------------------------
2731     bool TableControl_Impl::impl_isAccessibleAlive() const
2732     {
2733         DBG_CHECK_ME();
2734         return ( NULL != m_pAccessibleTable ) && m_pAccessibleTable->isAlive();
2735     }
2736 
2737     //------------------------------------------------------------------------------------------------------------------
2738     void TableControl_Impl::impl_commitAccessibleEvent( sal_Int16 const i_eventID, Any const & i_newValue, Any const & i_oldValue )
2739     {
2740         DBG_CHECK_ME();
2741         if ( impl_isAccessibleAlive() )
2742  	        m_pAccessibleTable->commitEvent( i_eventID, i_newValue, i_oldValue );
2743     }
2744 
2745     //==================================================================================================================
2746     //= TableFunctionSet
2747     //==================================================================================================================
2748     //------------------------------------------------------------------------------------------------------------------
2749     TableFunctionSet::TableFunctionSet(TableControl_Impl* _pTableControl)
2750     	:m_pTableControl( _pTableControl)
2751 	    ,m_nCurrentRow( ROW_INVALID )
2752     {
2753     }
2754     //------------------------------------------------------------------------------------------------------------------
2755     TableFunctionSet::~TableFunctionSet()
2756     {
2757     }
2758     //------------------------------------------------------------------------------------------------------------------
2759     void TableFunctionSet::BeginDrag()
2760     {
2761     }
2762     //------------------------------------------------------------------------------------------------------------------
2763     void TableFunctionSet::CreateAnchor()
2764     {
2765 	    m_pTableControl->setAnchor( m_pTableControl->getCurRow() );
2766     }
2767 
2768     //------------------------------------------------------------------------------------------------------------------
2769     void TableFunctionSet::DestroyAnchor()
2770     {
2771 	    m_pTableControl->setAnchor( ROW_INVALID );
2772     }
2773 
2774     //------------------------------------------------------------------------------------------------------------------
2775     sal_Bool TableFunctionSet::SetCursorAtPoint(const Point& rPoint, sal_Bool bDontSelectAtCursor)
2776     {
2777 	    sal_Bool bHandled = sal_False;
2778 	    // newRow is the row which includes the point, getCurRow() is the last selected row, before the mouse click
2779 	    RowPos newRow = m_pTableControl->getRowAtPoint( rPoint );
2780         if ( newRow == ROW_COL_HEADERS )
2781             newRow = m_pTableControl->getTopRow();
2782 
2783         ColPos newCol = m_pTableControl->getColAtPoint( rPoint );
2784         if ( newCol == COL_ROW_HEADERS )
2785             newCol = m_pTableControl->getLeftColumn();
2786 
2787 	    if ( ( newRow == ROW_INVALID ) || ( newCol == COL_INVALID ) )
2788 		    return sal_False;
2789 
2790 	    if ( bDontSelectAtCursor )
2791 	    {
2792 		    if ( m_pTableControl->getSelectedRowCount() > 1 )
2793 			    m_pTableControl->getSelEngine()->AddAlways(sal_True);
2794 		    bHandled = sal_True;
2795 	    }
2796 	    else if ( m_pTableControl->getAnchor() == m_pTableControl->getCurRow() )
2797 	    {
2798 		    //selecting region,
2799 		    int diff = m_pTableControl->getCurRow() - newRow;
2800 		    //selected region lies above the last selection
2801 		    if( diff >= 0)
2802 		    {
2803 			    //put selected rows in vector
2804 			    while ( m_pTableControl->getAnchor() >= newRow )
2805 			    {
2806 				    m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() );
2807 				    m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 );
2808 				    diff--;
2809 			    }
2810 			    m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 );
2811 		    }
2812 		    //selected region lies beneath the last selected row
2813 		    else
2814 		    {
2815 			    while ( m_pTableControl->getAnchor() <= newRow )
2816 			    {
2817 				    m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() );
2818 				    m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 );
2819 				    diff++;
2820 			    }
2821 			    m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 );
2822 		    }
2823 		    m_pTableControl->invalidateSelectedRegion( m_pTableControl->getCurRow(), newRow );
2824 		    bHandled = sal_True;
2825 	    }
2826 	    //no region selected
2827 	    else
2828 	    {
2829 		    if ( !m_pTableControl->hasRowSelection() )
2830                 m_pTableControl->markRowAsSelected( newRow );
2831 		    else
2832 		    {
2833 			    if ( m_pTableControl->getSelEngine()->GetSelectionMode() == SINGLE_SELECTION )
2834 			    {
2835 				    DeselectAll();
2836                     m_pTableControl->markRowAsSelected( newRow );
2837 			    }
2838 			    else
2839 			    {
2840                     m_pTableControl->markRowAsSelected( newRow );
2841 			    }
2842 		    }
2843 		    if ( m_pTableControl->getSelectedRowCount() > 1 && m_pTableControl->getSelEngine()->GetSelectionMode() != SINGLE_SELECTION )
2844 			    m_pTableControl->getSelEngine()->AddAlways(sal_True);
2845 
2846 		    m_pTableControl->invalidateRow( newRow );
2847 		    bHandled = sal_True;
2848 	    }
2849 	    m_pTableControl->goTo( newCol, newRow );
2850 	    return bHandled;
2851     }
2852     //------------------------------------------------------------------------------------------------------------------
2853     sal_Bool TableFunctionSet::IsSelectionAtPoint( const Point& rPoint )
2854     {
2855 	    m_pTableControl->getSelEngine()->AddAlways(sal_False);
2856 	    if ( !m_pTableControl->hasRowSelection() )
2857 		    return sal_False;
2858 	    else
2859 	    {
2860 		    RowPos curRow = m_pTableControl->getRowAtPoint( rPoint );
2861 		    m_pTableControl->setAnchor( ROW_INVALID );
2862 		    bool selected = m_pTableControl->isRowSelected( curRow );
2863 		    m_nCurrentRow = curRow;
2864 		    return selected;
2865 	    }
2866     }
2867     //------------------------------------------------------------------------------------------------------------------
2868     void TableFunctionSet::DeselectAtPoint( const Point& rPoint )
2869     {
2870 	    (void)rPoint;
2871 	    m_pTableControl->invalidateRow( m_nCurrentRow );
2872 	    m_pTableControl->markRowAsDeselected( m_nCurrentRow );
2873     }
2874 
2875     //------------------------------------------------------------------------------------------------------------------
2876     void TableFunctionSet::DeselectAll()
2877     {
2878 	    if ( m_pTableControl->hasRowSelection() )
2879 	    {
2880             for ( size_t i=0; i<m_pTableControl->getSelectedRowCount(); ++i )
2881 		    {
2882                 RowPos const rowIndex = m_pTableControl->getSelectedRowIndex(i);
2883 			    m_pTableControl->invalidateRow( rowIndex );
2884 		    }
2885 
2886 		    m_pTableControl->markAllRowsAsDeselected();
2887 	    }
2888     }
2889 
2890 //......................................................................................................................
2891 } } // namespace svt::table
2892 //......................................................................................................................
2893