summaryrefslogtreecommitdiff
path: root/svtools
diff options
context:
space:
mode:
authorFrank Schoenheit [fs] <frank.schoenheit@oracle.com>2011-03-28 10:56:24 +0200
committerMiklos Vajna <vmiklos@suse.cz>2012-09-14 09:49:28 +0200
commita1f695dea516dfb7199390f750ca8744bb7df912 (patch)
tree30c8f1cdab5d10db6b5f62968fd68f1efb97ee2d /svtools
parent1df082e4dc16a397d894e5b7f1a846bac63aa4e3 (diff)
UNFINISHED: column resizing refactoring
Change-Id: Iac8db5ee848f35f19f8fe4eb431485559600be49
Diffstat (limited to 'svtools')
-rw-r--r--svtools/source/table/tablecontrol_impl.cxx587
-rw-r--r--svtools/source/table/tablecontrol_impl.hxx41
2 files changed, 373 insertions, 255 deletions
diff --git a/svtools/source/table/tablecontrol_impl.cxx b/svtools/source/table/tablecontrol_impl.cxx
index 521bb2d79e9f..df6bbbc290c6 100644
--- a/svtools/source/table/tablecontrol_impl.cxx
+++ b/svtools/source/table/tablecontrol_impl.cxx
@@ -47,6 +47,7 @@
#include <tools/diagnose_ex.h>
#include <functional>
+#include <numeric>
#define MIN_COLUMN_WIDTH_PIXEL 4
@@ -499,7 +500,7 @@ namespace svt { namespace table
// recalc some model-dependent cached info
impl_ni_updateCachedModelValues();
- impl_ni_updateScrollbars();
+ impl_ni_relayout();
// completely invalidate
m_rAntiImpl.Invalidate();
@@ -547,8 +548,8 @@ namespace svt { namespace table
if ( i_first <= m_nCurRow )
goTo( m_nCurColumn, m_nCurRow + insertedRows );
- // adjust scrollbars
- impl_ni_updateScrollbars();
+ // relayout, since the scrollbar need might have changed
+ impl_ni_relayout();
// notify A1YY events
if ( impl_isAccessibleAlive() )
@@ -608,8 +609,8 @@ namespace svt { namespace table
m_nCurRow = ROW_INVALID;
}
- // adjust scrollbars
- impl_ni_updateScrollbars();
+ // relayout, since the scrollbar need might have changed
+ impl_ni_relayout();
// notify A11Y events
if ( impl_isAccessibleAlive() )
@@ -639,8 +640,7 @@ namespace svt { namespace table
void TableControl_Impl::columnInserted( ColPos const i_colIndex )
{
m_nColumnCount = m_pModel->getColumnCount();
- impl_ni_updateColumnWidths();
- impl_ni_updateScrollbars();
+ impl_ni_relayout();
m_rAntiImpl.Invalidate();
@@ -651,8 +651,7 @@ namespace svt { namespace table
void TableControl_Impl::columnRemoved( ColPos const i_colIndex )
{
m_nColumnCount = m_pModel->getColumnCount();
- impl_ni_updateColumnWidths();
- impl_ni_updateScrollbars();
+ impl_ni_relayout();
m_rAntiImpl.Invalidate();
@@ -663,8 +662,7 @@ namespace svt { namespace table
void TableControl_Impl::allColumnsRemoved()
{
m_nColumnCount = m_pModel->getColumnCount();
- impl_ni_updateColumnWidths();
- impl_ni_updateScrollbars();
+ impl_ni_relayout();
m_rAntiImpl.Invalidate();
}
@@ -681,11 +679,8 @@ namespace svt { namespace table
//------------------------------------------------------------------------------------------------------------------
void TableControl_Impl::tableMetricsChanged()
{
- long const oldRowHeaderWidthPixel = m_nRowHeaderWidthPixel;
impl_ni_updateCachedTableMetrics();
- if ( oldRowHeaderWidthPixel != m_nRowHeaderWidthPixel )
- impl_ni_updateColumnWidths();
- impl_ni_updateScrollbars();
+ impl_ni_relayout();
m_rAntiImpl.Invalidate();
}
@@ -715,9 +710,8 @@ namespace svt { namespace table
{
if ( !m_bUpdatingColWidths )
{
- impl_ni_updateColumnWidths( i_column );
+ impl_ni_relayout( i_column );
invalidate( TableAreaAll );
- impl_ni_updateScrollbars();
}
nGroup &= ~COL_ATTRS_WIDTH;
@@ -793,67 +787,157 @@ namespace svt { namespace table
//------------------------------------------------------------------------------------------------------------------
void TableControl_Impl::impl_ni_updateCachedModelValues()
{
- m_pInputHandler.reset();
- m_nColumnCount = m_nRowCount = 0;
-
- impl_ni_updateCachedTableMetrics();
- impl_ni_updateColumnWidths();
-
m_pInputHandler = m_pModel->getInputHandler();
if ( !m_pInputHandler )
m_pInputHandler.reset( new DefaultInputHandler );
m_nColumnCount = m_pModel->getColumnCount();
+ if ( m_nLeftColumn >= m_nColumnCount )
+ m_nLeftColumn = ( m_nColumnCount > 0 ) ? m_nColumnCount - 1 : 0;
+
m_nRowCount = m_pModel->getRowCount();
+ if ( m_nTopRow >= m_nRowCount )
+ m_nTopRow = ( m_nRowCount > 0 ) ? m_nRowCount - 1 : 0;
+
+ impl_ni_updateCachedTableMetrics();
}
//------------------------------------------------------------------------------------------------------------------
- void TableControl_Impl::impl_ni_updateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding )
+ namespace
{
- ENSURE_OR_RETURN_VOID( !m_bUpdatingColWidths, "TableControl_Impl::impl_ni_updateColumnWidths: recursive call detected!" );
+ //..............................................................................................................
+ /// determines whether a scrollbar is needed for the given values
+ bool lcl_determineScrollbarNeed( long const i_position, ScrollbarVisibility const i_visibility,
+ long const i_availableSpace, long const i_neededSpace )
+ {
+ if ( i_visibility == ScrollbarShowNever )
+ return false;
+ if ( i_visibility == ScrollbarShowAlways )
+ return true;
+ if ( i_position > 0 )
+ return true;
+ if ( i_availableSpace >= i_neededSpace )
+ return false;
+ return true;
+ }
- m_aColumnWidths.resize( 0 );
- if ( !m_pModel )
- return;
+ //..............................................................................................................
+ void lcl_setButtonRepeat( Window& _rWindow, sal_uLong _nDelay )
+ {
+ AllSettings aSettings = _rWindow.GetSettings();
+ MouseSettings aMouseSettings = aSettings.GetMouseSettings();
- const TableSize colCount = m_pModel->getColumnCount();
- if ( colCount == 0 )
- return;
+ aMouseSettings.SetButtonRepeat( _nDelay );
+ aSettings.SetMouseSettings( aMouseSettings );
- m_bUpdatingColWidths = true;
- const ::comphelper::FlagGuard aWidthUpdateFlag( m_bUpdatingColWidths );
+ _rWindow.SetSettings( aSettings, sal_True );
+ }
- m_aColumnWidths.reserve( colCount );
+ //..............................................................................................................
+ bool lcl_updateScrollbar( Window& _rParent, ScrollBar*& _rpBar,
+ bool const i_needBar, long _nVisibleUnits,
+ long _nPosition, long _nLineSize, long _nRange,
+ bool _bHorizontal, const Link& _rScrollHandler )
+ {
+ // do we currently have the scrollbar?
+ bool bHaveBar = _rpBar != NULL;
+
+ // do we need to correct the scrollbar visibility?
+ if ( bHaveBar && !i_needBar )
+ {
+ if ( _rpBar->IsTracking() )
+ _rpBar->EndTracking();
+ DELETEZ( _rpBar );
+ }
+ else if ( !bHaveBar && i_needBar )
+ {
+ _rpBar = new ScrollBar(
+ &_rParent,
+ WB_DRAG | ( _bHorizontal ? WB_HSCROLL : WB_VSCROLL )
+ );
+ _rpBar->SetScrollHdl( _rScrollHandler );
+ // get some speed into the scrolling ....
+ lcl_setButtonRepeat( *_rpBar, 0 );
+ }
+
+ if ( _rpBar )
+ {
+ _rpBar->SetRange( Range( 0, _nRange ) );
+ _rpBar->SetVisibleSize( _nVisibleUnits );
+ _rpBar->SetPageSize( _nVisibleUnits );
+ _rpBar->SetLineSize( _nLineSize );
+ _rpBar->SetThumbPos( _nPosition );
+ _rpBar->Show();
+ }
+
+ return ( bHaveBar != i_needBar );
+ }
+
+ //..............................................................................................................
+ /** returns the number of rows fitting into the given range,
+ for the given row height. Partially fitting rows are counted, too, if the
+ respective parameter says so.
+ */
+ TableSize lcl_getRowsFittingInto( long _nOverallHeight, long _nRowHeightPixel, bool _bAcceptPartialRow = false )
+ {
+ return _bAcceptPartialRow
+ ? ( _nOverallHeight + ( _nRowHeightPixel - 1 ) ) / _nRowHeightPixel
+ : _nOverallHeight / _nRowHeightPixel;
+ }
+ //..............................................................................................................
+ /** returns the number of columns fitting into the given area,
+ with the first visible column as given. Partially fitting columns are counted, too,
+ if the respective parameter says so.
+ */
+ TableSize lcl_getColumnsVisibleWithin( const Rectangle& _rArea, ColPos _nFirstVisibleColumn,
+ const TableControl_Impl& _rControl, bool _bAcceptPartialRow )
+ {
+ TableSize visibleColumns = 0;
+ TableColumnGeometry aColumn( _rControl, _rArea, _nFirstVisibleColumn );
+ while ( aColumn.isValid() )
+ {
+ if ( !_bAcceptPartialRow )
+ if ( aColumn.getRect().Right() > _rArea.Right() )
+ // this column is only partially visible, and this is not allowed
+ break;
+
+ aColumn.moveRight();
+ ++visibleColumns;
+ }
+ return visibleColumns;
+ }
+
+ }
+
+ //------------------------------------------------------------------------------------------------------------------
+ long TableControl_Impl::impl_ni_calculateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding,
+ bool const i_assumeVerticalScrollbar, ::std::vector< long >& o_newColWidthsPixel ) const
+ {
// the available horizontal space
long gridWidthPixel = m_rAntiImpl.GetOutputSizePixel().Width();
+ ENSURE_OR_RETURN( !!m_pModel, "TableControl_Impl::impl_ni_calculateColumnWidths: not allowed without a model!", gridWidthPixel );
if ( m_pModel->hasRowHeaders() && ( gridWidthPixel != 0 ) )
{
gridWidthPixel -= m_nRowHeaderWidthPixel;
}
- if ( m_pModel->getVerticalScrollbarVisibility() != ScrollbarShowNever )
+
+ if ( i_assumeVerticalScrollbar && ( m_pModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ) )
{
long nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize();
gridWidthPixel -= nScrollbarMetrics;
}
- // TODO: shouldn't we take the visibility of the vertical scroll bar into account here, too?
- long const gridWidthAppFont = m_rAntiImpl.PixelToLogic( Size( gridWidthPixel, 0 ), MAP_APPFONT ).Width();
-
- // determine the accumulated current width of all columns
- for ( ColPos col = 0; col < colCount; ++col )
- {
- const PColumnModel pColumn = m_pModel->getColumnModel( col );
- ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" );
-
- }
+ // no need to do anything without columns
+ TableSize const colCount = m_pModel->getColumnCount();
+ if ( colCount == 0 )
+ return gridWidthPixel;
// collect some meta data for our columns:
- // - their current (appt-font) metrics
+ // - their current (pixel) metrics
long accumulatedCurrentWidth = 0;
::std::vector< long > currentColWidths;
currentColWidths.reserve( colCount );
- // - their effective minimal and maximal width (app-font!)
typedef ::std::vector< ::std::pair< long, long > > ColumnLimits;
ColumnLimits effectiveColumnLimits;
effectiveColumnLimits.reserve( colCount );
@@ -863,13 +947,14 @@ namespace svt { namespace table
::std::vector< ::sal_Int32 > columnFlexibilities;
columnFlexibilities.reserve( colCount );
long flexibilityDenominator = 0;
+ size_t flexibleColumnCount = 0;
for ( ColPos col = 0; col < colCount; ++col )
{
PColumnModel const pColumn = m_pModel->getColumnModel( col );
ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" );
// current width
- TableMetrics const currentWidth = pColumn->getWidth();
+ long const currentWidth = appFontWidthToPixel( pColumn->getWidth() );
currentColWidths.push_back( currentWidth );
// accumulated width
@@ -877,7 +962,7 @@ namespace svt { namespace table
// flexibility
::sal_Int32 flexibility = pColumn->getFlexibility();
- OSL_ENSURE( flexibility >= 0, "TableControl_Impl::impl_ni_updateColumnWidths: a column's flexibility should be non-negative." );
+ OSL_ENSURE( flexibility >= 0, "TableControl_Impl::impl_ni_calculateColumnWidths: a column's flexibility should be non-negative." );
if ( ( flexibility < 0 ) // normalization
|| ( !pColumn->isResizable() ) // column not resizeable => no auto-resize
|| ( col <= i_assumeInflexibleColumnsUpToIncluding ) // column shall be treated as inflexible => respec this
@@ -889,18 +974,18 @@ namespace svt { namespace table
// if the column is not flexible, it will not be asked for min/max, but we assume the current width as limit then
if ( flexibility > 0 )
{
- long const minWidth = pColumn->getMinWidth();
+ long const minWidth = appFontWidthToPixel( pColumn->getMinWidth() );
if ( minWidth > 0 )
effectiveMin = minWidth;
else
effectiveMin = MIN_COLUMN_WIDTH_PIXEL;
- long const maxWidth = pColumn->getMaxWidth();
- OSL_ENSURE( minWidth <= maxWidth, "TableControl_Impl::impl_ni_updateColumnWidths: pretty undecided 'bout its width limits, this column!" );
+ long const maxWidth = appFontWidthToPixel( pColumn->getMaxWidth() );
+ OSL_ENSURE( minWidth <= maxWidth, "TableControl_Impl::impl_ni_calculateColumnWidths: pretty undecided 'bout its width limits, this column!" );
if ( ( maxWidth > 0 ) && ( maxWidth >= minWidth ) )
effectiveMax = maxWidth;
else
- effectiveMax = gridWidthAppFont; // TODO: any better guess here?
+ effectiveMax = gridWidthPixel; // TODO: any better guess here?
if ( effectiveMin == effectiveMax )
// if the min and the max are identical, this implies no flexibility at all
@@ -909,27 +994,29 @@ namespace svt { namespace table
columnFlexibilities.push_back( flexibility );
flexibilityDenominator += flexibility;
+ if ( flexibility > 0 )
+ ++flexibleColumnCount;
effectiveColumnLimits.push_back( ::std::pair< long, long >( effectiveMin, effectiveMax ) );
accumulatedMinWidth += effectiveMin;
accumulatedMaxWidth += effectiveMax;
}
- ::std::vector< long > newWidths( currentColWidths );
+ o_newColWidthsPixel = currentColWidths;
if ( flexibilityDenominator == 0 )
{
// no column is flexible => don't adjust anything
}
- else if ( gridWidthAppFont > accumulatedCurrentWidth )
+ else if ( gridWidthPixel > accumulatedCurrentWidth )
{ // we have space to give away ...
- long distributeAppFontUnits = gridWidthAppFont - accumulatedCurrentWidth;
- if ( gridWidthAppFont > accumulatedMaxWidth )
+ long distributePixel = gridWidthPixel - accumulatedCurrentWidth;
+ if ( gridWidthPixel > accumulatedMaxWidth )
{
// ... but the column's maximal widths are still less than we have
// => set them all to max
for ( size_t i = 0; i < size_t( colCount ); ++i )
{
- newWidths[i] = effectiveColumnLimits[i].second;
+ o_newColWidthsPixel[i] = effectiveColumnLimits[i].second;
}
}
else
@@ -939,13 +1026,13 @@ namespace svt { namespace table
{
startOver = false;
// distribute the remaining space amongst all columns with a positive flexibility
- for ( size_t i=0; i<newWidths.size() && !startOver; ++i )
+ for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i )
{
long const columnFlexibility = columnFlexibilities[i];
if ( columnFlexibility == 0 )
continue;
- long newColWidth = currentColWidths[i] + columnFlexibility * distributeAppFontUnits / flexibilityDenominator;
+ long newColWidth = currentColWidths[i] + columnFlexibility * distributePixel / flexibilityDenominator;
if ( newColWidth > effectiveColumnLimits[i].second )
{ // that was too much, we hit the col's maximum
@@ -954,9 +1041,10 @@ namespace svt { namespace table
// adjust the flexibility denominator ...
flexibilityDenominator -= columnFlexibility;
columnFlexibilities[i] = 0;
+ --flexibleColumnCount;
// ... and the remaining width ...
long const difference = newColWidth - currentColWidths[i];
- distributeAppFontUnits -= difference;
+ distributePixel -= difference;
// ... this way, we ensure that the width not taken up by this column is consumed by the other
// flexible ones (if there are some)
@@ -965,22 +1053,46 @@ namespace svt { namespace table
startOver = true;
}
- newWidths[i] = newColWidth;
+ o_newColWidthsPixel[i] = newColWidth;
}
}
while ( startOver );
+
+ // are there pixels left (might be caused by rounding errors)?
+ while ( ( distributePixel > 0 ) && ( flexibleColumnCount > 0 ) )
+ {
+ // yes => ignore relative flexibilities, and subsequently distribute single pixels to all flexible
+ // columns which did not yet reach their maximum.
+ for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( distributePixel > 0 ); ++i )
+ {
+ if ( columnFlexibilities[i] == 0 )
+ continue;
+
+ OSL_ENSURE( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].second,
+ "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" );
+ if ( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first )
+ {
+ columnFlexibilities[i] = 0;
+ --flexibleColumnCount;
+ continue;
+ }
+
+ ++o_newColWidthsPixel[i];
+ --distributePixel;
+ }
+ }
}
}
- else if ( gridWidthAppFont < accumulatedCurrentWidth )
+ else if ( gridWidthPixel < accumulatedCurrentWidth )
{ // we need to take away some space from the columns which allow it ...
- long takeAwayAppFontUnits = accumulatedCurrentWidth - gridWidthAppFont;
- if ( gridWidthAppFont < accumulatedMinWidth )
+ long takeAwayPixel = accumulatedCurrentWidth - gridWidthPixel;
+ if ( gridWidthPixel < accumulatedMinWidth )
{
// ... but the column's minimal widths are still more than we have
// => set them all to min
for ( size_t i = 0; i < size_t( colCount ); ++i )
{
- newWidths[i] = effectiveColumnLimits[i].first;
+ o_newColWidthsPixel[i] = effectiveColumnLimits[i].first;
}
}
else
@@ -990,13 +1102,13 @@ namespace svt { namespace table
{
startOver = false;
// take away the space we need from the columns with a positive flexibility
- for ( size_t i=0; i<newWidths.size() && !startOver; ++i )
+ for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i )
{
long const columnFlexibility = columnFlexibilities[i];
if ( columnFlexibility == 0 )
continue;
- long newColWidth = currentColWidths[i] - columnFlexibility * takeAwayAppFontUnits / flexibilityDenominator;
+ long newColWidth = currentColWidths[i] - columnFlexibility * takeAwayPixel / flexibilityDenominator;
if ( newColWidth < effectiveColumnLimits[i].first )
{ // that was too much, we hit the col's minimum
@@ -1005,172 +1117,78 @@ namespace svt { namespace table
// adjust the flexibility denominator ...
flexibilityDenominator -= columnFlexibility;
columnFlexibilities[i] = 0;
+ --flexibleColumnCount;
// ... and the remaining width ...
long const difference = currentColWidths[i] - newColWidth;
- takeAwayAppFontUnits -= difference;
+ takeAwayPixel -= difference;
// and start over with the first column, since there might be earlier columns which need
// to be recalculated now
startOver = true;
}
- newWidths[i] = newColWidth;
+ o_newColWidthsPixel[i] = newColWidth;
}
}
while ( startOver );
- }
- }
- // now that we have calculated the app-font widths, get the actual pixels
- long accumulatedWidthPixel = m_nRowHeaderWidthPixel;
- for ( ColPos col = 0; col < colCount; ++col )
- {
- long const colWidth = m_rAntiImpl.LogicToPixel( Size( newWidths[col], 0 ), MAP_APPFONT ).Width();
- const long columnStart = accumulatedWidthPixel;
- const long columnEnd = columnStart + colWidth;
- m_aColumnWidths.push_back( MutableColumnMetrics( columnStart, columnEnd ) );
- accumulatedWidthPixel = columnEnd;
-
- // and don't forget to forward this to the column models
- PColumnModel const pColumn = m_pModel->getColumnModel( col );
- ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" );
- pColumn->setWidth( newWidths[col] );
- }
+ // are there pixels left (might be caused by rounding errors)?
+ while ( ( takeAwayPixel > 0 ) && ( flexibleColumnCount > 0 ) )
+ {
+ // yes => ignore relative flexibilities, and subsequently take away pixels from all flexible
+ // columns which did not yet reach their minimum.
+ for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( takeAwayPixel > 0 ); ++i )
+ {
+ if ( columnFlexibilities[i] == 0 )
+ continue;
- // if the column resizing happened to leave some space at the right, but there are columns
- // scrolled out to the left, scroll them in
- while ( ( m_nLeftColumn > 0 )
- && ( accumulatedWidthPixel - m_aColumnWidths[ m_nLeftColumn - 1 ].getStart() <= gridWidthPixel )
- )
- {
- --m_nLeftColumn;
- }
+ OSL_ENSURE( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first,
+ "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" );
+ if ( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].first )
+ {
+ columnFlexibilities[i] = 0;
+ --flexibleColumnCount;
+ continue;
+ }
- // now adjust the column metrics, since they currently ignore the horizontal scroll position
- if ( m_nLeftColumn > 0 )
- {
- const long offsetPixel = m_aColumnWidths[ 0 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getStart();
- for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin();
- colPos != m_aColumnWidths.end();
- ++colPos
- )
- {
- colPos->move( offsetPixel );
+ --o_newColWidthsPixel[i];
+ --takeAwayPixel;
+ }
+ }
}
}
+
+ return gridWidthPixel;
}
//------------------------------------------------------------------------------------------------------------------
- namespace
+ void TableControl_Impl::impl_ni_relayout( ColPos const i_assumeInflexibleColumnsUpToIncluding )
{
- //..............................................................................................................
- /// determines whether a scrollbar is needed for the given values
- bool lcl_determineScrollbarNeed( long const i_position, ScrollbarVisibility const i_visibility,
- long const i_availableSpace, long const i_neededSpace )
- {
- if ( i_visibility == ScrollbarShowNever )
- return false;
- if ( i_visibility == ScrollbarShowAlways )
- return true;
- if ( i_position > 0 )
- return true;
- if ( i_availableSpace >= i_neededSpace )
- return false;
- return true;
- }
-
- //..............................................................................................................
- void lcl_setButtonRepeat( Window& _rWindow, sal_uLong _nDelay )
- {
- AllSettings aSettings = _rWindow.GetSettings();
- MouseSettings aMouseSettings = aSettings.GetMouseSettings();
-
- aMouseSettings.SetButtonRepeat( _nDelay );
- aSettings.SetMouseSettings( aMouseSettings );
-
- _rWindow.SetSettings( aSettings, sal_True );
- }
-
- //..............................................................................................................
- void lcl_updateScrollbar( Window& _rParent, ScrollBar*& _rpBar,
- bool const i_needBar, long _nVisibleUnits,
- long _nPosition, long _nLineSize, long _nRange,
- bool _bHorizontal, const Link& _rScrollHandler )
- {
- // do we currently have the scrollbar?
- bool bHaveBar = _rpBar != NULL;
-
- // do we need to correct the scrollbar visibility?
- if ( bHaveBar && !i_needBar )
- {
- if ( _rpBar->IsTracking() )
- _rpBar->EndTracking();
- DELETEZ( _rpBar );
- }
- else if ( !bHaveBar && i_needBar )
- {
- _rpBar = new ScrollBar(
- &_rParent,
- WB_DRAG | ( _bHorizontal ? WB_HSCROLL : WB_VSCROLL )
- );
- _rpBar->SetScrollHdl( _rScrollHandler );
- // get some speed into the scrolling ....
- lcl_setButtonRepeat( *_rpBar, 0 );
- }
+ ENSURE_OR_RETURN_VOID( !m_bUpdatingColWidths, "TableControl_Impl::impl_ni_relayout: recursive call detected!" );
- if ( _rpBar )
- {
- _rpBar->SetRange( Range( 0, _nRange ) );
- _rpBar->SetVisibleSize( _nVisibleUnits );
- _rpBar->SetPageSize( _nVisibleUnits );
- _rpBar->SetLineSize( _nLineSize );
- _rpBar->SetThumbPos( _nPosition );
- _rpBar->Show();
- }
- }
-
- //..............................................................................................................
- /** returns the number of rows fitting into the given range,
- for the given row height. Partially fitting rows are counted, too, if the
- respective parameter says so.
- */
- TableSize lcl_getRowsFittingInto( long _nOverallHeight, long _nRowHeightPixel, bool _bAcceptPartialRow = false )
- {
- return _bAcceptPartialRow
- ? ( _nOverallHeight + ( _nRowHeightPixel - 1 ) ) / _nRowHeightPixel
- : _nOverallHeight / _nRowHeightPixel;
- }
-
- //..............................................................................................................
- /** returns the number of columns fitting into the given area,
- with the first visible column as given. Partially fitting columns are counted, too,
- if the respective parameter says so.
- */
- TableSize lcl_getColumnsVisibleWithin( const Rectangle& _rArea, ColPos _nFirstVisibleColumn,
- const TableControl_Impl& _rControl, bool _bAcceptPartialRow )
- {
- TableSize visibleColumns = 0;
- TableColumnGeometry aColumn( _rControl, _rArea, _nFirstVisibleColumn );
- while ( aColumn.isValid() )
- {
- if ( !_bAcceptPartialRow )
- if ( aColumn.getRect().Right() > _rArea.Right() )
- // this column is only partially visible, and this is not allowed
- break;
-
- aColumn.moveRight();
- ++visibleColumns;
- }
- return visibleColumns;
- }
-
- }
+ m_aColumnWidths.resize( 0 );
+ if ( !m_pModel )
+ return;
- //------------------------------------------------------------------------------------------------------------------
- void TableControl_Impl::impl_ni_updateScrollbars()
- {
+ ::comphelper::FlagRestorationGuard const aWidthUpdateFlag( m_bUpdatingColWidths, true );
SuppressCursor aHideCursor( *this );
+ // layouting steps:
+ //
+ // 1. adjust column widths, leaving space for a vertical scrollbar
+ // 2. determine need for a vertical scrollbar
+ // - V-YES: all fine, result from 1. is still valid
+ // - V-NO: result from 1. is still under consideration
+ //
+ // 3. determine need for a horizontal scrollbar
+ // - H-NO: all fine, result from 2. is still valid
+ // - H-YES: reconsider need for a vertical scrollbar, if result of 2. was V-NO
+ // - V-YES: all fine, result from 1. is still valid
+ // - V-NO: redistribute the remaining space (if any) amongst all columns which allow it
+
+ ::std::vector< long > newWidthsPixel;
+ long gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, true, newWidthsPixel );
+
// the width/height of a scrollbar, needed several times below
long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize();
@@ -1179,18 +1197,13 @@ namespace svt { namespace table
Rectangle aDataCellPlayground( Point( 0, 0 ), m_rAntiImpl.GetOutputSizePixel() );
aDataCellPlayground.Left() = m_nRowHeaderWidthPixel;
aDataCellPlayground.Top() = m_nColHeaderHeightPixel;
- m_nRowCount = m_pModel->getRowCount();
- m_nColumnCount = m_pModel->getColumnCount();
- if ( m_aColumnWidths.empty() )
- impl_ni_updateColumnWidths();
- OSL_ENSURE( m_aColumnWidths.size() == size_t( m_nColumnCount ), "TableControl_Impl::impl_ni_updateScrollbars: inconsistency!" );
- const long nAllColumnsWidth = m_aColumnWidths.empty()
- ? 0
- : m_aColumnWidths[ m_nColumnCount - 1 ].getEnd() - m_aColumnWidths[ 0 ].getStart();
+ OSL_ENSURE( ( m_nRowCount == m_pModel->getRowCount() ) && ( m_nColumnCount == m_pModel->getColumnCount() ),
+ "TableControl_Impl::impl_ni_relayout: how is this expected to work with invalid data?" );
+ long const nAllColumnsWidth = ::std::accumulate( newWidthsPixel.begin(), newWidthsPixel.end(), 0 );
- const ScrollbarVisibility eVertScrollbar = m_pModel->getVerticalScrollbarVisibility();
- const ScrollbarVisibility eHorzScrollbar = m_pModel->getHorizontalScrollbarVisibility();
+ ScrollbarVisibility const eVertScrollbar = m_pModel->getVerticalScrollbarVisibility();
+ ScrollbarVisibility const eHorzScrollbar = m_pModel->getHorizontalScrollbarVisibility();
// do we need a vertical scrollbar?
bool bNeedVerticalScrollbar = lcl_determineScrollbarNeed(
@@ -1201,8 +1214,10 @@ namespace svt { namespace table
aDataCellPlayground.Right() -= nScrollbarMetrics;
bFirstRoundVScrollNeed = true;
}
+
// do we need a horizontal scrollbar?
- const bool bNeedHorizontalScrollbar = lcl_determineScrollbarNeed( m_nLeftColumn, eHorzScrollbar, aDataCellPlayground.GetWidth(), nAllColumnsWidth );
+ bool const bNeedHorizontalScrollbar = lcl_determineScrollbarNeed(
+ m_nLeftColumn, eHorzScrollbar, aDataCellPlayground.GetWidth(), nAllColumnsWidth );
if ( bNeedHorizontalScrollbar )
{
aDataCellPlayground.Bottom() -= nScrollbarMetrics;
@@ -1221,12 +1236,77 @@ namespace svt { namespace table
}
}
}
+
+ // show or hide the scrollbars as needed
+ impl_ni_positionChildWindows( aDataCellPlayground, bNeedVerticalScrollbar, bNeedHorizontalScrollbar );
+
+ // the initial call to impl_ni_calculateColumnWidths assumed that we need a vertical scrollbar. If, by now,
+ // we know that this is not the case, re-calculate the column widths.
+ if ( !bNeedVerticalScrollbar )
+ gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, false, newWidthsPixel );
+
+ // update the column objects with the new widths we finally calculated
+ TableSize const colCount = m_pModel->getColumnCount();
+ m_aColumnWidths.reserve( colCount );
+ long accumulatedWidthPixel = m_nRowHeaderWidthPixel;
+ bool anyColumnWidthChanged = false;
+ for ( ColPos col = 0; col < colCount; ++col )
+ {
+ const long columnStart = accumulatedWidthPixel;
+ const long columnEnd = columnStart + newWidthsPixel[col];
+ m_aColumnWidths.push_back( MutableColumnMetrics( columnStart, columnEnd ) );
+ accumulatedWidthPixel = columnEnd;
+
+ // and don't forget to forward this to the column models
+ PColumnModel const pColumn = m_pModel->getColumnModel( col );
+ ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" );
+
+ long const oldColumnWidthAppFont = pColumn->getWidth();
+ long const newColumnWidthAppFont = pixelWidthToAppFont( newWidthsPixel[col] );
+ pColumn->setWidth( newColumnWidthAppFont );
+
+ anyColumnWidthChanged |= ( oldColumnWidthAppFont != newColumnWidthAppFont );
+ }
+
+ // if the column widths changed, ensure everything is repainted
+ if ( anyColumnWidthChanged )
+ invalidate( TableAreaAll );
+
+ // if the column resizing happened to leave some space at the right, but there are columns
+ // scrolled out to the left, scroll them in
+ while ( ( m_nLeftColumn > 0 )
+ && ( accumulatedWidthPixel - m_aColumnWidths[ m_nLeftColumn - 1 ].getStart() <= gridWidthPixel )
+ )
+ {
+ --m_nLeftColumn;
+ }
+
+ // now adjust the column metrics, since they currently ignore the horizontal scroll position
+ if ( m_nLeftColumn > 0 )
+ {
+ const long offsetPixel = m_aColumnWidths[ 0 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getStart();
+ for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin();
+ colPos != m_aColumnWidths.end();
+ ++colPos
+ )
+ {
+ colPos->move( offsetPixel );
+ }
+ }
+ }
+
+ //------------------------------------------------------------------------------------------------------------------
+ void TableControl_Impl::impl_ni_positionChildWindows( Rectangle const & i_dataCellPlayground,
+ bool const i_verticalScrollbar, bool const i_horizontalScrollbar )
+ {
+ long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize();
+
// create or destroy the vertical scrollbar, as needed
lcl_updateScrollbar(
m_rAntiImpl,
m_pVScroll,
- bNeedVerticalScrollbar,
- lcl_getRowsFittingInto( aDataCellPlayground.GetHeight(), m_nRowHeightPixel ),
+ i_verticalScrollbar,
+ lcl_getRowsFittingInto( i_dataCellPlayground.GetHeight(), m_nRowHeightPixel ),
// visible units
m_nTopRow, // current position
1, // line size
@@ -1234,12 +1314,13 @@ namespace svt { namespace table
false, // vertical
LINK( this, TableControl_Impl, OnScroll ) // scroll handler
);
+
// position it
if ( m_pVScroll )
{
Rectangle aScrollbarArea(
- Point( aDataCellPlayground.Right() + 1, 0 ),
- Size( nScrollbarMetrics, aDataCellPlayground.Bottom() + 1 )
+ Point( i_dataCellPlayground.Right() + 1, 0 ),
+ Size( nScrollbarMetrics, i_dataCellPlayground.Bottom() + 1 )
);
m_pVScroll->SetPosSizePixel(
aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() );
@@ -1249,8 +1330,8 @@ namespace svt { namespace table
lcl_updateScrollbar(
m_rAntiImpl,
m_pHScroll,
- bNeedHorizontalScrollbar,
- lcl_getColumnsVisibleWithin( aDataCellPlayground, m_nLeftColumn, *this, false ),
+ i_horizontalScrollbar,
+ lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ),
// visible units
m_nLeftColumn, // current position
1, // line size
@@ -1258,22 +1339,23 @@ namespace svt { namespace table
true, // horizontal
LINK( this, TableControl_Impl, OnScroll ) // scroll handler
);
+
// position it
if ( m_pHScroll )
{
- TableSize const nVisibleUnits = lcl_getColumnsVisibleWithin( aDataCellPlayground, m_nLeftColumn, *this, false );
+ TableSize const nVisibleUnits = lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false );
TableMetrics const nRange = m_nColumnCount;
if( m_nLeftColumn + nVisibleUnits == nRange - 1 )
{
- if ( m_aColumnWidths[ nRange - 1 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getEnd() + m_aColumnWidths[ nRange-1 ].getWidth() > aDataCellPlayground.GetWidth() )
+ if ( m_aColumnWidths[ nRange - 1 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getEnd() + m_aColumnWidths[ nRange-1 ].getWidth() > i_dataCellPlayground.GetWidth() )
{
m_pHScroll->SetVisibleSize( nVisibleUnits -1 );
m_pHScroll->SetPageSize( nVisibleUnits - 1 );
}
}
Rectangle aScrollbarArea(
- Point( 0, aDataCellPlayground.Bottom() + 1 ),
- Size( aDataCellPlayground.Right() + 1, nScrollbarMetrics )
+ Point( 0, i_dataCellPlayground.Bottom() + 1 ),
+ Size( i_dataCellPlayground.Right() + 1, nScrollbarMetrics )
);
m_pHScroll->SetPosSizePixel(
aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() );
@@ -1290,19 +1372,19 @@ namespace svt { namespace table
{
m_pScrollCorner = new ScrollBarBox( &m_rAntiImpl );
m_pScrollCorner->SetSizePixel( Size( nScrollbarMetrics, nScrollbarMetrics ) );
- m_pScrollCorner->SetPosPixel( Point( aDataCellPlayground.Right() + 1, aDataCellPlayground.Bottom() + 1 ) );
+ m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) );
m_pScrollCorner->Show();
}
else if(bHaveScrollCorner && bNeedScrollCorner)
{
- m_pScrollCorner->SetPosPixel( Point( aDataCellPlayground.Right() + 1, aDataCellPlayground.Bottom() + 1 ) );
+ m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) );
m_pScrollCorner->Show();
}
// resize the data window
m_pDataWindow->SetSizePixel( Size(
- aDataCellPlayground.GetWidth() + m_nRowHeaderWidthPixel,
- aDataCellPlayground.GetHeight() + m_nColHeaderHeightPixel
+ i_dataCellPlayground.GetWidth() + m_nRowHeaderWidthPixel,
+ i_dataCellPlayground.GetHeight() + m_nColHeaderHeightPixel
) );
}
@@ -1311,8 +1393,7 @@ namespace svt { namespace table
{
DBG_CHECK_ME();
- impl_ni_updateColumnWidths();
- impl_ni_updateScrollbars();
+ impl_ni_relayout();
checkCursorPosition();
}
@@ -1982,6 +2063,12 @@ namespace svt { namespace table
}
//------------------------------------------------------------------------------------------------------------------
+ long TableControl_Impl::appFontWidthToPixel( long const i_appFontUnits ) const
+ {
+ return m_pDataWindow->LogicToPixel( Size( i_appFontUnits, 0 ), MAP_APPFONT ).Width();
+ }
+
+ //------------------------------------------------------------------------------------------------------------------
void TableControl_Impl::hideTracking()
{
m_pDataWindow->HideTracking();
@@ -2241,12 +2328,19 @@ namespace svt { namespace table
m_pDataWindow->Invalidate( INVALIDATE_UPDATE );
// update the position at the vertical scrollbar
- m_pVScroll->SetThumbPos( m_nTopRow );
- }
-
- // The scroll bar availaility might change when we scrolled. This is because we do not hide
- // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will
- // be auto-hidden when it's scrolled back to pos 0.
+ if ( m_pVScroll != NULL )
+ m_pVScroll->SetThumbPos( m_nTopRow );
+ }
+
+ // The scroll bar availaility might change when we scrolled.
+ // For instance, imagine a view with 10 rows, if which 5 fit into the window, numbered 1 to 10.
+ // Now let
+ // - the user scroll to row number 6, so the last 5 rows are visible
+ // - somebody remove the last 4 rows
+ // - the user scroll to row number 5 being the top row, so the last two rows are visible
+ // - somebody remove row number 6
+ // - the user scroll to row number 1
+ // => in this case, the need for the scrollbar vanishes immediately.
if ( m_nTopRow == 0 )
m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) );
@@ -2311,7 +2405,8 @@ namespace svt { namespace table
m_pDataWindow->Invalidate( INVALIDATE_UPDATE );
// update the position at the horizontal scrollbar
- m_pHScroll->SetThumbPos( m_nLeftColumn );
+ if ( m_pHScroll != NULL )
+ m_pHScroll->SetThumbPos( m_nLeftColumn );
}
// The scroll bar availaility might change when we scrolled. This is because we do not hide
@@ -2564,7 +2659,9 @@ namespace svt { namespace table
IMPL_LINK( TableControl_Impl, OnUpdateScrollbars, void*, /**/ )
{
DBG_CHECK_ME();
- impl_ni_updateScrollbars();
+ // TODO: can't we simply use lcl_updateScrollbar here, so the scrollbars ranges are updated, instead of
+ // doing a complete re-layout?
+ impl_ni_relayout();
return 1L;
}
diff --git a/svtools/source/table/tablecontrol_impl.hxx b/svtools/source/table/tablecontrol_impl.hxx
index 6fb6b94ee930..0af23ea2804e 100644
--- a/svtools/source/table/tablecontrol_impl.hxx
+++ b/svtools/source/table/tablecontrol_impl.hxx
@@ -305,6 +305,8 @@ namespace svt { namespace table
virtual bool isRowSelected( RowPos i_row ) const;
+ long appFontWidthToPixel( long const i_appFontUnits ) const;
+
TableDataWindow& getDataWindow() { return *m_pDataWindow; }
const TableDataWindow& getDataWindow() const { return *m_pDataWindow; }
ScrollBar* getHorzScrollbar();
@@ -384,26 +386,45 @@ namespace svt { namespace table
*/
void impl_ni_updateCachedTableMetrics();
- /** updates ->m_aColumnWidthsPixel with the current pixel widths of all model columns
+ /** does a relayout of the table control
- The method is not bound to the classes public invariants, as it's used in
- situations where the they must not necessarily be fullfilled.
+ Column widths, and consequently the availability of the vertical and horizontal scrollbar, are updated
+ with a call to this method.
@param i_assumeInflexibleColumnsUpToIncluding
the index of a column up to which all columns should be considered as inflexible, or
<code>COL_INVALID</code>.
*/
- void impl_ni_updateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding = COL_INVALID );
+ void impl_ni_relayout( ColPos const i_assumeInflexibleColumnsUpToIncluding = COL_INVALID );
- /** updates the scrollbars of the control
+ /** calculates the new width of our columns, taking into account their min and max widths, and their relative
+ flexibility.
- The method is not bound to the classes public invariants, as it's used in
- situations where the they must not necessarily be fullfilled.
+ @param i_assumeInflexibleColumnsUpToIncluding
+ the index of a column up to which all columns should be considered as inflexible, or
+ <code>COL_INVALID</code>.
+
+ @param i_assumeVerticalScrollbar
+ controls whether or not we should assume the presence of a vertical scrollbar. If <true/>, and
+ if the model has a VerticalScrollbarVisibility != ScrollbarShowNever, the method will leave
+ space for a vertical scrollbar.
+
+ @return
+ the overall width of the grid, which is available for columns
+ */
+ long impl_ni_calculateColumnWidths(
+ ColPos const i_assumeInflexibleColumnsUpToIncluding,
+ bool const i_assumeVerticalScrollbar,
+ ::std::vector< long >& o_newColWidthsPixel
+ ) const;
- This includes both the existence of the scrollbars, and their
- state.
+ /** positions all child windows, e.g. the both scrollbars, the corner window, and the data window
*/
- void impl_ni_updateScrollbars();
+ void impl_ni_positionChildWindows(
+ Rectangle const & i_dataCellPlayground,
+ bool const i_verticalScrollbar,
+ bool const i_horizontalScrollbar
+ );
/** scrolls the view by the given number of rows