/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <vcl/help.hxx>

#include <tabview.hxx>
#include <tabvwsh.hxx>
#include <document.hxx>
#include <docsh.hxx>
#include <scmod.hxx>
#include <gridwin.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <inputhdl.hxx>

// ---  Referenz-Eingabe / Fill-Cursor

void ScTabView::HideTip()
{
    if ( nTipVisible )
    {
        ScSplitPos eWhich = aViewData.GetActivePart();
        vcl::Window* pWin = pGridWin[eWhich];
        Help::HidePopover(pWin, nTipVisible);
        nTipVisible = nullptr;
        aTipRectangle = tools::Rectangle();
        nTipAlign = QuickHelpFlags::NONE;
        sTipString.clear();
        sTopParent.clear();
    }
}

void ScTabView::ShowRefTip()
{
    bool bDone = false;
    if ( aViewData.GetRefType() == SC_REFTYPE_REF && Help::IsQuickHelpEnabled() )
    {
        SCCOL nStartX = aViewData.GetRefStartX();
        SCROW nStartY = aViewData.GetRefStartY();
        SCCOL nEndX   = aViewData.GetRefEndX();
        SCROW nEndY   = aViewData.GetRefEndY();
        if ( nEndX != nStartX || nEndY != nStartY )     // not for a single cell
        {
            bool bLeft = ( nEndX < nStartX );
            bool bTop  = ( nEndY < nStartY );
            PutInOrder( nStartX, nEndX );
            PutInOrder( nStartY, nEndY );
            SCCOL nCols = nEndX+1-nStartX;
            SCROW nRows = nEndY+1-nStartY;

            OUString aHelp = ScResId( STR_QUICKHELP_REF );
            aHelp = aHelp.replaceFirst("%1", OUString::number(nRows) );
            aHelp = aHelp.replaceFirst("%2", OUString::number(nCols) );

            ScSplitPos eWhich = aViewData.GetActivePart();
            vcl::Window* pWin = pGridWin[eWhich];
            if ( pWin )
            {
                Point aStart = aViewData.GetScrPos( nStartX, nStartY, eWhich );
                Point aEnd = aViewData.GetScrPos( nEndX+1, nEndY+1, eWhich );

                Point aPos( bLeft ? aStart.X() : ( aEnd.X() + 3 ),
                            bTop ? aStart.Y() : ( aEnd.Y() + 3 ) );
                QuickHelpFlags nFlags = ( bLeft ? QuickHelpFlags::Right : QuickHelpFlags::Left ) |
                                ( bTop ? QuickHelpFlags::Bottom : QuickHelpFlags::Top );

                // not over the edited formula
                if ( !bTop && aViewData.HasEditView( eWhich ) &&
                        nEndY+1 == aViewData.GetEditViewRow() )
                {
                    //  then align at the upper border of edited cell
                    aPos.AdjustY( -2 );      // the three from above
                    nFlags = ( nFlags & ~QuickHelpFlags::Top ) | QuickHelpFlags::Bottom;
                }

                tools::Rectangle aRect( pWin->OutputToScreenPixel( aPos ), Size(1,1) );

                // Test if changed.
                if (!nTipVisible || nFlags != nTipAlign || aRect != aTipRectangle || sTipString != aHelp || sTopParent != pWin)
                {
                    HideTip();
                    nTipVisible = Help::ShowPopover(pWin, aRect, aHelp, nFlags);
                    aTipRectangle = aRect;
                    nTipAlign = nFlags;
                    sTipString = aHelp;
                    sTopParent = pWin;
                }
                bDone = true;
            }
        }
    }

    if (!bDone)
        HideTip();
}

void ScTabView::StopRefMode()
{
    if (aViewData.IsRefMode())
    {
        aViewData.SetRefMode( false, SC_REFTYPE_NONE );

        HideTip();
        UpdateShrinkOverlay();

        if ( aViewData.GetTabNo() >= aViewData.GetRefStartZ() &&
                aViewData.GetTabNo() <= aViewData.GetRefEndZ() )
        {
            ScDocument& rDoc = aViewData.GetDocument();
            SCCOL nStartX = aViewData.GetRefStartX();
            SCROW nStartY = aViewData.GetRefStartY();
            SCCOL nEndX = aViewData.GetRefEndX();
            SCROW nEndY = aViewData.GetRefEndY();
            if ( nStartX == nEndX && nStartY == nEndY )
                rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, aViewData.GetTabNo() );

            PaintArea( nStartX,nStartY,nEndX,nEndY, ScUpdateMode::Marks );
        }

        pSelEngine->Reset();
        pSelEngine->SetAddMode( false );        //! shouldn't that happen during reset?

        ScSplitPos eOld = pSelEngine->GetWhich();
        ScSplitPos eNew = aViewData.GetActivePart();
        if ( eNew != eOld )
        {
            pSelEngine->SetWindow( pGridWin[ eNew ] );
            pSelEngine->SetWhich( eNew );
            pSelEngine->SetVisibleArea( tools::Rectangle(Point(),
                                        pGridWin[eNew]->GetOutputSizePixel()) );
            pGridWin[eOld]->MoveMouseStatus(*pGridWin[eNew]);
        }
    }

    //  AlignToCursor(SC_FOLLOW_NONE): Only switch active part.
    //  This must also be done if no RefMode was active (for RangeFinder dragging),
    //  but if RefMode was set, AlignToCursor must be after SelectionEngine reset,
    //  so the SelectionEngine SetWindow call from AlignToCursor doesn't capture
    //  the mouse again when called from Tracking/MouseButtonUp (#94562#).
    AlignToCursor( aViewData.GetCurX(), aViewData.GetCurY(), SC_FOLLOW_NONE );
}

void ScTabView::DoneRefMode( bool bContinue )
{
    ScDocument& rDoc = aViewData.GetDocument();
    if ( aViewData.GetRefType() == SC_REFTYPE_REF && bContinue )
        SC_MOD()->AddRefEntry();

    bool bWasRef = aViewData.IsRefMode();
    aViewData.SetRefMode( false, SC_REFTYPE_NONE );

    HideTip();
    UpdateShrinkOverlay();

    //  Paint:
    if ( bWasRef && aViewData.GetTabNo() >= aViewData.GetRefStartZ() &&
                    aViewData.GetTabNo() <= aViewData.GetRefEndZ() )
    {
        SCCOL nStartX = aViewData.GetRefStartX();
        SCROW nStartY = aViewData.GetRefStartY();
        SCCOL nEndX = aViewData.GetRefEndX();
        SCROW nEndY = aViewData.GetRefEndY();
        if ( nStartX == nEndX && nStartY == nEndY )
            rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, aViewData.GetTabNo() );

        PaintArea( nStartX,nStartY,nEndX,nEndY, ScUpdateMode::Marks );
    }
}

