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