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