void ScTabView::UpdateRef( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ )
{
    ScDocument& rDoc = aViewData.GetDocument();

    if (!aViewData.IsRefMode())
    {
        //  This will happen, when first at a reference dialog with Control in
        //  the table is clicked. Then append the new reference to the old content:

        ScModule* pScMod = SC_MOD();
        if (pScMod->IsFormulaMode())
            pScMod->AddRefEntry();

        InitRefMode( nCurX, nCurY, nCurZ, SC_REFTYPE_REF );
    }

    if ( nCurX != aViewData.GetRefEndX() || nCurY != aViewData.GetRefEndY() ||
         nCurZ != aViewData.GetRefEndZ() )
    {
        ScMarkData& rMark = aViewData.GetMarkData();
        SCTAB nTab = aViewData.GetTabNo();

        SCCOL nStartX = aViewData.GetRefStartX();
        SCROW nStartY = aViewData.GetRefStartY();
        SCCOL nEndX = aViewData.GetRefEndX();
        SCROW nEndY = aViewData.GetRefEndY();
        if ( nStartX == nEndX && nStartY == nEndY )
            rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab );
        ScUpdateRect aRect( nStartX, nStartY, nEndX, nEndY );

        if (rDoc.HasAttrib(nCurX, nCurY, nCurZ, HasAttrFlags::Merged))
            rDoc.ExtendMerge(nStartX, nStartY, nCurX, nCurY, nCurZ);

        aViewData.SetRefEnd( nCurX, nCurY, nCurZ );

        nStartX = aViewData.GetRefStartX();
        nStartY = aViewData.GetRefStartY();
        nEndX = aViewData.GetRefEndX();
        nEndY = aViewData.GetRefEndY();
        if ( nStartX == nEndX && nStartY == nEndY )
            rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab );
        aRect.SetNew( nStartX, nStartY, nEndX, nEndY );

        ScRefType eType = aViewData.GetRefType();
        if ( eType == SC_REFTYPE_REF )
        {
            if ((nStartX > nEndX || nStartY > nEndY) &&
                rDoc.HasAttrib(nStartX, nStartY, nTab, HasAttrFlags::Merged))
                rDoc.ExtendMerge( nStartX, nStartY, nStartX, nStartY, nTab );

            ScRange aRef(
                    nStartX, nStartY, aViewData.GetRefStartZ(),
                    nEndX, nEndY, aViewData.GetRefEndZ() );
            SC_MOD()->SetReference( aRef, rDoc, &rMark );
            ShowRefTip();
        }
        else if ( eType == SC_REFTYPE_EMBED_LT || eType == SC_REFTYPE_EMBED_RB )
        {
            PutInOrder(nStartX,nEndX);
            PutInOrder(nStartY,nEndY);
            rDoc.SetEmbedded( ScRange(nStartX,nStartY,nTab, nEndX,nEndY,nTab) );
            ScDocShell* pDocSh = aViewData.GetDocShell();
            pDocSh->UpdateOle( aViewData, true );
            pDocSh->SetDocumentModified();
        }

        SCCOL nPaintStartX;
        SCROW nPaintStartY;
        SCCOL nPaintEndX;
        SCROW nPaintEndY;
        if (aRect.GetDiff( nPaintStartX, nPaintStartY, nPaintEndX, nPaintEndY ))
            PaintArea( nPaintStartX, nPaintStartY, nPaintEndX, nPaintEndY, ScUpdateMode::Marks );

        ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl();
        if (pInputHandler)
        {
            pInputHandler->UpdateLokReferenceMarks();
        }
    }

    //  autocomplete for Auto-Fill
    if ( !(aViewData.GetRefType() == SC_REFTYPE_FILL && Help::IsQuickHelpEnabled()) )
        return;

    vcl::Window* pWin = GetActiveWin();
    if ( !pWin )
        return;

    OUString aHelpStr;
    ScRange aMarkRange;
    aViewData.GetSimpleArea( aMarkRange );
    SCCOL nEndX = aViewData.GetRefEndX();
    SCROW nEndY = aViewData.GetRefEndY();
    ScRange aDelRange;
    if ( aViewData.GetFillMode() == ScFillMode::MATRIX && !(nScFillModeMouseModifier & KEY_MOD1) )
    {
        aHelpStr = ScResId( STR_TIP_RESIZEMATRIX );
        SCCOL nCols = nEndX + 1 - aViewData.GetRefStartX(); // order is right
        SCROW nRows = nEndY + 1 - aViewData.GetRefStartY();
        aHelpStr = aHelpStr.replaceFirst("%1", OUString::number(nRows) );
        aHelpStr = aHelpStr.replaceFirst("%2", OUString::number(nCols) );
    }
    else if ( aViewData.GetDelMark( aDelRange ) )
        aHelpStr = ScResId( STR_QUICKHELP_DELETE );
    else if ( nEndX != aMarkRange.aEnd.Col() || nEndY != aMarkRange.aEnd.Row() )
        aHelpStr = rDoc.GetAutoFillPreview( aMarkRange, nEndX, nEndY );

    if (!aHelpStr.getLength())
        return;

    //  depending on direction the upper or lower corner
    SCCOL nAddX = ( nEndX >= aMarkRange.aEnd.Col() ) ? 1 : 0;
    SCROW nAddY = ( nEndY >= aMarkRange.aEnd.Row() ) ? 1 : 0;
    Point aPos = aViewData.GetScrPos( nEndX+nAddX, nEndY+nAddY, aViewData.GetActivePart() );
    aPos.AdjustX(8 );
    aPos.AdjustY(4 );
    aPos = pWin->OutputToScreenPixel( aPos );
    tools::Rectangle aRect( aPos, aPos );
    QuickHelpFlags nAlign = QuickHelpFlags::Left|QuickHelpFlags::Top;
    if (!nTipVisible || nAlign != nTipAlign || aRect != aTipRectangle || sTipString != aHelpStr || sTopParent != pWin)
    {
        HideTip();
        nTipVisible = Help::ShowPopover(pWin, aRect, aHelpStr, nAlign);
        aTipRectangle = aRect;
        nTipAlign = nAlign;
        sTipString = aHelpStr;
        sTopParent = pWin;
    }
}

