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