Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/sc/source/ui/view/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 122 kB image not shown  

Quelle  tabview3.cxx   Sprache: C

 
/* -*- 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 <officecfg/Office/Calc.hxx>
#include <rangelst.hxx>
#include <scitems.hxx>

#include <editeng/editview.hxx>
#include <editeng/StripPortionsHelper.hxx>
#include <svx/fmshell.hxx>
#include <svx/sdr/overlay/overlaymanager.hxx>
#include <svx/svdoole2.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/lokhelper.hxx>
#include <sfx2/viewfrm.hxx>
#include <vcl/cursor.hxx>
#include <vcl/uitest/logger.hxx>
#include <vcl/uitest/eventdescription.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>

#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <svx/sdr/overlay/overlayselection.hxx>
#include <svtools/optionsdrawinglayer.hxx>

#include <IAnyRefDialog.hxx>
#include <tabview.hxx>
#include <tabvwsh.hxx>
#include <docsh.hxx>
#include <gridwin.hxx>
#include <olinewin.hxx>
#include <overlayobject.hxx>
#include <colrowba.hxx>
#include <tabcont.hxx>
#include <scmod.hxx>
#include <sc.hrc>
#include <viewutil.hxx>
#include <editutil.hxx>
#include <inputhdl.hxx>
#include <inputwin.hxx>
#include <validat.hxx>
#include <inputopt.hxx>
#include <rfindlst.hxx>
#include <hiranges.hxx>
#include <viewuno.hxx>
#include <dpobject.hxx>
#include <seltrans.hxx>
#include <fillinfo.hxx>
#include <rangeutl.hxx>
#include <client.hxx>
#include <tabprotection.hxx>
#include <spellcheckcontext.hxx>
#include <markdata.hxx>
#include <formula/FormulaCompiler.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/scopeguard.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <output.hxx>

#include <utility>

#include <com/sun/star/chart2/data/HighlightedRange.hpp>

namespace
{

ScRange lcl_getSubRangeByIndex( const ScRange& rRange, sal_Int32 nIndex )
{
    ScAddress aResult( rRange.aStart );

    SCCOL nWidth = rRange.aEnd.Col() - rRange.aStart.Col() + 1;
    SCROW nHeight = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
    SCTAB nDepth = rRange.aEnd.Tab() - rRange.aStart.Tab() + 1;
    if( (nWidth > 0) && (nHeight > 0) && (nDepth > 0) )
    {
        // row by row from first to last sheet
        sal_Int32 nArea = nWidth * nHeight;
        aResult.IncCol( static_cast< SCCOL >( nIndex % nWidth ) );
        aResult.IncRow( static_cast< SCROW >( (nIndex % nArea) / nWidth ) );
        aResult.IncTab( static_cast< SCTAB >( nIndex / nArea ) );
        if( !rRange.Contains( aResult ) )
            aResult = rRange.aStart;
    }

    return ScRange( aResult );
}

// anonymous namespace

using namespace com::sun::star;

ScExtraEditViewManager::~ScExtraEditViewManager()
{
    DBG_ASSERT(nTotalWindows == 0, "ScExtraEditViewManager dtor: some out window has not yet been removed!");
}

inline void ScExtraEditViewManager::Add(SfxViewShell* pViewShell, ScSplitPos eWhich)
{
    Apply<Adder>(pViewShell, eWhich);
}

inline void ScExtraEditViewManager::Remove(SfxViewShell* pViewShell, ScSplitPos eWhich)
{
    Apply<Remover>(pViewShell, eWhich);
}


template<ScExtraEditViewManager::ModifierTagType ModifierTag>
void ScExtraEditViewManager::Apply(SfxViewShell* pViewShell, ScSplitPos eWhich)
{
    ScTabViewShell* pOtherViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
    if (pOtherViewShell == nullptr || pOtherViewShell == mpThisViewShell)
        return;

    mpOtherEditView = pOtherViewShell->GetViewData().GetEditView(eWhich);
    if (mpOtherEditView != nullptr)
    {
        for (int i = 0; i < 4; ++i)
        {
            ScGridWindow* pWin = mpGridWin[i].get();
            if (pWin != nullptr)
            {
                Modifier<ModifierTag>(pWin);
            }
        }
    }
}

template<ScExtraEditViewManager::ModifierTagType ModifierTag>
void ScExtraEditViewManager::Modifier(ScGridWindow* /*pWin*/)
{
    (void)this;
    SAL_WARN("sc""ScExtraEditViewManager::Modifier: non-specialized version should not be invoked.");
}

template<>
void ScExtraEditViewManager::Modifier<ScExtraEditViewManager::Adder>(ScGridWindow* pWin)
{
    if (mpOtherEditView->AddOtherViewWindow(pWin))
        ++nTotalWindows;
}

template<>
void ScExtraEditViewManager::Modifier<ScExtraEditViewManager::Remover>(ScGridWindow* pWin)
{
    if (mpOtherEditView->RemoveOtherViewWindow(pWin))
        --nTotalWindows;
}

// ---  public functions

void ScTabView::ClickCursor( SCCOL nPosX, SCROW nPosY, bool bControl )
{
    ScDocument& rDoc = aViewData.GetDocument();
    SCTAB nTab = aViewData.GetTabNo();
    rDoc.SkipOverlapped(nPosX, nPosY, nTab);

    ScModule* mod = ScModule::get();
    bool bRefMode = mod->IsFormulaMode();

    if ( bRefMode )
    {
        DoneRefMode();

        if (bControl)
            mod->AddRefEntry();

        InitRefMode( nPosX, nPosY, nTab, SC_REFTYPE_REF );
    }
    else
    {
        DoneBlockMode( bControl );
        aViewData.ResetOldCursor();
        SetCursor( nPosX, nPosY );
    }
}

void ScTabView::UpdateAutoFillMark(bool bFromPaste)
{
    // single selection or cursor
    ScRange aMarkRange;
    ScMarkType eMarkType = aViewData.GetSimpleArea(aMarkRange);
    bool bMarked = eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED;

    for (sal_uInt16 i = 0; i < 4; i++)
    {
        if (pGridWin[i] && pGridWin[i]->IsVisible())
            pGridWin[i]->UpdateAutoFillMark( bMarked, aMarkRange );
    }

    for (sal_uInt16 i = 0; i < 2; i++)
    {
        if (pColBar[i] && pColBar[i]->IsVisible())
            pColBar[i]->SetMark( bMarked, aMarkRange.aStart.Col(), aMarkRange.aEnd.Col() );
        if (pRowBar[i] && pRowBar[i]->IsVisible())
            pRowBar[i]->SetMark( bMarked, aMarkRange.aStart.Row(), aMarkRange.aEnd.Row() );
    }

    //  selection transfer object is checked together with AutoFill marks,
    //  because it has the same requirement of a single continuous block.
    if (!bFromPaste)
        CheckSelectionTransfer();   // update selection transfer object
}

void ScTabView::FakeButtonUp( ScSplitPos eWhich )
{
    if (pGridWin[eWhich])
        pGridWin[eWhich]->FakeButtonUp();
}

void ScTabView::HideAllCursors()
{
    for (VclPtr<ScGridWindow> & pWin : pGridWin)
    {
        if (pWin && pWin->IsVisible())
        {
            vcl::Cursor* pCur = pWin->GetCursor();
            if (pCur && pCur->IsVisible())
                pCur->Hide();
            pWin->HideCursor();
        }
    }
}

void ScTabView::ShowAllCursors()
{
    for (VclPtr<ScGridWindow> & pWin : pGridWin)
    {
        if (pWin && pWin->IsVisible())
        {
            pWin->ShowCursor();
            pWin->CursorChanged();
        }
    }
}

void ScTabView::ShowCursor()
{
    pGridWin[aViewData.GetActivePart()]->ShowCursor();
    pGridWin[aViewData.GetActivePart()]->CursorChanged();
}