void ScTabView::InitRefMode( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ, ScRefType eType )
{
    ScDocument& rDoc = aViewData.GetDocument();
    ScMarkData& rMark = aViewData.GetMarkData();
    if (aViewData.IsRefMode())
        return;

    aViewData.SetRefMode( true, eType );
    aViewData.SetRefStart( nCurX, nCurY, nCurZ );
    aViewData.SetRefEnd( nCurX, nCurY, nCurZ );

    if (nCurZ == aViewData.GetTabNo())
    {
        SCCOL nStartX = nCurX;
        SCROW nStartY = nCurY;
        SCCOL nEndX = nCurX;
        SCROW nEndY = nCurY;
        rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, aViewData.GetTabNo() );

        //! draw only markings over content!
        PaintArea( nStartX,nStartY,nEndX,nEndY, ScUpdateMode::Marks );

        //  SetReference without Merge-Adjustment
        ScRange aRef( nCurX,nCurY,nCurZ, nCurX,nCurY,nCurZ );
        SC_MOD()->SetReference( aRef, rDoc, &rMark );
    }

    ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl();
    if (pInputHandler)
    {
        pInputHandler->UpdateLokReferenceMarks();
    }
}

void ScTabView::SetScrollBar( ScrollAdaptor& rScroll, tools::Long nRangeMax, tools::Long nVisible, tools::Long nPos, bool bLayoutRTL )
{
    if ( nVisible == 0 )
        nVisible = 1;       // #i59893# don't use visible size 0

    //  RTL layout uses a negative range to simulate a mirrored scroll bar.
    //  SetScrollBar/GetScrollBarPos hide this so outside of these functions normal cell
    //  addresses can be used.
    if ( bLayoutRTL )
    {
        rScroll.SetRange( Range( -nRangeMax, 0 ) );
        rScroll.SetVisibleSize( nVisible );
        rScroll.SetThumbPos( -nPos - nVisible );
    }
    else
    {
        rScroll.SetRange( Range( 0, nRangeMax ) );
        rScroll.SetVisibleSize( nVisible );
        rScroll.SetThumbPos( nPos );
    }

    // Related: tdf#93352 always disable RTL for scrollbars
    // Enabling RTL causes the following bugs when clicking or
    // dragging the mouse in scrollbars in Calc's RTL UI:
    // - Click or drag events get mirrored so you must click or
    //   drag in unexpected locations to move the scrollbar thumb
    //   in the desired direction
    // - Repeatedly dragging the scrollbar thumb leftward can only
    //   move no highter than the R, S, or T columns
    rScroll.EnableRTL( false );

    // Related: tdf#93352 swap arrows if layout is RTL
    // We cannot use EnableRTL(true) to signal that the arrows
    // should be swapped (see comment above) so explicitly enable
    // or disable arrow swapping.
    rScroll.SetSwapArrows( bLayoutRTL );
}

tools::Long ScTabView::GetScrollBarPos( const ScrollAdaptor& rScroll, bool bLayoutRTL )
{
    if ( bLayoutRTL )
        return -rScroll.GetThumbPos() - rScroll.GetVisibleSize();
    else
        return rScroll.GetThumbPos();
}

