1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "table/tablecontrol.hxx"
22 #include "table/defaultinputhandler.hxx"
23 #include <svtools/table/tablemodel.hxx>
25 #include "tabledatawindow.hxx"
26 #include "tablecontrol_impl.hxx"
27 #include "tablegeometry.hxx"
29 #include <com/sun/star/accessibility/XAccessible.hpp>
30 #include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
31 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
32 #include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
34 #include <comphelper/flagguard.hxx>
35 #include <vcl/scrbar.hxx>
36 #include <vcl/seleng.hxx>
37 #include <vcl/settings.hxx>
38 #include <rtl/ref.hxx>
39 #include <vcl/image.hxx>
40 #include <tools/diagnose_ex.h>
46 #define MIN_COLUMN_WIDTH_PIXEL 4
49 namespace svt
{ namespace table
53 using ::com::sun::star::accessibility::AccessibleTableModelChange
;
54 using ::com::sun::star::uno::makeAny
;
55 using ::com::sun::star::uno::Any
;
56 using ::com::sun::star::accessibility::XAccessible
;
57 using ::com::sun::star::uno::Reference
;
59 namespace AccessibleEventId
= ::com::sun::star::accessibility::AccessibleEventId
;
60 namespace AccessibleTableModelChangeType
= ::com::sun::star::accessibility::AccessibleTableModelChangeType
;
68 ITableControl
& m_rTable
;
71 SuppressCursor( ITableControl
& _rTable
)
74 m_rTable
.hideCursor();
78 m_rTable
.showCursor();
85 /** default implementation of an ->ITableModel, used as fallback when no
88 Instances of this class are static in any way, and provide the least
89 necessary default functionality for a table model.
91 class EmptyTableModel
: public ITableModel
98 // ITableModel overridables
99 virtual TableSize
getColumnCount() const SAL_OVERRIDE
103 virtual TableSize
getRowCount() const SAL_OVERRIDE
107 virtual bool hasColumnHeaders() const SAL_OVERRIDE
111 virtual bool hasRowHeaders() const SAL_OVERRIDE
115 virtual bool isCellEditable( ColPos col
, RowPos row
) const SAL_OVERRIDE
121 virtual PColumnModel
getColumnModel( ColPos column
) SAL_OVERRIDE
123 OSL_FAIL( "EmptyTableModel::getColumnModel: invalid call!" );
125 return PColumnModel();
127 virtual PTableRenderer
getRenderer() const SAL_OVERRIDE
129 return PTableRenderer();
131 virtual PTableInputHandler
getInputHandler() const SAL_OVERRIDE
133 return PTableInputHandler();
135 virtual TableMetrics
getRowHeight() const SAL_OVERRIDE
139 virtual TableMetrics
getColumnHeaderHeight() const SAL_OVERRIDE
143 virtual TableMetrics
getRowHeaderWidth() const SAL_OVERRIDE
147 virtual ScrollbarVisibility
getVerticalScrollbarVisibility() const SAL_OVERRIDE
149 return ScrollbarShowNever
;
151 virtual ScrollbarVisibility
getHorizontalScrollbarVisibility() const SAL_OVERRIDE
153 return ScrollbarShowNever
;
155 virtual void addTableModelListener( const PTableModelListener
& i_listener
) SAL_OVERRIDE
159 virtual void removeTableModelListener( const PTableModelListener
& i_listener
) SAL_OVERRIDE
163 virtual ::boost::optional
< ::Color
> getLineColor() const SAL_OVERRIDE
165 return ::boost::optional
< ::Color
>();
167 virtual ::boost::optional
< ::Color
> getHeaderBackgroundColor() const SAL_OVERRIDE
169 return ::boost::optional
< ::Color
>();
171 virtual ::boost::optional
< ::Color
> getHeaderTextColor() const SAL_OVERRIDE
173 return ::boost::optional
< ::Color
>();
175 virtual ::boost::optional
< ::Color
> getActiveSelectionBackColor() const SAL_OVERRIDE
177 return ::boost::optional
< ::Color
>();
179 virtual ::boost::optional
< ::Color
> getInactiveSelectionBackColor() const SAL_OVERRIDE
181 return ::boost::optional
< ::Color
>();
183 virtual ::boost::optional
< ::Color
> getActiveSelectionTextColor() const SAL_OVERRIDE
185 return ::boost::optional
< ::Color
>();
187 virtual ::boost::optional
< ::Color
> getInactiveSelectionTextColor() const SAL_OVERRIDE
189 return ::boost::optional
< ::Color
>();
191 virtual ::boost::optional
< ::Color
> getTextColor() const SAL_OVERRIDE
193 return ::boost::optional
< ::Color
>();
195 virtual ::boost::optional
< ::Color
> getTextLineColor() const SAL_OVERRIDE
197 return ::boost::optional
< ::Color
>();
199 virtual ::boost::optional
< ::std::vector
< ::Color
> > getRowBackgroundColors() const SAL_OVERRIDE
201 return ::boost::optional
< ::std::vector
< ::Color
> >();
203 virtual ::com::sun::star::style::VerticalAlignment
getVerticalAlign() const SAL_OVERRIDE
205 return com::sun::star::style::VerticalAlignment(0);
207 virtual ITableDataSort
* getSortAdapter() SAL_OVERRIDE
211 virtual bool isEnabled() const SAL_OVERRIDE
215 virtual void getCellContent( ColPos
const i_col
, RowPos
const i_row
, ::com::sun::star::uno::Any
& o_cellContent
) SAL_OVERRIDE
219 o_cellContent
.clear();
221 virtual void getCellToolTip( ColPos
const, RowPos
const, ::com::sun::star::uno::Any
& ) SAL_OVERRIDE
224 virtual Any
getRowHeading( RowPos
const i_rowPos
) const SAL_OVERRIDE
231 TableControl_Impl::TableControl_Impl( TableControl
& _rAntiImpl
)
232 :m_rAntiImpl ( _rAntiImpl
)
233 ,m_pModel ( new EmptyTableModel
)
235 ,m_nRowHeightPixel ( 15 )
236 ,m_nColHeaderHeightPixel( 0 )
237 ,m_nRowHeaderWidthPixel ( 0 )
238 ,m_nColumnCount ( 0 )
240 ,m_nCurColumn ( COL_INVALID
)
241 ,m_nCurRow ( ROW_INVALID
)
244 ,m_nCursorHidden ( 1 )
245 ,m_pDataWindow ( VclPtr
<TableDataWindow
>::Create( *this ) )
248 ,m_pScrollCorner ( NULL
)
251 ,m_pTableFunctionSet ( new TableFunctionSet( this ) )
253 ,m_bUpdatingColWidths ( false )
254 ,m_pAccessibleTable ( NULL
)
256 m_pSelEngine
= new SelectionEngine( m_pDataWindow
.get(), m_pTableFunctionSet
);
257 m_pSelEngine
->SetSelectionMode(SINGLE_SELECTION
);
258 m_pDataWindow
->SetPosPixel( Point( 0, 0 ) );
259 m_pDataWindow
->Show();
262 TableControl_Impl::~TableControl_Impl()
264 m_pVScroll
.disposeAndClear();
265 m_pHScroll
.disposeAndClear();
266 m_pScrollCorner
.disposeAndClear();
267 m_pDataWindow
.disposeAndClear();
268 DELETEZ( m_pTableFunctionSet
);
269 DELETEZ( m_pSelEngine
);
272 void TableControl_Impl::setModel( PTableModel _pModel
)
274 SuppressCursor
aHideCursor( *this );
277 m_pModel
->removeTableModelListener( shared_from_this() );
281 m_pModel
.reset( new EmptyTableModel
);
283 m_pModel
->addTableModelListener( shared_from_this() );
285 m_nCurRow
= ROW_INVALID
;
286 m_nCurColumn
= COL_INVALID
;
288 // recalc some model-dependent cached info
289 impl_ni_updateCachedModelValues();
292 // completely invalidate
293 m_rAntiImpl
.Invalidate();
295 // reset cursor to (0,0)
296 if ( m_nRowCount
) m_nCurRow
= 0;
297 if ( m_nColumnCount
) m_nCurColumn
= 0;
303 bool lcl_adjustSelectedRows( ::std::vector
< RowPos
>& io_selectionIndexes
, RowPos
const i_firstAffectedRowIndex
, TableSize
const i_offset
)
305 bool didChanges
= false;
306 for ( ::std::vector
< RowPos
>::iterator selPos
= io_selectionIndexes
.begin();
307 selPos
!= io_selectionIndexes
.end();
311 if ( *selPos
< i_firstAffectedRowIndex
)
321 void TableControl_Impl::rowsInserted( RowPos i_first
, RowPos i_last
)
323 OSL_PRECOND( i_last
>= i_first
, "TableControl_Impl::rowsInserted: invalid row indexes!" );
325 TableSize
const insertedRows
= i_last
- i_first
+ 1;
327 // adjust selection, if necessary
328 bool const selectionChanged
= lcl_adjustSelectedRows( m_aSelectedRows
, i_first
, insertedRows
);
330 // adjust our cached row count
331 m_nRowCount
= m_pModel
->getRowCount();
333 // if the rows have been inserted before the current row, adjust this
334 if ( i_first
<= m_nCurRow
)
335 goTo( m_nCurColumn
, m_nCurRow
+ insertedRows
);
337 // relayout, since the scrollbar need might have changed
340 // notify A1YY events
341 if ( impl_isAccessibleAlive() )
343 impl_commitAccessibleEvent( AccessibleEventId::TABLE_MODEL_CHANGED
,
344 makeAny( AccessibleTableModelChange( AccessibleTableModelChangeType::INSERT
, i_first
, i_last
, 0, m_pModel
->getColumnCount() ) ),
350 invalidateRowRange( i_first
, ROW_INVALID
);
352 // call selection handlers, if necessary
353 if ( selectionChanged
)
354 m_rAntiImpl
.Select();
358 void TableControl_Impl::rowsRemoved( RowPos i_first
, RowPos i_last
)
360 sal_Int32 firstRemovedRow
= i_first
;
361 sal_Int32 lastRemovedRow
= i_last
;
363 // adjust selection, if necessary
364 bool selectionChanged
= false;
367 selectionChanged
= markAllRowsAsDeselected();
370 lastRemovedRow
= m_nRowCount
- 1;
374 ENSURE_OR_RETURN_VOID( i_last
>= i_first
, "TableControl_Impl::rowsRemoved: illegal indexes!" );
376 for ( sal_Int32 row
= i_first
; row
<= i_last
; ++row
)
378 if ( markRowAsDeselected( row
) )
379 selectionChanged
= true;
382 if ( lcl_adjustSelectedRows( m_aSelectedRows
, i_last
+ 1, i_first
- i_last
- 1 ) )
383 selectionChanged
= true;
386 // adjust cached row count
387 m_nRowCount
= m_pModel
->getRowCount();
389 // adjust the current row, if it is larger than the row count now
390 if ( m_nCurRow
>= m_nRowCount
)
392 if ( m_nRowCount
> 0 )
393 goTo( m_nCurColumn
, m_nRowCount
- 1 );
396 m_nCurRow
= ROW_INVALID
;
400 else if ( m_nRowCount
== 0 )
406 // relayout, since the scrollbar need might have changed
409 // notify A11Y events
410 if ( impl_isAccessibleAlive() )
413 AccessibleEventId::TABLE_MODEL_CHANGED
,
414 makeAny( AccessibleTableModelChange(
415 AccessibleTableModelChangeType::DELETE
,
419 m_pModel
->getColumnCount()
425 // schedule a repaint
426 invalidateRowRange( firstRemovedRow
, ROW_INVALID
);
428 // call selection handlers, if necessary
429 if ( selectionChanged
)
430 m_rAntiImpl
.Select();
434 void TableControl_Impl::columnInserted( ColPos
const i_colIndex
)
436 m_nColumnCount
= m_pModel
->getColumnCount();
439 m_rAntiImpl
.Invalidate();
441 OSL_UNUSED( i_colIndex
);
445 void TableControl_Impl::columnRemoved( ColPos
const i_colIndex
)
447 m_nColumnCount
= m_pModel
->getColumnCount();
449 // adjust the current column, if it is larger than the column count now
450 if ( m_nCurColumn
>= m_nColumnCount
)
452 if ( m_nColumnCount
> 0 )
453 goTo( m_nCurColumn
- 1, m_nCurRow
);
455 m_nCurColumn
= COL_INVALID
;
460 m_rAntiImpl
.Invalidate();
462 OSL_UNUSED( i_colIndex
);
466 void TableControl_Impl::allColumnsRemoved()
468 m_nColumnCount
= m_pModel
->getColumnCount();
471 m_rAntiImpl
.Invalidate();
475 void TableControl_Impl::cellsUpdated( ColPos
const i_firstCol
, ColPos i_lastCol
, RowPos
const i_firstRow
, RowPos
const i_lastRow
)
477 invalidateRowRange( i_firstRow
, i_lastRow
);
479 OSL_UNUSED( i_firstCol
);
480 OSL_UNUSED( i_lastCol
);
484 void TableControl_Impl::tableMetricsChanged()
486 impl_ni_updateCachedTableMetrics();
488 m_rAntiImpl
.Invalidate();
492 void TableControl_Impl::impl_invalidateColumn( ColPos
const i_column
)
494 Rectangle
const aAllCellsArea( impl_getAllVisibleCellsArea() );
496 const TableColumnGeometry
aColumn( *this, aAllCellsArea
, i_column
);
497 if ( aColumn
.isValid() )
498 m_rAntiImpl
.Invalidate( aColumn
.getRect() );
502 void TableControl_Impl::columnChanged( ColPos
const i_column
, ColumnAttributeGroup
const i_attributeGroup
)
504 ColumnAttributeGroup
nGroup( i_attributeGroup
);
505 if ( nGroup
& ColumnAttributeGroup::APPEARANCE
)
507 impl_invalidateColumn( i_column
);
508 nGroup
&= ~ColumnAttributeGroup::APPEARANCE
;
511 if ( nGroup
& ColumnAttributeGroup::WIDTH
)
513 if ( !m_bUpdatingColWidths
)
515 impl_ni_relayout( i_column
);
516 invalidate( TableAreaAll
);
519 nGroup
&= ~ColumnAttributeGroup::WIDTH
;
522 OSL_ENSURE( ( nGroup
== ColumnAttributeGroup::NONE
) || ( i_attributeGroup
== ColumnAttributeGroup::ALL
),
523 "TableControl_Impl::columnChanged: don't know how to handle this change!" );
527 Rectangle
TableControl_Impl::impl_getAllVisibleCellsArea() const
529 Rectangle
aArea( Point( 0, 0 ), Size( 0, 0 ) );
531 // determine the right-most border of the last column which is
532 // at least partially visible
533 aArea
.Right() = m_nRowHeaderWidthPixel
;
534 if ( !m_aColumnWidths
.empty() )
536 // the number of pixels which are scrolled out of the left hand
537 // side of the window
538 const long nScrolledOutLeft
= m_nLeftColumn
== 0 ? 0 : m_aColumnWidths
[ m_nLeftColumn
- 1 ].getEnd();
540 ColumnPositions::const_reverse_iterator loop
= m_aColumnWidths
.rbegin();
543 aArea
.Right() = loop
->getEnd() - nScrolledOutLeft
+ m_nRowHeaderWidthPixel
;
546 while ( ( loop
!= m_aColumnWidths
.rend() )
547 && ( loop
->getEnd() - nScrolledOutLeft
>= aArea
.Right() )
550 // so far, aArea.Right() denotes the first pixel *after* the cell area
553 // determine the last row which is at least partially visible
555 m_nColHeaderHeightPixel
556 + impl_getVisibleRows( true ) * m_nRowHeightPixel
563 Rectangle
TableControl_Impl::impl_getAllVisibleDataCellArea() const
565 Rectangle
aArea( impl_getAllVisibleCellsArea() );
566 aArea
.Left() = m_nRowHeaderWidthPixel
;
567 aArea
.Top() = m_nColHeaderHeightPixel
;
572 void TableControl_Impl::impl_ni_updateCachedTableMetrics()
574 m_nRowHeightPixel
= m_rAntiImpl
.LogicToPixel( Size( 0, m_pModel
->getRowHeight() ), MAP_APPFONT
).Height();
576 m_nColHeaderHeightPixel
= 0;
577 if ( m_pModel
->hasColumnHeaders() )
578 m_nColHeaderHeightPixel
= m_rAntiImpl
.LogicToPixel( Size( 0, m_pModel
->getColumnHeaderHeight() ), MAP_APPFONT
).Height();
580 m_nRowHeaderWidthPixel
= 0;
581 if ( m_pModel
->hasRowHeaders() )
582 m_nRowHeaderWidthPixel
= m_rAntiImpl
.LogicToPixel( Size( m_pModel
->getRowHeaderWidth(), 0 ), MAP_APPFONT
).Width();
586 void TableControl_Impl::impl_ni_updateCachedModelValues()
588 m_pInputHandler
= m_pModel
->getInputHandler();
589 if ( !m_pInputHandler
)
590 m_pInputHandler
.reset( new DefaultInputHandler
);
592 m_nColumnCount
= m_pModel
->getColumnCount();
593 if ( m_nLeftColumn
>= m_nColumnCount
)
594 m_nLeftColumn
= ( m_nColumnCount
> 0 ) ? m_nColumnCount
- 1 : 0;
596 m_nRowCount
= m_pModel
->getRowCount();
597 if ( m_nTopRow
>= m_nRowCount
)
598 m_nTopRow
= ( m_nRowCount
> 0 ) ? m_nRowCount
- 1 : 0;
600 impl_ni_updateCachedTableMetrics();
607 /// determines whether a scrollbar is needed for the given values
608 bool lcl_determineScrollbarNeed( long const i_position
, ScrollbarVisibility
const i_visibility
,
609 long const i_availableSpace
, long const i_neededSpace
)
611 if ( i_visibility
== ScrollbarShowNever
)
613 if ( i_visibility
== ScrollbarShowAlways
)
615 if ( i_position
> 0 )
617 if ( i_availableSpace
>= i_neededSpace
)
623 void lcl_setButtonRepeat( vcl::Window
& _rWindow
, sal_uLong _nDelay
)
625 AllSettings aSettings
= _rWindow
.GetSettings();
626 MouseSettings aMouseSettings
= aSettings
.GetMouseSettings();
628 aMouseSettings
.SetButtonRepeat( _nDelay
);
629 aSettings
.SetMouseSettings( aMouseSettings
);
631 _rWindow
.SetSettings( aSettings
, true );
635 bool lcl_updateScrollbar( vcl::Window
& _rParent
, VclPtr
<ScrollBar
>& _rpBar
,
636 bool const i_needBar
, long _nVisibleUnits
,
637 long _nPosition
, long _nLineSize
, long _nRange
,
638 bool _bHorizontal
, const Link
<>& _rScrollHandler
)
640 // do we currently have the scrollbar?
641 bool bHaveBar
= _rpBar
!= nullptr;
643 // do we need to correct the scrollbar visibility?
644 if ( bHaveBar
&& !i_needBar
)
646 if ( _rpBar
->IsTracking() )
647 _rpBar
->EndTracking();
648 _rpBar
.disposeAndClear();
650 else if ( !bHaveBar
&& i_needBar
)
652 _rpBar
= VclPtr
<ScrollBar
>::Create(
655 WB_DRAG
| ( _bHorizontal
? WB_HSCROLL
: WB_VSCROLL
)
657 _rpBar
->SetScrollHdl( _rScrollHandler
);
658 // get some speed into the scrolling ....
659 lcl_setButtonRepeat( *_rpBar
, 0 );
664 _rpBar
->SetRange( Range( 0, _nRange
) );
665 _rpBar
->SetVisibleSize( _nVisibleUnits
);
666 _rpBar
->SetPageSize( _nVisibleUnits
);
667 _rpBar
->SetLineSize( _nLineSize
);
668 _rpBar
->SetThumbPos( _nPosition
);
672 return ( bHaveBar
!= i_needBar
);
676 /** returns the number of rows fitting into the given range,
677 for the given row height. Partially fitting rows are counted, too, if the
678 respective parameter says so.
680 TableSize
lcl_getRowsFittingInto( long _nOverallHeight
, long _nRowHeightPixel
, bool _bAcceptPartialRow
= false )
682 return _bAcceptPartialRow
683 ? ( _nOverallHeight
+ ( _nRowHeightPixel
- 1 ) ) / _nRowHeightPixel
684 : _nOverallHeight
/ _nRowHeightPixel
;
688 /** returns the number of columns fitting into the given area,
689 with the first visible column as given. Partially fitting columns are counted, too,
690 if the respective parameter says so.
692 TableSize
lcl_getColumnsVisibleWithin( const Rectangle
& _rArea
, ColPos _nFirstVisibleColumn
,
693 const TableControl_Impl
& _rControl
, bool _bAcceptPartialRow
)
695 TableSize visibleColumns
= 0;
696 TableColumnGeometry
aColumn( _rControl
, _rArea
, _nFirstVisibleColumn
);
697 while ( aColumn
.isValid() )
699 if ( !_bAcceptPartialRow
)
700 if ( aColumn
.getRect().Right() > _rArea
.Right() )
701 // this column is only partially visible, and this is not allowed
707 return visibleColumns
;
713 long TableControl_Impl::impl_ni_calculateColumnWidths( ColPos
const i_assumeInflexibleColumnsUpToIncluding
,
714 bool const i_assumeVerticalScrollbar
, ::std::vector
< long >& o_newColWidthsPixel
) const
716 // the available horizontal space
717 long gridWidthPixel
= m_rAntiImpl
.GetOutputSizePixel().Width();
718 ENSURE_OR_RETURN( !!m_pModel
, "TableControl_Impl::impl_ni_calculateColumnWidths: not allowed without a model!", gridWidthPixel
);
719 if ( m_pModel
->hasRowHeaders() && ( gridWidthPixel
!= 0 ) )
721 gridWidthPixel
-= m_nRowHeaderWidthPixel
;
724 if ( i_assumeVerticalScrollbar
&& ( m_pModel
->getVerticalScrollbarVisibility() != ScrollbarShowNever
) )
726 long nScrollbarMetrics
= m_rAntiImpl
.GetSettings().GetStyleSettings().GetScrollBarSize();
727 gridWidthPixel
-= nScrollbarMetrics
;
730 // no need to do anything without columns
731 TableSize
const colCount
= m_pModel
->getColumnCount();
733 return gridWidthPixel
;
735 // collect some meta data for our columns:
736 // - their current (pixel) metrics
737 long accumulatedCurrentWidth
= 0;
738 ::std::vector
< long > currentColWidths
;
739 currentColWidths
.reserve( colCount
);
740 typedef ::std::vector
< ::std::pair
< long, long > > ColumnLimits
;
741 ColumnLimits effectiveColumnLimits
;
742 effectiveColumnLimits
.reserve( colCount
);
743 long accumulatedMinWidth
= 0;
744 long accumulatedMaxWidth
= 0;
745 // - their relative flexibility
746 ::std::vector
< ::sal_Int32
> columnFlexibilities
;
747 columnFlexibilities
.reserve( colCount
);
748 long flexibilityDenominator
= 0;
749 size_t flexibleColumnCount
= 0;
750 for ( ColPos col
= 0; col
< colCount
; ++col
)
752 PColumnModel
const pColumn
= m_pModel
->getColumnModel( col
);
753 ENSURE_OR_THROW( !!pColumn
, "invalid column returned by the model!" );
756 long const currentWidth
= appFontWidthToPixel( pColumn
->getWidth() );
757 currentColWidths
.push_back( currentWidth
);
760 accumulatedCurrentWidth
+= currentWidth
;
763 ::sal_Int32 flexibility
= pColumn
->getFlexibility();
764 OSL_ENSURE( flexibility
>= 0, "TableControl_Impl::impl_ni_calculateColumnWidths: a column's flexibility should be non-negative." );
765 if ( ( flexibility
< 0 ) // normalization
766 || ( !pColumn
->isResizable() ) // column not resizable => no auto-resize
767 || ( col
<= i_assumeInflexibleColumnsUpToIncluding
) // column shall be treated as inflexible => respec this
772 long effectiveMin
= currentWidth
, effectiveMax
= currentWidth
;
773 // if the column is not flexible, it will not be asked for min/max, but we assume the current width as limit then
774 if ( flexibility
> 0 )
776 long const minWidth
= appFontWidthToPixel( pColumn
->getMinWidth() );
778 effectiveMin
= minWidth
;
780 effectiveMin
= MIN_COLUMN_WIDTH_PIXEL
;
782 long const maxWidth
= appFontWidthToPixel( pColumn
->getMaxWidth() );
783 OSL_ENSURE( minWidth
<= maxWidth
, "TableControl_Impl::impl_ni_calculateColumnWidths: pretty undecided 'bout its width limits, this column!" );
784 if ( ( maxWidth
> 0 ) && ( maxWidth
>= minWidth
) )
785 effectiveMax
= maxWidth
;
787 effectiveMax
= gridWidthPixel
; // TODO: any better guess here?
789 if ( effectiveMin
== effectiveMax
)
790 // if the min and the max are identical, this implies no flexibility at all
794 columnFlexibilities
.push_back( flexibility
);
795 flexibilityDenominator
+= flexibility
;
796 if ( flexibility
> 0 )
797 ++flexibleColumnCount
;
799 effectiveColumnLimits
.push_back( ::std::pair
< long, long >( effectiveMin
, effectiveMax
) );
800 accumulatedMinWidth
+= effectiveMin
;
801 accumulatedMaxWidth
+= effectiveMax
;
804 o_newColWidthsPixel
= currentColWidths
;
805 if ( flexibilityDenominator
== 0 )
807 // no column is flexible => don't adjust anything
809 else if ( gridWidthPixel
> accumulatedCurrentWidth
)
810 { // we have space to give away ...
811 long distributePixel
= gridWidthPixel
- accumulatedCurrentWidth
;
812 if ( gridWidthPixel
> accumulatedMaxWidth
)
814 // ... but the column's maximal widths are still less than we have
815 // => set them all to max
816 for ( size_t i
= 0; i
< size_t( colCount
); ++i
)
818 o_newColWidthsPixel
[i
] = effectiveColumnLimits
[i
].second
;
823 bool startOver
= false;
827 // distribute the remaining space amongst all columns with a positive flexibility
828 for ( size_t i
=0; i
<o_newColWidthsPixel
.size() && !startOver
; ++i
)
830 long const columnFlexibility
= columnFlexibilities
[i
];
831 if ( columnFlexibility
== 0 )
834 long newColWidth
= currentColWidths
[i
] + columnFlexibility
* distributePixel
/ flexibilityDenominator
;
836 if ( newColWidth
> effectiveColumnLimits
[i
].second
)
837 { // that was too much, we hit the col's maximum
838 // set the new width to exactly this maximum
839 newColWidth
= effectiveColumnLimits
[i
].second
;
840 // adjust the flexibility denominator ...
841 flexibilityDenominator
-= columnFlexibility
;
842 columnFlexibilities
[i
] = 0;
843 --flexibleColumnCount
;
844 // ... and the remaining width ...
845 long const difference
= newColWidth
- currentColWidths
[i
];
846 distributePixel
-= difference
;
847 // ... this way, we ensure that the width not taken up by this column is consumed by the other
848 // flexible ones (if there are some)
850 // and start over with the first column, since there might be earlier columns which need
851 // to be recalculated now
855 o_newColWidthsPixel
[i
] = newColWidth
;
860 // are there pixels left (might be caused by rounding errors)?
861 distributePixel
= gridWidthPixel
- ::std::accumulate( o_newColWidthsPixel
.begin(), o_newColWidthsPixel
.end(), 0 );
862 while ( ( distributePixel
> 0 ) && ( flexibleColumnCount
> 0 ) )
864 // yes => ignore relative flexibilities, and subsequently distribute single pixels to all flexible
865 // columns which did not yet reach their maximum.
866 for ( size_t i
=0; ( i
< o_newColWidthsPixel
.size() ) && ( distributePixel
> 0 ); ++i
)
868 if ( columnFlexibilities
[i
] == 0 )
871 OSL_ENSURE( o_newColWidthsPixel
[i
] <= effectiveColumnLimits
[i
].second
,
872 "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" );
873 if ( o_newColWidthsPixel
[i
] >= effectiveColumnLimits
[i
].first
)
875 columnFlexibilities
[i
] = 0;
876 --flexibleColumnCount
;
880 ++o_newColWidthsPixel
[i
];
886 else if ( gridWidthPixel
< accumulatedCurrentWidth
)
887 { // we need to take away some space from the columns which allow it ...
888 long takeAwayPixel
= accumulatedCurrentWidth
- gridWidthPixel
;
889 if ( gridWidthPixel
< accumulatedMinWidth
)
891 // ... but the column's minimal widths are still more than we have
892 // => set them all to min
893 for ( size_t i
= 0; i
< size_t( colCount
); ++i
)
895 o_newColWidthsPixel
[i
] = effectiveColumnLimits
[i
].first
;
900 bool startOver
= false;
904 // take away the space we need from the columns with a positive flexibility
905 for ( size_t i
=0; i
<o_newColWidthsPixel
.size() && !startOver
; ++i
)
907 long const columnFlexibility
= columnFlexibilities
[i
];
908 if ( columnFlexibility
== 0 )
911 long newColWidth
= currentColWidths
[i
] - columnFlexibility
* takeAwayPixel
/ flexibilityDenominator
;
913 if ( newColWidth
< effectiveColumnLimits
[i
].first
)
914 { // that was too much, we hit the col's minimum
915 // set the new width to exactly this minimum
916 newColWidth
= effectiveColumnLimits
[i
].first
;
917 // adjust the flexibility denominator ...
918 flexibilityDenominator
-= columnFlexibility
;
919 columnFlexibilities
[i
] = 0;
920 --flexibleColumnCount
;
921 // ... and the remaining width ...
922 long const difference
= currentColWidths
[i
] - newColWidth
;
923 takeAwayPixel
-= difference
;
925 // and start over with the first column, since there might be earlier columns which need
926 // to be recalculated now
930 o_newColWidthsPixel
[i
] = newColWidth
;
935 // are there pixels left (might be caused by rounding errors)?
936 takeAwayPixel
= ::std::accumulate( o_newColWidthsPixel
.begin(), o_newColWidthsPixel
.end(), 0 ) - gridWidthPixel
;
937 while ( ( takeAwayPixel
> 0 ) && ( flexibleColumnCount
> 0 ) )
939 // yes => ignore relative flexibilities, and subsequently take away pixels from all flexible
940 // columns which did not yet reach their minimum.
941 for ( size_t i
=0; ( i
< o_newColWidthsPixel
.size() ) && ( takeAwayPixel
> 0 ); ++i
)
943 if ( columnFlexibilities
[i
] == 0 )
946 OSL_ENSURE( o_newColWidthsPixel
[i
] >= effectiveColumnLimits
[i
].first
,
947 "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" );
948 if ( o_newColWidthsPixel
[i
] <= effectiveColumnLimits
[i
].first
)
950 columnFlexibilities
[i
] = 0;
951 --flexibleColumnCount
;
955 --o_newColWidthsPixel
[i
];
962 return gridWidthPixel
;
966 void TableControl_Impl::impl_ni_relayout( ColPos
const i_assumeInflexibleColumnsUpToIncluding
)
968 ENSURE_OR_RETURN_VOID( !m_bUpdatingColWidths
, "TableControl_Impl::impl_ni_relayout: recursive call detected!" );
970 m_aColumnWidths
.resize( 0 );
974 ::comphelper::FlagRestorationGuard
const aWidthUpdateFlag( m_bUpdatingColWidths
, true );
975 SuppressCursor
aHideCursor( *this );
979 // 1. adjust column widths, leaving space for a vertical scrollbar
980 // 2. determine need for a vertical scrollbar
981 // - V-YES: all fine, result from 1. is still valid
982 // - V-NO: result from 1. is still under consideration
984 // 3. determine need for a horizontal scrollbar
985 // - H-NO: all fine, result from 2. is still valid
986 // - H-YES: reconsider need for a vertical scrollbar, if result of 2. was V-NO
987 // - V-YES: all fine, result from 1. is still valid
988 // - V-NO: redistribute the remaining space (if any) amongst all columns which allow it
990 ::std::vector
< long > newWidthsPixel
;
991 long gridWidthPixel
= impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding
, true, newWidthsPixel
);
993 // the width/height of a scrollbar, needed several times below
994 long const nScrollbarMetrics
= m_rAntiImpl
.GetSettings().GetStyleSettings().GetScrollBarSize();
996 // determine the playground for the data cells (excluding headers)
997 // TODO: what if the control is smaller than needed for the headers/scrollbars?
998 Rectangle
aDataCellPlayground( Point( 0, 0 ), m_rAntiImpl
.GetOutputSizePixel() );
999 aDataCellPlayground
.Left() = m_nRowHeaderWidthPixel
;
1000 aDataCellPlayground
.Top() = m_nColHeaderHeightPixel
;
1002 OSL_ENSURE( ( m_nRowCount
== m_pModel
->getRowCount() ) && ( m_nColumnCount
== m_pModel
->getColumnCount() ),
1003 "TableControl_Impl::impl_ni_relayout: how is this expected to work with invalid data?" );
1004 long const nAllColumnsWidth
= ::std::accumulate( newWidthsPixel
.begin(), newWidthsPixel
.end(), 0 );
1006 ScrollbarVisibility
const eVertScrollbar
= m_pModel
->getVerticalScrollbarVisibility();
1007 ScrollbarVisibility
const eHorzScrollbar
= m_pModel
->getHorizontalScrollbarVisibility();
1009 // do we need a vertical scrollbar?
1010 bool bNeedVerticalScrollbar
= lcl_determineScrollbarNeed(
1011 m_nTopRow
, eVertScrollbar
, aDataCellPlayground
.GetHeight(), m_nRowHeightPixel
* m_nRowCount
);
1012 bool bFirstRoundVScrollNeed
= false;
1013 if ( bNeedVerticalScrollbar
)
1015 aDataCellPlayground
.Right() -= nScrollbarMetrics
;
1016 bFirstRoundVScrollNeed
= true;
1019 // do we need a horizontal scrollbar?
1020 bool const bNeedHorizontalScrollbar
= lcl_determineScrollbarNeed(
1021 m_nLeftColumn
, eHorzScrollbar
, aDataCellPlayground
.GetWidth(), nAllColumnsWidth
);
1022 if ( bNeedHorizontalScrollbar
)
1024 aDataCellPlayground
.Bottom() -= nScrollbarMetrics
;
1026 // now that we just found that we need a horizontal scrollbar,
1027 // the need for a vertical one may have changed, since the horizontal
1028 // SB might just occupy enough space so that not all rows do fit
1030 if ( !bFirstRoundVScrollNeed
)
1032 bNeedVerticalScrollbar
= lcl_determineScrollbarNeed(
1033 m_nTopRow
, eVertScrollbar
, aDataCellPlayground
.GetHeight(), m_nRowHeightPixel
* m_nRowCount
);
1034 if ( bNeedVerticalScrollbar
)
1036 aDataCellPlayground
.Right() -= nScrollbarMetrics
;
1041 // the initial call to impl_ni_calculateColumnWidths assumed that we need a vertical scrollbar. If, by now,
1042 // we know that this is not the case, re-calculate the column widths.
1043 if ( !bNeedVerticalScrollbar
)
1044 gridWidthPixel
= impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding
, false, newWidthsPixel
);
1046 // update the column objects with the new widths we finally calculated
1047 TableSize
const colCount
= m_pModel
->getColumnCount();
1048 m_aColumnWidths
.reserve( colCount
);
1049 long accumulatedWidthPixel
= m_nRowHeaderWidthPixel
;
1050 bool anyColumnWidthChanged
= false;
1051 for ( ColPos col
= 0; col
< colCount
; ++col
)
1053 const long columnStart
= accumulatedWidthPixel
;
1054 const long columnEnd
= columnStart
+ newWidthsPixel
[col
];
1055 m_aColumnWidths
.push_back( MutableColumnMetrics( columnStart
, columnEnd
) );
1056 accumulatedWidthPixel
= columnEnd
;
1058 // and don't forget to forward this to the column models
1059 PColumnModel
const pColumn
= m_pModel
->getColumnModel( col
);
1060 ENSURE_OR_THROW( !!pColumn
, "invalid column returned by the model!" );
1062 long const oldColumnWidthAppFont
= pColumn
->getWidth();
1063 long const newColumnWidthAppFont
= pixelWidthToAppFont( newWidthsPixel
[col
] );
1064 pColumn
->setWidth( newColumnWidthAppFont
);
1066 anyColumnWidthChanged
|= ( oldColumnWidthAppFont
!= newColumnWidthAppFont
);
1069 // if the column widths changed, ensure everything is repainted
1070 if ( anyColumnWidthChanged
)
1071 invalidate( TableAreaAll
);
1073 // if the column resizing happened to leave some space at the right, but there are columns
1074 // scrolled out to the left, scroll them in
1075 while ( ( m_nLeftColumn
> 0 )
1076 && ( accumulatedWidthPixel
- m_aColumnWidths
[ m_nLeftColumn
- 1 ].getStart() <= gridWidthPixel
)
1082 // now adjust the column metrics, since they currently ignore the horizontal scroll position
1083 if ( m_nLeftColumn
> 0 )
1085 const long offsetPixel
= m_aColumnWidths
[ 0 ].getStart() - m_aColumnWidths
[ m_nLeftColumn
].getStart();
1086 for ( ColumnPositions::iterator colPos
= m_aColumnWidths
.begin();
1087 colPos
!= m_aColumnWidths
.end();
1091 colPos
->move( offsetPixel
);
1095 // show or hide the scrollbars as needed, and position the data window
1096 impl_ni_positionChildWindows( aDataCellPlayground
, bNeedVerticalScrollbar
, bNeedHorizontalScrollbar
);
1100 void TableControl_Impl::impl_ni_positionChildWindows( Rectangle
const & i_dataCellPlayground
,
1101 bool const i_verticalScrollbar
, bool const i_horizontalScrollbar
)
1103 long const nScrollbarMetrics
= m_rAntiImpl
.GetSettings().GetStyleSettings().GetScrollBarSize();
1105 // create or destroy the vertical scrollbar, as needed
1106 lcl_updateScrollbar(
1109 i_verticalScrollbar
,
1110 lcl_getRowsFittingInto( i_dataCellPlayground
.GetHeight(), m_nRowHeightPixel
),
1112 m_nTopRow
, // current position
1114 m_nRowCount
, // range
1116 LINK( this, TableControl_Impl
, OnScroll
) // scroll handler
1122 Rectangle
aScrollbarArea(
1123 Point( i_dataCellPlayground
.Right() + 1, 0 ),
1124 Size( nScrollbarMetrics
, i_dataCellPlayground
.Bottom() + 1 )
1126 m_pVScroll
->SetPosSizePixel(
1127 aScrollbarArea
.TopLeft(), aScrollbarArea
.GetSize() );
1130 // create or destroy the horizontal scrollbar, as needed
1131 lcl_updateScrollbar(
1134 i_horizontalScrollbar
,
1135 lcl_getColumnsVisibleWithin( i_dataCellPlayground
, m_nLeftColumn
, *this, false ),
1137 m_nLeftColumn
, // current position
1139 m_nColumnCount
, // range
1141 LINK( this, TableControl_Impl
, OnScroll
) // scroll handler
1147 TableSize
const nVisibleUnits
= lcl_getColumnsVisibleWithin( i_dataCellPlayground
, m_nLeftColumn
, *this, false );
1148 TableMetrics
const nRange
= m_nColumnCount
;
1149 if( m_nLeftColumn
+ nVisibleUnits
== nRange
- 1 )
1151 if ( m_aColumnWidths
[ nRange
- 1 ].getStart() - m_aColumnWidths
[ m_nLeftColumn
].getEnd() + m_aColumnWidths
[ nRange
-1 ].getWidth() > i_dataCellPlayground
.GetWidth() )
1153 m_pHScroll
->SetVisibleSize( nVisibleUnits
-1 );
1154 m_pHScroll
->SetPageSize( nVisibleUnits
- 1 );
1157 Rectangle
aScrollbarArea(
1158 Point( 0, i_dataCellPlayground
.Bottom() + 1 ),
1159 Size( i_dataCellPlayground
.Right() + 1, nScrollbarMetrics
)
1161 m_pHScroll
->SetPosSizePixel(
1162 aScrollbarArea
.TopLeft(), aScrollbarArea
.GetSize() );
1165 // the corner window connecting the two scrollbars in the lower right corner
1166 bool bHaveScrollCorner
= nullptr != m_pScrollCorner
;
1167 bool bNeedScrollCorner
= ( nullptr != m_pHScroll
) && ( nullptr != m_pVScroll
);
1168 if ( bHaveScrollCorner
&& !bNeedScrollCorner
)
1170 m_pScrollCorner
.disposeAndClear();
1172 else if ( !bHaveScrollCorner
&& bNeedScrollCorner
)
1174 m_pScrollCorner
= VclPtr
<ScrollBarBox
>::Create( &m_rAntiImpl
);
1175 m_pScrollCorner
->SetSizePixel( Size( nScrollbarMetrics
, nScrollbarMetrics
) );
1176 m_pScrollCorner
->SetPosPixel( Point( i_dataCellPlayground
.Right() + 1, i_dataCellPlayground
.Bottom() + 1 ) );
1177 m_pScrollCorner
->Show();
1179 else if(bHaveScrollCorner
&& bNeedScrollCorner
)
1181 m_pScrollCorner
->SetPosPixel( Point( i_dataCellPlayground
.Right() + 1, i_dataCellPlayground
.Bottom() + 1 ) );
1182 m_pScrollCorner
->Show();
1185 // resize the data window
1186 m_pDataWindow
->SetSizePixel( Size(
1187 i_dataCellPlayground
.GetWidth() + m_nRowHeaderWidthPixel
,
1188 i_dataCellPlayground
.GetHeight() + m_nColHeaderHeightPixel
1193 void TableControl_Impl::onResize()
1196 checkCursorPosition();
1200 void TableControl_Impl::doPaintContent(vcl::RenderContext
& rRenderContext
, const Rectangle
& _rUpdateRect
)
1204 PTableRenderer pRenderer
= getModel()->getRenderer();
1205 DBG_ASSERT(!!pRenderer
, "TableDataWindow::doPaintContent: invalid renderer!");
1209 // our current style settings, to be passed to the renderer
1210 const StyleSettings
& rStyle
= rRenderContext
.GetSettings().GetStyleSettings();
1211 m_nRowCount
= m_pModel
->getRowCount();
1212 // the area occupied by all (at least partially) visible cells, including
1214 Rectangle
const aAllCellsWithHeaders( impl_getAllVisibleCellsArea() );
1216 // draw the header column area
1217 if (m_pModel
->hasColumnHeaders())
1219 TableRowGeometry
const aHeaderRow(*this, Rectangle(Point(0, 0), aAllCellsWithHeaders
.BottomRight()), ROW_COL_HEADERS
);
1220 Rectangle
const aColRect(aHeaderRow
.getRect());
1221 pRenderer
->PaintHeaderArea(rRenderContext
, aColRect
, true, false, rStyle
);
1222 // Note that strictly, aHeaderRow.getRect() also contains the intersection between column
1223 // and row header area. However, below we go to paint this intersection, again,
1224 // so this hopefully doesn't hurt if we already paint it here.
1226 for (TableCellGeometry
aCell(aHeaderRow
, m_nLeftColumn
); aCell
.isValid(); aCell
.moveRight())
1228 if (_rUpdateRect
.GetIntersection(aCell
.getRect()).IsEmpty())
1231 bool isActiveColumn
= (aCell
.getColumn() == getCurrentColumn());
1232 bool isSelectedColumn
= false;
1233 pRenderer
->PaintColumnHeader(aCell
.getColumn(), isActiveColumn
, isSelectedColumn
, rRenderContext
, aCell
.getRect(), rStyle
);
1236 // the area occupied by the row header, if any
1237 Rectangle aRowHeaderArea
;
1238 if (m_pModel
->hasRowHeaders())
1240 aRowHeaderArea
= aAllCellsWithHeaders
;
1241 aRowHeaderArea
.Right() = m_nRowHeaderWidthPixel
- 1;
1243 TableSize
const nVisibleRows
= impl_getVisibleRows(true);
1244 TableSize nActualRows
= nVisibleRows
;
1245 if (m_nTopRow
+ nActualRows
> m_nRowCount
)
1246 nActualRows
= m_nRowCount
- m_nTopRow
;
1247 aRowHeaderArea
.Bottom() = m_nColHeaderHeightPixel
+ m_nRowHeightPixel
* nActualRows
- 1;
1249 pRenderer
->PaintHeaderArea(rRenderContext
, aRowHeaderArea
, false, true, rStyle
);
1250 // Note that strictly, aRowHeaderArea also contains the intersection between column
1251 // and row header area. However, below we go to paint this intersection, again,
1252 // so this hopefully doesn't hurt if we already paint it here.
1254 if (m_pModel
->hasColumnHeaders())
1256 TableCellGeometry
const aIntersection(*this, Rectangle(Point(0, 0), aAllCellsWithHeaders
.BottomRight()),
1257 COL_ROW_HEADERS
, ROW_COL_HEADERS
);
1258 Rectangle
const aInters(aIntersection
.getRect());
1259 pRenderer
->PaintHeaderArea(rRenderContext
, aInters
, true, true, rStyle
);
1263 // draw the table content row by row
1264 TableSize colCount
= getModel()->getColumnCount();
1267 Rectangle
const aAllDataCellsArea(impl_getAllVisibleDataCellArea());
1268 for (TableRowGeometry
aRowIterator(*this, aAllCellsWithHeaders
, getTopRow()); aRowIterator
.isValid(); aRowIterator
.moveDown())
1270 if (_rUpdateRect
.GetIntersection(aRowIterator
.getRect() ).IsEmpty())
1273 bool const isControlFocused
= m_rAntiImpl
.HasControlFocus();
1274 bool const isSelectedRow
= isRowSelected(aRowIterator
.getRow());
1276 Rectangle
const aRect
= aRowIterator
.getRect().GetIntersection(aAllDataCellsArea
);
1278 // give the redenderer a chance to prepare the row
1279 pRenderer
->PrepareRow(aRowIterator
.getRow(), isControlFocused
, isSelectedRow
, rRenderContext
, aRect
, rStyle
);
1281 // paint the row header
1282 if (m_pModel
->hasRowHeaders())
1284 const Rectangle
aCurrentRowHeader(aRowHeaderArea
.GetIntersection(aRowIterator
.getRect()));
1285 pRenderer
->PaintRowHeader(isControlFocused
, isSelectedRow
, rRenderContext
, aCurrentRowHeader
, rStyle
);
1291 // paint all cells in this row
1292 for (TableCellGeometry
aCell(aRowIterator
, m_nLeftColumn
); aCell
.isValid(); aCell
.moveRight())
1294 bool isSelectedColumn
= false;
1295 pRenderer
->PaintCell(aCell
.getColumn(), isSelectedRow
|| isSelectedColumn
, isControlFocused
,
1296 rRenderContext
, aCell
.getRect(), rStyle
);
1301 void TableControl_Impl::hideCursor()
1303 if ( ++m_nCursorHidden
== 1 )
1304 impl_ni_doSwitchCursor( false );
1308 void TableControl_Impl::showCursor()
1310 DBG_ASSERT( m_nCursorHidden
> 0, "TableControl_Impl::showCursor: cursor not hidden!" );
1311 if ( --m_nCursorHidden
== 0 )
1312 impl_ni_doSwitchCursor( true );
1316 bool TableControl_Impl::dispatchAction( TableControlAction _eAction
)
1318 bool bSuccess
= false;
1319 bool selectionChanged
= false;
1324 if ( m_pSelEngine
->GetSelectionMode() == SINGLE_SELECTION
)
1326 //if other rows already selected, deselect them
1327 if(!m_aSelectedRows
.empty())
1329 invalidateSelectedRows();
1330 m_aSelectedRows
.clear();
1332 if ( m_nCurRow
< m_nRowCount
-1 )
1335 m_aSelectedRows
.push_back(m_nCurRow
);
1338 m_aSelectedRows
.push_back(m_nCurRow
);
1339 invalidateRow( m_nCurRow
);
1340 ensureVisible(m_nCurColumn
,m_nCurRow
,false);
1341 selectionChanged
= true;
1346 if ( m_nCurRow
< m_nRowCount
- 1 )
1347 bSuccess
= goTo( m_nCurColumn
, m_nCurRow
+ 1 );
1352 if(m_pSelEngine
->GetSelectionMode() == SINGLE_SELECTION
)
1354 if(!m_aSelectedRows
.empty())
1356 invalidateSelectedRows();
1357 m_aSelectedRows
.clear();
1362 m_aSelectedRows
.push_back(m_nCurRow
);
1363 invalidateRow( m_nCurRow
);
1367 m_aSelectedRows
.push_back(m_nCurRow
);
1368 invalidateRow( m_nCurRow
);
1370 ensureVisible(m_nCurColumn
,m_nCurRow
,false);
1371 selectionChanged
= true;
1376 if ( m_nCurRow
> 0 )
1377 bSuccess
= goTo( m_nCurColumn
, m_nCurRow
- 1 );
1381 if ( m_nCurColumn
> 0 )
1382 bSuccess
= goTo( m_nCurColumn
- 1, m_nCurRow
);
1384 if ( ( m_nCurColumn
== 0) && ( m_nCurRow
> 0 ) )
1385 bSuccess
= goTo( m_nColumnCount
- 1, m_nCurRow
- 1 );
1389 if ( m_nCurColumn
< m_nColumnCount
- 1 )
1390 bSuccess
= goTo( m_nCurColumn
+ 1, m_nCurRow
);
1392 if ( ( m_nCurColumn
== m_nColumnCount
- 1 ) && ( m_nCurRow
< m_nRowCount
- 1 ) )
1393 bSuccess
= goTo( 0, m_nCurRow
+ 1 );
1396 case cursorToLineStart
:
1397 bSuccess
= goTo( 0, m_nCurRow
);
1400 case cursorToLineEnd
:
1401 bSuccess
= goTo( m_nColumnCount
- 1, m_nCurRow
);
1404 case cursorToFirstLine
:
1405 bSuccess
= goTo( m_nCurColumn
, 0 );
1408 case cursorToLastLine
:
1409 bSuccess
= goTo( m_nCurColumn
, m_nRowCount
- 1 );
1414 RowPos nNewRow
= ::std::max( (RowPos
)0, m_nCurRow
- impl_getVisibleRows( false ) );
1415 bSuccess
= goTo( m_nCurColumn
, nNewRow
);
1419 case cursorPageDown
:
1421 RowPos nNewRow
= ::std::min( m_nRowCount
- 1, m_nCurRow
+ impl_getVisibleRows( false ) );
1422 bSuccess
= goTo( m_nCurColumn
, nNewRow
);
1427 bSuccess
= goTo( 0, 0 );
1430 case cursorBottomRight
:
1431 bSuccess
= goTo( m_nColumnCount
- 1, m_nRowCount
- 1 );
1434 case cursorSelectRow
:
1436 if(m_pSelEngine
->GetSelectionMode() == NO_SELECTION
)
1437 return bSuccess
= false;
1438 //pos is the position of the current row in the vector of selected rows, if current row is selected
1439 int pos
= getRowSelectedNumber(m_aSelectedRows
, m_nCurRow
);
1440 //if current row is selected, it should be deselected, when ALT+SPACE are pressed
1443 m_aSelectedRows
.erase(m_aSelectedRows
.begin()+pos
);
1444 if(m_aSelectedRows
.empty() && m_nAnchor
!= -1)
1447 //else select the row->put it in the vector
1449 m_aSelectedRows
.push_back(m_nCurRow
);
1450 invalidateRow( m_nCurRow
);
1451 selectionChanged
= true;
1455 case cursorSelectRowUp
:
1457 if(m_pSelEngine
->GetSelectionMode() == NO_SELECTION
)
1458 return bSuccess
= false;
1459 else if(m_pSelEngine
->GetSelectionMode() == SINGLE_SELECTION
)
1461 //if there are other selected rows, deselect them
1466 //there are other selected rows
1467 if(!m_aSelectedRows
.empty())
1469 //the anchor wasn't set -> a region is not selected, that's why clear all selection
1470 //and select the current row
1473 invalidateSelectedRows();
1474 m_aSelectedRows
.clear();
1475 m_aSelectedRows
.push_back(m_nCurRow
);
1476 invalidateRow( m_nCurRow
);
1480 //a region is already selected, prevRow is last selected row and the row above - nextRow - should be selected
1481 int prevRow
= getRowSelectedNumber(m_aSelectedRows
, m_nCurRow
);
1482 int nextRow
= getRowSelectedNumber(m_aSelectedRows
, m_nCurRow
-1);
1485 //if m_nCurRow isn't the upper one, can move up, otherwise not
1489 return bSuccess
= true;
1490 //if nextRow already selected, deselect it, otherwise select it
1491 if(nextRow
>-1 && m_aSelectedRows
[nextRow
] == m_nCurRow
)
1493 m_aSelectedRows
.erase(m_aSelectedRows
.begin()+prevRow
);
1494 invalidateRow( m_nCurRow
+ 1 );
1498 m_aSelectedRows
.push_back(m_nCurRow
);
1499 invalidateRow( m_nCurRow
);
1506 m_aSelectedRows
.push_back(m_nCurRow
);
1508 m_aSelectedRows
.push_back(m_nCurRow
);
1509 invalidateSelectedRegion( m_nCurRow
+1, m_nCurRow
);
1516 //if nothing is selected and the current row isn't the upper one
1517 //select the current and one row above
1518 //otherwise select only the upper row
1521 m_aSelectedRows
.push_back(m_nCurRow
);
1523 m_aSelectedRows
.push_back(m_nCurRow
);
1524 invalidateSelectedRegion( m_nCurRow
+1, m_nCurRow
);
1528 m_aSelectedRows
.push_back(m_nCurRow
);
1529 invalidateRow( m_nCurRow
);
1532 m_pSelEngine
->SetAnchor(true);
1533 m_nAnchor
= m_nCurRow
;
1534 ensureVisible(m_nCurColumn
, m_nCurRow
, false);
1535 selectionChanged
= true;
1540 case cursorSelectRowDown
:
1542 if(m_pSelEngine
->GetSelectionMode() == NO_SELECTION
)
1544 else if(m_pSelEngine
->GetSelectionMode() == SINGLE_SELECTION
)
1550 if(!m_aSelectedRows
.empty())
1552 //the anchor wasn't set -> a region is not selected, that's why clear all selection
1553 //and select the current row
1556 invalidateSelectedRows();
1557 m_aSelectedRows
.clear();
1558 m_aSelectedRows
.push_back(m_nCurRow
);
1559 invalidateRow( m_nCurRow
);
1563 //a region is already selected, prevRow is last selected row and the row beneath - nextRow - should be selected
1564 int prevRow
= getRowSelectedNumber(m_aSelectedRows
, m_nCurRow
);
1565 int nextRow
= getRowSelectedNumber(m_aSelectedRows
, m_nCurRow
+1);
1568 //if m_nCurRow isn't the last one, can move down, otherwise not
1569 if(m_nCurRow
<m_nRowCount
-1)
1572 return bSuccess
= true;
1573 //if next row already selected, deselect it, otherwise select it
1574 if(nextRow
>-1 && m_aSelectedRows
[nextRow
] == m_nCurRow
)
1576 m_aSelectedRows
.erase(m_aSelectedRows
.begin()+prevRow
);
1577 invalidateRow( m_nCurRow
- 1 );
1581 m_aSelectedRows
.push_back(m_nCurRow
);
1582 invalidateRow( m_nCurRow
);
1587 if(m_nCurRow
<m_nRowCount
-1)
1589 m_aSelectedRows
.push_back(m_nCurRow
);
1591 m_aSelectedRows
.push_back(m_nCurRow
);
1592 invalidateSelectedRegion( m_nCurRow
-1, m_nCurRow
);
1599 //there wasn't any selection, select current and row beneath, otherwise only row beneath
1600 if(m_nCurRow
<m_nRowCount
-1)
1602 m_aSelectedRows
.push_back(m_nCurRow
);
1604 m_aSelectedRows
.push_back(m_nCurRow
);
1605 invalidateSelectedRegion( m_nCurRow
-1, m_nCurRow
);
1609 m_aSelectedRows
.push_back(m_nCurRow
);
1610 invalidateRow( m_nCurRow
);
1613 m_pSelEngine
->SetAnchor(true);
1614 m_nAnchor
= m_nCurRow
;
1615 ensureVisible(m_nCurColumn
, m_nCurRow
, false);
1616 selectionChanged
= true;
1622 case cursorSelectRowAreaTop
:
1624 if(m_pSelEngine
->GetSelectionMode() == NO_SELECTION
)
1626 else if(m_pSelEngine
->GetSelectionMode() == SINGLE_SELECTION
)
1630 //select the region between the current and the upper row
1631 RowPos iter
= m_nCurRow
;
1632 invalidateSelectedRegion( m_nCurRow
, 0 );
1633 //put the rows in vector
1636 if ( !isRowSelected( iter
) )
1637 m_aSelectedRows
.push_back(iter
);
1641 m_nAnchor
= m_nCurRow
;
1642 m_pSelEngine
->SetAnchor(true);
1643 ensureVisible(m_nCurColumn
, 0, false);
1644 selectionChanged
= true;
1650 case cursorSelectRowAreaBottom
:
1652 if(m_pSelEngine
->GetSelectionMode() == NO_SELECTION
)
1653 return bSuccess
= false;
1654 else if(m_pSelEngine
->GetSelectionMode() == SINGLE_SELECTION
)
1655 return bSuccess
= false;
1656 //select the region between the current and the last row
1657 RowPos iter
= m_nCurRow
;
1658 invalidateSelectedRegion( m_nCurRow
, m_nRowCount
-1 );
1659 //put the rows in the vector
1660 while(iter
<=m_nRowCount
)
1662 if ( !isRowSelected( iter
) )
1663 m_aSelectedRows
.push_back(iter
);
1666 m_nCurRow
= m_nRowCount
-1;
1667 m_nAnchor
= m_nCurRow
;
1668 m_pSelEngine
->SetAnchor(true);
1669 ensureVisible(m_nCurColumn
, m_nRowCount
-1, false);
1670 selectionChanged
= true;
1675 OSL_FAIL( "TableControl_Impl::dispatchAction: unsupported action!" );
1679 if ( bSuccess
&& selectionChanged
)
1681 m_rAntiImpl
.Select();
1688 void TableControl_Impl::impl_ni_doSwitchCursor( bool _bShow
)
1690 PTableRenderer pRenderer
= !!m_pModel
? m_pModel
->getRenderer() : PTableRenderer();
1693 Rectangle aCellRect
;
1694 impl_getCellRect( m_nCurColumn
, m_nCurRow
, aCellRect
);
1696 pRenderer
->ShowCellCursor( *m_pDataWindow
, aCellRect
);
1698 pRenderer
->HideCellCursor( *m_pDataWindow
, aCellRect
);
1703 void TableControl_Impl::impl_getCellRect( ColPos _nColumn
, RowPos _nRow
, Rectangle
& _rCellRect
) const
1706 || ( COL_INVALID
== _nColumn
)
1707 || ( ROW_INVALID
== _nRow
)
1710 _rCellRect
.SetEmpty();
1714 TableCellGeometry
aCell( *this, impl_getAllVisibleCellsArea(), _nColumn
, _nRow
);
1715 _rCellRect
= aCell
.getRect();
1719 RowPos
TableControl_Impl::getRowAtPoint( const Point
& rPoint
) const
1721 return impl_getRowForAbscissa( rPoint
.Y() );
1725 ColPos
TableControl_Impl::getColAtPoint( const Point
& rPoint
) const
1727 return impl_getColumnForOrdinate( rPoint
.X() );
1731 TableCell
TableControl_Impl::hitTest( Point
const & i_point
) const
1733 TableCell
aCell( getColAtPoint( i_point
), getRowAtPoint( i_point
) );
1734 if ( aCell
.nColumn
> COL_ROW_HEADERS
)
1736 PColumnModel
const pColumn
= m_pModel
->getColumnModel( aCell
.nColumn
);
1737 MutableColumnMetrics
const & rColInfo( m_aColumnWidths
[ aCell
.nColumn
] );
1738 if ( ( rColInfo
.getEnd() - 3 <= i_point
.X() )
1739 && ( rColInfo
.getEnd() >= i_point
.X() )
1740 && pColumn
->isResizable()
1743 aCell
.eArea
= ColumnDivider
;
1750 ColumnMetrics
TableControl_Impl::getColumnMetrics( ColPos
const i_column
) const
1752 ENSURE_OR_RETURN( ( i_column
>= 0 ) && ( i_column
< m_pModel
->getColumnCount() ),
1753 "TableControl_Impl::getColumnMetrics: illegal column index!", ColumnMetrics() );
1754 return (ColumnMetrics
const &)m_aColumnWidths
[ i_column
];
1758 PTableModel
TableControl_Impl::getModel() const
1764 RowPos
TableControl_Impl::getCurrentColumn() const
1766 return m_nCurColumn
;
1770 RowPos
TableControl_Impl::getCurrentRow() const
1776 ::Size
TableControl_Impl::getTableSizePixel() const
1778 return m_pDataWindow
->GetOutputSizePixel();
1782 void TableControl_Impl::setPointer( Pointer
const & i_pointer
)
1784 m_pDataWindow
->SetPointer( i_pointer
);
1788 void TableControl_Impl::captureMouse()
1790 m_pDataWindow
->CaptureMouse();
1794 void TableControl_Impl::releaseMouse()
1796 m_pDataWindow
->ReleaseMouse();
1800 void TableControl_Impl::invalidate( TableArea
const i_what
)
1804 case TableAreaColumnHeaders
:
1805 m_pDataWindow
->Invalidate( calcHeaderRect( true ) );
1808 case TableAreaRowHeaders
:
1809 m_pDataWindow
->Invalidate( calcHeaderRect( false ) );
1812 case TableAreaDataArea
:
1813 m_pDataWindow
->Invalidate( impl_getAllVisibleDataCellArea() );
1817 m_pDataWindow
->Invalidate();
1818 m_pDataWindow
->GetParent()->Invalidate( INVALIDATE_TRANSPARENT
);
1824 long TableControl_Impl::pixelWidthToAppFont( long const i_pixels
) const
1826 return m_pDataWindow
->PixelToLogic( Size( i_pixels
, 0 ), MAP_APPFONT
).Width();
1830 long TableControl_Impl::appFontWidthToPixel( long const i_appFontUnits
) const
1832 return m_pDataWindow
->LogicToPixel( Size( i_appFontUnits
, 0 ), MAP_APPFONT
).Width();
1836 void TableControl_Impl::hideTracking()
1838 m_pDataWindow
->HideTracking();
1842 void TableControl_Impl::showTracking( Rectangle
const & i_location
, sal_uInt16
const i_flags
)
1844 m_pDataWindow
->ShowTracking( i_location
, i_flags
);
1848 bool TableControl_Impl::activateCell( ColPos
const i_col
, RowPos
const i_row
)
1850 return goTo( i_col
, i_row
);
1854 void TableControl_Impl::invalidateSelectedRegion( RowPos _nPrevRow
, RowPos _nCurRow
)
1856 // get the visible area of the table control and set the Left and right border of the region to be repainted
1857 Rectangle
const aAllCells( impl_getAllVisibleCellsArea() );
1859 Rectangle aInvalidateRect
;
1860 aInvalidateRect
.Left() = aAllCells
.Left();
1861 aInvalidateRect
.Right() = aAllCells
.Right();
1862 // if only one row is selected
1863 if ( _nPrevRow
== _nCurRow
)
1865 Rectangle aCellRect
;
1866 impl_getCellRect( m_nCurColumn
, _nCurRow
, aCellRect
);
1867 aInvalidateRect
.Top() = aCellRect
.Top();
1868 aInvalidateRect
.Bottom() = aCellRect
.Bottom();
1870 //if the region is above the current row
1871 else if(_nPrevRow
< _nCurRow
)
1873 Rectangle aCellRect
;
1874 impl_getCellRect( m_nCurColumn
, _nPrevRow
, aCellRect
);
1875 aInvalidateRect
.Top() = aCellRect
.Top();
1876 impl_getCellRect( m_nCurColumn
, _nCurRow
, aCellRect
);
1877 aInvalidateRect
.Bottom() = aCellRect
.Bottom();
1879 //if the region is beneath the current row
1882 Rectangle aCellRect
;
1883 impl_getCellRect( m_nCurColumn
, _nCurRow
, aCellRect
);
1884 aInvalidateRect
.Top() = aCellRect
.Top();
1885 impl_getCellRect( m_nCurColumn
, _nPrevRow
, aCellRect
);
1886 aInvalidateRect
.Bottom() = aCellRect
.Bottom();
1889 invalidateRect(aInvalidateRect
);
1892 void TableControl_Impl::invalidateRect(const Rectangle
&rInvalidateRect
)
1894 m_pDataWindow
->Invalidate( rInvalidateRect
,
1895 m_pDataWindow
->GetControlBackground().GetTransparency() ? INVALIDATE_TRANSPARENT
: 0 );
1899 void TableControl_Impl::invalidateSelectedRows()
1901 for ( ::std::vector
< RowPos
>::iterator selRow
= m_aSelectedRows
.begin();
1902 selRow
!= m_aSelectedRows
.end();
1906 invalidateRow( *selRow
);
1911 void TableControl_Impl::invalidateRowRange( RowPos
const i_firstRow
, RowPos
const i_lastRow
)
1913 RowPos
const firstRow
= i_firstRow
< m_nTopRow
? m_nTopRow
: i_firstRow
;
1914 RowPos
const lastVisibleRow
= m_nTopRow
+ impl_getVisibleRows( true ) - 1;
1915 RowPos
const lastRow
= ( ( i_lastRow
== ROW_INVALID
) || ( i_lastRow
> lastVisibleRow
) ) ? lastVisibleRow
: i_lastRow
;
1917 Rectangle aInvalidateRect
;
1919 Rectangle
const aVisibleCellsArea( impl_getAllVisibleCellsArea() );
1920 TableRowGeometry
aRow( *this, aVisibleCellsArea
, firstRow
, true );
1921 while ( aRow
.isValid() && ( aRow
.getRow() <= lastRow
) )
1923 aInvalidateRect
.Union( aRow
.getRect() );
1927 if ( i_lastRow
== ROW_INVALID
)
1928 aInvalidateRect
.Bottom() = m_pDataWindow
->GetOutputSizePixel().Height();
1930 invalidateRect(aInvalidateRect
);
1934 void TableControl_Impl::checkCursorPosition()
1937 TableSize nVisibleRows
= impl_getVisibleRows(true);
1938 TableSize nVisibleCols
= impl_getVisibleColumns(true);
1939 if ( ( m_nTopRow
+ nVisibleRows
> m_nRowCount
)
1940 && ( m_nRowCount
>= nVisibleRows
)
1950 if ( ( m_nLeftColumn
+ nVisibleCols
> m_nColumnCount
)
1951 && ( m_nColumnCount
>= nVisibleCols
)
1961 m_pDataWindow
->Invalidate();
1965 TableSize
TableControl_Impl::impl_getVisibleRows( bool _bAcceptPartialRow
) const
1967 DBG_ASSERT( m_pDataWindow
, "TableControl_Impl::impl_getVisibleRows: no data window!" );
1969 return lcl_getRowsFittingInto(
1970 m_pDataWindow
->GetOutputSizePixel().Height() - m_nColHeaderHeightPixel
,
1977 TableSize
TableControl_Impl::impl_getVisibleColumns( bool _bAcceptPartialCol
) const
1979 DBG_ASSERT( m_pDataWindow
, "TableControl_Impl::impl_getVisibleColumns: no data window!" );
1981 return lcl_getColumnsVisibleWithin(
1982 Rectangle( Point( 0, 0 ), m_pDataWindow
->GetOutputSizePixel() ),
1990 bool TableControl_Impl::goTo( ColPos _nColumn
, RowPos _nRow
)
1992 // TODO: give veto listeners a chance
1994 if ( ( _nColumn
< 0 ) || ( _nColumn
>= m_nColumnCount
)
1995 || ( _nRow
< 0 ) || ( _nRow
>= m_nRowCount
)
1998 OSL_ENSURE( false, "TableControl_Impl::goTo: invalid row or column index!" );
2002 SuppressCursor
aHideCursor( *this );
2003 m_nCurColumn
= _nColumn
;
2006 // ensure that the new cell is visible
2007 ensureVisible( m_nCurColumn
, m_nCurRow
, false );
2012 void TableControl_Impl::ensureVisible( ColPos _nColumn
, RowPos _nRow
, bool _bAcceptPartialVisibility
)
2014 DBG_ASSERT( ( _nColumn
>= 0 ) && ( _nColumn
< m_nColumnCount
)
2015 && ( _nRow
>= 0 ) && ( _nRow
< m_nRowCount
),
2016 "TableControl_Impl::ensureVisible: invalid coordinates!" );
2018 SuppressCursor
aHideCursor( *this );
2020 if ( _nColumn
< m_nLeftColumn
)
2021 impl_scrollColumns( _nColumn
- m_nLeftColumn
);
2024 TableSize nVisibleColumns
= impl_getVisibleColumns( _bAcceptPartialVisibility
);
2025 if ( _nColumn
> m_nLeftColumn
+ nVisibleColumns
- 1 )
2027 impl_scrollColumns( _nColumn
- ( m_nLeftColumn
+ nVisibleColumns
- 1 ) );
2028 // TODO: since not all columns have the same width, this might in theory result
2029 // in the column still not being visible.
2033 if ( _nRow
< m_nTopRow
)
2034 impl_scrollRows( _nRow
- m_nTopRow
);
2037 TableSize nVisibleRows
= impl_getVisibleRows( _bAcceptPartialVisibility
);
2038 if ( _nRow
> m_nTopRow
+ nVisibleRows
- 1 )
2039 impl_scrollRows( _nRow
- ( m_nTopRow
+ nVisibleRows
- 1 ) );
2044 OUString
TableControl_Impl::getCellContentAsString( RowPos
const i_row
, ColPos
const i_col
)
2047 m_pModel
->getCellContent( i_col
, i_row
, aCellValue
);
2049 OUString sCellStringContent
;
2050 m_pModel
->getRenderer()->GetFormattedCellString( aCellValue
, i_col
, i_row
, sCellStringContent
);
2052 return sCellStringContent
;
2056 TableSize
TableControl_Impl::impl_ni_ScrollRows( TableSize _nRowDelta
)
2058 // compute new top row
2061 ::std::min( (RowPos
)( m_nTopRow
+ _nRowDelta
), (RowPos
)( m_nRowCount
- 1 ) ),
2065 RowPos nOldTopRow
= m_nTopRow
;
2066 m_nTopRow
= nNewTopRow
;
2068 // if updates are enabled currently, scroll the viewport
2069 if ( m_nTopRow
!= nOldTopRow
)
2071 SuppressCursor
aHideCursor( *this );
2072 // TODO: call a onStartScroll at our listener (or better an own onStartScroll,
2073 // which hides the cursor and then calls the listener)
2074 // Same for onEndScroll
2076 // scroll the view port, if possible
2077 long nPixelDelta
= m_nRowHeightPixel
* ( m_nTopRow
- nOldTopRow
);
2079 Rectangle
aDataArea( Point( 0, m_nColHeaderHeightPixel
), m_pDataWindow
->GetOutputSizePixel() );
2081 if ( m_pDataWindow
->GetBackground().IsScrollable()
2082 && std::abs( nPixelDelta
) < aDataArea
.GetHeight()
2085 m_pDataWindow
->Scroll( 0, (long)-nPixelDelta
, aDataArea
, SCROLL_CLIP
| SCROLL_UPDATE
| SCROLL_CHILDREN
);
2089 m_pDataWindow
->Invalidate( INVALIDATE_UPDATE
);
2090 m_pDataWindow
->GetParent()->Invalidate( INVALIDATE_TRANSPARENT
);
2093 // update the position at the vertical scrollbar
2094 if ( m_pVScroll
!= nullptr )
2095 m_pVScroll
->SetThumbPos( m_nTopRow
);
2098 // The scroll bar availaility might change when we scrolled.
2099 // For instance, imagine a view with 10 rows, if which 5 fit into the window, numbered 1 to 10.
2101 // - the user scroll to row number 6, so the last 5 rows are visible
2102 // - somebody remove the last 4 rows
2103 // - the user scroll to row number 5 being the top row, so the last two rows are visible
2104 // - somebody remove row number 6
2105 // - the user scroll to row number 1
2106 // => in this case, the need for the scrollbar vanishes immediately.
2107 if ( m_nTopRow
== 0 )
2108 m_rAntiImpl
.PostUserEvent( LINK( this, TableControl_Impl
, OnUpdateScrollbars
) );
2110 return (TableSize
)( m_nTopRow
- nOldTopRow
);
2114 TableSize
TableControl_Impl::impl_scrollRows( TableSize
const i_rowDelta
)
2116 return impl_ni_ScrollRows( i_rowDelta
);
2120 TableSize
TableControl_Impl::impl_ni_ScrollColumns( TableSize _nColumnDelta
)
2122 // compute new left column
2123 const ColPos nNewLeftColumn
=
2125 ::std::min( (ColPos
)( m_nLeftColumn
+ _nColumnDelta
), (ColPos
)( m_nColumnCount
- 1 ) ),
2129 const ColPos nOldLeftColumn
= m_nLeftColumn
;
2130 m_nLeftColumn
= nNewLeftColumn
;
2132 // if updates are enabled currently, scroll the viewport
2133 if ( m_nLeftColumn
!= nOldLeftColumn
)
2135 SuppressCursor
aHideCursor( *this );
2136 // TODO: call a onStartScroll at our listener (or better an own onStartScroll,
2137 // which hides the cursor and then calls the listener)
2138 // Same for onEndScroll
2140 // scroll the view port, if possible
2141 const Rectangle
aDataArea( Point( m_nRowHeaderWidthPixel
, 0 ), m_pDataWindow
->GetOutputSizePixel() );
2144 m_aColumnWidths
[ nOldLeftColumn
].getStart()
2145 - m_aColumnWidths
[ m_nLeftColumn
].getStart();
2147 // update our column positions
2148 // Do this *before* scrolling, as SCROLL_UPDATE will trigger a paint, which already needs the correct
2149 // information in m_aColumnWidths
2150 for ( ColumnPositions::iterator colPos
= m_aColumnWidths
.begin();
2151 colPos
!= m_aColumnWidths
.end();
2155 colPos
->move( nPixelDelta
);
2158 // scroll the window content (if supported and possible), or invalidate the complete window
2159 if ( m_pDataWindow
->GetBackground().IsScrollable()
2160 && std::abs( nPixelDelta
) < aDataArea
.GetWidth()
2163 m_pDataWindow
->Scroll( nPixelDelta
, 0, aDataArea
, SCROLL_CLIP
| SCROLL_UPDATE
);
2167 m_pDataWindow
->Invalidate( INVALIDATE_UPDATE
);
2168 m_pDataWindow
->GetParent()->Invalidate( INVALIDATE_TRANSPARENT
);
2171 // update the position at the horizontal scrollbar
2172 if ( m_pHScroll
!= nullptr )
2173 m_pHScroll
->SetThumbPos( m_nLeftColumn
);
2176 // The scroll bar availaility might change when we scrolled. This is because we do not hide
2177 // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will
2178 // be auto-hidden when it's scrolled back to pos 0.
2179 if ( m_nLeftColumn
== 0 )
2180 m_rAntiImpl
.PostUserEvent( LINK( this, TableControl_Impl
, OnUpdateScrollbars
) );
2182 return (TableSize
)( m_nLeftColumn
- nOldLeftColumn
);
2186 TableSize
TableControl_Impl::impl_scrollColumns( TableSize
const i_columnDelta
)
2188 return impl_ni_ScrollColumns( i_columnDelta
);
2192 SelectionEngine
* TableControl_Impl::getSelEngine()
2194 return m_pSelEngine
;
2197 bool TableControl_Impl::isRowSelected( RowPos i_row
) const
2199 return ::std::find( m_aSelectedRows
.begin(), m_aSelectedRows
.end(), i_row
) != m_aSelectedRows
.end();
2203 RowPos
TableControl_Impl::getSelectedRowIndex( size_t const i_selectionIndex
) const
2205 if ( i_selectionIndex
< m_aSelectedRows
.size() )
2206 return m_aSelectedRows
[ i_selectionIndex
];
2211 int TableControl_Impl::getRowSelectedNumber(const ::std::vector
<RowPos
>& selectedRows
, RowPos current
)
2213 std::vector
<RowPos
>::const_iterator it
= ::std::find(selectedRows
.begin(),selectedRows
.end(),current
);
2214 if ( it
!= selectedRows
.end() )
2216 return it
- selectedRows
.begin();
2222 ColPos
TableControl_Impl::impl_getColumnForOrdinate( long const i_ordinate
) const
2224 if ( ( m_aColumnWidths
.empty() ) || ( i_ordinate
< 0 ) )
2227 if ( i_ordinate
< m_nRowHeaderWidthPixel
)
2228 return COL_ROW_HEADERS
;
2230 ColumnPositions::const_iterator lowerBound
= ::std::lower_bound(
2231 m_aColumnWidths
.begin(),
2232 m_aColumnWidths
.end(),
2233 MutableColumnMetrics(i_ordinate
+1, i_ordinate
+1),
2234 ColumnInfoPositionLess()
2236 if ( lowerBound
== m_aColumnWidths
.end() )
2238 // point is *behind* the start of the last column ...
2239 if ( i_ordinate
< m_aColumnWidths
.rbegin()->getEnd() )
2240 // ... but still before its end
2241 return m_nColumnCount
- 1;
2244 return lowerBound
- m_aColumnWidths
.begin();
2248 RowPos
TableControl_Impl::impl_getRowForAbscissa( long const i_abscissa
) const
2250 if ( i_abscissa
< 0 )
2253 if ( i_abscissa
< m_nColHeaderHeightPixel
)
2254 return ROW_COL_HEADERS
;
2256 long const abscissa
= i_abscissa
- m_nColHeaderHeightPixel
;
2257 long const row
= m_nTopRow
+ abscissa
/ m_nRowHeightPixel
;
2258 return row
< m_pModel
->getRowCount() ? row
: ROW_INVALID
;
2262 bool TableControl_Impl::markRowAsDeselected( RowPos
const i_rowIndex
)
2264 ::std::vector
< RowPos
>::iterator selPos
= ::std::find( m_aSelectedRows
.begin(), m_aSelectedRows
.end(), i_rowIndex
);
2265 if ( selPos
== m_aSelectedRows
.end() )
2268 m_aSelectedRows
.erase( selPos
);
2273 bool TableControl_Impl::markRowAsSelected( RowPos
const i_rowIndex
)
2275 if ( isRowSelected( i_rowIndex
) )
2278 SelectionMode
const eSelMode
= getSelEngine()->GetSelectionMode();
2281 case SINGLE_SELECTION
:
2282 if ( !m_aSelectedRows
.empty() )
2284 OSL_ENSURE( m_aSelectedRows
.size() == 1, "TableControl::markRowAsSelected: SingleSelection with more than one selected element?" );
2285 m_aSelectedRows
[0] = i_rowIndex
;
2290 case MULTIPLE_SELECTION
:
2291 m_aSelectedRows
.push_back( i_rowIndex
);
2295 OSL_ENSURE( false, "TableControl_Impl::markRowAsSelected: unsupported selection mode!" );
2303 bool TableControl_Impl::markAllRowsAsDeselected()
2305 if ( m_aSelectedRows
.empty() )
2308 m_aSelectedRows
.clear();
2313 bool TableControl_Impl::markAllRowsAsSelected()
2315 SelectionMode
const eSelMode
= getSelEngine()->GetSelectionMode();
2316 ENSURE_OR_RETURN_FALSE( eSelMode
== MULTIPLE_SELECTION
, "TableControl_Impl::markAllRowsAsSelected: unsupported selection mode!" );
2318 if ( m_aSelectedRows
.size() == size_t( m_pModel
->getRowCount() ) )
2320 #if OSL_DEBUG_LEVEL > 0
2321 for ( TableSize row
= 0; row
< m_pModel
->getRowCount(); ++row
)
2323 OSL_ENSURE( isRowSelected( row
), "TableControl_Impl::markAllRowsAsSelected: inconsistency in the selected rows!" );
2326 // already all rows marked as selected
2330 m_aSelectedRows
.clear();
2331 for ( RowPos i
=0; i
< m_pModel
->getRowCount(); ++i
)
2332 m_aSelectedRows
.push_back(i
);
2338 void TableControl_Impl::commitAccessibleEvent( sal_Int16
const i_eventID
, const Any
& i_newValue
, const Any
& i_oldValue
)
2340 impl_commitAccessibleEvent( i_eventID
, i_newValue
, i_oldValue
);
2344 void TableControl_Impl::commitCellEvent( sal_Int16
const i_eventID
, const Any
& i_newValue
, const Any
& i_oldValue
)
2346 if ( impl_isAccessibleAlive() )
2347 m_pAccessibleTable
->commitCellEvent( i_eventID
, i_newValue
, i_oldValue
);
2351 void TableControl_Impl::commitTableEvent( sal_Int16
const i_eventID
, const Any
& i_newValue
, const Any
& i_oldValue
)
2353 if ( impl_isAccessibleAlive() )
2354 m_pAccessibleTable
->commitTableEvent( i_eventID
, i_newValue
, i_oldValue
);
2358 Rectangle
TableControl_Impl::calcHeaderRect(bool bColHeader
)
2360 Rectangle
const aRectTableWithHeaders( impl_getAllVisibleCellsArea() );
2361 Size
const aSizeTableWithHeaders( aRectTableWithHeaders
.GetSize() );
2363 return Rectangle( aRectTableWithHeaders
.TopLeft(), Size( aSizeTableWithHeaders
.Width(), m_nColHeaderHeightPixel
) );
2365 return Rectangle( aRectTableWithHeaders
.TopLeft(), Size( m_nRowHeaderWidthPixel
, aSizeTableWithHeaders
.Height() ) );
2369 Rectangle
TableControl_Impl::calcHeaderCellRect( bool bColHeader
, sal_Int32 nPos
)
2371 Rectangle
const aHeaderRect
= calcHeaderRect( bColHeader
);
2372 TableCellGeometry
const aGeometry(
2374 bColHeader
? nPos
: COL_ROW_HEADERS
,
2375 bColHeader
? ROW_COL_HEADERS
: nPos
2377 return aGeometry
.getRect();
2381 Rectangle
TableControl_Impl::calcTableRect()
2383 return impl_getAllVisibleDataCellArea();
2387 Rectangle
TableControl_Impl::calcCellRect( sal_Int32 nRow
, sal_Int32 nCol
)
2389 Rectangle aCellRect
;
2390 impl_getCellRect( nRow
, nCol
, aCellRect
);
2395 IMPL_LINK_NOARG( TableControl_Impl
, OnUpdateScrollbars
)
2397 // TODO: can't we simply use lcl_updateScrollbar here, so the scrollbars ranges are updated, instead of
2398 // doing a complete re-layout?
2404 IMPL_LINK( TableControl_Impl
, OnScroll
, ScrollBar
*, _pScrollbar
)
2406 DBG_ASSERT( ( _pScrollbar
== m_pVScroll
) || ( _pScrollbar
== m_pHScroll
),
2407 "TableControl_Impl::OnScroll: where did this come from?" );
2409 if ( _pScrollbar
== m_pVScroll
)
2410 impl_ni_ScrollRows( _pScrollbar
->GetDelta() );
2412 impl_ni_ScrollColumns( _pScrollbar
->GetDelta() );
2418 Reference
< XAccessible
> TableControl_Impl::getAccessible( vcl::Window
& i_parentWindow
)
2420 DBG_TESTSOLARMUTEX();
2421 if ( m_pAccessibleTable
== NULL
)
2423 Reference
< XAccessible
> const xAccParent
= i_parentWindow
.GetAccessible();
2424 if ( xAccParent
.is() )
2426 m_pAccessibleTable
= m_aFactoryAccess
.getFactory().createAccessibleTableControl(
2427 xAccParent
, m_rAntiImpl
2432 Reference
< XAccessible
> xAccessible
;
2433 if ( m_pAccessibleTable
)
2434 xAccessible
= m_pAccessibleTable
->getMyself();
2439 void TableControl_Impl::disposeAccessible()
2441 if ( m_pAccessibleTable
)
2442 m_pAccessibleTable
->DisposeAccessImpl();
2443 m_pAccessibleTable
= NULL
;
2447 bool TableControl_Impl::impl_isAccessibleAlive() const
2449 return ( NULL
!= m_pAccessibleTable
) && m_pAccessibleTable
->isAlive();
2453 void TableControl_Impl::impl_commitAccessibleEvent( sal_Int16
const i_eventID
, Any
const & i_newValue
, Any
const & i_oldValue
)
2455 if ( impl_isAccessibleAlive() )
2456 m_pAccessibleTable
->commitEvent( i_eventID
, i_newValue
, i_oldValue
);
2460 //= TableFunctionSet
2463 TableFunctionSet::TableFunctionSet(TableControl_Impl
* _pTableControl
)
2464 :m_pTableControl( _pTableControl
)
2465 ,m_nCurrentRow( ROW_INVALID
)
2469 TableFunctionSet::~TableFunctionSet()
2473 void TableFunctionSet::BeginDrag()
2477 void TableFunctionSet::CreateAnchor()
2479 m_pTableControl
->setAnchor( m_pTableControl
->getCurRow() );
2483 void TableFunctionSet::DestroyAnchor()
2485 m_pTableControl
->setAnchor( ROW_INVALID
);
2489 bool TableFunctionSet::SetCursorAtPoint(const Point
& rPoint
, bool bDontSelectAtCursor
)
2491 bool bHandled
= false;
2492 // newRow is the row which includes the point, getCurRow() is the last selected row, before the mouse click
2493 RowPos newRow
= m_pTableControl
->getRowAtPoint( rPoint
);
2494 if ( newRow
== ROW_COL_HEADERS
)
2495 newRow
= m_pTableControl
->getTopRow();
2497 ColPos newCol
= m_pTableControl
->getColAtPoint( rPoint
);
2498 if ( newCol
== COL_ROW_HEADERS
)
2499 newCol
= m_pTableControl
->getLeftColumn();
2501 if ( ( newRow
== ROW_INVALID
) || ( newCol
== COL_INVALID
) )
2504 if ( bDontSelectAtCursor
)
2506 if ( m_pTableControl
->getSelectedRowCount() > 1 )
2507 m_pTableControl
->getSelEngine()->AddAlways(true);
2510 else if ( m_pTableControl
->getAnchor() == m_pTableControl
->getCurRow() )
2513 int diff
= m_pTableControl
->getCurRow() - newRow
;
2514 //selected region lies above the last selection
2517 //put selected rows in vector
2518 while ( m_pTableControl
->getAnchor() >= newRow
)
2520 m_pTableControl
->markRowAsSelected( m_pTableControl
->getAnchor() );
2521 m_pTableControl
->setAnchor( m_pTableControl
->getAnchor() - 1 );
2524 m_pTableControl
->setAnchor( m_pTableControl
->getAnchor() + 1 );
2526 //selected region lies beneath the last selected row
2529 while ( m_pTableControl
->getAnchor() <= newRow
)
2531 m_pTableControl
->markRowAsSelected( m_pTableControl
->getAnchor() );
2532 m_pTableControl
->setAnchor( m_pTableControl
->getAnchor() + 1 );
2535 m_pTableControl
->setAnchor( m_pTableControl
->getAnchor() - 1 );
2537 m_pTableControl
->invalidateSelectedRegion( m_pTableControl
->getCurRow(), newRow
);
2540 //no region selected
2543 if ( !m_pTableControl
->hasRowSelection() )
2544 m_pTableControl
->markRowAsSelected( newRow
);
2547 if ( m_pTableControl
->getSelEngine()->GetSelectionMode() == SINGLE_SELECTION
)
2550 m_pTableControl
->markRowAsSelected( newRow
);
2554 m_pTableControl
->markRowAsSelected( newRow
);
2557 if ( m_pTableControl
->getSelectedRowCount() > 1 && m_pTableControl
->getSelEngine()->GetSelectionMode() != SINGLE_SELECTION
)
2558 m_pTableControl
->getSelEngine()->AddAlways(true);
2560 m_pTableControl
->invalidateRow( newRow
);
2563 m_pTableControl
->goTo( newCol
, newRow
);
2567 bool TableFunctionSet::IsSelectionAtPoint( const Point
& rPoint
)
2569 m_pTableControl
->getSelEngine()->AddAlways(false);
2570 if ( !m_pTableControl
->hasRowSelection() )
2574 RowPos curRow
= m_pTableControl
->getRowAtPoint( rPoint
);
2575 m_pTableControl
->setAnchor( ROW_INVALID
);
2576 bool selected
= m_pTableControl
->isRowSelected( curRow
);
2577 m_nCurrentRow
= curRow
;
2582 void TableFunctionSet::DeselectAtPoint( const Point
& rPoint
)
2585 m_pTableControl
->invalidateRow( m_nCurrentRow
);
2586 m_pTableControl
->markRowAsDeselected( m_nCurrentRow
);
2590 void TableFunctionSet::DeselectAll()
2592 if ( m_pTableControl
->hasRowSelection() )
2594 for ( size_t i
=0; i
<m_pTableControl
->getSelectedRowCount(); ++i
)
2596 RowPos
const rowIndex
= m_pTableControl
->getSelectedRowIndex(i
);
2597 m_pTableControl
->invalidateRow( rowIndex
);
2600 m_pTableControl
->markAllRowsAsDeselected();
2605 } } // namespace svt::table
2608 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */