From f675f9531483cb59d34c13f3746010e438298704 Mon Sep 17 00:00:00 2001 From: Marco Cecchetti Date: Wed, 29 Mar 2017 13:50:18 +0200 Subject: LOK - Calc: row/column headers are updated faster Now the computation of the row/column headers data exploits the cached row/col positions in HeightHelper/WidthHelper. That makes updating row/column headers at the bottom of the document as fast as at the top even for very big spreadsheets. Change-Id: Ida0ed8d8885b71fe3206efbdaa62a0bb95153ed7 --- desktop/qa/desktop_lib/test_desktop_lib.cxx | 4 +- include/sal/log-areas.dox | 1 + sc/source/ui/inc/tabview.hxx | 5 + sc/source/ui/view/tabview.cxx | 426 +++++++++++++++++++++------- 4 files changed, 331 insertions(+), 105 deletions(-) diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index 20958267a291..2dfb036e6be7 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -636,7 +636,7 @@ void DesktopLOKTest::testRowColumnHeaders() if (bFirstHeader) { CPPUNIT_ASSERT(nSize <= nY); - CPPUNIT_ASSERT_EQUAL(OString("11"), aText); + CPPUNIT_ASSERT_EQUAL(OString("10"), aText); bFirstHeader = false; } else @@ -663,7 +663,7 @@ void DesktopLOKTest::testRowColumnHeaders() if (bFirstHeader) { CPPUNIT_ASSERT(nSize <= nX); - CPPUNIT_ASSERT_EQUAL(OString("4"), aText); + CPPUNIT_ASSERT_EQUAL(OString("3"), aText); bFirstHeader = false; } else diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox index b702c3ca2324..e51c82c14970 100644 --- a/include/sal/log-areas.dox +++ b/include/sal/log-areas.dox @@ -127,6 +127,7 @@ certain functionality. @li @c sc.core.grouparealistener - sc::FormulaGroupAreaListener @li @c sc.filter - Calc filter @li @c sc.lok.docsize +@li @c sc.lok.header @li @c sc.lok.poshelper @li @c sc.opencl - OpenCL-related stuff in general @li @c sc.opencl.source - Generated OpenCL source code diff --git a/sc/source/ui/inc/tabview.hxx b/sc/source/ui/inc/tabview.hxx index 76643e101be5..076f53f05d4a 100644 --- a/sc/source/ui/inc/tabview.hxx +++ b/sc/source/ui/inc/tabview.hxx @@ -195,6 +195,11 @@ private: double mfPendingTabBarWidth; // Tab bar width relative to frame window width. + SCROW mnLOKStartHeaderRow; + SCROW mnLOKEndHeaderRow; + SCCOL mnLOKStartHeaderCol; + SCCOL mnLOKEndHeaderCol; + bool bMinimized:1; bool bInUpdateHeader:1; bool bInActivatePart:1; diff --git a/sc/source/ui/view/tabview.cxx b/sc/source/ui/view/tabview.cxx index 6e32e84aa9ac..de98051ff3c5 100644 --- a/sc/source/ui/view/tabview.cxx +++ b/sc/source/ui/view/tabview.cxx @@ -227,6 +227,8 @@ ScTabView::ScTabView( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* nOldCurX( 0 ), nOldCurY( 0 ), mfPendingTabBarWidth( -1.0 ), + mnLOKStartHeaderRow( std::numeric_limits::min() ), + mnLOKEndHeaderRow( std::numeric_limits::min() ), bMinimized( false ), bInUpdateHeader( false ), bInActivatePart( false ), @@ -2305,6 +2307,259 @@ void ScTabView::SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vectorGetRowHeight(nRow, nTab); + return ScViewData::ToPixel(nSize, 1.0 / TWIPS_PER_PIXEL); +} + +inline +long lcl_GetColWidthPx(ScDocument* pDoc, SCCOL nCol, SCTAB nTab) +{ + const sal_uInt16 nSize = pDoc->GetColWidth(nCol, nTab); + return ScViewData::ToPixel(nSize, 1.0 / TWIPS_PER_PIXEL); +} + +} // anonymous namespace + +template +class BoundsProvider +{ + typedef ScPositionHelper::value_type value_type; + typedef IndexType index_type; + + static const index_type MAX_INDEX; + + ScDocument* pDoc; + const SCTAB nTab; + + index_type nFirstIndex; + index_type nSecondIndex; + long nFirstPositionPx; + long nSecondPositionPx; + +public: + BoundsProvider(ScDocument* pD, SCTAB nT) + : pDoc(pD) + , nTab(nT) + {} + + void GetStartIndexAndPosition(index_type& nIndex, long& nPosition) const + { + nIndex = nFirstIndex; + nPosition = nFirstPositionPx; + } + + void GetEndIndexAndPosition(index_type& nIndex, long& nPosition) const + { + nIndex = nSecondIndex; + nPosition = nSecondPositionPx; + } + + void Compute(value_type aFirstNearest, value_type aSecondNearest, + long nFirstBound, long nSecondBound); + + void EnlargeStartBy(long nOffset); + + void EnlargeEndBy(long nOffset); + + void EnlargeBy(long nOffset) + { + EnlargeStartBy(nOffset); + EnlargeEndBy(nOffset); + } + +private: + long GetSize(SCROW nIndex) const + { + return lcl_GetRowHeightPx(pDoc, nIndex, nTab); + } + + long GetSize(SCCOL nIndex) const + { + return lcl_GetColWidthPx(pDoc, nIndex, nTab); + } + + void GetIndexAndPos(index_type nNearestIndex, long nNearestPosition, + long nBound, index_type& nFoundIndex, long& nPosition, + bool bTowards, long nDiff) + { + if (nDiff > 0) // nBound < nNearestPosition + GeIndexBackwards(nNearestIndex, nNearestPosition, nBound, + nFoundIndex, nPosition, bTowards); + else + GetIndexTowards(nNearestIndex, nNearestPosition, nBound, + nFoundIndex, nPosition, bTowards); + } + + void GeIndexBackwards(index_type nNearestIndex, long nNearestPosition, + long nBound, index_type& nFoundIndex, long& nPosition, + bool bTowards); + + void GetIndexTowards(index_type nNearestIndex, long nNearestPosition, + long nBound, index_type& nFoundIndex, long& nPosition, + bool bTowards); +}; + +template +const IndexType BoundsProvider::MAX_INDEX; + +template<> +const SCROW BoundsProvider::MAX_INDEX = MAXTILEDROW; + +template<> +const SCCOL BoundsProvider::MAX_INDEX = MAXCOL; + +template +void BoundsProvider::Compute( + value_type aFirstNearest, value_type aSecondNearest, + long nFirstBound, long nSecondBound) +{ + SAL_INFO("sc.lok.header", "BoundsProvider: nFirstBound: " << nFirstBound + << ", nSecondBound: " << nSecondBound); + + long nFirstDiff = aFirstNearest.second - nFirstBound; + long nSecondDiff = aSecondNearest.second - nSecondBound; + SAL_INFO("sc.lok.header", "BoundsProvider: rTopNearest: index: " << aFirstNearest.first + << ", pos: " << aFirstNearest.second << ", diff: " << nFirstDiff); + SAL_INFO("sc.lok.header", "BoundsProvider: rBottomNearest: index: " << aSecondNearest.first + << ", pos: " << aSecondNearest.second << ", diff: " << nSecondDiff); + + bool bReverse = !(std::abs(nFirstDiff) < std::abs(nSecondDiff)); + + if(bReverse) + { + std::swap(aFirstNearest, aSecondNearest); + std::swap(nFirstBound, nSecondBound); + std::swap(nFirstDiff, nSecondDiff); + } + + index_type nNearestIndex = aFirstNearest.first; + long nNearestPosition = aFirstNearest.second; + SAL_INFO("sc.lok.header", "BoundsProvider: nearest to first bound: nNearestIndex: " + << nNearestIndex << ", nNearestPosition: " << nNearestPosition); + + GetIndexAndPos(nNearestIndex, nNearestPosition, nFirstBound, + nFirstIndex, nFirstPositionPx, !bReverse, nFirstDiff); + SAL_INFO("sc.lok.header", "BoundsProvider: nFirstIndex: " << nFirstIndex + << ", nFirstPositionPx: " << nFirstPositionPx); + + if (std::abs(nSecondDiff) < std::abs(nSecondBound - nFirstPositionPx)) + { + nNearestIndex = aSecondNearest.first; + nNearestPosition = aSecondNearest.second; + } + else + { + nNearestPosition = nFirstPositionPx; + nNearestIndex = nFirstIndex; + nSecondDiff = !bReverse ? -1 : 1; + } + SAL_INFO("sc.lok.header", "BoundsProvider: nearest to second bound: nNearestIndex: " + << nNearestIndex << ", nNearestPosition: " << nNearestPosition + << ", diff: " << nSecondDiff); + + GetIndexAndPos(nNearestIndex, nNearestPosition, nSecondBound, + nSecondIndex, nSecondPositionPx, bReverse, nSecondDiff); + SAL_INFO("sc.lok.header", "BoundsProvider: nSecondIndex: " << nSecondIndex + << ", nSecondPositionPx: " << nSecondPositionPx); + + if (bReverse) + { + std::swap(nFirstIndex, nSecondIndex); + std::swap(nFirstPositionPx, nSecondPositionPx); + } +} + +template +void BoundsProvider::EnlargeStartBy(long nOffset) +{ + const index_type nNewFirstIndex = + std::max(static_cast(-1), + static_cast(nFirstIndex - nOffset)); + for (index_type nIndex = nFirstIndex; nIndex > nNewFirstIndex; --nIndex) + { + const long nSizePx = GetSize(nIndex); + nFirstPositionPx -= nSizePx; + } + nFirstIndex = nNewFirstIndex; + SAL_INFO("sc.lok.header", "BoundsProvider: added offset: nFirstIndex: " << nFirstIndex + << ", nFirstPositionPx: " << nFirstPositionPx); +} + +template +void BoundsProvider::EnlargeEndBy(long nOffset) +{ + const index_type nNewSecondIndex = std::min(MAX_INDEX, static_cast(nSecondIndex + nOffset)); + for (index_type nIndex = nSecondIndex + 1; nIndex <= nNewSecondIndex; ++nIndex) + { + const long nSizePx = GetSize(nIndex); + nSecondPositionPx += nSizePx; + } + nSecondIndex = nNewSecondIndex; + SAL_INFO("sc.lok.header", "BoundsProvider: added offset: nSecondIndex: " << nSecondIndex + << ", nSecondPositionPx: " << nSecondPositionPx); +} + +template +void BoundsProvider::GeIndexBackwards( + index_type nNearestIndex, long nNearestPosition, + long nBound, index_type& nFoundIndex, long& nPosition, bool bTowards) +{ + nFoundIndex = -1; + for (index_type nIndex = nNearestIndex; nIndex >= 0; --nIndex) + { + if (nBound > nNearestPosition) + { + nFoundIndex = nIndex; // last index whose nPosition is less than nBound + nPosition = nNearestPosition; + break; + } + + const long nSizePx = GetSize(nIndex); + nNearestPosition -= nSizePx; + } + if (!bTowards && nFoundIndex != -1) + { + nFoundIndex += 1; + nPosition += GetSize(nFoundIndex); + } +} + +template +void BoundsProvider::GetIndexTowards( + index_type nNearestIndex, long nNearestPosition, + long nBound, index_type& nFoundIndex, long& nPosition, bool bTowards) +{ + nFoundIndex = -2; + for (index_type nIndex = nNearestIndex + 1; nIndex <= MAX_INDEX; ++nIndex) + { + const long nSizePx = GetSize(nIndex); + nNearestPosition += nSizePx; + + if (nNearestPosition > nBound) + { + nFoundIndex = nIndex; // first index whose nPosition is greater than nBound + nPosition = nNearestPosition; + break; + } + } + if (nFoundIndex == -2) + { + nFoundIndex = MAX_INDEX; + nPosition = nNearestPosition; + } + else if (bTowards) + { + nPosition -= GetSize(nFoundIndex); + nFoundIndex -= 1; + } +} + OUString ScTabView::getRowColumnHeaders(const Rectangle& rRectangle) { ScDocument* pDoc = aViewData.GetDocument(); @@ -2317,55 +2572,51 @@ OUString ScTabView::getRowColumnHeaders(const Rectangle& rRectangle) rtl::OUStringBuffer aBuffer(256); aBuffer.append("{ \"commandName\": \".uno:ViewRowColumnHeaders\",\n"); - SCROW nStartRow = 0; - SCROW nEndRow = 0; - SCCOL nStartCol = 0; - SCCOL nEndCol = 0; + SCTAB nTab = aViewData.GetTabNo(); + SCROW nStartRow = -1; + SCROW nEndRow = -1; + long nStartHeightPx = 0; + long nEndHeightPx = 0; + SCCOL nStartCol = -1; + SCCOL nEndCol = -1; + long nStartWidthPx = 0; + long nEndWidthPx = 0; + /// *** start collecting ROWS *** /// 1) compute start and end rows - long nTotalPixels = 0; if (rRectangle.Top() < rRectangle.Bottom()) { - long nUpperBoundPx = rRectangle.Top() / TWIPS_PER_PIXEL; - long nLowerBoundPx = rRectangle.Bottom() / TWIPS_PER_PIXEL; - nEndRow = MAXTILEDROW; - for (SCROW nRow = 0; nRow <= MAXTILEDROW; ++nRow) - { - if (nTotalPixels > nLowerBoundPx) - { - nEndRow = nRow; // first row below the rectangle - break; - } + SAL_INFO("sc.lok.header", "Row Header: compute start/end rows."); + long nRectTopPx = rRectangle.Top() / TWIPS_PER_PIXEL; + long nRectBottomPx = rRectangle.Bottom() / TWIPS_PER_PIXEL; - const sal_uInt16 nSize = pDoc->GetRowHeight(nRow, aViewData.GetTabNo()); - const long nSizePx = ScViewData::ToPixel(nSize, 1.0 / TWIPS_PER_PIXEL); + const auto& rTopNearest = aViewData.GetLOKHeightHelper().getNearestByPosition(nRectTopPx); + const auto& rBottomNearest = aViewData.GetLOKHeightHelper().getNearestByPosition(nRectBottomPx); - nTotalPixels += nSizePx; - - if (nTotalPixels < nUpperBoundPx) - { - nStartRow = nRow; // last row above the rectangle - continue; - } - } + BoundsProvider aBoundingRowsProvider(pDoc, nTab); + aBoundingRowsProvider.Compute(rTopNearest, rBottomNearest, nRectTopPx, nRectBottomPx); + aBoundingRowsProvider.EnlargeBy(2); + aBoundingRowsProvider.GetStartIndexAndPosition(nStartRow, nStartHeightPx); + aBoundingRowsProvider.GetEndIndexAndPosition(nEndRow, nEndHeightPx); - nStartRow -= 1; - nEndRow += 2; + aViewData.GetLOKHeightHelper().removeByIndex(mnLOKStartHeaderRow); + aViewData.GetLOKHeightHelper().removeByIndex(mnLOKEndHeaderRow); + aViewData.GetLOKHeightHelper().insert(nStartRow, nStartHeightPx); + aViewData.GetLOKHeightHelper().insert(nEndRow, nEndHeightPx); - if (nStartRow < 0) nStartRow = 0; - if (nEndRow > MAXTILEDROW) nEndRow = MAXTILEDROW; + mnLOKStartHeaderRow = nStartRow; + mnLOKEndHeaderRow = nEndRow; } - aBuffer.ensureCapacity( aBuffer.getCapacity() + (50 * (nEndRow - nStartRow + 1)) ); - - long nVisibleRows = nEndRow - nStartRow; if (nVisibleRows < 25) nVisibleRows = 25; + SAL_INFO("sc.lok.header", "Row Header: visible rows: " << nVisibleRows); + /// 2) if we are approaching current max tiled row, signal a size changed event /// and invalidate the involved area @@ -2384,6 +2635,7 @@ OUString ScTabView::getRowColumnHeaders(const Rectangle& rRectangle) if (pModelObj) aNewSize = pModelObj->getDocumentSize(); + SAL_INFO("sc.lok.header", "Row Header: a new height: " << aNewSize.Height()); if (pDocSh) { // Provide size in the payload, so clients don't have to @@ -2410,39 +2662,27 @@ OUString ScTabView::getRowColumnHeaders(const Rectangle& rRectangle) aBuffer.append("\"rows\": [\n"); - bool bFirstRow = true; - if (nStartRow == 0 && nStartRow != nEndRow) + long nTotalPixels = aViewData.GetLOKHeightHelper().getPosition(nStartRow); + SAL_INFO("sc.lok.header", "Row Header: [create string data for rows]: start row: " + << nStartRow << " start height: " << nTotalPixels); + + if (nStartRow != nEndRow) { - aBuffer.append("{ \"text\": \"").append("0").append("\", "); - aBuffer.append("\"size\": \"").append(OUString::number(0)).append("\" }"); - bFirstRow = false; + OUString aText = OUString::number(nStartRow + 1); + aBuffer.append("{ \"text\": \"").append(aText).append("\", "); + aBuffer.append("\"size\": \"").append(OUString::number(nTotalPixels * TWIPS_PER_PIXEL)).append("\" }"); } - nTotalPixels = 0; - for (SCROW nRow = 0; nRow < nEndRow; ++nRow) + for (SCROW nRow = nStartRow + 1; nRow < nEndRow; ++nRow) { // nSize will be 0 for hidden rows. - const sal_uInt16 nSize = pDoc->GetRowHeight(nRow, aViewData.GetTabNo()); - const long nSizePx = ScViewData::ToPixel(nSize, 1.0 / TWIPS_PER_PIXEL); + const long nSizePx = lcl_GetRowHeightPx(pDoc, nRow, nTab); nTotalPixels += nSizePx; - if (nRow < nStartRow) - continue; - OUString aText = pRowBar[SC_SPLIT_BOTTOM]->GetEntryText(nRow); - - if (!bFirstRow) - { - aBuffer.append(", "); - } - else - { - aText = OUString::number(nStartRow + 1); - } - + aBuffer.append(", "); aBuffer.append("{ \"text\": \"").append(aText).append("\", "); aBuffer.append("\"size\": \"").append(OUString::number(nTotalPixels * TWIPS_PER_PIXEL)).append("\" }"); - bFirstRow = false; } aBuffer.append("]"); @@ -2455,35 +2695,28 @@ OUString ScTabView::getRowColumnHeaders(const Rectangle& rRectangle) /// 1) compute start and end columns - nTotalPixels = 0; if (rRectangle.Left() < rRectangle.Right()) { - long nLeftBoundPx = rRectangle.Left() / TWIPS_PER_PIXEL; - long nRightBoundPx = rRectangle.Right() / TWIPS_PER_PIXEL; - nEndCol = MAXCOL; - for (SCCOL nCol = 0; nCol <= MAXCOL; ++nCol) - { - if (nTotalPixels > nRightBoundPx) - { - nEndCol = nCol; - break; - } + SAL_INFO("sc.lok.header", "Column Header: compute start/end columns."); + long nRectLeftPx = rRectangle.Left() / TWIPS_PER_PIXEL; + long nRectRightPx = rRectangle.Right() / TWIPS_PER_PIXEL; - const sal_uInt16 nSize = pDoc->GetColWidth(nCol, aViewData.GetTabNo()); - const long nSizePx = ScViewData::ToPixel(nSize, 1.0 / TWIPS_PER_PIXEL); - nTotalPixels += nSizePx; - if (nTotalPixels < nLeftBoundPx) - { - nStartCol = nCol; - continue; - } - } + const auto& rLeftNearest = aViewData.GetLOKWidthHelper().getNearestByPosition(nRectLeftPx); + const auto& rRightNearest = aViewData.GetLOKWidthHelper().getNearestByPosition(nRectRightPx); + + BoundsProvider aBoundingColsProvider(pDoc, nTab); + aBoundingColsProvider.Compute(rLeftNearest, rRightNearest, nRectLeftPx, nRectRightPx); + aBoundingColsProvider.EnlargeBy(2); + aBoundingColsProvider.GetStartIndexAndPosition(nStartCol, nStartWidthPx); + aBoundingColsProvider.GetEndIndexAndPosition(nEndCol, nEndWidthPx); - nStartCol -= 1; - nEndCol += 2; + aViewData.GetLOKWidthHelper().removeByIndex(mnLOKStartHeaderCol); + aViewData.GetLOKWidthHelper().removeByIndex(mnLOKEndHeaderCol); + aViewData.GetLOKWidthHelper().insert(nStartCol, nStartWidthPx); + aViewData.GetLOKWidthHelper().insert(nEndCol, nEndWidthPx); - if (nStartCol < 0) nStartCol = 0; - if (nEndCol > MAXCOL) nEndCol = MAXCOL; + mnLOKStartHeaderCol = nStartCol; + mnLOKEndHeaderCol = nEndCol; } aBuffer.ensureCapacity( aBuffer.getCapacity() + (50 * (nEndCol - nStartCol + 1)) ); @@ -2531,44 +2764,31 @@ OUString ScTabView::getRowColumnHeaders(const Rectangle& rRectangle) } } - /// 3) create string data for columns aBuffer.append("\"columns\": [\n"); - bool bFirstCol = true; - if (nStartCol == 0 && nStartCol != nEndCol ) + nTotalPixels = aViewData.GetLOKWidthHelper().getPosition(nStartCol); + SAL_INFO("sc.lok.header", "Col Header: [create string data for cols]: start col: " + << nStartRow << " start width: " << nTotalPixels); + + if (nStartCol != nEndCol) { - aBuffer.append("{ \"text\": \"").append("0").append("\", "); - aBuffer.append("\"size\": \"").append(OUString::number(0)).append("\" }"); - bFirstCol = false; + OUString aText = OUString::number(nStartCol + 1); + aBuffer.append("{ \"text\": \"").append(aText).append("\", "); + aBuffer.append("\"size\": \"").append(OUString::number(nTotalPixels * TWIPS_PER_PIXEL)).append("\" }"); } - nTotalPixels = 0; - for (SCCOL nCol = 0; nCol < nEndCol; ++nCol) + for (SCCOL nCol = nStartCol + 1; nCol < nEndCol; ++nCol) { // nSize will be 0 for hidden columns. - const sal_uInt16 nSize = pDoc->GetColWidth(nCol, aViewData.GetTabNo()); - const long nSizePx = ScViewData::ToPixel(nSize, 1.0 / TWIPS_PER_PIXEL); + const long nSizePx = lcl_GetColWidthPx(pDoc, nCol, nTab); nTotalPixels += nSizePx; - if (nCol < nStartCol) - continue; - OUString aText = pColBar[SC_SPLIT_LEFT]->GetEntryText(nCol); - - if (!bFirstCol) - { - aBuffer.append(", "); - } - else - { - aText = OUString::number(nStartCol + 1); - } - + aBuffer.append(", "); aBuffer.append("{ \"text\": \"").append(aText).append("\", "); aBuffer.append("\"size\": \"").append(OUString::number(nTotalPixels * TWIPS_PER_PIXEL)).append("\" }"); - bFirstCol = false; } aBuffer.append("]"); -- cgit v1.2.3