//  UpdateScrollBars - set visible area and scroll width of scroll bars
static tools::Long lcl_UpdateBar( ScrollAdaptor& rScroll, SCCOLROW nSize )        // Size = (complete) cells
{
    tools::Long nOldPos;
    tools::Long nNewPos;

    nOldPos = rScroll.GetThumbPos();
    rScroll.SetPageSize( static_cast<tools::Long>(nSize) );
    nNewPos = rScroll.GetThumbPos();
#ifndef UNX
    rScroll.SetPageSize( 1 );               // always possible !
#endif

    return nNewPos - nOldPos;
}

static tools::Long lcl_GetScrollRange( SCCOLROW nDocEnd, SCCOLROW nPos, SCCOLROW nVis, SCCOLROW nMax, SCCOLROW nStart )
{
    // get the end (positive) of a scroll bar range that always starts at 0

    ++nVis;
    ++nMax;     // for partially visible cells
    SCCOLROW nEnd = std::max(nDocEnd, static_cast<SCCOLROW>(nPos+nVis)) + nVis;
    if (nEnd > nMax)
        nEnd = nMax;

    return ( nEnd - nStart );       // for range starting at 0
}

void ScTabView::UpdateScrollBars( HeaderType eHeaderType )
{
    ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), eHeaderType, GetViewData().GetTabNo());

    tools::Long        nDiff;
    bool        bTop =   ( aViewData.GetVSplitMode() != SC_SPLIT_NONE );
    bool        bRight = ( aViewData.GetHSplitMode() != SC_SPLIT_NONE );
    ScDocument& rDoc = aViewData.GetDocument();
    SCTAB       nTab = aViewData.GetTabNo();
    bool        bLayoutRTL = rDoc.IsLayoutRTL( nTab );
    SCCOL       nUsedX;
    SCROW       nUsedY;
    rDoc.GetTableArea( nTab, nUsedX, nUsedY );     //! cached !!!!!!!!!!!!!!!

    SCCOL nVisXL = 0;
    SCCOL nVisXR = 0;
    SCROW nVisYB = 0;
    SCROW nVisYT = 0;

    SCCOL nStartX = 0;
    SCROW nStartY = 0;
    if (aViewData.GetHSplitMode()==SC_SPLIT_FIX)
        nStartX = aViewData.GetFixPosX();
    if (aViewData.GetVSplitMode()==SC_SPLIT_FIX)
        nStartY = aViewData.GetFixPosY();

    nVisXL = aViewData.VisibleCellsX( SC_SPLIT_LEFT );
    tools::Long nMaxXL = lcl_GetScrollRange( nUsedX, aViewData.GetPosX(SC_SPLIT_LEFT), nVisXL, rDoc.MaxCol(), 0 );
    SetScrollBar( *aHScrollLeft, nMaxXL, nVisXL, aViewData.GetPosX( SC_SPLIT_LEFT ), bLayoutRTL );

    nVisYB = aViewData.VisibleCellsY( SC_SPLIT_BOTTOM );
    tools::Long nMaxYB = lcl_GetScrollRange( nUsedY, aViewData.GetPosY(SC_SPLIT_BOTTOM), nVisYB, rDoc.MaxRow(), nStartY );
    SetScrollBar( *aVScrollBottom, nMaxYB, nVisYB, aViewData.GetPosY( SC_SPLIT_BOTTOM ) - nStartY, false );

    if (bRight)
    {
        nVisXR = aViewData.VisibleCellsX( SC_SPLIT_RIGHT );
        tools::Long nMaxXR = lcl_GetScrollRange( nUsedX, aViewData.GetPosX(SC_SPLIT_RIGHT), nVisXR, rDoc.MaxCol(), nStartX );
        SetScrollBar( *aHScrollRight, nMaxXR, nVisXR, aViewData.GetPosX( SC_SPLIT_RIGHT ) - nStartX, bLayoutRTL );
    }

    if (bTop)
    {
        nVisYT = aViewData.VisibleCellsY( SC_SPLIT_TOP );
        tools::Long nMaxYT = lcl_GetScrollRange( nUsedY, aViewData.GetPosY(SC_SPLIT_TOP), nVisYT, rDoc.MaxRow(), 0 );
        SetScrollBar( *aVScrollTop, nMaxYT, nVisYT, aViewData.GetPosY( SC_SPLIT_TOP ), false );
    }

    //      test the range

    nDiff = lcl_UpdateBar( *aHScrollLeft, nVisXL );
    if (nDiff) ScrollX( nDiff, SC_SPLIT_LEFT );
    if (bRight)
    {
        nDiff = lcl_UpdateBar( *aHScrollRight, nVisXR );
        if (nDiff) ScrollX( nDiff, SC_SPLIT_RIGHT );
    }

    nDiff = lcl_UpdateBar( *aVScrollBottom, nVisYB );
    if (nDiff) ScrollY( nDiff, SC_SPLIT_BOTTOM );
    if (bTop)
    {
        nDiff = lcl_UpdateBar( *aVScrollTop, nVisYT );
        if (nDiff) ScrollY( nDiff, SC_SPLIT_TOP );
    }

    //      set visible area for online spelling

    if ( aViewData.IsActive() )
    {
        if (UpdateVisibleRange())
            SC_MOD()->AnythingChanged();                // if visible area has changed
    }
}