void ScTabView::InvalidateAttribs()
{
    SfxBindings& rBindings = aViewData.GetBindings();

    rBindings.Invalidate( SID_STYLE_APPLY );
    rBindings.Invalidate( SID_STYLE_FAMILY2 );
    rBindings.Invalidate( SID_STYLE_FAMILY3 );

    rBindings.Invalidate( SID_ATTR_CHAR_FONT );
    rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
    rBindings.Invalidate( SID_ATTR_CHAR_COLOR );

    rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
    rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
    rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
    rBindings.Invalidate( SID_ULINE_VAL_NONE );
    rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
    rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
    rBindings.Invalidate( SID_ULINE_VAL_DOTTED );

    rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );

    rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
    rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
    rBindings.Invalidate( SID_SET_SUB_SCRIPT );
    rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
    rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );

    rBindings.Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
    rBindings.Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
    rBindings.Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
    rBindings.Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
    rBindings.Invalidate( SID_NUMBER_TYPE_FORMAT);

    rBindings.Invalidate( SID_ALIGNLEFT );
    rBindings.Invalidate( SID_ALIGNRIGHT );
    rBindings.Invalidate( SID_ALIGNBLOCK );
    rBindings.Invalidate( SID_ALIGNCENTERHOR );

    rBindings.Invalidate( SID_ALIGNTOP );
    rBindings.Invalidate( SID_ALIGNBOTTOM );
    rBindings.Invalidate( SID_ALIGNCENTERVER );

    rBindings.Invalidate( SID_SCATTR_CELLPROTECTION );

    // stuff for sidebar panels
    {
        rBindings.Invalidate( SID_H_ALIGNCELL );
        rBindings.Invalidate( SID_V_ALIGNCELL );
        rBindings.Invalidate( SID_ATTR_ALIGN_INDENT );
        rBindings.Invalidate( SID_FRAME_LINECOLOR );
        rBindings.Invalidate( SID_FRAME_LINESTYLE );
        rBindings.Invalidate( SID_ATTR_BORDER_OUTER );
        rBindings.Invalidate( SID_ATTR_BORDER_INNER );
        rBindings.Invalidate( SID_ATTR_BORDER_DIAG_TLBR );
        rBindings.Invalidate( SID_ATTR_BORDER_DIAG_BLTR );
        rBindings.Invalidate( SID_NUMBER_TYPE_FORMAT );
    }

    rBindings.Invalidate( SID_BACKGROUND_COLOR );

    rBindings.Invalidate( SID_ATTR_ALIGN_LINEBREAK );
    rBindings.Invalidate( SID_NUMBER_FORMAT );

    rBindings.Invalidate( SID_TEXTDIRECTION_LEFT_TO_RIGHT );
    rBindings.Invalidate( SID_TEXTDIRECTION_TOP_TO_BOTTOM );
    rBindings.Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT );
    rBindings.Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT );

    // pseudo slots for Format menu
    rBindings.Invalidate( SID_ALIGN_ANY_HDEFAULT );
    rBindings.Invalidate( SID_ALIGN_ANY_LEFT );
    rBindings.Invalidate( SID_ALIGN_ANY_HCENTER );
    rBindings.Invalidate( SID_ALIGN_ANY_RIGHT );
    rBindings.Invalidate( SID_ALIGN_ANY_JUSTIFIED );
    rBindings.Invalidate( SID_ALIGN_ANY_VDEFAULT );
    rBindings.Invalidate( SID_ALIGN_ANY_TOP );
    rBindings.Invalidate( SID_ALIGN_ANY_VCENTER );
    rBindings.Invalidate( SID_ALIGN_ANY_BOTTOM );

    rBindings.Invalidate( SID_NUMBER_CURRENCY );
    rBindings.Invalidate( SID_NUMBER_SCIENTIFIC );
    rBindings.Invalidate( SID_NUMBER_DATE );
    rBindings.Invalidate( SID_NUMBER_CURRENCY );
    rBindings.Invalidate( SID_NUMBER_PERCENT );
    rBindings.Invalidate( SID_NUMBER_TWODEC );
    rBindings.Invalidate( SID_NUMBER_TIME );
    rBindings.Invalidate( SID_NUMBER_STANDARD );
    rBindings.Invalidate( SID_NUMBER_THOUSANDS );
}

namespace {

void collectUIInformation(std::map<OUString, OUString>&& aParameters)
{
    EventDescription aDescription;
    aDescription.aID = "grid_window";
    aDescription.aAction = "SELECT";
    aDescription.aParameters = std::move(aParameters);
    aDescription.aParent = "MainWindow";
    aDescription.aKeyWord = "ScGridWinUIObject";

    UITestLogger::getInstance().logEvent(aDescription);
}

}

// SetCursor - Cursor, set, draw, update InputWin
// or send reference
// Optimising breaks the functionality

void ScTabView::SetCursor( SCCOL nPosX, SCROW nPosY, bool bNew )
{
    SCCOL nOldX = aViewData.GetCurX();
    SCROW nOldY = aViewData.GetCurY();

    //  DeactivateIP only for MarkListHasChanged

    // FIXME: this is to limit the number of rows handled in the Online
    // to 1000; this will be removed again when the performance
    // bottlenecks are sorted out
    if (comphelper::LibreOfficeKit::isActive())
        nPosY = std::min(nPosY, MAXTILEDROW);

    if ( !(nPosX != nOldX || nPosY != nOldY || bNew) )
    {
        HighlightOverlay();
        return;
    }

    ScTabViewShell* pViewShell = aViewData.GetViewShell();
    bool bRefMode = pViewShell && pViewShell->IsRefInputMode();
    if ( aViewData.HasEditView( aViewData.GetActivePart() ) && !bRefMode ) // 23259 or so
    {
        UpdateInputLine();
    }

    HideAllCursors();

    aViewData.SetCurX( nPosX );
    aViewData.SetCurY( nPosY );

    ShowAllCursors();

    HighlightOverlay();

    CursorPosChanged();

    OUString aCurrAddress = ScAddress(nPosX,nPosY,0).GetColRowString();
    collectUIInformation({{"CELL", aCurrAddress}});

    if (!comphelper::LibreOfficeKit::isActive())
        return;

    if (nPosX <= aViewData.GetMaxTiledCol() - 10 && nPosY <= aViewData.GetMaxTiledRow() - 25)
        return;

    ScDocument& rDoc = aViewData.GetDocument();
    ScDocShell& rDocSh = aViewData.GetDocShell();
    ScModelObj* pModelObj = rDocSh.GetModel();
    Size aOldSize(0, 0);
    if (pModelObj)
        aOldSize = pModelObj->getDocumentSize();

    if (nPosX > aViewData.GetMaxTiledCol() - 10)
        aViewData.SetMaxTiledCol(std::min<SCCOL>(std::max(nPosX, aViewData.GetMaxTiledCol()) + 10, rDoc.MaxCol()));

    if (nPosY > aViewData.GetMaxTiledRow() - 25)
        aViewData.SetMaxTiledRow(std::min<SCROW>(std::max(nPosY, aViewData.GetMaxTiledRow()) + 25,  MAXTILEDROW));

    Size aNewSize(0, 0);
    if (pModelObj)
        aNewSize = pModelObj->getDocumentSize();

    if (pModelObj)
    {
        ScGridWindow* pGridWindow = aViewData.GetActiveWin();
        if (pGridWindow)
        {
            Size aNewSizePx(aNewSize.Width() * aViewData.GetPPTX(), aNewSize.Height() * aViewData.GetPPTY());
            if (aNewSizePx != pGridWindow->GetOutputSizePixel())
                pGridWindow->SetOutputSizePixel(aNewSizePx);
        }
    }

    if (aOldSize == aNewSize)
        return;

    // New area extended to the right of the sheet after last column
    // including overlapping area with aNewRowArea
    tools::Rectangle aNewColArea(aOldSize.getWidth(), 0, aNewSize.getWidth(), aNewSize.getHeight());
    // New area extended to the bottom of the sheet after last row
    // excluding overlapping area with aNewColArea
    tools::Rectangle aNewRowArea(0, aOldSize.getHeight(), aOldSize.getWidth(), aNewSize.getHeight());

    // Only invalidate if spreadsheet extended to the right
    if (aNewColArea.getOpenWidth())
    {
        SfxLokHelper::notifyInvalidation(aViewData.GetViewShell(), &aNewColArea);
    }

    // Only invalidate if spreadsheet extended to the bottom
    if (aNewRowArea.getOpenHeight())
    {
        SfxLokHelper::notifyInvalidation(aViewData.GetViewShell(), &aNewRowArea);
    }

    // Provide size in the payload, so clients don't have to
    // call lok::Document::getDocumentSize().
    std::stringstream ss;
    ss << aNewSize.Width() << ", " << aNewSize.Height();
    OString sSize( ss.str() );
    ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(aViewData.GetViewShell()->GetCurrentDocument());
    SfxLokHelper::notifyDocumentSizeChanged(aViewData.GetViewShell(), sSize, pModel, false);
}

static bool lcl_IsRefDlgActive(SfxViewFrame& rViewFrm)
{
    ScModule* pScMod = ScModule::get();
    if (!pScMod->IsRefDialogOpen())
       return false;

    auto nDlgId = pScMod->GetCurRefDlgId();
    if (!rViewFrm.HasChildWindow(nDlgId))
        return false;

    SfxChildWindow* pChild = rViewFrm.GetChildWindow(nDlgId);
    if (!pChild)
        return false;

    auto xDlgController = pChild->GetController();
    if (!xDlgController || !xDlgController->getDialog()->get_visible())
        return false;

    IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(xDlgController.get());
    return pRefDlg && pRefDlg->IsRefInputMode();
}

void ScTabView::CheckSelectionTransfer()
{
    if ( !aViewData.IsActive() )     // only for active view
        return;

    ScModule* pScMod = ScModule::get();
    ScSelectionTransferObj* pOld = pScMod->GetSelectionTransfer();
    rtl::Reference<ScSelectionTransferObj> pNew = ScSelectionTransferObj::CreateFromView( this );
    if ( !pNew )
        return;

    //  create new selection

    if (pOld)
        pOld->ForgetView();

    pScMod->SetSelectionTransfer( pNew.get() );

    // tdf#124975/tdf#136242 changing the calc selection can trigger removal of the
    // selection of an open RefDlg dialog, so don't inform the
    // desktop clipboard of the changed selection if that dialog is open
    if (!lcl_IsRefDlgActive(aViewData.GetViewShell()->GetViewFrame()))
        pNew->CopyToPrimarySelection();                    // may delete pOld

    // Log the selection change
    ScMarkData& rMark = aViewData.GetMarkData();
    if (rMark.IsMarked())
    {
        const ScRange& aMarkRange = rMark.GetMarkArea();
        OUString aStartAddress =  aMarkRange.aStart.GetColRowString();
        OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
        collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}});
    }
}

// update input row / menus
// CursorPosChanged calls SelectionChanged
// SelectionChanged calls CellContentChanged

void ScTabView::CellContentChanged()
{
    SfxBindings& rBindings = aViewData.GetBindings();

    rBindings.Invalidate( SID_ATTR_SIZE );      // -> show error message
    rBindings.Invalidate( SID_THESAURUS );
    rBindings.Invalidate( SID_HYPERLINK_GETLINK );
    rBindings.Invalidate( SID_ROWCOL_SELCOUNT );

    InvalidateAttribs();                    // attributes updates

    aViewData.GetViewShell()->UpdateInputHandler();
}

void ScTabView::SetTabProtectionSymbol( SCTAB nTab, const bool bProtect )
{
    pTabControl->SetProtectionSymbol( static_cast<sal_uInt16>(nTab)+1, bProtect);
}

void ScTabView::SelectionChanged(bool bFromPaste)
{
    SfxViewFrame& rViewFrame = aViewData.GetViewShell()->GetViewFrame();
    uno::Reference<frame::XController> xController = rViewFrame.GetFrame().GetController();
    if (xController.is())
    {
        ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
        if (pImp)
            pImp->SelectionChanged();
    }

    UpdateAutoFillMark(bFromPaste);   // also calls CheckSelectionTransfer

    SfxBindings& rBindings = aViewData.GetBindings();

    rBindings.Invalidate( SID_CURRENTCELL );    // -> Navigator
    rBindings.Invalidate( SID_AUTO_FILTER );    // -> Menu
    rBindings.Invalidate( FID_NOTE_VISIBLE );
    rBindings.Invalidate( FID_SHOW_NOTE );
    rBindings.Invalidate( FID_HIDE_NOTE );
    rBindings.Invalidate( FID_SHOW_ALL_NOTES );
    rBindings.Invalidate( FID_HIDE_ALL_NOTES );
    rBindings.Invalidate( SID_TOGGLE_NOTES );
    rBindings.Invalidate( SID_DELETE_NOTE );
    rBindings.Invalidate( SID_ROWCOL_SELCOUNT );

        //  functions than may need to be disabled

    rBindings.Invalidate( FID_INS_ROWBRK );
    rBindings.Invalidate( FID_INS_COLBRK );
    rBindings.Invalidate( FID_DEL_ROWBRK );
    rBindings.Invalidate( FID_DEL_COLBRK );
    rBindings.Invalidate( FID_MERGE_ON );
    rBindings.Invalidate( FID_MERGE_OFF );
    rBindings.Invalidate( FID_MERGE_TOGGLE );
    rBindings.Invalidate( SID_AUTOFILTER_HIDE );
    rBindings.Invalidate( SID_UNFILTER );
    rBindings.Invalidate( SID_REIMPORT_DATA );
    rBindings.Invalidate( SID_REFRESH_DBAREA );
    rBindings.Invalidate( SID_OUTLINE_SHOW );
    rBindings.Invalidate( SID_OUTLINE_HIDE );
    rBindings.Invalidate( SID_OUTLINE_REMOVE );
    rBindings.Invalidate( FID_FILL_TO_BOTTOM );
    rBindings.Invalidate( FID_FILL_TO_RIGHT );
    rBindings.Invalidate( FID_FILL_TO_TOP );
    rBindings.Invalidate( FID_FILL_TO_LEFT );
    rBindings.Invalidate( FID_FILL_SERIES );
    rBindings.Invalidate( SID_SCENARIOS );
    rBindings.Invalidate( SID_AUTOFORMAT );
    rBindings.Invalidate( SID_OPENDLG_TABOP );
    rBindings.Invalidate( SID_DATA_SELECT );

    rBindings.Invalidate( SID_CUT );
    rBindings.Invalidate( SID_COPY );
    rBindings.Invalidate( SID_PASTE );
    rBindings.Invalidate( SID_PASTE_SPECIAL );
    rBindings.Invalidate( SID_PASTE_UNFORMATTED );
    rBindings.Invalidate( SID_COPYDELETE );

    rBindings.Invalidate( FID_INS_ROW );
    rBindings.Invalidate( FID_INS_COLUMN );
    rBindings.Invalidate( FID_INS_ROWS_BEFORE );
    rBindings.Invalidate( FID_INS_COLUMNS_BEFORE );
    rBindings.Invalidate( FID_INS_ROWS_AFTER );
    rBindings.Invalidate( FID_INS_COLUMNS_AFTER );
    rBindings.Invalidate( FID_INS_CELL );
    rBindings.Invalidate( FID_INS_CELLSDOWN );
    rBindings.Invalidate( FID_INS_CELLSRIGHT );

    rBindings.Invalidate( FID_CHG_COMMENT );

        // only due to  protect cell:

    rBindings.Invalidate( SID_CELL_FORMAT_RESET );
    rBindings.Invalidate( SID_DELETE );
    rBindings.Invalidate( SID_DELETE_CONTENTS );
    rBindings.Invalidate( FID_DELETE_CELL );
    rBindings.Invalidate( FID_CELL_FORMAT );
    rBindings.Invalidate( SID_ENABLE_HYPHENATION );
    rBindings.Invalidate( SID_INSERT_POSTIT );
    rBindings.Invalidate( SID_CHARMAP );
    rBindings.Invalidate( SID_OPENDLG_FUNCTION );
    rBindings.Invalidate( FID_VALIDATION );
    rBindings.Invalidate( SID_EXTERNAL_SOURCE );
    rBindings.Invalidate( SID_TEXT_TO_COLUMNS );
    rBindings.Invalidate( SID_SORT_ASCENDING );
    rBindings.Invalidate( SID_SORT_DESCENDING );
    rBindings.Invalidate( SID_SELECT_UNPROTECTED_CELLS );
    rBindings.Invalidate( SID_CLEAR_AUTO_FILTER );
    if (!comphelper::LibreOfficeKit::isActive())
        rBindings.Invalidate( SID_LANGUAGE_STATUS );

    if (aViewData.GetViewShell()->HasAccessibilityObjects())
        aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccCursorChanged));

    CellContentChanged();
}

void ScTabView::CursorPosChanged()
{
    bool bRefMode = ScModule::get()->IsFormulaMode();
    if ( !bRefMode ) // check that RefMode works when switching sheets
        aViewData.GetDocShell().Broadcast( SfxHint( SfxHintId::ScKillEditView ) );

    //  Broadcast, so that other Views of the document also switch

    ScDocument& rDocument = aViewData.GetDocument();
    bool bDataPilot = rDocument.HasDataPilotAtPosition(aViewData.GetCurPos());
    aViewData.GetViewShell()->SetPivotShell(bDataPilot);

    if (!bDataPilot)
    {
        bool bSparkline = rDocument.HasSparkline(aViewData.GetCurPos());
        aViewData.GetViewShell()->SetSparklineShell(bSparkline);
    }

    //  UpdateInputHandler now in CellContentChanged

    SelectionChanged();

    aViewData.SetTabStartCol( SC_TABSTART_NONE );
}

namespace {

Point calcHintWindowPosition(
    const Point& rCellPos, const Size& rCellSize, const Size& rFrameWndSize, const Size& rHintWndSize)
{
    const tools::Long nMargin = 20;

    tools::Long nMLeft = rCellPos.X();
    tools::Long nMRight = rFrameWndSize.Width() - rCellPos.X() - rCellSize.Width();
    tools::Long nMTop = rCellPos.Y();
    tools::Long nMBottom = rFrameWndSize.Height() - rCellPos.Y() - rCellSize.Height();

    // First, see if we can fit the entire hint window in the visible region.

    if (nMRight - nMargin >= rHintWndSize.Width())
    {
        // Right margin is wide enough.
        if (rFrameWndSize.Height() >= rHintWndSize.Height())
        {
            // The frame has enough height.  Take it.
            Point aPos = rCellPos;
            aPos.AdjustX(rCellSize.Width() + nMargin );
            if (aPos.Y() + rHintWndSize.Height() > rFrameWndSize.Height())
            {
                // Push the hint window up a bit to make it fit.
                aPos.setY( rFrameWndSize.Height() - rHintWndSize.Height() );
            }
            return aPos;
        }
    }

    if (nMBottom - nMargin >= rHintWndSize.Height())
    {
        // Bottom margin is high enough.
        if (rFrameWndSize.Width() >= rHintWndSize.Width())
        {
            // The frame has enough width.  Take it.
            Point aPos = rCellPos;
            aPos.AdjustY(rCellSize.Height() + nMargin );
            if (aPos.X() + rHintWndSize.Width() > rFrameWndSize.Width())
            {
                // Move the hint window to the left to make it fit.
                aPos.setX( rFrameWndSize.Width() - rHintWndSize.Width() );
            }
            return aPos;
        }
    }

    if (nMLeft - nMargin >= rHintWndSize.Width())
    {
        // Left margin is wide enough.
        if (rFrameWndSize.Height() >= rHintWndSize.Height())
        {
            // The frame is high enough.  Take it.
            Point aPos = rCellPos;
            aPos.AdjustX( -(rHintWndSize.Width() + nMargin) );
            if (aPos.Y() + rHintWndSize.Height() > rFrameWndSize.Height())
            {
                // Push the hint window up a bit to make it fit.
                aPos.setY( rFrameWndSize.Height() - rHintWndSize.Height() );
            }
            return aPos;
        }
    }

    if (nMTop - nMargin >= rHintWndSize.Height())
    {
        // Top margin is high enough.
        if (rFrameWndSize.Width() >= rHintWndSize.Width())
        {
            // The frame is wide enough.  Take it.
            Point aPos = rCellPos;
            aPos.AdjustY( -(rHintWndSize.Height() + nMargin) );
            if (aPos.X() + rHintWndSize.Width() > rFrameWndSize.Width())
            {
                // Move the hint window to the left to make it fit.
                aPos.setX( rFrameWndSize.Width() - rHintWndSize.Width() );
            }
            return aPos;
        }
    }

    // The popup doesn't fit in any direction in its entirety.  Do our best.

    if (nMRight - nMargin >= rHintWndSize.Width())
    {
        // Right margin is good enough.
        Point aPos = rCellPos;
        aPos.AdjustX(nMargin + rCellSize.Width() );
        aPos.setY( 0 );
        return aPos;
    }

    if (nMBottom - nMargin >= rHintWndSize.Height())
    {
        // Bottom margin is good enough.
        Point aPos = rCellPos;
        aPos.AdjustY(nMargin + rCellSize.Height() );
        aPos.setX( 0 );
        return aPos;
    }

    if (nMLeft - nMargin >= rHintWndSize.Width())
    {
        // Left margin is good enough.
        Point aPos = rCellPos;
        aPos.AdjustX( -(rHintWndSize.Width() + nMargin) );
        aPos.setY( 0 );
        return aPos;
    }

    if (nMTop - nMargin >= rHintWndSize.Height())
    {
        // Top margin is good enough.
        Point aPos = rCellPos;
        aPos.AdjustY( -(rHintWndSize.Height() + nMargin) );
        aPos.setX( 0 );
        return aPos;
    }

    // None of the above.  Hopeless.  At least try not to cover the current
    // cell.
    Point aPos = rCellPos;
    aPos.AdjustX(rCellSize.Width() );
    return aPos;
}

}

void ScTabView::TestHintWindow()
{
    //  show input help window and list drop-down button for validity

    mxInputHintOO.reset();

    bool bListValButton = false;
    ScAddress aListValPos;

    ScDocument& rDoc = aViewData.GetDocument();
    const SfxUInt32Item* pItem = rDoc.GetAttr( aViewData.GetCurX(),
                                               aViewData.GetCurY(),
                                               aViewData.GetTabNo(),
                                               ATTR_VALIDDATA );
    if ( pItem->GetValue() )
    {
        const ScValidationData* pData = rDoc.GetValidationEntry( pItem->GetValue() );
        OSL_ENSURE(pData,"ValidationData not found");
        OUString aTitle, aMessage;

        if ( pData && pData->GetInput( aTitle, aMessage ) && !aMessage.isEmpty() )
        {
            ScSplitPos eWhich = aViewData.GetActivePart();
            ScGridWindow* pWin = pGridWin[eWhich].get();
            SCCOL nCol = aViewData.GetCurX();
            SCROW nRow = aViewData.GetCurY();
            Point aPos = aViewData.GetScrPos( nCol, nRow, eWhich );
            Size aWinSize = pWin->GetOutputSizePixel();
            // cursor visible?
            if ( nCol >= aViewData.GetPosX(WhichH(eWhich)) &&
                 nRow >= aViewData.GetPosY(WhichV(eWhich)) &&
                 aPos.X() < aWinSize.Width() && aPos.Y() < aWinSize.Height() )
            {
                const svtools::ColorConfig& rColorCfg = ScModule::get()->GetColorConfig();
                // tdf#156398 use same color combination as used in XclDefaultPalette
                Color aCommentText = rColorCfg.GetColorValue(svtools::FONTCOLOR).nColor;
                Color aCommentBack = rColorCfg.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor;
                // create HintWindow, determines its size by itself
                ScOverlayHint* pOverlay = new ScOverlayHint(aTitle, aMessage,
                                                            aCommentBack, aCommentText,
                                                            pFrameWin->GetFont());

                mxInputHintOO.reset(new sdr::overlay::OverlayObjectList);
                mxInputHintOO->append(std::unique_ptr<sdr::overlay::OverlayObject>(pOverlay));

                Size aHintWndSize = pOverlay->GetSizePixel();
                tools::Long nCellSizeX = 0;
                tools::Long nCellSizeY = 0;
                aViewData.GetMergeSizePixel(nCol, nRow, nCellSizeX, nCellSizeY);

                Point aHintPos = calcHintWindowPosition(
                    aPos, Size(nCellSizeX,nCellSizeY), aWinSize, aHintWndSize);

                pOverlay->SetPos(pWin->PixelToLogic(aHintPos, pWin->GetDrawMapMode()), pWin->GetDrawMapMode());
                for (VclPtr<ScGridWindow> & pWindow : pGridWin)
                {
                    if (!pWindow)
                        continue;
                    if (!pWindow->IsVisible())
                        continue;
                    rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = pWindow->getOverlayManager();
                    if (!xOverlayManager.is())
                        continue;
                    if (pWindow == pWin)
                    {
                        xOverlayManager->add(*pOverlay);
                        pWindow->updateLOKInputHelp(aTitle, aMessage);
                    }
                    else
                    {
                        //tdf#92530 if the help tip doesn't fit into its allocated area in a split window
                        //scenario, then because here we place it into the other split windows as well the
                        //missing portions will be displayed in the other split windows to form an apparent
                        //single tip, albeit "under" the split lines
                        Point aOtherPos(pWindow->ScreenToOutputPixel(pWin->OutputToScreenPixel(aHintPos)));
                        std::unique_ptr<ScOverlayHint> pOtherOverlay(new ScOverlayHint(aTitle, aMessage,
                                                                                       aCommentBack,
                                                                                       aCommentText,
                                                                                       pFrameWin->GetFont()));
                        Point aFooPos(pWindow->PixelToLogic(aOtherPos, pWindow->GetDrawMapMode()));
                        pOtherOverlay->SetPos(aFooPos, pWindow->GetDrawMapMode());
                        xOverlayManager->add(*pOtherOverlay);
                        mxInputHintOO->append(std::move(pOtherOverlay));
                    }
                }
            }
        }

        // list drop-down button
        if ( pData && pData->HasSelectionList() )
        {
            aListValPos.Set( aViewData.GetCurX(), aViewData.GetCurY(), aViewData.GetTabNo() );
            bListValButton = true;
        }
    }

    for (VclPtr<ScGridWindow> const & pWin : pGridWin)
    {
        if (pWin && pWin->IsVisible())
            pWin->UpdateListValPos(bListValButton, aListValPos);
    }
}

bool ScTabView::HasHintWindow() const { return mxInputHintOO != nullptr; }

void ScTabView::RemoveHintWindow()
{
    mxInputHintOO.reset();
}

// find window that should not be over the cursor
static weld::Window* lcl_GetCareWin(SfxViewFrame& rViewFrm)
{
    //! also spelling ??? (then set the member variables when calling)

    // search & replace
    if (rViewFrm.HasChildWindow(SID_SEARCH_DLG))
    {
        SfxChildWindow* pChild = rViewFrm.GetChildWindow(SID_SEARCH_DLG);
        if (pChild)
        {
            auto xDlgController = pChild->GetController();
            if (xDlgController && xDlgController->getDialog()->get_visible())
                return xDlgController->getDialog();
        }
    }

    // apply changes
    if ( rViewFrm.HasChildWindow(FID_CHG_ACCEPT) )
    {
        SfxChildWindow* pChild = rViewFrm.GetChildWindow(FID_CHG_ACCEPT);
        if (pChild)
        {
            auto xDlgController = pChild->GetController();
            if (xDlgController && xDlgController->getDialog()->get_visible())
                return xDlgController->getDialog();
        }
    }

    return nullptr;
}

    // adjust screen with respect to cursor position

void ScTabView::AlignToCursor( SCCOL nCurX, SCROW nCurY, ScFollowMode eMode,
                                const ScSplitPos* pWhich )
{
    // now switch active part here

    ScSplitPos eActive = aViewData.GetActivePart();
    ScHSplitPos eActiveX = WhichH(eActive);
    ScVSplitPos eActiveY = WhichV(eActive);
    bool bHFix = (aViewData.GetHSplitMode() == SC_SPLIT_FIX);
    bool bVFix = (aViewData.GetVSplitMode() == SC_SPLIT_FIX);
    if (bHFix && eActiveX == SC_SPLIT_LEFT && nCurX >= aViewData.GetFixPosX())
    {
        ActivatePart( (eActiveY==SC_SPLIT_TOP) ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT );
        eActiveX = SC_SPLIT_RIGHT;
    }
    if (bVFix && eActiveY == SC_SPLIT_TOP && nCurY >= aViewData.GetFixPosY())
    {
        ActivatePart( (eActiveX==SC_SPLIT_LEFT) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT );
        eActiveY = SC_SPLIT_BOTTOM;
    }

    // actual align

    if ( eMode != SC_FOLLOW_NONE )
    {
        ScSplitPos eAlign;
        if (pWhich)
            eAlign = *pWhich;
        else
            eAlign = aViewData.GetActivePart();
        ScHSplitPos eAlignX = WhichH(eAlign);
        ScVSplitPos eAlignY = WhichV(eAlign);

        SCCOL nDeltaX = aViewData.GetPosX(eAlignX);
        SCROW nDeltaY = aViewData.GetPosY(eAlignY);
        SCCOL nSizeX = aViewData.VisibleCellsX(eAlignX);
        SCROW nSizeY = aViewData.VisibleCellsY(eAlignY);

        tools::Long nCellSizeX;
        tools::Long nCellSizeY;
        if ( nCurX >= 0 && nCurY >= 0 )
            aViewData.GetMergeSizePixel( nCurX, nCurY, nCellSizeX, nCellSizeY );
        else
            nCellSizeX = nCellSizeY = 0;
        Size aScrSize = aViewData.GetScrSize();

        tools::Long nDenom;
        if ( eMode == SC_FOLLOW_JUMP_END && nCurX > aViewData.GetRefStartX()
            && nCurY > aViewData.GetRefStartY() )
            nDenom = 1; // tdf#154271 Selected cell will be at the bottom corner
                        // to maximize the visible/usable area
        else
            nDenom = 2; // Selected cell will be at the center of the screen, so that
                        // it will be visible. This is useful for search results, etc.
        tools::Long nSpaceX = ( aScrSize.Width()  - nCellSizeX ) / nDenom;
        tools::Long nSpaceY = ( aScrSize.Height() - nCellSizeY ) / nDenom;
        //  nSpaceY: desired start position of cell for FOLLOW_JUMP, modified if dialog interferes

        bool bForceNew = false;     // force new calculation of JUMP position (vertical only)

        // VisibleCellsY == CellsAtY( GetPosY( eWhichY ), 1, eWhichY )

        // when for instance a search dialog is open, don't put the cursor behind the dialog
        // if possible, put the row with the cursor above or below the dialog
        //! not if already completely visible

        if ( eMode == SC_FOLLOW_JUMP || eMode == SC_FOLLOW_JUMP_END )
        {
            weld::Window* pCare = lcl_GetCareWin( aViewData.GetViewShell()->GetViewFrame() );
            if (pCare)
            {
                bool bLimit = false;
                tools::Rectangle aDlgPixel;
                Size aWinSize;
                vcl::Window* pWin = GetActiveWin();
                weld::Window* pFrame = pWin ? pWin->GetFrameWeld() : nullptr;
                int x, y, width, height;
                if (pFrame && pCare->get_extents_relative_to(*pFrame, x, y, width, height))
                {
                    aDlgPixel = tools::Rectangle(Point(x, y), Size(width, height));
                    aWinSize = pWin->GetOutputSizePixel();
                    // dos the dialog cover the GridWin?
                    if ( aDlgPixel.Right() >= 0 && aDlgPixel.Left() < aWinSize.Width() )
                    {
                        if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX ||
                             nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY )
                            bLimit = true;          // scroll anyway
                        else
                        {
                            // cursor is on the screen
                            Point aStart = aViewData.GetScrPos( nCurX, nCurY, eAlign );
                            tools::Long nCSX, nCSY;
                            aViewData.GetMergeSizePixel( nCurX, nCurY, nCSX, nCSY );
                            tools::Rectangle aCursor( aStart, Size( nCSX, nCSY ) );
                            if ( aCursor.Overlaps( aDlgPixel ) )
                                bLimit = true;      // cell is covered by the dialog
                        }
                    }
                }

                if (bLimit)
                {
                    bool bBottom = false;
                    tools::Long nTopSpace = aDlgPixel.Top();
                    tools::Long nBotSpace = aWinSize.Height() - aDlgPixel.Bottom();
                    if ( nBotSpace > 0 && nBotSpace > nTopSpace )
                    {
                        tools::Long nDlgBot = aDlgPixel.Bottom();
                        SCCOL nWPosX;
                        SCROW nWPosY;
                        aViewData.GetPosFromPixel( 0,nDlgBot, eAlign, nWPosX, nWPosY );
                        ++nWPosY;   // below the last affected cell

                        SCROW nDiff = nWPosY - nDeltaY;
                        if ( nCurY >= nDiff )           // position can not be negative
                        {
                            nSpaceY = nDlgBot + ( nBotSpace - nCellSizeY ) / 2;
                            bBottom = true;
                            bForceNew = true;
                        }
                    }
                    if ( !bBottom && nTopSpace > 0 )
                    {
                        nSpaceY = ( nTopSpace - nCellSizeY ) / 2;
                        bForceNew = true;
                    }
                }
            }
        }

        SCCOL nNewDeltaX = nDeltaX;
        SCROW nNewDeltaY = nDeltaY;
        bool bDoLine = false;

        switch (eMode)
        {
            case SC_FOLLOW_JUMP:
            case SC_FOLLOW_JUMP_END:
                if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX )
                {
                    nNewDeltaX = nCurX - aViewData.CellsAtX( nCurX, -1, eAlignX, static_cast<sal_uInt16>(nSpaceX) );
                    if (nNewDeltaX < 0)
                        nNewDeltaX = 0;
                    nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
                }
                if ( nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY || bForceNew )
                {
                    nNewDeltaY = nCurY - aViewData.CellsAtY( nCurY, -1, eAlignY, static_cast<sal_uInt16>(nSpaceY) );
                    if (nNewDeltaY < 0)
                        nNewDeltaY = 0;
                    nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
                }
                bDoLine = true;
                break;

            case SC_FOLLOW_LINE:
                bDoLine = true;
                break;

            case SC_FOLLOW_FIX:
                if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX )
                {
                    nNewDeltaX = nDeltaX + nCurX - aViewData.GetCurX();
                    if (nNewDeltaX < 0)
                        nNewDeltaX = 0;
                    nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
                }
                if ( nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY )
                {
                    nNewDeltaY = nDeltaY + nCurY - aViewData.GetCurY();
                    if (nNewDeltaY < 0)
                        nNewDeltaY = 0;
                    nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
                }

                //  like old version of SC_FOLLOW_JUMP:

                if ( nCurX < nNewDeltaX || nCurX >= nNewDeltaX+nSizeX )
                {
                    nNewDeltaX = nCurX - (nSizeX / 2);
                    if (nNewDeltaX < 0)
                        nNewDeltaX = 0;
                    nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
                }
                if ( nCurY < nNewDeltaY || nCurY >= nNewDeltaY+nSizeY )
                {
                    nNewDeltaY = nCurY - (nSizeY / 2);
                    if (nNewDeltaY < 0)
                        nNewDeltaY = 0;
                    nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
                }

                bDoLine = true;
                break;

            case SC_FOLLOW_NONE:
                break;
            default:
                OSL_FAIL("Wrong cursor mode");
                break;
        }

        ScDocument& rDoc = aViewData.GetDocument();
        if (bDoLine)
        {
            while ( nCurX >= nNewDeltaX+nSizeX )
            {
                nNewDeltaX = nCurX-nSizeX+1;
                SCTAB nTab = aViewData.GetTabNo();
                while ( nNewDeltaX < rDoc.MaxCol() && !rDoc.GetColWidth( nNewDeltaX, nTab ) )
                    ++nNewDeltaX;
                nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
            }
            while ( nCurY >= nNewDeltaY+nSizeY )
            {
                nNewDeltaY = nCurY-nSizeY+1;
                SCTAB nTab = aViewData.GetTabNo();
                while ( nNewDeltaY < rDoc.MaxRow() && !rDoc.GetRowHeight( nNewDeltaY, nTab ) )
                    ++nNewDeltaY;
                nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
            }
            if ( nCurX < nNewDeltaX )
                nNewDeltaX = nCurX;
            if ( nCurY < nNewDeltaY )
                nNewDeltaY = nCurY;
        }

        if ( nNewDeltaX != nDeltaX )
            nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
        if (nNewDeltaX+nSizeX-1 > rDoc.MaxCol())
            nNewDeltaX = rDoc.MaxCol()-nSizeX+1;
        if (nNewDeltaX < 0)
            nNewDeltaX = 0;

        if ( nNewDeltaY != nDeltaY )
            nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
        if (nNewDeltaY+nSizeY-1 > rDoc.MaxRow())
            nNewDeltaY = rDoc.MaxRow()-nSizeY+1;
        if (nNewDeltaY < 0)
            nNewDeltaY = 0;

        if ( nNewDeltaX != nDeltaX )
            ScrollX( nNewDeltaX - nDeltaX, eAlignX );
        if ( nNewDeltaY != nDeltaY )
            ScrollY( nNewDeltaY - nDeltaY, eAlignY );
    }

    // switch active part again

    if (bHFix)
        if (eActiveX == SC_SPLIT_RIGHT && nCurX < aViewData.GetFixPosX())
        {
            ActivatePart( (eActiveY==SC_SPLIT_TOP) ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT );
            eActiveX = SC_SPLIT_LEFT;
        }
    if (bVFix)
        if (eActiveY == SC_SPLIT_BOTTOM && nCurY < aViewData.GetFixPosY())
        {
            ActivatePart( (eActiveX==SC_SPLIT_LEFT) ? SC_SPLIT_TOPLEFT : SC_SPLIT_TOPRIGHT );
        }
}

bool ScTabView::SelMouseButtonDown( const MouseEvent& rMEvt )
{
    bool bRet = false;

    // #i3875# *Hack*
    bool bMod1Locked = (aViewData.GetViewShell()->GetLockedModifiers() & KEY_MOD1) != 0;
    aViewData.SetSelCtrlMouseClick( rMEvt.IsMod1() || bMod1Locked );

    if ( pSelEngine )
    {
        bMoveIsShift = rMEvt.IsShift();
        bRet = pSelEngine->SelMouseButtonDown( rMEvt );
        bMoveIsShift = false;
    }

    aViewData.SetSelCtrlMouseClick( false ); // #i3875# *Hack*

    return bRet;
}

    //  MoveCursor - with adjustment of the view section

void ScTabView::MoveCursorAbs( SCCOL nCurX, SCROW nCurY, ScFollowMode eMode,
                               bool bShift, bool bControl, bool bKeepOld, bool bKeepSel )
{
    if (!bKeepOld)
        aViewData.ResetOldCursor();

    ScDocument& rDoc = aViewData.GetDocument();
    // #i123629#
    if( aViewData.GetViewShell()->GetForceFocusOnCurCell() )
        aViewData.GetViewShell()->SetForceFocusOnCurCell( !rDoc.ValidColRow(nCurX, nCurY) );

    if (nCurX < 0) nCurX = 0;
    if (nCurY < 0) nCurY = 0;
    if (nCurX > rDoc.MaxCol()) nCurX = rDoc.MaxCol();
    if (nCurY > rDoc.MaxRow()) nCurY = rDoc.MaxRow();

    // FIXME: this is to limit the number of rows handled in the Online
    // to 1000; this will be removed again when the performance
    // bottlenecks are sorted out
    if (comphelper::LibreOfficeKit::isActive())
        nCurY = std::min(nCurY, MAXTILEDROW);

    HideAllCursors();

    // switch of active now in AlignToCursor

    AlignToCursor( nCurX, nCurY, eMode );

    if (bKeepSel)
    {
        SetCursor( nCurX, nCurY );      // keep selection

        // If the cursor is in existing selection, it's a cursor movement by
        // ENTER or TAB.  If not, then it's a new selection during ADD
        // selection mode.

        const ScMarkData& rMark = aViewData.GetMarkData();
        ScRangeList aSelList;
        rMark.FillRangeListWithMarks(&aSelList, false);
        if (!aSelList.Contains(ScRange(nCurX, nCurY, aViewData.GetTabNo())))
            // Cursor not in existing selection.  Start a new selection.
            DoneBlockMode(true);
    }
    else
    {
        if (!bShift)
        {
            // Remove all marked data on cursor movement unless the Shift is
            // locked or while editing a formula. It is cheaper to check for
            // marks first and then formula mode.
            ScMarkData& rMark = aViewData.GetMarkData();
            bool bMarked = rMark.IsMarked() || rMark.IsMultiMarked();
            if (bMarked && !ScModule::get()->IsFormulaMode())
            {
                rMark.ResetMark();
                DoneBlockMode();
                InitOwnBlockMode( ScRange( nCurX, nCurY, aViewData.GetTabNo()));
                MarkDataChanged();
            }
        }

        bool bSame = ( nCurX == aViewData.GetCurX() && nCurY == aViewData.GetCurY() );
        bMoveIsShift = bShift;
        pSelEngine->CursorPosChanging( bShift, bControl );
        bMoveIsShift = false;
        aFunctionSet.SetCursorAtCell( nCurX, nCurY, false );

        // If the cursor has not been moved, the SelectionChanged for canceling the
        // selection has to happen here individually:
        if (bSame)
            SelectionChanged();
    }

    ShowAllCursors();
    TestHintWindow();
}

void ScTabView::MoveCursorRel( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode,
                               bool bShift, bool bKeepSel )
{
    ScDocument& rDoc = aViewData.GetDocument();
    SCTAB nTab = aViewData.GetTabNo();

    bool bSkipProtected = false, bSkipUnprotected = false;
    const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
    if ( pProtect && pProtect->isProtected() )
    {
        bSkipProtected   = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
        bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
    }

    if ( bSkipProtected && bSkipUnprotected )
        return;

    SCCOL nOldX;
    SCROW nOldY;
    SCCOL nCurX;
    SCROW nCurY;
    if ( aViewData.IsRefMode() )
    {
        nOldX = aViewData.GetRefEndX();
        nOldY = aViewData.GetRefEndY();
        nCurX = nOldX + nMovX;
        nCurY = nOldY + nMovY;
    }
    else
    {
        nOldX = aViewData.GetCurX();
        nOldY = aViewData.GetCurY();
        nCurX = (nMovX != 0) ? nOldX+nMovX : aViewData.GetOldCurX();
        nCurY = (nMovY != 0) ? nOldY+nMovY : aViewData.GetOldCurY();
    }

    if (nMovX < 0 && nOldX == 0)
    { // trying to go left from 1st column
        if (nMovY == 0) // done, because no vertical move is requested
            return;
    }
    if (nMovY < 0 && nOldY == 0)
    { // trying to go up from 1st row
        if (nMovX == 0) // done, because no horizontal move is requested
            return;
    }

    aViewData.ResetOldCursor();

    if (nMovX != 0 && rDoc.ValidColRow(nCurX,nCurY))
        SkipCursorHorizontal(nCurX, nCurY, nOldX, nMovX);

    if (nMovY != 0 && rDoc.ValidColRow(nCurX,nCurY))
        SkipCursorVertical(nCurX, nCurY, nOldY, nMovY);

    MoveCursorAbs( nCurX, nCurY, eMode, bShift, falsetrue, bKeepSel );
}

void ScTabView::MoveCursorPage( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShiftbool bKeepSel )
{
    SCCOL nPageX;
    SCROW nPageY;
    GetPageMoveEndPosition(nMovX, nMovY, nPageX, nPageY);
    MoveCursorRel( nPageX, nPageY, eMode, bShift, bKeepSel );
}

void ScTabView::MoveCursorArea( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShiftbool bKeepSel, bool bInteractiveByUser )
{
    SCCOL nNewX;
    SCROW nNewY;
    GetAreaMoveEndPosition(nMovX, nMovY, eMode, nNewX, nNewY, eMode, bInteractiveByUser);
    MoveCursorRel(nNewX, nNewY, eMode, bShift, bKeepSel);
}

void ScTabView::MoveCursorEnd( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift, bool bKeepSel )
{
    ScDocument& rDoc = aViewData.GetDocument();
    SCTAB nTab = aViewData.GetTabNo();

    SCCOL nCurX;
    SCROW nCurY;
    aViewData.GetMoveCursor( nCurX,nCurY );
    SCCOL nNewX = nCurX;
    SCROW nNewY = nCurY;

    SCCOL nUsedX = 0;
    SCROW nUsedY = 0;
    if ( nMovX > 0 || nMovY > 0 )
        rDoc.GetPrintArea( nTab, nUsedX, nUsedY );     // get end

    if (nMovX<0)
        nNewX=0;
    else if (nMovX>0)
        nNewX=nUsedX;                                   // last used range

    if (nMovY<0)
        nNewY=0;
    else if (nMovY>0)
        nNewY=nUsedY;

    aViewData.ResetOldCursor();
    MoveCursorRel( nNewX-nCurX, nNewY-nCurY, eMode, bShift, bKeepSel );
}

void ScTabView::MoveCursorScreen( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift )
{
    ScDocument& rDoc = aViewData.GetDocument();
    SCTAB nTab = aViewData.GetTabNo();

    SCCOL nCurX;
    SCROW nCurY;
    aViewData.GetMoveCursor( nCurX,nCurY );
    SCCOL nNewX = nCurX;
    SCROW nNewY = nCurY;

    ScSplitPos eWhich = aViewData.GetActivePart();
    SCCOL nPosX = aViewData.GetPosX( WhichH(eWhich) );
    SCROW nPosY = aViewData.GetPosY( WhichV(eWhich) );

    SCCOL nAddX = aViewData.VisibleCellsX( WhichH(eWhich) );
    if (nAddX != 0)
        --nAddX;
    SCROW nAddY = aViewData.VisibleCellsY( WhichV(eWhich) );
    if (nAddY != 0)
        --nAddY;

    if (nMovX<0)
        nNewX=nPosX;
    else if (nMovX>0)
        nNewX=nPosX+nAddX;

    if (nMovY<0)
        nNewY=nPosY;
    else if (nMovY>0)
        nNewY=nPosY+nAddY;

    aViewData.SetOldCursor( nNewX,nNewY );
    rDoc.SkipOverlapped(nNewX, nNewY, nTab);
    MoveCursorAbs( nNewX, nNewY, eMode, bShift, falsetrue );
}

void ScTabView::MoveCursorEnter( bool bShift )          // bShift -> up/down
{
    const ScInputOptions& rOpt = ScModule::get()->GetInputOptions();
    if (!rOpt.GetMoveSelection())
    {
        aViewData.UpdateInputHandler(true);
        return;
    }

    SCCOL nMoveX = 0;
    SCROW nMoveY = 0;
    switch (static_cast<ScDirection>(rOpt.GetMoveDir()))
    {
        case DIR_BOTTOM:
            nMoveY = bShift ? -1 : 1;
            break;
        case DIR_RIGHT:
            nMoveX = bShift ? -1 : 1;
            break;
        case DIR_TOP:
            nMoveY = bShift ? 1 : -1;
            break;
        case DIR_LEFT:
            nMoveX = bShift ? 1 : -1;
            break;
    }

    SCCOL nCurX;
    SCROW nCurY;
    aViewData.GetMoveCursor( nCurX,nCurY );
    SCCOL nNewX = nCurX;
    SCROW nNewY = nCurY;
    SCTAB nTab  = aViewData.GetTabNo();

    ScMarkData& rMark = aViewData.GetMarkData();
    ScDocument& rDoc  = aViewData.GetDocument();

    if (rMark.IsMarked() || rMark.IsMultiMarked())
    {
        rDoc.GetNextPos( nNewX, nNewY, nTab, nMoveX, nMoveY, truefalse, rMark );

        MoveCursorRel( nNewX - nCurX, nNewY - nCurY, SC_FOLLOW_LINE, falsetrue );

        //  update input line even if cursor was not moved
        if ( nNewX == nCurX && nNewY == nCurY )
            aViewData.UpdateInputHandler(true);
    }
    else
    {
        // After Tab and Enter back to the starting column again.
        const SCCOL nTabStartCol = ((nMoveY != 0 && !nMoveX) ? aViewData.GetTabStartCol() : SC_TABSTART_NONE);
        rDoc.GetNextPos( nNewX, nNewY, nTab, nMoveX, nMoveY, falsetrue, rMark, nTabStartCol );

        MoveCursorRel( nNewX - nCurX, nNewY - nCurY, SC_FOLLOW_LINE, false);
    }
}

bool ScTabView::MoveCursorKeyInput( const KeyEvent& rKeyEvent )
{
    const vcl::KeyCode& rKCode = rKeyEvent.GetKeyCode();

    enum { MOD_NONE, MOD_CTRL, MOD_ALT, MOD_BOTH } eModifier =
        rKCode.IsMod1() ?
            (rKCode.IsMod2() ? MOD_BOTH : MOD_CTRL) :
            (rKCode.IsMod2() ? MOD_ALT : MOD_NONE);

    bool bSel = rKCode.IsShift();
    sal_uInt16 nCode = rKCode.GetCode();

    // CURSOR keys
    SCCOL nDX = 0;
    SCROW nDY = 0;
    switch( nCode )
    {
        case KEY_LEFT:  nDX = -1;   break;
        case KEY_RIGHT: nDX = 1;    break;
        case KEY_UP:    nDY = -1;   break;
        case KEY_DOWN:  nDY = 1;    break;
    }
    if( nDX != 0 || nDY != 0 )
    {
        switch( eModifier )
        {
            case MOD_NONE:  MoveCursorRel( nDX, nDY, SC_FOLLOW_LINE, bSel );    break;
            case MOD_CTRL:  MoveCursorArea( nDX, nDY, SC_FOLLOW_JUMP, bSel );   break;
            default:
            {
                // added to avoid warnings
            }
        }
        // always true to suppress changes of col/row size (ALT+CURSOR)
        return true;
    }

    // PAGEUP/PAGEDOWN
    if( (nCode == KEY_PAGEUP) || (nCode == KEY_PAGEDOWN) )
    {
        nDX = (nCode == KEY_PAGEUP) ? -1 : 1;
        switch( eModifier )
        {
            case MOD_NONE:  MoveCursorPage( 0, static_cast<SCCOLROW>(nDX), SC_FOLLOW_FIX, bSel );  break;
            case MOD_ALT:   MoveCursorPage( nDX, 0, SC_FOLLOW_FIX, bSel );  break;
            case MOD_CTRL:  SelectNextTab( nDX, false );                    break;
            default:
            {
                // added to avoid warnings
            }
        }
        return true;
    }

    // HOME/END
    if( (nCode == KEY_HOME) || (nCode == KEY_END) )
    {
        nDX = (nCode == KEY_HOME) ? -1 : 1;
        ScFollowMode eMode = (nCode == KEY_HOME) ? SC_FOLLOW_LINE : SC_FOLLOW_JUMP_END;
        switch( eModifier )
        {
            case MOD_NONE:  MoveCursorEnd( nDX, 0, eMode, bSel );   break;
            case MOD_CTRL:  MoveCursorEnd( nDX, static_cast<SCCOLROW>(nDX), eMode, bSel ); break;
            default:
            {
                // added to avoid warnings
            }
        }
        return true;
    }

    return false;
}

        // next/previous unprotected cell
void ScTabView::FindNextUnprot( bool bShift, bool bInSelection )
{
    short nMove = bShift ? -1 : 1;

    ScMarkData& rMark = aViewData.GetMarkData();
    bool bMarked = bInSelection && (rMark.IsMarked() || rMark.IsMultiMarked());

    SCCOL nCurX;
    SCROW nCurY;
    aViewData.GetMoveCursor( nCurX,nCurY );
    SCCOL nNewX = nCurX;
    SCROW nNewY = nCurY;
    SCTAB nTab = aViewData.GetTabNo();

    ScDocument& rDoc = aViewData.GetDocument();
    rDoc.GetNextPos( nNewX,nNewY, nTab, nMove,0, bMarked, true, rMark );

    SCCOL nTabCol = aViewData.GetTabStartCol();
    if ( nTabCol == SC_TABSTART_NONE )
        nTabCol = nCurX;                    // back to this column after Enter

    MoveCursorRel( nNewX-nCurX, nNewY-nCurY, SC_FOLLOW_LINE, falsetrue );

    // TabCol is reset in MoveCursorRel...
    aViewData.SetTabStartCol( nTabCol );
}

void ScTabView::MarkColumns()
{
    SCCOL nStartCol;
    SCCOL nEndCol;

    ScMarkData& rMark = aViewData.GetMarkData();
    if (rMark.IsMarked())
    {
        const ScRange& aMarkRange = rMark.GetMarkArea();
        nStartCol = aMarkRange.aStart.Col();
        nEndCol = aMarkRange.aEnd.Col();
    }
    else
    {
        SCROW nDummy;
        aViewData.GetMoveCursor( nStartCol, nDummy );
        nEndCol=nStartCol;
    }

    SCTAB nTab = aViewData.GetTabNo();
    ScDocument& rDoc = aViewData.GetDocument();
    DoneBlockMode();
    InitBlockMode( nStartCol,0, nTab );
    MarkCursor( nEndCol, rDoc.MaxRow(), nTab );
    SelectionChanged();
}

void ScTabView::MarkRows()
{
    SCROW nStartRow;
    SCROW nEndRow;

    ScMarkData& rMark = aViewData.GetMarkData();
    if (rMark.IsMarked())
    {
        const ScRange& aMarkRange = rMark.GetMarkArea();
        nStartRow = aMarkRange.aStart.Row();
        nEndRow = aMarkRange.aEnd.Row();
    }
    else
    {
        SCCOL nDummy;
        aViewData.GetMoveCursor( nDummy, nStartRow );
        nEndRow=nStartRow;
    }

    SCTAB nTab = aViewData.GetTabNo();
    ScDocument& rDoc = aViewData.GetDocument();
    DoneBlockMode();
    InitBlockMode( 0,nStartRow, nTab );
    MarkCursor( rDoc.MaxCol(), nEndRow, nTab );
    SelectionChanged();
}


void ScTabView::MarkColumns(SCCOL nCol, sal_Int16 nModifier)
{
    ScDocument& rDoc = aViewData.GetDocument();
    SCCOL nStartCol = nCol;
    SCTAB nTab = aViewData.GetTabNo();

    if ((nModifier & KEY_SHIFT) == KEY_SHIFT)
        bMoveIsShift = true;

    if (ScModule::get()->IsFormulaMode())
    {
        DoneRefMode( nModifier != 0 );
        InitRefMode( nCol, 0, nTab, SC_REFTYPE_REF );
        UpdateRef( nCol, rDoc.MaxRow(), nTab );
        bMoveIsShift = false;
    }
    else
    {
        DoneBlockMode( nModifier != 0 );
        InitBlockMode( nStartCol, 0, nTab, truetrue);
        MarkCursor( nCol, rDoc.MaxRow(), nTab );
        bMoveIsShift = false;
        SetCursor( nCol, 0 );
        SelectionChanged();
    }
}

void ScTabView::MarkRows(SCROW nRow, sal_Int16 nModifier)
{
    ScDocument& rDoc = aViewData.GetDocument();
    SCROW nStartRow = nRow;
    SCTAB nTab = aViewData.GetTabNo();

    if ((nModifier & KEY_SHIFT) == KEY_SHIFT)
        bMoveIsShift = true;

    if (ScModule::get()->IsFormulaMode())
    {
        DoneRefMode( nModifier != 0 );
        InitRefMode( 0, nRow, nTab, SC_REFTYPE_REF );
        UpdateRef( rDoc.MaxCol(), nRow, nTab );
        bMoveIsShift = false;
    }
    else
    {
        DoneBlockMode( nModifier != 0 );
        InitBlockMode( 0, nStartRow, nTab, truefalsetrue );
        MarkCursor( rDoc.MaxCol(), nRow, nTab );
        bMoveIsShift = false;
        SetCursor( 0, nRow );
        SelectionChanged();
    }
}

void ScTabView::HighlightOverlay()
{
    if (!officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::get())
    {
        aViewData.GetHighlightData().ResetMark();
        UpdateHighlightOverlay();
        return;
    }

    ScAddress aCell = GetViewData().GetCurPos();
    SCROW nRow = aCell.Row();
    SCCOL nCol = aCell.Col();

    bool nModifier = false;         // modifier key pressed?
    DoneBlockModeHighlight( nModifier );
    InitBlockModeHighlight( nCol, 0, aCell.Tab(), truefalse);
    nModifier = true;
    DoneBlockModeHighlight( nModifier );
    InitBlockModeHighlight( 0, nRow, aCell.Tab(), falsetrue );
}

void ScTabView::MarkDataArea( bool bIncludeCursor )
{
    ScDocument& rDoc = aViewData.GetDocument();
    SCTAB nTab = aViewData.GetTabNo();
    SCCOL nStartCol = aViewData.GetCurX();
    SCROW nStartRow = aViewData.GetCurY();
    SCCOL nEndCol = nStartCol;
    SCROW nEndRow = nStartRow;

    rDoc.GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow, bIncludeCursor, false );

    HideAllCursors();
    DoneBlockMode();
    InitBlockMode( nStartCol, nStartRow, nTab );
    MarkCursor( nEndCol, nEndRow, nTab );
    ShowAllCursors();

    SelectionChanged();
}

void ScTabView::MarkMatrixFormula()
{
    ScDocument& rDoc = aViewData.GetDocument();
    ScAddress aCursor( aViewData.GetCurX(), aViewData.GetCurY(), aViewData.GetTabNo() );
    ScRange aMatrix;
    if ( rDoc.GetMatrixFormulaRange( aCursor, aMatrix ) )
    {
        MarkRange( aMatrix, false );        // cursor is already within the range
    }
}

void ScTabView::MarkRange( const ScRange& rRange, bool bSetCursor, bool bContinue )
{
    ScDocument& rDoc = aViewData.GetDocument();
    SCTAB nTab = rRange.aStart.Tab();
    SetTabNo( nTab );

    HideAllCursors();
    DoneBlockMode( bContinue ); // bContinue==true -> clear old mark
    if (bSetCursor)             // if Cursor is set, also always align
    {
        SCCOL nAlignX = rRange.aStart.Col();
        SCROW nAlignY = rRange.aStart.Row();
        bool bCol = ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() ) && !aViewData.GetDocument().IsInVBAMode();
        bool bRow = ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() );
        if ( bCol )
            nAlignX = aViewData.GetPosX(WhichH(aViewData.GetActivePart()));
        if ( bRow )
            nAlignY = aViewData.GetPosY(WhichV(aViewData.GetActivePart()));
        AlignToCursor( nAlignX, nAlignY, SC_FOLLOW_JUMP );
    }
    InitBlockMode( rRange.aStart.Col(), rRange.aStart.Row(), nTab );
    MarkCursor( rRange.aEnd.Col(), rRange.aEnd.Row(), nTab );
    if (bSetCursor)
    {
        SCCOL nPosX = rRange.aStart.Col();
        SCROW nPosY = rRange.aStart.Row();
        rDoc.SkipOverlapped(nPosX, nPosY, nTab);

        aViewData.ResetOldCursor();
        SetCursor( nPosX, nPosY );
    }
    ShowAllCursors();

    SelectionChanged();
}

void ScTabView::Unmark()
{
    ScMarkData& rMark = aViewData.GetMarkData();
    if ( rMark.IsMarked() || rMark.IsMultiMarked() )
    {
        SCCOL nCurX;
        SCROW nCurY;
        aViewData.GetMoveCursor( nCurX,nCurY );
        MoveCursorAbs( nCurX, nCurY, SC_FOLLOW_NONE, falsefalse );

        SelectionChanged();
    }
}

void ScTabView::SetMarkData( const ScMarkData& rNew )
{
    DoneBlockMode();
    InitOwnBlockMode( rNew.GetMarkArea());
    aViewData.GetMarkData() = rNew;

    MarkDataChanged();
}

void ScTabView::MarkDataChanged()
{
    // has to be called after making direct changes to mark data (not via MarkCursor etc)

    UpdateSelectionOverlay();
}

void ScTabView::SelectNextTab( short nDir, bool bExtendSelection )
{
    if (!nDir)
        return;
    OSL_ENSURE( nDir==-1 || nDir==1, "SelectNextTab: invalid value");

    ScDocument& rDoc = aViewData.GetDocument();
    SCTAB nTab = aViewData.GetTabNo();
    SCTAB nNextTab = nTab;
    SCTAB nCount = rDoc.GetTableCount();
    if (nDir < 0)
    {
        do
        {
            --nNextTab;
            if (nNextTab < 0)
            {
                if (officecfg::Office::Calc::Input::WrapNextPrevSheetTab::get())
                    nNextTab = nCount;
                else
                    return;
            }
            if (rDoc.IsVisible(nNextTab))
                break;
        } while (nNextTab != nTab);
    }
    else // nDir > 0
    {
        do
        {
            ++nNextTab;
            if (nNextTab >= nCount)
            {
                if (officecfg::Office::Calc::Input::WrapNextPrevSheetTab::get())
                    nNextTab = 0;
                else
                    return;
            }
            if (rDoc.IsVisible(nNextTab))
                break;
        } while (nNextTab != nTab);
    }
    if (nNextTab == nTab)
        return;

    SetTabNo(nNextTab, false, bExtendSelection);
    PaintExtras();
}

void ScTabView::SelectTabPage( const sal_uInt16 nTab )
{
    pTabControl->SwitchToPageId( nTab );
}

//  SetTabNo - set the displayed sheet

void ScTabView::SetTabNo( SCTAB nTab, bool bNew, bool bExtendSelection, bool bSameTabButMoved )
{
    if ( !ValidTab(nTab) )
    {
        OSL_FAIL("SetTabNo: invalid sheet");
        return;
    }

    if (!bNew && nTab == aViewData.GetTabNo())
        return;

    // FormShell would like to be informed before the switch
    FmFormShell* pFormSh = aViewData.GetViewShell()->GetFormShell();
    if (pFormSh)
    {
        bool bAllowed = pFormSh->PrepareClose();
        if (!bAllowed)
        {
            //! error message? or does FormShell do it?
            //! return error flag and cancel actions

            return;     // FormShell says that it can not be switched
        }
    }

                                    // not InputEnterHandler due to reference input

    ScDocument& rDoc = aViewData.GetDocument();

    rDoc.MakeTable( nTab );

    // Update pending row heights before switching the sheet, so Reschedule from the progress bar
    // doesn't paint the new sheet with old heights
    aViewData.GetDocShell().UpdatePendingRowHeights( nTab );

    SCTAB nTabCount = rDoc.GetTableCount();
    SCTAB nOldPos = nTab;
    while (!rDoc.IsVisible(nTab))              // search for next visible
    {
        bool bUp = (nTab>=nOldPos);
        if (bUp)
        {
            ++nTab;
            if (nTab>=nTabCount)
            {
                nTab = nOldPos;
                bUp = false;
            }
        }

        if (!bUp)
        {
            if (nTab != 0)
                --nTab;
            else
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=93 G=92

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.