[Libreoffice-commits] .: svtools/source

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Fri Sep 14 00:48:02 PDT 2012


 svtools/source/table/tablecontrol_impl.cxx |  587 ++++++++++++++++-------------
 svtools/source/table/tablecontrol_impl.hxx |   41 +-
 2 files changed, 373 insertions(+), 255 deletions(-)

New commits:
commit a1f695dea516dfb7199390f750ca8744bb7df912
Author: Frank Schoenheit [fs] <frank.schoenheit at oracle.com>
Date:   Mon Mar 28 10:56:24 2011 +0200

    UNFINISHED: column resizing refactoring
    
    Change-Id: Iac8db5ee848f35f19f8fe4eb431485559600be49

diff --git a/svtools/source/table/tablecontrol_impl.cxx b/svtools/source/table/tablecontrol_impl.cxx
index 521bb2d..df6bbbc 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 6fb6b94..0af23ea 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
 


More information about the Libreoffice-commits mailing list