xref: /trunk/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