#ifndef HDR_SLIDERSIZE
#define HDR_SLIDERSIZE      2
#endif

void ScTabView::InvertHorizontal( ScVSplitPos eWhich, tools::Long nDragPos )
{
    for (sal_uInt16 i=0; i<4; i++)
        if (WhichV(static_cast<ScSplitPos>(i))==eWhich)
        {
            ScGridWindow* pWin = pGridWin[i].get();
            if (pWin)
            {
                tools::Rectangle aRect( 0,nDragPos, pWin->GetOutputSizePixel().Width()-1,nDragPos+HDR_SLIDERSIZE-1 );
                pWin->PaintImmediately();
                pWin->DoInvertRect( aRect );    // Pixel
            }
        }
}

void ScTabView::InvertVertical( ScHSplitPos eWhich, tools::Long nDragPos )
{
    for (sal_uInt16 i=0; i<4; i++)
        if (WhichH(static_cast<ScSplitPos>(i))==eWhich)
        {
            ScGridWindow* pWin = pGridWin[i].get();
            if (pWin)
            {
                tools::Rectangle aRect( nDragPos,0, nDragPos+HDR_SLIDERSIZE-1,pWin->GetOutputSizePixel().Height()-1 );
                pWin->PaintImmediately();
                pWin->DoInvertRect( aRect );    // Pixel
            }
        }
}

void ScTabView::InterpretVisible()
{
    //  make sure all visible cells are interpreted,
    //  so the next paint will not execute a macro function

    ScDocument& rDoc = aViewData.GetDocument();
    if ( !rDoc.GetAutoCalc() )
        return;

    SCTAB nTab = aViewData.GetTabNo();
    for (sal_uInt16 i=0; i<4; i++)
    {
        //  rely on gridwin pointers to find used panes
        //  no IsVisible test in case the whole view is not yet shown

        if (pGridWin[i])
        {
            ScHSplitPos eHWhich = WhichH( ScSplitPos(i) );
            ScVSplitPos eVWhich = WhichV( ScSplitPos(i) );

            SCCOL   nX1 = rDoc.SanitizeCol( aViewData.GetPosX( eHWhich ));
            SCROW   nY1 = rDoc.SanitizeRow( aViewData.GetPosY( eVWhich ));
            SCCOL   nX2 = rDoc.SanitizeCol( nX1 + aViewData.VisibleCellsX( eHWhich ));
            SCROW   nY2 = rDoc.SanitizeRow( nY1 + aViewData.VisibleCellsY( eVWhich ));

            rDoc.InterpretDirtyCells(ScRange(nX1, nY1, nTab, nX2, nY2, nTab));
        }
    }

    // #i65047# repaint during the above loop may have set the bNeedsRepaint flag
    CheckNeedsRepaint();
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */